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:
2026-10-23 10:44:37 +08:00
parent 0acef64f48
commit 6c69afb38a
11 changed files with 1997 additions and 18 deletions
+30 -4
View File
@@ -97,6 +97,27 @@ function setup() {
ensure(value = initial) { active = value; return manager.ensure(value); }};
}
test('lifecycle updates use reliable bot_event even while volatile logs are saturated', async t => {
const f = setup(); t.after(() => f.manager.stop());
await f.ensure();
const socket = f.sockets[0], stream = f.streams[0], received = [];
socket.on('bot_event', event => received.push(event));
socket.volatile = {emit() {}};
const base = {bot_id: 2, context_generation: randomUUID(), stream_id: randomUUID(), emitted_at: Date.now()};
for (let seq = 1; seq <= 160; seq++) stream.event({...base, seq, name: 'log', payload: {records: []}});
for (const [index, status] of ['online', 'disconnecting', 'offline'].entries()) {
stream.event({...base, seq: 161 + index, name: 'status_update', payload: {status, context_exists: true}});
}
assert.deepEqual(received.map(event => event.payload.status), ['online', 'disconnecting', 'offline']);
assert.ok(received.every(event => event.forward_session_id === f.command.forward_session_id));
stream.event({...base, seq: 165, name: 'message', payload: {text: 'not a lifecycle event'}});
assert.equal(received.length, 3);
socket.io.engine.writeBuffer.length = 64;
stream.event({...base, seq: 166, name: 'status_update', payload: {status: 'offline', context_exists: true}});
assert.equal(socket.connected, false);
assert.equal(stream.listeners, 0);
})
const tick = () => new Promise(resolve => setImmediate(resolve));
const code = response => response.data.code;
@@ -299,7 +320,7 @@ test('lease expiry retires data channel and queue capacity stays bounded under s
assert.equal(stalled.sockets.length, 1);
});
test('bot events are never forwarded while connected, congested or disconnected', async t => {
test('lifecycle events are forwarded while ready and congestion closes the stale transport', async t => {
const app = setup();
t.after(() => app.manager.stop());
await app.ensure();
@@ -308,23 +329,28 @@ test('bot events are never forwarded while connected, congested or disconnected'
let resyncs = 0;
stream.resync = () => { resyncs++; };
const event = {name: 'status_update', seq: 1, payload: {status: 'offline', context_exists: false}};
let lifecycle = 0;
socket.on('bot_event', () => lifecycle++);
stream.event(event);
assert.equal(socket.sent.filter(([name]) => name === 'bot_event').length, 0);
assert.equal(lifecycle, 1);
assert.equal(socket.sent.filter(([name]) => name === 'bot_log').length, 0);
stream.event({name: 'log', seq: 2, payload: {records: []}});
assert.equal(socket.sent.filter(([name]) => name === 'bot_log').length, 1);
socket.emit('relay_status', {forward_session_id: app.command.forward_session_id, node_ready: true, reason: 'resync_required'});
assert.equal(resyncs, 1);
socket.io.engine.writeBuffer.length = 64;
stream.event({...event, seq: 2});
stream.event({name: 'log', seq: 3, payload: {records: []}});
assert.equal(socket.sent.filter(([name]) => name === 'bot_log').length, 1);
assert.equal(socket.sent.filter(([name]) => name === 'bot_event').length, 0);
assert.equal(lifecycle, 1);
assert.equal(socket.connected, false);
socket.emit('relay_status', {forward_session_id: app.command.forward_session_id, node_ready: true, reason: 'resync_required'});
assert.equal(resyncs, 1);
socket.disconnect();
stream.event({...event, seq: 3});
stream.event({name: 'log', seq: 4, payload: {records: []}});
assert.equal(socket.sent.filter(([name]) => name === 'bot_log').length, 1);
assert.equal(socket.sent.filter(([name]) => name === 'bot_event').length, 0);
assert.equal(lifecycle, 1);
assert.equal(stream.listeners, 0);
});