public void UpdateNetworkEvents() { void handleReceivedData(IRemote source, INetworkData data, ResponseHandler responseHandler) { if (!(source is IRemoteServer server)) { Log.Error.Write(ErrorSystemType.Network, "Client received data from a non-server."); responseHandler?.Invoke(ResponseType.BadDestination); return; } ProcessData(server, data, responseHandler); } NetworkDataReceiver?.ProcessReceivedData(handleReceivedData); if (Game != null && client != null && client.Connected) { lock (Game) { var gameTime = Game.GameTime; // Save game state from time to time to avoid huge syncs. if (gameTime - lastSavedGameStateGameTime >= SavedGameState.SyncDelay && SavedGameState.TimeToSync(Game)) { Log.Verbose.Write(ErrorSystemType.Network, $"Saving game state with game time {Misc.SecondsToTime(gameTime)}."); lastSavedGameStateGameTime = gameTime; lastSavedGameStates.Add(gameTime, SavedGameState.FromGame(Game)); Log.Verbose.Write(ErrorSystemType.Network, $"Finished saving game state with game time {Misc.SecondsToTime(gameTime)}."); } } } }
void ProcessData(IRemoteServer server, INetworkData networkData, ResponseHandler responseHandler) { switch (networkData.Type) { case NetworkDataType.Request: HandleRequest(networkData as RequestData, responseHandler); break; case NetworkDataType.Heartbeat: { var heartbeat = networkData as Heartbeat; // Last heartbeat time was set before. if (PlayerIndex == 0u) { PlayerIndex = heartbeat.PlayerId; } foreach (var registeredHeartbeatHandler in registeredHeartbeatHandlers.ToArray()) { registeredHeartbeatHandler?.Invoke(heartbeat); } responseHandler?.Invoke(ResponseType.Ok); break; } case NetworkDataType.LobbyData: if (serverState != ServerState.Lobby) { responseHandler?.Invoke(ResponseType.BadState); } else { responseHandler?.Invoke(ResponseType.Ok); UpdateLobbyData(networkData as LobbyData); } break; case NetworkDataType.Response: { var responseData = networkData as ResponseData; foreach (var registeredResponseHandler in registeredResponseHandlers.ToArray()) { registeredResponseHandler?.Invoke(responseData); } break; } case NetworkDataType.InSync: { if (serverState != ServerState.Game && serverState != ServerState.Loading) { responseHandler?.Invoke(ResponseType.BadState); } else { if (Game == null) { serverState = ServerState.Offline; responseHandler?.Invoke(ResponseType.BadState); return; } try { var insyncData = networkData as InSyncData; Log.Verbose.Write(ErrorSystemType.Network, $"Processing in-sync message with game time {Misc.SecondsToTime(insyncData.GameTime)}."); if (!lastSavedGameStates.ContainsKey(insyncData.GameTime)) // We don't have the saved state anymore -> need full update { Log.Verbose.Write(ErrorSystemType.Network, $"Last saved game state with game time {Misc.SecondsToTime(insyncData.GameTime)} not available. Requesting re-sync."); RequestGameStateUpdate(); return; } Log.Verbose.Write(ErrorSystemType.Network, $"Updating last synced saved state to game time {Misc.SecondsToTime(insyncData.GameTime)} and discarding outdated saved game states."); lastVerifiedSavedGameState = lastSavedGameStates[insyncData.GameTime]; // Remove all outdated (timestamp before in-sync game time) saved states. foreach (var outdatedSavedGameState in lastSavedGameStates.Where(s => s.Key <= insyncData.GameTime).ToList()) { lastSavedGameStates.Remove(outdatedSavedGameState.Key); } } catch (Exception ex) { Log.Error.Write(ErrorSystemType.Network, "Failed to update game state: " + ex.Message); Disconnect(); throw ex; // TODO: Close game instead of crash? } } break; } case NetworkDataType.SyncData: { if (serverState != ServerState.Game && serverState != ServerState.Loading) { responseHandler?.Invoke(ResponseType.BadState); } else { if (Game == null) { serverState = ServerState.Offline; responseHandler?.Invoke(ResponseType.BadState); return; } try { var syncData = networkData as SyncData; #if DEBUG var stopWatch = System.Diagnostics.Stopwatch.StartNew(); Log.Verbose.Write(ErrorSystemType.Network, "Processing sync ... "); #endif lock (Game) { lastSavedGameStates.Clear(); if (lastVerifiedSavedGameState == null) { lastVerifiedSavedGameState = SavedGameState.FromGame(Game); } lastVerifiedSavedGameState = SavedGameState.UpdateGameAndLastState(Game, lastVerifiedSavedGameState, syncData.SerializedData, syncData.Full); } #if DEBUG Log.Verbose.Write(ErrorSystemType.Network, $"Processing sync done in {stopWatch.ElapsedMilliseconds / 1000.0} seconds"); #endif } catch (Exception ex) { Log.Error.Write(ErrorSystemType.Network, "Failed to update game state: " + ex.Message); Disconnect(); throw ex; // TODO: Close game instead of crash? } } break; } default: // Should have been handled by Server_DataReceived already. break; } }