30 Commits

Author SHA1 Message Date
1b1d9dcdac feat: Paddle Data Send 2024-07-14 23:55:06 +03:00
2565617ff9 chore: Cleanup 2024-07-14 23:22:30 +03:00
01668c7be3 feat: Added Networked Inputs to GameObjects 2024-02-13 11:54:01 +03:00
cafbc55780 feat: Networked Keyboard Inputs Finished 2024-02-13 11:16:43 +03:00
ea3c4a2d2a refactor: Removed Network Code From Paddle 2024-02-13 11:15:27 +03:00
89b756a615 refactor: Nullable Keyboard Input Actions 2024-02-13 11:08:53 +03:00
6af84bcb6d feat: Networked Keyboard Inputs 2024-02-12 17:53:51 +03:00
3c1528d879 wip: Network 2024-02-12 16:35:24 +03:00
6f3e7b4ae5 wip: INetworkEntity & INetworkManager 2024-02-09 11:50:16 +03:00
86ef57fb62 refactor: Folder and Namespace Restructure of Network Code 2024-02-09 10:00:59 +03:00
6c326b1fc6 chore: Engine Update 2024-02-09 09:44:25 +03:00
b8b10de08a wip: INetworkBehaviour 2024-02-08 17:59:04 +03:00
edd2dd8511 fix: Network Problems on Ball Synchronization 2024-02-05 13:32:27 +03:00
70ac012a83 refactor: ContentLoaderCacher Action 2024-02-05 13:23:59 +03:00
d5a904fe8f feat: Ball Hit Sound Effect 2024-02-05 13:19:40 +03:00
b1582ab5c2 feat: IMonoGameContentLoader 2024-02-05 13:18:33 +03:00
8a0a0289f9 chore: Engine Update 2024-02-05 12:20:20 +03:00
776d029e7e refactor: Code Readability 2024-02-05 12:09:32 +03:00
5afb8b69bf refactor: Window Title According to Network State 2024-02-04 12:23:41 +03:00
65073ec1ca fix: Pong Manager not Being Able to Send Data Over Network 2024-02-04 12:23:25 +03:00
0d62e5c986 chore: .vscode Location 2024-02-04 12:22:17 +03:00
fe5a08840c feat: Test Network 2024-02-02 17:44:51 +03:00
1c7b1cb78a feat: Entity Messaging 2024-02-02 17:44:20 +03:00
78010c266e feat: NetworkExtensions for Vector2D Communication 2024-02-02 17:44:11 +03:00
ef8b04648c feat: NetworkClient & Server to IBehaviour 2024-02-02 17:44:00 +03:00
d4c57c0153 feat: Network Interfaces 2024-02-02 17:43:49 +03:00
0e198afeab fix: Invalid Namespace 2024-02-02 09:13:51 +03:00
de267a9d0f feat: NetworkServer 2024-02-01 23:38:39 +03:00
977a2abdd7 feat: NetworkClient 2024-02-01 23:38:35 +03:00
91e88bbff8 feat: Added LiteNetLib 2024-02-01 22:14:59 +03:00
27 changed files with 464 additions and 296 deletions

23
.vscode/launch.json vendored
View File

@@ -5,33 +5,14 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "Start Client", "name": ".NET Core Launch (console)",
"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", "type": "coreclr",
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path. // If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/Game/bin/Debug/net8.0/Game.dll", "program": "${workspaceFolder}/Game/bin/Debug/net8.0/Game.dll",
"args": ["server"], "args": ["server"],
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}/Game",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole", "console": "internalConsole",
"stopAtEntry": false "stopAtEntry": false

5
.vscode/tasks.json vendored
View File

@@ -9,7 +9,10 @@
"kind": "build", "kind": "build",
"isDefault": true "isDefault": true
}, },
"label": "build" "label": "build",
"options": {
"cwd": "${workspaceFolder}/Game"
}
} }
] ]
} }

2
Engine

Submodule Engine updated: ef21cdf213...2cf6135063

View File

@@ -0,0 +1,8 @@
using Microsoft.Xna.Framework.Content;
namespace Pong.Behaviours;
public interface IMonoGameContentLoader
{
void LoadContent(ContentManager content);
}

View File

@@ -1,12 +1,19 @@
using System; using System;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using LiteNetLib;
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
using Syntriax.Engine.Network;
using Syntriax.Engine.Network.Abstract;
using Syntriax.Engine.Physics2D; using Syntriax.Engine.Physics2D;
using Syntriax.Engine.Physics2D.Abstract; using Syntriax.Engine.Physics2D.Abstract;
namespace Pong.Behaviours; namespace Pong.Behaviours;
public class BallBehaviour : BehaviourOverride public class BallBehaviour : NetworkBehaviour, IMonoGameContentLoader
{ {
public Vector2D StartDirection { get; private set; } = Vector2D.Zero; public Vector2D StartDirection { get; private set; } = Vector2D.Zero;
public float Speed { get; set; } = 500f; public float Speed { get; set; } = 500f;
@@ -14,6 +21,13 @@ public class BallBehaviour : BehaviourOverride
private readonly Random random = new(); private readonly Random random = new();
private IRigidBody2D rigidBody = null!; 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() protected override void OnFirstActiveFrame()
{ {
@@ -21,11 +35,13 @@ public class BallBehaviour : BehaviourOverride
throw new Exception($"{nameof(IRigidBody2D)} is missing on {GameObject.Name}."); throw new Exception($"{nameof(IRigidBody2D)} is missing on {GameObject.Name}.");
if (!BehaviourController.TryGetBehaviour(out ICollider2D? foundCollider)) if (!BehaviourController.TryGetBehaviour(out ICollider2D? foundCollider))
throw new Exception($"{nameof(ICollider2D)} is missing on {GameObject.Name}."); 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; foundCollider.OnCollisionDetected += OnCollisionDetected;
rigidBody = foundRigidBody; rigidBody = foundRigidBody;
communicator = foundCommunicator;
if (GameObject.GameManager.TryFindBehaviour(out PongManagerBehaviour? pongManager)) if (GameObject.GameManager.TryFindBehaviour(out PongManagerBehaviour? pongManager))
{ {
pongManager.OnReset += ResetBall; pongManager.OnReset += ResetBall;
@@ -45,6 +61,8 @@ public class BallBehaviour : BehaviourOverride
StateEnable.Enabled = true; StateEnable.Enabled = true;
BehaviourController.GameObject.Transform.Position = Vector2D.Zero; BehaviourController.GameObject.Transform.Position = Vector2D.Zero;
rigidBody.Velocity = GetRandomDirection() * Speed; rigidBody.Velocity = GetRandomDirection() * Speed;
SendBallData();
} }
private Vector2D GetRandomDirection() private Vector2D GetRandomDirection()
@@ -70,6 +88,27 @@ public class BallBehaviour : BehaviourOverride
rigidBody.Velocity = information.Left.Transform.Position.FromTo(information.Right.Transform.Position).Normalized * rigidBody.Velocity.Magnitude; rigidBody.Velocity = information.Left.Transform.Position.FromTo(information.Right.Transform.Position).Normalized * rigidBody.Velocity.Magnitude;
else else
rigidBody.Velocity = rigidBody.Velocity.Reflect(information.Normal); 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() { } public BallBehaviour() { }

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
@@ -10,8 +9,8 @@ namespace Pong.Behaviours;
public class KeyboardInputsBehaviour : BehaviourOverride, IButtonInputs<Keys> 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>?> OnPressed = new(256);
private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>> OnReleased = new(256); private readonly Dictionary<Keys, Action<IButtonInputs<Keys>, Keys>?> OnReleased = new(256);
private int cachePressedCurrentlyCount = 0; private int cachePressedCurrentlyCount = 0;
private readonly Keys[] cachePressedCurrently = new Keys[256]; private readonly Keys[] cachePressedCurrently = new Keys[256];
@@ -69,7 +68,7 @@ public class KeyboardInputsBehaviour : BehaviourOverride, IButtonInputs<Keys>
if (WasPressed(currentlyPressedKey)) if (WasPressed(currentlyPressedKey))
continue; continue;
action.Invoke(this, currentlyPressedKey); action?.Invoke(this, currentlyPressedKey);
} }
for (int i = 0; i < cachePressedPreviouslyCount; i++) for (int i = 0; i < cachePressedPreviouslyCount; i++)
@@ -82,7 +81,7 @@ public class KeyboardInputsBehaviour : BehaviourOverride, IButtonInputs<Keys>
if (IsPressed(previouslyPressedKey)) if (IsPressed(previouslyPressedKey))
continue; continue;
action.Invoke(this, previouslyPressedKey); action?.Invoke(this, previouslyPressedKey);
} }
Array.Copy(cachePressedCurrently, cachePressedPreviously, cachePressedCurrentlyCount); Array.Copy(cachePressedCurrently, cachePressedPreviously, cachePressedCurrentlyCount);

View File

@@ -8,9 +8,9 @@ namespace Pong.Behaviours;
public class MonoGameCamera2DBehaviour(GraphicsDeviceManager Graphics) : BehaviourOverride, ICamera2D public class MonoGameCamera2DBehaviour(GraphicsDeviceManager Graphics) : BehaviourOverride, ICamera2D
{ {
public event OnMatrixTransformChangedDelegate? OnMatrixTransformChanged = null; public System.Action<MonoGameCamera2DBehaviour>? OnMatrixTransformChanged { get; set; } = null;
public event OnViewportChangedDelegate? OnViewportChanged = null; public System.Action<MonoGameCamera2DBehaviour>? OnViewportChanged { get; set; } = null;
public event OnZoomChangedDelegate? OnZoomChanged = null; public System.Action<MonoGameCamera2DBehaviour>? OnZoomChanged { get; set; } = null;
private Matrix _matrixTransform = Matrix.Identity; private Matrix _matrixTransform = Matrix.Identity;
@@ -72,7 +72,7 @@ public class MonoGameCamera2DBehaviour(GraphicsDeviceManager Graphics) : Behavio
set => Transform.Rotation = value; set => Transform.Rotation = value;
} }
public event IAssignableTransform.OnTransformAssignedDelegate? OnTransformAssigned { add => GameObject.OnTransformAssigned += value; remove => GameObject.OnTransformAssigned -= value; } System.Action<IAssignableTransform>? IAssignableTransform.OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; }
ITransform IAssignableTransform.Transform => GameObject.Transform; ITransform IAssignableTransform.Transform => GameObject.Transform;
bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform); bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform);
@@ -99,8 +99,4 @@ public class MonoGameCamera2DBehaviour(GraphicsDeviceManager Graphics) : Behavio
Matrix.CreateScale(Zoom) * Matrix.CreateScale(Zoom) *
Matrix.CreateTranslation(new Vector3(_viewport.Width * .5f, _viewport.Height * .5f, 0f)); 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);
} }

View File

@@ -0,0 +1,154 @@
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);
}
}

View File

@@ -1,7 +1,10 @@
using System; using System;
using LiteNetLib.Utils;
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
using LiteNetLib;
using LiteNetLib.Utils;
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
using Syntriax.Engine.Input; using Syntriax.Engine.Input;
using Syntriax.Engine.Network; using Syntriax.Engine.Network;
@@ -9,7 +12,7 @@ using Syntriax.Engine.Network.Abstract;
namespace Pong.Behaviours; namespace Pong.Behaviours;
public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Speed) : NetworkBehaviour public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Speed) : BehaviourOverride
{ {
private Keys Up { get; } = Up; private Keys Up { get; } = Up;
private Keys Down { get; } = Down; private Keys Down { get; } = Down;
@@ -21,6 +24,7 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp
private bool isDownPressed = false; private bool isDownPressed = false;
private IButtonInputs<Keys> inputs = null!; private IButtonInputs<Keys> inputs = null!;
private INetworkCommunicator communicator = null!;
protected override void OnUpdate() protected override void OnUpdate()
{ {
@@ -28,27 +32,33 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp
return; return;
if (isUpPressed) if (isUpPressed)
Move(Vector2D.Up); MovePaddle(Vector2D.Up * (float)Time.Elapsed.TotalSeconds * Speed);
else if (isDownPressed) else if (isDownPressed)
Move(-Vector2D.Up); MovePaddle(-Vector2D.Up * (float)Time.Elapsed.TotalSeconds * Speed);
} }
private void Move(Vector2D vectorToMove) private void MovePaddle(Vector2D vectorToAdd)
{ {
GameObject.Transform.Position = GameObject.Transform.Position + vectorToMove * (float)Time.Elapsed.TotalSeconds * Speed; GameObject.Transform.Position += vectorToAdd;
GameObject.Transform.Position = new Vector2D(GameObject.Transform.Position.X, MathF.Max(MathF.Min(GameObject.Transform.Position.Y, High), Low)); GameObject.Transform.Position = new Vector2D(GameObject.Transform.Position.X, MathF.Max(MathF.Min(GameObject.Transform.Position.Y, High), Low));
} }
protected override void OnFirstActiveFrame() protected override void OnFirstActiveFrame()
{ {
if (!BehaviourController.TryGetBehaviour<IButtonInputs<Keys>>(out var behaviourResult)) BehaviourController.TryGetBehaviour<IButtonInputs<Keys>>(out var behaviourResult);
inputs = behaviourResult ?? BehaviourController.AddBehaviour<KeyboardInputsBehaviour>(); 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.");
inputs.RegisterOnPress(Up, OnUpPressed); inputs.RegisterOnPress(Up, OnUpPressed);
inputs.RegisterOnRelease(Up, OnUpReleased); inputs.RegisterOnRelease(Up, OnUpReleased);
inputs.RegisterOnPress(Down, OnDownPressed); inputs.RegisterOnPress(Down, OnDownPressed);
inputs.RegisterOnRelease(Down, OnDownReleased); inputs.RegisterOnRelease(Down, OnDownReleased);
communicator = foundCommunicator;
communicator.RegisterEntityListener(this, OnDataReceived);
} }
protected override void OnFinalize() protected override void OnFinalize()
@@ -60,40 +70,32 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp
inputs.UnregisterOnRelease(Down, OnDownReleased); inputs.UnregisterOnRelease(Down, OnDownReleased);
} }
private void OnUpPressed(IButtonInputs<Keys> inputs, Keys keys) { isUpPressed = true; SendData(new PaddleInputs() { IsUpPressed = isUpPressed, IsDownPressed = isDownPressed, PositionY = Transform.Position.Y }); } private void UpdateNetwork()
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)
{ {
if (data is not PaddleInputs paddleInputs) NetDataWriter netDataWriter = communicator.GetEntityWriter(this);
return; netDataWriter.Put(isUpPressed);
netDataWriter.Put(isDownPressed);
Transform.Position = new(Transform.Position.X, paddleInputs.PositionY); netDataWriter.Put(Transform.Position);
isUpPressed = paddleInputs.IsUpPressed; communicator.Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered);
isDownPressed = paddleInputs.IsDownPressed;
} }
[System.Serializable] private void OnDataReceived(NetPacketReader reader, NetPeer peer)
public struct PaddleInputs : INetworkPacket
{ {
public float PositionY { get; set; } if (communicator is INetworkServer server)
public bool IsUpPressed { get; set; }
public bool IsDownPressed { get; set; }
public void Deserialize(NetDataReader reader)
{ {
PositionY = reader.GetFloat(); reader.Get(out isUpPressed);
IsUpPressed = reader.GetBool(); reader.Get(out isDownPressed);
IsDownPressed = reader.GetBool(); }
else
{
reader.Get(out isUpPressed);
reader.Get(out isDownPressed);
Transform.Position = reader.GetVector2D();
}
} }
public void Serialize(NetDataWriter writer) private void OnUpPressed(IButtonInputs<Keys> inputs, Keys keys) { isUpPressed = true; UpdateNetwork(); }
{ private void OnUpReleased(IButtonInputs<Keys> inputs, Keys keys) { isUpPressed = false; UpdateNetwork(); }
writer.Put(PositionY); private void OnDownPressed(IButtonInputs<Keys> inputs, Keys keys) { isDownPressed = true; UpdateNetwork(); }
writer.Put(IsUpPressed); private void OnDownReleased(IButtonInputs<Keys> inputs, Keys keys) { isDownPressed = false; UpdateNetwork(); }
writer.Put(IsDownPressed);
}
}
} }

View File

@@ -2,16 +2,23 @@ using System;
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
using LiteNetLib;
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
using Syntriax.Engine.Network;
using Syntriax.Engine.Network.Abstract;
namespace Pong.Behaviours; namespace Pong.Behaviours;
public class PongManagerBehaviour : BehaviourOverride public class PongManagerBehaviour : NetworkBehaviour
{ {
public Action<PongManagerBehaviour>? OnReset { get; set; } = null; public Action<PongManagerBehaviour>? OnReset { get; set; } = null;
public Action<PongManagerBehaviour>? OnFinished { get; set; } = null; public Action<PongManagerBehaviour>? OnFinished { get; set; } = null;
public Action<PongManagerBehaviour>? OnScoresUpdated { get; set; } = null;
public Action<PongManagerBehaviour>? OnScored { get; set; } = null; public Action<PongManagerBehaviour>? OnScored { get; set; } = null;
private INetworkCommunicator communicator = null!;
public int ScoreLeft { get; private set; } = 0; public int ScoreLeft { get; private set; } = 0;
public int ScoreRight { get; private set; } = 0; public int ScoreRight { get; private set; } = 0;
public int ScoreSum => ScoreLeft + ScoreRight; public int ScoreSum => ScoreLeft + ScoreRight;
@@ -28,30 +35,43 @@ public class PongManagerBehaviour : BehaviourOverride
if (!BehaviourController.TryGetBehaviour(out buttonInputs)) if (!BehaviourController.TryGetBehaviour(out buttonInputs))
buttonInputs = BehaviourController.AddBehaviour<KeyboardInputsBehaviour>(); buttonInputs = BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
buttonInputs.RegisterOnRelease(Keys.Space, (_, _1) => Reset()); 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() public void ScoreToLeft()
{ {
ScoreLeft++; ScoreLeft++;
OnScoresUpdated?.Invoke(this);
OnScored?.Invoke(this); OnScored?.Invoke(this);
SendData();
CheckFinish(); CheckFinish();
} }
public void ScoreToRight() public void ScoreToRight()
{ {
ScoreRight++; ScoreRight++;
OnScoresUpdated?.Invoke(this);
OnScored?.Invoke(this); OnScored?.Invoke(this);
SendData();
CheckFinish(); CheckFinish();
} }
public void Reset() public void Reset()
{ {
ScoreLeft = ScoreRight = 0; ScoreLeft = ScoreRight = 0;
OnScoresUpdated?.Invoke(this);
OnReset?.Invoke(this); OnReset?.Invoke(this);
SendData();
} }
private void CheckFinish() private void CheckFinish()
@@ -61,4 +81,24 @@ public class PongManagerBehaviour : BehaviourOverride
if (ScoreLeft > halfwayScore || ScoreRight > halfwayScore) if (ScoreLeft > halfwayScore || ScoreRight > halfwayScore)
OnFinished?.Invoke(this); 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();
}
} }

View File

@@ -14,8 +14,7 @@ public class TextScoreBehaviour : TextBehaviour
if (!GameObject.GameManager.TryFindBehaviour(out pongManager)) if (!GameObject.GameManager.TryFindBehaviour(out pongManager))
return; return;
pongManager.OnScored += UpdateScores; pongManager.OnScoresUpdated += UpdateScores;
pongManager.OnReset += UpdateScores;
UpdateScores(pongManager); UpdateScores(pongManager);
} }

View File

@@ -13,6 +13,12 @@
#---------------------------------- Content ---------------------------------# #---------------------------------- Content ---------------------------------#
#begin Hit.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:Hit.wav
#begin UbuntuMono.spritefont #begin UbuntuMono.spritefont
/importer:FontDescriptionImporter /importer:FontDescriptionImporter
/processor:FontDescriptionProcessor /processor:FontDescriptionProcessor

BIN
Game/Content/Hit.wav Normal file

Binary file not shown.

View File

@@ -27,6 +27,7 @@ public class GamePong : Game
private GameManager gameManager = null!; private GameManager gameManager = null!;
private BehaviourCollector<IDisplayableSprite> displayableCollector = null!; private BehaviourCollector<IDisplayableSprite> displayableCollector = null!;
private BehaviourCollector<IDisplayableShape> displayableShapeCollector = null!; private BehaviourCollector<IDisplayableShape> displayableShapeCollector = null!;
private BehaviourCollector<IMonoGameContentLoader> monoGameContentLoaderCollector = null!;
private MonoGameCamera2DBehaviour cameraBehaviour = null!; private MonoGameCamera2DBehaviour cameraBehaviour = null!;
private PongManagerBehaviour pongManager = null!; private PongManagerBehaviour pongManager = null!;
@@ -52,8 +53,11 @@ public class GamePong : Game
gameManager = new(); gameManager = new();
displayableCollector = new(gameManager); displayableCollector = new(gameManager);
displayableShapeCollector = new(gameManager); displayableShapeCollector = new(gameManager);
monoGameContentLoaderCollector = new(gameManager);
physicsEngine = new PhysicsEngine2DCollector(gameManager) { IterationPerStep = 3 }; physicsEngine = new PhysicsEngine2DCollector(gameManager) { IterationPerStep = 3 };
monoGameContentLoaderCollector.OnCollected += (_, cached) => cached.LoadContent(Content);
gameManager.Initialize(); gameManager.Initialize();
base.Initialize(); base.Initialize();
@@ -87,9 +91,6 @@ public class GamePong : Game
Window.Title = $"Pong - Client -> 127.0.0.1"; 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"); ; IGameObject gameObjectCamera = gameManager.InstantiateGameObject().SetGameObject("Camera"); ;
@@ -116,12 +117,14 @@ public class GamePong : Game
IGameObject gameObjectLeftPaddle = gameManager.InstantiateGameObject().SetGameObject("Left Paddle"); IGameObject gameObjectLeftPaddle = gameManager.InstantiateGameObject().SetGameObject("Left Paddle");
gameObjectLeftPaddle.Transform.SetTransform(position: new Vector2D(-468f, 0f), scale: new Vector2D(15f, 60f)); 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<PaddleBehaviour>(Keys.W, Keys.S, 228f, -228f, 400f).Id = "leftPaddle";
gameObjectLeftPaddle.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.Box); gameObjectLeftPaddle.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.Box);
gameObjectLeftPaddle.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true; gameObjectLeftPaddle.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;
IGameObject gameObjectRightPaddle = gameManager.InstantiateGameObject().SetGameObject("Right Paddle"); IGameObject gameObjectRightPaddle = gameManager.InstantiateGameObject().SetGameObject("Right Paddle");
gameObjectRightPaddle.Transform.SetTransform(position: new Vector2D(468f, 0f), scale: new Vector2D(15f, 60f)); 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<PaddleBehaviour>(Keys.Up, Keys.Down, 228f, -228f, 400f).Id = "rightPaddle";
gameObjectRightPaddle.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.Box); gameObjectRightPaddle.BehaviourController.AddBehaviour<ShapeBehaviour>(Shape.Box);
gameObjectRightPaddle.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true; gameObjectRightPaddle.BehaviourController.AddBehaviour<RigidBody2D>().IsStatic = true;

View File

@@ -2,10 +2,17 @@ using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Network.Abstract; namespace Syntriax.Engine.Network.Abstract;
public interface INetworkBehaviour : IBehaviour, INetworkEntity public interface INetworkBehaviour : IBehaviour
{ {
int NetworkId { get; }
bool LocalAssigned { get; }
bool IsServer { get; } bool IsServer { get; }
bool IsClient { get; } bool IsClient { get; }
INetworkCommunicator NetworkCommunicator { get; } INetworkCommunicator NetworkCommunicator { get; }
public void RequestAssignment();
public void ReleaseAssignment();
} }

View File

@@ -1,6 +1,6 @@
namespace Syntriax.Engine.Network.Abstract; namespace Syntriax.Engine.Network.Abstract;
public interface INetworkCommunicatorClient : INetworkCommunicator public interface INetworkClient : INetworkCommunicator
{ {
void Connect(string address, int port, string? password = null); void Connect(string address, int port, string? password = null);
} }

View File

@@ -1,18 +1,22 @@
using System;
using LiteNetLib; using LiteNetLib;
using LiteNetLib.Utils;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Network.Abstract; namespace Syntriax.Engine.Network.Abstract;
public interface INetworkCommunicator public interface INetworkCommunicator
{ {
event OnPacketReceivedDelegate? OnPacketReceived;
EventBasedNetListener Listener { get; } EventBasedNetListener Listener { get; }
NetManager Manager { get; } NetManager Manager { get; }
void PollEvents(); void PollEvents();
void Stop(); void Stop();
void Send<T>(NetworkPacket<T> Data); void RegisterEntityListener(IEntity entity, Action<NetPacketReader, NetPeer> onDataReceived);
void UnregisterEntityListener(IEntity entity);
delegate void OnPacketReceivedDelegate(INetworkCommunicator sender, object packet); NetDataWriter GetEntityWriter(IEntity entity);
NetDataWriter GetMessageWriter(IEntity entity);
} }

View File

@@ -1,12 +1,11 @@
using System;
namespace Syntriax.Engine.Network.Abstract; namespace Syntriax.Engine.Network.Abstract;
public interface INetworkEntity internal interface INetworkEntity
{ {
event OnNetworkIdChangedDelegate? OnNetworkIdChanged; Action<INetworkEntity, int> OnNetworkIdChanged { get; set; }
int NetworkId { get; set; }
uint NetworkId { get; set; } void SetNetworkId(int id);
void ReceiveData<T>(T data);
delegate void OnNetworkIdChangedDelegate(INetworkEntity sender, uint previousId);
} }

View File

@@ -1,5 +0,0 @@
using LiteNetLib.Utils;
namespace Syntriax.Engine.Network.Abstract;
public interface INetworkPacket : INetSerializable;

View File

@@ -1,6 +1,6 @@
namespace Syntriax.Engine.Network.Abstract; namespace Syntriax.Engine.Network.Abstract;
public interface INetworkCommunicatorServer : INetworkCommunicator public interface INetworkServer : INetworkCommunicator
{ {
string Password { get; } string Password { get; }
int MaxConnectionCount { get; } int MaxConnectionCount { get; }

View File

@@ -1,7 +0,0 @@
namespace Syntriax.Engine.Network.Abstract;
public class NetworkPacket<T>
{
public uint NetworkId { get; set; } = 0;
public T Data { get; set; } = default!;
}

View File

@@ -1,25 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using LiteNetLib; using LiteNetLib;
using LiteNetLib.Utils; using LiteNetLib.Utils;
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Network.Abstract; using Syntriax.Engine.Network.Abstract;
namespace Syntriax.Engine.Network; namespace Syntriax.Engine.Network;
public abstract class NetworkBase : BehaviourOverride, INetworkCommunicator 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 EventBasedNetListener Listener { get; private set; } = null!;
public NetManager Manager { get; private set; } = null!; public NetManager Manager { get; private set; } = null!;
@@ -31,110 +23,59 @@ public abstract class NetworkBase : BehaviourOverride, INetworkCommunicator
Manager = new NetManager(Listener); Manager = new NetManager(Listener);
Listener.NetworkReceiveEvent += NetworkReceiveEvent; 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 PollEvents() => Manager.PollEvents();
public void Stop() => Manager.Stop(); public void Stop() => Manager.Stop();
protected override void OnUpdate() => PollEvents(); protected override void OnUpdate() => PollEvents();
protected override void OnFinalize() => Stop();
public abstract void Send<T>(NetworkPacket<T> packet); 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);
}
} }

View File

@@ -1,5 +1,7 @@
using System; using System;
using LiteNetLib;
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
using Syntriax.Engine.Network.Abstract; using Syntriax.Engine.Network.Abstract;
@@ -7,46 +9,91 @@ namespace Syntriax.Engine.Network;
public abstract class NetworkBehaviour : BehaviourOverride, INetworkBehaviour public abstract class NetworkBehaviour : BehaviourOverride, INetworkBehaviour
{ {
public event INetworkEntity.OnNetworkIdChangedDelegate? OnNetworkIdChanged = null; public int NetworkId { get; private set; } = 0;
private uint _networkId = 0; public bool LocalAssigned { get; private set; } = false;
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 IsServer { get; private set; } = false;
public bool IsClient { get; private set; } = false; public bool IsClient { get; private set; } = false;
public INetworkCommunicator NetworkCommunicator { get; private set; } = null!; public INetworkCommunicator NetworkCommunicator { get; private set; } = null!;
protected override void OnInitialize() protected override void OnInitialize()
{ {
NetworkCommunicator = BehaviourController.GetBehaviourInParent<INetworkCommunicator>() NetworkCommunicator = BehaviourController.GetBehaviourInParent<INetworkCommunicator>()
?? GameObject.GameManager.FindBehaviour<INetworkCommunicator>() ?? GameObject.GameManager.FindBehaviour<INetworkCommunicator>()
?? throw new Exception($"Could not find an {nameof(INetworkCommunicator)}."); ?? throw new Exception($"Could not find an {nameof(INetworkCommunicator)}.");
if (NetworkCommunicator is INetworkCommunicatorClient client) NetworkCommunicator.RegisterEntityListener(this, OnMessageReceivedInternal);
if (NetworkCommunicator is INetworkClient client)
{ {
IsClient = true; IsClient = true;
return; return;
} }
IsServer = true; IsServer = true;
LocalAssigned = true;
} }
public void SendData<T>(T data) public void ReleaseAssignment()
=> NetworkCommunicator.Send(new NetworkPacket<T>() { NetworkId = _networkId, Data = data }); {
if (IsServer)
return;
public abstract void ReceiveData<T>(T 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,
} }

View File

@@ -1,23 +1,12 @@
using LiteNetLib.Utils;
using Syntriax.Engine.Network.Abstract; using Syntriax.Engine.Network.Abstract;
namespace Syntriax.Engine.Network; namespace Syntriax.Engine.Network;
public class NetworkClient : NetworkBase, INetworkCommunicatorClient public class NetworkClient : NetworkBase, INetworkClient
{ {
private readonly NetDataWriter netDataWriter = new();
public void Connect(string address, int port, string? password = null) public void Connect(string address, int port, string? password = null)
{ {
Manager.Start(); Manager.Start();
Manager.Connect(address, port, password ?? string.Empty); 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);
}
} }

View File

@@ -9,7 +9,6 @@ public static class NetworkExtensions
{ {
public static Vector2D GetVector2D(this NetPacketReader reader) public static Vector2D GetVector2D(this NetPacketReader reader)
=> new(reader.GetFloat(), reader.GetFloat()); => new(reader.GetFloat(), reader.GetFloat());
public static void GetVector2D(this NetPacketReader reader, out Vector2D vector2D) public static void GetVector2D(this NetPacketReader reader, out Vector2D vector2D)
=> vector2D = new(reader.GetFloat(), reader.GetFloat()); => vector2D = new(reader.GetFloat(), reader.GetFloat());

View File

@@ -1,3 +1,4 @@
using LiteNetLib;
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
using Syntriax.Engine.Network.Abstract; using Syntriax.Engine.Network.Abstract;
@@ -8,23 +9,23 @@ public class NetworkManager : NetworkBehaviour, INetworkManager
{ {
private BehaviourCollector<INetworkEntity> entities = null!; private BehaviourCollector<INetworkEntity> entities = null!;
private static uint networkIdIndex = 0; private static int networkIdIndex = 0;
protected override void OnInitialize() protected override void OnInitialize()
{ {
base.OnInitialize(); base.OnInitialize();
NetworkId = networkIdIndex++; ((INetworkEntity)this).SetNetworkId(networkIdIndex++);
entities = new(GameObject.GameManager); entities = new(GameObject.GameManager);
foreach (var entity in entities) foreach (var entity in entities)
entity.NetworkId = networkIdIndex++; entity.SetNetworkId(networkIdIndex++);
entities.OnCollected += OnCollected; entities.OnCollected += OnCollected;
} }
private void OnCollected(BehaviourCollector<INetworkEntity> collector, INetworkEntity entity) private void OnCollected(BehaviourCollector<INetworkEntity> collector, INetworkEntity entity)
=> entity.NetworkId = networkIdIndex++; => entity.SetNetworkId(networkIdIndex++);
public override void ReceiveData<T>(T data) { } protected override void OnMessageReceived(NetPacketReader reader, NetPeer peer) { }
} }

View File

@@ -1,35 +1,22 @@
using System;
using System.Linq;
using System.Reflection;
using LiteNetLib.Utils;
using Syntriax.Engine.Network.Abstract; using Syntriax.Engine.Network.Abstract;
namespace Syntriax.Engine.Network; namespace Syntriax.Engine.Network;
public class NetworkServer : NetworkBase, INetworkCommunicatorServer public class NetworkServer : NetworkBase, INetworkServer
{ {
public string Password { get; private set; } = string.Empty; public string Password { get; private set; } = string.Empty;
public int MaxConnectionCount { get; private set; } = 2; public int MaxConnectionCount { get; private set; } = 0;
public int Port { get; private set; } = 8888; public int Port { get; private set; } = 8888;
private readonly NetDataWriter netDataWriter = new(); public NetworkServer() : base()
public NetworkServer() : this(8888, 2) { }
public NetworkServer(int port, int maxConnectionCount) : base()
{ {
MaxConnectionCount = maxConnectionCount;
Port = port;
Listener.ConnectionRequestEvent += request => Listener.ConnectionRequestEvent += request =>
{ {
if (Manager.ConnectedPeersCount < maxConnectionCount) if (Manager.ConnectedPeersCount < MaxConnectionCount)
request.AcceptIfKey(Password); request.AcceptIfKey(Password);
else else
request.Reject(); request.Reject();
}; };
OnPacketReceived += ServerOnPacketReceived;
} }
public void Start(int port, int maxConnectionCount, string? password = null) public void Start(int port, int maxConnectionCount, string? password = null)
@@ -40,28 +27,4 @@ public class NetworkServer : NetworkBase, INetworkCommunicatorServer
Manager.Start(port); 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]);
}
} }