feat: add world voxel snapshot forwarding
Collect chunk voxel data from the bot world and stream snapshot, chunk, and unwatch messages through the forward protocol.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type {EnsureForward, ForwardAck, ForwardErrorCode, JoinReady, RelayRequest, WorkflowPayloads} from '../types/forward.ts';
|
||||
import type {EnsureForward, ForwardAck, ForwardErrorCode, JoinReady, RelayRequest, WorkflowPayloads, WorldPayloads} from '../types/forward.ts';
|
||||
import {assert_json} from '../bot/workflow/runtime/value.ts';
|
||||
|
||||
const errors: Record<ForwardErrorCode, [string, boolean]> = {
|
||||
@@ -112,6 +112,11 @@ export function validate_rpc(value: unknown): RelayRequest {
|
||||
|| !uuid(value.request_id) || !uuid(value.correlation_id) || !uuid(value.forward_session_id)
|
||||
|| !integer(value.deadline_at, 1)) throw new ForwardFailure('INVALID_REQUEST');
|
||||
switch (value.method) {
|
||||
case 'world.snapshot':
|
||||
case 'world.chunk':
|
||||
case 'world.unwatch':
|
||||
validate_world_payload(value.method, value.payload);
|
||||
break;
|
||||
case 'workflow.start':
|
||||
case 'workflow.stop':
|
||||
case 'workflow.snapshot':
|
||||
@@ -217,3 +222,31 @@ export function validate_workflow_payload<M extends keyof WorkflowPayloads>(meth
|
||||
}
|
||||
return payload as WorkflowPayloads[M];
|
||||
}
|
||||
|
||||
export function validate_world_payload<M extends keyof WorldPayloads>(method: M, payload: unknown): WorldPayloads[M] {
|
||||
if (method === 'world.unwatch') {
|
||||
if (!exact(payload, ['watch_id']) || !uuid(payload.watch_id)) throw new ForwardFailure('INVALID_REQUEST');
|
||||
} else if (method === 'world.chunk') {
|
||||
if (!exact(payload, ['x', 'z', 'world_generation', 'watch_id'])
|
||||
|| !Number.isSafeInteger(payload.x) || !Number.isSafeInteger(payload.z)
|
||||
|| !uuid(payload.world_generation) || !uuid(payload.watch_id)) throw new ForwardFailure('INVALID_REQUEST');
|
||||
} else if (method === 'world.snapshot') {
|
||||
if (!exact(payload, ['radius', 'known'], ['world_generation', 'watch_id'])
|
||||
|| !integer(payload.radius, 1) || payload.radius > 3
|
||||
|| Object.hasOwn(payload, 'world_generation') && !uuid(payload.world_generation)
|
||||
|| Object.hasOwn(payload, 'watch_id') && !uuid(payload.watch_id)
|
||||
|| !Array.isArray(payload.known) || payload.known.length > 49
|
||||
|| Reflect.ownKeys(payload.known).length !== payload.known.length + 1) throw new ForwardFailure('INVALID_REQUEST');
|
||||
const seen = new Set<string>();
|
||||
for (let index = 0; index < payload.known.length; index++) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(payload.known, String(index));
|
||||
const item = descriptor && 'value' in descriptor && descriptor.enumerable ? descriptor.value : undefined;
|
||||
if (!exact(item, ['x', 'z', 'revision']) || !Number.isSafeInteger(item.x)
|
||||
|| !Number.isSafeInteger(item.z) || !uuid(item.revision)) throw new ForwardFailure('INVALID_REQUEST');
|
||||
const key = `${item.x},${item.z}`;
|
||||
if (seen.has(key)) throw new ForwardFailure('INVALID_REQUEST');
|
||||
seen.add(key);
|
||||
}
|
||||
} else throw new ForwardFailure('INVALID_REQUEST');
|
||||
return payload as WorldPayloads[M];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user