fix: 은퇴 명예 기록과 유산 수명주기 정합성을 복원한다

은퇴·사망·통일의 저장 순서와 명예의 전당 및 명장일람 판정을 Ref 흐름에 맞춘다.

유산 행동을 인증된 daemon transaction으로 통합하고 중복 지급·고유 아이템·로그·오류 경계를 회귀 테스트한다.
This commit is contained in:
2026-08-24 03:38:12 +00:00
parent 95cf68dffd
commit 4fc20de8d4
33 changed files with 4160 additions and 1166 deletions
@@ -265,6 +265,42 @@ const zPatchGeneral = z.object({
}),
});
const zInheritanceAction = z
.object({
type: z.literal('inheritanceAction'),
requestId: z.string().min(1).optional(),
userId: z.string().min(1),
input: z.discriminatedUnion('action', [
z.object({
action: z.literal('buyHiddenBuff'),
buffType: z.enum([
'warAvoidRatio',
'warCriticalRatio',
'warMagicTrialProb',
'domesticSuccessProb',
'domesticFailProb',
'warAvoidRatioOppose',
'warCriticalRatioOppose',
'warMagicTrialProbOppose',
]),
level: z.number().int().min(1).max(5),
}),
z.object({ action: z.literal('setNextSpecialWar'), specialKey: z.string().min(1) }),
z.object({ action: z.literal('resetSpecialWar') }),
z.object({ action: z.literal('resetTurnTime') }),
z.object({
action: z.literal('resetStat'),
leadership: z.number().int(),
strength: z.number().int(),
intel: z.number().int(),
inheritBonusStat: z.tuple([z.number().int(), z.number().int(), z.number().int()]).optional(),
}),
z.object({ action: z.literal('buyRandomUnique') }),
z.object({ action: z.literal('checkOwner'), targetGeneralId: z.number().int().positive() }),
]),
})
.strict();
const zAdjustGeneralIcon = z
.object({
type: z.literal('adjustGeneralIcon'),
@@ -643,6 +679,14 @@ const normalizePatchGeneral: CommandNormalizer<'patchGeneral'> = (envelope) => {
return { ...command, requestId: envelope.requestId };
};
const normalizeInheritanceAction: CommandNormalizer<'inheritanceAction'> = (envelope) => {
const command = parseWith(zInheritanceAction, envelope.command);
if (!command || (command.requestId !== undefined && command.requestId !== envelope.requestId)) {
return null;
}
return { ...command, requestId: envelope.requestId };
};
const normalizeAdjustGeneralIcon: CommandNormalizer<'adjustGeneralIcon'> = (envelope) => {
const command = parseWith(zAdjustGeneralIcon, envelope.command);
if (!command || (command.requestId !== undefined && command.requestId !== envelope.requestId)) {
@@ -764,6 +808,7 @@ const normalizers: CommandNormalizerMap = {
adjustGeneralMeta: normalizeAdjustGeneralMeta,
tournamentMatchResult: normalizeTournamentMatchResult,
patchGeneral: normalizePatchGeneral,
inheritanceAction: normalizeInheritanceAction,
adjustGeneralIcon: normalizeAdjustGeneralIcon,
joinCreateGeneral: normalizeJoinCreateGeneral,
npcPossessGeneral: normalizeNpcPossessGeneral,