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
+42
View File
@@ -41,6 +41,19 @@ export interface TurnDaemonStatus {
checkpoint?: TurnCheckpoint;
}
export type RuntimeAutorunUserOption = 'develop' | 'warp' | 'recruit' | 'recruit_high' | 'train' | 'battle' | 'chief';
export interface RuntimeAutorunUserSettings {
limitMinutes: number;
options: RuntimeAutorunUserOption[];
}
export interface RuntimeGameSettingsPatch {
turnTermMinutes?: number;
blockGeneralCreate?: 0 | 1 | 2;
autorunUser?: RuntimeAutorunUserSettings | null;
}
export type TurnDaemonCommand =
| {
type: 'run';
@@ -49,6 +62,12 @@ export type TurnDaemonCommand =
targetTime?: string;
budget?: TurnRunBudget;
}
| {
type: 'updateRuntimeSettings';
requestId?: string;
actionId: string;
settings: RuntimeGameSettingsPatch;
}
| { type: 'pause'; requestId?: string; reason?: string }
| { type: 'resume'; requestId?: string; reason?: string }
| { type: 'shutdown'; requestId?: string; reason?: string }
@@ -284,6 +303,29 @@ export type TurnDaemonCommandResult =
commandType: TurnDaemonCommand['type'];
reason: string;
}
| {
type: 'updateRuntimeSettings';
ok: true;
actionId: string;
settings: RuntimeGameSettingsPatch;
termChanged: boolean;
previousTurnTermMinutes: number;
turnTermMinutes: number;
previousClockBaseTime: string;
clockBaseTime: string;
lastTurnTime: string;
shiftedGenerals: number;
reprojectedAuctions: number;
reprojectedMessages: number;
reprojectedVotes: number;
checkpoint?: TurnCheckpoint;
}
| {
type: 'updateRuntimeSettings';
ok: false;
actionId: string;
reason: string;
}
| {
type: 'shiftSchedule';
ok: true;
@@ -0,0 +1,2 @@
ALTER TABLE "gateway_runtime_action"
ADD COLUMN "payload" JSONB NOT NULL DEFAULT '{}'::jsonb;
+30 -29
View File
@@ -77,35 +77,35 @@ enum GatewaySourceMode {
}
model AppUser {
id String @id @default(uuid())
loginId String @unique @map("login_id")
displayName String @unique @map("display_name")
passwordHash String @map("password_hash")
passwordSalt String @map("password_salt")
roles Json @default(dbgenerated("'[]'::jsonb"))
sanctions Json @default(dbgenerated("'{}'::jsonb"))
oauthType OAuthType @default(NONE) @map("oauth_type")
oauthId String? @unique @map("oauth_id")
email String? @unique
oauthInfo Json @default(dbgenerated("'{}'::jsonb")) @map("oauth_info")
picture String @default("default.jpg")
imageServer Int @default(0) @map("image_server")
iconUpdatedAt DateTime? @map("icon_updated_at")
iconRevision DateTime? @map("icon_revision")
profileIconResetAt DateTime? @map("profile_icon_reset_at")
iconRetiredAt DateTime? @map("icon_retired_at")
thirdPartyUse Boolean @default(true) @map("third_party_use")
termsAcceptedAt DateTime? @map("terms_accepted_at")
privacyAcceptedAt DateTime? @map("privacy_accepted_at")
kakaoVerifiedAt DateTime? @map("kakao_verified_at")
kakaoTalkVerifiedUntil DateTime? @map("kakao_talk_verified_until")
kakaoGraceStartedAt DateTime @default(now()) @map("kakao_grace_started_at")
kakaoGraceUntil DateTime? @map("kakao_grace_until")
deleteAfter DateTime? @map("delete_after")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
lastLoginAt DateTime? @map("last_login_at")
legacyData Json @default(dbgenerated("'{}'::jsonb")) @map("legacy_data")
id String @id @default(uuid())
loginId String @unique @map("login_id")
displayName String @unique @map("display_name")
passwordHash String @map("password_hash")
passwordSalt String @map("password_salt")
roles Json @default(dbgenerated("'[]'::jsonb"))
sanctions Json @default(dbgenerated("'{}'::jsonb"))
oauthType OAuthType @default(NONE) @map("oauth_type")
oauthId String? @unique @map("oauth_id")
email String? @unique
oauthInfo Json @default(dbgenerated("'{}'::jsonb")) @map("oauth_info")
picture String @default("default.jpg")
imageServer Int @default(0) @map("image_server")
iconUpdatedAt DateTime? @map("icon_updated_at")
iconRevision DateTime? @map("icon_revision")
profileIconResetAt DateTime? @map("profile_icon_reset_at")
iconRetiredAt DateTime? @map("icon_retired_at")
thirdPartyUse Boolean @default(true) @map("third_party_use")
termsAcceptedAt DateTime? @map("terms_accepted_at")
privacyAcceptedAt DateTime? @map("privacy_accepted_at")
kakaoVerifiedAt DateTime? @map("kakao_verified_at")
kakaoTalkVerifiedUntil DateTime? @map("kakao_talk_verified_until")
kakaoGraceStartedAt DateTime @default(now()) @map("kakao_grace_started_at")
kakaoGraceUntil DateTime? @map("kakao_grace_until")
deleteAfter DateTime? @map("delete_after")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
lastLoginAt DateTime? @map("last_login_at")
legacyData Json @default(dbgenerated("'{}'::jsonb")) @map("legacy_data")
icons UserIcon[]
specialAccessGrants SpecialAccountAccessGrant[]
@@ -242,6 +242,7 @@ model GatewayRuntimeAction {
profileName String @map("profile_name")
profile GatewayProfile @relation(fields: [profileName], references: [profileName], onDelete: Cascade)
action String
payload Json @default(dbgenerated("'{}'::jsonb"))
durationMinutes Int? @map("duration_minutes")
scheduledAt DateTime? @map("scheduled_at")
reason String?
+1
View File
@@ -171,6 +171,7 @@ export interface TurnEngineWorldStateUpdateInput {
clockMode: string;
clockWallAnchor: Date;
lastTurnTick: bigint;
config: InputJsonValue;
meta: InputJsonValue;
}