Rework README.ruleset_civ2civ3 defense bonus description.
[freeciv.git] / server / ggzserver.c
blobf1af8ef07eb478f5f247a93ac71e269582935036
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)
6 any later version.
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 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #ifdef GGZ_SERVER
20 #include <unistd.h>
22 #include <ggzdmod.h>
24 /* utility */
25 #include "fciconv.h"
26 #include "fcintl.h"
27 #include "log.h"
28 #include "support.h"
30 /* common */
31 #include "game.h"
32 #include "player.h"
34 /* server */
35 #include "connecthand.h"
36 #include "ggzserver.h"
37 #include "score.h"
38 #include "sernet.h"
39 #include "srv_main.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
49 seatless).
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) {
61 return seat.num;
65 return -1;
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);
76 switch (seat.type) {
77 case GGZ_SEAT_OPEN:
78 case GGZ_SEAT_NONE:
79 case GGZ_SEAT_RESERVED:
80 return NULL;
81 case GGZ_SEAT_PLAYER:
82 case GGZ_SEAT_BOT:
83 case GGZ_SEAT_ABANDONED:
84 break;
87 players_iterate(pplayer) {
88 if (fc_strcasecmp(pplayer->username, seat.name)) {
89 return pplayer;
91 } players_iterate_end;
93 /* This is probably a bad bad error. */
94 return NULL;
97 /****************************************************************************
98 Handles a state event as reported by the GGZ server.
99 ****************************************************************************/
100 static void handle_ggz_state_event(GGZdMod * ggz, GGZdModEvent event,
101 const void *data)
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);
113 if (savegame) {
114 if (!load_command(NULL, savegame, FALSE)) {
115 /* no error handling? */
116 server_quit();
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"));
126 server_quit();
130 /* Change into the server directory */
131 if (chdir(srvarg.serverid) < 0) {
132 log_error(_("Unable to change into temporary server directory %s.\n"),
133 srvarg.serverid);
134 server_quit();
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,
149 const void *data)
151 const GGZSeat *old_seat = data;
152 GGZSeat new_seat = ggzdmod_get_seat(ggz, old_seat->num);
153 #if 0
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;
165 #endif
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) {
179 leaving = pconn;
180 break;
182 } conn_list_iterate_end;
183 } players_iterate_end;
185 if (leaving) {
186 log_debug("%s is leaving.", old_seat->name);
187 leaving->sock = -1;
188 server_break_connection(leaving);
189 } else {
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,
199 const void *data)
201 #if 0
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);
207 if (new.name) {
209 } else {
212 #endif
215 /****************************************************************************
216 Handles a ggzdmod error. This simply exits the server with an error
217 message.
218 ****************************************************************************/
219 static void handle_ggz_error(GGZdMod * ggz, GGZdModEvent event,
220 const void *data)
222 const char *err = data;
224 log_error("Error in ggz: %s", err);
225 server_quit();
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();
235 if (with_ggz) {
236 int ggz_socket;
238 /* Detach from terminal. */
239 fclose(stdin);
240 fclose(stdout);
241 fclose(stderr);
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,
260 &handle_ggz_error);
261 if (ggzdmod_connect(ggzdmod) < 0) {
262 exit(EXIT_FAILURE);
264 ggz_socket = ggzdmod_get_fd(ggzdmod);
265 if (ggz_socket < 0) {
266 fc_fprintf(stderr, _("Only the GGZ client must call freeciv-client"
267 " in ggz mode!\n"));
268 exit(EXIT_FAILURE);
273 /****************************************************************************
274 Called by the network code when there is data to be read on the given
275 GGZ socket.
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);
301 if (!with_ggz) {
302 return;
305 /* All players, including AI, are included on this list. */
306 victors[num_victors] = winner;
307 num_victors++;
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...");
326 if (!with_ggz) {
327 return;
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);
336 if (seat < 0) {
337 /* FIXME: this can happen for AI players */
338 } else {
339 teams[get_seat_for_player(pplayer)] = num_teams;
342 } players_iterate_end;
343 num_teams++;
344 } teams_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);
350 if (!pplayer) {
351 teams[i] = -1;
352 } else if (!pplayer->team) {
353 teams[i] = num_teams;
354 num_teams++;
355 } else {
356 fc_assert(teams[i] >= 0 && teams[i] < num_teams);
360 /* Scores. */
361 for (i = 0; i < num_players; i++) {
362 const struct player *pplayer = get_player_for_seat(i);
364 if (pplayer) {
365 scores[i] = pplayer->score.game;
366 } else {
367 scores[i] = -1;
371 if (num_victors == 0) {
372 default_result = GGZ_GAME_TIE;
373 } else {
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]);
382 if (seat_num < 0) {
383 /* FIXME: this can happen for AI players */
384 } else {
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);
406 } else {
407 sz_strlcpy(full_filename, filename);
409 log_debug("Reporting filename %s => %s to ggz.", filename, full_filename);
411 if (with_ggz) {
412 ggzdmod_report_savegame(ggzdmod, full_filename);
416 #endif /* GGZ_SERVER */