1 /*
2  * Copyright (C) 2002-2020 by the Widelands Development Team
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  *
18  */
19 
20 #include "logic/game.h"
21 
22 #include <memory>
23 #include <string>
24 
25 #ifndef _WIN32
26 #include <unistd.h>  // for usleep
27 #else
28 #include <SDL_events.h>  // for a dirty hack.
29 #include <windows.h>
30 #endif
31 
32 #include "base/i18n.h"
33 #include "base/log.h"
34 #include "base/macros.h"
35 #include "base/time_string.h"
36 #include "base/warning.h"
37 #include "build_info.h"
38 #include "economy/economy.h"
39 #include "economy/portdock.h"
40 #include "game_io/game_loader.h"
41 #include "game_io/game_preload_packet.h"
42 #include "io/fileread.h"
43 #include "io/filesystem/filesystem_exceptions.h"
44 #include "io/filesystem/layered_filesystem.h"
45 #include "io/filewrite.h"
46 #include "logic/cmd_calculate_statistics.h"
47 #include "logic/cmd_luacoroutine.h"
48 #include "logic/cmd_luascript.h"
49 #include "logic/filesystem_constants.h"
50 #include "logic/game_settings.h"
51 #include "logic/map_objects/tribes/carrier.h"
52 #include "logic/map_objects/tribes/market.h"
53 #include "logic/map_objects/tribes/militarysite.h"
54 #include "logic/map_objects/tribes/ship.h"
55 #include "logic/map_objects/tribes/soldier.h"
56 #include "logic/map_objects/tribes/trainingsite.h"
57 #include "logic/map_objects/tribes/tribe_descr.h"
58 #include "logic/map_objects/tribes/warehouse.h"
59 #include "logic/player.h"
60 #include "logic/playercommand.h"
61 #include "logic/replay.h"
62 #include "logic/single_player_game_controller.h"
63 #include "map_io/widelands_map_loader.h"
64 #include "scripting/lua_table.h"
65 #include "sound/sound_handler.h"
66 #include "ui_basic/progresswindow.h"
67 #include "wlapplication_options.h"
68 #include "wui/interactive_player.h"
69 
70 namespace Widelands {
71 
72 /// Define this to get lots of debugging output concerned with syncs
73 // #define SYNC_DEBUG
74 
~SyncWrapper()75 Game::SyncWrapper::~SyncWrapper() {
76 	if (dump_ != nullptr) {
77 		if (!syncstreamsave_)
78 			try {
79 				g_fs->fs_unlink(dumpfname_);
80 			} catch (const FileError& e) {
81 				// not really a problem if deletion fails, but we'll log it
82 				log("Deleting synchstream file %s failed: %s\n", dumpfname_.c_str(), e.what());
83 			}
84 	}
85 }
86 
start_dump(const std::string & fname)87 void Game::SyncWrapper::start_dump(const std::string& fname) {
88 	dumpfname_ = fname + kSyncstreamExtension;
89 	dump_.reset(g_fs->open_stream_write(dumpfname_));
90 	current_excerpt_id_ = 0;
91 	excerpts_buffer_[current_excerpt_id_].clear();
92 }
93 
data(void const * const sync_data,size_t const size)94 void Game::SyncWrapper::data(void const* const sync_data, size_t const size) {
95 #ifdef SYNC_DEBUG
96 	uint32_t time = game_.get_gametime();
97 	log("[sync:%08u t=%6u]", counter_, time);
98 	for (size_t i = 0; i < size; ++i)
99 		log(" %02x", (static_cast<uint8_t const*>(sync_data))[i]);
100 	log("\n");
101 #endif
102 
103 	if (dump_ != nullptr && static_cast<int32_t>(counter_ - next_diskspacecheck_) >= 0) {
104 		next_diskspacecheck_ = counter_ + 16 * 1024 * 1024;
105 
106 		if (g_fs->disk_space() < kMinimumDiskSpace) {
107 			log("Stop writing to syncstream file: disk is getting full.\n");
108 			dump_.reset();
109 		}
110 	}
111 
112 	if (dump_ != nullptr) {
113 		try {
114 			dump_->data(sync_data, size);
115 		} catch (const WException&) {
116 			log("Writing to syncstream file %s failed. Stop synctream dump.\n", dumpfname_.c_str());
117 			dump_.reset();
118 		}
119 		assert(current_excerpt_id_ < kExcerptSize);
120 		excerpts_buffer_[current_excerpt_id_].append(static_cast<const char*>(sync_data), size);
121 	}
122 
123 	target_.data(sync_data, size);
124 	counter_ += size;
125 }
126 
Game()127 Game::Game()
128    : EditorGameBase(new LuaGameInterface(this)),
129      forester_cache_(),
130      syncwrapper_(*this, synchash_),
131      ctrl_(nullptr),
132      writereplay_(true),
133      writesyncstream_(false),
134      ai_training_mode_(false),
135      auto_speed_(false),
136      state_(gs_notrunning),
137      cmdqueue_(*this),
138      scenario_difficulty_(kScenarioDifficultyNotSet),
139      /** TRANSLATORS: Win condition for this game has not been set. */
140      win_condition_displayname_(_("Not set")),
141      replay_(false) {
142 	Economy::initialize_serial();
143 }
144 
~Game()145 Game::~Game() {
146 }
147 
sync_reset()148 void Game::sync_reset() {
149 	syncwrapper_.counter_ = 0;
150 
151 	synchash_.reset();
152 	log("[sync] Reset\n");
153 }
154 
155 /**
156  * \return a pointer to the \ref InteractivePlayer if any.
157  * \note This function may return 0 (in particular, it will return 0 during
158  * playback or if player is spectator)
159  */
get_ipl()160 InteractivePlayer* Game::get_ipl() {
161 	return dynamic_cast<InteractivePlayer*>(get_ibase());
162 }
163 
set_game_controller(GameController * const ctrl)164 void Game::set_game_controller(GameController* const ctrl) {
165 	ctrl_ = ctrl;
166 }
167 
set_ai_training_mode(const bool mode)168 void Game::set_ai_training_mode(const bool mode) {
169 	ai_training_mode_ = mode;
170 }
171 
set_auto_speed(const bool mode)172 void Game::set_auto_speed(const bool mode) {
173 	auto_speed_ = mode;
174 }
175 
game_controller()176 GameController* Game::game_controller() {
177 	return ctrl_;
178 }
179 
set_write_replay(bool const wr)180 void Game::set_write_replay(bool const wr) {
181 	//  we want to allow for the possibility to stop writing our replay
182 	//  this is to ensure we do not crash because of diskspace
183 	//  still this is only possibe to go from true->false
184 	//  still probally should not do this with an assert but with better checks
185 	assert(state_ == gs_notrunning || !wr);
186 
187 	writereplay_ = wr;
188 }
189 
set_write_syncstream(bool const wr)190 void Game::set_write_syncstream(bool const wr) {
191 	assert(state_ == gs_notrunning);
192 
193 	writesyncstream_ = wr;
194 }
195 
196 /**
197  * Set whether the syncstream dump should be copied to a permanent location
198  * at the end of the game.
199  */
save_syncstream(bool const save)200 void Game::save_syncstream(bool const save) {
201 	syncwrapper_.syncstreamsave_ = save;
202 }
203 
run_splayer_scenario_direct(const std::string & mapname,const std::string & script_to_run)204 bool Game::run_splayer_scenario_direct(const std::string& mapname,
205                                        const std::string& script_to_run) {
206 	// Replays can't handle scenarios
207 	set_write_replay(false);
208 
209 	std::unique_ptr<MapLoader> maploader(mutable_map()->get_correct_loader(mapname));
210 	if (!maploader) {
211 		throw wexception("could not load \"%s\"", mapname.c_str());
212 	}
213 
214 	create_loader_ui({"general_game"}, false);
215 
216 	step_loader_ui(_("Preloading map…"));
217 	maploader->preload_map(true);
218 	change_loader_ui_background(map().get_background());
219 
220 	step_loader_ui(_("Loading world…"));
221 	world();
222 	step_loader_ui(_("Loading tribes…"));
223 	tribes();
224 
225 	// If the scenario has custrom tribe entites, load them.
226 	const std::string custom_tribe_script = mapname + "/scripting/tribes/init.lua";
227 	if (g_fs->file_exists(custom_tribe_script)) {
228 		lua().run_script(custom_tribe_script);
229 	}
230 
231 	// We have to create the players here.
232 	step_loader_ui(_("Creating players…"));
233 	PlayerNumber const nr_players = map().get_nrplayers();
234 	iterate_player_numbers(p, nr_players) {
235 		// If tribe name is empty, pick a random tribe
236 		std::string tribe = map().get_scenario_player_tribe(p);
237 		if (tribe.empty()) {
238 			log("Setting random tribe for Player %d\n", static_cast<unsigned int>(p));
239 			const DescriptionIndex random = std::rand() % tribes().nrtribes();
240 			tribe = tribes().get_tribe_descr(random)->name();
241 		}
242 		add_player(p, 0, tribe, map().get_scenario_player_name(p));
243 		get_player(p)->set_ai(map().get_scenario_player_ai(p));
244 	}
245 	win_condition_displayname_ = "Scenario";
246 
247 	set_ibase(new InteractivePlayer(*this, get_config_section(), 1, false));
248 
249 	step_loader_ui(_("Loading map…"));
250 	maploader->load_map_complete(*this, Widelands::MapLoader::LoadType::kScenario);
251 	maploader.reset();
252 
253 	set_game_controller(new SinglePlayerGameController(*this, true, 1));
254 	try {
255 		bool const result = run(NewSPScenario, script_to_run, false, "single_player");
256 		delete ctrl_;
257 		ctrl_ = nullptr;
258 		return result;
259 	} catch (...) {
260 		delete ctrl_;
261 		ctrl_ = nullptr;
262 		throw;
263 	}
264 }
265 
266 /**
267  * Initialize the game based on the given settings.
268  *
269  */
init_newgame(const GameSettings & settings)270 void Game::init_newgame(const GameSettings& settings) {
271 	assert(has_loader_ui());
272 
273 	step_loader_ui(_("Preloading map…"));
274 
275 	std::unique_ptr<MapLoader> maploader(mutable_map()->get_correct_loader(settings.mapfilename));
276 	assert(maploader != nullptr);
277 	maploader->preload_map(settings.scenario);
278 	change_loader_ui_background(map().get_background());
279 
280 	step_loader_ui(_("Loading world…"));
281 	world();
282 
283 	step_loader_ui(_("Loading tribes…"));
284 	tribes();
285 
286 	step_loader_ui(_("Creating players…"));
287 
288 	std::vector<PlayerSettings> shared;
289 	std::vector<uint8_t> shared_num;
290 	for (uint32_t i = 0; i < settings.players.size(); ++i) {
291 		const PlayerSettings& playersettings = settings.players[i];
292 
293 		if (playersettings.state == PlayerSettings::State::kClosed ||
294 		    playersettings.state == PlayerSettings::State::kOpen)
295 			continue;
296 		else if (playersettings.state == PlayerSettings::State::kShared) {
297 			shared.push_back(playersettings);
298 			shared_num.push_back(i + 1);
299 			continue;
300 		}
301 
302 		add_player(i + 1, playersettings.initialization_index, playersettings.tribe,
303 		           playersettings.name, playersettings.team);
304 		get_player(i + 1)->set_ai(playersettings.ai);
305 	}
306 
307 	// Add shared in starting positions
308 	for (uint8_t n = 0; n < shared.size(); ++n) {
309 		// This player's starting position is used in another (shared) kingdom
310 		get_player(shared.at(n).shared_in)
311 		   ->add_further_starting_position(shared_num.at(n), shared.at(n).initialization_index);
312 	}
313 
314 	step_loader_ui(_("Loading map…"));
315 	maploader->load_map_complete(*this, settings.scenario ?
316 	                                       Widelands::MapLoader::LoadType::kScenario :
317 	                                       Widelands::MapLoader::LoadType::kGame);
318 
319 	// Check for win_conditions
320 	if (!settings.scenario) {
321 		step_loader_ui(_("Initializing game…"));
322 		if (settings.peaceful) {
323 			for (uint32_t i = 1; i < settings.players.size(); ++i) {
324 				if (Player* p1 = get_player(i)) {
325 					for (uint32_t j = i + 1; j <= settings.players.size(); ++j) {
326 						if (Player* p2 = get_player(j)) {
327 							p1->set_attack_forbidden(j, true);
328 							p2->set_attack_forbidden(i, true);
329 						}
330 					}
331 				}
332 			}
333 		}
334 
335 		std::unique_ptr<LuaTable> table(lua().run_script(settings.win_condition_script));
336 		table->do_not_warn_about_unaccessed_keys();
337 		win_condition_displayname_ = table->get_string("name");
338 		if (table->has_key<std::string>("init")) {
339 			std::unique_ptr<LuaCoroutine> cr = table->get_coroutine("init");
340 			cr->resume();
341 		}
342 		std::unique_ptr<LuaCoroutine> cr = table->get_coroutine("func");
343 		enqueue_command(new CmdLuaCoroutine(get_gametime() + 100, std::move(cr)));
344 	} else {
345 		win_condition_displayname_ = "Scenario";
346 	}
347 }
348 
349 /**
350  * Initialize the savegame based on the given settings.
351  * At return the game is at the same state like a map loaded with Game::init()
352  * Only difference is, that players are already initialized.
353  * run<Returncode>() takes care about this difference.
354  */
init_savegame(const GameSettings & settings)355 void Game::init_savegame(const GameSettings& settings) {
356 	assert(has_loader_ui());
357 
358 	step_loader_ui(_("Preloading map…"));
359 
360 	try {
361 		GameLoader gl(settings.mapfilename, *this);
362 		Widelands::GamePreloadPacket gpdp;
363 		gl.preload_game(gpdp);
364 		change_loader_ui_background(gpdp.get_background());
365 
366 		win_condition_displayname_ = gpdp.get_win_condition();
367 		if (win_condition_displayname_ == "Scenario") {
368 			// Replays can't handle scenarios
369 			set_write_replay(false);
370 		}
371 
372 		step_loader_ui(_("Loading…"));
373 		gl.load_game(settings.multiplayer);
374 		// Players might have selected a different AI type
375 		for (uint8_t i = 0; i < settings.players.size(); ++i) {
376 			const PlayerSettings& playersettings = settings.players[i];
377 			if (playersettings.state == PlayerSettings::State::kComputer) {
378 				get_player(i + 1)->set_ai(playersettings.ai);
379 			}
380 		}
381 	} catch (...) {
382 		throw;
383 	}
384 }
385 
run_load_game(const std::string & filename,const std::string & script_to_run)386 bool Game::run_load_game(const std::string& filename, const std::string& script_to_run) {
387 	create_loader_ui({"general_game", "singleplayer"}, false);
388 	int8_t player_nr;
389 
390 	step_loader_ui(_("Preloading map…"));
391 
392 	{
393 		GameLoader gl(filename, *this);
394 
395 		Widelands::GamePreloadPacket gpdp;
396 		gl.preload_game(gpdp);
397 		change_loader_ui_background(gpdp.get_background());
398 
399 		win_condition_displayname_ = gpdp.get_win_condition();
400 		if (win_condition_displayname_ == "Scenario") {
401 			// Replays can't handle scenarios
402 			set_write_replay(false);
403 		}
404 
405 		player_nr = gpdp.get_player_nr();
406 		set_ibase(new InteractivePlayer(*this, get_config_section(), player_nr, false));
407 
408 		step_loader_ui(_("Loading…"));
409 		gl.load_game();
410 	}
411 
412 	// Store the filename for further saves
413 	save_handler().set_current_filename(filename);
414 
415 	set_game_controller(new SinglePlayerGameController(*this, true, player_nr));
416 	try {
417 		bool const result = run(Loaded, script_to_run, false, "single_player");
418 		delete ctrl_;
419 		ctrl_ = nullptr;
420 		return result;
421 	} catch (...) {
422 		delete ctrl_;
423 		ctrl_ = nullptr;
424 		throw;
425 	}
426 }
427 
428 /**
429  * Called for every game after loading (from a savegame or just from a map
430  * during single/multiplayer/scenario).
431  *
432  * Ensure that players and player controllers are setup properly (in particular
433  * AI and the \ref InteractivePlayer if any).
434  */
postload()435 void Game::postload() {
436 	EditorGameBase::postload();
437 	get_ibase()->postload();
438 }
439 
440 /**
441  * This runs a game, including game creation phase.
442  *
443  * The setup and loading of a game happens (or rather: will happen) in three
444  * stages.
445  * 1.  First of all, the host (or single player) configures the game. During
446  *     this time, only short descriptions of the game data (such as map
447  *     headers)are loaded to minimize loading times.
448  * 2a. Once the game is about to start and the configuration screen is finished,
449  *     all logic data (map, tribe information, building information) is loaded
450  *     during postload.
451  * 2b. If a game is created, initial player positions are set. This step is
452  *     skipped when a game is loaded.
453  * 3.  After this has happened, the game graphics are loaded.
454  *
455  * \return true if a game actually took place, false otherwise
456  */
run(StartGameType const start_game_type,const std::string & script_to_run,bool replay,const std::string & prefix_for_replays)457 bool Game::run(StartGameType const start_game_type,
458                const std::string& script_to_run,
459                bool replay,
460                const std::string& prefix_for_replays) {
461 	assert(has_loader_ui());
462 
463 	replay_ = replay;
464 	postload();
465 
466 	if (start_game_type != Loaded) {
467 		PlayerNumber const nr_players = map().get_nrplayers();
468 		if (start_game_type == NewNonScenario) {
469 			step_loader_ui(_("Creating player infrastructure…"));
470 			iterate_players_existing(p, nr_players, *this, plr) {
471 				plr->create_default_infrastructure();
472 			}
473 		} else {
474 			// Is a scenario!
475 			// Replays can't handle scenarios
476 			set_write_replay(false);
477 			iterate_players_existing_novar(p, nr_players, *this) {
478 				if (!map().get_starting_pos(p))
479 					throw WLWarning(_("Missing starting position"),
480 					                _("Widelands could not start the game, because player %u has "
481 					                  "no starting position.\n"
482 					                  "You can manually add a starting position with the Widelands "
483 					                  "Editor to fix this problem."),
484 					                static_cast<unsigned int>(p));
485 			}
486 		}
487 
488 		if (get_ipl()) {
489 			// Scroll map to starting position for new games.
490 			// Loaded games are handled in GameInteractivePlayerPacket for single player, and in
491 			// InteractiveGameBase::start() for multiplayer.
492 			get_ipl()->map_view()->scroll_to_field(
493 			   map().get_starting_pos(get_ipl()->player_number()), MapView::Transition::Jump);
494 		}
495 
496 		// Prepare the map, set default textures
497 		mutable_map()->recalc_default_resources(world());
498 
499 		// Finally, set the scenario names and tribes to represent
500 		// the correct names of the players
501 		iterate_player_numbers(p, nr_players) {
502 			const Player* const plr = get_player(p);
503 			const std::string no_name;
504 			const std::string& player_tribe = plr ? plr->tribe().name() : no_name;
505 			const std::string& player_name = plr ? plr->get_name() : no_name;
506 			const std::string& player_ai = plr ? plr->get_ai() : no_name;
507 			mutable_map()->set_scenario_player_tribe(p, player_tribe);
508 			mutable_map()->set_scenario_player_name(p, player_name);
509 			mutable_map()->set_scenario_player_ai(p, player_ai);
510 			mutable_map()->set_scenario_player_closeable(p, false);  // player is already initialized.
511 		}
512 
513 		// Run the init script, if the map provides one.
514 		if (start_game_type == NewSPScenario)
515 			enqueue_command(new CmdLuaScript(get_gametime(), "map:scripting/init.lua"));
516 		else if (start_game_type == NewMPScenario)
517 			enqueue_command(new CmdLuaScript(get_gametime(), "map:scripting/multiplayer_init.lua"));
518 
519 		// Queue first statistics calculation
520 		enqueue_command(new CmdCalculateStatistics(get_gametime() + 1));
521 	}
522 
523 	if (!script_to_run.empty() && (start_game_type == NewSPScenario || start_game_type == Loaded)) {
524 		enqueue_command(new CmdLuaScript(get_gametime() + 1, script_to_run));
525 	}
526 
527 	if (writereplay_ || writesyncstream_) {
528 		// Derive a replay filename from the current time
529 		const std::string fname = kReplayDir + g_fs->file_separator() + std::string(timestring()) +
530 		                          std::string("_") + prefix_for_replays + kReplayExtension;
531 		if (writereplay_) {
532 			log("Starting replay writer\n");
533 
534 			assert(!replaywriter_);
535 			replaywriter_.reset(new ReplayWriter(*this, fname));
536 
537 			log("Replay writer has started\n");
538 		}
539 
540 		if (writesyncstream_)
541 			syncwrapper_.start_dump(fname);
542 	}
543 
544 	sync_reset();
545 
546 	load_graphics();
547 
548 #ifdef _WIN32
549 	//  Clear the event queue before starting game because we don't want
550 	//  to handle events at game start that happened during loading procedure.
551 	SDL_Event event;
552 	while (SDL_PollEvent(&event))
553 		;
554 #endif
555 
556 	g_sh->change_music("ingame", 1000);
557 
558 	state_ = gs_running;
559 
560 	remove_loader_ui();
561 
562 	get_ibase()->run<UI::Panel::Returncodes>();
563 
564 	state_ = gs_ending;
565 
566 	g_sh->change_music("menu", 1000);
567 
568 	cleanup_objects();
569 	set_ibase(nullptr);
570 
571 	state_ = gs_notrunning;
572 
573 	return true;
574 }
575 
576 /**
577  * think() is called by the UI objects initiated during Game::run()
578  * during their modal loop.
579  * Depending on the current state we advance game logic and stuff,
580  * running the cmd queue etc.
581  */
think()582 void Game::think() {
583 	assert(ctrl_);
584 
585 	ctrl_->think();
586 
587 	if (state_ == gs_running) {
588 		// TODO(sirver): This is not good. Here, it depends on the speed of the
589 		// computer and the fps if and when the game is saved - this is very bad
590 		// for scenarios and even worse for the regression suite (which relies on
591 		// the timings of savings.
592 		cmdqueue().run_queue(ctrl_->get_frametime(), get_gametime_pointer());
593 
594 		// check if autosave is needed
595 		savehandler_.think(*this);
596 	}
597 }
598 
599 /**
600  * Cleanup for load
601  * \deprecated
602  */
603 // TODO(unknown): Get rid of this. Prefer to delete and recreate Game-style objects
604 // Note that this needs fixes in the editor.
cleanup_for_load()605 void Game::cleanup_for_load() {
606 	state_ = gs_notrunning;
607 
608 	EditorGameBase::cleanup_for_load();
609 
610 	cmdqueue().flush();
611 
612 	// Statistics
613 	general_stats_.clear();
614 }
615 
616 /**
617  * Game logic code may write to the synchronization
618  * token stream. All written data will be hashed and can be used to
619  * check for network or replay desyncs.
620  *
621  * \return the synchronization token stream
622  *
623  * \note This is returned as a \ref StreamWrite object to prevent
624  * the caller from messing with the checksumming process.
625  */
syncstream()626 StreamWrite& Game::syncstream() {
627 	return syncwrapper_;
628 }
629 
630 /**
631  * Switches to the next part of the syncstream excerpt.
632  */
report_sync_request()633 void Game::report_sync_request() {
634 	syncwrapper_.current_excerpt_id_ =
635 	   (syncwrapper_.current_excerpt_id_ + 1) % SyncWrapper::kExcerptSize;
636 	syncwrapper_.excerpts_buffer_[syncwrapper_.current_excerpt_id_].clear();
637 }
638 
639 /**
640  * Triggers writing of syncstream excerpt and adds the playernumber of the desynced player
641  * to the stream.
642  * Playernumber should be negative when called by network clients
643  */
report_desync(int32_t playernumber)644 void Game::report_desync(int32_t playernumber) {
645 	if (syncwrapper_.dumpfname_.empty()) {
646 		log("Error: A desync occurred but no filename for the syncstream has been set.");
647 		return;
648 	}
649 	// Replace .wss extension of syncstream file with .wse extension for syncstream extract
650 	std::string filename = syncwrapper_.dumpfname_;
651 	assert(syncwrapper_.dumpfname_.length() > kSyncstreamExtension.length());
652 	filename.replace(filename.length() - kSyncstreamExtension.length(),
653 	                 kSyncstreamExtension.length(), kSyncstreamExcerptExtension);
654 	std::unique_ptr<StreamWrite> file(g_fs->open_stream_write(filename));
655 	assert(file != nullptr);
656 	// Write revision, branch and build type of this build to the file
657 	file->unsigned_32(build_id().length());
658 	file->text(build_id());
659 	file->unsigned_32(build_type().length());
660 	file->text(build_type());
661 	file->signed_32(playernumber);
662 	// Write our buffers to the file. Start with the oldest one
663 	const size_t i2 = (syncwrapper_.current_excerpt_id_ + 1) % SyncWrapper::kExcerptSize;
664 	size_t i = i2;
665 	do {
666 		file->text(syncwrapper_.excerpts_buffer_[i]);
667 		syncwrapper_.excerpts_buffer_[i].clear();
668 		i = (i + 1) % SyncWrapper::kExcerptSize;
669 	} while (i != i2);
670 	file->unsigned_8(SyncEntry::kDesync);
671 	file->signed_32(playernumber);
672 	// Restart buffers
673 	syncwrapper_.current_excerpt_id_ = 0;
674 }
675 
676 /**
677  * Calculate the current synchronization checksum and copy
678  * it into the given array, without affecting the subsequent
679  * checksumming process.
680  *
681  * \return the checksum
682  */
get_sync_hash() const683 Md5Checksum Game::get_sync_hash() const {
684 	MD5Checksum<StreamWrite> copy(synchash_);
685 
686 	copy.finish_checksum();
687 	return copy.get_checksum();
688 }
689 
690 /**
691  * Return a random value that can be used in parallel game logic
692  * simulation.
693  *
694  * \note Do NOT use for random events in the UI or other display code.
695  */
logic_rand()696 uint32_t Game::logic_rand() {
697 	uint32_t const result = rng().rand();
698 	syncstream().unsigned_8(SyncEntry::kRandom);
699 	syncstream().unsigned_32(result);
700 	return result;
701 }
702 
703 /**
704  * All player-issued commands must enter the queue through this function.
705  * It takes the appropriate action, i.e. either add to the cmd_queue or send
706  * across the network.
707  */
send_player_command(PlayerCommand * pc)708 void Game::send_player_command(PlayerCommand* pc) {
709 	ctrl_->send_player_command(pc);
710 }
711 
712 /**
713  * Actually enqueue a command.
714  *
715  * \note In a network game, player commands are only allowed to enter the
716  * command queue after being accepted by the networking logic via
717  * \ref send_player_command, so you must never enqueue a player command
718  * directly.
719  */
enqueue_command(Command * const cmd)720 void Game::enqueue_command(Command* const cmd) {
721 	if (writereplay_ && replaywriter_) {
722 		if (upcast(PlayerCommand, plcmd, cmd)) {
723 			replaywriter_->send_player_command(plcmd);
724 		}
725 	}
726 	cmdqueue().enqueue(cmd);
727 }
728 
729 // we might want to make these inlines:
send_player_bulldoze(PlayerImmovable & pi,bool const recurse)730 void Game::send_player_bulldoze(PlayerImmovable& pi, bool const recurse) {
731 	send_player_command(new CmdBulldoze(get_gametime(), pi.owner().player_number(), pi, recurse));
732 }
733 
send_player_dismantle(PlayerImmovable & pi,bool kw)734 void Game::send_player_dismantle(PlayerImmovable& pi, bool kw) {
735 	send_player_command(
736 	   new CmdDismantleBuilding(get_gametime(), pi.owner().player_number(), pi, kw));
737 }
738 
send_player_build(int32_t const pid,const Coords & coords,DescriptionIndex const id)739 void Game::send_player_build(int32_t const pid, const Coords& coords, DescriptionIndex const id) {
740 	assert(tribes().building_exists(id));
741 	send_player_command(new CmdBuild(get_gametime(), pid, coords, id));
742 }
743 
send_player_build_flag(int32_t const pid,const Coords & coords)744 void Game::send_player_build_flag(int32_t const pid, const Coords& coords) {
745 	send_player_command(new CmdBuildFlag(get_gametime(), pid, coords));
746 }
747 
send_player_build_road(int32_t pid,Path & path)748 void Game::send_player_build_road(int32_t pid, Path& path) {
749 	send_player_command(new CmdBuildRoad(get_gametime(), pid, path));
750 }
751 
send_player_build_waterway(int32_t pid,Path & path)752 void Game::send_player_build_waterway(int32_t pid, Path& path) {
753 	send_player_command(new CmdBuildWaterway(get_gametime(), pid, path));
754 }
755 
send_player_flagaction(Flag & flag)756 void Game::send_player_flagaction(Flag& flag) {
757 	send_player_command(new CmdFlagAction(get_gametime(), flag.owner().player_number(), flag));
758 }
759 
send_player_start_stop_building(Building & building)760 void Game::send_player_start_stop_building(Building& building) {
761 	send_player_command(
762 	   new CmdStartStopBuilding(get_gametime(), building.owner().player_number(), building));
763 }
764 
send_player_militarysite_set_soldier_preference(Building & building,SoldierPreference my_preference)765 void Game::send_player_militarysite_set_soldier_preference(Building& building,
766                                                            SoldierPreference my_preference) {
767 	send_player_command(new CmdMilitarySiteSetSoldierPreference(
768 	   get_gametime(), building.owner().player_number(), building, my_preference));
769 }
770 
send_player_start_or_cancel_expedition(Building & building)771 void Game::send_player_start_or_cancel_expedition(Building& building) {
772 	send_player_command(
773 	   new CmdStartOrCancelExpedition(get_gametime(), building.owner().player_number(), building));
774 }
775 
send_player_enhance_building(Building & building,DescriptionIndex const id,bool kw)776 void Game::send_player_enhance_building(Building& building, DescriptionIndex const id, bool kw) {
777 	assert(building.descr().type() == MapObjectType::CONSTRUCTIONSITE ||
778 	       building.owner().tribe().has_building(id));
779 	send_player_command(
780 	   new CmdEnhanceBuilding(get_gametime(), building.owner().player_number(), building, id, kw));
781 }
782 
send_player_evict_worker(Worker & worker)783 void Game::send_player_evict_worker(Worker& worker) {
784 	send_player_command(new CmdEvictWorker(get_gametime(), worker.owner().player_number(), worker));
785 }
786 
send_player_set_ware_priority(PlayerImmovable & imm,int32_t const type,DescriptionIndex const index,int32_t const prio,bool cs)787 void Game::send_player_set_ware_priority(PlayerImmovable& imm,
788                                          int32_t const type,
789                                          DescriptionIndex const index,
790                                          int32_t const prio,
791                                          bool cs) {
792 	send_player_command(new CmdSetWarePriority(
793 	   get_gametime(), imm.owner().player_number(), imm, type, index, prio, cs));
794 }
795 
send_player_set_input_max_fill(PlayerImmovable & imm,DescriptionIndex const index,WareWorker type,uint32_t const max_fill,bool cs)796 void Game::send_player_set_input_max_fill(PlayerImmovable& imm,
797                                           DescriptionIndex const index,
798                                           WareWorker type,
799                                           uint32_t const max_fill,
800                                           bool cs) {
801 	send_player_command(new CmdSetInputMaxFill(
802 	   get_gametime(), imm.owner().player_number(), imm, index, type, max_fill, cs));
803 }
804 
send_player_change_training_options(TrainingSite & ts,TrainingAttribute attr,int32_t const val)805 void Game::send_player_change_training_options(TrainingSite& ts,
806                                                TrainingAttribute attr,
807                                                int32_t const val) {
808 	send_player_command(
809 	   new CmdChangeTrainingOptions(get_gametime(), ts.owner().player_number(), ts, attr, val));
810 }
811 
send_player_drop_soldier(Building & b,int32_t const ser)812 void Game::send_player_drop_soldier(Building& b, int32_t const ser) {
813 	assert(ser != -1);
814 	send_player_command(new CmdDropSoldier(get_gametime(), b.owner().player_number(), b, ser));
815 }
816 
send_player_change_soldier_capacity(Building & b,int32_t const val)817 void Game::send_player_change_soldier_capacity(Building& b, int32_t const val) {
818 	send_player_command(
819 	   new CmdChangeSoldierCapacity(get_gametime(), b.owner().player_number(), b, val));
820 }
821 
send_player_enemyflagaction(const Flag & flag,PlayerNumber const who_attacks,const std::vector<Serial> & soldiers)822 void Game::send_player_enemyflagaction(const Flag& flag,
823                                        PlayerNumber const who_attacks,
824                                        const std::vector<Serial>& soldiers) {
825 	if (1 < player(who_attacks)
826 	           .vision(Map::get_index(flag.get_building()->get_position(), map().get_width())))
827 		send_player_command(new CmdEnemyFlagAction(get_gametime(), who_attacks, flag, soldiers));
828 }
829 
send_player_ship_scouting_direction(Ship & ship,WalkingDir direction)830 void Game::send_player_ship_scouting_direction(Ship& ship, WalkingDir direction) {
831 	send_player_command(new CmdShipScoutDirection(
832 	   get_gametime(), ship.get_owner()->player_number(), ship.serial(), direction));
833 }
834 
send_player_ship_construct_port(Ship & ship,Coords coords)835 void Game::send_player_ship_construct_port(Ship& ship, Coords coords) {
836 	send_player_command(new CmdShipConstructPort(
837 	   get_gametime(), ship.get_owner()->player_number(), ship.serial(), coords));
838 }
839 
send_player_ship_explore_island(Ship & ship,IslandExploreDirection direction)840 void Game::send_player_ship_explore_island(Ship& ship, IslandExploreDirection direction) {
841 	send_player_command(new CmdShipExploreIsland(
842 	   get_gametime(), ship.get_owner()->player_number(), ship.serial(), direction));
843 }
844 
send_player_sink_ship(Ship & ship)845 void Game::send_player_sink_ship(Ship& ship) {
846 	send_player_command(
847 	   new CmdShipSink(get_gametime(), ship.get_owner()->player_number(), ship.serial()));
848 }
849 
send_player_cancel_expedition_ship(Ship & ship)850 void Game::send_player_cancel_expedition_ship(Ship& ship) {
851 	send_player_command(new CmdShipCancelExpedition(
852 	   get_gametime(), ship.get_owner()->player_number(), ship.serial()));
853 }
854 
send_player_expedition_config(PortDock & pd,WareWorker ww,DescriptionIndex di,bool add)855 void Game::send_player_expedition_config(PortDock& pd,
856                                          WareWorker ww,
857                                          DescriptionIndex di,
858                                          bool add) {
859 	send_player_command(
860 	   new CmdExpeditionConfig(get_gametime(), pd.get_owner()->player_number(), pd, ww, di, add));
861 }
862 
send_player_propose_trade(const Trade & trade)863 void Game::send_player_propose_trade(const Trade& trade) {
864 	auto* object = objects().get_object(trade.initiator);
865 	assert(object != nullptr);
866 	send_player_command(
867 	   new CmdProposeTrade(get_gametime(), object->get_owner()->player_number(), trade));
868 }
869 
send_player_set_stock_policy(Building & imm,WareWorker ww,DescriptionIndex di,StockPolicy sp)870 void Game::send_player_set_stock_policy(Building& imm,
871                                         WareWorker ww,
872                                         DescriptionIndex di,
873                                         StockPolicy sp) {
874 	send_player_command(new CmdSetStockPolicy(
875 	   get_gametime(), imm.get_owner()->player_number(), imm, ww == wwWORKER, di, sp));
876 }
877 
propose_trade(const Trade & trade)878 int Game::propose_trade(const Trade& trade) {
879 	// TODO(sirver,trading): Check if a trade is possible (i.e. if there is a
880 	// path between the two markets);
881 	const int id = next_trade_agreement_id_;
882 	++next_trade_agreement_id_;
883 
884 	auto* initiator = dynamic_cast<Market*>(objects().get_object(trade.initiator));
885 	auto* receiver = dynamic_cast<Market*>(objects().get_object(trade.receiver));
886 	// This is only ever called through a PlayerCommand and that already made
887 	// sure that the objects still exist. Since no time has passed, they should
888 	// not have vanished under us.
889 	assert(initiator != nullptr);
890 	assert(receiver != nullptr);
891 
892 	receiver->removed.connect([this, id](const uint32_t /* serial */) { cancel_trade(id); });
893 	initiator->removed.connect([this, id](const uint32_t /* serial */) { cancel_trade(id); });
894 
895 	receiver->send_message(*this, Message::Type::kTradeOfferReceived, _("Trade Offer"),
896 	                       receiver->descr().icon_filename(), receiver->descr().descname(),
897 	                       _("This market has received a new trade offer."), true);
898 	trade_agreements_[id] = TradeAgreement{TradeAgreement::State::kProposed, trade};
899 
900 	// TODO(sirver,trading): this should be done through another player_command, but I
901 	// want to get to the trade logic implementation now.
902 	accept_trade(id);
903 	return id;
904 }
905 
accept_trade(const int trade_id)906 void Game::accept_trade(const int trade_id) {
907 	auto it = trade_agreements_.find(trade_id);
908 	if (it == trade_agreements_.end()) {
909 		log("Game::accept_trade: Trade %d has vanished. Ignoring.\n", trade_id);
910 		return;
911 	}
912 	const Trade& trade = it->second.trade;
913 	auto* initiator = dynamic_cast<Market*>(objects().get_object(trade.initiator));
914 	auto* receiver = dynamic_cast<Market*>(objects().get_object(trade.receiver));
915 	if (initiator == nullptr || receiver == nullptr) {
916 		cancel_trade(trade_id);
917 		return;
918 	}
919 
920 	initiator->new_trade(trade_id, trade.items_to_send, trade.num_batches, trade.receiver);
921 	receiver->new_trade(trade_id, trade.items_to_receive, trade.num_batches, trade.initiator);
922 
923 	// TODO(sirver,trading): Message the users that the trade has been accepted.
924 }
925 
cancel_trade(int trade_id)926 void Game::cancel_trade(int trade_id) {
927 	// The trade id might be long gone - since we never disconnect from the
928 	// 'removed' signal of the two buildings, we might be invoked long after the
929 	// trade was deleted for other reasons.
930 	const auto it = trade_agreements_.find(trade_id);
931 	if (it == trade_agreements_.end()) {
932 		return;
933 	}
934 	const auto& trade = it->second.trade;
935 
936 	auto* initiator = dynamic_cast<Market*>(objects().get_object(trade.initiator));
937 	if (initiator != nullptr) {
938 		initiator->cancel_trade(trade_id);
939 		// TODO(sirver,trading): Send message to owner that the trade has been canceled.
940 	}
941 
942 	auto* receiver = dynamic_cast<Market*>(objects().get_object(trade.receiver));
943 	if (receiver != nullptr) {
944 		receiver->cancel_trade(trade_id);
945 		// TODO(sirver,trading): Send message to owner that the trade has been canceled.
946 	}
947 	trade_agreements_.erase(trade_id);
948 }
949 
lua()950 LuaGameInterface& Game::lua() {
951 	return static_cast<LuaGameInterface&>(EditorGameBase::lua());
952 }
953 
get_win_condition_displayname() const954 const std::string& Game::get_win_condition_displayname() const {
955 	return win_condition_displayname_;
956 }
set_win_condition_displayname(const std::string & name)957 void Game::set_win_condition_displayname(const std::string& name) {
958 	win_condition_displayname_ = name;
959 }
960 
961 /**
962  * Sample global statistics for the game.
963  */
sample_statistics()964 void Game::sample_statistics() {
965 	// Update general stats
966 	PlayerNumber const nr_plrs = map().get_nrplayers();
967 	std::vector<uint32_t> land_size;
968 	std::vector<uint32_t> nr_buildings;
969 	std::vector<uint32_t> nr_casualties;
970 	std::vector<uint32_t> nr_kills;
971 	std::vector<uint32_t> nr_msites_lost;
972 	std::vector<uint32_t> nr_msites_defeated;
973 	std::vector<uint32_t> nr_civil_blds_lost;
974 	std::vector<uint32_t> nr_civil_blds_defeated;
975 	std::vector<uint32_t> miltary_strength;
976 	std::vector<uint32_t> nr_workers;
977 	std::vector<uint32_t> nr_wares;
978 	std::vector<uint32_t> productivity;
979 	std::vector<uint32_t> nr_production_sites;
980 	std::vector<uint32_t> custom_statistic;
981 	land_size.resize(nr_plrs);
982 	nr_buildings.resize(nr_plrs);
983 	nr_casualties.resize(nr_plrs);
984 	nr_kills.resize(nr_plrs);
985 	nr_msites_lost.resize(nr_plrs);
986 	nr_msites_defeated.resize(nr_plrs);
987 	nr_civil_blds_lost.resize(nr_plrs);
988 	nr_civil_blds_defeated.resize(nr_plrs);
989 	miltary_strength.resize(nr_plrs);
990 	nr_workers.resize(nr_plrs);
991 	nr_wares.resize(nr_plrs);
992 	productivity.resize(nr_plrs);
993 	nr_production_sites.resize(nr_plrs);
994 	custom_statistic.resize(nr_plrs);
995 
996 	//  We walk the map, to gain all needed information.
997 	const Map& themap = map();
998 	Extent const extent = themap.extent();
999 	iterate_Map_FCoords(themap, extent, fc) {
1000 		if (PlayerNumber const owner = fc.field->get_owned_by())
1001 			++land_size[owner - 1];
1002 
1003 		// Get the immovable
1004 		if (upcast(Building, building, fc.field->get_immovable()))
1005 			if (building->get_position() == fc) {  // only count main location
1006 				uint8_t const player_index = building->owner().player_number() - 1;
1007 				++nr_buildings[player_index];
1008 
1009 				//  If it is a productionsite, add its productivity.
1010 				if (upcast(ProductionSite, productionsite, building)) {
1011 					++nr_production_sites[player_index];
1012 					productivity[player_index] += productionsite->get_statistics_percent();
1013 				}
1014 			}
1015 
1016 		// Now, walk the bobs
1017 		for (Bob const* b = fc.field->get_first_bob(); b; b = b->get_next_bob())
1018 			if (upcast(Soldier const, s, b))
1019 				miltary_strength[s->owner().player_number() - 1] +=
1020 				   s->get_level(TrainingAttribute::kTotal) + 1;  //  So that level 0 also counts.
1021 	}
1022 
1023 	//  Number of workers / wares / casualties / kills.
1024 	iterate_players_existing(p, nr_plrs, *this, plr) {
1025 		uint32_t wostock = 0;
1026 		uint32_t wastock = 0;
1027 
1028 		for (const auto& economy : plr->economies()) {
1029 			const TribeDescr& tribe = plr->tribe();
1030 
1031 			switch (economy.second->type()) {
1032 			case wwWARE:
1033 				for (const DescriptionIndex& ware_index : tribe.wares()) {
1034 					wastock += economy.second->stock_ware_or_worker(ware_index);
1035 				}
1036 				break;
1037 			case wwWORKER:
1038 				for (const DescriptionIndex& worker_index : tribe.workers()) {
1039 					if (tribe.get_worker_descr(worker_index)->type() != MapObjectType::CARRIER) {
1040 						wostock += economy.second->stock_ware_or_worker(worker_index);
1041 					}
1042 				}
1043 				break;
1044 			}
1045 		}
1046 		nr_wares[p - 1] = wastock;
1047 		nr_workers[p - 1] = wostock;
1048 		nr_casualties[p - 1] = plr->casualties();
1049 		nr_kills[p - 1] = plr->kills();
1050 		nr_msites_lost[p - 1] = plr->msites_lost();
1051 		nr_msites_defeated[p - 1] = plr->msites_defeated();
1052 		nr_civil_blds_lost[p - 1] = plr->civil_blds_lost();
1053 		nr_civil_blds_defeated[p - 1] = plr->civil_blds_defeated();
1054 	}
1055 
1056 	// Now, divide the statistics
1057 	for (uint32_t i = 0; i < map().get_nrplayers(); ++i) {
1058 		if (productivity[i])
1059 			productivity[i] /= nr_production_sites[i];
1060 	}
1061 
1062 	// If there is a hook function defined to sample special statistics in this
1063 	// game, call the corresponding Lua function
1064 	std::unique_ptr<LuaTable> hook = lua().get_hook("custom_statistic");
1065 	if (hook) {
1066 		hook->do_not_warn_about_unaccessed_keys();
1067 		iterate_players_existing(p, nr_plrs, *this, plr) {
1068 			std::unique_ptr<LuaCoroutine> cr(hook->get_coroutine("calculator"));
1069 			cr->push_arg(plr);
1070 			cr->resume();
1071 			custom_statistic[p - 1] = cr->pop_uint32();
1072 		}
1073 	}
1074 
1075 	// Now, push this on the general statistics
1076 	general_stats_.resize(map().get_nrplayers());
1077 	for (uint32_t i = 0; i < map().get_nrplayers(); ++i) {
1078 		general_stats_[i].land_size.push_back(land_size[i]);
1079 		general_stats_[i].nr_buildings.push_back(nr_buildings[i]);
1080 		general_stats_[i].nr_casualties.push_back(nr_casualties[i]);
1081 		general_stats_[i].nr_kills.push_back(nr_kills[i]);
1082 		general_stats_[i].nr_msites_lost.push_back(nr_msites_lost[i]);
1083 		general_stats_[i].nr_msites_defeated.push_back(nr_msites_defeated[i]);
1084 		general_stats_[i].nr_civil_blds_lost.push_back(nr_civil_blds_lost[i]);
1085 		general_stats_[i].nr_civil_blds_defeated.push_back(nr_civil_blds_defeated[i]);
1086 		general_stats_[i].miltary_strength.push_back(miltary_strength[i]);
1087 		general_stats_[i].nr_workers.push_back(nr_workers[i]);
1088 		general_stats_[i].nr_wares.push_back(nr_wares[i]);
1089 		general_stats_[i].productivity.push_back(productivity[i]);
1090 		general_stats_[i].custom_statistic.push_back(custom_statistic[i]);
1091 	}
1092 
1093 	// Calculate statistics for the players
1094 	const PlayerNumber nr_players = map().get_nrplayers();
1095 	iterate_players_existing(p, nr_players, *this, plr) plr->sample_statistics();
1096 }
1097 
1098 /**
1099  * Read statistics data from a file.
1100  *
1101  * \param fr file to read from
1102  */
read_statistics(FileRead & fr)1103 void Game::read_statistics(FileRead& fr) {
1104 	fr.unsigned_32();  // used to be last stats update time
1105 
1106 	// Read general statistics
1107 	uint32_t entries = fr.unsigned_16();
1108 	const PlayerNumber nr_players = map().get_nrplayers();
1109 	general_stats_.resize(nr_players);
1110 
1111 	iterate_players_existing_novar(p, nr_players, *this) {
1112 		general_stats_[p - 1].land_size.resize(entries);
1113 		general_stats_[p - 1].nr_workers.resize(entries);
1114 		general_stats_[p - 1].nr_buildings.resize(entries);
1115 		general_stats_[p - 1].nr_wares.resize(entries);
1116 		general_stats_[p - 1].productivity.resize(entries);
1117 		general_stats_[p - 1].nr_casualties.resize(entries);
1118 		general_stats_[p - 1].nr_kills.resize(entries);
1119 		general_stats_[p - 1].nr_msites_lost.resize(entries);
1120 		general_stats_[p - 1].nr_msites_defeated.resize(entries);
1121 		general_stats_[p - 1].nr_civil_blds_lost.resize(entries);
1122 		general_stats_[p - 1].nr_civil_blds_defeated.resize(entries);
1123 		general_stats_[p - 1].miltary_strength.resize(entries);
1124 		general_stats_[p - 1].custom_statistic.resize(entries);
1125 	}
1126 
1127 	iterate_players_existing_novar(
1128 	   p, nr_players, *this) for (uint32_t j = 0; j < general_stats_[p - 1].land_size.size(); ++j) {
1129 		general_stats_[p - 1].land_size[j] = fr.unsigned_32();
1130 		general_stats_[p - 1].nr_workers[j] = fr.unsigned_32();
1131 		general_stats_[p - 1].nr_buildings[j] = fr.unsigned_32();
1132 		general_stats_[p - 1].nr_wares[j] = fr.unsigned_32();
1133 		general_stats_[p - 1].productivity[j] = fr.unsigned_32();
1134 		general_stats_[p - 1].nr_casualties[j] = fr.unsigned_32();
1135 		general_stats_[p - 1].nr_kills[j] = fr.unsigned_32();
1136 		general_stats_[p - 1].nr_msites_lost[j] = fr.unsigned_32();
1137 		general_stats_[p - 1].nr_msites_defeated[j] = fr.unsigned_32();
1138 		general_stats_[p - 1].nr_civil_blds_lost[j] = fr.unsigned_32();
1139 		general_stats_[p - 1].nr_civil_blds_defeated[j] = fr.unsigned_32();
1140 		general_stats_[p - 1].miltary_strength[j] = fr.unsigned_32();
1141 		general_stats_[p - 1].custom_statistic[j] = fr.unsigned_32();
1142 	}
1143 }
1144 
1145 /**
1146  * Write general statistics to the given file.
1147  */
write_statistics(FileWrite & fw)1148 void Game::write_statistics(FileWrite& fw) {
1149 	fw.unsigned_32(0);  // Used to be last stats update time. No longer needed
1150 
1151 	// General statistics
1152 	// First, we write the size of the statistics arrays
1153 	uint32_t entries = 0;
1154 
1155 	const PlayerNumber nr_players = map().get_nrplayers();
1156 	iterate_players_existing_novar(p, nr_players, *this) if (!general_stats_.empty()) {
1157 		entries = general_stats_[p - 1].land_size.size();
1158 		break;
1159 	}
1160 
1161 	fw.unsigned_16(entries);
1162 
1163 	iterate_players_existing_novar(p, nr_players, *this) for (uint32_t j = 0; j < entries; ++j) {
1164 		fw.unsigned_32(general_stats_[p - 1].land_size[j]);
1165 		fw.unsigned_32(general_stats_[p - 1].nr_workers[j]);
1166 		fw.unsigned_32(general_stats_[p - 1].nr_buildings[j]);
1167 		fw.unsigned_32(general_stats_[p - 1].nr_wares[j]);
1168 		fw.unsigned_32(general_stats_[p - 1].productivity[j]);
1169 		fw.unsigned_32(general_stats_[p - 1].nr_casualties[j]);
1170 		fw.unsigned_32(general_stats_[p - 1].nr_kills[j]);
1171 		fw.unsigned_32(general_stats_[p - 1].nr_msites_lost[j]);
1172 		fw.unsigned_32(general_stats_[p - 1].nr_msites_defeated[j]);
1173 		fw.unsigned_32(general_stats_[p - 1].nr_civil_blds_lost[j]);
1174 		fw.unsigned_32(general_stats_[p - 1].nr_civil_blds_defeated[j]);
1175 		fw.unsigned_32(general_stats_[p - 1].miltary_strength[j]);
1176 		fw.unsigned_32(general_stats_[p - 1].custom_statistic[j]);
1177 	}
1178 }
1179 }  // namespace Widelands
1180