From 8a81ee063ab1d803b1e98af92722ebd2dc8d9697 Mon Sep 17 00:00:00 2001 From: Mark Suckerberg Date: Thu, 2 Nov 2023 16:01:32 -0500 Subject: [PATCH] Extracts shared code in client and server to a base abstract class --- Blocktest/Code/Networking/Client.cs | 139 ++++++--------------- Blocktest/Code/Scenes/GameScene.cs | 18 +-- DedicatedServer/Code/Networking/Server.cs | 107 ++++------------ DedicatedServer/Code/WorldHandler.cs | 10 +- Shared/Code/Networking/NetworkInterface.cs | 93 ++++++++++++++ 5 files changed, 169 insertions(+), 198 deletions(-) create mode 100644 Shared/Code/Networking/NetworkInterface.cs diff --git a/Blocktest/Code/Networking/Client.cs b/Blocktest/Code/Networking/Client.cs index fd9c4b9..3f60d0d 100644 --- a/Blocktest/Code/Networking/Client.cs +++ b/Blocktest/Code/Networking/Client.cs @@ -1,4 +1,3 @@ -using System.Net; using Blocktest.Rendering; using Blocktest.Scenes; using Blocktest.UI; @@ -10,50 +9,27 @@ using Shared.Code.Packets; namespace Blocktest.Networking; -public sealed class Client { +public sealed class Client : NetworkInterface { private readonly Camera _camera; - private readonly EventBasedNetListener _listener; - private readonly NetManager _manager; - private readonly Dictionary _playerRenderables = new(); - - private readonly WorldState _worldState; - public TickBuffer ClientTickBuffer; private readonly BlocktestGame _game; + private readonly Dictionary _playerRenderables = new(); + private bool _initialized; - public Client(WorldState worldState, Camera camera, BlocktestGame game) { - _worldState = worldState; + public Client(WorldState worldState, Camera camera, BlocktestGame game) : base(worldState) { _camera = camera; _game = game; - ClientTickBuffer = new TickBuffer(0, _worldState); - _listener = new EventBasedNetListener(); - _manager = new NetManager(_listener); - _listener.NetworkReceiveEvent += NetworkReceiveEvent; - _listener.PeerConnectedEvent += PeerConnected; - _listener.PeerDisconnectedEvent += PeerDisconnected; - _manager.Start(); - } - - public NetPeer? Server { get; private set; } - - public void Start(IPEndPoint ipEndPoint, string key) { - _manager.Connect(ipEndPoint, key); - } - public void Stop() { - _manager.Stop(); - } - - public void Update() { - _manager.PollEvents(); + Listener.PeerConnectedEvent += PeerConnected; + Listener.PeerDisconnectedEvent += PeerDisconnected; } private void PeerConnected(NetPeer peer) { - Console.WriteLine("Connected to server"); + Console.WriteLine($"Connected to server {peer.EndPoint} as {peer.RemoteId}"); Transform newTransform = new(new Vector2Int(256, 128)); Renderable newRenderable = new(newTransform, Layer.Player, Drawable.ErrorDrawable, Color.Orange); - _worldState.PlayerPositions.Add(peer.RemoteId, newTransform); + LocalWorldState.PlayerPositions.Add(peer.RemoteId, newTransform); _playerRenderables.Add(peer.RemoteId, newRenderable); _camera.RenderedComponents.Add(newRenderable); } @@ -65,88 +41,54 @@ private void PeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) { new DialogueWindow("Disconnected from server.", $"{disconnectInfo.Reason}"))); } - /// - /// Recieve network events from LiteNetLib - /// - /// The server the packet is coming from. - /// Contains the packet from the server. - /// - /// The delivery method used to deliver this packet. - private void NetworkReceiveEvent(NetPeer peer, NetPacketReader packetReader, byte channelNumber, - DeliveryMethod deliveryMethod) { - if (Server == null) { - Server = peer; - - PacketType packetType = (PacketType)packetReader.GetByte(); - if (packetType != PacketType.WorldDownload) { - Console.WriteLine("Bad packet!!!"); - return; - } - - int sourceId = packetReader.GetInt(); - - WorldDownload worldPacket = HandlePacket(packetReader, sourceId); - worldPacket.Process(_worldState); - ClientTickBuffer = new TickBuffer(worldPacket.TickNum, _worldState); - - SendPacket(new PeerEvent { - SourceId = Server.RemoteId, - TickNum = worldPacket.TickNum, - Type = PeerEvent.PeerEventType.PlayerList - }); - } else { - HandlePackets(packetReader); - } - } - - /// - /// Handles packets after the first. - /// - /// Contains the packet sent by the server. - /// ACCEPTS PacketType:SourceID:TickNum:Packet - private void HandlePackets(NetDataReader packetReader) { - byte packetByte = packetReader.GetByte(); - PacketType packetType = (PacketType)packetByte; - - int sourceId = packetReader.GetInt(); - + protected override void HandlePackets(NetDataReader packetReader, int sourceId, PacketType packetType, + ushort tickNum, NetPeer peer) { switch (packetType) { case PacketType.TileChange: - ClientTickBuffer.AddPacket(HandlePacket(packetReader, sourceId)); + LocalTickBuffer.AddPacket(HandlePacket(packetReader, sourceId, tickNum)); break; case PacketType.BreakTile: - ClientTickBuffer.AddPacket(HandlePacket(packetReader, sourceId)); + LocalTickBuffer.AddPacket(HandlePacket(packetReader, sourceId, tickNum)); break; case PacketType.MovePlayer: - ClientTickBuffer.AddPacket(HandlePacket(packetReader, sourceId)); + LocalTickBuffer.AddPacket(HandlePacket(packetReader, sourceId, tickNum)); break; case PacketType.PlayerList: - ClientTickBuffer.AddPacket(HandlePacket(packetReader, sourceId)); + LocalTickBuffer.AddPacket(HandlePacket(packetReader, sourceId, tickNum)); break; case PacketType.PeerEvent: - PeerEvent eventPacket = new() { SourceId = sourceId, TickNum = packetReader.GetUShort() }; - eventPacket.Deserialize(packetReader); - HandleEvent(eventPacket); + HandleEvent(HandlePacket(packetReader, sourceId, tickNum)); break; case PacketType.WorldDownload: + if (_initialized) { + Console.WriteLine("Bad packet!!!"); + break; + } + + WorldDownload worldPacket = HandlePacket(packetReader, sourceId, tickNum); + worldPacket.Process(LocalWorldState); + LocalTickBuffer = new TickBuffer(worldPacket.TickNum, LocalWorldState); + + SendPacket(new PeerEvent { + SourceId = Server.RemoteId, + TickNum = worldPacket.TickNum, + Type = PeerEvent.PeerEventType.PlayerList + }, Server); + + _initialized = true; + break; default: Console.WriteLine("Bad packet!!!"); break; } } - private T HandlePacket(NetDataReader packetReader, int sourceId) where T : IPacket, new() { - T packet = new() { SourceId = sourceId, TickNum = packetReader.GetUShort() }; - packet.Deserialize(packetReader); - return packet; - } - - private void HandleEvent(PeerEvent peerEvent) { + protected override void HandleEvent(PeerEvent peerEvent) { switch (peerEvent.Type) { case PeerEvent.PeerEventType.PeerDisconnect: Console.WriteLine("Player disconnected"); - _worldState.PlayerPositions.Remove(peerEvent.SourceId); + LocalWorldState.PlayerPositions.Remove(peerEvent.SourceId); _camera.RenderedComponents.Remove(_playerRenderables[peerEvent.SourceId]); _playerRenderables.Remove(peerEvent.SourceId); break; @@ -155,7 +97,7 @@ private void HandleEvent(PeerEvent peerEvent) { Transform newTransform = new(new Vector2Int(256, 128)); Renderable newRenderable = new(newTransform, Layer.Player, Drawable.ErrorDrawable, Color.Orange); - _worldState.PlayerPositions.Add(peerEvent.SourceId, newTransform); + LocalWorldState.PlayerPositions.Add(peerEvent.SourceId, newTransform); _playerRenderables.Add(peerEvent.SourceId, newRenderable); _camera.RenderedComponents.Add(newRenderable); break; @@ -164,15 +106,4 @@ private void HandleEvent(PeerEvent peerEvent) { throw new ArgumentOutOfRangeException(nameof(peerEvent), peerEvent, null); } } - - // SENDS PacketType:TickNum:Packet - public void SendPacket(IPacket packet, DeliveryMethod deliveryMethod = DeliveryMethod.ReliableUnordered) { - NetDataWriter writer = new(); - - writer.Put((byte)packet.GetPacketType()); - writer.Put(packet.TickNum); - //ID is not needed for client to server packets - writer.Put(packet); - Server?.Send(writer, deliveryMethod); - } } \ No newline at end of file diff --git a/Blocktest/Code/Scenes/GameScene.cs b/Blocktest/Code/Scenes/GameScene.cs index 8d9b5f3..e45924d 100644 --- a/Blocktest/Code/Scenes/GameScene.cs +++ b/Blocktest/Code/Scenes/GameScene.cs @@ -12,6 +12,7 @@ namespace Blocktest.Scenes; public sealed class GameScene : IScene { private readonly RenderableTilemap _backgroundTilemapSprites; + private readonly string[] _blockStrings; private readonly Camera _camera; private readonly bool _connect; @@ -23,7 +24,6 @@ public sealed class GameScene : IScene { private readonly WorldState _worldState = new(); private int _blockSelected = 1; //ID of the block to place - private readonly string[] _blockStrings; private bool _buildMode = true; //true for build, false for destroy @@ -43,7 +43,7 @@ public GameScene(BlocktestGame game, bool doConnect, IPEndPoint? ip) { _blockStrings = BlockManagerShared.AllBlocks.Keys.ToArray(); if (_connect && ip != null) { - _networkingClient.Start(ip, "testKey"); + _networkingClient.Connect(ip); return; } @@ -58,7 +58,7 @@ public void Update(GameTime gameTime) { HandleInput(); - _networkingClient.ClientTickBuffer.IncrCurrTick(_worldState); + _networkingClient.LocalTickBuffer.IncrCurrTick(_worldState); } public void Draw(GameTime gameTime, GraphicsDevice graphicsDevice) { @@ -136,11 +136,11 @@ public void HandleInput() { _camera.Position += moveVector; MovePlayer movementPacket = new() { - TickNum = _networkingClient.ClientTickBuffer.CurrTick, + TickNum = _networkingClient.LocalTickBuffer.CurrTick, Position = (Vector2Int)_camera.Position, SourceId = _networkingClient.Server?.RemoteId ?? 0 }; - _networkingClient.ClientTickBuffer.AddPacket(movementPacket); + _networkingClient.LocalTickBuffer.AddPacket(movementPacket); if (_connect) { _networkingClient.SendPacket(movementPacket); } @@ -162,26 +162,26 @@ public void HandleInput() { if (_buildMode) { TileChange testChange = new() { - TickNum = _networkingClient.ClientTickBuffer.CurrTick, + TickNum = _networkingClient.LocalTickBuffer.CurrTick, Position = tilePos, Foreground = foreground, BlockUid = _blockStrings[_blockSelected], SourceId = _networkingClient.Server?.RemoteId ?? 0 }; - _networkingClient.ClientTickBuffer.AddPacket(testChange); + _networkingClient.LocalTickBuffer.AddPacket(testChange); if (_connect) { _networkingClient.SendPacket(testChange); } } else { BreakTile testBreak = new() { - TickNum = _networkingClient.ClientTickBuffer.CurrTick, + TickNum = _networkingClient.LocalTickBuffer.CurrTick, Position = tilePos, Foreground = foreground, SourceId = _networkingClient.Server?.RemoteId ?? 0 }; - _networkingClient.ClientTickBuffer.AddPacket(testBreak); + _networkingClient.LocalTickBuffer.AddPacket(testBreak); if (_connect) { _networkingClient.SendPacket(testBreak); } diff --git a/DedicatedServer/Code/Networking/Server.cs b/DedicatedServer/Code/Networking/Server.cs index 72a9842..027d798 100644 --- a/DedicatedServer/Code/Networking/Server.cs +++ b/DedicatedServer/Code/Networking/Server.cs @@ -7,31 +7,15 @@ using Shared.Code.Packets; namespace DedicatedServer.Code.Networking; -public sealed class Server { - private readonly EventBasedNetListener _listener = new(); - private readonly NetManager _manager; +public sealed class Server : NetworkInterface { private readonly ServerPlayerManager _playerManager; - private readonly WorldState _worldState; - public readonly TickBuffer ServerTickBuffer; - public Server(WorldState worldState) { - _worldState = worldState; - ServerTickBuffer = new TickBuffer(0, _worldState); - _manager = new NetManager(_listener); + public Server(WorldState worldState) : base(worldState) { _playerManager = new ServerPlayerManager(GlobalsShared.MaxPlayers); - _listener.ConnectionRequestEvent += NewConnection; - _listener.PeerConnectedEvent += NewPeer; - _listener.NetworkReceiveEvent += NetworkReceiveEvent; - _listener.PeerDisconnectedEvent += PeerDisconnected; - } - - public void Start() { - _manager.Start(9050); - } - - public void Update() { - _manager.PollEvents(); + Listener.ConnectionRequestEvent += NewConnection; + Listener.PeerConnectedEvent += NewPeer; + Listener.PeerDisconnectedEvent += PeerDisconnected; } private void NewConnection(ConnectionRequest request) { @@ -50,18 +34,18 @@ private void NewConnection(ConnectionRequest request) { private void NewPeer(NetPeer peer) { Console.WriteLine("New Peer"); _playerManager.AddPlayer(peer); - _worldState.PlayerPositions.Add(peer.Id, new Transform(Vector2Int.Zero)); + LocalWorldState.PlayerPositions.Add(peer.Id, new Transform(Vector2Int.Zero)); WorldDownload worldDownload = new() { - World = _worldState.CurrentWorld, - TickNum = ServerTickBuffer.CurrTick, + World = LocalWorldState.CurrentWorld, + TickNum = LocalTickBuffer.CurrTick, SourceId = -1 }; SendPacket(worldDownload, peer, DeliveryMethod.ReliableOrdered); SendPacketAllPeers(new PeerEvent { SourceId = peer.Id, - TickNum = ServerTickBuffer.CurrTick, + TickNum = LocalTickBuffer.CurrTick, Type = PeerEvent.PeerEventType.PeerConnect }, peer); } @@ -74,63 +58,46 @@ private void NewPeer(NetPeer peer) { private void PeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) { Console.WriteLine($"Peer Disconnected: {disconnectInfo.Reason}"); _playerManager.RemovePlayer(peer); - _worldState.PlayerPositions.Remove(peer.Id); + LocalWorldState.PlayerPositions.Remove(peer.Id); SendPacketAllPeers(new PeerEvent { SourceId = peer.Id, - TickNum = ServerTickBuffer.CurrTick, + TickNum = LocalTickBuffer.CurrTick, Type = PeerEvent.PeerEventType.PeerDisconnect }, peer); } - /// - /// Receive network events from LiteNetLib - /// - /// The client the packet is coming from. - /// Contains the packet from the client. - /// - /// The delivery method used to deliver this packet. - /// ACCEPTS PacketType:TickNum:Packet - private void NetworkReceiveEvent(NetPeer peer, NetPacketReader packetReader, byte channelNumber, - DeliveryMethod deliveryMethod) { - byte packetByte = packetReader.GetByte(); - PacketType packetType = (PacketType)packetByte; + protected override void HandlePackets(NetDataReader packetReader, int sourceId, PacketType packetType, + ushort tickNum, NetPeer peer) { + IPacket newPacket; switch (packetType) { case PacketType.TileChange: - HandlePacket(packetReader, peer); + newPacket = HandlePacket(packetReader, sourceId, tickNum); break; case PacketType.BreakTile: - HandlePacket(packetReader, peer); + newPacket = HandlePacket(packetReader, sourceId, tickNum); break; case PacketType.MovePlayer: - HandlePacket(packetReader, peer); + newPacket = HandlePacket(packetReader, sourceId, tickNum); break; case PacketType.PeerEvent: - PeerEvent eventPacket = new() { SourceId = peer.Id, TickNum = packetReader.GetUShort() }; - eventPacket.Deserialize(packetReader); - HandleEvent(eventPacket); - break; + HandleEvent(HandlePacket(packetReader, sourceId, tickNum)); + return; case PacketType.WorldDownload: case PacketType.PlayerList: default: Console.WriteLine("Bad packet!!!"); - break; + return; } + LocalTickBuffer.AddPacket(newPacket); + SendPacketAllPeers(newPacket, peer); } - private void HandlePacket(NetDataReader packetReader, NetPeer source) where T : IPacket, new() { - T packet = new() { SourceId = source.Id, TickNum = packetReader.GetUShort() }; - packet.Deserialize(packetReader); - ServerTickBuffer.AddPacket(packet); - - SendPacketAllPeers(packet, source); - } - - private void HandleEvent(PeerEvent peerEvent) { + protected override void HandleEvent(PeerEvent peerEvent) { switch (peerEvent.Type) { case PeerEvent.PeerEventType.PlayerList: - Console.WriteLine("PlayerList requested"); - NetPeer? peer = _manager.GetPeerById(peerEvent.SourceId); + Console.WriteLine("Player List requested"); + NetPeer? peer = Manager.GetPeerById(peerEvent.SourceId); foreach (var player in _playerManager.PlayerList) { if (player.Key == peerEvent.SourceId) { continue; @@ -138,7 +105,7 @@ private void HandleEvent(PeerEvent peerEvent) { SendPacket( new PeerEvent { SourceId = player.Key, Type = PeerEvent.PeerEventType.PeerConnect, - TickNum = ServerTickBuffer.CurrTick + TickNum = LocalTickBuffer.CurrTick }, peer); } @@ -150,26 +117,4 @@ private void HandleEvent(PeerEvent peerEvent) { throw new ArgumentOutOfRangeException(nameof(peerEvent), peerEvent, null); } } - - // SENDS PacketType:SourceID:TickNum:Packet - private void SendPacketAllPeers(IPacket packet, NetPeer source, - DeliveryMethod deliveryMethod = DeliveryMethod.ReliableUnordered) { - NetDataWriter writer = new(); - writer.Put((byte)packet.GetPacketType()); - writer.Put(source.Id); - writer.Put(packet.TickNum); - writer.Put(packet); - _manager.SendToAll(writer, deliveryMethod, source); // Send to all clients except the source - } - - // SENDS PacketType:SourceID:TickNum:Packet - private void SendPacket(IPacket packet, NetPeer target, - DeliveryMethod deliveryMethod = DeliveryMethod.ReliableUnordered) { - NetDataWriter writer = new(); - writer.Put((byte)packet.GetPacketType()); - writer.Put(packet.SourceId); - writer.Put(packet.TickNum); - writer.Put(packet); - target.Send(writer, deliveryMethod); - } } \ No newline at end of file diff --git a/DedicatedServer/Code/WorldHandler.cs b/DedicatedServer/Code/WorldHandler.cs index ca01a6d..f8f8e93 100644 --- a/DedicatedServer/Code/WorldHandler.cs +++ b/DedicatedServer/Code/WorldHandler.cs @@ -14,6 +14,8 @@ internal sealed class WorldHandler { private readonly Stopwatch _stopwatch; private readonly TimeSpan _targetTime = TimeSpan.FromMilliseconds(16); + private readonly WorldState _worldState; + private int _continueRun = 1; private bool _continueWait = true; private int _counter = 0; @@ -21,8 +23,6 @@ internal sealed class WorldHandler { private TimeSpan _currentTime = TimeSpan.Zero; private long _previousTicks = 0; - private readonly WorldState _worldState; - public WorldHandler() { BlockManagerShared.Initialize(); _worldState = new WorldState(); @@ -36,7 +36,7 @@ public WorldHandler() { public void Run() { lock (_locker) { - _server.Start(); + _server.StartServer(9050); } _stopwatch.Start(); Loop(); @@ -61,11 +61,13 @@ private void Tick(object? state) { previousTicks = currentTicks; counter++;*/ _server.Update(); - _server.ServerTickBuffer.IncrCurrTick(_worldState); } } public void Stop() { Interlocked.Exchange(ref _continueRun, 0); + lock (_locker) { + _server.Stop(); + } } } \ No newline at end of file diff --git a/Shared/Code/Networking/NetworkInterface.cs b/Shared/Code/Networking/NetworkInterface.cs new file mode 100644 index 0000000..983c03b --- /dev/null +++ b/Shared/Code/Networking/NetworkInterface.cs @@ -0,0 +1,93 @@ +using System.Net; +using LiteNetLib; +using LiteNetLib.Utils; +using Shared.Code.Packets; +namespace Shared.Code.Networking; + +public abstract class NetworkInterface { + protected readonly EventBasedNetListener Listener = new(); + protected readonly WorldState LocalWorldState; + protected readonly NetManager Manager; + + protected NetworkInterface(WorldState worldState) { + LocalWorldState = worldState; + LocalTickBuffer = new TickBuffer(0, LocalWorldState); + Manager = new NetManager(Listener); + + Listener.NetworkReceiveEvent += NetworkReceiveEvent; + } + + public TickBuffer LocalTickBuffer { get; protected set; } + public NetPeer? Server { get; private set; } + + public void StartServer(int port) { + Manager.Start(port); + } + + public void Connect(IPEndPoint ipEndPoint, string key = "testKey") { + if (Server != null) { + return; + } + Manager.Start(); + Server = Manager.Connect(ipEndPoint, key); + } + + public void Stop() { + Manager.Stop(); + } + + public void Update() { + Manager.PollEvents(); + LocalTickBuffer.IncrCurrTick(LocalWorldState); + } + + private void NetworkReceiveEvent(NetPeer peer, NetDataReader packetReader, byte channelNumber, + DeliveryMethod deliveryMethod) { + PacketType packetType = (PacketType)packetReader.GetByte(); + ushort tickNum = packetReader.GetUShort(); + int sourceId = packetReader.GetInt(); + + HandlePackets(packetReader, sourceId, packetType, tickNum, peer); + } + + protected abstract void HandlePackets(NetDataReader packetReader, int sourceId, PacketType packetType, + ushort tickNum, NetPeer peer); + + protected abstract void HandleEvent(PeerEvent peerEvent); + + protected T HandlePacket(NetDataReader packetReader, int sourceId, ushort tickNum) where T : IPacket, new() { + T packet = new() { SourceId = sourceId, TickNum = tickNum }; + packet.Deserialize(packetReader); + return packet; + } + + public void SendPacket(IPacket packet, DeliveryMethod deliveryMethod = DeliveryMethod.ReliableUnordered) { + if (Server == null) { + SendPacketAllPeers(packet); + return; + } + SendPacket(packet, Server, deliveryMethod); + } + + public void SendPacket(IPacket packet, NetPeer target, + DeliveryMethod deliveryMethod = DeliveryMethod.ReliableUnordered) { + NetDataWriter writer = new(); + + writer.Put((byte)packet.GetPacketType()); + writer.Put(packet.TickNum); + writer.Put(packet.SourceId); + writer.Put(packet); + target.Send(writer, deliveryMethod); + } + + public void SendPacketAllPeers(IPacket packet, NetPeer? source = null, + DeliveryMethod deliveryMethod = DeliveryMethod.ReliableUnordered) { + NetDataWriter writer = new(); + + writer.Put((byte)packet.GetPacketType()); + writer.Put(packet.TickNum); + writer.Put(packet.SourceId); + writer.Put(packet); + Manager.SendToAll(writer, deliveryMethod, source); // Send to all clients except the source + } +} \ No newline at end of file