Freeciv-3.1
Loading...
Searching...
No Matches
savemain.c
Go to the documentation of this file.
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.
7
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***********************************************************************/
13
14#ifdef HAVE_CONFIG_H
15#include <fc_config.h>
16#endif
17
18/* utility */
19#include "log.h"
20#include "mem.h"
21#include "registry.h"
22
23/* common */
24#include "ai.h"
25#include "capability.h"
26#include "game.h"
27
28/* server */
29#include "console.h"
30#include "notify.h"
31#include "plrhand.h"
32
33/* server/savegame */
34#include "savegame2.h"
35#include "savegame3.h"
36
37#include "savemain.h"
38
39static fc_thread *save_thread = NULL;
40
41/************************************************************************/
44void savegame_load(struct section_file *sfile)
45{
46 const char *savefile_options;
47
48 fc_assert_ret(sfile != NULL);
49
50#ifdef DEBUG_TIMERS
51 struct timer *loadtimer = timer_new(TIMER_CPU, TIMER_DEBUG);
52 timer_start(loadtimer);
53#endif
54
55 savefile_options = secfile_lookup_str(sfile, "savefile.options");
56
57 if (!savefile_options) {
58 log_error("Missing savefile options. Can not load the savegame.");
59 return;
60 }
61
62 if (has_capabilities("+version3", savefile_options)) {
63 /* Load new format (freeciv 3.0.x and newer) */
64 log_verbose("loading savefile in 3.0+ format ...");
65 savegame3_load(sfile);
66 } else if (has_capabilities("+version2", savefile_options)) {
67 /* Load old format (freeciv 2.3 - 2.6) */
68 log_verbose("loading savefile in 2.3 - 2.6 format ...");
69 savegame2_load(sfile);
70 } else {
71 log_error("Too old savegame format not supported any more.");
72 return;
73 }
74
75 players_iterate(pplayer) {
76 unit_list_iterate(pplayer->units, punit) {
77 CALL_FUNC_EACH_AI(unit_created, punit);
78 CALL_PLR_AI_FUNC(unit_got, pplayer, punit);
80
81 city_list_iterate(pplayer->cities, pcity) {
82 CALL_FUNC_EACH_AI(city_created, pcity);
83 CALL_PLR_AI_FUNC(city_got, pplayer, pplayer, pcity);
86
87 /* Capital information was not saved in savegames,
88 * so we have no capital setup at all at the moment.
89 * Best we can do is to do recalculation now. It might be
90 * a bit off compared to the situation before the game was saved,
91 * but definitely better than not setting capitals at all. */
92 players_iterate_alive(pplayer) {
93 update_capital(pplayer);
95
96#ifdef DEBUG_TIMERS
97 timer_stop(loadtimer);
98 log_debug("Loading secfile in %.3f seconds.", timer_read_seconds(loadtimer));
99 timer_destroy(loadtimer);
100#endif /* DEBUG_TIMERS */
101}
102
103/************************************************************************/
106void savegame_save(struct section_file *sfile, const char *save_reason,
107 bool scenario)
108{
109 savegame3_save(sfile, save_reason, scenario);
110}
111
119
120/************************************************************************/
123static void save_thread_data_free(struct save_thread_data *stdata)
124{
125 secfile_destroy(stdata->sfile);
126 free(stdata);
127}
128
129/************************************************************************/
132static void save_thread_run(void *arg)
133{
134 struct save_thread_data *stdata = (struct save_thread_data *)arg;
135
136 if (!secfile_save(stdata->sfile, stdata->filepath, stdata->save_compress_level,
137 stdata->save_compress_type)) {
138 con_write(C_FAIL, _("Failed saving game as %s"), stdata->filepath);
139 log_error("Game saving failed: %s", secfile_error());
140 notify_conn(NULL, NULL, E_LOG_ERROR, ftc_warning, _("Failed saving game."));
141 } else {
142 con_write(C_OK, _("Game saved as %s"), stdata->filepath);
143 }
144
145 save_thread_data_free(stdata);
146}
147
148/************************************************************************/
152void save_game(const char *orig_filename, const char *save_reason,
153 bool scenario)
154{
155 char *dot, *filename;
156 struct timer *timer_cpu, *timer_user;
157 struct save_thread_data *stdata;
158
159 stdata = fc_malloc(sizeof(*stdata));
160
163
164 if (!orig_filename) {
165 stdata->filepath[0] = '\0';
166 filename = stdata->filepath;
167 } else {
168 sz_strlcpy(stdata->filepath, orig_filename);
169 if ((filename = strrchr(stdata->filepath, '/'))) {
170 filename++;
171 } else {
172 filename = stdata->filepath;
173 }
174
175 /* Ignores the dot at the start of the filename. */
176 for (dot = filename; '.' == *dot; dot++) {
177 /* Nothing. */
178 }
179 if ('\0' == *dot) {
180 /* Only dots in this file name, consider it as empty. */
181 filename[0] = '\0';
182 } else {
183 char *end_dot;
184 char *strip_extensions[] = {
185 ".sav", ".gz", ".bz2", ".xz", ".zst", NULL };
186 bool stripped = TRUE;
187
188 while ((end_dot = strrchr(dot, '.')) && stripped) {
189 int i;
190
191 stripped = FALSE;
192
193 for (i = 0; strip_extensions[i] != NULL && !stripped; i++) {
194 if (!strcmp(end_dot, strip_extensions[i])) {
195 *end_dot = '\0';
196 stripped = TRUE;
197 }
198 }
199 }
200 }
201 }
202
203 /* If orig_filename is NULL or empty, use a generated default name. */
204 if (filename[0] == '\0') {
205 /* manual save */
207 sizeof(stdata->filepath) + stdata->filepath - filename, "manual");
208 }
209
210 timer_cpu = timer_new(TIMER_CPU, TIMER_ACTIVE);
211 timer_start(timer_cpu);
212 timer_user = timer_new(TIMER_USER, TIMER_ACTIVE);
213 timer_start(timer_user);
214
215 /* Allowing duplicates shouldn't be allowed. However, it takes very too
216 * long time for huge game saving... */
217 stdata->sfile = secfile_new(TRUE);
218 savegame_save(stdata->sfile, save_reason, scenario);
219
220 /* We have consistent game state in stdata->sfile now, so
221 * we could pass it to the saving thread already. We want to
222 * handle below notify_conn() and directory creation in
223 * main thread, though. */
224
225 /* Append ".sav" to filename. */
226 sz_strlcat(stdata->filepath, ".sav");
227
228 if (stdata->save_compress_level > 0) {
229 switch (stdata->save_compress_type) {
230#ifdef FREECIV_HAVE_LIBZ
231 case FZ_ZLIB:
232 /* Append ".gz" to filename. */
233 sz_strlcat(stdata->filepath, ".gz");
234 break;
235#endif
236#ifdef FREECIV_HAVE_LIBBZ2
237 case FZ_BZIP2:
238 /* Append ".bz2" to filename. */
239 sz_strlcat(stdata->filepath, ".bz2");
240 break;
241#endif
242#ifdef FREECIV_HAVE_LIBLZMA
243 case FZ_XZ:
244 /* Append ".xz" to filename. */
245 sz_strlcat(stdata->filepath, ".xz");
246 break;
247#endif
248#ifdef FREECIV_HAVE_LIBZSTD
249 case FZ_ZSTD:
250 /* Append ".zstd" to filename. */
251 sz_strlcat(stdata->filepath, ".zst");
252 break;
253#endif /* FREECIV_HAVE_LIBZSTD */
254 case FZ_PLAIN:
255 break;
256 default:
257 log_error(_("Unsupported compression type %d."),
258 stdata->save_compress_type);
259 notify_conn(NULL, NULL, E_SETTING, ftc_warning,
260 _("Unsupported compression type %d."),
261 stdata->save_compress_type);
262 break;
263 }
264 }
265
266 if (!path_is_absolute(stdata->filepath)) {
267 char tmpname[600];
268
269 if (!scenario) {
270 /* Ensure the saves directory exists. */
271 if (srvarg.saves_pathname[0] != '\0'
273 log_error(_("Can't create saves directory %s!"),
275 /* Don't tell server paths to clients */
276 notify_conn(NULL, NULL, E_SETTING, ftc_warning,
277 _("Can't create saves directory!"));
278
279 save_thread_data_free(stdata);
280 timer_destroy(timer_cpu);
281 timer_destroy(timer_user);
282
283 return;
284 }
285
287 } else {
288 /* Make sure scenario directory exist */
289 if (srvarg.scenarios_pathname[0] != '\0'
291 log_error(_("Can't create scenario saves directory %s!"),
293 /* Don't tell server paths to clients */
294 notify_conn(NULL, NULL, E_SETTING, ftc_warning,
295 _("Can't create scenario saves directory!"));
296
297 save_thread_data_free(stdata);
298 timer_destroy(timer_cpu);
299 timer_destroy(timer_user);
300
301 return;
302 }
303
305 }
306
307 if (tmpname[0] != '\0') {
308 sz_strlcat(tmpname, "/");
309 }
310 sz_strlcat(tmpname, stdata->filepath);
311 sz_strlcpy(stdata->filepath, tmpname);
312 }
313
314 if (save_thread != NULL) {
315 /* Previously started thread */
318 /* Setting has changed since the last save */
319 free(save_thread);
320 save_thread = NULL;
321 }
322 } else if (game.server.threaded_save) {
324 }
325
326 if (save_thread != NULL) {
328 } else {
329 save_thread_run(stdata);
330 }
331
332#ifdef LOG_TIMERS
333 log_verbose("Save time: %g seconds (%g apparent)",
334 timer_read_seconds(timer_cpu), timer_read_seconds(timer_user));
335#endif
336
337 timer_destroy(timer_cpu);
338 timer_destroy(timer_user);
339}
340
341/************************************************************************/
345{
346 if (save_thread != NULL) {
348 free(save_thread);
349 save_thread = NULL;
350 }
351}
352
#define CALL_FUNC_EACH_AI(_func,...)
Definition ai.h:384
#define CALL_PLR_AI_FUNC(_func, _player,...)
Definition ai.h:374
bool has_capabilities(const char *us, const char *them)
Definition capability.c:86
#define city_list_iterate(citylist, pcity)
Definition city.h:488
#define city_list_iterate_end
Definition city.h:490
void con_write(enum rfc_status rfc_status, const char *message,...)
Definition console.c:203
@ C_FAIL
Definition console.h:45
@ C_OK
Definition console.h:41
struct unit struct city struct unit struct tile struct extra_type const struct act_prob *act_probs int actor_unit_id struct unit struct unit * punit
Definition dialogs_g.h:73
#define _(String)
Definition fcintl.h:67
int fc_thread_start(fc_thread *thread, void(*function)(void *arg), void *arg)
void fc_thread_wait(fc_thread *thread)
const struct ft_color ftc_warning
struct civ_game game
Definition game.c:57
int generate_save_name(const char *format, char *buf, int buflen, const char *reason)
Definition game.c:771
fz_method
Definition ioz.h:36
@ FZ_PLAIN
Definition ioz.h:37
#define fc_assert_ret(condition)
Definition log.h:191
#define log_verbose(message,...)
Definition log.h:109
#define log_debug(message,...)
Definition log.h:115
#define log_error(message,...)
Definition log.h:103
#define fc_malloc(sz)
Definition mem.h:34
void notify_conn(struct conn_list *dest, const struct tile *ptile, enum event_type event, const struct ft_color color, const char *format,...)
Definition notify.c:238
#define players_iterate_end
Definition player.h:535
#define players_iterate(_pplayer)
Definition player.h:530
#define players_iterate_alive_end
Definition player.h:545
#define players_iterate_alive(_pplayer)
Definition player.h:540
void update_capital(struct player *pplayer)
Definition plrhand.c:612
struct section_file * secfile_new(bool allow_duplicates)
const char * secfile_error(void)
void secfile_destroy(struct section_file *secfile)
const char * secfile_lookup_str(const struct section_file *secfile, const char *path,...)
bool secfile_save(const struct section_file *secfile, const char *filename, int compression_level, enum fz_method compression_method)
void savegame2_load(struct section_file *file)
Definition savegame2.c:387
void savegame3_save(struct section_file *sfile, const char *save_reason, bool scenario)
Definition savegame3.c:417
void savegame3_load(struct section_file *file)
Definition savegame3.c:444
void savegame_load(struct section_file *sfile)
Definition savemain.c:44
void savegame_save(struct section_file *sfile, const char *save_reason, bool scenario)
Definition savemain.c:106
void save_system_close(void)
Definition savemain.c:344
static void save_thread_run(void *arg)
Definition savemain.c:132
static void save_thread_data_free(struct save_thread_data *stdata)
Definition savemain.c:123
void save_game(const char *orig_filename, const char *save_reason, bool scenario)
Definition savemain.c:152
static fc_thread * save_thread
Definition savemain.c:39
bool make_dir(const char *pathname)
Definition shared.c:1772
bool path_is_absolute(const char *filename)
Definition shared.c:1873
struct server_arguments srvarg
Definition srv_main.c:173
bool threaded_save
Definition game.h:179
int save_compress_level
Definition game.h:180
struct civ_game::@30::@34 server
char save_name[MAX_LEN_NAME]
Definition game.h:219
enum fz_method save_compress_type
Definition game.h:181
int save_compress_level
Definition savemain.c:116
enum fz_method save_compress_type
Definition savemain.c:117
struct section_file * sfile
Definition savemain.c:114
char filepath[600]
Definition savemain.c:115
char * scenarios_pathname
Definition srv_main.h:47
char * saves_pathname
Definition srv_main.h:46
Definition timing.c:81
#define sz_strlcpy(dest, src)
Definition support.h:167
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
#define sz_strlcat(dest, src)
Definition support.h:168
struct timer * timer_new(enum timer_timetype type, enum timer_use use)
Definition timing.c:157
void timer_destroy(struct timer *t)
Definition timing.c:191
void timer_start(struct timer *t)
Definition timing.c:224
void timer_stop(struct timer *t)
Definition timing.c:268
double timer_read_seconds(struct timer *t)
Definition timing.c:344
@ TIMER_ACTIVE
Definition timing.h:45
#define TIMER_DEBUG
Definition timing.h:59
@ TIMER_CPU
Definition timing.h:40
@ TIMER_USER
Definition timing.h:41
#define unit_list_iterate(unitlist, punit)
Definition unitlist.h:31
#define unit_list_iterate_end
Definition unitlist.h:33