diff --git a/app/game-engine/src/scenario/scenarioSeeder.ts b/app/game-engine/src/scenario/scenarioSeeder.ts index fabe1c5d..67a34404 100644 --- a/app/game-engine/src/scenario/scenarioSeeder.ts +++ b/app/game-engine/src/scenario/scenarioSeeder.ts @@ -54,6 +54,7 @@ export interface ScenarioInstallOptions { season?: number; firstGameIdx?: number; serverId?: string; + serverName?: string; installOperationId?: string; installCommitSha?: string; } @@ -315,6 +316,9 @@ export const seedScenarioToDatabase = async (options: ScenarioSeedOptions): Prom if (typeof install?.serverId === 'string' && install.serverId.trim()) { worldMeta.serverId = install.serverId.trim(); } + if (typeof install?.serverName === 'string' && install.serverName.trim()) { + worldMeta.serverName = install.serverName.trim(); + } if (typeof install?.installOperationId === 'string' && install.installOperationId.trim()) { worldMeta.installOperationId = install.installOperationId.trim(); } diff --git a/app/game-engine/test/scenarioSeeder.test.ts b/app/game-engine/test/scenarioSeeder.test.ts index b81d9a3e..11a337d4 100644 --- a/app/game-engine/test/scenarioSeeder.test.ts +++ b/app/game-engine/test/scenarioSeeder.test.ts @@ -367,6 +367,7 @@ describeDb('scenario database seed', () => { }, preopenAt: new Date('2030-01-01T01:00:00Z'), openAt: new Date('2030-01-01T02:00:00Z'), + serverName: '훼', }, }); @@ -400,6 +401,7 @@ describeDb('scenario database seed', () => { (worldState.currentYear - (scenario.startYear ?? worldState.currentYear) + 10) * 2 ); expect(meta.killturn).toBe(1600); + expect(meta.serverName).toBe('훼'); const autorun = (meta.autorun_user ?? {}) as Record; const autorunOptions = (autorun.options ?? {}) as Record; expect(autorunOptions.develop).toBe(true); diff --git a/app/game-engine/test/unificationPersistence.test.ts b/app/game-engine/test/unificationPersistence.test.ts index e180fad9..f1b89d1c 100644 --- a/app/game-engine/test/unificationPersistence.test.ts +++ b/app/game-engine/test/unificationPersistence.test.ts @@ -260,7 +260,12 @@ describe('persistUnificationFinalization', () => { expect(hallCreate).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ - aux: expect.objectContaining({ ownerDisplayName: '표시 이름', fgColor: '#000000' }), + aux: expect.objectContaining({ + ownerDisplayName: '표시 이름', + fgColor: '#000000', + serverName: '테스트', + serverIdx: 2, + }), }), }) ); @@ -279,7 +284,10 @@ describe('persistUnificationFinalization', () => { ); expect(emperorCreate).toHaveBeenCalledWith( expect.objectContaining({ - data: expect.objectContaining({ aux: { winnerNationId: 1, generationKey: input.generationKey } }), + data: expect.objectContaining({ + phase: '테스트2기', + aux: { winnerNationId: 1, generationKey: input.generationKey }, + }), }) ); const archiveWrite = oldGeneralUpsert.mock.calls[0]?.[0] as { diff --git a/app/gateway-api/src/orchestrator/gatewayOrchestrator.ts b/app/gateway-api/src/orchestrator/gatewayOrchestrator.ts index 522f1021..816f506b 100644 --- a/app/gateway-api/src/orchestrator/gatewayOrchestrator.ts +++ b/app/gateway-api/src/orchestrator/gatewayOrchestrator.ts @@ -23,6 +23,7 @@ import { import { isRecord } from '@sammo-ts/common'; import { resolveGatewayPostgresConfigFromEnv } from '../gatewayPostgresConfig.js'; +import { resolveGatewayProfileKoreanName } from '../profileOrder.js'; import { buildTurboReleaseCommand, @@ -281,6 +282,13 @@ const buildServerId = (profileName: string, now: Date, installOperationId?: stri return `${profileName}_${year}${month}${day}_${suffix}`; }; +export const resolveProfileArchiveServerName = ( + profile: Pick +): string => { + const meta = normalizeMeta(profile.meta); + return resolveGatewayProfileKoreanName(profile.profile, meta.korName); +}; + const readMetaNumber = (meta: Record, key: string): number | null => { const raw = meta[key]; if (typeof raw === 'number' && Number.isFinite(raw)) { @@ -2010,6 +2018,9 @@ export class GatewayOrchestrator implements GatewayOrchestratorHandle { season, firstGameIdx, serverId, + // Snapshot the Gateway display name into this season. Hall and + // dynasty archives must not fall back to the runtime instance key. + serverName: resolveProfileArchiveServerName(profile), installCommitSha: commitSha, }, adminUser, diff --git a/app/gateway-api/test/orchestratorPlan.test.ts b/app/gateway-api/test/orchestratorPlan.test.ts index 5ee3535e..60cc4069 100644 --- a/app/gateway-api/test/orchestratorPlan.test.ts +++ b/app/gateway-api/test/orchestratorPlan.test.ts @@ -9,6 +9,7 @@ import { buildSharedProfileFrontendCommands, buildWorkspaceCommands, planProfileReconcile, + resolveProfileArchiveServerName, resolveResetLifecycleStatus, } from '../src/orchestrator/gatewayOrchestrator.js'; import { GATEWAY_PROFILE_ORDER } from '../src/profileOrder.js'; @@ -134,6 +135,30 @@ describe('resolveResetLifecycleStatus', () => { }); }); +describe('resolveProfileArchiveServerName', () => { + it('uses the configured Gateway name and never the runtime instance key', () => { + expect( + resolveProfileArchiveServerName({ + ...buildProfile(), + profile: 'hwe', + profileName: 'hwe:default', + meta: { korName: ' 훼 ' }, + }) + ).toBe('훼'); + }); + + it('uses the canonical profile label when Gateway has no override', () => { + expect( + resolveProfileArchiveServerName({ + ...buildProfile(), + profile: 'hwe', + profileName: 'hwe:default', + meta: {}, + }) + ).toBe('훼'); + }); +}); + describe('buildProcessDefinitions', () => { const processConfig = { workspaceRoot: '/srv/sammo/main',