diff --git a/cmd/client/connection_native.go b/cmd/client/connection_native.go index 9915781..97875b6 100644 --- a/cmd/client/connection_native.go +++ b/cmd/client/connection_native.go @@ -85,6 +85,13 @@ func (g *Game) wsReadPump() { payloadBytes, _ := json.Marshal(msg.Payload) var state game.GameState if err := json.Unmarshal(payloadBytes, &state); err == nil { + // Out-of-Order-Erkennung: Ignoriere alte Updates + if state.Sequence > 0 && state.Sequence <= g.lastRecvSeq { + // Alte Nachricht - ignorieren + continue + } + g.lastRecvSeq = state.Sequence + // Server Reconciliation für lokalen Spieler (VOR dem Lock) for _, p := range state.Players { if p.Name == g.playerName { diff --git a/cmd/client/connection_wasm.go b/cmd/client/connection_wasm.go index d986174..1839bec 100644 --- a/cmd/client/connection_wasm.go +++ b/cmd/client/connection_wasm.go @@ -72,6 +72,13 @@ func (g *Game) connectToServer() { payloadBytes, _ := json.Marshal(msg.Payload) var state game.GameState if err := json.Unmarshal(payloadBytes, &state); err == nil { + // Out-of-Order-Erkennung: Ignoriere alte Updates + if state.Sequence > 0 && state.Sequence <= g.lastRecvSeq { + // Alte Nachricht - ignorieren + return nil + } + g.lastRecvSeq = state.Sequence + // Server Reconciliation für lokalen Spieler (VOR dem Lock) for _, p := range state.Players { if p.Name == g.playerName { diff --git a/cmd/client/main.go b/cmd/client/main.go index ff97511..570ce4b 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -92,6 +92,7 @@ type Game struct { pendingInputs map[uint32]InputState // Noch nicht bestätigte Inputs lastServerSeq uint32 // Letzte vom Server bestätigte Sequenz predictionMutex sync.Mutex // Mutex für pendingInputs + lastRecvSeq uint32 // Letzte empfangene Server-Sequenznummer (für Out-of-Order-Erkennung) // Smooth Correction correctionX float64 // Verbleibende Korrektur in X diff --git a/pkg/game/data.go b/pkg/game/data.go index 4ae5457..adc316e 100644 --- a/pkg/game/data.go +++ b/pkg/game/data.go @@ -113,6 +113,7 @@ type GameState struct { CollectedCoins map[string]bool `json:"collected_coins"` // Welche Coins wurden eingesammelt (Key: ChunkID_ObjectIndex) CollectedPowerups map[string]bool `json:"collected_powerups"` // Welche Powerups wurden eingesammelt MovingPlatforms []MovingPlatformSync `json:"moving_platforms"` // Bewegende Plattformen + Sequence uint32 `json:"sequence"` // Sequenznummer für Out-of-Order-Erkennung } // MovingPlatformSync: Synchronisiert die Position einer bewegenden Plattform diff --git a/pkg/server/room.go b/pkg/server/room.go index aee2d81..81a382a 100644 --- a/pkg/server/room.go +++ b/pkg/server/room.go @@ -83,6 +83,7 @@ type Room struct { ChunkSpawnedCount map[string]int // Wie oft wurde jeder Chunk gespawnt MovingPlatforms []*MovingPlatform // Aktive bewegende Plattformen firstBroadcast bool // Wurde bereits geloggt? + sequence uint32 // Sequenznummer für Broadcasts stopChan chan struct{} @@ -784,6 +785,9 @@ func (r *Room) Broadcast() { r.Mutex.RLock() defer r.Mutex.RUnlock() + // Sequenznummer erhöhen + r.sequence++ + state := game.GameState{ RoomID: r.ID, Players: make(map[string]game.PlayerState), @@ -797,6 +801,7 @@ func (r *Room) Broadcast() { CollectedCoins: r.CollectedCoins, CollectedPowerups: r.CollectedPowerups, MovingPlatforms: make([]game.MovingPlatformSync, 0, len(r.MovingPlatforms)), + Sequence: r.sequence, } for id, p := range r.Players {