Files
core2026/app/game-api/src/context.ts
T
Hide_D 5040691d7c feat: implement input event system with durable command handling
- Introduced InputEvent model with status tracking (PENDING, PROCESSING, SUCCEEDED, FAILED) and unique request IDs.
- Added DatabaseTurnDaemonTransport for sending commands and handling idempotency.
- Implemented executeInputEvent function to manage input event lifecycle and error handling.
- Created DatabaseTurnDaemonCommandQueue for managing command processing and lease recovery.
- Enhanced turn daemon lifecycle to support atomic command execution and error recovery.
- Added tests for input event atomicity, command queuing, and error handling scenarios.
2026-07-25 05:36:34 +00:00

110 lines
3.8 KiB
TypeScript

import { z } from 'zod';
import type { GameSessionTokenPayload } from '@sammo-ts/common/auth/gameToken';
import type { DatabaseClient as InfraDatabaseClient, RedisConnector, GamePrisma } from '@sammo-ts/infra';
import type { TurnDaemonTransport } from './daemon/transport.js';
import type { BattleSimTransport } from './battleSim/transport.js';
import type { FlushStore } from './auth/flushStore.js';
import type { RedisAccessTokenStore } from './auth/accessTokenStore.js';
export interface GameProfile {
id: string;
scenario: string;
name: string;
}
export const zWorldStateConfig = z.object({
maxUserCnt: z.number().optional(),
fictionMode: z.string().optional(),
fiction: z.number().optional(),
joinMode: z.string().optional(),
blockGeneralCreate: z.number().optional(),
npcMode: z.number().optional(),
showImgLevel: z.number().optional(),
tournamentTrig: z.boolean().optional(),
extendedGeneral: z.boolean().optional(),
turnTermMinutes: z.number().optional(),
syncTurnTime: z.boolean().optional(),
});
export type WorldStateConfig = z.infer<typeof zWorldStateConfig>;
export const zWorldStateMeta = z.object({
starttime: z.string().optional(),
opentime: z.string().optional(),
preopenAt: z.string().optional(),
turntime: z.string().optional(),
otherTextInfo: z.string().optional(),
isUnited: z.number().optional(),
autorun_user: z
.object({
limit_minutes: z.number().optional(),
options: z.record(z.string(), z.boolean()).optional(),
})
.nullable()
.optional(),
});
export type WorldStateMeta = z.infer<typeof zWorldStateMeta>;
export type WorldStateRow = GamePrisma.WorldStateGetPayload<Record<string, never>>;
export type GeneralRow = GamePrisma.GeneralGetPayload<Record<string, never>>;
export type GeneralTurnRow = GamePrisma.GeneralTurnGetPayload<Record<string, never>>;
export type NationTurnRow = GamePrisma.NationTurnGetPayload<Record<string, never>>;
export type CityRow = GamePrisma.CityGetPayload<Record<string, never>>;
export type NationRow = GamePrisma.NationGetPayload<Record<string, never>>;
export type TroopRow = GamePrisma.TroopGetPayload<Record<string, never>>;
export type JsonValue = GamePrisma.JsonValue;
export type JsonObject = GamePrisma.JsonObject;
export type JsonArray = GamePrisma.JsonArray;
export type InputJsonValue = GamePrisma.InputJsonValue;
export type DatabaseClient = InfraDatabaseClient;
export interface GameApiContext {
requestId?: string;
db: DatabaseClient;
redis: RedisConnector['client'];
turnDaemon: TurnDaemonTransport;
battleSim: BattleSimTransport;
profile: GameProfile;
uploadDir: string;
uploadPath: string;
uploadPublicUrl: string | null;
auth: GameSessionTokenPayload | null;
accessTokenStore: RedisAccessTokenStore;
flushStore: FlushStore;
gameTokenSecret: string;
}
export const createGameApiContext = (options: {
requestId?: string;
db: DatabaseClient;
redis: RedisConnector['client'];
turnDaemon: TurnDaemonTransport;
battleSim: BattleSimTransport;
profile: GameProfile;
uploadDir: string;
uploadPath: string;
uploadPublicUrl: string | null;
auth: GameSessionTokenPayload | null;
accessTokenStore: RedisAccessTokenStore;
flushStore: FlushStore;
gameTokenSecret: string;
}): GameApiContext => {
return {
requestId: options.requestId,
db: options.db,
redis: options.redis,
turnDaemon: options.turnDaemon,
battleSim: options.battleSim,
profile: options.profile,
uploadDir: options.uploadDir,
uploadPath: options.uploadPath,
uploadPublicUrl: options.uploadPublicUrl,
auth: options.auth,
accessTokenStore: options.accessTokenStore,
flushStore: options.flushStore,
gameTokenSecret: options.gameTokenSecret,
};
};