UNPKG

11.2 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
142 // ავტორიზაციის შესახებ ინფორმაციის გაგზავნა კლიენტთან, რათა გააგრძელოს პროცესი
143 socket.send(JSON.stringify(['UserAuthenticated', userid]));
144
145
146 gameTable.join(data, ipaddress, channel, gamemode);
147
148 }, true);
149
150
151
152 socket.on('message', (msg) => {
153
154 // თუ არ არის ავტორიზირებული, ან არ აქვს მაგიდა მინიჭებული, არცერთ ბრძანებას არ ვასრულებთ
155 if (!userid || !gameTable || !msg) return;
156
157
158 // ობიექტად გადაქცევა
159 try {
160 if (typeof msg == 'string')
161 msg = JSON.parse(msg);
162 }
163 catch (err) { }
164
165
166 // აუცილებელია გადმოწოდებული იყოს კომანდის პარამეტრი, რის მიხედვითაც მეთოდს მოძებნის მაგიდის კლასში
167 if (Object.prototype.toString.call(msg) !== '[object Array]') {
168 return;
169 }
170
171 if (!msg.length) return;
172
173 var command = msg.shift();
174 var params = msg;
175
176 if (!command) {
177 console.log('Every message must have "command" and optionaly "params" properties');
178 return;
179 }
180
181 var reservedWords = ['Join', 'Leave'];
182 if (command in reservedWords) {
183 console.log('Reserved words cant be used as command:', reservedWords);
184 return;
185 }
186
187 command = 'on' + command;
188
189
190 // მაგიდას თუ არ გააჩნია კომანდის შესაბამისი მეთოდი, ვიკიდებთ
191 if (typeof gameTable[command] != 'function') {
192 console.log('GameTable method not found with name:', command);
193 return;
194 }
195
196 gameTable[command].apply(gameTable, params);
197 });
198
199
200
201 socket.on('close', () => {
202
203 // მონიშვნა როგორც გათიშული, რათა არ გაგრძელდეს სხვა პროცესები
204 disconnected = true;
205
206 // თუ არ არის ავტორიზირებული გამოვდივართ
207 if (!userid) return;
208
209 // მომხმარებლების რაოდენობის შემცირება
210 this.UsersCount--;
211
212 // მაგიდიდან გამოსვლა
213 gameTable && gameTable.leave(userid);
214
215 if (!gameTable.Players.length) {
216 var index = this.GameTables.indexOf(gameTable);
217
218 if (index > -1)
219 this.GameTables.splice(index, 1);
220 }
221 });
222 }
223
224
225 // find table
226 findTable(user, channel: string, mode: number): TGameTable {
227
228
229 // თუ უკვე თამაშობდა რომელიმე მაგიდაზე
230 var table = this.GameTables.filter(t =>
231 (t.Players.filter(p => p.UserID == user.UserID)[0] != undefined) && // მომხმარებელი იყო მაგიდაზე
232 (t.Status == TableStatus.Started) && // დაწყებულია თამაში
233 (t.Status != TableStatus.Finished) // ჯერ არ მორჩენილა
234 )[0]
235 if (table) return table;
236
237
238 // გადმოწოდებული პარამეტრების მიხედვით შასაბამისი მაგიდის მოძებნა
239 table = this.GameTables.filter(t =>
240 t.Channel == channel && // გადმოწოდებული კანალით გაფილტვრა
241 t.Mode == mode && // თამაშის mode-თ გაფილტვრა
242 t.Players.length < t.MaxPlayersCount && // მოთამაშეების რაოდენობა არ შევსებულა
243 (t.Status != TableStatus.Started) && // თამაში არ დაწყებულა
244 (t.Status != TableStatus.Finished) && // თამაში არ დამთავრებულა
245 this.isTournamentValid(channel, t, user) // ტურნირების დროს ნავაროჩენი ლოგიკა
246 )[0]
247 if (table) return table;
248
249
250 // თუ არცერთი არ მოიძებნა, მაშინ უკვე ვქმნით ახალ მაგიდას
251 if (!this.createTable) return;
252
253 table = this.createTable(user, channel, mode);
254 if (!table) return;
255
256 this.GameTables.push(table);
257
258 return table;
259 }
260
261
262 createTable(user, channel, mode): TGameTable {
263 return new this.GameTableClass(this.GamePlayerClass, channel, mode, 2, this.isTournamentChannel(channel) ? user.IsVIP : false);
264 }
265
266 isTournamentValid(channel: string, table: TGameTable, user): boolean {
267
268 if (!this.isTournamentChannel(channel))
269 return true;
270
271 return (user.IsVIP == table.IsVIPTable);
272 }
273
274 isTournamentChannel(channel: string) {
275 return (channel == 'tournament');
276 }
277
278
279 // Static
280 public static Start<TGamePlayer extends GamePlayerBase, TGameTable extends GameTableBase<TGamePlayer>>(port?, TGameTable?, TGamePlayerClass?) {
281 return new Server<TGamePlayer, TGameTable>(port, TGameTable, TGamePlayerClass);
282 }
283}
284
285
286exports.Server = Server;
287exports.Helper = Helper;
288exports.GameTableBase = GameTableBase;
289exports.GamePlayerBase = GamePlayerBase;
290exports.TableStatus = TableStatus;