/// <summary> /// Adds a packet to the queue for serial processing. /// </summary> /// <param name="con">the connection which sent the packet</param> /// <param name="msg">the packet</param> public void AddGamePacketForProcessing(GSLobbyInboundPlayerConnection con, PacketGameMessage msg) { Action <object> lambda = (state) => { Action <ServerUser, PacketGameMessage> handler = m_PacketHandlers.GetHandlerDelegate(msg.PacketSubTypeID); if (handler != null && OnBeforeHandleGamePacket(con.ServerUser, msg)) { try { handler(con.ServerUser, msg); } catch (Exception e) { Log.LogMsg("Exception thrown whilst processing game packet type " + msg.PacketTypeID.ToString() + ", sub-type " + msg.PacketSubTypeID + ". Object = " + this.GetType().ToString() + ", Message: " + e.Message + ". Stack:\r\n " + e.StackTrace); } con.OnAfterPacketProcessed(msg); return; } con.KillConnection("Did not have a registered game packet handler for game packet. " + msg.PacketTypeID.ToString() + ", SubType " + msg.PacketSubTypeID.ToString() + ". "); }; // if we're not processing packets immediately, then this method (AddGamePacketForProcessing) is being called as part of a tight networking processing loop // which is executed serially, so we need to NOT queue the packet in that case and just run it if (!con.ProcessIncomingPacketsImmediately) { lambda(null); } else { Task t = new Task(lambda, "Game [" + GameID.ToString() + "] Process game packet " + msg.PacketSubTypeID.ToString(), TaskCreationOptions.LongRunning); m_NetQ.AddTask(t); } }
/// <summary> /// Gets called when a player is removed from the game /// </summary> /// <param name="character"></param> protected virtual void OnPlayerRemoved(ServerCharacterInfo toon, string reason, bool playerInitiated) { toon.SetCurrentGame(null); // Update game stats in game listing DB DB.Instance.Lobby_UpdateGameForServer(this); // Send notification to player that player left, including game state update. Player is no longer part of "AllPlayers" list at this point, so we // have to manually send this notification to them. SendGamePropertiesUpdateToPlayer(toon, this.Properties.ID, this.Properties.AllProperties); SendMatchChangeNotificationToPlayer(toon, MatchNotificationType.PlayerRemoved, toon, reason, false); // Broadcast to all remaining players SendMatchChangeNotificationToPlayers(MatchNotificationType.PlayerRemoved, toon, reason, false); SendGamePropertiesUpdateToPlayers(this.Properties.ID, this.Properties.AllProperties); // Set new game owner if necessary if (Owner == toon.ID) { if (AllPlayers.Count > 0) { Owner = AllPlayers[0].ID; BroadcastGameInfoMessage(AllPlayers[0].CharacterName + " is now the owner of this game.", true); PropertyBag props = new PropertyBag(); props.SetProperty("NewOwner", Owner); BroadcastGameMessage((int)LobbyGameMessageSubType.NewOwner, props, true, false, false); } } // when a player leaves the game, they go back to Central if (CurrentGameState == GameState.Lobby || CurrentGameState == GameState.Started) { string address = ""; int port = 0; string serverId = ""; if (!GSLobbyInboundPlayerConnection.GetCentralHandoffAddress(ref address, ref port, ref serverId)) { toon.OwningAccount.MyConnection.KillConnection("Unable to host player on this server. No lobby server found to hand off too after leaving game."); return; } toon.OwningAccount.TransferToServerUnassisted(address, port, Guid.Empty, "Lobby Server", serverId); } }
/// <summary> /// Override from ServerBase, to make sure we create the proper connection object for inbound connections. /// If we don't override this method, a generic InboundConnection class will be instantiated. /// </summary> protected override InboundConnection CreateInboundConnection(Socket s, ServerBase server, int serviceID, bool isBlocking) { InboundConnection con = null; // ServiceIDs represents the type connection that is being requested. // these IDs are set in the App.Config of the initiating server and are any arbitrarily agreed upon integers switch (serviceID) { case 7: con = new ZeusInboundConnection(s, server, isBlocking); con.ServiceID = serviceID; break; case 1: // central server con = new GSLobbyInboundCentralConnection(s, server, isBlocking); con.ServiceID = serviceID; GameManager.Instance.CentralServer = con as GSInboundServerConnection; break; case 8: // Beholder Daemon con = new GSLobbyInboundBeholderConnection(s, server, isBlocking); con.ServiceID = 8; break; default: // assume player client con = new GSLobbyInboundPlayerConnection(s, server, isBlocking); con.ServiceID = serviceID; break; } #if DEBUG if (con == null) { throw new ArgumentOutOfRangeException("ServiceID " + serviceID.ToString() + " is unknown to CreateInboundConnection. Cannot process connection."); } #endif return(con); }
/// <summary> /// Override from ServerBase, to make sure we create the proper connection object for inbound connections. /// If we don't override this method, a generic InboundConnection class will be instantiated. /// </summary> protected override InboundConnection CreateInboundConnection(Socket s, ServerBase server, int serviceID, bool isBlocking) { InboundConnection con = null; // ServiceIDs represents the type connection that is being requested. // these IDs are set in the App.Config of the initiating server and are any arbitrarily agreed upon integers switch (serviceID) { case 7: con = new ZeusInboundConnection(s, server, isBlocking); con.ServiceID = serviceID; break; case 1: // central server con = new GSLobbyInboundCentralConnection(s, server, isBlocking); con.ServiceID = serviceID; GameManager.Instance.CentralServer = con as GSInboundServerConnection; break; case 8: // Beholder Daemon con = new GSLobbyInboundBeholderConnection(s, server, isBlocking); con.ServiceID = 8; break; default: // assume player client con = new GSLobbyInboundPlayerConnection(s, server, isBlocking); con.ServiceID = serviceID; break; } #if DEBUG if (con == null) { throw new ArgumentOutOfRangeException("ServiceID " + serviceID.ToString() + " is unknown to CreateInboundConnection. Cannot process connection."); } #endif return con; }