package main import ( "git.zb-server.de/ZB-Server/EscapeFromTeacher/pkg/game" "git.zb-server.de/ZB-Server/EscapeFromTeacher/pkg/physics" ) // ApplyInput wendet einen Input auf den vorhergesagten Zustand an // Nutzt die gemeinsame Physik-Engine aus pkg/physics func (g *Game) ApplyInput(input InputState) { // Horizontale Bewegung mit analogem Joystick moveX := 0.0 if input.Left { moveX = -1.0 } else if input.Right { moveX = 1.0 } // Wenn Joystick benutzt wird, überschreibe moveX mit analogem Wert if input.JoyX != 0 { moveX = input.JoyX } // Physik-State vorbereiten state := physics.PlayerPhysicsState{ X: g.predictedX, Y: g.predictedY, VX: g.predictedVX, VY: g.predictedVY, OnGround: g.predictedGround, OnWall: g.predictedOnWall, } // Physik-Input vorbereiten physicsInput := physics.PhysicsInput{ InputX: moveX, Jump: input.Jump, Down: input.Down, } // Kollisions-Checker vorbereiten g.stateMutex.Lock() collisionChecker := &physics.ClientCollisionChecker{ World: g.world, ActiveChunks: g.gameState.WorldChunks, MovingPlatforms: g.gameState.MovingPlatforms, } g.stateMutex.Unlock() // Gemeinsame Physik anwenden (1:1 wie Server) physics.ApplyPhysics(&state, physicsInput, g.currentSpeed, collisionChecker, physics.DefaultPlayerConstants()) // Ergebnis zurückschreiben g.predictedX = state.X g.predictedY = state.Y g.predictedVX = state.VX g.predictedVY = state.VY g.predictedGround = state.OnGround g.predictedOnWall = state.OnWall } // ReconcileWithServer gleicht lokale Prediction mit Server-State ab func (g *Game) ReconcileWithServer(serverState game.PlayerState) { g.predictionMutex.Lock() defer g.predictionMutex.Unlock() // Server-bestätigte Sequenz g.lastServerSeq = serverState.LastInputSeq // Entferne alle bestätigten Inputs for seq := range g.pendingInputs { if seq <= g.lastServerSeq { delete(g.pendingInputs, seq) } } // Temporäre Position für Replay (jetzt MIT Y-Achse) replayX := serverState.X replayY := serverState.Y replayVX := serverState.VX replayVY := serverState.VY replayGround := serverState.OnGround replayOnWall := serverState.OnWall // Replay alle noch nicht bestätigten Inputs mit VOLLER Physik if len(g.pendingInputs) > 0 { for seq := g.lastServerSeq + 1; seq <= g.inputSequence; seq++ { if input, ok := g.pendingInputs[seq]; ok { // Temporär auf Replay-Position setzen oldX, oldY := g.predictedX, g.predictedY oldVX, oldVY := g.predictedVX, g.predictedVY oldGround := g.predictedGround oldOnWall := g.predictedOnWall g.predictedX = replayX g.predictedY = replayY g.predictedVX = replayVX g.predictedVY = replayVY g.predictedGround = replayGround g.predictedOnWall = replayOnWall g.ApplyInput(input) replayX = g.predictedX replayY = g.predictedY replayVX = g.predictedVX replayVY = g.predictedVY replayGround = g.predictedGround replayOnWall = g.predictedOnWall // Zurücksetzen g.predictedX = oldX g.predictedY = oldY g.predictedVX = oldVX g.predictedVY = oldVY g.predictedGround = oldGround g.predictedOnWall = oldOnWall } } } // Berechne Differenz zwischen Client-Prediction und Server-Replay (X und Y) diffX := replayX - g.predictedX diffY := replayY - g.predictedY dist := diffX*diffX + diffY*diffY // Speichere Korrektur-Magnitude für Debug g.correctionX = diffX g.correctionY = diffY // === NEUE KORREKTUR-LOGIK MIT TOLERANZ === const ( // Toleranzen für Client-Abweichungen smallTolerance = 4.0 // 2px - sofort korrigieren normalTolerance = 225.0 // 15px - akzeptable Abweichung, sanfte Korrektur largeTolerance = 900.0 // 30px - Warnschwelle, nur noch Monitoring hugeTolerance = 10000.0 // 100px - kritisch (Teleport/Respawn) ) if dist < smallTolerance { // Bei sehr kleinen Abweichungen (<2px): Sofort korrigieren um Drift zu vermeiden g.predictedX = replayX g.predictedY = replayY } else if dist < normalTolerance { // Bei kleinen Abweichungen (2-15px): Sanfte Korrektur, Client hat etwas Spielraum interpFactor := 0.3 // Nur 30% Korrektur - mehr Client-Freiheit g.predictedX += diffX * interpFactor g.predictedY += diffY * interpFactor g.correctionCount++ } else if dist < largeTolerance { // Bei mittleren Abweichungen (15-30px): Stärkere Korrektur interpFactor := 0.5 // 50% Korrektur g.predictedX += diffX * interpFactor g.predictedY += diffY * interpFactor g.correctionCount++ } else if dist < hugeTolerance { // Bei großen Abweichungen (30-100px): Nur noch Monitoring, KEINE Korrektur mehr // Der Unterschied ist zu groß geworden - Client-Position wird akzeptiert // Server überwacht weiterhin, greift aber nicht mehr ein // (Dies verhindert "Ruckeln" bei starker Latenz) } else { // Bei kritischen Abweichungen (>100px): Sofort korrigieren (Teleport/Respawn/Desync) g.predictedX = replayX g.predictedY = replayY g.correctionCount++ } // Velocity und Ground Status vom Server übernehmen g.predictedVX = replayVX g.predictedVY = replayVY g.predictedGround = replayGround g.predictedOnWall = replayOnWall }