/** * Binary decoding utilities for territory snapshot data from Rust backend. * * The TerritorySnapshot uses a sparse binary format to minimize data transfer: * - Only claimed tiles (owner_id != 0) are transmitted * - All other tiles default to unclaimed (owner_id = 0) * - Binary format: [count:4][tile_changes...] * where each change is [index:4][owner:2] (6 bytes per claimed tile) */ export interface TileOwnership { index: number; owner_id: number; } /** * Decode sparse binary territory snapshot from Rust backend. * * @param data Binary data array (serialized from Vec in Rust) * @returns Array of claimed tiles with indices and owner IDs, or null if invalid */ export function decodeTerritorySnapshot(data: number[] | Uint8Array): TileOwnership[] | null { // Convert to Uint8Array if needed for faster access const bytes = data instanceof Uint8Array ? data : new Uint8Array(data); if (bytes.length < 4) { console.error("Invalid territory snapshot: not enough data for count"); return null; } // Use DataView for faster little-endian reads const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); // Read count (4 bytes, little-endian u32) const count = view.getUint32(0, true); const expectedSize = 4 + count * 6; if (bytes.length !== expectedSize) { console.error( `Invalid territory snapshot: expected ${expectedSize} bytes, got ${bytes.length}` ); return null; } const tiles: TileOwnership[] = new Array(count); for (let i = 0; i < count; i++) { const offset = 4 + i * 6; // Read index (4 bytes, little-endian u32) const index = view.getUint32(offset, true); // Read owner_id (2 bytes, little-endian u16) const owner_id = view.getUint16(offset + 4, true); tiles[i] = { index, owner_id }; } return tiles; }