⚔️ Mereb Arena Lobby
{status.toUpperCase()}
📡 {quality.ping} ms ({quality.rating})
{status === 'connected' ? (
Match Active! Room: {roomCode}
Role: {role} {isBot && '(Bot Opponent)'}
) : (
)}
);
}
```
---
## 2. Three.js 3D WebGL Implementation
In a 3D web game (Three.js), decouple network ticks from the render loop (`requestAnimationFrame`):
```javascript
import * as THREE from 'three';
import { MerebClient, SnapshotInterpolator } from 'mereb-netcode';
// 1. Scene & Meshes
const scene = new THREE.Scene();
const localPlayerMesh = createTankMesh(0x00a2ff);
const remoteOpponentMesh = createTankMesh(0xff3366);
scene.add(localPlayerMesh, remoteOpponentMesh);
// 2. Network & Interpolation
const net = new MerebClient({ host: 'mereb-signaling.workers.dev' });
const interpolator = new SnapshotInterpolator({ bufferTime: 60 });
net.on('match-start', ({ isHost }) => {
console.log('3D Match initiated! Host mode:', isHost);
});
// 3. 20Hz Fixed Physics Tick
setInterval(() => {
if (net.isHost) {
// Authoritative Host broadcasts 3D transforms
net.sendState({
t: Date.now(),
x: localPlayerMesh.position.x,
z: localPlayerMesh.position.z,
rotY: localPlayerMesh.rotation.y
});
}
}, 50);
// 4. Client receives 3D state
net.on('state', (state) => {
interpolator.pushSnapshot(state.t, {
opponent: { x: state.x, z: state.z, rotation: state.rotY }
});
});
// 5. 60/120 FPS Render Loop
function animate() {
requestAnimationFrame(animate);
if (!net.isHost) {
const smooth = interpolator.getInterpolated();
if (smooth.opponent) {
remoteOpponentMesh.position.x = smooth.opponent.x;
remoteOpponentMesh.position.z = smooth.opponent.z;
remoteOpponentMesh.rotation.y = smooth.opponent.rotation;
}
}
renderer.render(scene, camera);
}
requestAnimationFrame(animate);
```
---
## 3. Phaser 3 Arcade Implementation
In Phaser 3, update remote sprite bodies within your scene's `update()` method:
```javascript
import Phaser from 'phaser';
import { MerebClient } from 'mereb-netcode';
export class DuelScene extends Phaser.Scene {
create() {
this.player = this.physics.add.sprite(100, 300, 'ship');
this.enemy = this.physics.add.sprite(700, 300, 'enemy-ship');
this.net = new MerebClient({ host: 'mereb-signaling.workers.dev' });
this.net.quickPlay();
this.net.on('state', (state) => {
this.enemy.setPosition(state.x, state.y);
});
}
update() {
if (this.net.isHost) {
this.net.sendState({
x: this.player.x,
y: this.player.y
});
}
}
}
```
---
> [!TIP]
> Ready to optimize state serialization? Review the [Netcode & Binary Packing Guide](08_NETCODE_BINARY_AND_INTERPOLATION.md).
================================================================================
FILE: docs\manual\08_NETCODE_BINARY_AND_INTERPOLATION.md
================================================================================
# Mereb Manual: Netcode, Binary Packing & Snapshot Interpolation
> **Version:** 2026.1 LTS | **Category:** Optimization & Algorithms | [Switch to Scripting API](../api/SCRIPTING_REFERENCE.md)
---
## 1. Why JSON Destroys Multiplayer Performance
The default transport serialization for web games is typically JSON over WebSockets:
```javascript
// A typical uncompressed 60Hz JSON snapshot:
const snapshot = JSON.stringify({
t: 1729000000000,
players: [
{ id: "peer_a", x: 124.582, z: -43.192, rotation: 1.5707, health: 100, isShielded: true },
{ id: "peer_b", x: 210.119, z: 98.421, rotation: 3.1415, health: 85, isShielded: false }
]
});
// Payload Size: 248 Bytes!
```
At 60Hz across 1,000 matches, this JSON stream:
1. Consumes **~30 Megabits per second** of continuous network throughput.
2. Creates hundreds of thousands of short-lived JavaScript objects every second, causing the browser Garbage Collector (GC) to trigger frequent 15–40ms execution freezes ("micro-stutter").
---
## 2. Declarative Schema Binary Packing (`defineSchema`)
Mereb provides `defineSchema`, which compiles declarative object templates into compact binary byte buffers:
```javascript
import { defineSchema } from 'mereb-netcode';
// Define exact field formats and fixed-point multipliers
export const TankStateSchema = defineSchema({
x: 'fixed16', // 2 bytes: [-327.68 to +327.67] with precision 100
z: 'fixed16', // 2 bytes
rotation: 'fixed16', // 2 bytes: precision 1000 for smooth radians
turretRot: 'fixed16',// 2 bytes
health: 'uint8', // 1 byte: [0 to 255]
score: 'uint8', // 1 byte: [0 to 255]
isShielded: 'boolean',// 1 bit
isFiring: 'boolean' // 1 bit
});
// Total Packed Size: 11 BYTES PER ENTITY! (88% Bandwidth Reduction vs JSON)
```
### Encoding and Decoding Example
```javascript
// Authoritative Host: Encode and stream via WebRTC UDP DataChannel
const binaryBuffer = TankStateSchema.encode({
x: localPlayer.x,
z: localPlayer.z,
rotation: localPlayer.rotation,
turretRot: localPlayer.turretRot,
health: localPlayer.health,
score: localPlayer.score,
isShielded: localPlayer.shieldActive,
isFiring: localPlayer.firing
});
net.sendBinary(binaryBuffer);
// Client Peer: Decode binary packet in <0.02ms with ZERO GC allocations
net.on('binary', (arrayBuffer) => {
const state = TankStateSchema.decode(arrayBuffer);
interpolator.pushSnapshot(Date.now(), {
opponent: state
});
});
```
---
## 3. Low-Level Control: `BinaryWriter` & `BinaryReader`
For custom packet headers or polymorphic messages, use the zero-allocation streaming classes:
```javascript
import { BinaryWriter, BinaryReader } from 'mereb-netcode';
// 1. Pack with BinaryWriter
const writer = new BinaryWriter();
writer
.writeUint8(0x01) // Opcode: 0x01 (WORLD_SNAPSHOT)
.writeUint32(Date.now()) // Timestamp
.writeFixed16(player.x, 100) // Signed 16-bit fixed point
.writeFixed16(player.z, 100)
.writeFixed16(player.angle, 1000)
.writeUint8(player.hp)
.writeBoolean(player.isGrounded);
const packet = writer.getView(); // Returns Uint8Array view
net.sendBinary(packet);
// 2. Unpack with BinaryReader
net.on('binary', (buffer) => {
const reader = new BinaryReader(buffer);
const opcode = reader.readUint8();
if (opcode === 0x01) {
const timestamp = reader.readUint32();
const x = reader.readFixed16(100);
const z = reader.readFixed16(100);
const angle = reader.readFixed16(1000);
const hp = reader.readUint8();
const isGrounded = reader.readBoolean();
}
});
```
---
## 4. Hermite Snapshot Interpolation (`SnapshotInterpolator`)
Discrete network ticks arrive at intervals (e.g. 50ms at 20Hz), but the browser screen refreshes at 60Hz, 120Hz, or 144Hz. Without interpolation, remote entities appear to jump or stutter.
Mereb's `SnapshotInterpolator` implements cubic Hermite spline interpolation with **shortest-angle spherical interpolation (`slerp`) for rotations**:
```javascript
import { SnapshotInterpolator } from 'mereb-netcode';
const interpolator = new SnapshotInterpolator({
bufferTime: 70, // Render delay in milliseconds (smooths jitter)
maxBufferSize: 30 // Max snapshots cached before pruning
});
// Push incoming network snapshots
net.on('state', (state) => {
interpolator.pushSnapshot(state.timestamp, {
[enemyId]: { x: state.x, z: state.z, rotation: state.rot }
});
});
// Render loop at 60-144 FPS
function render() {
requestAnimationFrame(render);
const smoothState = interpolator.getInterpolated();
if (smoothState[enemyId]) {
enemyMesh.position.x = smoothState[enemyId].x;
enemyMesh.position.z = smoothState[enemyId].z;
// Automatically calculates shortest-angle angular transition (no 360-degree flip glitch!)
enemyMesh.rotation.y = smoothState[enemyId].rotation;
}
renderer.render();
}
```
---
> [!TIP]
> Ready to review portal integration for Poki and CrazyGames? Visit the [Portal Hooks Guide](09_PORTAL_HOOKS_POKI_CRAZYGAMES.md).
================================================================================
FILE: docs\manual\09_PORTAL_HOOKS_POKI_CRAZYGAMES.md
================================================================================
# Mereb Manual: Web Portal Hooks (Poki & CrazyGames SDKs)
> **Version:** 2026.1 LTS | **Verified Platforms:** Poki, CrazyGames, GameDistribution | [Switch to Scripting API](../api/SCRIPTING_REFERENCE.md)
---
## 1. The Video Ad Disconnect Problem
When a player triggers a **commercial break** or watches a **rewarded video ad** on portals like Poki or CrazyGames:
1. The browser pauses or severely throttles `requestAnimationFrame` and `setTimeout` loops for background tabs.
2. Standard WebRTC / WebSocket ping-pong timers stop firing.
3. Upon returning from the 30-second ad, the network connection has closed, and the player is booted to the menu.
Mereb includes a native **`PortalManager`** that orchestrates network state during portal monetization events.
---
## 2. Automatic Portal Hooking
Mereb automatically detects and connects to the official Poki or CrazyGames SDK loaded in the browser:
```javascript
import { MerebClient } from 'mereb-netcode';
const net = new MerebClient({
host: 'mereb-signaling.dawitbaslyos.workers.dev',
portal: 'auto' // Or explicitly 'poki' | 'crazygames'
});
```
---
## 3. Explicit Lifecycle Hooks
### Poki Integration Example
```javascript
// Register official Poki SDK instance
net.usePortal('poki', window.PokiSDK);
// Trigger a mid-roll commercial break between rounds:
async function triggerBetweenRoundAd() {
// Pauses simulation, mutes audio, and suppresses WebRTC timeout alerts
const adSuccess = await net.portal.commercialBreak();
if (adSuccess) {
console.log('[Portal] Commercial break completed successfully.');
}
}
// Generate viral invite URL using Poki shareableURL API:
const inviteUrl = net.portal.getShareableUrl('W7KP');
console.log('Share this link on Poki:', inviteUrl);
```
### CrazyGames Integration Example
```javascript
// Register official CrazyGames SDK
net.usePortal('crazygames', window.CrazyGames.SDK);
// Trigger rewarded video ad for in-game currency:
async function watchRewardedAd() {
const rewarded = await net.portal.rewardedBreak();
if (rewarded) {
grantBonusGems(500);
}
}
```
---
> [!NOTE]
> During active ad playback, `net.portal` sends a reliable `ad-start` event to the connected peer over the reliable WebRTC channel, notifying the opponent's game to temporarily pause round countdowns or display an "Opponent watching ad..." banner.
================================================================================
FILE: docs\manual\10_TROUBLESHOOTING_AND_FAQ.md
================================================================================
# Mereb Manual: Troubleshooting, Diagnostics & Production Checklist
> **Version:** 2026.1 LTS | **Verified Stacks:** Production WebRTC & Edge Mesh | [Switch to Scripting API](../api/SCRIPTING_REFERENCE.md)
---
## 1. Top Diagnostic Scenarios & Solutions
### Issue A: "Connection stuck in 'connecting' on School / University Wi-Fi"
* **Root Cause**: Many institutional firewalls block UDP ports entirely and restrict STUN requests.
* **Resolution**: Ensure your `TurnPool` has TLS TURN enabled over port 443:
```javascript
const net = new MerebClient({
host: 'mereb-signaling.workers.dev',
turnServers: [
{
urls: 'turns:openrelay.metered.ca:443?transport=tcp',
username: 'openrelay',
credential: 'openrelaypassword'
}
]
});
```
### Issue B: "Remote player stutters or jerks forward"
* **Root Cause**: The client render loop is evaluating ahead of the interpolation buffer, or network jitter exceeds `bufferTime`.
* **Resolution**: Increase the `bufferTime` parameter in `SnapshotInterpolator`:
```javascript
// Default is 70ms. For mobile cellular networks, increase to 90-110ms:
const interpolator = new SnapshotInterpolator({ bufferTime: 100 });
```
### Issue C: "WebGL build throws 'LibraryManager.library mergeInto' error"
* **Root Cause**: The JavaScript bridge file was placed outside the special `Assets/Plugins/WebGL/` folder.
* **Resolution**: In Unity, `.jslib` files MUST reside inside `Assets/Plugins/WebGL/`.
---
## 2. In-Game Developer Console (`MerebTerminal`)
Mereb provides an interactive, in-game terminal for live debugging. Press **`** (backtick) or tap the floating **`⚡ MEREB DEV`** badge to open the terminal.
| Command | Action | Description |
| :--- | :--- | :--- |
| `help` | Show command list | Lists all available console diagnostic utilities. |
| `status` | Query netcode state | Displays current role, room code, peerId, and signaling state. |
| `ping` | Probe latency | Triggers real-time RTT measurement across DataChannels. |
| `lag