123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- /*
- GNU General Public License version 3 notice
- Copyright (C) 2012 Mihawk <luiz@netdome.biz>. All rights reserved.
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see < http://www.gnu.org/licenses/ >.
- */
- #include "ServerQuery.h"
- #include <QUdpSocket>
- #include <QHostInfo>
- #include <QStringList>
- #include <QRegExp>
- #include <QTime>
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ServerQuery::ServerQuery(QObject *parent) :
- QObject(parent),
- mySocket(new QUdpSocket(this)),
- myPort(27500),
- myActiveFlag(false)
- {
- connect(mySocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError)));
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ServerQuery::~ServerQuery()
- {
- delete mySocket;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- void ServerQuery::run()
- {
- if(mySocket->hasPendingDatagrams())
- parseServerInfo();
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- bool ServerQuery::query(bool pingServer)
- {
- myActiveFlag = true;
- if(myAddress.isNull() || !myPort)
- {
- emit error(HostLookupError);
- myActiveFlag = false;
- return false;
- }
- if(mySocket->isOpen())
- mySocket->close();
- mySocket->connectToHost(myAddress, myPort);
- if(!mySocket->waitForConnected(100))
- {
- emit error(ConnectionError);
- myActiveFlag = false;
- return false;
- }
- if(mySocket->write("\xff\xff\xff\xffstatus 23\n", 14) == -1)
- {
- emit error(SendError);
- myActiveFlag = false;
- return false;
- }
- if(!mySocket->waitForBytesWritten(300))
- {
- emit error(SendError);
- myActiveFlag = false;
- return false;
- }
- if(pingServer)
- myPing = ping(10); //avg ping
- else
- myPing = 0;
- return true;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- const QHostAddress &ServerQuery::address() const
- {
- return myAddress;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- quint16 ServerQuery::port() const
- {
- return myPort;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- bool ServerQuery::setAddress(const QHostAddress &address, quint16 port)
- {
- myAddress = address;
- myPort = port;
- return true;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- bool ServerQuery::setAddress(const QString &address, quint16 port)
- {
- QHostInfo hi = QHostInfo::fromName(address);
- if(hi.error() != QHostInfo::NoError)
- {
- emit error(HostLookupError);
- return false;
- }
- myAddress = hi.addresses().at(0);
- myPort = port;
- return true;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- void ServerQuery::parseServerInfo()
- {
- QByteArray serverData = mySocket->readAll();
- if(serverData.isEmpty())
- {
- emit error(EmptyResponseError);
- myActiveFlag = false;
- return;
- }
- if(!serverData.startsWith("\xff\xff\xff\xffn"))
- {
- emit error(InvalidResponseError);
- myActiveFlag = false;
- return;
- }
- /* Parse server rules */
- myRules.clear();
- QString infoString(serverData.data()+5);
- QStringList splitInfoString = infoString.split("\n", QString::SkipEmptyParts);
- int i;
- QStringList rules = splitInfoString.at(0).split("\\", QString::SkipEmptyParts);
- if((rules.size() % 2) != 0)
- {
- emit error(InvalidInfoStringError);
- myActiveFlag = false;
- return;
- }
- ServerRule rule;
- for(i = 0; i < rules.size(); i += 2)
- {
- rule.rule = rules.at(i);
- rule.value = rules.at(i+1);
- /* Proxy detection */
- if(rule.rule == "*QTV")
- {
- myIsProxyFlag = true;
- }
- else if(rule.rule == "*version")
- {
- QString value = rule.value;
- if(value.startsWith("qwfwd", Qt::CaseInsensitive) || value.startsWith("QTV", Qt::CaseInsensitive) || value.startsWith("2.91"))
- myIsProxyFlag = true;
- }
- /* Adjust FPS */
- if(rule.rule == "maxfps" && myPing > 0)
- myPing += rule.value.toUInt() * 12 / 77;
- myRules.append(rule);
- }
- /* Parse player info */
- myPlayers.clear();
- Player player;
- QRegExp regex("^([-0-9]+)\\s+([-0-9]+)\\s+([-0-9]+)\\s+([-0-9]+)\\s+\"([^\"]*)\"\\s+\"([^\"]*)\"\\s+([0-9]+)\\s+([0-9]+)(?:\\s+\"([^\"]*)\")?$");
- QStringList playerInfo;
- for(i = 1; i < splitInfoString.size(); i++)
- {
- if(regex.indexIn(splitInfoString.at(i)) == -1)
- {
- emit error(InvalidPlayerInfoError);
- continue;
- }
- playerInfo = regex.capturedTexts();
- player.id = playerInfo.at(1).toInt();
- player.frags = playerInfo.at(2).toInt();
- player.time = playerInfo.at(3).toInt();
- player.ping = playerInfo.at(4).toInt();
- player.name = playerInfo.at(5);
- player.skin = playerInfo.at(6);
- player.topColor = playerInfo.at(7).toInt();
- player.bottomColor = playerInfo.at(8).toInt();
- player.team = playerInfo.at(9);
- if(player.name.startsWith("\\s\\"))
- {
- player.spectator = true;
- player.team = "(spec)";
- player.name = player.name.replace(QRegExp("^\\\\s\\\\"), "");
- if(player.ping < 0)
- player.ping = -player.ping;
- }
- else
- {
- player.spectator = false;
- }
- myPlayers.append(player);
- }
- myActiveFlag = false;
- emit finished();
- return;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- bool ServerQuery::isActive() const
- {
- return myActiveFlag;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- void ServerQuery::socketError(QAbstractSocket::SocketError)
- {
- emit error(UnknownError);
- myActiveFlag = false;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- int ServerQuery::ping(int count, int timeout)
- {
- int pong = 0;
- QTime timer;
- timer.start();
- for(int i = 0; i < count; i++)
- {
- mySocket->write("\xff\xff\xff\xffk\n");
- timer.restart();
- if(!mySocket->waitForReadyRead(timeout))
- {
- pong += 999;
- }
- else
- {
- if(mySocket->readAll() == "l")
- pong += timer.elapsed();
- else
- pong += 999;
- }
- }
- return pong/count;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- PlayerList ServerQuery::playerList() const
- {
- return myPlayers;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ServerRules ServerQuery::serverRules() const
- {
- return myRules;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- QString ServerQuery::serverRuleValue(const QString &ruleName) const
- {
- ServerRule r;
- foreach(r, myRules)
- {
- if(r.rule == ruleName)
- return r.value;
- }
- return QString();
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- char ServerQuery::ourReadableCharsTable[256] = { '.', '_' , '_' , '_' , '_' , '.' , '_' , '_' , '_' , '_' , '\n' , '_' , '\n' , '>' , '.' , '.',
- '[', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '_', '_', '_'
- };
- bool ServerQuery::ourReadableCharsTableInitialized = false;
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- void ServerQuery::fillReadableCharsTable()
- {
- int i;
- for(i = 32; i < 127; i++)
- ourReadableCharsTable[i] = ourReadableCharsTable[128 + i] = i;
- ourReadableCharsTable[127] = ourReadableCharsTable[128 + 127] = '_';
- for(i = 0; i < 32; i++)
- ourReadableCharsTable[128 + i] = ourReadableCharsTable[i];
- ourReadableCharsTable[128] = '_';
- ourReadableCharsTable[10 + 128] = '_';
- ourReadableCharsTable[12 + 128] = '_';
- ourReadableCharsTableInitialized = true;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- QString ServerQuery::convertNameFun(const QString &name)
- {
- if(!ourReadableCharsTableInitialized)
- fillReadableCharsTable();
- QString stripped;
- for(int i = 0; i < name.length(); i++)
- stripped.append(QChar(ourReadableCharsTable[(unsigned char)name.at(i).toAscii()] & 127));
- return stripped;
- }
|