using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
namespace codelanguage
{
///
/// Represents a connected client on the server (networking)
///
public class ServerClientConnection
{
public int ClientId { get; set; }
public string Username { get; set; }
public TcpClient Socket { get; set; }
public bool IsAuthenticated { get; set; }
public bool IsConnected { get; set; }
public DateTime ConnectedAt { get; set; }
public DateTime LastHeartbeat { get; set; }
public int Latency { get; set; } // milliseconds
// Client state
public CraftValue PlayerMob { get; set; } // Player character
public List OwnedObjects { get; set; } = new();
// Message queues
public MessageQueue IncomingMessages { get; set; } = new();
public MessageQueue OutgoingMessages { get; set; } = new();
public ServerClientConnection(int clientId)
{
ClientId = clientId;
ConnectedAt = DateTime.UtcNow;
LastHeartbeat = DateTime.UtcNow;
IsConnected = true;
}
///
/// Check if this connection has timed out
///
public bool IsTimedOut()
{
return DateTime.UtcNow - LastHeartbeat > TimeSpan.FromMilliseconds(NetworkConfig.HeartbeatTimeoutMs);
}
///
/// Check if heartbeat is due
///
public bool IsHeartbeatDue()
{
return DateTime.UtcNow - LastHeartbeat > TimeSpan.FromMilliseconds(NetworkConfig.HeartbeatIntervalMs);
}
///
/// Refresh client liveness only, preserving the previous latency value.
///
public void TouchHeartbeat(int latency = -1)
{
LastHeartbeat = DateTime.UtcNow;
if (latency >= 0)
Latency = latency;
}
public override string ToString()
{
return $"ClientConnection(Id={ClientId}, User={Username ?? "unknown"}, Connected={ConnectedAt}, Auth={IsAuthenticated})";
}
}
///
/// Server-side connection manager
/// Handles client lifecycle: connect, authenticate, disconnect
///
public class ConnectionManager
{
private Dictionary _clients = new();
private int _nextClientId = 1;
private object _clientLock = new object();
///
/// Create a new client connection
///
public ServerClientConnection CreateConnection(TcpClient socket = null)
{
lock (_clientLock)
{
int clientId = _nextClientId++;
var connection = new ServerClientConnection(clientId)
{
Socket = socket
};
_clients[clientId] = connection;
return connection;
}
}
///
/// Get a connection by ID
///
public ServerClientConnection? GetConnection(int clientId)
{
lock (_clientLock)
{
_clients.TryGetValue(clientId, out var conn);
return conn;
}
}
///
/// Authenticate a client
///
public bool AuthenticateClient(int clientId, string username)
{
lock (_clientLock)
{
if (!_clients.TryGetValue(clientId, out var conn))
return false;
conn.Username = username;
conn.IsAuthenticated = true;
return true;
}
}
///
/// Disconnect a client
///
public bool DisconnectClient(int clientId)
{
lock (_clientLock)
{
if (!_clients.TryGetValue(clientId, out var conn))
return false;
conn.IsConnected = false;
conn.Socket?.Close();
_clients.Remove(clientId);
return true;
}
}
///
/// Update heartbeat for a client
///
public void UpdateHeartbeat(int clientId, int latency = 0)
{
lock (_clientLock)
{
if (_clients.TryGetValue(clientId, out var conn))
{
conn.LastHeartbeat = DateTime.UtcNow;
if (latency >= 0)
conn.Latency = latency;
}
}
}
public List GetConnectedClients()
{
lock (_clientLock)
{
var result = new List(_clients.Count);
foreach (var kvp in _clients)
if (kvp.Value.IsConnected) result.Add(kvp.Value);
return result;
}
}
public List GetAuthenticatedClients()
{
lock (_clientLock)
{
var result = new List(_clients.Count);
foreach (var kvp in _clients)
if (kvp.Value.IsConnected && kvp.Value.IsAuthenticated) result.Add(kvp.Value);
return result;
}
}
public List DetectDeadConnections()
{
var deadClients = new List();
List toRemove;
lock (_clientLock)
{
toRemove = new List(_clients.Count);
foreach (var kvp in _clients)
{
if (kvp.Value.IsTimedOut())
{
deadClients.Add(kvp.Key);
toRemove.Add(kvp.Key);
}
}
foreach (var id in toRemove)
{
if (_clients.TryGetValue(id, out var conn))
{
conn.IsConnected = false;
_clients.Remove(id);
}
}
}
return deadClients;
}
///
/// Touch a client's heartbeat without otherwise enqueueing any message.
/// Active inbound traffic uses this to refresh liveness so legitimate
/// busy clients are not killed by the heartbeat timeout.
///
public void TouchHeartbeat(int clientId, int latency = -1)
{
lock (_clientLock)
{
if (_clients.TryGetValue(clientId, out var conn))
{
conn.LastHeartbeat = DateTime.UtcNow;
if (latency >= 0)
conn.Latency = latency;
}
}
}
///
/// Send message to a specific client
///
public void SendToClient(int clientId, NetworkMessage msg)
{
lock (_clientLock)
{
if (_clients.TryGetValue(clientId, out var conn))
{
msg.ClientId = clientId;
conn.OutgoingMessages.Enqueue(msg);
}
}
}
public void BroadcastToClients(NetworkMessage msg, int excludeClientId = -1)
{
List<(int id, ServerClientConnection conn)> targets;
lock (_clientLock)
{
targets = new List<(int, ServerClientConnection)>(_clients.Count);
foreach (var kvp in _clients)
if (kvp.Value.IsConnected && kvp.Key != excludeClientId)
targets.Add((kvp.Key, kvp.Value));
}
// Enqueue outside lock to reduce contention. Each recipient gets its own
// message copy so mutating ClientId for one recipient cannot leak into
// messages already queued for other clients.
foreach (var (id, conn) in targets)
{
var recipientMessage = msg.Clone();
recipientMessage.ClientId = id;
conn.OutgoingMessages.Enqueue(recipientMessage);
}
}
public void BroadcastToAuthenticatedClients(NetworkMessage msg, int excludeClientId = -1)
{
List<(int id, ServerClientConnection conn)> targets;
lock (_clientLock)
{
targets = new List<(int, ServerClientConnection)>(_clients.Count);
foreach (var kvp in _clients)
if (kvp.Value.IsConnected && kvp.Value.IsAuthenticated && kvp.Key != excludeClientId)
targets.Add((kvp.Key, kvp.Value));
}
// Each recipient must observe a distinct message object. Reusing the same
// instance and mutating ClientId after enqueueing would let a later
// assignment leak into messages already queued for other clients.
foreach (var (id, conn) in targets)
{
var recipientMessage = msg.Clone();
recipientMessage.ClientId = id;
conn.OutgoingMessages.Enqueue(recipientMessage);
}
}
public int GetConnectionCount()
{
lock (_clientLock)
{
int count = 0;
foreach (var kvp in _clients)
if (kvp.Value.IsConnected) count++;
return count;
}
}
public int GetTotalClientCount()
{
lock (_clientLock)
{
return _clients.Count;
}
}
public bool IsAtCapacity()
{
return GetConnectionCount() >= NetworkConfig.MaxConnections;
}
///
/// Clear all connections (for testing)
///
public void ClearAll()
{
lock (_clientLock)
{
foreach (var conn in _clients.Values)
{
conn.Socket?.Close();
}
_clients.Clear();
}
}
}
///
/// Client-side connection state
///
public class ClientConnectionState
{
public int ClientId { get; set; }
public string ServerAddress { get; set; }
public int ServerPort { get; set; }
public bool IsConnected { get; set; }
public bool IsAuthenticated { get; set; }
public string SessionToken { get; set; }
public DateTime ConnectedAt { get; set; }
public DateTime LastHeartbeat { get; set; }
public int Latency { get; set; }
// Message queues
public MessageQueue IncomingMessages { get; set; } = new();
public MessageQueue OutgoingMessages { get; set; } = new();
public ClientConnectionState()
{
ConnectedAt = DateTime.UtcNow;
LastHeartbeat = DateTime.UtcNow;
}
public bool IsTimedOut()
{
return DateTime.UtcNow - LastHeartbeat > TimeSpan.FromMilliseconds(NetworkConfig.HeartbeatTimeoutMs);
}
public void TouchHeartbeat(int latency = -1)
{
LastHeartbeat = DateTime.UtcNow;
if (latency >= 0)
Latency = latency;
}
public override string ToString()
{
return $"ClientConnectionState(Id={ClientId}, Server={ServerAddress}:{ServerPort}, Connected={IsConnected}, Auth={IsAuthenticated})";
}
}
}