Skip to content

Instantly share code, notes, and snippets.

@labohkip81
Created January 11, 2022 06:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save labohkip81/0150b4941761d36380de86ac36a67aca to your computer and use it in GitHub Desktop.
Save labohkip81/0150b4941761d36380de86ac36a67aca to your computer and use it in GitHub Desktop.
const { Client, Session } = require("./nakama-js.umd");
const { GAME_CONFIG } = require('./Config');
const { Game } = require('./core/checkers');
const { gamebot } = require('./core/gamebot');
const fs = require('fs');
const { ACCOUNT_INFO } = require('./Config');
const BOT_STATE = {
UN_USE: 0,
ACTIVE: 1,
HAD_LOGIN: 2,
JOIN_LOBBY: 3,
PLAYING: 4
};
const OpCodeTurn = 1
const OpCodeWelcome = 2
const OpJoin = 3
const OpStart = 4
const OpCodeTurnError = 5
const OpCodeTerminate = 6
const OpLobby = 7
const OpCodeRematch = 8
const OpCodeDrawRequest = 9
const OpCodeDrawResponse = 10
const OpCodeChat = 11
const OpCodeLeave = 12
const OpCodeReady = 13
const OpCodeGetWelcome = 14
const OpCodePlayerDisconnect = 15
const OpCodeEnterRoom = 16
const OpCodeLeaveRoom = 17
const OpCodeUpdateRoom = 18
const OpCodeForceDraw = 19
const OpCodeLoadGameError = 20
const CELL_VAL_SERVER_SIDE = {
Empty: 0,
MenWhite: 1,
MenBlack: 2,
KingWhite: 3,
KingBlack: 4,
}
var Bot = class {
constructor(phone, password, assignTable) {
this.email = "+254" + phone + "@chezaent.co.ke";
this.phone = phone;
this.password = password;
this.session = "";
this.nakamaAuthToken = "";
this.state = BOT_STATE.ACTIVE;
this.id = -1; //index in bots array
this.client = new Client(GAME_CONFIG.HTTP_KEY, GAME_CONFIG.SERVER_IP, GAME_CONFIG.PORT);
this.client.ssl = false;
this.account = null;
this.wallet = { Coins: 0, Gold: 0, V: 1 };
//--info for play game
this.gameId = 0;
this.socket = null;
this.lobby_id = null;
this.match_id = null;
this.ticket = null;
this.gameConfig = null;//table.model.gameInfo <=> table.gameInfo
this.selectedGame = null;
this.bot_id = null;//identify of bot in database
//--
this.board = null;
this.CurrentPlayerId = null;
this.turnend_time = null;
this.color = null;
this.results = null;
//--opponent
this.opponent_color = null;
this.opponent_id = null;
this.opponent_account = null;
//--ai
this.game = new Game();
this.minmaxBot = null;
this.assignTable = assignTable;
//--
this.previousPiece = 0;
this.countPiece = 0;
//--
this.isForceStop = false;
//--skip force to lobby when player rematch success
this.alReadyRematch = false;
this.waitTimeInLobby = 0;
}
reset() {
this.email = "";
this.phone = "";
this.password = "";
this.session = "";
this.nakamaAuthToken = "";
this.state = BOT_STATE.UN_USE;
this.id = -1;
this.account = null;
this.wallet = { Coins: 0, Gold: 0, V: 1 };
//--info for play game
this.gameId = 0;
this.socket = null;
this.lobby_id = null;
this.match_id = null;
this.ticket = null;
this.gameConfig = null;
this.previousPiece = 0;
this.countPiece = 0;
//--
this.isForceStop = false;
}
async authenticateEmail() {
let message = { email: this.email, password: this.password, create: false };
return await this.client.authenticateEmail(message);
}
connect(session) {
const secure = false; // enable if server is run with an SSL certificate
const trace = true;
this.session = session
if (!this.socket) {
this.socket = this.client.createSocket(secure, trace);
this.socket.ondisconnect = this.onDisconnect.bind(this);
this.socket.onchannelmessage = this.onChannelMessage.bind(this);
this.socket.onmatchdata = this.onMatchData.bind(this);
this.socket.onnotification = this.onNotification.bind(this);
this.socket.onstreamdata = this.onStreamData.bind(this);
this.socket.onmatchmakermatched = this.onMatchmakerMatched.bind(this);
this.socket.onstatuspresence = this.onStatusPresence.bind(this)
this.socket.onerror = this.onSocketError.bind(this);
}
return this.socket.connect(this.session)
}
disconnect() {
if (this.socket != null)
this.socket.disconnect(true);
}
//--rpc event ---------------------------------------------------------------
onSocketError(evt) {
console.log(">>> rpc: onSocketError", evt);
}
onDisconnect(e) {
console.log(">>> rpc: onDisconnect");
}
onChannelMessage(message) {
console.log(`>>> rpc: onChannelMessage: user chat with bot ${message}`);
}
onMatchData(result) {
if (this.isForceStop) {
return;
}
let content = result.data;
//console.log(`>>> rpc: onMatchData ${JSON.stringify(result)}`);
if (result.match_id != this.match_id && result.match_id != this.lobby_id) {
//console.log(">>> rpc: Incorrect match id:" + result.match_id)
return
}
switch (result.op_code) {
case OpCodeGetWelcome:
case OpCodeWelcome://[step]14. welcome signal
//console.log(">>> rpc: Received welcome message:");// + JSON.stringify(content));
//player ready -> wait opponent ready
this.board = content.Board;
for (const p of content.Players) {
if (p.Id == this.bot_id) {
this.color = p.Color;
} else {
this.loadGame(p.Id, p.Color);
}
}
break;
case OpCodeTurn://[step]17. oponent has move chess done
//console.log(">>> rpc: Received turn message:");// + JSON.stringify(content) );
this.board = content.Board;
if (content.CurrentUserId != "") {
this.CurrentPlayerId = content.CurrentUserId;
this.turnend_time = new Date().getTime() + content.TimeOut;
}
this.results = content.Results;
if (this.results && this.results.length > 1) {//end game
let obj111 = null;
let obj222 = null;
let timeall = new Date().getTime();
for (const obj2 of this.results) {
if (obj2.PlayerId == this.bot_id) {
obj111 = {
"PlayerId": obj2.PlayerId,
"Coins": obj2.Coins,
"IsWinner": obj2.IsWinner,
"IsDraw": obj2.IsDraw,
"MissTurnUser": obj2.MissTurnUser,
"username": this.account.user.username,
"display_name": this.account.user.display_name,
"isBot": true,
"time": timeall
}
} else {
let username2 = '';
let display_name2 = '';
if (this.opponent_account && this.opponent_account.username) {
username2 = this.opponent_account.username;
} else {
console.log(`Result username2 error`);
}
if (this.opponent_account && this.opponent_account.display_name) {
display_name2 = this.opponent_account.display_name;
} else {
console.log(`display_name2 error`);
}
obj222 = {
"PlayerId": obj2.PlayerId,
"Coins": obj2.Coins,
"IsWinner": obj2.IsWinner,
"IsDraw": obj2.IsDraw,
"MissTurnUser": obj2.MissTurnUser,
"username": username2,
"display_name": display_name2,
"isBot": false,
"time": timeall
}
}
}
this.writeData(obj111, obj222);
//--update balance
var TIMEOUT_OUT_LOBBY = setTimeout(() => {
clearTimeout(TIMEOUT_OUT_LOBBY);
if(this.alReadyRematch){
this.alReadyRematch = false;
return;
}
this.client.getAccount(this.session).then((account) => {
this.account = account;
this.wallet.Coins = JSON.parse(account.wallet).Coins;
//console.log(`>>> rpc: reload coins: ${this.wallet.Coins}`);
if (this.selectedGame != null && (this.wallet.Coins < this.selectedGame.BuyIn)) {//not enough coin for current room -> kickout
if (this.selectedGame.Id != null) {
this.leaveRoom(this.selectedGame.Id - 1);
}
this.leaveLobby();
} else {
this.selectedGame = null;
this.state = BOT_STATE.JOIN_LOBBY;
}
}).catch(err => {
console.log(`getAccount2 err: ${err}`);
});
}, 10000);//10s auto leave playing to lobby
} else {
this.timeout = content.TimeOut;
this.game.board.applyNewMatrix(this.board.Cells);
this.thinking(false);
this.checkCanDraw();
}
break;
case OpJoin://[step]15. opponent ready -> load game scene
//console.log(">>> rpc: user joined:" + JSON.stringify(content) );
let playerVO = content.Player;
this.loadGame(playerVO.Id, playerVO.Color);
//color 1: black, color 2: white
//* if ai join lobby after player -> can't recive join message -> get player info from welcome */
break;
case OpStart://[step]16. wait signal start
//console.log(">>> rpc: game start:");// + JSON.stringify(content) );
this.board = content.Board;
this.CurrentPlayerId = content.CurrentUserId;//xac dinh player nao di dau tien & countdown tuong ung
this.turnend_time = new Date().getTime() + content.TimeOut;
this.color = content.Color;
this.results = null;
//--minmaxbot
//this.minmaxBot = new gamebot(this.game, this.color, 'minmax', 'piece_and_board_pov', '_sum_of_dist', 3);
this.minmaxBot = new gamebot(this.game, this.color, 'minmax', 'piece_and_board_pov', '_sum_of_dist', 5);
this.game.board.applyNewMatrix(this.board.Cells);
this.game.board.setDirection(this.color);
this.game.turn = this.color;
//--
this.playerReady();//[step]17. player ready
this.thinking(true);
break;
case OpCodeTurnError:
//console.log("turn error:" + JSON.stringify(content) );
this.thinking(2);
break;
case OpCodeTerminate:
//console.log("game terminate:" + JSON.stringify(content) );
break;
case OpLobby:
//console.log("lobby update:" + JSON.stringify(content) );
//console.log(">>> rpc: OpLobby: number player online: " + content.Online);
this.playersOnline = content.Online;
// this.node.emit("lobby",result);
break;
case OpCodeRematch:
console.log("Rematch");
this.client.getAccount(this.session).then((account) => {
this.account = account;
this.wallet.Coins = JSON.parse(account.wallet).Coins;
//console.log(`>>> rpc: reload coins before start: ${this.wallet.Coins}`);
//--waiting 5s
if (this.selectedGame != null && this.selectedGame.BuyIn != null && this.wallet.Coins >= this.selectedGame.BuyIn) {
//console.log(`>>><<<bot enough coin to rematch bot coin: ${this.wallet.Coins} > table coin ${this.selectedGame.BuyIn}`);
this.alReadyRematch = true;
this.rematch();
} else {
console.log(`>>><<<bot not enough coin to rematch bot coin:`);
if (this.selectedGame != null && this.selectedGame.Id != null) {
this.leaveRoom(this.selectedGame.Id - 1);//leave room
}
this.leaveLobby();
}
}).catch(err => {
console.log(`getAccount3 err: ${err}`);
});
// this.results = null
break;
case OpCodeDrawRequest:
//console.log("Draw Request")
this.drawResponse(true);
break;
case OpCodeDrawResponse:
//console.log("Draw Response")
//this.node.emit("drawresponse",result);
this.drawResponse(true);
break;
case OpCodeChat:
//console.log("Draw Response")
//this.node.emit("chat",result);
break;
case OpCodeLeave:
//console.log("Opponent leave match")
// if(AppModel.instance.opponent != null){
// AppModel.instance.opponent.leave_game = true;
// this.node.emit("left");
// }
this.leaveLobby();
break;
case OpCodeReady:
console.log(">>> rpc Opponent ready: doi thu da ready -> co the play")
// this.node.emit("ready",result);
break;
case OpCodePlayerDisconnect:
console.log("Opponent disconnected")
// this.node.emit("opponent-disconnected",result);
this.leaveLobby();
break;
case OpCodeUpdateRoom://tra ve trang thai room trong lobby, room co bao nhieu user
//[step]9. listen all table update
//console.log(">>> rpc: Update Rooms"+JSON.stringify(content));
if (usersAtRooms.length > 8) {
if (usersAtRooms[this.assignTable] < content.UsersAtRooms[this.assignTable]) {//su thay doi nhieu hon
//--khi room co su thay doi ve so luong
//console.log(`### ${this.account.user.username}: state = ${this.state}`);
if (this.state == BOT_STATE.JOIN_LOBBY) {
this.client.getAccount(this.session).then((account) => {
this.account = account;
this.wallet.Coins = JSON.parse(account.wallet).Coins;
//console.log(`>>> rpc: reload coins before start: ${this.wallet.Coins}`);
//--waiting 5s
var TIMEOUT_JOIN_ROOM = setTimeout(() => {
clearTimeout(TIMEOUT_JOIN_ROOM);
let tableInfo = this.gameConfig.Tables[this.assignTable];//test room free so 8
if (this.wallet.Coins >= tableInfo.BuyIn) {
//console.log(`>>><<<bot enough coin to join bot coin: ${this.wallet.Coins} > table coin ${tableInfo.BuyIn}`);
if (tableInfo.BuyIn > 0) {
this.openTable(tableInfo.Id);
}
this.selectedGame = tableInfo;
this.state = BOT_STATE.PLAYING;
console.log(`***[bot] add bot to BOT_STATE.PLAYING room ${this.assignTable}: ${this.phone}`);
this.joinTable(tableInfo);//[step]10. jointo table have 1, 3, 5, ... player
} else {
//console.log(`>>><<<bot not enough coin to join bot coin: ${this.wallet.Coins} < table coin ${tableInfo.BuyIn}`);
this.leaveLobby();
}
}, 2000);
}).catch(err => {
console.log(`getAccount4 err: ${err}`);
});
}
}
usersAtRooms[this.assignTable] = content.UsersAtRooms[this.assignTable];
}
/*
join vao room hay kg -> khi so luong player la so chan -> kg can join, so le -> join
*/
break;
case OpCodeForceDraw:
//console.log(">>> rpc: OpCodeForceDraw");//user want draw -> force draw
this.drawResponse(true);
this.leaveLobby();
break;
case OpCodeLoadGameError:
if (this.selectedGame != null && this.selectedGame.Id != null) {
this.leaveRoom(this.selectedGame.Id - 1);
}
this.leaveLobby();
//console.log(">>> rpc: OpCodeLoadGameError");
break;
default:
//console.log("Unhandled message " + JSON.stringify(result));
}
}
turn(move) {
this.socket.send({ match_data_send: { match_id: this.match_id, op_code: OpCodeTurn, data: { Move: move } } });
}
rematch() {
this.socket.send({ match_data_send: { match_id: this.match_id, op_code: OpCodeRematch, data: {} } });
}
async getUser(user_id) {
return await this.client.getUsers(this.session, [user_id]);
}
checkCanDraw() {
let piece = 0;
for (const row of this.board.Cells) {
for (let col of row) {
if (col != 0) {
piece++;
}
}
}
if (this.previousPiece == piece) {
this.countPiece++;
} else {
this.previousPiece = piece;
this.countPiece = 0;
}
//--
//console.log(`>>><<< forcedraw: ${this.countPiece}`);
if (this.countPiece == 181) {
this.forceDrawApply();
this.previousPiece = 0;
this.countPiece = 0;
}
}
loadGame(opponent_id, opponent_color) {//[step]16. get opponent account
let self = this;
this.opponent_color = opponent_color;
this.opponent_id = opponent_id;
this.getUser(this.opponent_id).then((message) => {
if (message && message.users && message.users.length > 0) {
self.opponent_account = message.users[0];
} else {
console.log(`Opponent acc error: ${JSON.stringify(message)}`);
self.leaveLobby();
}
}).catch(err => {
console.log(`getAccount5 err: ${err}`);
this.leaveLobby();
});
}
onNotification(notification) {
//console.log("Received notification " + JSON.stringify(notification));
}
onStreamData(data) {
if (data.stream.mode == 10) {//so luong player
//console.log(">>> rpc:onStreamData: Received lobby message:" + data.data);
this.node.emit('lobby', JSON.parse(data.data));
this.playersOnline = JSON.parse(data.data).Online
}
else if (data.stream.mode == 100) { //debug server
//console.log(">>> rpc:onStreamData: Received Server Debug message:" + data.data);
}
}
onMatchmakerMatched(matched) {
//console.log("Received MatchmakerMatched message:" + JSON.stringify(matched));
//console.log("Matched opponents:" + matched.users.toString());
//--check user matched co phai 2 bot, neu la 2 bots -> remove
for (const obj of ACCOUNT_INFO) {
for (const obj2 of matched.users) {
if (this.account.user.id == obj2.presence.user_id) {
continue;
} else {//player khac
if (obj2.presence.user_id == obj.playerId) { //neu la bot
if (this.selectedGame != null && this.selectedGame.Id != null) {
this.leaveRoom(this.selectedGame.Id - 1);//leave room
}
this.leaveLobby();
console.log(`*** Bot leave room, 2 bots can't join into 1 room`);
return;
}
}
}
}
//--tim duoc user de match
const message = {
match_join: {
match_id: matched.match_id
}
};
this.ticket = null;
this.match_id = matched.match_id
//console.log("Opponent found, joining to match, ticket:" + matched.ticket);
this.socket.send(message);
}
onStatusPresence(statuspresence) {
if (statuspresence.leaves != undefined) {
statuspresence.leaves.forEach((leave) => {
//console.log("User %o no longer has status %o", leave.user_id, leave.status);
if (leave.user_id != this.user_id) {
//console.log(">>> rpc: onStatusPresence: friend_disconnect")
}
});
} else {
statuspresence.joins.forEach((join) => {
//console.log("User %o now has status %o", join.user_id, join.status);
});
}
}
//--call action
online() {
this.socket.send({ status_update: { status: "Online" } });
}
async updateToken() {
let self = this;
return await this.socket.send({ rpc: { id: "token_update", payload: JSON.stringify({ Username: this.session.username }) } }).then((message) => {
let response = JSON.parse(message.rpc.payload);
if (response.Success) {
self.session = Session.restore(response.Token);
//console.log("UPDATE TOKEN = ",self.session);
}
return "";
});
}
async getLobbyId() {
return await this.socket.send({ rpc: { id: "get_lobby_id", payload: "" } });
}
async joinTo() {//join to lobby
const message = {
match_join: {
match_id: this.lobby_id
}
};
return await this.socket.send(message);
}
joinTable(gameInfo) {
const message = {
matchmaker_add: {
min_count: 2,
max_count: 2,
query: "+properties.game_type:" + GAME_CONFIG.gameType + " +properties.table:" + gameInfo.Id + " +properties.bet:" + gameInfo.BuyIn,
string_properties: {
game_type: GAME_CONFIG.gameType,
table: gameInfo.Id + "",
bet: "" + gameInfo.BuyIn
},
}
};
this.socket.send(message).then((event) => {
this.ticket = event.matchmaker_ticket.ticket;
this.enterRoom(gameInfo.Id - 1);//[step]11. enter room
}).catch((error) => {
console.log(`joinTable error${error}`);
this.selectedGame = null;
this.state = BOT_STATE.JOIN_LOBBY;
});
}
async ping() {
return await this.client.rpc(this.session, "ping", { data: 'a' });
}
enterRoom(id) {
this.socket.send({ match_data_send: { match_id: this.lobby_id, op_code: OpCodeEnterRoom, data: { RoomId: id } } });
}
leaveLobby() {
if(this.state != BOT_STATE.HAD_LOGIN){
this.socket.send({ match_leave: { match_id: this.lobby_id } });
this.selectedGame = null;
this.state = BOT_STATE.HAD_LOGIN;
console.log(`***[bot] remove bot to login: ${this.phone}`);
}
}
leaveRoom(id) {
this.socket.send({ match_data_send: { match_id: this.lobby_id, op_code: OpCodeLeaveRoom, data: { RoomId: id } } });
}
forceDrawApply() {
this.socket.send({ match_data_send: { match_id: this.match_id, op_code: OpCodeForceDraw, data: {} } });
}
drawRequest() {
this.socket.send({ match_data_send: { match_id: this.match_id, op_code: OpCodeDrawRequest, data: {} } });
}
drawResponse(yesOrNo) {
let val = this.minmaxBot._piece2val(this.minmaxBot.game.board);
if (val > 0) {
yesOrNo = false;
} else {
yesOrNo = true;
}
this.socket.send({ match_data_send: { match_id: this.match_id, op_code: OpCodeDrawResponse, data: { Yes: yesOrNo } } });
}
playerReady() {
this.socket.send({ match_data_send: { match_id: this.match_id, op_code: OpCodeReady, data: {} } });
}
openTable(id) {
//console.log('open table rpc',id);
return this.client.rpc(this.session, "open_table", { TableId: id, UserId: this.session.user_id }).then((message) => {
//console.log('open table response',message);
if (!message.payload.Success) {
if (message.payload.Code == 0) {
//consoloe.log(">>>[openTable] not_enough_money" + this.account.user.id);
}
if (message.payload.Code != 1 && message.payload.Code != 6) {
//consoloe.log(">>>[openTable] wallet_update" + this.account.user.id);
}
return;
}
/*----------------------------------------------------------------
this.node.emit('open_table',message.payload);
server tru tien 2 player va tra ve info
payload: {
Success: true, //neu fail -> thong bao voi user la false
Message: 'Table unlocked', //-> hien thi animation table da unlock
TableOpen: { Id: 1, OpenTime: 1632223340, NeedRefund: false },
Wallet: { Coins: 0, Gold: 0, V: 1 },
Code: 0
}
cap nhat lai balance hien tai cua player sau khi join room
this.node.emit("wallet_update",message.payload.Wallet);
//--bot no need leave game
if (this.gameId != 0){
LiveCasinoApp.instance.leaveRoom(this.gameId);
}
----------------------------------------------------------------*/
}).catch((error) => {
this.selectedGame = null;
this.state = BOT_STATE.JOIN_LOBBY;
console.log(`openTable error ${error}`);
});
}
//--------------------------------------logic thinking of bot ---------------------------------------------
thinking(isfirst) {
//console.log(`>>><<<this.CurrentPlayerId: ${this.CurrentPlayerId}, this.bot_id: ${this.bot_id}, this.opponent_id=${this.opponent_id}`);
//console.log(`>>><<<this.color: ${this.color}, this.opponent_color: ${this.opponent_color}`);
if (this.CurrentPlayerId != this.bot_id) {
return;
} //not bot turn
let currentTime = new Date().getTime();
const delayTime = isfirst ? 15000 : 1000;
if (currentTime < this.turnend_time) {//countdown end
var MOVE_TIMEOUT = setTimeout(() => {
let move = this.minmaxBot._minmax_stepR(this.game.board);
if (move != null) {
// console.log(">>>move: " + JSON.stringify(move));
// this.game.board.print_matrix();
this.turn(move);
}
clearTimeout(MOVE_TIMEOUT);
}, delayTime);
}
}
checkFileExist(obj1, obj2) {
let d = new Date();
let date = `${d.getFullYear()}_${d.getMonth()}_${d.getDate()}`;
if (fs.existsSync(`./save_data/${date}.json`)) {
} else {
}
}
writeData(obj1, obj2) {
let d = new Date();
let date = `${d.getFullYear()}_${d.getMonth()}_${d.getDate()}`;
//--check file exists
try {
if (fs.existsSync(`./save_data/${date}.json`)) {
fs.readFile(`./save_data/${date}.json`, "utf8", (err, jsonString) => {
if (err) {
console.log("File read failed:", err);
jsonString = "[]";
}
//console.log("File data:", jsonString);
try {
let players = JSON.parse(jsonString);
players.push(obj1);
players.push(obj2);
fs.writeFile(`./save_data/${date}.json`, JSON.stringify(players), err => {
if (err) {
console.log("Error writing file:", err);
}
});
} catch (err) {
console.log(`writeData error: ${err}`);
}
});
} else {
try {
let players = [];
players.push(obj1);
players.push(obj2);
fs.writeFile(`./save_data/${date}.json`, JSON.stringify(players), err => {
if (err) {
console.log("Error writing file:", err);
}
});
} catch (err) {
console.log(`writeData error: ${err}`);
}
}
} catch (err) {
console.error(`existsSync error: ${err}`)
}
}
};
function CreateBot(phone, password, assignTable) {
let i = 0;
let bIsNeedCreateNew = true;
let length = bots.length;
for (; i < length; i++) {
let bot = bots[i];
if (bot.state == BOT_STATE.UN_USE) {
bot.state = BOT_STATE.ACTIVE;
bot.phone = phone;
bot.email = "+254" + phone + "@chezaent.co.ke";
bot.password = password;
bot.assignTable = assignTable;
bIsNeedCreateNew = false;
break;
}
}
if (bIsNeedCreateNew) {
let bot = new Bot(phone, password, assignTable);
bot.phone = phone;
bot.password = password;
bots.push(bot);
}
return i;
}
function stopAllBot() {
for (let obj of bots) {
try {
if (obj != null)
{
obj.disconnect();
obj.reset();
delete obj;
obj = null;
}
} catch (err) {
console.error(`stopAllBot bot error: ${err}`)
}
}
bots = [];
}
var bots = [];
var usersAtRooms = []; //trang thai tat ca cac room
module.exports = {
bots: bots,
usersAtRooms: usersAtRooms,
BOT_STATE: BOT_STATE,
Bot: Bot,
CreateBot: CreateBot,
stopAllBot: stopAllBot
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment