Rework server to be non static

This commit is contained in:
derek.holloway committed 2026-07-08 13:13:23 -07:00
1 parent 875a5ae3e9
commit 6f910e969a
6 files changed
+96 -78

No files matched your search

+12 -21
View File
@@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -8,44 +7,36 @@ using Mistox.Server;
namespace Mistox { namespace Mistox {
public class ServerInterface { public class ServerInterface {
public static MistoxServer ServerInstance; // Server Data public static ServerEntity ServerInstance; // Server Data
public static ServerListener Listener; // Server Listener
public static ConcurrentDictionary<string, TcpClientConnection> Connections; // Server Connections
public event Action<TcpClientConnection> onUserConnected; // User connected event handler public event Action<ServerConnection> onUserConnected; // User connected event handler
public event Action<dynamic> onDataReceived; // Data received event handler public event Action<dynamic> onDataReceived; // Data received event handler
public event Action<TcpClientConnection> onUserDisconnected; // User disconnected event handler public event Action<ServerConnection> onUserDisconnected; // User disconnected event handler
public ServerInterface( int port, string serverName ) { public ServerInterface( int port, string serverName ) {
IPEndPoint listenerAddress = new IPEndPoint( IPAddress.Any, port );
// Initilize the server // Initilize the server
ServerInstance = new MistoxServer() { ServerInstance = new ServerEntity(listenerAddress);
ServerName = serverName, ServerInstance.ServerName = serverName;
BindAddress = IPAddress.Any,
Port = port,
MaxClients = 10
};
Connections = new ConcurrentDictionary<string, TcpClientConnection>();
// Setup the event handlers // 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.onDataReceived += (dynamic data) => { onDataReceived?.Invoke(data); };
ServerInstance.onUserDisconnected += (TcpClientConnection disconnectedUser) => { onUserDisconnected?.Invoke(disconnectedUser); }; ServerInstance.onUserDisconnected += (ServerConnection disconnectedUser) => { onUserDisconnected?.Invoke(disconnectedUser); };
// Start listening for connections and wire up the server
Listener = new ServerListener();
Console.WriteLine( "The Server is initilized and waiting for clients to connect at port : " + port ); Console.WriteLine( "The Server is initilized and waiting for clients to connect at port : " + port );
} }
public async Task SendAllClients<Packet>(Packet data) { public async Task SendAllClients<Packet>(Packet data) {
foreach( KeyValuePair<string, TcpClientConnection> cur in Connections ) { foreach( KeyValuePair<string, ServerConnection> cur in ServerInstance.Connections ) {
await cur.Value.Connection.Send(data); await cur.Value.Send(data);
} }
} }
public async Task SendClient<Packet>(string UserName, Packet data) { public async Task SendClient<Packet>(string UserName, Packet data) {
await Connections[UserName].Connection.Send( data ); await ServerInstance.Connections[UserName].Send( data );
} }
} }
} }
@@ -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<TcpClientConnection> onUserConnected;
public void InvokeUserConnected( TcpClientConnection user ){ onUserConnected?.Invoke( user ); }
public event Action<dynamic> onDataReceived;
public void InvokeDataReceived( dynamic data ){ onDataReceived?.Invoke( data ); }
public event Action<TcpClientConnection> onUserDisconnected;
public void InvokeUserDisconnected( TcpClientConnection user ){ onUserDisconnected?.Invoke( user ); }
}
}
@@ -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
}
}
+43
View File
@@ -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<string, ServerConnection> Connections;
public ServerEntity(IPEndPoint ListenerAddress) {
Listener = new ServerListener(ListenerAddress, this);
Connections = new ConcurrentDictionary<string, ServerConnection>();
}
// Functions
public event Action<ServerConnection> onUserConnected;
public void InvokeUserConnected( ServerConnection user ){
onUserConnected?.Invoke( user );
}
public event Action<dynamic> onDataReceived;
public void InvokeDataReceived( dynamic data ){
onDataReceived?.Invoke( data );
}
public event Action<ServerConnection> onUserDisconnected;
public void InvokeUserDisconnected( ServerConnection user ){
onUserDisconnected?.Invoke( user );
}
}
}
@@ -1,20 +1,30 @@
using System; using System;
using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Numerics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Mistox.Attributes;
namespace Mistox.Server { 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; TcpClient _client;
TcpClientConnection _connection; IPEndPoint _remoteAddress;
public TCPConnection( TcpClient tcpClient, string ConnectionID ) { public ServerConnection( TcpClient tcpClient, ServerEntity parent, IPEndPoint remote ) {
_parent = parent;
_client = tcpClient; _client = tcpClient;
_remoteAddress = remote;
_client.NoDelay = true; _client.NoDelay = true;
_connection = ServerInterface.Connections[ConnectionID];
ServerInterface.ServerInstance.InvokeUserConnected( ServerInterface.Connections[ConnectionID] ); ServerInterface.ServerInstance.InvokeUserConnected( this );
// Start the receive thread // Start the receive thread
Thread receiveThread = new Thread(async () => await ReceiveThread() ); Thread receiveThread = new Thread(async () => await ReceiveThread() );
receiveThread.Start(); receiveThread.Start();
@@ -25,7 +35,16 @@ namespace Mistox.Server {
try { try {
byte[] headerBuffer = new byte[DataPacket.HeaderLength]; byte[] headerBuffer = new byte[DataPacket.HeaderLength];
byte[] payloadBuffer = new byte[65535]; // Upper limit for safety 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 // Read the header off
await ns.ReadExactlyAsync(headerBuffer, 0, 3); await ns.ReadExactlyAsync(headerBuffer, 0, 3);
@@ -46,7 +65,7 @@ namespace Mistox.Server {
dynamic dataobject = DataPacket.Deserialize( packetbyte, dataLength, payloadBuffer.AsMemory(DataPacket.HeaderLength, dataLength) ); dynamic dataobject = DataPacket.Deserialize( packetbyte, dataLength, payloadBuffer.AsMemory(DataPacket.HeaderLength, dataLength) );
// Return the object // Return the object
ServerInterface.ServerInstance.InvokeDataReceived(dataobject); _parent.InvokeDataReceived(dataobject);
} }
} catch (Exception e) { } catch (Exception e) {
@@ -63,10 +82,11 @@ namespace Mistox.Server {
} }
public void Dispose() { public void Dispose() {
_connection.Connected = false; Connected = false;
ServerInterface.ServerInstance.InvokeUserDisconnected( _connection ); _parent.InvokeUserDisconnected( this );
ServerInterface.ServerInstance.Alive = false; Connected = false;
ServerInterface.Connections.TryRemove( _connection.UserName, out _ ); _parent.Alive = false;
// Remove this item from the Dictionary on the Server for garbage collection
} }
} }
} }
+9 -8
View File
@@ -9,30 +9,31 @@ using System.Threading.Tasks;
namespace Mistox.Server { namespace Mistox.Server {
public class ServerListener : IDisposable { public class ServerListener : IDisposable {
ServerEntity _parent;
IPEndPoint _bindAddress;
TcpListener _Listener; TcpListener _Listener;
public ServerListener() { public ServerListener(IPEndPoint listenerAddress, ServerEntity parent) {
_bindAddress = listenerAddress;
_parent = parent;
Thread ConnectionThread = new Thread(async () => await ListenerThread() ); Thread ConnectionThread = new Thread(async () => await ListenerThread() );
ConnectionThread.Start(); ConnectionThread.Start();
} }
async Task ListenerThread() { async Task ListenerThread() {
_Listener = new TcpListener( ServerInterface.ServerInstance.BindAddress, ServerInterface.ServerInstance.Port ); _Listener = new TcpListener( _bindAddress );
_Listener.Start(); _Listener.Start();
while( ServerInterface.ServerInstance.Alive ) { while( _parent.Alive ) {
TcpClient client = await _Listener.AcceptTcpClientAsync(); TcpClient client = await _Listener.AcceptTcpClientAsync();
if (ServerInterface.Connections.Count + 1 > ServerInterface.ServerInstance.MaxClients) { if (_parent.Connections.Count + 1 > _parent.MaxClients) {
continue; continue;
} }
IPEndPoint remote = client.Client.RemoteEndPoint as IPEndPoint; IPEndPoint remote = client.Client.RemoteEndPoint as IPEndPoint;
string connectionID = Guid.NewGuid().ToString(); string connectionID = Guid.NewGuid().ToString();
ServerInterface.Connections[connectionID] = new TcpClientConnection() { _parent.Connections[connectionID] = new ServerConnection( client, _parent, remote );
Connection = new TCPConnection( client, connectionID ),
RemoteAddress = remote.Address
};
Console.WriteLine( "New User Connected" ); Console.WriteLine( "New User Connected" );
} }