feat: 실행 중 게임 옵션 변경을 지원

Gateway 관리 화면에서 현재 기수의 장수 생성 제한, 유저 자동턴, 턴 간격을 내구성 action으로 변경한다.

턴 간격 변경은 논리 tick과 현재 게임 시각을 보존하며 DB와 Redis의 tick 기반 시각을 재투영하고 기존 로그 timestamp는 유지한다.
This commit is contained in:
2026-08-17 16:05:07 +00:00
parent 50e7d894e4
commit ca6823d409
26 changed files with 1680 additions and 115 deletions
@@ -368,6 +368,30 @@ const zShiftSchedule = z.object({
.refine((value) => value !== 0),
});
const zRuntimeAutorunOption = z.enum(['develop', 'warp', 'recruit', 'recruit_high', 'train', 'battle', 'chief']);
const zUpdateRuntimeSettings = z.object({
type: z.literal('updateRuntimeSettings'),
actionId: z.string().uuid(),
settings: z
.object({
turnTermMinutes: z
.number()
.int()
.refine((value) => [1, 2, 5, 10, 20, 30, 60, 120].includes(value))
.optional(),
blockGeneralCreate: z.union([z.literal(0), z.literal(1), z.literal(2)]).optional(),
autorunUser: z
.object({
limitMinutes: z.number().int().min(1).max(43200),
options: z.array(zRuntimeAutorunOption).min(1),
})
.nullable()
.optional(),
})
.refine((settings) => Object.values(settings).some((value) => value !== undefined)),
});
const normalizeAuctionFinalize: CommandNormalizer<'auctionFinalize'> = (envelope) => {
const command = parseWith(zAuctionFinalize, envelope.command);
if (!command) {
@@ -660,6 +684,14 @@ const normalizeShiftSchedule: CommandNormalizer<'shiftSchedule'> = (envelope) =>
return { ...command, requestId: envelope.requestId };
};
const normalizeUpdateRuntimeSettings: CommandNormalizer<'updateRuntimeSettings'> = (envelope) => {
const command = parseWith(zUpdateRuntimeSettings, envelope.command);
if (!command) {
return null;
}
return { ...command, requestId: envelope.requestId };
};
const normalizers: CommandNormalizerMap = {
auctionFinalize: normalizeAuctionFinalize,
auctionOpen: normalizeAuctionOpen,
@@ -699,6 +731,7 @@ const normalizers: CommandNormalizerMap = {
resume: normalizeResume,
shutdown: normalizeShutdown,
shiftSchedule: normalizeShiftSchedule,
updateRuntimeSettings: normalizeUpdateRuntimeSettings,
};
export const normalizeTurnDaemonCommand = (envelope: TurnDaemonCommandEnvelope): TurnDaemonCommand | null => {