Freeciv21
Develop your civilization from humble roots to a global empire
savemain.cpp
Go to the documentation of this file.
1 /*__ ___ ***************************************
2 / \ / \ Copyright (c) 1996-2020 Freeciv21 and Freeciv
3 \_ \ / __/ contributors. This file is part of Freeciv21.
4  _\ \ / /__ Freeciv21 is free software: you can redistribute it
5  \___ \____/ __/ and/or modify it under the terms of the GNU General
6  \_ _/ Public License as published by the Free Software
7  | @ @ \_ Foundation, either version 3 of the License,
8  | or (at your option) any later version.
9  _/ /\ You should have received a copy of the GNU
10  /o) (o/\ \_ General Public License along with Freeciv21.
11  \_____/ / If not, see https://www.gnu.org/licenses/.
12  \____/ ********************************************************/
13 
14 #include <fc_config.h>
15 
16 // Qt
17 #include <QDir>
18 #include <QString>
19 
20 // utility
21 #include "fcthread.h"
22 #include "log.h"
23 #include "registry.h"
24 #include "registry_ini.h"
25 
26 // common
27 #include "ai.h"
28 #include "capability.h"
29 #include "game.h"
30 
31 // server
32 #include "console.h"
33 #include "notify.h"
34 
35 /* server/savegame */
36 #include "savegame2.h"
37 #include "savegame3.h"
38 
39 #include "savemain.h"
40 
41 Q_GLOBAL_STATIC(fcThread, save_thread);
42 
46 void savegame_load(struct section_file *sfile)
47 {
48  const char *savefile_options;
49 
50  fc_assert_ret(sfile != nullptr);
51 
52  civtimer *loadtimer = timer_new(TIMER_CPU, TIMER_DEBUG);
53  timer_start(loadtimer);
54 
55  savefile_options = secfile_lookup_str(sfile, "savefile.options");
56 
57  if (!savefile_options) {
58  qCritical("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  qDebug("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  qDebug("loading savefile in 2.3 - 2.6 format ...");
69  savegame2_load(sfile);
70  } else {
71  qCritical("Too old savegame format not supported any more.");
72  return;
73  }
74 
75  players_iterate(pplayer)
76  {
77  unit_list_iterate(pplayer->units, punit)
78  {
79  CALL_FUNC_EACH_AI(unit_created, punit);
80  CALL_PLR_AI_FUNC(unit_got, pplayer, punit);
81  }
83 
84  city_list_iterate(pplayer->cities, pcity)
85  {
86  CALL_FUNC_EACH_AI(city_created, pcity);
87  CALL_PLR_AI_FUNC(city_got, pplayer, pplayer, pcity);
88  }
90  }
92 
93  timer_stop(loadtimer);
94  qCDebug(timers_category, "Loading secfile in %.3f seconds.",
95  timer_read_seconds(loadtimer));
96  timer_destroy(loadtimer);
97 }
98 
102 void savegame_save(struct section_file *sfile, const char *save_reason,
103  bool scenario)
104 {
105  savegame3_save(sfile, save_reason, scenario);
106 }
107 
110  char filepath[600];
112 };
113 
117 static void save_thread_run(void *arg)
118 {
119  struct save_thread_data *stdata =
120  static_cast<struct save_thread_data *>(arg);
121 
122  if (!secfile_save(stdata->sfile, stdata->filepath)) {
123  con_write(C_FAIL, _("Failed saving game as %s"), stdata->filepath);
124  qCritical("Game saving failed: %s", secfile_error());
125  notify_conn(nullptr, nullptr, E_LOG_ERROR, ftc_warning,
126  _("Failed saving game."));
127  } else {
128  con_write(C_OK, _("Game saved as %s"), stdata->filepath);
129  }
130 
131  secfile_destroy(stdata->sfile);
132  delete stdata;
133 }
134 
139 void save_game(const char *orig_filename, const char *save_reason,
140  bool scenario)
141 {
142  char *dot, *filename;
143  civtimer *timer_cpu;
144  struct save_thread_data *stdata = new save_thread_data();
145 
146  stdata->save_compress_type = game.server.save_compress_type;
147 
148  if (!orig_filename) {
149  stdata->filepath[0] = '\0';
150  filename = stdata->filepath;
151  } else {
152  sz_strlcpy(stdata->filepath, orig_filename);
153  filename = strrchr(stdata->filepath, '/');
154  if (filename) {
155  filename++;
156  } else {
157  filename = stdata->filepath;
158  }
159 
160  // Ignores the dot at the start of the filename.
161  for (dot = filename; '.' == *dot; dot++) {
162  // Nothing.
163  }
164  if ('\0' == *dot) {
165  // Only dots in this file name, consider it as empty.
166  filename[0] = '\0';
167  } else {
168  char *end_dot;
169  const char *strip_extensions[] = {".sav", ".gz", ".bz2",
170  ".xz", ".zst", nullptr};
171  bool stripped = true;
172 
173  while ((end_dot = strrchr(dot, '.')) && stripped) {
174  int i;
175 
176  stripped = false;
177 
178  for (i = 0; strip_extensions[i] != nullptr && !stripped; i++) {
179  if (!strcmp(end_dot, strip_extensions[i])) {
180  *end_dot = '\0';
181  stripped = true;
182  }
183  }
184  }
185  }
186  }
187 
188  // If orig_filename is nullptr or empty, use a generated default name.
189  if (filename[0] == '\0') {
190  // manual save
192  game.server.save_name, filename,
193  sizeof(stdata->filepath) + stdata->filepath - filename, "manual");
194  }
195 
196  timer_cpu = timer_new(TIMER_CPU, TIMER_ACTIVE);
197  timer_start(timer_cpu);
198 
199  /* Allowing duplicates shouldn't be allowed. However, it takes very too
200  * long time for huge game saving... */
201  stdata->sfile = secfile_new(true);
202  savegame_save(stdata->sfile, save_reason, scenario);
203 
204  /* We have consistent game state in stdata->sfile now, so
205  * we could pass it to the saving thread already. We want to
206  * handle below notify_conn() and directory creation in
207  * main thread, though. */
208 
209  // Append ".sav" to filename.
210  sz_strlcat(stdata->filepath, ".sav");
211 
212  {
213  switch (stdata->save_compress_type) {
214  case COMPRESS_ZLIB:
215  // Append ".gz" to filename.
216  sz_strlcat(stdata->filepath, ".gz");
217  break;
218 #ifdef FREECIV_HAVE_BZ2
219  case COMPRESS_BZIP2:
220  // Append ".bz2" to filename.
221  sz_strlcat(stdata->filepath, ".bz2");
222  break;
223 #endif
224 #ifdef FREECIV_HAVE_LZMA
225  case COMPRESS_XZ:
226  // Append ".xz" to filename.
227  sz_strlcat(stdata->filepath, ".xz");
228  break;
229 #endif
230 #ifdef FREECIV_HAVE_ZSTD
231  case COMPRESS_ZSTD:
232  // Append ".zst" to filename.
233  sz_strlcat(stdata->filepath, ".zst");
234  break;
235 #endif
236  case COMPRESS_PLAIN:
237  break;
238  default:
239  qCritical(_("Unsupported compression type %d."),
240  stdata->save_compress_type);
241  notify_conn(nullptr, nullptr, E_SETTING, ftc_warning,
242  _("Unsupported compression type %d."),
243  stdata->save_compress_type);
244  break;
245  }
246  }
247 
248  if (!QFileInfo(stdata->filepath).isAbsolute()) {
249  QString tmpname;
250 
251  if (!scenario) {
252  // Ensure the saves directory exists.
253  if (!srvarg.saves_pathname.isEmpty()) {
254  QDir().mkpath(srvarg.saves_pathname);
255  }
256  tmpname = srvarg.saves_pathname;
257  } else {
258  // Make sure scenario directory exist
259  if (!srvarg.scenarios_pathname.isEmpty()) {
260  QDir().mkpath(srvarg.scenarios_pathname);
261  }
262  tmpname = srvarg.scenarios_pathname;
263  }
264 
265  if (!tmpname.isEmpty()) {
266  tmpname += QLatin1String("/");
267  }
268  tmpname += QString::fromUtf8(stdata->filepath);
269  sz_strlcpy(stdata->filepath, qUtf8Printable(tmpname));
270  }
271 
272  // Don't start another thread if game is being saved now
273  if (!save_thread->isRunning()) {
274  save_thread->set_func(save_thread_run, stdata);
275  save_thread->start(QThread::LowestPriority);
276  }
277 
278  log_time(QStringLiteral("Save time: %1 seconds")
279  .arg(timer_read_seconds(timer_cpu)));
280  timer_destroy(timer_cpu);
281 }
282 
286 void save_system_close() { save_thread->wait(); }
#define CALL_FUNC_EACH_AI(_func,...)
Definition: ai.h:393
#define CALL_PLR_AI_FUNC(_func, _player,...)
Definition: ai.h:383
bool has_capabilities(const char *us, const char *them)
This routine returns true if all the mandatory capabilities in us appear in them.
Definition: capability.cpp:80
#define city_list_iterate(citylist, pcity)
Definition: city.h:482
#define city_list_iterate_end
Definition: city.h:484
void con_write(enum rfc_status rfc_status, const char *message,...)
Write to console and add line-break, and show prompt if required.
Definition: console.cpp:139
@ C_FAIL
Definition: console.h:43
@ C_OK
Definition: console.h:41
#define _(String)
Definition: fcintl.h:50
const struct ft_color ftc_warning
struct civ_game game
Definition: game.cpp:47
int generate_save_name(const char *format, char *buf, int buflen, const char *reason)
Generate a default save file name and place it in the provided buffer.
Definition: game.cpp:756
compress_type
Definition: game.h:40
@ COMPRESS_ZLIB
Definition: game.h:42
@ COMPRESS_PLAIN
Definition: game.h:41
void log_time(const QString &msg, bool log)
Definition: log.cpp:253
#define fc_assert_ret(condition)
Definition: log.h:112
void notify_conn(struct conn_list *dest, const struct tile *ptile, enum event_type event, const struct ft_color color, const char *format,...)
See notify_conn_packet - this is just the "non-v" version, with varargs.
Definition: notify.cpp:235
#define players_iterate_end
Definition: player.h:520
#define players_iterate(_pplayer)
Definition: player.h:514
const char * secfile_error()
Returns the last error which occurred in a string.
struct section_file * secfile_new(bool allow_duplicates)
Create a new empty section file.
void secfile_destroy(struct section_file *secfile)
Free a section file.
bool secfile_save(const struct section_file *secfile, QString filename)
Save the previously filled in section_file to disk.
const char * secfile_lookup_str(const struct section_file *secfile, const char *path,...)
Lookup a string value in the secfile.
void savegame2_load(struct section_file *file)
Really loading the savegame.
Definition: savegame2.cpp:377
void savegame3_save(struct section_file *sfile, const char *save_reason, bool scenario)
Main entry point for saving a game in savegame3 format.
Definition: savegame3.cpp:412
void savegame3_load(struct section_file *file)
Really loading the savegame.
Definition: savegame3.cpp:436
void savegame_load(struct section_file *sfile)
Main entry point for loading a game.
Definition: savemain.cpp:46
void save_system_close()
Close saving system.
Definition: savemain.cpp:286
void savegame_save(struct section_file *sfile, const char *save_reason, bool scenario)
Main entry point for saving a game.
Definition: savemain.cpp:102
Q_GLOBAL_STATIC(fcThread, save_thread)
static void save_thread_run(void *arg)
Run game saving thread.
Definition: savemain.cpp:117
void save_game(const char *orig_filename, const char *save_reason, bool scenario)
Unconditionally save the game, with specified filename.
Definition: savemain.cpp:139
struct server_arguments srvarg
Definition: srv_main.cpp:118
struct civ_game::@28::@32 server
struct section_file * sfile
Definition: savemain.cpp:109
char filepath[600]
Definition: savemain.cpp:110
compress_type save_compress_type
Definition: savemain.cpp:111
QString saves_pathname
Definition: srv_main.h:49
QString scenarios_pathname
Definition: srv_main.h:50
#define sz_strlcpy(dest, src)
Definition: support.h:140
#define sz_strlcat(dest, src)
Definition: support.h:142
void timer_destroy(civtimer *t)
Deletes timer.
Definition: timing.cpp:66
double timer_read_seconds(civtimer *t)
Read value from timer.
Definition: timing.cpp:137
civtimer * timer_new(enum timer_timetype type, enum timer_use use)
Allocate a new timer with specified "type" and "use".
Definition: timing.cpp:43
void timer_start(civtimer *t)
Start timing, adding to previous accumulated time if timer has not been cleared.
Definition: timing.cpp:95
void timer_stop(civtimer *t)
Stop timing, and accumulate time so far.
Definition: timing.cpp:116
@ TIMER_ACTIVE
Definition: timing.h:25
#define TIMER_DEBUG
Definition: timing.h:39
@ TIMER_CPU
Definition: timing.h:20
#define unit_list_iterate(unitlist, punit)
Definition: unitlist.h:25
#define unit_list_iterate_end
Definition: unitlist.h:27