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