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