import { NetworkedPredictedEngine } from "./networked-client.js"; import { NetworkedAuthoritativeEngine } from "./networked-server.js"; import type { NetworkedGameDefinition } from "./networked-types.js"; import { createBinaryProtocol, type BinaryProtocol } from "./protocol.js"; import type { ServerEngineOptions } from "./server.js"; export interface DefinedNetworkedGame< AuthorityState, ClientState, Input, AuthorityEvent = never, PerceptionEvent = never, > extends NetworkedGameDefinition< AuthorityState, ClientState, Input, AuthorityEvent, PerceptionEvent > { protocol: BinaryProtocol; createServer( options?: ServerEngineOptions, ): NetworkedAuthoritativeEngine< AuthorityState, ClientState, Input, AuthorityEvent, PerceptionEvent >; createClient(): NetworkedPredictedEngine< AuthorityState, ClientState, Input, AuthorityEvent, PerceptionEvent >; } export function defineNetworkedGame< AuthorityState, ClientState, Input, AuthorityEvent = never, PerceptionEvent = never, >( definition: NetworkedGameDefinition< AuthorityState, ClientState, Input, AuthorityEvent, PerceptionEvent >, ): DefinedNetworkedGame< AuthorityState, ClientState, Input, AuthorityEvent, PerceptionEvent > { validateRates(definition.tickRateHz, definition.snapshotRateHz); const protocol = createBinaryProtocol( definition.codecs, ); return Object.freeze({ ...definition, protocol, createServer(options?: ServerEngineOptions) { return new NetworkedAuthoritativeEngine(definition, options); }, createClient() { return new NetworkedPredictedEngine(definition); }, }); } function validateRates(tickRateHz: number, snapshotRateHz: number): void { if (!Number.isInteger(tickRateHz) || tickRateHz <= 0) { throw new RangeError("tickRateHz must be a positive integer"); } if ( !Number.isInteger(snapshotRateHz) || snapshotRateHz <= 0 || snapshotRateHz > tickRateHz || tickRateHz % snapshotRateHz !== 0 ) { throw new RangeError( "snapshotRateHz must be a positive divisor of tickRateHz", ); } }