wip: Network

This commit is contained in:
Syntriax 2024-02-12 16:35:24 +03:00
parent 6f3e7b4ae5
commit 3c1528d879
11 changed files with 159 additions and 80 deletions

View File

@ -13,7 +13,7 @@ using Syntriax.Engine.Physics2D.Abstract;
namespace Pong.Behaviours; namespace Pong.Behaviours;
public class BallBehaviour : BehaviourOverride, IMonoGameContentLoader 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;
@ -42,7 +42,7 @@ public class BallBehaviour : BehaviourOverride, IMonoGameContentLoader
rigidBody = foundRigidBody; rigidBody = foundRigidBody;
communicator = foundCommunicator; communicator = foundCommunicator;
communicator.RegisterEntityListener(this, OnDataReceived); communicator.Listener.PeerConnectedEvent += PeerConnected;
if (GameObject.GameManager.TryFindBehaviour(out PongManagerBehaviour? pongManager)) if (GameObject.GameManager.TryFindBehaviour(out PongManagerBehaviour? pongManager))
{ {
pongManager.OnReset += ResetBall; pongManager.OnReset += ResetBall;
@ -51,10 +51,11 @@ public class BallBehaviour : BehaviourOverride, IMonoGameContentLoader
} }
} }
private void OnDataReceived(NetPacketReader reader) private void PeerConnected(NetPeer peer)
{ {
rigidBody.Transform.Position = reader.GetVector2D(); if (!IsServer)
rigidBody.Velocity = reader.GetVector2D(); return;
if (communicator.Manager.ConnectedPeersCount == 1)
} }
private void DisableBall(PongManagerBehaviour pongManager) private void DisableBall(PongManagerBehaviour pongManager)
@ -106,12 +107,18 @@ public class BallBehaviour : BehaviourOverride, IMonoGameContentLoader
if (communicator is not INetworkServer server) if (communicator is not INetworkServer server)
return; return;
LiteNetLib.Utils.NetDataWriter dataWriter = communicator.GetEntityWriter(this); LiteNetLib.Utils.NetDataWriter dataWriter = communicator.GetMessageWriter(this);
dataWriter.Put(rigidBody.Transform.Position); dataWriter.Put(rigidBody.Transform.Position);
dataWriter.Put(rigidBody.Velocity); dataWriter.Put(rigidBody.Velocity);
server.Manager.SendToAll(dataWriter, LiteNetLib.DeliveryMethod.ReliableOrdered); 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() { }
public BallBehaviour(Vector2D startDirection, float speed) public BallBehaviour(Vector2D startDirection, float speed)
{ {

View File

@ -12,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) : BehaviourOverride public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Speed) : NetworkBehaviour
{ {
private Keys Up { get; } = Up; private Keys Up { get; } = Up;
private Keys Down { get; } = Down; private Keys Down { get; } = Down;
@ -41,9 +41,12 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp
private void MovePaddle(Vector2D vectorToAdd) private void MovePaddle(Vector2D vectorToAdd)
{ {
if (!LocalAssigned && !IsServer)
return;
GameObject.Transform.Position += vectorToAdd; GameObject.Transform.Position += vectorToAdd;
NetDataWriter dataWriter = communicator.GetEntityWriter(this); NetDataWriter dataWriter = communicator.GetMessageWriter(this);
dataWriter.Put(Transform.Position); dataWriter.Put(Transform.Position);
communicator.Manager.SendToAll(dataWriter, LiteNetLib.DeliveryMethod.Unreliable); communicator.Manager.SendToAll(dataWriter, LiteNetLib.DeliveryMethod.Unreliable);
} }
@ -63,12 +66,6 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp
inputs.RegisterOnRelease(Down, OnDownReleased); inputs.RegisterOnRelease(Down, OnDownReleased);
communicator = foundCommunicator; communicator = foundCommunicator;
communicator.RegisterEntityListener(this, OnDataReceived);
}
private void OnDataReceived(NetPacketReader reader)
{
Transform.Position = reader.GetVector2D();
} }
protected override void OnFinalize() protected override void OnFinalize()
@ -84,4 +81,9 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp
private void OnUpReleased(IButtonInputs<Keys> inputs, Keys keys) => isUpPressed = false; private void OnUpReleased(IButtonInputs<Keys> inputs, Keys keys) => isUpPressed = false;
private void OnDownPressed(IButtonInputs<Keys> inputs, Keys keys) => isDownPressed = true; private void OnDownPressed(IButtonInputs<Keys> inputs, Keys keys) => isDownPressed = true;
private void OnDownReleased(IButtonInputs<Keys> inputs, Keys keys) => isDownPressed = false; private void OnDownReleased(IButtonInputs<Keys> inputs, Keys keys) => isDownPressed = false;
protected override void OnMessageReceived(NetPacketReader reader, NetPeer peer)
{
Transform.Position = reader.GetVector2D();
}
} }

View File

@ -10,7 +10,7 @@ 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;
@ -41,16 +41,6 @@ public class PongManagerBehaviour : BehaviourOverride
buttonInputs.RegisterOnRelease(Keys.Space, (_, _1) => Reset()); buttonInputs.RegisterOnRelease(Keys.Space, (_, _1) => Reset());
communicator = foundCommunicator; communicator = foundCommunicator;
communicator.RegisterEntityListener(this, OnMessageReceived);
}
private void OnMessageReceived(NetPacketReader reader)
{
ScoreLeft = reader.GetInt();
ScoreRight = reader.GetInt();
OnScoresUpdated?.Invoke(this);
CheckFinish();
} }
public void ScoreToLeft() public void ScoreToLeft()
@ -97,9 +87,18 @@ public class PongManagerBehaviour : BehaviourOverride
if (communicator is not INetworkServer server) if (communicator is not INetworkServer server)
return; return;
LiteNetLib.Utils.NetDataWriter dataWriter = communicator.GetEntityWriter(this); LiteNetLib.Utils.NetDataWriter dataWriter = communicator.GetMessageWriter(this);
dataWriter.Put(ScoreLeft); dataWriter.Put(ScoreLeft);
dataWriter.Put(ScoreRight); dataWriter.Put(ScoreRight);
server.Manager.SendToAll(dataWriter, LiteNetLib.DeliveryMethod.ReliableOrdered); 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

@ -71,6 +71,28 @@ public class GamePong : Game
//////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////
string[] commandLineArguments = Environment.GetCommandLineArgs();
if (commandLineArguments.Length != 1)
{
if (commandLineArguments[1].Equals("server", StringComparison.OrdinalIgnoreCase))
{
gameManager.InstantiateGameObject().BehaviourController.AddBehaviour<NetworkServer>().Start(8888, 2);
Window.Title = "Pong - Server";
}
else
{
Window.Title = $"Pong - Client -> {commandLineArguments[1]}";
gameManager.InstantiateGameObject().BehaviourController.AddBehaviour<NetworkClient>().Connect(commandLineArguments[1], 8888);
}
}
else
{
gameManager.InstantiateGameObject().BehaviourController.AddBehaviour<NetworkClient>().Connect("127.0.0.1", 8888);
Window.Title = $"Pong - Client -> 127.0.0.1";
}
////////////////////////////////////////////////////////////////////////////////////
IGameObject gameObjectCamera = gameManager.InstantiateGameObject().SetGameObject("Camera"); ; IGameObject gameObjectCamera = gameManager.InstantiateGameObject().SetGameObject("Camera"); ;
gameObjectCamera.BehaviourController.AddBehaviour<CameraController>(); gameObjectCamera.BehaviourController.AddBehaviour<CameraController>();
cameraBehaviour = gameObjectCamera.BehaviourController.AddBehaviour<MonoGameCamera2DBehaviour>(graphics); cameraBehaviour = gameObjectCamera.BehaviourController.AddBehaviour<MonoGameCamera2DBehaviour>(graphics);
@ -138,26 +160,6 @@ public class GamePong : Game
IGameObject gameObjectRightScoreText = gameManager.InstantiateGameObject().SetGameObject("Score Right"); IGameObject gameObjectRightScoreText = gameManager.InstantiateGameObject().SetGameObject("Score Right");
gameObjectRightScoreText.Transform.SetTransform(position: new Vector2D(250f, 250f), scale: Vector2D.One * .25f); gameObjectRightScoreText.Transform.SetTransform(position: new Vector2D(250f, 250f), scale: Vector2D.One * .25f);
gameObjectRightScoreText.BehaviourController.AddBehaviour<TextScoreBehaviour>(false, spriteFont); gameObjectRightScoreText.BehaviourController.AddBehaviour<TextScoreBehaviour>(false, spriteFont);
string[] commandLineArguments = Environment.GetCommandLineArgs();
if (commandLineArguments.Length != 1)
{
if (commandLineArguments[1].Equals("server", StringComparison.OrdinalIgnoreCase))
{
pongManager.BehaviourController.AddBehaviour<NetworkServer>().Start(8888, 2);
Window.Title = "Pong - Server";
}
else
{
Window.Title = $"Pong - Client -> {commandLineArguments[1]}";
pongManager.BehaviourController.AddBehaviour<NetworkClient>().Connect(commandLineArguments[1], 8888);
}
}
else
{
pongManager.BehaviourController.AddBehaviour<NetworkClient>().Connect("127.0.0.1", 8888);
Window.Title = $"Pong - Client -> 127.0.0.1";
}
} }
protected override void Update(GameTime gameTime) protected override void Update(GameTime gameTime)

View File

@ -12,4 +12,7 @@ public interface INetworkBehaviour : IBehaviour
bool IsClient { get; } bool IsClient { get; }
INetworkCommunicator NetworkCommunicator { get; } INetworkCommunicator NetworkCommunicator { get; }
public void RequestAssignment();
public void ReleaseAssignment();
} }

View File

@ -15,7 +15,8 @@ public interface INetworkCommunicator
void PollEvents(); void PollEvents();
void Stop(); void Stop();
void RegisterEntityListener(IEntity entity, Action<NetPacketReader> onDataReceived); void RegisterEntityListener(IEntity entity, Action<NetPacketReader, NetPeer> onDataReceived);
void UnregisterEntityListener(IEntity entity); void UnregisterEntityListener(IEntity entity);
NetDataWriter GetEntityWriter(IEntity entity); NetDataWriter GetEntityWriter(IEntity entity);
NetDataWriter GetMessageWriter(IEntity entity);
} }

View File

@ -1,7 +1,10 @@
using System;
namespace Syntriax.Engine.Network.Abstract; namespace Syntriax.Engine.Network.Abstract;
internal interface INetworkEntity internal interface INetworkEntity
{ {
Action<INetworkEntity, int> OnNetworkIdChanged { get; set; }
int NetworkId { get; set; } int NetworkId { get; set; }
void SetNetworkId(int id); void SetNetworkId(int id);

View File

@ -1,14 +1,10 @@
using System;
using System.Threading.Tasks;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Network.Abstract; namespace Syntriax.Engine.Network.Abstract;
public interface INetworkManager public interface INetworkManager
{ {
Action<IGameObject>? OnNetworkGameObjectInstantiated { get; set; } // Action<IGameObject>? OnNetworkGameObjectInstantiated { get; set; }
INetworkCommunicator Communicator { get; } INetworkCommunicator NetworkCommunicator { get; }
Task<T> Instantiate<T>(params object?[]? args) where T : class, IGameObject; // Task<T> Instantiate<T>(params object?[]? args) where T : class, IGameObject;
} }

View File

@ -31,17 +31,17 @@ public abstract class NetworkBase : BehaviourOverride, INetworkCommunicator
protected override void OnUpdate() => PollEvents(); protected override void OnUpdate() => PollEvents();
protected override void OnFinalize() => Stop(); protected override void OnFinalize() => Stop();
private Dictionary<string, Action<NetPacketReader>> callbacks = new(32); private readonly Dictionary<string, Action<NetPacketReader, NetPeer>> callbacks = new(32);
private void NetworkReceiveEvent(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod) private void NetworkReceiveEvent(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod)
{ {
if (callbacks.TryGetValue(reader.GetString(), out var action)) if (callbacks.TryGetValue(reader.GetString(), out var action))
action?.Invoke(reader); action?.Invoke(reader, peer);
reader.Recycle(); reader.Recycle();
} }
public void RegisterEntityListener(IEntity entity, Action<NetPacketReader> onDataReceived) public void RegisterEntityListener(IEntity entity, Action<NetPacketReader, NetPeer> onDataReceived)
{ {
if (callbacks.ContainsKey(entity.Id)) if (callbacks.ContainsKey(entity.Id))
return; return;
@ -58,6 +58,13 @@ public abstract class NetworkBase : BehaviourOverride, INetworkCommunicator
entity.OnIdChanged -= OnEntityIdChanged; entity.OnIdChanged -= OnEntityIdChanged;
} }
public NetDataWriter GetMessageWriter(IEntity entity)
{
NetDataWriter netDataWriter = GetEntityWriter(entity);
netDataWriter.Put((int)MessageType.Message);
return netDataWriter;
}
public NetDataWriter GetEntityWriter(IEntity entity) public NetDataWriter GetEntityWriter(IEntity entity)
{ {
NetDataWriter netDataWriter = new(); NetDataWriter netDataWriter = new();

View File

@ -1,9 +1,13 @@
using System;
using LiteNetLib;
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
using Syntriax.Engine.Network.Abstract; using Syntriax.Engine.Network.Abstract;
namespace Syntriax.Engine.Network; namespace Syntriax.Engine.Network;
public class NetworkBehaviour : BehaviourOverride, INetworkBehaviour, INetworkEntity public abstract class NetworkBehaviour : BehaviourOverride, INetworkBehaviour
{ {
public int NetworkId { get; private set; } = 0; public int NetworkId { get; private set; } = 0;
@ -18,7 +22,9 @@ public class NetworkBehaviour : BehaviourOverride, INetworkBehaviour, INetworkEn
{ {
NetworkCommunicator = BehaviourController.GetBehaviourInParent<INetworkCommunicator>() NetworkCommunicator = BehaviourController.GetBehaviourInParent<INetworkCommunicator>()
?? GameObject.GameManager.FindBehaviour<INetworkCommunicator>() ?? GameObject.GameManager.FindBehaviour<INetworkCommunicator>()
?? throw new System.Exception($"Could not find an {nameof(INetworkCommunicator)}."); ?? throw new Exception($"Could not find an {nameof(INetworkCommunicator)}.");
NetworkCommunicator.RegisterEntityListener(this, OnMessageReceivedInternal);
if (NetworkCommunicator is INetworkClient client) if (NetworkCommunicator is INetworkClient client)
{ {
@ -28,10 +34,66 @@ public class NetworkBehaviour : BehaviourOverride, INetworkBehaviour, INetworkEn
IsServer = true; IsServer = true;
LocalAssigned = true; LocalAssigned = true;
// TODO Set and send Network Id
} }
int INetworkEntity.NetworkId { get => NetworkId; set => NetworkId = value; } public void ReleaseAssignment()
void INetworkEntity.SetNetworkId(int id) => NetworkId = id; {
if (IsServer)
return;
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,34 +1,31 @@
using System; using LiteNetLib;
using System.Threading.Tasks;
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 class NetworkManager : NetworkBehaviour, INetworkManager public class NetworkManager : NetworkBehaviour, INetworkManager
{ {
public Action<IGameObject>? OnNetworkGameObjectInstantiated { get; set; } = null;
public INetworkCommunicator Communicator { get; private set; } = null!;
private BehaviourCollector<INetworkEntity> entities = null!; private BehaviourCollector<INetworkEntity> entities = null!;
private static int networkIdIndex = 0;
protected override void OnInitialize() protected override void OnInitialize()
{ {
base.OnInitialize(); base.OnInitialize();
((INetworkEntity)this).SetNetworkId(networkIdIndex++);
entities = new(GameObject.GameManager); entities = new(GameObject.GameManager);
foreach (var entity in entities)
entity.SetNetworkId(networkIdIndex++);
entities.OnCollected += OnCollected;
} }
public Task<T> Instantiate<T>(params object?[]? args) where T : class, IGameObject private void OnCollected(BehaviourCollector<INetworkEntity> collector, INetworkEntity entity)
{ => entity.SetNetworkId(networkIdIndex++);
return Task.Run(() =>
{
if (IsServer)
return GameObject.GameManager.InstantiateGameObject<T>(args);
// Request Instantiation From Server protected override void OnMessageReceived(NetPacketReader reader, NetPeer peer) { }
int id = 0;
return GameObject.GameManager.InstantiateGameObject<T>(args);
});
}
} }