feat: relay world snapshot and voxel payloads

Allow world.* methods through the relay, validate their payloads, and
track per-session world revisions in the session store.
This commit is contained in:
2026-10-23 10:44:42 +08:00
parent 6ccb102d38
commit c82f15e8b6
8 changed files with 1064 additions and 46 deletions
+52 -1
View File
@@ -5,6 +5,7 @@ export const RELAY_METHODS = ['bot.snapshot', 'bot.logs', 'bot.subscribe', 'bot.
export const WORKFLOW_ACTIONS = ['run_once', 'trigger_start', 'trigger_stop', 'stop', 'stop_run', 'reload'] as const;
export const WORKFLOW_METHODS = ['workflow.start', 'workflow.stop', 'workflow.snapshot', 'workflow.catalog',
'workflow.control', 'workflow.command', 'workflow.runtime'] as const;
export const WORLD_METHODS = ['world.snapshot', 'world.chunk', 'world.unwatch'] as const;
export const BOT_EVENTS = ['status_update', 'log', 'message', 'bot_error'] as const;
export type RelayMethod = typeof RELAY_METHODS[number];
export type BotEventName = typeof BOT_EVENTS[number];
@@ -49,7 +50,7 @@ export interface SessionClosed {
}
export interface RelayRequest {
request_id: string;
method: RelayMethod | keyof WorkflowPayloads | 'get_log_history' | 'tab_complete' | 'get_context_info' | 'get_data_file_list' | 'get_file_data';
method: RelayMethod | keyof WorkflowPayloads | keyof WorldPayloads | 'get_log_history' | 'tab_complete' | 'get_context_info' | 'get_data_file_list' | 'get_file_data';
payload: unknown;
}
export interface NodeRelayRequest extends RelayRequest {
@@ -57,6 +58,51 @@ export interface NodeRelayRequest extends RelayRequest {
forward_session_id: string;
deadline_at: number;
}
export interface WorldChunkRevision {x: number; z: number; revision: string}
export interface WorldChunk extends WorldChunkRevision {
palette: Array<{name: string; color: number}>;
// Canonical Base64 UInt16LE[256]; heights/blocks retain the top visible surface.
heights: string;
blocks: string;
// Emitted together for chunks with fluids; omitted by legacy snapshots.
terrain?: {heights: string; blocks: string};
fluids?: {heights: string; blocks: string; levels: string};
spikes?: {heights: string; blocks: string; segments: Array<{
index: number; height: number; direction: 'up' | 'down';
thickness: 'tip' | 'tip_merge' | 'frustum' | 'middle' | 'base';
}>};
features?: {heights: string; blocks: string; entries: Array<{
index: number; height: number; block: number; properties: Record<string, string | number | boolean>;
}>};
}
export interface WorldPayloads {
'world.snapshot': {radius: number; known: WorldChunkRevision[]; world_generation?: string; watch_id?: string};
'world.chunk': {x: number; z: number; world_generation: string; watch_id: string};
'world.unwatch': {watch_id: string};
}
export interface WorldVoxelChunk extends WorldChunkRevision {
world_generation: string;
min_y: number;
height: number;
palette: Array<{name: string; color: number; properties: Record<string, string | number | boolean>; occluding: boolean}>;
// Sparse sections ordered by y relative to min_y; blocks are canonical Base64 UInt16LE[4096].
sections: Array<{y: number; blocks: string}>;
}
export interface WorldSnapshot {
world_generation: string;
context_generation: string;
stream_id: string;
watch_id: string;
version: string;
dimension: string;
min_y: number;
height: number;
center: {x: number; z: number};
position: {x: number; y: number; z: number; yaw: number};
chunks: WorldChunk[];
loaded: WorldChunkRevision[];
partial: boolean;
}
export interface BotSnapshot {
bot_id: number;
bot_status: BotStatus;
@@ -208,6 +254,11 @@ export type BotEvent = {[K in BotEventName]: {
name: K;
payload: BotEventPayloads[K];
}}[BotEventName];
export interface RelayEventPayloads {
bot_event: Extract<BotEvent, {name: 'status_update'}>;
bot_log: Extract<BotEvent, {name: 'log'}>;
info_update: InfoUpdate;
}
export interface OpenSessionCommand extends SessionScope {lease_expires_at: number}
export interface AuthorizeGrantCommand {
forward_session_id: string;