Stop help talking about units' ability to attack relative to non-native
[freeciv.git] / server / notify.c
blobb103f5106e883b02f463a1b08b03a67582077493
1 /**********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
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 #include <stdarg.h>
20 /* utility */
21 #include "bitvector.h"
22 #include "log.h"
23 #include "registry.h"
25 /* common */
26 #include "connection.h"
27 #include "events.h"
28 #include "featured_text.h"
29 #include "game.h"
30 #include "research.h"
31 #include "packets.h"
32 #include "player.h"
33 #include "tile.h"
35 /* server */
36 #include "maphand.h"
37 #include "srv_main.h"
39 #include "notify.h"
42 /**************************************************************************
43 Fill a packet_chat_msg structure.
45 packet: A pointer to the packet.
46 ptile: A pointer to a tile the event is occuring.
47 event: The event type.
48 pconn: The sender of the event (e.g. when event is E_CHAT_MSG).
49 color: The requested color or ftc_any if not requested. Some colors are
50 predefined in common/featured_text.h. You can pass a custom one using
51 ft_color().
52 format: The format of the message.
53 vargs: The extra arguments to build the message.
54 **************************************************************************/
55 static void package_event_full(struct packet_chat_msg *packet,
56 const struct tile *ptile,
57 enum event_type event,
58 const struct connection *pconn,
59 const struct ft_color color,
60 const char *format, va_list vargs)
62 char buf[MAX_LEN_MSG];
63 char *str;
65 fc_assert_ret(NULL != packet);
67 packet->tile = (NULL != ptile ? tile_index(ptile) : -1);
68 packet->event = event;
69 packet->conn_id = pconn ? pconn->id : -1;
71 fc_vsnprintf(buf, sizeof(buf), format, vargs);
72 if (is_capitalization_enabled()) {
73 str = capitalized_string(buf);
74 } else {
75 str = buf;
78 if (ft_color_requested(color)) {
79 featured_text_apply_tag(str, packet->message, sizeof(packet->message),
80 TTT_COLOR, 0, FT_OFFSET_UNSET, color);
81 } else {
82 /* Simple case */
83 strncpy(packet->message, str, sizeof(packet->message));
86 if (is_capitalization_enabled()) {
87 free_capitalized(str);
91 /**************************************************************************
92 Fill a packet_chat_msg structure for a chat message.
94 packet: A pointer to the packet.
95 sender: The sender of the message.
96 color: The requested color or ftc_any if not requested. Some colors are
97 predefined in common/featured_text.h. You can pass a custom one using
98 ft_color().
99 format: The format of the message.
100 vargs: The extra arguments to build the message.
101 **************************************************************************/
102 void vpackage_chat_msg(struct packet_chat_msg *packet,
103 const struct connection *sender,
104 const struct ft_color color,
105 const char *format, va_list vargs)
107 package_event_full(packet, NULL, E_CHAT_MSG, sender, color, format, vargs);
110 /**************************************************************************
111 Fill a packet_chat_msg structure for a chat message.
113 packet: A pointer to the packet.
114 sender: The sender of the message.
115 color: The requested color or ftc_any if not requested. Some colors are
116 predefined in common/featured_text.h. You can pass a custom one using
117 ft_color().
118 format: The format of the message.
119 ...: The extra arguments to build the message.
120 **************************************************************************/
121 void package_chat_msg(struct packet_chat_msg *packet,
122 const struct connection *sender,
123 const struct ft_color color,
124 const char *format, ...)
126 va_list args;
128 va_start(args, format);
129 vpackage_chat_msg(packet, sender, color, format, args);
130 va_end(args);
133 /**************************************************************************
134 Fill a packet_chat_msg structure for common server event.
136 packet: A pointer to the packet.
137 ptile: A pointer to a tile the event is occuring.
138 event: The event type.
139 color: The requested color or ftc_any if not requested. Some colors are
140 predefined in common/featured_text.h. You can pass a custom one using
141 ft_color().
142 format: The format of the message.
143 vargs: The extra arguments to build the message.
144 **************************************************************************/
145 void vpackage_event(struct packet_chat_msg *packet,
146 const struct tile *ptile,
147 enum event_type event,
148 const struct ft_color color,
149 const char *format, va_list vargs)
151 package_event_full(packet, ptile, event, NULL, color, format, vargs);
154 /**************************************************************************
155 Fill a packet_chat_msg structure for common server event.
157 packet: A pointer to the packet.
158 ptile: A pointer to a tile the event is occuring.
159 event: The event type.
160 color: The requested color or ftc_any if not requested. Some colors are
161 predefined in common/featured_text.h. You can pass a custom one using
162 ft_color().
163 format: The format of the message.
164 ...: The extra arguments to build the message.
165 **************************************************************************/
166 void package_event(struct packet_chat_msg *packet,
167 const struct tile *ptile,
168 enum event_type event,
169 const struct ft_color color,
170 const char *format, ...)
172 va_list args;
174 va_start(args, format);
175 vpackage_event(packet, ptile, event, color, format, args);
176 va_end(args);
180 /**************************************************************************
181 This is the basis for following notify_* functions. It uses the struct
182 packet_chat_msg as defined by vpackage_event().
184 Notify specified connections of an event of specified type (from events.h)
185 and specified (x,y) coords associated with the event. Coords will only
186 apply if game has started and the conn's player knows that tile (or
187 NULL == pconn->playing && pconn->observer). If coords are not required,
188 caller should specify (x,y) = (-1,-1); otherwise make sure that the
189 coordinates have been normalized.
190 **************************************************************************/
191 static void notify_conn_packet(struct conn_list *dest,
192 const struct packet_chat_msg *packet)
194 struct packet_chat_msg real_packet = *packet;
195 int tile = packet->tile;
196 struct tile *ptile = index_to_tile(tile);
198 if (!dest) {
199 dest = game.est_connections;
202 conn_list_iterate(dest, pconn) {
203 /* Avoid sending messages that could potentially reveal
204 * internal information about the server machine to
205 * connections that do not already have hack access. */
206 if ((packet->event == E_LOG_ERROR || packet->event == E_LOG_FATAL)
207 && pconn->access_level != ALLOW_HACK) {
208 continue;
211 if (S_S_RUNNING <= server_state()
212 && ptile /* special case, see above */
213 && ((NULL == pconn->playing && pconn->observer)
214 || (NULL != pconn->playing
215 && map_is_known(ptile, pconn->playing)))) {
216 /* tile info is OK; see above. */
217 /* FIXME: in the case this is a city event, we should check if the
218 * city is really known. */
219 real_packet.tile = tile;
220 } else {
221 /* No tile info. */
222 real_packet.tile = -1;
225 send_packet_chat_msg(pconn, &real_packet);
226 } conn_list_iterate_end;
229 /**************************************************************************
230 See notify_conn_packet - this is just the "non-v" version, with varargs.
231 **************************************************************************/
232 void notify_conn(struct conn_list *dest,
233 const struct tile *ptile,
234 enum event_type event,
235 const struct ft_color color,
236 const char *format, ...)
238 struct packet_chat_msg genmsg;
239 va_list args;
241 va_start(args, format);
242 vpackage_event(&genmsg, ptile, event, color, format, args);
243 va_end(args);
245 notify_conn_packet(dest, &genmsg);
247 if (!dest || dest == game.est_connections) {
248 /* Add to the cache */
249 event_cache_add_for_all(&genmsg);
253 /**************************************************************************
254 Similar to notify_conn_packet (see also), but takes player as "destination".
255 If player != NULL, sends to all connections for that player.
256 If player == NULL, sends to all game connections, to support
257 old code, but this feature may go away - should use notify_conn(NULL)
258 instead.
259 **************************************************************************/
260 void notify_player(const struct player *pplayer,
261 const struct tile *ptile,
262 enum event_type event,
263 const struct ft_color color,
264 const char *format, ...)
266 struct conn_list *dest = pplayer ? pplayer->connections : NULL;
267 struct packet_chat_msg genmsg;
268 va_list args;
270 va_start(args, format);
271 vpackage_event(&genmsg, ptile, event, color, format, args);
272 va_end(args);
274 notify_conn_packet(dest, &genmsg);
276 /* Add to the cache */
277 event_cache_add_for_player(&genmsg, pplayer);
280 /**************************************************************************
281 Send message to all players who have an embassy with pplayer,
282 but excluding pplayer and specified player.
283 **************************************************************************/
284 void notify_embassies(const struct player *pplayer,
285 const struct player *exclude,
286 const struct tile *ptile,
287 enum event_type event,
288 const struct ft_color color,
289 const char *format, ...)
291 struct packet_chat_msg genmsg;
292 struct event_cache_players *players = NULL;
293 va_list args;
295 va_start(args, format);
296 vpackage_event(&genmsg, ptile, event, color, format, args);
297 va_end(args);
299 players_iterate(other_player) {
300 if (player_has_embassy(other_player, pplayer)
301 && exclude != other_player
302 && pplayer != other_player) {
303 notify_conn_packet(other_player->connections, &genmsg);
304 players = event_cache_player_add(players, other_player);
306 } players_iterate_end;
308 /* Add to the cache */
309 event_cache_add_for_players(&genmsg, players);
312 /**************************************************************************
313 Sends a message to all players on pplayer's team. If 'pplayer' is NULL,
314 sends to all players.
315 **************************************************************************/
316 void notify_team(const struct player *pplayer,
317 const struct tile *ptile,
318 enum event_type event,
319 const struct ft_color color,
320 const char *format, ...)
322 struct conn_list *dest = game.est_connections;
323 struct packet_chat_msg genmsg;
324 struct event_cache_players *players = NULL;
325 va_list args;
327 va_start(args, format);
328 vpackage_event(&genmsg, ptile, event, color, format, args);
329 va_end(args);
331 if (pplayer) {
332 dest = conn_list_new();
333 players_iterate(other_player) {
334 if (!players_on_same_team(pplayer, other_player)) {
335 continue;
337 conn_list_iterate(other_player->connections, pconn) {
338 conn_list_append(dest, pconn);
339 } conn_list_iterate_end;
340 players = event_cache_player_add(players, other_player);
341 } players_iterate_end;
343 /* Add to the cache */
344 event_cache_add_for_players(&genmsg, players);
346 } else {
347 /* Add to the cache for all players. */
348 event_cache_add_for_all(&genmsg);
351 notify_conn_packet(dest, &genmsg);
353 if (pplayer) {
354 conn_list_destroy(dest);
358 /****************************************************************************
359 Sends a message to all players that share research with pplayer. Currently
360 this is all players on the same team but it may not always be that way.
362 Unlike other notify functions this one does not take a tile argument. We
363 assume no research message will have a tile associated.
364 ****************************************************************************/
365 void notify_research(const struct player *pplayer,
366 enum event_type event,
367 const struct ft_color color,
368 const char *format, ...)
370 struct packet_chat_msg genmsg;
371 struct event_cache_players *players = NULL;
372 va_list args;
373 struct player_research *research = player_research_get(pplayer);
375 va_start(args, format);
376 vpackage_event(&genmsg, NULL, event, color, format, args);
377 va_end(args);
379 players_iterate(other_player) {
380 if (player_research_get(other_player) == research) {
381 lsend_packet_chat_msg(other_player->connections, &genmsg);
382 players = event_cache_player_add(players, other_player);
384 } players_iterate_end;
386 /* Add to the cache */
387 event_cache_add_for_players(&genmsg, players);
391 /**************************************************************************
392 Event cache datas.
393 **************************************************************************/
394 enum event_cache_target {
395 ECT_ALL,
396 ECT_PLAYERS,
397 ECT_GLOBAL_OBSERVERS
400 /* Events are saved in that structure. */
401 struct event_cache_data {
402 struct packet_chat_msg packet;
403 int turn;
404 time_t timestamp;
405 enum server_states server_state;
406 enum event_cache_target target_type;
407 bv_player target; /* Used if target_type == ECT_PLAYERS. */
410 #define SPECLIST_TAG event_cache_data
411 #define SPECLIST_TYPE struct event_cache_data
412 #include "speclist.h"
413 #define event_cache_iterate(pdata) \
414 TYPED_LIST_ITERATE(struct event_cache_data, event_cache, pdata)
415 #define event_cache_iterate_end LIST_ITERATE_END
417 struct event_cache_players {
418 bv_player vector;
421 /* The full list of the events. */
422 static struct event_cache_data_list *event_cache = NULL;
424 /* Event cache status: ON(TRUE) / OFF(FALSE); used for saving the
425 * event cache */
426 static bool event_cache_status = FALSE;
428 /**************************************************************************
429 Destroy an event_cache_data. Removes it from the cache.
430 **************************************************************************/
431 static void event_cache_data_destroy(struct event_cache_data *pdata)
433 fc_assert_ret(NULL != event_cache);
434 fc_assert_ret(NULL != pdata);
436 event_cache_data_list_remove(event_cache, pdata);
437 free(pdata);
440 /**************************************************************************
441 Creates a new event_cache_data, appened to the list. It mays remove an
442 old entry if needed.
443 **************************************************************************/
444 static struct event_cache_data *
445 event_cache_data_new(const struct packet_chat_msg *packet, int turn,
446 time_t timestamp, enum server_states server_status,
447 enum event_cache_target target_type,
448 struct event_cache_players *players)
450 struct event_cache_data *pdata;
451 int max_events;
453 if (NULL == event_cache) {
454 /* Don't do log for this, because this could make an infinite
455 * recursion. */
456 return NULL;
458 fc_assert_ret_val(NULL != packet, NULL);
460 if (packet->event == E_MESSAGE_WALL) {
461 /* No popups at save game load. */
462 return NULL;
465 if (!game.server.event_cache.chat && packet->event == E_CHAT_MSG) {
466 /* chat messages should _not_ be saved */
467 return NULL;
470 /* check if cache is active */
471 if (!event_cache_status) {
472 return NULL;
475 pdata = fc_malloc(sizeof(*pdata));
476 pdata->packet = *packet;
477 pdata->turn = turn;
478 pdata->timestamp = timestamp;
479 pdata->server_state = server_status;
480 pdata->target_type = target_type;
481 if (players) {
482 pdata->target = players->vector;
483 } else {
484 BV_CLR_ALL(pdata->target);
486 event_cache_data_list_append(event_cache, pdata);
488 max_events = game.server.event_cache.max_size
489 ? game.server.event_cache.max_size
490 : GAME_MAX_EVENT_CACHE_MAX_SIZE;
491 while (event_cache_data_list_size(event_cache) > max_events) {
492 event_cache_data_destroy(event_cache_data_list_get(event_cache, 0));
495 return pdata;
498 /**************************************************************************
499 Initializes the event cache.
500 **************************************************************************/
501 void event_cache_init(void)
503 if (event_cache != NULL) {
504 event_cache_free();
506 event_cache = event_cache_data_list_new();
507 event_cache_status = TRUE;
510 /**************************************************************************
511 Frees the event cache.
512 **************************************************************************/
513 void event_cache_free(void)
515 if (event_cache != NULL) {
516 event_cache_iterate(pdata) {
517 event_cache_data_destroy(pdata);
518 } event_cache_iterate_end;
519 event_cache_data_list_destroy(event_cache);
520 event_cache = NULL;
522 event_cache_status = FALSE;
525 /**************************************************************************
526 Remove all events from the cache.
527 **************************************************************************/
528 void event_cache_clear(void)
530 event_cache_iterate(pdata) {
531 event_cache_data_destroy(pdata);
532 } event_cache_iterate_end;
535 /**************************************************************************
536 Remove the old events from the cache.
537 **************************************************************************/
538 void event_cache_remove_old(void)
540 event_cache_iterate(pdata) {
541 if (pdata->turn + game.server.event_cache.turns <= game.info.turn) {
542 event_cache_data_destroy(pdata);
544 } event_cache_iterate_end;
547 /**************************************************************************
548 Add an event to the cache for all connections.
549 **************************************************************************/
550 void event_cache_add_for_all(const struct packet_chat_msg *packet)
552 if (0 < game.server.event_cache.turns) {
553 (void) event_cache_data_new(packet, game.info.turn, time(NULL),
554 server_state(), ECT_ALL, NULL);
558 /**************************************************************************
559 Add an event to the cache for all global observers.
560 **************************************************************************/
561 void event_cache_add_for_global_observers(const struct packet_chat_msg *packet)
563 if (0 < game.server.event_cache.turns) {
564 (void) event_cache_data_new(packet, game.info.turn, time(NULL),
565 server_state(), ECT_GLOBAL_OBSERVERS, NULL);
569 /**************************************************************************
570 Add an event to the cache for one player.
572 N.B.: event_cache_add_for_player(&packet, NULL) will have the same effect
573 as event_cache_add_for_all(&packet).
574 N.B.: in pregame, this will never success, because players are not fixed.
575 **************************************************************************/
576 void event_cache_add_for_player(const struct packet_chat_msg *packet,
577 const struct player *pplayer)
579 if (NULL == pplayer) {
580 event_cache_add_for_all(packet);
581 return;
584 if (0 < game.server.event_cache.turns
585 && (server_state() > S_S_INITIAL || !game.info.is_new_game)) {
586 struct event_cache_data *pdata;
588 pdata = event_cache_data_new(packet, game.info.turn, time(NULL),
589 server_state(), ECT_PLAYERS, NULL);
590 fc_assert_ret(NULL != pdata);
591 BV_SET(pdata->target, player_index(pplayer));
595 /**************************************************************************
596 Add an event to the cache for selected players. See
597 event_cache_player_add() to see how to select players. This also
598 free the players pointer argument.
600 N.B.: in pregame, this will never success, because players are not fixed.
601 **************************************************************************/
602 void event_cache_add_for_players(const struct packet_chat_msg *packet,
603 struct event_cache_players *players)
605 if (0 < game.server.event_cache.turns
606 && NULL != players
607 && BV_ISSET_ANY(players->vector)
608 && (server_state() > S_S_INITIAL || !game.info.is_new_game)) {
609 (void) event_cache_data_new(packet, game.info.turn, time(NULL),
610 server_state(), ECT_PLAYERS, players);
613 if (NULL != players) {
614 free(players);
618 /**************************************************************************
619 Select players for event_cache_add_for_players(). Pass NULL as players
620 argument to create a new selection. Usually the usage of this function
621 would look to:
623 struct event_cache_players *players = NULL;
625 players_iterate(pplayer) {
626 if (some_condition) {
627 players = event_cache_player_add(players, pplayer);
629 } players_iterate_end;
630 // Now add to the cache.
631 event_cache_add_for_players(&packet, players); // Free players.
632 **************************************************************************/
633 struct event_cache_players *
634 event_cache_player_add(struct event_cache_players *players,
635 const struct player *pplayer)
637 if (NULL == players) {
638 players = fc_malloc(sizeof(*players));
639 BV_CLR_ALL(players->vector);
642 if (NULL != pplayer) {
643 BV_SET(players->vector, player_index(pplayer));
646 return players;
649 /**************************************************************************
650 Returns whether the event may be displayed for the connection.
651 **************************************************************************/
652 static bool event_cache_match(const struct event_cache_data *pdata,
653 const struct player *pplayer,
654 bool is_global_observer,
655 bool include_public)
657 if (server_state() != pdata->server_state) {
658 return FALSE;
661 if (server_state() == S_S_RUNNING
662 && game.info.turn < pdata->turn
663 && game.info.turn > pdata->turn - game.server.event_cache.turns) {
664 return FALSE;
667 switch (pdata->target_type) {
668 case ECT_ALL:
669 return include_public;
670 case ECT_PLAYERS:
671 return (NULL != pplayer
672 && BV_ISSET(pdata->target, player_index(pplayer)));
673 case ECT_GLOBAL_OBSERVERS:
674 return is_global_observer;
677 return FALSE;
680 /**************************************************************************
681 Send all available events. If include_public is TRUE, also fully global
682 message will be sent.
683 **************************************************************************/
684 void send_pending_events(struct connection *pconn, bool include_public)
686 const struct player *pplayer = conn_get_player(pconn);
687 bool is_global_observer = conn_is_global_observer(pconn);
688 char timestr[64];
689 struct packet_chat_msg pcm;
691 event_cache_iterate(pdata) {
692 if (event_cache_match(pdata, pplayer,
693 is_global_observer, include_public)) {
694 if (game.server.event_cache.info) {
695 /* add turn and time to the message */
696 strftime(timestr, sizeof(timestr), "%H:%M:%S",
697 localtime(&pdata->timestamp));
698 pcm = pdata->packet;
699 fc_snprintf(pcm.message, sizeof(pcm.message), "(T%d - %s) %s",
700 pdata->turn, timestr, pdata->packet.message);
701 notify_conn_packet(pconn->self, &pcm);
702 } else {
703 notify_conn_packet(pconn->self, &pdata->packet);
706 } event_cache_iterate_end;
709 /***************************************************************
710 Load the event cache from a savefile.
711 ***************************************************************/
712 void event_cache_load(struct section_file *file, const char *section)
714 struct packet_chat_msg packet;
715 enum event_cache_target target_type;
716 enum server_states server_status;
717 struct event_cache_players *players = NULL;
718 int i, x, y, turn, event_count;
719 time_t timestamp, now;
720 const char *p, *q;
722 event_count = secfile_lookup_int_default(file, 0, "%s.count", section);
723 log_verbose("Saved events: %d.", event_count);
725 if (0 >= event_count) {
726 return;
729 now = time(NULL);
730 for (i = 0; i < event_count; i++) {
731 /* restore packet */
732 x = secfile_lookup_int_default(file, -1, "%s.events%d.x", section, i);
733 y = secfile_lookup_int_default(file, -1, "%s.events%d.y", section, i);
734 packet.tile = (is_normal_map_pos(x, y) ? map_pos_to_index(x, y) : -1);
735 packet.conn_id = -1;
737 p = secfile_lookup_str(file, "%s.events%d.event", section, i);
738 if (NULL == p) {
739 log_verbose("[Event cache %4d] Missing event type.", i);
740 continue;
742 packet.event = event_type_by_name(p, fc_strcasecmp);
743 if (!event_type_is_valid(packet.event)) {
744 log_verbose("[Event cache %4d] Not supported event type: %s", i, p);
745 continue;
748 p = secfile_lookup_str(file, "%s.events%d.message", section, i);
749 if (NULL == p) {
750 log_verbose("[Event cache %4d] Missing message.", i);
751 continue;
753 sz_strlcpy(packet.message, p);
755 /* restore event cache data */
756 turn = secfile_lookup_int_default(file, 0, "%s.events%d.turn",
757 section, i);
758 timestamp = secfile_lookup_int_default(file, now,
759 "%s.events%d.timestamp",
760 section, i);
762 p = secfile_lookup_str(file, "%s.events%d.server_state", section, i);
763 if (NULL == p) {
764 log_verbose("[Event cache %4d] Missing server state info.", i);
765 continue;
767 server_status = server_states_by_name(p, fc_strcasecmp);
768 if (!server_states_is_valid(server_status)) {
769 log_verbose("[Event cache %4d] Server state no supported: %s", i, p);
770 continue;
773 p = secfile_lookup_str(file, "%s.events%d.target", section, i);
774 if (NULL == p) {
775 log_verbose("[Event cache %4d] Missing target info.", i);
776 continue;
777 } else if (0 == fc_strcasecmp(p, "All")) {
778 target_type = ECT_ALL;
779 } else if (0 == fc_strcasecmp(p, "Global Observers")) {
780 target_type = ECT_GLOBAL_OBSERVERS;
781 } else {
782 bool valid = TRUE;
784 target_type = ECT_PLAYERS;
785 q = p;
786 players_iterate(pplayer) {
787 if ('1' == *q) {
788 players = event_cache_player_add(players, pplayer);
789 } else if ('0' != *q) {
790 /* a value not '0' or '1' means a corruption of the savegame */
791 valid = FALSE;
792 break;
795 q++;
796 } players_iterate_end;
798 if (!valid && NULL == players) {
799 log_verbose("[Event cache %4d] invalid target bitmap: %s", i, p);
800 if (NULL != players) {
801 FC_FREE(players);
806 /* insert event into the cache */
807 (void) event_cache_data_new(&packet, turn, timestamp, server_status,
808 target_type, players);
810 if (NULL != players) {
811 /* free the event cache player selection */
812 FC_FREE(players);
815 log_verbose("Event %4d loaded.", i);
819 /***************************************************************
820 Save the event cache into the savegame.
821 ***************************************************************/
822 void event_cache_save(struct section_file *file, const char *section)
824 int event_count = 0;
826 /* stop event logging; this way events from log_*() will not be added
827 * to the event list while saving the event list */
828 event_cache_status = FALSE;
830 event_cache_iterate(pdata) {
831 struct tile *ptile = index_to_tile(pdata->packet.tile);
832 char target[MAX_NUM_PLAYER_SLOTS + 1];
833 char *p;
834 int tile_x = -1, tile_y = -1;
836 if (ptile != NULL) {
837 index_to_map_pos(&tile_x, &tile_y, tile_index(ptile));
840 secfile_insert_int(file, pdata->turn, "%s.events%d.turn",
841 section, event_count);
842 secfile_insert_int(file, pdata->timestamp, "%s.events%d.timestamp",
843 section, event_count);
844 secfile_insert_int(file, tile_x, "%s.events%d.x", section, event_count);
845 secfile_insert_int(file, tile_y, "%s.events%d.y", section, event_count);
846 secfile_insert_str(file, server_states_name(pdata->server_state),
847 "%s.events%d.server_state", section, event_count);
848 secfile_insert_str(file, event_type_name(pdata->packet.event),
849 "%s.events%d.event", section, event_count);
850 switch (pdata->target_type) {
851 case ECT_ALL:
852 fc_snprintf(target, sizeof(target), "All");
853 break;
854 case ECT_PLAYERS:
855 p = target;
856 players_iterate(pplayer) {
857 *p++ = (BV_ISSET(pdata->target, player_index(pplayer)) ? '1' : '0');
858 } players_iterate_end;
859 *p = '\0';
860 break;
861 case ECT_GLOBAL_OBSERVERS:
862 fc_snprintf(target, sizeof(target), "Global Observers");
863 break;
865 secfile_insert_str(file, target, "%s.events%d.target",
866 section, event_count);
867 secfile_insert_str(file, pdata->packet.message, "%s.events%d.message",
868 section, event_count);
870 log_verbose("Event %4d saved.", event_count);
872 event_count++;
873 } event_cache_iterate_end;
875 /* save the number of events in the event cache */
876 secfile_insert_int(file, event_count, "%s.count", section);
878 log_verbose("Events saved: %d.", event_count);
880 event_cache_status = TRUE;