netcore-networking
v2.3.0
Published
Transport-agnostic, room-based networking library for multiplayer games
Maintainers
Readme
netcore-networking
Transport-agnostic, room-based networking library for multiplayer games. Zero game engine dependency — pure networking primitives.
Install
npm install netcore-networkingBrowser / CDN
The library ships browser-ready builds alongside the Node.js version.
ESM (bundlers, Vite, etc.):
import { NetCoreClient, WebSocketClientTransport } from "netcore-networking/browser";IIFE (CDN, script tag):
<script src="https://unpkg.com/netcore-networking/dist/netcore.iife.js"></script>
<script>
const client = new netcore.NetCoreClient({ url: "ws://localhost:7777" });
</script>Server-only classes (GameServer, WebSocketAdapter, NetCoreServer, etc.) are excluded from browser builds at runtime.
Quick Start
Server
import { GameServer } from "netcore-networking";
const server = new GameServer({ port: 7777, defaultTickRate: 64 });
server.on("connection", (client) => {
const room = server.getOrCreateRoom("lobby", { maxClients: 16 });
room.addClient(client);
});
server.on("message", (client, message) => {
console.log(`Client ${client.id}:`, message);
});
await server.start();Client
import { NetCoreClient } from "netcore-networking";
const client = new NetCoreClient({
url: "ws://localhost:7777",
handshake: { name: "Player1" },
});
client.on("open", () => {
console.log("Connected as", client.clientId);
});
client.on("state", (state, tick) => {
// Apply authoritative server state
});
client.on("close", (reason) => {
console.log("Disconnected:", reason);
});
await client.connect();Architecture
core/ Transport, codec, server, client — raw networking primitives
high-level/ Rooms, tick loops, input tracking, state sync
security/ Middleware pipeline (rate limiting, signing, anti-cheat)
client-utils/ Prediction, interpolation — client-side smoothingCore API
| Class | Purpose |
|-------|---------|
| NetCoreServer | Transport-agnostic server with middleware pipeline |
| NetCoreClient | Auto-reconnect, sequenced input, handshake protocol |
| RemoteClient | Server-side representation of a connected client |
| BinaryCodec | Compact 9-byte header wire format |
| JsonCodec | JSON fallback for debugging |
| WebSocketAdapter | Server-side WebSocket transport |
| WebSocketClientTransport | Client-side WebSocket transport |
| TypedEmitter | Type-safe event emitter |
| NetCoreError | Base error class with code property |
| CodecError | Codec encoding/decoding errors |
| ConnectionError | Connection lifecycle errors |
| SecurityError | Security violation errors |
Custom Transport
import { type TransportAdapter, type ClientTransport, NetCoreServer, NetCoreClient } from "netcore-networking";
// Server-side transport
const myTransport: TransportAdapter = {
name: "my-transport",
listen(port, callbacks) { /* ... */ return Promise.resolve(port); },
send(peerId, data) { /* ... */ },
broadcast(data, exclude) { /* ... */ },
getPeerCount() { return 0; },
close() { /* ... */ },
};
// Client-side transport
const myClientTransport: ClientTransport = {
name: "my-client-transport",
get isConnected() { return false; },
connect(callbacks) { /* ... */ return Promise.resolve(); },
send(data) { /* ... */ },
close() { /* ... */ },
};
const server = new NetCoreServer({ transport: myTransport });
const client = new NetCoreClient({ transport: myClientTransport, handshake: {} });Pluggable Codec
import { type MessageCodec, NetCoreServer } from "netcore-networking";
const myCodec: MessageCodec = {
encode(message) { /* ... */ return new Uint8Array(); },
decode(data) { /* ... */ return { type: 0, sequence: 0, payload: new Uint8Array() }; },
extractPayload(wire) { return JSON.parse(new TextDecoder().decode(wire.payload)); },
};
const server = new NetCoreServer({ codec: myCodec });High-Level API
| Class | Purpose |
|-------|---------|
| GameServer | Room management + input routing |
| Room | Game instance with own tick loop and state sync |
| StateSync | Authoritative state broadcasting with delta compression |
| InputTracker | Input deduplication (144Hz client on 64Hz server) |
Rooms
// Create a room
const room = server.createRoom("arena", { maxClients: 8, tickRate: 64 });
// Game loop
room.on("tick", (inputs) => {
// Process inputs, update game state
room.setState(myGame.serialize());
});
// Client lifecycle
room.on("join", (client) => console.log(`${client.id} joined`));
room.on("leave", (client, reason) => console.log(`${client.id} left: ${reason}`));
// Move client between rooms
lobbyRoom.removeClient(client.id);
matchRoom.addClient(client);Delta Compression
const room = server.createRoom("arena", {
serializeDelta(prev, curr) {
// Return only changed fields, or null for full state
return diffState(prev, curr);
},
});Client Input
// Send sequenced input (auto-injects sequence + timestamp)
client.sendInput({ dx: 1, dy: 0, actions: ["jump"] });
// Track last acknowledged sequence for prediction
client.on("state", (state, tick) => {
prediction.reconcile(state, client.lastAckedSequence);
});Security
Middleware Pipeline
import { RateLimiter, MessageSigner, AntiCheatManager, SpeedDetector } from "netcore-networking";
server.use(new RateLimiter({ maxConnectionsPerIp: 5, maxMessagesPerSecond: 60 }));
server.use(new MessageSigner({
secret: new Uint8Array(32), // your secret key
maxAgeMs: 5000, // replay protection window
}));
server.use(new AntiCheatManager({
plugins: [new SpeedDetector({ maxDistancePerTick: 10 })],
autoKickOnCritical: true,
}));Rate Limiter
const limiter = new RateLimiter({
maxConnectionsPerIp: 5,
maxMessagesPerSecond: 60,
whitelistIps: ["127.0.0.1"],
});
limiter.banIp("1.2.3.4");
limiter.unbanIp("1.2.3.4");Anti-Cheat Plugins
// Built-in plugins
const speedDetector = new SpeedDetector({
maxDistancePerTick: 10,
burstWindowTicks: 5,
maxBurstAverage: 8,
});
const teleportDetector = new TeleportDetector({
maxTeleportDistance: 50,
minOccurrences: 2,
});
// Allow legitimate teleport zones
teleportDetector.addAllowedZone(clientId, spawnX, spawnY, 5);
// Write your own plugin
const myPlugin: AntiCheatPlugin = {
name: "my-detector",
description: "Custom cheat detection",
onClientConnect(clientId) { },
onClientUpdate(clientId, data) { return null; },
onClientDisconnect(clientId) { },
destroy() { },
};Client Utilities
Prediction (Client-Side)
import { PredictionManager } from "netcore-networking";
const prediction = new PredictionManager({
applyInput: (state, input) => ({
...state,
x: state.x + input.dx * speed,
y: state.y + input.dy * speed,
}),
serialize: (state) => JSON.stringify(state),
deserialize: (data) => JSON.parse(data),
});
// Record input sent to server
prediction.recordInput(sequence++, { dx: 1, dy: 0 });
// On server state update, reconcile
const corrected = prediction.reconcile(serverState, serverLastSeq);Interpolation (Remote Players)
import { InterpolationManager } from "netcore-networking";
const interp = new InterpolationManager({
renderDelayMs: 31,
interpolate: (a, b, t) => ({
x: a.x + (b.x - a.x) * t,
y: a.y + (b.y - a.y) * t,
}),
});
// Feed server snapshots
interp.addSnapshot(playerId, { x: 100, y: 200 }, timestamp);
// Render at interpolated position
const pos = interp.getPosition(playerId, performance.now());Message Wire Format
Default BinaryCodec uses a compact binary format:
[1B type][4B sequence LE][4B payload length LE][NB payload]| Type | Value | Direction |
|------|-------|-----------|
| HANDSHAKE | 0x00 | Both |
| INPUT | 0x01 | Client -> Server |
| STATE | 0x02 | Server -> Client |
| PING | 0x03 | Both |
| PONG | 0x04 | Both |
| ROOM_JOIN | 0x05 | Server -> Client |
| ROOM_LEAVE | 0x06 | Server -> Client |
| KICK | 0x07 | Server -> Client |
| Custom | 0x80+ | Either |
Error Handling
All errors extend NetCoreError with a code property:
import { NetCoreError, CodecError, ConnectionError, SecurityError } from "netcore-networking";
try {
await client.connect();
} catch (err) {
if (err instanceof ConnectionError) {
console.error(`[${err.code}] ${err.message}`);
}
}| Error | Code | When |
|-------|------|------|
| CodecError | ERR_CODEC | Invalid message encoding/decoding |
| ConnectionError | ERR_CONNECTION | Connection failed, timeout, or rejected |
| SecurityError | ERR_SECURITY | Rate limited, origin rejected, or HMAC failure |
License
MIT
