增加服务端机器人
This commit is contained in:
parent
fa949dd70c
commit
c7c8ee38c0
@ -5,3 +5,5 @@ export const debugRoom = debug('jc:room');
|
|||||||
export const error = debug('jc:error');
|
export const error = debug('jc:error');
|
||||||
|
|
||||||
export const msgLog = debug('jc:msg');
|
export const msgLog = debug('jc:msg');
|
||||||
|
|
||||||
|
export const robotLog = debug('jc:robot');
|
||||||
|
2
src/global.d.ts
vendored
2
src/global.d.ts
vendored
@ -206,6 +206,8 @@ declare module "colyseus" {
|
|||||||
*/
|
*/
|
||||||
addScheduleTime(millisecond: number, reason?: string): void;
|
addScheduleTime(millisecond: number, reason?: string): void;
|
||||||
|
|
||||||
|
addRobot():void;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ const server = http.createServer(app);
|
|||||||
const gameServer = new Server({
|
const gameServer = new Server({
|
||||||
server,
|
server,
|
||||||
// driver: new MongooseDriver('mongodb://127.0.0.1/card-development'),
|
// 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
|
// register your room handlers
|
||||||
|
135
src/robot/RobotClient.ts
Normal file
135
src/robot/RobotClient.ts
Normal 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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import {Client, Room} from "colyseus";
|
import {Client, generateId, Room} from "colyseus";
|
||||||
import {CardGameState} from "./schema/CardGameState";
|
import {CardGameState} from "./schema/CardGameState";
|
||||||
import {OnJoinCommand} from "./commands/OnJoinCommand";
|
import {OnJoinCommand} from "./commands/OnJoinCommand";
|
||||||
import {PlayReadyCommand} from "./commands/PlayReadyCommand";
|
import {PlayReadyCommand} from "./commands/PlayReadyCommand";
|
||||||
@ -18,6 +18,7 @@ import {Player} from "./schema/Player";
|
|||||||
import {GMCommand} from "./commands/GMCommand";
|
import {GMCommand} from "./commands/GMCommand";
|
||||||
import {GameStateConst} from "../constants/GameStateConst";
|
import {GameStateConst} from "../constants/GameStateConst";
|
||||||
import {GameRestartCommand} from "./commands/GameRestartCommand";
|
import {GameRestartCommand} from "./commands/GameRestartCommand";
|
||||||
|
import {RobotClient} from "../robot/RobotClient";
|
||||||
|
|
||||||
|
|
||||||
export class GeneralRoom extends Room {
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ export class BeginGameCommand extends Command<CardGameState, {}> {
|
|||||||
for (let client of this.room.clients) {
|
for (let client of this.room.clients) {
|
||||||
this.room.addCard(client.sessionId, singleton(GameEnv).initCardNum, 0);
|
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;
|
const cardChangeTime = singleton(GameEnv).cardChangeTime;
|
||||||
await this.delay(cardChangeTime*1000);
|
await this.delay(cardChangeTime*1000);
|
||||||
|
@ -54,7 +54,7 @@ export class ChangeCardCommand extends Command<CardGameState, { client: Client,
|
|||||||
if (finishCount >= this.room.maxClients) {
|
if (finishCount >= this.room.maxClients) {
|
||||||
// TODO:: 根据 this.state.firstPlayer确定先手
|
// TODO:: 根据 this.state.firstPlayer确定先手
|
||||||
// 正式开始游戏, 第一个玩家出牌
|
// 正式开始游戏, 第一个玩家出牌
|
||||||
this.state.gameState = GameStateConst.STATE_BEGIN_DRAW;
|
this.state.updateGameState(GameStateConst.STATE_BEGIN_DRAW);
|
||||||
return [new NextTurnCommand()];
|
return [new NextTurnCommand()];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ export class DiscardCommand extends Command<CardGameState, { client: Client, car
|
|||||||
} else {
|
} else {
|
||||||
let cardArr: Card[] = [...this.state.cards.values()];
|
let cardArr: Card[] = [...this.state.cards.values()];
|
||||||
this.room.battleMan.onCardLinkOver(player, cardArr);
|
this.room.battleMan.onCardLinkOver(player, cardArr);
|
||||||
this.state.gameState = GameStateConst.STATE_PICK_PET;
|
this.state.updateGameState(GameStateConst.STATE_PICK_PET);
|
||||||
let self = this;
|
let self = this;
|
||||||
let time = singleton(GameEnv).playerActTime * 1000 + player.extraTime;
|
let time = singleton(GameEnv).playerActTime * 1000 + player.extraTime;
|
||||||
// 开启选随从计时, 计时结束后结束当前轮
|
// 开启选随从计时, 计时结束后结束当前轮
|
||||||
|
@ -76,7 +76,7 @@ export class EatConfirmCommand extends Command<CardGameState, { timeUp: boolean
|
|||||||
player.cardSet.delete(id + '');
|
player.cardSet.delete(id + '');
|
||||||
}
|
}
|
||||||
let cardArr: Card[] = [...this.state.cards.values()];
|
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 time = singleton(GameEnv).playerActTime * 1000 + player.extraTime;
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
|
@ -22,6 +22,9 @@ export class GMCommand extends Command<CardGameState, {client: Client, message:
|
|||||||
case 'herohp':
|
case 'herohp':
|
||||||
this.updateHeroHp(arr[1]);
|
this.updateHeroHp(arr[1]);
|
||||||
break;
|
break;
|
||||||
|
case 'addrobot':
|
||||||
|
this.addRobot(arr[1]);
|
||||||
|
break;
|
||||||
case 'help':
|
case 'help':
|
||||||
this.sendHelp(client, arr[1]);
|
this.sendHelp(client, arr[1]);
|
||||||
break;
|
break;
|
||||||
@ -40,6 +43,18 @@ export class GMCommand extends Command<CardGameState, {client: Client, message:
|
|||||||
str += '更新英雄血量: herohp:玩家index|血量 例: herohp:0|500 \n';
|
str += '更新英雄血量: herohp:玩家index|血量 例: herohp:0|500 \n';
|
||||||
client.send('notice_msg', str);
|
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
|
* changeeffect:玩家index|效果卡id
|
||||||
|
@ -14,7 +14,7 @@ export class GameResultCommand extends Command<CardGameState, {}> {
|
|||||||
|
|
||||||
async execute() {
|
async execute() {
|
||||||
this.room.bGameResult({});
|
this.room.bGameResult({});
|
||||||
this.state.gameState = GameStateConst.STATE_GAME_OVER;
|
this.state.updateGameState(GameStateConst.STATE_GAME_OVER);
|
||||||
this.resetAllState();
|
this.resetAllState();
|
||||||
//TODO: 启动定时, 时间到后, 踢出没离开且没点重玩的玩家, 并将房间设为非private
|
//TODO: 启动定时, 时间到后, 踢出没离开且没点重玩的玩家, 并将房间设为非private
|
||||||
let self = this;
|
let self = this;
|
||||||
|
@ -13,7 +13,7 @@ import {PlayerStateConst} from "../../constants/PlayerStateConst";
|
|||||||
export class NextSubCommand extends Command<CardGameState, {}> {
|
export class NextSubCommand extends Command<CardGameState, {}> {
|
||||||
|
|
||||||
async execute() {
|
async execute() {
|
||||||
this.state.gameState = GameStateConst.STATE_BEGIN_EAT;
|
this.state.updateGameState(GameStateConst.STATE_BEGIN_EAT);
|
||||||
this.state.tmpActionMap.clear();
|
this.state.tmpActionMap.clear();
|
||||||
// 先过滤已死亡玩家, 直接置为已放弃
|
// 先过滤已死亡玩家, 直接置为已放弃
|
||||||
for (let [key, player] of this.state.players) {
|
for (let [key, player] of this.state.players) {
|
||||||
|
@ -14,7 +14,7 @@ import {GameEnv} from "../../cfg/GameEnv";
|
|||||||
export class NextTurnCommand extends Command<CardGameState, {}> {
|
export class NextTurnCommand extends Command<CardGameState, {}> {
|
||||||
|
|
||||||
async execute(){
|
async execute(){
|
||||||
this.state.gameState = GameStateConst.STATE_BEGIN_DRAW;
|
this.state.updateGameState(GameStateConst.STATE_BEGIN_DRAW);
|
||||||
const sessionIds = [...this.state.players.keys()];
|
const sessionIds = [...this.state.players.keys()];
|
||||||
if (!this.state.currentTurn) {
|
if (!this.state.currentTurn) {
|
||||||
this.state.round = 0;
|
this.state.round = 0;
|
||||||
|
@ -17,7 +17,7 @@ export class OnJoinCommand extends Command<CardGameState, {
|
|||||||
this.state.players.set(client.sessionId, player);
|
this.state.players.set(client.sessionId, player);
|
||||||
if (this.state.players.size >= this.room.maxClients) {
|
if (this.state.players.size >= this.room.maxClients) {
|
||||||
this.room.lock().then(() => {});
|
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});
|
this.room.bUserJoin(`${client.sessionId}`, {except: client});
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ const comparePlayer = function (room: Room, p0: Player, p1: Player): CompareResu
|
|||||||
export class PartResultCommand extends Command<CardGameState, {}> {
|
export class PartResultCommand extends Command<CardGameState, {}> {
|
||||||
|
|
||||||
execute() : Array<Command> | void {
|
execute() : Array<Command> | void {
|
||||||
this.state.gameState = GameStateConst.STATE_ROUND_RESULT;
|
this.state.updateGameState(GameStateConst.STATE_ROUND_RESULT);
|
||||||
const time = singleton(GameEnv).resultShowTime || 1;
|
const time = singleton(GameEnv).resultShowTime || 1;
|
||||||
|
|
||||||
let t0 = [];
|
let t0 = [];
|
||||||
|
@ -29,7 +29,7 @@ export class PlayReadyCommand extends Command<CardGameState, {
|
|||||||
player.team = (i ++ / 2) | 0;
|
player.team = (i ++ / 2) | 0;
|
||||||
}
|
}
|
||||||
await this.room.setPrivate(true);
|
await this.room.setPrivate(true);
|
||||||
this.room.state.gameState = GameStateConst.CHANGE_HERO;
|
this.room.state.updateGameState(GameStateConst.CHANGE_HERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import {BeginGameCommand} from "./BeginGameCommand";
|
|||||||
export class PrepareCommand extends Command<CardGameState, {}> {
|
export class PrepareCommand extends Command<CardGameState, {}> {
|
||||||
|
|
||||||
execute() {
|
execute() {
|
||||||
this.state.gameState = GameStateConst.DETERMINE_TURN;
|
this.state.updateGameState(GameStateConst.DETERMINE_TURN);
|
||||||
let time = 3000;
|
let time = 3000;
|
||||||
//TODO:: 每人发2张牌, 并比较大小, 确定先手
|
//TODO:: 每人发2张牌, 并比较大小, 确定先手
|
||||||
for (let client of this.room.clients) {
|
for (let client of this.room.clients) {
|
||||||
|
@ -57,4 +57,15 @@ export class CardGameState extends Schema {
|
|||||||
*/
|
*/
|
||||||
restartCount = 0;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user