feat: add GeneralTurn and NationTurn models with CRUD operations

- Introduced GeneralTurn and NationTurn models in Prisma schema.
- Implemented reserved turns management in the game API, including loading, setting, and shifting turns for generals and nations.
- Created unit tests for reserved turns functionality to ensure correctness.
- Developed reserved turn handler to process actions based on reserved turns.
- Established in-memory storage for reserved turns with persistence to the database.
This commit is contained in:
2025-12-30 04:45:07 +00:00
parent e4c57e2467
commit 6fe6d54432
13 changed files with 1949 additions and 15 deletions
+45 -4
View File
@@ -20,6 +20,8 @@ import type {
import { InMemoryTurnWorld } from './inMemoryWorld.js';
import { InMemoryTurnProcessor } from './inMemoryTurnProcessor.js';
import { InMemoryTurnStateStore } from './inMemoryStateStore.js';
import { createReservedTurnHandler } from './reservedTurnHandler.js';
import { createReservedTurnStore } from './reservedTurnStore.js';
import { loadTurnWorldFromDatabase } from './worldLoader.js';
export interface TurnDaemonRuntimeOptions {
@@ -71,24 +73,63 @@ export const createTurnDaemonRuntime = async (
? { ...state, tickSeconds: tickMinutes * 60 }
: state;
const schedule = options.schedule ?? buildFixedSchedule(tickMinutes);
const reservedTurnStoreHandle = options.generalTurnHandler
? null
: await createReservedTurnStore({
databaseUrl: options.databaseUrl,
});
let worldRef: InMemoryTurnWorld | null = null;
const worldOptions: InMemoryTurnWorldOptions = {
schedule,
generalTurnHandler: options.generalTurnHandler,
generalTurnHandler:
options.generalTurnHandler ??
createReservedTurnHandler({
reservedTurns: reservedTurnStoreHandle!.store,
scenarioConfig: snapshot.scenarioConfig,
scenarioMeta: snapshot.scenarioMeta,
diplomacy: snapshot.diplomacy,
getWorld: () => worldRef,
}),
calendarHandler: options.calendarHandler,
};
const world = new InMemoryTurnWorld(resolvedState, snapshot, worldOptions);
worldRef = world;
const stateStore = new InMemoryTurnStateStore(world);
const processor = new InMemoryTurnProcessor(world, { tickMinutes });
const processor = new InMemoryTurnProcessor(world, {
tickMinutes,
beforeExecuteGeneral: reservedTurnStoreHandle
? async (general) => {
await reservedTurnStoreHandle.store.refreshGeneralTurns(
general.id
);
if (general.nationId > 0 && general.officerLevel >= 5) {
await reservedTurnStoreHandle.store.refreshNationTurns(
general.nationId,
general.officerLevel
);
}
}
: undefined,
});
const controlQueue = options.controlQueue ?? new InMemoryControlQueue();
const clock = options.clock ?? new SystemClock();
let hooks: TurnDaemonHooks | undefined;
let close = async () => {};
if (options.enableDatabaseFlush ?? true) {
const dbHooks = await createDatabaseTurnHooks(options.databaseUrl, world);
const dbHooks = await createDatabaseTurnHooks(options.databaseUrl, world, {
reservedTurns: reservedTurnStoreHandle?.store,
});
hooks = dbHooks.hooks;
close = dbHooks.close;
close = async () => {
await dbHooks.close();
if (reservedTurnStoreHandle) {
await reservedTurnStoreHandle.close();
}
};
} else if (reservedTurnStoreHandle) {
close = async () => reservedTurnStoreHandle.close();
}
const defaultBudget: TurnRunBudget = options.defaultBudget ?? {