Compare commits
	
		
			11 Commits
		
	
	
		
			feat/netwo
			...
			feat/liten
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 05dfb92bab | |||
| 7f169fc788 | |||
| 376f18c43a | |||
| 150deaa618 | |||
| d011bf9a7a | |||
| 19124e733c | |||
| c28568d0cb | |||
| 91d301677f | |||
| 73ae55e1d4 | |||
| a35e25eb31 | |||
| 775f24c560 | 
							
								
								
									
										23
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										23
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							@@ -5,14 +5,33 @@
 | 
			
		||||
  "version": "0.2.0",
 | 
			
		||||
  "configurations": [
 | 
			
		||||
    {
 | 
			
		||||
      "name": ".NET Core Launch (console)",
 | 
			
		||||
      "name": "Start Client",
 | 
			
		||||
      "type": "coreclr",
 | 
			
		||||
      "request": "launch",
 | 
			
		||||
      "preLaunchTask": "build",
 | 
			
		||||
      // If you have changed target frameworks, make sure to update the program path.
 | 
			
		||||
      "program": "${workspaceFolder}/Game/bin/Debug/net8.0/Game.dll",
 | 
			
		||||
      "args": ["127.0.0.1"],
 | 
			
		||||
      "cwd": "${workspaceFolder}",
 | 
			
		||||
      // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
 | 
			
		||||
      "console": "internalConsole",
 | 
			
		||||
      "stopAtEntry": false
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": ".NET Core Attach",
 | 
			
		||||
      "type": "coreclr",
 | 
			
		||||
      "request": "attach",
 | 
			
		||||
      "processId": "${command:pickProcess}"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "Start Server",
 | 
			
		||||
      "type": "coreclr",
 | 
			
		||||
      "request": "launch",
 | 
			
		||||
      "preLaunchTask": "build",
 | 
			
		||||
      // If you have changed target frameworks, make sure to update the program path.
 | 
			
		||||
      "program": "${workspaceFolder}/Game/bin/Debug/net8.0/Game.dll",
 | 
			
		||||
      "args": ["server"],
 | 
			
		||||
      "cwd": "${workspaceFolder}/Game",
 | 
			
		||||
      "cwd": "${workspaceFolder}",
 | 
			
		||||
      // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
 | 
			
		||||
      "console": "internalConsole",
 | 
			
		||||
      "stopAtEntry": false
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							@@ -9,10 +9,7 @@
 | 
			
		||||
        "kind": "build",
 | 
			
		||||
        "isDefault": true
 | 
			
		||||
      },
 | 
			
		||||
      "label": "build",
 | 
			
		||||
      "options": {
 | 
			
		||||
        "cwd": "${workspaceFolder}/Game"
 | 
			
		||||
      }
 | 
			
		||||
      "label": "build"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								Engine
									
									
									
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								Engine
									
									
									
									
									
								
							 Submodule Engine updated: 2cf6135063...ef21cdf213
									
								
							@@ -1,8 +0,0 @@
 | 
			
		||||
using Microsoft.Xna.Framework.Content;
 | 
			
		||||
 | 
			
		||||
namespace Pong.Behaviours;
 | 
			
		||||
 | 
			
		||||
public interface IMonoGameContentLoader
 | 
			
		||||
{
 | 
			
		||||
    void LoadContent(ContentManager content);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,19 +1,12 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
using Microsoft.Xna.Framework.Audio;
 | 
			
		||||
using Microsoft.Xna.Framework.Content;
 | 
			
		||||
 | 
			
		||||
using LiteNetLib;
 | 
			
		||||
 | 
			
		||||
using Syntriax.Engine.Core;
 | 
			
		||||
using Syntriax.Engine.Network;
 | 
			
		||||
using Syntriax.Engine.Network.Abstract;
 | 
			
		||||
using Syntriax.Engine.Physics2D;
 | 
			
		||||
using Syntriax.Engine.Physics2D.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Pong.Behaviours;
 | 
			
		||||
 | 
			
		||||
public class BallBehaviour : NetworkBehaviour, IMonoGameContentLoader
 | 
			
		||||
public class BallBehaviour : BehaviourOverride
 | 
			
		||||
{
 | 
			
		||||
    public Vector2D StartDirection { get; private set; } = Vector2D.Zero;
 | 
			
		||||
    public float Speed { get; set; } = 500f;
 | 
			
		||||
@@ -21,13 +14,6 @@ public class BallBehaviour : NetworkBehaviour, IMonoGameContentLoader
 | 
			
		||||
 | 
			
		||||
    private readonly Random random = new();
 | 
			
		||||
    private IRigidBody2D rigidBody = null!;
 | 
			
		||||
    private INetworkCommunicator communicator = null!;
 | 
			
		||||
    private SoundEffect soundEffect = null!;
 | 
			
		||||
 | 
			
		||||
    public void LoadContent(ContentManager content)
 | 
			
		||||
    {
 | 
			
		||||
        soundEffect = content.Load<SoundEffect>("Hit");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnFirstActiveFrame()
 | 
			
		||||
    {
 | 
			
		||||
@@ -35,13 +21,11 @@ public class BallBehaviour : NetworkBehaviour, IMonoGameContentLoader
 | 
			
		||||
            throw new Exception($"{nameof(IRigidBody2D)} is missing on {GameObject.Name}.");
 | 
			
		||||
        if (!BehaviourController.TryGetBehaviour(out ICollider2D? foundCollider))
 | 
			
		||||
            throw new Exception($"{nameof(ICollider2D)} is missing on {GameObject.Name}.");
 | 
			
		||||
        if (!GameObject.GameManager.TryFindBehaviour(out INetworkCommunicator? foundCommunicator))
 | 
			
		||||
            throw new Exception($"{nameof(INetworkCommunicator)} is missing on GameManager.");
 | 
			
		||||
 | 
			
		||||
        foundCollider.OnCollisionDetected += OnCollisionDetected;
 | 
			
		||||
 | 
			
		||||
        rigidBody = foundRigidBody;
 | 
			
		||||
        communicator = foundCommunicator;
 | 
			
		||||
 | 
			
		||||
        if (GameObject.GameManager.TryFindBehaviour(out PongManagerBehaviour? pongManager))
 | 
			
		||||
        {
 | 
			
		||||
            pongManager.OnReset += ResetBall;
 | 
			
		||||
@@ -61,8 +45,6 @@ public class BallBehaviour : NetworkBehaviour, IMonoGameContentLoader
 | 
			
		||||
        StateEnable.Enabled = true;
 | 
			
		||||
        BehaviourController.GameObject.Transform.Position = Vector2D.Zero;
 | 
			
		||||
        rigidBody.Velocity = GetRandomDirection() * Speed;
 | 
			
		||||
 | 
			
		||||
        SendBallData();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Vector2D GetRandomDirection()
 | 
			
		||||
@@ -88,27 +70,6 @@ public class BallBehaviour : NetworkBehaviour, IMonoGameContentLoader
 | 
			
		||||
            rigidBody.Velocity = information.Left.Transform.Position.FromTo(information.Right.Transform.Position).Normalized * rigidBody.Velocity.Magnitude;
 | 
			
		||||
        else
 | 
			
		||||
            rigidBody.Velocity = rigidBody.Velocity.Reflect(information.Normal);
 | 
			
		||||
 | 
			
		||||
        soundEffect.Play();
 | 
			
		||||
 | 
			
		||||
        SendBallData();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void SendBallData()
 | 
			
		||||
    {
 | 
			
		||||
        if (communicator is not INetworkServer server)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        LiteNetLib.Utils.NetDataWriter dataWriter = communicator.GetMessageWriter(this);
 | 
			
		||||
        dataWriter.Put(rigidBody.Transform.Position);
 | 
			
		||||
        dataWriter.Put(rigidBody.Velocity);
 | 
			
		||||
        server.Manager.SendToAll(dataWriter, LiteNetLib.DeliveryMethod.ReliableOrdered);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnMessageReceived(NetPacketReader reader, NetPeer peer)
 | 
			
		||||
    {
 | 
			
		||||
        rigidBody.Transform.Position = reader.GetVector2D();
 | 
			
		||||
        rigidBody.Velocity = reader.GetVector2D();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public BallBehaviour() { }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
using Microsoft.Xna.Framework.Input;
 | 
			
		||||
 | 
			
		||||
using Syntriax.Engine.Core;
 | 
			
		||||
@@ -9,8 +10,8 @@ namespace Pong.Behaviours;
 | 
			
		||||
 | 
			
		||||
public class KeyboardInputsBehaviour : BehaviourOverride, IButtonInputs<Keys>
 | 
			
		||||
{
 | 
			
		||||
    private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>?> OnPressed = new(256);
 | 
			
		||||
    private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>?> OnReleased = new(256);
 | 
			
		||||
    private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>> OnPressed = new(256);
 | 
			
		||||
    private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>> OnReleased = new(256);
 | 
			
		||||
 | 
			
		||||
    private int cachePressedCurrentlyCount = 0;
 | 
			
		||||
    private readonly Keys[] cachePressedCurrently = new Keys[256];
 | 
			
		||||
@@ -68,7 +69,7 @@ public class KeyboardInputsBehaviour : BehaviourOverride, IButtonInputs<Keys>
 | 
			
		||||
            if (WasPressed(currentlyPressedKey))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            action?.Invoke(this, currentlyPressedKey);
 | 
			
		||||
            action.Invoke(this, currentlyPressedKey);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < cachePressedPreviouslyCount; i++)
 | 
			
		||||
@@ -81,7 +82,7 @@ public class KeyboardInputsBehaviour : BehaviourOverride, IButtonInputs<Keys>
 | 
			
		||||
            if (IsPressed(previouslyPressedKey))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            action?.Invoke(this, previouslyPressedKey);
 | 
			
		||||
            action.Invoke(this, previouslyPressedKey);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Array.Copy(cachePressedCurrently, cachePressedPreviously, cachePressedCurrentlyCount);
 | 
			
		||||
 
 | 
			
		||||
@@ -8,9 +8,9 @@ namespace Pong.Behaviours;
 | 
			
		||||
 | 
			
		||||
public class MonoGameCamera2DBehaviour(GraphicsDeviceManager Graphics) : BehaviourOverride, ICamera2D
 | 
			
		||||
{
 | 
			
		||||
    public System.Action<MonoGameCamera2DBehaviour>? OnMatrixTransformChanged { get; set; } = null;
 | 
			
		||||
    public System.Action<MonoGameCamera2DBehaviour>? OnViewportChanged { get; set; } = null;
 | 
			
		||||
    public System.Action<MonoGameCamera2DBehaviour>? OnZoomChanged { get; set; } = null;
 | 
			
		||||
    public event OnMatrixTransformChangedDelegate? OnMatrixTransformChanged = null;
 | 
			
		||||
    public event OnViewportChangedDelegate? OnViewportChanged = null;
 | 
			
		||||
    public event OnZoomChangedDelegate? OnZoomChanged = null;
 | 
			
		||||
 | 
			
		||||
    private Matrix _matrixTransform = Matrix.Identity;
 | 
			
		||||
 | 
			
		||||
@@ -72,7 +72,7 @@ public class MonoGameCamera2DBehaviour(GraphicsDeviceManager Graphics) : Behavio
 | 
			
		||||
        set => Transform.Rotation = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    System.Action<IAssignableTransform>? IAssignableTransform.OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; }
 | 
			
		||||
    public event IAssignableTransform.OnTransformAssignedDelegate? OnTransformAssigned { add => GameObject.OnTransformAssigned += value; remove => GameObject.OnTransformAssigned -= value; }
 | 
			
		||||
    ITransform IAssignableTransform.Transform => GameObject.Transform;
 | 
			
		||||
    bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform);
 | 
			
		||||
 | 
			
		||||
@@ -99,4 +99,8 @@ public class MonoGameCamera2DBehaviour(GraphicsDeviceManager Graphics) : Behavio
 | 
			
		||||
            Matrix.CreateScale(Zoom) *
 | 
			
		||||
            Matrix.CreateTranslation(new Vector3(_viewport.Width * .5f, _viewport.Height * .5f, 0f));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public delegate void OnMatrixTransformChangedDelegate(MonoGameCamera2DBehaviour sender);
 | 
			
		||||
    public delegate void OnViewportChangedDelegate(MonoGameCamera2DBehaviour sender);
 | 
			
		||||
    public delegate void OnZoomChangedDelegate(MonoGameCamera2DBehaviour sender);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,154 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using LiteNetLib;
 | 
			
		||||
using Microsoft.Xna.Framework.Input;
 | 
			
		||||
using Syntriax.Engine.Input;
 | 
			
		||||
using Syntriax.Engine.Network;
 | 
			
		||||
 | 
			
		||||
namespace Pong.Behaviours;
 | 
			
		||||
 | 
			
		||||
public class NetworkedKeyboardInputs : NetworkBehaviour, IButtonInputs<Keys>
 | 
			
		||||
{
 | 
			
		||||
    private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>?> OnPressed = new(256);
 | 
			
		||||
    private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>?> OnReleased = new(256);
 | 
			
		||||
 | 
			
		||||
    private int cachePressedCurrentlyCount = 0;
 | 
			
		||||
    private readonly Keys[] cachePressedCurrently = new Keys[256];
 | 
			
		||||
 | 
			
		||||
    private int cachePressedPreviouslyCount = 0;
 | 
			
		||||
    private readonly Keys[] cachePressedPreviously = new Keys[256];
 | 
			
		||||
 | 
			
		||||
    public void RegisterOnPress(Keys key, Action<IButtonInputs<Keys>, Keys> callback)
 | 
			
		||||
    {
 | 
			
		||||
        if (OnPressed.TryGetValue(key, out var action))
 | 
			
		||||
        {
 | 
			
		||||
            action += callback;
 | 
			
		||||
            OnPressed.Remove(key);
 | 
			
		||||
            OnPressed.Add(key, action);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        OnPressed.Add(key, callback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void UnregisterOnPress(Keys key, Action<IButtonInputs<Keys>, Keys> callback)
 | 
			
		||||
    {
 | 
			
		||||
        if (OnPressed.TryGetValue(key, out var action))
 | 
			
		||||
            action -= callback;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void RegisterOnRelease(Keys key, Action<IButtonInputs<Keys>, Keys> callback)
 | 
			
		||||
    {
 | 
			
		||||
        if (OnReleased.TryGetValue(key, out var action))
 | 
			
		||||
        {
 | 
			
		||||
            action += callback;
 | 
			
		||||
            OnReleased.Remove(key);
 | 
			
		||||
            OnReleased.Add(key, action);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        OnReleased.Add(key, callback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void UnregisterOnRelease(Keys key, Action<IButtonInputs<Keys>, Keys> callback)
 | 
			
		||||
    {
 | 
			
		||||
        if (OnReleased.TryGetValue(key, out var action))
 | 
			
		||||
            action -= callback;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnUpdate()
 | 
			
		||||
    {
 | 
			
		||||
        if (!IsServer && !LocalAssigned)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        KeyboardState keyboardState = Keyboard.GetState();
 | 
			
		||||
        keyboardState.GetPressedKeys(cachePressedCurrently);
 | 
			
		||||
        cachePressedCurrentlyCount = keyboardState.GetPressedKeyCount();
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < cachePressedCurrentlyCount; i++)
 | 
			
		||||
        {
 | 
			
		||||
            Keys currentlyPressedKey = cachePressedCurrently[i];
 | 
			
		||||
 | 
			
		||||
            if (!OnPressed.TryGetValue(currentlyPressedKey, out var action))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (WasPressed(currentlyPressedKey))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            action?.Invoke(this, currentlyPressedKey);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < cachePressedPreviouslyCount; i++)
 | 
			
		||||
        {
 | 
			
		||||
            Keys previouslyPressedKey = cachePressedPreviously[i];
 | 
			
		||||
 | 
			
		||||
            if (!OnReleased.TryGetValue(previouslyPressedKey, out var action))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (IsPressed(previouslyPressedKey))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            action?.Invoke(this, previouslyPressedKey);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Array.Copy(cachePressedCurrently, cachePressedPreviously, cachePressedCurrentlyCount);
 | 
			
		||||
        cachePressedPreviouslyCount = cachePressedCurrentlyCount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool IsPressed(Keys key)
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = 0; i < cachePressedCurrentlyCount; i++)
 | 
			
		||||
            if (cachePressedCurrently[i] == key)
 | 
			
		||||
                return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool WasPressed(Keys key)
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = 0; i < cachePressedPreviouslyCount; i++)
 | 
			
		||||
            if (cachePressedPreviously[i] == key)
 | 
			
		||||
                return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnInitialize()
 | 
			
		||||
    {
 | 
			
		||||
        base.OnInitialize();
 | 
			
		||||
        foreach (Keys key in Enum.GetValues(typeof(Keys)))
 | 
			
		||||
        {
 | 
			
		||||
            RegisterOnPress(key, (keys, keyVal) =>
 | 
			
		||||
            {
 | 
			
		||||
                if (!LocalAssigned && !IsServer)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                var netDataWriter = NetworkCommunicator.GetMessageWriter(this);
 | 
			
		||||
                netDataWriter.Put(true);
 | 
			
		||||
                netDataWriter.Put((int)key);
 | 
			
		||||
                NetworkCommunicator.Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered);
 | 
			
		||||
            });
 | 
			
		||||
            RegisterOnRelease(key, (keys, keyVal) =>
 | 
			
		||||
            {
 | 
			
		||||
                if (!LocalAssigned && !IsServer)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                var netDataWriter = NetworkCommunicator.GetMessageWriter(this);
 | 
			
		||||
                netDataWriter.Put(false);
 | 
			
		||||
                netDataWriter.Put((int)key);
 | 
			
		||||
                NetworkCommunicator.Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnMessageReceived(NetPacketReader reader, NetPeer peer)
 | 
			
		||||
    {
 | 
			
		||||
        bool isPressed = reader.GetBool();
 | 
			
		||||
        Keys key = (Keys)reader.GetInt();
 | 
			
		||||
 | 
			
		||||
        if (isPressed)
 | 
			
		||||
            if (OnPressed.TryGetValue(key, out var action))
 | 
			
		||||
                action?.Invoke(this, key);
 | 
			
		||||
        if (!isPressed)
 | 
			
		||||
            if (OnReleased.TryGetValue(key, out var action))
 | 
			
		||||
                action?.Invoke(this, key);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +1,6 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
using Microsoft.Xna.Framework.Input;
 | 
			
		||||
 | 
			
		||||
using LiteNetLib;
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
using Microsoft.Xna.Framework.Input;
 | 
			
		||||
 | 
			
		||||
using Syntriax.Engine.Core;
 | 
			
		||||
using Syntriax.Engine.Input;
 | 
			
		||||
@@ -12,7 +9,7 @@ using Syntriax.Engine.Network.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Pong.Behaviours;
 | 
			
		||||
 | 
			
		||||
public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Speed) : BehaviourOverride
 | 
			
		||||
public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Speed) : NetworkBehaviour
 | 
			
		||||
{
 | 
			
		||||
    private Keys Up { get; } = Up;
 | 
			
		||||
    private Keys Down { get; } = Down;
 | 
			
		||||
@@ -24,7 +21,6 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp
 | 
			
		||||
    private bool isDownPressed = false;
 | 
			
		||||
 | 
			
		||||
    private IButtonInputs<Keys> inputs = null!;
 | 
			
		||||
    private INetworkCommunicator communicator = null!;
 | 
			
		||||
 | 
			
		||||
    protected override void OnUpdate()
 | 
			
		||||
    {
 | 
			
		||||
@@ -32,33 +28,27 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (isUpPressed)
 | 
			
		||||
            MovePaddle(Vector2D.Up * (float)Time.Elapsed.TotalSeconds * Speed);
 | 
			
		||||
            Move(Vector2D.Up);
 | 
			
		||||
        else if (isDownPressed)
 | 
			
		||||
            MovePaddle(-Vector2D.Up * (float)Time.Elapsed.TotalSeconds * Speed);
 | 
			
		||||
            Move(-Vector2D.Up);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void MovePaddle(Vector2D vectorToAdd)
 | 
			
		||||
    private void Move(Vector2D vectorToMove)
 | 
			
		||||
    {
 | 
			
		||||
        GameObject.Transform.Position += vectorToAdd;
 | 
			
		||||
        GameObject.Transform.Position = GameObject.Transform.Position + vectorToMove * (float)Time.Elapsed.TotalSeconds * Speed;
 | 
			
		||||
        GameObject.Transform.Position = new Vector2D(GameObject.Transform.Position.X, MathF.Max(MathF.Min(GameObject.Transform.Position.Y, High), Low));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnFirstActiveFrame()
 | 
			
		||||
    {
 | 
			
		||||
        BehaviourController.TryGetBehaviour<IButtonInputs<Keys>>(out var behaviourResult);
 | 
			
		||||
        inputs = behaviourResult ?? throw new Exception($"{nameof(IButtonInputs<Keys>)} is missing on {GameObject.Name}.");
 | 
			
		||||
 | 
			
		||||
        if (!GameObject.GameManager.TryFindBehaviour(out INetworkCommunicator? foundCommunicator))
 | 
			
		||||
            throw new Exception($"{nameof(INetworkCommunicator)} is missing on GameManager.");
 | 
			
		||||
        if (!BehaviourController.TryGetBehaviour<IButtonInputs<Keys>>(out var behaviourResult))
 | 
			
		||||
            inputs = behaviourResult ?? BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
 | 
			
		||||
 | 
			
		||||
        inputs.RegisterOnPress(Up, OnUpPressed);
 | 
			
		||||
        inputs.RegisterOnRelease(Up, OnUpReleased);
 | 
			
		||||
 | 
			
		||||
        inputs.RegisterOnPress(Down, OnDownPressed);
 | 
			
		||||
        inputs.RegisterOnRelease(Down, OnDownReleased);
 | 
			
		||||
 | 
			
		||||
        communicator = foundCommunicator;
 | 
			
		||||
        communicator.RegisterEntityListener(this, OnDataReceived);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnFinalize()
 | 
			
		||||
@@ -70,32 +60,40 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp
 | 
			
		||||
        inputs.UnregisterOnRelease(Down, OnDownReleased);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void UpdateNetwork()
 | 
			
		||||
    private void OnUpPressed(IButtonInputs<Keys> inputs, Keys keys) { isUpPressed = true; SendData(new PaddleInputs() { IsUpPressed = isUpPressed, IsDownPressed = isDownPressed, PositionY = Transform.Position.Y }); }
 | 
			
		||||
    private void OnUpReleased(IButtonInputs<Keys> inputs, Keys keys) { isUpPressed = false; SendData(new PaddleInputs() { IsUpPressed = isUpPressed, IsDownPressed = isDownPressed, PositionY = Transform.Position.Y }); }
 | 
			
		||||
    private void OnDownPressed(IButtonInputs<Keys> inputs, Keys keys) { isDownPressed = true; SendData(new PaddleInputs() { IsUpPressed = isUpPressed, IsDownPressed = isDownPressed, PositionY = Transform.Position.Y }); }
 | 
			
		||||
    private void OnDownReleased(IButtonInputs<Keys> inputs, Keys keys) { isDownPressed = false; SendData(new PaddleInputs() { IsUpPressed = isUpPressed, IsDownPressed = isDownPressed, PositionY = Transform.Position.Y }); }
 | 
			
		||||
 | 
			
		||||
    public override void ReceiveData<T>(T data)
 | 
			
		||||
    {
 | 
			
		||||
        NetDataWriter netDataWriter = communicator.GetEntityWriter(this);
 | 
			
		||||
        netDataWriter.Put(isUpPressed);
 | 
			
		||||
        netDataWriter.Put(isDownPressed);
 | 
			
		||||
        netDataWriter.Put(Transform.Position);
 | 
			
		||||
        communicator.Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered);
 | 
			
		||||
        if (data is not PaddleInputs paddleInputs)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        Transform.Position = new(Transform.Position.X, paddleInputs.PositionY);
 | 
			
		||||
        isUpPressed = paddleInputs.IsUpPressed;
 | 
			
		||||
        isDownPressed = paddleInputs.IsDownPressed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnDataReceived(NetPacketReader reader, NetPeer peer)
 | 
			
		||||
    [System.Serializable]
 | 
			
		||||
    public struct PaddleInputs : INetworkPacket
 | 
			
		||||
    {
 | 
			
		||||
        if (communicator is INetworkServer server)
 | 
			
		||||
        public float PositionY { get; set; }
 | 
			
		||||
        public bool IsUpPressed { get; set; }
 | 
			
		||||
        public bool IsDownPressed { get; set; }
 | 
			
		||||
 | 
			
		||||
        public void Deserialize(NetDataReader reader)
 | 
			
		||||
        {
 | 
			
		||||
            reader.Get(out isUpPressed);
 | 
			
		||||
            reader.Get(out isDownPressed);
 | 
			
		||||
            PositionY = reader.GetFloat();
 | 
			
		||||
            IsUpPressed = reader.GetBool();
 | 
			
		||||
            IsDownPressed = reader.GetBool();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
 | 
			
		||||
        public void Serialize(NetDataWriter writer)
 | 
			
		||||
        {
 | 
			
		||||
            reader.Get(out isUpPressed);
 | 
			
		||||
            reader.Get(out isDownPressed);
 | 
			
		||||
            Transform.Position = reader.GetVector2D();
 | 
			
		||||
            writer.Put(PositionY);
 | 
			
		||||
            writer.Put(IsUpPressed);
 | 
			
		||||
            writer.Put(IsDownPressed);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnUpPressed(IButtonInputs<Keys> inputs, Keys keys) { isUpPressed = true; UpdateNetwork(); }
 | 
			
		||||
    private void OnUpReleased(IButtonInputs<Keys> inputs, Keys keys) { isUpPressed = false; UpdateNetwork(); }
 | 
			
		||||
    private void OnDownPressed(IButtonInputs<Keys> inputs, Keys keys) { isDownPressed = true; UpdateNetwork(); }
 | 
			
		||||
    private void OnDownReleased(IButtonInputs<Keys> inputs, Keys keys) { isDownPressed = false; UpdateNetwork(); }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,23 +2,16 @@ using System;
 | 
			
		||||
 | 
			
		||||
using Microsoft.Xna.Framework.Input;
 | 
			
		||||
 | 
			
		||||
using LiteNetLib;
 | 
			
		||||
 | 
			
		||||
using Syntriax.Engine.Core;
 | 
			
		||||
using Syntriax.Engine.Network;
 | 
			
		||||
using Syntriax.Engine.Network.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Pong.Behaviours;
 | 
			
		||||
 | 
			
		||||
public class PongManagerBehaviour : NetworkBehaviour
 | 
			
		||||
public class PongManagerBehaviour : BehaviourOverride
 | 
			
		||||
{
 | 
			
		||||
    public Action<PongManagerBehaviour>? OnReset { get; set; } = null;
 | 
			
		||||
    public Action<PongManagerBehaviour>? OnFinished { get; set; } = null;
 | 
			
		||||
    public Action<PongManagerBehaviour>? OnScoresUpdated { get; set; } = null;
 | 
			
		||||
    public Action<PongManagerBehaviour>? OnScored { get; set; } = null;
 | 
			
		||||
 | 
			
		||||
    private INetworkCommunicator communicator = null!;
 | 
			
		||||
 | 
			
		||||
    public int ScoreLeft { get; private set; } = 0;
 | 
			
		||||
    public int ScoreRight { get; private set; } = 0;
 | 
			
		||||
    public int ScoreSum => ScoreLeft + ScoreRight;
 | 
			
		||||
@@ -35,43 +28,30 @@ public class PongManagerBehaviour : NetworkBehaviour
 | 
			
		||||
        if (!BehaviourController.TryGetBehaviour(out buttonInputs))
 | 
			
		||||
            buttonInputs = BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
 | 
			
		||||
 | 
			
		||||
        if (!GameObject.GameManager.TryFindBehaviour(out INetworkCommunicator? foundCommunicator))
 | 
			
		||||
            throw new Exception($"{nameof(INetworkCommunicator)} is missing on GameManager.");
 | 
			
		||||
 | 
			
		||||
        buttonInputs.RegisterOnRelease(Keys.Space, (_, _1) => Reset());
 | 
			
		||||
 | 
			
		||||
        communicator = foundCommunicator;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void ScoreToLeft()
 | 
			
		||||
    {
 | 
			
		||||
        ScoreLeft++;
 | 
			
		||||
        OnScoresUpdated?.Invoke(this);
 | 
			
		||||
        OnScored?.Invoke(this);
 | 
			
		||||
 | 
			
		||||
        SendData();
 | 
			
		||||
 | 
			
		||||
        CheckFinish();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void ScoreToRight()
 | 
			
		||||
    {
 | 
			
		||||
        ScoreRight++;
 | 
			
		||||
        OnScoresUpdated?.Invoke(this);
 | 
			
		||||
        OnScored?.Invoke(this);
 | 
			
		||||
 | 
			
		||||
        SendData();
 | 
			
		||||
 | 
			
		||||
        CheckFinish();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Reset()
 | 
			
		||||
    {
 | 
			
		||||
        ScoreLeft = ScoreRight = 0;
 | 
			
		||||
        OnScoresUpdated?.Invoke(this);
 | 
			
		||||
        OnReset?.Invoke(this);
 | 
			
		||||
 | 
			
		||||
        SendData();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void CheckFinish()
 | 
			
		||||
@@ -81,24 +61,4 @@ public class PongManagerBehaviour : NetworkBehaviour
 | 
			
		||||
        if (ScoreLeft > halfwayScore || ScoreRight > halfwayScore)
 | 
			
		||||
            OnFinished?.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void SendData()
 | 
			
		||||
    {
 | 
			
		||||
        if (communicator is not INetworkServer server)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        LiteNetLib.Utils.NetDataWriter dataWriter = communicator.GetMessageWriter(this);
 | 
			
		||||
        dataWriter.Put(ScoreLeft);
 | 
			
		||||
        dataWriter.Put(ScoreRight);
 | 
			
		||||
        server.Manager.SendToAll(dataWriter, LiteNetLib.DeliveryMethod.ReliableOrdered);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnMessageReceived(NetPacketReader reader, NetPeer peer)
 | 
			
		||||
    {
 | 
			
		||||
        ScoreLeft = reader.GetInt();
 | 
			
		||||
        ScoreRight = reader.GetInt();
 | 
			
		||||
        OnScoresUpdated?.Invoke(this);
 | 
			
		||||
 | 
			
		||||
        CheckFinish();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,8 @@ public class TextScoreBehaviour : TextBehaviour
 | 
			
		||||
        if (!GameObject.GameManager.TryFindBehaviour(out pongManager))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        pongManager.OnScoresUpdated += UpdateScores;
 | 
			
		||||
        pongManager.OnScored += UpdateScores;
 | 
			
		||||
        pongManager.OnReset += UpdateScores;
 | 
			
		||||
 | 
			
		||||
        UpdateScores(pongManager);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -13,12 +13,6 @@
 | 
			
		||||
 | 
			
		||||
#---------------------------------- Content ---------------------------------#
 | 
			
		||||
 | 
			
		||||
#begin Hit.wav
 | 
			
		||||
/importer:WavImporter
 | 
			
		||||
/processor:SoundEffectProcessor
 | 
			
		||||
/processorParam:Quality=Best
 | 
			
		||||
/build:Hit.wav
 | 
			
		||||
 | 
			
		||||
#begin UbuntuMono.spritefont
 | 
			
		||||
/importer:FontDescriptionImporter
 | 
			
		||||
/processor:FontDescriptionProcessor
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							@@ -27,7 +27,6 @@ public class GamePong : Game
 | 
			
		||||
    private GameManager gameManager = null!;
 | 
			
		||||
    private BehaviourCollector<IDisplayableSprite> displayableCollector = null!;
 | 
			
		||||
    private BehaviourCollector<IDisplayableShape> displayableShapeCollector = null!;
 | 
			
		||||
    private BehaviourCollector<IMonoGameContentLoader> monoGameContentLoaderCollector = null!;
 | 
			
		||||
    private MonoGameCamera2DBehaviour cameraBehaviour = null!;
 | 
			
		||||
 | 
			
		||||
    private PongManagerBehaviour pongManager = null!;
 | 
			
		||||
@@ -53,11 +52,8 @@ public class GamePong : Game
 | 
			
		||||
        gameManager = new();
 | 
			
		||||
        displayableCollector = new(gameManager);
 | 
			
		||||
        displayableShapeCollector = new(gameManager);
 | 
			
		||||
        monoGameContentLoaderCollector = new(gameManager);
 | 
			
		||||
        physicsEngine = new PhysicsEngine2DCollector(gameManager) { IterationPerStep = 3 };
 | 
			
		||||
 | 
			
		||||
        monoGameContentLoaderCollector.OnCollected += (_, cached) => cached.LoadContent(Content);
 | 
			
		||||
 | 
			
		||||
        gameManager.Initialize();
 | 
			
		||||
 | 
			
		||||
        base.Initialize();
 | 
			
		||||
@@ -91,6 +87,9 @@ public class GamePong : Game
 | 
			
		||||
            Window.Title = $"Pong - Client -> 127.0.0.1";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        IGameObject gameObjectNetworkManager = gameManager.InstantiateGameObject().SetGameObject("Network Manager"); ;
 | 
			
		||||
        gameObjectNetworkManager.BehaviourController.AddBehaviour<NetworkManager>();
 | 
			
		||||
 | 
			
		||||
        ////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
        IGameObject gameObjectCamera = gameManager.InstantiateGameObject().SetGameObject("Camera"); ;
 | 
			
		||||
@@ -117,14 +116,12 @@ public class GamePong : Game
 | 
			
		||||
        IGameObject gameObjectLeftPaddle = gameManager.InstantiateGameObject().SetGameObject("Left Paddle");
 | 
			
		||||
        gameObjectLeftPaddle.Transform.SetTransform(position: new Vector2D(-468f, 0f), scale: new Vector2D(15f, 60f));
 | 
			
		||||
 | 
			
		||||
        gameObjectLeftPaddle.BehaviourController.AddBehaviour<NetworkedKeyboardInputs>().Id = "leftPaddleInput";
 | 
			
		||||
        gameObjectLeftPaddle.BehaviourController.AddBehaviour<PaddleBehaviour>(Keys.W, Keys.S, 228f, -228f, 400f).Id = "leftPaddle";
 | 
			
		||||
        gameObjectLeftPaddle.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.Box);
 | 
			
		||||
        gameObjectLeftPaddle.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;
 | 
			
		||||
 | 
			
		||||
        IGameObject gameObjectRightPaddle = gameManager.InstantiateGameObject().SetGameObject("Right Paddle");
 | 
			
		||||
        gameObjectRightPaddle.Transform.SetTransform(position: new Vector2D(468f, 0f), scale: new Vector2D(15f, 60f));
 | 
			
		||||
        gameObjectRightPaddle.BehaviourController.AddBehaviour<NetworkedKeyboardInputs>().Id = "rightPaddleInput";
 | 
			
		||||
        gameObjectRightPaddle.BehaviourController.AddBehaviour<PaddleBehaviour>(Keys.Up, Keys.Down, 228f, -228f, 400f).Id = "rightPaddle";
 | 
			
		||||
        gameObjectRightPaddle.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.Box);
 | 
			
		||||
        gameObjectRightPaddle.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,17 +2,10 @@ using Syntriax.Engine.Core.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Network.Abstract;
 | 
			
		||||
 | 
			
		||||
public interface INetworkBehaviour : IBehaviour
 | 
			
		||||
public interface INetworkBehaviour : IBehaviour, INetworkEntity
 | 
			
		||||
{
 | 
			
		||||
    int NetworkId { get; }
 | 
			
		||||
 | 
			
		||||
    bool LocalAssigned { get; }
 | 
			
		||||
 | 
			
		||||
    bool IsServer { get; }
 | 
			
		||||
    bool IsClient { get; }
 | 
			
		||||
 | 
			
		||||
    INetworkCommunicator NetworkCommunicator { get; }
 | 
			
		||||
 | 
			
		||||
    public void RequestAssignment();
 | 
			
		||||
    public void ReleaseAssignment();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,18 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
using LiteNetLib;
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Syntriax.Engine.Core.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Network.Abstract;
 | 
			
		||||
 | 
			
		||||
public interface INetworkCommunicator
 | 
			
		||||
{
 | 
			
		||||
    event OnPacketReceivedDelegate? OnPacketReceived;
 | 
			
		||||
 | 
			
		||||
    EventBasedNetListener Listener { get; }
 | 
			
		||||
    NetManager Manager { get; }
 | 
			
		||||
 | 
			
		||||
    void PollEvents();
 | 
			
		||||
    void Stop();
 | 
			
		||||
 | 
			
		||||
    void RegisterEntityListener(IEntity entity, Action<NetPacketReader, NetPeer> onDataReceived);
 | 
			
		||||
    void UnregisterEntityListener(IEntity entity);
 | 
			
		||||
    NetDataWriter GetEntityWriter(IEntity entity);
 | 
			
		||||
    NetDataWriter GetMessageWriter(IEntity entity);
 | 
			
		||||
    void Send<T>(NetworkPacket<T> Data);
 | 
			
		||||
 | 
			
		||||
    delegate void OnPacketReceivedDelegate(INetworkCommunicator sender, object packet);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
namespace Syntriax.Engine.Network.Abstract;
 | 
			
		||||
 | 
			
		||||
public interface INetworkClient : INetworkCommunicator
 | 
			
		||||
public interface INetworkCommunicatorClient : INetworkCommunicator
 | 
			
		||||
{
 | 
			
		||||
    void Connect(string address, int port, string? password = null);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
namespace Syntriax.Engine.Network.Abstract;
 | 
			
		||||
 | 
			
		||||
public interface INetworkServer : INetworkCommunicator
 | 
			
		||||
public interface INetworkCommunicatorServer : INetworkCommunicator
 | 
			
		||||
{
 | 
			
		||||
    string Password { get; }
 | 
			
		||||
    int MaxConnectionCount { get; }
 | 
			
		||||
@@ -1,11 +1,12 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Network.Abstract;
 | 
			
		||||
 | 
			
		||||
internal interface INetworkEntity
 | 
			
		||||
public interface INetworkEntity
 | 
			
		||||
{
 | 
			
		||||
    Action<INetworkEntity, int> OnNetworkIdChanged { get; set; }
 | 
			
		||||
    int NetworkId { get; set; }
 | 
			
		||||
    event OnNetworkIdChangedDelegate? OnNetworkIdChanged;
 | 
			
		||||
 | 
			
		||||
    void SetNetworkId(int id);
 | 
			
		||||
    uint NetworkId { get; set; }
 | 
			
		||||
 | 
			
		||||
    void ReceiveData<T>(T data);
 | 
			
		||||
 | 
			
		||||
    delegate void OnNetworkIdChangedDelegate(INetworkEntity sender, uint previousId);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								Game/Network/Abstract/INetworkPacket.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								Game/Network/Abstract/INetworkPacket.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Network.Abstract;
 | 
			
		||||
 | 
			
		||||
public interface INetworkPacket : INetSerializable;
 | 
			
		||||
							
								
								
									
										7
									
								
								Game/Network/Abstract/NetworkPacket.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								Game/Network/Abstract/NetworkPacket.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
namespace Syntriax.Engine.Network.Abstract;
 | 
			
		||||
 | 
			
		||||
public class NetworkPacket<T>
 | 
			
		||||
{
 | 
			
		||||
    public uint NetworkId { get; set; } = 0;
 | 
			
		||||
    public T Data { get; set; } = default!;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,17 +1,25 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using LiteNetLib;
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Syntriax.Engine.Core;
 | 
			
		||||
using Syntriax.Engine.Core.Abstract;
 | 
			
		||||
using Syntriax.Engine.Network.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Network;
 | 
			
		||||
 | 
			
		||||
public abstract class NetworkBase : BehaviourOverride, INetworkCommunicator
 | 
			
		||||
{
 | 
			
		||||
    public event INetworkCommunicator.OnPacketReceivedDelegate? OnPacketReceived = null;
 | 
			
		||||
 | 
			
		||||
    protected readonly NetPacketProcessor netPacketProcessor = new();
 | 
			
		||||
 | 
			
		||||
    protected readonly Dictionary<uint, INetworkEntity> networkEntities = [];
 | 
			
		||||
 | 
			
		||||
    protected BehaviourCollector<INetworkEntity> networkEntityCollector = null!;
 | 
			
		||||
 | 
			
		||||
    public EventBasedNetListener Listener { get; private set; } = null!;
 | 
			
		||||
    public NetManager Manager { get; private set; } = null!;
 | 
			
		||||
 | 
			
		||||
@@ -23,59 +31,110 @@ public abstract class NetworkBase : BehaviourOverride, INetworkCommunicator
 | 
			
		||||
        Manager = new NetManager(Listener);
 | 
			
		||||
 | 
			
		||||
        Listener.NetworkReceiveEvent += NetworkReceiveEvent;
 | 
			
		||||
 | 
			
		||||
        netPacketProcessor.RegisterNestedType<Pong.Behaviours.PaddleBehaviour.PaddleInputs>();
 | 
			
		||||
        RegisterPackets();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void RegisterPackets()
 | 
			
		||||
    {
 | 
			
		||||
        var packetTypes = Assembly.GetExecutingAssembly().GetTypes()
 | 
			
		||||
            .Where(t => typeof(INetworkPacket).IsAssignableFrom(t) && !t.IsInterface)
 | 
			
		||||
            .ToList();
 | 
			
		||||
 | 
			
		||||
        MethodInfo[] subscribeMethods = netPacketProcessor.GetType()
 | 
			
		||||
            .GetMethods()
 | 
			
		||||
            .Where(m => m.Name == nameof(NetPacketProcessor.SubscribeReusable))
 | 
			
		||||
            .ToArray();
 | 
			
		||||
 | 
			
		||||
        MethodInfo subscribeMethod = subscribeMethods
 | 
			
		||||
            .FirstOrDefault(m =>
 | 
			
		||||
                m.GetParameters().Length == 1 &&
 | 
			
		||||
                m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Action<,>)
 | 
			
		||||
            ) ?? throw new Exception();
 | 
			
		||||
 | 
			
		||||
        MethodInfo[] methodInfos = typeof(NetworkBase)
 | 
			
		||||
            .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);
 | 
			
		||||
 | 
			
		||||
        MethodInfo method = methodInfos
 | 
			
		||||
            .FirstOrDefault(
 | 
			
		||||
                m => m.Name == nameof(OnPacketArrived) &&
 | 
			
		||||
                m.IsGenericMethodDefinition
 | 
			
		||||
            ) ?? throw new Exception();
 | 
			
		||||
 | 
			
		||||
        foreach (var packetType in packetTypes)
 | 
			
		||||
        {
 | 
			
		||||
            var networkPacketType = typeof(NetworkPacket<>).MakeGenericType(packetType);
 | 
			
		||||
            MethodInfo genericSubscribe = subscribeMethod.MakeGenericMethod(networkPacketType, typeof(NetPeer));
 | 
			
		||||
 | 
			
		||||
            Action<object, NetPeer> handler = (packet, peer) =>
 | 
			
		||||
            {
 | 
			
		||||
                MethodInfo handlerMethod = method.MakeGenericMethod(packetType);
 | 
			
		||||
                handlerMethod.Invoke(this, [packet, peer]);
 | 
			
		||||
            };
 | 
			
		||||
            genericSubscribe.Invoke(netPacketProcessor, [handler]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnPacketArrived<T>(NetworkPacket<T> packet, NetPeer peer) where T : INetworkPacket
 | 
			
		||||
    {
 | 
			
		||||
        if (networkEntities.TryGetValue(packet.NetworkId, out INetworkEntity? entity))
 | 
			
		||||
            entity.ReceiveData(packet.Data);
 | 
			
		||||
 | 
			
		||||
        OnPacketReceived?.Invoke(this, packet);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnInitialize()
 | 
			
		||||
    {
 | 
			
		||||
        base.OnInitialize();
 | 
			
		||||
 | 
			
		||||
        networkEntityCollector = new(GameObject.GameManager);
 | 
			
		||||
        networkEntityCollector.OnCollected += OnNetworkEntityCollected;
 | 
			
		||||
        networkEntityCollector.OnRemoved += OnNetworkEntityRemoved;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnFinalize()
 | 
			
		||||
    {
 | 
			
		||||
        networkEntityCollector.OnCollected -= OnNetworkEntityCollected;
 | 
			
		||||
        networkEntityCollector.OnRemoved -= OnNetworkEntityRemoved;
 | 
			
		||||
        Stop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnNetworkEntityCollected(BehaviourCollector<INetworkEntity> sender, INetworkEntity behaviourCollected)
 | 
			
		||||
    {
 | 
			
		||||
        if (behaviourCollected.NetworkId != 0)
 | 
			
		||||
            networkEntities.Add(behaviourCollected.NetworkId, behaviourCollected);
 | 
			
		||||
 | 
			
		||||
        behaviourCollected.OnNetworkIdChanged += OnNetworkIdChanged;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnNetworkIdChanged(INetworkEntity sender, uint previousId)
 | 
			
		||||
    {
 | 
			
		||||
        if (networkEntities.TryGetValue(previousId, out INetworkEntity? networkEntity) && sender == networkEntity)
 | 
			
		||||
            networkEntities.Remove(previousId);
 | 
			
		||||
 | 
			
		||||
        networkEntities.Add(sender.NetworkId, sender);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnNetworkEntityRemoved(BehaviourCollector<INetworkEntity> sender, INetworkEntity behaviourRemoved)
 | 
			
		||||
    {
 | 
			
		||||
        networkEntities.Remove(behaviourRemoved.NetworkId);
 | 
			
		||||
        behaviourRemoved.OnNetworkIdChanged -= OnNetworkIdChanged;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void NetworkReceiveEvent(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            netPacketProcessor.ReadAllPackets(reader, peer);
 | 
			
		||||
        }
 | 
			
		||||
        catch { }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void PollEvents() => Manager.PollEvents();
 | 
			
		||||
    public void Stop() => Manager.Stop();
 | 
			
		||||
 | 
			
		||||
    protected override void OnUpdate() => PollEvents();
 | 
			
		||||
    protected override void OnFinalize() => Stop();
 | 
			
		||||
 | 
			
		||||
    private readonly Dictionary<string, Action<NetPacketReader, NetPeer>> callbacks = new(32);
 | 
			
		||||
 | 
			
		||||
    private void NetworkReceiveEvent(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod)
 | 
			
		||||
    {
 | 
			
		||||
        if (callbacks.TryGetValue(reader.GetString(), out var action))
 | 
			
		||||
            action?.Invoke(reader, peer);
 | 
			
		||||
 | 
			
		||||
        reader.Recycle();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void RegisterEntityListener(IEntity entity, Action<NetPacketReader, NetPeer> onDataReceived)
 | 
			
		||||
    {
 | 
			
		||||
        if (callbacks.ContainsKey(entity.Id))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        callbacks.Add(entity.Id, onDataReceived);
 | 
			
		||||
        entity.OnIdChanged += OnEntityIdChanged;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void UnregisterEntityListener(IEntity entity)
 | 
			
		||||
    {
 | 
			
		||||
        if (!callbacks.Remove(entity.Id))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        entity.OnIdChanged -= OnEntityIdChanged;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public NetDataWriter GetMessageWriter(IEntity entity)
 | 
			
		||||
    {
 | 
			
		||||
        NetDataWriter netDataWriter = GetEntityWriter(entity);
 | 
			
		||||
        netDataWriter.Put((int)MessageType.Message);
 | 
			
		||||
        return netDataWriter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public NetDataWriter GetEntityWriter(IEntity entity)
 | 
			
		||||
    {
 | 
			
		||||
        NetDataWriter netDataWriter = new();
 | 
			
		||||
        netDataWriter.Put(entity.Id);
 | 
			
		||||
        return netDataWriter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnEntityIdChanged(IEntity entity, string previousId)
 | 
			
		||||
    {
 | 
			
		||||
        var action = callbacks[previousId];
 | 
			
		||||
        callbacks.Remove(previousId);
 | 
			
		||||
        callbacks.Add(entity.Id, action);
 | 
			
		||||
    }
 | 
			
		||||
    public abstract void Send<T>(NetworkPacket<T> packet);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,5 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
using LiteNetLib;
 | 
			
		||||
 | 
			
		||||
using Syntriax.Engine.Core;
 | 
			
		||||
using Syntriax.Engine.Network.Abstract;
 | 
			
		||||
 | 
			
		||||
@@ -9,91 +7,46 @@ namespace Syntriax.Engine.Network;
 | 
			
		||||
 | 
			
		||||
public abstract class NetworkBehaviour : BehaviourOverride, INetworkBehaviour
 | 
			
		||||
{
 | 
			
		||||
    public int NetworkId { get; private set; } = 0;
 | 
			
		||||
    public event INetworkEntity.OnNetworkIdChangedDelegate? OnNetworkIdChanged = null;
 | 
			
		||||
 | 
			
		||||
    public bool LocalAssigned { get; private set; } = false;
 | 
			
		||||
    private uint _networkId = 0;
 | 
			
		||||
    public uint NetworkId
 | 
			
		||||
    {
 | 
			
		||||
        get => _networkId;
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            if (value == _networkId)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            uint previousNetworkId = _networkId;
 | 
			
		||||
            _networkId = value;
 | 
			
		||||
            OnNetworkIdChanged?.Invoke(this, previousNetworkId);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool IsServer { get; private set; } = false;
 | 
			
		||||
    public bool IsClient { get; private set; } = false;
 | 
			
		||||
 | 
			
		||||
    public INetworkCommunicator NetworkCommunicator { get; private set; } = null!;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    protected override void OnInitialize()
 | 
			
		||||
    {
 | 
			
		||||
        NetworkCommunicator = BehaviourController.GetBehaviourInParent<INetworkCommunicator>()
 | 
			
		||||
                              ?? GameObject.GameManager.FindBehaviour<INetworkCommunicator>()
 | 
			
		||||
                              ?? throw new Exception($"Could not find an {nameof(INetworkCommunicator)}.");
 | 
			
		||||
 | 
			
		||||
        NetworkCommunicator.RegisterEntityListener(this, OnMessageReceivedInternal);
 | 
			
		||||
 | 
			
		||||
        if (NetworkCommunicator is INetworkClient client)
 | 
			
		||||
        if (NetworkCommunicator is INetworkCommunicatorClient client)
 | 
			
		||||
        {
 | 
			
		||||
            IsClient = true;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        IsServer = true;
 | 
			
		||||
        LocalAssigned = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void ReleaseAssignment()
 | 
			
		||||
    {
 | 
			
		||||
        if (IsServer)
 | 
			
		||||
            return;
 | 
			
		||||
    public void SendData<T>(T data)
 | 
			
		||||
        => NetworkCommunicator.Send(new NetworkPacket<T>() { NetworkId = _networkId, Data = data });
 | 
			
		||||
 | 
			
		||||
        var netDataWriter = NetworkCommunicator.GetEntityWriter(this);
 | 
			
		||||
        netDataWriter.Put((int)MessageType.AssignmentRelease);
 | 
			
		||||
        NetworkCommunicator.Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void RequestAssignment()
 | 
			
		||||
    {
 | 
			
		||||
        if (IsServer)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        var netDataWriter = NetworkCommunicator.GetEntityWriter(this);
 | 
			
		||||
        netDataWriter.Put((int)MessageType.AssignmentRequest);
 | 
			
		||||
        NetworkCommunicator.Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected abstract void OnMessageReceived(NetPacketReader reader, NetPeer peer);
 | 
			
		||||
    private void OnMessageReceivedInternal(NetPacketReader reader, NetPeer peer)
 | 
			
		||||
    {
 | 
			
		||||
        MessageType messageType = (MessageType)reader.GetInt();
 | 
			
		||||
 | 
			
		||||
        if (IsServer)
 | 
			
		||||
        {
 | 
			
		||||
            switch (messageType)
 | 
			
		||||
            {
 | 
			
		||||
                case MessageType.Message: OnMessageReceived(reader, peer); break;
 | 
			
		||||
                case MessageType.AssignmentRequest:
 | 
			
		||||
                case MessageType.AssignmentRelease:
 | 
			
		||||
                    var netDataWriter = NetworkCommunicator.GetEntityWriter(this);
 | 
			
		||||
                    netDataWriter.Put((int)(messageType == MessageType.AssignmentRequest ? MessageType.Assigned : MessageType.Unassigned));
 | 
			
		||||
                    peer.Send(netDataWriter, DeliveryMethod.ReliableOrdered);
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch (messageType)
 | 
			
		||||
        {
 | 
			
		||||
            case MessageType.Message: OnMessageReceived(reader, peer); break;
 | 
			
		||||
            case MessageType.Assigned: LocalAssigned = true; break;
 | 
			
		||||
            case MessageType.Unassigned: LocalAssigned = false; break;
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal enum MessageType
 | 
			
		||||
{
 | 
			
		||||
    Message,
 | 
			
		||||
    AssignmentRequest,
 | 
			
		||||
    AssignmentRelease,
 | 
			
		||||
    Assigned,
 | 
			
		||||
    Unassigned,
 | 
			
		||||
    public abstract void ReceiveData<T>(T data);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,23 @@
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Syntriax.Engine.Network.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Network;
 | 
			
		||||
 | 
			
		||||
public class NetworkClient : NetworkBase, INetworkClient
 | 
			
		||||
public class NetworkClient : NetworkBase, INetworkCommunicatorClient
 | 
			
		||||
{
 | 
			
		||||
    private readonly NetDataWriter netDataWriter = new();
 | 
			
		||||
 | 
			
		||||
    public void Connect(string address, int port, string? password = null)
 | 
			
		||||
    {
 | 
			
		||||
        Manager.Start();
 | 
			
		||||
        Manager.Connect(address, port, password ?? string.Empty);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Send<T>(NetworkPacket<T> packet)
 | 
			
		||||
    {
 | 
			
		||||
        netDataWriter.Reset();
 | 
			
		||||
        netPacketProcessor.Write(netDataWriter, packet);
 | 
			
		||||
        Manager.FirstPeer.Send(netDataWriter, LiteNetLib.DeliveryMethod.ReliableOrdered);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ public static class NetworkExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static Vector2D GetVector2D(this NetPacketReader reader)
 | 
			
		||||
        => new(reader.GetFloat(), reader.GetFloat());
 | 
			
		||||
 | 
			
		||||
    public static void GetVector2D(this NetPacketReader reader, out Vector2D vector2D)
 | 
			
		||||
        => vector2D = new(reader.GetFloat(), reader.GetFloat());
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
using LiteNetLib;
 | 
			
		||||
using Syntriax.Engine.Core;
 | 
			
		||||
 | 
			
		||||
using Syntriax.Engine.Network.Abstract;
 | 
			
		||||
@@ -9,23 +8,23 @@ public class NetworkManager : NetworkBehaviour, INetworkManager
 | 
			
		||||
{
 | 
			
		||||
    private BehaviourCollector<INetworkEntity> entities = null!;
 | 
			
		||||
 | 
			
		||||
    private static int networkIdIndex = 0;
 | 
			
		||||
    private static uint networkIdIndex = 0;
 | 
			
		||||
 | 
			
		||||
    protected override void OnInitialize()
 | 
			
		||||
    {
 | 
			
		||||
        base.OnInitialize();
 | 
			
		||||
 | 
			
		||||
        ((INetworkEntity)this).SetNetworkId(networkIdIndex++);
 | 
			
		||||
        NetworkId = networkIdIndex++;
 | 
			
		||||
 | 
			
		||||
        entities = new(GameObject.GameManager);
 | 
			
		||||
        foreach (var entity in entities)
 | 
			
		||||
            entity.SetNetworkId(networkIdIndex++);
 | 
			
		||||
            entity.NetworkId = networkIdIndex++;
 | 
			
		||||
 | 
			
		||||
        entities.OnCollected += OnCollected;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnCollected(BehaviourCollector<INetworkEntity> collector, INetworkEntity entity)
 | 
			
		||||
        => entity.SetNetworkId(networkIdIndex++);
 | 
			
		||||
        => entity.NetworkId = networkIdIndex++;
 | 
			
		||||
 | 
			
		||||
    protected override void OnMessageReceived(NetPacketReader reader, NetPeer peer) { }
 | 
			
		||||
    public override void ReceiveData<T>(T data) { }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,35 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Syntriax.Engine.Network.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Network;
 | 
			
		||||
 | 
			
		||||
public class NetworkServer : NetworkBase, INetworkServer
 | 
			
		||||
public class NetworkServer : NetworkBase, INetworkCommunicatorServer
 | 
			
		||||
{
 | 
			
		||||
    public string Password { get; private set; } = string.Empty;
 | 
			
		||||
    public int MaxConnectionCount { get; private set; } = 0;
 | 
			
		||||
    public int MaxConnectionCount { get; private set; } = 2;
 | 
			
		||||
    public int Port { get; private set; } = 8888;
 | 
			
		||||
 | 
			
		||||
    public NetworkServer() : base()
 | 
			
		||||
    private readonly NetDataWriter netDataWriter = new();
 | 
			
		||||
 | 
			
		||||
    public NetworkServer() : this(8888, 2) { }
 | 
			
		||||
    public NetworkServer(int port, int maxConnectionCount) : base()
 | 
			
		||||
    {
 | 
			
		||||
        MaxConnectionCount = maxConnectionCount;
 | 
			
		||||
        Port = port;
 | 
			
		||||
 | 
			
		||||
        Listener.ConnectionRequestEvent += request =>
 | 
			
		||||
        {
 | 
			
		||||
            if (Manager.ConnectedPeersCount < MaxConnectionCount)
 | 
			
		||||
            if (Manager.ConnectedPeersCount < maxConnectionCount)
 | 
			
		||||
                request.AcceptIfKey(Password);
 | 
			
		||||
            else
 | 
			
		||||
                request.Reject();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        OnPacketReceived += ServerOnPacketReceived;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Start(int port, int maxConnectionCount, string? password = null)
 | 
			
		||||
@@ -27,4 +40,28 @@ public class NetworkServer : NetworkBase, INetworkServer
 | 
			
		||||
 | 
			
		||||
        Manager.Start(port);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void Send<T>(NetworkPacket<T> packet)
 | 
			
		||||
    {
 | 
			
		||||
        netDataWriter.Reset();
 | 
			
		||||
        netPacketProcessor.Write(netDataWriter, packet);
 | 
			
		||||
        Manager.SendToAll(netDataWriter, LiteNetLib.DeliveryMethod.ReliableOrdered);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void ServerOnPacketReceived(INetworkCommunicator sender, object packet)
 | 
			
		||||
    {
 | 
			
		||||
        MethodInfo[] methodInfos = GetType()
 | 
			
		||||
            .GetMethods(BindingFlags.Public | BindingFlags.Instance);
 | 
			
		||||
 | 
			
		||||
        MethodInfo method = methodInfos
 | 
			
		||||
            .FirstOrDefault(
 | 
			
		||||
                m => m.Name == nameof(Send) && m.IsGenericMethod
 | 
			
		||||
            ) ?? throw new Exception();
 | 
			
		||||
 | 
			
		||||
        Type typeArguments = packet.GetType().GetGenericArguments()[0];
 | 
			
		||||
 | 
			
		||||
        MethodInfo methodInfo = method.MakeGenericMethod(typeArguments);
 | 
			
		||||
 | 
			
		||||
        methodInfo.Invoke(this, [packet]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user