1 /* Pioneers - Implementation of the excellent Settlers of Catan board game.
2 * Go buy a copy.
3 *
4 * Copyright (C) 1999 Dave Cole
5 * Copyright (C) 2003-2007 Bas Wijnen <shevek@fmf.nl>
6 * Copyright (C) 2005-2010 Roland Clobus <rclobus@rclobus.nl>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include "config.h"
24 #include <stdio.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include "server.h"
28 #include "network.h"
29 #include "random.h"
30
31 /* Local function prototypes */
32 static gboolean mode_check_version(Player * player, gint event);
33 static gboolean mode_check_status(Player * player, gint event);
34 static gboolean mode_bad_version(Player * player, gint event);
35 static gboolean mode_global(Player * player, gint event);
36 static gboolean mode_unhandled(Player * player, gint event);
37 static void player_setup(Player * player, gint playernum,
38 const gchar * name, gboolean force_spectator);
39 static Player *player_by_name(Game * game, char *name);
40
41 #define tournament_minute 1000 * 60
42 #define time_to_wait_for_players 30 * 60 * 1000
43
44 /** Is the game a tournament game?
45 * @param game The game
46 * @return TRUE if this game is a tournament game
47 */
is_tournament_game(const Game * game)48 static gboolean is_tournament_game(const Game * game)
49 {
50 return game->params->tournament_time > 0;
51 }
52
53 /** Find a free number for a connecting player.
54 * The number has not been used before.
55 * @param game The game
56 * @param force_spectator The connecting player must be a spectator
57 */
next_free_player_num(Game * game,gboolean force_spectator)58 static gint next_free_player_num(Game * game, gboolean force_spectator)
59 {
60 gint idx;
61
62 if (!force_spectator) {
63 GList *list;
64 gboolean player_taken[MAX_PLAYERS];
65 guint available = game->params->num_players;
66
67 memset(player_taken, 0, sizeof(player_taken));
68 playerlist_inc_use_count(game);
69 for (list = game->player_list;
70 list != NULL; list = g_list_next(list)) {
71 Player *player = list->data;
72 if (player->num >= 0
73 && !player_is_spectator(game, player->num)) {
74 player_taken[player->num] = TRUE;
75 --available;
76 }
77 }
78 playerlist_dec_use_count(game);
79 if (available > 0) {
80 guint skip;
81 if (game->random_order) {
82 skip = random_guint(available);
83 } else {
84 skip = 0;
85 }
86 idx = 0;
87 ++skip;
88 while (player_taken[idx] || --skip != 0)
89 ++idx;
90 return idx;
91 }
92 }
93
94 /* No players available/wanted, look for a spectator number */
95 idx = (gint) game->params->num_players;
96 while (player_by_num(game, idx) != NULL)
97 ++idx;
98 return idx;
99 }
100
mode_global(Player * player,gint event)101 static gboolean mode_global(Player * player, gint event)
102 {
103 StateMachine *sm = player->sm;
104 Game *game = player->game;
105 gchar *text;
106
107 switch (event) {
108 case SM_FREE:
109 if (player->name != NULL)
110 g_free(player->name);
111 if (player->style != NULL)
112 g_free(player->style);
113 if (player->location != NULL)
114 g_free(player->location);
115 if (player->devel != NULL)
116 deck_free(player->devel, NULL);
117 if (player->num >= 0
118 && !player_is_spectator(game, player->num)
119 && !player->disconnected) {
120 game->num_players--;
121 meta_report_num_players(game->num_players);
122 }
123 g_list_free(player->build_list);
124 g_list_free(player->special_points);
125 g_free(player);
126 return TRUE;
127 case SM_NET_CLOSE:
128 player_remove(player);
129 if (player->num >= 0) {
130 player_broadcast(player, PB_OTHERS, FIRST_VERSION,
131 LATEST_VERSION, "has quit\n");
132 player_archive(player);
133 } else {
134 player_free(player);
135 }
136 driver->player_change(game);
137 return TRUE;
138 case SM_RECV:
139 if (sm_recv(sm, "chat %S", &text)) {
140 if (strlen(text) > MAX_CHAT)
141 player_send(player, FIRST_VERSION,
142 LATEST_VERSION, "ERR %s\n",
143 _("chat too long"));
144 else
145 player_broadcast(player, PB_ALL,
146 FIRST_VERSION,
147 LATEST_VERSION,
148 "chat %s\n", text);
149 g_free(text);
150 return TRUE;
151 }
152 if (sm_recv(sm, "name %S", &text)) {
153 if (text[0] == '\0')
154 player_send(player, FIRST_VERSION,
155 LATEST_VERSION,
156 "ERR invalid-name\n");
157 else if (strlen(text) > MAX_NAME_LENGTH)
158 player_send(player, FIRST_VERSION,
159 LATEST_VERSION, "ERR %s\n",
160 _("name too long"));
161 else
162 player_set_name(player, text);
163 g_free(text);
164 return TRUE;
165 }
166 if (sm_recv(sm, "style %S", &text)) {
167 if (player->style)
168 g_free(player->style);
169 player->style = text;
170 player_broadcast(player, PB_ALL, V0_11,
171 LATEST_VERSION, "style %s\n",
172 text);
173 return TRUE;
174 }
175 break;
176 default:
177 break;
178 }
179 return FALSE;
180 }
181
mode_unhandled(Player * player,gint event)182 static gboolean mode_unhandled(Player * player, gint event)
183 {
184 StateMachine *sm = player->sm;
185 gchar *text;
186
187 switch (event) {
188 case SM_RECV:
189 if (sm_recv(sm, "extension %S", &text)) {
190 player_send(player, FIRST_VERSION, LATEST_VERSION,
191 "NOTE %s\n",
192 N_("ignoring unknown extension"));
193 log_message(MSG_INFO,
194 "ignoring unknown extension from %s: %s\n",
195 player->name, text);
196 g_free(text);
197 return TRUE;
198 }
199 break;
200 default:
201 break;
202 }
203 return FALSE;
204 }
205
206 /* Called to start the game (if it hasn't been yet). Add computer
207 * players to fill any empty spots
208 *
209 */
tournament_start_cb(gpointer data)210 static gboolean tournament_start_cb(gpointer data)
211 {
212 guint i;
213 Game *game = (Game *) data;
214 GList *player;
215 gboolean human_player_present;
216
217 g_source_remove(game->tournament_timer);
218 game->tournament_timer = 0;
219
220 /* if game already started */
221 if (game->num_players == game->params->num_players)
222 return FALSE;
223
224 if (game->num_players == 0) {
225 player_broadcast(player_none(game), PB_SILENT,
226 FIRST_VERSION, LATEST_VERSION,
227 "NOTE %s\n",
228 N_
229 ("The last player left, the "
230 "tournament timer is reset."));
231 game->tournament_countdown = game->params->tournament_time;
232 return FALSE;
233 }
234
235 /* remove all disconnected players */
236 playerlist_inc_use_count(game);
237 for (player = game->player_list; player != NULL;
238 player = g_list_next(player)) {
239 Player *p = player->data;
240 if (p->disconnected && !sm_get_use_cache(p->sm)) {
241 player_free(p);
242 }
243 }
244 playerlist_dec_use_count(game);
245
246 /* if no human players are present, quit */
247 playerlist_inc_use_count(game);
248 human_player_present = FALSE;
249 for (player = game->player_list;
250 player != NULL && !human_player_present;
251 player = g_list_next(player)) {
252 Player *p = player->data;
253 if (!player_is_spectator(game, p->num)
254 && determine_player_type(p->style) == PLAYER_HUMAN) {
255 human_player_present = TRUE;
256 }
257 }
258 playerlist_dec_use_count(game);
259 if (!human_player_present) {
260 player_broadcast(player_none(game), PB_SILENT,
261 FIRST_VERSION, LATEST_VERSION,
262 "NOTE %s\n",
263 N_("No human players present. Bye."));
264 request_server_stop(game);
265 return FALSE;
266 }
267
268 player_broadcast(player_none(game), PB_SILENT, FIRST_VERSION,
269 LATEST_VERSION, "NOTE %s\n",
270 N_("Game starts, adding computer players."));
271
272 /* add computer players to start game */
273 for (i = game->num_players; i < game->params->num_players; i++) {
274 add_computer_player(game, TRUE);
275 }
276
277 return FALSE;
278 }
279
280 /*
281 * Keep players notified about when the tournament game is going to start
282 *
283 */
talk_about_tournament_cb(gpointer data)284 static gboolean talk_about_tournament_cb(gpointer data)
285 {
286 Game *game = (Game *) data;
287 const gchar *message;
288
289 /* if game already started */
290 if (game->num_players == game->params->num_players)
291 return FALSE;
292
293 if (game->num_players == 0) {
294 if (game->tournament_timer != 0) {
295 player_broadcast(player_none(game), PB_SILENT,
296 FIRST_VERSION, LATEST_VERSION,
297 "NOTE %s\n",
298 N_
299 ("The last player left, the "
300 "tournament timer is reset."));
301 game->tournament_countdown =
302 game->params->tournament_time;
303 g_source_remove(game->tournament_timer);
304 game->tournament_timer = 0;
305 }
306 return FALSE;
307 }
308
309 /* ngettext can not be used here,
310 * because the string must be sent untranslated */
311 message = game->tournament_countdown != 1 ?
312 N_("The game starts in %s minutes.") :
313 N_("The game starts in %s minute.");
314
315 player_broadcast(player_none(game), PB_SILENT, FIRST_VERSION,
316 LATEST_VERSION, "NOTE1 %d|%s\n",
317 game->tournament_countdown, message);
318 game->tournament_countdown--;
319
320 if (game->tournament_countdown > 0)
321 g_timeout_add(tournament_minute,
322 &talk_about_tournament_cb, game);
323
324 return FALSE;
325 }
326
327 /** Generate a name for a computer player.
328 * The name will be unique for the game.
329 */
generate_name_for_computer_player(Game * game)330 static gchar *generate_name_for_computer_player(Game * game)
331 {
332 gchar *filename;
333 FILE *stream;
334 gchar *line;
335 gchar *name = NULL;
336 int num = 1;
337
338 filename =
339 g_build_filename(get_pioneers_dir(), "computer_names", NULL);
340 stream = fopen(filename, "r");
341 if (!stream) {
342 g_warning("Unable to open %s", filename);
343 /* Default name for the AI when the computer_names file
344 * is not found or empty.
345 */
346 } else {
347 while (read_line_from_file(&line, stream)) {
348 if (player_by_name(game, line) == NULL) {
349 if (g_random_int_range(0, num) == 0) {
350 if (name)
351 g_free(name);
352 name = g_strdup(line);
353 }
354 num++;
355 }
356 }
357 fclose(stream);
358 if (num == 1) {
359 g_warning("Empty file or all names taken: %s",
360 filename);
361 }
362 }
363 g_free(filename);
364
365 if (name == NULL) {
366 gint counter = 2;
367
368 /* Default name for the AI when the computer_names file
369 * is not found or empty.
370 */
371 name = g_strdup(_("Computer Player"));
372
373 while (player_by_name(game, name) != NULL) {
374 g_free(name);
375 name =
376 g_strdup_printf("%s (%d)",
377 _("Computer Player"),
378 counter++);
379 }
380 }
381
382 return name;
383 }
384
385 /** Add a new computer player (disconnected)
386 */
player_new_computer_player(Game * game)387 gchar *player_new_computer_player(Game * game)
388 {
389 Player *player;
390 gchar *name;
391
392 /* Reserve the name, so the names of the computer players will
393 be unique */
394 name = generate_name_for_computer_player(game);
395 player = player_new(game, name);
396 player->disconnected = TRUE;
397 sm_goto(player->sm, (StateFunc) mode_idle);
398 return name;
399 }
400
401 /** Allocate a new Player struct.
402 * The StateMachine is not initialized.
403 * */
player_new(Game * game,const gchar * name)404 Player *player_new(Game * game, const gchar * name)
405 {
406 Player *player;
407 StateMachine *sm;
408
409 player = g_malloc0(sizeof(*player));
410 sm = player->sm = sm_new(player);
411
412 sm_global_set(sm, (StateFunc) mode_global);
413 sm_unhandled_set(sm, (StateFunc) mode_unhandled);
414
415 player->game = game;
416 player->location = g_strdup("not connected");
417 player->devel = deck_new();
418 game->player_list = g_list_append(game->player_list, player);
419 player->num = -1;
420 player->chapel_played = 0;
421 player->univ_played = 0;
422 player->gov_played = 0;
423 player->libr_played = 0;
424 player->market_played = 0;
425 player->islands_discovered = 0;
426 player->disconnected = FALSE;
427 player->name = g_strdup(name);
428 player->style = NULL;
429 player->special_points = NULL;
430 player->special_points_next_id = 0;
431
432 driver->player_change(game);
433
434 return player;
435 }
436
player_new_connection(Game * game,Session * ses)437 Player *player_new_connection(Game * game, Session * ses)
438 {
439 gchar name[100];
440 size_t i;
441 Player *player;
442 StateMachine *sm;
443 GError *error;
444 gchar *location;
445 gchar *port;
446
447 error = NULL;
448 if (!net_get_peer_name(ses, &location, &port, &error)) {
449 /* %s = error message */
450 log_message(MSG_ERROR,
451 _("Unable to determine the "
452 "hostname of the player: %s"),
453 error->message);
454 g_error_free(error);
455 }
456
457 /* give player a name, some functions need it */
458 strcpy(name, "connecting");
459 for (i = strlen(name); i < G_N_ELEMENTS(name) - 1; ++i) {
460 if (player_by_name(game, name) == NULL)
461 break;
462 name[i] = '_';
463 name[i + 1] = 0;
464 }
465 if (i == G_N_ELEMENTS(name) - 1) {
466 /* there are too many pending connections */
467 net_write(ses, "ERR Too many connections\n");
468 g_free(location);
469 g_free(port);
470 return NULL;
471 }
472
473 if (game->is_game_over) {
474 /* The game is over, don't accept new players */
475 /* Message to send to the client when the game is already over
476 * when a connection is made. */
477 net_printf(ses, "NOTE %s\n", N_("Sorry, game is over."));
478 log_message(MSG_INFO,
479 _("Player from %s is refused: game is over\n"),
480 location);
481 g_free(location);
482 g_free(port);
483 return NULL;
484 }
485
486 player = player_new(game, name);
487 sm = player->sm;
488 sm_set_session(sm, ses);
489 net_set_check_connection_alive(ses, 30);
490 g_free(player->location);
491 player->location = g_strdup(location);
492
493 /* Cache messages of the game in progress until all initial
494 * messages have been sent
495 */
496 sm_set_use_cache(sm, TRUE);
497
498 sm_goto(sm, (StateFunc) mode_check_version);
499
500 driver->player_change(game);
501 return player;
502 }
503
504 /* set the player name. Most of the time, player_set_name is called instead,
505 * which calls this function with public set to TRUE. Only player_setup calls
506 * this with public == FALSE, because it doesn't want the broadcast. */
player_set_name_real(Player * player,gchar * name,gboolean public)507 static void player_set_name_real(Player * player, gchar * name,
508 gboolean public)
509 {
510 Game *game = player->game;
511 Player *player_temp;
512
513 g_assert(name[0] != 0);
514
515 if (((player_temp = player_by_name(game, name)) != NULL) &&
516 (player_temp != player)) {
517 /* make it a note, not an error, so nothing bad happens
518 * (on error the AI would disconnect) */
519 player_send(player, FIRST_VERSION, LATEST_VERSION,
520 "NOTE %s\n",
521 N_(""
522 "Name not changed: new name is already in use"));
523 return;
524 }
525
526 if (player->name != name) {
527 g_free(player->name);
528 player->name = g_strdup(name);
529 }
530
531 if (public)
532 player_broadcast(player, PB_ALL, FIRST_VERSION,
533 LATEST_VERSION, "is %s\n", player->name);
534
535 driver->player_renamed(player);
536 driver->player_change(game);
537 }
538
player_setup(Player * player,gint playernum,const gchar * name,gboolean force_spectator)539 static void player_setup(Player * player, gint playernum,
540 const gchar * name, gboolean force_spectator)
541 {
542 gchar nm[MAX_NAME_LENGTH + 1];
543 Game *game = player->game;
544 StateMachine *sm = player->sm;
545 Player *other;
546
547 player->num = playernum;
548 if (player->num < 0) {
549 player->num = next_free_player_num(game, force_spectator);
550 }
551
552 if (!player_is_spectator(game, player->num)) {
553 game->num_players++;
554 meta_report_num_players(game->num_players);
555 }
556
557 player->num_roads = 0;
558 player->num_bridges = 0;
559 player->num_ships = 0;
560 player->num_settlements = 0;
561 player->num_cities = 0;
562
563 /* give the player her new name */
564 if (name == NULL) {
565 if (player_is_spectator(game, player->num)) {
566 gint num = 1;
567 do {
568 sprintf(nm, _("Spectator %d"), num++);
569 } while (player_by_name(game, nm) != NULL);
570 } else {
571 sprintf(nm, _("Player %d"), player->num);
572 }
573 } else {
574 strncpy(nm, name, G_N_ELEMENTS(nm));
575 nm[G_N_ELEMENTS(nm) - 1] = '\0';
576 }
577
578 /* if the new name exists, try padding it with underscores */
579 other = player_by_name(game, nm);
580 if (other != player && other != NULL) {
581 size_t i;
582 /* add underscores until the name is unique */
583 for (i = strlen(nm); i < G_N_ELEMENTS(nm) - 1; ++i) {
584 if (player_by_name(game, nm) == NULL)
585 break;
586 nm[i] = '_';
587 nm[i + 1] = 0;
588 }
589 /* Adding underscores was not enough to make the name unique.
590 * While staying within the maximum name length,
591 * create numbers at the end of the name.
592 * Repeat until an unique name has been found.
593 */
594 while (player_by_name(game, nm)) {
595 gint digit = 10;
596 i = G_N_ELEMENTS(nm) - 1;
597 while (digit == 10 && i > 0) {
598 /* Digit will be: 0..10 */
599 --i;
600 digit = g_ascii_digit_value(nm[i]) + 1;
601 nm[i] = (gchar) ('0' + digit % 10);
602 }
603 }
604 }
605 /* copy the (possibly new) name to dynamic memory */
606 /* don't broadcast the name. This is done by mode_pre_game, after
607 * telling the user how many players are in the game.
608 * That should keep things easier for the client. */
609 player_set_name_real(player, nm, FALSE);
610
611 /* add the info in the output device */
612 driver->player_added(player);
613 driver->player_change(game);
614 if (playernum < 0)
615 sm_goto(sm, (StateFunc) mode_pre_game);
616 }
617
player_free(Player * player)618 void player_free(Player * player)
619 {
620 Game *game = player->game;
621
622 if (game->player_list_use_count > 0) {
623 game->dead_players =
624 g_list_append(game->dead_players, player);
625 player->disconnected = TRUE;
626 return;
627 }
628
629 game->player_list = g_list_remove(game->player_list, player);
630 driver->player_change(game);
631
632 sm_free(player->sm);
633 }
634
timed_out(gpointer data)635 static gboolean timed_out(gpointer data)
636 {
637 Game *game = data;
638 log_message(MSG_INFO,
639 _(""
640 "Was hanging around for too long without players... bye.\n"));
641 player_broadcast(player_none(game), PB_SILENT,
642 FIRST_VERSION, LATEST_VERSION,
643 "NOTE %s\n",
644 N_("No human players present. Bye."));
645 request_server_stop(game);
646 return FALSE;
647 }
648
player_archive(Player * player)649 void player_archive(Player * player)
650 {
651 StateFunc state;
652 Game *game = player->game;
653 gboolean human_player_present;
654 GList *pl;
655
656 /* If this was a spectator, forget about him */
657 if (player_is_spectator(game, player->num)) {
658 player_free(player);
659 return;
660 }
661 /* If this game can't be started, forget old players */
662 if (params_game_is_unstartable(game->params)) {
663 player_free(player);
664 return;
665 }
666
667 /* Mark the player as disconnected */
668 player->disconnected = TRUE;
669
670 /* If the player was in the middle of a trade, pop the state
671 machine and inform others as necessary */
672 state = sm_current(player->sm);
673 if (state == (StateFunc) mode_domestic_quote_rejected) {
674 /* No special actions needed */
675 } else if (state == (StateFunc) mode_domestic_quote) {
676 /* Retract all quotes */
677 for (;;) {
678 QuoteInfo *quote;
679 quote = quotelist_find_domestic(game->quotes,
680 player->num, -1);
681 if (quote == NULL)
682 break;
683 quotelist_delete(game->quotes, quote);
684 player_broadcast(player, PB_RESPOND, FIRST_VERSION,
685 LATEST_VERSION,
686 "domestic-quote delete %d\n",
687 quote->var.d.quote_num);
688 }
689 } else if (state == (StateFunc) mode_domestic_initiate) {
690 /* End the trade */
691 trade_finish_domestic(player);
692 }
693
694 /* If the player was robbing something, auto-undo to robber
695 * placement. */
696 if (state == (StateFunc) mode_select_robbed
697 || state == (StateFunc) mode_select_pirated)
698 robber_undo(player);
699
700 /* Inform the metaserver */
701 game->num_players--;
702 meta_report_num_players(game->num_players);
703
704 /* if no human players are present, start timer */
705 playerlist_inc_use_count(game);
706 human_player_present = FALSE;
707 for (pl = game->player_list;
708 pl != NULL && !human_player_present; pl = g_list_next(pl)) {
709 Player *p = pl->data;
710 if (!player_is_spectator(game, p->num)
711 && !p->disconnected
712 && determine_player_type(p->style) == PLAYER_HUMAN) {
713 human_player_present = TRUE;
714 }
715 }
716 playerlist_dec_use_count(game);
717 if (!human_player_present && game->no_humans_timer == 0
718 && is_tournament_game(game)) {
719 game->no_humans_timer =
720 g_timeout_add(time_to_wait_for_players, timed_out,
721 game);
722 player_broadcast(player_none(game), PB_SILENT,
723 FIRST_VERSION, LATEST_VERSION,
724 "NOTE %s\n",
725 N_
726 ("The last human player left. Waiting for the return of a player."));
727 }
728 }
729
730 /* Try to revive the player
731 newp: Player* attempt to revive this player
732 name: The player wants to have this name, if possible
733 */
player_revive(Player * newp,char * name)734 void player_revive(Player * newp, char *name)
735 {
736 Game *game = newp->game;
737 GList *current = NULL;
738 Player *p = NULL;
739 gboolean reviving_player_in_setup;
740 gchar *safe_name;
741
742 if (game->no_humans_timer != 0) {
743 g_source_remove(game->no_humans_timer);
744 game->no_humans_timer = 0;
745 player_broadcast(player_none(game), PB_SILENT,
746 FIRST_VERSION, LATEST_VERSION,
747 "NOTE %s\n", N_("Resuming the game."));
748 }
749
750 /* first see if a player with the given name exists */
751 if (name) {
752 playerlist_inc_use_count(game);
753 for (current = game->player_list; current != NULL;
754 current = g_list_next(current)) {
755 p = current->data;
756 if (!strcmp(name, p->name))
757 if (p->disconnected
758 && !sm_get_use_cache(p->sm)
759 && p != newp)
760 break;
761 }
762 playerlist_dec_use_count(game);
763 }
764 /* if not, try to find an unused player number */
765 if (current == NULL) {
766 gint num;
767
768 num = next_free_player_num(game, FALSE);
769 if (num < (gint) game->params->num_players) {
770 player_setup(newp, -1, name, FALSE);
771 return;
772 }
773 }
774 /* if not, try to take over another disconnected player */
775 if (current == NULL) {
776 playerlist_inc_use_count(game);
777 for (current = game->player_list; current != NULL;
778 current = g_list_next(current)) {
779 p = current->data;
780 if (p->disconnected && !sm_get_use_cache(p->sm)
781 && p != newp)
782 break;
783 }
784 playerlist_dec_use_count(game);
785 }
786 /* if still no player is found, do a normal setup */
787 if (current == NULL) {
788 player_setup(newp, -1, name, FALSE);
789 return;
790 }
791
792 /* Reviving the player that is currently in the setup phase */
793 reviving_player_in_setup =
794 (game->setup_player && game->setup_player->data == p);
795
796 /* remove the disconnected player from the player list, it's memory will be freed at the end of this routine */
797 game->player_list = g_list_remove(game->player_list, p);
798
799 /* initialize the player */
800 player_setup(newp, p->num, name, FALSE);
801
802 /* mark the player as a reconnect */
803 newp->disconnected = TRUE;
804
805 /* Don't use the old player's name */
806
807 /* copy over all the data from p */
808 g_assert(newp->build_list == NULL);
809 newp->build_list = p->build_list;
810 p->build_list = NULL; /* prevent deletion */
811
812 memcpy(newp->prev_assets, p->prev_assets,
813 sizeof(newp->prev_assets));
814 memcpy(newp->assets, p->assets, sizeof(newp->assets));
815 newp->gold = p->gold;
816 /* take over the development deck */
817 deck_free(newp->devel, NULL);
818 newp->devel = p->devel;
819 p->devel = NULL;
820
821 g_assert(newp->special_points == NULL);
822 newp->special_points = p->special_points;
823 p->special_points = NULL; /* prevent deletion */
824
825 newp->discard_num = p->discard_num;
826 newp->num_roads = p->num_roads;
827 newp->num_bridges = p->num_bridges;
828 newp->num_ships = p->num_ships;
829 newp->num_settlements = p->num_settlements;
830 newp->num_cities = p->num_cities;
831 newp->num_soldiers = p->num_soldiers;
832 newp->develop_points = p->develop_points;
833 newp->chapel_played = p->chapel_played;
834 newp->univ_played = p->univ_played;
835 newp->gov_played = p->gov_played;
836 newp->libr_played = p->libr_played;
837 newp->market_played = p->market_played;
838 /* Not copied: sm, game, location, num, client_version */
839
840 sm_copy_stack(newp->sm, p->sm);
841 if (sm_current(newp->sm) != (StateFunc) mode_pre_game)
842 sm_push(newp->sm, (StateFunc) mode_pre_game);
843 else
844 sm_goto(newp->sm, (StateFunc) mode_pre_game);
845
846 /* Copy longest road and largest army */
847 if (game->longest_road == p)
848 game->longest_road = newp;
849 if (game->largest_army == p)
850 game->largest_army = newp;
851
852 if (reviving_player_in_setup) {
853 /* Restore the pointer */
854 game->setup_player = game->player_list;
855 while (game->setup_player
856 && game->setup_player->data != newp) {
857 game->setup_player =
858 g_list_next(game->setup_player);
859 }
860 g_assert(game->setup_player != NULL);
861 }
862 p->num = -1; /* prevent the number of players
863 from getting decremented */
864
865 player_free(p);
866
867 /* Make sure the name in the broadcast doesn't contain the separator */
868 safe_name = g_strdup(newp->name);
869 g_strdelimit(safe_name, "|", '_');
870 player_broadcast(newp, PB_SILENT, FIRST_VERSION, LATEST_VERSION,
871 "NOTE1 %s|%s\n", safe_name,
872 /* %s is the name of the reconnecting player */
873 N_("%s has reconnected."));
874 g_free(safe_name);
875 return;
876 }
877
mode_spectator(Player * player,gint event)878 gboolean mode_spectator(Player * player, gint event)
879 {
880 gint num;
881 Game *game = player->game;
882 StateMachine *sm = player->sm;
883 Player *other;
884
885 sm_state_name(sm, "mode_spectator");
886 if (event != SM_RECV)
887 return FALSE;
888 /* first see if this is a valid event for this mode */
889 if (sm_recv(sm, "play")) {
890 /* try to be the first available player */
891 num = next_free_player_num(game, FALSE);
892 if (num >= (gint) game->params->num_players) {
893 player_send(player, FIRST_VERSION, LATEST_VERSION,
894 "ERR game-full");
895 return TRUE;
896 }
897 } else if (sm_recv(sm, "play %d", &num)) {
898 /* try to be the specified player number */
899 if (num >= (gint) game->params->num_players
900 || num < 0
901 || !player_by_num(game, num)->disconnected) {
902 player_send(player, FIRST_VERSION, LATEST_VERSION,
903 "ERR invalid-player");
904 return TRUE;
905 }
906 } else
907 /* input was not what we expected,
908 * see if mode_unhandled likes it */
909 return FALSE;
910
911 other = player_by_num(game, num);
912 if (other == NULL) {
913 player_send(player, FIRST_VERSION, LATEST_VERSION, "Ok\n");
914 player_broadcast(player, PB_ALL, FIRST_VERSION,
915 LATEST_VERSION, "was spectator %d\n",
916 player->num);
917 sm_set_use_cache(player->sm, TRUE);
918 player_setup(player, -1, player->name, FALSE);
919 sm_goto(sm, (StateFunc) mode_pre_game);
920 return TRUE;
921 }
922 sm_set_use_cache(player->sm, TRUE);
923 player_revive(player, player->name);
924 return TRUE;
925 }
926
mode_bad_version(Player * player,gint event)927 static gboolean mode_bad_version(Player * player, gint event)
928 {
929 StateMachine *sm = player->sm;
930
931 sm_state_name(sm, "mode_bad_version");
932 switch (event) {
933 case SM_ENTER:
934 player_send_uncached(player, FIRST_VERSION, LATEST_VERSION,
935 "ERR sorry, version conflict\n");
936 player_free(player);
937 break;
938 }
939 return FALSE;
940 }
941
mode_check_version(Player * player,gint event)942 static gboolean mode_check_version(Player * player, gint event)
943 {
944 StateMachine *sm = player->sm;
945 gchar *version;
946
947 sm_state_name(sm, "mode_check_version");
948 switch (event) {
949 case SM_ENTER:
950 player_send_uncached(player, UNKNOWN_VERSION,
951 UNKNOWN_VERSION, "version report\n");
952 break;
953
954 case SM_RECV:
955 if (sm_recv(sm, "version %S", &version)) {
956 ClientVersionType cvt =
957 client_version_type_from_string(version);
958 player->version = cvt;
959 if (can_client_connect_to_server
960 (cvt, LATEST_VERSION)) {
961 sm_goto(sm, (StateFunc) mode_check_status);
962 } else {
963 gchar *mismatch =
964 g_strdup_printf("%s <-> %s",
965 client_version_type_to_string
966 (LATEST_VERSION),
967 version);
968 /* Make sure the argument does not contain the separator */
969 g_strdelimit(mismatch, "|", '_');
970 player_send_uncached(player, cvt, cvt,
971 "NOTE1 %s|%s\n",
972 mismatch,
973 N_(""
974 "Version mismatch: %s"));
975 g_free(mismatch);
976 sm_goto(sm, (StateFunc) mode_bad_version);
977 }
978 g_free(version);
979 return TRUE;
980 }
981 break;
982 default:
983 break;
984 }
985 return FALSE;
986 }
987
start_tournament_mode(Player * player)988 static void start_tournament_mode(Player * player)
989 {
990 Game *game = player->game;
991
992 if (is_tournament_game(game)) {
993 /* if first player in and this is a tournament start the timer */
994 if (game->num_players == 1) {
995 game->tournament_countdown =
996 game->params->tournament_time;
997 game->tournament_timer =
998 g_timeout_add(game->tournament_countdown *
999 tournament_minute + 500,
1000 &tournament_start_cb, game);
1001 g_timeout_add(1000, &talk_about_tournament_cb,
1002 game);
1003 } else {
1004 if (game->tournament_timer != 0
1005 && game->num_players !=
1006 game->params->num_players) {
1007 player_send(player, FIRST_VERSION,
1008 LATEST_VERSION, "NOTE %s\n",
1009 N_
1010 ("This game will start soon."));
1011 }
1012 }
1013 }
1014 }
1015
mode_check_status(Player * player,gint event)1016 static gboolean mode_check_status(Player * player, gint event)
1017 {
1018 StateMachine *sm = player->sm;
1019 gchar *playername;
1020
1021 sm_state_name(sm, "mode_check_status");
1022 switch (event) {
1023 case SM_ENTER:
1024 player_send_uncached(player, FIRST_VERSION, LATEST_VERSION,
1025 "status report\n");
1026 break;
1027
1028 case SM_RECV:
1029 if (sm_recv(sm, "status newplayer")) {
1030 player_setup(player, -1, NULL, FALSE);
1031 start_tournament_mode(player);
1032 return TRUE;
1033 }
1034 if (sm_recv(sm, "status reconnect %S", &playername)) {
1035 /* if possible, try to revive the player */
1036 player_revive(player, playername);
1037 g_free(playername);
1038 start_tournament_mode(player);
1039 return TRUE;
1040 }
1041 if (sm_recv(sm, "status newviewer")) {
1042 player_setup(player, -1, NULL, TRUE);
1043 return TRUE;
1044 }
1045 if (sm_recv(sm, "status viewer %S", &playername)) {
1046 player_setup(player, -1, playername, TRUE);
1047 g_free(playername);
1048 return TRUE;
1049 }
1050 break;
1051 default:
1052 break;
1053 }
1054 return FALSE;
1055 }
1056
1057 /* Returns a GList* to player 0 */
player_first_real(Game * game)1058 GList *player_first_real(Game * game)
1059 {
1060 GList *list;
1061 /* search for player 0 */
1062 playerlist_inc_use_count(game);
1063 for (list = game->player_list;
1064 list != NULL; list = g_list_next(list)) {
1065 Player *player = list->data;
1066 if (player->num == 0)
1067 break;
1068 }
1069 playerlist_dec_use_count(game);
1070 return list;
1071 }
1072
1073 /* Returns a GList * to a player with a number one higher than last */
player_next_real(GList * last)1074 GList *player_next_real(GList * last)
1075 {
1076 Player *player;
1077 Game *game;
1078 guint numplayers;
1079 gint nextnum;
1080 GList *list;
1081
1082 if (!last)
1083 return NULL;
1084
1085 player = last->data;
1086 game = player->game;
1087 numplayers = game->params->num_players;
1088 nextnum = player->num + 1;
1089
1090 if (nextnum >= (gint) numplayers)
1091 return NULL;
1092
1093 playerlist_inc_use_count(game);
1094 for (list = game->player_list; list != NULL;
1095 list = g_list_next(list)) {
1096 Player *scan = list->data;
1097 if (scan->num == nextnum)
1098 break;
1099 }
1100 playerlist_dec_use_count(game);
1101 return list;
1102 }
1103
player_by_name(Game * game,char * name)1104 static Player *player_by_name(Game * game, char *name)
1105 {
1106 GList *list;
1107
1108 playerlist_inc_use_count(game);
1109 for (list = game->player_list;
1110 list != NULL; list = g_list_next(list)) {
1111 Player *player = list->data;
1112
1113 if (player->name != NULL
1114 && strcmp(player->name, name) == 0) {
1115 playerlist_dec_use_count(game);
1116 return player;
1117 }
1118 }
1119 playerlist_dec_use_count(game);
1120
1121 return NULL;
1122 }
1123
player_by_num(Game * game,gint num)1124 Player *player_by_num(Game * game, gint num)
1125 {
1126 GList *list;
1127
1128 playerlist_inc_use_count(game);
1129 for (list = game->player_list;
1130 list != NULL; list = g_list_next(list)) {
1131 Player *player = list->data;
1132
1133 if (player->num == num) {
1134 playerlist_dec_use_count(game);
1135 return player;
1136 }
1137 }
1138 playerlist_dec_use_count(game);
1139
1140 return NULL;
1141 }
1142
player_is_spectator(Game * game,gint player_num)1143 gboolean player_is_spectator(Game * game, gint player_num)
1144 {
1145 return (gint) game->params->num_players <= player_num;
1146 }
1147
1148 /* Returns a player that's not part of the game.
1149 */
player_none(Game * game)1150 Player *player_none(Game * game)
1151 {
1152 static Player player;
1153
1154 player.game = game;
1155 player.num = -1;
1156 player.disconnected = TRUE;
1157 return &player;
1158 }
1159
1160 /** Broadcast a message to all players and spectators - prepend "player %d " to
1161 * all players except the one generating the message.
1162 * Also prepend 'extension' when this message is a protocol extension.
1163 *
1164 * send to PB_SILENT PB_RESPOND PB_ALL PB_OTHERS
1165 * player - - + **
1166 * other - + + +
1167 * ** = don't send to the player
1168 * + = prepend 'player %d' to the message
1169 * - = don't alter the message
1170 */
player_broadcast_internal(Player * player,BroadcastType type,const gchar * message,gboolean is_extension,ClientVersionType first_supported_version,ClientVersionType last_supported_version)1171 static void player_broadcast_internal(Player * player, BroadcastType type,
1172 const gchar * message,
1173 gboolean is_extension,
1174 ClientVersionType
1175 first_supported_version,
1176 ClientVersionType
1177 last_supported_version)
1178 {
1179 Game *game = player->game;
1180 GList *list;
1181
1182 playerlist_inc_use_count(game);
1183 for (list = game->player_list; list != NULL;
1184 list = g_list_next(list)) {
1185 Player *scan = list->data;
1186 if ((scan->disconnected && !sm_get_use_cache(scan->sm))
1187 || scan->num < 0
1188 || scan->version < first_supported_version
1189 || scan->version > last_supported_version)
1190 continue;
1191 if (type == PB_SILENT
1192 || (scan == player && type == PB_RESPOND)) {
1193 if (is_extension) {
1194 player_send(scan, first_supported_version,
1195 last_supported_version,
1196 "extension %s", message);
1197 } else {
1198 player_send(scan, first_supported_version,
1199 last_supported_version, "%s",
1200 message);
1201 }
1202 } else if (scan != player || type == PB_ALL) {
1203 if (is_extension) {
1204 player_send(scan, first_supported_version,
1205 last_supported_version,
1206 "extension player %d %s",
1207 player->num, message);
1208 } else {
1209 player_send(scan, first_supported_version,
1210 last_supported_version,
1211 "player %d %s", player->num,
1212 message);
1213 }
1214
1215 }
1216 }
1217 playerlist_dec_use_count(game);
1218 }
1219
1220 /** As player_broadcast, but will add the 'extension' keyword */
player_broadcast_extension(Player * player,BroadcastType type,ClientVersionType first_supported_version,ClientVersionType last_supported_version,const char * fmt,...)1221 void player_broadcast_extension(Player * player, BroadcastType type,
1222 ClientVersionType first_supported_version,
1223 ClientVersionType last_supported_version,
1224 const char *fmt, ...)
1225 {
1226 gchar *buff;
1227 va_list ap;
1228
1229 va_start(ap, fmt);
1230 buff = game_vprintf(fmt, ap);
1231 va_end(ap);
1232
1233 player_broadcast_internal(player, type, buff, TRUE,
1234 first_supported_version,
1235 last_supported_version);
1236 g_free(buff);
1237 }
1238
1239 /** Broadcast a message to all players and spectators */
player_broadcast(Player * player,BroadcastType type,ClientVersionType first_supported_version,ClientVersionType last_supported_version,const char * fmt,...)1240 void player_broadcast(Player * player, BroadcastType type,
1241 ClientVersionType first_supported_version,
1242 ClientVersionType last_supported_version,
1243 const char *fmt, ...)
1244 {
1245 gchar *buff;
1246 va_list ap;
1247
1248 va_start(ap, fmt);
1249 buff = game_vprintf(fmt, ap);
1250 va_end(ap);
1251
1252 player_broadcast_internal(player, type, buff, FALSE,
1253 first_supported_version,
1254 last_supported_version);
1255 g_free(buff);
1256 }
1257
1258 /** Send a message to one player */
player_send(Player * player,ClientVersionType first_supported_version,ClientVersionType last_supported_version,const char * fmt,...)1259 void player_send(Player * player,
1260 ClientVersionType first_supported_version,
1261 ClientVersionType last_supported_version, const char *fmt,
1262 ...)
1263 {
1264 gchar *buff;
1265 va_list ap;
1266
1267 if (player->version < first_supported_version
1268 || player->version > last_supported_version)
1269 return;
1270
1271 va_start(ap, fmt);
1272 buff = game_vprintf(fmt, ap);
1273 va_end(ap);
1274
1275 sm_write(player->sm, buff);
1276 g_free(buff);
1277 }
1278
1279 /** Send a message to one player, even when caching is turned on */
player_send_uncached(Player * player,ClientVersionType first_supported_version,ClientVersionType last_supported_version,const char * fmt,...)1280 void player_send_uncached(Player * player,
1281 ClientVersionType first_supported_version,
1282 ClientVersionType last_supported_version,
1283 const char *fmt, ...)
1284 {
1285 gchar *buff;
1286 va_list ap;
1287
1288 if (player->version < first_supported_version
1289 || player->version > last_supported_version)
1290 return;
1291
1292 va_start(ap, fmt);
1293 buff = game_vprintf(fmt, ap);
1294 va_end(ap);
1295
1296 sm_write_uncached(player->sm, buff);
1297 g_free(buff);
1298 }
1299
player_set_name(Player * player,gchar * name)1300 void player_set_name(Player * player, gchar * name)
1301 {
1302 player_set_name_real(player, name, TRUE);
1303 }
1304
player_remove(Player * player)1305 void player_remove(Player * player)
1306 {
1307 driver->player_removed(player);
1308 }
1309
list_from_player(Player * player)1310 GList *list_from_player(Player * player)
1311 {
1312 GList *list;
1313 for (list = player_first_real(player->game); list != NULL;
1314 list = player_next_real(list))
1315 if (list->data == player)
1316 break;
1317 g_assert(list != NULL);
1318 return list;
1319 }
1320
next_player_loop(GList * current,Player * first)1321 GList *next_player_loop(GList * current, Player * first)
1322 {
1323 current = player_next_real(current);
1324 if (current == NULL)
1325 current = player_first_real(first->game);
1326 if (current->data == first)
1327 return NULL;
1328 return current;
1329 }
1330
playerlist_inc_use_count(Game * game)1331 void playerlist_inc_use_count(Game * game)
1332 {
1333 game->player_list_use_count++;
1334 }
1335
playerlist_dec_use_count(Game * game)1336 void playerlist_dec_use_count(Game * game)
1337 {
1338 game->player_list_use_count--;
1339 if (game->player_list_use_count == 0) {
1340 GList *current;
1341 GList *all_dead_players;
1342 current = game->dead_players;
1343 all_dead_players = game->dead_players; /* Remember this for g_list_free */
1344 game->dead_players = NULL; /* Clear the list */
1345 for (; current != NULL; current = g_list_next(current)) {
1346 Player *p = current->data;
1347 player_free(p);
1348 }
1349 g_list_free(all_dead_players);
1350 }
1351 }
1352