1 /*******************************************************************************
2 *
3 * Copyright (c) 2004-2012 Guillaume Cottenceau
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2, as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 ******************************************************************************/
19
20 /*
21 * this file holds game operations: create, join, list etc.
22 * it should be as far away as possible from network operations
23 */
24
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <stdarg.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <sys/socket.h>
32 #include <regex.h>
33
34 #include <glib.h>
35
36 #include "net.h"
37 #include "tools.h"
38 #include "log.h"
39 #include "game.h"
40
41 enum game_status { GAME_STATUS_OPEN, GAME_STATUS_CLOSED, GAME_STATUS_PLAYING };
42
43 #define MAX_PLAYERS_PER_GAME 5
44 struct game
45 {
46 enum game_status status;
47 int players_number;
48 int players_conn[MAX_PLAYERS_PER_GAME];
49 char* players_nick[MAX_PLAYERS_PER_GAME];
50 int players_started[MAX_PLAYERS_PER_GAME];
51 };
52
53 static GList * games = NULL;
54 static GList * open_players = NULL;
55
56 static ssize_t amount_transmitted = 0;
57
58 static char ok_pong[] = "PONG";
59 static char ok_player_joined[] = "JOINED: %s";
60 static char ok_player_parted[] = "PARTED: %s";
61 static char ok_player_kicked[] = "KICKED: %s";
62 static char ok_talk[] = "TALK: %s";
63 static char ok_can_start[] = "GAME_CAN_START: %s";
64
65 static char wn_unknown_command[] = "UNKNOWN_COMMAND";
66 static char wn_missing_arguments[] = "MISSING_ARGUMENTS";
67 static char wn_nick_invalid[] = "INVALID_NICK";
68 static char wn_nick_in_use[] = "NICK_IN_USE";
69 static char wn_no_such_game[] = "NO_SUCH_GAME";
70 static char wn_game_full[] = "GAME_FULL";
71 static char wn_already_in_game[] = "ALREADY_IN_GAME";
72 static char wn_max_open_games[] = "ALREADY_MAX_OPEN_GAMES";
73 static char wn_not_started[] = "NOT_STARTED";
74 static char wn_already_ok_started[] = "ALREADY_OK_STARTED";
75 static char wn_not_in_game[] = "NOT_IN_GAME";
76 static char wn_alone_in_the_dark[] = "ALONE_IN_THE_DARK";
77 static char wn_not_creator[] = "NOT_CREATOR";
78 static char wn_no_such_player[] = "NO_SUCH_PLAYER";
79 static char wn_denied[] = "DENIED";
80 static char wn_flooding[] = "FLOODING";
81 static char wn_others_not_ready[] = "OTHERS_NOT_READY";
82
83 static char fl_line_unrecognized[] = "MISSING_FB_PROTOCOL_TAG";
84 static char fl_proto_mismatch[] = "INCOMPATIBLE_PROTOCOL";
85
86 char* nick[256];
87 char* geoloc[256];
88 char* IP[256];
89 int remote_proto_minor[256];
90 int admin_authorized[256];
91
92 // calculate the list of players for a given game
list_game(const struct game * g)93 static char* list_game(const struct game * g)
94 {
95 char list_game_str[8192] = "";
96 int i;
97 for (i = 0; i < g->players_number; i++) {
98 strconcat(list_game_str, g->players_nick[i], sizeof(list_game_str));
99 if (i < g->players_number - 1)
100 strconcat(list_game_str, ",", sizeof(list_game_str));
101 }
102 return memdup(list_game_str, strlen(list_game_str) + 1);
103 }
104
105 // calculate the list of players for a given game with geolocation
list_game_with_geolocation(const struct game * g)106 static char* list_game_with_geolocation(const struct game * g)
107 {
108 char list_game_str[8192] = "";
109 int i;
110 char* n;
111 for (i = 0; i < g->players_number; i++) {
112 strconcat(list_game_str, g->players_nick[i], sizeof(list_game_str));
113 n = geoloc[g->players_conn[i]];
114 if (n != NULL) {
115 strconcat(list_game_str, ":", sizeof(list_game_str));
116 strconcat(list_game_str, n, sizeof(list_game_str));
117 }
118 if (i < g->players_number - 1)
119 strconcat(list_game_str, ",", sizeof(list_game_str));
120 }
121 return memdup(list_game_str, strlen(list_game_str) + 1);
122 }
123
124 static char list_games_str[16384] __attribute__((aligned(4096))) = "";
125 static char list_playing_geolocs_str[16384] __attribute__((aligned(4096))) = "";
126 static int players_in_game;
127 static int games_open;
128 static int games_running;
list_open_nicks_aux(gpointer data,gpointer user_data)129 static void list_open_nicks_aux(gpointer data, gpointer user_data)
130 {
131 char* n = nick[GPOINTER_TO_INT(data)];
132 if (n == NULL)
133 return;
134 strconcat(list_games_str, n, sizeof(list_games_str));
135 n = geoloc[GPOINTER_TO_INT(data)];
136 if (n != NULL) {
137 strconcat(list_games_str, ":", sizeof(list_games_str));
138 strconcat(list_games_str, n, sizeof(list_games_str));
139 }
140 strconcat(list_games_str, ",", sizeof(list_games_str));
141 }
list_games_aux(gpointer data,gpointer user_data)142 static void list_games_aux(gpointer data, gpointer user_data)
143 {
144 const struct game* g = data;
145 if (g->status == GAME_STATUS_OPEN) {
146 char* game;
147 games_open++;
148 strconcat(list_games_str, "[", sizeof(list_games_str));
149 game = list_game(g);
150 strconcat(list_games_str, game, sizeof(list_games_str));
151 free(game);
152 strconcat(list_games_str, "]", sizeof(list_games_str));
153 } else {
154 int i;
155 char* geo;
156 players_in_game += g->players_number;
157 games_running++;
158 for (i = 0; i < g->players_number; i++) {
159 geo = geoloc[g->players_conn[i]];
160 if (geo != NULL) {
161 strconcat(list_playing_geolocs_str, geo, sizeof(list_playing_geolocs_str));
162 strconcat(list_playing_geolocs_str, ",", sizeof(list_playing_geolocs_str));
163 }
164 }
165 return;
166 }
167 }
168 /* Game list is of the following scheme:
169 * 1.1 protocol:
170 * <list-of-open-players format="NICK|NICK:GEOLOC"> [<list-of-open-games format=<list-of-players format="NICK">>] free:%d games:%d playing:%d at:<list-of-playing-geolocs>
171 * 1.0 protocol:
172 * <list-of-open-players format="NICK|NICK:GEOLOC"> [<list-of-open-games format=<list-of-players format="NICK">>] free:%d games:%d playing:%d
173 */
calculate_list_games(void)174 void calculate_list_games(void)
175 {
176 char * free_players;
177 list_games_str[0] = '\0';
178 list_playing_geolocs_str[0] = '\0';
179 players_in_game = 0;
180 games_open = 0;
181 games_running = 0;
182 g_list_foreach(open_players, list_open_nicks_aux, NULL);
183 strconcat(list_games_str, " ", sizeof(list_games_str));
184 g_list_foreach(games, list_games_aux, NULL);
185 free_players = asprintf_(" free:%d games:%d playing:%d at:%s", conns_nb() - players_in_game - 1, games_running, players_in_game, list_playing_geolocs_str); // 1: don't count myself
186 strconcat(list_games_str, free_players, sizeof(list_games_str));
187 free(free_players);
188 }
189
create_game(int fd,char * nick)190 static void create_game(int fd, char* nick)
191 {
192 struct game * g = malloc_(sizeof(struct game));
193 g->players_number = 1;
194 g->players_conn[0] = fd;
195 g->players_nick[0] = nick;
196 g->status = GAME_STATUS_OPEN;
197 games = g_list_append(games, g);
198 open_players = g_list_remove(open_players, GINT_TO_POINTER(fd));
199 calculate_list_games();
200 }
201
add_player(struct game * g,int fd,char * nick)202 static int add_player(struct game * g, int fd, char* nick)
203 {
204 char joined_msg[1000];
205 int i;
206 if (g->players_number < MAX_PLAYERS_PER_GAME) {
207 /* inform other players */
208 snprintf(joined_msg, sizeof(joined_msg), ok_player_joined, nick);
209 for (i = 0; i < g->players_number; i++)
210 send_line_log_push(g->players_conn[i], joined_msg);
211
212 g->players_conn[g->players_number] = fd;
213 g->players_nick[g->players_number] = nick;
214 g->players_number++;
215 open_players = g_list_remove(open_players, GINT_TO_POINTER(fd));
216 calculate_list_games();
217 return 1;
218 } else {
219 free(nick);
220 return 0;
221 }
222 }
223
find_game_by_nick_aux(gconstpointer game,gconstpointer nick)224 static int find_game_by_nick_aux(gconstpointer game, gconstpointer nick)
225 {
226 const struct game * g = game;
227 if (g->status == GAME_STATUS_OPEN
228 && streq(g->players_nick[0], (char *) nick))
229 return 0;
230 else
231 return 1;
232 }
find_game_by_nick(char * nick)233 static struct game* find_game_by_nick(char* nick)
234 {
235 return GListp2data(g_list_find_custom(games, nick, find_game_by_nick_aux));
236 }
237
find_game_by_fd_aux(gconstpointer game,gconstpointer fd)238 static int find_game_by_fd_aux(gconstpointer game, gconstpointer fd)
239 {
240 const struct game* g = game;
241 int fd_ = GPOINTER_TO_INT(fd);
242 int i;
243 for (i = 0; i < g->players_number; i++)
244 if (g->players_conn[i] == fd_)
245 return 0;
246 return 1;
247 }
find_game_by_fd(int fd)248 static struct game* find_game_by_fd(int fd)
249 {
250 return GListp2data(g_list_find_custom(games, GINT_TO_POINTER(fd), find_game_by_fd_aux));
251 }
252
find_player_number(struct game * g,int fd)253 int find_player_number(struct game *g, int fd)
254 {
255 int i;
256 for (i = 0; i < g->players_number; i++)
257 if (g->players_conn[i] == fd)
258 return i;
259 l0(OUTPUT_TYPE_ERROR, "Internal error");
260 exit(EXIT_FAILURE);
261 }
262
real_start_game(struct game * g)263 static void real_start_game(struct game* g)
264 {
265 int i;
266 char mapping_str[4096] = "";
267 char can_start_msg[1000];
268 for (i = 0; i < g->players_number; i++) {
269 int len = strlen(mapping_str);
270 if (len >= sizeof(mapping_str)-1)
271 return;
272 mapping_str[len] = g->players_conn[i];
273 mapping_str[len+1] = '\0';
274 strconcat(mapping_str, g->players_nick[i], sizeof(mapping_str));
275 if (i < g->players_number - 1)
276 strconcat(mapping_str, ",", sizeof(mapping_str));
277 }
278 snprintf(can_start_msg, sizeof(can_start_msg), ok_can_start, mapping_str);
279 for (i = 0; i < g->players_number; i++) {
280 send_line_log_push_binary(g->players_conn[i], can_start_msg, ok_can_start);
281 g->players_started[i] = 0;
282 }
283 g->status = GAME_STATUS_PLAYING;
284 }
285
start_game(int fd)286 static void start_game(int fd)
287 {
288 struct game * g = find_game_by_fd(fd);
289 if (g) {
290 if (g->players_conn[0] == fd) {
291 if (g->players_number == 1) {
292 send_line_log(fd, wn_alone_in_the_dark, "START");
293 return;
294 }
295 send_ok(fd, "START");
296 real_start_game(g);
297 calculate_list_games();
298 l2(OUTPUT_TYPE_INFO, "running games increments to: %d (%d players)", games_running, players_in_game);
299 } else {
300 send_line_log(fd, wn_not_creator, "START");
301 }
302
303 } else {
304 l0(OUTPUT_TYPE_ERROR, "Internal error");
305 exit(EXIT_FAILURE);
306 }
307 }
308
close_game(int fd)309 static void close_game(int fd)
310 {
311 struct game * g = find_game_by_fd(fd);
312 if (g) {
313 if (g->players_conn[0] == fd) {
314 if (g->players_number == 1) {
315 send_line_log(fd, wn_alone_in_the_dark, "CLOSE");
316 return;
317 }
318 send_ok(fd, "CLOSE");
319 g->status = GAME_STATUS_CLOSED;
320 calculate_list_games();
321 } else {
322 send_line_log(fd, wn_not_creator, "CLOSE");
323 }
324
325 } else {
326 l0(OUTPUT_TYPE_ERROR, "Internal error");
327 exit(EXIT_FAILURE);
328 }
329 }
330
min_protocol_level(struct game * g)331 static int min_protocol_level(struct game* g)
332 {
333 int i;
334 int minor = remote_proto_minor[g->players_conn[0]];
335 for (i = 1; i < g->players_number; i++)
336 minor = MIN(minor, remote_proto_minor[g->players_conn[i]]);
337 return minor;
338 }
339
setoptions(int fd,char * options)340 static void setoptions(int fd, char* options)
341 {
342 struct game * g = find_game_by_fd(fd);
343 if (g) {
344 if (g->players_conn[0] == fd) {
345 int i;
346 char* msg;
347 send_ok(fd, "SETOPTIONS");
348 msg = asprintf_("OPTIONS: %s,PROTOCOLLEVEL:%d", options, min_protocol_level(g));
349 for (i = 0; i < g->players_number; i++)
350 if (remote_proto_minor[g->players_conn[i]] >= 1)
351 send_line_log_push(g->players_conn[i], msg);
352 free(msg);
353 } else {
354 send_line_log(fd, wn_not_creator, "SETOPTIONS");
355 }
356
357 } else {
358 l0(OUTPUT_TYPE_ERROR, "Internal error");
359 exit(EXIT_FAILURE);
360 }
361 }
362
leader_check_game_start(int fd)363 static void leader_check_game_start(int fd)
364 {
365 struct game * g = find_game_by_fd(fd);
366 if (g) {
367 if (g->status == GAME_STATUS_PLAYING) {
368 int i;
369 for (i = 0; i < g->players_number; i++) {
370 if (fd != g->players_conn[i]) {
371 if (!g->players_started[i]) {
372 send_line_log(fd, wn_others_not_ready, "LEADER_CHECK_GAME_START");
373 return;
374 }
375 }
376 }
377 send_ok(fd, "LEADER_CHECK_GAME_START");
378 } else {
379 send_line_log(fd, wn_not_started, "LEADER_CHECK_GAME_START");
380 }
381 } else {
382 l0(OUTPUT_TYPE_ERROR, "Internal error");
383 exit(EXIT_FAILURE);
384 }
385 }
386
ok_start_game(int fd)387 static void ok_start_game(int fd)
388 {
389 struct game * g = find_game_by_fd(fd);
390 if (g) {
391 if (g->status == GAME_STATUS_PLAYING) {
392 int i;
393 for (i = 0; i < g->players_number; i++) {
394 if (g->players_conn[i] == fd) {
395 if (!g->players_started[i]) {
396 if (remote_proto_minor[g->players_conn[i]] >= 1)
397 send_ok(fd, "OK_GAME_START");
398 g->players_started[i] = 1;
399 l1(OUTPUT_TYPE_DEBUG, "[%d] entering prio mode", g->players_conn[i]);
400 add_prio(g->players_conn[i]);
401 } else {
402 send_line_log(fd, wn_already_ok_started, "OK_GAME_START");
403 }
404 }
405 }
406 } else {
407 send_line_log(fd, wn_not_started, "OK_GAME_START");
408 }
409 } else {
410 l0(OUTPUT_TYPE_ERROR, "Internal error");
411 exit(EXIT_FAILURE);
412 }
413 }
414
kick_player(int fd,struct game * g,char * nick)415 static void kick_player(int fd, struct game * g, char * nick)
416 {
417 int i;
418 for (i = 0; i < g->players_number; i++) {
419 if (g->players_conn[i] != fd && streq(g->players_nick[i], nick)) {
420 send_ok(fd, "KICK");
421 send_line_log_push(g->players_conn[i], "KICKED");
422 player_part_game_(g->players_conn[i], ok_player_kicked);
423 return;
424 }
425 }
426 send_line_log(fd, wn_no_such_player, "KICK");
427 }
428
player_connects(int fd)429 void player_connects(int fd)
430 {
431 open_players = g_list_append(open_players, GINT_TO_POINTER(fd));
432 }
433
player_disconnects(int fd)434 void player_disconnects(int fd)
435 {
436 open_players = g_list_remove(open_players, GINT_TO_POINTER(fd));
437 }
438
talk_serverwide_aux(gpointer data,gpointer user_data)439 static void talk_serverwide_aux(gpointer data, gpointer user_data)
440 {
441 send_line_log_push(GPOINTER_TO_INT(data), user_data);
442 }
443
check_match_alert_words(gconstpointer data,gconstpointer user_data)444 static gboolean check_match_alert_words(gconstpointer data, gconstpointer user_data)
445 {
446 const regex_t* preg = data;
447 const char* msg = user_data;
448 if (regexec(preg, msg, 0, NULL, 0) == 0) {
449 return TRUE;
450 }
451 return FALSE;
452 }
453
talk(int fd,char * msg)454 static void talk(int fd, char* msg)
455 {
456 struct game * g = find_game_by_fd(fd);
457 char talk_msg[1000];
458
459 if (g_list_any(alert_words, check_match_alert_words, msg))
460 l2(OUTPUT_TYPE_INFO, "message '%s' from %s matches alert words!", msg, IP[fd]);
461
462 amount_talk_flood[fd]++;
463 if (amount_talk_flood[fd] == 15) {
464 l1(OUTPUT_TYPE_INFO, "'%s' is flooding!", IP[fd]);
465 send_line_log(fd, wn_flooding, msg);
466 conn_terminated(fd, "flooding");
467 return;
468 }
469
470 snprintf(talk_msg, sizeof(talk_msg), ok_talk, msg);
471 if (g) {
472 // player is in a game, it's a game-only chat
473 int i;
474 for (i = 0; i < g->players_number; i++)
475 send_line_log_push(g->players_conn[i], talk_msg);
476 } else {
477 // player is not in a game, it's a server-wide chat
478 g_list_foreach(open_players, talk_serverwide_aux, talk_msg);
479 }
480 }
481
status(int fd,char * msg)482 static void status(int fd, char* msg)
483 {
484 struct game * g = find_game_by_fd(fd);
485 if (g) {
486 char* game = list_game(g);
487 send_line_log(fd, game, msg);
488 free(game);
489 } else {
490 send_line_log(fd, wn_not_in_game, msg);
491 }
492 }
493
status_geo(int fd,char * msg)494 static void status_geo(int fd, char* msg)
495 {
496 struct game * g = find_game_by_fd(fd);
497 if (g) {
498 char* game = list_game_with_geolocation(g);
499 send_line_log(fd, game, msg);
500 free(game);
501 } else {
502 send_line_log(fd, wn_not_in_game, msg);
503 }
504 }
505
protocol_level(int fd,char * msg)506 static void protocol_level(int fd, char* msg)
507 {
508 // Find the smallest minor protocol level among players in game
509 struct game * g = find_game_by_fd(fd);
510 if (g) {
511 char* response;
512 int level = min_protocol_level(g);
513 response = asprintf_("%d", level);
514 send_line_log(fd, response, msg);
515 free(response);
516 } else {
517 send_line_log(fd, wn_not_in_game, msg);
518 }
519 }
520
nick_available_aux(gconstpointer data,gconstpointer user_data)521 static gboolean nick_available_aux(gconstpointer data, gconstpointer user_data)
522 {
523 const struct game* g = data;
524 const char* nick = user_data;
525 int i;
526 for (i = 0; i < g->players_number; i++)
527 if (streq(g->players_nick[i], nick))
528 return TRUE;
529 return FALSE;
530 }
nick_available(char * nick)531 static int nick_available(char* nick)
532 {
533 return !g_list_any(games, nick_available_aux, nick);
534 }
535
already_in_game_aux(gconstpointer data,gconstpointer user_data)536 static gboolean already_in_game_aux(gconstpointer data, gconstpointer user_data)
537 {
538 const struct game* g = data;
539 int fd = GPOINTER_TO_INT(user_data);
540 int i;
541 for (i = 0; i < g->players_number; i++)
542 if (g->players_conn[i] == fd)
543 return TRUE;
544 return FALSE;
545 }
already_in_game(int fd)546 static int already_in_game(int fd)
547 {
548 return g_list_any(games, already_in_game_aux, GINT_TO_POINTER(fd));
549 }
550
is_nick_ok(char * nick)551 static int is_nick_ok(char* nick)
552 {
553 int i;
554 if (strlen(nick) > 10)
555 return 0;
556 for (i = 0; i < strlen(nick); i++) {
557 if (!((nick[i] >= 'a' && nick[i] <= 'z')
558 || (nick[i] >= 'A' && nick[i] <= 'Z')
559 || (nick[i] >= '0' && nick[i] <= '9')
560 || nick[i] == '-' || nick[i] == '_')) {
561 return 0;
562 }
563 }
564 return 1;
565 }
566
567 /* true return value indicates that connection must be closed */
process_msg(int fd,char * msg)568 int process_msg(int fd, char* msg)
569 {
570 int client_proto_major;
571 int client_proto_minor;
572 char * args;
573 char * ptr, * ptr2;
574 char * msg_orig;
575
576 /* check for leading protocol tag */
577 if (!str_begins_static_str(msg, "FB/")
578 || strlen(msg) < 8) { // 8 stands for "FB/M.m f"(oo)
579 send_line_log(fd, fl_line_unrecognized, msg);
580 return 1;
581 }
582
583 /* check if client protocol is compatible; for simplicity, we don't support client protocol more recent
584 * than server protocol, we suppose that our servers are upgraded when a new release appears (but of
585 * course client protocol older is supported within the major protocol) */
586 client_proto_major = charstar_to_int(msg + 3);
587 client_proto_minor = charstar_to_int(msg + 5);
588 if (client_proto_major != proto_major
589 || client_proto_minor > proto_minor) {
590 send_line_log(fd, fl_proto_mismatch, msg);
591 return 1;
592 }
593
594 if (remote_proto_minor[fd] == -1)
595 remote_proto_minor[fd] = client_proto_minor;
596
597 msg_orig = strdup(msg);
598
599 /* after protocol, first word is command, then possible args */
600 current_command = msg + 7; // 7 stands for "FB/M.m "
601 if ((ptr = strchr(current_command, ' '))) {
602 *ptr = '\0';
603 args = current_command + strlen(current_command) + 1;
604 } else
605 args = NULL;
606
607 if (streq(current_command, "PING")) {
608 send_line_log(fd, ok_pong, msg_orig);
609 } else if (streq(current_command, "NICK")) {
610 if (!args) {
611 send_line_log(fd, wn_missing_arguments, msg_orig);
612 } else {
613 if ((ptr = strchr(args, ' ')))
614 *ptr = '\0';
615 if (strlen(args) > 10)
616 args[10] = '\0';
617 if (!is_nick_ok(args)) {
618 send_line_log(fd, wn_nick_invalid, msg_orig);
619 } else {
620 if (nick[fd] != NULL) {
621 free(nick[fd]);
622 }
623 nick[fd] = strdup(args);
624 calculate_list_games();
625 send_ok(fd, msg_orig);
626 }
627 }
628 } else if (streq(current_command, "GEOLOC")) {
629 if (!args) {
630 send_line_log(fd, wn_missing_arguments, msg_orig);
631 } else {
632 if ((ptr = strchr(args, ' ')))
633 *ptr = '\0';
634 if (strlen(args) > 13) // sign, 4 digits, dot, colon, sign, 4 digits, dot
635 args[13] = '\0';
636 if (geoloc[fd] != NULL) {
637 free(geoloc[fd]);
638 }
639 geoloc[fd] = strdup(args);
640 calculate_list_games();
641 send_ok(fd, msg_orig);
642 }
643 } else if (streq(current_command, "CREATE")) {
644 if (!args) {
645 send_line_log(fd, wn_missing_arguments, msg_orig);
646 } else {
647 if ((ptr = strchr(args, ' ')))
648 *ptr = '\0';
649 if (strlen(args) > 10)
650 args[10] = '\0';
651 if (!is_nick_ok(args)) {
652 send_line_log(fd, wn_nick_invalid, msg_orig);
653 } else if (!nick_available(args)) {
654 send_line_log(fd, wn_nick_in_use, msg_orig);
655 } else if (already_in_game(fd)) {
656 send_line_log(fd, wn_already_in_game, msg_orig);
657 } else if (games_open == 16) { // FB client can display 16 max
658 send_line_log(fd, wn_max_open_games, msg_orig);
659 } else {
660 create_game(fd, strdup(args));
661 send_ok(fd, msg_orig);
662 }
663 }
664 } else if (streq(current_command, "JOIN")) {
665 if (!args || !(ptr = strchr(args, ' '))) {
666 send_line_log(fd, wn_missing_arguments, msg_orig);
667 } else {
668 struct game * g;
669 char* nick = ptr + 1;
670 *ptr = '\0';
671 if ((ptr2 = strchr(ptr, ' ')))
672 *ptr2 = '\0';
673 if (strlen(nick) > 10)
674 nick[10] = '\0';
675 if (!is_nick_ok(nick)) {
676 send_line_log(fd, wn_nick_invalid, msg_orig);
677 } else if (!nick_available(nick)) {
678 send_line_log(fd, wn_nick_in_use, msg_orig);
679 } else if (already_in_game(fd)) {
680 send_line_log(fd, wn_already_in_game, msg_orig);
681 } else if (!(g = find_game_by_nick(args))) {
682 send_line_log(fd, wn_no_such_game, msg_orig);
683 } else {
684 if (add_player(g, fd, strdup(nick)))
685 send_ok(fd, msg_orig);
686 else
687 send_line_log(fd, wn_game_full, msg_orig);
688 }
689 }
690 } else if (streq(current_command, "KICK")) {
691 if (!args) {
692 send_line_log(fd, wn_missing_arguments, msg_orig);
693 } else {
694 if ((ptr = strchr(args, ' ')))
695 *ptr = '\0';
696 if (strlen(args) > 10)
697 args[10] = '\0';
698 if (!already_in_game(fd)) {
699 send_line_log(fd, wn_not_in_game, msg_orig);
700 } else {
701 struct game * g = find_game_by_fd(fd);
702 if (g->players_conn[0] != fd) {
703 send_line_log(fd, wn_not_creator, msg_orig);
704 } else {
705 kick_player(fd, g, args);
706 }
707 }
708 }
709 } else if (streq(current_command, "PART")) {
710 if (!already_in_game(fd)) {
711 send_line_log(fd, wn_not_in_game, msg_orig);
712 } else {
713 player_part_game(fd);
714 send_ok(fd, msg_orig);
715 }
716 } else if (streq(current_command, "LIST")) {
717 send_line_log(fd, list_games_str, msg_orig);
718 } else if (streq(current_command, "STATUS")) { // 1.0 command
719 if (!already_in_game(fd)) {
720 send_line_log(fd, wn_not_in_game, msg_orig);
721 } else {
722 status(fd, msg_orig);
723 }
724 } else if (streq(current_command, "STATUSGEO")) {
725 if (!already_in_game(fd)) {
726 send_line_log(fd, wn_not_in_game, msg_orig);
727 } else {
728 status_geo(fd, msg_orig);
729 }
730 } else if (streq(current_command, "PROTOCOL_LEVEL")) {
731 if (!already_in_game(fd)) {
732 send_line_log(fd, wn_not_in_game, msg_orig);
733 } else {
734 protocol_level(fd, msg_orig);
735 }
736 } else if (streq(current_command, "TALK")) {
737 if (!args) {
738 send_line_log(fd, wn_missing_arguments, msg_orig);
739 } else {
740 talk(fd, args);
741 }
742 } else if (streq(current_command, "START")) {
743 if (!already_in_game(fd)) {
744 send_line_log(fd, wn_not_in_game, msg_orig);
745 } else {
746 start_game(fd);
747 }
748 } else if (streq(current_command, "CLOSE")) {
749 if (!already_in_game(fd)) {
750 send_line_log(fd, wn_not_in_game, msg_orig);
751 } else {
752 close_game(fd);
753 }
754 } else if (streq(current_command, "SETOPTIONS")) {
755 if (!args) {
756 send_line_log(fd, wn_missing_arguments, msg_orig);
757 } else if (!already_in_game(fd)) {
758 send_line_log(fd, wn_not_in_game, msg_orig);
759 } else {
760 setoptions(fd, args);
761 }
762 } else if (streq(current_command, "LEADER_CHECK_GAME_START")) {
763 if (!already_in_game(fd)) {
764 send_line_log(fd, wn_not_in_game, msg_orig);
765 } else {
766 leader_check_game_start(fd);
767 }
768 } else if (streq(current_command, "OK_GAME_START")) {
769 if (!already_in_game(fd)) {
770 send_line_log(fd, wn_not_in_game, msg_orig);
771 } else {
772 ok_start_game(fd);
773 }
774 } else if (streq(current_command, "ADMIN_REREAD")) {
775 if (!admin_authorized[fd]) {
776 send_line_log(fd, wn_denied, msg_orig);
777 } else {
778 reread();
779 send_ok(fd, "ADMIN_REREAD");
780 }
781 } else {
782 send_line_log(fd, wn_unknown_command, msg);
783 }
784
785 free(msg_orig);
786 current_command = NULL;
787
788 return 0;
789 }
790
791
get_reset_amount_transmitted(void)792 ssize_t get_reset_amount_transmitted(void)
793 {
794 ssize_t ret = amount_transmitted;
795 amount_transmitted = 0;
796 return ret;
797 }
798
conn_to_terminate_helper(gpointer data,gpointer user_data)799 static void conn_to_terminate_helper(gpointer data, gpointer user_data)
800 {
801 conn_terminated(GPOINTER_TO_INT(data), "system error on send (probably peer shutdown or try again)");
802 }
803
process_msg_prio_(int fd,char * msg,ssize_t len,struct game * g)804 void process_msg_prio_(int fd, char* msg, ssize_t len, struct game* g)
805 {
806 GList * conn_to_terminate = NULL;
807 if (!g)
808 g = find_game_by_fd(fd);
809 if (g) {
810 int i;
811 for (i = 0; i < g->players_number; i++) {
812 // Pings are for the server only. Don't broadcast them to save bandwidth.
813 if (len == 3 && msg[1] == 'p') {
814 // nada
815
816 // Emitter wants to receive synchro message as well
817 } else if (g->players_conn[i] == fd && len > 2 && msg[1] == '!') {
818 char synchro4self[] = "?!\n";
819 ssize_t retval;
820 synchro4self[0] = fd;
821 l1(OUTPUT_TYPE_DEBUG, "[%d] sending self synchro", g->players_conn[i]);
822 retval = send(g->players_conn[i], synchro4self, sizeof(synchro4self) - 1, MSG_NOSIGNAL|MSG_DONTWAIT);
823 if (retval != sizeof(synchro4self) - 1) {
824 if (retval != -1) {
825 l4(OUTPUT_TYPE_INFO, "[%d] short send of %zd instead of %zd bytes from %d - destination is not reading data "
826 "(illegal FB client) or our upload bandwidth is saturated - sorry, cannot continue serving "
827 "this client in this situation, closing connection",
828 g->players_conn[i], retval, sizeof(synchro4self) - 1, fd);
829 }
830 conn_to_terminate = g_list_append(conn_to_terminate, GINT_TO_POINTER(g->players_conn[i]));
831 }
832
833 } else if (g->players_conn[i] != fd) {
834 ssize_t retval;
835 l3(OUTPUT_TYPE_DEBUG, "[%d] sending %zd bytes to %d", fd, len, g->players_conn[i]);
836 retval = send(g->players_conn[i], msg, len, MSG_NOSIGNAL|MSG_DONTWAIT);
837 if (retval != len) {
838 if (retval != -1) {
839 l4(OUTPUT_TYPE_INFO, "[%d] short send of %zd instead of %zd bytes from %d - destination is not reading data "
840 "(illegal FB client) or our upload bandwidth is saturated - sorry, cannot continue serving "
841 "this client in this situation, closing connection",
842 g->players_conn[i], retval, len, fd);
843 }
844 conn_to_terminate = g_list_append(conn_to_terminate, GINT_TO_POINTER(g->players_conn[i]));
845 }
846 }
847 }
848 if (conn_to_terminate) {
849 g_list_foreach(conn_to_terminate, conn_to_terminate_helper, NULL);
850 g_list_free(conn_to_terminate);
851 }
852 } else {
853 l1(OUTPUT_TYPE_ERROR, "Internal error: could not find game by fd: %d", fd);
854 exit(EXIT_FAILURE);
855 }
856 }
857
process_msg_prio(int fd,char * msg,ssize_t len)858 void process_msg_prio(int fd, char* msg, ssize_t len)
859 {
860 process_msg_prio_(fd, msg, len, NULL);
861 }
862
player_part_game(int fd)863 void player_part_game(int fd)
864 {
865 player_part_game_(fd, NULL);
866 }
867
player_part_game_(int fd,char * reason)868 void player_part_game_(int fd, char* reason)
869 {
870 struct game * g = find_game_by_fd(fd);
871 if (g) {
872 char * save_nick;
873 int j;
874 int i = find_player_number(g, fd);
875
876 // remove parting player from game
877 save_nick = g->players_nick[i];
878 for (j = i; j < g->players_number - 1; j++) {
879 g->players_conn[j] = g->players_conn[j + 1];
880 g->players_nick[j] = g->players_nick[j + 1];
881 g->players_started[j] = g->players_started[j + 1];
882 }
883 g->players_number--;
884
885 // completely remove game if empty
886 if (g->players_number == 0) {
887 int was_running = g->status == GAME_STATUS_PLAYING;
888 games = g_list_remove(games, g);
889 free(g);
890 calculate_list_games();
891 if (was_running)
892 l2(OUTPUT_TYPE_INFO, "running games decrements to: %d (%d players)", games_running, players_in_game);
893
894 } else {
895 if (g->status == GAME_STATUS_PLAYING) {
896 // inform other players, playing state
897 char leave_player_prio_msg[] = "?l\n";
898 leave_player_prio_msg[0] = fd;
899 process_msg_prio_(fd, leave_player_prio_msg, strlen(leave_player_prio_msg), g);
900 } else {
901 char parted_msg[1000];
902 // inform other players, non-playing state
903 snprintf(parted_msg, sizeof(parted_msg), reason ? reason : ok_player_parted, save_nick);
904 for (j = 0; j < g->players_number; j++)
905 send_line_log_push(g->players_conn[j], parted_msg);
906 }
907 calculate_list_games();
908 }
909 free(save_nick);
910
911 open_players = g_list_append(open_players, GINT_TO_POINTER(fd));
912 }
913 }
914