/// <summary> /// This method parses and handles a MOTD request from a game server or /// game client. /// </summary> /// <param name="Parser">Supplies the message parse context.</param> /// <param name="Sender">Supplies the game server address.</param> /// <param name="Socket">Supplies the associated socket descriptor /// upon which the message was received.</param> private void OnRecvMstMOTDRequest(ExoParseBuffer Parser, IPEndPoint Sender, SocketInfo Socket) { UInt16 DataPort; Byte Unknown0; // 0 Byte Unknown1; // 0 if (!Parser.ReadWORD(out DataPort)) return; if (!Parser.ReadBYTE(out Unknown0)) return; if (!Parser.ReadBYTE(out Unknown1)) return; SendMstMOTD(Sender, MOTD); }
/// <summary> /// This method parses and handles a version request from a game server /// or game client. /// </summary> /// <param name="Parser">Supplies the message parse context.</param> /// <param name="Sender">Supplies the game server address.</param> /// <param name="Socket">Supplies the associated socket descriptor /// upon which the message was received.</param> private void OnRecvMstVersionRequest(ExoParseBuffer Parser, IPEndPoint Sender, SocketInfo Socket) { UInt16 DataPort; Byte Unknown0; // 0 Byte Unknown1; // 0 Byte Unknown2; // 1 if (!Parser.ReadWORD(out DataPort)) return; if (!Parser.ReadBYTE(out Unknown0)) return; if (!Parser.ReadBYTE(out Unknown1)) return; if (!Parser.ReadBYTE(out Unknown2)) return; SendMstVersion(Sender, BuildNumber); }
/// <summary> /// This method parses and handles a module load notify message from a /// game server. /// </summary> /// <param name="Parser">Supplies the message parse context.</param> /// <param name="Sender">Supplies the game server address.</param> /// <param name="Socket">Supplies the associated socket descriptor /// upon which the message was received.</param> private void OnRecvMstModuleLoadNotify(ExoParseBuffer Parser, IPEndPoint Sender, SocketInfo Socket) { Byte ExpansionsMask; string ModuleName; if (!Parser.ReadBYTE(out ExpansionsMask)) return; if (!Parser.ReadSmallString(out ModuleName, 16)) return; // // Query for whether the sender is blacklisted and drop the message // if so. // BlacklistLookup Lookup = new BlacklistLookup(); Lookup.ServerAddress = Sender; Lookup.ModuleName = ModuleName; if (ServerTracker.IsBlacklisted(Lookup)) return; NWGameServer Server = ServerTracker.LookupServerByAddress(Sender); // // Record the module load and request an updated player count. // Server.OnModuleLoad(ExpansionsMask, ModuleName); RefreshServerStatus(Sender); Logger.Log(LogLevel.Verbose, "NWMasterServer.OnRecvMstModuleLoadNotify(): Server {0} ModuleName={1} ExpansionsMask={2}.", Sender, ModuleName, ExpansionsMask); }
/// <summary> /// This method parses and handles a server startup notify message from /// a game server. /// </summary> /// <param name="Parser">Supplies the message parse context.</param> /// <param name="Sender">Supplies the game server address.</param> /// <param name="Socket">Supplies the associated socket descriptor /// upon which the message was received.</param> private void OnRecvMstStartupNotify(ExoParseBuffer Parser, IPEndPoint Sender, SocketInfo Socket) { Byte Platform; UInt16 BuildNumber; Byte Unknown0; // 0 Byte Unknown1; // 0 Byte Unknown2; // 1 Byte Unknown3; // 0 Byte Unknown4; // 3 if (!Parser.ReadBYTE(out Platform)) return; if (!Parser.ReadWORD(out BuildNumber)) return; if (!Parser.ReadBYTE(out Unknown0)) return; if (!Parser.ReadBYTE(out Unknown1)) return; if (!Parser.ReadBYTE(out Unknown2)) return; if (!Parser.ReadBYTE(out Unknown3)) return; if (!Parser.ReadBYTE(out Unknown4)) return; // // Query for whether the sender is blacklisted and drop the message // if so. // BlacklistLookup Lookup = new BlacklistLookup(); Lookup.ServerAddress = Sender; if (ServerTracker.IsBlacklisted(Lookup)) return; NWGameServer Server = ServerTracker.LookupServerByAddress(Sender); // // Record the server startup. // Server.OnStartupNotify(Platform, BuildNumber); Logger.Log(LogLevel.Verbose, "NWMasterServer.OnRecvMstStartupNotify(): Server {0} Platform={1} BuildNumber={2}.", Sender, (char)Platform, BuildNumber); }
/// <summary> /// This method parses and handles a community authorization request /// from a game server. /// </summary> /// <param name="Parser">Supplies the message parse context.</param> /// <param name="Sender">Supplies the game server address.</param> /// <param name="Socket">Supplies the associated socket descriptor /// upon which the message was received.</param> private void OnRecvMstCommunityAuthorizationRequest(ExoParseBuffer Parser, IPEndPoint Sender, SocketInfo Socket) { UInt16 DataPort; UInt16 Length; byte[] ServerChallenge; string AccountName; byte[] ClientVerifier; UInt16 Language; Byte Platform; Byte IsPlayer; if (!Parser.ReadWORD(out DataPort)) return; if (!Parser.ReadWORD(out Length)) return; if ((ServerChallenge = Parser.ReadBytes(Length)) == null) return; if (!Parser.ReadSmallString(out AccountName, 16)) return; if (!Parser.ReadWORD(out Length)) return; if ((ClientVerifier = Parser.ReadBytes(Length)) == null) return; if (!Parser.ReadWORD(out Language)) return; if (!Parser.ReadBYTE(out Platform)) return; if (!Parser.ReadBYTE(out IsPlayer)) return; SendMstCommunityAccountAuthorization(Sender, AccountName, ConnectStatus.CONNECT_ERR_SUCCESS); }
/// <summary> /// This method parses a server name response from a game server. /// </summary> /// <param name="Parser">Supplies the message parser context.</param> /// <param name="Sender">Supplies the game server address.</param> /// <param name="Socket">Supplies the associated socket descriptor /// upon which the message was received.</param> private void OnRecvServerNameResponse(ExoParseBuffer Parser, IPEndPoint Sender, SocketInfo Socket) { Byte UpdateType; UInt16 DataPort; Byte RequestCorrelationCookie; string ServerName; if (!Parser.ReadBYTE(out UpdateType)) return; if (UpdateType != 'U') return; if (!Parser.ReadWORD(out DataPort)) return; if (!Parser.ReadBYTE(out RequestCorrelationCookie)) return; if (RequestCorrelationCookie != 0) return; if (!Parser.ReadSmallString(out ServerName)) return; NWGameServer Server = ServerTracker.LookupServerByAddress(Sender, false); if (Server != null) Server.OnServerNameUpdate(ServerName); Logger.Log(LogLevel.Verbose, "NWMasterServer.OnRecvServerNameResponse(): Server {0} name is {1}.", Sender, ServerName); }
/// <summary> /// This method parses a server info response from a game server. /// </summary> /// <param name="Parser">Supplies the message parser context.</param> /// <param name="Sender">Supplies the game server address.</param> /// <param name="Socket">Supplies the associated socket descriptor /// upon which the message was received.</param> private void OnRecvServerInfoResponse(ExoParseBuffer Parser, IPEndPoint Sender, SocketInfo Socket) { UInt16 DataPort; Byte Reserved; // 0xFC Byte HasPlayerPassword; Byte MinLevel; Byte MaxLevel; Byte ActivePlayers; Byte MaximumPlayers; Byte IsLocalVault; Byte PVPLevel; Byte IsPlayerPauseAllowed; Byte IsOnePartyOnly; Byte IsELC; Byte HasILR; Byte ExpansionsMask; string ModuleName; string BuildNumber; ServerInfo Info = new ServerInfo(); if (!Parser.ReadWORD(out DataPort)) return; if (!Parser.ReadBYTE(out Reserved)) return; if (Mode == GameMode.NWN2) { if (Reserved != 0xFC) return; } else { if (Reserved != 0xFD) return; } if (!Parser.ReadBYTE(out HasPlayerPassword)) return; if (!Parser.ReadBYTE(out MinLevel)) return; if (!Parser.ReadBYTE(out MaxLevel)) return; if (!Parser.ReadBYTE(out ActivePlayers)) return; if (!Parser.ReadBYTE(out MaximumPlayers)) return; if (!Parser.ReadBYTE(out IsLocalVault)) return; if (!Parser.ReadBYTE(out PVPLevel)) return; if (!Parser.ReadBYTE(out IsPlayerPauseAllowed)) return; if (!Parser.ReadBYTE(out IsOnePartyOnly)) return; if (!Parser.ReadBYTE(out IsELC)) return; if (!Parser.ReadBYTE(out HasILR)) return; if (!Parser.ReadBYTE(out ExpansionsMask)) return; if (!Parser.ReadSmallString(out ModuleName)) return; if (Mode == GameMode.NWN2) { if (!Parser.ReadSmallString(out BuildNumber)) return; } else { BuildNumber = "0"; } try { Info.BuildNumber = Convert.ToUInt16(BuildNumber); } catch { Info.BuildNumber = 0; } // // Query for whether the sender is blacklisted and drop the message // if so. // BlacklistLookup Lookup = new BlacklistLookup(); Lookup.ServerAddress = Sender; Lookup.ModuleName = ModuleName; if (ServerTracker.IsBlacklisted(Lookup)) return; Info.HasPlayerPassword = (HasPlayerPassword != 0); Info.MinLevel = MinLevel; Info.MaxLevel = MaxLevel; Info.ActivePlayers = ActivePlayers; Info.MaximumPlayers = MaximumPlayers; Info.IsLocalVault = (IsLocalVault != 0); Info.PVPLevel = PVPLevel; Info.IsPlayerPauseAllowed = (IsPlayerPauseAllowed != 0); Info.IsOnePartyOnly = (IsOnePartyOnly != 0); Info.IsELC = (IsELC != 0); Info.HasILR = (HasILR != 0); Info.ExpansionsMask = ExpansionsMask; Info.ModuleName = ModuleName; // // Look up the server and update the current server information. // Since the BNXR reply is used to differentiate between broken // NATs and endpoints with multiple servers on the same IP address, // carefully check for whether a duplicate server record exists on // the server internal port before creating a new server record. // NWGameServer Server; Server = ServerTracker.LookupServerByAddress(Sender); if (Mode != GameMode.NWN2) Info.BuildNumber = Server.BuildNumber; Server.OnServerInfoUpdate(Info); if (DataPort == (UInt16)Sender.Port) { // // Both internal and external ports match; the sender is not // likely behind a NAT. No action is necessary behind the // creation of the server record above. // } else { NWGameServer ServerInternal; IPEndPoint InternalAddress = new IPEndPoint(Sender.Address, (int)DataPort); ServerInternal = ServerTracker.LookupServerByAddress(InternalAddress, false); if (ServerInternal == null) { // // No record of a server existing at the internal address // is yet known. Proceed to create the server record at // the external address (as was already performed above). // } else { // // A record exists for both internal and external // addresses for the server. If the configuration values // between both servers are the same, then mark the // external address version as offline and prefer the // internal server address as authoritative (since it must // be globally reachable for a response to have been // received). // if (ServerInternal.CheckForNATDuplicate(Server)) { Logger.Log(LogLevel.Normal, "NWMasterServer.OnRecvServerInfoResponse(): Removing NAT duplicate server {0} in preference of server {1}.", Sender, InternalAddress); return; } } } Logger.Log(LogLevel.Verbose, "NWMasterServer.OnRecvServerInfoResponse(): Server {0} has {1}/{2} players ({3}).", Sender, Info.ActivePlayers, Info.MaximumPlayers, Info.ModuleName); }