Spaces:
Paused
Paused
File size: 2,648 Bytes
a2cd246 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | /* ============================================================
The in-process Transport β same tab, same process, no socket (ADR-0004).
Wraps a server/session.js session directly. Single-player wires a client
to a session through THIS, exactly as multiplayer will wire a client to a
session through a (Phase 3) WebSocketTransport β same interface
(net/transport.js), so client code written against Transport never knows
which one it has.
submitCommand returns a Promise even though the work underneath is
genuinely synchronous β the mutation happens and is visible to the caller
BEFORE the promise ever resolves (see test/loopback.test.js's own test for
this). That is deliberate: client code written against a Promise-returning
submitCommand is already correct for a transport where the promise takes
real time to resolve, so nothing about the client changes when
WebSocketTransport replaces this. It is also what T-013's fault-injection
layer needs β there is a real async gap here to delay.
tick() and getState() are LOOPBACK-SPECIFIC EXTENSIONS beyond the generic
Transport contract β a real remote transport cannot offer either (ticking
happens server-side, invisible to a remote client; state arrives only as
pushed events, never by synchronous pull). T-012 decides how much of the
client actually leans on these vs. driving purely off onEvent; both are
valid against this shape. Do not add either to net/transport.js's
documented interface β that would silently make it a contract only
loopback can satisfy.
============================================================ */
"use strict";
/**
* @param {ReturnType<import("../server/session.js").createSession>} session
* @returns {Transport & { tick: (dt: number) => void, getState: () => State }}
*/
export function createLoopbackTransport(session) {
const handlers = new Set();
let closed = false;
function emit(event) {
if (closed) return;
for (const h of handlers) h(event);
}
return {
submitCommand(cmd) {
if (closed) return Promise.resolve({ ok: false, code: "closed" });
const result = session.submitCommand(cmd); // synchronous β see file header
emit({ type: "commandResult", result });
return Promise.resolve(result);
},
onEvent(handler) {
handlers.add(handler);
},
close() {
closed = true;
handlers.clear();
},
tick(dt) {
if (closed) return;
session.tick(dt);
emit({ type: "state", state: session.getState() });
},
getState() {
return session.getState();
},
};
}
|