1 /**********************************************************************
2 Freeciv - Copyright (C) 2005 - Freeciv Development Team
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
15 #include <fc_config.h>
35 #include "connecthand.h"
36 #include "ggzserver.h"
40 #include "stdinhand.h"
42 bool with_ggz
= FALSE
;
44 static GGZdMod
*ggzdmod
;
47 /****************************************************************************
48 Return the seat occupied by this player (or -1 if the player i
50 ****************************************************************************/
51 static int get_seat_for_player(const struct player
*pplayer
)
53 int num_players
= ggzdmod_get_num_seats(ggzdmod
), i
;
55 /* This is pretty inefficient. It could be faster if a player->seat
56 * association was tracked but this is probably overkill. */
57 for (i
= 0; i
< num_players
; i
++) {
58 GGZSeat seat
= ggzdmod_get_seat(ggzdmod
, i
);
60 if (fc_strcasecmp(pplayer
->username
, seat
.name
) == 0) {
68 /****************************************************************************
69 Return the player sitting at the given seat (or NULL if no player can
70 be found or if the seat is empty).
71 ****************************************************************************/
72 static struct player
*get_player_for_seat(int seat_num
)
74 GGZSeat seat
= ggzdmod_get_seat(ggzdmod
, seat_num
);
79 case GGZ_SEAT_RESERVED
:
83 case GGZ_SEAT_ABANDONED
:
87 players_iterate(pplayer
) {
88 if (fc_strcasecmp(pplayer
->username
, seat
.name
)) {
91 } players_iterate_end
;
93 /* This is probably a bad bad error. */
97 /****************************************************************************
98 Handles a state event as reported by the GGZ server.
99 ****************************************************************************/
100 static void handle_ggz_state_event(GGZdMod
* ggz
, GGZdModEvent event
,
103 const GGZdModState
*old_state
= data
;
104 GGZdModState new_state
= ggzdmod_get_state(ggz
);
106 log_debug("ggz changed state to %d.", new_state
);
108 if (*old_state
== GGZDMOD_STATE_CREATED
) {
109 const char *savegame
= ggzdmod_get_savedgame(ggz
);
111 /* If a savegame is given, load it. */
112 log_debug("Instructed to load \"%s\".", savegame
);
114 if (!load_command(NULL
, savegame
, FALSE
)) {
115 /* no error handling? */
120 /* If we loaded a game that'll include the serverid. If not we
121 * generate one here. */
122 if (strlen(srvarg
.serverid
) == 0) {
123 strcpy(srvarg
.serverid
, "ggz-civ-XXXXXX");
124 if (!mkdtemp(srvarg
.serverid
)) {
125 log_error(_("Unable to make temporary directory for GGZ game.\n"));
130 /* Change into the server directory */
131 if (chdir(srvarg
.serverid
) < 0) {
132 log_error(_("Unable to change into temporary server directory %s.\n"),
136 log_debug("Changed into directory %s.", srvarg
.serverid
);
140 /****************************************************************************
141 Handles a seat-change event as reported by the GGZ server.
143 This typically happens when a human player joins or leaves the game.
144 This function links the GGZ seat player (which is just a seat number) to
145 the correct player. In the case of a join event we also get the socket
146 for the player's connection, which is treated just like a new connection.
147 ****************************************************************************/
148 static void handle_ggz_seat_event(GGZdMod
*ggz
, GGZdModEvent event
,
151 const GGZSeat
*old_seat
= data
;
152 GGZSeat new_seat
= ggzdmod_get_seat(ggz
, old_seat
->num
);
154 /* These values could be useful at some point. */
155 bool is_join
= ((new_seat
.type
== GGZ_SEAT_PLAYER
156 || new_seat
.type
== GGZ_SEAT_BOT
)
157 && (old_seat
->type
!= new_seat
.type
158 || strcmp(old_seat
->name
, new_seat
.name
)));
159 bool is_leave
= ((old_seat
->type
== GGZ_SEAT_PLAYER
160 || old_seat
->type
== GGZ_SEAT_BOT
)
161 && (new_seat
.type
!= old_seat
->type
162 || strcmp(old_seat
->name
, new_seat
.name
)));
163 GGZdModState new_state
;
167 if (new_seat
.type
== GGZ_SEAT_PLAYER
168 && old_seat
->type
!= GGZ_SEAT_PLAYER
) {
169 /* Player joins game. */
170 server_make_connection(new_seat
.fd
, "", "");
171 } else if (new_seat
.type
!= GGZ_SEAT_PLAYER
172 && old_seat
->type
== GGZ_SEAT_PLAYER
) {
173 /* Player leaves game. */
174 struct connection
*leaving
= NULL
;
176 players_iterate(pplayer
) {
177 conn_list_iterate(pplayer
->connections
, pconn
) {
178 if (strcmp(pconn
->username
, old_seat
->name
) == 0) {
182 } conn_list_iterate_end
;
183 } players_iterate_end
;
186 log_debug("%s is leaving.", old_seat
->name
);
188 server_break_connection(leaving
);
190 log_error("Couldn't match player %s.", old_seat
->name
);
195 /****************************************************************************
196 Handles a spectator seat event as reported by the GGZ server.
197 ****************************************************************************/
198 static void handle_ggz_spectator_seat_event(GGZdMod
*ggz
, GGZdModEvent event
,
202 /* Currently spectators are not supported. TODO: spectators should be
203 * integrated with observers. */
204 const GGZSpectator
*old
= data
;
205 GGZSpectator
new = ggzdmod_get_spectator(ggz
, spectator
);
215 /****************************************************************************
216 Handles a ggzdmod error. This simply exits the server with an error
218 ****************************************************************************/
219 static void handle_ggz_error(GGZdMod
* ggz
, GGZdModEvent event
,
222 const char *err
= data
;
224 log_error("Error in ggz: %s", err
);
228 /****************************************************************************
229 Connect to the GGZ server, if GGZ is being used.
230 ****************************************************************************/
231 void ggz_initialize(void)
233 with_ggz
= ggzdmod_is_ggz_mode();
238 /* Detach from terminal. */
243 /* We're in GGZ mode */
244 ggzdmod
= ggzdmod_new(GGZDMOD_GAME
);
245 ggzdmod_set_handler(ggzdmod
, GGZDMOD_EVENT_STATE
,
246 &handle_ggz_state_event
);
247 ggzdmod_set_handler(ggzdmod
, GGZDMOD_EVENT_JOIN
,
248 &handle_ggz_seat_event
);
249 ggzdmod_set_handler(ggzdmod
, GGZDMOD_EVENT_LEAVE
,
250 &handle_ggz_seat_event
);
251 ggzdmod_set_handler(ggzdmod
, GGZDMOD_EVENT_SEAT
,
252 &handle_ggz_seat_event
);
253 ggzdmod_set_handler(ggzdmod
, GGZDMOD_EVENT_SPECTATOR_JOIN
,
254 &handle_ggz_spectator_seat_event
);
255 ggzdmod_set_handler(ggzdmod
, GGZDMOD_EVENT_SPECTATOR_LEAVE
,
256 &handle_ggz_spectator_seat_event
);
257 ggzdmod_set_handler(ggzdmod
, GGZDMOD_EVENT_SPECTATOR_SEAT
,
258 &handle_ggz_spectator_seat_event
);
259 ggzdmod_set_handler(ggzdmod
, GGZDMOD_EVENT_ERROR
,
261 if (ggzdmod_connect(ggzdmod
) < 0) {
264 ggz_socket
= ggzdmod_get_fd(ggzdmod
);
265 if (ggz_socket
< 0) {
266 fc_fprintf(stderr
, _("Only the GGZ client must call freeciv-client"
273 /****************************************************************************
274 Called by the network code when there is data to be read on the given
276 ****************************************************************************/
277 void input_from_ggz(int socket
)
279 ggzdmod_dispatch(ggzdmod
);
282 /****************************************************************************
283 Return the file descriptor for the GGZ socket. The network code needs to
284 monitor this socket and call input_from_ggz when data is to be read.
285 ****************************************************************************/
286 int get_ggz_socket(void)
288 return ggzdmod_get_fd(ggzdmod
);
291 static const struct player
*victors
[MAX_NUM_PLAYERS
];
292 static int num_victors
;
294 /****************************************************************************
295 Register a single player as the game victor. See ggz_report_victory().
296 ****************************************************************************/
297 void ggz_report_victor(const struct player
*winner
)
299 log_verbose("Victor: %s", winner
->name
);
305 /* All players, including AI, are included on this list. */
306 victors
[num_victors
] = winner
;
310 /****************************************************************************
311 Report victory to the GGZ server.
313 One or more victor players may have been registered already using
314 ggz_report_victor; all other players are assumed to be losers.
316 Currently AI players are not considered at all.
317 ****************************************************************************/
318 void ggz_report_victory(void)
320 int num_players
= ggzdmod_get_num_seats(ggzdmod
), i
;
321 int teams
[num_players
], num_teams
= 0, scores
[num_players
];
322 GGZGameResult results
[num_players
], default_result
;
324 log_verbose("Victory...");
330 /* Assign teams. First put players who are on teams. */
331 teams_iterate(pteam
) {
332 players_iterate(pplayer
) {
333 if (pplayer
->team
== pteam
) {
334 int seat
= get_seat_for_player(pplayer
);
337 /* FIXME: this can happen for AI players */
339 teams
[get_seat_for_player(pplayer
)] = num_teams
;
342 } players_iterate_end
;
346 /* Then assign team numbers for non-team players. */
347 for (i
= 0; i
< num_players
; i
++) {
348 const struct player
*pplayer
= get_player_for_seat(i
);
352 } else if (!pplayer
->team
) {
353 teams
[i
] = num_teams
;
356 fc_assert(teams
[i
] >= 0 && teams
[i
] < num_teams
);
361 for (i
= 0; i
< num_players
; i
++) {
362 const struct player
*pplayer
= get_player_for_seat(i
);
365 scores
[i
] = pplayer
->score
.game
;
371 if (num_victors
== 0) {
372 default_result
= GGZ_GAME_TIE
;
374 default_result
= GGZ_GAME_LOSS
;
376 for (i
= 0; i
< num_players
; i
++) {
377 results
[i
] = default_result
;
379 for (i
= 0; i
< num_victors
; i
++) {
380 int seat_num
= get_seat_for_player(victors
[i
]);
383 /* FIXME: this can happen for AI players */
385 results
[seat_num
] = GGZ_GAME_WIN
;
389 ggzdmod_report_game(ggzdmod
, teams
, results
, scores
);
391 num_victors
= 0; /* In case there's another game. */
394 /****************************************************************************
395 Reports a savegame file to the GGZ server. GGZ will allow
396 reloading from a file later by providing the savegame at launch time
397 (in the STATE event when leaving the CREATED state).
398 ****************************************************************************/
399 void ggz_game_saved(const char *filename
)
401 char full_filename
[strlen(filename
) + strlen(srvarg
.serverid
) + 2];
403 if (!path_is_absolute(filename
)) {
404 snprintf(full_filename
, sizeof(full_filename
), "%s/%s",
405 srvarg
.serverid
, filename
);
407 sz_strlcpy(full_filename
, filename
);
409 log_debug("Reporting filename %s => %s to ggz.", filename
, full_filename
);
412 ggzdmod_report_savegame(ggzdmod
, full_filename
);
416 #endif /* GGZ_SERVER */