@utsp/network-server
Version:
UTSP Network Server - Server-side communication adapters
572 lines (568 loc) • 17.8 kB
TypeScript
import { INetworkServer, NetworkServerOptions, ClientInfo, ServerEventHandler, ConnectionHandler, DisconnectionHandler } from '@utsp/types';
export { AnyNetworkMessage, ChatMessage, ClientInfo, ConnectionHandler, DisconnectionHandler, ErrorMessage, INetworkServer, InputMessage, JoinMessage, JoinResponseMessage, LeaveMessage, LoadMessage, MessageType, NetworkMessage, NetworkServerOptions, PingMessage, PongMessage, ServerEventHandler, UpdateMessage } from '@utsp/types';
/**
* Socket.IO Server Implementation
*
* Production-ready Socket.IO server for UTSP real-time networking.
* Implements the INetworkServer interface with room management, client tracking,
* and comprehensive event handling. Node.js only.
*
* @example Basic Server
* ```typescript
* import { SocketIOServer } from '@utsp/network-server';
*
* const server = new SocketIOServer({
* port: 3000,
* host: '0.0.0.0',
* debug: false
* });
*
* await server.start();
* console.log('Server running on port 3000');
*
* server.onConnect((clientId) => {
* console.log('Client connected:', clientId);
* server.sendToClient(clientId, 'welcome', { message: 'Hello!' });
* });
*
* server.on('input', (clientId, data) => {
* console.log('Input from', clientId, data);
* server.broadcast('update', data);
* });
* ```
*
* @example With Rooms
* ```typescript
* server.onConnect((clientId) => {
* server.joinRoom(clientId, 'lobby');
* server.sendToRoom('lobby', 'playerJoined', { clientId });
* });
*
* server.on('joinGame', (clientId, { gameId }) => {
* server.leaveRoom(clientId, 'lobby');
* server.joinRoom(clientId, `game-${gameId}`);
* });
* ```
*
* @example Client Data Storage
* ```typescript
* server.onConnect((clientId) => {
* server.setClientData(clientId, 'username', 'Player1');
* server.setClientData(clientId, 'score', 0);
* });
*
* server.on('updateScore', (clientId, { points }) => {
* const currentScore = server.getClientData(clientId, 'score') || 0;
* server.setClientData(clientId, 'score', currentScore + points);
* });
* ```
*/
/**
* Socket.IO server implementation for UTSP network communication
*
* Features:
* - Connection management with max connections limit
* - Room-based broadcasting
* - Client data storage per connection
* - Volatile messaging for high-frequency updates
* - Comprehensive event handling system
* - Connection/disconnection lifecycle hooks
* - Server statistics tracking
* - CORS configuration
*
* @implements {INetworkServer}
*/
declare class SocketIOServer implements INetworkServer {
private io;
private httpServer;
private options;
private running;
private clients;
private clientData;
private connectionHandlers;
private disconnectionHandlers;
private eventHandlers;
private stats;
/**
* Create a new Socket.IO server instance
*
* @param options - Server configuration options
* @throws {Error} If port is invalid or options are malformed
*
* @example
* ```typescript
* const server = new SocketIOServer({
* port: 3000,
* host: '0.0.0.0',
* cors: { origin: '*' },
* maxConnections: 1000,
* pingInterval: 25000,
* pingTimeout: 5000,
* debug: true
* });
* ```
*/
constructor(options: NetworkServerOptions);
/**
* Check if server is currently running
*
* @returns true if server is running and accepting connections
*
* @example
* ```typescript
* if (server.isRunning()) {
* console.log('Server is active');
* }
* ```
*/
isRunning(): boolean;
/**
* Start the server and begin accepting connections
*
* Creates an HTTP server, initializes Socket.IO, and starts listening
* on the configured port and host.
*
* @returns Promise resolving when server is ready to accept connections
* @throws {Error} If server fails to start (port in use, permission denied, etc.)
*
* @example
* ```typescript
* try {
* await server.start();
* console.log('Server started successfully');
* } catch (error) {
* console.error('Failed to start server:', error);
* }
* ```
*/
start(): Promise<void>;
/**
* Stop the server and disconnect all clients
*
* Gracefully shuts down the server by disconnecting all clients,
* closing Socket.IO, and stopping the HTTP server.
*
* @returns Promise resolving when server is fully stopped
*
* @example
* ```typescript
* await server.stop();
* console.log('Server stopped');
* ```
*/
stop(): Promise<void>;
/**
* Get list of all connected client IDs
*
* @returns Array of client socket IDs
*
* @example
* ```typescript
* const clients = server.getClients();
* console.log(`${clients.length} clients connected`);
* ```
*/
getClients(): string[];
/**
* Get information about a specific client
*
* @param clientId - Client socket ID
* @returns Client information object, or null if client not found
*
* @example
* ```typescript
* const info = server.getClientInfo(clientId);
* if (info) {
* console.log('Client:', info.id);
* console.log('Connected at:', new Date(info.connectedAt));
* console.log('Address:', info.address);
* console.log('Data:', info.data);
* }
* ```
*/
getClientInfo(clientId: string): ClientInfo | null;
/**
* Send an event to a specific client
*
* Sends a message to a single client identified by their socket ID.
* Message is silently dropped if client is not connected.
*
* @param clientId - Client socket ID
* @param event - Event name
* @param data - Event payload (any serializable data)
*
* @example
* ```typescript
* server.sendToClient(clientId, 'notification', {
* type: 'info',
* message: 'You won!'
* });
* ```
*/
sendToClient(clientId: string, event: string, data: any): void;
/**
* Send volatile message to a specific client
*
* Sends a "volatile" message that can be dropped if the network is congested.
* Perfect for high-frequency updates (game state, animations, dynamic layers)
* where missing a frame is acceptable.
*
* **Note:** Uses `.compress(false)` instead of `.volatile` due to Socket.IO limitations.
* This disables compression for better performance with frequent updates.
*
* @param clientId - Client socket ID
* @param event - Event name
* @param data - Event payload
*
* @example
* ```typescript
* // Send 60 updates per second - some can be dropped
* setInterval(() => {
* server.sendToClientVolatile(clientId, 'gameState', {
* position: player.position,
* velocity: player.velocity
* });
* }, 16); // ~60 FPS
* ```
*/
sendToClientVolatile(clientId: string, event: string, data: any): void;
/**
* Broadcast an event to all connected clients
*
* Sends a message to every connected client simultaneously.
*
* @param event - Event name
* @param data - Event payload
*
* @example
* ```typescript
* server.broadcast('serverMessage', {
* type: 'announcement',
* message: 'Server restarting in 5 minutes'
* });
* ```
*/
broadcast(event: string, data: any): void;
/**
* Broadcast volatile message to all clients
*
* Sends a volatile message to all clients. Messages can be dropped
* if the network is congested. Useful for high-frequency broadcasts.
*
* @param event - Event name
* @param data - Event payload
*
* @example
* ```typescript
* // Broadcast game tick (60 times per second)
* setInterval(() => {
* server.broadcastVolatile('tick', {
* timestamp: Date.now(),
* frame: frameCount++
* });
* }, 16);
* ```
*/
broadcastVolatile(event: string, data: any): void;
/**
* Broadcast to all clients except one
*
* Sends a message to all connected clients except the specified one.
* Useful for broadcasting player actions to other players.
*
* @param excludeClientId - Client ID to exclude from broadcast
* @param event - Event name
* @param data - Event payload
*
* @example
* ```typescript
* // When a player moves, tell everyone else
* server.on('playerMove', (clientId, position) => {
* server.broadcastExcept(clientId, 'playerMoved', {
* playerId: clientId,
* position
* });
* });
* ```
*/
broadcastExcept(excludeClientId: string, event: string, data: any): void;
/**
* Send event to all clients in a room
*
* Broadcasts a message to all clients that have joined the specified room.
*
* @param room - Room name
* @param event - Event name
* @param data - Event payload
*
* @example
* ```typescript
* // Send message to all players in a game
* server.sendToRoom('game-123', 'gameUpdate', {
* score: { team1: 10, team2: 8 }
* });
* ```
*/
sendToRoom(room: string, event: string, data: any): void;
/**
* Add a client to a room
*
* Rooms allow grouping clients for targeted broadcasts. A client
* can be in multiple rooms simultaneously.
*
* @param clientId - Client socket ID
* @param room - Room name
*
* @example
* ```typescript
* // When player joins a game
* server.on('joinGame', (clientId, { gameId }) => {
* server.joinRoom(clientId, `game-${gameId}`);
* server.sendToRoom(`game-${gameId}`, 'playerJoined', {
* clientId,
* playerCount: server.getRoomClients(`game-${gameId}`).length
* });
* });
* ```
*/
joinRoom(clientId: string, room: string): void;
/**
* Remove a client from a room
*
* @param clientId - Client socket ID
* @param room - Room name
*
* @example
* ```typescript
* server.on('leaveGame', (clientId) => {
* server.leaveRoom(clientId, 'game-123');
* server.joinRoom(clientId, 'lobby');
* });
* ```
*/
leaveRoom(clientId: string, room: string): void;
/**
* Get list of all client IDs in a room
*
* @param room - Room name
* @returns Array of client socket IDs in the room
*
* @example
* ```typescript
* const players = server.getRoomClients('game-123');
* console.log(`${players.length} players in game`);
* ```
*/
getRoomClients(room: string): string[];
/**
* Forcefully disconnect a client
*
* Immediately closes the client's connection. Use sparingly as it
* does not allow graceful cleanup on the client side.
*
* @param clientId - Client socket ID
* @param reason - Reason for disconnection (default: 'Server disconnect')
*
* @example
* ```typescript
* // Kick idle players
* if (isIdle(clientId)) {
* server.disconnectClient(clientId, 'Idle timeout');
* }
* ```
*/
disconnectClient(clientId: string, reason?: string): void;
/**
* Register an event listener for client messages
*
* Handles events sent from clients. Multiple handlers can be registered
* for the same event. Handlers receive the client ID and event data.
*
* @param event - Event name to listen for
* @param handler - Callback function (clientId, data) => void
*
* @example
* ```typescript
* server.on<InputMessage>('input', (clientId, data) => {
* console.log('Input from', clientId, data);
* // Process input and broadcast to others
* server.broadcastExcept(clientId, 'playerAction', data);
* });
*
* server.on('chat', (clientId, message) => {
* const username = server.getClientData(clientId, 'username');
* server.broadcast('chatMessage', { username, message });
* });
* ```
*/
on<T = any>(event: string, handler: ServerEventHandler<T>): void;
/**
* Register a connection handler
*
* Called whenever a new client successfully connects. Use this to
* initialize client state, send welcome messages, etc.
*
* @param handler - Callback function receiving client ID
*
* @example
* ```typescript
* server.onConnect((clientId) => {
* console.log('Client connected:', clientId);
* server.setClientData(clientId, 'joinedAt', Date.now());
* server.joinRoom(clientId, 'lobby');
* server.sendToClient(clientId, 'welcome', {
* message: 'Welcome to the server!',
* serverId: 'game-server-1'
* });
* });
* ```
*/
onConnect(handler: ConnectionHandler): void;
/**
* Register a disconnection handler
*
* Called whenever a client disconnects (gracefully or due to error).
* Use this for cleanup and notifying other clients.
*
* @param handler - Callback function receiving client ID and reason
*
* @example
* ```typescript
* server.onDisconnect((clientId, reason) => {
* console.log('Client disconnected:', clientId, reason);
* const username = server.getClientData(clientId, 'username');
* server.broadcast('playerLeft', { username, reason });
* });
* ```
*/
onDisconnect(handler: DisconnectionHandler): void;
/**
* Unregister a specific event handler
*
* Removes a previously registered handler for an event. The handler
* reference must be the exact same function object that was registered.
*
* @param event - Event name
* @param handler - Handler function to remove (must be same reference)
*
* @example
* ```typescript
* const handler = (clientId, data) => { ... };
* server.on('input', handler);
* // Later...
* server.off('input', handler);
* ```
*/
off<T = any>(event: string, handler: ServerEventHandler<T>): void;
/**
* Store arbitrary data for a client
*
* Associates key-value data with a client connection. Data is
* automatically cleared when the client disconnects.
*
* @param clientId - Client socket ID
* @param key - Data key
* @param value - Data value (any serializable type)
*
* @example
* ```typescript
* server.onConnect((clientId) => {
* server.setClientData(clientId, 'username', 'Player1');
* server.setClientData(clientId, 'score', 0);
* server.setClientData(clientId, 'team', 'red');
* });
*
* server.on('scorePoint', (clientId) => {
* const score = server.getClientData(clientId, 'score') || 0;
* server.setClientData(clientId, 'score', score + 1);
* });
* ```
*/
setClientData(clientId: string, key: string, value: any): void;
/**
* Retrieve stored data for a client
*
* @param clientId - Client socket ID
* @param key - Data key
* @returns Stored value, or undefined if not found
*
* @example
* ```typescript
* const username = server.getClientData(clientId, 'username');
* const score = server.getClientData(clientId, 'score') || 0;
* ```
*/
getClientData(clientId: string, key: string): any;
/**
* Get server statistics
*
* Returns current server metrics including connected clients,
* total connections since start, and uptime.
*
* @returns Object containing server stats
*
* @example
* ```typescript
* const stats = server.getStats();
* console.log('Connected:', stats.connectedClients);
* console.log('Total connections:', stats.totalConnections);
* console.log('Uptime:', Math.floor(stats.uptime / 1000), 'seconds');
* ```
*/
getStats(): {
connectedClients: number;
totalConnections: number;
uptime: number;
};
/**
* Destroy the server and clean up all resources
*
* Stops the server, disconnects all clients, and clears all handlers.
* Server instance cannot be reused after calling destroy().
*
* @returns Promise resolving when cleanup is complete
*
* @example
* ```typescript
* // Graceful shutdown
* process.on('SIGTERM', async () => {
* console.log('Shutting down...');
* await server.destroy();
* process.exit(0);
* });
* ```
*/
destroy(): Promise<void>;
/**
* Handle new client connection
*
* Called when a client successfully connects. Checks max connections limit,
* stores client data, sets up event handlers, and calls connection handlers.
*
* @param socket - Socket.IO socket instance
* @private
*/
private handleConnection;
/**
* Setup event handlers for a client socket
*
* Registers disconnect handler and all custom event handlers for this client.
* Called once when client connects.
*
* @param socket - Socket.IO socket instance
* @private
*/
private setupClientHandlers;
/**
* Debug logging utility
*
* Logs messages when debug mode is enabled. Uses console.warn
* to ensure visibility in production environments.
*
* @param message - Log message
* @param data - Optional data to log
* @private
*/
private log;
}
export { SocketIOServer, SocketIOServer as SocketIOServerType };