가오픈 장수 턴의 오픈 전 적체 방지
This commit is contained in:
@@ -80,6 +80,27 @@ describe('generic join deterministic contracts', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('moves an inherited turn phase past the runnable opening boundary', () => {
|
||||
const rng = {
|
||||
nextRangeInt(min: number) {
|
||||
return min;
|
||||
},
|
||||
};
|
||||
const openingGameAt = new Date('2026-07-30T10:00:00.000Z');
|
||||
const preopenCursor = new Date(openingGameAt.getTime() - 30 * 60_000);
|
||||
|
||||
const turnTime = resolveJoinTurnTime(
|
||||
rng,
|
||||
{ tickSeconds: 60 } as Parameters<typeof resolveJoinTurnTime>[1],
|
||||
openingGameAt,
|
||||
preopenCursor,
|
||||
0
|
||||
);
|
||||
|
||||
expect(turnTime.getTime()).toBeGreaterThan(openingGameAt.getTime());
|
||||
expect((turnTime.getTime() - preopenCursor.getTime()) % 60_000).toBe(0);
|
||||
});
|
||||
|
||||
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');
|
||||
|
||||
@@ -174,6 +174,26 @@ describe('runtime clock shift', () => {
|
||||
expect(world.getGameClockState().wallAnchor).toEqual(resumedAt);
|
||||
});
|
||||
|
||||
it('keeps runnable general scheduling at the future opening anchor during PREOPEN', () => {
|
||||
const gameBase = new Date('2026-07-30T10:00:00.000Z');
|
||||
const openAt = new Date('2026-09-02T23:30:00.000Z');
|
||||
const world = buildWorld({
|
||||
clockBaseTime: gameBase,
|
||||
clockTick: 0,
|
||||
clockMode: 'realtime',
|
||||
clockWallAnchor: openAt,
|
||||
lastTurnTick: 0,
|
||||
});
|
||||
const preopenAt = new Date('2026-09-02T23:03:00.000Z');
|
||||
|
||||
expect(world.getGameNow(preopenAt).getTime()).toBeLessThan(gameBase.getTime());
|
||||
expect(world.getRunnableGameNow(preopenAt)).toEqual(gameBase);
|
||||
expect(world.getRunnableGameNow(openAt)).toEqual(gameBase);
|
||||
expect(world.getRunnableGameNow(new Date(openAt.getTime() + 60_000))).toEqual(
|
||||
new Date(gameBase.getTime() + 60_000)
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[5, 6],
|
||||
[10, 3],
|
||||
|
||||
@@ -4,7 +4,11 @@ import { GAME_TICKS_PER_TURN } from '@sammo-ts/common';
|
||||
import { parseScenarioGeneralPoolCandidate } from '@sammo-ts/logic';
|
||||
|
||||
import { InMemoryTurnWorld } from '../src/turn/inMemoryWorld.js';
|
||||
import { reserveSelectionPool, resolveSelectionPoolUserIcon } from '../src/turn/selectPoolService.js';
|
||||
import {
|
||||
buildInitialTurnTime,
|
||||
reserveSelectionPool,
|
||||
resolveSelectionPoolUserIcon,
|
||||
} from '../src/turn/selectPoolService.js';
|
||||
import type { TurnGeneralPoolEntry, TurnWorldSnapshot, TurnWorldState } from '../src/turn/types.js';
|
||||
|
||||
interface TestPoolRow {
|
||||
@@ -28,6 +32,30 @@ interface PoolWhere {
|
||||
|
||||
const acceptedAt = new Date('0200-05-01T00:00:00.000Z');
|
||||
|
||||
describe('selection-pool initial turn scheduling', () => {
|
||||
it('does not inherit a turntime base from before the opening boundary', () => {
|
||||
const openingGameAt = new Date('0200-01-01T00:00:00.000Z');
|
||||
const preopenGameAt = new Date(openingGameAt.getTime() - 30 * 60_000);
|
||||
const rng = {
|
||||
nextRangeInt(min: number) {
|
||||
return min;
|
||||
},
|
||||
};
|
||||
|
||||
const turnTime = buildInitialTurnTime(
|
||||
rng,
|
||||
{
|
||||
tickSeconds: 300,
|
||||
meta: { turntime: preopenGameAt.toISOString() },
|
||||
} as unknown as Parameters<typeof buildInitialTurnTime>[1],
|
||||
preopenGameAt,
|
||||
openingGameAt
|
||||
);
|
||||
|
||||
expect(turnTime.getTime()).toBeGreaterThanOrEqual(openingGameAt.getTime());
|
||||
});
|
||||
});
|
||||
|
||||
const buildRows = (): TestPoolRow[] =>
|
||||
Array.from({ length: 29 }, (_, index) => {
|
||||
const uniqueName = `P${String(index + 1).padStart(2, '0')}`;
|
||||
|
||||
Reference in New Issue
Block a user