Show messages related to the client followtag received from the metaserver.
[freeciv.git] / server / notify.c
blobe2baac7ebe13c713520b53059d34d5f94fef628f
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;
70 packet->turn = game.info.turn;
71 packet->phase = game.info.phase;
73 fc_vsnprintf(buf, sizeof(buf), format, vargs);
74 if (is_capitalization_enabled()) {
75 str = capitalized_string(buf);
76 } else {
77 str = buf;
80 if (ft_color_requested(color)) {
81 featured_text_apply_tag(str, packet->message, sizeof(packet->message),
82 TTT_COLOR, 0, FT_OFFSET_UNSET, color);
83 } else {
84 /* Simple case */
85 strncpy(packet->message, str, sizeof(packet->message));
88 if (is_capitalization_enabled()) {
89 free_capitalized(str);
93 /**************************************************************************
94 Fill a packet_chat_msg structure for a chat message.
96 packet: A pointer to the packet.
97 sender: The sender of the message.
98 color: The requested color or ftc_any if not requested. Some colors are
99 predefined in common/featured_text.h. You can pass a custom one using
100 ft_color().
101 format: The format of the message.
102 vargs: The extra arguments to build the message.
103 **************************************************************************/
104 void vpackage_chat_msg(struct packet_chat_msg *packet,
105 const struct connection *sender,
106 const struct ft_color color,
107 const char *format, va_list vargs)
109 package_event_full(packet, NULL, E_CHAT_MSG, sender, color, format, vargs);
112 /**************************************************************************
113 Fill a packet_chat_msg structure for a chat message.
115 packet: A pointer to the packet.
116 sender: The sender of the message.
117 color: The requested color or ftc_any if not requested. Some colors are
118 predefined in common/featured_text.h. You can pass a custom one using
119 ft_color().
120 format: The format of the message.
121 ...: The extra arguments to build the message.
122 **************************************************************************/
123 void package_chat_msg(struct packet_chat_msg *packet,
124 const struct connection *sender,
125 const struct ft_color color,
126 const char *format, ...)
128 va_list args;
130 va_start(args, format);
131 vpackage_chat_msg(packet, sender, color, format, args);
132 va_end(args);
135 /**************************************************************************
136 Fill a packet_chat_msg structure for common server event.
138 packet: A pointer to the packet.
139 ptile: A pointer to a tile the event is occuring.
140 event: The event type.
141 color: The requested color or ftc_any if not requested. Some colors are
142 predefined in common/featured_text.h. You can pass a custom one using
143 ft_color().
144 format: The format of the message.
145 vargs: The extra arguments to build the message.
146 **************************************************************************/
147 void vpackage_event(struct packet_chat_msg *packet,
148 const struct tile *ptile,
149 enum event_type event,
150 const struct ft_color color,
151 const char *format, va_list vargs)
153 package_event_full(packet, ptile, event, NULL, color, format, vargs);
156 /**************************************************************************
157 Fill a packet_chat_msg structure for common server event.
159 packet: A pointer to the packet.
160 ptile: A pointer to a tile the event is occuring.
161 event: The event type.
162 color: The requested color or ftc_any if not requested. Some colors are
163 predefined in common/featured_text.h. You can pass a custom one using
164 ft_color().
165 format: The format of the message.
166 ...: The extra arguments to build the message.
167 **************************************************************************/
168 void package_event(struct packet_chat_msg *packet,
169 const struct tile *ptile,
170 enum event_type event,
171 const struct ft_color color,
172 const char *format, ...)
174 va_list args;
176 va_start(args, format);
177 vpackage_event(packet, ptile, event, color, format, args);
178 va_end(args);
182 /**************************************************************************
183 This is the basis for following notify_* functions. It uses the struct
184 packet_chat_msg as defined by vpackage_event().
186 Notify specified connections of an event of specified type (from events.h)
187 and specified (x,y) coords associated with the event. Coords will only
188 apply if game has started and the conn's player knows that tile (or
189 NULL == pconn->playing && pconn->observer). If coords are not required,
190 caller should specify (x,y) = (-1,-1); otherwise make sure that the
191 coordinates have been normalized.
192 **************************************************************************/
193 static void notify_conn_packet(struct conn_list *dest,
194 const struct packet_chat_msg *packet,
195 bool early)
197 struct packet_chat_msg real_packet = *packet;
198 int tile = packet->tile;
199 struct tile *ptile = index_to_tile(tile);
201 if (!dest) {
202 dest = game.est_connections;
205 conn_list_iterate(dest, pconn) {
206 /* Avoid sending messages that could potentially reveal
207 * internal information about the server machine to
208 * connections that do not already have hack access. */
209 if ((packet->event == E_LOG_ERROR || packet->event == E_LOG_FATAL)
210 && pconn->access_level != ALLOW_HACK) {
211 continue;
214 if (S_S_RUNNING <= server_state()
215 && ptile /* special case, see above */
216 && ((NULL == pconn->playing && pconn->observer)
217 || (NULL != pconn->playing
218 && map_is_known(ptile, pconn->playing)))) {
219 /* tile info is OK; see above. */
220 /* FIXME: in the case this is a city event, we should check if the
221 * city is really known. */
222 real_packet.tile = tile;
223 } else {
224 /* No tile info. */
225 real_packet.tile = -1;
228 if (early) {
229 send_packet_early_chat_msg(pconn, (struct packet_early_chat_msg *)(&real_packet));
230 } else {
231 send_packet_chat_msg(pconn, &real_packet);
233 } conn_list_iterate_end;
236 /**************************************************************************
237 See notify_conn_packet - this is just the "non-v" version, with varargs.
238 **************************************************************************/
239 void notify_conn(struct conn_list *dest,
240 const struct tile *ptile,
241 enum event_type event,
242 const struct ft_color color,
243 const char *format, ...)
245 struct packet_chat_msg genmsg;
246 va_list args;
248 va_start(args, format);
249 vpackage_event(&genmsg, ptile, event, color, format, args);
250 va_end(args);
252 notify_conn_packet(dest, &genmsg, FALSE);
254 if (!dest || dest == game.est_connections) {
255 /* Add to the cache */
256 event_cache_add_for_all(&genmsg);
260 /**************************************************************************
261 See notify_conn_packet - this is just the "non-v" version, with varargs.
262 Use for early connecting protocol messages.
263 **************************************************************************/
264 void notify_conn_early(struct conn_list *dest,
265 const struct tile *ptile,
266 enum event_type event,
267 const struct ft_color color,
268 const char *format, ...)
270 struct packet_chat_msg genmsg;
271 va_list args;
273 va_start(args, format);
274 vpackage_event(&genmsg, ptile, event, color, format, args);
275 va_end(args);
277 notify_conn_packet(dest, &genmsg, TRUE);
279 if (!dest || dest == game.est_connections) {
280 /* Add to the cache */
281 event_cache_add_for_all(&genmsg);
285 /**************************************************************************
286 Similar to notify_conn_packet (see also), but takes player as "destination".
287 If player != NULL, sends to all connections for that player.
288 If player == NULL, sends to all game connections, to support
289 old code, but this feature may go away - should use notify_conn(NULL)
290 instead.
291 **************************************************************************/
292 void notify_player(const struct player *pplayer,
293 const struct tile *ptile,
294 enum event_type event,
295 const struct ft_color color,
296 const char *format, ...)
298 struct conn_list *dest = pplayer ? pplayer->connections : NULL;
299 struct packet_chat_msg genmsg;
300 va_list args;
302 va_start(args, format);
303 vpackage_event(&genmsg, ptile, event, color, format, args);
304 va_end(args);
306 notify_conn_packet(dest, &genmsg, FALSE);
308 /* Add to the cache */
309 event_cache_add_for_player(&genmsg, pplayer);
312 /**************************************************************************
313 Send message to all players who have an embassy with pplayer,
314 but excluding pplayer and specified player.
315 **************************************************************************/
316 void notify_embassies(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 packet_chat_msg genmsg;
323 struct event_cache_players *players = NULL;
324 va_list args;
326 va_start(args, format);
327 vpackage_event(&genmsg, ptile, event, color, format, args);
328 va_end(args);
330 players_iterate(other_player) {
331 if (player_has_embassy(other_player, pplayer)
332 && pplayer != other_player) {
333 notify_conn_packet(other_player->connections, &genmsg, FALSE);
334 players = event_cache_player_add(players, other_player);
336 } players_iterate_end;
338 /* Add to the cache */
339 event_cache_add_for_players(&genmsg, players);
342 /**************************************************************************
343 Sends a message to all players on pplayer's team. If 'pplayer' is NULL,
344 sends to all players.
345 **************************************************************************/
346 void notify_team(const struct player *pplayer,
347 const struct tile *ptile,
348 enum event_type event,
349 const struct ft_color color,
350 const char *format, ...)
352 struct conn_list *dest = game.est_connections;
353 struct packet_chat_msg genmsg;
354 struct event_cache_players *players = NULL;
355 va_list args;
357 va_start(args, format);
358 vpackage_event(&genmsg, ptile, event, color, format, args);
359 va_end(args);
361 if (pplayer) {
362 dest = conn_list_new();
363 players_iterate(other_player) {
364 if (!players_on_same_team(pplayer, other_player)) {
365 continue;
367 conn_list_iterate(other_player->connections, pconn) {
368 conn_list_append(dest, pconn);
369 } conn_list_iterate_end;
370 players = event_cache_player_add(players, other_player);
371 } players_iterate_end;
373 /* Add to the cache */
374 event_cache_add_for_players(&genmsg, players);
376 } else {
377 /* Add to the cache for all players. */
378 event_cache_add_for_all(&genmsg);
381 notify_conn_packet(dest, &genmsg, FALSE);
383 if (pplayer) {
384 conn_list_destroy(dest);
388 /****************************************************************************
389 Sends a message to all players that share research.
391 Unlike other notify functions this one does not take a tile argument. We
392 assume no research message will have a tile associated.
393 ****************************************************************************/
394 void notify_research(const struct research *presearch,
395 const struct player *exclude,
396 enum event_type event,
397 const struct ft_color color,
398 const char *format, ...)
400 struct packet_chat_msg genmsg;
401 struct event_cache_players *players = NULL;
402 va_list args;
404 va_start(args, format);
405 vpackage_event(&genmsg, NULL, event, color, format, args);
406 va_end(args);
408 research_players_iterate(presearch, aplayer) {
409 if (exclude != aplayer) {
410 lsend_packet_chat_msg(aplayer->connections, &genmsg);
411 players = event_cache_player_add(players, aplayer);
413 } research_players_iterate_end;
415 /* Add to the cache */
416 event_cache_add_for_players(&genmsg, players);
419 /****************************************************************************
420 Sends a message to all players that have embassies with someone who
421 shares research.
423 Unlike other notify functions this one does not take a tile argument. We
424 assume no research message will have a tile associated.
426 Exclude parameter excludes everyone who has embassy (only) with that
427 player.
429 FIXME: Should not send multiple messages if one has embassy with multiple
430 members of the research group, should really exclude ones having
431 embassy with the exclude -one as the use-case for exclusion is that
432 different message is being sent to those excluded here.
433 ****************************************************************************/
434 void notify_research_embassies(const struct research *presearch,
435 const struct player *exclude,
436 enum event_type event,
437 const struct ft_color color,
438 const char *format, ...)
440 struct packet_chat_msg genmsg;
441 struct event_cache_players *players = NULL;
442 va_list args;
444 va_start(args, format);
445 vpackage_event(&genmsg, NULL, event, color, format, args);
446 va_end(args);
448 players_iterate(aplayer) {
449 if (exclude == aplayer || research_get(aplayer) == presearch) {
450 continue;
453 research_players_iterate(presearch, rplayer) {
454 if (player_has_embassy(aplayer, rplayer)) {
455 lsend_packet_chat_msg(aplayer->connections, &genmsg);
456 players = event_cache_player_add(players, aplayer);
457 break;
459 } research_players_iterate_end;
460 } players_iterate_end;
462 /* Add to the cache */
463 event_cache_add_for_players(&genmsg, players);
467 /**************************************************************************
468 Event cache datas.
469 **************************************************************************/
470 enum event_cache_target {
471 ECT_ALL,
472 ECT_PLAYERS,
473 ECT_GLOBAL_OBSERVERS
476 /* Events are saved in that structure. */
477 struct event_cache_data {
478 struct packet_chat_msg packet;
479 time_t timestamp;
480 enum server_states server_state;
481 enum event_cache_target target_type;
482 bv_player target; /* Used if target_type == ECT_PLAYERS. */
485 #define SPECLIST_TAG event_cache_data
486 #define SPECLIST_TYPE struct event_cache_data
487 #include "speclist.h"
488 #define event_cache_iterate(pdata) \
489 TYPED_LIST_ITERATE(struct event_cache_data, event_cache, pdata)
490 #define event_cache_iterate_end LIST_ITERATE_END
492 struct event_cache_players {
493 bv_player vector;
496 /* The full list of the events. */
497 static struct event_cache_data_list *event_cache = NULL;
499 /* Event cache status: ON(TRUE) / OFF(FALSE); used for saving the
500 * event cache */
501 static bool event_cache_status = FALSE;
503 /**************************************************************************
504 Callback for freeing event cache data
505 **************************************************************************/
506 static void event_cache_data_free(struct event_cache_data *data)
508 free(data);
511 /**************************************************************************
512 Creates a new event_cache_data, appened to the list. It mays remove an
513 old entry if needed.
514 **************************************************************************/
515 static struct event_cache_data *
516 event_cache_data_new(const struct packet_chat_msg *packet,
517 time_t timestamp, enum server_states server_status,
518 enum event_cache_target target_type,
519 struct event_cache_players *players)
521 struct event_cache_data *pdata;
522 int max_events;
524 if (NULL == event_cache) {
525 /* Don't do log for this, because this could make an infinite
526 * recursion. */
527 return NULL;
529 fc_assert_ret_val(NULL != packet, NULL);
531 if (packet->event == E_MESSAGE_WALL) {
532 /* No popups at save game load. */
533 return NULL;
536 if (!game.server.event_cache.chat && packet->event == E_CHAT_MSG) {
537 /* chat messages should _not_ be saved */
538 return NULL;
541 /* check if cache is active */
542 if (!event_cache_status) {
543 return NULL;
546 pdata = fc_malloc(sizeof(*pdata));
547 pdata->packet = *packet;
548 pdata->timestamp = timestamp;
549 pdata->server_state = server_status;
550 pdata->target_type = target_type;
551 if (players) {
552 pdata->target = players->vector;
553 } else {
554 BV_CLR_ALL(pdata->target);
556 event_cache_data_list_append(event_cache, pdata);
558 max_events = game.server.event_cache.max_size
559 ? game.server.event_cache.max_size
560 : GAME_MAX_EVENT_CACHE_MAX_SIZE;
561 while (event_cache_data_list_size(event_cache) > max_events) {
562 event_cache_data_list_pop_front(event_cache);
565 return pdata;
568 /**************************************************************************
569 Initializes the event cache.
570 **************************************************************************/
571 void event_cache_init(void)
573 if (event_cache != NULL) {
574 event_cache_free();
576 event_cache = event_cache_data_list_new_full(event_cache_data_free);
577 event_cache_status = TRUE;
580 /**************************************************************************
581 Frees the event cache.
582 **************************************************************************/
583 void event_cache_free(void)
585 if (event_cache != NULL) {
586 event_cache_data_list_destroy(event_cache);
587 event_cache = NULL;
589 event_cache_status = FALSE;
592 /**************************************************************************
593 Remove all events from the cache.
594 **************************************************************************/
595 void event_cache_clear(void)
597 event_cache_data_list_clear(event_cache);
600 /**************************************************************************
601 Remove the old events from the cache.
602 **************************************************************************/
603 void event_cache_remove_old(void)
605 struct event_cache_data *current;
607 /* This assumes that entries are in order, the ones to be removed first. */
608 current = event_cache_data_list_get(event_cache, 0);
610 while (current != NULL
611 && current->packet.turn + game.server.event_cache.turns <= game.info.turn) {
612 event_cache_data_list_pop_front(event_cache);
613 current = event_cache_data_list_get(event_cache, 0);
617 /**************************************************************************
618 Add an event to the cache for all connections.
619 **************************************************************************/
620 void event_cache_add_for_all(const struct packet_chat_msg *packet)
622 if (0 < game.server.event_cache.turns) {
623 (void) event_cache_data_new(packet, time(NULL),
624 server_state(), ECT_ALL, NULL);
628 /**************************************************************************
629 Add an event to the cache for all global observers.
630 **************************************************************************/
631 void event_cache_add_for_global_observers(const struct packet_chat_msg *packet)
633 if (0 < game.server.event_cache.turns) {
634 (void) event_cache_data_new(packet, time(NULL),
635 server_state(), ECT_GLOBAL_OBSERVERS, NULL);
639 /**************************************************************************
640 Add an event to the cache for one player.
642 N.B.: event_cache_add_for_player(&packet, NULL) will have the same effect
643 as event_cache_add_for_all(&packet).
644 N.B.: in pregame, this will never success, because players are not fixed.
645 **************************************************************************/
646 void event_cache_add_for_player(const struct packet_chat_msg *packet,
647 const struct player *pplayer)
649 if (NULL == pplayer) {
650 event_cache_add_for_all(packet);
651 return;
654 if (0 < game.server.event_cache.turns
655 && (server_state() > S_S_INITIAL || !game.info.is_new_game)) {
656 struct event_cache_data *pdata;
658 pdata = event_cache_data_new(packet, time(NULL),
659 server_state(), ECT_PLAYERS, NULL);
660 fc_assert_ret(NULL != pdata);
661 BV_SET(pdata->target, player_index(pplayer));
665 /**************************************************************************
666 Add an event to the cache for selected players. See
667 event_cache_player_add() to see how to select players. This also
668 free the players pointer argument.
670 N.B.: in pregame, this will never success, because players are not fixed.
671 **************************************************************************/
672 void event_cache_add_for_players(const struct packet_chat_msg *packet,
673 struct event_cache_players *players)
675 if (0 < game.server.event_cache.turns
676 && NULL != players
677 && BV_ISSET_ANY(players->vector)
678 && (server_state() > S_S_INITIAL || !game.info.is_new_game)) {
679 (void) event_cache_data_new(packet, time(NULL),
680 server_state(), ECT_PLAYERS, players);
683 if (NULL != players) {
684 free(players);
688 /**************************************************************************
689 Select players for event_cache_add_for_players(). Pass NULL as players
690 argument to create a new selection. Usually the usage of this function
691 would look to:
693 struct event_cache_players *players = NULL;
695 players_iterate(pplayer) {
696 if (some_condition) {
697 players = event_cache_player_add(players, pplayer);
699 } players_iterate_end;
700 // Now add to the cache.
701 event_cache_add_for_players(&packet, players); // Free players.
702 **************************************************************************/
703 struct event_cache_players *
704 event_cache_player_add(struct event_cache_players *players,
705 const struct player *pplayer)
707 if (NULL == players) {
708 players = fc_malloc(sizeof(*players));
709 BV_CLR_ALL(players->vector);
712 if (NULL != pplayer) {
713 BV_SET(players->vector, player_index(pplayer));
716 return players;
719 /**************************************************************************
720 Returns whether the event may be displayed for the connection.
721 **************************************************************************/
722 static bool event_cache_match(const struct event_cache_data *pdata,
723 const struct player *pplayer,
724 bool is_global_observer,
725 bool include_public)
727 if (server_state() != pdata->server_state) {
728 return FALSE;
731 if (server_state() == S_S_RUNNING
732 && game.info.turn < pdata->packet.turn
733 && game.info.turn > pdata->packet.turn - game.server.event_cache.turns) {
734 return FALSE;
737 switch (pdata->target_type) {
738 case ECT_ALL:
739 return include_public;
740 case ECT_PLAYERS:
741 return (NULL != pplayer
742 && BV_ISSET(pdata->target, player_index(pplayer)));
743 case ECT_GLOBAL_OBSERVERS:
744 return is_global_observer;
747 return FALSE;
750 /**************************************************************************
751 Send all available events. If include_public is TRUE, also fully global
752 message will be sent.
753 **************************************************************************/
754 void send_pending_events(struct connection *pconn, bool include_public)
756 const struct player *pplayer = conn_get_player(pconn);
757 bool is_global_observer = conn_is_global_observer(pconn);
758 char timestr[64];
759 struct packet_chat_msg pcm;
761 event_cache_iterate(pdata) {
762 if (event_cache_match(pdata, pplayer,
763 is_global_observer, include_public)) {
764 if (game.server.event_cache.info) {
765 /* add turn and time to the message */
766 strftime(timestr, sizeof(timestr), "%H:%M:%S",
767 localtime(&pdata->timestamp));
768 pcm = pdata->packet;
769 fc_snprintf(pcm.message, sizeof(pcm.message), "(T%d - %s) %s",
770 pdata->packet.turn, timestr, pdata->packet.message);
771 notify_conn_packet(pconn->self, &pcm, FALSE);
772 } else {
773 notify_conn_packet(pconn->self, &pdata->packet, FALSE);
776 } event_cache_iterate_end;
779 /***************************************************************
780 Load the event cache from a savefile.
781 ***************************************************************/
782 void event_cache_load(struct section_file *file, const char *section)
784 struct packet_chat_msg packet;
785 enum event_cache_target target_type;
786 enum server_states server_status;
787 struct event_cache_players *players = NULL;
788 int i, x, y, event_count;
789 time_t timestamp, now;
790 const char *p, *q;
792 event_count = secfile_lookup_int_default(file, 0, "%s.count", section);
793 log_verbose("Saved events: %d.", event_count);
795 if (0 >= event_count) {
796 return;
799 now = time(NULL);
800 for (i = 0; i < event_count; i++) {
801 int turn;
802 int phase;
804 /* restore packet */
805 x = secfile_lookup_int_default(file, -1, "%s.events%d.x", section, i);
806 y = secfile_lookup_int_default(file, -1, "%s.events%d.y", section, i);
807 packet.tile = (is_normal_map_pos(x, y) ? map_pos_to_index(x, y) : -1);
808 packet.conn_id = -1;
810 p = secfile_lookup_str(file, "%s.events%d.event", section, i);
811 if (NULL == p) {
812 log_verbose("[Event cache %4d] Missing event type.", i);
813 continue;
815 packet.event = event_type_by_name(p, fc_strcasecmp);
816 if (!event_type_is_valid(packet.event)) {
817 log_verbose("[Event cache %4d] Not supported event type: %s", i, p);
818 continue;
821 p = secfile_lookup_str(file, "%s.events%d.message", section, i);
822 if (NULL == p) {
823 log_verbose("[Event cache %4d] Missing message.", i);
824 continue;
826 sz_strlcpy(packet.message, p);
828 /* restore event cache data */
829 turn = secfile_lookup_int_default(file, 0, "%s.events%d.turn",
830 section, i);
831 packet.turn = turn;
833 phase = secfile_lookup_int_default(file, PHASE_UNKNOWN, "%s.events%d.phase",
834 section, i);
835 packet.phase = phase;
837 timestamp = secfile_lookup_int_default(file, now,
838 "%s.events%d.timestamp",
839 section, i);
841 p = secfile_lookup_str(file, "%s.events%d.server_state", section, i);
842 if (NULL == p) {
843 log_verbose("[Event cache %4d] Missing server state info.", i);
844 continue;
846 server_status = server_states_by_name(p, fc_strcasecmp);
847 if (!server_states_is_valid(server_status)) {
848 log_verbose("[Event cache %4d] Server state no supported: %s", i, p);
849 continue;
852 p = secfile_lookup_str(file, "%s.events%d.target", section, i);
853 if (NULL == p) {
854 log_verbose("[Event cache %4d] Missing target info.", i);
855 continue;
856 } else if (0 == fc_strcasecmp(p, "All")) {
857 target_type = ECT_ALL;
858 } else if (0 == fc_strcasecmp(p, "Global Observers")) {
859 target_type = ECT_GLOBAL_OBSERVERS;
860 } else {
861 bool valid = TRUE;
863 target_type = ECT_PLAYERS;
864 q = p;
865 players_iterate(pplayer) {
866 if ('1' == *q) {
867 players = event_cache_player_add(players, pplayer);
868 } else if ('0' != *q) {
869 /* a value not '0' or '1' means a corruption of the savegame */
870 valid = FALSE;
871 break;
874 q++;
875 } players_iterate_end;
877 if (!valid && NULL == players) {
878 log_verbose("[Event cache %4d] invalid target bitmap: %s", i, p);
879 if (NULL != players) {
880 FC_FREE(players);
885 /* insert event into the cache */
886 (void) event_cache_data_new(&packet, timestamp, server_status,
887 target_type, players);
889 if (NULL != players) {
890 /* free the event cache player selection */
891 FC_FREE(players);
894 log_verbose("Event %4d loaded.", i);
898 /***************************************************************
899 Save the event cache into the savegame.
900 ***************************************************************/
901 void event_cache_save(struct section_file *file, const char *section)
903 int event_count = 0;
905 /* stop event logging; this way events from log_*() will not be added
906 * to the event list while saving the event list */
907 event_cache_status = FALSE;
909 event_cache_iterate(pdata) {
910 struct tile *ptile = index_to_tile(pdata->packet.tile);
911 char target[MAX_NUM_PLAYER_SLOTS + 1];
912 char *p;
913 int tile_x = -1, tile_y = -1;
915 if (ptile != NULL) {
916 index_to_map_pos(&tile_x, &tile_y, tile_index(ptile));
919 secfile_insert_int(file, pdata->packet.turn, "%s.events%d.turn",
920 section, event_count);
921 if (pdata->packet.phase != PHASE_UNKNOWN) {
922 /* Do not save current value of PHASE_UNKNOWN to savegame.
923 * It practically means that "savegame had no phase stored".
924 * Note that the only case where phase might be PHASE_UNKNOWN
925 * may be present is that the event was loaded from previous
926 * savegame created by a freeciv version that did not store event
927 * phases. */
928 secfile_insert_int(file, pdata->packet.phase, "%s.events%d.phase",
929 section, event_count);
931 secfile_insert_int(file, pdata->timestamp, "%s.events%d.timestamp",
932 section, event_count);
933 secfile_insert_int(file, tile_x, "%s.events%d.x", section, event_count);
934 secfile_insert_int(file, tile_y, "%s.events%d.y", section, event_count);
935 secfile_insert_str(file, server_states_name(pdata->server_state),
936 "%s.events%d.server_state", section, event_count);
937 secfile_insert_str(file, event_type_name(pdata->packet.event),
938 "%s.events%d.event", section, event_count);
939 switch (pdata->target_type) {
940 case ECT_ALL:
941 fc_snprintf(target, sizeof(target), "All");
942 break;
943 case ECT_PLAYERS:
944 p = target;
945 players_iterate(pplayer) {
946 *p++ = (BV_ISSET(pdata->target, player_index(pplayer)) ? '1' : '0');
947 } players_iterate_end;
948 *p = '\0';
949 break;
950 case ECT_GLOBAL_OBSERVERS:
951 fc_snprintf(target, sizeof(target), "Global Observers");
952 break;
954 secfile_insert_str(file, target, "%s.events%d.target",
955 section, event_count);
956 secfile_insert_str(file, pdata->packet.message, "%s.events%d.message",
957 section, event_count);
959 log_verbose("Event %4d saved.", event_count);
961 event_count++;
962 } event_cache_iterate_end;
964 /* save the number of events in the event cache */
965 secfile_insert_int(file, event_count, "%s.count", section);
967 log_verbose("Events saved: %d.", event_count);
969 event_cache_status = TRUE;
972 /***************************************************************
973 Mark all existing phase values in event cache invalid.
974 ***************************************************************/
975 void event_cache_phases_invalidate(void)
977 event_cache_iterate(pdata) {
978 if (pdata->packet.phase >= 0) {
979 pdata->packet.phase = PHASE_INVALIDATED;
981 } event_cache_iterate_end;