feat: Action 호출 타입 설계
- 호출 시점에선 강타입으로.
This commit is contained in:
@@ -0,0 +1,62 @@
|
|||||||
|
import type { IResourceController } from "@/IResourceController.js";
|
||||||
|
import type { CityID, GeneralID, NationID, TroopID } from "@/defs.js";
|
||||||
|
import { NotYetImplemented } from "@sammo/util";
|
||||||
|
|
||||||
|
|
||||||
|
export type QueueType = 'server' | 'api';
|
||||||
|
//type QueueType = 'server' | 'npc' | 'api';
|
||||||
|
|
||||||
|
export function setDefenceTrained(invoker: QueueType, arg: { generalID: GeneralID, defenceTrained: number }, rsc: IResourceController): void{
|
||||||
|
throw new NotYetImplemented();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function kickTroopMember(invoker: QueueType, arg: { nationID: NationID, troopID: TroopID, memberID: GeneralID }, rsc: IResourceController): void {
|
||||||
|
throw new NotYetImplemented();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function blockAttackCommand(invoker: QueueType, arg: { nationID: NationID, block: boolean }, rsc: IResourceController): void {
|
||||||
|
throw new NotYetImplemented();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function blockJoinNation(invoker: QueueType, arg: { nationID: NationID, block: boolean }, rsc: IResourceController): void {
|
||||||
|
throw new NotYetImplemented();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createGeneral(invoker: QueueType, arg: {
|
||||||
|
//유산 값등은 외부에서 처리하고 내부에는 순수 랜덤류만 입력
|
||||||
|
name: string,
|
||||||
|
ownerID: number,
|
||||||
|
ownerName: string,
|
||||||
|
leadership: number,
|
||||||
|
strength: number,
|
||||||
|
intel: number,
|
||||||
|
pic: string
|
||||||
|
character: string
|
||||||
|
|
||||||
|
turntimeZone?: number,
|
||||||
|
warSpecial?: string,
|
||||||
|
city?: CityID,
|
||||||
|
bonusStat?: [number, number, number],
|
||||||
|
|
||||||
|
//penalty: Record<GeneralPenalty, number>,
|
||||||
|
}, rsc: IResourceController):void{
|
||||||
|
throw new NotYetImplemented();
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActionFunc<Arg extends Record<string, any>, Res> = (invoker: QueueType, arg: Arg, rsc: IResourceController)=>Res;
|
||||||
|
export type ActionPackDef = {
|
||||||
|
[key: string]: ActionFunc<any, any>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ActionRequestList = {
|
||||||
|
setDefenceTrained,
|
||||||
|
kickTroopMember,
|
||||||
|
blockAttackCommand,
|
||||||
|
blockJoinNation,
|
||||||
|
createGeneral,
|
||||||
|
} as const satisfies ActionPackDef;
|
||||||
|
|
||||||
|
export type ActionRequestKey = keyof typeof ActionRequestList;
|
||||||
|
export type ActionMainArg<T extends ActionRequestKey> = Parameters<typeof ActionRequestList[T]>[1];
|
||||||
|
export type ActionRes<T extends ActionRequestKey> = ReturnType<typeof ActionRequestList[T]>;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
export * from "./LogicFunc/generalStats.js";
|
export * from "./LogicFunc/generalStats.js";
|
||||||
export * as defs from "./defs.js";
|
export * as defs from "./defs.js";
|
||||||
export * from "./IResourceController.js";
|
export * from "./IResourceController.js";
|
||||||
|
export * from "./ActionRequest/index.js";
|
||||||
// NOTE: game_logic은 client, server 모두에서 사용되는 라이브러리이다.
|
// NOTE: game_logic은 client, server 모두에서 사용되는 라이브러리이다.
|
||||||
// DB에 접근한다면 DI여야하나?
|
// DB에 접근한다면 DI여야하나?
|
||||||
@@ -4,9 +4,11 @@ import { ResourceController } from "./ResourceController.js";
|
|||||||
import { entriesWithType } from "@sammo/util/converter";
|
import { entriesWithType } from "@sammo/util/converter";
|
||||||
import db from "./connectDB.js";
|
import db from "./connectDB.js";
|
||||||
import type { Mongoose } from "mongoose";
|
import type { Mongoose } from "mongoose";
|
||||||
import type { GameEngineMsg } from "./GameEngineDefs.js";
|
import type { ActionPack, GameEngineMsg } from "./GameEngineDefs.js";
|
||||||
|
import { ActionRequestList, type QueueType } from "@sammo/game_logic";
|
||||||
export interface ActionRequest {
|
export interface ActionRequest {
|
||||||
|
name: keyof ActionPack;
|
||||||
|
args: Parameters<ActionPack[keyof ActionPack]>[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ActionRequestContainer {
|
export interface ActionRequestContainer {
|
||||||
@@ -16,7 +18,7 @@ export interface ActionRequestContainer {
|
|||||||
|
|
||||||
export type ActionSuccess = {
|
export type ActionSuccess = {
|
||||||
success: true;
|
success: true;
|
||||||
message?: string;
|
value: ReturnType<ActionPack[keyof ActionPack]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ActionFailure = {
|
export type ActionFailure = {
|
||||||
@@ -25,8 +27,6 @@ export type ActionFailure = {
|
|||||||
}
|
}
|
||||||
export type ActionResult = ActionSuccess | ActionFailure;
|
export type ActionResult = ActionSuccess | ActionFailure;
|
||||||
|
|
||||||
type QueueType = 'server' | 'api';
|
|
||||||
//type QueueType = 'server' | 'npc' | 'api';
|
|
||||||
|
|
||||||
export class GameEngine {
|
export class GameEngine {
|
||||||
|
|
||||||
@@ -107,8 +107,7 @@ export class GameEngine {
|
|||||||
while (!queue.isEmpty()) {
|
while (!queue.isEmpty()) {
|
||||||
const action = must(queue.shift());
|
const action = must(queue.shift());
|
||||||
|
|
||||||
//TODO: action 처리
|
const actionResult = this.processAction(invoker, action.action);
|
||||||
const errorReason: string | undefined = undefined;
|
|
||||||
|
|
||||||
const [ticketResolve, ticketRejected] = must_err(
|
const [ticketResolve, ticketRejected] = must_err(
|
||||||
this.queueWaiters.get(action.waiterSeq),
|
this.queueWaiters.get(action.waiterSeq),
|
||||||
@@ -116,14 +115,14 @@ export class GameEngine {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.queueWaiters.delete(action.waiterSeq);
|
this.queueWaiters.delete(action.waiterSeq);
|
||||||
if (errorReason) {
|
if(actionResult.success){
|
||||||
responseQueue.push(() => {
|
responseQueue.push(() => {
|
||||||
ticketRejected(errorReason);
|
ticketResolve(actionResult);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else{
|
||||||
responseQueue.push(() => {
|
responseQueue.push(() => {
|
||||||
ticketResolve({ success: true });
|
ticketRejected(actionResult);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,6 +235,26 @@ export class GameEngine {
|
|||||||
console.log('GameEngine stopped');
|
console.log('GameEngine stopped');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private processAction(invoker: QueueType, action: ActionRequest): ActionResult {
|
||||||
|
const actionName = action.name;
|
||||||
|
const actionArgs = action.args;
|
||||||
|
|
||||||
|
try{
|
||||||
|
const result = ActionRequestList[actionName](invoker, actionArgs as any, this.rsc);
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
value: result
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch(e){
|
||||||
|
const msg = e instanceof Error ? e.message : e;
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
reason: String(msg),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private async pushAction(queueName: QueueType, action: ActionRequest): Promise<ActionResult> {
|
private async pushAction(queueName: QueueType, action: ActionRequest): Promise<ActionResult> {
|
||||||
const queue = this.actionQueue[queueName];
|
const queue = this.actionQueue[queueName];
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import * as nodePath from "node:path";
|
|||||||
import { delay, must } from "@sammo/util";
|
import { delay, must } from "@sammo/util";
|
||||||
import type { GameEngineMsg, GameEngineRPCDefs } from "./GameEngineDefs.js";
|
import type { GameEngineMsg, GameEngineRPCDefs } from "./GameEngineDefs.js";
|
||||||
import { RPCClient } from "@sammo/server_util";
|
import { RPCClient } from "@sammo/server_util";
|
||||||
|
import type { ActionMainArg, ActionRes, ActionRequestKey, ActionRequestList } from "@sammo/game_logic";
|
||||||
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(new URL(import.meta.url));
|
const __filename = fileURLToPath(new URL(import.meta.url));
|
||||||
@@ -90,11 +91,23 @@ class GameEngineController {
|
|||||||
await this.start();
|
await this.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
async call<T extends keyof GameEngineRPCDefs & string>(name: T, arg: Parameters<GameEngineRPCDefs[T]>[0]): Promise<ReturnType<GameEngineRPCDefs[T]>> {
|
async pushAPIAction<T extends ActionRequestKey>(action: T, arg: ActionMainArg<T>): Promise<ActionRes<T>> {
|
||||||
return must(this.rpcClient).callFunction(name, arg);
|
const result = await must(this.rpcClient).callFunction('pushAPIAction', { name: action, args: arg });
|
||||||
|
if (!result.success) {
|
||||||
|
throw new Error(result.reason);
|
||||||
|
}
|
||||||
|
return result.value as ReturnType<typeof ActionRequestList[T]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
async pushServerAction<T extends ActionRequestKey>(action: T, arg: ActionMainArg<T>): Promise<ActionRes<T>> {
|
||||||
|
const result = await must(this.rpcClient).callFunction('pushServerAction', { name: action, args: arg });
|
||||||
|
if (!result.success) {
|
||||||
|
throw new Error(result.reason);
|
||||||
|
}
|
||||||
|
return result.value as ReturnType<typeof ActionRequestList[T]>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getGameEngineController(): GameEngineController {
|
export function getGameEngineController(): GameEngineController {
|
||||||
return GameEngineController.instance;
|
return GameEngineController.instance;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
import type { RPCLists } from "@sammo/server_util";
|
import type { RPCLists } from "@sammo/server_util";
|
||||||
import type { ActionRequest, ActionResult } from "./GameEngine.js";
|
import type { ActionRequest, ActionResult } from "./GameEngine.js";
|
||||||
import type { MessagePort } from "node:worker_threads";
|
import type { MessagePort } from "node:worker_threads";
|
||||||
|
import type { CityID, GeneralID, NationID, TroopID } from "@sammo/game_logic/src/defs.js";
|
||||||
|
import type { ActionPackDef } from "@sammo/game_logic";
|
||||||
|
import type { ActionRequestList, QueueType } from "@sammo/game_logic/src/ActionRequest/index.js";
|
||||||
|
//Remove Third argument. Use Infer
|
||||||
|
export type MapRscActionRequest<T extends ActionPackDef> = {
|
||||||
|
[P in keyof T]: (invoker: Parameters<T[P]>[1] & QueueType, arg: Parameters<T[P]>[1]) => ReturnType<T[P]>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ActionPack = MapRscActionRequest<typeof ActionRequestList>;
|
||||||
|
|
||||||
export type GameEngineRPCDefs = {
|
export type GameEngineRPCDefs = {
|
||||||
stop: ()=>Promise<void>,
|
stop: () => Promise<void>,
|
||||||
pushAPIAction: (action: ActionRequest)=>Promise<ActionResult>,
|
pushAPIAction: (action: ActionRequest) => Promise<ActionResult>,
|
||||||
pushServerAction: (action: ActionRequest)=>Promise<ActionResult>,
|
pushServerAction: (action: ActionRequest) => Promise<ActionResult>,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user