From 6f910e969a3b3c8d54241967835c848cabf12c25 Mon Sep 17 00:00:00 2001 From: "derek.holloway" Date: Wed, 8 Jul 2026 13:13:23 -0700 Subject: [PATCH] Rework server to be non static --- MistoxServer/Interface/ServerInterface.cs | 35 ++++++--------- MistoxServer/Server/Entities/MistoxServer.cs | 21 --------- .../Server/Entities/TcpClientConnection.cs | 16 ------- MistoxServer/Server/MistoxServer.cs | 43 ++++++++++++++++++ .../{TCPConnection.cs => ServerConnection.cs} | 44 ++++++++++++++----- MistoxServer/Server/ServerListener.cs | 17 +++---- 6 files changed, 97 insertions(+), 79 deletions(-) delete mode 100644 MistoxServer/Server/Entities/MistoxServer.cs delete mode 100644 MistoxServer/Server/Entities/TcpClientConnection.cs create mode 100644 MistoxServer/Server/MistoxServer.cs rename MistoxServer/Server/{TCPConnection.cs => ServerConnection.cs} (58%) diff --git a/MistoxServer/Interface/ServerInterface.cs b/MistoxServer/Interface/ServerInterface.cs index f91fb39..c79ae62 100644 --- a/MistoxServer/Interface/ServerInterface.cs +++ b/MistoxServer/Interface/ServerInterface.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Net; using System.Threading.Tasks; @@ -8,44 +7,36 @@ using Mistox.Server; namespace Mistox { public class ServerInterface { - public static MistoxServer ServerInstance; // Server Data - public static ServerListener Listener; // Server Listener - public static ConcurrentDictionary Connections; // Server Connections + public static ServerEntity ServerInstance; // Server Data - public event Action onUserConnected; // User connected event handler - public event Action onDataReceived; // Data received event handler - public event Action onUserDisconnected; // User disconnected event handler + public event Action onUserConnected; // User connected event handler + public event Action onDataReceived; // Data received event handler + public event Action onUserDisconnected; // User disconnected event handler public ServerInterface( int port, string serverName ) { + IPEndPoint listenerAddress = new IPEndPoint( IPAddress.Any, port ); + // Initilize the server - ServerInstance = new MistoxServer() { - ServerName = serverName, - BindAddress = IPAddress.Any, - Port = port, - MaxClients = 10 - }; - Connections = new ConcurrentDictionary(); + ServerInstance = new ServerEntity(listenerAddress); + ServerInstance.ServerName = serverName; // Setup the event handlers - ServerInstance.onUserConnected += (TcpClientConnection connectedUser) => { onUserConnected?.Invoke(connectedUser); }; + ServerInstance.onUserConnected += (ServerConnection connectedUser) => { onUserConnected?.Invoke(connectedUser); }; ServerInstance.onDataReceived += (dynamic data) => { onDataReceived?.Invoke(data); }; - ServerInstance.onUserDisconnected += (TcpClientConnection disconnectedUser) => { onUserDisconnected?.Invoke(disconnectedUser); }; - - // Start listening for connections and wire up the server - Listener = new ServerListener(); + ServerInstance.onUserDisconnected += (ServerConnection disconnectedUser) => { onUserDisconnected?.Invoke(disconnectedUser); }; Console.WriteLine( "The Server is initilized and waiting for clients to connect at port : " + port ); } public async Task SendAllClients(Packet data) { - foreach( KeyValuePair cur in Connections ) { - await cur.Value.Connection.Send(data); + foreach( KeyValuePair cur in ServerInstance.Connections ) { + await cur.Value.Send(data); } } public async Task SendClient(string UserName, Packet data) { - await Connections[UserName].Connection.Send( data ); + await ServerInstance.Connections[UserName].Send( data ); } } } diff --git a/MistoxServer/Server/Entities/MistoxServer.cs b/MistoxServer/Server/Entities/MistoxServer.cs deleted file mode 100644 index f1df656..0000000 --- a/MistoxServer/Server/Entities/MistoxServer.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Net; - -namespace Mistox.Server { - - public class MistoxServer { - public string ServerName { get; set; } = ""; // The name of the server - public IPAddress BindAddress { get; set; } = IPAddress.Any; // What IP to bind on - public int Port { get; set; } = 9126; // What Port to bind on - public int MaxClients { get; set; } = 10; // The max players per server - public bool Alive { get; set; } = true; // Is the server running - - public event Action onUserConnected; - public void InvokeUserConnected( TcpClientConnection user ){ onUserConnected?.Invoke( user ); } - public event Action onDataReceived; - public void InvokeDataReceived( dynamic data ){ onDataReceived?.Invoke( data ); } - public event Action onUserDisconnected; - public void InvokeUserDisconnected( TcpClientConnection user ){ onUserDisconnected?.Invoke( user ); } - } - -} diff --git a/MistoxServer/Server/Entities/TcpClientConnection.cs b/MistoxServer/Server/Entities/TcpClientConnection.cs deleted file mode 100644 index 81b0a07..0000000 --- a/MistoxServer/Server/Entities/TcpClientConnection.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Net; -using System.Numerics; - -namespace Mistox.Server { - public class TcpClientConnection { - public string UserName { get; set; } = ""; // The Players UserName - public bool IsAuthenticated { get; set; } = false; // Has the player authenticated - public Vector3 Position { get; set; } // The ingame position of the player - public float ConnectionRange { get; set; } // The ingame distance players need to be to be connected together - public DateTime LastHeartbeat { get; set; } = DateTime.UtcNow; // Connection heartbeat for integrity - public bool Connected { get; set; } = true; // Is the connection valid - public IPAddress RemoteAddress { get; set; } // The remote users IP Address - public TCPConnection Connection { get; set; } // The TCP Server Object - } -} \ No newline at end of file diff --git a/MistoxServer/Server/MistoxServer.cs b/MistoxServer/Server/MistoxServer.cs new file mode 100644 index 0000000..e7cd89e --- /dev/null +++ b/MistoxServer/Server/MistoxServer.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Concurrent; +using System.Net; + +namespace Mistox.Server { + + public class ServerEntity { + + // Class Definitions + + public string ServerName { get; set; } = ""; // The name of the server + public int MaxClients { get; set; } = 10; // The max players per server + public float ConnectionRange { get; set; } = 40; // InGame distance for valid conections + public int ConnectionTimeoutSeconds { get; set; } = 10; // If no reply in time kill the connection + public bool Alive { get; set; } = true; // Is the server running + + ServerListener Listener { get; set;} + public ConcurrentDictionary Connections; + + public ServerEntity(IPEndPoint ListenerAddress) { + Listener = new ServerListener(ListenerAddress, this); + Connections = new ConcurrentDictionary(); + } + + // Functions + + public event Action onUserConnected; + public void InvokeUserConnected( ServerConnection user ){ + onUserConnected?.Invoke( user ); + } + + public event Action onDataReceived; + public void InvokeDataReceived( dynamic data ){ + onDataReceived?.Invoke( data ); + } + + public event Action onUserDisconnected; + public void InvokeUserDisconnected( ServerConnection user ){ + onUserDisconnected?.Invoke( user ); + } + } + +} diff --git a/MistoxServer/Server/TCPConnection.cs b/MistoxServer/Server/ServerConnection.cs similarity index 58% rename from MistoxServer/Server/TCPConnection.cs rename to MistoxServer/Server/ServerConnection.cs index 72cd8d3..ff74abb 100644 --- a/MistoxServer/Server/TCPConnection.cs +++ b/MistoxServer/Server/ServerConnection.cs @@ -1,20 +1,30 @@ using System; +using System.Net; using System.Net.Sockets; +using System.Numerics; using System.Threading; using System.Threading.Tasks; -using Mistox.Attributes; namespace Mistox.Server { - public class TCPConnection : IDisposable { + public class ServerConnection : IDisposable { + public string UserName { get; set; } = ""; // The Players UserName + public bool IsAuthenticated { get; set; } = false; // Has the player authenticated + public Vector3 Position { get; set; } // The ingame position of the player + public DateTime LastHeartbeat { get; set; } = DateTime.UtcNow; // Connection heartbeat for integrity + public bool Connected { get; set; } = true; // Is the connection valid + + ServerEntity _parent; TcpClient _client; - TcpClientConnection _connection; + IPEndPoint _remoteAddress; - public TCPConnection( TcpClient tcpClient, string ConnectionID ) { + public ServerConnection( TcpClient tcpClient, ServerEntity parent, IPEndPoint remote ) { + _parent = parent; _client = tcpClient; + _remoteAddress = remote; _client.NoDelay = true; - _connection = ServerInterface.Connections[ConnectionID]; - ServerInterface.ServerInstance.InvokeUserConnected( ServerInterface.Connections[ConnectionID] ); + + ServerInterface.ServerInstance.InvokeUserConnected( this ); // Start the receive thread Thread receiveThread = new Thread(async () => await ReceiveThread() ); receiveThread.Start(); @@ -25,7 +35,16 @@ namespace Mistox.Server { try { byte[] headerBuffer = new byte[DataPacket.HeaderLength]; byte[] payloadBuffer = new byte[65535]; // Upper limit for safety - while( _connection.Connected ) { + while( Connected && _parent.Alive ) { + + // Tick the last heartbeat or drop + DateTime now = DateTime.UtcNow; + if (LastHeartbeat.AddSeconds(_parent.ConnectionTimeoutSeconds) >= now) { + Connected = false; + Dispose(); + break; + } + LastHeartbeat = DateTime.UtcNow; // Read the header off await ns.ReadExactlyAsync(headerBuffer, 0, 3); @@ -46,7 +65,7 @@ namespace Mistox.Server { dynamic dataobject = DataPacket.Deserialize( packetbyte, dataLength, payloadBuffer.AsMemory(DataPacket.HeaderLength, dataLength) ); // Return the object - ServerInterface.ServerInstance.InvokeDataReceived(dataobject); + _parent.InvokeDataReceived(dataobject); } } catch (Exception e) { @@ -63,10 +82,11 @@ namespace Mistox.Server { } public void Dispose() { - _connection.Connected = false; - ServerInterface.ServerInstance.InvokeUserDisconnected( _connection ); - ServerInterface.ServerInstance.Alive = false; - ServerInterface.Connections.TryRemove( _connection.UserName, out _ ); + Connected = false; + _parent.InvokeUserDisconnected( this ); + Connected = false; + _parent.Alive = false; + // Remove this item from the Dictionary on the Server for garbage collection } } } diff --git a/MistoxServer/Server/ServerListener.cs b/MistoxServer/Server/ServerListener.cs index 8229097..3091ce2 100644 --- a/MistoxServer/Server/ServerListener.cs +++ b/MistoxServer/Server/ServerListener.cs @@ -9,30 +9,31 @@ using System.Threading.Tasks; namespace Mistox.Server { public class ServerListener : IDisposable { + ServerEntity _parent; + IPEndPoint _bindAddress; TcpListener _Listener; - public ServerListener() { + public ServerListener(IPEndPoint listenerAddress, ServerEntity parent) { + _bindAddress = listenerAddress; + _parent = parent; Thread ConnectionThread = new Thread(async () => await ListenerThread() ); ConnectionThread.Start(); } async Task ListenerThread() { - _Listener = new TcpListener( ServerInterface.ServerInstance.BindAddress, ServerInterface.ServerInstance.Port ); + _Listener = new TcpListener( _bindAddress ); _Listener.Start(); - while( ServerInterface.ServerInstance.Alive ) { + while( _parent.Alive ) { TcpClient client = await _Listener.AcceptTcpClientAsync(); - if (ServerInterface.Connections.Count + 1 > ServerInterface.ServerInstance.MaxClients) { + if (_parent.Connections.Count + 1 > _parent.MaxClients) { continue; } IPEndPoint remote = client.Client.RemoteEndPoint as IPEndPoint; string connectionID = Guid.NewGuid().ToString(); - ServerInterface.Connections[connectionID] = new TcpClientConnection() { - Connection = new TCPConnection( client, connectionID ), - RemoteAddress = remote.Address - }; + _parent.Connections[connectionID] = new ServerConnection( client, _parent, remote ); Console.WriteLine( "New User Connected" ); }