시간 도메인과 정지 중 메시지·베팅 경계 정리
This commit is contained in:
@@ -9,13 +9,18 @@ declare const gameTickBrand: unique symbol;
|
||||
declare const observedGameInstantBrand: unique symbol;
|
||||
declare const scheduleInstantBrand: unique symbol;
|
||||
declare const clockRevisionBrand: unique symbol;
|
||||
declare const deadlineGenerationBrand: unique symbol;
|
||||
declare const wallInstantBrand: unique symbol;
|
||||
declare const monotonicDurationBrand: unique symbol;
|
||||
|
||||
export type GameTick = number & { readonly [gameTickBrand]: 'GameTick' };
|
||||
export type ObservedGameInstant = GameTick & { readonly [observedGameInstantBrand]: 'ObservedGameInstant' };
|
||||
export type ScheduleInstant = GameTick & { readonly [scheduleInstantBrand]: 'ScheduleInstant' };
|
||||
export type ClockRevision = number & { readonly [clockRevisionBrand]: 'ClockRevision' };
|
||||
export type DeadlineGeneration = number & { readonly [deadlineGenerationBrand]: 'DeadlineGeneration' };
|
||||
export type WallInstant = Date & { readonly [wallInstantBrand]: 'WallInstant' };
|
||||
/** Process-local elapsed milliseconds; never persist this value as a business timestamp. */
|
||||
export type MonotonicDuration = number & { readonly [monotonicDurationBrand]: 'MonotonicDuration' };
|
||||
|
||||
export interface ClockAlignmentPlan {
|
||||
policy: ClockAlignmentPolicy;
|
||||
@@ -59,6 +64,13 @@ export const asClockRevision = (revision: number): ClockRevision => {
|
||||
return revision as ClockRevision;
|
||||
};
|
||||
|
||||
export const asDeadlineGeneration = (generation: number): DeadlineGeneration => {
|
||||
if (!Number.isSafeInteger(generation) || generation < 1) {
|
||||
throw new Error(`Deadline generation must be a positive safe integer: ${generation}`);
|
||||
}
|
||||
return generation as DeadlineGeneration;
|
||||
};
|
||||
|
||||
export const asWallInstant = (instant: Date): WallInstant => {
|
||||
if (Number.isNaN(instant.getTime())) {
|
||||
throw new Error('Wall instant must be a valid date.');
|
||||
@@ -66,6 +78,13 @@ export const asWallInstant = (instant: Date): WallInstant => {
|
||||
return new Date(instant.getTime()) as WallInstant;
|
||||
};
|
||||
|
||||
export const asMonotonicDuration = (milliseconds: number): MonotonicDuration => {
|
||||
if (!Number.isFinite(milliseconds) || milliseconds < 0) {
|
||||
throw new Error(`Monotonic duration must be a non-negative finite number: ${milliseconds}`);
|
||||
}
|
||||
return milliseconds as MonotonicDuration;
|
||||
};
|
||||
|
||||
export const inferClockPhase = (mode: GameClockMode): GameClockPhase => (mode === 'manual' ? 'MANUAL' : 'RUNNING');
|
||||
|
||||
const GAME_CLOCK_PHASES: readonly GameClockPhase[] = [
|
||||
@@ -136,8 +155,7 @@ const buildAlignmentPlan = (input: {
|
||||
const remainingMilliseconds = elapsedMilliseconds - wholeSeconds * 1_000;
|
||||
const gapTicks = asGameTick(
|
||||
requireSafeTick(
|
||||
wholeSeconds * input.ticksPerSecond +
|
||||
Math.trunc((remainingMilliseconds * input.ticksPerSecond) / 1_000)
|
||||
wholeSeconds * input.ticksPerSecond + Math.trunc((remainingMilliseconds * input.ticksPerSecond) / 1_000)
|
||||
)
|
||||
);
|
||||
const catchUpTicks = asGameTick(input.catchUpTicks ?? 0);
|
||||
@@ -250,7 +268,13 @@ export class GameClock {
|
||||
}
|
||||
|
||||
nowTick(wallNow: Date): number {
|
||||
if (this.mode === 'manual' || this.phase === 'MANUAL' || this.phase === 'COMPLETED') {
|
||||
if (
|
||||
this.mode === 'manual' ||
|
||||
this.phase === 'MANUAL' ||
|
||||
this.phase === 'SUSPENDED' ||
|
||||
this.phase === 'RECONCILING' ||
|
||||
this.phase === 'COMPLETED'
|
||||
) {
|
||||
return this.tick;
|
||||
}
|
||||
const elapsedTicks = this.ticksBetween(this.wallAnchor, wallNow);
|
||||
|
||||
@@ -24,7 +24,7 @@ export interface TournamentClockFence {
|
||||
phaseKey: string;
|
||||
revision: number;
|
||||
deadlineGeneration: number;
|
||||
phase: 'RUNNING';
|
||||
phase: 'RUNNING' | 'MANUAL' | 'SUSPENDED';
|
||||
}
|
||||
|
||||
const WRITE_TOURNAMENT_PROJECTION_SCRIPT = `
|
||||
|
||||
@@ -190,7 +190,7 @@ export type TurnDaemonCommand =
|
||||
requestId?: string;
|
||||
auctionId: number;
|
||||
expectedCloseAt?: string;
|
||||
expectedCloseTick?: number;
|
||||
expectedCloseTick: number;
|
||||
}
|
||||
| {
|
||||
type: 'auctionOpen';
|
||||
@@ -260,7 +260,6 @@ export type TurnDaemonCommand =
|
||||
voteId: number;
|
||||
generalId: number;
|
||||
selection: number[];
|
||||
acceptedGameTick?: number;
|
||||
}
|
||||
| {
|
||||
type: 'setNationSetting';
|
||||
@@ -386,15 +385,12 @@ export type TurnDaemonCommand =
|
||||
ownerLegacyPenalty?: Record<string, unknown>;
|
||||
generalId: number;
|
||||
tokenNonce: number;
|
||||
acceptedGameAt?: string;
|
||||
}
|
||||
| {
|
||||
type: 'selectPoolReserve';
|
||||
requestId?: string;
|
||||
userId: string;
|
||||
seedOwnerIdentity: string | number;
|
||||
acceptedGameAt: string;
|
||||
acceptedGameTick?: number;
|
||||
}
|
||||
| {
|
||||
type: 'selectPoolCreate';
|
||||
@@ -407,8 +403,6 @@ export type TurnDaemonCommand =
|
||||
ownerPicture?: string;
|
||||
ownerImageServer?: number;
|
||||
ownerIconRevision?: string;
|
||||
acceptedGameAt?: string;
|
||||
acceptedGameTick?: number;
|
||||
}
|
||||
| {
|
||||
type: 'selectPoolReselect';
|
||||
@@ -416,8 +410,6 @@ export type TurnDaemonCommand =
|
||||
userId: string;
|
||||
ownerDisplayName: string;
|
||||
uniqueName: string;
|
||||
acceptedGameAt?: string;
|
||||
acceptedGameTick?: number;
|
||||
}
|
||||
| {
|
||||
type: 'auctionBid';
|
||||
@@ -426,7 +418,6 @@ export type TurnDaemonCommand =
|
||||
auctionId: number;
|
||||
generalId: number;
|
||||
amount: number;
|
||||
acceptedGameTick?: number;
|
||||
tryExtendCloseDate?: boolean;
|
||||
};
|
||||
|
||||
@@ -505,6 +496,7 @@ export type TurnDaemonCommandResult =
|
||||
ok: true;
|
||||
auctionId: number;
|
||||
closeAt: string;
|
||||
closeTick: number;
|
||||
}
|
||||
| {
|
||||
type: 'auctionOpen';
|
||||
@@ -835,6 +827,7 @@ export type TurnDaemonCommandResult =
|
||||
ok: true;
|
||||
auctionId: number;
|
||||
closeAt: string;
|
||||
closeTick: number;
|
||||
}
|
||||
| {
|
||||
type: 'auctionBid';
|
||||
|
||||
Reference in New Issue
Block a user