Added different textures for each ptolemaic camel rank
[0ad.git] / source / network / NetTurnManager.cpp
blob0eb332d99027bf30e25deff15565666043731d29
1 /* Copyright (C) 2012 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #include "precompiled.h"
20 #include "NetTurnManager.h"
22 #include "network/NetServer.h"
23 #include "network/NetClient.h"
24 #include "network/NetMessage.h"
26 #include "gui/GUIManager.h"
27 #include "maths/MathUtil.h"
28 #include "ps/CLogger.h"
29 #include "ps/Profile.h"
30 #include "ps/Pyrogenesis.h"
31 #include "ps/Replay.h"
32 #include "ps/SavedGame.h"
33 #include "scriptinterface/ScriptInterface.h"
34 #include "simulation2/Simulation2.h"
36 #include <sstream>
37 #include <fstream>
38 #include <iomanip>
40 static const int DEFAULT_TURN_LENGTH_MP = 500;
41 static const int DEFAULT_TURN_LENGTH_SP = 200;
43 static const int COMMAND_DELAY = 2;
45 #if 0
46 #define NETTURN_LOG(args) debug_printf args
47 #else
48 #define NETTURN_LOG(args)
49 #endif
51 static std::wstring Hexify(const std::string& s)
53 std::wstringstream str;
54 str << std::hex;
55 for (size_t i = 0; i < s.size(); ++i)
56 str << std::setfill(L'0') << std::setw(2) << (int)(unsigned char)s[i];
57 return str.str();
60 CNetTurnManager::CNetTurnManager(CSimulation2& simulation, u32 defaultTurnLength, int clientId, IReplayLogger& replay) :
61 m_Simulation2(simulation), m_CurrentTurn(0), m_ReadyTurn(1), m_TurnLength(defaultTurnLength), m_DeltaSimTime(0),
62 m_PlayerId(-1), m_ClientId(clientId), m_HasSyncError(false), m_Replay(replay),
63 m_TimeWarpNumTurns(0)
65 // When we are on turn n, we schedule new commands for n+2.
66 // We know that all other clients have finished scheduling commands for n (else we couldn't have got here).
67 // We know we have not yet finished scheduling commands for n+2.
68 // Hence other clients can be on turn n-1, n, n+1, and no other.
69 // So they can be sending us commands scheduled for n+1, n+2, n+3.
70 // So we need a 3-element buffer:
71 m_QueuedCommands.resize(COMMAND_DELAY + 1);
74 void CNetTurnManager::ResetState(u32 newCurrentTurn, u32 newReadyTurn)
76 m_CurrentTurn = newCurrentTurn;
77 m_ReadyTurn = newReadyTurn;
78 m_DeltaSimTime = 0;
79 size_t queuedCommandsSize = m_QueuedCommands.size();
80 m_QueuedCommands.clear();
81 m_QueuedCommands.resize(queuedCommandsSize);
84 void CNetTurnManager::SetPlayerID(int playerId)
86 m_PlayerId = playerId;
89 bool CNetTurnManager::WillUpdate(float simFrameLength)
91 // Keep this in sync with the return value of Update()
93 if (m_DeltaSimTime + simFrameLength < 0)
94 return false;
96 if (m_ReadyTurn <= m_CurrentTurn)
97 return false;
99 return true;
102 bool CNetTurnManager::Update(float simFrameLength, size_t maxTurns)
104 m_DeltaSimTime += simFrameLength;
106 // If we haven't reached the next turn yet, do nothing
107 if (m_DeltaSimTime < 0)
108 return false;
110 NETTURN_LOG((L"Update current=%d ready=%d\n", m_CurrentTurn, m_ReadyTurn));
112 // Check that the next turn is ready for execution
113 if (m_ReadyTurn <= m_CurrentTurn)
115 // Oops, we wanted to start the next turn but it's not ready yet -
116 // there must be too much network lag.
117 // TODO: complain to the user.
118 // TODO: send feedback to the server to increase the turn length.
120 // Reset the next-turn timer to 0 so we try again next update but
121 // so we don't rush to catch up in subsequent turns.
122 // TODO: we should do clever rate adjustment instead of just pausing like this.
123 m_DeltaSimTime = 0;
125 return false;
128 maxTurns = std::max((size_t)1, maxTurns); // always do at least one turn
130 for (size_t i = 0; i < maxTurns; ++i)
132 // Check that we've reached the i'th next turn
133 if (m_DeltaSimTime < 0)
134 break;
136 // Check that the i'th next turn is still ready
137 if (m_ReadyTurn <= m_CurrentTurn)
138 break;
140 NotifyFinishedOwnCommands(m_CurrentTurn + COMMAND_DELAY);
142 m_CurrentTurn += 1; // increase the turn number now, so Update can send new commands for a subsequent turn
144 // Clean up any destroyed entities since the last turn (e.g. placement previews
145 // or rally point flags generated by the GUI). (Must do this before the time warp
146 // serialization.)
147 m_Simulation2.FlushDestroyedEntities();
149 // Save the current state for rewinding, if enabled
150 if (m_TimeWarpNumTurns && (m_CurrentTurn % m_TimeWarpNumTurns) == 0)
152 PROFILE3("time warp serialization");
153 std::stringstream stream;
154 m_Simulation2.SerializeState(stream);
155 m_TimeWarpStates.push_back(stream.str());
158 // Put all the client commands into a single list, in a globally consistent order
159 std::vector<SimulationCommand> commands;
160 for (std::map<u32, std::vector<SimulationCommand> >::iterator it = m_QueuedCommands[0].begin(); it != m_QueuedCommands[0].end(); ++it)
162 commands.insert(commands.end(), it->second.begin(), it->second.end());
164 m_QueuedCommands.pop_front();
165 m_QueuedCommands.resize(m_QueuedCommands.size() + 1);
167 m_Replay.Turn(m_CurrentTurn-1, m_TurnLength, commands);
169 NETTURN_LOG((L"Running %d cmds\n", commands.size()));
171 m_Simulation2.Update(m_TurnLength, commands);
173 NotifyFinishedUpdate(m_CurrentTurn);
175 // Set the time for the next turn update
176 m_DeltaSimTime -= m_TurnLength / 1000.f;
179 return true;
182 bool CNetTurnManager::UpdateFastForward()
184 m_DeltaSimTime = 0;
186 NETTURN_LOG((L"UpdateFastForward current=%d ready=%d\n", m_CurrentTurn, m_ReadyTurn));
188 // Check that the next turn is ready for execution
189 if (m_ReadyTurn <= m_CurrentTurn)
190 return false;
192 while (m_ReadyTurn > m_CurrentTurn)
194 // TODO: It would be nice to remove some of the duplication with Update()
195 // (This is similar but doesn't call any Notify functions or update DeltaTime,
196 // it just updates the simulation state)
198 m_CurrentTurn += 1;
200 m_Simulation2.FlushDestroyedEntities();
202 // Put all the client commands into a single list, in a globally consistent order
203 std::vector<SimulationCommand> commands;
204 for (std::map<u32, std::vector<SimulationCommand> >::iterator it = m_QueuedCommands[0].begin(); it != m_QueuedCommands[0].end(); ++it)
206 commands.insert(commands.end(), it->second.begin(), it->second.end());
208 m_QueuedCommands.pop_front();
209 m_QueuedCommands.resize(m_QueuedCommands.size() + 1);
211 m_Replay.Turn(m_CurrentTurn-1, m_TurnLength, commands);
213 NETTURN_LOG((L"Running %d cmds\n", commands.size()));
215 m_Simulation2.Update(m_TurnLength, commands);
218 return true;
221 void CNetTurnManager::OnSyncError(u32 turn, const std::string& expectedHash)
223 NETTURN_LOG((L"OnSyncError(%d, %ls)\n", turn, Hexify(expectedHash).c_str()));
225 // Only complain the first time
226 if (m_HasSyncError)
227 return;
228 m_HasSyncError = true;
230 bool quick = !TurnNeedsFullHash(turn);
231 std::string hash;
232 bool ok = m_Simulation2.ComputeStateHash(hash, quick);
233 ENSURE(ok);
235 OsPath path = psLogDir()/"oos_dump.txt";
236 std::ofstream file (OsString(path).c_str(), std::ofstream::out | std::ofstream::trunc);
237 m_Simulation2.DumpDebugState(file);
238 file.close();
240 std::wstringstream msg;
241 msg << L"Out of sync on turn " << turn << L": expected hash " << Hexify(expectedHash) << L"\n\n";
242 msg << L"Current state: turn " << m_CurrentTurn << L", hash " << Hexify(hash) << L"\n\n";
243 msg << L"Dumping current state to " << path;
244 if (g_GUI)
245 g_GUI->DisplayMessageBox(600, 350, L"Sync error", msg.str());
246 else
247 LOGERROR(L"%ls", msg.str().c_str());
250 void CNetTurnManager::Interpolate(float simFrameLength, float realFrameLength)
252 // TODO: using m_TurnLength might be a bit dodgy when length changes - maybe
253 // we need to save the previous turn length?
255 float offset = clamp(m_DeltaSimTime / (m_TurnLength / 1000.f) + 1.0, 0.0, 1.0);
256 m_Simulation2.Interpolate(simFrameLength, offset, realFrameLength);
259 void CNetTurnManager::AddCommand(int client, int player, CScriptValRooted data, u32 turn)
261 NETTURN_LOG((L"AddCommand(client=%d player=%d turn=%d)\n", client, player, turn));
263 if (!(m_CurrentTurn < turn && turn <= m_CurrentTurn + COMMAND_DELAY + 1))
265 debug_warn(L"Received command for invalid turn");
266 return;
269 SimulationCommand cmd;
270 cmd.player = player;
271 cmd.data = data;
272 m_QueuedCommands[turn - (m_CurrentTurn+1)][client].push_back(cmd);
275 void CNetTurnManager::FinishedAllCommands(u32 turn, u32 turnLength)
277 NETTURN_LOG((L"FinishedAllCommands(%d, %d)\n", turn, turnLength));
279 ENSURE(turn == m_ReadyTurn + 1);
280 m_ReadyTurn = turn;
281 m_TurnLength = turnLength;
284 bool CNetTurnManager::TurnNeedsFullHash(u32 turn)
286 // Check immediately for errors caused by e.g. inconsistent game versions
287 // (The hash is computed after the first sim update, so we start at turn == 1)
288 if (turn == 1)
289 return true;
291 // Otherwise check the full state every ~10 seconds in multiplayer games
292 // (TODO: should probably remove this when we're reasonably sure the game
293 // isn't too buggy, since the full hash is still pretty slow)
294 if (turn % 20 == 0)
295 return true;
297 return false;
300 void CNetTurnManager::EnableTimeWarpRecording(size_t numTurns)
302 m_TimeWarpStates.clear();
303 m_TimeWarpNumTurns = numTurns;
306 void CNetTurnManager::RewindTimeWarp()
308 if (m_TimeWarpStates.empty())
309 return;
311 std::stringstream stream(m_TimeWarpStates.back());
312 m_Simulation2.DeserializeState(stream);
313 m_TimeWarpStates.pop_back();
315 // Reset the turn manager state, so we won't execute stray commands and
316 // won't do the next snapshot until the appropriate time.
317 // (Ideally we ought to serialise the turn manager state and restore it
318 // here, but this is simpler for now.)
319 ResetState(0, 1);
322 void CNetTurnManager::QuickSave()
324 TIMER(L"QuickSave");
326 std::stringstream stream;
327 bool ok = m_Simulation2.SerializeState(stream);
328 if (!ok)
330 LOGERROR(L"Failed to quicksave game");
331 return;
334 m_QuickSaveState = stream.str();
335 if (g_GUI)
336 m_QuickSaveMetadata = g_GUI->GetScriptInterface().StringifyJSON(g_GUI->GetSavedGameData().get(), false);
337 else
338 m_QuickSaveMetadata = std::string();
340 LOGMESSAGERENDER(L"Quicksaved game");
344 void CNetTurnManager::QuickLoad()
346 TIMER(L"QuickLoad");
348 if (m_QuickSaveState.empty())
350 LOGERROR(L"Cannot quickload game - no game was quicksaved");
351 return;
354 std::stringstream stream(m_QuickSaveState);
355 bool ok = m_Simulation2.DeserializeState(stream);
356 if (!ok)
358 LOGERROR(L"Failed to quickload game");
359 return;
362 if (g_GUI && !m_QuickSaveMetadata.empty())
363 g_GUI->GetScriptInterface().CallFunctionVoid(OBJECT_TO_JSVAL(g_GUI->GetScriptObject()),
364 "restoreSavedGameData", g_GUI->GetScriptInterface().ParseJSON(m_QuickSaveMetadata));
366 LOGMESSAGERENDER(L"Quickloaded game");
368 // See RewindTimeWarp
369 ResetState(0, 1);
373 CNetClientTurnManager::CNetClientTurnManager(CSimulation2& simulation, CNetClient& client, int clientId, IReplayLogger& replay) :
374 CNetTurnManager(simulation, DEFAULT_TURN_LENGTH_MP, clientId, replay), m_NetClient(client)
378 void CNetClientTurnManager::PostCommand(CScriptValRooted data)
380 NETTURN_LOG((L"PostCommand()\n"));
382 // Transmit command to server
383 CSimulationMessage msg(m_Simulation2.GetScriptInterface(), m_ClientId, m_PlayerId, m_CurrentTurn + COMMAND_DELAY, data.get());
384 m_NetClient.SendMessage(&msg);
386 // Add to our local queue
387 //AddCommand(m_ClientId, m_PlayerId, data, m_CurrentTurn + COMMAND_DELAY);
388 // TODO: we should do this when the server stops sending our commands back to us
391 void CNetClientTurnManager::NotifyFinishedOwnCommands(u32 turn)
393 NETTURN_LOG((L"NotifyFinishedOwnCommands(%d)\n", turn));
395 // Send message to the server
396 CEndCommandBatchMessage msg;
397 msg.m_TurnLength = DEFAULT_TURN_LENGTH_MP; // TODO: why do we send this?
398 msg.m_Turn = turn;
399 m_NetClient.SendMessage(&msg);
402 void CNetClientTurnManager::NotifyFinishedUpdate(u32 turn)
404 bool quick = !TurnNeedsFullHash(turn);
405 std::string hash;
407 PROFILE3("state hash check");
408 bool ok = m_Simulation2.ComputeStateHash(hash, quick);
409 ENSURE(ok);
412 NETTURN_LOG((L"NotifyFinishedUpdate(%d, %ls)\n", turn, Hexify(hash).c_str()));
414 m_Replay.Hash(hash, quick);
416 // Send message to the server
417 CSyncCheckMessage msg;
418 msg.m_Turn = turn;
419 msg.m_Hash = hash;
420 m_NetClient.SendMessage(&msg);
423 void CNetClientTurnManager::OnSimulationMessage(CSimulationMessage* msg)
425 // Command received from the server - store it for later execution
426 AddCommand(msg->m_Client, msg->m_Player, msg->m_Data, msg->m_Turn);
430 CNetLocalTurnManager::CNetLocalTurnManager(CSimulation2& simulation, IReplayLogger& replay) :
431 CNetTurnManager(simulation, DEFAULT_TURN_LENGTH_SP, 0, replay)
435 void CNetLocalTurnManager::PostCommand(CScriptValRooted data)
437 // Add directly to the next turn, ignoring COMMAND_DELAY,
438 // because we don't need to compensate for network latency
439 AddCommand(m_ClientId, m_PlayerId, data, m_CurrentTurn + 1);
442 void CNetLocalTurnManager::NotifyFinishedOwnCommands(u32 turn)
444 FinishedAllCommands(turn, m_TurnLength);
447 void CNetLocalTurnManager::NotifyFinishedUpdate(u32 UNUSED(turn))
449 #if 0 // this hurts performance and is only useful for verifying log replays
450 std::string hash;
452 PROFILE3("state hash check");
453 bool ok = m_Simulation2.ComputeStateHash(hash);
454 ENSURE(ok);
456 m_Replay.Hash(hash);
457 #endif
460 void CNetLocalTurnManager::OnSimulationMessage(CSimulationMessage* UNUSED(msg))
462 debug_warn(L"This should never be called");
468 CNetServerTurnManager::CNetServerTurnManager(CNetServerWorker& server) :
469 m_NetServer(server), m_ReadyTurn(1), m_TurnLength(DEFAULT_TURN_LENGTH_MP)
471 // The first turn we will actually execute is number 2,
472 // so store dummy values into the saved lengths list
473 m_SavedTurnLengths.push_back(0);
474 m_SavedTurnLengths.push_back(0);
477 void CNetServerTurnManager::NotifyFinishedClientCommands(int client, u32 turn)
479 NETTURN_LOG((L"NotifyFinishedClientCommands(client=%d, turn=%d)\n", client, turn));
481 // Must be a client we've already heard of
482 ENSURE(m_ClientsReady.find(client) != m_ClientsReady.end());
484 // Clients must advance one turn at a time
485 ENSURE(turn == m_ClientsReady[client] + 1);
486 m_ClientsReady[client] = turn;
488 // Check whether this was the final client to become ready
489 CheckClientsReady();
492 void CNetServerTurnManager::CheckClientsReady()
494 // See if all clients (including self) are ready for a new turn
495 for (std::map<int, u32>::iterator it = m_ClientsReady.begin(); it != m_ClientsReady.end(); ++it)
497 NETTURN_LOG((L" %d: %d <=? %d\n", it->first, it->second, m_ReadyTurn));
498 if (it->second <= m_ReadyTurn)
499 return; // wasn't ready for m_ReadyTurn+1
502 // Advance the turn
503 ++m_ReadyTurn;
505 NETTURN_LOG((L"CheckClientsReady: ready for turn %d\n", m_ReadyTurn));
507 // Tell all clients that the next turn is ready
508 CEndCommandBatchMessage msg;
509 msg.m_TurnLength = m_TurnLength;
510 msg.m_Turn = m_ReadyTurn;
511 m_NetServer.Broadcast(&msg);
513 // Save the turn length in case it's needed later
514 ENSURE(m_SavedTurnLengths.size() == m_ReadyTurn);
515 m_SavedTurnLengths.push_back(m_TurnLength);
518 void CNetServerTurnManager::NotifyFinishedClientUpdate(int client, u32 turn, const std::string& hash)
520 // Clients must advance one turn at a time
521 ENSURE(turn == m_ClientsSimulated[client] + 1);
522 m_ClientsSimulated[client] = turn;
524 m_ClientStateHashes[turn][client] = hash;
526 // Find the newest turn which we know all clients have simulated
527 u32 newest = std::numeric_limits<u32>::max();
528 for (std::map<int, u32>::iterator it = m_ClientsSimulated.begin(); it != m_ClientsSimulated.end(); ++it)
530 if (it->second < newest)
531 newest = it->second;
534 // For every set of state hashes that all clients have simulated, check for OOS
535 for (std::map<u32, std::map<int, std::string> >::iterator it = m_ClientStateHashes.begin(); it != m_ClientStateHashes.end(); ++it)
537 if (it->first > newest)
538 break;
540 // Assume the host is correct (maybe we should choose the most common instead to help debugging)
541 std::string expected = it->second.begin()->second;
543 for (std::map<int, std::string>::iterator cit = it->second.begin(); cit != it->second.end(); ++cit)
545 NETTURN_LOG((L"sync check %d: %d = %ls\n", it->first, cit->first, Hexify(cit->second).c_str()));
546 if (cit->second != expected)
548 // Oh no, out of sync
550 // Tell everyone about it
551 CSyncErrorMessage msg;
552 msg.m_Turn = it->first;
553 msg.m_HashExpected = expected;
554 m_NetServer.Broadcast(&msg);
556 break;
561 // Delete the saved hashes for all turns that we've already verified
562 m_ClientStateHashes.erase(m_ClientStateHashes.begin(), m_ClientStateHashes.lower_bound(newest+1));
565 void CNetServerTurnManager::InitialiseClient(int client, u32 turn)
567 NETTURN_LOG((L"InitialiseClient(client=%d, turn=%d)\n", client, turn));
569 ENSURE(m_ClientsReady.find(client) == m_ClientsReady.end());
570 m_ClientsReady[client] = turn + 1;
571 m_ClientsSimulated[client] = turn;
574 void CNetServerTurnManager::UninitialiseClient(int client)
576 NETTURN_LOG((L"UninitialiseClient(client=%d)\n", client));
578 ENSURE(m_ClientsReady.find(client) != m_ClientsReady.end());
579 m_ClientsReady.erase(client);
580 m_ClientsSimulated.erase(client);
582 // Check whether we're ready for the next turn now that we're not
583 // waiting for this client any more
584 CheckClientsReady();
587 void CNetServerTurnManager::SetTurnLength(u32 msecs)
589 m_TurnLength = msecs;
592 u32 CNetServerTurnManager::GetSavedTurnLength(u32 turn)
594 ENSURE(turn <= m_ReadyTurn);
595 return m_SavedTurnLengths.at(turn);