Beispiel #1
0
 /// <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);
     }
 }
        /// <summary>
        /// Tests connections between a server and clients
        /// </summary>
        /// <param name="port">Network port</param>
        /// <param name="onCreateConnection">On create connection</param>
        public void TestConnections(ushort port, CreateClientConnectionDelegate onCreateConnection)
        {
            if (port == 0)
            {
                throw new ArgumentException("Port can't be zero.", nameof(port));
            }
            if (onCreateConnection == null)
            {
                throw new ArgumentNullException(nameof(onCreateConnection));
            }
            UnitTestsConfigurationData unit_tests_configuration = null;

            if (File.Exists(unitTestsConfigurationPath))
            {
                using FileStream file_stream          = File.OpenRead(unitTestsConfigurationPath);
                using StreamReader file_stream_reader = new StreamReader(file_stream);
                unit_tests_configuration = JsonConvert.DeserializeObject <UnitTestsConfigurationData>(file_stream_reader.ReadToEnd());
            }
            unit_tests_configuration ??= new UnitTestsConfigurationData();
            Assert.IsTrue(unit_tests_configuration.IsValid);
            bool is_running    = true;
            uint perform_ticks = unit_tests_configuration.PerformTicks;

            using IServerSynchronizer server = Servers.Create(port, Defaults.timeoutTime);
            Assert.IsNotNull(server);
            server.AddGameResource <ExampleGameResource>();
            server.OnPeerConnectionAttempted += (peer) => Console.WriteLine($"[SERVER] Peer GUID \"{ peer.GUID }\" with secret \"{ peer.Secret }\" attempted to connect.");
            server.OnPeerConnected           += (peer) => Console.WriteLine($"[SERVER] Peer GUID \"{ peer.GUID }\" with secret \"{ peer.Secret }\" is connect.");
            server.OnPeerDisconnected        += (peer) => Console.WriteLine($"[SERVER] Peer GUID \"{ peer.GUID }\" with secret \"{ peer.Secret }\" has been disconnect.");
            server.OnUnknownMessageReceived  += (message, json) =>
            {
                Console.Error.WriteLine($"[SERVER] Message type: \"{ message.MessageType }\"");
                Console.Error.WriteLine("[SERVER] JSON:");
                Console.Error.WriteLine();
                Console.Error.WriteLine(json);
                Assert.Fail("Server has received an unknown message.", message, json);
            };
            server.OnPeerMessageReceived += (peer, message) => Console.WriteLine($"[SERVER] Peer GUID \"{ peer.GUID }\" sent a message of length \"{ message.Length }\".");
            IClientSynchronizer[] clients = new IClientSynchronizer[Defaults.maximalUserCount + 1U];
            IUser[]  users   = new IUser[clients.Length];
            ILobby[] lobbies = new ILobby[clients.Length];
            for (int index = 0; index < clients.Length; index++)
            {
                int current_index          = index;
                IClientSynchronizer client = onCreateConnection(server);
                Assert.IsNotNull(client);
                clients[index] = client;
                client.OnPeerConnectionAttempted += (peer) => Console.WriteLine($"[CLIENT] Peer GUID \"{ peer.GUID }\" with secret \"{ peer.Secret }\" attempted to connect.");
                client.OnPeerConnected           += (peer) => Console.WriteLine($"[CLIENT] Peer GUID \"{ peer.GUID }\" with secret \"{ peer.Secret }\" is connect.");
                client.OnPeerDisconnected        += (peer) => Console.WriteLine($"[CLIENT] Peer GUID \"{ peer.GUID }\" with secret \"{ peer.Secret }\" has been disconnect.");
                client.OnUnknownMessageReceived  += (message, json) =>
                {
                    Console.Error.WriteLine($"Message type: \"{ message.MessageType }\"");
                    Console.Error.WriteLine("JSON:");
                    Console.Error.WriteLine();
                    Console.Error.WriteLine(json);
                    Assert.Fail($"Client index { current_index } has received an unknown message.", message, json, current_index);
                };
                client.OnPeerMessageReceived          += (peer, message) => Console.WriteLine($"[CLIENT] Peer GUID \"{ peer.GUID }\" sent a message of length \"{ message.Length }\".");
                client.OnLobbyJoinAcknowledged        += (lobby) => lobbies[current_index] = lobby;
                client.OnAuthentificationAcknowledged += (user) =>
                {
                    Assert.IsNotNull(user);
                    users[current_index] = user;
                    if (current_index == 0)
                    {
                        client.OnLobbyJoinAcknowledged += (lobby) =>
                        {
                            for (int client_index = 1; client_index < clients.Length; client_index++)
                            {
                                clients[client_index].JoinLobby(lobby.LobbyCode, $"Client_{ client_index }");
                            }
                        };
                        client.CreateAndJoinLobby($"Client_{ current_index }", "Test lobby", typeof(ExampleGameMode).FullName);
                    }
                };
                client.OnErrorMessageReceived += (errorType, message) => Console.Error.WriteLine($"Error at client index { current_index }: [{ errorType }] { message }");
            }
            ;
            TimeSpan  tick_time_span = TimeSpan.FromSeconds(1.0 / unit_tests_configuration.TickRate);
            Stopwatch stopwatch      = new Stopwatch();

            stopwatch.Start();
            while (is_running && (perform_ticks > 0U))
            {
                stopwatch.Restart();
                server.ProcessEvents();
                foreach (IClientSynchronizer client in clients)
                {
                    client.ProcessEvents();
                }
                stopwatch.Stop();
                TimeSpan elapsed_time = stopwatch.Elapsed;
                if (elapsed_time > tick_time_span)
                {
                    Console.Error.WriteLine($"Can't keep up server and clients. Please lower the tick rate. Current tick rate: { unit_tests_configuration.TickRate }");
                    is_running = false;
                }
                else if (elapsed_time < tick_time_span)
                {
                    Thread.Sleep(tick_time_span - elapsed_time);
                }
                --perform_ticks;
            }
            server.Dispose();
            foreach (IClientSynchronizer client in clients)
            {
                client.Dispose();
            }
            Assert.AreEqual(0U, perform_ticks);
            uint no_lobby_client_count = 0U;

            for (int index = 0; index < clients.Length; index++)
            {
                Assert.IsNotNull(users[index], $"User at client index { index } was not authentificated.", index);
                if (lobbies[index] == null)
                {
                    ++no_lobby_client_count;
                }
            }
            if (no_lobby_client_count != 1U)
            {
                if (no_lobby_client_count == 0U)
                {
                    Assert.Fail("Every client is in a lobby, which is wrong.");
                }
                else
                {
                    Assert.Fail($"{ no_lobby_client_count } clients are not in a lobby.");
                }
            }
            Assert.Pass();
        }