feat: 예약 턴 명령별 중간 외교 전이와 실행 정보를 기록
This commit is contained in:
@@ -46,3 +46,56 @@ export const recordMonthlyAuditDiplomacy = (
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export interface AuditDiplomacyAction {
|
||||
actionKey: string;
|
||||
kind: 'nation' | 'general';
|
||||
actionOrdinal: number;
|
||||
actor: {
|
||||
generalId: number;
|
||||
userId: string | null;
|
||||
name: string;
|
||||
nationId: number;
|
||||
officerLevel: number;
|
||||
npcState: number;
|
||||
};
|
||||
}
|
||||
|
||||
export const recordTurnAuditDiplomacy = (
|
||||
world: InMemoryTurnWorld,
|
||||
before: TurnDiplomacy,
|
||||
after: TurnDiplomacy,
|
||||
action: AuditDiplomacyAction,
|
||||
turn: { generalId: number; tick: number; ordinal: number }
|
||||
): void => {
|
||||
const state = world.getState();
|
||||
const serverId = state.meta.serverId;
|
||||
if (typeof serverId !== 'string' || !serverId.trim()) return;
|
||||
const previousState = { state: before.state, term: before.term, dead: before.dead };
|
||||
const nextState = { state: after.state, term: after.term, dead: after.dead };
|
||||
if (JSON.stringify(previousState) === JSON.stringify(nextState)) return;
|
||||
const clock = world.getGameClockState();
|
||||
world.queueAuditDiplomacy({
|
||||
schemaVersion: 1,
|
||||
serverId,
|
||||
srcNationId: before.fromNationId,
|
||||
destNationId: before.toNationId,
|
||||
category: 'RELATION',
|
||||
source: 'ENGINE',
|
||||
eventType: 'TURN_RELATION_CHANGED',
|
||||
documentId: null,
|
||||
documentHash: null,
|
||||
previousDocumentId: null,
|
||||
year: state.currentYear,
|
||||
month: state.currentMonth,
|
||||
tick: BigInt(turn.tick),
|
||||
clockRevision: BigInt(clock.revision),
|
||||
executionId: `turn:${turn.generalId}:${turn.tick}:${clock.revision}`,
|
||||
ordinal: turn.ordinal,
|
||||
requestId: null,
|
||||
inputSequence: null,
|
||||
actor: { ...action.actor, actionKey: action.actionKey, kind: action.kind, actionOrdinal: action.actionOrdinal },
|
||||
before: previousState,
|
||||
after: nextState,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { recordTurnAuditDiplomacy, type AuditDiplomacyAction } from '../playAudit/diplomacy.js';
|
||||
import type { AuditDiplomacyEventDraft } from '@sammo-ts/infra';
|
||||
import { initializeNationAuditPolicies, type PendingAuditPolicy } from '../playAudit/policy.js';
|
||||
import type { PendingAuditMonth } from '../playAudit/persistence.js';
|
||||
@@ -71,6 +72,7 @@ export interface GeneralTurnResult {
|
||||
srcNationId: number;
|
||||
destNationId: number;
|
||||
patch: DiplomacyPatch;
|
||||
audit?: AuditDiplomacyAction;
|
||||
}>;
|
||||
created?: {
|
||||
generals: TurnGeneral[];
|
||||
@@ -1940,6 +1942,7 @@ export class InMemoryTurnWorld {
|
||||
executeGeneralTurn(general: TurnGeneral): GeneralTurnExecution {
|
||||
assertGameplayCommitAllowed(this.getGameClock().phase);
|
||||
const currentGeneral = this.generals.get(general.id) ?? general;
|
||||
const executionTick = currentGeneral.turnTick ?? this.getGameClock().dateToTick(currentGeneral.turnTime);
|
||||
const executionYear = this.state.currentYear;
|
||||
const executionMonth = this.state.currentMonth;
|
||||
const city = this.cities.get(currentGeneral.cityId);
|
||||
@@ -2064,12 +2067,22 @@ export class InMemoryTurnWorld {
|
||||
}
|
||||
}
|
||||
if (result.diplomacyPatches) {
|
||||
for (const patch of result.diplomacyPatches) {
|
||||
for (const [index, patch] of result.diplomacyPatches.entries()) {
|
||||
const key = buildDiplomacyKey(patch.srcNationId, patch.destNationId);
|
||||
const before = this.diplomacy.get(key) ?? buildDefaultDiplomacy(patch.srcNationId, patch.destNationId);
|
||||
this.applyDiplomacyPatch({
|
||||
srcNationId: patch.srcNationId,
|
||||
destNationId: patch.destNationId,
|
||||
patch: patch.patch,
|
||||
});
|
||||
const after = this.diplomacy.get(key);
|
||||
if (patch.audit && after) {
|
||||
recordTurnAuditDiplomacy(this, before, after, patch.audit, {
|
||||
generalId: currentGeneral.id,
|
||||
tick: executionTick,
|
||||
ordinal: index + 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result.created) {
|
||||
|
||||
@@ -1056,11 +1056,8 @@ export const createReservedTurnHandler = async (options: {
|
||||
nations: [] as Array<{ id: number; patch: Partial<Nation> }>,
|
||||
troops: [] as Array<{ id: number; patch: Partial<Troop> }>,
|
||||
};
|
||||
const diplomacyPatches: Array<{
|
||||
srcNationId: number;
|
||||
destNationId: number;
|
||||
patch: DiplomacyPatch;
|
||||
}> = [];
|
||||
const diplomacyPatches: NonNullable<GeneralTurnResult['diplomacyPatches']> = [];
|
||||
let auditActionOrdinal = 0;
|
||||
const createdGenerals: TurnGeneral[] = [];
|
||||
const createdNations: Nation[] = [];
|
||||
const commandDeletedTroopIds = new Set<number>();
|
||||
@@ -1333,6 +1330,15 @@ export const createReservedTurnHandler = async (options: {
|
||||
|
||||
const lastTurnBeforeExecution = JSON.stringify(currentGeneral.lastTurn ?? {});
|
||||
const generalBeforeExecution = currentGeneral;
|
||||
const actionOrdinal = ++auditActionOrdinal;
|
||||
const auditActor = {
|
||||
generalId: currentGeneral.id,
|
||||
userId: currentGeneral.userId ?? null,
|
||||
name: currentGeneral.name,
|
||||
nationId: currentGeneral.nationId,
|
||||
officerLevel: currentGeneral.officerLevel,
|
||||
npcState: currentGeneral.npcState,
|
||||
};
|
||||
const cityNationIdsBeforeExecution = new Map(
|
||||
(worldView?.listCities() ?? []).map((city) => [city.id, city.nationId] as const)
|
||||
);
|
||||
@@ -1574,6 +1580,9 @@ export const createReservedTurnHandler = async (options: {
|
||||
srcNationId: effect.srcNationId,
|
||||
destNationId: effect.destNationId,
|
||||
patch: effect.patch,
|
||||
...(typeof context.world.meta.serverId === 'string'
|
||||
? { audit: { actionKey, kind, actionOrdinal, actor: auditActor } }
|
||||
: {}),
|
||||
});
|
||||
worldOverlay?.applyDiplomacyPatch(effect.srcNationId, effect.destNationId, effect.patch);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user