UNPKG

11.3 kBPlain TextView Raw
1/// <reference path="gameplayerbase.ts" />
2/// <reference path="gametablebase.ts" />
3/// <reference path="helper.ts" />
4
5
6var engine = require('engine.io');
7var engineRooms = require('engine.io-rooms');
8var http = require('http');
9var urlParser = require('url');
10
11
12class Server<TGamePlayer extends GamePlayerBase, TGameTable extends GameTableBase<TGamePlayer>> {
13
14 public static API_ROOT_URL = 'http://api.jok.io/';
15
16 public StartTime;
17 public GameTables: TGameTable[] = [];
18 public UsersCount = 0;
19
20 private io;
21
22 constructor(private port = process.env.PORT || 9003, private GameTableClass?, private GamePlayerClass?) {
23
24 var server = http.createServer(this.httpHandler.bind(this));
25 this.io = engine.attach(server);
26
27 // added channels support
28 this.io = engineRooms(this.io);
29
30 Helper.IO = this.io;
31
32
33 this.StartTime = Date.now();
34
35 this.io.on('connection', this.onConnectionOpen.bind(this));
36
37 server.listen(this.port, () => {
38 console.log('server listening at port:', this.port);
39 });
40
41
42 Helper.SendMail('status-update@jok.io', 'jok-realtime-server started', 'StartTime: ' + new Date());
43 }
44
45
46 // processing http request
47 httpHandler(req, res) {
48
49 var urlInfo = urlParser.parse(req.url, true);
50
51 switch (urlInfo.pathname) {
52 case '/stats':
53 {
54 res.end(JSON.stringify({
55 ConnectionsCount: this.io.clientsCount,
56 UsersCount: this.UsersCount,
57 TablesCount: this.GameTables.length,
58 Uptime: (Date.now() - this.StartTime) / (1000 * 60) + ' min.'
59 }));
60 }
61 break;
62
63 default:
64 {
65 res.end('Hi, Bye');
66 }
67 break;
68 }
69 }
70
71
72 // processing commands
73 onConnectionOpen(socket) {
74
75 var sid = socket.request.query.token;
76 var gameid = socket.request.query.gameid;
77 var gamemode = socket.request.query.gamemode;
78 var channel = socket.request.query.channel;
79 var ipaddress = socket.request.headers["x-forwarded-for"];
80
81
82 // channel-ის შემთხვევაში case sensitive არ არის
83 if (!channel)
84 channel = '';
85
86 channel = channel.toLowerCase();
87
88
89 // ip მისამართის გაგება, გათვალისწინებულია proxy-დან მოსული შეტყობინებებიც (heroku-ს შემთხვევა)
90 if (ipaddress) {
91 var list = ipaddress.split(",");
92 ipaddress = list[list.length - 1];
93 } else {
94 ipaddress = socket.request.connection.remoteAddress;
95 }
96
97
98 // აუცილებელ ველებზე შემოწმება
99 if (!sid || !ipaddress || !gameid) return;
100
101
102 var userid;
103 var disconnected;
104 var gameTable: TGameTable;
105
106 var url = Server.API_ROOT_URL + 'User/InfoBySID?sid=' + sid + '&ipaddress=' + ipaddress + '&gameid=' + gameid;
107
108
109
110 // მომხმარებლის ინფორმაციის აღება
111 Helper.HttpGet(url, (isSuccess, data) => {
112
113 if (!isSuccess || !data.UserID || disconnected) return;
114
115
116 // ე.წ. ავტორიზაცია კომექშენის
117 userid = data.UserID;
118
119
120 // ავტორიზირებული მომხმარებლების რაოდენობის გაზრდა
121 this.UsersCount++;
122
123
124 // მომხმარებლის გაწევრიანება საკუთარ ჩანელში, რათა შემდეგ მოხდეს ინფორმაციის გაგზავნა
125 var userChannel = 'User' + userid;
126 var oldConnections = Helper.ChannelSockets(userChannel);
127 socket.join(userChannel);
128
129
130 // ძველი კონექშენების გათიშვა
131 if (oldConnections)
132 oldConnections.forEach(c => c.close());
133
134
135 // მაგიდაზე შეყვანა ეგრევე
136 gameTable = this.findTable(data, channel, gamemode);
137 if (!gameTable) {
138 console.log('GameTable not found, it must not happen. Passed parameters:', channel, gamemode);
139 return;
140 }
141 gameTable.join(data, ipaddress, channel, gamemode);
142
143
144 // ავტორიზაციის შესახებ ინფორმაციის გაგზავნა კლიენტთან, რათა გააგრძელოს პროცესი
145 socket.send(Helper.BuildCommand('UserAuthenticated', userid));
146
147 }, true);
148
149
150
151 socket.on('message', (msg) => {
152
153 // თუ არ არის ავტორიზირებული, ან არ აქვს მაგიდა მინიჭებული, არცერთ ბრძანებას არ ვასრულებთ
154 if (!userid || !gameTable || !msg) return;
155
156
157 // ობიექტად გადაქცევა
158 try {
159 if (typeof msg == 'string')
160 msg = JSON.parse(msg);
161 }
162 catch (err) { }
163
164
165 // აუცილებელია გადმოწოდებული იყოს კომანდის პარამეტრი, რის მიხედვითაც მეთოდს მოძებნის მაგიდის კლასში
166 if (Object.prototype.toString.call(msg) === '[object Array]') {
167 return;
168 }
169
170 if (!msg.length) return;
171
172 var command = msg.shift();
173 var params = msg;
174
175 var command = msg.command;
176 var params = msg.params;
177
178 if (!command) {
179 console.log('Every message must have "command" and optionaly "params" properties');
180 return;
181 }
182
183 var reservedWords = ['Join', 'Leave'];
184 if (command in reservedWords) {
185 console.log('Reserved words cant be used as command:', reservedWords);
186 return;
187 }
188
189 command = 'on' + command;
190
191
192 // მაგიდას თუ არ გააჩნია კომანდის შესაბამისი მეთოდი, ვიკიდებთ
193 if (typeof gameTable[command] != 'function') {
194 console.log('GameTable method not found with name:', command);
195 return;
196 }
197
198 gameTable[command].apply(gameTable, params);
199 });
200
201
202
203 socket.on('close', () => {
204
205 // მონიშვნა როგორც გათიშული, რათა არ გაგრძელდეს სხვა პროცესები
206 disconnected = true;
207
208 // თუ არ არის ავტორიზირებული გამოვდივართ
209 if (!userid) return;
210
211 // მომხმარებლების რაოდენობის შემცირება
212 this.UsersCount--;
213
214 // მაგიდიდან გამოსვლა
215 gameTable && gameTable.leave(userid);
216
217 if (!gameTable.Players.length) {
218 var index = this.GameTables.indexOf(gameTable);
219
220 if (index > -1)
221 this.GameTables.splice(index, 1);
222 }
223 });
224 }
225
226
227 // find table
228 findTable(user, channel: string, mode: number): TGameTable {
229
230
231 // თუ უკვე თამაშობდა რომელიმე მაგიდაზე
232 var table = this.GameTables.filter(t =>
233 (t.Players.filter(p => p.UserID == user.UserID)[0] != undefined) && // მომხმარებელი იყო მაგიდაზე
234 (t.Status == TableStatus.Started) && // დაწყებულია თამაში
235 (t.Status != TableStatus.Finished) // ჯერ არ მორჩენილა
236 )[0]
237 if (table) return table;
238
239
240 // გადმოწოდებული პარამეტრების მიხედვით შასაბამისი მაგიდის მოძებნა
241 table = this.GameTables.filter(t =>
242 t.Channel == channel && // გადმოწოდებული კანალით გაფილტვრა
243 t.Mode == mode && // თამაშის mode-თ გაფილტვრა
244 t.Players.length < t.MaxPlayersCount && // მოთამაშეების რაოდენობა არ შევსებულა
245 (t.Status != TableStatus.Started) && // თამაში არ დაწყებულა
246 (t.Status != TableStatus.Finished) && // თამაში არ დამთავრებულა
247 this.isTournamentValid(channel, t, user) // ტურნირების დროს ნავაროჩენი ლოგიკა
248 )[0]
249 if (table) return table;
250
251
252 // თუ არცერთი არ მოიძებნა, მაშინ უკვე ვქმნით ახალ მაგიდას
253 if (!this.createTable) return;
254
255 table = this.createTable(user, channel, mode);
256 if (!table) return;
257
258 this.GameTables.push(table);
259
260 return table;
261 }
262
263
264 createTable(user, channel, mode): TGameTable {
265 return new this.GameTableClass(this.GamePlayerClass, channel, mode, 2, this.isTournamentChannel(channel) ? user.IsVIP : false);
266 }
267
268 isTournamentValid(channel: string, table: TGameTable, user): boolean {
269
270 if (!this.isTournamentChannel(channel))
271 return true;
272
273 return (user.IsVIP == table.IsVIPTable);
274 }
275
276 isTournamentChannel(channel: string) {
277 return (channel == 'tournament');
278 }
279
280
281 // Static
282 public static Start<TGamePlayer extends GamePlayerBase, TGameTable extends GameTableBase<TGamePlayer>>(port?, TGameTable?, TGamePlayerClass?) {
283 return new Server<TGamePlayer, TGameTable>(port, TGameTable, TGamePlayerClass);
284 }
285}
286
287
288exports.Server = Server;
289exports.Helper = Helper;
290exports.GameTableBase = GameTableBase;
291exports.GamePlayerBase = GamePlayerBase;