feat: 시즌 번호 필드 추가 및 메타 데이터 처리 로직 개선
This commit is contained in:
@@ -34,6 +34,7 @@ export interface ScenarioInstallOptions {
|
|||||||
joinMode?: 'full' | 'onlyRandom';
|
joinMode?: 'full' | 'onlyRandom';
|
||||||
autorunUser?: ScenarioAutorunOptions | null;
|
autorunUser?: ScenarioAutorunOptions | null;
|
||||||
preopenAt?: Date | null;
|
preopenAt?: Date | null;
|
||||||
|
season?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ScenarioSeedOptions {
|
export interface ScenarioSeedOptions {
|
||||||
@@ -254,6 +255,10 @@ export const seedScenarioToDatabase = async (options: ScenarioSeedOptions): Prom
|
|||||||
lastTurnTime: formatDateTime(now),
|
lastTurnTime: formatDateTime(now),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (typeof install?.season === 'number' && Number.isFinite(install.season)) {
|
||||||
|
worldMeta.season = Math.floor(install.season);
|
||||||
|
}
|
||||||
|
|
||||||
const integrationSeed = process.env[INTEGRATION_WORLD_SEED_ENV];
|
const integrationSeed = process.env[INTEGRATION_WORLD_SEED_ENV];
|
||||||
if (typeof integrationSeed === 'string' && integrationSeed.trim().length > 0) {
|
if (typeof integrationSeed === 'string' && integrationSeed.trim().length > 0) {
|
||||||
worldMeta.hiddenSeed = integrationSeed.trim();
|
worldMeta.hiddenSeed = integrationSeed.trim();
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import path from 'node:path';
|
|||||||
import { TRPCError } from '@trpc/server';
|
import { TRPCError } from '@trpc/server';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { resolvePostgresConfigFromEnv } from '@sammo-ts/infra';
|
import { createGamePostgresConnector, resolvePostgresConfigFromEnv } from '@sammo-ts/infra';
|
||||||
|
|
||||||
import { procedure, router } from './trpc.js';
|
import { procedure, router } from './trpc.js';
|
||||||
import { listScenarioPreviews, resolveGitCommitSha } from './scenario/scenarioCatalog.js';
|
import { listScenarioPreviews, resolveGitCommitSha } from './scenario/scenarioCatalog.js';
|
||||||
@@ -329,6 +329,20 @@ const readMetaObject = (value: unknown): Record<string, unknown> => {
|
|||||||
return value as Record<string, unknown>;
|
return value as Record<string, unknown>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const readMetaNumber = (meta: Record<string, unknown>, key: string): number | null => {
|
||||||
|
const raw = meta[key];
|
||||||
|
if (typeof raw === 'number' && Number.isFinite(raw)) {
|
||||||
|
return Math.floor(raw);
|
||||||
|
}
|
||||||
|
if (typeof raw === 'string') {
|
||||||
|
const parsed = Number(raw);
|
||||||
|
if (Number.isFinite(parsed)) {
|
||||||
|
return Math.floor(parsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
const applyMetaPatch = (
|
const applyMetaPatch = (
|
||||||
meta: Record<string, unknown>,
|
meta: Record<string, unknown>,
|
||||||
patch: Record<string, unknown | null | undefined>
|
patch: Record<string, unknown | null | undefined>
|
||||||
@@ -850,6 +864,22 @@ export const adminRouter = router({
|
|||||||
schema: updatedProfile.profile,
|
schema: updatedProfile.profile,
|
||||||
}).url;
|
}).url;
|
||||||
const resourcesRoot = path.resolve(process.cwd(), 'resources');
|
const resourcesRoot = path.resolve(process.cwd(), 'resources');
|
||||||
|
const profileMeta = readMetaObject(updatedProfile.meta);
|
||||||
|
const nextSeasonIdx = readMetaNumber(profileMeta, 'nextSeasonIdx');
|
||||||
|
let baseSeason: number | null = null;
|
||||||
|
const connector = createGamePostgresConnector({ url: databaseUrl });
|
||||||
|
try {
|
||||||
|
await connector.connect();
|
||||||
|
const row = await connector.prisma.worldState.findFirst({
|
||||||
|
select: { meta: true },
|
||||||
|
});
|
||||||
|
if (row) {
|
||||||
|
baseSeason = readMetaNumber(readMetaObject(row.meta), 'season');
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await connector.disconnect();
|
||||||
|
}
|
||||||
|
const season = nextSeasonIdx ?? baseSeason ?? 1;
|
||||||
|
|
||||||
await seedProfileDatabase({
|
await seedProfileDatabase({
|
||||||
databaseUrl,
|
databaseUrl,
|
||||||
@@ -866,6 +896,7 @@ export const adminRouter = router({
|
|||||||
showImgLevel: input.install.showImgLevel,
|
showImgLevel: input.install.showImgLevel,
|
||||||
tournamentTrig: input.install.tournamentTrig,
|
tournamentTrig: input.install.tournamentTrig,
|
||||||
joinMode: input.install.joinMode,
|
joinMode: input.install.joinMode,
|
||||||
|
season,
|
||||||
autorunUser: input.install.autorunUser
|
autorunUser: input.install.autorunUser
|
||||||
? {
|
? {
|
||||||
limitMinutes: input.install.autorunUser.limitMinutes,
|
limitMinutes: input.install.autorunUser.limitMinutes,
|
||||||
|
|||||||
@@ -113,6 +113,20 @@ interface GatewayAdminActionResult {
|
|||||||
|
|
||||||
const normalizeMeta = (value: unknown): Record<string, unknown> => (isRecord(value) ? value : {});
|
const normalizeMeta = (value: unknown): Record<string, unknown> => (isRecord(value) ? value : {});
|
||||||
|
|
||||||
|
const readMetaNumber = (meta: Record<string, unknown>, key: string): number | null => {
|
||||||
|
const raw = meta[key];
|
||||||
|
if (typeof raw === 'number' && Number.isFinite(raw)) {
|
||||||
|
return Math.floor(raw);
|
||||||
|
}
|
||||||
|
if (typeof raw === 'string') {
|
||||||
|
const parsed = Number(raw);
|
||||||
|
if (Number.isFinite(parsed)) {
|
||||||
|
return Math.floor(parsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
const normalizeStatus = (value: unknown): GatewayAdminActionStatus | null => {
|
const normalizeStatus = (value: unknown): GatewayAdminActionStatus | null => {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
return value as GatewayAdminActionStatus;
|
return value as GatewayAdminActionStatus;
|
||||||
@@ -615,6 +629,10 @@ export class GatewayOrchestrator implements GatewayOrchestratorHandle {
|
|||||||
if (!seedInfo.scenarioId) {
|
if (!seedInfo.scenarioId) {
|
||||||
return { status: 'FAILED', detail: 'scenarioId is missing' };
|
return { status: 'FAILED', detail: 'scenarioId is missing' };
|
||||||
}
|
}
|
||||||
|
const profileMeta = normalizeMeta(profile.meta);
|
||||||
|
const nextSeasonIdx = readMetaNumber(profileMeta, 'nextSeasonIdx');
|
||||||
|
const baseSeason = readMetaNumber(normalizeMeta(seedInfo.meta), 'season');
|
||||||
|
const season = nextSeasonIdx ?? baseSeason ?? 1;
|
||||||
const seedTime =
|
const seedTime =
|
||||||
openAt ??
|
openAt ??
|
||||||
(action.scheduledAt && action.action === 'RESET_SCHEDULED' ? new Date(action.scheduledAt) : this.now());
|
(action.scheduledAt && action.action === 'RESET_SCHEDULED' ? new Date(action.scheduledAt) : this.now());
|
||||||
@@ -650,7 +668,10 @@ export class GatewayOrchestrator implements GatewayOrchestratorHandle {
|
|||||||
scenarioId: seedInfo.scenarioId,
|
scenarioId: seedInfo.scenarioId,
|
||||||
tickSeconds: seedInfo.tickSeconds,
|
tickSeconds: seedInfo.tickSeconds,
|
||||||
now: seedTime,
|
now: seedTime,
|
||||||
installOptions: installOptions ?? undefined,
|
installOptions: {
|
||||||
|
...(installOptions ?? {}),
|
||||||
|
season,
|
||||||
|
},
|
||||||
scenarioOptions: { scenarioRoot: path.join(resourceRoot, 'scenario') },
|
scenarioOptions: { scenarioRoot: path.join(resourceRoot, 'scenario') },
|
||||||
mapOptions: { mapRoot: path.join(resourceRoot, 'map') },
|
mapOptions: { mapRoot: path.join(resourceRoot, 'map') },
|
||||||
unitSetOptions: { unitSetRoot: path.join(resourceRoot, 'unitset') },
|
unitSetOptions: { unitSetRoot: path.join(resourceRoot, 'unitset') },
|
||||||
@@ -685,18 +706,19 @@ export class GatewayOrchestrator implements GatewayOrchestratorHandle {
|
|||||||
private async resolveResetSeedInfo(
|
private async resolveResetSeedInfo(
|
||||||
profile: GatewayProfileRecord,
|
profile: GatewayProfileRecord,
|
||||||
overrides?: { scenarioId?: number | null; tickSeconds?: number }
|
overrides?: { scenarioId?: number | null; tickSeconds?: number }
|
||||||
): Promise<{ databaseUrl: string; scenarioId: number | null; tickSeconds?: number }> {
|
): Promise<{ databaseUrl: string; scenarioId: number | null; tickSeconds?: number; meta: Record<string, unknown> } > {
|
||||||
const databaseUrl = resolvePostgresConfigFromEnv({
|
const databaseUrl = resolvePostgresConfigFromEnv({
|
||||||
env: this.processConfig.baseEnv ?? process.env,
|
env: this.processConfig.baseEnv ?? process.env,
|
||||||
schema: profile.profile,
|
schema: profile.profile,
|
||||||
}).url;
|
}).url;
|
||||||
let scenarioId = overrides?.scenarioId ?? parseScenarioId(profile.scenario);
|
let scenarioId = overrides?.scenarioId ?? parseScenarioId(profile.scenario);
|
||||||
let tickSeconds: number | undefined = overrides?.tickSeconds;
|
let tickSeconds: number | undefined = overrides?.tickSeconds;
|
||||||
|
let meta: Record<string, unknown> = {};
|
||||||
const connector = createGamePostgresConnector({ url: databaseUrl });
|
const connector = createGamePostgresConnector({ url: databaseUrl });
|
||||||
await connector.connect();
|
await connector.connect();
|
||||||
try {
|
try {
|
||||||
const row = await connector.prisma.worldState.findFirst({
|
const row = await connector.prisma.worldState.findFirst({
|
||||||
select: { scenarioCode: true, tickSeconds: true },
|
select: { scenarioCode: true, tickSeconds: true, meta: true },
|
||||||
});
|
});
|
||||||
if (row) {
|
if (row) {
|
||||||
if (scenarioId === null) {
|
if (scenarioId === null) {
|
||||||
@@ -708,11 +730,12 @@ export class GatewayOrchestrator implements GatewayOrchestratorHandle {
|
|||||||
if (tickSeconds === undefined && typeof row.tickSeconds === 'number' && Number.isFinite(row.tickSeconds)) {
|
if (tickSeconds === undefined && typeof row.tickSeconds === 'number' && Number.isFinite(row.tickSeconds)) {
|
||||||
tickSeconds = row.tickSeconds;
|
tickSeconds = row.tickSeconds;
|
||||||
}
|
}
|
||||||
|
meta = normalizeMeta(row.meta);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
await connector.disconnect();
|
await connector.disconnect();
|
||||||
}
|
}
|
||||||
return { databaseUrl, scenarioId, tickSeconds };
|
return { databaseUrl, scenarioId, tickSeconds, meta };
|
||||||
}
|
}
|
||||||
|
|
||||||
private async runBuildCommands(
|
private async runBuildCommands(
|
||||||
|
|||||||
Reference in New Issue
Block a user