refactor
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
import { IPlayer, IRoom } from "./Model"
|
||||
export interface IPlayer {
|
||||
id: number, nickname: string, rid: number
|
||||
}
|
||||
|
||||
export interface IRoom {
|
||||
id: number, players: Array<IPlayer>
|
||||
}
|
||||
|
||||
export interface IApiPlayerListReq {
|
||||
}
|
||||
@@ -46,4 +52,10 @@ export interface IApiGameStartReq {
|
||||
rid: number
|
||||
}
|
||||
|
||||
export interface IApiGameStartRes { }
|
||||
export interface IApiGameStartRes { }
|
||||
|
||||
export interface IApiGameEndReq {
|
||||
rid: number
|
||||
}
|
||||
|
||||
export interface IApiGameEndRes { }
|
@@ -22,10 +22,12 @@ export enum ApiMsgEnum {
|
||||
ApiRoomJoin,
|
||||
ApiRoomLeave,
|
||||
ApiGameStart,
|
||||
ApiGameEnd,
|
||||
MsgPlayerList,
|
||||
MsgRoomList,
|
||||
MsgRoom,
|
||||
MsgGameStart,
|
||||
MsgGameEnd,
|
||||
MsgClientSync,
|
||||
MsgServerSync,
|
||||
}
|
||||
|
@@ -1,9 +1,50 @@
|
||||
import { IApiGameEndReq, IApiGameStartReq, IApiGameStartRes, IApiPlayerJoinReq, IApiPlayerJoinRes, IApiPlayerListReq, IApiPlayerListRes, IApiRoomCreateReq, IApiRoomCreateRes, IApiRoomJoinReq, IApiRoomJoinRes, IApiRoomLeaveReq, IApiRoomLeaveRes, IApiRoomListReq, IApiRoomListRes, IApiGameEndRes } from './Api'
|
||||
import { ApiMsgEnum } from './Enum'
|
||||
import { IMsgClientSync, IMsgGameEnd, IMsgGameStart, IMsgPlayerList, IMsgRoom, IMsgRoomList, IMsgServerSync } from './Msg'
|
||||
|
||||
export interface IPlayer {
|
||||
id: number, nickname: string, rid: number
|
||||
}
|
||||
|
||||
export interface IRoom {
|
||||
id: number, players: Array<IPlayer>
|
||||
export interface IModel {
|
||||
api: {
|
||||
[ApiMsgEnum.ApiPlayerJoin]: {
|
||||
req: IApiPlayerJoinReq,
|
||||
res: IApiPlayerJoinRes,
|
||||
}
|
||||
[ApiMsgEnum.ApiPlayerList]: {
|
||||
req: IApiPlayerListReq,
|
||||
res: IApiPlayerListRes,
|
||||
}
|
||||
[ApiMsgEnum.ApiRoomList]: {
|
||||
req: IApiRoomListReq,
|
||||
res: IApiRoomListRes,
|
||||
}
|
||||
[ApiMsgEnum.ApiRoomCreate]: {
|
||||
req: IApiRoomCreateReq,
|
||||
res: IApiRoomCreateRes,
|
||||
}
|
||||
[ApiMsgEnum.ApiRoomJoin]: {
|
||||
req: IApiRoomJoinReq,
|
||||
res: IApiRoomJoinRes,
|
||||
}
|
||||
[ApiMsgEnum.ApiRoomLeave]: {
|
||||
req: IApiRoomLeaveReq,
|
||||
res: IApiRoomLeaveRes,
|
||||
}
|
||||
[ApiMsgEnum.ApiGameStart]: {
|
||||
req: IApiGameStartReq,
|
||||
res: IApiGameStartRes,
|
||||
},
|
||||
[ApiMsgEnum.ApiGameEnd]: {
|
||||
req: IApiGameEndReq,
|
||||
res: IApiGameEndRes,
|
||||
}
|
||||
},
|
||||
msg: {
|
||||
[ApiMsgEnum.MsgPlayerList]: IMsgPlayerList
|
||||
[ApiMsgEnum.MsgRoomList]: IMsgRoomList,
|
||||
[ApiMsgEnum.MsgRoom]: IMsgRoom,
|
||||
[ApiMsgEnum.MsgGameStart]: IMsgGameStart,
|
||||
[ApiMsgEnum.MsgGameEnd]: IMsgGameEnd,
|
||||
[ApiMsgEnum.MsgClientSync]: IMsgClientSync,
|
||||
[ApiMsgEnum.MsgServerSync]: IMsgServerSync,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { IPlayer, IRoom } from "./Model"
|
||||
import { IPlayer, IRoom } from "./Api"
|
||||
import { IClientInput, IState } from "./State"
|
||||
|
||||
export interface IMsgPlayerList {
|
||||
@@ -22,6 +22,10 @@ export interface IMsgGameStart {
|
||||
state: IState
|
||||
}
|
||||
|
||||
export interface IMsgGameEnd {
|
||||
}
|
||||
|
||||
|
||||
export type IMsgClientSync = IClientInput
|
||||
|
||||
export type IMsgServerSync = Array<IClientInput>
|
@@ -7,36 +7,43 @@ export default class Room {
|
||||
id: number
|
||||
players: Set<Player> = new Set()
|
||||
lastSyncTime?: number
|
||||
timers: NodeJS.Timer[] = []
|
||||
|
||||
private timers: NodeJS.Timer[] = []
|
||||
private inputs: Array<IClientInput> = []
|
||||
|
||||
constructor(rid: number) {
|
||||
this.id = rid
|
||||
}
|
||||
|
||||
|
||||
join(uid: number) {
|
||||
const player = PlayerManager.Instance.getPlayerById(uid)
|
||||
if (player) {
|
||||
player.rid = this.id
|
||||
this.players.add(player)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
leave(uid: number) {
|
||||
const player = PlayerManager.Instance.getPlayerById(uid)
|
||||
if (player) {
|
||||
player.rid = -1
|
||||
player.connection.unlistenMsg(ApiMsgEnum.MsgClientSync, this.getInput, this)
|
||||
this.players.delete(player)
|
||||
if (!this.players.size) {
|
||||
this.timers.forEach(t => clearInterval(t))
|
||||
RoomManager.Instance.closeRoom(this.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
this.timers.forEach(t => clearInterval(t))
|
||||
for (const player of this.players) {
|
||||
player.connection.sendMsg(ApiMsgEnum.MsgGameEnd, {})
|
||||
player.connection.unlistenMsg(ApiMsgEnum.MsgClientSync, this.getInput, this)
|
||||
}
|
||||
this.players.clear()
|
||||
}
|
||||
|
||||
sync() {
|
||||
for (const player of this.players) {
|
||||
player.connection.sendMsg(ApiMsgEnum.MsgRoom, {
|
||||
@@ -71,10 +78,10 @@ export default class Room {
|
||||
player.connection.sendMsg(ApiMsgEnum.MsgGameStart, {
|
||||
state
|
||||
})
|
||||
player.connection.listenMsg(ApiMsgEnum.MsgClientSync, this.getInput, this)
|
||||
}
|
||||
this.listenPlayer()
|
||||
let t1 = setInterval(() => {
|
||||
this.syncInput()
|
||||
this.sendInput()
|
||||
}, 100)
|
||||
let t2 = setInterval(() => {
|
||||
this.timePast()
|
||||
@@ -82,19 +89,14 @@ export default class Room {
|
||||
this.timers = [t1, t2]
|
||||
}
|
||||
|
||||
listenPlayer() {
|
||||
for (const player of this.players) {
|
||||
player.connection.listenMsg(ApiMsgEnum.MsgClientSync, (input) => {
|
||||
this.inputs.push(input)
|
||||
})
|
||||
}
|
||||
getInput(input: IClientInput) {
|
||||
this.inputs.push(input)
|
||||
}
|
||||
|
||||
syncInput() {
|
||||
sendInput() {
|
||||
const inputs = this.inputs
|
||||
this.inputs = []
|
||||
|
||||
|
||||
for (const player of this.players) {
|
||||
player.connection.sendMsg(ApiMsgEnum.MsgServerSync, inputs)
|
||||
}
|
||||
|
@@ -37,6 +37,7 @@ export default class RoomManager extends Singleton {
|
||||
closeRoom(rid: number) {
|
||||
const room = this.getRoomById(rid)
|
||||
if (room) {
|
||||
room.close()
|
||||
this.rooms.delete(room)
|
||||
this.idMapRoom.delete(rid)
|
||||
}
|
||||
|
@@ -1,6 +1,3 @@
|
||||
import { IApiGameStartReq, IApiGameStartRes, IApiPlayerJoinReq, IApiPlayerJoinRes, IApiPlayerListReq, IApiPlayerListRes, IApiRoomCreateReq, IApiRoomCreateRes, IApiRoomJoinReq, IApiRoomJoinRes, IApiRoomLeaveReq, IApiRoomLeaveRes, IApiRoomListReq, IApiRoomListRes } from './Api'
|
||||
import { ApiMsgEnum } from './Enum'
|
||||
import { IMsgClientSync, IMsgGameStart, IMsgPlayerList, IMsgRoom, IMsgRoomList, IMsgServerSync } from './Msg'
|
||||
export * from './Api'
|
||||
export * from './Msg'
|
||||
export * from './Enum'
|
||||
@@ -9,44 +6,3 @@ export * from './State'
|
||||
export * from './Utils'
|
||||
export * from './Binary'
|
||||
|
||||
export interface IModel {
|
||||
api: {
|
||||
[ApiMsgEnum.ApiPlayerJoin]: {
|
||||
req: IApiPlayerJoinReq,
|
||||
res: IApiPlayerJoinRes,
|
||||
}
|
||||
[ApiMsgEnum.ApiPlayerList]: {
|
||||
req: IApiPlayerListReq,
|
||||
res: IApiPlayerListRes,
|
||||
}
|
||||
[ApiMsgEnum.ApiRoomList]: {
|
||||
req: IApiRoomListReq,
|
||||
res: IApiRoomListRes,
|
||||
}
|
||||
[ApiMsgEnum.ApiRoomCreate]: {
|
||||
req: IApiRoomCreateReq,
|
||||
res: IApiRoomCreateRes,
|
||||
}
|
||||
[ApiMsgEnum.ApiRoomJoin]: {
|
||||
req: IApiRoomJoinReq,
|
||||
res: IApiRoomJoinRes,
|
||||
}
|
||||
[ApiMsgEnum.ApiRoomLeave]: {
|
||||
req: IApiRoomLeaveReq,
|
||||
res: IApiRoomLeaveRes,
|
||||
}
|
||||
[ApiMsgEnum.ApiGameStart]: {
|
||||
req: IApiGameStartReq,
|
||||
res: IApiGameStartRes,
|
||||
}
|
||||
},
|
||||
msg: {
|
||||
[ApiMsgEnum.MsgPlayerList]: IMsgPlayerList
|
||||
[ApiMsgEnum.MsgRoomList]: IMsgRoomList,
|
||||
[ApiMsgEnum.MsgRoom]: IMsgRoom,
|
||||
[ApiMsgEnum.MsgGameStart]: IMsgGameStart,
|
||||
[ApiMsgEnum.MsgClientSync]: IMsgClientSync,
|
||||
[ApiMsgEnum.MsgServerSync]: IMsgServerSync,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -9,10 +9,15 @@ export enum ConnectionEventEnum {
|
||||
Close = 'Close',
|
||||
}
|
||||
|
||||
interface IItem {
|
||||
cb: Function;
|
||||
ctx: unknown;
|
||||
}
|
||||
|
||||
export class Connection extends EventEmitter {
|
||||
server: MyServer
|
||||
ws: WebSocket
|
||||
msgMap: Map<ApiMsgEnum, Function[]> = new Map()
|
||||
msgMap: Map<ApiMsgEnum, Array<IItem>> = new Map()
|
||||
playerId?: number;
|
||||
|
||||
constructor(server: MyServer, ws: WebSocket) {
|
||||
@@ -34,7 +39,7 @@ export class Connection extends EventEmitter {
|
||||
if (this.server.apiMap.has(name)) {
|
||||
try {
|
||||
const cb = this.server.apiMap.get(name)
|
||||
const res = cb?.(this, data)
|
||||
const res = cb.call(null, data)
|
||||
this.sendMsg(name, {
|
||||
success: true,
|
||||
res,
|
||||
@@ -45,31 +50,34 @@ export class Connection extends EventEmitter {
|
||||
error: (error as Error)?.message,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (this.msgMap.has(name)) {
|
||||
this.msgMap.get(name)?.forEach(cb => cb(data))
|
||||
} else {
|
||||
try {
|
||||
if (this.msgMap.has(name)) {
|
||||
this.msgMap.get(name)?.forEach(({ cb, ctx }) => cb.call(ctx, data))
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// console.log(`解析失败,${str}不是合法的JSON格式:`, error)
|
||||
console.log(error)
|
||||
console.log(`解析失败,不是合法的JSON格式:`, error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
listenMsg<T extends keyof IModel['msg']>(name: T, cb: (args: IModel['msg'][T]) => void) {
|
||||
listenMsg<T extends keyof IModel['msg']>(name: T, cb: (args: IModel['msg'][T]) => void, ctx: unknown) {
|
||||
if (this.msgMap.has(name)) {
|
||||
this.msgMap.get(name)?.push(cb)
|
||||
this.msgMap.get(name)?.push({ cb, ctx })
|
||||
} else {
|
||||
this.msgMap.set(name, [cb])
|
||||
this.msgMap.set(name, [{ cb, ctx }])
|
||||
}
|
||||
}
|
||||
|
||||
unlistenMsg<T extends keyof IModel['msg']>(name: T, cb: (args: IModel['msg'][T]) => void) {
|
||||
unlistenMsg<T extends keyof IModel['msg']>(name: T, cb: (args: IModel['msg'][T]) => void, ctx: unknown) {
|
||||
if (this.msgMap.has(name)) {
|
||||
const index = this.msgMap.get(name)?.indexOf(cb) || -1
|
||||
index > -1 && this.msgMap.get(name)?.splice(index, 1)
|
||||
const items = this.msgMap.get(name)
|
||||
const index = items.findIndex(i => cb === i.cb && i.ctx === ctx);
|
||||
index > -1 && items.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +88,7 @@ export class Connection extends EventEmitter {
|
||||
})
|
||||
const view = binaryEncode(name, data)
|
||||
const buffer = Buffer.from(view.buffer)
|
||||
console.log(`${getTime()}发送|字节数${buffer.length}|${this.playerId || -1}|${msg}`)
|
||||
console.log(`${getTime()}发送|字节数${buffer.length}|${this.playerId || -1}|内存${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)}MB|${msg}`)
|
||||
this.ws.send(buffer)
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@ import { Connection, MyServer, MyServerEventEnum } from './Core';
|
||||
import PlayerManager from './Biz/PlayerManager';
|
||||
import RoomManager from './Biz/RoomManager';
|
||||
import { getTime, symlinkCommon } from './Utils';
|
||||
import { ApiMsgEnum, IApiGameStartReq, IApiGameStartRes, IApiPlayerJoinReq, IApiPlayerJoinRes, IApiPlayerListReq, IApiPlayerListRes, IApiRoomCreateReq, IApiRoomCreateRes, IApiRoomJoinReq, IApiRoomJoinRes, IApiRoomLeaveReq, IApiRoomLeaveRes, IApiRoomListReq, IApiRoomListRes, IModel } from './Common';
|
||||
import { ApiMsgEnum, IApiGameEndReq, IApiGameEndRes, IApiGameStartReq, IApiGameStartRes, IApiPlayerJoinReq, IApiPlayerJoinRes, IApiPlayerListReq, IApiPlayerListRes, IApiRoomCreateReq, IApiRoomCreateRes, IApiRoomJoinReq, IApiRoomJoinRes, IApiRoomLeaveReq, IApiRoomLeaveRes, IApiRoomListReq, IApiRoomListRes, IModel } from './Common';
|
||||
|
||||
const server = new MyServer({ port: 8888 })
|
||||
|
||||
@@ -31,7 +31,6 @@ server.setApi(ApiMsgEnum.ApiPlayerJoin, (connection: Connection, { nickname }: I
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
server.setApi(ApiMsgEnum.ApiRoomList, (connection: Connection, data: IApiRoomListReq): IApiRoomListRes => {
|
||||
return { list: RoomManager.Instance.getRoomsView() }
|
||||
})
|
||||
@@ -114,6 +113,28 @@ server.setApi(ApiMsgEnum.ApiGameStart, (connection: Connection, data: IApiGameSt
|
||||
}
|
||||
})
|
||||
|
||||
server.setApi(ApiMsgEnum.ApiGameEnd, (connection: Connection, data: IApiGameEndReq): IApiGameEndRes => {
|
||||
if (connection.playerId) {
|
||||
const player = PlayerManager.Instance.getPlayerById(connection.playerId)
|
||||
if (player) {
|
||||
const rid = player.rid
|
||||
if (rid) {
|
||||
RoomManager.Instance.closeRoom(rid)
|
||||
PlayerManager.Instance.syncPlayers()
|
||||
RoomManager.Instance.syncRooms()
|
||||
return {}
|
||||
} else {
|
||||
throw new Error("ApiGameEnd 玩家不在房间")
|
||||
}
|
||||
} else {
|
||||
throw new Error("ApiGameEnd 玩家不存在")
|
||||
}
|
||||
} else {
|
||||
throw new Error("ApiGameEnd 玩家未登录")
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// start!!
|
||||
server.start().then(() => {
|
||||
symlinkCommon()
|
||||
|
@@ -72,7 +72,7 @@
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
|
Reference in New Issue
Block a user