using System; using System.Collections.Generic; using System.Linq; using FlaxEditor; using FlaxEngine; using FlaxEngine.Assertions; using FlaxEngine.Networking; using Console = Game.Console; namespace Game; [AttributeUsage(AttributeTargets.Class)] public class NetworkPredictedAttribute : Attribute { } // TODO: insert code to update variables with this attribute? // rename to NetworkReplicatedAttribute? [AttributeUsage(AttributeTargets.Class)] public class NetworkedAttribute : Attribute { } // NetworkMulticastAttribute: calls methods marked with this in all clients public enum NetworkMessageType : byte { Handshake = 1, Message } public static partial class NetworkManager { public delegate bool OnMessageDecl(ref NetworkEvent networkEvent); public delegate bool OnClientConnectingDecl(NetworkConnection sender); public delegate void OnClientConnectedDecl(NetworkConnection sender); private static bool initialized; public static NetworkPeer server; public static NetworkPeer client; private static readonly ushort ServerPort = 59183; private static string ServerAddress; private static readonly ushort MTU = 1500; private static readonly ushort MaximumClients = 32; private static List OnClientMessageDelegates = new(3); private static List OnClientConnectingDelegates = new(3); private static List OnClientConnectedDelegates = new(3); private static List OnServerMessageDelegates = new(3); private static List OnServerConnectingDelegates = new(3); private static List OnServerConnectedDelegates = new(3); public static void RegisterClientCallbacks(OnMessageDecl onMessage, OnClientConnectingDecl onClientConnecting, OnClientConnectedDecl onClientConnected) { Assert.IsTrue(!OnClientMessageDelegates.Contains(onMessage)); OnClientMessageDelegates.Add(onMessage); if (onClientConnecting != null) { Assert.IsTrue(!OnClientConnectingDelegates.Contains(onClientConnecting)); OnClientConnectingDelegates.Add(onClientConnecting); } if (onClientConnected != null) { Assert.IsTrue(!OnClientConnectedDelegates.Contains(onClientConnected)); OnClientConnectedDelegates.Add(onClientConnected); } } public static void UnregisterClientCallbacks(OnMessageDecl onMessage, OnClientConnectingDecl onClientConnecting, OnClientConnectedDecl onClientConnected) { Assert.IsTrue(OnClientMessageDelegates.Contains(onMessage)); OnClientMessageDelegates.Remove(onMessage); if (onClientConnecting != null) { Assert.IsTrue(OnClientConnectingDelegates.Contains(onClientConnecting)); OnClientConnectingDelegates.Remove(onClientConnecting); } if (onClientConnected != null) { Assert.IsTrue(OnClientConnectedDelegates.Contains(onClientConnected)); OnClientConnectedDelegates.Remove(onClientConnected); } } public static void RegisterServerCallbacks(OnMessageDecl onMessage, OnClientConnectingDecl onClientConnecting, OnClientConnectedDecl onClientConnected) { Assert.IsTrue(!OnServerMessageDelegates.Contains(onMessage)); OnServerMessageDelegates.Add(onMessage); if (onClientConnecting != null) { Assert.IsTrue(!OnServerConnectingDelegates.Contains(onClientConnecting)); OnServerConnectingDelegates.Add(onClientConnecting); } if (onClientConnected != null) { Assert.IsTrue(!OnServerConnectedDelegates.Contains(onClientConnected)); OnServerConnectedDelegates.Add(onClientConnected); } } public static void UnregisterServerCallbacks(OnMessageDecl onMessage, OnClientConnectingDecl onClientConnecting, OnClientConnectedDecl onClientConnected) { Assert.IsTrue(OnServerMessageDelegates.Contains(onMessage)); OnServerMessageDelegates.Remove(onMessage); if (onClientConnecting != null) { Assert.IsTrue(OnServerConnectingDelegates.Contains(onClientConnecting)); OnServerConnectingDelegates.Remove(onClientConnecting); } if (onClientConnected != null) { Assert.IsTrue(OnServerConnectedDelegates.Contains(onClientConnected)); OnServerConnectedDelegates.Remove(onClientConnected); } } public static string DebugLastHandledMessage = ""; //public static bool IsServer = false; //public static bool IsClient = false; // static bool IsLocalClient = false; // Context dependant, true when message is handled by local client public static void Init() { if (initialized) return; /*if (Engine.CommandLine.Contains("-server")) { StartServer(); ServerAddress = "localhost"; ConnectServer(); } else if (Engine.CommandLine.Contains("-client")) { ServerAddress = "localhost"; ConnectServer(); } //#if FLAX_EDITOR else { StartServer(); ServerAddress = "localhost"; ConnectServer(); }*/ //#endif initialized = true; #if FLAX_EDITOR Editor.Instance.PlayModeEnd += Cleanup; #endif //WorldStateManager.Init(); // FIXME } public static void Cleanup() { Scripting.Exit -= Cleanup; Scripting.FixedUpdate -= OnDemoUpdate; Scripting.FixedUpdate -= OnServerNetworkUpdate; Level.ActorSpawned -= OnServerActorSpawned; Scripting.FixedUpdate -= OnClientNetworkUpdate; Level.ActorSpawned -= OnClientActorSpawned; if (server != null) { NetworkPeer.ShutdownPeer(server); server = null; } if (client != null) { NetworkPeer.ShutdownPeer(client); client = null; } //LocalPlayerClientId = 0; #if FLAX_EDITOR Editor.Instance.PlayModeEnd -= Cleanup; //GameModeManager.Cleanup(); // FIXME #endif World.CleanupClient(); World.CleanupServer(); StopRecording(); initialized = false; } private static void OnNetworkMessage(ref NetworkEvent networkEvent, Span funcs) { byte messageTypeByte = networkEvent.Message.ReadByte(); if (!Enum.IsDefined(typeof(NetworkMessageType), messageTypeByte)) { Console.PrintError($"Unsupported message type received from client: {messageTypeByte}"); return; } NetworkMessageType messageType = (NetworkMessageType)messageTypeByte; switch (messageType) { case NetworkMessageType.Handshake: { string message = networkEvent.Message.ReadString(); Console.Print($"Received handshake from {networkEvent.Sender.ConnectionId}, msg: " + message); break; } case NetworkMessageType.Message: { var messageStartPosition = networkEvent.Message.Position; foreach (var func in funcs) { networkEvent.Message.Position = messageStartPosition; bool handled = func(ref networkEvent); if (handled) break; } //if (OnMessage != null) /*{ // GetInvocationList() allocates, use allocation-free but unsafe way to iterate // //foreach (OnMessageDecl func in OnMessage.GetInvocationList() // .Cast().ToArray()) foreach (OnMessageDecl func in OnMessageDelegates) { bool ret = func.Invoke(ref networkEvent); if (ret) break; } }*/ break; } default: Console.PrintError($"Unsupported message type received from client: {messageTypeByte}"); break; } } }