import type { CommandResult } from "../commands/commandRegistry.js"; import type { GameDefinition, LoopPlayer } from "../game/defineGame.js"; import type { ModelAssetRef } from "../scene/assetCatalog.js"; import { type GameContext, type GameContextContent, type GameContextModels } from "./gameContext.js"; import { type InputFrame } from "./inputSnapshot.js"; import { type WorldDiff } from "./worldReplication.js"; import type { SnapshotViewer, WorldSnapshot } from "./worldSnapshot.js"; export type { InputFrame }; /** Reserved command name the authoritative host intercepts on the existing `runCommand` transport to route a client's {@link InputFrame} to `session.input`, so per-tick input needs no separate wire. */ export declare const INPUT_COMMAND = "engine.input"; /** Config for {@link createHostedGameRunner}: the game definition, its content lookup, and an optional host identity. */ export interface HostedGameRunnerOptions { definition: GameDefinition; content: GameContextContent; /** The world's own authoritative identity (`ctx.player` server-side); real players join as members. */ host?: LoopPlayer; now?: () => number; /** * Rehydrate a persisted world instead of booting a fresh one — for stateless hosts (Convex) that reconstruct * the runner each invocation. `onInit` still runs (so commands and systems it registers exist), then this * snapshot overlays the world state it seeded. Omit for a long-lived stateful host (ws) that keeps one runner. */ restore?: WorldSnapshot; /** Persisted replication cursor; restored hosts continue above it. */ revision?: number; /** Render-model lookup for collider auto-fit — pass the same lookup the shell derives from `entityModels`/`objectModels` so host and clients resolve identical hitboxes. */ models?: GameContextModels; /** Process-local reconnect protection. Departed admission marks expire after 5 minutes or the oldest is evicted above 256 users; zero disables retention. Live members and staged replay ticks are never evicted. */ inputRetention?: { maxDepartedUsers?: number; departedTtlMs?: number; /** Wall-clock milliseconds; injectable for deterministic hosts and tests. */ nowMs?: () => number; }; } /** * The GameContext-loop equivalent of the pure-reducer `createGameHost`: one authoritative `createGameContext` * per world, driven server-side. `onInit` runs once at construction; `onNewPlayer`/`onPlayerLeave` fire per * join/leave; `tick` advances `onTick` then commits a revision. Clients pull a full {@link WorldSnapshot} * baseline once, then per-tick {@link WorldDiff}s from their last revision. Games ship only normal GameContext * code — the runner adds no per-game surface. */ export interface HostedGameRunner { join(userId: string, isNew: boolean): void; /** Re-attach an already-joined member on a reconstructed runner — restores membership and `ctx.game.players` without re-firing `onNewPlayer`. Stateless hosts call it once per persisted member after `restore`. */ resume(userId: string): void; leave(userId: string): void; /** Record a joined/resumed client's latest input frame; nonmember input is ignored. Stashed for {@link heldInput} and sampled onto `ctx.game.players` in `onTick`. */ input(userId: string, frame: InputFrame): void; /** Counts actual retained input storage for host health checks; elapsed recorder history is excluded and staged future replay ticks remain. */ inputStats(): { members: number; recorders: number; recordedFrames: number; latestInputs: number; wireSequences: number; pressSequences: number; departedUsers: number; }; heldInput(userId: string): InputFrame | null; command(userId: string, name: string, input: unknown): CommandResult; tick(dt: number): number; diff(sinceRevision: number): WorldDiff; revision(): number; /** The full world baseline — projected to only what `viewer` may see when the context carries a replication policy, else the whole world. */ snapshot(viewer?: SnapshotViewer): WorldSnapshot; /** Complete detached authoritative state for persistence, including save-only modules. */ state(): WorldSnapshot; /** Commit replication without advancing simulation or running game ticks. */ commit(): number; /** True when {@link snapshot} is viewer-dependent (a replication policy projects private/AOI state); a host must then serve each viewer its own frame. */ projectsViewers(): boolean; members(): readonly string[]; context(): GameContext; } /** Build a {@link HostedGameRunner} — one authoritative GameContext world driven server-side from the game's own loop. * @internal */ export declare function createHostedGameRunner(options: HostedGameRunnerOptions): HostedGameRunner;