diff --git a/@sammo/server/src/GameEngine.ts b/@sammo/server/src/GameEngine.ts index 2cfe20c..a5cb0c2 100644 --- a/@sammo/server/src/GameEngine.ts +++ b/@sammo/server/src/GameEngine.ts @@ -3,41 +3,54 @@ import Denque from "denque"; import { ResourceController } from "./ResourceController.js"; import { entriesWithType } from "@sammo/util/converter"; -export interface ActionQueueItem { - waiterSeq: number; +export interface ActionRequest { + } -export interface ActionQueueItemResult { - success: boolean; - reason?: string; +export interface ActionRequestContainer { + waiterSeq: number; + action: ActionRequest; +} + +export type ActionSuccess = { + success: true; message?: string; } +export type ActionFailure = { + success: false; + reason: string; +} +export type ActionResult = ActionSuccess | ActionFailure; + +type QueueType = 'server' | 'api'; +//type QueueType = 'server' | 'npc' | 'api'; + export class GameEngine { private static instance: GameEngine | null = null; private actionSeq = 0; - private queueWaiters = new Map void, reject: (reason: unknown) => void]>(); + private queueWaiters = new Map void, reject: (reason: ActionFailure) => void]>(); private signal = new Signal(30000); private actionQueue = { - server: new Denque(), - npc: new Denque(), - api: new Denque(), - } as const; + server: new Denque(), + //npc: new Denque(), + api: new Denque(), + } as const satisfies Record>; private rsc: ResourceController private constructor() { + //TODO MongoDB를 넘겨줘야함 this.rsc = new ResourceController(); } private continueRun = true; - private async gameLoop(): Promise { let generalWaitTimer: ReturnType | null = null; let prevSaveWaiter: Promise | null = null; @@ -46,7 +59,7 @@ export class GameEngine { //가장 빠른 장수 do { const now = new Date(); - const upcomingGeneral = this.rsc.upcomingGeneral(); + const upcomingGeneral = this.rsc.peekGeneralTurntimeQueue(); if (!upcomingGeneral) { break; } @@ -64,6 +77,13 @@ export class GameEngine { } } while (0); + for(const queue of Object.values(this.actionQueue)){ + if(!queue.isEmpty()){ + this.signal.notify(); + break; + } + } + //이벤트 대기 await this.signal.wait(); @@ -80,18 +100,23 @@ export class GameEngine { while (true) { //장수턴 부터 처리 - const upcomingGeneral = this.rsc.upcomingGeneral(); + const upcomingGeneral = this.rsc.popGeneralTurntimeQueue(); if (!upcomingGeneral) { break; } - if (upcomingGeneral.raw.turntime > now) { + const generalTurntime = upcomingGeneral.raw.turntime; + if (generalTurntime > now) { break; } //TODO: 턴 수행해야지! upcomingGeneral.updateTurntime(); + this.rsc.insertGeneralTurntimeQueue(upcomingGeneral, true); + this.rsc.gameEnv().update((env)=>{ + env.lastExecuted = generalTurntime; + }) processedGeneralCount++; } @@ -136,7 +161,7 @@ export class GameEngine { await prevSaveWaiter; prevSaveWaiter = null; await this.rsc.saveAll(); - for(const response of responseQueue){ + for (const response of responseQueue) { response(); } } @@ -147,9 +172,18 @@ export class GameEngine { private gameLoopPromise: Promise | null = null; + public static async initInstance(): Promise { + if (GameEngine.instance) { + throw new Error('GameEngine is already initialized'); + } + + GameEngine.instance = new GameEngine(); + return GameEngine.instance; + } + public static getInstance() { if (!GameEngine.instance) { - GameEngine.instance = new GameEngine(); + throw new Error('GameEngine is not initialized'); } return GameEngine.instance; } @@ -182,7 +216,8 @@ export class GameEngine { } - private async pushAction(queneName: string, queue: Denque, action: ActionQueueItem): Promise { + private async pushAction(queueName: QueueType, action: ActionRequest): Promise { + const queue = this.actionQueue[queueName]; if (this.actionSeq >= Number.MAX_SAFE_INTEGER) { this.actionSeq = 0; } @@ -197,29 +232,34 @@ export class GameEngine { const waiter = new Promise((resolve, reject) => { this.queueWaiters.set(seq, [resolve, reject]); done = true; - queue.push(action); + queue.push({ + waiterSeq: seq, + action + }); }); await delay(0); this.signal.notify(); if (!done) { - throw new Error(`GameEngine: push${queneName}Action failed`); + throw new Error(`GameEngine: pushAction(${queueName}) failed`); } - return waiter as Promise; + return waiter as Promise; } - public async pushServerAction(action: ActionQueueItem): Promise { - return this.pushAction('Server', this.actionQueue.server, action); + public async pushServerAction(action: ActionRequest): Promise { + return this.pushAction('server', action); } - public async pushNPCAction(action: ActionQueueItem): Promise { - return this.pushAction('NPC', this.actionQueue.npc, action); + /* + public async pushNPCAction(action: ActionRequest): Promise { + return this.pushAction('npc', action); } + */ - public async pushAPIAction(action: ActionQueueItem): Promise { - return this.pushAction('API', this.actionQueue.api, action); + public async pushAPIAction(action: ActionRequest): Promise { + return this.pushAction('api', action); } } \ No newline at end of file