Network Interception
Network interceptors allow you to hook into the message flow between client and server. Any object with an onMessage method can be registered as an interceptor.
The NetIntercept Interface
Interceptors implement the NetIntercept interface:
NetIntercept Interface
interface NetIntercept {
onMessage?: (message: MessageProtocol) => void;
packets?: any[];
}
onMessage- Called when any message is received from the serverpackets- Array of outgoing packets to send on the next flush
Registering Interceptors
Register interceptors with the network:
Registering Interceptors
const network = new VOXELIZE.Network();
network.register(world);
network.register(peers);
network.register(entities);
network.register(myCustomInterceptor);
Built-in interceptors:
World- Handles chunk loading, world updates, initializationPeers- Handles peer position and metadata updatesEntities- Handles entity creation, updates, and deletionEvents- Handles custom event messagesMethod- Handles method call responses
Creating a Custom Interceptor
Custom Interceptor
import { MessageProtocol } from "@voxelize/protocol";
const chatLogger = {
onMessage(message: MessageProtocol) {
if (message.type === "CHAT" && message.chat) {
console.log(`[${message.chat.sender}]: ${message.chat.body}`);
}
},
};
network.register(chatLogger);
Message Types
Common message types you can intercept:
| Type | Description |
|---|---|
INIT | Initial world data (config, blocks, peers, entities) |
JOIN | Player joined the world |
LEAVE | Player left the world |
PEER | Peer position/metadata update |
ENTITY | Entity create/update/delete |
LOAD | Chunk data received |
UPDATE | World stats update (time, etc.) |
CHAT | Chat message |
EVENT | Custom event |
METHOD | Method call response |
Sending Packets
Interceptors can queue outgoing packets:
Sending Packets
const myInterceptor = {
packets: [] as any[],
sendChat(message: string) {
this.packets.push({
type: "CHAT",
chat: {
sender: "Player",
body: message,
},
});
},
onMessage(message: MessageProtocol) {
// Handle incoming messages
},
};
network.register(myInterceptor);
// Later, send a chat message
myInterceptor.sendChat("Hello world!");
Packets are automatically flushed each frame by the network.
Example: Entity Counter
Track entity counts by type:
Entity Counter
import { MessageProtocol } from "@voxelize/protocol";
class EntityCounter {
counts: Map<string, number> = new Map();
onMessage = (message: MessageProtocol) => {
if (!message.entities) return;
for (const entity of message.entities) {
const current = this.counts.get(entity.type) || 0;
switch (entity.operation) {
case "CREATE":
this.counts.set(entity.type, current + 1);
break;
case "DELETE":
this.counts.set(entity.type, Math.max(0, current - 1));
break;
}
}
};
getCount(type: string): number {
return this.counts.get(type) || 0;
}
}
const counter = new EntityCounter();
network.register(counter);
Example: Latency Monitor
Measure round-trip time:
Latency Monitor
class LatencyMonitor {
packets: any[] = [];
private pingTime = 0;
latency = 0;
ping() {
this.pingTime = performance.now();
this.packets.push({
type: "METHOD",
method: { name: "ping", payload: "{}" },
});
}
onMessage = (message: MessageProtocol) => {
if (message.type === "METHOD" && message.method?.name === "pong") {
this.latency = performance.now() - this.pingTime;
}
};
}
Order of Execution
When a message arrives:
network.sync()is called (automatically each frame)- Message is decoded from the WebSocket
- Each registered interceptor's
onMessageis called in registration order - Interceptors process the message
When sending:
- Interceptors add packets to their
packetsarray network.flush()is called (automatically each frame)- All packets from all interceptors are collected and sent
packetsarrays are cleared
Read on to learn about collision detection.