# FREEEXILE SERVER ARCHITECTURE, PROTOBUF SCHEMAS & COCOS CREATOR 3.X INTEGRATION BLUEPRINT

- **Document ID**: `SRV-SURVEY-PROTO-20261003`
- **Author**: Explorer Survey Agent 2 (`explorer_survey_2`)
- **Target Audience**: Orchestrator, Cocos Client Engineers, Server Architects
- **Date**: 2026-10-03
- **Classification**: Authoritative Integration Specification

---

## 1. Executive Summary & System Overview

FreeExile combines a high-performance **C++20 SIMD native simulation core** (running 30Hz tick loops with 10,000+ combat entities) with a **Python Server-Authoritative Actor layer** (`asyncio` gateways, movement authority, spatial grids, economy, and quests). 

To migrate the game client to **Cocos Creator 3.x (TypeScript)** while maintaining 100% Server-Authoritative integrity (Directive § 3.1) and eliminating client-side drift or desync, the Cocos client connects directly to the server cluster via the **WebSocket Gateway Bridge on port `8080` (`ws://127.0.0.1:8080`)** using **Google Protocol Buffers (protobufjs)**.

```
+-----------------------------------------------------------------------------------------+
|                              FREEEXILE MULTI-TIER ARCHITECTURE                          |
+-----------------------------------------------------------------------------------------+
|                                                                                         |
|  [ Cocos Creator 3.x TypeScript Client ]                                                |
|       │                                                                                 |
|       │ W3C WebSocket RFC 6455 (Binary Frame)                                           |
|       │ [2-Byte Opcode] + [Protobuf Binary Payload]                                     |
|       ▼                                                                                 |
|  [ WebSocket Gateway Bridge (server/gateway/ws_gateway_bridge.py) :8080 ]               |
|       │                                                                                 |
|       │ IPC / In-Memory Actor Stream                                                    |
|       ▼                                                                                 |
|  [ Python Authoritative Gateway & Game Logic ]                                          |
|       ├── MovementAuthorityEngine (server/world/movement_authority.py)                  |
|       ├── ZoneEngine & Catalogs (server/world/zone_engine.py)                           |
|       ├── CombatEngine & Damage Matrix (server/combat/combat_engine.py)                 |
|       └── SessionManager & Touch Biometrics (server/gateway/session_manager.py)         |
|       │                                                                                 |
|       │ C-FFI / ctypes Bridge (server/world/native_engine_bridge.py)                    |
|       ▼                                                                                 |
|  [ C++20 Native AVX2 Simulation Core (server_cpp/src/sim_core_ffi.cpp) ]                |
|       ├── ZoneServer (Structure-of-Arrays EntityPoolSoA, 100k capacity)                 |
|       ├── FlatSpatialGrid (64.0m cell size, O(1) Zero-Allocation Linked List)           |
|       └── 30Hz SIMD Vectorized Displacement Tick Loop (avg latency < 0.1ms)             |
|                                                                                         |
+-----------------------------------------------------------------------------------------+
```

---

## 2. Server Architecture & Native Simulation Stack

### 2.1 C++20 SIMD Hot-Path Simulation Core
The hot path handles real-time entity displacement and spatial partitioning for tens of thousands of simultaneous entities.
- **Header**: `server_cpp/include/world/ZoneServer.hpp`
- **FFI Wrapper**: `server_cpp/src/sim_core_ffi.cpp` (exports `engine_init`, `engine_add_entity`, `engine_set_velocity`, `engine_step_tick`, `engine_query_aoi`, `engine_get_entity_pos`)
- **Shared Object**: `server/engine_native/freeexile_sim_core.dll` (Win32/x64) and `server_cpp/build_ninja/freeexile_sim_core.dll`
- **Memory Layout**: Structure-of-Arrays (`EntityPoolSoA`), eliminating cache misses across 100,000 entities:
  ```cpp
  struct EntityPoolSoA {
      int32_t entity_ids[MAX_ENTITIES];
      float pos_x[MAX_ENTITIES];
      float pos_y[MAX_ENTITIES];
      float pos_z[MAX_ENTITIES];
      float vel_x[MAX_ENTITIES];
      float vel_y[MAX_ENTITIES];
      float move_speed[MAX_ENTITIES];
      float collision_radius[MAX_ENTITIES];
      float hp[MAX_ENTITIES];
      FiveElements element[MAX_ENTITIES];
      uint32_t flags[MAX_ENTITIES]; // 1: Active, 2: iFrame, 4: Dirty
      int32_t next_in_cell[MAX_ENTITIES];
      int32_t count = 0;
  };
  ```
- **Tick Rate**: Fixed 30Hz ($\Delta t = 1.0 / 30.0 = 33.333\text{ ms}$). Micro-benchmarks demonstrate that 10,000 active entities require $< 0.1\text{ ms}$ per tick on a single modern CPU core, leaving $> 99.7\%$ CPU idle.
- **Area of Interest (AoI)**: Flat spatial hash with cell size 64.0m (`FlatSpatialGrid`). Fast bitwise coordinate hashing maps world coordinates to cells in $O(1)$ time with zero heap allocations during query.

### 2.2 Python Authoritative Engine & Game Logic Layer
- **Python Bridge**: `server/world/native_engine_bridge.py` dynamically resolves the C++ DLL and binds ctypes signatures for tick advancement and AoI queries.
- **Movement Authority**: `server/world/movement_authority.py`:
  - `process_move_input(entity_id, dir_x, dir_y, dt)`: Calculates pure server-authoritative displacement using vector normalization and player move speed.
  - `validate_and_reconcile_position(entity_id, claimed_x, claimed_y, dt)`: Compares client distance against `max_allowed = speed * dt * tolerance_multiplier` (default tolerance: `1.15`). If exceeded, increments suspicion score and forces a rubberband back to the authoritative position.
- **Zone Authority**: `server/world/zone_engine.py`:
  - Enforces PoE2 Safe Haven rules (`can_spawn_hostile_monsters` returns `false` in `zone_boundless_sanctuary` and `zone_player_hideout`).
  - Waypoint Safe Radius: Default `8.0m` radius around waypoints gives immunity and rejects monster spawn (`is_in_waypoint_safe_radius`).
- **Gateway Services**:
  - `AuthoritativeGatewayService` (`server/gateway/authoritative_gateway_service.py`): TCP port 7777 (native iOS client with AEAD ChaCha20-Poly1305 and Apple App Attest).
  - `WsGatewayBridge` (`server/gateway/ws_gateway_bridge.py`): WebSocket port 8080 (cross-platform, WebApp, and Cocos Creator 3.x).

---

## 3. WebSocket Gateway Bridge Inspection & Wire Protocol

### 3.1 Network Endpoint & Lifecycle
- **Host**: `127.0.0.1` (configurable via environment or CLI)
- **Port**: `8080`
- **Protocol**: W3C WebSocket (RFC 6455)
- **URL**: `ws://127.0.0.1:8080`
- **Connection Handshake**:
  1. Client initiates WebSocket connection: `new WebSocket("ws://127.0.0.1:8080")`.
  2. Server immediately sends Handshake Welcome payload:
     ```json
     {
       "type": "welcome",
       "server_time": 1727941700.123,
       "authoritative": true,
       "version": "2.0-Bridge"
     }
     ```
  3. Client sends `AuthSimulator` / `SessionToken`:
     - Token prefix `DEV_SIMULATOR_TOKEN` grants simulator privileges.
     - Production tokens authenticate via JWT/Session ID from `server/auth/`.
  4. Server responds with `AuthResult`: `{ "type": "auth_result", "authorized": true, "role": "simulator" }`.
  5. Connection enters full duplex state: 30Hz World Sync updates, 2.0s RTT ping/pong heartbeat, combat actions, and move inputs.

### 3.2 Wire Framing: Opcode-Prefixed Binary Protobuf
Unlike raw TCP streams which require length-prefix framing (`PacketCodec` with 4-byte big-endian header), WebSocket RFC 6455 provides native frame demarcation. However, binary Protobuf payloads do not encode message type metadata.

To achieve maximum throughput with minimal overhead, the authoritative wire format uses a **2-byte Big-Endian Opcode Header** preceding the Protobuf payload:

```
+--------------------------+------------------------------------------------+
|  Opcode (2 Bytes, BE)    |       Protobuf Serialized Payload (N Bytes)    |
|  uint16: 0x0001 - 0xFFFF |       freeexile.* Protobuf Binary              |
+--------------------------+------------------------------------------------+
```

### 3.3 Authoritative Opcode Table

| Opcode (Hex) | Direction | Protobuf Message Type | Canonical Schema Source | Description |
|---|---|---|---|---|
| `0x0001` | C → S | `PingMessage` (or JSON fallback) | `proto/network.proto` | Ping heartbeat (contains `client_timestamp_ms`) |
| `0x0002` | S → C | `PongMessage` (or JSON fallback) | `proto/network.proto` | Pong response (echoes `client_timestamp_ms`, adds `server_timestamp_ms`) |
| `0x0010` | C → S | `freeexile.network.PlayerMoveInput` | `proto/network.proto:25` | Client move vector input with input sequence and biometrics |
| `0x0011` | S → C | `freeexile.network.WorldStateSync` | `proto/network.proto:46` | Authoritative 30Hz world snapshot of all AoI entities |
| `0x0012` | S → C | `freeexile.network.EntitySnapshot` | `proto/network.proto:34` | Single entity state update / reconciliation response |
| `0x0020` | C → S | `freeexile.combat.CastMartialSkillRequest` | `proto/combat.proto:63` | Trigger skill cast with targeting and weapon set |
| `0x0021` | C → S | `freeexile.combat.PhantomEvasionRequest` | `proto/combat.proto:73` | Trigger 0.25s i-frame dodge roll |
| `0x0022` | S → C | `freeexile.combat.CombatDamageEvent` | `proto/combat.proto:80` | Broadcast damage, critical hits, and recoil events |
| `0x0030` | C → S | `freeexile.map_zone.EnterZoneRequest` | `proto/map_zone.proto:65` | Request zone traversal via portal |
| `0x0031` | S → C | `freeexile.map_zone.EnterZoneResponse` | `proto/map_zone.proto:71` | Zone data, bounds, waypoints, portals, spawn position |
| `0x0032` | C → S | `freeexile.map_zone.WaypointTeleportRequest` | `proto/map_zone.proto:80` | Fast travel via unlocked waypoint |
| `0x0033` | S → C | `freeexile.map_zone.WaypointTeleportResponse` | `proto/map_zone.proto:85` | Teleport confirmation with coordinates |
| `0x0040` | C → S | `freeexile.npc.InteractNpcRequest` | `proto/npc.proto:72` | Proximity interaction with NPC |
| `0x0041` | S → C | `freeexile.npc.InteractNpcResponse` | `proto/npc.proto:83` | NPC dialogue tree node, services, quest triggers |
| `0x0042` | C → S | `freeexile.npc.SelectDialogueChoiceRequest` | `proto/npc.proto:92` | Advance dialogue branch |
| `0x0043` | S → C | `freeexile.npc.SelectDialogueChoiceResponse` | `proto/npc.proto:101` | Next dialogue node or service activation |
| `0x0050` | C → S | `freeexile.portal.OpenTownPortalRequest` | `proto/portal.proto:103` | Open 2-way Town Portal to Sanctuary/Hideout |
| `0x0051` | S → C | `freeexile.portal.OpenTownPortalResponse` | `proto/portal.proto:111` | Portal link metadata (remaining charges, IDs) |
| `0x0052` | C → S | `freeexile.portal.EnterTownPortalRequest` | `proto/portal.proto:118` | Traverse through Town Portal |
| `0x0053` | S → C | `freeexile.portal.EnterTownPortalResponse` | `proto/portal.proto:124` | Destination zone coordinates and charge deduction |
| `0x0060` | C → S | `freeexile.chat.SendChatRequest` | `proto/chat.proto:53` | Send chat message to Channel with item links |
| `0x0061` | S → C | `freeexile.chat.ChatMessage` | `proto/chat.proto:37` | Broadcast chat message with censorship and item snapshots |
| `0x0070` | S → C | `freeexile.quest.QuestProgressSyncNotice` | `proto/quest.proto:116` | Full synchronization of active quests and milestones |

*Note: For backward compatibility during migration, the bridge automatically inspects the first frame byte: if ASCII (`{`), it routes via JSON; if binary, it reads the 2-byte opcode and decodes Protobuf.*

---

## 4. Comprehensive Protobuf Schema Specification

The FreeExile protocol contains 17 `.proto` files in `proto/`. Below are the complete specifications for the primary game messages required for the Cocos Creator client.

### 4.1 Movement & Kinematics (`proto/network.proto`)

#### Message: `freeexile.network.PlayerMoveInput`
Client sends this message every input frame or at a fixed 30Hz rate when moving.
```protobuf
message PlayerMoveInput {
  uint32 entity_id = 1;        // Player entity ID assigned upon login
  float dir_x = 2;             // Normalized direction vector X [-1.0 .. 1.0]
  float dir_y = 3;             // Normalized direction vector Y [-1.0 .. 1.0]
  uint32 input_sequence = 4;   // Monotonically increasing sequence number (for reconciliation)
  TouchBiometrics biometrics = 5; // Optional touch analysis parameters
}
```

#### Message: `freeexile.network.TouchBiometrics`
Captured on mobile touch displays to detect synthetic bots or macros.
```protobuf
message TouchBiometrics {
  float major_radius = 1;          // Contact area radius (default ~20.0 pt)
  float force = 2;                 // Pressure force (default 1.0)
  float micro_tremor_hz = 3;       // Biological hand jitter (8-12 Hz)
  float trajectory_curvature = 4; // Non-linear curvature deviation (0 = bot straight line)
  uint32 device_type = 5;          // 1 = iPhone, 2 = iPad, 99 = Desktop / Simulator
  bytes app_attest_token = 6;      // Hardware attestation statement
}
```

#### Message: `freeexile.network.EntitySnapshot` (EntityState)
Server authoritative state broadcast for a single entity.
```protobuf
message EntitySnapshot {
  uint32 entity_id = 1;
  float pos_x = 2;
  float pos_y = 3;
  float velocity_x = 4;
  float velocity_y = 5;
  uint32 current_hp = 6;
  uint32 max_hp = 7;
  uint32 animation_state = 8;          // 0=Idle, 1=Run, 2=Attack, 3=Hurt, 4=Dodge, 5=Die
  uint64 last_processed_input_seq = 9; // Client sequence number acknowledged by this state
}
```

#### Message: `freeexile.network.WorldStateSync`
Server 30Hz broadcast package sent to all clients in the AoI grid.
```protobuf
message WorldStateSync {
  uint64 server_tick = 1;
  repeated EntitySnapshot entities = 2;
}
```

### 4.2 Combat & Animation System (`proto/combat.proto`)

#### Message: `freeexile.combat.CastMartialSkillRequest` (CombatAction)
Client sends when activating an ability (Q/W/E/R or Left/Right click).
```protobuf
message CastMartialSkillRequest {
  uint32 caster_entity_id = 1;
  uint32 skill_id = 2;
  float target_x = 3;
  float target_y = 4;
  uint32 active_weapon_set = 5; // 1 = Main Weapon, 2 = Secondary Weapon
  uint64 client_timestamp_ms = 6;
  bool auto_weapon_swapped = 7; // True if client auto-swapped weapon for skill requirement
}
```

#### Message: `freeexile.combat.PhantomEvasionRequest`
Client sends when pressing Spacebar for Huyễn Ảnh Bộ dodge roll.
```protobuf
message PhantomEvasionRequest {
  uint32 entity_id = 1;
  float evasion_dir_x = 2;
  float evasion_dir_y = 3;
  uint64 client_timestamp_ms = 4;
}
```

#### Message: `freeexile.combat.CombatDamageEvent`
Server broadcasts damage application to all observers in AoI.
```protobuf
message CombatDamageEvent {
  uint32 source_entity_id = 1;
  uint32 target_entity_id = 2;
  uint32 raw_damage = 3;
  uint32 mitigated_damage = 4;
  bool is_critical = 5;
  bool target_evaded = 6;
  FiveElementsType element = 7;         // KIM=0, MOC=1, THUY=2, HOA=3, THO=4
  uint32 recoil_damage_to_source = 8;
}
```

### 4.3 Map, Zone & World Data (`proto/map_zone.proto`)

#### Message: `freeexile.map_zone.ZoneInfoPayload` (ZoneData)
Contains map dimensions, portals, waypoints, and NPC anchors upon entering a zone.
```protobuf
message ZoneInfoPayload {
  string zone_id = 1;
  string name = 2;
  ZoneType zone_type = 3;         // SANCTUARY=1, OPEN_WORLD=2, DUNGEON=3, SECRET_CHAMBER=4
  ZoneEnvironment environment = 4;// NORMAL=1, SANDSTORM=2, MIASMA=3, CHILL=4, HEAT=5, VOID=6
  uint32 min_level = 5;
  float bounds_width = 6;
  float bounds_height = 7;
  repeated WaypointData waypoints = 8;
  repeated ZonePortalData portals = 9;
  repeated string npc_ids = 10;
}
```

#### Message: `freeexile.map_zone.WaypointData`
```protobuf
message WaypointData {
  string waypoint_id = 1;
  string name = 2;
  string zone_id = 3;
  float x = 4;
  float y = 5;
  float z = 6;
  bool is_unlocked = 7;
}
```

#### Message: `freeexile.map_zone.EnterZoneResponse`
```protobuf
message EnterZoneResponse {
  bool success = 1;
  string error_message = 2;
  ZoneInfoPayload current_zone = 3;
  float spawn_x = 4;
  float spawn_y = 5;
}
```

### 4.4 NPC & Dialogue Interaction (`proto/npc.proto`)
- `InteractNpcRequest`: Player approaches NPC and taps/clicks.
- `InteractNpcResponse`: Returns `NpcInfoPayload`, active `NpcDialogueNodePayload` (speaker title, content, voice cue ID), and branching choices `choices` (`NpcDialogueChoicePayload`).
- `SelectDialogueChoiceRequest` & `SelectDialogueChoiceResponse`: Advances dialogue tree or triggers services (`SHOP_VAULT`, `CRAFTING_FORGE`, `MERIDIAN_INSIGHT`, `QUEST_DISPATCH`).

### 4.5 Town Portal & Quarantine Protection (`proto/portal.proto`)
- `OpenTownPortalRequest` & `OpenTownPortalResponse`: Opens portal link with `remaining_charges` (max 6 portals per map).
- `QuarantineErrorCode`: Enforces PoE2 anti-exploit rules:
  - `QUARANTINE_BOSS_COMBAT_ACTIVE` (cannot open portal or flee during boss fights)
  - `QUARANTINE_SECRET_CHAMBER_ACTIVE` (cannot open portal in timed challenge chambers)
  - `QUARANTINE_MAX_PORTALS_EXHAUSTED` (all 6 portals consumed)

---

## 5. Simulation Contracts: Tick Rate, Authority & Reconciliation

### 5.1 Tick Rate & Time Base
- **Server Tick**: Exactly 30Hz ($\Delta t = 33.333\text{ ms}$). Every tick steps physics, moves entities according to velocities, and queries the spatial grid for dirty entities.
- **Client Render Loop**: 60 FPS (standard mobile/desktop) or 120 FPS (iOS ProMotion). Because render FPS exceeds server tick rate ($120 > 30$), the client must execute **linear/Hermite interpolation between snapshots** to prevent visual stutter.

### 5.2 Client Prediction & Zero-Residual Momentum
To satisfy Requirement R1 ("phanh dừng tức thì / Zero-Residual Momentum"), the client must never apply trailing inertia lerps when inputs are zero:
1. **Input Phase**:
   - Player holds key/joystick: input vector `(dirX, dirY)` is non-zero.
   - Player releases key/joystick: input vector **immediately snaps to `(0, 0)` in frame 0**.
2. **Local Prediction**:
   - In frame $k$, client advances predicted position: $\vec{x}_{k} = \vec{x}_{k-1} + \vec{v}_{\text{pred}} \cdot \Delta t_{\text{render}}$.
   - When $\vec{v}_{\text{input}} = \vec{0}$, $\vec{v}_{\text{pred}} = \vec{0}$ immediately. No deceleration tail or spinning turn inertia is added.
3. **Sequence Buffering**:
   - Client assigns `input_sequence = ++seqCounter` to the `PlayerMoveInput` packet.
   - Stores `{ seq, x, y, dt }` in `pendingInputQueue`.

### 5.3 Server Authority & Reconciliation Contract
1. Server receives `PlayerMoveInput(dir_x, dir_y, input_sequence)`.
2. Server computes displacement using pure server speed: $\vec{x}_{\text{auth}} \mathrel{+}= \hat{u} \cdot (\text{speed} \cdot \Delta t)$.
3. Server emits `EntitySnapshot` with `last_processed_input_seq`.
4. Client receives `EntitySnapshot`:
   - Discards all pending inputs where `seq <= last_processed_input_seq`.
   - Computes error vector $\vec{e} = \vec{x}_{\text{auth}} - \vec{x}_{\text{pred\_at\_ack}}$.
   - **Tolerant Smoothing**:
     - If $\|\vec{e}\| < 0.05\text{ m}$: Error is negligible (float precision noise). No correction applied.
     - If $0.05\text{ m} \le \|\vec{e}\| \le 2.0\text{ m}$: Exponential damping correction is applied:
       $$\vec{x}_{\text{pred}} \leftarrow \vec{x}_{\text{pred}} + 0.25 \cdot \vec{e}$$
       This avoids visual snapping while converging to authoritative truth over 4 frames.
     - If $\|\vec{e}\| > 2.0\text{ m}$: Hard rubberband (wall collision or teleportation rejection). Snaps immediately to $\vec{x}_{\text{auth}}$.
   - Re-applies remaining unacknowledged inputs on top of $\vec{x}_{\text{auth}}$.

### 5.4 Remote Entity Snapshot Interpolation
For other players and monsters, client cannot predict inputs. Instead, it uses **Entity Snapshot Interpolation**:
- Client maintains a ring buffer of the last 30 snapshots (~1.0 second).
- Renders remote entities at time $t_{\text{render}} = t_{\text{client}} - \Delta t_{\text{interp}}$ (where $\Delta t_{\text{interp}} = 66.66\text{ ms} = 2\text{ ticks}$).
- Interpolates between the two snapshots surrounding $t_{\text{render}}$:
  $$\alpha = \frac{t_{\text{render}} - t_{1}}{t_{2} - t_{1}}, \quad \vec{x} = \vec{x}_{1} + \alpha (\vec{x}_{2} - \vec{x}_{1})$$
- Results in buttery smooth 120 FPS movement for all monsters and remote players with zero extrapolation artifacts.

---

## 6. Cocos Creator 3.x TypeScript Network Layer Architecture

### 6.1 Protobuf Pipeline in Cocos Creator
The project already features an automated compilation pipeline: `scripts/compile_protos.py`.
- **Generated Bundle**: `client/src/proto/bundle.js` and `client/src/proto/bundle.d.ts`.
- **Cocos Creator Integration**:
  1. The compilation script can be configured to export to `client/cocos/assets/scripts/proto/bundle.js`.
  2. In Cocos Creator 3.x, `protobufjs` is loaded as an ES Module:
     ```typescript
     import $protobuf from 'protobufjs/minimal.js';
     import { freeexile } from './proto/bundle.js';
     ```
  3. `bundle.d.ts` provides complete auto-completion and static type safety across all 17 packages (`freeexile.network`, `freeexile.combat`, `freeexile.map_zone`, `freeexile.auth`, `freeexile.chat`, `freeexile.npc`, etc.).

### 6.2 Component Hierarchy
```
client/cocos/assets/scripts/network/
├── NetworkManager.ts          // Persistent Singleton (WebSocket, opcodes, ping loop)
├── OpcodeRegistry.ts          // Enum of all Opcode constants & packet mapping
├── PacketSerializer.ts        // Encodes & decodes binary frames [Opcode + Proto]
├── PredictionController.ts    // Local player input queue & server reconciliation
└── RemoteEntityInterpolation.ts // Remote entity snapshot ring-buffer lerp
```

### 6.3 `NetworkManager.ts` Implementation Specification
Below is the concrete TypeScript design for the Cocos Creator network singleton:

```typescript
import { _decorator, Component, director } from 'cc';
import { freeexile } from '../proto/bundle.js';

const { ccclass, property } = _decorator;

export enum NetworkState {
    DISCONNECTED = 0,
    CONNECTING = 1,
    CONNECTED = 2,
    AUTHENTICATED = 3,
    RECONNECTING = 4,
}

export type PacketHandler = (payload: Uint8Array) => void;

@ccclass('NetworkManager')
export class NetworkManager extends Component {
    private static _instance: NetworkManager | null = null;
    public static get instance(): NetworkManager {
        return NetworkManager._instance!;
    }

    @property
    public serverUrl: string = 'ws://127.0.0.1:8080';

    private socket: WebSocket | null = null;
    private state: NetworkState = NetworkState.DISCONNECTED;
    private handlers: Map<number, PacketHandler[]> = new Map();

    // Telemetry
    public currentPingMs: number = 0;
    private pingIntervalTimer: number = 0;
    private lastPingSentTimestamp: number = 0;

    // Reconnection
    private reconnectAttempts: number = 0;
    private maxReconnectDelayMs: number = 10000;

    onLoad() {
        if (NetworkManager._instance === null) {
            NetworkManager._instance = this;
            director.addPersistRootNode(this.node);
        } else {
            this.node.destroy();
            return;
        }
    }

    start() {
        this.connect();
    }

    public connect(): void {
        if (this.socket && (this.socket.readyState === WebSocket.OPEN || this.socket.readyState === WebSocket.CONNECTING)) {
            return;
        }

        this.state = NetworkState.CONNECTING;
        try {
            this.socket = new WebSocket(this.serverUrl);
            this.socket.binaryType = 'arraybuffer';

            this.socket.onopen = this.onSocketOpen.bind(this);
            this.socket.onmessage = this.onSocketMessage.bind(this);
            this.socket.onerror = this.onSocketError.bind(this);
            this.socket.onclose = this.onSocketClose.bind(this);
        } catch (err) {
            this.scheduleReconnect();
        }
    }

    private onSocketOpen(): void {
        this.state = NetworkState.CONNECTED;
        this.reconnectAttempts = 0;
        this.startPingHeartbeat();
        // Send simulator auth token
        this.sendSimulatorAuth();
    }

    private onSocketMessage(event: MessageEvent): void {
        if (typeof event.data === 'string') {
            // JSON fallback frame handling (welcome, json ping/pong)
            try {
                const data = JSON.parse(event.data);
                if (data.type === 'pong' && data.client_ts) {
                    this.currentPingMs = Math.max(1, Date.now() - data.client_ts);
                } else if (data.type === 'auth_result' && data.authorized) {
                    this.state = NetworkState.AUTHENTICATED;
                }
            } catch (_) {}
            return;
        }

        // Binary Protobuf frame handling: [uint16 Opcode] + [Protobuf payload]
        const buffer = event.data as ArrayBuffer;
        if (buffer.byteLength < 2) return;

        const view = new DataView(buffer);
        const opcode = view.getUint16(0, false); // Big-Endian
        const payload = new Uint8Array(buffer, 2);

        this.dispatchPacket(opcode, payload);
    }

    private onSocketError(event: Event): void {
        if (this.socket) {
            this.socket.close();
        }
    }

    private onSocketClose(): void {
        this.state = NetworkState.DISCONNECTED;
        this.stopPingHeartbeat();
        this.scheduleReconnect();
    }

    private scheduleReconnect(): void {
        this.state = NetworkState.RECONNECTING;
        const delay = Math.min(this.maxReconnectDelayMs, 1000 * Math.pow(1.5, this.reconnectAttempts++));
        setTimeout(() => this.connect(), delay);
    }

    public registerHandler(opcode: number, handler: PacketHandler): void {
        if (!this.handlers.has(opcode)) {
            this.handlers.set(opcode, []);
        }
        this.handlers.get(opcode)!.push(handler);
    }

    public unregisterHandler(opcode: number, handler: PacketHandler): void {
        const list = this.handlers.get(opcode);
        if (list) {
            this.handlers.set(opcode, list.filter(h => h !== handler));
        }
    }

    private dispatchPacket(opcode: number, payload: Uint8Array): void {
        const list = this.handlers.get(opcode);
        if (list) {
            for (const handler of list) {
                try {
                    handler(payload);
                } catch (e) {
                    console.error(`[NetworkManager] Error in handler for opcode 0x${opcode.toString(16)}:`, e);
                }
            }
        }
    }

    public sendPacket(opcode: number, payload: Uint8Array): void {
        if (!this.socket || this.socket.readyState !== WebSocket.OPEN) return;

        const packet = new Uint8Array(2 + payload.byteLength);
        // Write 2-byte Opcode Big-Endian
        packet[0] = (opcode >> 8) & 0xff;
        packet[1] = opcode & 0xff;
        packet.set(payload, 2);

        this.socket.send(packet.buffer);
    }

    private startPingHeartbeat(): void {
        this.stopPingHeartbeat();
        this.sendPing();
        this.pingIntervalTimer = window.setInterval(() => this.sendPing(), 2000);
    }

    private stopPingHeartbeat(): void {
        if (this.pingIntervalTimer) {
            clearInterval(this.pingIntervalTimer);
            this.pingIntervalTimer = 0;
        }
    }

    private sendPing(): void {
        if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            this.lastPingSentTimestamp = Date.now();
            this.socket.send(JSON.stringify({
                type: 'ping',
                client_ts: this.lastPingSentTimestamp
            }));
        }
    }

    private sendSimulatorAuth(): void {
        if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            this.socket.send(JSON.stringify({
                type: 'auth_simulator',
                token: 'DEV_SIMULATOR_TOKEN_LOCAL'
            }));
        }
    }
}
```

### 6.4 Convenience Encoding & Decoding Example
Using `freeexile.network.PlayerMoveInput` and `freeexile.network.WorldStateSync`:

```typescript
// Sending PlayerMoveInput
export function sendPlayerMove(entityId: number, dirX: number, dirY: number, seq: number) {
    const moveInput = freeexile.network.PlayerMoveInput.create({
        entityId: entityId,
        dirX: dirX,
        dirY: dirY,
        inputSequence: seq,
        biometrics: {
            majorRadius: 20.0,
            force: 1.0,
            microTremorHz: 10.2,
            trajectoryCurvature: 0.85,
            deviceType: 1
        }
    });

    const encodedBytes = freeexile.network.PlayerMoveInput.encode(moveInput).finish();
    NetworkManager.instance.sendPacket(0x0010, encodedBytes);
}

// Listening for 30Hz WorldStateSync
NetworkManager.instance.registerHandler(0x0011, (payload: Uint8Array) => {
    const worldSync = freeexile.network.WorldStateSync.decode(payload);
    for (const entity of worldSync.entities) {
        if (entity.entityId === myPlayerId) {
            predictionController.reconcile(entity);
        } else {
            remoteInterpolation.pushSnapshot(entity);
        }
    }
});
```

---

## 7. Implementation Roadmap & Verification Gates

### Phase 1: Bridge Dual-Protocol Extension (`server/gateway/ws_gateway_bridge.py`)
- [ ] Add binary frame detection in `_handle_connection`.
- [ ] Parse 2-byte big-endian opcode when frame is binary `bytes`.
- [ ] Implement dispatch table mapping opcodes (`0x0010`, `0x0020`, `0x0030`) to Python subsystems:
  - `0x0010` (`PlayerMoveInput`) $\rightarrow$ `MovementAuthorityEngine.process_move_input` $\rightarrow$ broadcast `0x0011` (`WorldStateSync`).
  - `0x0020` (`CastMartialSkillRequest`) $\rightarrow$ `CombatEngine.execute_skill` $\rightarrow$ broadcast `0x0022` (`CombatDamageEvent`).
- [ ] Retain fallback for legacy JSON clients (`ping`, `auth_simulator`).

### Phase 2: Cocos Creator Protobuf Asset Generation
- [ ] Update `scripts/compile_protos.py` to copy/bundle `bundle.js` and `bundle.d.ts` into `client/cocos/assets/scripts/proto/`.
- [ ] Ensure `protobufjs/minimal.js` is imported without module resolution warnings.

### Phase 3: Cocos Client Network Module Implementation
- [ ] Create `client/cocos/assets/scripts/network/NetworkManager.ts` implementing the specification in Section 6.3.
- [ ] Create `PredictionController.ts` enforcing zero-momentum stop upon input release and exponential damping reconciliation.
- [ ] Create `RemoteEntityInterpolation.ts` managing 30-snapshot buffer for smooth 120 FPS rendering.
- [ ] Wire `#hud-ping` equivalent in Cocos HUD to display live RTT (`NetworkManager.instance.currentPingMs`).

### Phase 4: Verification & Automated Test Gates
- [ ] **Unit Tests**:
  - `tests/unit/test_ws_gateway_protobuf.py`: Establish WebSocket connection to `ws://127.0.0.1:8080`, transmit binary `PlayerMoveInput` packet with Opcode `0x0010`, and verify receipt of binary `WorldStateSync` with Opcode `0x0011`.
- [ ] **Regression Suite**:
  - All existing 1,240+ unit tests must pass (`pytest tests/`).
  - Strict hygiene check `python tools/lint/check_code_and_doc_hygiene.py --strict` must return zero violations.
- [ ] **Human-Centric Real Browser QA**:
  - Launch Cocos Creator Web preview (`http://localhost:7456` or exported build).
  - Assert zero console errors, dynamic ping display, zero momentum drift upon key release, and smooth monster state replication.

---

## 8. Conclusion & Handoff Summary
The server architecture, native C++ simulation core, Python authority rules, and WebSocket bridge have been thoroughly analyzed. The Opcode table, Protobuf schema mappings, and Cocos Creator TypeScript network layer blueprint defined in this document establish the exact, unambiguous technical foundation required for the Cocos Creator 3.x client port.
