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 #include "precompiled.h"
19 
20 #include "ReplayTurnManager.h"
21 
22 #include "gui/GUIManager.h"
23 #include "ps/Util.h"
24 #include "simulation2/Simulation2.h"
25 
CReplayTurnManager(CSimulation2 & simulation,IReplayLogger & replay)26 CReplayTurnManager::CReplayTurnManager(CSimulation2& simulation, IReplayLogger& replay)
27 	: CLocalTurnManager(simulation, replay)
28 {
29 }
30 
StoreReplayCommand(u32 turn,int player,const std::string & command)31 void CReplayTurnManager::StoreReplayCommand(u32 turn, int player, const std::string& command)
32 {
33 	// Using the pair we make sure that commands per turn will be processed in the correct order
34 	m_ReplayCommands[turn].emplace_back(player, command);
35 }
36 
StoreReplayHash(u32 turn,const std::string & hash,bool quick)37 void CReplayTurnManager::StoreReplayHash(u32 turn, const std::string& hash, bool quick)
38 {
39 	m_ReplayHash[turn] = std::make_pair(hash, quick);
40 }
41 
StoreReplayTurnLength(u32 turn,u32 turnLength)42 void CReplayTurnManager::StoreReplayTurnLength(u32 turn, u32 turnLength)
43 {
44 	m_ReplayTurnLengths[turn] = turnLength;
45 
46 	// Initialize turn length
47 	if (turn == 0)
48 		m_TurnLength = m_ReplayTurnLengths[0];
49 }
50 
StoreFinalReplayTurn(u32 turn)51 void CReplayTurnManager::StoreFinalReplayTurn(u32 turn)
52 {
53 	m_FinalTurn = turn;
54 }
55 
NotifyFinishedUpdate(u32 turn)56 void CReplayTurnManager::NotifyFinishedUpdate(u32 turn)
57 {
58 	if (turn == 1 && m_FinalTurn == 0)
59 		g_GUI->SendEventToAll("ReplayFinished");
60 
61 	if (turn > m_FinalTurn)
62 		return;
63 
64 	DoTurn(turn);
65 
66 	// Compare hash if it exists in the replay and if we didn't have an OOS already
67 	std::map<u32, std::pair<std::string, bool>>::iterator turnHashIt = m_ReplayHash.find(turn);
68 	if (m_HasSyncError || turnHashIt == m_ReplayHash.end())
69 		return;
70 
71 	std::string expectedHash = turnHashIt->second.first;
72 	bool quickHash = turnHashIt->second.second;
73 
74 	// Compute hash
75 	std::string hash;
76 	ENSURE(m_Simulation2.ComputeStateHash(hash, quickHash));
77 	hash = Hexify(hash);
78 
79 	if (hash != expectedHash)
80 	{
81 		m_HasSyncError = true;
82 		LOGERROR("Replay out of sync on turn %d", turn);
83 		g_GUI->SendEventToAll("ReplayOutOfSync");
84 	}
85 }
86 
DoTurn(u32 turn)87 void CReplayTurnManager::DoTurn(u32 turn)
88 {
89 	debug_printf("Executing turn %u of %u\n", turn, m_FinalTurn);
90 
91 	m_TurnLength = m_ReplayTurnLengths[turn];
92 
93 	JSContext* cx = m_Simulation2.GetScriptInterface().GetContext();
94 	JSAutoRequest rq(cx);
95 
96 	// Simulate commands for that turn
97 	for (const std::pair<player_id_t, std::string>& p : m_ReplayCommands[turn])
98 	{
99 		JS::RootedValue command(cx);
100 		m_Simulation2.GetScriptInterface().ParseJSON(p.second, &command);
101 		AddCommand(m_ClientId, p.first, command, m_CurrentTurn + 1);
102 	}
103 
104 	if (turn == m_FinalTurn)
105 		g_GUI->SendEventToAll("ReplayFinished");
106 }
107