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