feat: refactor database client types and update related logic across game engine modules
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import type { Prisma } from '@prisma/client';
|
||||
|
||||
import { createPostgresConnector } from '@sammo-ts/infra';
|
||||
import {
|
||||
createPostgresConnector,
|
||||
type InputJsonValue,
|
||||
type TurnEngineDatabaseClient,
|
||||
type TurnEngineEventCreateManyInput,
|
||||
} from '@sammo-ts/infra';
|
||||
import {
|
||||
buildScenarioBootstrap,
|
||||
type ScenarioBootstrapWarning,
|
||||
@@ -37,8 +40,7 @@ export interface ScenarioSeedResult {
|
||||
warnings: ScenarioBootstrapWarning[];
|
||||
}
|
||||
|
||||
const asJson = (value: unknown): Prisma.InputJsonValue =>
|
||||
value as Prisma.InputJsonValue;
|
||||
const asJson = (value: unknown): InputJsonValue => value as InputJsonValue;
|
||||
|
||||
const resolveGeneralAge = (
|
||||
startYear: number | null,
|
||||
@@ -53,8 +55,8 @@ const resolveGeneralAge = (
|
||||
const buildEventRows = (
|
||||
rows: unknown[],
|
||||
targetOverride?: string
|
||||
): Prisma.EventCreateManyInput[] => {
|
||||
const result: Prisma.EventCreateManyInput[] = [];
|
||||
): TurnEngineEventCreateManyInput[] => {
|
||||
const result: TurnEngineEventCreateManyInput[] = [];
|
||||
|
||||
for (const row of rows) {
|
||||
if (!Array.isArray(row)) {
|
||||
@@ -123,7 +125,7 @@ export const seedScenarioToDatabase = async (
|
||||
|
||||
await connector.connect();
|
||||
try {
|
||||
const prisma = connector.prisma;
|
||||
const prisma = connector.prisma as TurnEngineDatabaseClient;
|
||||
|
||||
if (options.resetTables ?? true) {
|
||||
await prisma.event.deleteMany();
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
import type { Prisma } from '@prisma/client';
|
||||
|
||||
import { createPostgresConnector } from '@sammo-ts/infra';
|
||||
import {
|
||||
createPostgresConnector,
|
||||
type InputJsonValue,
|
||||
type TurnEngineCityUpdateInput,
|
||||
type TurnEngineDatabaseClient,
|
||||
type TurnEngineGeneralCreateManyInput,
|
||||
type TurnEngineGeneralUpdateInput,
|
||||
type TurnEngineLogEntryCreateManyInput,
|
||||
type TurnEngineNationUpdateInput,
|
||||
type TurnEngineTroopCreateManyInput,
|
||||
type TurnEngineTroopUpdateInput,
|
||||
type TurnEngineWorldStateUpdateInput,
|
||||
} from '@sammo-ts/infra';
|
||||
import { finalizeLogEntry, type LogEntryDraft } from '@sammo-ts/logic';
|
||||
|
||||
import type { TurnDaemonHooks } from '../lifecycle/types.js';
|
||||
@@ -12,8 +22,7 @@ export interface DatabaseTurnHooks {
|
||||
close(): Promise<void>;
|
||||
}
|
||||
|
||||
const asJson = (value: unknown): Prisma.InputJsonValue =>
|
||||
value as Prisma.InputJsonValue;
|
||||
const asJson = (value: unknown): InputJsonValue => value as InputJsonValue;
|
||||
|
||||
const toCode = (value: string | null | undefined): string =>
|
||||
value && value !== 'None' ? value : 'None';
|
||||
@@ -28,7 +37,7 @@ const readMetaNumber = (
|
||||
|
||||
const buildGeneralUpdate = (
|
||||
general: ReturnType<InMemoryTurnWorld['consumeDirtyState']>['generals'][number]
|
||||
): Prisma.GeneralUpdateInput => ({
|
||||
): TurnEngineGeneralUpdateInput => ({
|
||||
name: general.name,
|
||||
nationId: general.nationId,
|
||||
cityId: general.cityId,
|
||||
@@ -62,7 +71,7 @@ const buildGeneralUpdate = (
|
||||
|
||||
const buildGeneralCreate = (
|
||||
general: ReturnType<InMemoryTurnWorld['consumeDirtyState']>['generals'][number]
|
||||
): Prisma.GeneralCreateManyInput => ({
|
||||
): TurnEngineGeneralCreateManyInput => ({
|
||||
id: general.id,
|
||||
name: general.name,
|
||||
nationId: general.nationId,
|
||||
@@ -97,13 +106,13 @@ const buildGeneralCreate = (
|
||||
|
||||
const buildCityUpdate = (
|
||||
city: ReturnType<InMemoryTurnWorld['consumeDirtyState']>['cities'][number]
|
||||
): Prisma.CityUpdateInput => {
|
||||
): TurnEngineCityUpdateInput => {
|
||||
const meta = city.meta as Record<string, unknown>;
|
||||
const trust = readMetaNumber(meta, 'trust');
|
||||
const trade = readMetaNumber(meta, 'trade');
|
||||
const region = readMetaNumber(meta, 'region');
|
||||
|
||||
const data: Prisma.CityUpdateInput = {
|
||||
const data: TurnEngineCityUpdateInput = {
|
||||
name: city.name,
|
||||
nationId: city.nationId,
|
||||
level: city.level,
|
||||
@@ -139,7 +148,7 @@ const buildCityUpdate = (
|
||||
|
||||
const buildNationUpdate = (
|
||||
nation: ReturnType<InMemoryTurnWorld['consumeDirtyState']>['nations'][number]
|
||||
): Prisma.NationUpdateInput => ({
|
||||
): TurnEngineNationUpdateInput => ({
|
||||
name: nation.name,
|
||||
color: nation.color,
|
||||
capitalCityId: nation.capitalCityId,
|
||||
@@ -152,14 +161,14 @@ const buildNationUpdate = (
|
||||
|
||||
const buildTroopUpdate = (
|
||||
troop: ReturnType<InMemoryTurnWorld['consumeDirtyState']>['troops'][number]
|
||||
): Prisma.TroopUpdateInput => ({
|
||||
): TurnEngineTroopUpdateInput => ({
|
||||
nationId: troop.nationId,
|
||||
name: troop.name,
|
||||
});
|
||||
|
||||
const buildTroopCreate = (
|
||||
troop: ReturnType<InMemoryTurnWorld['consumeDirtyState']>['troops'][number]
|
||||
): Prisma.TroopCreateManyInput => ({
|
||||
): TurnEngineTroopCreateManyInput => ({
|
||||
troopLeaderId: troop.id,
|
||||
nationId: troop.nationId,
|
||||
name: troop.name,
|
||||
@@ -168,7 +177,7 @@ const buildTroopCreate = (
|
||||
const buildLogCreateData = (
|
||||
entry: LogEntryDraft,
|
||||
context: { year: number; month: number; at: Date }
|
||||
): Prisma.LogEntryCreateManyInput | null => {
|
||||
): TurnEngineLogEntryCreateManyInput | null => {
|
||||
const record = finalizeLogEntry(entry, {
|
||||
year: context.year,
|
||||
month: context.month,
|
||||
@@ -201,6 +210,7 @@ export const createDatabaseTurnHooks = async (
|
||||
// 턴 처리 결과를 DB에 반영하는 훅을 만든다.
|
||||
const connector = createPostgresConnector({ url: databaseUrl });
|
||||
await connector.connect();
|
||||
const prisma = connector.prisma as TurnEngineDatabaseClient;
|
||||
|
||||
const hooks: TurnDaemonHooks = {
|
||||
flushChanges: async () => {
|
||||
@@ -215,14 +225,15 @@ export const createDatabaseTurnHooks = async (
|
||||
createdTroops,
|
||||
} = world.consumeDirtyState();
|
||||
|
||||
await connector.prisma.worldState.update({
|
||||
const worldStateUpdate: TurnEngineWorldStateUpdateInput = {
|
||||
currentYear: state.currentYear,
|
||||
currentMonth: state.currentMonth,
|
||||
tickSeconds: state.tickSeconds,
|
||||
meta: asJson(state.meta),
|
||||
};
|
||||
await prisma.worldState.update({
|
||||
where: { id: state.id },
|
||||
data: {
|
||||
currentYear: state.currentYear,
|
||||
currentMonth: state.currentMonth,
|
||||
tickSeconds: state.tickSeconds,
|
||||
meta: asJson(state.meta),
|
||||
},
|
||||
data: worldStateUpdate,
|
||||
});
|
||||
|
||||
const createdIds = new Set(
|
||||
@@ -233,12 +244,12 @@ export const createDatabaseTurnHooks = async (
|
||||
);
|
||||
|
||||
if (createdGenerals.length > 0) {
|
||||
await connector.prisma.general.createMany({
|
||||
await prisma.general.createMany({
|
||||
data: createdGenerals.map(buildGeneralCreate),
|
||||
});
|
||||
}
|
||||
if (createdTroops.length > 0) {
|
||||
await connector.prisma.troop.createMany({
|
||||
await prisma.troop.createMany({
|
||||
data: createdTroops.map(buildTroopCreate),
|
||||
});
|
||||
}
|
||||
@@ -247,19 +258,19 @@ export const createDatabaseTurnHooks = async (
|
||||
...generals
|
||||
.filter((general) => !createdIds.has(general.id))
|
||||
.map((general) =>
|
||||
connector.prisma.general.update({
|
||||
prisma.general.update({
|
||||
where: { id: general.id },
|
||||
data: buildGeneralUpdate(general),
|
||||
})
|
||||
),
|
||||
...cities.map((city) =>
|
||||
connector.prisma.city.update({
|
||||
prisma.city.update({
|
||||
where: { id: city.id },
|
||||
data: buildCityUpdate(city),
|
||||
})
|
||||
),
|
||||
...nations.map((nation) =>
|
||||
connector.prisma.nation.update({
|
||||
prisma.nation.update({
|
||||
where: { id: nation.id },
|
||||
data: buildNationUpdate(nation),
|
||||
})
|
||||
@@ -267,7 +278,7 @@ export const createDatabaseTurnHooks = async (
|
||||
...troops
|
||||
.filter((troop) => !createdTroopIds.has(troop.id))
|
||||
.map((troop) =>
|
||||
connector.prisma.troop.update({
|
||||
prisma.troop.update({
|
||||
where: { troopLeaderId: troop.id },
|
||||
data: buildTroopUpdate(troop),
|
||||
})
|
||||
@@ -283,11 +294,13 @@ export const createDatabaseTurnHooks = async (
|
||||
const payload = logs
|
||||
.map((entry) => buildLogCreateData(entry, logContext))
|
||||
.filter(
|
||||
(entry): entry is Prisma.LogEntryCreateManyInput =>
|
||||
(
|
||||
entry
|
||||
): entry is TurnEngineLogEntryCreateManyInput =>
|
||||
Boolean(entry)
|
||||
);
|
||||
if (payload.length > 0) {
|
||||
await connector.prisma.logEntry.createMany({
|
||||
await prisma.logEntry.createMany({
|
||||
data: payload,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { Prisma } from '@prisma/client';
|
||||
|
||||
import { createPostgresConnector } from '@sammo-ts/infra';
|
||||
import {
|
||||
createPostgresConnector,
|
||||
type InputJsonValue,
|
||||
type TurnEngineDatabaseClient,
|
||||
} from '@sammo-ts/infra';
|
||||
|
||||
export interface ReservedTurnEntry {
|
||||
action: string;
|
||||
@@ -22,8 +24,7 @@ const DEFAULT_TURN_ACTION = '휴식';
|
||||
const DEFAULT_GENERAL_TURNS = 30;
|
||||
const DEFAULT_NATION_TURNS = 12;
|
||||
|
||||
const asJson = (value: unknown): Prisma.InputJsonValue =>
|
||||
value as Prisma.InputJsonValue;
|
||||
const asJson = (value: unknown): InputJsonValue => value as InputJsonValue;
|
||||
|
||||
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
||||
value !== null && typeof value === 'object' && !Array.isArray(value);
|
||||
@@ -81,50 +82,10 @@ const buildTurnListFromRows = (
|
||||
const buildNationKey = (nationId: number, officerLevel: number): string =>
|
||||
`${nationId}:${officerLevel}`;
|
||||
|
||||
interface PrismaReservedTurnClient {
|
||||
generalTurn: {
|
||||
findMany(args?: unknown): Promise<
|
||||
Array<{
|
||||
generalId: number;
|
||||
turnIdx: number;
|
||||
actionCode: string;
|
||||
arg: unknown;
|
||||
}>
|
||||
>;
|
||||
deleteMany(args: { where: { generalId: number } }): Promise<unknown>;
|
||||
createMany(args: {
|
||||
data: Array<{
|
||||
generalId: number;
|
||||
turnIdx: number;
|
||||
actionCode: string;
|
||||
arg: Prisma.InputJsonValue;
|
||||
}>;
|
||||
}): Promise<unknown>;
|
||||
};
|
||||
nationTurn: {
|
||||
findMany(args?: unknown): Promise<
|
||||
Array<{
|
||||
nationId: number;
|
||||
officerLevel: number;
|
||||
turnIdx: number;
|
||||
actionCode: string;
|
||||
arg: unknown;
|
||||
}>
|
||||
>;
|
||||
deleteMany(args: {
|
||||
where: { nationId: number; officerLevel: number };
|
||||
}): Promise<unknown>;
|
||||
createMany(args: {
|
||||
data: Array<{
|
||||
nationId: number;
|
||||
officerLevel: number;
|
||||
turnIdx: number;
|
||||
actionCode: string;
|
||||
arg: Prisma.InputJsonValue;
|
||||
}>;
|
||||
}): Promise<unknown>;
|
||||
};
|
||||
}
|
||||
type ReservedTurnDatabaseClient = Pick<
|
||||
TurnEngineDatabaseClient,
|
||||
'generalTurn' | 'nationTurn'
|
||||
>;
|
||||
|
||||
export class InMemoryReservedTurnStore {
|
||||
private readonly generalTurns = new Map<number, ReservedTurnEntry[]>();
|
||||
@@ -135,7 +96,7 @@ export class InMemoryReservedTurnStore {
|
||||
private readonly maxNationTurns: number;
|
||||
|
||||
constructor(
|
||||
private readonly prisma: PrismaClientOrAdapter,
|
||||
private readonly prisma: ReservedTurnDatabaseClient,
|
||||
options: { maxGeneralTurns: number; maxNationTurns: number }
|
||||
) {
|
||||
this.maxGeneralTurns = options.maxGeneralTurns;
|
||||
@@ -305,18 +266,13 @@ export class InMemoryReservedTurnStore {
|
||||
}
|
||||
}
|
||||
|
||||
type PrismaClientOrAdapter = ReturnType<
|
||||
typeof createPostgresConnector
|
||||
>['prisma'] &
|
||||
PrismaReservedTurnClient;
|
||||
|
||||
export const createReservedTurnStore = async (
|
||||
options: ReservedTurnStoreOptions
|
||||
): Promise<ReservedTurnStoreHandle> => {
|
||||
const connector = createPostgresConnector({ url: options.databaseUrl });
|
||||
await connector.connect();
|
||||
const store = new InMemoryReservedTurnStore(
|
||||
connector.prisma as PrismaClientOrAdapter,
|
||||
connector.prisma as ReservedTurnDatabaseClient,
|
||||
{
|
||||
maxGeneralTurns: options.maxGeneralTurns ?? DEFAULT_GENERAL_TURNS,
|
||||
maxNationTurns: options.maxNationTurns ?? DEFAULT_NATION_TURNS,
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type {
|
||||
City as PrismaCity,
|
||||
Diplomacy as PrismaDiplomacy,
|
||||
General as PrismaGeneral,
|
||||
Nation as PrismaNation,
|
||||
Prisma,
|
||||
Troop as PrismaTroop,
|
||||
} from '@prisma/client';
|
||||
|
||||
import { createPostgresConnector } from '@sammo-ts/infra';
|
||||
import {
|
||||
createPostgresConnector,
|
||||
type JsonValue,
|
||||
type TurnEngineCityRow,
|
||||
type TurnEngineDatabaseClient,
|
||||
type TurnEngineDiplomacyRow,
|
||||
type TurnEngineGeneralRow,
|
||||
type TurnEngineNationRow,
|
||||
type TurnEngineTroopRow,
|
||||
} from '@sammo-ts/infra';
|
||||
import type {
|
||||
City,
|
||||
Nation,
|
||||
@@ -126,7 +126,7 @@ const alignToPreviousTick = (base: Date, tickMinutes: number): Date => {
|
||||
return new Date(nextTick.getTime() - tickMinutes * 60_000);
|
||||
};
|
||||
|
||||
const mapScenarioConfig = (raw: Prisma.JsonValue): ScenarioConfig => {
|
||||
const mapScenarioConfig = (raw: JsonValue): ScenarioConfig => {
|
||||
const parsed = zScenarioConfig.safeParse(raw);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`world_state.config is invalid: ${parsed.error.message}`);
|
||||
@@ -134,7 +134,7 @@ const mapScenarioConfig = (raw: Prisma.JsonValue): ScenarioConfig => {
|
||||
return parsed.data;
|
||||
};
|
||||
|
||||
const mapGeneralRow = (row: PrismaGeneral): TurnGeneral => ({
|
||||
const mapGeneralRow = (row: TurnEngineGeneralRow): TurnGeneral => ({
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
nationId: row.nationId,
|
||||
@@ -179,7 +179,7 @@ const mapGeneralRow = (row: PrismaGeneral): TurnGeneral => ({
|
||||
recentWarTime: row.recentWarTime ?? null,
|
||||
});
|
||||
|
||||
const mapCityRow = (row: PrismaCity): City => ({
|
||||
const mapCityRow = (row: TurnEngineCityRow): City => ({
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
nationId: row.nationId,
|
||||
@@ -206,7 +206,7 @@ const mapCityRow = (row: PrismaCity): City => ({
|
||||
},
|
||||
});
|
||||
|
||||
const mapNationRow = (row: PrismaNation): Nation => ({
|
||||
const mapNationRow = (row: TurnEngineNationRow): Nation => ({
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
color: row.color,
|
||||
@@ -223,14 +223,14 @@ const mapNationRow = (row: PrismaNation): Nation => ({
|
||||
},
|
||||
});
|
||||
|
||||
const mapDiplomacyRow = (row: PrismaDiplomacy): ScenarioDiplomacy => ({
|
||||
const mapDiplomacyRow = (row: TurnEngineDiplomacyRow): ScenarioDiplomacy => ({
|
||||
fromNationId: row.srcNationId,
|
||||
toNationId: row.destNationId,
|
||||
state: row.stateCode,
|
||||
durationMonths: row.term,
|
||||
});
|
||||
|
||||
const mapTroopRow = (row: PrismaTroop): Troop => ({
|
||||
const mapTroopRow = (row: TurnEngineTroopRow): Troop => ({
|
||||
id: row.troopLeaderId,
|
||||
nationId: row.nationId,
|
||||
name: row.name,
|
||||
@@ -242,7 +242,7 @@ export const loadTurnWorldFromDatabase = async (
|
||||
const connector = createPostgresConnector({ url: options.databaseUrl });
|
||||
await connector.connect();
|
||||
try {
|
||||
const prisma = connector.prisma;
|
||||
const prisma = connector.prisma as TurnEngineDatabaseClient;
|
||||
const worldState = await prisma.worldState.findFirst();
|
||||
if (!worldState) {
|
||||
throw new Error('world_state row is required to start turn daemon.');
|
||||
|
||||
Reference in New Issue
Block a user