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)
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>
21 #include "bitvector.h"
26 #include "connection.h"
28 #include "featured_text.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
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
];
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
);
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
);
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
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
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
, ...)
130 va_start(args
, format
);
131 vpackage_chat_msg(packet
, sender
, color
, format
, 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
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
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
, ...)
176 va_start(args
, format
);
177 vpackage_event(packet
, ptile
, event
, color
, format
, 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
,
197 struct packet_chat_msg real_packet
= *packet
;
198 int tile
= packet
->tile
;
199 struct tile
*ptile
= index_to_tile(tile
);
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
) {
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
;
225 real_packet
.tile
= -1;
229 send_packet_early_chat_msg(pconn
, (struct packet_early_chat_msg
*)(&real_packet
));
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
;
248 va_start(args
, format
);
249 vpackage_event(&genmsg
, ptile
, event
, color
, format
, 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
;
273 va_start(args
, format
);
274 vpackage_event(&genmsg
, ptile
, event
, color
, format
, 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)
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
;
302 va_start(args
, format
);
303 vpackage_event(&genmsg
, ptile
, event
, color
, format
, 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
;
326 va_start(args
, format
);
327 vpackage_event(&genmsg
, ptile
, event
, color
, format
, 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
;
357 va_start(args
, format
);
358 vpackage_event(&genmsg
, ptile
, event
, color
, format
, args
);
362 dest
= conn_list_new();
363 players_iterate(other_player
) {
364 if (!players_on_same_team(pplayer
, other_player
)) {
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
);
377 /* Add to the cache for all players. */
378 event_cache_add_for_all(&genmsg
);
381 notify_conn_packet(dest
, &genmsg
, FALSE
);
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
;
404 va_start(args
, format
);
405 vpackage_event(&genmsg
, NULL
, event
, color
, format
, 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
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
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
;
444 va_start(args
, format
);
445 vpackage_event(&genmsg
, NULL
, event
, color
, format
, args
);
448 players_iterate(aplayer
) {
449 if (exclude
== aplayer
|| research_get(aplayer
) == presearch
) {
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
);
459 } research_players_iterate_end
;
460 } players_iterate_end
;
462 /* Add to the cache */
463 event_cache_add_for_players(&genmsg
, players
);
467 /**************************************************************************
469 **************************************************************************/
470 enum event_cache_target
{
476 /* Events are saved in that structure. */
477 struct event_cache_data
{
478 struct packet_chat_msg packet
;
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
{
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
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
)
511 /**************************************************************************
512 Creates a new event_cache_data, appened to the list. It mays remove an
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
;
524 if (NULL
== event_cache
) {
525 /* Don't do log for this, because this could make an infinite
529 fc_assert_ret_val(NULL
!= packet
, NULL
);
531 if (packet
->event
== E_MESSAGE_WALL
) {
532 /* No popups at save game load. */
536 if (!game
.server
.event_cache
.chat
&& packet
->event
== E_CHAT_MSG
) {
537 /* chat messages should _not_ be saved */
541 /* check if cache is active */
542 if (!event_cache_status
) {
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
;
552 pdata
->target
= players
->vector
;
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
);
568 /**************************************************************************
569 Initializes the event cache.
570 **************************************************************************/
571 void event_cache_init(void)
573 if (event_cache
!= NULL
) {
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
);
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
);
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
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
) {
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
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
));
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
,
727 if (server_state() != pdata
->server_state
) {
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
) {
737 switch (pdata
->target_type
) {
739 return include_public
;
741 return (NULL
!= pplayer
742 && BV_ISSET(pdata
->target
, player_index(pplayer
)));
743 case ECT_GLOBAL_OBSERVERS
:
744 return is_global_observer
;
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
);
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
));
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
);
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
;
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
) {
800 for (i
= 0; i
< event_count
; i
++) {
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);
810 p
= secfile_lookup_str(file
, "%s.events%d.event", section
, i
);
812 log_verbose("[Event cache %4d] Missing event type.", i
);
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
);
821 p
= secfile_lookup_str(file
, "%s.events%d.message", section
, i
);
823 log_verbose("[Event cache %4d] Missing message.", i
);
826 sz_strlcpy(packet
.message
, p
);
828 /* restore event cache data */
829 turn
= secfile_lookup_int_default(file
, 0, "%s.events%d.turn",
833 phase
= secfile_lookup_int_default(file
, PHASE_UNKNOWN
, "%s.events%d.phase",
835 packet
.phase
= phase
;
837 timestamp
= secfile_lookup_int_default(file
, now
,
838 "%s.events%d.timestamp",
841 p
= secfile_lookup_str(file
, "%s.events%d.server_state", section
, i
);
843 log_verbose("[Event cache %4d] Missing server state info.", i
);
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
);
852 p
= secfile_lookup_str(file
, "%s.events%d.target", section
, i
);
854 log_verbose("[Event cache %4d] Missing target info.", i
);
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
;
863 target_type
= ECT_PLAYERS
;
865 players_iterate(pplayer
) {
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 */
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
) {
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 */
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
)
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];
913 int tile_x
= -1, tile_y
= -1;
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
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
) {
941 fc_snprintf(target
, sizeof(target
), "All");
945 players_iterate(pplayer
) {
946 *p
++ = (BV_ISSET(pdata
->target
, player_index(pplayer
)) ? '1' : '0');
947 } players_iterate_end
;
950 case ECT_GLOBAL_OBSERVERS
:
951 fc_snprintf(target
, sizeof(target
), "Global Observers");
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
);
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
;