merge: 최신 main을 자동 외교 분리 브랜치에 통합한다

This commit is contained in:
2026-08-24 08:15:37 +00:00
7 changed files with 38 additions and 16 deletions
@@ -522,8 +522,9 @@ export const createGeneralFromJoin = async (options: {
worldState: WorldStateRow;
input: JoinCreateGeneralInput;
acceptedAt: Date;
operationalAcceptedAt: Date;
}): Promise<{ ok: true; generalId: number }> => {
const { db, world, worldState, input, acceptedAt } = options;
const { db, world, worldState, input, acceptedAt, operationalAcceptedAt } = options;
await lockJoinMutation(db, input.userId);
await assertGeneralIdSnapshotMatches(db, world);
@@ -734,7 +735,10 @@ export const createGeneralFromJoin = async (options: {
const nextInheritancePoint = currentInheritancePoint - inheritRequiredPoint;
const restInheritanceBonus = await resolveRestInheritanceBonus(db, worldState, input.userId);
const finalInheritancePoint = nextInheritancePoint + restInheritanceBonus;
const prestartDeleteAfter = buildPrestartDeleteAfter(acceptedAt, worldState.tickSeconds, config);
// Ref의 가오픈 삭제 대기는 정지된 게임 clock이 아니라 실제 요청 접수 시각부터 흐른다.
// 미래 정식 오픈에 clock을 고정한 PREOPEN에서도 사용자가 가오픈 중 두 턴을 기다리면
// 삭제할 수 있어야 하므로 RNG/턴 배치용 acceptedAt과 이 벽시계 경계를 분리한다.
const prestartDeleteAfter = buildPrestartDeleteAfter(operationalAcceptedAt, worldState.tickSeconds, config);
const general: TurnGeneral = {
id: generalId,
userId: input.userId,
@@ -839,11 +843,11 @@ export const createGeneralFromJoin = async (options: {
});
await db.generalAccessLog.upsert({
where: { generalId },
update: { userId: input.userId, lastRefresh: acceptedAt },
update: { userId: input.userId, lastRefresh: operationalAcceptedAt },
create: {
generalId,
userId: input.userId,
lastRefresh: acceptedAt,
lastRefresh: operationalAcceptedAt,
},
});
if (inheritRequiredPoint > 0) {
@@ -690,6 +690,7 @@ export const createGeneralFromSelectionPool = async (options: {
uniqueName: string;
personality: string;
now?: Date;
operationalAcceptedAt: Date;
seedOwnerIdentity?: string | number;
ownerPicture?: string;
ownerImageServer?: number;
@@ -754,7 +755,7 @@ export const createGeneralFromSelectionPool = async (options: {
const nextChangeAt = new Date(
now.getTime() + resolveTurnTermMinutes(worldState) * RESELECTION_TURN_MULTIPLIER * 60_000
);
const prestartDeleteAfter = buildPrestartDeleteAfter(now, worldState.tickSeconds, config);
const prestartDeleteAfter = buildPrestartDeleteAfter(options.operationalAcceptedAt, worldState.tickSeconds, config);
const showImgLevel = asNumber(config.showImgLevel, 0);
const useOwnerPicture =
showImgLevel >= 1 && typeof options.ownerPicture === 'string' && options.ownerPicture !== 'default.jpg';
@@ -893,8 +894,8 @@ export const createGeneralFromSelectionPool = async (options: {
}
await db.generalAccessLog.upsert({
where: { generalId },
update: { userId, lastRefresh: now },
create: { generalId, userId, lastRefresh: now },
update: { userId, lastRefresh: options.operationalAcceptedAt },
create: { generalId, userId, lastRefresh: options.operationalAcceptedAt },
});
await clearUnusedReservations(db, userId, now, nowTick);
await synchronizeSelectionPoolWorld(db, world);
@@ -296,6 +296,7 @@ async function handleJoinCreateGeneral(
...(command.inheritBonusStat !== undefined ? { inheritBonusStat: command.inheritBonusStat } : {}),
},
acceptedAt,
operationalAcceptedAt,
})),
};
} catch (error) {
@@ -366,7 +367,13 @@ async function handleSelectPoolCreate(
if (!worldState) {
throw new Error('Selection-pool world state is missing.');
}
const acceptedAt = await resolveSelectionCommandAcceptedAt(db, ctx.world, command);
const operationalAcceptedAt = await resolveCommandAcceptedAt(db, command);
const acceptedAt =
command.acceptedGameTick !== undefined
? ctx.world.gameTickToDate(command.acceptedGameTick)
: command.acceptedGameAt !== undefined
? new Date(command.acceptedGameAt)
: ctx.world.getGameNow(operationalAcceptedAt);
try {
return {
type: 'selectPoolCreate',
@@ -383,6 +390,7 @@ async function handleSelectPoolCreate(
...(command.ownerImageServer !== undefined ? { ownerImageServer: command.ownerImageServer } : {}),
...(command.ownerIconRevision ? { ownerIconRevision: command.ownerIconRevision } : {}),
now: acceptedAt,
operationalAcceptedAt,
})),
};
} catch (error) {