feat: 월간 외교 전이를 상태와 함께 감사 이력으로 저장

This commit is contained in:
2026-09-16 06:00:08 +00:00
parent bfe9ef479c
commit 248d457dd5
7 changed files with 154 additions and 4 deletions
@@ -0,0 +1,48 @@
import type { InMemoryTurnWorld } from '../turn/inMemoryWorld.js';
import type { TurnDiplomacy } from '../turn/types.js';
/** 기존 월간 처리의 전후 값을 기록한다. 기본 TRADE 행 생성은 전이가 아니다. */
export const recordMonthlyAuditDiplomacy = (
world: InMemoryTurnWorld,
before: readonly TurnDiplomacy[],
afterByKey: ReadonlyMap<string, TurnDiplomacy>
): void => {
const state = world.getState();
const serverId = state.meta.serverId;
if (typeof serverId !== 'string' || !serverId.trim()) return;
const clock = world.getGameClockState();
const project = (entry: TurnDiplomacy) => ({ state: entry.state, term: entry.term, dead: entry.dead });
let ordinal = 0;
for (const entry of [...before].sort(
(left, right) => left.fromNationId - right.fromNationId || left.toNationId - right.toNationId
)) {
const next = afterByKey.get(`${entry.fromNationId}:${entry.toNationId}`);
if (!next) continue;
const previousState = project(entry);
const nextState = project(next);
if (JSON.stringify(previousState) === JSON.stringify(nextState)) continue;
world.queueAuditDiplomacy({
schemaVersion: 1,
serverId,
srcNationId: entry.fromNationId,
destNationId: entry.toNationId,
category: 'RELATION',
source: 'ENGINE',
eventType: 'MONTHLY_RELATION_CHANGED',
documentId: null,
documentHash: null,
previousDocumentId: null,
year: state.currentYear,
month: state.currentMonth,
tick: BigInt(clock.tick),
clockRevision: BigInt(clock.revision),
executionId: `monthly:${state.currentYear}:${state.currentMonth}:${clock.revision}`,
ordinal: ++ordinal,
requestId: null,
inputSequence: null,
actor: null,
before: previousState,
after: nextState,
});
}
};
@@ -1,3 +1,4 @@
import { persistAuditDiplomacyEvents } from '@sammo-ts/infra';
import { persistAuditPolicies } from '../playAudit/policyPersistence.js';
import { prunePreviousAuditBatch, type AuditRetentionResult } from '../playAudit/retention.js';
import { persistAuditMonth } from '../playAudit/persistence.js';
@@ -1148,6 +1149,7 @@ export const createDatabaseTurnHooks = async (
pendingYearbookSnapshots,
pendingAuditMonths,
pendingAuditPolicies,
pendingAuditDiplomacy,
pendingUnificationFinalizations,
} = changes;
const reservedTurnChanges = options?.reservedTurns?.peekDirtyState();
@@ -1889,6 +1891,7 @@ export const createDatabaseTurnHooks = async (
});
}
await persistAuditPolicies(prisma, pendingAuditPolicies, auditCommand);
await persistAuditDiplomacyEvents(prisma, pendingAuditDiplomacy);
for (const snapshot of pendingAuditMonths) {
await persistAuditMonth(prisma, snapshot);
}
+18 -1
View File
@@ -1,3 +1,4 @@
import type { AuditDiplomacyEventDraft } from '@sammo-ts/infra';
import { initializeNationAuditPolicies, type PendingAuditPolicy } from '../playAudit/policy.js';
import type { PendingAuditMonth } from '../playAudit/persistence.js';
import type {
@@ -203,6 +204,7 @@ export interface TurnWorldChanges {
pendingYearbookSnapshots: PendingYearbookSnapshot[];
pendingAuditMonths: PendingAuditMonth[];
pendingAuditPolicies: PendingAuditPolicy[];
pendingAuditDiplomacy: AuditDiplomacyEventDraft[];
pendingUnificationFinalizations: PendingUnificationFinalization[];
}
@@ -245,6 +247,7 @@ export interface InMemoryTurnWorldStateSnapshot {
pendingYearbookSnapshots: PendingYearbookSnapshot[];
pendingAuditMonths: PendingAuditMonth[];
pendingAuditPolicies: PendingAuditPolicy[];
pendingAuditDiplomacy: AuditDiplomacyEventDraft[];
pendingUnificationFinalizations: PendingUnificationFinalization[];
pendingRealtimeBacklogShiftTicks: number;
}
@@ -551,6 +554,7 @@ export class InMemoryTurnWorld {
private readonly pendingYearbookSnapshots: PendingYearbookSnapshot[] = [];
private readonly pendingAuditMonths: PendingAuditMonth[] = [];
private readonly pendingAuditPolicies: PendingAuditPolicy[] = [];
private readonly pendingAuditDiplomacy: AuditDiplomacyEventDraft[] = [];
private readonly pendingUnificationFinalizations: PendingUnificationFinalization[] = [];
private pendingRealtimeBacklogShiftTicks = 0;
private readonly scenarioConfig: ScenarioConfig;
@@ -1101,6 +1105,7 @@ export class InMemoryTurnWorld {
pendingYearbookSnapshots: this.pendingYearbookSnapshots,
pendingAuditMonths: this.pendingAuditMonths,
pendingAuditPolicies: this.pendingAuditPolicies,
pendingAuditDiplomacy: this.pendingAuditDiplomacy,
pendingUnificationFinalizations: this.pendingUnificationFinalizations,
pendingRealtimeBacklogShiftTicks: this.pendingRealtimeBacklogShiftTicks,
} satisfies InMemoryTurnWorldStateSnapshot);
@@ -1149,6 +1154,7 @@ export class InMemoryTurnWorld {
this.replaceArray(this.pendingYearbookSnapshots, restored.pendingYearbookSnapshots);
this.replaceArray(this.pendingAuditMonths, restored.pendingAuditMonths);
this.replaceArray(this.pendingAuditPolicies, restored.pendingAuditPolicies);
this.replaceArray(this.pendingAuditDiplomacy, restored.pendingAuditDiplomacy);
this.replaceArray(this.pendingUnificationFinalizations, restored.pendingUnificationFinalizations);
this.pendingRealtimeBacklogShiftTicks = restored.pendingRealtimeBacklogShiftTicks ?? 0;
}
@@ -1373,12 +1379,20 @@ export class InMemoryTurnWorld {
return ordinal;
}
queueAuditDiplomacy(event: AuditDiplomacyEventDraft): void {
this.pendingAuditDiplomacy.push(structuredClone(event));
}
queueAuditPolicy(policy: PendingAuditPolicy): void {
this.pendingAuditPolicies.push(structuredClone(policy));
}
hasPendingAuditRecords(): boolean {
return this.pendingAuditPolicies.length > 0 || this.pendingAuditMonths.length > 0;
return (
this.pendingAuditPolicies.length > 0 ||
this.pendingAuditMonths.length > 0 ||
this.pendingAuditDiplomacy.length > 0
);
}
queueAuditMonth(snapshot: PendingAuditMonth): void {
@@ -2230,6 +2244,7 @@ export class InMemoryTurnWorld {
const pendingYearbookSnapshots = structuredClone(this.pendingYearbookSnapshots);
const pendingAuditMonths = structuredClone(this.pendingAuditMonths);
const pendingAuditPolicies = structuredClone(this.pendingAuditPolicies);
const pendingAuditDiplomacy = structuredClone(this.pendingAuditDiplomacy);
const pendingUnificationFinalizations = structuredClone(this.pendingUnificationFinalizations);
const accessScoreResetGeneralIds = Array.from(this.accessScoreResetGeneralIds).sort(
(left, right) => left - right
@@ -2264,6 +2279,7 @@ export class InMemoryTurnWorld {
pendingYearbookSnapshots,
pendingAuditMonths,
pendingAuditPolicies,
pendingAuditDiplomacy,
pendingUnificationFinalizations,
};
}
@@ -2304,6 +2320,7 @@ export class InMemoryTurnWorld {
this.pendingYearbookSnapshots.splice(0, changes.pendingYearbookSnapshots.length);
this.pendingAuditMonths.splice(0, changes.pendingAuditMonths.length);
this.pendingAuditPolicies.splice(0, changes.pendingAuditPolicies.length);
this.pendingAuditDiplomacy.splice(0, changes.pendingAuditDiplomacy.length);
this.pendingUnificationFinalizations.splice(0, changes.pendingUnificationFinalizations.length);
}
@@ -1,3 +1,4 @@
import { recordMonthlyAuditDiplomacy } from '../playAudit/diplomacy.js';
import { asRecord, JosaUtil, LiteHashDRBG, RandUtil } from '@sammo-ts/common';
import { DIPLOMACY_STATE, LogCategory, LogFormat, LogScope } from '@sammo-ts/logic';
import { simpleSerialize } from '@sammo-ts/logic/war/utils.js';
@@ -218,6 +219,8 @@ export const createMonthlyDiplomacyHandler = (options: {
world.listDiplomacy().map((entry) => [`${entry.fromNationId}:${entry.toNationId}`, entry] as const)
);
recordMonthlyAuditDiplomacy(world, before, afterByKey);
for (const entry of declarationStarts) {
const nation1 = world.getNationById(entry.fromNationId);
const nation2 = world.getNationById(entry.toNationId);