refresher.cpp
1 //------------------------------------------------------------------------------
2 // refresher.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301 USA
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2009 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "refresher.h"
24 
25 #include "configuration/doomseekerconfig.h"
26 #include "configuration/queryspeed.h"
27 #include "log.h"
28 #include "plugins/pluginloader.h"
29 #include "refresher/canrefreshserver.h"
30 #include "refresher/udpsocketpool.h"
31 #include "serverapi/masterclient.h"
32 #include "serverapi/mastermanager.h"
33 #include "serverapi/server.h"
34 
35 #include <QElapsedTimer>
36 #include <QHash>
37 #include <QList>
38 #include <QMutex>
39 #include <QMutexLocker>
40 #if QT_VERSION >= 0x060000
41 #include <QRandomGenerator>
42 #endif
43 #include <QRunnable>
44 #include <QSet>
45 #include <QThread>
46 #include <QTimer>
47 
48 class ServerRefreshTime
49 {
50 public:
51  QWeakPointer<Server> server;
52  QElapsedTimer time;
53 
54  ServerRefreshTime(ServerPtr server)
55  {
56  this->server = server;
57  time.start();
58  }
59 
60  bool operator==(const ServerRefreshTime &other) const
61  {
62  return server == other.server;
63  }
64 };
65 
66 class Refresher::Data
67 {
68 public:
69  typedef QHash<MasterClient *, MasterClientInfo *> MasterHashtable;
70  typedef QHash<MasterClient *, MasterClientInfo *>::iterator MasterHashtableIt;
71 
72  QTime batchTime;
73  bool bSleeping;
74  bool bKeepRunning;
75  int delayBetweenResends;
76  QTimer flushPendingDatagramsTimer;
77  MasterHashtable registeredMasters;
78 
79  QList<QWeakPointer<Server> > unchallengedServers;
80  QList<ServerRefreshTime> refreshingServers;
81  UdpSocketPool socketPool;
82  QSet<MasterClient *> unchallengedMasters;
83 
84  bool hasAnyServers() const
85  {
86  return !unchallengedServers.isEmpty() || !refreshingServers.isEmpty();
87  }
88 
89  bool isServerRegistered(const ServerPtr &server) const
90  {
91  return unchallengedServers.contains(server) ||
92  refreshingServers.contains(ServerRefreshTime(server));
93  }
94 
95  ServerPtr popNextUnchallengedServer()
96  {
97  while (!unchallengedServers.isEmpty())
98  {
99  // Temporary fix #4083 to keep Qt 6 compiling
100  #if QT_VERSION >= 0x060000
101  #define qrand() QRandomGenerator::global()->generate()
102  #endif
103 
104  // Take a random server so that they aren't favored by the order
105  // in which they were presented by the plugin.
106  quint32 index = static_cast<quint32>(qrand()) % unchallengedServers.size();
107  ServerPtr server = unchallengedServers.takeAt(index).toStrongRef();
108  if (!server.isNull())
109  {
110  return server;
111  }
112  }
113  return ServerPtr();
114  }
115 
116  QUdpSocket *socket(const ServerPtr &server)
117  {
118  return socketPool.acquire(server->address(), server->port());
119  }
120 };
121 
123 
124 class Refresher::MasterClientInfo
125 {
126 public:
127  MasterClientInfo(Refresher *parent)
128  {
129  connect(&lastChallengeTimer, SIGNAL(timeout()),
130  parent, SLOT(attemptTimeoutMasters()));
131  lastChallengeTimer.setSingleShot(true);
132  lastChallengeTimer.setInterval(MASTER_SERVER_TIMEOUT_DELAY);
133  }
134 
135  void fireLastChallengeSentTimer()
136  {
137  lastChallengeTimer.start();
138  }
139 
140  bool isLastChallengeTimerActive() const
141  {
142  return lastChallengeTimer.isActive();
143  }
144 
145 private:
146  QTimer lastChallengeTimer;
147 };
148 
150 
151 Refresher *Refresher::staticInstance = nullptr;
152 QMutex Refresher::instanceMutex;
153 
154 Refresher::Refresher()
155 {
156  d = new Data;
157  d->bSleeping = true;
158  d->bKeepRunning = true;
159  d->delayBetweenResends = 1000;
160 
161  this->connect(&d->flushPendingDatagramsTimer, SIGNAL(timeout()), SLOT(readAllPendingDatagrams()));
162  this->connect(&d->socketPool, SIGNAL(readyRead()), SLOT(readAllPendingDatagrams()));
163  d->flushPendingDatagramsTimer.start(1000);
164 }
165 
166 Refresher::~Refresher()
167 {
168  delete d;
169 }
170 
171 void Refresher::attemptTimeoutMasters()
172 {
173  QList<MasterClient *> masters = d->registeredMasters.keys();
174  for (MasterClient *master : masters)
175  {
176  MasterClientInfo *pMasterInfo = d->registeredMasters[master];
177  if (!pMasterInfo->isLastChallengeTimerActive())
178  {
179  master->timeoutRefresh();
180  }
181  }
182 }
183 
184 void Refresher::concludeRefresh()
185 {
186  d->bSleeping = true;
187  d->socketPool.releaseAll();
188  emit sleepingModeEnter();
189 }
190 
191 Refresher *Refresher::instance()
192 {
193  if (staticInstance == nullptr)
194  {
195  QMutexLocker locker(&instanceMutex);
196  if (staticInstance == nullptr)
197  {
198  staticInstance = new Refresher();
199  }
200  }
201  return staticInstance;
202 }
203 
204 bool Refresher::isInstantiated()
205 {
206  return staticInstance != nullptr;
207 }
208 
209 void Refresher::deinstantiate()
210 {
211  QMutexLocker locker(&instanceMutex);
212  if (isInstantiated())
213  {
214  delete staticInstance;
215  staticInstance = nullptr;
216  }
217 }
218 
219 ServerPtr Refresher::findRefreshingServer(const QHostAddress &address,
220  unsigned short port)
221 {
222  for (auto it = d->refreshingServers.begin(); it != d->refreshingServers.end();)
223  {
224  ServerPtr server = it->server.toStrongRef();
225  if (server.isNull())
226  {
227  it = d->refreshingServers.erase(it);
228  continue;
229  }
230  if (server->address().toIPv4Address() == address.toIPv4Address() && server->port() == port)
231  {
232  return server;
233  }
234  ++it;
235  }
236  return ServerPtr();
237 }
238 
239 bool Refresher::isAnythingToRefresh() const
240 {
241  return d->hasAnyServers() || !d->registeredMasters.isEmpty() || !d->unchallengedMasters.isEmpty();
242 }
243 
244 void Refresher::masterFinishedRefreshing()
245 {
246  auto pMaster = static_cast<MasterClient *>(sender());
247  unregisterMaster(pMaster);
248 
249  QTimer::singleShot(0, this, [this]()
250  {
251  if (!isAnythingToRefresh())
252  {
253  concludeRefresh();
254  }
255  });
256 
257  emit finishedQueryingMaster(pMaster);
258 }
259 
261 {
262  d->bKeepRunning = false;
263 }
264 
266 {
267  if (!d->registeredMasters.contains(pMaster))
268  {
269  auto pMasterInfo = new MasterClientInfo(this);
270  this->connect(pMaster, SIGNAL(listUpdated()), SLOT(masterFinishedRefreshing()));
271 
272  d->registeredMasters.insert(pMaster, pMasterInfo);
273  d->unchallengedMasters.insert(pMaster);
274  emit block();
275 
276  if (d->registeredMasters.size() == 1)
277  {
278  if (d->bSleeping)
279  {
280  d->bSleeping = false;
281  emit sleepingModeExit();
282  }
283  QTimer::singleShot(20, this, SLOT(sendMasterQueries()));
284  }
285  }
286 }
287 
288 bool Refresher::registerServer(ServerPtr server)
289 {
290  bool hadAnyServers = d->hasAnyServers();
291  if (!d->isServerRegistered(server))
292  {
293  CanRefreshServer refreshChecker(server.data());
294  if (!refreshChecker.shouldRefresh())
295  {
296  return false;
297  }
298  d->unchallengedServers.append(server);
299  if (!server->isCustom() && !server->isLan())
300  {
301  emit block();
302  }
303 
304  server->refreshStarts();
305  if (!hadAnyServers && d->hasAnyServers())
306  {
307  if (d->bSleeping)
308  {
309  d->bSleeping = false;
310  emit sleepingModeExit();
311  }
312  QTimer::singleShot(20, this, SLOT(sendServerQueries()));
313  }
314  }
315  return true;
316 }
317 
318 void Refresher::readAllPendingDatagrams()
319 {
320  while (d->socketPool.hasPendingDatagrams() && d->bKeepRunning)
321  {
322  readPendingDatagram();
323  }
324 }
325 
326 void Refresher::readPendingDatagram()
327 {
328  QHostAddress address;
329  quint16 port = 0;
330  QByteArray dataArray = d->socketPool.readNextDatagram(&address, &port);
331 
332  if (tryReadDatagramByMasterClient(address, port, dataArray))
333  {
334  return;
335  }
336  tryReadDatagramByServer(address, port, dataArray);
337 }
338 
339 void Refresher::sendMasterQueries()
340 {
341  while (!d->unchallengedMasters.isEmpty())
342  {
343  MasterClient *pMaster = *d->unchallengedMasters.begin();
344 
345  MasterClientInfo *pMasterInfo = d->registeredMasters[pMaster];
346  pMasterInfo->fireLastChallengeSentTimer();
347 
348  pMaster->refreshStarts();
349  pMaster->sendRequest(d->socketPool.acquireMasterSocket());
350  d->unchallengedMasters.remove(pMaster);
351  }
352 }
353 
354 void Refresher::sendServerQueries()
355 {
356  if (!d->bKeepRunning)
357  {
358  return;
359  }
360 
361  if (d->hasAnyServers())
362  {
363  startNewServerRefresh();
364  resendCurrentServerRefreshesIfTimeout();
365  // Call self. This will continue until there's nothing more
366  // to refresh. Also make sure that there is at least some
367  // delay between calls or Doomseeker will hog CPU.
368  QTimer::singleShot(qMax(1, gConfig.doomseeker.querySpeed().intervalBetweenServers),
369  this, SLOT(sendServerQueries()));
370  }
371  else
372  {
373  if (!isAnythingToRefresh())
374  {
375  concludeRefresh();
376  }
377  }
378 }
379 
381 {
382  d->delayBetweenResends = qMax(delay, 100);
383 }
384 
385 bool Refresher::start()
386 {
387  return d->socketPool.acquireMasterSocket() != nullptr;
388 }
389 
390 void Refresher::startNewServerRefresh()
391 {
392  // Copy the list as the original will be modified.
393  // We don't want to confuse the foreach.
394  ServerPtr server = d->popNextUnchallengedServer();
395  if (!server.isNull())
396  {
397  if (server->sendRefreshQuery(d->socket(server)))
398  {
399  d->refreshingServers.append(server);
400  }
401  }
402 }
403 
404 void Refresher::resendCurrentServerRefreshesIfTimeout()
405 {
406  for (auto it = d->refreshingServers.begin(); it != d->refreshingServers.end();)
407  {
408  ServerRefreshTime &refreshOp = *it;
409  ServerPtr server = refreshOp.server.toStrongRef();
410  if (server.isNull())
411  {
412  it = d->refreshingServers.erase(it);
413  continue;
414  }
415 
416  if (refreshOp.time.elapsed() > d->delayBetweenResends)
417  {
418  if (server->sendRefreshQuery(d->socket(server)))
419  {
420  refreshOp.time.start();
421  }
422  else
423  {
424  it = d->refreshingServers.erase(it);
425  continue;
426  }
427  }
428  ++it;
429  }
430 }
431 
432 bool Refresher::tryReadDatagramByMasterClient(QHostAddress &address,
433  unsigned short port, QByteArray &packet)
434 {
435  for (MasterClient *pMaster : d->registeredMasters.keys())
436  {
437  if (!d->bKeepRunning)
438  {
439  return true;
440  }
441  if (pMaster->isAddressSame(address, port))
442  {
443  MasterClient::Response response = pMaster->readResponse(packet);
444  switch (response)
445  {
446  case MasterClient::RESPONSE_BANNED:
447  case MasterClient::RESPONSE_WAIT:
448  case MasterClient::RESPONSE_BAD:
449  case MasterClient::RESPONSE_OLD:
450  pMaster->notifyResponse(response);
451  unregisterMaster(pMaster);
452  return true;
453  case MasterClient::RESPONSE_REPLY:
454  pMaster->sendRequest(d->socketPool.acquireMasterSocket());
455  return true;
456  default:
457  return true;
458  }
459  }
460  }
461  return false;
462 }
463 
464 bool Refresher::tryReadDatagramByServer(const QHostAddress &address,
465  unsigned short port, QByteArray &packet)
466 {
467  if (!d->bKeepRunning)
468  {
469  return true;
470  }
471  ServerPtr server = findRefreshingServer(address, port);
472  if (!server.isNull())
473  {
474  // Store the state of request read.
475  int response = server->readRefreshQueryResponse(packet);
476  switch (response)
477  {
479  if (server->sendRefreshQuery(d->socket(server)))
480  {
481  // Reset timer
482  ServerRefreshTime refreshOp(server);
483  d->refreshingServers.removeAll(refreshOp);
484  d->refreshingServers.append(refreshOp);
485  break;
486  }
487  response = Server::RESPONSE_BAD;
488  [[gnu::fallthrough]];
489  default:
490  d->refreshingServers.removeAll(ServerRefreshTime(server));
491  server->refreshStops(static_cast<Server::Response>(response));
492  return true;
493 
495  {
496  // Reset timer
497  ServerRefreshTime refreshOp(server);
498  d->refreshingServers.removeAll(refreshOp);
499  d->refreshingServers.append(refreshOp);
500  break;
501  }
502  }
503  }
504  return false;
505 }
506 
507 void Refresher::unregisterMaster(MasterClient *pMaster)
508 {
509  pMaster->disconnect(this);
510  Data::MasterHashtableIt it = d->registeredMasters.find(pMaster);
511  if (it != d->registeredMasters.end())
512  {
513  MasterClientInfo *pMasterInfo = it.value();
514  delete pMasterInfo;
515  d->registeredMasters.erase(it);
516  }
517 }