using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Net; using System.Threading.Tasks; using Mistox.ResevedPackets; namespace Mistox.Server { public class MistoxServer { public string ServerName { get; private 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 string ConnectionKey { get; set; } = ""; // The connection key for the server public int ConnectionTimeoutSeconds { get; set; } = 10; // If no reply in time kill the connection public bool Alive { get; set; } = true; // Is the server running public ConcurrentDictionary Connections = new(); // All the active connections. key is username private readonly ServerListener Listener; // The Connection Listener singleton public MistoxServer(IPEndPoint ListenerAddress, string serverName) { ServerName = serverName; Listener = new ServerListener(ListenerAddress, this); Console.WriteLine( "The Server is initilized and waiting for clients to connect at port : " + ListenerAddress.Port ); } async Task RunAsync(Func work) { try{ await work(); } catch (Exception ex) { Console.WriteLine($"Error during background packet processing: {ex.Message}"); } } ///////////////////////////// // Heartbeat ///////////////////////////// async Task BuildServerList(ServerConnection user, HeartbeatPacket packet) { packet.ServerList = new List(); foreach( ServerConnection cur in Connections.Values) { packet.ServerList.Add( new UserTableObject() { UserName = cur.UserName, Position = cur.Position, IpAddress = cur.getRemote() } ); } await user.Send( packet ); } ///////////////////////////// // Auth ///////////////////////////// async Task ProcessAuthRequest(ServerConnection user, UserAuthPacket packet) { packet.ServerName = ServerName; if (packet.ConnectionKey == ConnectionKey) { packet.AuthSuccessful = 1; Connections.TryRemove(user.UserName, out _); user.UserName = packet.UserName; Connections[packet.UserName] = user; user.IsAuthenticated = true; InvokeUserConnected(user); } else { packet.AuthSuccessful = -1; } await user.Send(packet); } ///////////////////////////// // Events ///////////////////////////// public event Action? onUserConnected; public void InvokeUserConnected( ServerConnection user ){ Connections[user.UserName] = user; onUserConnected?.Invoke( user ); } public event Action? onDataReceived; public void InvokeDataReceived( ServerConnection user, dynamic data ){ switch (data) { case UserAuthPacket packet when !user.IsAuthenticated: _ = RunAsync(async () => { await ProcessAuthRequest(user, packet); }); break; case HeartbeatPacket packet when user.IsAuthenticated: user.Position = packet.Position; _ = RunAsync(async () => { await BuildServerList(user, packet); }); break; case null: Console.WriteLine("Received a null packet."); break; default: onDataReceived?.Invoke(user, data); break; } } public event Action? onUserDisconnected; public void InvokeUserDisconnected( ServerConnection user ){ Connections.Remove(user.UserName, out _); onUserDisconnected?.Invoke( user ); } ///////////////////////////// // Send Methods ///////////////////////////// public async Task SendAllClients(Packet data) { foreach( ServerConnection cur in Connections.Values ) { await cur.Send(data); } } public async Task SendClient(string UserName, Packet data) { await Connections[UserName].Send( data ); } } }