Network System
@esengine/network provides a type-safe network synchronization solution based on @esengine/rpc for multiplayer games, including entity synchronization, input handling, and state interpolation.
Overview
Section titled “Overview”The network module consists of two core packages:
| Package | Description |
|---|---|
@esengine/rpc | Type-safe RPC communication library |
@esengine/network | RPC-based game networking plugin |
Installation
Section titled “Installation”npm install @esengine/network
@esengine/rpcis automatically installed as a dependency.
Architecture
Section titled “Architecture”Client Server┌────────────────────┐ ┌────────────────┐│ NetworkPlugin │◄── WS ───►│ RpcServer ││ ├─ NetworkService │ │ ├─ Protocol ││ ├─ SyncSystem │ │ └─ Handlers ││ ├─ SpawnSystem │ └────────────────┘│ └─ InputSystem │└────────────────────┘Quick Start
Section titled “Quick Start”Client
Section titled “Client”import { Core, Scene } from '@esengine/ecs-framework';import { NetworkPlugin, NetworkIdentity, NetworkTransform } from '@esengine/network';
// Define game sceneclass GameScene extends Scene { initialize(): void { this.name = 'Game'; }}
// InitializeCore.create({ debug: false });const scene = new GameScene();Core.setScene(scene);
// Install network pluginconst networkPlugin = new NetworkPlugin();await Core.installPlugin(networkPlugin);
// Register prefab factorynetworkPlugin.registerPrefab('player', (scene, spawn) => { const entity = scene.createEntity(`player_${spawn.netId}`);
const identity = entity.addComponent(new NetworkIdentity()); identity.netId = spawn.netId; identity.ownerId = spawn.ownerId; identity.bIsLocalPlayer = spawn.ownerId === networkPlugin.localPlayerId;
entity.addComponent(new NetworkTransform()); return entity;});
// Connect to serverconst success = await networkPlugin.connect({ url: 'ws://localhost:3000', playerName: 'Player1', roomId: 'room-1' // optional});
if (success) { console.log('Connected! Player ID:', networkPlugin.localPlayerId);}Server
Section titled “Server”import { serve } from '@esengine/rpc/server';import { gameProtocol } from '@esengine/network';
let nextPlayerId = 1;
const server = serve(gameProtocol, { port: 3000, api: { join: async (input, conn) => { const playerId = nextPlayerId++; console.log(`Player joined: ${input.playerName} (ID: ${playerId})`); return { playerId, roomId: input.roomId ?? 'default' }; }, leave: async (_input, conn) => { console.log(`Player left: ${conn.id}`); }, reconnect: async (input, conn) => { // Handle reconnection logic return { success: true }; }, }, onConnect: (conn) => console.log(`Client connected: ${conn.id}`), onDisconnect: (conn) => console.log(`Client disconnected: ${conn.id}`), onStart: (port) => console.log(`Server running on ws://localhost:${port}`),});
await server.start();Custom Protocol
Section titled “Custom Protocol”You can define your own protocol using @esengine/rpc:
import { rpc } from '@esengine/rpc';
// Define custom protocolexport const myProtocol = rpc.define({ api: { login: rpc.api<{ username: string }, { token: string }>(), getData: rpc.api<{ id: number }, { data: object }>(), }, msg: { chat: rpc.msg<{ from: string; text: string }>(), notification: rpc.msg<{ type: string; content: string }>(), },});
// Create service with custom protocolimport { RpcService } from '@esengine/network';
const service = new RpcService(myProtocol);await service.connect({ url: 'ws://localhost:3000' });
// Type-safe API callsconst result = await service.call('login', { username: 'test' });console.log(result.token);
// Type-safe message listeningservice.on('chat', (data) => { console.log(`${data.from}: ${data.text}`);});Interactive Demo
Section titled “Interactive Demo”Experience the network synchronization system with interactive demos:
- Network Sync Demo - Client prediction and interpolation visualization
Documentation
Section titled “Documentation”- Client Usage - NetworkPlugin, components and systems
- Server Side - GameServer and Room management
- Distributed Rooms - Multi-server room management and player routing
- State Sync - Interpolation and snapshot buffering
- Client Prediction - Input prediction and server reconciliation
- Area of Interest (AOI) - View filtering and bandwidth optimization
- Delta Compression - State delta synchronization
- API Reference - Complete API documentation
Service Tokens
Section titled “Service Tokens”For dependency injection:
import { NetworkServiceToken, NetworkSyncSystemToken, NetworkSpawnSystemToken, NetworkInputSystemToken, NetworkPredictionSystemToken, NetworkAOISystemToken,} from '@esengine/network';
const networkService = services.get(NetworkServiceToken);const predictionSystem = services.get(NetworkPredictionSystemToken);const aoiSystem = services.get(NetworkAOISystemToken);Blueprint Nodes
Section titled “Blueprint Nodes”The network module provides visual scripting support:
IsLocalPlayer- Check if entity is local playerIsServer- Check if running on serverHasAuthority- Check if has authority over entityGetNetworkId- Get entity’s network IDGetLocalPlayerId- Get local player ID