packages/protocol holds the one list of /sim messages and checks every field, naming what is wrong. packages/sim has the simulation room for F and the web worker: the document's snapshot at start, the engine's clock on the wall clock with Java's speed as cable delay, subscriptions to cable activity, a card's new frames and a program's new window lines, capture and clock kept over a stop, the command line as first action, a core failure told to every client. LocalSimHost mirrors the document into the worker and back without echo. M9 in Chromium: 17 examples give the same capture as in Node, character for character; the room runs in a real worker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
29 lines
1.2 KiB
TypeScript
29 lines
1.2 KiB
TypeScript
// The worker's side: a replica of the editor's document and the room that
|
|
// reads it, ticking every TICK_MILLISECONDS.
|
|
|
|
import * as Y from 'yjs';
|
|
import { CHANNEL_ORIGIN, type ChannelMessage, type Port } from './channel.js';
|
|
import { SimulationRoom, TICK_MILLISECONDS, type RoomOptions } from './room.js';
|
|
|
|
export interface ServeOptions extends RoomOptions {
|
|
/** Calls `tick` every so many milliseconds. */
|
|
readonly every: (milliseconds: number, tick: () => void) => void;
|
|
}
|
|
|
|
export function serveRoom(port: Port, options: ServeOptions): SimulationRoom {
|
|
const doc = new Y.Doc();
|
|
doc.on('update', (update: Uint8Array, origin: unknown) => {
|
|
if (origin !== CHANNEL_ORIGIN) port.postMessage({ type: 'doc', update });
|
|
});
|
|
const room = new SimulationRoom(doc, options);
|
|
const client = room.connect((message) => port.postMessage({ type: 'host', message }));
|
|
port.addEventListener('message', (event) => {
|
|
const data = event.data as ChannelMessage;
|
|
if (data?.type === 'doc') Y.applyUpdate(doc, data.update, CHANNEL_ORIGIN);
|
|
else if (data?.type === 'client') room.receive(client, data.message);
|
|
});
|
|
port.start?.();
|
|
options.every(TICK_MILLISECONDS, () => room.tick());
|
|
return room;
|
|
}
|