@joystyk/rooms 0.0.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/README.md +147 -2
- package/dist/canonical.d.ts +10 -0
- package/dist/canonical.js +30 -0
- package/dist/canonical.js.map +1 -0
- package/dist/core.d.ts +3 -0
- package/dist/core.js +1879 -0
- package/dist/core.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/names.d.ts +19 -0
- package/dist/names.js +28 -0
- package/dist/names.js.map +1 -0
- package/dist/node/host.d.ts +67 -0
- package/dist/node/host.js +323 -0
- package/dist/node/host.js.map +1 -0
- package/dist/node/index.d.ts +6 -0
- package/dist/node/index.js +7 -0
- package/dist/node/jobs.d.ts +48 -0
- package/dist/node/jobs.js +176 -0
- package/dist/node/jobs.js.map +1 -0
- package/dist/node/registry.d.ts +32 -0
- package/dist/node/registry.js +43 -0
- package/dist/node/registry.js.map +1 -0
- package/dist/node/server.d.ts +59 -0
- package/dist/node/server.js +577 -0
- package/dist/node/server.js.map +1 -0
- package/dist/node/store.d.ts +53 -0
- package/dist/node/store.js +245 -0
- package/dist/node/store.js.map +1 -0
- package/dist/node/support.d.ts +67 -0
- package/dist/node/support.js +126 -0
- package/dist/node/support.js.map +1 -0
- package/dist/policy.d.ts +15 -0
- package/dist/policy.js +41 -0
- package/dist/policy.js.map +1 -0
- package/dist/protocol/index.d.ts +43 -0
- package/dist/protocol/index.js +48 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/schemas.d.ts +792 -0
- package/dist/protocol/schemas.js +332 -0
- package/dist/protocol/schemas.js.map +1 -0
- package/dist/protocol/types.d.ts +279 -0
- package/dist/protocol/types.js +1 -0
- package/dist/record.d.ts +173 -0
- package/dist/record.js +181 -0
- package/dist/record.js.map +1 -0
- package/dist/types.d.ts +374 -0
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -0
- package/package.json +56 -11
package/dist/core.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","names":[],"sources":["../src/core.ts"],"sourcesContent":["// The room core: `step(room, command, now) → { room', effects }` (room-server.md section 3).\n// Pure: it reads no clock, randomness, storage or network. The adapter supplies `now`, fresh\n// seeds and token hashes, and carries out the effects.\nimport {\n err,\n ok,\n type EngineInput,\n type Json,\n type JobOutcome,\n type Outcome,\n type Result,\n type Rng,\n type RngState,\n type SeatClock,\n type SeatId,\n type SeatInfo,\n type Transition,\n} from '@joystyk/contracts';\nimport { forkRng, restoreRng, SFC32 } from '@joystyk/kit';\nimport { canonicalJson, fingerprint } from './canonical.ts';\nimport { admitName, nameReplacedMessage } from './names.ts';\nimport { policyProblem } from './policy.ts';\nimport { PROTOCOL_VERSION, RejectionCode, gameRejection } from './protocol/index.ts';\nimport type {\n Presence,\n PublicSeat,\n RoomControl,\n RoomOffer,\n RoomSettings,\n ServerMessage,\n SnapshotMessage,\n UpdateMessage,\n WelcomeMessage,\n} from './protocol/index.ts';\nimport {\n DEFAULT_LIMITS,\n type AiRetry,\n type ConnId,\n type CoreLimits,\n type CreateParams,\n type DecisionClock,\n type DropReason,\n type LiveRoom,\n type LoggedInput,\n type MatchRecord,\n type MatchSeeds,\n type Member,\n type OfferRecord,\n type RoomCommand,\n type RoomCore,\n type RoomCoreOptions,\n type RoomEffect,\n type RoomError,\n type RoomRecord,\n type SeatRecord,\n type Step,\n} from './types.ts';\n\ntype Rejection = { readonly code: string; readonly message: string };\ntype Viewer = SeatId | null;\ntype LogLevel = 'info' | 'warn' | 'error';\n\n/** Binds the core to one engine at one rules version. */\nexport const createRoomCore = (options: RoomCoreOptions): RoomCore => {\n const { engine, pin } = options;\n const limits: CoreLimits = { ...DEFAULT_LIMITS, ...options.limits };\n if (pin.rulesVersion !== engine.rulesVersion) {\n throw new Error(`The pin's rules version ${pin.rulesVersion} differs from the engine's ${engine.rulesVersion}.`);\n }\n const defaultLevel = (): string | null => engine.ai?.levels[0] ?? null;\n\n const displayName = (seat: SeatRecord): string =>\n seat.claim?.name ?? (seat.controller === 'ai' ? `Bot ${seat.id + 1}` : `Seat ${seat.id + 1}`);\n\n const seatInfo = (seat: SeatRecord): SeatInfo =>\n seat.controller === 'ai' && seat.aiLevel !== null\n ? { id: seat.id, name: displayName(seat), controller: 'ai', aiLevel: seat.aiLevel }\n : { id: seat.id, name: displayName(seat), controller: seat.controller };\n\n const publicSeat = (seat: SeatRecord): PublicSeat => ({\n id: seat.id,\n name: displayName(seat),\n controller: seat.controller,\n aiLevel: seat.controller === 'ai' ? seat.aiLevel : null,\n claimed: seat.claim !== null,\n ready: seat.ready,\n });\n\n const keysOf = (state: Json, seats: number): string[] =>\n Array.from({ length: seats }, (_, seat) =>\n canonicalJson(engine.decisionKey ? engine.decisionKey(state, seat) : engine.viewFor(state, seat)),\n );\n\n const settingsOf = (record: RoomRecord): RoomSettings => ({\n decisionMs: record.policy.decisionMs,\n bank: record.policy.bank ?? null,\n aiLevel: record.aiLevel,\n });\n\n /** The offer as clients see it: for a rematch, every computer seat has accepted too. */\n const publicOffer = (record: RoomRecord): RoomOffer | null => {\n const offer = record.offer ?? null;\n if (offer === null) return null;\n const computers =\n offer.kind === 'rematch'\n ? record.seats.filter((s) => s.controller === 'ai' && !offer.accepted.includes(s.id)).map((s) => s.id)\n : [];\n return { ...offer, accepted: [...offer.accepted, ...computers] };\n };\n\n // ── The AI's stream (game-contract.md, \"The AI's stream\"; rule 7) ─────────────────────\n // Forks are always the kit's SFC32; any other saved algorithm goes to the engine's factory.\n const restoreAi = (state: RngState): Rng => (state.algorithm === SFC32 ? restoreRng(state) : options.rng(state));\n /** The child stream for one decision of `seat`: fork number `seat` of the AI stream. */\n const aiDecisionSeed = (state: RngState, seat: SeatId): RngState => {\n const parent = restoreAi(state);\n for (let i = 0; i < seat; i += 1) forkRng(parent);\n return forkRng(parent).save();\n };\n /** The AI stream after an accepted AI move: one fork per seat, past every child it handed out. */\n const advanceAi = (state: RngState, seats: number): RngState => {\n const parent = restoreAi(state);\n for (let i = 0; i < seats; i += 1) forkRng(parent);\n return parent.save();\n };\n\n /** A new match on `seats`, or the engine's reason it can't start. */\n const newMatch = (\n record: RoomRecord,\n seats: readonly SeatRecord[],\n seeds: MatchSeeds,\n revision: number,\n now: number,\n ): Result<MatchRecord> => {\n const infos = seats.map(seatInfo);\n const rng = options.rng(seeds.engine);\n const init = engine.init(record.config, infos, rng);\n if (!init.ok) return init;\n const state = init.value;\n return ok({\n number: (record.match?.number ?? 0) + 1,\n game: pin,\n config: record.config,\n seats: infos,\n seed: seeds.engine,\n state,\n rng: rng.save(),\n aiRng: seeds.ai,\n decisionRevision: seats.map(() => revision),\n clocks: [],\n ...(record.policy.bank ? { banks: seats.map(() => record.policy.bank?.initialMs ?? 0) } : {}),\n jobs: [],\n presentation: null,\n log: [],\n outcome: engine.outcome(state),\n startedAt: now,\n });\n };\n\n const create = (params: CreateParams, now: number): Result<RoomRecord, RoomError> => {\n const problem = policyProblem(params.policy);\n if (problem !== null) return err({ code: 'bad-policy', message: problem });\n const immediate = params.policy.start === 'immediate';\n const creator = admitName(params.creator.name, { room: params.code, epoch: 0, revision: 0, seat: 0 }).name;\n const seats: SeatRecord[] = Array.from({ length: params.policy.seats.max }, (_, id) => {\n if (id === 0)\n return {\n id,\n claim: { tokenHash: params.creator.tokenHash, name: creator },\n controller: 'human',\n aiLevel: null,\n ready: false,\n };\n return immediate\n ? { id, claim: null, controller: 'ai', aiLevel: defaultLevel(), ready: false }\n : { id, claim: null, controller: 'human', aiLevel: null, ready: false };\n });\n const record: RoomRecord = {\n format: 1,\n code: params.code,\n epoch: 0,\n writes: 0,\n policy: params.policy,\n phase: 'lobby',\n revision: 0,\n game: pin,\n config: params.config,\n aiLevel: defaultLevel(),\n seats,\n host: 0,\n match: null,\n receipts: [],\n createdAt: now,\n updatedAt: now,\n };\n if (!immediate) return ok(record);\n const initFailed = (error: string): Result<RoomRecord, RoomError> => {\n const reference = `init-${params.code}-${now}`;\n return err({\n code: 'init-failed',\n message: `The game could not start. Reference: ${reference}.`,\n diagnostic: { reference, error },\n });\n };\n let match: Result<MatchRecord>;\n try {\n match = newMatch(record, seats, params.seeds, 1, now);\n } catch (error) {\n return initFailed(String(error));\n }\n if (!match.ok) return initFailed(match.error.message);\n return ok({\n ...record,\n revision: 1,\n match: match.value,\n phase: match.value.outcome === null ? 'playing' : 'finished',\n });\n };\n\n const load = (record: RoomRecord): LiveRoom => ({\n record,\n members: new Map(),\n absentSince: new Map(),\n jobStartedAt: new Map(),\n aiInFlight: new Map(),\n aiRetry: new Map(),\n alarmAt: null,\n closed: false,\n });\n\n /** One step's working state. Everything here is local to a single `step` call. */\n class Tx {\n record: RoomRecord;\n readonly members: Map<ConnId, Member>;\n readonly absentSince: Map<SeatId, number>;\n readonly jobStartedAt: Map<string, number>;\n readonly aiInFlight: Map<SeatId, number>;\n readonly aiRetry: Map<SeatId, AiRetry>;\n /** The seat whose own command produced the input being applied (bank increments). */\n endedBy: SeatId | null = null;\n readonly now: number;\n readonly start: LiveRoom;\n dirty = false;\n closed: boolean;\n readonly inputs: LoggedInput[] = [];\n readonly beats: { readonly event: Json; readonly state?: Json }[] = [];\n presentationChanged = false;\n holdUntil: number;\n membershipChanged = false;\n snapshotAll = false;\n readonly snapshotTo = new Set<ConnId>();\n readonly early: RoomEffect[] = [];\n readonly receipts: RoomEffect[] = [];\n readonly jobs: RoomEffect[] = [];\n readonly logs: RoomEffect[] = [];\n autoInputs = 0;\n aiLoopLogged = false;\n private keys: string[] | null = null;\n\n constructor(room: LiveRoom, now: number) {\n this.start = room;\n this.record = room.record;\n this.members = new Map(room.members);\n this.absentSince = new Map(room.absentSince);\n this.jobStartedAt = new Map(room.jobStartedAt);\n this.aiInFlight = new Map(room.aiInFlight);\n this.aiRetry = new Map(room.aiRetry);\n this.closed = room.closed;\n this.now = now;\n this.holdUntil = now;\n }\n\n log(level: LogLevel, event: string, fields: { readonly [k: string]: Json } = {}): void {\n this.logs.push({ type: 'log', level, event, fields: { room: this.record.code, ...fields } });\n }\n\n set(record: RoomRecord): void {\n this.record = record;\n this.dirty = true;\n }\n\n setMatch(match: MatchRecord): void {\n this.set({ ...this.record, match });\n }\n\n bump(): void {\n this.set({ ...this.record, revision: this.record.revision + 1 });\n }\n\n setSeat(seat: SeatRecord): void {\n this.set({ ...this.record, seats: this.record.seats.map((s) => (s.id === seat.id ? seat : s)) });\n }\n\n seat(id: SeatId): SeatRecord | undefined {\n return this.record.seats[id];\n }\n\n hasMember(seat: SeatId): boolean {\n for (const member of this.members.values()) if (member.seat === seat) return true;\n return false;\n }\n\n connsOf(seat: SeatId): ConnId[] {\n return [...this.members].filter(([, m]) => m.seat === seat).map(([conn]) => conn);\n }\n\n /** No human seat has a connection: the table is paused, so no AI move, clock or offer runs (section 3.5 rule 8, D-58). */\n frozen(): boolean {\n for (const member of this.members.values()) {\n if (member.seat !== null && this.seat(member.seat)?.controller === 'human') return false;\n }\n return true;\n }\n\n playing(): boolean {\n return this.record.phase === 'playing' && this.record.match !== null;\n }\n\n send(to: readonly ConnId[], message: ServerMessage, bucket: RoomEffect[] = this.early): void {\n if (to.length > 0) bucket.push({ type: 'send', to, message });\n }\n\n drop(conn: ConnId, reason: DropReason): void {\n this.members.delete(conn);\n this.snapshotTo.delete(conn);\n this.early.push({ type: 'drop', conn, reason });\n this.membershipChanged = true;\n }\n\n receipt(\n conn: ConnId,\n id: string,\n result: { status: 'accepted'; revision: number } | ({ status: 'rejected' } & Rejection),\n ): void {\n const message: ServerMessage =\n result.status === 'accepted'\n ? { v: PROTOCOL_VERSION, t: 'receipt', id, result }\n : { v: PROTOCOL_VERSION, t: 'receipt', id, result: { ...result, revision: this.record.revision } };\n this.send([conn], message, this.receipts);\n }\n\n reject(conn: ConnId, id: string, rejection: Rejection, seat: Viewer): void {\n this.receipt(conn, id, { status: 'rejected', ...rejection });\n if (rejection.code === RejectionCode.stale) {\n this.snapshotTo.add(conn);\n this.log('info', 'stale_action', { seat });\n } else {\n this.log('info', 'action_rejected', { code: rejection.code, seat });\n }\n }\n\n // ── Engine inputs ─────────────────────────────────────────────────────────────────\n\n /** Each seat's decision key for the current state, cached until the state changes. */\n decisionKeys(): string[] {\n if (this.keys !== null) return this.keys;\n const match = this.record.match;\n if (match === null) return [];\n this.keys = keysOf(match.state, match.seats.length);\n return this.keys;\n }\n\n /** Applies one engine input. On success the revision rises by one and the input is logged. */\n apply(input: EngineInput<Json>): Result<Transition<Json, Json>, Rejection> {\n const match = this.record.match;\n if (match === null || this.record.phase !== 'playing')\n return err({ code: RejectionCode.wrongPhase, message: 'No match is in progress.' });\n const before = this.decisionKeys();\n const rng = options.rng(match.rng);\n let result: Result<Transition<Json, Json>>;\n try {\n result = engine.apply(match.state, input, rng);\n } catch (error) {\n this.log('error', 'engine_error', { input: input.kind, error: String(error) });\n return err({ code: RejectionCode.engineError, message: 'The game hit an error and ignored that move.' });\n }\n if (!result.ok) return err({ code: gameRejection(result.error.code), message: result.error.message });\n const transition = result.value;\n const revision = this.record.revision + 1;\n const state = transition.state;\n const after = keysOf(state, match.seats.length);\n this.keys = after;\n const decisionRevision = match.decisionRevision.map((r, seat) => (before[seat] === after[seat] ? r : revision));\n const events = transition.beats.map((beat) => beat.event);\n const log = [...match.log, ...events].slice(-limits.logEvents);\n const requested = (transition.jobs ?? []).map((request) => ({ request, revision }));\n for (const { request } of requested) {\n const spec = engine.jobs?.[request.kind];\n this.jobStartedAt.set(request.id, this.now);\n this.jobs.push({\n type: 'start-job',\n jobId: request.id,\n kind: request.kind,\n payload: request.payload,\n timeoutMs: spec?.timeoutMs ?? 0,\n });\n this.log('info', 'job_started', { kind: request.kind, job: request.id });\n }\n let outcome: Outcome | null = engine.outcome(state);\n if (outcome !== null) outcome = { ...outcome, elapsedMs: Math.max(0, this.now - match.startedAt) };\n this.set({\n ...this.record,\n revision,\n phase: outcome === null ? 'playing' : 'finished',\n match: {\n ...match,\n state,\n rng: rng.save(),\n decisionRevision,\n log,\n jobs: [...match.jobs, ...requested],\n outcome,\n },\n });\n this.beats.push(...transition.beats);\n const logged: EngineInput<Json> =\n input.kind === 'job' && input.outcome.ok\n ? { kind: 'job', jobId: input.jobId, outcome: { ok: true, value: input.outcome.value } }\n : input;\n this.inputs.push({ match: match.number, revision, at: this.now, input: logged });\n const hold = engine.holdMs ? engine.holdMs(transition, match.presentation) : 0;\n this.holdUntil = this.now + Math.max(0, hold);\n if (outcome !== null)\n this.log('info', 'match_finished', { result: outcome.result, match: match.number, ms: outcome.elapsedMs ?? 0 });\n this.endedBy = input.kind === 'command' ? input.seat : null;\n this.recomputeClocks();\n this.endedBy = null;\n return ok(transition);\n }\n\n /** Hands a seat to the AI or back to a human, telling the engine through a `seat` input. */\n setController(id: SeatId, controller: 'human' | 'ai'): void {\n const seat = this.seat(id);\n if (seat === undefined || seat.controller === controller) return;\n const next: SeatRecord = {\n ...seat,\n controller,\n aiLevel: controller === 'ai' ? (seat.aiLevel ?? this.record.aiLevel ?? defaultLevel()) : seat.aiLevel,\n };\n this.setSeat(next);\n const before = this.record.revision;\n if (this.playing()) {\n const change =\n controller === 'ai' && next.aiLevel !== null\n ? { type: 'controller' as const, controller, name: displayName(next), aiLevel: next.aiLevel }\n : { type: 'controller' as const, controller, name: displayName(next) };\n const result = this.apply({ kind: 'seat', seat: id, change });\n if (!result.ok) this.log('warn', 'seat_input_rejected', { seat: id, code: result.error.code });\n }\n if (this.record.revision === before) this.bump();\n this.log('info', 'seat_handover', { seat: id, to: controller });\n }\n\n // ── Clocks and deadlines ──────────────────────────────────────────────────────────\n\n /**\n * Ends the clocks whose decision is over and starts clocks for new decisions (section\n * 3.7). In bank mode an ended clock charges the seat's bank for the time it ran, and adds\n * the increment when the seat's own command ended it.\n */\n recomputeClocks(): void {\n // A pause that just began or ended settles first, so a new clock never moves with an old pause.\n this.syncPause();\n const match = this.record.match;\n if (match === null) return;\n const bank = this.record.policy.bank ?? null;\n const frozen = this.frozen();\n const next: DecisionClock[] = [];\n if (this.record.phase === 'playing') {\n for (const seat of new Set(engine.pending(match.state))) {\n if (this.seat(seat)?.controller !== 'human') continue;\n const decisionRevision = match.decisionRevision[seat] ?? 0;\n const existing = match.clocks.find((c) => c.seat === seat && c.decisionRevision === decisionRevision);\n if (existing !== undefined) {\n next.push(existing);\n continue;\n }\n if (frozen) continue;\n const startsAt = Math.max(this.now, this.holdUntil);\n if (bank !== null) {\n const remaining = match.banks?.[seat] ?? bank.initialMs;\n const ms = engine.decisionMs ? engine.decisionMs(match.state, seat, remaining) : remaining;\n if (ms === null) continue;\n next.push({ seat, at: startsAt + ms, decisionRevision, startsAt, onBank: ms === remaining });\n } else {\n const ms = engine.decisionMs\n ? engine.decisionMs(match.state, seat, this.record.policy.decisionMs)\n : this.record.policy.decisionMs;\n if (ms === null) continue;\n next.push({ seat, at: startsAt + ms, decisionRevision });\n }\n }\n }\n let banks = match.banks;\n if (bank !== null && banks !== undefined) {\n const charged = [...banks];\n // A paused table's clocks stopped when the pause began: only live time is charged.\n const until = match.pausedAt ?? this.now;\n for (const ended of match.clocks) {\n if (next.includes(ended) || ended.onBank !== true) continue;\n const ran = Math.max(0, until - (ended.startsAt ?? until));\n charged[ended.seat] =\n Math.max(0, (charged[ended.seat] ?? 0) - ran) + (this.endedBy === ended.seat ? bank.incrementMs : 0);\n }\n banks = charged;\n }\n const clocksChanged = canonicalJson(next) !== canonicalJson(match.clocks);\n const banksChanged = banks !== match.banks && canonicalJson(banks ?? []) !== canonicalJson(match.banks ?? []);\n if (clocksChanged || banksChanged)\n this.setMatch({ ...match, clocks: next, ...(banks === undefined ? {} : { banks }) });\n }\n\n /** A timed-out bank clock empties the bank before its timeout input applies. */\n emptyBank(clock: DecisionClock): void {\n const match = this.record.match;\n if (match?.banks === undefined || clock.onBank !== true) return;\n this.setMatch({ ...match, banks: match.banks.map((b, seat) => (seat === clock.seat ? 0 : b)) });\n }\n\n /**\n * Starts or ends the table's pause to match who is connected (D-58). While no player is\n * connected nothing runs; when one returns, every clock's deadline, a bank clock's start and\n * an end offer's deadline move on by the length of the pause, so nobody is charged for it.\n */\n syncPause(): void {\n const match = this.record.match;\n if (match === null) return;\n if (this.frozen()) {\n if (match.pausedAt !== undefined || this.record.phase !== 'playing') return;\n this.setMatch({ ...match, pausedAt: this.now });\n this.log('info', 'table_paused', {});\n return;\n }\n if (match.pausedAt === undefined) return;\n const { pausedAt, ...rest } = match;\n const shift = Math.max(0, this.now - pausedAt);\n const clocks = match.clocks.map((c) =>\n c.startsAt === undefined\n ? { ...c, at: c.at + shift }\n : { ...c, at: c.at + shift, startsAt: c.startsAt + shift },\n );\n this.setMatch({ ...rest, clocks });\n const offer = this.record.offer ?? null;\n if (offer?.status === 'open' && offer.endsAt !== null)\n this.set({ ...this.record, offer: { ...offer, endsAt: offer.endsAt + shift } });\n // Grace periods stood still too: an absent player's seat isn't handed on for the pause.\n for (const [seat, since] of this.absentSince) this.absentSince.set(seat, since + shift);\n this.log('info', 'table_resumed', { pausedMs: shift });\n }\n\n /** Ends the match early with no result, as an offer to end it asked (D-61). */\n endMatch(how: 'agreed' | 'unopposed'): void {\n const match = this.record.match;\n if (match === null || this.record.phase !== 'playing') return;\n for (const job of match.jobs) this.jobStartedAt.delete(job.request.id);\n this.aiInFlight.clear();\n this.aiRetry.clear();\n const summary =\n how === 'agreed'\n ? 'The players agreed to end the match early.'\n : 'The match ended early: nobody declined the offer to end it.';\n const outcome: Outcome = {\n result: 'abandoned',\n placements: [],\n summary,\n elapsedMs: Math.max(0, this.now - match.startedAt),\n };\n this.set({\n ...this.record,\n revision: this.record.revision + 1,\n phase: 'finished',\n offer: null,\n match: { ...match, jobs: [], outcome },\n });\n this.log('info', 'match_ended_early', { how, match: match.number });\n this.recomputeClocks();\n }\n\n /** After a restart: at least `resumeGraceMs` for every clock; a bank clock's start moves too, so the wait isn't charged. */\n extendClocks(): void {\n const match = this.record.match;\n if (match === null) return;\n const floor = this.now + limits.resumeGraceMs;\n if (!match.clocks.some((c) => c.at < floor)) return;\n const clocks = match.clocks.map((c) => {\n if (c.at >= floor) return c;\n const shift = floor - c.at;\n return c.startsAt === undefined ? { ...c, at: floor } : { ...c, at: floor, startsAt: c.startsAt + shift };\n });\n this.setMatch({ ...match, clocks });\n }\n\n budgetLeft(): boolean {\n if (this.autoInputs < limits.aiInputsPerStep) return true;\n if (!this.aiLoopLogged) {\n this.aiLoopLogged = true;\n this.log('error', 'ai_loop', { inputs: this.autoInputs });\n }\n return false;\n }\n\n completeJob(jobId: string, outcome: JobOutcome): void {\n const match = this.record.match;\n if (match === null) return;\n this.jobStartedAt.delete(jobId);\n const presentation = outcome.ok && outcome.presentation !== undefined ? outcome.presentation : match.presentation;\n if (presentation !== match.presentation) this.presentationChanged = true;\n this.setMatch({ ...match, jobs: match.jobs.filter((j) => j.request.id !== jobId), presentation });\n const stripped: JobOutcome = outcome.ok ? { ok: true, value: outcome.value } : outcome;\n this.log('info', 'job_finished', { job: jobId, ok: outcome.ok });\n const result = this.apply({ kind: 'job', jobId, outcome: stripped });\n if (!result.ok) this.log('error', 'job_input_rejected', { job: jobId, code: result.error.code });\n }\n\n /** Absence deadlines for seats, or null when the seat has none. */\n absentDue(seat: SeatRecord): number | null {\n if (seat.controller !== 'human' || this.hasMember(seat.id)) return null;\n const since = this.absentSince.get(seat.id);\n if (since !== undefined) return since + this.record.policy.graceMs;\n // A human seat nobody holds (its player left) is overdue at once.\n return seat.claim === null ? Number.NEGATIVE_INFINITY : null;\n }\n\n hostDue(): number | null {\n const host = this.record.host;\n if (host === null || this.hasMember(host)) return null;\n if (this.hostCandidate() === null) return null;\n return (this.absentSince.get(host) ?? Number.NEGATIVE_INFINITY) + this.record.policy.graceMs;\n }\n\n /** The connected human in the lowest-numbered seat, other than the host. */\n hostCandidate(exclude: SeatId | null = this.record.host): SeatId | null {\n for (const seat of this.record.seats) {\n if (seat.id !== exclude && seat.claim !== null && seat.controller === 'human' && this.hasMember(seat.id))\n return seat.id;\n }\n return null;\n }\n\n /** Applies the first due deadline. True when something happened. */\n applyOneDue(): boolean {\n const match = this.record.match;\n const now = this.now;\n if (match !== null && this.record.phase === 'playing') {\n for (const job of match.jobs) {\n const started = this.jobStartedAt.get(job.request.id);\n const spec = engine.jobs?.[job.request.kind];\n if (started !== undefined && spec !== undefined && started + spec.timeoutMs <= now) {\n this.log('warn', 'job_timeout', { job: job.request.id });\n this.completeJob(job.request.id, { ok: false, reason: 'timeout' });\n return true;\n }\n }\n if (!this.frozen()) {\n const due = [...match.clocks].filter((c) => c.at <= now).sort((a, b) => a.at - b.at || a.seat - b.seat)[0];\n if (due !== undefined) {\n this.autoInputs += 1;\n this.emptyBank(due);\n const result = this.apply({ kind: 'timeout', seat: due.seat });\n if (!result.ok) {\n // A refused timeout must not refire at once: drop this clock; a fresh one starts.\n this.log('error', 'timeout_rejected', { seat: due.seat, code: result.error.code });\n const current = this.record.match;\n if (current !== null) this.setMatch({ ...current, clocks: current.clocks.filter((c) => c !== due) });\n this.recomputeClocks();\n }\n return true;\n }\n const offer = this.record.offer ?? null;\n if (offer?.status === 'open' && offer.endsAt !== null && offer.endsAt <= now) {\n this.autoInputs += 1;\n this.endMatch('unopposed');\n return true;\n }\n const pending = new Set(engine.pending(match.state));\n for (const seat of this.record.seats) {\n const at = this.absentDue(seat);\n if (at === null || at > now) continue;\n if (this.record.policy.absent === 'ai' && seat.claim !== null) {\n this.autoInputs += 1;\n this.setController(seat.id, 'ai');\n return true;\n }\n if (this.record.policy.absent === 'timeout' && pending.has(seat.id)) {\n this.autoInputs += 1;\n const result = this.apply({ kind: 'timeout', seat: seat.id });\n if (!result.ok) {\n this.log('error', 'timeout_rejected', { seat: seat.id, code: result.error.code });\n return false;\n }\n return true;\n }\n }\n }\n }\n const hostAt = this.hostDue();\n if (hostAt !== null && hostAt <= now) {\n const next = this.hostCandidate();\n if (next !== null) {\n this.set({ ...this.record, host: next, revision: this.record.revision + 1 });\n this.log('info', 'host_transferred', { seat: next });\n return true;\n }\n }\n return false;\n }\n\n /**\n * Plays one runner-driven AI move (rule 7). True when something happened. A heavy AI\n * never chooses here: the core asks the pool with `start-ai`, once per decision.\n */\n playOneAi(): boolean {\n const match = this.record.match;\n const ai = engine.ai;\n if (ai === undefined || match === null || !this.playing() || match.jobs.length > 0 || this.frozen()) return false;\n const seats = engine.pending(match.state).filter((s) => this.seat(s)?.controller === 'ai');\n if (options.heavyAi === true) {\n for (const seat of seats) {\n const decisionRevision = match.decisionRevision[seat] ?? 0;\n if (this.aiInFlight.get(seat) === decisionRevision) continue;\n const retry = this.aiRetry.get(seat);\n if (retry?.decisionRevision === decisionRevision && retry.at > this.now) continue;\n this.aiInFlight.set(seat, decisionRevision);\n const level = this.seat(seat)?.aiLevel ?? ai.levels[0] ?? '';\n this.jobs.push({\n type: 'start-ai',\n seat,\n decisionRevision,\n view: engine.viewFor(match.state, seat),\n level,\n rng: aiDecisionSeed(match.aiRng, seat),\n });\n this.log('info', 'ai_started', { seat });\n }\n return false;\n }\n const seat = seats[0];\n if (seat === undefined) return false;\n const level = this.seat(seat)?.aiLevel ?? ai.levels[0] ?? '';\n const child = restoreRng(aiDecisionSeed(match.aiRng, seat));\n this.autoInputs += 1;\n let command: Json;\n try {\n command = ai.choose(engine.viewFor(match.state, seat), seat, level, child);\n } catch (error) {\n this.log('error', 'engine_error', { seat, input: 'ai', error: String(error) });\n return this.aiFallback(seat);\n }\n return this.applyAi(seat, command);\n }\n\n /** Applies an AI's command; the AI stream moves on only when it is accepted. */\n applyAi(seat: SeatId, command: Json): boolean {\n const result = this.apply({ kind: 'command', seat, command });\n if (result.ok) {\n const match = this.record.match;\n if (match !== null) this.setMatch({ ...match, aiRng: advanceAi(match.aiRng, match.seats.length) });\n return true;\n }\n this.log('error', 'ai_illegal_move', { seat, code: result.error.code });\n return this.aiFallback(seat);\n }\n\n aiFallback(seat: SeatId): boolean {\n const result = this.apply({ kind: 'timeout', seat });\n if (result.ok) return true;\n this.log('error', 'timeout_rejected', { seat, code: result.error.code });\n return false;\n }\n\n /** Deadlines, then AI moves, until nothing more is due or the loop bound is reached. */\n settle(): void {\n for (;;) {\n if (!this.budgetLeft()) return;\n if (this.applyOneDue()) continue;\n if (!this.budgetLeft()) return;\n if (this.playOneAi()) continue;\n return;\n }\n }\n\n alarm(): number | null {\n const candidates: number[] = [];\n const match = this.record.match;\n if (match !== null && this.record.phase === 'playing') {\n for (const job of match.jobs) {\n const started = this.jobStartedAt.get(job.request.id);\n const spec = engine.jobs?.[job.request.kind];\n if (started !== undefined && spec !== undefined) candidates.push(started + spec.timeoutMs);\n }\n if (!this.frozen()) {\n for (const clock of match.clocks) candidates.push(clock.at);\n const offer = this.record.offer ?? null;\n if (offer?.status === 'open' && offer.endsAt !== null) candidates.push(offer.endsAt);\n const pending = new Set(engine.pending(match.state));\n for (const [seat, retry] of this.aiRetry) {\n const due = pending.has(seat) && this.seat(seat)?.controller === 'ai' && match.jobs.length === 0;\n if (due && (match.decisionRevision[seat] ?? 0) === retry.decisionRevision) candidates.push(retry.at);\n }\n for (const seat of this.record.seats) {\n const at = this.absentDue(seat);\n if (at === null || !Number.isFinite(at)) continue;\n if (this.record.policy.absent === 'ai' && seat.claim !== null) candidates.push(at);\n if (this.record.policy.absent === 'timeout' && pending.has(seat.id)) candidates.push(at);\n }\n }\n }\n const hostAt = this.hostDue();\n if (hostAt !== null && Number.isFinite(hostAt)) candidates.push(hostAt);\n if (candidates.length === 0) return null;\n const at = Math.min(...candidates);\n // Something due that couldn't be applied (the loop bound, a refused input): look again soon.\n return at <= this.now ? this.now + 1_000 : at;\n }\n\n // ── Messages ──────────────────────────────────────────────────────────────────────\n\n presence(): Presence {\n let spectators = 0;\n for (const m of this.members.values()) if (m.seat === null) spectators += 1;\n return {\n connected: this.record.seats.map((s) => this.hasMember(s.id)),\n clocks: this.seatClocks(),\n paused: this.record.phase === 'playing' && this.frozen(),\n spectators,\n };\n }\n\n /**\n * Clocks as clients show them: running per-move clocks, or every seat's bank. On a paused\n * table (D-58) each clock reads as it stood when the pause began, and says it is paused.\n */\n seatClocks(): SeatClock[] {\n const match = this.record.match;\n if (match === null || this.record.phase !== 'playing') return [];\n const frozen = this.frozen();\n const at = frozen ? (match.pausedAt ?? this.now) : this.now;\n const paused = frozen ? { paused: true } : {};\n const bank = this.record.policy.bank ?? null;\n if (bank === null)\n return match.clocks.map((c) => ({\n seat: c.seat,\n remainingMs: Math.max(0, c.at - at),\n running: !frozen,\n ...paused,\n }));\n return match.seats.map(({ id: seat }) => {\n const left = match.banks?.[seat] ?? bank.initialMs;\n const clock = match.clocks.find((c) => c.seat === seat);\n if (clock === undefined) return { seat, remainingMs: left, running: false };\n const startsAt = clock.startsAt ?? at;\n const remainingMs =\n clock.onBank === true ? Math.max(0, left - Math.max(0, at - startsAt)) : Math.max(0, clock.at - at);\n return { seat, remainingMs, running: !frozen && at >= startsAt, ...paused };\n });\n }\n\n private readonly views = new Map<Viewer, Json | null>();\n\n view(viewer: Viewer): Json | null {\n if (this.views.has(viewer)) return this.views.get(viewer) ?? null;\n const match = this.record.match;\n const view = match === null ? null : engine.viewFor(match.state, viewer);\n this.views.set(viewer, view);\n return view;\n }\n\n snapshot(viewer: Viewer, presence: Presence): SnapshotMessage {\n const match = this.record.match;\n const history: Json[] = [];\n for (const event of match?.log ?? []) {\n const redacted = engine.eventFor(event, viewer);\n if (redacted !== null) history.push(redacted);\n }\n return {\n v: PROTOCOL_VERSION,\n t: 'snapshot',\n epoch: this.record.epoch,\n revision: this.record.revision,\n phase: this.record.phase,\n seat: viewer,\n seats: this.record.seats.map(publicSeat),\n host: this.record.host,\n game: this.record.game,\n settings: settingsOf(this.record),\n view: this.view(viewer),\n history,\n pending: match !== null && this.record.phase === 'playing' ? engine.pending(match.state) : [],\n outcome: match?.outcome ?? null,\n presentation: match?.presentation ?? null,\n offer: publicOffer(this.record),\n presence,\n serverTime: this.now,\n };\n }\n\n welcome(conn: ConnId, seat: Viewer, issueToken: boolean): void {\n const message: WelcomeMessage = {\n v: PROTOCOL_VERSION,\n t: 'welcome',\n room: this.record.code,\n seat,\n host: this.record.host,\n game: this.record.game,\n protocol: PROTOCOL_VERSION,\n };\n this.early.push({ type: 'welcome', conn, message, issueToken });\n }\n\n finish(): Step {\n const start = this.start.record;\n const effects: RoomEffect[] = [];\n let record = this.record;\n if (this.dirty) {\n record = { ...record, writes: start.writes + 1, updatedAt: this.now };\n this.record = record;\n effects.push({ type: 'persist', record, inputs: this.inputs });\n }\n effects.push(...this.early);\n const presence = this.presence();\n const changed = record.revision !== start.revision;\n const groups = new Map<Viewer, ConnId[]>();\n for (const [conn, member] of this.members) {\n if (this.snapshotAll || this.snapshotTo.has(conn)) continue;\n const list = groups.get(member.seat) ?? [];\n list.push(conn);\n groups.set(member.seat, list);\n }\n if (changed) {\n const seats = record.seats.map(publicSeat);\n const seatsChanged = canonicalJson(seats) !== canonicalJson(start.seats.map(publicSeat));\n const settings = settingsOf(record);\n const settingsChanged = canonicalJson(settings) !== canonicalJson(settingsOf(start));\n const match = record.match;\n const pending = match !== null && record.phase === 'playing' ? engine.pending(match.state) : [];\n for (const [viewer, conns] of groups) {\n const beats = [];\n for (const beat of this.beats) {\n const event = engine.eventFor(beat.event, viewer);\n if (event === null) continue;\n beats.push(beat.state === undefined ? { event } : { event, view: engine.viewFor(beat.state, viewer) });\n }\n const update: UpdateMessage = {\n v: PROTOCOL_VERSION,\n t: 'update',\n epoch: record.epoch,\n revision: record.revision,\n from: start.revision,\n phase: record.phase,\n host: record.host,\n ...(seatsChanged ? { seats } : {}),\n ...(settingsChanged ? { settings } : {}),\n beats,\n view: this.view(viewer),\n pending,\n outcome: match?.outcome ?? null,\n ...(this.presentationChanged ? { presentation: match?.presentation ?? null } : {}),\n offer: publicOffer(record),\n presence,\n serverTime: this.now,\n };\n effects.push({ type: 'send', to: conns, message: update });\n }\n }\n for (const [conn, member] of this.members) {\n if (this.snapshotAll || this.snapshotTo.has(conn))\n effects.push({ type: 'send', to: [conn], message: this.snapshot(member.seat, presence) });\n }\n effects.push(...this.receipts);\n const clocksChanged = canonicalJson(record.match?.clocks ?? []) !== canonicalJson(start.match?.clocks ?? []);\n if (!changed && (this.membershipChanged || clocksChanged)) {\n const others = [...groups.values()].flat();\n this.send(others, { v: PROTOCOL_VERSION, t: 'presence', presence, serverTime: this.now }, effects);\n }\n const alarmAt = this.closed ? null : this.alarm();\n if (alarmAt !== this.start.alarmAt) effects.push({ type: 'alarm', at: alarmAt });\n effects.push(...this.jobs, ...this.logs);\n return {\n room: {\n record,\n members: this.members,\n absentSince: this.absentSince,\n jobStartedAt: this.jobStartedAt,\n aiInFlight: this.aiInFlight,\n aiRetry: this.aiRetry,\n alarmAt,\n closed: this.closed,\n },\n effects,\n };\n }\n }\n\n // ── Commands ──────────────────────────────────────────────────────────────────────────\n\n const uniqueName = (tx: Tx, name: string): string => {\n // Names differ ignoring case, so \"Ana\" and \"ana\" never sit at one table.\n const used = new Set(tx.record.seats.flatMap((s) => (s.claim === null ? [] : [s.claim.name.toLowerCase()])));\n if (!used.has(name.toLowerCase())) return name;\n const base = name.slice(0, 21);\n let n = 2;\n while (used.has(`${base} ${n}`.toLowerCase())) n += 1;\n return `${base} ${n}`;\n };\n\n const join = (tx: Tx, command: Extract<RoomCommand, { type: 'join' }>): void => {\n if (tx.members.has(command.conn)) return;\n const wasFrozen = tx.frozen();\n const member = (seat: Viewer): Member => ({ seat, protocol: command.protocol, joinedAt: tx.now });\n const claimed =\n command.presentedTokenHash === null\n ? undefined\n : tx.record.seats.find((s) => s.claim?.tokenHash === command.presentedTokenHash);\n if (claimed !== undefined && !command.watch) {\n for (const conn of tx.connsOf(claimed.id)) {\n tx.send([conn], { v: PROTOCOL_VERSION, t: 'replaced' });\n tx.drop(conn, 'replaced');\n tx.log('info', 'replaced', { seat: claimed.id });\n }\n tx.members.set(command.conn, member(claimed.id));\n tx.absentSince.delete(claimed.id);\n if (claimed.controller === 'ai') tx.setController(claimed.id, 'human');\n tx.welcome(command.conn, claimed.id, false);\n } else {\n const free = command.watch ? undefined : freeSeat(tx);\n if (free !== undefined) {\n const { code: room, epoch, revision } = tx.record;\n const admitted = admitName(command.name, { room, epoch, revision, seat: free.id });\n const name = uniqueName(tx, admitted.name);\n const wasAi = free.controller === 'ai';\n tx.setSeat({ ...free, claim: { tokenHash: command.freshTokenHash, name }, ready: false });\n tx.members.set(command.conn, member(free.id));\n tx.absentSince.delete(free.id);\n if (wasAi) tx.setController(free.id, 'human');\n else tx.bump();\n if (tx.record.host === null) tx.set({ ...tx.record, host: free.id });\n tx.log('info', 'seat_claimed', { seat: free.id });\n tx.welcome(command.conn, free.id, true);\n if (admitted.rejection !== null) {\n tx.send([command.conn], {\n v: PROTOCOL_VERSION,\n t: 'notice',\n code: 'name-replaced',\n message: nameReplacedMessage(admitted.rejection, name),\n });\n tx.log('info', 'name_replaced', { seat: free.id, reason: admitted.rejection.code });\n }\n } else {\n let spectators = 0;\n for (const m of tx.members.values()) if (m.seat === null) spectators += 1;\n if (spectators >= tx.record.policy.maxSpectators) {\n tx.send([command.conn], {\n v: PROTOCOL_VERSION,\n t: 'notice',\n code: 'room-full',\n message: 'This room is full.',\n });\n tx.early.push({ type: 'drop', conn: command.conn, reason: 'room-full' });\n return;\n }\n tx.members.set(command.conn, member(null));\n tx.welcome(command.conn, null, false);\n }\n }\n tx.membershipChanged = true;\n tx.snapshotTo.add(command.conn);\n // A player is back at a paused table: its clocks run on from where they stopped (D-58).\n if (wasFrozen && !tx.frozen()) tx.syncPause();\n };\n\n const freeSeat = (tx: Tx): SeatRecord | undefined => {\n if (tx.record.phase === 'lobby') return tx.record.seats.find((s) => s.claim === null && s.controller === 'human');\n if (tx.record.policy.lateJoin === 'take-ai-seat' && tx.record.phase !== 'closed')\n return tx.record.seats.find((s) => s.claim === null && s.controller === 'ai');\n return undefined;\n };\n\n const leave = (tx: Tx, conn: ConnId): void => {\n const member = tx.members.get(conn);\n if (member === undefined) return;\n tx.members.delete(conn);\n tx.membershipChanged = true;\n if (member.seat === null || tx.hasMember(member.seat)) return;\n if (tx.seat(member.seat)?.claim) tx.absentSince.set(member.seat, tx.now);\n };\n\n /** The receipt and revision checks shared by `act` and `control` (section 3.8). True: proceed. */\n const admit = (\n tx: Tx,\n conn: ConnId,\n id: string,\n seat: SeatId,\n expectedRevision: number,\n print: string,\n judgeStale: boolean,\n ): boolean => {\n const receipt = tx.record.receipts.find((r) => r.seat === seat && r.id === id);\n if (receipt !== undefined) {\n if (receipt.fingerprint === print) {\n tx.receipt(conn, id, { status: 'accepted', revision: receipt.revision });\n tx.log('info', 'duplicate_action', { seat });\n } else {\n tx.reject(\n conn,\n id,\n { code: RejectionCode.actionIdReused, message: 'That action ID was already used for a different action.' },\n seat,\n );\n }\n return false;\n }\n if (expectedRevision > tx.record.revision) {\n tx.reject(\n conn,\n id,\n { code: RejectionCode.badRevision, message: 'Your table is ahead of the server. Reloading it.' },\n seat,\n );\n tx.snapshotTo.add(conn);\n return false;\n }\n const decided = tx.record.match?.decisionRevision[seat] ?? 0;\n if (judgeStale && expectedRevision < decided) {\n tx.reject(conn, id, { code: RejectionCode.stale, message: 'The table changed. Review it and try again.' }, seat);\n return false;\n }\n return true;\n };\n\n const accept = (tx: Tx, conn: ConnId, id: string, seat: SeatId, print: string, revision: number): void => {\n const mine = tx.record.receipts.filter((r) => r.seat === seat);\n const others = tx.record.receipts.filter((r) => r.seat !== seat);\n const kept = mine.slice(Math.max(0, mine.length - (limits.receiptsPerSeat - 1)));\n tx.set({ ...tx.record, receipts: [...others, ...kept, { seat, id, fingerprint: print, revision }] });\n tx.receipt(conn, id, { status: 'accepted', revision });\n };\n\n const act = (tx: Tx, command: Extract<RoomCommand, { type: 'act' }>): void => {\n const member = tx.members.get(command.conn);\n if (member === undefined) return void tx.log('warn', 'unknown_connection', {});\n if (member.seat === null) {\n return tx.reject(\n command.conn,\n command.id,\n { code: RejectionCode.spectator, message: 'Spectators can watch but not play.' },\n null,\n );\n }\n const seat = member.seat;\n const print = fingerprint({ expectedRevision: command.expectedRevision, command: command.command });\n if (!admit(tx, command.conn, command.id, seat, command.expectedRevision, print, true)) return;\n if (!tx.playing())\n return tx.reject(\n command.conn,\n command.id,\n { code: RejectionCode.wrongPhase, message: 'No match is in progress.' },\n seat,\n );\n const result = tx.apply({ kind: 'command', seat, command: command.command });\n if (!result.ok) return tx.reject(command.conn, command.id, result.error, seat);\n accept(tx, command.conn, command.id, seat, print, tx.record.revision);\n };\n\n const notHost: Rejection = { code: RejectionCode.notHost, message: 'Only the host can do that.' };\n const wrongPhase = (message: string): Rejection => ({ code: RejectionCode.wrongPhase, message });\n const notAllowed = (message: string): Rejection => ({ code: RejectionCode.notAllowed, message });\n\n /** Rebuilds the seat list for a new match; members, absences and the host follow their seats. */\n const startMatch = (tx: Tx, seeds: MatchSeeds, compact: boolean): Rejection | null => {\n const filled = compact ? tx.record.seats.filter((s) => s.claim !== null || s.controller === 'ai') : tx.record.seats;\n const remap = new Map(filled.map((s, i) => [s.id, i]));\n const seats = filled.map((s, i) => ({ ...s, id: i, ready: false }));\n const revision = tx.record.revision + 1;\n let match: Result<MatchRecord>;\n let config = tx.record.config;\n try {\n const finished = tx.record.match;\n if (finished !== null && finished.outcome !== null && engine.rematch !== undefined) {\n const state = engine.schemas.state['~standard'].validate(finished.state);\n if (!(state instanceof Promise) && state.issues === undefined)\n config = engine.rematch(finished.config, state.value);\n }\n const parsed = engine.schemas.config['~standard'].validate(config);\n if (parsed instanceof Promise || parsed.issues !== undefined)\n return { code: RejectionCode.engineError, message: 'The rematch settings could not be prepared.' };\n config = parsed.value;\n match = newMatch({ ...tx.record, seats, config }, seats, seeds, revision, tx.now);\n } catch (error) {\n tx.log('error', 'engine_error', { input: 'init', error: String(error) });\n return { code: RejectionCode.engineError, message: 'The game could not start.' };\n }\n if (!match.ok) return { code: gameRejection(match.error.code), message: match.error.message };\n for (const [conn, member] of tx.members) {\n if (member.seat === null) continue;\n const next = remap.get(member.seat);\n if (next === undefined) tx.drop(conn, 'left-room');\n else tx.members.set(conn, { ...member, seat: next });\n }\n const absent = [...tx.absentSince].flatMap(([seat, since]) => {\n const next = remap.get(seat);\n return next === undefined ? [] : [[next, since] as const];\n });\n tx.absentSince.clear();\n for (const [seat, since] of absent) tx.absentSince.set(seat, since);\n const host = tx.record.host === null ? null : (remap.get(tx.record.host) ?? null);\n // Receipts follow their seats, so a retried lobby action still finds its receipt.\n const receipts = tx.record.receipts.flatMap((r) => {\n const next = remap.get(r.seat);\n return next === undefined ? [] : [{ ...r, seat: next }];\n });\n const value = match.value;\n tx.set({\n ...tx.record,\n seats,\n host,\n receipts,\n revision,\n config,\n game: pin,\n match: value,\n offer: null,\n phase: value.outcome === null ? 'playing' : 'finished',\n });\n tx.snapshotAll = true;\n tx.log('info', value.number === 1 ? 'match_started' : 'rematch', {\n match: value.number,\n game: pin.id,\n rules: pin.rulesVersion,\n });\n tx.recomputeClocks();\n return null;\n };\n\n // ── Offers: rematch and end-match by offer and accept (section 3.10, D-61) ──────────────\n\n /** Seats whose yes a rematch needs: every seat a player holds. Computer seats accept on their own. */\n const rematchVoters = (tx: Tx): SeatId[] =>\n tx.record.seats.filter((s) => s.claim !== null && s.controller === 'human').map((s) => s.id);\n\n /** Seats whose yes ends a match early: every other connected player. */\n const endVoters = (tx: Tx, by: SeatId): SeatId[] =>\n tx.record.seats\n .filter((s) => s.id !== by && s.claim !== null && s.controller === 'human' && tx.hasMember(s.id))\n .map((s) => s.id);\n\n const setOffer = (tx: Tx, offer: OfferRecord | null): void => tx.set({ ...tx.record, offer });\n\n /**\n * Acts on the offer once it has every yes it needs: a rematch starts (when this step carries\n * seeds), or the match ends. An offer outlives neither its match nor its proposer's seat.\n */\n const resolveOffer = (tx: Tx, seeds: MatchSeeds | null): Rejection | null => {\n const offer = tx.record.offer ?? null;\n if (offer === null) return null;\n const phase = tx.record.phase;\n if ((offer.kind === 'rematch' && phase !== 'finished') || (offer.kind === 'end' && phase !== 'playing')) {\n setOffer(tx, null);\n return null;\n }\n if (offer.status !== 'open') return null;\n const proposer = tx.seat(offer.by);\n if (proposer?.claim == null || proposer.controller !== 'human') {\n setOffer(tx, { ...offer, status: 'withdrawn' });\n tx.log('info', 'offer_withdrawn', { kind: offer.kind, seat: offer.by, why: 'seat' });\n return null;\n }\n if (offer.kind === 'rematch') {\n if (seeds === null || !rematchVoters(tx).every((s) => offer.accepted.includes(s))) return null;\n return startMatch(tx, seeds, false);\n }\n if (!tx.frozen() && endVoters(tx, offer.by).every((s) => offer.accepted.includes(s))) tx.endMatch('agreed');\n return null;\n };\n\n const OFFER_LOG = { accept: 'offer_accepted', decline: 'offer_declined', withdraw: 'offer_withdrawn' } as const;\n\n /** Makes an offer, or answers the open one. */\n const offerControl = (\n tx: Tx,\n seat: SeatId,\n ctl: Extract<RoomControl, { kind: 'offer' | 'accept' | 'decline' | 'withdraw' }>,\n seeds: MatchSeeds,\n ): Rejection | null => {\n const offer = tx.record.offer ?? null;\n const open = offer?.status === 'open' ? offer : null;\n if (ctl.kind === 'offer') {\n if (ctl.offer === 'rematch' && tx.record.phase !== 'finished')\n return wrongPhase('A rematch can be offered once the match is over.');\n if (ctl.offer === 'end' && tx.record.phase !== 'playing')\n return wrongPhase('Only a match in progress can be ended.');\n if (open !== null) {\n // Offering what is already on the table accepts it.\n if (open.accepted.includes(seat)) return notAllowed('You have already agreed to that.');\n setOffer(tx, { ...open, accepted: [...open.accepted, seat] });\n tx.log('info', OFFER_LOG.accept, { kind: open.kind, seat });\n } else {\n const endsAt = ctl.offer === 'end' ? tx.now + limits.endOfferMs : null;\n setOffer(tx, {\n id: tx.record.revision + 1,\n kind: ctl.offer,\n by: seat,\n status: 'open',\n accepted: [seat],\n declinedBy: null,\n endsAt,\n });\n tx.log('info', 'offer_made', { kind: ctl.offer, seat });\n }\n return resolveOffer(tx, seeds);\n }\n if (open === null || open.id !== ctl.offer) return notAllowed('That offer is no longer open.');\n if (ctl.kind === 'withdraw') {\n if (open.by !== seat) return notAllowed('Only the player who made the offer can withdraw it.');\n setOffer(tx, { ...open, status: 'withdrawn' });\n } else if (open.by === seat) {\n return notAllowed('You made this offer.');\n } else if (ctl.kind === 'decline') {\n setOffer(tx, { ...open, status: 'declined', declinedBy: seat });\n } else {\n if (open.accepted.includes(seat)) return notAllowed('You have already accepted.');\n setOffer(tx, { ...open, accepted: [...open.accepted, seat] });\n }\n tx.log('info', OFFER_LOG[ctl.kind], { kind: open.kind, seat });\n return ctl.kind === 'accept' ? resolveOffer(tx, seeds) : null;\n };\n\n const passHost = (tx: Tx, leaving: SeatId): void => {\n if (tx.record.host !== leaving) return;\n const connected = tx.hostCandidate(leaving);\n const claimed = tx.record.seats.find((s) => s.id !== leaving && s.claim !== null)?.id ?? null;\n tx.set({ ...tx.record, host: connected ?? claimed });\n tx.log('info', 'host_transferred', { seat: connected ?? claimed });\n };\n\n const control = (tx: Tx, command: Extract<RoomCommand, { type: 'control' }>): void => {\n const member = tx.members.get(command.conn);\n if (member === undefined) return void tx.log('warn', 'unknown_connection', {});\n const { conn, id } = command;\n if (member.seat === null)\n return tx.reject(\n conn,\n id,\n { code: RejectionCode.spectator, message: 'Spectators can watch but not play.' },\n null,\n );\n const seat = member.seat;\n const print = fingerprint({ expectedRevision: command.expectedRevision, control: command.control });\n if (!admit(tx, conn, id, seat, command.expectedRevision, print, false)) return;\n const [record, dirty, logs] = [tx.record, tx.dirty, tx.logs.length];\n const problem = applyControl(tx, seat, command.control, command.seeds);\n if (problem !== null) {\n // A rejection changes nothing durable, even after a change (an accept whose rematch the engine refused).\n tx.record = record;\n tx.dirty = dirty;\n tx.logs.length = logs;\n return tx.reject(conn, id, problem, seat);\n }\n // Starting a match can renumber seats; the receipt belongs to the seat as it is now.\n accept(tx, conn, id, tx.members.get(conn)?.seat ?? seat, print, tx.record.revision);\n };\n\n const applyControl = (tx: Tx, seat: SeatId, ctl: RoomControl, seeds: MatchSeeds): Rejection | null => {\n const phase = tx.record.phase;\n const isHost = tx.record.host === seat;\n const before = tx.record.revision;\n switch (ctl.kind) {\n case 'ready': {\n if (phase !== 'lobby') return wrongPhase('Readiness is set in the lobby.');\n const current = tx.seat(seat);\n if (current === undefined) return notAllowed('You have no seat.');\n tx.setSeat({ ...current, ready: ctl.ready });\n break;\n }\n case 'settings': {\n if (!isHost) return notHost;\n if (phase !== 'lobby') return wrongPhase('Settings change in the lobby only.');\n if (ctl.aiLevel !== undefined && engine.ai !== undefined && !engine.ai.levels.includes(ctl.aiLevel)) {\n return { code: RejectionCode.badCommand, message: 'That AI level does not exist.' };\n }\n const aiLevel = ctl.aiLevel ?? tx.record.aiLevel;\n // A per-move clock and a bank exclude each other: setting one turns the other off.\n let policy = tx.record.policy;\n if (ctl.decisionMs !== undefined)\n policy = { ...policy, decisionMs: ctl.decisionMs, ...(ctl.decisionMs === null ? {} : { bank: null }) };\n if (ctl.bank !== undefined)\n policy = { ...policy, bank: ctl.bank, ...(ctl.bank === null ? {} : { decisionMs: null }) };\n tx.set({\n ...tx.record,\n policy,\n config: ctl.config ?? tx.record.config,\n aiLevel,\n seats:\n ctl.aiLevel === undefined\n ? tx.record.seats\n : tx.record.seats.map((s) => (s.controller === 'ai' ? { ...s, aiLevel } : s)),\n });\n break;\n }\n case 'fill-with-ai': {\n if (!isHost) return notHost;\n const target = tx.seat(ctl.seat);\n if (target === undefined) return notAllowed('There is no such seat.');\n if (phase === 'lobby') {\n if (target.claim !== null || target.controller !== 'human')\n return notAllowed('Only an open seat can be filled with the AI.');\n tx.setSeat({ ...target, controller: 'ai', aiLevel: tx.record.aiLevel ?? defaultLevel() });\n } else if (phase === 'playing') {\n if (target.controller !== 'human' || tx.hasMember(target.id))\n return notAllowed('Only an absent player’s seat can be handed to the AI.');\n tx.setController(target.id, 'ai');\n } else {\n return wrongPhase('The match is over.');\n }\n break;\n }\n case 'remove-seat': {\n if (!isHost) return notHost;\n if (phase !== 'lobby') return wrongPhase('Seats are removed in the lobby only.');\n const target = tx.seat(ctl.seat);\n if (target === undefined || ctl.seat === seat) return notAllowed('You can’t remove that seat.');\n for (const c of tx.connsOf(target.id)) tx.drop(c, 'left-room');\n tx.absentSince.delete(target.id);\n tx.setSeat({ id: target.id, claim: null, controller: 'human', aiLevel: null, ready: false });\n break;\n }\n case 'start': {\n if (!isHost) return notHost;\n if (phase !== 'lobby') return wrongPhase('The match has already started.');\n const filled = tx.record.seats.filter((s) => s.claim !== null || s.controller === 'ai');\n if (filled.length < tx.record.policy.seats.min)\n return notAllowed(`At least ${tx.record.policy.seats.min} seats must be filled.`);\n const waiting = filled.some((s) => s.controller === 'human' && (!s.ready || !tx.hasMember(s.id)));\n if (waiting) return notAllowed('Everyone must be connected and ready.');\n const problem = startMatch(tx, seeds, true);\n if (problem !== null) return problem;\n break;\n }\n case 'offer':\n case 'accept':\n case 'decline':\n case 'withdraw': {\n const problem = offerControl(tx, seat, ctl, seeds);\n if (problem !== null) return problem;\n break;\n }\n case 'leave-seat': {\n const current = tx.seat(seat);\n if (current === undefined) return notAllowed('You have no seat.');\n if (phase === 'playing') {\n const result = tx.apply({ kind: 'seat', seat, change: { type: 'left' } });\n if (!result.ok) tx.log('warn', 'seat_input_rejected', { seat, code: result.error.code });\n }\n const afterLeft = tx.seat(seat) ?? current;\n tx.setSeat({\n ...afterLeft,\n claim: null,\n ready: false,\n ...(phase === 'lobby' ? { controller: 'human' as const, aiLevel: null } : {}),\n });\n for (const c of tx.connsOf(seat)) tx.drop(c, 'left-room');\n if (phase === 'playing') {\n if (tx.record.policy.absent === 'ai') tx.setController(seat, 'ai');\n else tx.absentSince.set(seat, tx.now - tx.record.policy.graceMs);\n } else {\n tx.absentSince.delete(seat);\n }\n passHost(tx, seat);\n // A rematch may have waited only for this player; an offer of theirs goes with them.\n const problem = resolveOffer(tx, seeds);\n if (problem !== null) tx.log('warn', 'rematch_refused', { code: problem.code });\n break;\n }\n case 'resign': {\n if (phase !== 'playing') return wrongPhase('No match is in progress.');\n const result = tx.apply({ kind: 'resign', seat });\n if (!result.ok) return result.error;\n break;\n }\n }\n if (tx.record.revision === before) tx.bump();\n return null;\n };\n\n /** A heavy AI's answer (rule 7): applied only if that seat still waits on the same decision. */\n const aiChosen = (tx: Tx, command: Extract<RoomCommand, { type: 'ai-chosen' }>): void => {\n const { seat, decisionRevision } = command;\n if (tx.aiInFlight.get(seat) === decisionRevision) tx.aiInFlight.delete(seat);\n const match = tx.record.match;\n const current =\n match !== null &&\n tx.playing() &&\n tx.seat(seat)?.controller === 'ai' &&\n engine.pending(match.state).includes(seat) &&\n (match.decisionRevision[seat] ?? 0) === decisionRevision;\n if (!current) return void tx.log('info', 'ai_superseded', { seat });\n // A frozen room plays no AI; the same child stream asks again once a player returns.\n if (tx.frozen()) return void tx.log('info', 'ai_deferred', { seat });\n if ('failure' in command) {\n // The pool failing is not the AI breaking the rules: ask again, and give up only when this\n // decision itself keeps timing out or crashing its worker, or the AI's module threw.\n const prior = tx.aiRetry.get(seat);\n const same = prior?.decisionRevision === decisionRevision ? prior : undefined;\n const failures = (same?.failures ?? 0) + 1;\n const faults = (same?.faults ?? 0) + (command.failure === 'timeout' || command.failure === 'crashed' ? 1 : 0);\n if (command.failure !== 'failed' && faults < limits.aiAttempts) {\n const at = tx.now + Math.min(limits.aiRetryMaxMs, limits.aiRetryMs * 2 ** (failures - 1));\n tx.aiRetry.set(seat, { decisionRevision, at, failures, faults });\n tx.log('warn', 'ai_retry', { seat, failure: command.failure, failures, at });\n return;\n }\n tx.aiRetry.delete(seat);\n tx.autoInputs += 1;\n tx.log('warn', 'ai_failed', { seat, failure: command.failure, failures });\n tx.aiFallback(seat);\n return;\n }\n tx.aiRetry.delete(seat);\n tx.autoInputs += 1;\n const parsed = engine.schemas.command['~standard'].validate(command.command);\n if (parsed instanceof Promise || parsed.issues !== undefined) {\n tx.log('error', 'ai_illegal_move', { seat, code: RejectionCode.badCommand });\n tx.aiFallback(seat);\n return;\n }\n tx.applyAi(seat, parsed.value);\n };\n\n const resume = (tx: Tx): void => {\n for (const seat of tx.record.seats) if (seat.claim !== null) tx.absentSince.set(seat.id, tx.now);\n // A table that was live when the server stopped lost the downtime: every clock gets at least\n // `resumeGraceMs`, and the pause begins now. A table already paused keeps its clocks as they stood.\n if (tx.record.match?.pausedAt === undefined) tx.extendClocks();\n for (const job of tx.record.match?.jobs ?? []) {\n const spec = engine.jobs?.[job.request.kind];\n tx.jobStartedAt.set(job.request.id, tx.now);\n tx.jobs.push({\n type: 'start-job',\n jobId: job.request.id,\n kind: job.request.kind,\n payload: job.request.payload,\n timeoutMs: spec?.timeoutMs ?? 0,\n });\n tx.log('info', 'job_started', { kind: job.request.kind, job: job.request.id, resumed: true });\n }\n };\n\n const close = (tx: Tx, reason: 'expired' | 'unload'): void => {\n tx.closed = true;\n if (reason === 'unload') return;\n tx.set({ ...tx.record, phase: 'closed', revision: tx.record.revision + 1 });\n for (const conn of [...tx.members.keys()]) {\n tx.send([conn], { v: PROTOCOL_VERSION, t: 'closing', reason: 'expired', retryAfterMs: null });\n tx.drop(conn, 'room-closed');\n }\n tx.log('info', 'room_expired', {});\n };\n\n const closedStep = (room: LiveRoom, command: RoomCommand): Step => {\n if (command.type !== 'join' && command.type !== 'act' && command.type !== 'control' && command.type !== 'resync')\n return { room, effects: [] };\n return {\n room,\n effects: [\n {\n type: 'send',\n to: [command.conn],\n message: { v: PROTOCOL_VERSION, t: 'closing', reason: 'closed', retryAfterMs: null },\n },\n { type: 'drop', conn: command.conn, reason: 'room-closed' },\n ],\n };\n };\n\n const step = (room: LiveRoom, command: RoomCommand, now: number): Step => {\n if (room.closed || room.record.phase === 'closed') return closedStep(room, command);\n const tx = new Tx(room, now);\n try {\n // The clock wins ties: whatever is due at `now` applies before the command (rule 4).\n if (command.type !== 'resume' && command.type !== 'close') tx.settle();\n switch (command.type) {\n case 'join':\n join(tx, command);\n break;\n case 'leave':\n leave(tx, command.conn);\n break;\n case 'act':\n act(tx, command);\n break;\n case 'control':\n control(tx, command);\n break;\n case 'resync':\n if (tx.members.has(command.conn)) tx.snapshotTo.add(command.conn);\n break;\n case 'timer-fired':\n break;\n case 'ai-chosen':\n aiChosen(tx, command);\n break;\n case 'job-completed':\n if (tx.record.match?.jobs.some((j) => j.request.id === command.jobId) !== true) {\n tx.log('info', 'job_superseded', { job: command.jobId });\n } else {\n tx.completeJob(command.jobId, command.outcome);\n }\n break;\n case 'resume':\n resume(tx);\n break;\n case 'close':\n close(tx, command.reason);\n break;\n }\n if (!tx.closed) {\n resolveOffer(tx, null);\n tx.settle();\n tx.recomputeClocks();\n resolveOffer(tx, null);\n tx.syncPause();\n // An offer that changed on its own (a moved deadline, a proposer gone) is news to every client.\n if (\n tx.record.revision === room.record.revision &&\n canonicalJson(publicOffer(tx.record)) !== canonicalJson(publicOffer(room.record))\n )\n tx.bump();\n }\n return tx.finish();\n } catch (error) {\n // An engine that throws outside `apply` (pending, viewFor, ai…) is a bug: keep the old state.\n const effects: RoomEffect[] = [\n {\n type: 'log',\n level: 'error',\n event: 'engine_error',\n fields: { room: room.record.code, command: command.type, error: String(error) },\n },\n ];\n if (command.type === 'act' || command.type === 'control') {\n effects.unshift({\n type: 'send',\n to: [command.conn],\n message: {\n v: PROTOCOL_VERSION,\n t: 'receipt',\n id: command.id,\n result: {\n status: 'rejected',\n code: RejectionCode.engineError,\n message: 'The game hit an error and ignored that move.',\n revision: room.record.revision,\n },\n },\n });\n }\n return { room, effects };\n }\n };\n\n return { engine, pin, create, load, step };\n};\n"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createRoomCore } from './core.js';
|
|
2
|
+
export { canonicalJson, fingerprint, hashText } from './canonical.js';
|
|
3
|
+
export { admitName, nameReplacedMessage, type AdmittedName, type NameSeed } from './names.js';
|
|
4
|
+
export { DEFAULT_GRACE_MS, DEFAULT_MAX_SPECTATORS, DEFAULT_RETAIN_MS, MAX_ROOM_AGE_MS, policyFromManifest, policyProblem, } from './policy.js';
|
|
5
|
+
export { decodeRecord, RECORD_FORMAT, RoomRecordSchema, type DecodeError } from './record.js';
|
|
6
|
+
export { DEFAULT_LIMITS, type ActionId, type AiFailure, type AiRetry, type ConnId, type CoreLimits, type CreateParams, type DecisionClock, type DropReason, type GamePin, type LiveRoom, type LoggedInput, type MatchRecord, type MatchSeeds, type Member, type OfferKind, type OfferRecord, type PendingJob, type Receipt, type RoomCode, type RoomCommand, type RoomControl, type RoomCore, type RoomCoreOptions, type RoomEffect, type RoomError, type RoomOffer, type RoomPhase, type RoomPolicy, type RoomRecord, type SeatClaim, type SeatRecord, type Step, } from './types.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { canonicalJson, fingerprint, hashText } from "./canonical.js";
|
|
2
|
+
import { admitName, nameReplacedMessage } from "./names.js";
|
|
3
|
+
import { DEFAULT_GRACE_MS, DEFAULT_MAX_SPECTATORS, DEFAULT_RETAIN_MS, MAX_ROOM_AGE_MS, policyFromManifest, policyProblem } from "./policy.js";
|
|
4
|
+
import { DEFAULT_LIMITS } from "./types.js";
|
|
5
|
+
import { createRoomCore } from "./core.js";
|
|
6
|
+
import { RECORD_FORMAT, RoomRecordSchema, decodeRecord } from "./record.js";
|
|
7
|
+
export { DEFAULT_GRACE_MS, DEFAULT_LIMITS, DEFAULT_MAX_SPECTATORS, DEFAULT_RETAIN_MS, MAX_ROOM_AGE_MS, RECORD_FORMAT, RoomRecordSchema, admitName, canonicalJson, createRoomCore, decodeRecord, fingerprint, hashText, nameReplacedMessage, policyFromManifest, policyProblem };
|
package/dist/names.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type NameRejection } from '@joystyk/identity/names';
|
|
2
|
+
import type { SeatId } from '@joystyk/contracts';
|
|
3
|
+
import type { RoomCode } from './protocol/index.js';
|
|
4
|
+
/** The name a seat gets, and why the one asked for was refused (null when it was kept). */
|
|
5
|
+
export type AdmittedName = {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly rejection: NameRejection | null;
|
|
8
|
+
};
|
|
9
|
+
/** Where in the room a name is admitted; it seeds the generated name. */
|
|
10
|
+
export type NameSeed = {
|
|
11
|
+
readonly room: RoomCode;
|
|
12
|
+
readonly epoch: number;
|
|
13
|
+
readonly revision: number;
|
|
14
|
+
readonly seat: SeatId;
|
|
15
|
+
};
|
|
16
|
+
/** The normalized name if `checkDisplayName` accepts it; otherwise a generated one and the reason. */
|
|
17
|
+
export declare const admitName: (input: string, seed: NameSeed) => AdmittedName;
|
|
18
|
+
/** What the player is told when their name was replaced. It never repeats the refused name. */
|
|
19
|
+
export declare const nameReplacedMessage: (rejection: NameRejection, shown: string) => string;
|
package/dist/names.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { restoreRng, seedFromParts } from "@joystyk/kit";
|
|
2
|
+
import { checkDisplayName, generateName } from "@joystyk/identity/names";
|
|
3
|
+
//#region src/names.ts
|
|
4
|
+
/** The normalized name if `checkDisplayName` accepts it; otherwise a generated one and the reason. */
|
|
5
|
+
var admitName = (input, seed) => {
|
|
6
|
+
const checked = checkDisplayName(input);
|
|
7
|
+
if (checked.ok) return {
|
|
8
|
+
name: checked.value,
|
|
9
|
+
rejection: null
|
|
10
|
+
};
|
|
11
|
+
const rng = restoreRng(seedFromParts([
|
|
12
|
+
"display-name",
|
|
13
|
+
seed.room,
|
|
14
|
+
seed.epoch,
|
|
15
|
+
seed.revision,
|
|
16
|
+
seed.seat
|
|
17
|
+
]));
|
|
18
|
+
return {
|
|
19
|
+
name: generateName(() => rng.next()),
|
|
20
|
+
rejection: checked.error
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
/** What the player is told when their name was replaced. It never repeats the refused name. */
|
|
24
|
+
var nameReplacedMessage = (rejection, shown) => `${rejection.message} You're playing as ${shown}.`;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { admitName, nameReplacedMessage };
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=names.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"names.js","names":[],"sources":["../src/names.ts"],"sourcesContent":["// Seat names (D-11, D-26). Every name another player sees passes the identity package's\n// filter. A refused name is replaced by a generated one, drawn from a stream seeded by the\n// room itself (code, epoch, revision, seat), so the core stays pure and the same step always\n// picks the same name.\nimport { checkDisplayName, generateName, type NameRejection } from '@joystyk/identity/names';\nimport { restoreRng, seedFromParts } from '@joystyk/kit';\nimport type { SeatId } from '@joystyk/contracts';\nimport type { RoomCode } from './protocol/index.ts';\n\n/** The name a seat gets, and why the one asked for was refused (null when it was kept). */\nexport type AdmittedName = { readonly name: string; readonly rejection: NameRejection | null };\n\n/** Where in the room a name is admitted; it seeds the generated name. */\nexport type NameSeed = {\n readonly room: RoomCode;\n readonly epoch: number;\n readonly revision: number;\n readonly seat: SeatId;\n};\n\n/** The normalized name if `checkDisplayName` accepts it; otherwise a generated one and the reason. */\nexport const admitName = (input: string, seed: NameSeed): AdmittedName => {\n const checked = checkDisplayName(input);\n if (checked.ok) return { name: checked.value, rejection: null };\n const rng = restoreRng(seedFromParts(['display-name', seed.room, seed.epoch, seed.revision, seed.seat]));\n return { name: generateName(() => rng.next()), rejection: checked.error };\n};\n\n/** What the player is told when their name was replaced. It never repeats the refused name. */\nexport const nameReplacedMessage = (rejection: NameRejection, shown: string): string =>\n `${rejection.message} You're playing as ${shown}.`;\n"],"mappings":";;;;AAqBA,IAAa,aAAa,OAAe,SAAiC;CACxE,MAAM,UAAU,iBAAiB,KAAK;CACtC,IAAI,QAAQ,IAAI,OAAO;EAAE,MAAM,QAAQ;EAAO,WAAW;CAAK;CAC9D,MAAM,MAAM,WAAW,cAAc;EAAC;EAAgB,KAAK;EAAM,KAAK;EAAO,KAAK;EAAU,KAAK;CAAI,CAAC,CAAC;CACvG,OAAO;EAAE,MAAM,mBAAmB,IAAI,KAAK,CAAC;EAAG,WAAW,QAAQ;CAAM;AAC1E;;AAGA,IAAa,uBAAuB,WAA0B,UAC5D,GAAG,UAAU,QAAQ,qBAAqB,MAAM"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { ConnId, LiveRoom, RoomCode, RoomCommand, RoomCore } from '../types.js';
|
|
2
|
+
import type { JobPool } from './jobs.js';
|
|
3
|
+
import type { EngineRegistry } from './registry.js';
|
|
4
|
+
import { type RoomStore } from './store.js';
|
|
5
|
+
import type { Clock, Logger } from './support.js';
|
|
6
|
+
export interface Connection {
|
|
7
|
+
readonly id: ConnId;
|
|
8
|
+
/** The wire version this connection joined with. */
|
|
9
|
+
protocol: number;
|
|
10
|
+
/** The fresh token generated for this connection's join; revealed only if the core issues a seat. */
|
|
11
|
+
pendingToken: string | null;
|
|
12
|
+
readonly bufferedAmount: number;
|
|
13
|
+
send(text: string): void;
|
|
14
|
+
close(code: number, reason: string): void;
|
|
15
|
+
}
|
|
16
|
+
type Live = {
|
|
17
|
+
room: LiveRoom;
|
|
18
|
+
core: RoomCore;
|
|
19
|
+
timer: {
|
|
20
|
+
cancel(): void;
|
|
21
|
+
} | null;
|
|
22
|
+
readonly conns: Map<ConnId, Connection>;
|
|
23
|
+
idleSince: number;
|
|
24
|
+
/** Commands whose persist failed and that must not be lost: retried before the next timer. */
|
|
25
|
+
retry: RoomCommand[];
|
|
26
|
+
};
|
|
27
|
+
export type HostOptions = {
|
|
28
|
+
readonly store: RoomStore;
|
|
29
|
+
readonly registry: EngineRegistry;
|
|
30
|
+
readonly clock: Clock;
|
|
31
|
+
readonly pool: JobPool;
|
|
32
|
+
readonly log: Logger;
|
|
33
|
+
readonly maxRooms: number;
|
|
34
|
+
readonly outgoingBufferBytes: number;
|
|
35
|
+
readonly persistRetryMs: number;
|
|
36
|
+
/** How long a heavy AI may choose (ROOMS_AI_TIMEOUT_MS). */
|
|
37
|
+
readonly aiTimeoutMs: number;
|
|
38
|
+
readonly draining: () => boolean;
|
|
39
|
+
};
|
|
40
|
+
export type LoadResult = {
|
|
41
|
+
readonly live: Live;
|
|
42
|
+
} | {
|
|
43
|
+
readonly problem: 'missing' | 'busy' | 'engine-missing';
|
|
44
|
+
};
|
|
45
|
+
export declare class RoomHost {
|
|
46
|
+
readonly rooms: Map<string, Live>;
|
|
47
|
+
private readonly o;
|
|
48
|
+
constructor(options: HostOptions);
|
|
49
|
+
private log;
|
|
50
|
+
/** The live room, loading it from storage (and resuming it) on first use. */
|
|
51
|
+
load(code: RoomCode): LoadResult;
|
|
52
|
+
/** Steps one command and carries out its effects: persist first, then everything else. */
|
|
53
|
+
dispatch(code: RoomCode, command: RoomCommand): void;
|
|
54
|
+
/** The old state stays; tell whoever asked, and keep what must not be lost for the retry. */
|
|
55
|
+
private storageUnavailable;
|
|
56
|
+
private armRetry;
|
|
57
|
+
private sendTo;
|
|
58
|
+
private perform;
|
|
59
|
+
private startJob;
|
|
60
|
+
/** Heavy AI on the pool, through the game's `jobs/ai.worker.ts`; a failure answers null and says why. */
|
|
61
|
+
private startAi;
|
|
62
|
+
/** Forgets a room: no further callback may write it (the core marks it closed). */
|
|
63
|
+
unload(code: RoomCode, reason: 'idle' | 'conflict' | 'drain' | 'expired'): void;
|
|
64
|
+
/** Unloads idle rooms and closes expired ones. */
|
|
65
|
+
sweep(idleMs: number): void;
|
|
66
|
+
}
|
|
67
|
+
export {};
|