diff --git a/app/game-engine/package.json b/app/game-engine/package.json index 7dc04d5..f45acb5 100644 --- a/app/game-engine/package.json +++ b/app/game-engine/package.json @@ -9,6 +9,9 @@ "lint": "node -e \"console.log('lint not configured')\"", "test": "vitest run --config vitest.config.ts" }, + "dependencies": { + "@sammo-ts/common": "workspace:*" + }, "devDependencies": { "vitest": "^4.0.16" } diff --git a/app/game-engine/src/lifecycle/clock.ts b/app/game-engine/src/lifecycle/clock.ts index ce2c6b1..e29f259 100644 --- a/app/game-engine/src/lifecycle/clock.ts +++ b/app/game-engine/src/lifecycle/clock.ts @@ -1,76 +1 @@ -import type { Clock } from './types.js'; - -export class SystemClock implements Clock { - // 시스템 시간을 기준으로 동작하는 기본 시계. - nowMs(): number { - return Date.now(); - } - - async sleepMs(ms: number): Promise { - 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 { - 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 { - if (ms <= 0) { - return; - } - this.currentMs += ms; - } - - setMs(ms: number): void { - this.currentMs = ms; - } -} +export { ManualClock, StepClock, SystemClock } from '@sammo-ts/common'; diff --git a/app/game-engine/src/lifecycle/types.ts b/app/game-engine/src/lifecycle/types.ts index 1f6b91c..b564d88 100644 --- a/app/game-engine/src/lifecycle/types.ts +++ b/app/game-engine/src/lifecycle/types.ts @@ -43,10 +43,7 @@ export type TurnDaemonCommand = | { type: 'resume'; reason?: string } | { type: 'shutdown'; reason?: string }; -export interface Clock { - nowMs(): number; - sleepMs(ms: number): Promise; -} +export type { Clock } from '@sammo-ts/common'; export interface TurnSchedule { getNextTurnTime(lastTurnTime: Date): Date; diff --git a/packages/common/package.json b/packages/common/package.json index c3f65ec..72cae07 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -9,10 +9,12 @@ "build": "tsc -p tsconfig.json", "dev": "node -e \"console.log('dev 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": { "js-sha512": "^0.9.0" }, - "devDependencies": {} + "devDependencies": { + "vitest": "^4.0.16" + } } diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 4b4a672..bd23b9e 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -1,4 +1,5 @@ export * from './rng.js'; +export * from './time/Clock.js'; export * from './util/BytesLike.js'; export * from './util/convertBytesLikeToArrayBuffer.js'; export * from './util/convertBytesLikeToUint8Array.js'; diff --git a/packages/common/src/time/Clock.ts b/packages/common/src/time/Clock.ts new file mode 100644 index 0000000..d285bab --- /dev/null +++ b/packages/common/src/time/Clock.ts @@ -0,0 +1,86 @@ +export interface Clock { + nowMs(): number; + sleepMs(ms: number): Promise; +} + +export class SystemClock implements Clock { + // 시스템 시간을 기준으로 동작하는 기본 시계. + nowMs(): number { + return Date.now(); + } + + async sleepMs(ms: number): Promise { + 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 { + 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 { + 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; + } +} diff --git a/app/game-engine/test/clock.test.ts b/packages/common/test/clock.test.ts similarity index 85% rename from app/game-engine/test/clock.test.ts rename to packages/common/test/clock.test.ts index 0df36ec..7a7a8c5 100644 --- a/app/game-engine/test/clock.test.ts +++ b/packages/common/test/clock.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { ManualClock, StepClock } from '../src/lifecycle/clock.js'; + +import { ManualClock, StepClock } from '../src/time/Clock.js'; describe('ManualClock', () => { it('returns current time without advancing', () => { @@ -37,6 +38,12 @@ describe('StepClock', () => { 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', () => { expect(() => new StepClock(0)).toThrow('stepMs must be positive'); expect(() => new StepClock(-5)).toThrow('stepMs must be positive'); diff --git a/packages/common/vitest.config.ts b/packages/common/vitest.config.ts new file mode 100644 index 0000000..5c63f1e --- /dev/null +++ b/packages/common/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + environment: 'node', + globals: true, + include: ['test/**/*.test.ts'], + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3d322a..d912c28 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,6 +18,10 @@ importers: app/game-api: {} app/game-engine: + dependencies: + '@sammo-ts/common': + specifier: workspace:* + version: link:../../packages/common devDependencies: vitest: specifier: ^4.0.16 @@ -34,6 +38,10 @@ importers: js-sha512: specifier: ^0.9.0 version: 0.9.0 + devDependencies: + vitest: + specifier: ^4.0.16 + version: 4.0.16(@types/node@20.19.27) packages/infra: dependencies: