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 #ifndef WL_LOGIC_GAME_H
21 #define WL_LOGIC_GAME_H
22 
23 #include <memory>
24 
25 #include "base/md5.h"
26 #include "base/random.h"
27 #include "io/streamwrite.h"
28 #include "logic/cmd_queue.h"
29 #include "logic/editor_game_base.h"
30 #include "logic/save_handler.h"
31 #include "logic/trade_agreement.h"
32 #include "scripting/logic.h"
33 
34 class InteractivePlayer;
35 struct GameSettings;
36 class GameController;
37 
38 namespace Widelands {
39 
40 /// How often are statistics to be sampled.
41 constexpr uint32_t kStatisticsSampleTime = 30000;
42 // See forester_cache_
43 constexpr int16_t kInvalidForesterEntry = -1;
44 
45 constexpr uint32_t kScenarioDifficultyNotSet = 0;
46 
47 struct Flag;
48 struct Path;
49 struct PlayerImmovable;
50 enum class IslandExploreDirection;
51 class PortDock;
52 enum class ScoutingDirection;
53 enum class SoldierPreference : uint8_t;
54 struct Ship;
55 class TrainingSite;
56 enum class StockPolicy;
57 
58 enum {
59 	gs_notrunning = 0,  // game is being prepared
60 	gs_running,         // game was fully prepared at some point and is now in-game
61 	gs_ending
62 };
63 
64 // The entry types that are written to the syncstream
65 // The IDs are a number in the higher 4 bits and the length in bytes in the lower 4 bits
66 // Keep this synchronized with utils/syncstream/syncexcerpt-to-text.py
67 enum SyncEntry : uint8_t {
68 	// Used in:
69 	// game.cc Game::report_desync()
70 	// Parameters:
71 	// s32 id of desynced user, -1 when written on client
72 	kDesync = 0x14,
73 	// map_object.cc CmdDestroyMapObject::execute()
74 	// u32 object serial
75 	kDestroyObject = 0x24,
76 	// economy.cc Economy::process_requests()
77 	// u8 request type
78 	// u8 request index
79 	// u32 target serial
80 	kProcessRequests = 0x36,
81 	// economy.cc Economy::handle_active_supplies()
82 	// u32 assignments size
83 	kHandleActiveSupplies = 0x44,
84 	// request.cc Request::start_transfer()
85 	// u32 target serial
86 	// u32 source(?) serial
87 	kStartTransfer = 0x58,
88 	// cmd_queue.cc CmdQueue::run_queue()
89 	// u32 duetime
90 	// u32 command id
91 	kRunQueue = 0x68,
92 	// game.h Game::logic_rand_seed()
93 	// u32 random seed
94 	kRandomSeed = 0x74,
95 	// game.cc Game::logic_rand()
96 	// u32 random value
97 	kRandom = 0x84,
98 	// map_object.cc CmdAct::execute()
99 	// u32 object serial
100 	// u8 object type (see map_object.h MapObjectType)
101 	kCmdAct = 0x95,
102 	// battle.cc Battle::Battle()
103 	// u32 first soldier serial
104 	// u32 second soldier serial
105 	kBattle = 0xA8,
106 	// bob.cc Bob::set_position()
107 	// u32 bob serial
108 	// s16 position x
109 	// s16 position y
110 	kBobSetPosition = 0xB8
111 };
112 
113 class PlayerCommand;
114 class ReplayWriter;
115 
116 /** class Game
117  *
118  * This class manages the entire lifetime of a game session, from creating the
119  * game and setting options, selecting maps to the actual playing phase and the
120  * final statistics screen(s).
121  */
122 
123 class Game : public EditorGameBase {
124 public:
125 	struct GeneralStats {
126 		std::vector<uint32_t> land_size;
127 		std::vector<uint32_t> nr_workers;
128 		std::vector<uint32_t> nr_buildings;
129 		std::vector<uint32_t> nr_wares;
130 		std::vector<uint32_t> productivity;
131 		std::vector<uint32_t> nr_casualties;
132 		std::vector<uint32_t> nr_kills;
133 		std::vector<uint32_t> nr_msites_lost;
134 		std::vector<uint32_t> nr_msites_defeated;
135 		std::vector<uint32_t> nr_civil_blds_lost;
136 		std::vector<uint32_t> nr_civil_blds_defeated;
137 		std::vector<uint32_t> miltary_strength;
138 
139 		std::vector<uint32_t> custom_statistic;
140 	};
141 	using GeneralStatsVector = std::vector<GeneralStats>;
142 
143 	friend class CmdQueue;  // this class handles the commands
144 	friend struct GameClassPacket;
145 	friend struct GamePlayerInfoPacket;
146 	friend struct GameLoader;
147 
148 	// TODO(kxq): The lifetime of game-instance is okay for this, but is this the right spot?
149 	// TODO(kxq): I should find the place where LUA changes map, and clear this whenever that
150 	// happens.
151 	// TODO(kxq): When done, the x_check in worker.cc could be removed (or made more rare, and put
152 	// under an assert as then mismatch would indicate the presence of a bug).
153 	// TODO(k.halfmann): this shoud perhpas better be a map, it will be quite sparse?
154 
155 	/** Qualitity of terrain for tree planting normalized to int16.
156 	 *
157 	 *  Indexed by MapIndex. -1  is an ivalid entry. Shared between all tribes (on the same server)
158 	 *  will be cleared when diffrences are detected. Presently, that can only happen if terrain
159 	 *  is changed (lua scripting). The map is sparse, lookups are fast.
160 	 */
161 	std::vector<int16_t> forester_cache_;
162 
163 	Game();
164 	~Game() override;
165 
166 	// life cycle
167 	void set_game_controller(GameController*);
168 	GameController* game_controller();
169 	void set_write_replay(bool wr);
170 	void set_write_syncstream(bool wr);
171 	void save_syncstream(bool save);
172 	void init_newgame(const GameSettings&);
173 	void init_savegame(const GameSettings&);
174 
175 	enum StartGameType { NewSPScenario, NewNonScenario, Loaded, NewMPScenario };
176 
177 	bool run(StartGameType,
178 	         const std::string& script_to_run,
179 	         bool replay,
180 	         const std::string& prefix_for_replays);
181 
182 	// Returns the upcasted lua interface.
183 	LuaGameInterface& lua() override;
184 
185 	// Run a single player scenario directly via --scenario on the cmdline. Will
186 	// run the 'script_to_run' after any init scripts of the map.
187 	// Returns the result of run().
188 	bool run_splayer_scenario_direct(const std::string& mapname, const std::string& script_to_run);
189 
190 	// Run a single player loaded game directly via --loadgame on the cmdline. Will
191 	// run the 'script_to_run' directly after the game was loaded.
192 	// Returns the result of run().
193 	bool run_load_game(const std::string& filename, const std::string& script_to_run);
194 
195 	void postload() override;
196 
197 	void think() override;
198 
get_replaywriter()199 	ReplayWriter* get_replaywriter() {
200 		return replaywriter_.get();
201 	}
202 
203 	/**
204 	 * \return \c true if the game is completely loaded and running (or paused)
205 	 * or \c false otherwise.
206 	 */
is_loaded()207 	bool is_loaded() {
208 		return state_ == gs_running;
209 	}
210 
211 	void cleanup_for_load() override;
212 
213 	// in-game logic
cmdqueue()214 	const CmdQueue& cmdqueue() const {
215 		return cmdqueue_;
216 	}
cmdqueue()217 	CmdQueue& cmdqueue() {
218 		return cmdqueue_;
219 	}
rng()220 	const RNG& rng() const {
221 		return rng_;
222 	}
rng()223 	RNG& rng() {
224 		return rng_;
225 	}
226 
227 	uint32_t logic_rand();
228 
229 	/// Generate a random location within radius from location.
230 	Coords random_location(Coords location, uint8_t radius);
231 
logic_rand_seed(uint32_t const seed)232 	void logic_rand_seed(uint32_t const seed) {
233 		rng().seed(seed);
234 		syncstream().unsigned_8(SyncEntry::kRandomSeed);
235 		syncstream().unsigned_32(seed);
236 	}
237 
238 	StreamWrite& syncstream();
239 	void report_sync_request();
240 	void report_desync(int32_t playernumber);
241 	Md5Checksum get_sync_hash() const;
242 
243 	void enqueue_command(Command* const);
244 
245 	void send_player_command(Widelands::PlayerCommand*);
246 
247 	void send_player_bulldoze(PlayerImmovable&, bool recurse = false);
248 	void send_player_dismantle(PlayerImmovable&, bool keep_wares);
249 	void send_player_build(int32_t, const Coords&, DescriptionIndex);
250 	void send_player_build_flag(int32_t, const Coords&);
251 	void send_player_build_road(int32_t, Path&);
252 	void send_player_build_waterway(int32_t, Path&);
253 	void send_player_flagaction(Flag&);
254 	void send_player_start_stop_building(Building&);
255 	void send_player_militarysite_set_soldier_preference(Building&, SoldierPreference preference);
256 	void send_player_start_or_cancel_expedition(Building&);
257 	void send_player_expedition_config(PortDock&, WareWorker, DescriptionIndex, bool);
258 
259 	void send_player_enhance_building(Building&, DescriptionIndex, bool keep_wares);
260 	void send_player_evict_worker(Worker&);
261 	void send_player_set_stock_policy(Building&, WareWorker, DescriptionIndex, StockPolicy);
262 	void send_player_set_ware_priority(
263 	   PlayerImmovable&, int32_t type, DescriptionIndex index, int32_t prio, bool is_cs = false);
264 	void send_player_set_input_max_fill(
265 	   PlayerImmovable&, DescriptionIndex index, WareWorker type, uint32_t, bool is_cs = false);
266 	void send_player_change_training_options(TrainingSite&, TrainingAttribute, int32_t);
267 	void send_player_drop_soldier(Building&, int32_t);
268 	void send_player_change_soldier_capacity(Building&, int32_t);
269 	void send_player_enemyflagaction(const Flag&, PlayerNumber, const std::vector<Serial>&);
270 
271 	void send_player_ship_scouting_direction(Ship&, WalkingDir);
272 	void send_player_ship_construct_port(Ship&, Coords);
273 	void send_player_ship_explore_island(Ship&, IslandExploreDirection);
274 	void send_player_sink_ship(Ship&);
275 	void send_player_cancel_expedition_ship(Ship&);
276 	void send_player_propose_trade(const Trade& trade);
277 
278 	InteractivePlayer* get_ipl();
279 
save_handler()280 	SaveHandler& save_handler() {
281 		return savehandler_;
282 	}
283 
get_scenario_difficulty()284 	uint32_t get_scenario_difficulty() const {
285 		return scenario_difficulty_;
286 	}
set_scenario_difficulty(uint32_t d)287 	void set_scenario_difficulty(uint32_t d) {
288 		assert(scenario_difficulty_ == kScenarioDifficultyNotSet);
289 		assert(d != kScenarioDifficultyNotSet);
290 		scenario_difficulty_ = d;
291 	}
292 
293 	// Statistics
get_general_statistics()294 	const GeneralStatsVector& get_general_statistics() const {
295 		return general_stats_;
296 	}
297 
298 	void read_statistics(FileRead&);
299 	void write_statistics(FileWrite&);
300 
301 	void sample_statistics();
302 
303 	const std::string& get_win_condition_displayname() const;
304 	void set_win_condition_displayname(const std::string& name);
305 
is_replay()306 	bool is_replay() const {
307 		return replay_;
308 	}
309 
is_ai_training_mode()310 	bool is_ai_training_mode() const {
311 		return ai_training_mode_;
312 	}
313 
is_auto_speed()314 	bool is_auto_speed() const {
315 		return auto_speed_;
316 	}
317 
318 	void set_ai_training_mode(bool);
319 
320 	void set_auto_speed(bool);
321 
322 	// TODO(sirver,trading): document these functions once the interface settles.
323 	int propose_trade(const Trade& trade);
324 	void accept_trade(int trade_id);
325 	void cancel_trade(int trade_id);
326 
327 private:
328 	void sync_reset();
329 
330 	MD5Checksum<StreamWrite> synchash_;
331 
332 	struct SyncWrapper : public StreamWrite {
SyncWrapperSyncWrapper333 		SyncWrapper(Game& game, StreamWrite& target)
334 		   : game_(game),
335 		     target_(target),
336 		     counter_(0),
337 		     next_diskspacecheck_(0),
338 		     syncstreamsave_(false),
339 		     current_excerpt_id_(0) {
340 		}
341 
342 		~SyncWrapper() override;
343 
344 		/// Start dumping the entire syncstream into a file.
345 		///
346 		/// Note that this file is deleted at the end of the game, unless
347 		/// \ref syncstreamsave_ has been set.
348 		void start_dump(const std::string& fname);
349 
350 		void data(void const* data, size_t size) override;
351 
flushSyncWrapper352 		void flush() override {
353 			target_.flush();
354 		}
355 
356 	public:
357 		Game& game_;
358 		StreamWrite& target_;
359 		uint32_t counter_;
360 		uint32_t next_diskspacecheck_;
361 		std::unique_ptr<StreamWrite> dump_;
362 		std::string dumpfname_;
363 		bool syncstreamsave_;
364 		// Use a cyclic buffer for storing parts of the syncstream
365 		// Currently used buffer
366 		size_t current_excerpt_id_;
367 		// (Arbitrary) count of buffers
368 		// Syncreports seem to be requested from the network clients every game second so this
369 		// buffer should be big enough to store the last 32 seconds of the game actions leading
370 		// up to the desync
371 		static constexpr size_t kExcerptSize = 32;
372 		// Array of byte buffers
373 		// std::string is used as a binary buffer here
374 		std::string excerpts_buffer_[kExcerptSize];
375 	} syncwrapper_;
376 
377 	GameController* ctrl_;
378 
379 	/// Whether a replay writer should be created.
380 	/// Defaults to \c true, and should only be set to \c false for playing back
381 	/// replays.
382 	bool writereplay_;
383 
384 	/// Whether a syncsteam file should be created.
385 	/// Defaults to \c false, and can be set to true for network games. The file
386 	/// is written only if \ref writereplay_ is true too.
387 	bool writesyncstream_;
388 
389 	bool ai_training_mode_;
390 	bool auto_speed_;
391 
392 	int32_t state_;
393 
394 	RNG rng_;
395 
396 	CmdQueue cmdqueue_;
397 
398 	SaveHandler savehandler_;
399 
400 	std::unique_ptr<ReplayWriter> replaywriter_;
401 
402 	uint32_t scenario_difficulty_;
403 
404 	GeneralStatsVector general_stats_;
405 	int next_trade_agreement_id_ = 1;
406 	// Maps from trade agreement id to the agreement.
407 	std::map<int, TradeAgreement> trade_agreements_;
408 
409 	/// For save games and statistics generation
410 	std::string win_condition_displayname_;
411 	bool replay_;
412 };
413 
random_location(Coords location,uint8_t radius)414 inline Coords Game::random_location(Coords location, uint8_t radius) {
415 	const uint16_t s = radius * 2 + 1;
416 	location.x += logic_rand() % s - radius;
417 	location.y += logic_rand() % s - radius;
418 	return location;
419 }
420 }  // namespace Widelands
421 
422 #endif  // end of include guard: WL_LOGIC_GAME_H
423