12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078 |
- /*
- 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 "QWClient.h"
- #include "QWClientPrivate.h"
- #include "QWPack.h"
- #include "QWTables.h"
- #include <QUdpSocket>
- #include <QTime>
- #include <QBuffer>
- #include <QFile>
- #include <QDir>
- #include <QRegExp>
- #include <QStringList>
- #include <QFileInfoList>
- #include <QHostInfo>
- #include <QCryptographicHash>
- #include <QCoreApplication>
- #include <QtEndian>
- #include <QDebug>
- const char* QWClientPrivate::ClientName = "libqwclient";
- const char* QWClientPrivate::ClientVersion = "0.1";
- QWClientPrivate::QWClientPrivate(QWClient* client):
- myClient(client),
- mySocket(new QUdpSocket),
- myTime(new QTime),
- myLastServerReplyTime(new QTime),
- myDownload(new QFile),
- myClientName(ClientName),
- myClientVersion(ClientVersion),
- myState(QWClient::DisconnectedState),
- myGameDir("qw"),
- myQuakeDir(QCoreApplication::applicationDirPath()),
- myPing(666),
- myRate(3000),
- myTopColor(0),
- myBottomColor(0),
- myName(ClientName),
- mySpectatorFlag(true),
- myTeam("lqwc")
- {
- /* Setup IO streams */
- myInBuffer.setBuffer(&myInData);
- myInStream.setDevice(&myInBuffer);
- myInStream.setByteOrder(QDataStream::LittleEndian);
- myInBuffer.open(QIODevice::ReadOnly);
- myUnreliableOutBuffer.setBuffer(&myUnreliableOutData);
- myUnreliableOutStream.setDevice(&myUnreliableOutBuffer);
- myUnreliableOutBuffer.open(QIODevice::WriteOnly);
- myUnreliableOutStream.setByteOrder(QDataStream::LittleEndian);
- myReliableOutBuffer.setBuffer(&myReliableOutData);
- myReliableOutStream.setDevice(&myReliableOutBuffer);
- myReliableOutBuffer.open(QIODevice::WriteOnly);
- myReliableOutStream.setByteOrder(QDataStream::LittleEndian);
- myOutBuffer.setBuffer(&myOutData);
- myOutStream.setDevice(&myOutBuffer);
- myOutBuffer.open(QIODevice::ReadWrite);
- myOutStream.setByteOrder(QDataStream::LittleEndian);
- reloadPackFiles();
- }
- void QWClientPrivate::setPing(quint16 ping)
- {
- myPing = qBound<quint16>(13, ping, 999);
- }
- void QWClientPrivate::reconnect()
- {
- disconnect();
- connect(myHost.toString().toLatin1().data(), myPort);
- }
- void QWClientPrivate::setRate(quint16 rate)
- {
- myRate = qBound<quint16>(2500, rate, 30000);
- if(myState != QWClient::ConnectedState)
- return;
- sendCmd("setinfo \"rate\" \"" + QString::number(rate) + "\"");
- }
- void QWClientPrivate::setSpectator(bool spectate)
- {
- if(myState == QWClient::ConnectedState)
- {
- if(spectate && !mySpectatorFlag)
- observe();
- else if(!spectate && mySpectatorFlag)
- join();
- return;
- }
- mySpectatorFlag = spectate;
- }
- void QWClientPrivate::setPassword(const QString &password)
- {
- myPassword = password;
- }
- void QWClientPrivate::setQuakeFolder(const QString &path)
- {
- myQuakeDir = path;
- QDir dir(myQuakeDir);
- dir.mkpath(myGameDir);
- reloadPackFiles();
- }
- void QWClientPrivate::join()
- {
- mySpectatorFlag = false;
- if(myState != QWClient::ConnectedState)
- return;
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, "setinfo \"spectator\" \"\"");
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, "join");
- }
- void QWClientPrivate::observe()
- {
- mySpectatorFlag = true;
- if(myState != QWClient::ConnectedState)
- return;
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, "setinfo \"spectator\" \"" + QString::number(mySpectatorFlag) + "\"");
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, "observe");
- }
- const QString& QWClientPrivate::gameDir() const
- {
- return myGameDir;
- }
- const QString& QWClientPrivate::quakeDir() const
- {
- return myQuakeDir;
- }
- void QWClientPrivate::setColor(quint8 bottom, quint8 top)
- {
- myBottomColor = bottom;
- myTopColor = top;
- if(myState != QWClient::ConnectedState)
- return;
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, "setinfo \"bottomcolor\" \"" + QString::number(myBottomColor) + "\"");
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, "setinfo \"topcolor\" \"" + QString::number(myTopColor) + "\"");
- }
- void QWClientPrivate::setName(const char *name)
- {
- myName = QString(name);
- if(myState == QWClient::ConnectedState)
- {
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, "setinfo \"name\" \"" + myName + "\"");
- }
- }
- void QWClientPrivate::setTeam(const char *team)
- {
- myTeam = QString(team);
- if(myState == QWClient::ConnectedState)
- {
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, "setinfo \"name\" \"" + myTeam + "\"");
- }
- }
- void QWClientPrivate::sendCmd(const QString &cmd)
- {
- if(myState != QWClient::ConnectedState)
- return;
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, cmd);
- }
- QWClientPrivate::~QWClientPrivate()
- {
- delete myTime;
- delete myLastServerReplyTime;
- delete mySocket;
- delete myDownload;
- }
- static QRegExp packRegex("pak[0-9]+\\.pak", Qt::CaseInsensitive);
- void QWClientPrivate::reloadPackFiles()
- {
- for(int i = 0; i < myPacks.size(); ++i)
- delete myPacks.at(i);
- myPacks.clear();
- QDir gameDir(myQuakeDir + "/" + myGameDir);
- if(!gameDir.isReadable())
- return;
- QFileInfoList files = gameDir.entryInfoList(QStringList("*.pak"), QDir::Files);
- for(int i = 0; i < files.size(); ++i)
- {
- if(packRegex.indexIn(files.at(i).fileName()) != -1)
- {
- QWPack* pack = new QWPack();
- if(!pack->load(files.at(i).absoluteFilePath()))
- {
- delete pack;
- continue;
- }
- myPacks.push_back(pack);
- }
- }
- }
- void QWClientPrivate::run()
- {
- /* Read and parse packets sent by the server */
- readPackets();
- /* Send something to the server to keep the connection alive */
- sendToServer();
- }
- void QWClientPrivate::sendToServer(bool dontWait)
- {
- /* Check for resend */
- if(myState == QWClient::ConnectingState)
- {
- if(myTime->elapsed() >= 5000)
- {
- sendConnectionless("getchallenge\n");
- myClient->onChallenge();
- myTime->restart();
- }
- return;
- }
- /* Go full speed when connecting or downloading a new map */
- if(!dontWait && (myState != QWClient::ConnectedState || myDownload->isOpen() ? 12 : myPing) > myTime->elapsed())
- return;
- sendMovement(); //send some movement every frame
- bool sendReliable = false;
- if(myIncomingAck > myLastRealiableSeq && myIncomingAckReliableFlag != myOutgoingSeqReliableFlag)
- sendReliable = true;
- if(!myReliableData.size() && myReliableOutData.size())
- {
- myReliableData = myReliableOutData;
- myOutgoingSeqReliableFlag ^= 1;
- sendReliable = true;
- myReliableOutData.clear();
- myReliableOutStream.device()->seek(0);
- }
- /* Write packet header */
- myOutData.clear();
- myOutStream.device()->seek(0);
- myOutStream << (myOutgoingSeq | (sendReliable << 31));
- myOutStream << (myIncomingSeq | (myIncomingSeqReliableFlag << 31));
- myOutStream << myQPort;
- myOutgoingSeq++;
- /* Write reliable buffer first */
- if(sendReliable)
- {
- myOutData.append(myReliableData);
- myLastRealiableSeq = myOutgoingSeq;
- }
- /* Unreliable part afterwards */
- if(myOutData.size() + myUnreliableOutData.size() < MAX_MSGLEN && myUnreliableOutData.size())
- {
- myOutData.append(myUnreliableOutData);
- myUnreliableOutData.clear();
- myUnreliableOutStream.device()->seek(0);
- }
- /* Finally send the packet */
- mySocket->write(myOutData);
- mySocket->waitForBytesWritten();
- myOutData.clear();
- myOutStream.device()->seek(0);
- myTime->restart();
- }
- void QWClientPrivate::readPackets()
- {
- if(!mySocket->isOpen())
- return;
- if(!mySocket->hasPendingDatagrams())
- {
- if(myLastServerReplyTime->secsTo(QTime::currentTime()) >= 30)
- {
- myClient->onError("Client Timed Out.");
- disconnect();
- return;
- }
- return;
- }
- *myLastServerReplyTime = QTime::currentTime();
- myInData.resize(mySocket->pendingDatagramSize());
- // myInData = mySocket->readAll();
- mySocket->readDatagram(myInData.data(), mySocket->pendingDatagramSize());
- myInStream.device()->seek(0);
- quint32 seq;
- myInStream >> seq;
- if(seq == 0xffffffff)
- parseConnectionless();
- else
- parseServerMessage();
- }
- void QWClientPrivate::parseConnectionless()
- {
- quint8 c;
- c = readByte();
- switch(c)
- {
- case S2C_CHALLENGE:
- {
- QString challenge;
- QString connString;
- challenge = readString();
- connString.append("connect " + QString::number(PROTOCOL_VERSION) + " " + QString::number(myQPort) + " " + challenge);
- connString.append(" \"\\rate\\" + QString::number(myRate));
- if(!myPassword.isEmpty())
- connString.append("\\password\\" + myPassword);
- connString.append("\\msg\\1\\noaim\\1\\topcolor\\" + QString::number(myTopColor) + "\\bottomcolor\\" + QString::number(myBottomColor) + "\\w_switch\\2\\b_switch\\2\\*client\\" + myClientName);
- connString.append(" " + myClientVersion + "\\name\\" + myName + "\\team\\" + myTeam + "\\spectator\\" + (mySpectatorFlag ? "1" : "0") + "\\pmodel\\33168\\emodel\\6967\\*z_ext\\383\"");
- myInStream.device()->seek(0);
- sendConnectionless(connString.toLatin1());
- myClient->onConnection();
- }
- break;
- case S2C_CONNECTION:
- {
- myState = QWClient::ConnectedState;
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, "new");
- myClient->onConnected();
- }
- break;
- case A2C_PRINT:
- myClient->onOOBPrint(readString().toLatin1().data());
- break;
- case A2C_CLIENT_COMMAND:
- myClient->onOOBCommand(readString().toLatin1().data());
- break;
- case A2A_PING:
- sendConnectionless(QString(QChar(A2A_ACK)).toLatin1());
- break;
- case A2A_ACK:
- // qDebug("Ack");
- break;
- case A2A_NACK:
- // qDebug("Nack");
- break;
- case A2A_ECHO:
- myClient->onOOBEcho(readString().toLatin1().data());
- break;
- }
- }
- bool QWClientPrivate::fileExists(const QString &filename)
- {
- /* Search on the gamedir first */
- if(QFile::exists(myQuakeDir + "/" + myGameDir + "/" + filename))
- return true;
- /* Search quake pak files next */
- for(int i = 0; i < myPacks.size(); ++i)
- {
- if(myPacks[i]->exists(filename))
- return true;
- }
- return false;
- }
- bool QWClientPrivate::readFile(const QString &filename, char **data, quint64 *len)
- {
- /* Try disk */
- QFile file(myQuakeDir + "/" + myGameDir + "/" + filename);
- *len = 0;
- if(file.open(QIODevice::ReadOnly))
- {
- *len = file.size();
- *data = new char[*len];
- file.read(*data, *len);
- return true;
- }
- /* Try packs */
- for(int i = 0; i < myPacks.size(); ++i)
- {
- QWPack* pack = myPacks.at(i);
- if(!pack->exists(filename))
- continue;
- pack->read(filename, data, len);
- return true;
- }
- return false;
- }
- //int bitCounts[32]; /// just for protocol profiling
- void QWClientPrivate::parseDelta(entityState_t *from, entityState_t *to, int bits)
- {
- int i;
- int morebits;
- // set everything to the state we are delta'ing from
- *to = *from;
- to->number = bits & 511;
- bits &= ~511;
- if(bits & U_MOREBITS)
- { // read in the low order bits
- i = readByte();
- bits |= i;
- }
- if(bits & U_FTE_EVENMORE && myFTEProtocolExtensions) {
- morebits = readByte();
- if (morebits & U_FTE_YETMORE)
- morebits |= readByte() << 8;
- } else {
- morebits = 0;
- }
- // // count the bits for net profiling
- // for (i=0 ; i<16 ; i++)
- // if (bits&(1<<i))
- // bitCounts[i]++;
- to->flags = bits;
- if(bits & U_MODEL)
- to->modelindex = readByte();
- if(bits & U_FRAME)
- to->frame = readByte();
- if(bits & U_COLORMAP)
- to->colormap = readByte();
- if(bits & U_SKIN)
- to->skinnum = readByte();
- if(bits & U_EFFECTS)
- to->effects = readByte();
- if(bits & U_ORIGIN1)
- to->origin[0] = readCoord();
- if(bits & U_ANGLE1)
- to->angles[0] = readAngle();
- if(bits & U_ORIGIN2)
- to->origin[1] = readCoord();
- if(bits & U_ANGLE2)
- to->angles[1] = readAngle();
- if(bits & U_ORIGIN3)
- to->origin[2] = readCoord();
- if(bits & U_ANGLE3)
- to->angles[2] = readAngle();
- if(bits & U_SOLID)
- {}
- if(morebits & U_FTE_TRANS && myFTEProtocolExtensions & FTE_PEXT_TRANS)
- readByte();
- if(morebits & U_FTE_ENTITYDBL)
- {}
- if(morebits & U_FTE_ENTITYDBL2)
- {}
- if(morebits & U_FTE_MODELDBL)
- {}
- }
- //========================================================================
- // Parser
- quint32 QWClientPrivate::littleLong(quint32 l)
- {
- if(QSysInfo::ByteOrder == QSysInfo::LittleEndian)
- return qToLittleEndian<quint32>(l);
- else
- return l;
- }
- quint16 QWClientPrivate::littleShort(quint16 s)
- {
- if(QSysInfo::ByteOrder == QSysInfo::LittleEndian)
- return qToLittleEndian<quint16>(s);
- else
- return s;
- }
- float QWClientPrivate::littleFloat(float f)
- {
- if(QSysInfo::ByteOrder == QSysInfo::LittleEndian)
- return qToLittleEndian<float>(f);
- else
- return f;
- }
- bool QWClientPrivate::checkForBadRead(quint8 typeSize)
- {
- if(myInStream.device()->pos()+typeSize > myInStream.device()->size())
- {
- myBadReadFlag = true;
- return true;
- }
- return false;
- }
- float QWClientPrivate::readAngle()
- {
- if(checkForBadRead(1))
- return -1;
- quint8 angle;
- myInStream >> angle;
- return angle * (360.0f/256);
- }
- float QWClientPrivate::readAngle16()
- {
- if(checkForBadRead(2))
- return -1;
- quint16 angle;
- myInStream >> angle;
- return angle * (360.0f/65536);
- }
- float QWClientPrivate::readCoord()
- {
- if(checkForBadRead(2))
- return -1;
- quint16 coord;
- myInStream >> coord;
- return coord * (1.0f/8);
- }
- quint8 QWClientPrivate::readByte()
- {
- if(checkForBadRead(1))
- return 0xff;
- quint8 byte;
- myInStream >> byte;
- return byte;
- }
- float QWClientPrivate::readFloat()
- {
- union
- {
- quint8 b[4];
- int l;
- float f;
- } d;
- d.b[0] = readByte();
- d.b[1] = readByte();
- d.b[2] = readByte();
- d.b[3] = readByte();
- return littleFloat(d.f);
- }
- const QString QWClientPrivate::readString()
- {
- QString str;
- quint8 c;
- c = readByte();
- while(c && c != 255) //bad read or null terminator
- {
- str.append(c);
- c = readByte();
- }
- return str;
- }
- qint32 QWClientPrivate::readLong()
- {
- if(checkForBadRead(4))
- return -1;
- quint32 l;
- myInStream >> l;
- return l;
- }
- qint16 QWClientPrivate::readShort()
- {
- if(checkForBadRead(2))
- return -1;
- qint16 s;
- myInStream >> s;
- return s;
- }
- //========================================================================
- void QWClientPrivate::parseSvcNoop()
- {
- }
- void QWClientPrivate::parseSvcDisconnect()
- {
- mySocket->close();
- myState = QWClient::DisconnectedState;
- myClient->onDisconnect();
- }
- void QWClientPrivate::parseSvcPrint()
- {
- quint8 level = readByte();
- QString msg = readString();
- myClient->onPrint(level, msg.toLatin1().data());
- }
- void QWClientPrivate::parseSvcCenterPrint()
- {
- myClient->onCenterPrint(readString().toLatin1().data());
- }
- void QWClientPrivate::parseSvcStuffText()
- {
- QStringList commands = readString().split("\n");
- for(int i = 0; i < commands.size(); ++i)
- {
- QString cmd = commands.at(i);
- if(cmd == "reconnect" || cmd == "cmd new")
- {
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, "new");
- }
- else if(cmd == "cmd pext")
- {
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, "pext 0x00000000 0x00000000 0x00000000");
- }
- else if(cmd.startsWith("cmd spawn"))
- {
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, cmd.section(' ', 1));
- }
- else if(cmd.startsWith("cmd prespawn"))
- {
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, cmd.section(' ', 1));
- }
- else if(cmd == "skins")
- {
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, QString("begin " + QString::number(myServerCount)));
- }
- else if(cmd.startsWith("packet"))
- {
- QRegExp regex("\"(.+)\"");
- int pos = regex.indexIn(cmd);
- if(pos != -1)
- sendConnectionless(regex.capturedTexts().at(1).toLatin1());
- }
- myClient->onStuffedCmd(cmd.toLatin1().data());
- }
- }
- void QWClientPrivate::parseSvcDamage()
- {
- int armor = readByte();
- int blood = readByte();
- myClient->onDamage(armor, blood);
- readCoord();
- readCoord();
- readCoord();
- }
- void QWClientPrivate::parseSvcServerData()
- {
- while (1) {
- myProtocolVersion = readLong();
- if(myProtocolVersion == PROTOCOL_VERSION_FTE || myProtocolVersion == PROTOCOL_VERSION_FTE2)
- {
- myFTEProtocolExtensions = readLong();
- continue;
- }
- if(myProtocolVersion == PROTOCOL_VERSION)
- break;
- }
- myServerCount = readLong();
- myGameDir = readString();
- QDir quakeDir(myQuakeDir);
- if(!quakeDir.cd(myGameDir))
- quakeDir.mkdir(myGameDir);
- quint8 playerNum = readByte(); //playernum
- if(playerNum & 0x80)
- {
- mySpectatorFlag = true;
- playerNum &= ~0x80;
- }
- else
- mySpectatorFlag = false;
- QString lvlName = readString();
- float a = readFloat();
- float b = readFloat();
- float c = readFloat();
- float d = readFloat();
- float e = readFloat();
- float f = readFloat();
- float g = readFloat();
- float h = readFloat();
- float i = readFloat();
- float j = readFloat();
- myClient->onLevelChanged(
- playerNum,
- lvlName.toLatin1().data(),
- a,b,c,d,e,f,g,h,i,j
- );
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, QString("soundlist " + QString::number(myServerCount) + " 0"));
- if(myDownload->isOpen())
- myDownload->close();
- }
- void QWClientPrivate::parseSvcSetAngle()
- {
- readAngle();
- readAngle();
- readAngle();
- }
- void QWClientPrivate::parseSvcLightStyle()
- {
- readByte();
- readString();
- }
- void QWClientPrivate::parseSvcSound()
- {
- quint16 channel;
- channel = readShort();
- if (channel & SND_VOLUME)
- readByte();
- if (channel & SND_ATTENUATION)
- readByte();
- myClient->onPlaySound(readByte());
- readCoord();
- readCoord();
- readCoord();
- }
- void QWClientPrivate::parseSvcStopSound()
- {
- readShort();
- }
- void QWClientPrivate::parseSvcUpdateFrags()
- {
- //printf("svc_updatefrags\n");
- quint8 playerNum = readByte();
- quint16 frags = readShort();
- myClient->onUpdateFrags(playerNum, frags);
- }
- void QWClientPrivate::parseSvcUpdatePing()
- {
- quint8 playerNum = readByte();
- quint16 ping = readShort();
- myClient->onUpdatePing(playerNum, ping);
- }
- void QWClientPrivate::parseSvcUpdatePL()
- {
- quint8 playerNum = readByte();
- quint8 pl = readByte();
- myClient->onUpdatePL(playerNum, pl);
- }
- void QWClientPrivate::parseSvcUpdateEnterTime()
- {
- readByte();
- readFloat();
- }
- void QWClientPrivate::parseSvcSpawnBaseLine()
- {
- //printf("svc_spawnbaseline\n");
- readShort();
- readByte();
- readByte();
- readByte();
- readByte();
- for(int i = 0; i < 3; i++)
- {
- readCoord();
- readAngle();
- }
- }
- void QWClientPrivate::parseSvcSpawnStatic()
- {
- //printf("svc_spawnstatic\n");
- //rawReadShort();
- readByte();
- readByte();
- readByte();
- readByte();
- for(int i = 0; i < 3; i++)
- {
- readCoord();
- readAngle();
- }
- }
- void QWClientPrivate::parseSvcTempEntity()
- {
- //printf("svc_temp_entity\n");
- quint8 c = readByte();
- bool parsed = false;
- switch(c)
- {
- case TE_LIGHTNING1:
- case TE_LIGHTNING2:
- case TE_LIGHTNING3:
- readShort();
- readCoord();
- readCoord();
- readCoord();
- readCoord();
- readCoord();
- readCoord();
- parsed = true;
- break;
- case TE_GUNSHOT:
- readByte();
- readCoord();
- readCoord();
- readCoord();
- parsed = true;
- break;
- case TE_BLOOD:
- readByte();
- readCoord();
- readCoord();
- readCoord();
- parsed = true;
- break;
- case TE_LIGHTNINGBLOOD:
- readCoord();
- readCoord();
- readCoord();
- parsed = true;
- break;
- }
- if(!parsed)
- {
- readCoord();
- readCoord();
- readCoord();
- }
- }
- void QWClientPrivate::parseSvcUpdateStat()
- {
- readByte();
- readByte();
- }
- void QWClientPrivate::parseSvcUpdateStatLong()
- {
- //printf("svc_updatestatlong\n");
- readByte();
- readLong();
- }
- void QWClientPrivate::parseSvcSpawnStaticSound()
- {
- //printf("svc_spawnstaticsound\n");
- readCoord();
- readCoord();
- readCoord();
- readByte();
- readByte();
- readByte();
- }
- void QWClientPrivate::parseSvcCDTrack()
- {
- readByte();
- }
- void QWClientPrivate::parseSvcIntermission()
- {
- //printf("svc_intermission\n");
- readCoord();
- readCoord();
- readCoord();
- readAngle();
- readAngle();
- readAngle();
- }
- void QWClientPrivate::parseSvcFinale()
- {
- //printf("svc_finale\n");
- readString();
- }
- void QWClientPrivate::parseSvcSellScreen()
- {
- }
- void QWClientPrivate::parseSvcSmallKick()
- {
- }
- void QWClientPrivate::parseSvcBigKick()
- {
- }
- void QWClientPrivate::parseSvcMuzzleFlash()
- {
- //printf("svc_muzzleflash\n");
- readShort();
- }
- void QWClientPrivate::parseSvcUpdateUserinfo()
- {
- quint8 playerNum = readByte();
- quint32 userID = readLong();
- QString info = readString();
- myClient->onUpdateUserInfo(playerNum, userID, info.toLatin1().data());
- }
- void QWClientPrivate::parseSvcSetinfo()
- {
- //printf("svc_setinfo\n");
- QString key, value;
- int playerNum = readByte();
- key = readString();
- value = readString();
- //that must be done
- if(key == "rate")
- {
- myRate = value.toUInt();
- sendCmd("setinfo \"rate\" \"" + value + "\"");
- }
- myClient->onSetInfo(playerNum, key.toLatin1().data(), value.toLatin1().data());
- }
- void QWClientPrivate::parseSvcServerinfo()
- {
- //printf("svc_serverinfo\n");
- QString key, value;
- key = readString();
- value = readString();
- myClient->onServerInfo(key.toLatin1().data(), value.toLatin1().data());
- }
- void QWClientPrivate::startDownload(const QString &fileName)
- {
- if (myDownload->isOpen()) {
- myDownload->close();
- myDownload->remove();
- }
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, QString("download " + fileName));
- if (fileName.contains('/')) {
- QString path = fileName.mid(0, (fileName.size() - fileName.section('/', -1).size() - 1));
- QDir quakeDir(myGameDir);
- quakeDir.mkpath(myQuakeDir + "/" + myGameDir + "/" + path);
- }
- //generate disk path
- int tmpNo = 0;
- QString tmpFileName = myQuakeDir + "/" + myGameDir + "/" + fileName + QString::number(tmpNo) + ".tmp";
- while (myDownload->exists(tmpFileName)) {
- tmpNo++;
- tmpFileName = myQuakeDir + "/" + myGameDir + "/" + fileName + QString::number(tmpNo) + ".tmp";
- }
- myDownload->setFileName(tmpFileName);
- myDownload->open(QIODevice::WriteOnly);
- myClient->onDownloadStarted(fileName.toLatin1().data());
- }
- #if 0
- void QWClientPrivate::parseChunkedDownload()
- {
- QString svname;
- int totalsize;
- int chunknum;
- char data[DLBLOCKSIZE];
- chunknum = readLong();
- if(chunknum < 0)
- {
- totalsize = readLong();
- svname = readString();
- if(myDownload->isOpen())
- {
- // Ensure FILE is closed
- if(totalsize != -3) // -3 = dl stopped, so this known issue, do not warn
- qDebug("cls.download shouldn't have been set\n");
- myDownload->close();
- }
- if(totalsize < 0)
- {
- switch(totalsize)
- {
- case -3: qDebug("Server cancel downloading file %s\n", svname.toLatin1().data()); break;
- case -2: qDebug("Server permissions deny downloading file %s\n", svname.toLatin1().data()); break;
- default: qDebug("Couldn't find file %s on the server\n", svname.toLatin1().data()); break;
- }
- myDownload->close();
- return;
- }
- if(cls.downloadmethod == DL_QWCHUNKS)
- {
- qDebug("Received second download - \"%s\"\n", svname.toLatin1().data());
- disconnect();
- }
- if(!myDownload->isOpen())
- {
- qDebug("Failed to open %s", myDownload->fileName());
- return;
- }
- cls.downloadmethod = DL_QWCHUNKS;
- cls.downloadpercent = 0;
- chunked_download_number++;
- downloadsize = totalsize;
- firstblock = 0;
- receivedbytes = 0;
- blockcycle = -1; //so it requests 0 first. :)
- memset(recievedblock, 0, sizeof(recievedblock));
- return;
- }
- MSG_ReadData(data, DLBLOCKSIZE);
- if (myDownload->isOpen())
- return;
- if (cls.downloadmethod != DL_QWCHUNKS)
- Host_Error("cls.downloadmethod != DL_QWCHUNKS\n");
- if(chunknum < firstblock)
- return;
- if(chunknum - firstblock >= MAXBLOCKS)
- return;
- if(recievedblock[chunknum&(MAXBLOCKS-1)])
- return;
- receivedbytes += DLBLOCKSIZE;
- recievedblock[chunknum&(MAXBLOCKS-1)] = true;
- while(recievedblock[firstblock&(MAXBLOCKS-1)])
- {
- recievedblock[firstblock&(MAXBLOCKS-1)] = false;
- firstblock++;
- }
- fseek(cls.download, chunknum * DLBLOCKSIZE, SEEK_SET);
- if (downloadsize - chunknum * DLBLOCKSIZE < DLBLOCKSIZE) //final block is actually meant to be smaller than we recieve.
- fwrite(data, 1, downloadsize - chunknum * DLBLOCKSIZE, cls.download);
- else
- fwrite(data, 1, DLBLOCKSIZE, cls.download);
- cls.downloadpercent = receivedbytes/(float)downloadsize*100;
- tm = Sys_DoubleTime() - cls.downloadstarttime; // how long we dl-ing
- cls.downloadrate = (tm ? receivedbytes / 1024 / tm : 0); // some average dl speed in KB/s
- }
- #endif
- void QWClientPrivate::parseSvcDownload()
- {
- // if(myFTEProtocolExtensions & FTE_PEXT_CHUNKEDDOWNLOADS)
- // {
- // parseChunkedDownload();
- // return;
- // }
- qint16 size;
- quint8 percent;
- size = readShort();
- percent = readByte();
- if(size == -1)
- {
- //file not found
- disconnect();
- return;
- }
- if(!myDownload->isOpen())
- {
- myInStream.device()->seek(myInStream.device()->pos() + size);
- return;
- }
- char* temp = new char[size];
- myInStream.readRawData(temp, size);
- myDownload->write(temp, size);
- myDownload->waitForBytesWritten(1000);
- delete[] temp;
- myClient->onDownloadProgress(percent);
- if(percent != 100)
- {
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, "nextdl");
- }
- else
- {
- myDownload->close();
- QString normalFileName = myDownload->fileName().left(myDownload->fileName().length()-5); //strip #.tmp
- if(!myDownload->rename(normalFileName))
- {
- QFile::remove(normalFileName);
- myDownload->rename(normalFileName); //now it should succeed
- }
- myClient->onDownloadFinished();
- preSpawn(mapChecksum(myMapName));
- }
- }
- void QWClientPrivate::readUserDeltaCmd(userCmd_t *from, userCmd_t *move)
- {
- quint8 bits;
- memcpy(move, from, sizeof(*move));
- bits = readByte();
- // read current angles
- if(bits & CM_ANGLE1)
- move->angles[0] = readAngle16();
- if(bits & CM_ANGLE2)
- move->angles[1] = readAngle16();
- if(bits & CM_ANGLE3)
- move->angles[2] = readAngle16();
- // read movement
- if(bits & CM_FORWARD)
- move->forwardmove = readShort();
- if(bits & CM_SIDE)
- move->sidemove = readShort();
- if(bits & CM_UP)
- move->upmove = readShort();
- // read buttons
- if(bits & CM_BUTTONS)
- move->buttons = readByte();
- if(bits & CM_IMPULSE)
- move->impulse = readByte();
- // read time to run command
- move->msec = readByte();
- }
- void QWClientPrivate::parseSvcPlayerinfo()
- {
- quint16 flags;
- userCmd_t from, move;
- memset(&from, 0, sizeof(userCmd_t));
- memset(&move, 0, sizeof(userCmd_t));
- int playerNum = readByte();
- flags = readShort();
- float x = readCoord();
- float y = readCoord();
- float z = readCoord();
- myClient->onPlayerInfo(playerNum, x, y, z);
- readByte();
- if(flags & PF_MSEC)
- readByte();
- if(flags & PF_COMMAND)
- readUserDeltaCmd(&from, &move);
- for(int i = 0; i < 3; i++)
- {
- if(flags & (PF_VELOCITY1<<i))
- readShort();
- }
- if(flags & PF_MODEL)
- readByte();
- if(flags & PF_SKINNUM)
- readByte();
- if(flags & PF_EFFECTS)
- readByte();
- if(flags & PF_WEAPONFRAME)
- readByte();
- if(flags & PF_TRANS_Z && myFTEProtocolExtensions & FTE_PEXT_TRANS)
- readByte();
- }
- void QWClientPrivate::parseSvcNails()
- {
- //printf("svc_nails\n");
- quint8 c = readByte();
- for(int i = 0; i < c; i++)
- {
- for(int j = 0; j < 6; j++)
- readByte();
- }
- }
- void QWClientPrivate::parseSvcChokeCount()
- {
- readByte();
- }
- void QWClientPrivate::parseSvcModellist()
- {
- quint8 i = readByte();
- bool firstLoop = true;
- for(;;)
- {
- QString s = readString();
- if(s.isEmpty())
- break;
- if(!i && firstLoop)
- {
- myMapName = s;
- firstLoop = false;
- }
- myClient->onModelListFile(s.toLatin1().data());
- }
- i = readByte();
- if(i)
- {
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, QString("modellist " + QString::number(myServerCount) + " " + QString::number(i)));
- return;
- }
- if(!fileExists(myMapName) && !QWTables::getOriginalMapChecksum(myMapName))
- {
- startDownload(myMapName);
- return;
- }
- preSpawn(mapChecksum(myMapName));
- }
- void QWClientPrivate::parseSvcSoundlist()
- {
- quint8 i;
- i = readByte();
- for(;;)
- {
- QString s = readString();
- if(s.isEmpty())
- break;
- myClient->onSoundListFile(s.toLatin1().data());
- }
- i = readByte();
- if(i)
- {
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, QString("soundlist " + QString::number(myServerCount) + " " + QString::number(i)));
- return;
- }
- //continue login
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, QString("modellist " + QString::number(myServerCount) + " 0"));
- }
- void QWClientPrivate::parseSvcPacketEntities()
- {
- int word;
- entityState_t olde, newe;
- memset(&olde, 0, sizeof(olde));
- while (1)
- {
- word = (unsigned short)readShort();
- if (!word)
- break; // done
- parseDelta(&olde, &newe, word);
- }
- }
- void QWClientPrivate::parseSvcDeltaPacketEntities()
- {
- parseSvcPacketEntities();
- }
- void QWClientPrivate::parseSvcMaxSpeed()
- {
- myClient->onMaxSpeedChange(readFloat());
- }
- void QWClientPrivate::parseSvcEntGravity()
- {
- myClient->onEntGravityChange(readFloat());
- }
- void QWClientPrivate::parseSvcSetPause()
- {
- myClient->onSetPause(readByte());
- }
- void QWClientPrivate::parseSvcNails2()
- {
- quint8 c = readByte();
- for(int i = 0; i < c; i++)
- {
- readByte();
- for(int j = 0; j < 6; j++)
- readByte();
- }
- }
- void QWClientPrivate::parseSvcFTEModellistShort()
- {
- quint16 i = readShort();
- bool firstLoop = true;
- for(;;)
- {
- QString s;
- s = readString();
- if(s.isEmpty())
- break;
- if(!i && firstLoop)
- {
- myMapName = s;
- firstLoop = false;
- }
- myClient->onModelListFile(s.toLatin1().data());
- }
- i = readByte();
- if(i)
- {
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, QString("modellist " + QString::number(myServerCount) + " " + QString::number(i)));
- return;
- }
- if(!fileExists(myMapName) && !QWTables::getOriginalMapChecksum(myMapName))
- {
- startDownload(myMapName);
- return;
- }
- preSpawn(mapChecksum(myMapName));
- }
- void QWClientPrivate::parseSvcFTESpawnBaseline2()
- {
- entityState_t nullst, es;
- if(!(myFTEProtocolExtensions & FTE_PEXT_SPAWNSTATIC2))
- {
- myClient->onError("illegible server message\nsvc_fte_spawnbaseline2 without FTE_PEXT_SPAWNSTATIC2\n");
- disconnect();
- return;
- }
- memset(&nullst, 0, sizeof (entityState_t));
- memset(&es, 0, sizeof (entityState_t));
- parseDelta(&nullst, &es, readShort());
- }
- void QWClientPrivate::parseSvcQizmoVoice()
- {
- // Read the two-byte header.
- readByte();
- readByte();
- // 32 bytes of voice data follow
- for(int i = 0; i < 32; i++)
- readByte();
- }
- void QWClientPrivate::parseSvcFTEVoiceChat()
- {
- readByte();
- readByte();
- readByte();
- quint16 i = readShort();
- myInStream.device()->seek(myInStream.device()->pos() + i);
- }
- void QWClientPrivate::parseSvcFTESpawnStatic2()
- {
- if(myFTEProtocolExtensions & FTE_PEXT_SPAWNSTATIC2)
- {
- entityState_t from, to;
- memset(&from, 0, sizeof(entityState_t));
- memset(&to, 0, sizeof(entityState_t));
- parseDelta(&from, &to, readShort());
- }
- }
- void QWClientPrivate::parseSvcKilledMonster()
- {
- }
- void QWClientPrivate::parseSvcFoundSecret()
- {
- }
- void QWClientPrivate::parseSvcUpdateName()
- {
- readByte();
- readString();
- }
- void QWClientPrivate::parseSvcUpdateColors()
- {
- readByte();
- readByte();
- }
- void QWClientPrivate::parseSvcTime()
- {
- readFloat();
- }
- void QWClientPrivate::parseSvcSignonNum()
- {
- readByte();
- }
- void QWClientPrivate::parseSvcParticle()
- {
- for(int i = 0; i < 3; ++i)
- readCoord();
- for(int i = 0; i < 3; ++i)
- readByte();
- readByte();
- readByte();
- }
- void QWClientPrivate::parseSvcClientData()
- {
- qDebug() << "WHY?!?!?!?!?!?! WHY ME BEING CALLED!";
- }
- void QWClientPrivate::parseSvcVersion()
- {
- }
- void QWClientPrivate::preSpawn(int mapChecksum)
- {
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, "setinfo pmodel 33168");
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, "setinfo emodel 6967");
- writeByte(&myReliableOutStream, clc_stringcmd);
- writeString(&myReliableOutStream, QString("prespawn " + QString::number(myServerCount) + " 0 " + QString::number(mapChecksum)));
- }
- void QWClientPrivate::parseServerMessage()
- {
- quint32 incomingSeq, incomingAck;
- bool incomingSeqReliable, incomingAckReliable;
- myBadReadFlag = false;
- myInStream.device()->seek(0);
- myInStream >> incomingSeq;
- myInStream >> incomingAck;
- incomingSeqReliable = incomingSeq >> 31;
- incomingAckReliable = incomingAck >> 31;
- incomingSeq &= ~0x80000000;
- incomingAck &= ~0x80000000;
- if(incomingSeq <= myIncomingSeq)
- return;
- myPacketLoss = incomingSeq - (myIncomingSeq + 1);
- if(incomingAckReliable == myOutgoingSeqReliableFlag)
- myReliableData.clear();
- if(incomingSeqReliable)
- myIncomingSeqReliableFlag ^= 1;
- myIncomingSeq = incomingSeq;
- myIncomingAck = incomingAck;
- myIncomingAckReliableFlag = incomingAckReliable;
- while(!myInStream.atEnd())
- {
- if(myBadReadFlag)
- {
- myClient->onError("Bad read from server.");
- disconnect();
- return;
- }
- quint8 c;
- quint8 last = 0;
- myInStream >> c;
- if(c == 0xff)
- break;
- switch(c)
- {
- case svc_bad:
- myClient->onError("Bad read from server.");
- disconnect();
- return;
- case svc_nop:
- parseSvcNoop();
- break;
- case svc_disconnect:
- parseSvcDisconnect();
- break;
- case svc_print:
- parseSvcPrint();
- break;
- case svc_centerprint:
- parseSvcCenterPrint();
- break;
- case svc_stufftext:
- parseSvcStuffText();
- break;
- case svc_damage:
- parseSvcDamage();
- break;
- case svc_serverdata:
- parseSvcServerData();
- break;
- case svc_setangle:
- parseSvcSetAngle();
- break;
- case svc_lightstyle:
- parseSvcLightStyle();
- break;
- case svc_sound:
- parseSvcSound();
- break;
- case svc_stopsound:
- parseSvcStopSound();
- break;
- case svc_updatefrags:
- parseSvcUpdateFrags();
- break;
- case svc_updateping:
- parseSvcUpdatePing();
- break;
- case svc_updatepl:
- parseSvcUpdatePL();
- break;
- case svc_updateentertime:
- parseSvcUpdateEnterTime();
- break;
- case svc_spawnbaseline:
- parseSvcSpawnBaseLine();
- break;
- case svc_spawnstatic:
- parseSvcSpawnStatic();
- break;
- case svc_temp_entity:
- parseSvcTempEntity();
- break;
- case svc_killedmonster:
- parseSvcKilledMonster();
- break;
- case svc_foundsecret:
- parseSvcFoundSecret();
- break;
- case svc_updatestat:
- parseSvcUpdateStat();
- break;
- case svc_updatestatlong:
- parseSvcUpdateStatLong();
- break;
- case svc_spawnstaticsound:
- parseSvcSpawnStaticSound();
- break;
- case svc_cdtrack:
- parseSvcCDTrack();
- break;
- case svc_intermission:
- parseSvcIntermission();
- break;
- case svc_finale:
- parseSvcFinale();
- break;
- case svc_sellscreen:
- parseSvcSellScreen();
- break;
- case svc_smallkick:
- parseSvcSmallKick();
- break;
- case svc_bigkick:
- parseSvcBigKick();
- break;
- case svc_muzzleflash:
- parseSvcMuzzleFlash();
- break;
- case svc_updateuserinfo:
- parseSvcUpdateUserinfo();
- break;
- case svc_setinfo:
- parseSvcSetinfo();
- break;
- case svc_serverinfo:
- parseSvcServerinfo();
- break;
- case svc_download:
- parseSvcDownload();
- break;
- case svc_playerinfo:
- parseSvcPlayerinfo();
- break;
- case svc_nails:
- parseSvcNails();
- break;
- case svc_chokecount:
- parseSvcChokeCount();
- break;
- case svc_modellist:
- parseSvcModellist();
- break;
- case svc_soundlist:
- parseSvcSoundlist();
- break;
- case svc_packetentities:
- parseSvcPacketEntities();
- break;
- case svc_deltapacketentities:
- parseSvcDeltaPacketEntities();
- break;
- case svc_maxspeed:
- parseSvcMaxSpeed();
- break;
- case svc_entgravity:
- parseSvcEntGravity();
- break;
- case svc_setpause:
- parseSvcSetPause();
- break;
- case svc_nails2:
- parseSvcNails2();
- break;
- case svc_fte_modellistshort:
- parseSvcFTEModellistShort();
- break;
- case svc_fte_spawnbaseline2:
- parseSvcFTESpawnBaseline2();
- break;
- case svc_qizmovoice:
- parseSvcQizmoVoice();
- break;
- case svc_fte_voicechat:
- parseSvcFTEVoiceChat();
- break;
- case svc_fte_spawnstatic2:
- parseSvcFTESpawnStatic2();
- break;
- case nq_svc_time:
- parseSvcTime();
- break;
- case nq_svc_clientdata:
- parseSvcClientData();
- break;
- case nq_svc_version:
- parseSvcVersion();
- break;
- case nq_svc_particle:
- parseSvcParticle();
- break;
- case nq_svc_signonnum:
- parseSvcSignonNum();
- break;
- case nq_svc_updatecolors:
- parseSvcUpdateColors();
- break;
- case nq_svc_updatename:
- parseSvcUpdateName();
- break;
- default:
- myClient->onError(QString("Unknown message from server. Last Cmd: [" + QString::number(last) + "] Current Cmd: [" + QString::number(c) + "]").toLatin1().data());
- disconnect();
- return;
- }
- last = c;
- }
- }
- void QWClientPrivate::connect(const char *host, quint16 port)
- {
- if(myState != QWClient::DisconnectedState)
- return;
- /* Disabled this, blocking too much, now user is supposed to send the string already resolved. */
- // QHostInfo hi = QHostInfo::fromName(host);
- // if(hi.error() != QHostInfo::NoError)
- // {
- // myClient->onError(hi.errorString().toLatin1().data());
- // return;
- // }
- myHost.setAddress(host);
- myPort = port;
- myIncomingSeq = 0;
- myIncomingAck = 0;
- myOutgoingSeq = 0;
- myLastRealiableSeq = 0;
- myIncomingSeqReliableFlag = false;
- myIncomingAckReliableFlag = false;
- myOutgoingSeqReliableFlag = false;
- myPacketLoss = 0;
- mySocket->connectToHost(myHost, myPort);
- mySocket->waitForConnected();
- sendConnectionless("getchallenge\n");
- myClient->onChallenge();
- myQPort = qrand() & 0xffff;
- myTime->start();
- myState = QWClient::ConnectingState;
- *myLastServerReplyTime = QTime::currentTime();
- }
- void QWClientPrivate::setBindHost(const QString &host)
- {
- QHostAddress address(host);
- mySocket->bind(address, 0);
- }
- void QWClientPrivate::disconnect()
- {
- if(myState == QWClient::ConnectedState)
- {
- writeByte(&myUnreliableOutStream, clc_stringcmd);
- writeString(&myUnreliableOutStream, "drop");
- sendToServer(true);
- writeByte(&myUnreliableOutStream, clc_stringcmd);
- writeString(&myUnreliableOutStream, "drop");
- sendToServer(true);
- writeByte(&myUnreliableOutStream, clc_stringcmd);
- writeString(&myUnreliableOutStream, "drop");
- sendToServer(true);
- }
- mySocket->close();
- myState = QWClient::DisconnectedState;
- }
- void QWClientPrivate::sendConnectionless(const QByteArray &data)
- {
- QByteArray d;
- d.append("\xff\xff\xff\xff");
- d.append(data);
- mySocket->write(d);
- mySocket->waitForBytesWritten();
- }
- void QWClientPrivate::sendMovement()
- {
- myUnreliableOutStream << (quint8)clc_move << (quint8)0x10 << (quint8)myPacketLoss << (quint8)0x00 << (quint8)0x00 << (quint8)0x00 << (quint8)0x22 << (quint8)0x00 << (quint8)0x21;
- }
- //=====================================================================
- // "GPL map" support. If we encounter a map with a known "GPL" CRC,
- // we fake the CRC so that, on the client side, the CRC of the original
- // map is transferred to the server, and on the server side, comparison
- // of clients' CRC is done against the orignal one
- typedef struct {
- const char *mapname;
- unsigned int original;
- unsigned int gpl;
- } csentry_t;
- static csentry_t table[] = {
- // CRCs for AquaShark's "simpletextures" maps
- { "dm1", 0xc5c7dab3, 0x7d37618e },
- { "dm2", 0x65f63634, 0x7b337440 },
- { "dm3", 0x15e20df8, 0x912781ae },
- { "dm4", 0x9c6fe4bf, 0xc374df89 },
- { "dm5", 0xb02d48fd, 0x77ca7ce5 },
- { "dm6", 0x5208da2b, 0x200c8b5d },
- { "end", 0xbbd4b4a5, 0xf89b12ae }, // this is the version with the extra room
- { NULL, 0, 0 },
- };
- int Com_TranslateMapChecksum (const char *mapname, unsigned int checksum)
- {
- csentry_t *p;
- // Com_Printf ("Map checksum (%s): 0x%x\n", mapname, checksum);
- for (p = table; p->mapname; p++)
- if (!strcmp(p->mapname, mapname)) {
- if (checksum == p->gpl)
- return p->original;
- else
- return checksum;
- }
- return checksum;
- }
- quint32 QWClientPrivate::mapChecksum(const QString &mapName)
- {
- // Check if this is an original map, if it is we have the checksum table ready
- quint32 checksum = 0;
- checksum = QWTables::getOriginalMapChecksum(mapName);
- if(checksum)
- return checksum;
- char* mapdata;
- quint64 maplen;
- if(!readFile(mapName, &mapdata, &maplen))
- return 0;
- if(!maplen || !mapdata)
- return 0;
- dheader_t* header;
- uchar* mod_base;
- header = (dheader_t*)mapdata;
- mod_base = (uchar*)mapdata;
- for(int i = 0; i < HEADER_LUMPS; ++i)
- {
- if(i == LUMP_ENTITIES || i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES)
- continue;
- checksum ^= blockCheckSum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
- }
- delete[] mapdata;
- QString cleanMapName;
- cleanMapName = mapName.section('/', -1);
- cleanMapName.chop(4); //strip .bsp
- return Com_TranslateMapChecksum(cleanMapName.toLatin1().data(), checksum);
- }
- unsigned QWClientPrivate::blockCheckSum(void* buffer, int len)
- {
- QByteArray digest = QCryptographicHash::hash(QByteArray((const char*)buffer, len), QCryptographicHash::Md4);
- int *d = (int*)digest.data();
- return (d[0] ^ d[1] ^ d[2] ^ d[3]);
- }
- void QWClientPrivate::writeByte(QDataStream *stream, const quint8 b)
- {
- *stream << b;
- }
- void QWClientPrivate::writeLong(QDataStream *stream, const quint32 l)
- {
- *stream << l;
- }
- void QWClientPrivate::writeShort(QDataStream *stream, const quint16 s)
- {
- *stream << s;
- }
- void QWClientPrivate::writeString(QDataStream *stream, const QString &str)
- {
- stream->writeRawData(str.toLatin1().data(), str.size()+1);
- }
- //========================================================================
- // NAME FUN
- char QWClientPrivate::ourReadableCharsTable[256] = { '.', '_' , '_' , '_' , '_' , '.' , '_' , '_' , '_' , '_' , '\n' , '_' , '\n' , '>' , '.' , '.',
- '[', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '_', '_', '_'
- };
- bool QWClientPrivate::ourReadableCharsTableInitialized = false;
- void QWClientPrivate::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;
- }
- void QWClientPrivate::stripColor(char* string)
- {
- /* Parse simple quake red text and special chars */
- if(!ourReadableCharsTableInitialized)
- fillReadableCharsTable();
- while(*string) {
- *string = ourReadableCharsTable[(unsigned char)*string] & 127;
- string++;
- }
- }
|