/// <summary> /// Selects a server for management, the control interface calls this function to nominate a server for /// further commands. Commands executed after this point apply to the selected server. We tag the server /// as selected rather than keeping a reference to ensure that servers are automatically deselected when /// they go out of scope, and also to allow possible multi-selection of servers in the future. /// </summary> /// <param name="serverId">Server ID comprising the server IP and listen port</param> public void SelectServer(string serverId) { Server selectedServer = null; if (!serverId.Contains(":")) { serverId = serverId + ":7777"; } lock (serverListLock) { foreach (Server server in servers) { if (server.DisplayAddress == serverId) { selectedServer = server; break; } } if (selectedServer != null) { foreach (Server server in servers) { server.Selected = (server == selectedServer); } MasterServer.LogMessage("Selected server [{0}]", serverId); } else { MasterServer.LogMessage("Server [{0}] was not found", serverId); } } }
/// <summary> /// Attempt to load an assembly .dll /// </summary> /// <param name="moduleAssemblyName"></param> private static bool TryLoadDll(string moduleAssemblyName) { string assemblyPath = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.FullName; string moduleAssemblyPath = Path.Combine(assemblyPath, moduleAssemblyName + ".dll"); if (File.Exists(moduleAssemblyPath)) { try { Assembly moduleAssembly = Assembly.LoadFile(moduleAssemblyPath); foreach (Type moduleType in moduleAssembly.GetTypes()) { TryLoadType(moduleType, moduleAssembly); } } catch (Exception ex) { Debug.WriteLine(String.Format("Exception loading assembly {0}: {1} ", moduleAssemblyName, ex.Message)); MasterServer.LogMessage("Error loading assembly {0}.dll", moduleAssemblyName); MasterServer.LogMessage(ex.Message); return(false); } } else { Debug.WriteLine(String.Format("Assembly {0}.dll was not found.", moduleAssemblyName)); MasterServer.LogMessage("Assembly {0}.dll was not found", moduleAssemblyName); return(false); } return(true); }
/// <summary> /// Challenge a connected client /// </summary> /// <param name="clientAddress"></param> public void ChallengeClient(string clientAddress) { if (Connection == null) { return; } if (clientAddress == "*") { foreach (Player player in Players) { Connection.ChallengeClient(player.Address); } } else { if (GetClientPlayer(clientAddress) != null) { Connection.ChallengeClient(clientAddress); } else { MasterServer.LogMessage("Client {0} not found", clientAddress); } } }
/// <summary> /// Handle a "client" command /// </summary> /// <param name="command"></param> protected void ClientCommand(string[] command, Server server) { if (command.Length < 2) { MasterServer.LogMessage("client list Displays clients on the selected server"); MasterServer.LogMessage("client challenge Challenges the specified client"); MasterServer.LogMessage("client kick Disconnects a client from the server"); return; } switch (command[1].ToLower()) { case "list": if (server != null) { server.ListClients(); return; } break; case "challenge": if (command.Length > 2) { if (server != null) { server.ChallengeClient(command[2]); return; } } else { MasterServer.LogMessage("client challenge <address|*>"); MasterServer.LogMessage("Hint: use \"client list\" to determine client address"); } break; case "kick": if (command.Length > 2) { if (server != null) { server.DisconnectClient(command[2]); return; } } else { MasterServer.LogMessage("client kick <address>"); MasterServer.LogMessage("Hint: use \"client list\" to determine client address"); } break; } }
/// <summary> /// List clients connected to this server to the console /// </summary> public void ListClients() { if (Players != null && Players.Count > 0) { foreach (Player player in Players) { MasterServer.LogMessage("{0,-22}{1,-20}{2,5}", player.Address, ColourCodeParser.StripColourCodes(player.Name), player.Ping); } } else { MasterServer.LogMessage("No clients connected"); } }
/// <summary> /// Handle a server selection command /// </summary> /// <param name="command"></param> protected void SelectCommand(string[] command) { if (command.Length > 1) { SelectServer(command[1]); } else { if (servers.Count == 1) { servers[0].Selected = true; MasterServer.LogMessage("Selected server [{0}]", servers[0]); } } }
/// <summary> /// Handle a "get" command /// </summary> /// <param name="command"></param> protected void GetCommand(string[] command, Server server) { if (command.Length > 2) { if (server != null) { server.CheckOption(command[1], command[2]); return; } } else { MasterServer.LogMessage("get <package.section> <variable>"); } }
/// <summary> /// Get the currently selected server /// </summary> /// <returns></returns> private Server GetSelectedServer() { lock (serverListLock) { foreach (Server s in servers) { if (s.Selected) { return(s); } } } MasterServer.LogMessage("No server selected"); return(null); }
/// <summary> /// Attempts to load a module type if it is a valid master server module /// </summary> /// <param name="moduleType"></param> /// <param name="assembly"></param> private static void TryLoadType(Type moduleType, Assembly assembly) { try { if (moduleType.IsClass && !moduleType.IsAbstract && moduleType.GetInterface(typeof(IMasterServerModule).Name, true) != null && !loadedModules.ContainsKey(moduleType.Name)) { Debug.WriteLine(String.Format("Loading module {0} from {1}", moduleType.Name, assembly.FullName)); loadedModules.Add(moduleType.Name, (IMasterServerModule)Activator.CreateInstance(moduleType)); } } catch (Exception ex) { Debug.WriteLine(String.Format("Exception loading module {0} from {1}: {2}", moduleType.Name, assembly.FullName, ex.Message)); MasterServer.LogMessage("Error loading type {0} from {1}", moduleType.Name, assembly.FullName); MasterServer.LogMessage(ex.Message); } }
/// <summary> /// Disconnect a player /// </summary> /// <param name="clientAddress"></param> public void DisconnectClient(string clientAddress) { if (Connection == null) { return; } Player player = GetClientPlayer(clientAddress); if (player != null) { Connection.DisconnectClient(player.Address); Players.Remove(player); } else { MasterServer.LogMessage("Client {0} not found", clientAddress); } }
/// <summary> /// Restart the master server instance /// </summary> public static void Restart() { if (instance != null) { if (service != null) { MasterServer.LogMessage("Master server cannot be restarted in service mode, restart the service instead"); } else if (instance.statusDisplay is ConsoleStatusDisplay && instance.commandInterface is ConsoleCommandLine) { // Stop the server instance.Shutdown(); instance = null; // Release log writer, game stats, and validation modules ReleaseModules(); // Clean up garbage Console.WriteLine("Purging garbage..."); GC.Collect(); // Wait a couple of seconds to let any processes which haven't terminated yet finish closing Console.WriteLine("Restarting master server..."); Thread.Sleep(2000); Console.Clear(); // Get new status display and input modules IStatusDisplay statusDisplay = ModuleManager.GetModule <IStatusDisplay>(typeof(ConsoleStatusDisplay)); ICommandInterface commandInterface = ModuleManager.GetModule <ICommandInterface>(typeof(ConsoleCommandLine)); // Start a new master server Start(statusDisplay, commandInterface); } } if (shutdownThread != null) { shutdownThread = null; } }
/// <summary> /// Handle a "get" command /// </summary> /// <param name="command"></param> protected void SetCommand(string[] command, Server server) { if (command.Length > 3) { if (server != null) { string value = command[3]; for (int i = 4; i < command.Length; i++) { value += " " + command[i]; } server.SetOption(command[1], command[2], value); return; } } else { MasterServer.LogMessage("set <package.section> <variable> <value>"); } }
/// <summary> /// List server info to the console /// </summary> public void Print() { MasterServer.LogMessage("Server info:"); MasterServer.LogMessage("------------------------------------------------------------"); MasterServer.LogMessage(" Name: {0}", Name); MasterServer.LogMessage(" Address: {0}", Address); MasterServer.LogMessage(" GPort/QPort/GSQPort: {0}/{1}/{2}", Port, QueryPort, GamespyQueryPort > 0 ? GamespyQueryPort.ToString() : "-"); MasterServer.LogMessage(" Active: {0}", Active); MasterServer.LogMessage(" Type: {0}", Local ? "Local" : "RPC"); MasterServer.LogMessage(" Version: {0}", Version); MasterServer.LogMessage(" CDKey: {0}", CDKey); MasterServer.LogMessage(" ServerBehindNAT: {0}", ServerBehindNAT); MasterServer.LogMessage(" UplinkToGamespy: {0}", UplinkToGamespy); MasterServer.LogMessage(" EnableStatLogging: {0}", EnableStatLogging); MasterServer.LogMessage(" OperatingSystem: {0}", OperatingSystem); MasterServer.LogMessage(" Map (GameType): {0} ({1})", Map, GameType); MasterServer.LogMessage(" Players (Max): {0} ({1})", CurrentPlayers, MaxPlayers); foreach (KeyValuePair <string, string> property in Properties) { MasterServer.LogMessage(" {0,19}: {1}", property.Key, property.Value); } }
/// <summary> /// Handle a console command /// </summary> /// <param name="command"></param> public void Command(string[] command) { if (command.Length > 0) { switch (command[0].ToLower()) { case "client": ClientCommand(command, GetSelectedServer()); break; case "select": SelectCommand(command); break; case "sel": SelectCommand(command); break; case "s": SelectCommand(command); break; case "info": InfoCommand(command); break; case "poke": PokeCommand(command); break; case "get": GetCommand(command, GetSelectedServer()); break; case "set": SetCommand(command, GetSelectedServer()); break; #if WCF case "link": LinkCommand(command); break; #endif case "help": case "?": MasterServer.LogMessage("link Administer remote server links"); MasterServer.LogMessage("select Select a server to command"); MasterServer.LogMessage("client Server client commands"); MasterServer.LogMessage("info Show information about the selected server"); MasterServer.LogMessage("get Get a remote INI file setting value"); MasterServer.LogMessage("set Set a remote file setting value"); break; } } }
/// <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> /// 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> /// 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; } } }