Ticket #9: t0009_visualreplay_v1_r16653.patch
File t0009_visualreplay_v1_r16653.patch, 14.1 KB (added by , 9 years ago) |
---|
-
source/network/NetTurnManager.cpp
462 462 void CNetLocalTurnManager::OnSimulationMessage(CSimulationMessage* UNUSED(msg)) 463 463 { 464 464 debug_warn(L"This should never be called"); 465 465 } 466 466 467 CNetReplayTurnManager::CNetReplayTurnManager(CSimulation2& simulation, IReplayLogger& replay) : 468 CNetLocalTurnManager(simulation, replay) 469 { 470 } 471 472 void CNetReplayTurnManager::StoreReplayCommand(u32 turn, int player, const std::string& command) 473 { 474 m_ReplayCommands[turn][player].push_back(command); 475 } 476 477 void CNetReplayTurnManager::StoreReplayHash(u32 turn, const std::string& hash, bool quick) 478 { 479 m_ReplayHash[turn] = std::make_pair(hash, quick); 480 } 481 482 void CNetReplayTurnManager::StoreReplayTurnLength(u32 turn, u32 turnLength) 483 { 484 debug_printf("Setting turn %d length to %d\n", turn, turnLength); 485 m_ReplayTurnLengths[turn] = turnLength; 486 487 // Initialize turn length 488 if (turn == 0) 489 m_TurnLength = m_ReplayTurnLengths[0]; 490 } 467 491 492 void CNetReplayTurnManager::StoreFinalReplayTurn(u32 turn) 493 { 494 m_FinalReplayTurn = turn; 495 } 496 void CNetReplayTurnManager::NotifyFinishedUpdate(u32 turn) 497 { 498 if (turn > m_FinalReplayTurn) 499 return; 500 501 DoTurn(turn); 502 503 // Compare hash, if it exists in the replay 504 if (m_ReplayHash.count(turn) == 0) 505 return; 506 507 std::string hash = m_ReplayHash[turn].first; 508 bool quick = m_ReplayHash[turn].second; 468 509 510 // Compute hash 511 std::string expectedHash; 512 bool ok = m_Simulation2.ComputeStateHash(expectedHash, quick); 513 expectedHash = Hexify(expectedHash); 514 ENSURE(ok); 515 516 if (hash != expectedHash) 517 debug_printf("Hash mismatch on turn %d (expected %s) (current %s)\n", turn, expectedHash.c_str(), hash.c_str()); 518 } 519 520 void CNetReplayTurnManager::DoTurn(u32 turn) 521 { 522 m_TurnLength = m_ReplayTurnLengths[turn]; 523 524 // Add commands 525 std::map<int, std::vector<std::string> > playerCommands = m_ReplayCommands[turn]; 526 std::map<int, std::vector<std::string> >::iterator it; 527 for (it = playerCommands.begin(); it != playerCommands.end(); ++it) 528 { 529 int player = it->first; 530 for (size_t i = 0; i < it->second.size(); ++i) 531 { 532 JS::RootedValue data(m_Simulation2.GetScriptInterface().GetContext()); 533 m_Simulation2.GetScriptInterface().ParseJSON(it->second[i], &data); 534 AddCommand(m_ClientId, player, data, m_CurrentTurn + 1); 535 } 536 } 537 } 469 538 470 539 CNetServerTurnManager::CNetServerTurnManager(CNetServerWorker& server) : 471 540 m_NetServer(server), m_ReadyTurn(1), m_TurnLength(DEFAULT_TURN_LENGTH_MP) 472 541 { 473 542 // The first turn we will actually execute is number 2, -
source/network/NetTurnManager.h
20 20 21 21 #include "simulation2/helpers/SimulationCommand.h" 22 22 23 23 #include <list> 24 24 #include <map> 25 25 #include <vector> 26 26 class CNetServerWorker; 27 27 class CNetClient; 28 28 class CSimulationMessage; 29 29 class CSimulation2; 30 30 class IReplayLogger; … … 187 187 std::list<std::string> m_TimeWarpStates; 188 188 std::string m_QuickSaveState; // TODO: should implement a proper disk-based quicksave system 189 189 std::string m_QuickSaveMetadata; 190 190 }; 191 191 192 192 193 /** 193 194 * Implementation of CNetTurnManager for network clients. 194 195 */ 195 196 class CNetClientTurnManager : public CNetTurnManager 196 197 { … … 231 232 232 233 virtual void NotifyFinishedUpdate(u32 turn); 233 234 }; 234 235 235 236 237 238 /** 239 * Implementation of CNetTurnManager for replay games. 240 */ 241 class CNetReplayTurnManager : public CNetLocalTurnManager 242 { 243 public: 244 CNetReplayTurnManager(CSimulation2& simulation, IReplayLogger& replay); 245 246 void StoreReplayCommand(u32 turn, int player, const std::string& command); 247 248 void StoreReplayTurnLength(u32 turn, u32 turnLength); 249 250 void StoreReplayHash(u32 turn, const std::string& hash, bool quick); 251 252 void StoreFinalReplayTurn(u32 turn); 253 254 255 protected: 256 virtual void NotifyFinishedUpdate(u32 turn); 257 258 void DoTurn(u32 turn); 259 260 // Contains the commands of every player on each turn 261 std::map<u32, std::map<int, std::vector<std::string> > > m_ReplayCommands; 262 263 // Contains the length of every turn 264 std::map<u32, u32 > m_ReplayTurnLengths; 265 266 // Contains all replay hash values and weather or not the quick hash method was used 267 std::map<u32, std::pair<std::string, bool> > m_ReplayHash; 268 269 // The number of the last turn in the replay 270 u32 m_FinalReplayTurn; 271 272 }; 236 273 /** 237 274 * The server-side counterpart to CNetClientTurnManager. 238 275 * Records the turn state of each client, and sends turn advancement messages 239 276 * when all clients are ready. 240 277 * -
source/ps/Game.cpp
61 61 62 62 /** 63 63 * Constructor 64 64 * 65 65 **/ 66 CGame::CGame(bool disableGraphics ):66 CGame::CGame(bool disableGraphics, bool replayLog): 67 67 m_World(new CWorld(this)), 68 68 m_Simulation2(new CSimulation2(&m_World->GetUnitManager(), g_ScriptRuntime, m_World->GetTerrain())), 69 69 m_GameView(disableGraphics ? NULL : new CGameView(this)), 70 70 m_GameStarted(false), 71 71 m_Paused(false), 72 72 m_SimRate(1.0f), 73 73 m_PlayerID(-1), 74 m_IsSavedGame(false) 74 m_IsSavedGame(false), 75 m_IsReplay(false), 76 m_ReplayStream(NULL) 75 77 { 76 m_ReplayLogger = new CReplayLogger(m_Simulation2->GetScriptInterface());77 78 // TODO: should use CDummyReplayLogger unless activated by cmd-line arg, perhaps? 79 if (replayLog) 80 m_ReplayLogger = new CReplayLogger(m_Simulation2->GetScriptInterface()); 81 else 82 m_ReplayLogger = new CDummyReplayLogger(); 78 83 79 84 // Need to set the CObjectManager references after various objects have 80 85 // been initialised, so do it here rather than via the initialisers above. 81 86 if (m_GameView) 82 87 m_World->GetUnitManager().SetObjectManager(m_GameView->GetObjectManager()); 83 88 84 89 m_TurnManager = new CNetLocalTurnManager(*m_Simulation2, GetReplayLogger()); // this will get replaced if we're a net server/client 85 90 86 91 m_Simulation2->LoadDefaultScripts(); 87 92 } 93 int CGame::LoadReplayData() 94 { 95 ENSURE(m_IsReplay); 96 ENSURE(!m_ReplayPath.empty()); 97 98 CNetReplayTurnManager* replayTurnMgr = static_cast<CNetReplayTurnManager*>(GetTurnManager()); 99 100 u32 currentTurn = 0; 101 std::string type; 102 while ((*m_ReplayStream >> type).good()) 103 { 104 if (type == "turn") 105 { 106 u32 turn = 0; 107 u32 turnLength = 0; 108 *m_ReplayStream >> turn >> turnLength; 109 ENSURE(turn == currentTurn); 110 replayTurnMgr->StoreReplayTurnLength(currentTurn, turnLength); 111 } 112 else if (type == "cmd") 113 { 114 int player; 115 *m_ReplayStream >> player; 116 117 std::string line; 118 std::getline(*m_ReplayStream, line); 119 replayTurnMgr->StoreReplayCommand(currentTurn, player, line); 120 } 121 else if (type == "hash" || type == "hash-quick") 122 { 123 bool quick = (type == "hash-quick"); 124 std::string replayHash; 125 *m_ReplayStream >> replayHash; 126 replayTurnMgr->StoreReplayHash(currentTurn, replayHash, quick); 127 } 128 else if (type == "end") 129 { 130 currentTurn++; 131 } 132 else 133 { 134 CancelLoad(L"Failed to load replay data (unrecognized content)"); 135 } 136 } 137 m_FinalReplayTurn = currentTurn; 138 replayTurnMgr->StoreFinalReplayTurn(currentTurn); 139 return 0; 140 } 141 void CGame::StartReplay(const std::string& replayPath) 142 { 143 m_IsReplay = true; 144 ScriptInterface& scriptInterface = m_Simulation2->GetScriptInterface(); 145 146 SetTurnManager(new CNetReplayTurnManager(*m_Simulation2, GetReplayLogger())); 147 148 // TODO: IF FILE m_ReplayPath NOT EXISTS PRINT ERROR 88 149 150 m_ReplayPath = replayPath; 151 m_ReplayStream = new std::ifstream(m_ReplayPath.c_str()); 152 if (!m_ReplayStream->good()) 153 { 154 debug_printf("Could not open replay file!\n"); 155 //TODO: how to exit properly? std::exit(0);? 156 ENSURE(m_ReplayStream->good()); 157 } 158 159 std::string type; 160 ENSURE((*m_ReplayStream >> type).good() && type == "start"); 161 162 std::string line; 163 std::getline(*m_ReplayStream, line); 164 JS::RootedValue attribs(scriptInterface.GetContext()); 165 scriptInterface.ParseJSON(line, &attribs); 166 StartGame(&attribs, ""); 167 } 89 168 /** 90 169 * Destructor 91 170 * 92 171 **/ 93 172 CGame::~CGame() … … 99 178 delete m_TurnManager; 100 179 delete m_GameView; 101 180 delete m_Simulation2; 102 181 delete m_World; 103 182 delete m_ReplayLogger; 183 delete m_ReplayStream; 104 184 } 105 185 106 186 void CGame::SetTurnManager(CNetTurnManager* turnManager) 107 187 { 108 188 if (m_TurnManager) … … 174 254 RegMemFun(g_Renderer.GetSingletonPtr()->GetWaterManager(), &WaterManager::LoadWaterTextures, L"LoadWaterTextures", 80); 175 255 176 256 if (m_IsSavedGame) 177 257 RegMemFun(this, &CGame::LoadInitialState, L"Loading game", 1000); 178 258 259 if (m_IsReplay) 260 RegMemFun(this, &CGame::LoadReplayData, L"Loading replay data", 1000); 261 179 262 LDR_EndRegistering(); 180 263 } 181 264 182 265 int CGame::LoadInitialState() 183 266 { … … 270 353 m_TurnManager->SetPlayerID(m_PlayerID); 271 354 } 272 355 273 356 void CGame::StartGame(JS::MutableHandleValue attribs, const std::string& savedState) 274 357 { 358 if (m_ReplayLogger != false) 275 359 m_ReplayLogger->StartGame(attribs); 360 276 361 RegisterInit(attribs, savedState); 277 362 } 278 363 279 364 // TODO: doInterpolate is optional because Atlas interpolates explicitly, 280 365 // so that it has more control over the update rate. The game might want to … … 308 393 { 309 394 { 310 395 PROFILE3("gui sim update"); 311 396 g_GUI->SendEventToAll("SimulationUpdate"); 312 397 } 398 if (m_IsReplay && m_TurnManager->GetCurrentTurn() == m_FinalReplayTurn) 399 g_GUI->SendEventToAll("ReplayFinished"); 313 400 314 401 GetView()->GetLOSTexture().MakeDirty(); 315 402 } 316 403 317 404 if (CRenderer::IsInitialised()) -
source/ps/Game.h
18 18 #ifndef INCLUDED_GAME 19 19 #define INCLUDED_GAME 20 20 21 21 #include "ps/Errors.h" 22 22 #include <vector> 23 23 #include <map> 24 24 #include "scriptinterface/ScriptVal.h" 25 25 26 26 class CWorld; 27 27 class CSimulation2; 28 28 class CGameView; … … 63 63 int m_PlayerID; 64 64 65 65 CNetTurnManager* m_TurnManager; 66 66 67 67 public: 68 CGame(bool disableGraphics = false );68 CGame(bool disableGraphics = false, bool replayLog = true); 69 69 ~CGame(); 70 70 71 71 /** 72 72 * the game is paused and no updates will be performed if true. 73 73 **/ 74 74 bool m_Paused; 75 75 76 76 void StartGame(JS::MutableHandleValue attribs, const std::string& savedState); 77 77 PSRETURN ReallyStartGame(); 78 78 79 void StartReplay(const std::string& replayPath); 80 79 81 /** 80 82 * Periodic heartbeat that controls the process. performs all per-frame updates. 81 83 * Simulation update is called and game status update is called. 82 84 * 83 85 * @param deltaRealTime Elapsed real time since last beat/frame, in seconds. … … 169 171 std::vector<CColor> m_PlayerColors; 170 172 171 173 int LoadInitialState(); 172 174 std::string m_InitialSavedState; // valid between RegisterInit and LoadInitialState 173 175 bool m_IsSavedGame; // true if loading a saved game; false for a new game 176 177 int LoadReplayData(); 178 std::string m_ReplayPath; 179 bool m_IsReplay; 180 std::istream* m_ReplayStream; 181 u32 m_FinalReplayTurn; 174 182 }; 175 183 176 184 extern CGame *g_Game; 177 185 178 186 #endif -
source/ps/GameSetup/GameSetup.cpp
878 878 // Initialise the low-quality rand function 879 879 srand(time(NULL)); // NOTE: this rand should *not* be used for simulation! 880 880 } 881 881 882 882 bool Autostart(const CmdLineArgs& args); 883 bool VisualReplay(const CmdLineArgs& args); 883 884 884 885 bool Init(const CmdLineArgs& args, int flags) 885 886 { 886 887 h_mgr_init(); 887 888 … … 1071 1072 1072 1073 ogl_WarnIfError(); 1073 1074 1074 1075 try 1075 1076 { 1076 if (! Autostart(args))1077 if (!VisualReplay(args) && !Autostart(args)) 1077 1078 { 1078 1079 const bool setup_gui = ((flags & INIT_NO_GUI) == 0); 1079 1080 // We only want to display the splash screen at startup 1080 1081 shared_ptr<ScriptInterface> scriptInterface = g_GUI->GetScriptInterface(); 1081 1082 JSContext* cx = scriptInterface->GetContext(); … … 1471 1472 } 1472 1473 1473 1474 return true; 1474 1475 } 1475 1476 1477 bool VisualReplay(const CmdLineArgs& args) 1478 { 1479 CStr replayPath = args.Get("replay-visual"); 1480 1481 if (replayPath.empty()) 1482 return false; 1483 1484 g_Game = new CGame(false, false); 1485 1486 g_Game->SetPlayerID(-1); 1487 g_Game->StartReplay(replayPath); 1488 1489 // TODO: Non progressive load can fail - need a decent way to handle this 1490 LDR_NonprogressiveLoad(); 1491 1492 PSRETURN ret = g_Game->ReallyStartGame(); 1493 ENSURE(ret == PSRETURN_OK); 1494 1495 ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); 1496 1497 InitPs(true, L"page_session.xml", &scriptInterface, JS::UndefinedHandleValue); 1498 return true; 1499 } 1500 1476 1501 void CancelLoad(const CStrW& message) 1477 1502 { 1478 1503 shared_ptr<ScriptInterface> pScriptInterface = g_GUI->GetActiveGUI()->GetScriptInterface(); 1479 1504 JSContext* cx = pScriptInterface->GetContext(); 1480 1505 JSAutoRequest rq(cx); -
source/ps/Replay.h
54 54 */ 55 55 class CDummyReplayLogger : public IReplayLogger 56 56 { 57 57 public: 58 58 virtual void StartGame(JS::MutableHandleValue UNUSED(attribs)) { } 59 virtual void Turn(u32 UNUSED(n), u32 UNUSED(turnLength), conststd::vector<SimulationCommand>& UNUSED(commands)) { }59 virtual void Turn(u32 UNUSED(n), u32 UNUSED(turnLength), std::vector<SimulationCommand>& UNUSED(commands)) { } 60 60 virtual void Hash(const std::string& UNUSED(hash), bool UNUSED(quick)) { } 61 61 }; 62 62 63 63 /** 64 64 * Implementation of IReplayLogger that saves data to a file in the logs directory.