Enhance world simulation and API functionality; implement clock management improvements, ensure proper handling of world states during updates, and refine data persistence methods to prevent resurrecting deleted worlds.

This commit is contained in:
Leonid Pershin
2026-08-16 22:02:45 +03:00
parent 9d1e3c29c7
commit 3610ee8051
12 changed files with 352 additions and 80 deletions
@@ -24,6 +24,12 @@ describe('parseGameTime', () => {
expect(parseGameTime('')).toBeNull();
expect(parseGameTime('not-a-date')).toBeNull();
});
it('rejects out-of-range components instead of rolling them over', () => {
expect(parseGameTime('2012-04-12T06:00:60')).toBeNull();
expect(parseGameTime('2012-04-12T06:60:00')).toBeNull();
expect(parseGameTime('2012-02-30T06:00:00')).toBeNull();
});
});
describe('formatGameTime', () => {
@@ -65,6 +71,27 @@ describe('interpolateGameTime', () => {
expect(date).not.toBeNull();
expect(formatGameTime(date!)).toBe('12 April 2012 · 06:10');
});
it('clamps timeScale the way the server does', () => {
const tooFast = interpolateGameTime(
{ gameTime: '2012-04-12T06:00:00', timeScale: 99, paused: false },
1000,
);
expect(formatGameTime(tooFast!)).toBe('12 April 2012 · 06:20');
const tooSlow = interpolateGameTime(
{ gameTime: '2012-04-12T06:00:00', timeScale: 0, paused: false },
1000,
);
expect(formatGameTime(tooSlow!)).toBe('12 April 2012 · 06:05');
});
});
describe('startGameTimeFromInput', () => {
it('rejects out-of-range components', () => {
expect(startGameTimeFromInput('2012-04-12T06:60')).toBeNull();
expect(startGameTimeFromInput('2012-04-12T06:00:60')).toBeNull();
});
});
describe('startGameTimeFromInput', () => {
+10 -1
View File
@@ -3,6 +3,10 @@ import type { WorldClock } from '../api/types';
/** Matches server GameTime: five game minutes per real second at x1. */
export const GAME_MINUTES_PER_REAL_SECOND = 5;
/** Mirrors GameTime.MinTimeScale / MaxTimeScale, which the server clamps to. */
export const MIN_TIME_SCALE = 1;
export const MAX_TIME_SCALE = 4;
const MONTHS = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December',
@@ -30,6 +34,7 @@ export function parseGameTime(raw: string): Date | null {
|| date.getDate() !== day
|| date.getHours() !== hour
|| date.getMinutes() !== minute
|| date.getSeconds() !== second
) {
return null;
}
@@ -64,7 +69,10 @@ export function interpolateGameTime(
if (!base) return null;
if (clock.paused || elapsedRealMs <= 0) return base;
const scale = Number.isFinite(clock.timeScale) ? clock.timeScale : 1;
// Clamp like the server does, so a contract drift cannot make the client draw a time nobody is simulating.
const scale = Number.isFinite(clock.timeScale)
? Math.min(Math.max(clock.timeScale, MIN_TIME_SCALE), MAX_TIME_SCALE)
: MIN_TIME_SCALE;
const gameMs = (elapsedRealMs / 1000) * GAME_MINUTES_PER_REAL_SECOND * scale * 60_000;
return new Date(base.getTime() + gameMs);
}
@@ -97,6 +105,7 @@ export function startGameTimeFromInput(raw: string): string | null {
|| date.getDate() !== day
|| date.getHours() !== hour
|| date.getMinutes() !== minute
|| date.getSeconds() !== second
) {
return null;
}