File: NetTurnManager.h

package info (click to toggle)
0ad 0.0.21-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 54,068 kB
  • sloc: cpp: 230,527; ansic: 23,115; python: 13,559; perl: 2,499; sh: 948; xml: 776; makefile: 696; java: 533; ruby: 229; erlang: 53; sql: 21
file content (356 lines) | stat: -rw-r--r-- 10,802 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/* Copyright (C) 2016 Wildfire Games.
 * This file is part of 0 A.D.
 *
 * 0 A.D. is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * 0 A.D. is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef INCLUDED_NETTURNMANAGER
#define INCLUDED_NETTURNMANAGER

#include "simulation2/helpers/SimulationCommand.h"
#include "lib/os_path.h"
#include "NetMessage.h"

#include <list>
#include <map>
#include <vector>

extern const u32 DEFAULT_TURN_LENGTH_MP;
extern const u32 DEFAULT_TURN_LENGTH_SP;

class CNetServerWorker;
class CNetClient;
class CSimulationMessage;
class CSimulation2;
class IReplayLogger;

/*
 * This file deals with the logic of the network turn system. The basic idea is as in
 * http://www.gamasutra.com/view/feature/3094/1500_archers_on_a_288_network_.php?print=1
 *
 * Each player performs the simulation for turn N.
 * User input is translated into commands scheduled for execution in turn N+2 which are
 * distributed to all other clients.
 * After a while, a player wants to perform the simulation for turn N+1,
 * which first requires that it has all the other clients' commands for turn N+1.
 * In that case, it does the simulation and tells all the other clients (via the server)
 * it has finished sending commands for turn N+2, and it starts sending commands for turn N+3.
 *
 * Commands are redistributed immediately by the server.
 * To ensure a consistent execution of commands, they are each associated with a
 * client session ID (which is globally unique and consistent), which is used to sort them.
 */

/**
 * Common network turn system (used by clients and offline games).
 */
class CNetTurnManager
{
	NONCOPYABLE(CNetTurnManager);
public:
	/**
	 * Construct for a given network session ID.
	 */
	CNetTurnManager(CSimulation2& simulation, u32 defaultTurnLength, int clientId, IReplayLogger& replay);

	virtual ~CNetTurnManager() { }

	void ResetState(u32 newCurrentTurn, u32 newReadyTurn);

	/**
	 * Set the current user's player ID, which will be added into command messages.
	 */
	void SetPlayerID(int playerId);

	/**
	 * Advance the simulation by a certain time. If this brings us past the current
	 * turn length, the next turns are processed and the function returns true.
	 * Otherwise, nothing happens and it returns false.
	 *
	 * @param simFrameLength Length of the previous frame, in simulation seconds
	 * @param maxTurns Maximum number of turns to simulate at once
	 */
	bool Update(float simFrameLength, size_t maxTurns);

	/**
	 * Advance the simulation by as much as possible. Intended for catching up
	 * over a small number of turns when rejoining a multiplayer match.
	 * Returns true if it advanced by at least one turn.
	 */
	bool UpdateFastForward();

	/**
	 * Returns whether Update(simFrameLength, ...) will process at least one new turn.
	 * @param simFrameLength Length of the previous frame, in simulation seconds
	 */
	bool WillUpdate(float simFrameLength);

	/**
	 * Advance the graphics by a certain time.
	 * @param simFrameLength Length of the previous frame, in simulation seconds
	 * @param realFrameLength Length of the previous frame, in real time seconds
	 */
	void Interpolate(float simFrameLength, float realFrameLength);

	/**
	 * Called by networking code when a simulation message is received.
	 */
	virtual void OnSimulationMessage(CSimulationMessage* msg) = 0;

	/**
	 * Called when there has been an out-of-sync error.
	 */
	virtual void OnSyncError(u32 turn, const CStr& expectedHash, std::vector<CSyncErrorMessage::S_m_PlayerNames>& playerNames);

	/**
	 * Shows a message box when an out of sync error has been detected in the session or visual replay.
	 */
	virtual void DisplayOOSError(u32 turn, const CStr& hash, const CStr& expectedHash, bool isReplay, std::vector<CSyncErrorMessage::S_m_PlayerNames>* playerNames, OsPath* path);

	/**
	 * Called by simulation code, to add a new command to be distributed to all clients and executed soon.
	 */
	virtual void PostCommand(JS::HandleValue data) = 0;

	/**
	 * Called when all commands for a given turn have been received.
	 * This allows Update to progress to that turn.
	 */
	void FinishedAllCommands(u32 turn, u32 turnLength);

	/**
	 * Enables the recording of state snapshots every @p numTurns,
	 * which can be jumped back to via RewindTimeWarp().
	 * If @p numTurns is 0 then recording is disabled.
	 */
	void EnableTimeWarpRecording(size_t numTurns);

	/**
	 * Jumps back to the latest recorded state snapshot (if any).
	 */
	void RewindTimeWarp();

	void QuickSave();
	void QuickLoad();

	u32 GetCurrentTurn() { return m_CurrentTurn; }

protected:
	/**
	 * Store a command to be executed at a given turn.
	 */
	void AddCommand(int client, int player, JS::HandleValue data, u32 turn);

	/**
	 * Called when this client has finished sending all its commands scheduled for the given turn.
	 */
	virtual void NotifyFinishedOwnCommands(u32 turn) = 0;

	/**
	 * Called when this client has finished a simulation update.
	 */
	virtual void NotifyFinishedUpdate(u32 turn) = 0;

	/**
	 * Returns whether we should compute a complete state hash for the given turn,
	 * instead of a quick less-complete hash.
	 */
	bool TurnNeedsFullHash(u32 turn);

	CSimulation2& m_Simulation2;

	/// The turn that we have most recently executed
	u32 m_CurrentTurn;

	/// The latest turn for which we have received all commands from all clients
	u32 m_ReadyTurn;

	// Current turn length
	u32 m_TurnLength;

	/// Commands queued at each turn (index 0 is for m_CurrentTurn+1)
	std::deque<std::map<u32, std::vector<SimulationCommand>>> m_QueuedCommands;

	int m_PlayerId;
	uint m_ClientId;

	/// Simulation time remaining until we ought to execute the next turn (as a negative value to
	/// add elapsed time increments to until we reach 0).
	float m_DeltaSimTime;

	bool m_HasSyncError;

	IReplayLogger& m_Replay;

	// The number of the last turn that is allowed to be executed (used for replays)
	u32 m_FinalTurn;

private:
	size_t m_TimeWarpNumTurns; // 0 if disabled
	std::list<std::string> m_TimeWarpStates;
	std::string m_QuickSaveState; // TODO: should implement a proper disk-based quicksave system
	std::string m_QuickSaveMetadata;
};


/**
 * Implementation of CNetTurnManager for network clients.
 */
class CNetClientTurnManager : public CNetTurnManager
{
public:
	CNetClientTurnManager(CSimulation2& simulation, CNetClient& client, int clientId, IReplayLogger& replay);

	virtual void OnSimulationMessage(CSimulationMessage* msg);

	virtual void PostCommand(JS::HandleValue data);

	/**
	 * Notifiy the server that all commands are sent to prepare the connection for termination.
	 */
	void OnDestroyConnection();

protected:
	virtual void NotifyFinishedOwnCommands(u32 turn);

	virtual void NotifyFinishedUpdate(u32 turn);

	CNetClient& m_NetClient;
};

/**
 * Implementation of CNetTurnManager for offline games.
 */
class CNetLocalTurnManager : public CNetTurnManager
{
public:
	CNetLocalTurnManager(CSimulation2& simulation, IReplayLogger& replay);

	virtual void OnSimulationMessage(CSimulationMessage* msg);

	virtual void PostCommand(JS::HandleValue data);

protected:
	virtual void NotifyFinishedOwnCommands(u32 turn);

	virtual void NotifyFinishedUpdate(u32 turn);
};



/**
 * Implementation of CNetTurnManager for replay games.
 */
class CNetReplayTurnManager : public CNetLocalTurnManager
{
public:
	CNetReplayTurnManager(CSimulation2& simulation, IReplayLogger& replay);

	void StoreReplayCommand(u32 turn, int player, const std::string& command);

	void StoreReplayTurnLength(u32 turn, u32 turnLength);

	void StoreReplayHash(u32 turn, const std::string& hash, bool quick);

	void StoreFinalReplayTurn(u32 turn);


protected:
	virtual void NotifyFinishedUpdate(u32 turn);

	void DoTurn(u32 turn);

	// Contains the commands of every player on each turn
	std::map<u32, std::vector<std::pair<player_id_t, std::string>>> m_ReplayCommands;

	// Contains the length of every turn
	std::map<u32, u32> m_ReplayTurnLengths;

	// Contains all replay hash values and weather or not the quick hash method was used
	std::map<u32, std::pair<std::string, bool>> m_ReplayHash;
};
/**
 * The server-side counterpart to CNetClientTurnManager.
 * Records the turn state of each client, and sends turn advancement messages
 * when all clients are ready.
 *
 * Thread-safety:
 * - This is constructed and used by CNetServerWorker in the network server thread.
 */
class CNetServerTurnManager
{
	NONCOPYABLE(CNetServerTurnManager);
public:
	CNetServerTurnManager(CNetServerWorker& server);

	void NotifyFinishedClientCommands(int client, u32 turn);

	void NotifyFinishedClientUpdate(int client, const CStrW& playername, u32 turn, const CStr& hash);

	/**
	 * Inform the turn manager of a new client who will be sending commands.
	 */
	void InitialiseClient(int client, u32 turn);

	/**
	 * Inform the turn manager that a previously-initialised client has left the game
	 * and will no longer be sending commands.
	 */
	void UninitialiseClient(int client);

	void SetTurnLength(u32 msecs);

	/**
	 * Returns the latest turn for which all clients are ready;
	 * they will have already been told to execute this turn.
	 */
	u32 GetReadyTurn() { return m_ReadyTurn; }

	/**
	 * Returns the turn length that was used for the given turn.
	 * Requires turn <= GetReadyTurn().
	 */
	u32 GetSavedTurnLength(u32 turn);

protected:
	void CheckClientsReady();

	/// The latest turn for which we have received all commands from all clients
	u32 m_ReadyTurn;

	// Client ID -> ready turn number (the latest turn for which all commands have been received from that client)
	std::map<int, u32> m_ClientsReady;

	// Client ID -> last known simulated turn number (for which we have the state hash)
	// (the client has reached the start of this turn, not done the update for it yet)
	std::map<int, u32> m_ClientsSimulated;

	// Map of turn -> {Client ID -> state hash}; old indexes <= min(m_ClientsSimulated) are deleted
	std::map<u32, std::map<int, std::string>> m_ClientStateHashes;

	// Map of client ID -> playername
	std::map<u32, CStrW> m_ClientPlayernames;

	// Current turn length
	u32 m_TurnLength;

	// Turn lengths for all previously executed turns
	std::vector<u32> m_SavedTurnLengths;

	CNetServerWorker& m_NetServer;

	bool m_HasSyncError;
};

#endif // INCLUDED_NETTURNMANAGER