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 #include <QPointer>
41 #include <QRunnable>
42 #include <QSet>
43 #include <QThread>
44 #include <QTimer>
45 
46 class ServerRefreshTime
47 {
48 public:
49  QPointer<Server> server;
50  QElapsedTimer time;
51 
52  ServerRefreshTime(QPointer<Server> server)
53  {
54  this->server = server;
55  time.start();
56  }
57 
58  bool operator==(const ServerRefreshTime &other) const
59  {
60  return server == other.server;
61  }
62 };
63 
64 class Refresher::Data
65 {
66 public:
67  typedef QHash<MasterClient *, MasterClientInfo *> MasterHashtable;
68  typedef QHash<MasterClient *, MasterClientInfo *>::iterator MasterHashtableIt;
69 
70  QTime batchTime;
71  bool bSleeping;
72  bool bKeepRunning;
73  int delayBetweenResends;
74  QTimer flushPendingDatagramsTimer;
75  MasterHashtable registeredMasters;
76 
77  QList<QPointer<Server> > unchallengedServers;
78  QList<ServerRefreshTime> refreshingServers;
79  UdpSocketPool socketPool;
80  QSet<MasterClient *> unchallengedMasters;
81 
82  bool hasAnyServers() const
83  {
84  return !unchallengedServers.isEmpty() || !refreshingServers.isEmpty();
85  }
86 
87  bool isServerRegistered(Server *server) const
88  {
89  return unchallengedServers.contains(server) ||
90  refreshingServers.contains(ServerRefreshTime(server));
91  }
92 
93  QPointer<Server> popNextUnchallengedServer()
94  {
95  while (!unchallengedServers.isEmpty())
96  {
97  // Take a random server so that they aren't favored by the order
98  // in which they were presented by the plugin.
99  quint32 index = static_cast<quint32>(qrand()) % unchallengedServers.size();
100  QPointer<Server> server = unchallengedServers.takeAt(index);
101  if (!server.isNull())
102  {
103  return server;
104  }
105  }
106  return nullptr;
107  }
108 
109  QUdpSocket *socket(const Server *server)
110  {
111  return socketPool.acquire(server->address(), server->port());
112  }
113 };
114 
116 
117 class Refresher::MasterClientInfo
118 {
119 public:
120  MasterClientInfo(Refresher *parent)
121  {
122  connect(&lastChallengeTimer, SIGNAL(timeout()),
123  parent, SLOT(attemptTimeoutMasters()));
124  lastChallengeTimer.setSingleShot(true);
125  lastChallengeTimer.setInterval(MASTER_SERVER_TIMEOUT_DELAY);
126  }
127 
128  void fireLastChallengeSentTimer()
129  {
130  lastChallengeTimer.start();
131  }
132 
133  bool isLastChallengeTimerActive() const
134  {
135  return lastChallengeTimer.isActive();
136  }
137 
138 private:
139  QTimer lastChallengeTimer;
140 };
141 
143 
144 Refresher *Refresher::staticInstance = nullptr;
145 QMutex Refresher::instanceMutex;
146 
147 Refresher::Refresher()
148 {
149  d = new Data;
150  d->bSleeping = true;
151  d->bKeepRunning = true;
152  d->delayBetweenResends = 1000;
153 
154  this->connect(&d->flushPendingDatagramsTimer, SIGNAL(timeout()), SLOT(readAllPendingDatagrams()));
155  this->connect(&d->socketPool, SIGNAL(readyRead()), SLOT(readAllPendingDatagrams()));
156  d->flushPendingDatagramsTimer.start(1000);
157 }
158 
159 Refresher::~Refresher()
160 {
161  delete d;
162 }
163 
164 void Refresher::attemptTimeoutMasters()
165 {
166  QList<MasterClient *> masters = d->registeredMasters.keys();
167  for (MasterClient *master : masters)
168  {
169  MasterClientInfo *pMasterInfo = d->registeredMasters[master];
170  if (!pMasterInfo->isLastChallengeTimerActive())
171  {
172  master->timeoutRefresh();
173  }
174  }
175 }
176 
177 void Refresher::concludeRefresh()
178 {
179  d->bSleeping = true;
180  d->socketPool.releaseAll();
181  emit sleepingModeEnter();
182 }
183 
184 Refresher *Refresher::instance()
185 {
186  if (staticInstance == nullptr)
187  {
188  QMutexLocker locker(&instanceMutex);
189  if (staticInstance == nullptr)
190  {
191  staticInstance = new Refresher();
192  }
193  }
194  return staticInstance;
195 }
196 
197 bool Refresher::isInstantiated()
198 {
199  return staticInstance != nullptr;
200 }
201 
202 void Refresher::deinstantiate()
203 {
204  QMutexLocker locker(&instanceMutex);
205  if (isInstantiated())
206  {
207  delete staticInstance;
208  staticInstance = nullptr;
209  }
210 }
211 
212 Server *Refresher::findRefreshingServer(const QHostAddress &address,
213  unsigned short port)
214 {
215  for (const ServerRefreshTime &refreshOp : d->refreshingServers)
216  {
217  if (refreshOp.server.isNull())
218  {
219  continue;
220  }
221  if (refreshOp.server->address().toIPv4Address() == address.toIPv4Address() && refreshOp.server->port() == port)
222  {
223  return refreshOp.server;
224  }
225  }
226  return nullptr;
227 }
228 
229 bool Refresher::isAnythingToRefresh() const
230 {
231  return d->hasAnyServers() || !d->registeredMasters.isEmpty() || !d->unchallengedMasters.isEmpty();
232 }
233 
234 void Refresher::masterFinishedRefreshing()
235 {
236  auto pMaster = static_cast<MasterClient *>(sender());
237  const QList<ServerPtr> &servers = pMaster->servers();
238  for (ServerPtr pServer : servers)
239  {
240  registerServer(pServer.data());
241  }
242 
243  unregisterMaster(pMaster);
244 
245  if (servers.size() == 0 && !isAnythingToRefresh())
246  {
247  concludeRefresh();
248  }
249 
250  emit finishedQueryingMaster(pMaster);
251 }
252 
253 void Refresher::purgeNullServers()
254 {
255  d->refreshingServers.removeAll(ServerRefreshTime(nullptr));
256  d->unchallengedServers.removeAll(nullptr);
257 }
258 
260 {
261  d->bKeepRunning = false;
262 }
263 
265 {
266  if (!d->registeredMasters.contains(pMaster))
267  {
268  auto pMasterInfo = new MasterClientInfo(this);
269  this->connect(pMaster, SIGNAL(listUpdated()), SLOT(masterFinishedRefreshing()));
270 
271  d->registeredMasters.insert(pMaster, pMasterInfo);
272  d->unchallengedMasters.insert(pMaster);
273  emit block();
274 
275  if (d->registeredMasters.size() == 1)
276  {
277  if (d->bSleeping)
278  {
279  d->bSleeping = false;
280  emit sleepingModeExit();
281  }
282  QTimer::singleShot(20, this, SLOT(sendMasterQueries()));
283  }
284  }
285 }
286 
288 {
289  bool hadAnyServers = d->hasAnyServers();
290  purgeNullServers();
291  if (!d->isServerRegistered(server))
292  {
293  CanRefreshServer refreshChecker(server);
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  purgeNullServers();
362  if (d->hasAnyServers())
363  {
364  startNewServerRefresh();
365  resendCurrentServerRefreshesIfTimeout();
366  // Call self. This will continue until there's nothing more
367  // to refresh. Also make sure that there is at least some
368  // delay between calls or Doomseeker will hog CPU.
369  QTimer::singleShot(qMax(1, gConfig.doomseeker.querySpeed().intervalBetweenServers),
370  this, SLOT(sendServerQueries()));
371  }
372  else
373  {
374  if (!isAnythingToRefresh())
375  {
376  concludeRefresh();
377  }
378  }
379 }
380 
382 {
383  d->delayBetweenResends = qMax(delay, 100);
384 }
385 
386 bool Refresher::start()
387 {
388  return d->socketPool.acquireMasterSocket() != nullptr;
389 }
390 
391 void Refresher::startNewServerRefresh()
392 {
393  // Copy the list as the original will be modified.
394  // We don't want to confuse the foreach.
395  QPointer<Server> server = d->popNextUnchallengedServer();
396  if (!server.isNull())
397  {
398  if (server->sendRefreshQuery(d->socket(server)))
399  {
400  d->refreshingServers.append(server);
401  }
402  }
403 }
404 
405 void Refresher::resendCurrentServerRefreshesIfTimeout()
406 {
407  for (int i = 0; i < d->refreshingServers.size(); ++i)
408  {
409  ServerRefreshTime &refreshOp = d->refreshingServers[i];
410  if (refreshOp.time.elapsed() > d->delayBetweenResends)
411  {
412  if (refreshOp.server->sendRefreshQuery(d->socket(refreshOp.server)))
413  {
414  refreshOp.time.start();
415  }
416  else
417  {
418  d->refreshingServers.removeOne(refreshOp);
419  // The collection on which we iterate just got shortened
420  // so let's back up by one step.
421  --i;
422  }
423  }
424  }
425 }
426 
427 bool Refresher::tryReadDatagramByMasterClient(QHostAddress &address,
428  unsigned short port, QByteArray &packet)
429 {
430  for (MasterClient *pMaster : d->registeredMasters.keys())
431  {
432  if (!d->bKeepRunning)
433  {
434  return true;
435  }
436  if (pMaster->isAddressSame(address, port))
437  {
438  MasterClient::Response response = pMaster->readResponse(packet);
439  switch (response)
440  {
441  case MasterClient::RESPONSE_BANNED:
442  case MasterClient::RESPONSE_WAIT:
443  case MasterClient::RESPONSE_BAD:
444  case MasterClient::RESPONSE_OLD:
445  pMaster->notifyResponse(response);
446  unregisterMaster(pMaster);
447  return true;
448  case MasterClient::RESPONSE_REPLY:
449  pMaster->sendRequest(d->socketPool.acquireMasterSocket());
450  return true;
451  default:
452  return true;
453  }
454  }
455  }
456  return false;
457 }
458 
459 bool Refresher::tryReadDatagramByServer(const QHostAddress &address,
460  unsigned short port, QByteArray &packet)
461 {
462  if (!d->bKeepRunning)
463  {
464  return true;
465  }
466  Server *server = findRefreshingServer(address, port);
467  if (server != nullptr)
468  {
469  // Store the state of request read.
470  int response = server->readRefreshQueryResponse(packet);
471  switch (response)
472  {
474  if (server->sendRefreshQuery(d->socket(server)))
475  {
476  // Reset timer
477  ServerRefreshTime refreshOp(server);
478  d->refreshingServers.removeAll(refreshOp);
479  d->refreshingServers.append(refreshOp);
480  break;
481  }
482  response = Server::RESPONSE_BAD;
483  [[gnu::fallthrough]];
484  default:
485  d->refreshingServers.removeAll(ServerRefreshTime(server));
486  server->refreshStops(static_cast<Server::Response>(response));
487  return true;
488 
490  {
491  // Reset timer
492  ServerRefreshTime refreshOp(server);
493  d->refreshingServers.removeAll(refreshOp);
494  d->refreshingServers.append(refreshOp);
495  break;
496  }
497  }
498  }
499  return false;
500 }
501 
502 void Refresher::unregisterMaster(MasterClient *pMaster)
503 {
504  pMaster->disconnect(this);
505  Data::MasterHashtableIt it = d->registeredMasters.find(pMaster);
506  if (it != d->registeredMasters.end())
507  {
508  MasterClientInfo *pMasterInfo = it.value();
509  delete pMasterInfo;
510  d->registeredMasters.erase(it);
511  }
512 }