import {Client, Room} from "colyseus"; import {CardGameState} from "./schema/CardGameState"; import {OnJoinCommand} from "./commands/OnJoinCommand"; import {PlayReadyCommand} from "./commands/PlayReadyCommand"; import {Dispatcher} from "@colyseus/command"; import {DiscardCommand} from "./commands/DiscardCommand"; import {SelectPetCommand} from "./commands/SelectPetCommand"; import {ChangeCardCommand} from "./commands/ChangeCardCommand"; import {SelectHeroCommand} from "./commands/SelectHeroCommand"; import {EatCardCommand} from "./commands/EatCardCommand"; import {GiveUpCommand} from "./commands/GiveUpCommand"; import {BattleHandler} from "./logic/Handler/BattleHandler"; import {debugRoom, error, msgLog} from "../common/Debug"; import {Delayed} from "@gamestdio/timer/lib/Delayed"; import {IncomingMessage} from "http"; import {PlayerStateConst} from "../constants/PlayerStateConst"; import {Player} from "./schema/Player"; import {GMCommand} from "./commands/GMCommand"; import {GameStateConst} from "../constants/GameStateConst"; import {GameRestartCommand} from "./commands/GameRestartCommand"; import {RobotClient} from "../robot/RobotClient"; import {ChangePetCommand} from "./commands/ChangePetCommand"; import {createRobot} from "../common/WebApi"; export class GeneralRoom extends Room { dispatcher = new Dispatcher(this); maxClients = 4; battleMan = new BattleHandler(); // 用于游戏过程中各种计时器, 使用该计时器的前提是, 只针对当前操作玩家 gameClock: Map = new Map(); assistMap: Map = new Map(); async onAuth (client:Client, options: any, request: IncomingMessage) { debugRoom(options); // TODO: 验证用户信息 // client.auth.accountId = options.accountId; // client.auth.sessionId = options.sessionId; return true; } onCreate (options: any) { let cs = new CardGameState(); this.setMetadata({rank: 2}); this.setState(cs); this.battleMan.init(cs, this); this.clock.start(); this.state.gameState = GameStateConst.STATE_WAIT_JOIN; this.onMessage("play_ready_c2s", (client, message) => { msgLog('play_ready from ', client.sessionId, JSON.stringify(message)); this.dispatcher.dispatch(new PlayReadyCommand(), {client}); }); this.onMessage("change_card_c2s", (client, message) => { msgLog('change_card from ', client.sessionId, JSON.stringify(message)); this.dispatcher.dispatch(new ChangeCardCommand(), {client, cards: message.cards}); }); this.onMessage("discard_card_c2s", (client, message) => { msgLog('discard_card from ', client.sessionId, JSON.stringify(message)); this.dispatcher.dispatch(new DiscardCommand(), {client, cards: message.cards, dtype: 0}); }); this.onMessage("eat_card_c2s", (client, message) => { msgLog('eat_card from ', client.sessionId, JSON.stringify(message)); this.dispatcher.dispatch(new EatCardCommand(), {client, cards: message.cards, target: message.target}); }); this.onMessage("give_up_eat_c2s", (client, message) => { msgLog('give_up_take from ', client.sessionId, JSON.stringify(message)); this.dispatcher.dispatch(new GiveUpCommand(), {client}); }); this.onMessage("select_pet_c2s", (client, message) => { msgLog('select_pet from ', client.sessionId, JSON.stringify(message)); this.dispatcher.dispatch(new SelectPetCommand(), {client, cardId: message.card, playerId: message.player, pos: message.pos, effCards: message.effCards, oldpos: message.oldPos }); }); this.onMessage("select_hero_c2s", (client, message) => { msgLog('select_hero from ', client.sessionId, JSON.stringify(message)); this.dispatcher.dispatch(new SelectHeroCommand(), {client, heroId: message.heroId, cardGroup: message.cardgroup}); }); this.onMessage("gm", (client, message) => { msgLog('gm command from ', client.sessionId, JSON.stringify(message)); this.dispatcher.dispatch(new GMCommand(), {client, message}); }); this.onMessage("restart_c2s", (client, message) => { msgLog('restart game from ', client.sessionId, JSON.stringify(message)); this.dispatcher.dispatch(new GameRestartCommand(), {client}); }); this.onMessage("change_pet_c2s", (client, message) => { msgLog('change pet from ', client.sessionId, JSON.stringify(message)); this.dispatcher.dispatch(new ChangePetCommand(), {client, pos: message.pos, petId: message.petId}); }); this.onMessage("*", (client, type, message) => { // // Triggers when any other type of message is sent, // excluding "action", which has its own specific handler defined above. // msgLog(client.sessionId, "sent", type, JSON.stringify(message)); }); } onJoin (client: Client, options: any) { let data = { client: client, accountId: options.accountid, }; this.dispatcher.dispatch(new OnJoinCommand(), data); } //TODO: 掉线逻辑 async onLeave (client: Client, consented: boolean) { if (this.state.gameState === GameStateConst.STATE_GAME_OVER || this.state.gameState === GameStateConst.STATE_WAIT_JOIN) { this.state.players.delete(client.sessionId); if (this.assistMap.has(client.sessionId)) { this.assistMap.get(client.sessionId).leave(); this.assistMap.delete(client.sessionId); } this.bUserLeft(client.sessionId); } else { this.state.players.get(client.sessionId).state = PlayerStateConst.PLAYER_OFFLINE; let assistClient = this.getAssistClient(client.sessionId); assistClient.active = true; try { if (consented) { throw new Error("consented leave"); } else { await this.allowReconnection(client, 60); debugRoom(`${client.sessionId} 重连`); assistClient.active = false; this.state.players.get(client.sessionId).state = PlayerStateConst.PLAYER_NORMAL; } } catch (e) { debugRoom(`player realy level :${client.sessionId}, try add robot`); // this.state.players.delete(client.sessionId); } } } onDispose() { this.gameClock.clear(); this.assistMap.clear(); this.dispatcher.stop(); } getClient(player: string | Player): Client { let result: Client; let sessionId; if (typeof player == 'string') { sessionId = player; result = this.clients.find(client => client.sessionId == player ); } else { sessionId = player.id; result = this.clients.find(client => client.sessionId == player.id ); } if (!result) { error(`无法获取id为: ${typeof player == 'string' ? player : player.id} 的客户端`); result = this.getAssistClient(sessionId); debugRoom(`启用辅助机器人, 当前机器人状态: ${(result as RobotClient).active}`) } return result; } clientCount(): number { return this.clients.length; } /** * 给room.gameClock设定任务 * gameClock任何时候只有一个可执行的任务 * @param millisecond * @param handler * @param name */ beginSchedule(millisecond: number, handler: Function, name: string): void { debugRoom(`begin schedule: `, name, millisecond / 1000); if (this.gameClock.has(name) && this.gameClock.get(name)?.active) { error(`当前已存在进行中的gameClock: ${name}`); this.gameClock.get(name).clear(); this.gameClock.delete(name); } let self = this; let timeOverFun = function () { handler && handler(); } this.gameClock.set(name, this.clock.setTimeout(timeOverFun, millisecond , name)); } /** * 取消当前room.gameClock的任务 * gameClock任何时候只有一个可执行的任务 */ stopSchedule(name: string): number { debugRoom(`manual stop schedule: ${name}`); if (!this.gameClock.has(name)) { return -1; } else { let clock = this.gameClock.get(name) if (!clock.active) { this.gameClock.delete(name); return -1; } else { let time = clock.elapsedTime; clock.clear(); this.gameClock.delete(name); return time; } } } /** * 给room的gameClock增加n秒 * @param name * @param millisecond * @param reason */ addScheduleTime(millisecond: number, reason?: string, name?: string): void { let current; let currentName = name; if (!name) { for (let [id, clock] of this.gameClock) { if (clock.active) { current = clock; currentName = id; break; } } } else { if (this.gameClock.has(name)) { current = this.gameClock.get(name); } } debugRoom(`add schedule for ${currentName}, time: ${millisecond/1000}, reason: ${reason}`); if (current) { current.time += millisecond ; debugRoom(`schedule for ${name} remain: ${(current.time - current.elapsedTime)/1000}`); } } addRobot(playerId?: string) { let data = { host: 'ws://127.0.0.1:2567', room: this.roomId, sessionId: playerId } createRobot(data); } addAssistClient(sessionId: string) { if (!this.assistMap.has(sessionId)) { let client = new RobotClient(sessionId, this.state, this['onMessageHandlers']); this.assistMap.set(sessionId, client); } } getAssistClient(sessionId: string): RobotClient { return this.assistMap.get(sessionId); } getOppositePlayer(srcPlayer: string | Player): Player { let playerId; if (typeof srcPlayer === 'string') { playerId = srcPlayer as string; } else { playerId = srcPlayer.id; } if (!this.state.players.has(playerId)) { return null; } const sessionIds = [...this.state.players.keys()]; let idx = sessionIds.indexOf(playerId); let playerId2 = sessionIds[(2 + idx) % 4]; return this.state.players.get(playerId2); } }