/// <summary> /// Unbind query and heartbeat listeners from the specified port /// </summary> /// <param name="listenPort">Port to unbind from</param> private void UnBind(int listenPort) { if (queryListeners.ContainsKey(listenPort)) { try { queryListeners[listenPort].Shutdown(); queryListeners.Remove(listenPort); heartbeatListeners[listenPort].Shutdown(); heartbeatListeners[listenPort].ReceivedHeartbeat -= new ReceivedHeartbeatHandler(serverList.ReceivedHeartbeat); heartbeatListeners.Remove(listenPort); MasterServer.Log("[NET] Port {0} unbound successfully", listenPort); } catch (Exception ex) { MasterServer.Log("[NET] Error unbinding port(s) {0}, {1}", listenPort, ex.Message); } } else { MasterServer.Log("[NET] Port {0} not bound", listenPort); } UpdateListenPortList(); }
/// <summary> /// Add/update a remote server /// </summary> /// <param name="active">True if the server is active (gamestate has been received)</param> /// <param name="address">IP address of the server</param> /// <param name="cdkey">CD key hash of the server</param> /// <param name="name">Name of the server</param> /// <param name="country">Server's country</param> /// <param name="locale">Server's locale (eg. int)</param> /// <param name="port">Server listen port</param> /// <param name="queryport">Server query port</param> /// <param name="map">Current map</param> /// <param name="gametype">Current game type</param> /// <param name="maxplayers">Max connections</param> /// <param name="currentplayers">Current player count</param> /// <param name="properties">Array of server properties</param> /// <param name="players">List of players on the server</param> public void Update(bool active, IPAddress address, string cdkey, string name, string country, string locale, int port, int queryport, string map, string gametype, int maxplayers, int currentplayers, Dictionary <string, string> properties, List <Player> players) { Debug.WriteLine(String.Format("[RPC] Received UPDATE packet for {0}:{1}", address.ToString(), port)); lock (serverListLock) { foreach (Server server in servers) { // Match remote servers on IP and listen port if (server.Address.Equals(address) && server.Port == port) { if (!server.Local) { Debug.WriteLine(String.Format("[RPC] Applying update packet for {0}:{1}", address.ToString(), port)); server.Update(active, address, cdkey, name, country, locale, port, queryport, map, gametype, maxplayers, currentplayers, properties, players); } // Found server so return return; } } } MasterServer.Log("[{0}:{1}] Creating non-local server", address.ToString(), port); // // Didn't find a matching server so create a new nonlocal server // Server newServer = new Server(null, Protocol.MIN_SUPPORTED_CLIENT_VERSION, address, "", geoIP, 0, false, XMPMS.Net.OperatingSystem.UnknownOS, locale, 0); newServer.Update(active, address, cdkey, name, country, locale, port, queryport, map, gametype, maxplayers, currentplayers, properties, players); Add(newServer); }
/// <summary> /// Terminate the listeners /// </summary> private void EndListening() { if (listening) { MasterServer.Log("Shutting down sockets..."); listening = false; // Create list of active listen ports List <int> listenPorts = new List <int>(queryListeners.Keys); // Unbind listen ports in order foreach (int listenPort in listenPorts) { UnBind(listenPort); } // Abort any remaining connections which were not terminated by the listeners (shouldn't be any but it's best to make sure! ConnectionManager.AbortAll(); try { if (webServer != null) { webServer.EndListening(); } } catch { } } }
/// <summary> /// Bind query and heartbeat listeners to the specified port /// </summary> /// <param name="listenPort">Port (TCP and UDP) to bind the listeners to</param> private void Bind(int listenPort) { if (queryListeners.ContainsKey(listenPort)) { MasterServer.Log("[NET] Port {0} already bound", listenPort); return; } try { IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, listenPort); QueryListener queryListener = new QueryListener(endpoint, serverList, geoIP, md5Manager, banManager, cdKeyValidator, gameStats); queryListeners.Add(listenPort, queryListener); HeartbeatListener heartbeatListener = new HeartbeatListener(endpoint); heartbeatListener.ReceivedHeartbeat += new ReceivedHeartbeatHandler(serverList.ReceivedHeartbeat); heartbeatListeners.Add(listenPort, heartbeatListener); MasterServer.Log("[NET] Port {0} bound successfully", listenPort); } catch (Exception ex) { MasterServer.Log("[NET] Error binding port(s) {0}, {1}", listenPort, ex.Message); queryListeners.Remove(listenPort); heartbeatListeners.Remove(listenPort); } UpdateListenPortList(); }
/// <summary> /// Tests a remote master server link by requesting PING /// </summary> /// <param name="index"></param> /// <returns></returns> public bool TestLink(int index) { #if !WCF #warning "WCF functionality disabled - ServerList RPC will not function" #else if (index > -1 && index < MasterServer.Settings.SyncServiceUris.Count) { string remoteMasterUri = MasterServer.Settings.SyncServiceUris[index]; RemoteMasterServer testMe = GetRemoteServer(remoteMasterUri); if (testMe != null) { MasterServer.Log("[RPC] Testing connection to {0}", testMe.InnerChannel.RemoteAddress.Uri.Host); try { string pingResponse = testMe.Ping(); MasterServer.Log("[RPC] Test link succeeded."); return(true); } catch (MessageSecurityException) { MasterServer.Log("[RPC] Test link failed: credentials rejected by remote server"); } catch (CommunicationException ex) { MasterServer.Log("[RPC] Test link failed:"); MasterServer.Log("[RPC] {0}", ex.Message); } } } #endif return(false); }
/// <summary> /// Server is shutting down, release resources /// </summary> public void Shutdown() { lock (serverListLock) { foreach (Server server in servers) { server.Shutdown(); } } #if !WCF #warning "WCF functionality disabled - ServerList RPC will not function" #else if (serviceHost != null) { try { if (serviceHost.State == CommunicationState.Opened) { MasterServer.Log("[RPC] Closing service host"); serviceHost.Close(); } serviceHost.Abort(); } catch { } } #endif ModuleManager.ReleaseModule <IConnectionLogWriter>(); }
/// <summary> /// Update information for a local server using the supplied gamestate packet /// </summary> /// <param name="server">Server to update</param> /// <param name="serverInfo">Gamestate packet</param> public void UpdateServer(Server server, ServerInfoPacket serverInfo) { // Update the server server.Update(serverInfo); // Remove duplicate servers List <Server> pendingRemoval = new List <Server>(); lock (serverListLock) { foreach (Server otherServer in servers) { if (otherServer != server && otherServer.IsDuplicateOf(server)) { // Can't modify the collection here pendingRemoval.Add(otherServer); } } } foreach (Server removeServer in pendingRemoval) { MasterServer.Log("[{0}] Removing duplicate server record.", removeServer); Remove(removeServer); } #if !WCF #warning "WCF functionality disabled - ServerList RPC will not function" #else // Propogate server information to remote master servers foreach (RemoteMasterServer remoteMaster in remoteMasters) { try { remoteMaster.Update( server.Active, server.Address, server.CDKey, server.Name, server.Country, server.Locale, server.Port, server.QueryPort, server.Map, server.GameType, server.MaxPlayers, server.CurrentPlayers, server.Properties, server.Players ); } catch (MessageSecurityException) { MasterServer.Log("[RPC] Credentials rejected by remote server at {0}", remoteMaster.InnerChannel.RemoteAddress.Uri.Host); } catch (CommunicationException) { } } #endif }
/// <summary> /// The query response timed out, close the connection since non-queryable servers are probably not accessible /// </summary> /// <param name="queryType">Query Type which timed out</param> protected virtual void QueryResponseTimeout(UDPServerQueryClient.UDPServerQueryType queryType) { if (queryType == UDPServerQueryClient.UDPServerQueryType.Basic) { MasterServer.Log("[{0}] Timeout contacting queryport on {1}. Type={2}", DisplayAddress, QueryPort, queryType); Shutdown(); OnConnectionError(); } }
/// <summary> /// Remove a server by IP and port /// </summary> /// <param name="address"></param> /// <param name="port"></param> public void Remove(IPAddress address, int port) { Debug.WriteLine(String.Format("[RPC] Received REMOVE packet for {0}:{1}", address.ToString(), port)); // Find the matching server Server serverToRemove = GetServer(address, port, true); if (serverToRemove != null) { MasterServer.Log("[{0}:{1}] Removing non-local server", address.ToString(), port); Remove(serverToRemove); } }
/// <summary> /// Add a new remote master server entry /// </summary> /// <param name="remoteMasterUri">RPC URI of the remote server</param> private void AddRemoteServer(string remoteMasterUri) { try { #if !WCF #warning "WCF functionality disabled - ServerList RPC will not function" #else BasicHttpBinding remoteBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly); remoteBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic; remoteMasters.Add(new RemoteMasterServer(remoteBinding, remoteMasterUri)); #endif } catch (Exception ex) { MasterServer.Log("[RPC] Error initialising remote link to {0}: {1}", remoteMasterUri, ex.Message); } }
/// <summary> /// Remove a server from the server list /// </summary> /// <param name="server">Server to remove</param> public void Remove(Server server) { // Don't want to do the remote removal inside the critical section bool serverRemoved = false; lock (serverListLock) { if (servers.Contains(server)) { servers.Remove(server); serverRemoved = true; } } if (serverRemoved) { // If the server is local, notify remote master servers to remove the server if (server.Local) { server.ConnectionError -= new EventHandler(server_ConnectionError); #if !WCF #warning "WCF functionality disabled - ServerList RPC will not function" #else foreach (RemoteMasterServer remoteMaster in remoteMasters) { try { Debug.WriteLine(String.Format("[RPC] Attempting to remove server remotely on {0}", remoteMaster.InnerChannel.RemoteAddress.Uri)); remoteMaster.Remove(server.Address, server.Port); } catch (MessageSecurityException) { MasterServer.Log("[RPC] Credentials rejected by remote server at {0}", remoteMaster.InnerChannel.RemoteAddress.Uri.Host); } catch (CommunicationException) { } } #endif } server.Dispose(); } }
/// <summary> /// Callback for the scavenge timer, performs scavenging of stale remote server records /// </summary> /// <param name="State"></param> protected void Scavenge(object state) { List <Server> pendingRemoval = new List <Server>(); lock (serverListLock) { foreach (Server server in servers) { if (!server.Local && (DateTime.Now - server.LastUpdate).TotalSeconds > scavengeAfter.TotalSeconds) { // Can't modify the collection here pendingRemoval.Add(server); } } } foreach (Server removeServer in pendingRemoval) { MasterServer.Log("[{0}] Removing stale server record.", removeServer); Remove(removeServer); } scavengeTimer.Change(10000, Timeout.Infinite); }
/// <summary> /// Handle application shutting down /// </summary> /// <param name="sender"></param> /// <param name="e"></param> static void CurrentDomain_ProcessExit(object sender, EventArgs e) { MasterServer.Log("ProcessExit()"); ModuleManager.ReleaseAllModules(); }
/// <summary> /// Callback from the module manager when a console command is issued /// </summary> /// <param name="command"></param> public void Command(string[] command) { if (command.Length > 0 && command[0].Trim() != "") { if (commandInterface == null || commandInterface.EchoCommands) { LogCommand(command); } switch (command[0].ToLower()) { case "stop": BeginStop(); break; case "ver": MasterServer.LogMessage("[INFO] Application : {0}", MasterServer.Title); MasterServer.LogMessage("[INFO] Version : {0}", MasterServer.Version); MasterServer.LogMessage("[INFO] NetVersion : {0}", MasterServer.NetVersion); MasterServer.LogMessage("[INFO] Copyright : {0}", MasterServer.Copyright); MasterServer.LogMessage("[INFO] Legal Notice : Unreal and the Unreal logo are registered trademarks of Epic"); MasterServer.LogMessage(" Games, Inc. ALL RIGHTS RESERVED."); break; case "clear": case "cls": log.Clear(); if (commandInterface != null) { commandInterface.Notify("LOG", "\u001B[2J"); } break; case "ls": MasterServer.LogMessage("List what?"); break; case "dir": MasterServer.LogMessage("This isn't DOS..."); break; case "motd": if (command.Length > 1) { string locale = command[1].ToLower(); if (command.Length > 2) { if (!MasterServer.SetMOTD(locale, String.Join(" ", command, 2, command.Length - 2))) { MasterServer.LogMessage("[MOTD] Error, locale \"{0}\" is not defined. MOTD was not updated.", locale); } } MasterServer.LogMessage("[MOTD] {0} = \"{1}\"", locale, MasterServer.GetMOTD(locale, false)); } else { MasterServer.LogMessage("motd <locale> <message>"); } break; case "stat": if (command.Length > 1) { switch (command[1].ToLower()) { case "clear": MasterServer.Log("Total Queries = {0}", TotalQueries); MasterServer.Log("Total Web Queries = {0}", TotalWebQueries); Stats.Default.TotalQueries = 0; Stats.Default.TotalWebQueries = 0; Stats.Default.Save(); MasterServer.Log("Stats cleared"); break; } } else { MasterServer.LogMessage("stat clear Clear statistics"); } break; case "log": if (command.Length > 1) { switch (command[1].ToLower()) { case "clear": log.Clear(); break; case "commit": if (logWriter != null) { logWriter.Commit(); } break; } } else { MasterServer.LogMessage("log clear Clear log buffer"); MasterServer.LogMessage("log commit Commit unsaved log"); } break; case "mslist": if (command.Length > 1) { switch (command[1].ToLower()) { case "on": MasterServer.Settings.MSListEnabled = true; MasterServer.Settings.Save(); MasterServer.LogMessage("MSLIST function turned ON"); break; case "off": MasterServer.Settings.MSListEnabled = false; MasterServer.Settings.Save(); MasterServer.LogMessage("MSLIST function turned OFF"); break; case "add": if (command.Length > 3) { ushort portNumber = 0; if (ushort.TryParse(command[3], out portNumber)) { } else { MasterServer.LogMessage("Invalid port number specified"); } } else { MasterServer.LogMessage("mslist add <host> <port>"); } break; case "port": if (command.Length > 2) { ushort portNumber = 0; if (ushort.TryParse(command[2], out portNumber)) { if (MasterServer.Settings.MSListInterfaces == null) { MasterServer.Settings.MSListInterfaces = new List <ushort>(); } if (MasterServer.Settings.MSListInterfaces.Contains(portNumber)) { if (MasterServer.Settings.ListenPorts.Contains(portNumber)) { MasterServer.Settings.MSListInterfaces.Remove(portNumber); MasterServer.Settings.Save(); } else { MasterServer.LogMessage("Error adding MSLIST port, the specified port is not bound"); } } else { MasterServer.Settings.MSListInterfaces.Add(portNumber); MasterServer.Settings.Save(); } break; } else { MasterServer.LogMessage("Invalid port number specified"); } } else { MasterServer.LogMessage("mslist port <port>"); List <string> boundPorts = new List <string>(); if (MasterServer.Settings.MSListInterfaces != null) { foreach (ushort port in MasterServer.Settings.MSListInterfaces) { boundPorts.Add(port.ToString()); } } MasterServer.LogMessage("Current MSLIST port bindings: {0}", String.Join(",", boundPorts.ToArray())); } break; } } else { MasterServer.LogMessage("--------------------------------"); MasterServer.LogMessage("MSLIST function is currently {0}", MasterServer.Settings.MSListEnabled ? "ON" : "OFF"); MasterServer.LogMessage("--------------------------------"); MasterServer.LogMessage("mslist on Turn MSLIST on"); MasterServer.LogMessage("mslist off Turn MSLIST off"); MasterServer.LogMessage("mslist port Set MSLIST ports"); MasterServer.LogMessage("mslist add Add MSLIST entries"); MasterServer.LogMessage("mslist remove Remove MSLIST entries"); } break; case "port": if (command.Length > 1 && (command[1].ToLower() == "bind" || command[1].ToLower() == "unbind")) { if (command.Length > 2) { ushort portNumber = 0; if (ushort.TryParse(command[2], out portNumber)) { switch (command[1].ToLower()) { case "bind": if (!MasterServer.Settings.ListenPorts.Contains(portNumber)) { MasterServer.Settings.ListenPorts.Add(portNumber); MasterServer.Settings.Save(); } Bind(portNumber); break; case "unbind": if (MasterServer.Settings.ListenPorts.Contains(portNumber)) { MasterServer.Settings.ListenPorts.Remove(portNumber); MasterServer.Settings.Save(); } if (MasterServer.Settings.MSListInterfaces != null && MasterServer.Settings.MSListInterfaces.Contains(portNumber)) { MasterServer.Settings.MSListInterfaces.Remove(portNumber); MasterServer.Settings.Save(); } UnBind(portNumber); break; } } else { MasterServer.LogMessage("[NET] Invalid port number specified"); } } else { MasterServer.LogMessage("port {0} <port>", command[1]); MasterServer.LogMessage("Current listen ports: {0}", MasterServer.ListenPorts); } } else { MasterServer.LogMessage("port bind Bind a new listen port"); MasterServer.LogMessage("port unbind Unbind a listen port"); MasterServer.LogMessage("Current listen ports: {0}", MasterServer.ListenPorts); } break; case "help": case "?": MasterServer.LogMessage("help Displays this message"); MasterServer.LogMessage("stop Gracefully stops the master server"); MasterServer.LogMessage("motd Set the Message of the Day (MOTD)"); MasterServer.LogMessage("stat Statistics commands"); MasterServer.LogMessage("log Server log commands"); break; } } }
/// <summary> /// Unhandled exceptions, try to gracefully stop the master server /// </summary> /// <param name="sender"></param> /// <param name="e"></param> static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { MasterServer.Log("CRITICAL: {0}", (e.ExceptionObject as Exception).Message); MasterServer.Log("CRITICAL: {0}", (e.ExceptionObject as Exception).StackTrace); MasterServer.Stop(); }
/// <summary> /// Get configured modules from the module manager /// </summary> /// <param name="gameStats">GameStats module will be returned in this variable</param> /// <param name="cdKeyValidator">CD Key Validator module will be returned in this variable</param> /// <returns>True if all modules were loaded correctly</returns> private static bool LoadConfiguredModules(out IGameStatsLog gameStats, out ICDKeyValidator cdKeyValidator) { Console.WriteLine("Initialising log writer module..."); logWriter = ModuleManager.GetModule <ILogWriter>(); ConsoleColor oldColour = Console.ForegroundColor; // Warn if the CD key validator module was not loaded if (logWriter == null) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Configuration error: the specified log writer module was not loaded"); Console.ForegroundColor = oldColour; MasterServer.Log("Configuration error: the specified log writer module was not loaded"); if (!ConsoleUtilities.InConsoleSession()) { WinForms.MessageBox.Show("Configuration error: the specified log writer module was not loaded", "Configuration error", WinForms.MessageBoxButtons.OK); } } Console.WriteLine("Initialising GameStats module..."); gameStats = ModuleManager.GetModule <IGameStatsLog>(); // Warn if the GameStats module was not loaded if (gameStats == null) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Configuration error: the specified gamestats module was not loaded"); Console.ForegroundColor = oldColour; MasterServer.Log("Configuration error: the specified gamestats module was not loaded"); if (!ConsoleUtilities.InConsoleSession()) { WinForms.MessageBox.Show("Configuration error: the specified gamestats module was not loaded", "Configuration error", WinForms.MessageBoxButtons.OK); } } Console.WriteLine("Initialising CD key validator module..."); cdKeyValidator = ModuleManager.GetModule <ICDKeyValidator>(); // Can't continue without a CD key validator module if (cdKeyValidator == null) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Configuration error: the specified CD key validator module was not loaded"); Console.WriteLine("Critical. Master server shutting down"); Console.ForegroundColor = oldColour; if (!ConsoleUtilities.InConsoleSession()) { WinForms.MessageBox.Show("Configuration error: the specified CD key validator module was not loaded", "Critical error", WinForms.MessageBoxButtons.OK); } ReleaseModules(); return(false); } Console.WriteLine(); return(true); }
/// <summary> /// Handle module commands /// </summary> /// <param name="command"></param> private static void Command(string[] command) { if (command.Length > 0 && command[0].Trim() != "") { switch (command[0].ToLower()) { case "module": if (command.Length > 1) { switch (command[1].ToLower()) { case "list": MasterServer.LogMessage("Loaded modules:"); lock (repositoryLock) { foreach (string moduleName in loadedModules.Keys) { MasterServer.LogMessage(" {0}", moduleName); } } break; case "show": MasterServer.LogMessage("Configured assemblies:"); lock (repositoryLock) { if (Modules.Default.LoadAssemblies != null) { foreach (string assemblyName in Modules.Default.LoadAssemblies) { MasterServer.LogMessage(" {0}", assemblyName); } } else { MasterServer.Log("No configured module assemblies"); } } break; case "add": if (command.Length > 2) { MasterServer.LogMessage("Attempting to add {0}.dll", command[2]); if (TryLoadDll(command[2]) && !Modules.Default.LoadAssemblies.Contains(command[2])) { Modules.Default.LoadAssemblies.Add(command[2]); Modules.Default.Save(); MasterServer.LogMessage("Assembly loaded ok, added assembly to configuration"); } } else { MasterServer.LogMessage("module add <assemblyname>"); } break; case "remove": if (command.Length > 2) { if (Modules.Default.LoadAssemblies.Contains(command[2])) { Modules.Default.LoadAssemblies.Remove(command[2]); Modules.Default.Save(); MasterServer.LogMessage("Removed assembly {0} from configuration", command[2]); } } else { MasterServer.LogMessage("module remove <assemblyname>"); } break; } } else { MasterServer.LogMessage("module list List loaded modules"); MasterServer.LogMessage("module show Show configured module .dlls"); MasterServer.LogMessage("module add Load a module .dll"); MasterServer.LogMessage("module remove Remove a module .dll"); } break; case "help": case "?": MasterServer.LogMessage("module Module manager commands"); break; } } }
/// <summary> /// Handle a "link" command /// </summary> /// <param name="command"></param> protected void LinkCommand(string[] command) { if (command.Length < 2) { MasterServer.LogMessage("link list Displays current remote links"); MasterServer.LogMessage("link user Set username password for this server"); MasterServer.LogMessage("link add Add a new master server link"); MasterServer.LogMessage("link remove Remove a master server link"); MasterServer.LogMessage("link test Test a master server link"); return; } switch (command[1].ToLower()) { #if !WCF #warning "WCF functionality disabled - ServerList RPC will not function" #else case "list": MasterServer.LogMessage("Configured links:"); int serverIndex = 0; foreach (RemoteMasterServer remoteMaster in remoteMasters) { MasterServer.LogMessage("{0,2} -> {1}", serverIndex++, remoteMaster.InnerChannel.RemoteAddress.Uri.ToString()); } break; #endif case "user": if (command.Length > 3) { MasterServer.Settings.SyncServiceUsername = command[2]; MasterServer.Settings.SyncServicePassword = command[3]; MasterServer.Settings.Save(); MasterServer.Log("[RPC] Local user/pass updated"); } else { MasterServer.LogMessage("link user <user> <pass>"); } break; case "add": if (command.Length > 7) { ushort port = 0; if (ushort.TryParse(command[3], out port) && port > 0) { if (AddLink(command[2], port, command[4], command[5], command[6], command[7])) { MasterServer.Log("[RPC] Add link succeeded"); } else { MasterServer.LogMessage("[RPC] Add link failed"); } } else { MasterServer.LogMessage("Error: port must be a valid number"); MasterServer.LogMessage("link add <host> <port> <endpoint> <svc> <user> <pass>"); } } else { MasterServer.LogMessage("link add <host> <port> <endpoint> <svc> <user> <pass>"); } break; case "remove": if (command.Length > 2) { int index = 0; if (int.TryParse(command[2], out index) && index >= 0 && index < remoteMasters.Count) { if (RemoveLink(index)) { MasterServer.Log("[RPC] Remove link succeeded"); } else { MasterServer.LogMessage("[RPC] Remove link failed"); } } else { MasterServer.LogMessage("Error: index must be a valid index number"); } } else { MasterServer.LogMessage("link remove <index>"); MasterServer.LogMessage("Hint: use \"link list\" to determine link index"); } break; case "test": if (command.Length > 2) { int index = 0; if (int.TryParse(command[2], out index) && index >= 0 && index < remoteMasters.Count) { TestLink(index); } else { MasterServer.LogMessage("Error: index must be a valid index number"); } } else { MasterServer.LogMessage("link test <index>"); MasterServer.LogMessage("Hint: use \"link list\" to determine link index"); } break; } }
/// <summary> /// Try to start the RPC server /// </summary> public void StartService() { #if !WCF #warning "WCF functionality disabled - ServerList RPC will not function" #else if (MasterServer.Settings.WebServerListenPort == 0) { return; } Uri uri = new Uri(String.Format("http://localhost:{0}/{1}/", MasterServer.Settings.WebServerListenPort, MasterServer.Settings.SyncServiceEndpoint)); if (serviceHost == null) { try { MasterServer.Log("[RPC] Starting service host"); // ServiceBinding for the RPC service BasicHttpBinding ServiceBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly); ServiceBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic; // Host for the RPC service serviceHost = new ServiceHost(this, uri); // Add local endpoint serviceHost.AddServiceEndpoint(typeof(IServerList), ServiceBinding, MasterServer.Settings.SyncServiceName); // Set up custom authentication serviceHost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom; serviceHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new RPCCredentialValidator(); // Enable WSDL fetch functionality over HTTP ServiceMetadataBehavior remoteServiceHostMetaDataBehaviour = new ServiceMetadataBehavior(); remoteServiceHostMetaDataBehaviour.HttpGetEnabled = true; serviceHost.Description.Behaviors.Add(remoteServiceHostMetaDataBehaviour); // Disable the HTML help message (just return the WSDL to browser clients) ServiceDebugBehavior remoteServiceHostDebugBehaviour = serviceHost.Description.Behaviors.Find <ServiceDebugBehavior>(); remoteServiceHostDebugBehaviour.HttpHelpPageEnabled = false; // Open the service host serviceHost.Open(); MasterServer.Log("[RPC] Initialised ok. Listening at {0}/{1}/{2}/", 80, MasterServer.Settings.SyncServiceEndpoint, MasterServer.Settings.SyncServiceName); } catch (AddressAlreadyInUseException) { MasterServer.Log("[RPC] FAULT initialising listener: Port {0} already in use.", MasterServer.Settings.WebServerListenPort); } catch (CommunicationObjectFaultedException ex) { MasterServer.Log("[RPC] FAULT initialising listener: {0}", ex.Message); } catch (TimeoutException ex) { MasterServer.Log("[RPC] TIMEOUT initialising listener: {0}", ex.Message); } catch (InvalidOperationException) { MasterServer.Log("[RPC] Error initialising listener, invalid operation."); } catch (ArgumentNullException) { MasterServer.Log("[RPC] Error initialising listener, configuration error."); } catch (ArgumentOutOfRangeException) { MasterServer.Log("[RPC] Error initialising listener, configuration error."); } } #endif }