1 /* Copyright (C) 2021 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 "NetClientTurnManager.h"
21 #include "NetClient.h"
23 #include "ps/CLogger.h"
24 #include "ps/Pyrogenesis.h"
25 #include "ps/Replay.h"
26 #include "ps/Profile.h"
28 #include "simulation2/Simulation2.h"
33 #define NETCLIENTTURN_LOG(...) debug_printf(__VA_ARGS__)
35 #define NETCLIENTTURN_LOG(...)
38 extern CStrW g_UniqueLogPostfix
;
40 CNetClientTurnManager::CNetClientTurnManager(CSimulation2
& simulation
, CNetClient
& client
, int clientId
, IReplayLogger
& replay
)
41 : CTurnManager(simulation
, DEFAULT_TURN_LENGTH
, COMMAND_DELAY_MP
, clientId
, replay
), m_NetClient(client
)
45 void CNetClientTurnManager::PostCommand(JS::HandleValue data
)
47 NETCLIENTTURN_LOG("PostCommand()\n");
49 // Transmit command to server
50 CSimulationMessage
msg(m_Simulation2
.GetScriptInterface(), m_ClientId
, m_PlayerId
, m_CurrentTurn
+ m_CommandDelay
, data
);
51 m_NetClient
.SendMessage(&msg
);
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
58 void CNetClientTurnManager::NotifyFinishedOwnCommands(u32 turn
)
60 NETCLIENTTURN_LOG("NotifyFinishedOwnCommands(%d)\n", turn
);
62 CEndCommandBatchMessage msg
;
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.
71 m_NetClient
.SendMessage(&msg
);
74 void CNetClientTurnManager::NotifyFinishedUpdate(u32 turn
)
76 bool quick
= !TurnNeedsFullHash(turn
);
79 PROFILE3("state hash check");
80 ENSURE(m_Simulation2
.ComputeStateHash(hash
, quick
));
83 NETCLIENTTURN_LOG("NotifyFinishedUpdate(%d, %hs)\n", turn
, Hexify(hash
).c_str());
85 m_Replay
.Hash(hash
, quick
);
87 // Send message to the server
88 CSyncCheckMessage msg
;
91 m_NetClient
.SendMessage(&msg
);
94 void CNetClientTurnManager::OnDestroyConnection()
96 // Attempt to flush messages before leaving.
97 // Notice the sending is not reliable and rarely makes it to the Server.
98 if (m_NetClient
.GetCurrState() == NCS_INGAME
)
99 NotifyFinishedOwnCommands(m_CurrentTurn
+ m_CommandDelay
);
102 void CNetClientTurnManager::OnSimulationMessage(CSimulationMessage
* msg
)
104 // Command received from the server - store it for later execution
105 AddCommand(msg
->m_Client
, msg
->m_Player
, msg
->m_Data
, msg
->m_Turn
);
108 void CNetClientTurnManager::OnSyncError(u32 turn
, const CStr
& expectedHash
, const std::vector
<CSyncErrorMessage::S_m_PlayerNames
>& playerNames
)
110 CStr
expectedHashHex(Hexify(expectedHash
));
111 NETCLIENTTURN_LOG("OnSyncError(%d, %hs)\n", turn
, expectedHashHex
.c_str());
114 ENSURE(m_Simulation2
.ComputeStateHash(hash
, !TurnNeedsFullHash(turn
)));
116 OsPath
oosdumpPath(psLogDir() / (L
"oos_dump" + g_UniqueLogPostfix
+ L
".txt"));
117 std::ofstream
file (OsString(oosdumpPath
).c_str(), std::ofstream::out
| std::ofstream::trunc
);
118 file
<< "oos turn: " << turn
<< std::endl
;
119 file
<< "net client turn: " << m_CurrentTurn
<< std::endl
;
120 m_Simulation2
.DumpDebugState(file
);
123 std::ofstream
binfile (OsString(oosdumpPath
.ChangeExtension(L
".dat")).c_str(), std::ofstream::out
| std::ofstream::trunc
| std::ofstream::binary
);
124 m_Simulation2
.SerializeState(binfile
);
127 std::stringstream playerNamesString
;
128 std::vector
<CStr
> playerNamesStrings
;
129 playerNamesStrings
.reserve(playerNames
.size());
130 for (size_t i
= 0; i
< playerNames
.size(); ++i
)
132 CStr name
= utf8_from_wstring(playerNames
[i
].m_Name
);
133 playerNamesString
<< (i
== 0 ? "" : ", ") << name
;
134 playerNamesStrings
.push_back(name
);
137 LOGERROR("Out-Of-Sync on turn %d\nPlayers: %s\nDumping state to %s", turn
, playerNamesString
.str().c_str(), oosdumpPath
.string8());
139 m_NetClient
.PushGuiMessage(
140 "type", "out-of-sync",
142 "players", playerNamesStrings
,
143 "expectedHash", expectedHashHex
,
144 "hash", Hexify(hash
),
145 "path_oos_dump", wstring_from_utf8(oosdumpPath
.string8()),
146 "path_replay", wstring_from_utf8(m_Replay
.GetDirectory().string8()));