merge: 최신 core2026 main을 접속 제한 작업에 통합

This commit is contained in:
2026-08-15 18:49:52 +00:00
21 changed files with 616 additions and 58 deletions
@@ -330,8 +330,8 @@ export const cutJoinTurnTime = (value: Date, tickSeconds: number): Date => {
return new Date(baseTime + alignedSeconds * 1000);
};
const resolveTurnTime = (
rng: RandUtil,
export const resolveJoinTurnTime = (
rng: Pick<RandUtil, 'nextRangeInt'>,
worldState: WorldStateRow,
acceptedAt: Date,
runtimeTurnTime: Date,
@@ -348,7 +348,12 @@ const resolveTurnTime = (
offsetSeconds = inheritTurntimeZone * legacyTurnTermMinutes + rng.nextRangeInt(0, legacyTurnTermMinutes - 1);
offsetMicros = rng.nextRangeInt(0, 999_999);
} else {
turnTimeBase = base;
// Ref normally uses game_env.turntime as a near-current cursor. Core's
// durable daemon can legitimately be catching up from an older cursor,
// so scheduling from runtimeTurnTime may put a newly created general
// hours behind the game clock. The accepted game time is the equivalent
// current-time boundary for a new general.
turnTimeBase = acceptedAt;
offsetSeconds = rng.nextRangeInt(0, tickSeconds - 1);
offsetMicros = rng.nextRangeInt(0, 999_999);
}
@@ -662,7 +667,7 @@ export const createGeneralFromJoin = async (options: {
}
const experience = await resolveCatchupExperience(db, relativeYear);
const turnTime = resolveTurnTime(
const turnTime = resolveJoinTurnTime(
rng,
worldState,
acceptedAt,
@@ -18,13 +18,16 @@ const GENERAL_AI_ACTIONS = [
const NATION_AI_ACTIONS = ['che_몰수', 'che_발령', 'che_선전포고', 'che_천도', 'che_포상'] as const;
const GENERAL_REF_EDITOR_ACTIONS = [
'che_은퇴',
'che_임관',
'che_랜덤임관',
'che_강행',
'che_징병',
'che_출병',
'che_농지개간',
'che_화계',
'che_증여',
'che_하야',
'che_장비매매',
] as const;
const NATION_REF_EDITOR_ACTIONS = ['che_포상', 'che_발령', 'che_증축', 'che_필사즉생'] as const;
@@ -4,6 +4,7 @@ import {
buildJoinCreateGeneralSeed,
cutJoinTurnTime,
JOIN_WELCOME_MESSAGE,
resolveJoinTurnTime,
} from '../src/turn/joinCreateGeneralService.js';
describe('generic join legacy time contracts', () => {
@@ -19,6 +20,35 @@ describe('generic join legacy time contracts', () => {
);
});
it('schedules a new general within one turn of the accepted game time even when the daemon cursor is stale', () => {
const calls: Array<[number, number]> = [];
const values = [59, 250_000];
const rng = {
nextRangeInt(min: number, max: number) {
calls.push([min, max]);
return values.shift() ?? min;
},
};
const acceptedAt = new Date('2026-08-15T17:57:05.837Z');
const staleRuntimeTurnTime = new Date('2026-08-15T07:10:00.000Z');
const turnTime = resolveJoinTurnTime(
rng,
{ tickSeconds: 120 } as Parameters<typeof resolveJoinTurnTime>[1],
acceptedAt,
staleRuntimeTurnTime,
undefined
);
expect(turnTime.toISOString()).toBe('2026-08-15T17:58:05.087Z');
expect(turnTime.getTime()).toBeGreaterThan(acceptedAt.getTime());
expect(turnTime.getTime()).toBeLessThanOrEqual(acceptedAt.getTime() + 120_000);
expect(calls).toEqual([
[0, 119],
[0, 999_999],
]);
});
it('uses the HiDCHe product name without the legacy PHP runtime label', () => {
expect(JOIN_WELCOME_MESSAGE).toBe('삼국지 모의전투 HiDCHe의 세계에 오신 것을 환영합니다 ^o^');
expect(JOIN_WELCOME_MESSAGE).not.toContain('PHP');