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)
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  QPointer<Server> server = unchallengedServers.takeFirst();
98  if (!server.isNull())
99  {
100  return server;
101  }
102  }
103  return nullptr;
104  }
105 
106  QUdpSocket *socket(const Server *server)
107  {
108  return socketPool.acquire(server->address(), server->port());
109  }
110 };
111 
113 
114 class Refresher::MasterClientInfo
115 {
116 public:
117  MasterClientInfo(Refresher *parent)
118  {
119  connect(&lastChallengeTimer, SIGNAL(timeout()),
120  parent, SLOT(attemptTimeoutMasters()));
121  lastChallengeTimer.setSingleShot(true);
122  lastChallengeTimer.setInterval(MASTER_SERVER_TIMEOUT_DELAY);
123  }
124 
125  void fireLastChallengeSentTimer()
126  {
127  lastChallengeTimer.start();
128  }
129 
130  bool isLastChallengeTimerActive() const
131  {
132  return lastChallengeTimer.isActive();
133  }
134 
135 private:
136  QTimer lastChallengeTimer;
137 };
138 
140 
141 Refresher *Refresher::staticInstance = nullptr;
142 QMutex Refresher::instanceMutex;
143 
144 Refresher::Refresher()
145 {
146  d = new Data;
147  d->bSleeping = true;
148  d->bKeepRunning = true;
149  d->delayBetweenResends = 1000;
150 
151  this->connect(&d->flushPendingDatagramsTimer, SIGNAL(timeout()), SLOT(readAllPendingDatagrams()));
152  this->connect(&d->socketPool, SIGNAL(readyRead()), SLOT(readAllPendingDatagrams()));
153  d->flushPendingDatagramsTimer.start(1000);
154 }
155 
156 Refresher::~Refresher()
157 {
158  delete d;
159 }
160 
161 void Refresher::attemptTimeoutMasters()
162 {
163  QList<MasterClient *> masters = d->registeredMasters.keys();
164  for (MasterClient *master : masters)
165  {
166  MasterClientInfo *pMasterInfo = d->registeredMasters[master];
167  if (!pMasterInfo->isLastChallengeTimerActive())
168  {
169  master->timeoutRefresh();
170  }
171  }
172 }
173 
174 void Refresher::concludeRefresh()
175 {
176  d->bSleeping = true;
177  d->socketPool.releaseAll();
178  emit sleepingModeEnter();
179 }
180 
181 Refresher *Refresher::instance()
182 {
183  if (staticInstance == nullptr)
184  {
185  QMutexLocker locker(&instanceMutex);
186  if (staticInstance == nullptr)
187  {
188  staticInstance = new Refresher();
189  }
190  }
191  return staticInstance;
192 }
193 
194 bool Refresher::isInstantiated()
195 {
196  return staticInstance != nullptr;
197 }
198 
199 void Refresher::deinstantiate()
200 {
201  QMutexLocker locker(&instanceMutex);
202  if (isInstantiated())
203  {
204  delete staticInstance;
205  staticInstance = nullptr;
206  }
207 }
208 
209 Server *Refresher::findRefreshingServer(const QHostAddress &address,
210  unsigned short port)
211 {
212  for (const ServerRefreshTime &refreshOp : d->refreshingServers)
213  {
214  if (refreshOp.server.isNull())
215  {
216  continue;
217  }
218  if (refreshOp.server->address().toIPv4Address() == address.toIPv4Address() && refreshOp.server->port() == port)
219  {
220  return refreshOp.server;
221  }
222  }
223  return nullptr;
224 }
225 
226 bool Refresher::isAnythingToRefresh() const
227 {
228  return d->hasAnyServers() || !d->registeredMasters.isEmpty() || !d->unchallengedMasters.isEmpty();
229 }
230 
231 void Refresher::masterFinishedRefreshing()
232 {
233  auto pMaster = static_cast<MasterClient *>(sender());
234  const QList<ServerPtr> &servers = pMaster->servers();
235  for (ServerPtr pServer : servers)
236  {
237  registerServer(pServer.data());
238  }
239 
240  unregisterMaster(pMaster);
241 
242  if (servers.size() == 0 && !isAnythingToRefresh())
243  {
244  concludeRefresh();
245  }
246 
247  emit finishedQueryingMaster(pMaster);
248 }
249 
250 void Refresher::purgeNullServers()
251 {
252  d->refreshingServers.removeAll(ServerRefreshTime(nullptr));
253  d->unchallengedServers.removeAll(nullptr);
254 }
255 
257 {
258  d->bKeepRunning = false;
259 }
260 
262 {
263  if (!d->registeredMasters.contains(pMaster))
264  {
265  auto pMasterInfo = new MasterClientInfo(this);
266  this->connect(pMaster, SIGNAL(listUpdated()), SLOT(masterFinishedRefreshing()));
267 
268  d->registeredMasters.insert(pMaster, pMasterInfo);
269  d->unchallengedMasters.insert(pMaster);
270  emit block();
271 
272  if (d->registeredMasters.size() == 1)
273  {
274  if (d->bSleeping)
275  {
276  d->bSleeping = false;
277  emit sleepingModeExit();
278  }
279  QTimer::singleShot(20, this, SLOT(sendMasterQueries()));
280  }
281  }
282 }
283 
285 {
286  bool hadAnyServers = d->hasAnyServers();
287  purgeNullServers();
288  if (!d->isServerRegistered(server))
289  {
290  CanRefreshServer refreshChecker(server);
291  if (!refreshChecker.shouldRefresh())
292  {
293  return false;
294  }
295  d->unchallengedServers.append(server);
296  if (!server->isCustom() && !server->isLan())
297  {
298  emit block();
299  }
300 
301  server->refreshStarts();
302  if (!hadAnyServers && d->hasAnyServers())
303  {
304  if (d->bSleeping)
305  {
306  d->bSleeping = false;
307  emit sleepingModeExit();
308  }
309  QTimer::singleShot(20, this, SLOT(sendServerQueries()));
310  }
311  }
312  return true;
313 }
314 
315 void Refresher::readAllPendingDatagrams()
316 {
317  while (d->socketPool.hasPendingDatagrams() && d->bKeepRunning)
318  {
319  readPendingDatagram();
320  }
321 }
322 
323 void Refresher::readPendingDatagram()
324 {
325  QHostAddress address;
326  quint16 port = 0;
327  QByteArray dataArray = d->socketPool.readNextDatagram(&address, &port);
328 
329  if (tryReadDatagramByMasterClient(address, port, dataArray))
330  {
331  return;
332  }
333  tryReadDatagramByServer(address, port, dataArray);
334 }
335 
336 void Refresher::sendMasterQueries()
337 {
338  while (!d->unchallengedMasters.isEmpty())
339  {
340  MasterClient *pMaster = *d->unchallengedMasters.begin();
341 
342  MasterClientInfo *pMasterInfo = d->registeredMasters[pMaster];
343  pMasterInfo->fireLastChallengeSentTimer();
344 
345  pMaster->refreshStarts();
346  pMaster->sendRequest(d->socketPool.acquireMasterSocket());
347  d->unchallengedMasters.remove(pMaster);
348  }
349 }
350 
351 void Refresher::sendServerQueries()
352 {
353  if (!d->bKeepRunning)
354  {
355  return;
356  }
357 
358  purgeNullServers();
359  if (d->hasAnyServers())
360  {
361  startNewServerRefresh();
362  resendCurrentServerRefreshesIfTimeout();
363  // Call self. This will continue until there's nothing more
364  // to refresh. Also make sure that there is at least some
365  // delay between calls or Doomseeker will hog CPU.
366  QTimer::singleShot(qMax(1, gConfig.doomseeker.querySpeed().intervalBetweenServers),
367  this, SLOT(sendServerQueries()));
368  }
369  else
370  {
371  if (!isAnythingToRefresh())
372  {
373  concludeRefresh();
374  }
375  }
376 }
377 
379 {
380  d->delayBetweenResends = qMax(delay, 100);
381 }
382 
383 bool Refresher::start()
384 {
385  return d->socketPool.acquireMasterSocket() != nullptr;
386 }
387 
388 void Refresher::startNewServerRefresh()
389 {
390  // Copy the list as the original will be modified.
391  // We don't want to confuse the foreach.
392  QPointer<Server> server = d->popNextUnchallengedServer();
393  if (!server.isNull())
394  {
395  if (server->sendRefreshQuery(d->socket(server)))
396  {
397  d->refreshingServers.append(server);
398  }
399  }
400 }
401 
402 void Refresher::resendCurrentServerRefreshesIfTimeout()
403 {
404  for (int i = 0; i < d->refreshingServers.size(); ++i)
405  {
406  ServerRefreshTime &refreshOp = d->refreshingServers[i];
407  if (refreshOp.time.elapsed() > d->delayBetweenResends)
408  {
409  if (refreshOp.server->sendRefreshQuery(d->socket(refreshOp.server)))
410  {
411  refreshOp.time.start();
412  }
413  else
414  {
415  d->refreshingServers.removeOne(refreshOp);
416  // The collection on which we iterate just got shortened
417  // so let's back up by one step.
418  --i;
419  }
420  }
421  }
422 }
423 
424 bool Refresher::tryReadDatagramByMasterClient(QHostAddress &address,
425  unsigned short port, QByteArray &packet)
426 {
427  for (MasterClient *pMaster : d->registeredMasters.keys())
428  {
429  if (!d->bKeepRunning)
430  {
431  return true;
432  }
433  if (pMaster->isAddressSame(address, port))
434  {
435  MasterClient::Response response = pMaster->readResponse(packet);
436  switch (response)
437  {
438  case MasterClient::RESPONSE_BANNED:
439  case MasterClient::RESPONSE_WAIT:
440  case MasterClient::RESPONSE_BAD:
441  case MasterClient::RESPONSE_OLD:
442  pMaster->notifyResponse(response);
443  unregisterMaster(pMaster);
444  return true;
445  case MasterClient::RESPONSE_REPLY:
446  pMaster->sendRequest(d->socketPool.acquireMasterSocket());
447  return true;
448  default:
449  return true;
450  }
451  }
452  }
453  return false;
454 }
455 
456 bool Refresher::tryReadDatagramByServer(const QHostAddress &address,
457  unsigned short port, QByteArray &packet)
458 {
459  if (!d->bKeepRunning)
460  {
461  return true;
462  }
463  Server *server = findRefreshingServer(address, port);
464  if (server != nullptr)
465  {
466  // Store the state of request read.
467  int response = server->readRefreshQueryResponse(packet);
468  switch (response)
469  {
471  if (server->sendRefreshQuery(d->socket(server)))
472  {
473  // Reset timer
474  ServerRefreshTime refreshOp(server);
475  d->refreshingServers.removeAll(refreshOp);
476  d->refreshingServers.append(refreshOp);
477  break;
478  }
479  response = Server::RESPONSE_BAD;
480  [[gnu::fallthrough]];
481  default:
482  d->refreshingServers.removeAll(ServerRefreshTime(server));
483  server->refreshStops(static_cast<Server::Response>(response));
484  return true;
485 
487  {
488  // Reset timer
489  ServerRefreshTime refreshOp(server);
490  d->refreshingServers.removeAll(refreshOp);
491  d->refreshingServers.append(refreshOp);
492  break;
493  }
494  }
495  }
496  return false;
497 }
498 
499 void Refresher::unregisterMaster(MasterClient *pMaster)
500 {
501  pMaster->disconnect(this);
502  Data::MasterHashtableIt it = d->registeredMasters.find(pMaster);
503  if (it != d->registeredMasters.end())
504  {
505  MasterClientInfo *pMasterInfo = it.value();
506  delete pMasterInfo;
507  d->registeredMasters.erase(it);
508  }
509 }