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 "NetMessage.h"
21 : #include "NetServerTurnManager.h"
22 : #include "NetServer.h"
23 : #include "NetSession.h"
24 :
25 : #include "lib/utf8.h"
26 : #include "ps/CLogger.h"
27 : #include "ps/ConfigDB.h"
28 : #include "simulation2/system/TurnManager.h"
29 :
30 : #if 0
31 : #include "ps/Util.h"
32 : #define NETSERVERTURN_LOG(...) debug_printf(__VA_ARGS__)
33 : #else
34 : #define NETSERVERTURN_LOG(...)
35 : #endif
36 :
37 0 : CNetServerTurnManager::CNetServerTurnManager(CNetServerWorker& server)
38 0 : : m_NetServer(server), m_ReadyTurn(COMMAND_DELAY_MP - 1), m_TurnLength(DEFAULT_TURN_LENGTH)
39 : {
40 : // Turn 0 is not actually executed, store a dummy value.
41 0 : m_SavedTurnLengths.push_back(0);
42 : // Turns [1..COMMAND_DELAY - 1] are special: all clients run them without waiting on a server command batch.
43 : // Because of this, they are always run with the default MP turn length.
44 0 : for (u32 i = 1; i < COMMAND_DELAY_MP; ++i)
45 0 : m_SavedTurnLengths.push_back(m_TurnLength);
46 0 : }
47 :
48 0 : void CNetServerTurnManager::NotifyFinishedClientCommands(CNetServerSession& session, u32 turn)
49 : {
50 0 : int client = session.GetHostID();
51 :
52 : NETSERVERTURN_LOG("NotifyFinishedClientCommands(client=%d, turn=%d)\n", client, turn);
53 :
54 : // Must be a client we've already heard of
55 0 : ENSURE(m_ClientsData.find(client) != m_ClientsData.end());
56 :
57 : // Clients must advance one turn at a time
58 0 : if (turn != m_ClientsData[client].readyTurn + 1)
59 : {
60 0 : LOGERROR("NotifyFinishedClientCommands: Client %d (%s) is ready for turn %d, but expected %d",
61 : client,
62 : utf8_from_wstring(session.GetUserName()).c_str(),
63 : turn,
64 : m_ClientsData[client].readyTurn + 1);
65 :
66 0 : session.Disconnect(NDR_INCORRECT_READY_TURN_COMMANDS);
67 : }
68 :
69 0 : m_ClientsData[client].readyTurn = turn;
70 :
71 : // Check whether this was the final client to become ready
72 0 : CheckClientsReady();
73 0 : }
74 :
75 0 : void CNetServerTurnManager::CheckClientsReady()
76 : {
77 0 : int max_observer_lag = -1;
78 0 : CFG_GET_VAL("network.observermaxlag", max_observer_lag);
79 : // Clamp to 0-10000 turns, below/above that is no limit.
80 0 : max_observer_lag = max_observer_lag < 0 ? -1 : max_observer_lag > 10000 ? -1 : max_observer_lag;
81 :
82 : // See if all clients (including self) are ready for a new turn
83 0 : for (const std::pair<const int, Client>& clientData : m_ClientsData)
84 : {
85 : // Observers are allowed to lag more than regular clients.
86 0 : if (clientData.second.isObserver && (max_observer_lag == -1 || clientData.second.readyTurn > m_ReadyTurn - max_observer_lag))
87 0 : continue;
88 : NETSERVERTURN_LOG(" %d: %d <=? %d\n", clientReady.first, clientReady.second, m_ReadyTurn);
89 0 : if (clientData.second.readyTurn <= m_ReadyTurn)
90 0 : return; // wasn't ready for m_ReadyTurn+1
91 : }
92 :
93 0 : ++m_ReadyTurn;
94 :
95 : NETSERVERTURN_LOG("CheckClientsReady: ready for turn %d\n", m_ReadyTurn);
96 :
97 : // Tell all clients that the next turn is ready
98 0 : CEndCommandBatchMessage msg;
99 0 : msg.m_TurnLength = m_TurnLength;
100 0 : msg.m_Turn = m_ReadyTurn;
101 0 : m_NetServer.Broadcast(&msg, { NSS_INGAME });
102 :
103 0 : ENSURE(m_SavedTurnLengths.size() == m_ReadyTurn);
104 0 : m_SavedTurnLengths.push_back(m_TurnLength);
105 : }
106 :
107 0 : void CNetServerTurnManager::NotifyFinishedClientUpdate(CNetServerSession& session, u32 turn, const CStr& hash)
108 : {
109 :
110 0 : int client = session.GetHostID();
111 0 : const CStrW& playername = session.GetUserName();
112 :
113 : // Clients must advance one turn at a time
114 0 : if (turn != m_ClientsData[client].simulatedTurn + 1)
115 : {
116 0 : LOGERROR("NotifyFinishedClientUpdate: Client %d (%s) is ready for turn %d, but expected %d",
117 : client,
118 : utf8_from_wstring(playername).c_str(),
119 : turn,
120 : m_ClientsData[client].simulatedTurn + 1);
121 :
122 0 : session.Disconnect(NDR_INCORRECT_READY_TURN_SIMULATED);
123 : }
124 :
125 0 : m_ClientsData[client].simulatedTurn = turn;
126 :
127 : // Check for OOS only if in sync
128 0 : if (m_HasSyncError)
129 0 : return;
130 :
131 0 : m_ClientsData[client].playerName = playername;
132 0 : m_ClientStateHashes[turn][client] = hash;
133 :
134 : // Find the newest turn which we know all clients have simulated
135 0 : u32 newest = std::numeric_limits<u32>::max();
136 0 : for (const std::pair<const int, Client>& clientData : m_ClientsData)
137 0 : if (clientData.second.simulatedTurn < newest)
138 0 : newest = clientData.second.simulatedTurn;
139 :
140 : // For every set of state hashes that all clients have simulated, check for OOS
141 0 : for (const std::pair<const u32, std::map<int, std::string>>& clientStateHash : m_ClientStateHashes)
142 : {
143 0 : if (clientStateHash.first > newest)
144 0 : break;
145 :
146 : // Assume the host is correct (maybe we should choose the most common instead to help debugging)
147 0 : std::string expected = clientStateHash.second.begin()->second;
148 :
149 : // Find all players that are OOS on that turn
150 0 : std::vector<CStrW> OOSPlayerNames;
151 0 : for (const std::pair<const int, std::string>& hashPair : clientStateHash.second)
152 : {
153 : NETSERVERTURN_LOG("sync check %d: %d = %hs\n", clientStateHash.first, hashPair.first, Hexify(hashPair.second).c_str());
154 0 : if (hashPair.second != expected)
155 : {
156 : // Oh no, out of sync
157 0 : m_HasSyncError = true;
158 0 : m_ClientsData[hashPair.first].isOOS = true;
159 0 : OOSPlayerNames.push_back(m_ClientsData[hashPair.first].playerName);
160 : }
161 : }
162 :
163 : // Tell everyone about it
164 0 : if (m_HasSyncError)
165 : {
166 0 : CSyncErrorMessage msg;
167 0 : msg.m_Turn = clientStateHash.first;
168 0 : msg.m_HashExpected = expected;
169 0 : for (const CStrW& oosPlayername : OOSPlayerNames)
170 : {
171 0 : CSyncErrorMessage::S_m_PlayerNames h;
172 0 : h.m_Name = oosPlayername;
173 0 : msg.m_PlayerNames.push_back(h);
174 : }
175 0 : m_NetServer.Broadcast(&msg, { NSS_INGAME });
176 0 : break;
177 : }
178 : }
179 :
180 : // Delete the saved hashes for all turns that we've already verified
181 0 : m_ClientStateHashes.erase(m_ClientStateHashes.begin(), m_ClientStateHashes.lower_bound(newest+1));
182 : }
183 :
184 0 : void CNetServerTurnManager::InitialiseClient(int client, u32 turn, bool observer)
185 : {
186 : NETSERVERTURN_LOG("InitialiseClient(client=%d, turn=%d)\n", client, turn);
187 :
188 0 : ENSURE(m_ClientsData.find(client) == m_ClientsData.end());
189 0 : Client& data = m_ClientsData[client];
190 0 : data.readyTurn = turn + COMMAND_DELAY_MP - 1;
191 0 : data.simulatedTurn = turn;
192 0 : data.isObserver = observer;
193 0 : }
194 :
195 0 : void CNetServerTurnManager::UninitialiseClient(int client)
196 : {
197 : NETSERVERTURN_LOG("UninitialiseClient(client=%d)\n", client);
198 :
199 0 : ENSURE(m_ClientsData.find(client) != m_ClientsData.end());
200 0 : bool checkOOS = m_ClientsData[client].isOOS;
201 0 : m_ClientsData.erase(client);
202 :
203 : // Check whether we're ready for the next turn now that we're not
204 : // waiting for this client any more
205 0 : CheckClientsReady();
206 :
207 : // Check whether we're still OOS.
208 0 : if (checkOOS)
209 : {
210 0 : for (const std::pair<const int, Client>& clientData : m_ClientsData)
211 0 : if (clientData.second.isOOS)
212 0 : return;
213 0 : m_HasSyncError = false;
214 : }
215 : }
216 :
217 0 : void CNetServerTurnManager::SetTurnLength(u32 msecs)
218 : {
219 0 : m_TurnLength = msecs;
220 0 : }
221 :
222 0 : u32 CNetServerTurnManager::GetSavedTurnLength(u32 turn)
223 : {
224 0 : ENSURE(turn <= m_ReadyTurn);
225 0 : return m_SavedTurnLengths.at(turn);
226 : }
|