feat: 게임 엔진에 Clock 인터페이스 및 관련 클래스 추가, 테스트 케이스 작성
This commit is contained in:
@@ -9,6 +9,9 @@
|
|||||||
"lint": "node -e \"console.log('lint not configured')\"",
|
"lint": "node -e \"console.log('lint not configured')\"",
|
||||||
"test": "vitest run --config vitest.config.ts"
|
"test": "vitest run --config vitest.config.ts"
|
||||||
},
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@sammo-ts/common": "workspace:*"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vitest": "^4.0.16"
|
"vitest": "^4.0.16"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,76 +1 @@
|
|||||||
import type { Clock } from './types.js';
|
export { ManualClock, StepClock, SystemClock } from '@sammo-ts/common';
|
||||||
|
|
||||||
export class SystemClock implements Clock {
|
|
||||||
// 시스템 시간을 기준으로 동작하는 기본 시계.
|
|
||||||
nowMs(): number {
|
|
||||||
return Date.now();
|
|
||||||
}
|
|
||||||
|
|
||||||
async sleepMs(ms: number): Promise<void> {
|
|
||||||
if (ms <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ManualClock implements Clock {
|
|
||||||
// 테스트에서 시간을 직접 이동시키는 수동 시계.
|
|
||||||
private currentMs: number;
|
|
||||||
|
|
||||||
constructor(initialMs = 0) {
|
|
||||||
this.currentMs = initialMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
nowMs(): number {
|
|
||||||
return this.currentMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
async sleepMs(ms: number): Promise<void> {
|
|
||||||
if (ms <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.currentMs += ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
advanceMs(ms: number): void {
|
|
||||||
if (ms <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.currentMs += ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
setMs(ms: number): void {
|
|
||||||
this.currentMs = ms;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class StepClock implements Clock {
|
|
||||||
// 호출마다 일정 간격씩 시간이 진행되는 시계.
|
|
||||||
private currentMs: number;
|
|
||||||
private readonly stepMs: number;
|
|
||||||
|
|
||||||
constructor(stepMs: number, initialMs = 0) {
|
|
||||||
if (stepMs <= 0) {
|
|
||||||
throw new Error('stepMs must be positive');
|
|
||||||
}
|
|
||||||
this.stepMs = stepMs;
|
|
||||||
this.currentMs = initialMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
nowMs(): number {
|
|
||||||
this.currentMs += this.stepMs;
|
|
||||||
return this.currentMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
async sleepMs(ms: number): Promise<void> {
|
|
||||||
if (ms <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.currentMs += ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
setMs(ms: number): void {
|
|
||||||
this.currentMs = ms;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -43,10 +43,7 @@ export type TurnDaemonCommand =
|
|||||||
| { type: 'resume'; reason?: string }
|
| { type: 'resume'; reason?: string }
|
||||||
| { type: 'shutdown'; reason?: string };
|
| { type: 'shutdown'; reason?: string };
|
||||||
|
|
||||||
export interface Clock {
|
export type { Clock } from '@sammo-ts/common';
|
||||||
nowMs(): number;
|
|
||||||
sleepMs(ms: number): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TurnSchedule {
|
export interface TurnSchedule {
|
||||||
getNextTurnTime(lastTurnTime: Date): Date;
|
getNextTurnTime(lastTurnTime: Date): Date;
|
||||||
|
|||||||
@@ -9,10 +9,12 @@
|
|||||||
"build": "tsc -p tsconfig.json",
|
"build": "tsc -p tsconfig.json",
|
||||||
"dev": "node -e \"console.log('dev not configured')\"",
|
"dev": "node -e \"console.log('dev not configured')\"",
|
||||||
"lint": "node -e \"console.log('lint not configured')\"",
|
"lint": "node -e \"console.log('lint not configured')\"",
|
||||||
"test": "node -e \"console.log('test not configured')\""
|
"test": "vitest run --config vitest.config.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"js-sha512": "^0.9.0"
|
"js-sha512": "^0.9.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {}
|
"devDependencies": {
|
||||||
|
"vitest": "^4.0.16"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export * from './rng.js';
|
export * from './rng.js';
|
||||||
|
export * from './time/Clock.js';
|
||||||
export * from './util/BytesLike.js';
|
export * from './util/BytesLike.js';
|
||||||
export * from './util/convertBytesLikeToArrayBuffer.js';
|
export * from './util/convertBytesLikeToArrayBuffer.js';
|
||||||
export * from './util/convertBytesLikeToUint8Array.js';
|
export * from './util/convertBytesLikeToUint8Array.js';
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
export interface Clock {
|
||||||
|
nowMs(): number;
|
||||||
|
sleepMs(ms: number): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SystemClock implements Clock {
|
||||||
|
// 시스템 시간을 기준으로 동작하는 기본 시계.
|
||||||
|
nowMs(): number {
|
||||||
|
return Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
async sleepMs(ms: number): Promise<void> {
|
||||||
|
if (ms <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ManualClock implements Clock {
|
||||||
|
// 테스트에서 시간을 직접 이동시키는 수동 시계.
|
||||||
|
private currentMs: number;
|
||||||
|
|
||||||
|
constructor(initialMs = 0) {
|
||||||
|
this.currentMs = initialMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
nowMs(): number {
|
||||||
|
return this.currentMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
async sleepMs(ms: number): Promise<void> {
|
||||||
|
if (ms <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.currentMs += ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
advanceMs(ms: number): void {
|
||||||
|
if (ms <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.currentMs += ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMs(ms: number): void {
|
||||||
|
this.currentMs = ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StepClock implements Clock {
|
||||||
|
// 호출마다 일정 간격씩 시간이 진행되는 시계.
|
||||||
|
private currentMs: number;
|
||||||
|
private readonly stepMs: number;
|
||||||
|
|
||||||
|
constructor(stepMs: number, initialMs = 0) {
|
||||||
|
if (stepMs <= 0) {
|
||||||
|
throw new Error('stepMs must be positive');
|
||||||
|
}
|
||||||
|
this.stepMs = stepMs;
|
||||||
|
this.currentMs = initialMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
nowMs(): number {
|
||||||
|
this.currentMs += this.stepMs;
|
||||||
|
return this.currentMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
async sleepMs(ms: number): Promise<void> {
|
||||||
|
if (ms <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.currentMs += ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
advanceMs(ms: number): void {
|
||||||
|
if (ms <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.currentMs += ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMs(ms: number): void {
|
||||||
|
this.currentMs = ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import { ManualClock, StepClock } from '../src/lifecycle/clock.js';
|
|
||||||
|
import { ManualClock, StepClock } from '../src/time/Clock.js';
|
||||||
|
|
||||||
describe('ManualClock', () => {
|
describe('ManualClock', () => {
|
||||||
it('returns current time without advancing', () => {
|
it('returns current time without advancing', () => {
|
||||||
@@ -37,6 +38,12 @@ describe('StepClock', () => {
|
|||||||
expect(clock.nowMs()).toBe(1250);
|
expect(clock.nowMs()).toBe(1250);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('advances manually', () => {
|
||||||
|
const clock = new StepClock(10, 0);
|
||||||
|
clock.advanceMs(50);
|
||||||
|
expect(clock.nowMs()).toBe(60);
|
||||||
|
});
|
||||||
|
|
||||||
it('rejects non-positive step', () => {
|
it('rejects non-positive step', () => {
|
||||||
expect(() => new StepClock(0)).toThrow('stepMs must be positive');
|
expect(() => new StepClock(0)).toThrow('stepMs must be positive');
|
||||||
expect(() => new StepClock(-5)).toThrow('stepMs must be positive');
|
expect(() => new StepClock(-5)).toThrow('stepMs must be positive');
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
environment: 'node',
|
||||||
|
globals: true,
|
||||||
|
include: ['test/**/*.test.ts'],
|
||||||
|
},
|
||||||
|
});
|
||||||
Generated
+8
@@ -18,6 +18,10 @@ importers:
|
|||||||
app/game-api: {}
|
app/game-api: {}
|
||||||
|
|
||||||
app/game-engine:
|
app/game-engine:
|
||||||
|
dependencies:
|
||||||
|
'@sammo-ts/common':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/common
|
||||||
devDependencies:
|
devDependencies:
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^4.0.16
|
specifier: ^4.0.16
|
||||||
@@ -34,6 +38,10 @@ importers:
|
|||||||
js-sha512:
|
js-sha512:
|
||||||
specifier: ^0.9.0
|
specifier: ^0.9.0
|
||||||
version: 0.9.0
|
version: 0.9.0
|
||||||
|
devDependencies:
|
||||||
|
vitest:
|
||||||
|
specifier: ^4.0.16
|
||||||
|
version: 4.0.16(@types/node@20.19.27)
|
||||||
|
|
||||||
packages/infra:
|
packages/infra:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
Reference in New Issue
Block a user