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
+31 -7
View File
@@ -23,6 +23,7 @@ const parseWith = <T>(schema: z.ZodType<T>, value: unknown): T | null => {
};
const zFiniteNumber = z.number().finite();
const zSafeInteger = zFiniteNumber.int().min(Number.MIN_SAFE_INTEGER).max(Number.MAX_SAFE_INTEGER);
const zRecord = z.record(z.string(), z.unknown());
const zRunReason = z.enum(['schedule', 'manual', 'poke']);
@@ -35,6 +36,8 @@ const zTurnRunBudget = z.object({
const zAuctionFinalize = z.object({
type: z.literal('auctionFinalize'),
auctionId: zFiniteNumber,
expectedCloseAt: z.string().refine(isCanonicalIsoTimestamp).optional(),
expectedCloseTick: zSafeInteger.optional(),
});
const zAuctionOpen = z.object({
@@ -53,6 +56,7 @@ const zAuctionBid = z.object({
auctionId: zFiniteNumber,
generalId: zFiniteNumber,
amount: zFiniteNumber,
acceptedGameTick: zSafeInteger.optional(),
tryExtendCloseDate: z.boolean().optional(),
});
@@ -176,6 +180,7 @@ const zTournamentRefund = z.object({
const zTournamentBettingPayout = z.object({
type: z.literal('tournamentBettingPayout'),
bettingId: zFiniteNumber.optional(),
tournamentType: zFiniteNumber.optional(),
reason: z.string().optional(),
payouts: z.array(z.object({ generalId: zFiniteNumber, amount: zFiniteNumber })).min(1),
});
@@ -194,13 +199,8 @@ const zVoteReward = z.object({
type: z.literal('voteReward'),
voteId: zFiniteNumber,
generalId: zFiniteNumber,
goldReward: zFiniteNumber,
unique: z
.object({
expected: z.boolean(),
itemKey: z.string().nullable().optional(),
})
.optional(),
selection: z.array(zFiniteNumber.int()).min(1),
acceptedGameTick: zSafeInteger.optional(),
});
const zSetNationMeta = z.object({
@@ -329,6 +329,19 @@ const zSelectPoolCreate = z
ownerPicture: z.string().optional(),
ownerImageServer: z.number().int().nonnegative().optional(),
ownerIconRevision: z.string().refine(isCanonicalIsoTimestamp).optional(),
acceptedGameAt: z.string().refine(isCanonicalIsoTimestamp).optional(),
acceptedGameTick: zFiniteNumber.int().min(Number.MIN_SAFE_INTEGER).max(Number.MAX_SAFE_INTEGER).optional(),
})
.strict();
const zSelectPoolReserve = z
.object({
type: z.literal('selectPoolReserve'),
requestId: z.string().optional(),
userId: z.string().min(1),
seedOwnerIdentity: z.union([z.string().min(1), zFiniteNumber]),
acceptedGameAt: z.string().refine(isCanonicalIsoTimestamp),
acceptedGameTick: zFiniteNumber.int().min(Number.MIN_SAFE_INTEGER).max(Number.MAX_SAFE_INTEGER).optional(),
})
.strict();
@@ -339,6 +352,8 @@ const zSelectPoolReselect = z
userId: z.string().min(1),
ownerDisplayName: z.string().min(1),
uniqueName: z.string().min(1).max(20),
acceptedGameAt: z.string().refine(isCanonicalIsoTimestamp).optional(),
acceptedGameTick: zFiniteNumber.int().min(Number.MIN_SAFE_INTEGER).max(Number.MAX_SAFE_INTEGER).optional(),
})
.strict();
@@ -660,6 +675,14 @@ const normalizeSelectPoolCreate: CommandNormalizer<'selectPoolCreate'> = (envelo
return { ...command, requestId: envelope.requestId };
};
const normalizeSelectPoolReserve: CommandNormalizer<'selectPoolReserve'> = (envelope) => {
const command = parseWith(zSelectPoolReserve, envelope.command);
if (!command) {
return null;
}
return { ...command, requestId: envelope.requestId };
};
const normalizeSelectPoolReselect: CommandNormalizer<'selectPoolReselect'> = (envelope) => {
const command = parseWith(zSelectPoolReselect, envelope.command);
if (!command) {
@@ -744,6 +767,7 @@ const normalizers: CommandNormalizerMap = {
adjustGeneralIcon: normalizeAdjustGeneralIcon,
joinCreateGeneral: normalizeJoinCreateGeneral,
npcPossessGeneral: normalizeNpcPossessGeneral,
selectPoolReserve: normalizeSelectPoolReserve,
selectPoolCreate: normalizeSelectPoolCreate,
selectPoolReselect: normalizeSelectPoolReselect,
getStatus: normalizeGetStatus,