card_svr/src/rooms/GeneralRoom.ts

310 lines
11 KiB
TypeScript

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";
import { Service } from '../service/Service'
export class GeneralRoom extends Room {
dispatcher = new Dispatcher(this);
maxClients = 4;
score = 0;
battleMan = new BattleHandler();
// 用于游戏过程中各种计时器, 使用该计时器的前提是, 只针对当前操作玩家
gameClock: Map<string, Delayed> = new Map();
assistMap: Map<String, RobotClient> = new Map();
match = '';
robotCount = 0;
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();
if (options.clients) {
this.maxClients = options.clients
}
this.setState(cs);
this.setPrivate(true);
if (options.count) {
this.robotCount = Math.min(Math.max(0, options.count), this.maxClients - 1);
}
this.match = options.match;
if (options.score) {
this.score = options.score;
}
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, target: message.target, 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});
});
/**
* broadcast_c2s的消息, 原样广播出去
*/
this.onMessage("broadcast_c2s", (client, message) => {
msgLog('broadcast_c2s from ', client.sessionId, JSON.stringify(message));
this.broadcast("broadcast_s2c", message, {except: client});
});
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,
seat: options.seat,
score: options.score,
};
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).online = false;
let assistClient = this.getAssistClient(client.sessionId);
assistClient.active = true;
try {
if (consented) {
throw new Error("consented leave");
} else {
await this.allowReconnection(client, 10 * 60);
debugRoom(`${client.sessionId} 重连`);
assistClient.active = false;
this.state.players.get(client.sessionId).online = true;
}
} 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) {
debugRoom(`无法获取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}`);
}
}
async addRobot(playerId?: string) {
const host = new Service().selfHost
let data = {
host,
room: this.roomId,
sessionId: playerId
}
await 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);
}
getPlayerByIdx(idx: number) {
for (let [, player] of this.state.players) {
if (player.idx == idx) {
return player;
}
}
}
/**
* 获取对位玩家
* @param srcPlayer
*/
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;
}
let idx = this.state.players.get(playerId).idx;
let opposIdx = ((this.maxClients / 2 | 0) + idx) % this.maxClients;
return this.getPlayerByIdx(opposIdx);
}
}