import type { ChatSync } from "../multiplayer/chatContract.js"; import type { PlayerPose } from "../multiplayer/poseSyncGate.js"; import type { PresenceTransport } from "../multiplayer/presenceContract.js"; import type { WorldDiff } from "./worldReplication.js"; import type { WorldSnapshot } from "./worldSnapshot.js"; /** A revision-stamped world baseline or compact diff sent to a server-authoritative client. */ export type WorldSyncFrame = { kind: "baseline"; revision: number; snapshot: WorldSnapshot; } | { kind: "diff"; revision: number; diff: WorldDiff; }; /** A short-lived credential allowing a disconnected client to reclaim one server membership. */ export type ResumeTicket = { userId: string; serverId: string; token: string; }; export type JoinServerResult = { serverId: string; isNew: boolean; resumeTicket?: ResumeTicket; }; /** Join failures are returned to the caller so the triggering surface can offer retry. */ export type JoinServerOutcome = (JoinServerResult & { ok: true; }) | { ok: false; reason: "full" | "closed" | "unauthorized"; }; /** Connection role used for authoritative world access; spectators are read-only. */ export type MultiplayerRole = "player" | "spectator"; export type RunCommandArgs = { serverId: string; command: string; input: unknown; }; export type TransportRunCommandResult = { ok: true; } /** `message` is the command's player-facing refusal text, when its `validate` gave one. */ | { ok: false; reason: string; message?: string; }; export type GameRuntimeServerView = { serverId: string; gameId: string; revision: number; memberUserIds: string[]; serverState: unknown | WorldSyncFrame; updatedAt: number; }; export type GameRuntimePlayerView = { userId: string; gameId: string; playerState: unknown; updatedAt: number; }; export type GameRuntimeFeedView = { action: string; entries: unknown[]; }; export type GameRuntimeTransport = { /** * Join a server. `sessionId` names this client session (one tab, one mounted view) so the host keeps * the user a member while any of their sessions is still live. */ joinServer: (args: { gameId: string; serverId?: string; role?: MultiplayerRole; sessionId?: string; }) => Promise; /** Leave a server. With `sessionId` only that session ends; the user leaves once no session remains. Without it the user leaves outright. */ leaveServer: (args: { serverId: string; sessionId?: string; }) => Promise; runCommand: (args: RunCommandArgs) => Promise; }; /** Fresh id for one client session's join/leave pair (see {@link GameRuntimeTransport.joinServer}). */ export declare function createTransportSessionId(): string; export type FeedUnsubscribe = () => void; export type GameRuntimeFeeds = { subscribeServer: (serverId: string, onChange: (view: GameRuntimeServerView | null) => void) => FeedUnsubscribe; subscribePlayer: (args: { serverId: string; }, onChange: (view: GameRuntimePlayerView | null) => void) => FeedUnsubscribe; subscribeFeed: (args: { serverId: string; action: string; }, onChange: (view: GameRuntimeFeedView) => void) => FeedUnsubscribe; requestServerBaseline?: (serverId: string) => void; }; export type GameBackend = { transport: GameRuntimeTransport; feeds?: GameRuntimeFeeds; presence?: PresenceTransport; }; export type PresencePoseRow = { userId: string; /** Set when the host tracks presence per session, so one user can hold two rows. */ sessionId?: string; /** Actor class, e.g. `"player"` / `"agent"` — a host may clamp each differently. */ kind?: string; /** Display name carried on the row, so a nameplate needs no second lookup. */ label?: string; position: { x: number; y: number; z: number; }; rotationY: number; rotationPitch: number; lastSeenAt: number; }; export type PresenceSync = { subscribe: (serverId: string, onChange: (rows: PresencePoseRow[]) => void) => FeedUnsubscribe; syncPose: (serverId: string, pose: PlayerPose) => void; }; export type LiveGameBackend = GameBackend & { presenceSync: PresenceSync; pushFeedEntry: (args: { serverId: string; action: string; entry: unknown; }) => Promise; chatSyncFor?: (serverId: string) => ChatSync; }; export type MultiplayerSession = { gameId: string; userId: string; backend: LiveGameBackend; feedActions: string[]; };