增加服务端机器人

This commit is contained in:
zhl 2020-12-11 16:58:12 +08:00
parent fa949dd70c
commit c7c8ee38c0
18 changed files with 203 additions and 13 deletions

View File

@ -5,3 +5,5 @@ export const debugRoom = debug('jc:room');
export const error = debug('jc:error');
export const msgLog = debug('jc:msg');
export const robotLog = debug('jc:robot');

2
src/global.d.ts vendored
View File

@ -206,6 +206,8 @@ declare module "colyseus" {
*/
addScheduleTime(millisecond: number, reason?: string): void;
addRobot():void;
}
}

View File

@ -23,7 +23,7 @@ const server = http.createServer(app);
const gameServer = new Server({
server,
// driver: new MongooseDriver('mongodb://127.0.0.1/card-development'),
driver: new MongooseDriver('mongodb://192.168.100.24/card-development'),
driver: new MongooseDriver('mongodb://192.168.100.24/card-development-z'),
});
// register your room handlers

135
src/robot/RobotClient.ts Normal file
View File

@ -0,0 +1,135 @@
import {Client, Room} from "colyseus";
import {ClientState, ISendOptions} from "colyseus/lib/transport/Transport";
import { EventEmitter } from 'events';
import {robotLog as log} from '../common/Debug';
import {CardGameState} from "../rooms/schema/CardGameState";
import Clock from "@gamestdio/timer";
import {GameStateConst} from "../constants/GameStateConst";
import {Card} from "../rooms/schema/Card";
export class RobotClient implements Client {
id: string;
readyState: number;
ref: EventEmitter;
sessionId: string;
state: ClientState;
svrstate: CardGameState;
room: Room;
clock: Clock;
myTurn: boolean = false;
cards: Map<String, Card>;
onMessageHandlers: {[id: string]: (client: Client, message: any) => void} = {};
constructor(sessionId: string, state: CardGameState, clock: Clock, onMessageHandlers: {[id: string]: (client: Client, message: any) => void}) {
this.sessionId = sessionId;
this.svrstate = state;
this.clock = clock;
this.onMessageHandlers = onMessageHandlers;
let self = this;
let time = Math.random() * 2500 | 0;
this.clock.setTimeout(function (){
self.reply('play_ready_c2s', '');
}, time);
this.addListeners();
log(`new robot with session: ${sessionId}`);
}
addListeners() {
let self = this;
this.svrstate.listen('gameState', function (currentValue, previousValue) {
log(`server game state change: ${currentValue}, pre value: ${previousValue}`);
switch (currentValue) {
case GameStateConst.CHANGE_HERO:
self.selectHero();
break;
case GameStateConst.STATE_CHANGE_CARD:
self.changeCard([]);
break;
case GameStateConst.STATE_BEGIN_EAT:
if (!self.myTurn) {
setTimeout(self.giveup.bind(self), 1000);
}
break;
case GameStateConst.STATE_ROUND_RESULT:
break;
}
});
this.svrstate.listen('currentTurn', function (currentValue, previousValue) {
log(`server turn change: ${currentValue}, pre value: ${previousValue}`);
self.myTurn = currentValue === self.sessionId;
if (self.myTurn) {
self.discard();
}
})
}
close(code?: number, data?: string): void {
}
enqueueRaw(data: ArrayLike<number>, options?: ISendOptions): void {
// log('enqueueRaw:' + data.length);
}
error(code: number, message?: string): void {
}
leave(code?: number, data?: string): void {
}
raw(data: ArrayLike<number>, options?: ISendOptions): void {
}
public send(messageOrType: any, messageOrOptions?: any | ISendOptions, options?: ISendOptions) {
log(`receive server msg: ${messageOrType}, ${messageOrOptions}`);
switch (messageOrType) {
case 'draw_card_s2c':
break;
case 'player_ready_s2c':
break;
case 'steal_card_s2c':
break;
}
}
private reply(messageType: string, message: any) {
if (this.onMessageHandlers[messageType]) {
this.onMessageHandlers[messageType](this, message);
} else if (this.onMessageHandlers['*']) {
(this.onMessageHandlers['*'] as any)(this, messageType, message);
}
}
// >>>>>>>>>>>>>>>>>> begin
private discard() {
let card;
for (let [key, d] of this.cards) {
card = d;
break;
}
if (!card) {
return;
}
this.reply('discard_card_c2s', {
cards: [card.id]
});
}
private giveup () {
this.reply('give_up_eat_c2s', {});
}
private selectHero() {
this.cards = this.svrstate.players.get(this.sessionId).cards;
this.reply('select_hero_c2s', {
heroId: 30011
});
}
private changeCard(cardIds: number[]) {
this.reply('change_card_c2s', {
cards: cardIds
});
}
}

View File

@ -1,4 +1,4 @@
import {Client, Room} from "colyseus";
import {Client, generateId, Room} from "colyseus";
import {CardGameState} from "./schema/CardGameState";
import {OnJoinCommand} from "./commands/OnJoinCommand";
import {PlayReadyCommand} from "./commands/PlayReadyCommand";
@ -18,6 +18,7 @@ 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";
export class GeneralRoom extends Room {
@ -176,4 +177,28 @@ export class GeneralRoom extends Room {
}
}
addRobot() {
const sessionId = generateId();
let client = new RobotClient(sessionId, this.state, this.clock, this['onMessageHandlers']);
if (this.reservedSeatTimeouts[sessionId]) {
clearTimeout(this.reservedSeatTimeouts[sessionId]);
delete this.reservedSeatTimeouts[sessionId];
}
// get seat reservation options and clear it
const options = this.reservedSeats[sessionId];
delete this.reservedSeats[sessionId];
this.clients.push(client);
// client.ref.on('message', this.onMessage.bind(this, client));
const reconnection = this.reconnections[sessionId];
if (reconnection) {
reconnection.resolve(client);
}
else {
if (this.onJoin) {
this.onJoin(client, options);
}
delete this.reservedSeats[sessionId];
}
this._events.emit('join', client);
}
}

View File

@ -18,7 +18,7 @@ export class BeginGameCommand extends Command<CardGameState, {}> {
for (let client of this.room.clients) {
this.room.addCard(client.sessionId, singleton(GameEnv).initCardNum, 0);
}
this.state.gameState = GameStateConst.STATE_CHANGE_CARD;
this.state.updateGameState(GameStateConst.STATE_CHANGE_CARD);
// 超时后结束换卡, 进入下一轮
const cardChangeTime = singleton(GameEnv).cardChangeTime;
await this.delay(cardChangeTime*1000);

View File

@ -54,7 +54,7 @@ export class ChangeCardCommand extends Command<CardGameState, { client: Client,
if (finishCount >= this.room.maxClients) {
// TODO:: 根据 this.state.firstPlayer确定先手
// 正式开始游戏, 第一个玩家出牌
this.state.gameState = GameStateConst.STATE_BEGIN_DRAW;
this.state.updateGameState(GameStateConst.STATE_BEGIN_DRAW);
return [new NextTurnCommand()];
}

View File

@ -75,7 +75,7 @@ export class DiscardCommand extends Command<CardGameState, { client: Client, car
} else {
let cardArr: Card[] = [...this.state.cards.values()];
this.room.battleMan.onCardLinkOver(player, cardArr);
this.state.gameState = GameStateConst.STATE_PICK_PET;
this.state.updateGameState(GameStateConst.STATE_PICK_PET);
let self = this;
let time = singleton(GameEnv).playerActTime * 1000 + player.extraTime;
// 开启选随从计时, 计时结束后结束当前轮

View File

@ -76,7 +76,7 @@ export class EatConfirmCommand extends Command<CardGameState, { timeUp: boolean
player.cardSet.delete(id + '');
}
let cardArr: Card[] = [...this.state.cards.values()];
this.state.gameState = GameStateConst.STATE_PICK_PET;
this.state.updateGameState(GameStateConst.STATE_PICK_PET);
let time = singleton(GameEnv).playerActTime * 1000 + player.extraTime;
let self = this;

View File

@ -22,6 +22,9 @@ export class GMCommand extends Command<CardGameState, {client: Client, message:
case 'herohp':
this.updateHeroHp(arr[1]);
break;
case 'addrobot':
this.addRobot(arr[1]);
break;
case 'help':
this.sendHelp(client, arr[1]);
break;
@ -40,6 +43,18 @@ export class GMCommand extends Command<CardGameState, {client: Client, message:
str += '更新英雄血量: herohp:玩家index|血量 例: herohp:0|500 \n';
client.send('notice_msg', str);
}
/**
*
* addrobot:1
* @param msg
*/
addRobot(msg:string) {
let count = msg ? parseInt(msg) : 1;
for (let i = 0; i< count; i++) {
this.room.addRobot();
}
}
/**
*
* changeeffect:玩家index|id

View File

@ -14,7 +14,7 @@ export class GameResultCommand extends Command<CardGameState, {}> {
async execute() {
this.room.bGameResult({});
this.state.gameState = GameStateConst.STATE_GAME_OVER;
this.state.updateGameState(GameStateConst.STATE_GAME_OVER);
this.resetAllState();
//TODO: 启动定时, 时间到后, 踢出没离开且没点重玩的玩家, 并将房间设为非private
let self = this;

View File

@ -13,7 +13,7 @@ import {PlayerStateConst} from "../../constants/PlayerStateConst";
export class NextSubCommand extends Command<CardGameState, {}> {
async execute() {
this.state.gameState = GameStateConst.STATE_BEGIN_EAT;
this.state.updateGameState(GameStateConst.STATE_BEGIN_EAT);
this.state.tmpActionMap.clear();
// 先过滤已死亡玩家, 直接置为已放弃
for (let [key, player] of this.state.players) {

View File

@ -14,7 +14,7 @@ import {GameEnv} from "../../cfg/GameEnv";
export class NextTurnCommand extends Command<CardGameState, {}> {
async execute(){
this.state.gameState = GameStateConst.STATE_BEGIN_DRAW;
this.state.updateGameState(GameStateConst.STATE_BEGIN_DRAW);
const sessionIds = [...this.state.players.keys()];
if (!this.state.currentTurn) {
this.state.round = 0;

View File

@ -17,7 +17,7 @@ export class OnJoinCommand extends Command<CardGameState, {
this.state.players.set(client.sessionId, player);
if (this.state.players.size >= this.room.maxClients) {
this.room.lock().then(() => {});
this.state.gameState = GameStateConst.STATE_WAIT_PREPARE;
this.state.updateGameState(GameStateConst.STATE_WAIT_PREPARE);
}
this.room.bUserJoin(`${client.sessionId}`, {except: client});
}

View File

@ -91,7 +91,7 @@ const comparePlayer = function (room: Room, p0: Player, p1: Player): CompareResu
export class PartResultCommand extends Command<CardGameState, {}> {
execute() : Array<Command> | void {
this.state.gameState = GameStateConst.STATE_ROUND_RESULT;
this.state.updateGameState(GameStateConst.STATE_ROUND_RESULT);
const time = singleton(GameEnv).resultShowTime || 1;
let t0 = [];

View File

@ -29,7 +29,7 @@ export class PlayReadyCommand extends Command<CardGameState, {
player.team = (i ++ / 2) | 0;
}
await this.room.setPrivate(true);
this.room.state.gameState = GameStateConst.CHANGE_HERO;
this.room.state.updateGameState(GameStateConst.CHANGE_HERO);
}
}

View File

@ -9,7 +9,7 @@ import {BeginGameCommand} from "./BeginGameCommand";
export class PrepareCommand extends Command<CardGameState, {}> {
execute() {
this.state.gameState = GameStateConst.DETERMINE_TURN;
this.state.updateGameState(GameStateConst.DETERMINE_TURN);
let time = 3000;
//TODO:: 每人发2张牌, 并比较大小, 确定先手
for (let client of this.room.clients) {

View File

@ -57,4 +57,15 @@ export class CardGameState extends Schema {
*/
restartCount = 0;
updateGameState(val: number) {
let preVal = this.gameState;
this.gameState = val;
this.$listeners.gameState.invoke(val, preVal);
}
updateGameTurn(val: string) {
let preVal = this.currentTurn;
this.currentTurn = val;
this.$listeners.currentTurn.invoke(val, preVal);
}
}