/// <summary> /// Handle disconnect of the remote player. /// </summary> /// <param name="timeout">Was the disconnect caused by timeout?</param> private void HandleDisconnect(bool timeout) { ShowLoadingScreen(false); if (IsHost) { string reason = timeout ? "timeout" : "part"; MessagesList.AddMessage($"Player {players[1].GetName()} disconnected. ({reason})", MessageSeverity.Info); } CleanupPlayer(); // Go to main menu if we are normal player - the session just closed. if (IsPlayer) { LeaveLobby(); MPController.Instance.LoadLevel("MainMenu"); if (timeout) { MPGUI.Instance.ShowMessageBox("Session timed out."); } else { MPGUI.Instance.ShowMessageBox("Host closed the session."); } } }
/// <summary> /// Handle result of join lobby operation. /// </summary> /// <param name="result">The operation result.</param> /// <param name="ioFailure">Did IO failure happen?</param> void OnLobbyEnter(Steamworks.LobbyEnter_t result, bool ioFailure) { if (ioFailure || result.m_EChatRoomEnterResponse != (uint)Steamworks.EChatRoomEnterResponse.k_EChatRoomEnterResponseSuccess) { Logger.Error("Failed to join lobby. (reponse: {result.m_EChatRoomEnterResponse}, ioFailure: {ioFailure})"); MPGUI.Instance.ShowMessageBox($"Failed to join lobby.\n(reponse: {result.m_EChatRoomEnterResponse}, ioFailure: {ioFailure})"); players[1] = null; return; } // Setup local player. players[0] = new NetLocalPlayer(this, netWorld, Steamworks.SteamUser.GetSteamID()); Logger.Debug("Entered lobby: " + result.m_ulSteamIDLobby); MessagesList.AddMessage("Entered lobby.", MessageSeverity.Info); mode = Mode.Player; state = State.LoadingGameWorld; currentLobbyId = new Steamworks.CSteamID(result.m_ulSteamIDLobby); ShowLoadingScreen(true); SendHandshake(players[1]); }
/// <summary> /// Handle disconnect of a remote player. /// </summary> /// <param name="timeout">Was the disconnect caused by timeout?</param> private void HandleDisconnect(int playerID, bool timeout) { ShowLoadingScreen(false); if (IsHost) { string reason = timeout ? "timeout" : "part"; MessagesList.AddMessage($"Player {players[playerID].GetName()} disconnected. ({reason})", MessageSeverity.Info); CleanupPlayer(playerID); Messages.PlayerLeaveMessage msg = new Messages.PlayerLeaveMessage(); msg.playerID = playerID; msg.reason = reason; BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable); } // Go to main menu if we are normal player - the session just closed. if (IsPlayer && players[playerID].SteamId == Steamworks.SteamUser.GetSteamID()) { LeaveLobby(); MPController.Instance.LoadLevel("MainMenu"); if (timeout) { MPGUI.Instance.ShowMessageBox("Session timed out."); } else { MPGUI.Instance.ShowMessageBox("Host closed the session."); } } }
/// <summary> /// Reject remote player during connection phase. /// </summary> /// <param name="reason">The rejection reason.</param> void RejectPlayer(string reason) { MessagesList.AddMessage($"Player {players[1].GetName()} connection rejected. {reason}", MessageSeverity.Error); Logger.Error($"Player rejected. {reason}"); SendHandshake(players[1]); players[1].Dispose(); players[1] = null; }
/// <summary> /// Handle result of create lobby operation. /// </summary> /// <param name="result">The operation result.</param> /// <param name="ioFailure">Did IO failure happen?</param> void OnLobbyCreated(Steamworks.LobbyCreated_t result, bool ioFailure) { if (result.m_eResult != Steamworks.EResult.k_EResultOK || ioFailure) { Logger.Log($"Failed to create lobby. (result: {result.m_eResult}, io failure: {ioFailure})"); MPGUI.Instance.ShowMessageBox($"Failed to create lobby due to steam error.\n{result.m_eResult}/{ioFailure}", () => { MPController.Instance.LoadLevel("MainMenu"); }); return; } Logger.Debug($"Lobby has been created, lobby id: {result.m_ulSteamIDLobby}"); MessagesList.AddMessage("Session started.", MessageSeverity.Info); // Setup local player. players[0] = new NetLocalPlayer(this, netWorld, Steamworks.SteamUser.GetSteamID()); mode = Mode.Host; state = State.Playing; currentLobbyId = new Steamworks.CSteamID(result.m_ulSteamIDLobby); }
/// <summary> /// Process handshake message received from the given steam id. /// </summary> /// <param name="senderSteamId">The steam id of the sender.</param> /// <param name="msg">Hand shake message.</param> private void HandleHandshake(Steamworks.CSteamID senderSteamId, Messages.HandshakeMessage msg) { if (IsHost) { if (players[1] != null) { Logger.Log("Received handshake from player but player is already here."); LeaveLobby(); return; } // Setup THE PLAYER timeSinceLastHeartbeat = 0.0f; players[1] = new NetPlayer(this, netWorld, senderSteamId); // Check if version matches - if not ignore this player. if (msg.protocolVersion != PROTOCOL_VERSION) { RejectPlayer($"Mod version mismatch."); return; } // Player can be spawned here safely. Host is already in game and all game objects are here. players[1].Spawn(); SendHandshake(players[1]); MessagesList.AddMessage($"Player {players[1].GetName()} joined.", MessageSeverity.Info); } else { if (players[1] == null) { Logger.Log("Received handshake from host but host is not here."); LeaveLobby(); return; } // Check if protocol version matches. if (msg.protocolVersion != PROTOCOL_VERSION) { string message; if (msg.protocolVersion > PROTOCOL_VERSION) { message = "Host has newer version of the mod."; } else { message = "Host has older version of the mod."; } AbortJoining($"{message}\n(Your mod version: {PROTOCOL_VERSION}, Host mod version: {msg.protocolVersion})"); return; } // All is fine - load game world. MessagesList.AddMessage($"Connection established!", MessageSeverity.Info); MPController.Instance.LoadLevel("GAME"); // Host will be spawned when game will be loaded and OnGameWorldLoad callback will be called. } remoteClock = msg.clock; players[1].hasHandshake = true; }
/// <summary> /// Process handshake message received from the given steam id. /// </summary> /// <param name="senderSteamId">The steam id of the sender.</param> /// <param name="msg">Hand shake message.</param> private void HandleHandshake(Steamworks.CSteamID senderSteamId, Messages.HandshakeMessage msg) { if (IsHost) { // Setup THE PLAYER timeSinceLastHeartbeat = 0.0f; players.Add(players.Count, new NetPlayer(this, netWorld, senderSteamId)); int connectingPlayerID = players.Count - 1; Logger.Log("Connecting player is now ID: " + (players.Count - 1)); // Check if version matches - if not ignore this player. if (msg.protocolVersion != PROTOCOL_VERSION) { RejectPlayer(connectingPlayerID, $"Mod version mismatch."); return; } // Player can be spawned here safely. Host is already in game and all game objects are here. players[connectingPlayerID].Spawn(); SendHandshake(players[connectingPlayerID]); MessagesList.AddMessage($"Player {players[connectingPlayerID].GetName()} joined.", MessageSeverity.Info); players[connectingPlayerID].hasHandshake = true; SendPlayerJoined(connectingPlayerID, players[players.Count - 1]); } else { // Check if protocol version matches. if (msg.protocolVersion != PROTOCOL_VERSION) { string message; if (msg.protocolVersion > PROTOCOL_VERSION) { message = "Host has newer version of the mod."; } else { message = "Host has older version of the mod."; } AbortJoining($"{message}\n(Your mod version: {PROTOCOL_VERSION}, Host mod version: {msg.protocolVersion})"); return; } // All is fine - load game world. MessagesList.AddMessage($"Connection established!", MessageSeverity.Info); MPController.Instance.LoadLevel("GAME"); // Host will be spawned when game will be loaded and OnGameWorldLoad callback will be called. } remoteClock = msg.clock; connectionStartedTime = DateTime.UtcNow; }
/// <summary> /// Register protocol related network messages handlers. /// </summary> void RegisterProtocolMessagesHandlers() { netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.HandshakeMessage msg) => { HandleHandshake(sender, msg); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.HeartbeatMessage msg) => { var message = new Messages.HeartbeatResponseMessage(); message.clientClock = msg.clientClock; message.clock = GetNetworkClock(); BroadcastMessage(message, Steamworks.EP2PSend.k_EP2PSendReliable); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.HeartbeatResponseMessage msg) => { ping = (uint)(GetNetworkClock() - msg.clientClock); // TODO: Some smart lag compensation. remoteClock = msg.clock; timeSinceLastHeartbeat = 0.0f; }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.PhoneMessage msg) => { Game.PhoneManager.Instance.PhoneCall(msg.topic, msg.timesToRing); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.DartSyncMessage msg) => { Game.MapManager.Instance.SyncDartsHandler(msg.darts); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.DisconnectMessage msg) => { HandleDisconnect(GetPlayerIDBySteamID(sender), false); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.ConnectedPlayersMessage msg) => { int i = 0; foreach (int newPlayerID in msg.playerIDs) { Steamworks.CSteamID localSteamID = Steamworks.SteamUser.GetSteamID(); Steamworks.CSteamID newPlayerSteamID = new Steamworks.CSteamID(msg.steamIDs[i]); // If player is not host or local player, setup new player. if (newPlayerSteamID != hostSteamID && newPlayerSteamID != localSteamID) { players.Add(newPlayerID, new NetPlayer(this, netWorld, newPlayerSteamID)); players[newPlayerID].Spawn(); Logger.Debug("Setup new player at ID: " + newPlayerID); } i++; } }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.PlayerJoinMessage msg) => { if (NetWorld.Instance.playerIsLoading) { if (msg.steamID == Steamworks.SteamUser.GetSteamID().m_SteamID&& localPlayerID == -1) { players.Add(msg.playerID, new NetLocalPlayer(this, netWorld, new Steamworks.CSteamID(msg.steamID))); localPlayerID = msg.playerID; Logger.Debug("Setup local player as ID: " + msg.playerID); } else { Logger.Debug("Ignored connecting player as curent player is still loading!"); } return; } Steamworks.CSteamID newPlayerSteamID = new Steamworks.CSteamID(msg.steamID); if (newPlayerSteamID != hostSteamID && newPlayerSteamID != GetLocalPlayer().SteamId) { players.Add(msg.playerID, new NetPlayer(this, netWorld, newPlayerSteamID)); players[msg.playerID].Spawn(); MessagesList.AddMessage($"Player {players[msg.playerID].GetName()} joined.", MessageSeverity.Info); Logger.Debug("New player connected at ID: " + msg.playerID); } }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.PlayerLeaveMessage msg) => { if (NetWorld.Instance.playerIsLoading) { Logger.Debug("Ignored connecting player as curent player is still loading!"); return; } MessagesList.AddMessage($"Player {players[msg.playerID].GetName()} disconnected. ({msg.reason})", MessageSeverity.Info); CleanupPlayer(msg.playerID); }); }