using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using FlaxEngine; using FlaxEngine.Networking; using Console = Game.Console; using Object = FlaxEngine.Object; namespace Game; public static partial class NetworkManager { private static List ConnectedClients; private static List NetworkedTypes; public static INetworkDriver ServerNetworkDriver { get; set; } //public static WorldStateManager serverWorldStateManager = null; public static bool StartServer(bool listenServer = true) { Time.Synchronize(); Cleanup(); ConnectedClients = new List(MaximumClients); INetworkDriver driver = Object.New(); //NetworkLagDriver driver = Object.New(); if (driver is NetworkLagDriver networkLagDriver) networkLagDriver.Lag = 50.0f;//200f; ServerNetworkDriver = driver; server = NetworkPeer.CreatePeer(new NetworkConfig { NetworkDriver = (Object)driver, ConnectionsLimit = MaximumClients, MessagePoolSize = 2048, MessageSize = MTU, Address = "any", Port = ServerPort }); if (!server.Listen()) { Console.PrintError("Failed to start the server."); return false; } Scripting.Update += OnServerNetworkUpdate; Scripting.Exit += Cleanup; Level.ActorSpawned += OnServerActorSpawned; NetworkedTypes = new List(); #if false var assemblies = Utils.GetAssemblies(); foreach (Assembly assembly in assemblies) { // Skip common assemblies string assemblyName = assembly.GetName().Name; if (assemblyName == "System" || assemblyName.StartsWith("System.") || assemblyName.StartsWith("Mono.") || assemblyName == "mscorlib" || assemblyName == "Newtonsoft.Json" || assemblyName == "Snippets" || assemblyName == "netstandard" || assemblyName == "Anonymously Hosted DynamicMethods Assembly" || assemblyName.StartsWith("FlaxEngine.") || assemblyName.StartsWith("JetBrains.") || assemblyName.StartsWith("Microsoft.") || assemblyName.StartsWith("nunit.")) continue; foreach (Type type in assembly.GetTypes()) if (type.GetCustomAttributes().Any(x => x is NetworkedAttribute)) NetworkedTypes.Add(type); } foreach (Type type in NetworkedTypes) Console.Print("tracking networked type: " + type.Name); #endif //serverWorldStateManager = new WorldStateManager(isServer: true); //WorldStateManager.Init(); World.InitServer(); if (listenServer) return ConnectServer(listenServer: true); return true; } public static NetworkMessage ServerBeginSendMessage() { NetworkMessage message = server.BeginSendMessage(); message.WriteByte((byte)NetworkMessageType.Message); return message; } public static void ServerEndSendMessage(ref NetworkMessage message, NetworkConnection connection, NetworkChannelType channelType = NetworkChannelType.Reliable) { server.EndSendMessage(channelType, message, connection); } public static void ServerEndSendMessage(ref NetworkMessage message, NetworkConnection[] connections, NetworkChannelType channelType = NetworkChannelType.Reliable) { server.EndSendMessage(channelType, message, connections); } public static NetworkMessage ClientBeginSendMessage() { NetworkMessage message = client.BeginSendMessage(); message.WriteByte((byte)NetworkMessageType.Message); return message; } public static void ClientEndSendMessage(ref NetworkMessage message, NetworkChannelType channelType = NetworkChannelType.Reliable) { client.EndSendMessage(channelType, message); } private static void OnServerNetworkUpdate() { using var _ = Utilities.ProfileScope("NetworkManager_OnServerNetworkUpdate"); while (server.PopEvent(out NetworkEvent networkEvent)) OnServerReadMessage(ref networkEvent); } private static void OnServerReadMessage(ref NetworkEvent networkEvent) { using var _ = Utilities.ProfileScope("NetworkManager_OnServerReadMessage"); switch (networkEvent.EventType) { case NetworkEventType.Connected: { Console.Print($"Client({networkEvent.Sender.ConnectionId}) is trying to connect"); try { //IsServer = true; bool handled = false; foreach (var func in OnServerConnectingDelegates) { handled = func(networkEvent.Sender); if (handled) break; } if (handled) { ConnectedClients.Add(networkEvent.Sender); Console.Print( $"Client({networkEvent.Sender.ConnectionId}) connected. Total clients: {ConnectedClients.Count}"); foreach (var func in OnServerConnectedDelegates) func(networkEvent.Sender); } else Console.Print($"Client({networkEvent.Sender.ConnectionId}) connection refused"); } finally { //IsServer = false; } break; } case NetworkEventType.Disconnected: case NetworkEventType.Timeout: { Console.Print($"Client({networkEvent.Sender.ConnectionId}) disconnected!"); ConnectedClients.Remove(networkEvent.Sender); Console.Print("Connected clients: " + ConnectedClients.Count); break; } case NetworkEventType.Message: { try { //IsServer = true; OnNetworkMessage(ref networkEvent, CollectionsMarshal.AsSpan(OnServerMessageDelegates)); if (networkEvent.Message.Position > 0 && networkEvent.Message.Position < networkEvent.Message.Length) { string err = $"Network message was not fully read: {networkEvent.Message.Position} / {networkEvent.Message.Length}."; networkEvent.Message.Position = 0; byte[] messageBytes = new byte[networkEvent.Message.Length]; unsafe { fixed (byte* messageBytePtr = &messageBytes[0]) networkEvent.Message.ReadBytes(messageBytePtr, (int)networkEvent.Message.Length); } string messageBytesStr = string.Join(", ", messageBytes.Select(x => "0x" + ((int)x).ToString("X2"))); Console.PrintError(err + $"Message dump: {messageBytesStr}"); } } finally { //IsServer = false; server.RecycleMessage(networkEvent.Message); } break; } default: throw new ArgumentOutOfRangeException(); } } private static void OnServerActorSpawned(Actor actor) { //Console.Print($"actor spawned: {actor.Name} ({actor.TypeName})"); } }