Original Behaviour was using old methods for detecting entering/exiting universe, they are now all under the same hood and the original is kept for UniverseEntranceManager because it needs to enter the universe without itself. The internal behaviour kept under a subnamespace of "Core.Internal" for the purpose that it might come in handy for other use cases.
194 lines
8.5 KiB
C#
194 lines
8.5 KiB
C#
using System.Reflection;
|
|
|
|
using Engine.Core;
|
|
using Engine.Core.Debug;
|
|
using Engine.Systems.Network.Packers;
|
|
|
|
using LiteNetLib;
|
|
using LiteNetLib.Utils;
|
|
|
|
namespace Engine.Systems.Network;
|
|
|
|
public abstract class LiteNetLibCommunicatorBase : Behaviour, IEnterUniverse, IExitUniverse, INetworkCommunicator
|
|
{
|
|
protected readonly NetPacketProcessor netPacketProcessor = new();
|
|
|
|
private readonly Dictionary<Type, Event<IConnection, object>> listeners = [];
|
|
private readonly Dictionary<string, IConnection> _connections = [];
|
|
|
|
private readonly Dictionary<int, IConnection> localPeerIdToConnectionDictionary = [];
|
|
|
|
protected ILogger? logger = null;
|
|
|
|
public IReadOnlyDictionary<string, IConnection> Connections => _connections;
|
|
public EventBasedNetListener Listener { get; private set; } = null!;
|
|
public NetManager Manager { get; private set; } = null!;
|
|
|
|
public Event<INetworkCommunicator, IConnection> OnConnectionEstablished { get; } = new();
|
|
public Event<INetworkCommunicator, IConnection> OnConnectionAbolished { get; } = new();
|
|
|
|
public INetworkCommunicator Stop()
|
|
{
|
|
Manager.Stop();
|
|
return this;
|
|
}
|
|
|
|
public virtual void EnterUniverse(IUniverse universe)
|
|
{
|
|
logger = universe.FindBehaviour<ILogger>();
|
|
}
|
|
|
|
public virtual void ExitUniverse(IUniverse universe)
|
|
{
|
|
logger = null;
|
|
Stop();
|
|
}
|
|
|
|
protected virtual void OnPacketArrived<T>(T packet, NetPeer peer) where T : INetworkPacket
|
|
{
|
|
if (!listeners.TryGetValue(typeof(T), out Event<IConnection, object>? @event))
|
|
return;
|
|
|
|
@event.Invoke(localPeerIdToConnectionDictionary[peer.Id], packet);
|
|
}
|
|
|
|
private void NetworkReceiveEvent(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod)
|
|
{
|
|
try { netPacketProcessor.ReadAllPackets(reader, peer); }
|
|
catch (Exception exception) { logger?.LogException(this, exception, force: true); }
|
|
}
|
|
|
|
protected abstract IConnection GetConnection(NetPeer peer);
|
|
private void ConnectionEstablished(NetPeer peer)
|
|
{
|
|
IConnection connection = GetConnection(peer);
|
|
|
|
localPeerIdToConnectionDictionary.Add(peer.Id, connection);
|
|
_connections.Add(connection.Id, connection);
|
|
|
|
logger?.Log(this, $"Connection established with ip '{peer.Address}' and id '{connection.Id}'");
|
|
OnConnectionEstablished.Invoke(this, connection);
|
|
}
|
|
|
|
private void ConnectionAbolished(NetPeer peer, DisconnectInfo disconnectInfo)
|
|
{
|
|
if (!localPeerIdToConnectionDictionary.TryGetValue(peer.Id, out IConnection? connection))
|
|
return;
|
|
|
|
localPeerIdToConnectionDictionary.Remove(peer.Id);
|
|
_connections.Remove(connection.Id);
|
|
|
|
logger?.Log(this, $"Connection abolished with ip '{peer.Address}' and id '{connection.Id}'");
|
|
OnConnectionAbolished.Invoke(this, connection);
|
|
}
|
|
|
|
private void SetupPackets()
|
|
{
|
|
// Find network packets implementing INetworkPacket
|
|
IEnumerable<Type> packetTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes())
|
|
.Where(t => typeof(INetworkPacket).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract && !t.IsGenericType);
|
|
|
|
MethodInfo subscribeReusableMethod = typeof(NetPacketProcessor)
|
|
.GetMethods()
|
|
.FirstOrDefault(m => m.Name == nameof(NetPacketProcessor.SubscribeReusable) &&
|
|
m.GetParameters().Length == 1 &&
|
|
m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Action<,>)
|
|
)!;
|
|
|
|
MethodInfo registerNestedTypeMethod = typeof(NetPacketProcessor)
|
|
.GetMethods()
|
|
.FirstOrDefault(m => m.Name == nameof(NetPacketProcessor.RegisterNestedType) &&
|
|
m.GetParameters().Length == 0
|
|
)!;
|
|
|
|
MethodInfo onPacketArrivedMethod = typeof(LiteNetLibCommunicatorBase)
|
|
.GetMethod(nameof(OnPacketArrived), BindingFlags.NonPublic | BindingFlags.Instance)!;
|
|
|
|
// Register all network packets by calling the methods bellow where T is our found network packet type
|
|
// NetPacketProcessor.SubscribeReusable<T, NetPeer>(Action<T, NetPeer> onReceive)
|
|
// NetPacketProcessor.RegisterNestedType<T>()
|
|
foreach (Type packetType in packetTypes)
|
|
{
|
|
if (!packetType.IsClass)
|
|
{
|
|
MethodInfo genericRegisterNestedTypeMethod = registerNestedTypeMethod.MakeGenericMethod(packetType);
|
|
genericRegisterNestedTypeMethod.Invoke(netPacketProcessor, []);
|
|
continue;
|
|
}
|
|
|
|
MethodInfo genericOnPacketArrivedMethod = onPacketArrivedMethod.MakeGenericMethod(packetType);
|
|
|
|
Type delegateType = typeof(Action<,>).MakeGenericType(packetType, typeof(NetPeer));
|
|
Delegate delegateHandler = Delegate.CreateDelegate(delegateType, this, genericOnPacketArrivedMethod);
|
|
|
|
MethodInfo genericSubscribeReusableMethod = subscribeReusableMethod.MakeGenericMethod(packetType, typeof(NetPeer));
|
|
genericSubscribeReusableMethod.Invoke(netPacketProcessor, [delegateHandler]);
|
|
}
|
|
}
|
|
|
|
public LiteNetLibCommunicatorBase()
|
|
{
|
|
Listener = new EventBasedNetListener();
|
|
Manager = new NetManager(Listener);
|
|
|
|
Listener.NetworkReceiveEvent += NetworkReceiveEvent;
|
|
Listener.PeerConnectedEvent += ConnectionEstablished;
|
|
Listener.PeerDisconnectedEvent += ConnectionAbolished;
|
|
|
|
SetupEnginePackets();
|
|
SetupPackets();
|
|
}
|
|
|
|
private void SetupEnginePackets()
|
|
{
|
|
// I know, ugly af. I need to find a better way
|
|
netPacketProcessor.RegisterNestedType(AABB2DNetPacker.Write, AABB2DNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(AABB3DNetPacker.Write, AABB3DNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(CircleNetPacker.Write, CircleNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(ColorHSVNetPacker.Write, ColorHSVNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(ColorHSVANetPacker.Write, ColorHSVANetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(ColorRGBNetPacker.Write, ColorRGBNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(ColorRGBANetPacker.Write, ColorRGBANetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(Line2DNetPacker.Write, Line2DNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(Line2DEquationNetPacker.Write, Line2DEquationNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(Line3DNetPacker.Write, Line3DNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(Projection1DNetPacker.Write, Projection1DNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(QuaternionNetPacker.Write, QuaternionNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(Ray2DNetPacker.Write, Ray2DNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(Ray3DNetPacker.Write, Ray3DNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(Shape2DNetPacker.Write, Shape2DNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(Sphere3DNetPacker.Write, Sphere3DNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(TriangleNetPacker.Write, TriangleNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(Vector2DNetPacker.Write, Vector2DNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(Vector2DIntNetPacker.Write, Vector2DIntNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(Vector3DNetPacker.Write, Vector3DNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(Vector3DIntNetPacker.Write, Vector3DIntNetPacker.Read);
|
|
netPacketProcessor.RegisterNestedType(Vector4DNetPacker.Write, Vector4DNetPacker.Read);
|
|
}
|
|
|
|
public INetworkCommunicator SubscribeToPackets<T>(Event<IConnection, T>.EventHandler callback)
|
|
{
|
|
Type type = typeof(T);
|
|
if (!listeners.TryGetValue(type, out Event<IConnection, object>? @event))
|
|
{
|
|
@event = new();
|
|
listeners.Add(type, @event);
|
|
}
|
|
|
|
@event.AddListener(EventDelegateWrapper(callback));
|
|
return this;
|
|
}
|
|
|
|
public INetworkCommunicator UnsubscribeFromPackets<T>(Event<IConnection, T>.EventHandler callback)
|
|
{
|
|
Type type = typeof(T);
|
|
if (!listeners.TryGetValue(type, out Event<IConnection, object>? @event))
|
|
return this;
|
|
|
|
@event.RemoveListener(EventDelegateWrapper(callback));
|
|
return this;
|
|
}
|
|
|
|
private static Event<IConnection, object>.EventHandler EventDelegateWrapper<T>(Event<IConnection, T>.EventHandler callback) => (sender, @object) => callback.Invoke(sender, (T)@object);
|
|
}
|