public Node() { // Load the node configuration. XmlDocument config = new XmlDocument(); config.Load(Environment.CurrentDirectory + "\\servers.xml"); XmlNodeList nodes = config.SelectNodes("//server"); foreach (XmlNode serverConfigNode in nodes) { string processID = serverConfigNode.SelectSingleNode("./process_id").InnerText; XmlNode processConfigNode = config.SelectSingleNode( String.Format("//process[@process_id='{0}']", processID)); ProcessConfigurationData processConfig = new ProcessConfigurationData(processConfigNode, serverConfigNode); ServerConfigurationData serverConfig = new ServerConfigurationData(serverConfigNode); //TODO: Create custom exception classes for different exception reasons, and catch them seperately. try { ServerManager.RegisterServer(processConfig, serverConfig); } catch (Exception ex) { Trace.WriteLine(ex.Message); } } }
//TODO: Create a "ConfigurationManager" so that process and server info can be queried and provided seperately. //This will remove the need to pass references to Process and Server configs to each object. /// <summary> /// Creates a server based on the supplied configuration data, and adds /// it to the Server Manager. /// </summary> public static void RegisterServer(ProcessConfigurationData processConfig, ServerConfigurationData serverConfig) { //TODO: Decide how best to determine which type of server to create. //For now, we will use the if/then approach. if (serverConfig.ServerType == ServerType.Game) { if (serverConfig.SubType == "HaloCombatEvolved") { try { Server s = new GenericServer(processConfig, serverConfig); servers.Add(s); } catch (Exception ex) { throw new Exception("Unable to create server object: " + ex.Message); } } else { throw new Exception("Unable to create server object: " + serverConfig.SubType + " is not a supported game server type."); } } }
/// <summary> /// Creates a new Server object based on the supplied configuraton data. /// </summary> /// <param name="serverConfig">A valid server configuration.</param> public GenericServer(ProcessConfigurationData processConfig, ServerConfigurationData serverConfig) : base(processConfig, serverConfig) { ; }
/// <summary> /// Main entry point /// </summary> /// <param name="args">Command line arguments</param> private static void Main(string[] args) { try { commandLineCommands.AddCommand("-help", "Show help topics", "This command shows help topics.", HelpCommandLineCommandExecutedEvent, CommandArgument.Optional("helpTopic")); commandLineCommands.AddCommand("-port", "Set port", "This command sets the port the server should be available from.", PortCommandLineCommandExecutedEvent, "port"); commandLineCommands.AddCommand("-tick-rate", "Set tick rate", "This command sets the tick rate the server should run on.", TickRateCommandLineCommandExecutedEvent, "tickRate"); commandLineCommands.AddAlias("h", "-help"); commandLineCommands.AddAlias("p", "-port"); commandLineCommands.AddAlias("t", "-tick-rate"); consoleCommands.AddCommand("help", "Show help topic", "This command shows help topics.", HelpConsoleCommmandExecutedEvent, CommandArgument.Optional("helpTopic")); consoleCommands.AddCommand("connectors", "List connectors", "This command lists all connectors.", ConnectorsConsoleCommmandExecutedEvent); consoleCommands.AddCommand("gameresources", "List game resources", "This command lists all game resources.", GameResourcesConsoleCommmandExecutedEvent); consoleCommands.AddCommand("lobbies", "List lobbies", "This command lists all lobbies.", LobbiesConsoleCommmandExecutedEvent); consoleCommands.AddCommand("users", "List users", "This command lists all users.", UsersConsoleCommmandExecutedEvent, CommandArgument.Optional("lobbyGUID")); consoleCommands.AddCommand("kick", "Kick user", "This command kicks the specified user.", KickConsoleCommmandExecutedEvent, "userGUID"); consoleCommands.AddCommand("ban", "Ban user", "This command bans the specified user.", BanConsoleCommmandExecutedEvent, "userGUID"); consoleCommands.AddCommand("bansecret", "Ban IP address", "This command bans the specified peer secret.", BanSecretConsoleCommmandExecutedEvent, "secret"); consoleCommands.AddCommand("unbansecret", "Unban IP address", "This command unbans the specified peer secret.", UnbanSecretConsoleCommmandExecutedEvent, "secret"); consoleCommands.AddCommand("reloadbans", $"Reload bans from \"{ bansJSONFilePath }\"", $"This command reloads the bans from \"{ bansJSONFilePath }\".", ReloadBansConsoleCommmandExecutedEvent); consoleCommands.AddCommand("closelobby", "Close lobby", "This command closes the specified lobby.", CloseLobbyConsoleCommmandExecutedEvent, "lobbyCode"); consoleCommands.AddCommand("exit", "Exit process", "This command exists the current process.", ExitConsoleCommmandExecutedEvent); consoleCommands.AddAlias("commands", "help"); consoleCommands.AddAlias("cmds", "commands"); consoleCommands.AddAlias("cmd", "cmds"); consoleCommands.AddAlias("h", "help"); consoleCommands.AddAlias("?", "help"); consoleCommands.AddAlias("c", "connectors"); consoleCommands.AddAlias("resources", "gameresources"); consoleCommands.AddAlias("gr", "gameresources"); consoleCommands.AddAlias("r", "resources"); consoleCommands.AddAlias("l", "lobbies"); consoleCommands.AddAlias("u", "users"); consoleCommands.AddAlias("k", "kick"); consoleCommands.AddAlias("b", "ban"); consoleCommands.AddAlias("bsecret", "bansecret"); consoleCommands.AddAlias("ubsecret", "unbansecret"); consoleCommands.AddAlias("cl", "closelobby"); consoleCommands.AddAlias("quit", "exit"); consoleCommands.AddAlias("q", "quit"); if (File.Exists(serverJSONFilePath)) { try { using FileStream file_stream = File.OpenRead(serverJSONFilePath); using StreamReader stream_reader = new StreamReader(file_stream); serverConfigurationData = JsonConvert.DeserializeObject <ServerConfigurationData>(stream_reader.ReadToEnd()); } catch (Exception e) { Console.Error.WriteLine(e); } } else { serverConfigurationData = new ServerConfigurationData(); try { using FileStream file_stream = File.Open(serverJSONFilePath, FileMode.Create, FileAccess.Write); using StreamWriter stream_writer = new StreamWriter(file_stream); stream_writer.Write(JsonConvert.SerializeObject(serverConfigurationData)); } catch (Exception e) { Console.Error.WriteLine(e); } } if (serverConfigurationData == null) { serverConfigurationData = new ServerConfigurationData(); } if (serverConfigurationData.NetworkPort == 0) { Console.Error.WriteLine($"Port is set to \"0\". Port will be set to default port \"{ ServerConfigurationData.defaultNetworkPort }\"."); serverConfigurationData.NetworkPort = ServerConfigurationData.defaultNetworkPort; } if ((serverConfigurationData.TickRate <= 0) || (serverConfigurationData.TickRate > 1000)) { Console.Error.WriteLine($"Tick rate is set to \"{ serverConfigurationData.TickRate }\". Tick rate will be set to default tick rate \"{ ServerConfigurationData.defaultTickRate }\"."); serverConfigurationData.TickRate = ServerConfigurationData.defaultTickRate; } if (string.IsNullOrWhiteSpace(serverConfigurationData.OutputLogPath)) { Console.Error.WriteLine($"Output log path is not defined. Output log path will be set to default output log path \"{ ServerConfigurationData.defaultOutputLogPath }\"."); serverConfigurationData.OutputLogPath = ServerConfigurationData.defaultOutputLogPath; } if (string.IsNullOrWhiteSpace(serverConfigurationData.ErrorLogPath)) { Console.Error.WriteLine($"Error log path is not defined. Error log path will be set to default error log path \"{ ServerConfigurationData.defaultErrorLogPath }\"."); serverConfigurationData.ErrorLogPath = ServerConfigurationData.defaultErrorLogPath; } StringBuilder command_line_arguments_string_builder = new StringBuilder(); bool first = true; foreach (string arg in args) { if (first) { first = false; } else { command_line_arguments_string_builder.Append(' '); } command_line_arguments_string_builder.Append(arg); } if (commandLineCommands.ParseCommands(command_line_arguments_string_builder.ToString(), "-")) { outputLogger = Logger.Open(serverConfigurationData.OutputLogPath); if (serverConfigurationData.OutputLogPath == serverConfigurationData.ErrorLogPath) { errorLogger = outputLogger; } else { errorLogger = Logger.Open(serverConfigurationData.ErrorLogPath); } if (keepServerRunning) { using (server = Servers.Create(serverConfigurationData.NetworkPort, serverConfigurationData.TimeoutTime)) { if (server == null) { WriteErrorLogLine($"Failed to create server at port { serverConfigurationData.NetworkPort }."); } else { TimeSpan tick_time_span = TimeSpan.FromMilliseconds(1000 / serverConfigurationData.TickRate); Stopwatch stopwatch = new Stopwatch(); uint lag_count = 0U; ConsoleKeyInfo console_key_information; StringBuilder input_string_builder = new StringBuilder(); foreach (IConnector connector in server.Connectors) { connector.OnPeerConnectionAttempted += PeerConnectionAttemptedEvent; connector.OnPeerConnected += PeerConnectedEvent; connector.OnPeerDisconnected += PeerDisconnectedEvent; } server.Bans.AppendFromFile(bansJSONFilePath); try { foreach (string game_resources_file_path in Directory.GetFiles("./GameResources/", "*.dll")) { WriteOutputLogLine($"Loading game resources file \"{ game_resources_file_path }\"..."); try { Assembly assembly = Assembly.LoadFrom(game_resources_file_path); if (assembly == null) { WriteErrorLogLine($"Failed to load game resource assembly from \"{ game_resources_file_path }\""); } else { foreach (Type type in assembly.GetTypes()) { if (type.IsClass && !type.IsAbstract && typeof(IGameResource).IsAssignableFrom(type)) { bool has_default_constructor = true; foreach (ConstructorInfo constructor in type.GetConstructors(BindingFlags.Public)) { has_default_constructor = false; if (constructor.GetParameters().Length <= 0) { has_default_constructor = true; break; } } if (has_default_constructor) { if (server.AddGameResource(type)) { WriteOutputLogLine($"Loaded game resource \"{ type.FullName }\" from \"{ game_resources_file_path }\"."); } else { WriteErrorLogLine($"Failed to load game resource \"{ type.FullName }\" from \"{ game_resources_file_path }\"."); } } } } } } catch (Exception e) { WriteErrorLogLine(e); } } } catch (Exception e) { WriteErrorLogLine(e); } WriteOutputLogLine($"Starting server at port { serverConfigurationData.NetworkPort }..."); while (keepServerRunning) { stopwatch.Restart(); server.ProcessEvents(); if (!Console.IsInputRedirected) { while (Console.KeyAvailable) { console_key_information = Console.ReadKey(); if (console_key_information.KeyChar != '\0') { if (console_key_information.Key == ConsoleKey.Enter) { string input = input_string_builder.ToString(); WriteOutputLogLine(input); if (!consoleCommands.ParseCommand(input)) { WriteErrorLogLine("Failed to execute command."); } input_string_builder.Clear(); } else { input_string_builder.Append(console_key_information.KeyChar); } } } } stopwatch.Stop(); if (tick_time_span > stopwatch.Elapsed) { lag_count = (lag_count > 0U) ? (lag_count - 1U) : 0U; Thread.Sleep(tick_time_span - stopwatch.Elapsed); } else { ++lag_count; if (lag_count >= serverConfigurationData.TickRate) { lag_count = 0U; WriteErrorLogLine($"Server can't keep up. Try lowering the tick rate in \"{ serverJSONFilePath }\" or run the server with a lower tick rate."); } } } server.Bans.WriteToFile(bansJSONFilePath); WriteOutputLogLine("Server has shut down."); } } } } else { PrintHelpTopic(commandLineCommands, "-"); } command_line_arguments_string_builder.Clear(); if (outputLogger != null) { outputLogger.Dispose(); if (outputLogger == errorLogger) { outputLogger = null; errorLogger = null; } } if (errorLogger != null) { errorLogger.Dispose(); errorLogger = null; } } catch (Exception e) { Console.Error.WriteLine(e); } }