Line data Source code
1 : /* Copyright (C) 2021 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 "NetClientTurnManager.h"
21 : #include "NetClient.h"
22 :
23 : #include "ps/CLogger.h"
24 : #include "ps/Pyrogenesis.h"
25 : #include "ps/Replay.h"
26 : #include "ps/Profile.h"
27 : #include "ps/Util.h"
28 : #include "simulation2/Simulation2.h"
29 :
30 : #include <fstream>
31 :
32 : #if 0
33 : #define NETCLIENTTURN_LOG(...) debug_printf(__VA_ARGS__)
34 : #else
35 : #define NETCLIENTTURN_LOG(...)
36 : #endif
37 :
38 : extern CStrW g_UniqueLogPostfix;
39 :
40 0 : CNetClientTurnManager::CNetClientTurnManager(CSimulation2& simulation, CNetClient& client, int clientId, IReplayLogger& replay)
41 0 : : CTurnManager(simulation, DEFAULT_TURN_LENGTH, COMMAND_DELAY_MP, clientId, replay), m_NetClient(client)
42 : {
43 0 : }
44 :
45 0 : void CNetClientTurnManager::PostCommand(JS::HandleValue data)
46 : {
47 : NETCLIENTTURN_LOG("PostCommand()\n");
48 :
49 : // Transmit command to server
50 0 : CSimulationMessage msg(m_Simulation2.GetScriptInterface(), m_ClientId, m_PlayerId, m_CurrentTurn + m_CommandDelay, data);
51 0 : m_NetClient.SendMessage(&msg);
52 :
53 : // Add to our local queue
54 : //AddCommand(m_ClientId, m_PlayerId, data, m_CurrentTurn + m_CommandDelay);
55 : // TODO: we should do this when the server stops sending our commands back to us
56 0 : }
57 :
58 0 : void CNetClientTurnManager::NotifyFinishedOwnCommands(u32 turn)
59 : {
60 : NETCLIENTTURN_LOG("NotifyFinishedOwnCommands(%d)\n", turn);
61 :
62 0 : CEndCommandBatchMessage msg;
63 :
64 0 : msg.m_Turn = turn;
65 :
66 : // The turn-length field of the CEndCommandBatchMessage is currently only relevant
67 : // when sending it from the server to the clients.
68 : // It could be used to verify that the client simulated the correct turn length.
69 0 : msg.m_TurnLength = 0;
70 :
71 0 : m_NetClient.SendMessage(&msg);
72 0 : }
73 :
74 0 : void CNetClientTurnManager::NotifyFinishedUpdate(u32 turn)
75 : {
76 0 : bool quick = !TurnNeedsFullHash(turn);
77 0 : std::string hash;
78 : {
79 0 : PROFILE3("state hash check");
80 0 : ENSURE(m_Simulation2.ComputeStateHash(hash, quick));
81 : }
82 :
83 : NETCLIENTTURN_LOG("NotifyFinishedUpdate(%d, %hs)\n", turn, Hexify(hash).c_str());
84 :
85 0 : m_Replay.Hash(hash, quick);
86 :
87 : // Send message to the server
88 0 : CSyncCheckMessage msg;
89 0 : msg.m_Turn = turn;
90 0 : msg.m_Hash = hash;
91 0 : m_NetClient.SendMessage(&msg);
92 0 : }
93 :
94 0 : void CNetClientTurnManager::OnDestroyConnection()
95 : {
96 : // Attempt to flush messages before leaving.
97 : // Notice the sending is not reliable and rarely makes it to the Server.
98 0 : if (m_NetClient.GetCurrState() == NCS_INGAME)
99 0 : NotifyFinishedOwnCommands(m_CurrentTurn + m_CommandDelay);
100 0 : }
101 :
102 0 : void CNetClientTurnManager::OnSimulationMessage(CSimulationMessage* msg)
103 : {
104 : // Command received from the server - store it for later execution
105 0 : AddCommand(msg->m_Client, msg->m_Player, msg->m_Data, msg->m_Turn);
106 0 : }
107 :
108 0 : void CNetClientTurnManager::OnSyncError(u32 turn, const CStr& expectedHash, const std::vector<CSyncErrorMessage::S_m_PlayerNames>& playerNames)
109 : {
110 0 : CStr expectedHashHex(Hexify(expectedHash));
111 : NETCLIENTTURN_LOG("OnSyncError(%d, %hs)\n", turn, expectedHashHex.c_str());
112 :
113 0 : std::string hash;
114 0 : ENSURE(m_Simulation2.ComputeStateHash(hash, !TurnNeedsFullHash(turn)));
115 :
116 0 : OsPath oosdumpPath(psLogDir() / (L"oos_dump" + g_UniqueLogPostfix + L".txt"));
117 0 : std::ofstream file (OsString(oosdumpPath).c_str(), std::ofstream::out | std::ofstream::trunc);
118 0 : file << "oos turn: " << turn << std::endl;
119 0 : file << "net client turn: " << m_CurrentTurn << std::endl;
120 0 : m_Simulation2.DumpDebugState(file);
121 0 : file.close();
122 :
123 0 : std::ofstream binfile (OsString(oosdumpPath.ChangeExtension(L".dat")).c_str(), std::ofstream::out | std::ofstream::trunc | std::ofstream::binary);
124 0 : m_Simulation2.SerializeState(binfile);
125 0 : binfile.close();
126 :
127 0 : std::stringstream playerNamesString;
128 0 : std::vector<CStr> playerNamesStrings;
129 0 : playerNamesStrings.reserve(playerNames.size());
130 0 : for (size_t i = 0; i < playerNames.size(); ++i)
131 : {
132 0 : CStr name = utf8_from_wstring(playerNames[i].m_Name);
133 0 : playerNamesString << (i == 0 ? "" : ", ") << name;
134 0 : playerNamesStrings.push_back(name);
135 : }
136 :
137 0 : LOGERROR("Out-Of-Sync on turn %d\nPlayers: %s\nDumping state to %s", turn, playerNamesString.str().c_str(), oosdumpPath.string8());
138 :
139 0 : m_NetClient.PushGuiMessage(
140 : "type", "out-of-sync",
141 : "turn", turn,
142 : "players", playerNamesStrings,
143 : "expectedHash", expectedHashHex,
144 0 : "hash", Hexify(hash),
145 0 : "path_oos_dump", wstring_from_utf8(oosdumpPath.string8()),
146 0 : "path_replay", wstring_from_utf8(m_Replay.GetDirectory().string8()));
147 0 : }
|