feat(editor): F4.6b the command line on a computer's desktop
A new oracle, TerminalWindowRun, types into Java's own command line window: the window writes each line after its prompt, takes a blank line, cls, reset and exit itself, keeps the lines for the arrow keys, and keeps its text, history and working directory over a stop and a start. The room now holds a command line window per computer for everyone (TerminalWindow), kept over stops; the protocol carries its lines and the one way to read them (terminalScreen); the core goes on in the working directory of the run before (continueIn). The desktop opens programs in Java's frame, and the command line works with keyboard and finger: grey on black, the prompt hidden while a command runs, history, chips for the commands, Ctrl+C. The room's failure is now tested in the browser. Fixed: the room handed the engine the wall clock with fractions of a millisecond, and a ping hung after its first timeout. The engine and the room count whole milliseconds, as Java's clock does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -199,6 +199,16 @@ export class Terminal {
|
||||
return this.directoryValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes on in the working directory of a run before, where it is still a
|
||||
* folder: Java's command line keeps it over a stop and a start
|
||||
* (TerminalWindowRun), and the room's window holds it meanwhile.
|
||||
*/
|
||||
continueIn(directory: string): void {
|
||||
const found = this.content(directory);
|
||||
if (found !== undefined && found.content.kind === 'directory') this.directoryValue = found.path;
|
||||
}
|
||||
|
||||
/** The window's prompt. */
|
||||
get prompt(): string {
|
||||
return `${this.directoryValue}> `;
|
||||
|
||||
@@ -1031,7 +1031,7 @@ export class NetworkRuntimeEngine {
|
||||
this.stateValue = initialState(seed);
|
||||
this.packetCapture.beginRun();
|
||||
this.stateValue.phase = 'running';
|
||||
this.stateValue.currentTimeMilliseconds = input.initialTimeMilliseconds;
|
||||
this.stateValue.currentTimeMilliseconds = Math.floor(input.initialTimeMilliseconds);
|
||||
this.stateValue.simulationSpeedPercent = this.runtimeSpeed.percent;
|
||||
this.stateValue.topologySnapshot = input.snapshot;
|
||||
this.random = new JavaCompatibleRandom(seed);
|
||||
@@ -1097,7 +1097,8 @@ export class NetworkRuntimeEngine {
|
||||
}
|
||||
|
||||
case 'advance':
|
||||
return this.advance(input.toMilliseconds);
|
||||
// Whole milliseconds, as Java's clock counts (System.currentTimeMillis): a wall clock's fraction waits for the next step.
|
||||
return this.advance(Math.floor(input.toMilliseconds));
|
||||
|
||||
case 'schedule': {
|
||||
if (this.stateValue.phase !== 'running') return [this.output({ type: 'advanceIgnoredWhileStopped' })];
|
||||
|
||||
@@ -141,6 +141,17 @@ describe('event queue and virtual clock', () => {
|
||||
expect(engine.state.pendingEvents).toEqual([]);
|
||||
});
|
||||
|
||||
it("counts whole milliseconds, as Java's clock does: a time's fraction waits for the next step", () => {
|
||||
const engine = new NetworkRuntimeEngine(9);
|
||||
engine.handle({ type: 'start', snapshot: EMPTY_TOPOLOGY_SNAPSHOT, seed: 9, initialTimeMilliseconds: 10.6 });
|
||||
expect(engine.state.currentTimeMilliseconds).toBe(10);
|
||||
engine.handle({ type: 'schedule', deadlineMilliseconds: 100, kind: marker('at-100') });
|
||||
expect(firedKinds(engine.handle({ type: 'advance', toMilliseconds: 99.9 }))).toEqual([]);
|
||||
expect(engine.state.currentTimeMilliseconds).toBe(99);
|
||||
expect(firedKinds(engine.handle({ type: 'advance', toMilliseconds: 100.2 }))).toEqual([marker('at-100')]);
|
||||
expect(engine.state.currentTimeMilliseconds).toBe(100);
|
||||
});
|
||||
|
||||
it('rejects a past time without changing the clock', () => {
|
||||
const engine = new NetworkRuntimeEngine(7);
|
||||
engine.handle({ type: 'start', snapshot: EMPTY_TOPOLOGY_SNAPSHOT, seed: 7, initialTimeMilliseconds: 10 });
|
||||
|
||||
Reference in New Issue
Block a user