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",
);
}
}