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,2006 Bas Wijnen <shevek@fmf.nl>
6 * Copyright (C) 2004,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 <ctype.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <fcntl.h>
28
29 #include "frontend.h"
30 #include "network.h"
31 #include "log.h"
32 #include "config-gnome.h"
33 #include "select-game.h"
34 #include "game-settings.h"
35 #include "game-rules.h"
36 #include "metaserver.h"
37 #include "avahi.h"
38 #include "avahi-browser.h"
39 #include "client.h"
40 #include "common_gtk.h"
41 #include "game-list.h"
42
43 const int PRIVATE_GAME_HISTORY_SIZE = 10;
44
45 static gboolean connect_spectator; /* Prefer to be a spectator */
46 static gboolean connect_spectator_allowed; /* Spectator allowed */
47 static gchar *connect_server; /* Name of the server */
48 static gchar *connect_port; /* Port of the server */
49
50 static GtkWidget *connect_dlg; /* Dialog for starting a new game */
51 static GtkWidget *name_entry; /* Name of the player */
52 static GtkWidget *spectator_toggle; /* Prefer to be a spectator */
53 static GtkWidget *metaserver_entry; /* Name of the metaserver */
54
55 static GtkWidget *meta_dlg; /* Dialog for joining a public game */
56 static GtkWidget *server_status; /* Description of the current metaserver */
57 static GtkListStore *meta_games_model; /* The list of the games at the metaserver */
58 static GtkWidget *meta_games_view; /* The view on meta_games_model */
59
60 enum {
61 META_RESPONSE_NEW = 1, /* Response for new game */
62 META_RESPONSE_REFRESH = 2 /* Response for refresh of the list */
63 };
64
65 enum { /* The columns of the meta_games_model */
66 C_META_HOST,
67 C_META_PORT,
68 C_META_HOST_SORTABLE, /* Invisible, used for sorting */
69 C_META_VERSION,
70 C_META_MAX,
71 C_META_CUR,
72 C_META_TERRAIN,
73 C_META_VICTORY,
74 C_META_SEVENS,
75 C_META_MAP,
76 META_N_COLUMNS
77 };
78
79 static GtkWidget *cserver_dlg; /* Dialog for creating a public game */
80 static GtkWidget *select_game; /* select game type */
81 static GtkWidget *game_settings; /* game settings widget */
82 static GtkWidget *aiplayers_spin; /* number of AI players */
83 static GtkWidget *game_rules; /* Adjust some rules */
84
85 static gboolean cfg_terrain; /* Random terrain */
86 static guint cfg_num_players;
87 static guint cfg_victory_points;
88 static guint cfg_sevens_rule;
89 static guint cfg_ai_players;
90 static const gchar *cfg_gametype; /* Will be set be the widget */
91
92 static GtkWidget *connect_private_dlg; /* Join a private game */
93 static GtkWidget *host_entry; /* Host name entry */
94 static GtkWidget *port_entry; /* Host port entry */
95
96 static enum {
97 GAMETYPE_MODE_SIGNON,
98 GAMETYPE_MODE_LIST
99 } gametype_mode;
100
101 static enum {
102 CREATE_MODE_SIGNON,
103 CREATE_MODE_WAIT_FOR_INFO,
104 CREATE_MODE_STARTING_OK,
105 CREATE_MODE_STARTING_ERROR
106 } create_mode;
107
108 static enum {
109 MODE_SIGNON,
110 MODE_REDIRECT,
111 MODE_LIST,
112 MODE_CAPABILITY,
113 MODE_REDIRECT_OVERFLOW,
114 MODE_DONE,
115 MODE_SERVER_INFO
116 } meta_mode;
117
118 /** Information about the metaserver */
119 static struct {
120 /** Server name */
121 gchar *server;
122 /** Port */
123 gchar *port;
124 /** Major version number of metaserver protocol */
125 gint version_major;
126 /** Minor version number of metaserver protocol */
127 gint version_minor;
128 /** Number of times the metaserver has redirected */
129 gint num_redirects;
130 /** The metaserver can create remote games */
131 gboolean can_create_games;
132 /** Active session */
133 Session *session;
134 /** Number of available game titles */
135 guint num_available_titles;
136 /** The settings of a game */
137 GameParams *params;
138 } metaserver_info = {
139 NULL, NULL, 0, 0, 0, FALSE, NULL, 0u, NULL
140 };
141
142 #define STRARG_LEN 128
143 #define INTARG_LEN 16
144
145 static gchar server_host[STRARG_LEN];
146 static gchar server_port[STRARG_LEN];
147 static gchar server_version[STRARG_LEN];
148 static gchar server_max[INTARG_LEN];
149 static gchar server_curr[INTARG_LEN];
150 static gchar server_vpoints[STRARG_LEN];
151 static gchar *server_sevenrule = NULL;
152 static gchar *server_terrain = NULL;
153 static gchar server_title[STRARG_LEN];
154
155 static void query_metaserver(const gchar * server, const gchar * port);
156 static void show_waiting_box(const gchar * message, const gchar * server,
157 const gchar * port);
158 static void close_waiting_box(void);
159
160 static void connect_set_field(gchar ** field, const gchar * value);
161 static void connect_close_all(gboolean user_pressed_ok,
162 gboolean can_be_spectator);
163 static void set_metaserver_info(void);
164 static void connect_private_dialog(G_GNUC_UNUSED GtkWidget * widget,
165 GtkWindow * parent);
166
connect_set_field(gchar ** field,const gchar * value)167 static void connect_set_field(gchar ** field, const gchar * value)
168 {
169 gchar *temp = g_strdup(value);
170 g_free(*field);
171 if (temp != NULL) {
172 *field = g_strdup(g_strstrip(temp));
173 } else {
174 *field = NULL;
175 }
176 g_free(temp);
177 }
178
179 /* Reset all pointers to the destroyed children of the dialog */
connect_dlg_destroyed(GtkWidget * widget,GtkWidget ** widget_pointer)180 static void connect_dlg_destroyed(GtkWidget * widget,
181 GtkWidget ** widget_pointer)
182 {
183 name_entry = NULL;
184 spectator_toggle = NULL;
185 metaserver_entry = NULL;
186 gtk_widget_destroyed(widget, widget_pointer);
187 }
188
connect_name_change_cb(G_GNUC_UNUSED gpointer ns)189 static void connect_name_change_cb(G_GNUC_UNUSED gpointer ns)
190 {
191 gchar *name = notifying_string_get(requested_name);
192 if (name_entry != NULL)
193 gtk_entry_set_text(GTK_ENTRY(name_entry), name);
194 g_free(name);
195 }
196
197 /* Public functions */
connect_get_spectator(void)198 gboolean connect_get_spectator(void)
199 {
200 return connect_spectator && connect_spectator_allowed;
201 }
202
connect_set_spectator(gboolean spectator)203 void connect_set_spectator(gboolean spectator)
204 {
205 connect_spectator = spectator;
206 connect_spectator_allowed = TRUE;
207 if (spectator_toggle != NULL)
208 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
209 (spectator_toggle),
210 connect_spectator);
211 }
212
connect_get_server(void)213 const gchar *connect_get_server(void)
214 {
215 return connect_server;
216 }
217
connect_set_server(const gchar * server)218 void connect_set_server(const gchar * server)
219 {
220 connect_set_field(&connect_server, server);
221 }
222
connect_get_metaserver(void)223 static gchar *connect_get_metaserver(void)
224 {
225 if (metaserver_entry == NULL)
226 return NULL;
227 return metaserver_get(METASERVER(metaserver_entry));
228 }
229
connect_set_metaserver(const gchar * metaserver)230 void connect_set_metaserver(const gchar * metaserver)
231 {
232 connect_set_field(&metaserver_info.server, metaserver);
233 if (metaserver_entry != NULL)
234 metaserver_add(METASERVER(metaserver_entry),
235 metaserver_info.server);
236 }
237
connect_get_port(void)238 const gchar *connect_get_port(void)
239 {
240 return connect_port;
241 }
242
connect_set_port(const gchar * port)243 void connect_set_port(const gchar * port)
244 {
245 connect_set_field(&connect_port, port);
246 }
247
connect_close_all(gboolean user_pressed_ok,gboolean can_be_spectator)248 static void connect_close_all(gboolean user_pressed_ok,
249 gboolean can_be_spectator)
250 {
251 connect_spectator_allowed = can_be_spectator;
252 if (user_pressed_ok) {
253 gchar *metaserver;
254
255 notifying_string_set(requested_name,
256 gtk_entry_get_text(GTK_ENTRY
257 (name_entry)));
258 connect_spectator =
259 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
260 (spectator_toggle));
261 /* Save connect dialogue entries */
262 metaserver = connect_get_metaserver();
263 config_set_string("connect/metaserver", metaserver);
264 g_free(metaserver);
265
266 frontend_gui_register_destroy(connect_dlg,
267 GUI_CONNECT_TRY);
268 } else {
269 frontend_gui_register_destroy(connect_dlg,
270 GUI_CONNECT_CANCEL);
271 }
272 if (connect_dlg)
273 gtk_widget_destroy(GTK_WIDGET(connect_dlg));
274 if (meta_dlg)
275 gtk_widget_destroy(GTK_WIDGET(meta_dlg));
276 if (cserver_dlg)
277 gtk_widget_destroy(GTK_WIDGET(cserver_dlg));
278 if (connect_private_dlg)
279 gtk_widget_destroy(GTK_WIDGET(connect_private_dlg));
280 }
281
282 /* Messages explaining some delays */
show_waiting_box(const gchar * message,const gchar * server,const gchar * port)283 static void show_waiting_box(const gchar * message, const gchar * server,
284 const gchar * port)
285 {
286 if (meta_dlg) {
287 gchar *s = g_strdup_printf(_("Metaserver at %s, port %s"),
288 server, port);
289 gtk_label_set_text(GTK_LABEL(server_status), s);
290 g_free(s);
291 }
292 log_message(MSG_INFO, message);
293 }
294
close_waiting_box(void)295 static void close_waiting_box(void)
296 {
297 log_message(MSG_INFO, _("Finished.\n"));
298 }
299
300 /* -------------------- get game types -------------------- */
301
meta_gametype_notify(Session * ses,NetEvent event,const gchar * line,G_GNUC_UNUSED gpointer user_data)302 static void meta_gametype_notify(Session * ses, NetEvent event,
303 const gchar * line,
304 G_GNUC_UNUSED gpointer user_data)
305 {
306 switch (event) {
307 case NET_CONNECT:
308 metaserver_info.num_available_titles = 0u;
309 break;
310 case NET_CONNECT_FAIL:
311 log_message(MSG_ERROR,
312 _("The metaserver is not available "
313 "anymore.\n"));
314 net_free(&ses);
315 metaserver_info.session = NULL;
316 if (cserver_dlg != NULL) {
317 gtk_widget_destroy(GTK_WIDGET(cserver_dlg));
318 }
319 break;
320 case NET_CLOSE:
321 if (gametype_mode == GAMETYPE_MODE_SIGNON) {
322 log_message(MSG_ERROR,
323 _("The metaserver closed "
324 "the connection "
325 "unexpectedly.\n"));
326 if (cserver_dlg != NULL) {
327 gtk_widget_destroy(GTK_WIDGET
328 (cserver_dlg));
329 }
330 } else {
331 close_waiting_box();
332 }
333 if (cserver_dlg != NULL
334 && metaserver_info.num_available_titles > 0u) {
335 gtk_dialog_set_response_sensitive(GTK_DIALOG
336 (cserver_dlg),
337 GTK_RESPONSE_OK,
338 TRUE);
339 }
340 net_free(&ses);
341 metaserver_info.session = NULL;
342 break;
343 case NET_READ:
344 switch (gametype_mode) {
345 case GAMETYPE_MODE_SIGNON:
346 net_printf(ses, "listtypes\n");
347 gametype_mode = GAMETYPE_MODE_LIST;
348 break;
349 case GAMETYPE_MODE_LIST:
350 /* A server description looks like this:
351 * title=%s\n
352 */
353 if (strncmp(line, "title=", 6) == 0) {
354 const GameParams *params;
355
356 line += 6;
357 if (game_list_is_empty()) {
358 game_list_prepare();
359 }
360 params = game_list_find_item(line);
361 if (params != NULL) {
362 select_game_add_details(SELECTGAME
363 (select_game),
364 params);
365 } else {
366 select_game_add(SELECTGAME
367 (select_game),
368 line);
369 }
370 metaserver_info.num_available_titles++;
371 }
372 break;
373 }
374 break;
375 }
376 }
377
get_metaserver_games_types(const gchar * server,const gchar * port)378 static void get_metaserver_games_types(const gchar * server,
379 const gchar * port)
380 {
381 show_waiting_box(_("Receiving game names from the metaserver.\n"),
382 server, port);
383 g_assert(metaserver_info.session == NULL);
384 metaserver_info.session = net_new(meta_gametype_notify, NULL);
385 if (net_connect(metaserver_info.session, server, port))
386 gametype_mode = GAMETYPE_MODE_SIGNON;
387 else {
388 net_free(&metaserver_info.session);
389 log_message(MSG_ERROR,
390 _("The metaserver is not available "
391 "anymore.\n"));
392 if (cserver_dlg != NULL) {
393 gtk_widget_destroy(GTK_WIDGET(cserver_dlg));
394 }
395 }
396 }
397
398 /* -------------------- create game server -------------------- */
399
meta_create_notify(Session * ses,NetEvent event,const gchar * line,G_GNUC_UNUSED gpointer user_data)400 static void meta_create_notify(Session * ses, NetEvent event,
401 const gchar * line,
402 G_GNUC_UNUSED gpointer user_data)
403 {
404 switch (event) {
405 case NET_CONNECT:
406 break;
407 case NET_CONNECT_FAIL:
408 log_message(MSG_ERROR,
409 _("The metaserver is not available "
410 "anymore.\n"));
411 net_free(&ses);
412 metaserver_info.session = NULL;
413 if (cserver_dlg != NULL) {
414 gtk_widget_destroy(GTK_WIDGET(cserver_dlg));
415 }
416 break;
417 case NET_CLOSE:
418 net_free(&ses);
419 metaserver_info.session = NULL;
420 switch (create_mode) {
421 case CREATE_MODE_STARTING_OK:
422 log_message(MSG_INFO,
423 _("New game server requested "
424 "on %s port %s.\n"),
425 connect_server, connect_port);
426 /* The metaserver is now busy creating the new game.
427 * UGLY FIX: Wait for some time */
428 g_usleep(500000);
429 connect_close_all(TRUE, FALSE);
430 break;
431 case CREATE_MODE_STARTING_ERROR:
432 log_message(MSG_ERROR,
433 _("Incomplete information about the "
434 "new game server received.\n"));
435 if (cserver_dlg != NULL) {
436 gtk_dialog_set_response_sensitive
437 (GTK_DIALOG(cserver_dlg),
438 GTK_RESPONSE_OK, TRUE);
439 }
440 break;
441 case CREATE_MODE_SIGNON:
442 case CREATE_MODE_WAIT_FOR_INFO:
443 log_message(MSG_ERROR,
444 _("The metaserver closed "
445 "the connection "
446 "unexpectedly.\n"));
447 if (cserver_dlg != NULL) {
448 gtk_dialog_set_response_sensitive
449 (GTK_DIALOG(cserver_dlg),
450 GTK_RESPONSE_OK, TRUE);
451 }
452 break;
453 }
454 break;
455 case NET_READ:
456 switch (create_mode) {
457 case CREATE_MODE_SIGNON:
458 net_printf(ses,
459 "create %d %d %d %d %d %s\n",
460 cfg_terrain, cfg_num_players,
461 cfg_victory_points, cfg_sevens_rule,
462 cfg_ai_players, cfg_gametype);
463 create_mode = CREATE_MODE_WAIT_FOR_INFO;
464 connect_set_field(&connect_server, NULL);
465 connect_set_field(&connect_port, NULL);
466 break;
467
468 case CREATE_MODE_WAIT_FOR_INFO:
469 if (strncmp(line, "host=", 5) == 0)
470 connect_set_field(&connect_server,
471 line + 5);
472 else if (strncmp(line, "port=", 5) == 0)
473 connect_set_field(&connect_port, line + 5);
474 else if (strcmp(line, "started") == 0) {
475 if (connect_get_server() != NULL
476 && connect_get_port() != NULL) {
477 create_mode =
478 CREATE_MODE_STARTING_OK;
479 } else {
480 create_mode =
481 CREATE_MODE_STARTING_ERROR;
482 }
483 net_close(ses);
484 metaserver_info.session = NULL;
485 } else
486 log_message(MSG_ERROR,
487 _("Unknown message from the "
488 "metaserver: %s\n"), line);
489 break;
490 case CREATE_MODE_STARTING_OK:
491 case CREATE_MODE_STARTING_ERROR:
492 log_message(MSG_ERROR,
493 _("Unknown message from the "
494 "metaserver: %s\n"), line);
495 break;
496 }
497 }
498 }
499
500 /* -------------------- get running servers info -------------------- */
501
check_str_info(const gchar * line,const gchar * prefix,gchar * data)502 static gboolean check_str_info(const gchar * line, const gchar * prefix,
503 gchar * data)
504 {
505 size_t len = strlen(prefix);
506
507 if (strncmp(line, prefix, len) != 0)
508 return FALSE;
509 strncpy(data, line + len, STRARG_LEN);
510 return TRUE;
511 }
512
check_int_info(const gchar * line,const gchar * prefix,gchar * data)513 static gboolean check_int_info(const gchar * line, const gchar * prefix,
514 gchar * data)
515 {
516 size_t len = strlen(prefix);
517
518 if (strncmp(line, prefix, len) != 0)
519 return FALSE;
520 sprintf(data, "%d", atoi(line + len));
521 return TRUE;
522 }
523
server_end(void)524 static void server_end(void)
525 {
526 GtkTreeIter iter;
527
528 if (meta_dlg) {
529 gtk_list_store_append(meta_games_model, &iter);
530 gtk_list_store_set(meta_games_model, &iter,
531 C_META_HOST, server_host,
532 C_META_PORT, server_port,
533 C_META_HOST_SORTABLE,
534 g_strdup_printf("%s:%s", server_host,
535 server_port),
536 C_META_VERSION, server_version,
537 C_META_MAX, server_max, C_META_CUR,
538 server_curr, C_META_TERRAIN,
539 server_terrain, C_META_VICTORY,
540 server_vpoints, C_META_SEVENS,
541 server_sevenrule, C_META_MAP,
542 server_title, -1);
543 }
544 }
545
meta_free_session(Session * ses)546 static void meta_free_session(Session * ses)
547 {
548 if (ses == metaserver_info.session) {
549 metaserver_info.session = NULL;
550 }
551 net_free(&ses);
552 }
553
554 /* Developer note: very similar code exists in server/meta.c
555 * Keep both routines up-to-date
556 */
meta_notify(Session * ses,NetEvent event,const gchar * line,G_GNUC_UNUSED gpointer user_data)557 static void meta_notify(Session * ses,
558 NetEvent event, const gchar * line,
559 G_GNUC_UNUSED gpointer user_data)
560 {
561 gchar argument[STRARG_LEN];
562 switch (event) {
563 case NET_READ:
564 if (ses != metaserver_info.session) {
565 log_message(MSG_ERROR,
566 _("Receiving data from inactive "
567 "session: %s\n"), line);
568 return;
569 }
570 switch (meta_mode) {
571 case MODE_SIGNON:
572 case MODE_REDIRECT:
573 if (strncmp(line, "goto ", 5) == 0) {
574 gchar **split_result;
575 const gchar *port;
576 if (metaserver_info.num_redirects++ >= 10) {
577 log_message(MSG_ERROR,
578 _("Too many "
579 "metaserver "
580 "redirects.\n"));
581 meta_mode = MODE_REDIRECT_OVERFLOW;
582 net_close(ses);
583 return;
584 }
585 meta_mode = MODE_REDIRECT;
586 meta_free_session(ses);
587 split_result = g_strsplit(line, " ", 0);
588 g_assert(split_result[0] != NULL);
589 g_assert(!strcmp(split_result[0], "goto"));
590 if (split_result[1]) {
591 g_free(metaserver_info.server);
592 g_free(metaserver_info.port);
593
594 metaserver_info.server =
595 g_strdup(split_result[1]);
596
597 port = PIONEERS_DEFAULT_META_PORT;
598 if (split_result[2])
599 port = split_result[2];
600 metaserver_info.port =
601 g_strdup(port);
602 query_metaserver
603 (metaserver_info.server,
604 metaserver_info.port);
605 } else {
606 log_message(MSG_ERROR,
607 _("Bad redirect line: "
608 "%s\n"), line);
609 };
610 g_strfreev(split_result);
611 break;
612 }
613
614 metaserver_info.version_major = 0;
615 metaserver_info.version_minor = 0;
616 metaserver_info.can_create_games = FALSE;
617 if (strncmp(line, "welcome ", 8) == 0) {
618 char *p = strstr(line, "version ");
619 if (p) {
620 p += 8;
621 metaserver_info.version_major =
622 atoi(p);
623 p += strspn(p, "0123456789");
624 if (*p == '.')
625 metaserver_info.
626 version_minor =
627 atoi(p + 1);
628 }
629 }
630 if (metaserver_info.version_major < 1) {
631 log_message(MSG_INFO,
632 _("The metaserver is too old "
633 "to create servers "
634 "(version %d.%d)\n"),
635 metaserver_info.version_major,
636 metaserver_info.version_minor);
637 } else {
638 net_printf(ses, "version %s\n",
639 META_PROTOCOL_VERSION);
640 }
641
642 if ((metaserver_info.version_major > 1) ||
643 (metaserver_info.version_major == 1
644 && metaserver_info.version_minor >= 1)) {
645 net_printf(ses, "capability\n");
646 meta_mode = MODE_CAPABILITY;
647 } else {
648 net_printf(ses,
649 metaserver_info.version_major >=
650 1 ? "listservers\n" :
651 "client\n");
652 meta_mode = MODE_LIST;
653 }
654 break;
655 case MODE_CAPABILITY:
656 if (!strcmp(line, "create games")) {
657 metaserver_info.can_create_games = TRUE;
658 } else if (!strcmp(line, "end")) {
659 net_printf(ses,
660 metaserver_info.version_major >=
661 1 ? "listservers\n" :
662 "client\n");
663 meta_mode = MODE_LIST;
664 }
665 break;
666 case MODE_LIST:
667 if (strcmp(line, "server") == 0) {
668 meta_mode = MODE_SERVER_INFO;
669 } else {
670 log_message(MSG_ERROR,
671 _("Unexpected data from the "
672 "metaserver: %s\n"), line);
673 }
674 break;
675 case MODE_SERVER_INFO:
676 if (strcmp(line, "end") == 0) {
677 server_end();
678 meta_mode = MODE_LIST;
679 } else
680 if (check_str_info(line, "host=", server_host))
681 break;
682 else if (check_str_info
683 (line, "port=", server_port))
684 break;
685 else if (check_str_info
686 (line, "version=", server_version))
687 break;
688 else if (check_int_info(line, "max=", server_max))
689 break;
690 else if (check_int_info
691 (line, "curr=", server_curr))
692 break;
693 else if (check_str_info
694 (line, "vpoints=", server_vpoints))
695 break;
696 else if (check_str_info
697 (line, "sevenrule=", argument)) {
698 if (server_sevenrule)
699 g_free(server_sevenrule);
700 if (!strcmp(argument, "normal")) {
701 server_sevenrule =
702 g_strdup(_("Normal"));
703 } else
704 if (!strcmp
705 (argument, "reroll first 2")) {
706 server_sevenrule =
707 g_strdup(_(""
708 "Reroll on 1st 2 turns"));
709 } else if (!strcmp(argument, "reroll all")) {
710 server_sevenrule =
711 g_strdup(_("Reroll all 7s"));
712 } else {
713 g_warning("Unknown seven rule: %s",
714 argument);
715 server_sevenrule =
716 g_strdup(argument);
717 }
718 break;
719 } else if (check_str_info
720 (line, "terrain=", argument)) {
721 if (server_terrain)
722 g_free(server_terrain);
723 if (!strcmp(argument, "default"))
724 server_terrain =
725 g_strdup(_("Default"));
726 else if (!strcmp(argument, "random"))
727 server_terrain =
728 g_strdup(_("Random"));
729 else {
730 g_warning
731 ("Unknown terrain type: %s",
732 argument);
733 server_terrain =
734 g_strdup(argument);
735 }
736 break;
737 } else
738 if (check_str_info
739 (line, "title=", server_title))
740 break;
741 /* meta-protocol 0 compat */
742 else if (check_str_info
743 (line, "map=", server_terrain))
744 break;
745 else if (check_str_info
746 (line, "comment=", server_title))
747 break;
748 else
749 log_message(MSG_ERROR,
750 _("Unexpected data from the "
751 "metaserver: %s\n"), line);
752 break;
753 case MODE_DONE:
754 case MODE_REDIRECT_OVERFLOW:
755 log_message(MSG_ERROR,
756 _("Unexpected data from the "
757 "metaserver: %s\n"), line);
758 break;
759 }
760 break;
761 case NET_CLOSE:
762 /* During a reconnect, different sessions might co-exist */
763 if (ses == metaserver_info.session) {
764 metaserver_info.session = NULL;
765 switch (meta_mode) {
766 case MODE_SIGNON:
767 case MODE_REDIRECT:
768 case MODE_SERVER_INFO:
769 case MODE_CAPABILITY:
770 log_message(MSG_ERROR,
771 _("The metaserver closed "
772 "the connection "
773 "unexpectedly.\n"));
774 break;
775 case MODE_REDIRECT_OVERFLOW:
776 if (meta_dlg)
777 gtk_widget_destroy(GTK_WIDGET(meta_dlg)); /* Close the dialog */
778 if (ses == metaserver_info.session) {
779 metaserver_info.session = NULL;
780 }
781 break;
782 case MODE_LIST:
783 case MODE_DONE:
784 close_waiting_box();
785 break;
786 }
787 if (meta_dlg) {
788 gtk_dialog_set_response_sensitive
789 (GTK_DIALOG(meta_dlg),
790 META_RESPONSE_NEW,
791 metaserver_info.can_create_games);
792 gtk_dialog_set_response_sensitive
793 (GTK_DIALOG(meta_dlg),
794 META_RESPONSE_REFRESH, TRUE);
795 }
796 }
797 net_free(&ses);
798 break;
799 case NET_CONNECT:
800 break;
801 case NET_CONNECT_FAIL:
802 /* Can't connect to the metaserver, don't show the GUI */
803 if (meta_dlg)
804 gtk_widget_destroy(GTK_WIDGET(meta_dlg));
805 if (ses == metaserver_info.session) {
806 metaserver_info.session = NULL;
807 }
808 net_free(&ses);
809 break;
810 }
811 }
812
query_metaserver(const gchar * server,const gchar * port)813 static void query_metaserver(const gchar * server, const gchar * port)
814 {
815 gchar *message;
816
817 if (metaserver_info.num_redirects > 0) {
818 if (strcmp(port, PIONEERS_DEFAULT_META_PORT) == 0) {
819 message =
820 g_strdup_printf(_("Redirected to the "
821 "metaserver at %s.\n"),
822 server);
823 } else {
824 message =
825 g_strdup_printf(_("Redirected to the "
826 "metaserver at %s, port %s.\n"),
827 server, port);
828 }
829 } else
830 message =
831 g_strdup(_
832 ("Receiving a list of Pioneers servers "
833 "from the metaserver.\n"));
834 show_waiting_box(message, server, port);
835 g_free(message);
836
837 g_assert(metaserver_info.session == NULL);
838 metaserver_info.session = net_new(meta_notify, NULL);
839 if (net_connect(metaserver_info.session, server, port))
840 meta_mode = MODE_SIGNON;
841 else {
842 net_free(&metaserver_info.session);
843 close_waiting_box();
844 }
845 }
846
847 /* -------------------- create server dialog -------------------- */
848
player_change_cb(GameSettings * gs,G_GNUC_UNUSED gpointer user_data)849 static void player_change_cb(GameSettings * gs,
850 G_GNUC_UNUSED gpointer user_data)
851 {
852 guint players;
853 guint ai_players;
854 GtkSpinButton *ai_spin;
855
856 ai_spin = GTK_SPIN_BUTTON(aiplayers_spin);
857 players = game_settings_get_players(gs);
858 ai_players = (guint) gtk_spin_button_get_value_as_int(ai_spin);
859 gtk_spin_button_set_range(ai_spin, 0, players - 1);
860 if (ai_players >= players)
861 gtk_spin_button_set_value(ai_spin, players - 1);
862 }
863
game_select_cb(SelectGame * sg,G_GNUC_UNUSED gpointer user_data)864 static void game_select_cb(SelectGame * sg,
865 G_GNUC_UNUSED gpointer user_data)
866 {
867 const GameParams *params;
868
869 params = select_game_get_active_game(sg);
870
871 if (params != NULL) {
872 game_settings_set_players(GAMESETTINGS(game_settings),
873 params->num_players);
874 game_settings_set_victory_points(GAMESETTINGS
875 (game_settings),
876 params->victory_points);
877 game_rules_set_random_terrain(GAMERULES(game_rules),
878 params->random_terrain);
879 game_rules_set_sevens_rule(GAMERULES(game_rules),
880 params->sevens_rule);
881
882 player_change_cb(GAMESETTINGS(game_settings), NULL);
883 }
884 }
885
build_create_interface(void)886 static GtkWidget *build_create_interface(void)
887 {
888 GtkWidget *vbox;
889 GtkWidget *label;
890 GtkAdjustment *adj;
891 guint row;
892
893 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
894 gtk_widget_show(vbox);
895 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
896
897 select_game = select_game_new();
898 gtk_widget_show(select_game);
899 gtk_box_pack_start(GTK_BOX(vbox), select_game, FALSE, FALSE, 3);
900
901 g_signal_connect(G_OBJECT(select_game), "activate",
902 G_CALLBACK(game_select_cb), NULL);
903
904 game_settings = game_settings_new(FALSE);
905 gtk_widget_show(game_settings);
906 gtk_box_pack_start(GTK_BOX(vbox), game_settings, FALSE, FALSE, 3);
907
908 /* Dynamically adjust the maximum number of AI's */
909 g_signal_connect(G_OBJECT(game_settings),
910 "change-players",
911 G_CALLBACK(player_change_cb), NULL);
912
913 /* Add the number of computer players at the end of the grid */
914 row = 99;
915 /* Label */
916 label = gtk_label_new(_("Number of computer players"));
917 gtk_widget_show(label);
918 gtk_grid_attach(GTK_GRID(game_settings), label, 0, row, 1, 1);
919 gtk_label_set_xalign(GTK_LABEL(label), 0.0);
920
921 adj = GTK_ADJUSTMENT(gtk_adjustment_new(0,
922 0,
923 game_settings_get_players
924 (GAMESETTINGS
925 (game_settings))
926 - 1, 1, 4, 0));
927 aiplayers_spin = gtk_spin_button_new(adj, 1, 0);
928 gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(aiplayers_spin), TRUE);
929 gtk_widget_show(aiplayers_spin);
930 gtk_entry_set_alignment(GTK_ENTRY(aiplayers_spin), 1.0);
931 gtk_grid_attach(GTK_GRID(game_settings), aiplayers_spin, 1, row, 1,
932 1);
933 gtk_widget_set_tooltip_text(aiplayers_spin,
934 /* Tooltip */
935 _("The number of computer players"));
936
937 game_rules = game_rules_new_metaserver();
938 gtk_widget_show(game_rules);
939 gtk_box_pack_start(GTK_BOX(vbox), game_rules, FALSE, FALSE, 3);
940
941 return vbox;
942 }
943
create_server_dlg_cb(GtkDialog * dlg,gint arg1,G_GNUC_UNUSED gpointer user_data)944 static void create_server_dlg_cb(GtkDialog * dlg, gint arg1,
945 G_GNUC_UNUSED gpointer user_data)
946 {
947 GameSettings *gs = GAMESETTINGS(game_settings);
948 SelectGame *sg = SELECTGAME(select_game);
949 GameRules *gr = GAMERULES(game_rules);
950
951 switch (arg1) {
952 case GTK_RESPONSE_OK:
953 gtk_dialog_set_response_sensitive(dlg, GTK_RESPONSE_OK,
954 FALSE);
955
956 if (metaserver_info.session != NULL) {
957 log_message(MSG_INFO, _("Canceled.\n"));
958 net_close(metaserver_info.session);
959 }
960 log_message(MSG_INFO, _("Requesting new game server.\n"));
961
962 cfg_terrain = game_rules_get_random_terrain(gr);
963 cfg_num_players = game_settings_get_players(gs);
964 cfg_victory_points = game_settings_get_victory_points(gs);
965 cfg_sevens_rule = game_rules_get_sevens_rule(gr);
966 cfg_ai_players = (guint)
967 gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON
968 (aiplayers_spin));
969 cfg_gametype = select_game_get_active_title(sg);
970
971 metaserver_info.session =
972 net_new(meta_create_notify, NULL);
973 if (net_connect
974 (metaserver_info.session, metaserver_info.server,
975 metaserver_info.port)) {
976 create_mode = CREATE_MODE_SIGNON;
977 } else {
978 log_message(MSG_ERROR,
979 _("The metaserver is not available "
980 "anymore.\n"));
981 net_free(&metaserver_info.session);
982 gtk_widget_destroy(GTK_WIDGET(dlg));
983 }
984 break;
985 case GTK_RESPONSE_CANCEL:
986 default: /* For the compiler */
987 gtk_widget_destroy(GTK_WIDGET(dlg));
988 if (metaserver_info.session != NULL) {
989 net_close(metaserver_info.session);
990 /* Canceled retrieving information
991 * from the metaserver */
992 log_message(MSG_INFO, _("Canceled.\n"));
993 }
994 break;
995 };
996 }
997
998 /** Launch the server gtk. */
launch_server_gtk(G_GNUC_UNUSED GtkWidget * widget,G_GNUC_UNUSED GtkWindow * parent)999 static void launch_server_gtk(G_GNUC_UNUSED GtkWidget * widget,
1000 G_GNUC_UNUSED GtkWindow * parent)
1001 {
1002 gchar *child_argv[3];
1003 GSpawnFlags flags = G_SPAWN_STDOUT_TO_DEV_NULL |
1004 G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_SEARCH_PATH;
1005 GError *error = NULL;
1006 gint i;
1007
1008 child_argv[0] = g_strdup(PIONEERS_SERVER_GTK_PROGRAM_NAME);
1009 child_argv[1] = g_strdup(PIONEERS_SERVER_GTK_PROGRAM_NAME);
1010 child_argv[2] = NULL;
1011 if (!g_spawn_async(NULL, child_argv, NULL, flags, NULL, NULL, NULL,
1012 &error)) {
1013 log_message(MSG_ERROR,
1014 /* Error message when program %1 is started, reason is %2 */
1015 _("Error starting %s: %s\n"),
1016 child_argv[0], error->message);
1017 g_error_free(error);
1018 }
1019 for (i = 0; child_argv[i] != NULL; i++)
1020 g_free(child_argv[i]);
1021 }
1022
create_server_dlg(G_GNUC_UNUSED GtkWidget * widget,GtkWindow * parent)1023 static void create_server_dlg(G_GNUC_UNUSED GtkWidget * widget,
1024 GtkWindow * parent)
1025 {
1026 GtkWidget *dlg_vbox;
1027 GtkWidget *vbox;
1028
1029 if (cserver_dlg) {
1030 gtk_window_present(GTK_WINDOW(cserver_dlg));
1031 return;
1032 }
1033 set_metaserver_info();
1034
1035 cserver_dlg =
1036 /* Dialog caption */
1037 gtk_dialog_new_with_buttons(_("Create a Public Game"), parent,
1038 GTK_DIALOG_DESTROY_WITH_PARENT,
1039 /* Button text */
1040 _("_Cancel"), GTK_RESPONSE_CANCEL,
1041 /* Button text */
1042 _("C_reate"), GTK_RESPONSE_OK,
1043 NULL);
1044 g_signal_connect(G_OBJECT(cserver_dlg), "destroy",
1045 G_CALLBACK(gtk_widget_destroyed), &cserver_dlg);
1046 gtk_widget_realize(cserver_dlg);
1047
1048 dlg_vbox = gtk_dialog_get_content_area(GTK_DIALOG(cserver_dlg));
1049 gtk_widget_show(dlg_vbox);
1050
1051 vbox = build_create_interface();
1052 gtk_widget_show(vbox);
1053 gtk_box_pack_start(GTK_BOX(dlg_vbox), vbox, TRUE, TRUE, 0);
1054 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
1055
1056 if (cserver_dlg != NULL) {
1057 gtk_dialog_set_response_sensitive(GTK_DIALOG(cserver_dlg),
1058 GTK_RESPONSE_OK, FALSE);
1059 }
1060 g_signal_connect(G_OBJECT(cserver_dlg), "response",
1061 G_CALLBACK(create_server_dlg_cb), NULL);
1062 gtk_widget_show(cserver_dlg);
1063
1064 get_metaserver_games_types(metaserver_info.server,
1065 metaserver_info.port);
1066
1067 }
1068
1069 /* -------------------- select server dialog -------------------- */
1070
meta_click_cb(G_GNUC_UNUSED GtkWidget * widget,G_GNUC_UNUSED GdkEventButton * event,G_GNUC_UNUSED gpointer user_data)1071 static gint meta_click_cb(G_GNUC_UNUSED GtkWidget * widget,
1072 G_GNUC_UNUSED GdkEventButton * event,
1073 G_GNUC_UNUSED gpointer user_data)
1074 {
1075 if (event->type == GDK_2BUTTON_PRESS) {
1076 gtk_dialog_response(GTK_DIALOG(meta_dlg), GTK_RESPONSE_OK);
1077 };
1078 return FALSE;
1079 }
1080
meta_select_cb(G_GNUC_UNUSED GtkTreeSelection * selection,G_GNUC_UNUSED gpointer user_data)1081 static void meta_select_cb(G_GNUC_UNUSED GtkTreeSelection * selection,
1082 G_GNUC_UNUSED gpointer user_data)
1083 {
1084 gtk_dialog_set_response_sensitive(GTK_DIALOG(meta_dlg),
1085 GTK_RESPONSE_OK, TRUE);
1086 }
1087
meta_dlg_cb(GtkDialog * dlg,gint arg1,G_GNUC_UNUSED gpointer userdata)1088 static void meta_dlg_cb(GtkDialog * dlg, gint arg1,
1089 G_GNUC_UNUSED gpointer userdata)
1090 {
1091 GtkTreePath *path;
1092 GtkTreeViewColumn *column;
1093 GtkTreeIter iter;
1094 gchar *host;
1095 gchar *port;
1096
1097 switch (arg1) {
1098 case META_RESPONSE_REFRESH: /* Refresh the list */
1099 gtk_list_store_clear(meta_games_model);
1100 metaserver_info.num_redirects = 0;
1101 query_metaserver(metaserver_info.server,
1102 metaserver_info.port);
1103 break;
1104 case META_RESPONSE_NEW: /* Add a server */
1105 create_server_dlg(NULL, GTK_WINDOW(dlg));
1106 break;
1107 case GTK_RESPONSE_OK: /* Select this server */
1108 gtk_tree_view_get_cursor(GTK_TREE_VIEW(meta_games_view),
1109 &path, &column);
1110 if (path != NULL) {
1111 gtk_tree_model_get_iter(GTK_TREE_MODEL
1112 (meta_games_model), &iter,
1113 path);
1114 gtk_tree_model_get(GTK_TREE_MODEL
1115 (meta_games_model), &iter,
1116 C_META_HOST, &host, C_META_PORT,
1117 &port, -1);
1118 connect_set_field(&connect_server, host);
1119 connect_set_field(&connect_port, port);
1120 g_free(host);
1121 g_free(port);
1122 gtk_tree_path_free(path);
1123 connect_close_all(TRUE, TRUE);
1124 }
1125 break;
1126 case GTK_RESPONSE_CANCEL: /* Cancel */
1127 default:
1128 gtk_widget_destroy(GTK_WIDGET(dlg));
1129 if (metaserver_info.session != NULL) {
1130 net_close(metaserver_info.session);
1131 /* Canceled retrieving information
1132 * from the metaserver */
1133 log_message(MSG_INFO, _("Canceled.\n"));
1134 }
1135 break;
1136 }
1137 }
1138
set_metaserver_info(void)1139 static void set_metaserver_info(void)
1140 {
1141 gchar *meta_tmp;
1142
1143 g_free(metaserver_info.server);
1144 g_free(metaserver_info.port);
1145
1146 meta_tmp = metaserver_get(METASERVER(metaserver_entry));
1147 metaserver_info.server = meta_tmp; /* Take-over of the pointer */
1148 metaserver_info.port = g_strdup(PIONEERS_DEFAULT_META_PORT);
1149 }
1150
create_meta_dlg(G_GNUC_UNUSED GtkWidget * widget,GtkWidget * parent)1151 static void create_meta_dlg(G_GNUC_UNUSED GtkWidget * widget,
1152 GtkWidget * parent)
1153 {
1154 GtkWidget *dlg_vbox;
1155 GtkWidget *vbox;
1156 GtkWidget *scroll_win;
1157 GtkTreeViewColumn *column;
1158 GtkCellRenderer *renderer;
1159
1160 set_metaserver_info();
1161
1162 if (meta_dlg != NULL) {
1163 if (metaserver_info.session == NULL) {
1164 gtk_list_store_clear(meta_games_model);
1165 metaserver_info.num_redirects = 0;
1166 metaserver_info.version_major = 0;
1167 metaserver_info.version_minor = 0;
1168 metaserver_info.can_create_games = FALSE;
1169 gtk_dialog_set_response_sensitive
1170 (GTK_DIALOG(meta_dlg),
1171 META_RESPONSE_NEW,
1172 metaserver_info.can_create_games);
1173
1174 query_metaserver(metaserver_info.server,
1175 metaserver_info.port);
1176 }
1177 gtk_window_present(GTK_WINDOW(meta_dlg));
1178 return;
1179 }
1180
1181 /* Dialog caption */
1182 meta_dlg = gtk_dialog_new_with_buttons(_("Join a Public Game"),
1183 GTK_WINDOW(parent), 0,
1184 /* Button text */
1185 _("_Refresh"),
1186 META_RESPONSE_REFRESH,
1187 /* Button text */
1188 _("_New Remote Game"),
1189 META_RESPONSE_NEW,
1190 /* Button text */
1191 _("_Cancel"),
1192 GTK_RESPONSE_CANCEL,
1193 /* Button text */
1194 _("_Join"),
1195 GTK_RESPONSE_OK, NULL);
1196 g_signal_connect(G_OBJECT(meta_dlg), "destroy",
1197 G_CALLBACK(gtk_widget_destroyed), &meta_dlg);
1198 g_signal_connect(G_OBJECT(meta_dlg), "response",
1199 G_CALLBACK(meta_dlg_cb), NULL);
1200 gtk_widget_realize(meta_dlg);
1201
1202 dlg_vbox = gtk_dialog_get_content_area(GTK_DIALOG(meta_dlg));
1203 gtk_widget_show(dlg_vbox);
1204
1205 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
1206 gtk_widget_show(vbox);
1207 gtk_box_pack_start(GTK_BOX(dlg_vbox), vbox, TRUE, TRUE, 0);
1208 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
1209
1210 server_status = gtk_label_new("");
1211 gtk_widget_show(server_status);
1212 gtk_box_pack_start(GTK_BOX(vbox), server_status, FALSE, TRUE, 0);
1213
1214 scroll_win = gtk_scrolled_window_new(NULL, NULL);
1215 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win),
1216 GTK_POLICY_NEVER,
1217 GTK_POLICY_AUTOMATIC);
1218 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW
1219 (scroll_win), GTK_SHADOW_IN);
1220 gtk_widget_show(scroll_win);
1221 gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0);
1222
1223 meta_games_model = gtk_list_store_new(META_N_COLUMNS,
1224 G_TYPE_STRING, G_TYPE_STRING,
1225 G_TYPE_STRING, G_TYPE_STRING,
1226 G_TYPE_STRING, G_TYPE_STRING,
1227 G_TYPE_STRING, G_TYPE_STRING,
1228 G_TYPE_STRING,
1229 G_TYPE_STRING);
1230
1231 meta_games_view =
1232 gtk_tree_view_new_with_model(GTK_TREE_MODEL(meta_games_model));
1233 gtk_widget_show(meta_games_view);
1234 gtk_container_add(GTK_CONTAINER(scroll_win), meta_games_view);
1235 gtk_widget_set_tooltip_text(meta_games_view,
1236 /* Tooltip */
1237 _("Select a game to join"));
1238
1239 column =
1240 /* Column name */
1241 gtk_tree_view_column_new_with_attributes(_("Map Name"),
1242 gtk_cell_renderer_text_new
1243 (), "text",
1244 C_META_MAP, NULL);
1245 gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1246 column);
1247 gtk_tree_view_column_set_sort_column_id(column, C_META_MAP);
1248 /* Tooltip for column 'Map Name' */
1249 set_tooltip_on_column(column, _("Name of the game"));
1250
1251 renderer = gtk_cell_renderer_text_new();
1252 g_object_set(renderer, "xalign", 1.0f, NULL);
1253 column =
1254 /* Column name */
1255 gtk_tree_view_column_new_with_attributes(_("Curr"), renderer,
1256 "text", C_META_CUR,
1257 NULL);
1258 gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1259 column);
1260 gtk_tree_view_column_set_sort_column_id(column, C_META_CUR);
1261 /* Tooltip for column 'Curr' */
1262 set_tooltip_on_column(column, _("Number of players in the game"));
1263
1264 renderer = gtk_cell_renderer_text_new();
1265 g_object_set(renderer, "xalign", 1.0f, NULL);
1266 column =
1267 /* Column name */
1268 gtk_tree_view_column_new_with_attributes(_("Max"), renderer,
1269 "text", C_META_MAX,
1270 NULL);
1271 gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1272 column);
1273 gtk_tree_view_column_set_sort_column_id(column, C_META_MAX);
1274 /* Tooltip for column 'Max' */
1275 set_tooltip_on_column(column, _("Maximum players for the game"));
1276
1277 column =
1278 /* Column name */
1279 gtk_tree_view_column_new_with_attributes(_("Terrain"),
1280 gtk_cell_renderer_text_new
1281 (), "text",
1282 C_META_TERRAIN, NULL);
1283 gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1284 column);
1285 gtk_tree_view_column_set_sort_column_id(column, C_META_TERRAIN);
1286 /* Tooltip for column 'Terrain' */
1287 set_tooltip_on_column(column, _("Random of default terrain"));
1288
1289 renderer = gtk_cell_renderer_text_new();
1290 g_object_set(renderer, "xalign", 1.0f, NULL);
1291 column =
1292 /* Column name */
1293 gtk_tree_view_column_new_with_attributes(_("Vic. Points"),
1294 renderer, "text",
1295 C_META_VICTORY, NULL);
1296 gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1297 column);
1298 gtk_tree_view_column_set_sort_column_id(column, C_META_VICTORY);
1299 /* Tooltip for column 'Vic. Points' */
1300 set_tooltip_on_column(column, _("Points needed to win"));
1301
1302 column =
1303 /* Column name */
1304 gtk_tree_view_column_new_with_attributes(_("Sevens Rule"),
1305 gtk_cell_renderer_text_new
1306 (), "text",
1307 C_META_SEVENS, NULL);
1308 gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1309 column);
1310 gtk_tree_view_column_set_sort_column_id(column, C_META_SEVENS);
1311 /* Tooltip for column 'Sevens Rule' */
1312 set_tooltip_on_column(column, _("Sevens rule"));
1313
1314 column =
1315 /* Column name */
1316 gtk_tree_view_column_new_with_attributes(_("Host"),
1317 gtk_cell_renderer_text_new
1318 (), "text",
1319 C_META_HOST, NULL);
1320 gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1321 column);
1322 gtk_tree_view_column_set_sort_column_id(column,
1323 C_META_HOST_SORTABLE);
1324 /* Tooltip for column 'Host' */
1325 set_tooltip_on_column(column, _("Host of the game"));
1326
1327 renderer = gtk_cell_renderer_text_new();
1328 g_object_set(renderer, "xalign", 1.0f, NULL);
1329 column =
1330 /* Column name */
1331 gtk_tree_view_column_new_with_attributes(_("Port"), renderer,
1332 "text", C_META_PORT,
1333 NULL);
1334 gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1335 column);
1336 gtk_tree_view_column_set_sort_column_id(column,
1337 C_META_HOST_SORTABLE);
1338 /* Tooltip for column 'Port' */
1339 set_tooltip_on_column(column, _("Port of the game"));
1340
1341 column =
1342 /* Column name */
1343 gtk_tree_view_column_new_with_attributes(_("Version"),
1344 gtk_cell_renderer_text_new
1345 (), "text",
1346 C_META_VERSION, NULL);
1347 gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1348 column);
1349 gtk_tree_view_column_set_sort_column_id(column, C_META_VERSION);
1350 /* Tooltip for column 'Version' */
1351 set_tooltip_on_column(column, _("Version of the host"));
1352 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE
1353 (meta_games_model),
1354 C_META_HOST_SORTABLE,
1355 GTK_SORT_ASCENDING);
1356
1357 /* Register double-click */
1358 g_signal_connect(G_OBJECT(meta_games_view), "button_press_event",
1359 G_CALLBACK(meta_click_cb), NULL);
1360
1361 g_signal_connect(G_OBJECT
1362 (gtk_tree_view_get_selection
1363 (GTK_TREE_VIEW(meta_games_view))), "changed",
1364 G_CALLBACK(meta_select_cb), NULL);
1365
1366 /* This button will be enabled when a game is selected */
1367 gtk_dialog_set_response_sensitive(GTK_DIALOG(meta_dlg),
1368 GTK_RESPONSE_OK, FALSE);
1369
1370 /* These buttons will be enabled when the metaserver has responded */
1371 gtk_dialog_set_response_sensitive(GTK_DIALOG(meta_dlg),
1372 META_RESPONSE_NEW, FALSE);
1373 gtk_dialog_set_response_sensitive(GTK_DIALOG(meta_dlg),
1374 META_RESPONSE_REFRESH, FALSE);
1375
1376 gtk_widget_show(meta_dlg);
1377
1378 metaserver_info.num_redirects = 0;
1379 query_metaserver(metaserver_info.server, metaserver_info.port);
1380
1381 /* Workaround: Set the size of the widget as late as possible, to avoid a strange display */
1382 gtk_widget_set_size_request(scroll_win, -1, 150);
1383 }
1384
connect_dlg_cb(G_GNUC_UNUSED GtkDialog * dlg,G_GNUC_UNUSED gint arg1,G_GNUC_UNUSED gpointer userdata)1385 static void connect_dlg_cb(G_GNUC_UNUSED GtkDialog * dlg,
1386 G_GNUC_UNUSED gint arg1,
1387 G_GNUC_UNUSED gpointer userdata)
1388 {
1389 connect_close_all(FALSE, FALSE);
1390 }
1391
1392 #ifdef HAVE_AVAHI
connect_avahi_cb(G_GNUC_UNUSED GtkWidget * widget,GtkWidget * avahibrowser_entry)1393 static void connect_avahi_cb(G_GNUC_UNUSED GtkWidget * widget,
1394 GtkWidget * avahibrowser_entry)
1395 {
1396 gchar *server =
1397 avahibrowser_get_server(AVAHIBROWSER(avahibrowser_entry));
1398 gchar *port =
1399 avahibrowser_get_port(AVAHIBROWSER(avahibrowser_entry));
1400
1401 connect_set_field(&connect_server, server);
1402 connect_set_field(&connect_port, port);
1403
1404 g_free(server);
1405 g_free(port);
1406
1407 /* connect */
1408 connect_close_all(TRUE, TRUE);
1409 }
1410 #endif
1411
connect_create_dlg(void)1412 void connect_create_dlg(void)
1413 {
1414 GtkWidget *dlg_vbox;
1415 GtkWidget *grid;
1416 GtkWidget *lbl;
1417 GtkWidget *hbox;
1418 GtkWidget *btn;
1419 GtkWidget *sep;
1420 #ifdef HAVE_AVAHI
1421 GtkWidget *avahibrowser_entry;
1422 #endif /* HAVE_AVAHI */
1423 gchar *fullname;
1424 gchar *name;
1425 guint row;
1426
1427 if (connect_dlg) {
1428 gtk_window_present(GTK_WINDOW(connect_dlg));
1429 return;
1430 }
1431
1432 g_signal_connect(requested_name, "changed",
1433 G_CALLBACK(connect_name_change_cb), NULL);
1434
1435 /* Dialog caption */
1436 connect_dlg = gtk_dialog_new_with_buttons(_("Start a New Game"),
1437 GTK_WINDOW(app_window),
1438 0,
1439 /* Button text */
1440 _("_Cancel"),
1441 GTK_RESPONSE_CANCEL,
1442 NULL);
1443 g_signal_connect(G_OBJECT(connect_dlg), "response",
1444 G_CALLBACK(connect_dlg_cb), NULL);
1445 g_signal_connect(G_OBJECT(connect_dlg), "destroy",
1446 G_CALLBACK(connect_dlg_destroyed), &connect_dlg);
1447
1448 gtk_widget_realize(connect_dlg);
1449 gdk_window_set_functions(gtk_widget_get_window(connect_dlg),
1450 GDK_FUNC_MOVE | GDK_FUNC_CLOSE);
1451
1452 dlg_vbox = gtk_dialog_get_content_area(GTK_DIALOG(connect_dlg));
1453 gtk_widget_show(dlg_vbox);
1454
1455 grid = gtk_grid_new();
1456 row = 0;
1457 gtk_widget_show(grid);
1458 gtk_box_pack_start(GTK_BOX(dlg_vbox), grid, FALSE, TRUE, 0);
1459 gtk_container_set_border_width(GTK_CONTAINER(grid), 5);
1460 gtk_grid_set_row_spacing(GTK_GRID(grid), 3);
1461 gtk_grid_set_column_spacing(GTK_GRID(grid), 5);
1462
1463 /* Label */
1464 lbl = gtk_label_new(_("Player name"));
1465 gtk_widget_show(lbl);
1466 gtk_grid_attach(GTK_GRID(grid), lbl, 0, row, 1, 1);
1467 gtk_label_set_xalign(GTK_LABEL(lbl), 0.0);
1468
1469 name = notifying_string_get(requested_name);
1470 name_entry = gtk_entry_new();
1471 gtk_entry_set_max_length(GTK_ENTRY(name_entry), MAX_NAME_LENGTH);
1472 gtk_widget_show(name_entry);
1473 gtk_entry_set_text(GTK_ENTRY(name_entry), name);
1474 g_free(name);
1475
1476 gtk_grid_attach(GTK_GRID(grid), name_entry, 1, row, 1, 1);
1477 /* Tooltip */
1478 gtk_widget_set_tooltip_text(name_entry, _("Enter your name"));
1479
1480 /* Check button */
1481 spectator_toggle = gtk_check_button_new_with_label(_("Spectator"));
1482 gtk_widget_show(spectator_toggle);
1483 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(spectator_toggle),
1484 connect_spectator);
1485
1486 gtk_grid_attach(GTK_GRID(grid), spectator_toggle, 2, row, 1, 1);
1487 gtk_widget_set_tooltip_text(spectator_toggle,
1488 /* Tooltip for checkbox Spectator */
1489 _
1490 ("Check if you want to be a spectator"));
1491 row++;
1492
1493 sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1494 gtk_widget_show(sep);
1495 gtk_grid_attach(GTK_GRID(grid), sep, 0, row, 3, 1);
1496 row++;
1497
1498 #ifdef HAVE_AVAHI
1499 /* Label */
1500 lbl = gtk_label_new(_("Avahi"));
1501 gtk_widget_show(lbl);
1502 gtk_grid_attach(GTK_GRID(grid), lbl, 0, row, 1, 1);
1503 gtk_label_set_xalign(GTK_LABEL(lbl), 0.0);
1504
1505 /* Button */
1506 btn = gtk_button_new_with_label(_("Join"));
1507 gtk_widget_show(btn);
1508 gtk_widget_set_tooltip_text(btn,
1509 /* Tooltip for button Join */
1510 _(""
1511 "Join an automatically discovered game"));
1512 gtk_grid_attach(GTK_GRID(grid), btn, 2, row, 1, 1);
1513
1514 avahibrowser_entry = avahibrowser_new(btn);
1515 gtk_widget_show(avahibrowser_entry);
1516 gtk_grid_attach(GTK_GRID(grid), avahibrowser_entry, 1, row, 1, 1);
1517 row++;
1518
1519 g_signal_connect(G_OBJECT(btn), "clicked",
1520 G_CALLBACK(connect_avahi_cb), avahibrowser_entry);
1521
1522 /* enable avahi */
1523 /* storing the pointer to this widget for later use */
1524 avahi_register(AVAHIBROWSER(avahibrowser_entry));
1525
1526 sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1527 gtk_widget_show(sep);
1528 gtk_grid_attach(GTK_GRID(grid), sep, 0, row, 3, 1);
1529 row++;
1530 #endif /* HAVE_AVAHI */
1531
1532 /* Label */
1533 lbl = gtk_label_new(_("Metaserver"));
1534 gtk_widget_show(lbl);
1535 gtk_grid_attach(GTK_GRID(grid), lbl, 0, row, 1, 1);
1536 gtk_label_set_xalign(GTK_LABEL(lbl), 0.0);
1537
1538 metaserver_entry = metaserver_new();
1539 gtk_widget_show(metaserver_entry);
1540 gtk_grid_attach(GTK_GRID(grid), metaserver_entry, 1, row, 2, 1);
1541 metaserver_add(METASERVER(metaserver_entry),
1542 metaserver_info.server);
1543 row++;
1544
1545 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
1546 gtk_widget_show(hbox);
1547 gtk_grid_attach(GTK_GRID(grid), hbox, 0, row, 3, 1);
1548 row++;
1549
1550 /* Button */
1551 btn = gtk_button_new_with_label(_("Join Public Game"));
1552 gtk_widget_show(btn);
1553 gtk_box_pack_start(GTK_BOX(hbox), btn, TRUE, TRUE, 0);
1554 g_signal_connect(G_OBJECT(btn), "clicked",
1555 G_CALLBACK(create_meta_dlg), app_window);
1556 /* Tooltip */
1557 gtk_widget_set_tooltip_text(btn, _("Join a public game"));
1558 gtk_widget_set_can_default(btn, TRUE);
1559 gtk_widget_grab_default(btn);
1560
1561 /* Button */
1562 btn = gtk_button_new_with_label(_("Create Game"));
1563 gtk_widget_show(btn);
1564 gtk_box_pack_start(GTK_BOX(hbox), btn, TRUE, TRUE, 0);
1565 /* Tooltip */
1566 gtk_widget_set_tooltip_text(btn, _("Create a game"));
1567 g_signal_connect(G_OBJECT(btn), "clicked",
1568 G_CALLBACK(launch_server_gtk), app_window);
1569 fullname =
1570 g_find_program_in_path(PIONEERS_SERVER_GTK_PROGRAM_NAME);
1571 gtk_widget_set_sensitive(GTK_WIDGET(btn), fullname != NULL);
1572 g_free(fullname);
1573
1574 /* Button */
1575 btn = gtk_button_new_with_label(_("Join Private Game"));
1576 gtk_widget_show(btn);
1577 gtk_box_pack_start(GTK_BOX(hbox), btn, TRUE, TRUE, 0);
1578 /* Tooltip */
1579 gtk_widget_set_tooltip_text(btn, _("Join a private game"));
1580 g_signal_connect(G_OBJECT(btn), "clicked",
1581 G_CALLBACK(connect_private_dialog), app_window);
1582
1583 gtk_entry_set_activates_default(GTK_ENTRY(name_entry), TRUE);
1584
1585 gtk_widget_show(connect_dlg);
1586
1587 gtk_widget_grab_focus(name_entry);
1588 }
1589
1590 /* ------------ Join a private game dialog ------------------- */
1591
update_recent_servers_list(void)1592 static void update_recent_servers_list(void)
1593 {
1594 gchar keyname1[50], keyname2[50];
1595 gchar *temp_name, *temp_port;
1596 gchar *cur_name, *cur_port;
1597 gchar *conn_name, *conn_port;
1598 gboolean default_used;
1599 gboolean done;
1600 gint i;
1601
1602 done = FALSE;
1603 i = 0;
1604
1605 conn_name = g_strdup(connect_get_server());
1606 conn_port = g_strdup(connect_get_port());
1607
1608 temp_name = g_strdup(conn_name);
1609 temp_port = g_strdup(conn_port);
1610
1611 do {
1612 sprintf(keyname1, "favorites/server%dname=", i);
1613 sprintf(keyname2, "favorites/server%dport=", i);
1614 cur_name =
1615 g_strdup(config_get_string(keyname1, &default_used));
1616 cur_port =
1617 g_strdup(config_get_string(keyname2, &default_used));
1618
1619 if (temp_name) {
1620 sprintf(keyname1, "favorites/server%dname", i);
1621 sprintf(keyname2, "favorites/server%dport", i);
1622 config_set_string(keyname1, temp_name);
1623 config_set_string(keyname2, temp_port);
1624 } else {
1625 g_free(cur_name);
1626 g_free(cur_port);
1627 break;
1628 }
1629
1630 if (strlen(cur_name) == 0) {
1631 g_free(cur_name);
1632 g_free(cur_port);
1633 break;
1634 }
1635
1636 g_free(temp_name);
1637 g_free(temp_port);
1638 if (!strcmp(cur_name, conn_name)
1639 && !strcmp(cur_port, conn_port)) {
1640 temp_name = NULL;
1641 temp_port = NULL;
1642 } else {
1643 temp_name = g_strdup(cur_name);
1644 temp_port = g_strdup(cur_port);
1645 }
1646
1647 i++;
1648 if (i > PRIVATE_GAME_HISTORY_SIZE) {
1649 done = TRUE;
1650 }
1651 g_free(cur_name);
1652 g_free(cur_port);
1653 } while (!done);
1654 g_free(temp_name);
1655 g_free(temp_port);
1656 g_free(conn_name);
1657 g_free(conn_port);
1658 }
1659
host_list_select_cb(GtkWidget * widget,gpointer user_data)1660 static void host_list_select_cb(GtkWidget * widget, gpointer user_data)
1661 {
1662 GPtrArray *host_entries = user_data;
1663 gint idx;
1664 gchar *entry;
1665 gchar **strs;
1666
1667 idx = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
1668 entry = g_ptr_array_index(host_entries, idx);
1669 strs = g_strsplit(entry, ":", 2);
1670
1671 connect_set_field(&connect_server, strs[0]);
1672 connect_set_field(&connect_port, strs[1]);
1673 gtk_entry_set_text(GTK_ENTRY(host_entry), connect_server);
1674 gtk_entry_set_text(GTK_ENTRY(port_entry), connect_port);
1675 g_strfreev(strs);
1676 }
1677
1678
connect_private_dlg_cb(GtkDialog * dlg,gint arg1,G_GNUC_UNUSED gpointer user_data)1679 static void connect_private_dlg_cb(GtkDialog * dlg, gint arg1,
1680 G_GNUC_UNUSED gpointer user_data)
1681 {
1682 switch (arg1) {
1683 case GTK_RESPONSE_OK:
1684 connect_set_field(&connect_server,
1685 gtk_entry_get_text(GTK_ENTRY
1686 (host_entry)));
1687 connect_set_field(&connect_port,
1688 gtk_entry_get_text(GTK_ENTRY
1689 (port_entry)));
1690 update_recent_servers_list();
1691 config_set_string("connect/server", connect_server);
1692 config_set_string("connect/port", connect_port);
1693 connect_close_all(TRUE, TRUE);
1694 break;
1695 case GTK_RESPONSE_CANCEL:
1696 default: /* For the compiler */
1697 gtk_widget_destroy(GTK_WIDGET(dlg));
1698 break;
1699 };
1700 }
1701
connect_private_dialog(G_GNUC_UNUSED GtkWidget * widget,GtkWindow * parent)1702 static void connect_private_dialog(G_GNUC_UNUSED GtkWidget * widget,
1703 GtkWindow * parent)
1704 {
1705 GtkWidget *dlg_vbox;
1706 GtkWidget *grid;
1707 GtkWidget *lbl;
1708 GtkWidget *hbox;
1709
1710 GtkWidget *host_list;
1711 GPtrArray *host_entries;
1712
1713 gint i;
1714 gchar *host_name, *host_port, *host_name_port, temp_str[50];
1715 gboolean default_returned;
1716
1717 if (connect_private_dlg) {
1718 gtk_window_present(GTK_WINDOW(connect_private_dlg));
1719 return;
1720 }
1721
1722 connect_private_dlg = gtk_dialog_new_with_buttons(
1723 /* Dialog caption */
1724 _(""
1725 "Join a private game"),
1726 GTK_WINDOW
1727 (parent),
1728 0,
1729 /* Button text */
1730 _
1731 ("_Cancel"),
1732 GTK_RESPONSE_CANCEL,
1733 /* Button text */
1734 _
1735 ("_Join"),
1736 GTK_RESPONSE_OK,
1737 NULL);
1738 gtk_dialog_set_default_response(GTK_DIALOG(connect_private_dlg),
1739 GTK_RESPONSE_OK);
1740 g_signal_connect(G_OBJECT(connect_private_dlg), "response",
1741 G_CALLBACK(connect_private_dlg_cb), NULL);
1742 g_signal_connect(G_OBJECT(connect_private_dlg), "destroy",
1743 G_CALLBACK(gtk_widget_destroyed),
1744 &connect_private_dlg);
1745
1746
1747 gtk_widget_realize(connect_private_dlg);
1748 gdk_window_set_functions(gtk_widget_get_window
1749 (connect_private_dlg),
1750 GDK_FUNC_MOVE | GDK_FUNC_CLOSE);
1751
1752 dlg_vbox =
1753 gtk_dialog_get_content_area(GTK_DIALOG(connect_private_dlg));
1754 gtk_widget_show(dlg_vbox);
1755
1756 grid = gtk_grid_new();
1757 gtk_widget_show(grid);
1758 gtk_box_pack_start(GTK_BOX(dlg_vbox), grid, FALSE, TRUE, 0);
1759 gtk_container_set_border_width(GTK_CONTAINER(grid), 5);
1760 gtk_grid_set_row_spacing(GTK_GRID(grid), 3);
1761 gtk_grid_set_column_spacing(GTK_GRID(grid), 5);
1762
1763 /* Label */
1764 lbl = gtk_label_new(_("Server host"));
1765 gtk_widget_show(lbl);
1766 gtk_grid_attach(GTK_GRID(grid), lbl, 0, 0, 1, 1);
1767 gtk_label_set_xalign(GTK_LABEL(lbl), 0.0);
1768
1769 host_entry = gtk_entry_new();
1770 gtk_widget_show(host_entry);
1771 gtk_grid_attach(GTK_GRID(grid), host_entry, 1, 0, 1, 1);
1772 gtk_entry_set_text(GTK_ENTRY(host_entry), connect_server);
1773 gtk_widget_set_tooltip_text(host_entry,
1774 /* Tooltip */
1775 _("Name of the host of the game"));
1776 connect_set_field(&connect_server, connect_server);
1777
1778 /* Label */
1779 lbl = gtk_label_new(_("Server port"));
1780 gtk_widget_show(lbl);
1781 gtk_grid_attach(GTK_GRID(grid), lbl, 0, 1, 1, 1);
1782 gtk_label_set_xalign(GTK_LABEL(lbl), 0.0);
1783
1784 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1785 gtk_widget_show(hbox);
1786 gtk_grid_attach(GTK_GRID(grid), hbox, 1, 1, 1, 1);
1787
1788 port_entry = gtk_entry_new();
1789 gtk_widget_show(port_entry);
1790 gtk_box_pack_start(GTK_BOX(hbox), port_entry, FALSE, TRUE, 0);
1791 gtk_entry_set_text(GTK_ENTRY(port_entry), connect_port);
1792 gtk_widget_set_tooltip_text(port_entry,
1793 /* Tooltip */
1794 _("Port of the host of the game"));
1795 connect_set_field(&connect_port, connect_port);
1796
1797 host_list = gtk_combo_box_text_new();
1798 host_entries = g_ptr_array_new();
1799
1800 gtk_widget_show(host_list);
1801
1802 for (i = 0; i < PRIVATE_GAME_HISTORY_SIZE; i++) {
1803 sprintf(temp_str, "favorites/server%dname=", i);
1804 host_name = config_get_string(temp_str, &default_returned);
1805 if (default_returned || !strlen(host_name)) {
1806 g_free(host_name);
1807 break;
1808 }
1809
1810 sprintf(temp_str, "favorites/server%dport=", i);
1811 host_port = config_get_string(temp_str, &default_returned);
1812 if (default_returned || !strlen(host_port)) {
1813 g_free(host_name);
1814 g_free(host_port);
1815 break;
1816 }
1817
1818 host_name_port =
1819 g_strconcat(host_name, ":", host_port, NULL);
1820 g_free(host_name);
1821 g_free(host_port);
1822
1823 g_ptr_array_add(host_entries, host_name_port);
1824 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT
1825 (host_list),
1826 host_name_port);
1827 }
1828 if (i > 0)
1829 gtk_combo_box_set_active(GTK_COMBO_BOX(host_list), 0);
1830 g_signal_connect(G_OBJECT(host_list), "changed",
1831 G_CALLBACK(host_list_select_cb), host_entries);
1832
1833 gtk_grid_attach(GTK_GRID(grid), host_list, 1, 2, 1, 1);
1834 /* Tooltip */
1835 gtk_widget_set_tooltip_text(host_list, _("Recent games"));
1836
1837 /* Label */
1838 lbl = gtk_label_new(_("Recent games"));
1839 gtk_widget_show(lbl);
1840 gtk_grid_attach(GTK_GRID(grid), lbl, 0, 2, 1, 1);
1841 gtk_label_set_xalign(GTK_LABEL(lbl), 0.0);
1842
1843 gtk_entry_set_activates_default(GTK_ENTRY(host_entry), TRUE);
1844 gtk_entry_set_activates_default(GTK_ENTRY(port_entry), TRUE);
1845
1846 gtk_widget_show(connect_private_dlg);
1847 }
1848