Files
GoakeFlax/Source/Game/GameMode/NetworkManager.cs
2025-06-21 20:59:56 +03:00

246 lines
8.2 KiB
C#

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<OnMessageDecl> OnClientMessageDelegates = new(3);
private static List<OnClientConnectingDecl> OnClientConnectingDelegates = new(3);
private static List<OnClientConnectedDecl> OnClientConnectedDelegates = new(3);
private static List<OnMessageDecl> OnServerMessageDelegates = new(3);
private static List<OnClientConnectingDecl> OnServerConnectingDelegates = new(3);
private static List<OnClientConnectedDecl> 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.Update -= OnDemoUpdate;
Scripting.Update -= OnServerNetworkUpdate;
Level.ActorSpawned -= OnServerActorSpawned;
Scripting.Update -= 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<OnMessageDecl> 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<OnMessageDecl>().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;
}
}
}