1 /* Copyright (C) 2017 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. 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 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef INCLUDED_TURNMANAGER
19 #define INCLUDED_TURNMANAGER
20 
21 #include "simulation2/helpers/SimulationCommand.h"
22 
23 #include <list>
24 #include <map>
25 #include <vector>
26 
27 extern const u32 DEFAULT_TURN_LENGTH_SP;
28 extern const u32 DEFAULT_TURN_LENGTH_MP;
29 
30 extern const int COMMAND_DELAY;
31 
32 class CSimulationMessage;
33 class CSimulation2;
34 class IReplayLogger;
35 
36 /**
37  * This file defines the base class of the turn managers for clients, local games and replays.
38  * The basic idea of our turn managing system across a network is as in this article:
39  * http://www.gamasutra.com/view/feature/3094/1500_archers_on_a_288_network_.php?print=1
40  *
41  * Each player performs the simulation for turn N.
42  * User input is translated into commands scheduled for execution in turn N+2 which are
43  * distributed to all other clients.
44  * After a while, a client wants to perform the simulation for turn N+1,
45  * which first requires that it has all the other clients' commands for turn N+1.
46  * In that case, it does the simulation and tells all the other clients (via the server)
47  * it has finished sending commands for turn N+2, and it starts sending commands for turn N+3.
48  *
49  * Commands are redistributed immediately by the server.
50  * To ensure a consistent execution of commands, they are each associated with a
51  * client session ID (which is globally unique and consistent), which is used to sort them.
52  */
53 
54 /**
55  * Common turn system (used by clients and offline games).
56  */
57 class CTurnManager
58 {
59 	NONCOPYABLE(CTurnManager);
60 public:
61 	/**
62 	 * Construct for a given network session ID.
63 	 */
64 	CTurnManager(CSimulation2& simulation, u32 defaultTurnLength, int clientId, IReplayLogger& replay);
65 
~CTurnManager()66 	virtual ~CTurnManager() { }
67 
68 	void ResetState(u32 newCurrentTurn, u32 newReadyTurn);
69 
70 	/**
71 	 * Set the current user's player ID, which will be added into command messages.
72 	 */
73 	void SetPlayerID(int playerId);
74 
75 	/**
76 	 * Advance the simulation by a certain time. If this brings us past the current
77 	 * turn length, the next turns are processed and the function returns true.
78 	 * Otherwise, nothing happens and it returns false.
79 	 *
80 	 * @param simFrameLength Length of the previous frame, in simulation seconds
81 	 * @param maxTurns Maximum number of turns to simulate at once
82 	 */
83 	bool Update(float simFrameLength, size_t maxTurns);
84 
85 	/**
86 	 * Advance the simulation by as much as possible. Intended for catching up
87 	 * over a small number of turns when rejoining a multiplayer match.
88 	 * Returns true if it advanced by at least one turn.
89 	 */
90 	bool UpdateFastForward();
91 
92 	/**
93 	 * Returns whether Update(simFrameLength, ...) will process at least one new turn.
94 	 * @param simFrameLength Length of the previous frame, in simulation seconds
95 	 */
96 	bool WillUpdate(float simFrameLength) const;
97 
98 	/**
99 	 * Advance the graphics by a certain time.
100 	 * @param simFrameLength Length of the previous frame, in simulation seconds
101 	 * @param realFrameLength Length of the previous frame, in real time seconds
102 	 */
103 	void Interpolate(float simFrameLength, float realFrameLength);
104 
105 	/**
106 	 * Called by networking code when a simulation message is received.
107 	 */
108 	virtual void OnSimulationMessage(CSimulationMessage* msg) = 0;
109 
110 	/**
111 	 * Called by simulation code, to add a new command to be distributed to all clients and executed soon.
112 	 */
113 	virtual void PostCommand(JS::HandleValue data) = 0;
114 
115 	/**
116 	 * Called when all commands for a given turn have been received.
117 	 * This allows Update to progress to that turn.
118 	 */
119 	void FinishedAllCommands(u32 turn, u32 turnLength);
120 
121 	/**
122 	 * Enables the recording of state snapshots every @p numTurns,
123 	 * which can be jumped back to via RewindTimeWarp().
124 	 * If @p numTurns is 0 then recording is disabled.
125 	 */
126 	void EnableTimeWarpRecording(size_t numTurns);
127 
128 	/**
129 	 * Jumps back to the latest recorded state snapshot (if any).
130 	 */
131 	void RewindTimeWarp();
132 
133 	void QuickSave();
134 	void QuickLoad();
135 
GetCurrentTurn()136 	u32 GetCurrentTurn() { return m_CurrentTurn; }
137 
138 protected:
139 	/**
140 	 * Store a command to be executed at a given turn.
141 	 */
142 	void AddCommand(int client, int player, JS::HandleValue data, u32 turn);
143 
144 	/**
145 	 * Called when this client has finished sending all its commands scheduled for the given turn.
146 	 */
147 	virtual void NotifyFinishedOwnCommands(u32 turn) = 0;
148 
149 	/**
150 	 * Called when this client has finished a simulation update.
151 	 */
152 	virtual void NotifyFinishedUpdate(u32 turn) = 0;
153 
154 	/**
155 	 * Returns whether we should compute a complete state hash for the given turn,
156 	 * instead of a quick less-complete hash.
157 	 */
158 	bool TurnNeedsFullHash(u32 turn) const;
159 
160 	CSimulation2& m_Simulation2;
161 
162 	/// The turn that we have most recently executed
163 	u32 m_CurrentTurn;
164 
165 	/// The latest turn for which we have received all commands from all clients
166 	u32 m_ReadyTurn;
167 
168 	// Current turn length
169 	u32 m_TurnLength;
170 
171 	/// Commands queued at each turn (index 0 is for m_CurrentTurn+1)
172 	std::deque<std::map<u32, std::vector<SimulationCommand>>> m_QueuedCommands;
173 
174 	int m_PlayerId;
175 	uint m_ClientId;
176 
177 	/// Simulation time remaining until we ought to execute the next turn (as a negative value to
178 	/// add elapsed time increments to until we reach 0).
179 	float m_DeltaSimTime;
180 
181 	bool m_HasSyncError;
182 
183 	IReplayLogger& m_Replay;
184 
185 	// The number of the last turn that is allowed to be executed (used for replays)
186 	u32 m_FinalTurn;
187 
188 private:
189 	size_t m_TimeWarpNumTurns; // 0 if disabled
190 	std::list<std::string> m_TimeWarpStates;
191 	std::string m_QuickSaveState; // TODO: should implement a proper disk-based quicksave system
192 	std::string m_QuickSaveMetadata;
193 };
194 
195 #endif // INCLUDED_TURNMANAGER
196