///
///
///
var engine = require('engine.io');
var engineRooms = require('engine.io-rooms');
var http = require('http');
var urlParser = require('url');
class Server> {
public static API_ROOT_URL = 'http://api.jok.io/';
public StartTime;
public GameTables: TGameTable[] = [];
public UsersCount = 0;
private io;
constructor(private port = process.env.PORT || 9003, private GameTableClass?, private GamePlayerClass?) {
var server = http.createServer(this.httpHandler.bind(this));
this.io = engine.attach(server);
// added channels support
this.io = engineRooms(this.io);
Helper.IO = this.io;
this.StartTime = Date.now();
this.io.on('connection', this.onConnectionOpen.bind(this));
server.listen(this.port, () => {
console.log('server listening at port:', this.port);
});
Helper.SendMail('status-update@jok.io', 'jok-realtime-server started', 'StartTime: ' + new Date());
}
// processing http request
httpHandler(req, res) {
var urlInfo = urlParser.parse(req.url, true);
switch (urlInfo.pathname) {
case '/stats':
{
res.end(JSON.stringify({
ConnectionsCount: this.io.clientsCount,
UsersCount: this.UsersCount,
TablesCount: this.GameTables.length,
Uptime: (Date.now() - this.StartTime) / (1000 * 60) + ' min.'
}));
}
break;
default:
{
res.end('Hi, Bye');
}
break;
}
}
// processing commands
onConnectionOpen(socket) {
var sid = socket.request.query.token;
var gameid = socket.request.query.gameid;
var gamemode = socket.request.query.gamemode;
var channel = socket.request.query.channel;
var ipaddress = socket.request.headers["x-forwarded-for"];
// channel-ის შემთხვევაში case sensitive არ არის
if (!channel)
channel = '';
channel = channel.toLowerCase();
// ip მისამართის გაგება, გათვალისწინებულია proxy-დან მოსული შეტყობინებებიც (heroku-ს შემთხვევა)
if (ipaddress) {
var list = ipaddress.split(",");
ipaddress = list[list.length - 1];
} else {
ipaddress = socket.request.connection.remoteAddress;
}
// აუცილებელ ველებზე შემოწმება
if (!sid || !ipaddress || !gameid) return;
var userid;
var disconnected;
var gameTable: TGameTable;
var url = Server.API_ROOT_URL + 'User/InfoBySID?sid=' + sid + '&ipaddress=' + ipaddress + '&gameid=' + gameid;
// მომხმარებლის ინფორმაციის აღება
Helper.HttpGet(url, (isSuccess, data) => {
if (!isSuccess || !data.UserID || disconnected) return;
// ე.წ. ავტორიზაცია კომექშენის
userid = data.UserID;
// ავტორიზირებული მომხმარებლების რაოდენობის გაზრდა
this.UsersCount++;
// მომხმარებლის გაწევრიანება საკუთარ ჩანელში, რათა შემდეგ მოხდეს ინფორმაციის გაგზავნა
var userChannel = 'User' + userid;
var oldConnections = Helper.ChannelSockets(userChannel);
socket.join(userChannel);
// ძველი კონექშენების გათიშვა
if (oldConnections)
oldConnections.forEach(c => c.close());
// მაგიდაზე შეყვანა ეგრევე
gameTable = this.findTable(data, channel, gamemode);
if (!gameTable) {
console.log('GameTable not found, it must not happen. Passed parameters:', channel, gamemode);
return;
}
gameTable.join(data, ipaddress, channel, gamemode);
// ავტორიზაციის შესახებ ინფორმაციის გაგზავნა კლიენტთან, რათა გააგრძელოს პროცესი
socket.send(Helper.BuildCommand('UserAuthenticated', userid));
}, true);
socket.on('message', (msg) => {
// თუ არ არის ავტორიზირებული, ან არ აქვს მაგიდა მინიჭებული, არცერთ ბრძანებას არ ვასრულებთ
if (!userid || !gameTable || !msg) return;
// ობიექტად გადაქცევა
try {
if (typeof msg == 'string')
msg = JSON.parse(msg);
}
catch (err) { }
// აუცილებელია გადმოწოდებული იყოს კომანდის პარამეტრი, რის მიხედვითაც მეთოდს მოძებნის მაგიდის კლასში
if (Object.prototype.toString.call(msg) === '[object Array]') {
return;
}
if (!msg.length) return;
var command = msg.shift();
var params = msg;
var command = msg.command;
var params = msg.params;
if (!command) {
console.log('Every message must have "command" and optionaly "params" properties');
return;
}
var reservedWords = ['Join', 'Leave'];
if (command in reservedWords) {
console.log('Reserved words cant be used as command:', reservedWords);
return;
}
command = 'on' + command;
// მაგიდას თუ არ გააჩნია კომანდის შესაბამისი მეთოდი, ვიკიდებთ
if (typeof gameTable[command] != 'function') {
console.log('GameTable method not found with name:', command);
return;
}
gameTable[command].apply(gameTable, params);
});
socket.on('close', () => {
// მონიშვნა როგორც გათიშული, რათა არ გაგრძელდეს სხვა პროცესები
disconnected = true;
// თუ არ არის ავტორიზირებული გამოვდივართ
if (!userid) return;
// მომხმარებლების რაოდენობის შემცირება
this.UsersCount--;
// მაგიდიდან გამოსვლა
gameTable && gameTable.leave(userid);
if (!gameTable.Players.length) {
var index = this.GameTables.indexOf(gameTable);
if (index > -1)
this.GameTables.splice(index, 1);
}
});
}
// find table
findTable(user, channel: string, mode: number): TGameTable {
// თუ უკვე თამაშობდა რომელიმე მაგიდაზე
var table = this.GameTables.filter(t =>
(t.Players.filter(p => p.UserID == user.UserID)[0] != undefined) && // მომხმარებელი იყო მაგიდაზე
(t.Status == TableStatus.Started) && // დაწყებულია თამაში
(t.Status != TableStatus.Finished) // ჯერ არ მორჩენილა
)[0]
if (table) return table;
// გადმოწოდებული პარამეტრების მიხედვით შასაბამისი მაგიდის მოძებნა
table = this.GameTables.filter(t =>
t.Channel == channel && // გადმოწოდებული კანალით გაფილტვრა
t.Mode == mode && // თამაშის mode-თ გაფილტვრა
t.Players.length < t.MaxPlayersCount && // მოთამაშეების რაოდენობა არ შევსებულა
(t.Status != TableStatus.Started) && // თამაში არ დაწყებულა
(t.Status != TableStatus.Finished) && // თამაში არ დამთავრებულა
this.isTournamentValid(channel, t, user) // ტურნირების დროს ნავაროჩენი ლოგიკა
)[0]
if (table) return table;
// თუ არცერთი არ მოიძებნა, მაშინ უკვე ვქმნით ახალ მაგიდას
if (!this.createTable) return;
table = this.createTable(user, channel, mode);
if (!table) return;
this.GameTables.push(table);
return table;
}
createTable(user, channel, mode): TGameTable {
return new this.GameTableClass(this.GamePlayerClass, channel, mode, 2, this.isTournamentChannel(channel) ? user.IsVIP : false);
}
isTournamentValid(channel: string, table: TGameTable, user): boolean {
if (!this.isTournamentChannel(channel))
return true;
return (user.IsVIP == table.IsVIPTable);
}
isTournamentChannel(channel: string) {
return (channel == 'tournament');
}
// Static
public static Start>(port?, TGameTable?, TGamePlayerClass?) {
return new Server(port, TGameTable, TGamePlayerClass);
}
}
exports.Server = Server;
exports.Helper = Helper;
exports.GameTableBase = GameTableBase;
exports.GamePlayerBase = GamePlayerBase;