fix: Ref 게임 로직과 시나리오 풀 호환을 보정

월 경계, 전투 기술 상한, 연감과 베팅·설문·경매 정산 순서를 Ref 계약에 맞춘다.\n\n시나리오 일반 풀을 ENGINE mutation과 logical tick 기반으로 직렬화하고 914·915 catalog 및 조건부 100기 pool 실행 경계를 추가한다.\n\n경매 worker는 세대별 durable event만 만들고 ENGINE이 row lock 후 상태 전이와 정산을 단일 transaction으로 소유한다.
This commit is contained in:
2026-08-23 16:26:14 +00:00
parent bf6b7be7b0
commit 85591c68ad
114 changed files with 13327 additions and 901 deletions
@@ -21,6 +21,7 @@ const runtimeSettingsRequestId = 'integration:engine:runtime-game-settings';
const runtimeSettingsActionId = 'c9f68480-dba9-4e03-a62b-499e6234f18a';
const generalIds = [990_301, 990_302, 990_303, 990_304] as const;
const runtimeSettingsLogText = 'runtime-settings-existing-log';
const backlogPoolUniqueName = 'rebase-pool-990304';
const buildGeneral = (id: number, turnTime: Date): TurnGeneral =>
({
@@ -86,6 +87,7 @@ integration('runtime clock shift persistence', () => {
await db.message.deleteMany({ where: { mailbox: generalIds[2] } });
await db.logEntry.deleteMany({ where: { text: runtimeSettingsLogText } });
await db.auction.deleteMany({ where: { hostGeneralId: { in: [...generalIds] } } });
await db.selectPoolEntry.deleteMany({ where: { uniqueName: backlogPoolUniqueName } });
await db.general.deleteMany({ where: { id: { in: [...generalIds] } } });
await db.worldState.deleteMany({
where: {
@@ -100,6 +102,7 @@ integration('runtime clock shift persistence', () => {
await db.message.deleteMany({ where: { mailbox: generalIds[2] } });
await db.logEntry.deleteMany({ where: { text: runtimeSettingsLogText } });
await db.auction.deleteMany({ where: { hostGeneralId: { in: [...generalIds] } } });
await db.selectPoolEntry.deleteMany({ where: { uniqueName: backlogPoolUniqueName } });
await db.general.deleteMany({ where: { id: { in: [...generalIds] } } });
await db.worldState.deleteMany({
where: {
@@ -351,6 +354,26 @@ integration('runtime clock shift persistence', () => {
})
)
);
const poolEntry = await db.selectPoolEntry.create({
data: {
uniqueName: backlogPoolUniqueName,
ownerUserId: 'rebase-pool-user',
generalId: null,
reservedUntil: new Date('2099-09-01T00:10:00.000Z'),
reservedUntilTick: BigInt(2 * GAME_TICKS_PER_TURN),
info: {
uniqueName: backlogPoolUniqueName,
generalName: '재개예약후보',
leadership: 70,
strength: 70,
intel: 10,
specialDomestic: null,
dex: [10, 10, 10, 10, 10],
imgsvr: 0,
picture: 'default.jpg',
} as GamePrisma.InputJsonValue,
},
});
const world = new InMemoryTurnWorld(
{
id: row.id,
@@ -422,8 +445,15 @@ integration('runtime clock shift persistence', () => {
closeTick: BigInt(2 * GAME_TICKS_PER_TURN),
closeAt: new Date('2099-09-01T00:10:00.000Z'),
});
expect(await db.selectPoolEntry.findUniqueOrThrow({ where: { id: poolEntry.id } })).toMatchObject({
ownerUserId: 'rebase-pool-user',
generalId: null,
reservedUntilTick: BigInt(9 * GAME_TICKS_PER_TURN),
reservedUntil: new Date('2099-09-01T00:45:00.000Z'),
});
await db.auction.deleteMany({ where: { id: { in: [openAuction.id, finishedAuction.id] } } });
await db.selectPoolEntry.delete({ where: { id: poolEntry.id } });
await db.general.delete({ where: { id: general.id } });
await db.worldState.delete({ where: { id: row.id } });
});