123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- /*
- 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>
- #include <QTimer>
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ServerQuery::ServerQuery(QObject *parent) :
- QObject(parent),
- mySocket(new QUdpSocket(this)),
- myPort(27500),
- myActiveFlag(false),
- myCurrentPingIndex(0)
- {
- connect(mySocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError)));
- connect(mySocket, SIGNAL(readyRead()), SLOT(parseServerInfo()));
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ServerQuery::~ServerQuery()
- {
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- bool ServerQuery::query()
- {
- 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;
- }
- myPingTime.restart();
- 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();
- // qDebug() << mySocket->peerAddress() << mySocket->peerPort();
- if(serverData.isEmpty())
- {
- emit error(EmptyResponseError);
- myActiveFlag = false;
- return;
- }
- myPings[myCurrentPingIndex++ & 7] = myPingTime.elapsed();
- 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() const
- {
- quint16 avgPing = 0;
- quint8 divisor;
- if(!myCurrentPingIndex)
- return 0xffff;
- if(myCurrentPingIndex < 8)
- {
- for(int i = 0; i < myCurrentPingIndex; ++i)
- avgPing += myPings[i];
- divisor = myCurrentPingIndex;
- }
- else
- {
- for(int i = 0; i < 8; ++i)
- avgPing += myPings[i];
- divisor = 8;
- }
- return avgPing / divisor;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 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;
- }
|