First commit
This commit is contained in:
commit
1dee5e84ae
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
bin
|
||||||
|
QBERcon.pro.user
|
19
QBERcon.pro
Normal file
19
QBERcon.pro
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
QT += core widgets network
|
||||||
|
|
||||||
|
TARGET = QBERcon
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
INCLUDEPATH += src example
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
example/main.cpp \
|
||||||
|
example/examplegui.cpp \
|
||||||
|
src/QBERcon.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
example/examplegui.h \
|
||||||
|
src/QBERcon.h
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
example/examplegui.ui
|
29
README.md
Normal file
29
README.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
QBERcon - BattlEye Rcon connector for Qt5 C++
|
||||||
|
=============================================
|
||||||
|
|
||||||
|
## Public functions:
|
||||||
|
```c++
|
||||||
|
void connectToServer(QString password, QString hostname, quint16 port = 2302);
|
||||||
|
void disconnectFromServer();
|
||||||
|
bool isConnected() const;
|
||||||
|
void setKeepAliveInterval(int value); // In milliseconds. Default interval is 5 seconds
|
||||||
|
quint8 sendCommand(QString cmd); // Send command and return command sequence number
|
||||||
|
```
|
||||||
|
|
||||||
|
## Signals:
|
||||||
|
```c++
|
||||||
|
void messageReceived(QString &message); // Emitted when server broadcasts message
|
||||||
|
void commandReceived(QString message, quint8 seqNumber); // Emitted when server replies command with sequence number
|
||||||
|
void connected(); // Emitted after successful login
|
||||||
|
void disconnected(); // Emitted after disconnect, timeout, etc.
|
||||||
|
void error(QBERcon::RconError err); // Emitted when error thrown
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors:
|
||||||
|
```c++
|
||||||
|
QBERcon::ERROR_LOGIN_FAILED // Wrong password
|
||||||
|
QBERcon::ERROR_KEEPALIVE_EXCEEDED // Timeout
|
||||||
|
QBERcon::ERROR_MISSING_LOGIN_DATA // No login/password specified
|
||||||
|
```
|
||||||
|
|
||||||
|
Not fully tested. Use at your own risk.
|
85
example/examplegui.cpp
Normal file
85
example/examplegui.cpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#include "examplegui.h"
|
||||||
|
#include "ui_examplegui.h"
|
||||||
|
|
||||||
|
#define Q(x) #x
|
||||||
|
#define QUOTE(x) Q(x)
|
||||||
|
|
||||||
|
ExampleGui::ExampleGui(QWidget *parent) :
|
||||||
|
QWidget(parent),
|
||||||
|
ui(new Ui::ExampleGui) {
|
||||||
|
ui->setupUi(this);
|
||||||
|
be = new QBERcon::Client(this);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef PASSWORD
|
||||||
|
ui->passwordInput->setText(QUOTE(PASSWORD));
|
||||||
|
#endif
|
||||||
|
#ifdef SERVER_IP
|
||||||
|
ui->hostInput->setText(QUOTE(SERVER_IP));
|
||||||
|
#endif
|
||||||
|
#ifdef SERVER_PORT
|
||||||
|
ui->portInput->setValue(SERVER_PORT);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
connect(be, SIGNAL(connected()), this, SLOT(connected()));
|
||||||
|
connect(be, SIGNAL(disconnected()), this, SLOT(disconnected()));
|
||||||
|
connect(be, SIGNAL(error(QBERcon::RconError)), this, SLOT(error(QBERcon::RconError)));
|
||||||
|
connect(be, SIGNAL(messageReceived(QString&)), this, SLOT(messageReceived(QString&)));
|
||||||
|
connect(be, SIGNAL(commandReceived(QString, quint8)), this, SLOT(commandReceived(QString, quint8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ExampleGui::~ExampleGui() {
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ExampleGui::messageReceived(QString &message) {
|
||||||
|
ui->logOutput->append(QString("<font color=\"#67983E\">[MSG]: %1</font>")
|
||||||
|
.arg(message));
|
||||||
|
qDebug() << __PRETTY_FUNCTION__ << message;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleGui::commandReceived(QString message, quint8 seqNumber) {
|
||||||
|
ui->logOutput->append(QString("<font color=\"#E19B3E\">[CMD ID=%1]: %2</font>")
|
||||||
|
.arg(QString::number(seqNumber), message));
|
||||||
|
qDebug() << __PRETTY_FUNCTION__ << "number =" << seqNumber << "Data:" << message;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleGui::connected() {
|
||||||
|
ui->logOutput->append(QString("<font color=\"#42B555\">[CONNECTED]</font>"));
|
||||||
|
qDebug() << __PRETTY_FUNCTION__;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleGui::disconnected() {
|
||||||
|
ui->logOutput->append(QString("<font color=\"#D82672\">[DISCONNECTED]</font>"));
|
||||||
|
qDebug() << __PRETTY_FUNCTION__;
|
||||||
|
ui->connectButton->setEnabled(true);
|
||||||
|
ui->disconnectButton->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleGui::error(QBERcon::RconError err) {
|
||||||
|
ui->logOutput->append(QString("<font color=\"#8E1717\">[ERR]: %1</font>")
|
||||||
|
.arg(QString::number(err)));
|
||||||
|
qDebug() << __PRETTY_FUNCTION__ << err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleGui::on_connectButton_clicked() {
|
||||||
|
be->connectToServer(ui->passwordInput->text(), ui->hostInput->text(), ui->portInput->value());
|
||||||
|
ui->connectButton->setEnabled(false);
|
||||||
|
ui->disconnectButton->setEnabled(true);
|
||||||
|
ui->commandButton->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleGui::on_disconnectButton_clicked() {
|
||||||
|
be->disconnectFromServer();
|
||||||
|
ui->connectButton->setEnabled(true);
|
||||||
|
ui->disconnectButton->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleGui::on_commandButton_clicked() {
|
||||||
|
QString command = ui->commandInput->text();
|
||||||
|
quint8 number = be->sendCommand(command);
|
||||||
|
ui->logOutput->append(QString("<font color=\"#5D5815\">[CMD]: Sent %1 with ID=%2</font>")
|
||||||
|
.arg(command, QString::number(number)));
|
||||||
|
}
|
38
example/examplegui.h
Normal file
38
example/examplegui.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#ifndef EXAMPLEGUI_H
|
||||||
|
#define EXAMPLEGUI_H
|
||||||
|
|
||||||
|
#include "QBERcon.h"
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class ExampleGui;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExampleGui : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ExampleGui(QWidget *parent = 0);
|
||||||
|
~ExampleGui();
|
||||||
|
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void messageReceived(QString &message);
|
||||||
|
void commandReceived(QString message, quint8 seqNumber);
|
||||||
|
void connected();
|
||||||
|
void disconnected();
|
||||||
|
void error(QBERcon::RconError err);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_connectButton_clicked();
|
||||||
|
void on_disconnectButton_clicked();
|
||||||
|
void on_commandButton_clicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QBERcon::Client *be;
|
||||||
|
Ui::ExampleGui *ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // EXAMPLEGUI_H
|
99
example/examplegui.ui
Normal file
99
example/examplegui.ui
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ExampleGui</class>
|
||||||
|
<widget class="QWidget" name="ExampleGui">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>600</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>RCON Test</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="4" column="0" colspan="2">
|
||||||
|
<widget class="QPushButton" name="disconnectButton">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Disconnect</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="QPushButton" name="connectButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Connect</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QSpinBox" name="portInput">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65535</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>2302</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Port</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Password</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="passwordInput">
|
||||||
|
<property name="echoMode">
|
||||||
|
<enum>QLineEdit::Password</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QPushButton" name="commandButton">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Command</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>IP/Host</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="hostInput"/>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QLineEdit" name="commandInput">
|
||||||
|
<property name="text">
|
||||||
|
<string>players</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0" colspan="2">
|
||||||
|
<widget class="QTextBrowser" name="logOutput"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
12
example/main.cpp
Normal file
12
example/main.cpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include <QApplication>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <examplegui.h>
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
ExampleGui *example = new ExampleGui();
|
||||||
|
example->show();
|
||||||
|
return a.exec();
|
||||||
|
}
|
253
src/QBERcon.cpp
Normal file
253
src/QBERcon.cpp
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
#include "QBERcon.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
QBERcon::Client::Client(QObject *parent) : QObject(parent) {
|
||||||
|
this->port = 2302;
|
||||||
|
commandSequenceNumber = 0;
|
||||||
|
keepAliveInterval = 5000;
|
||||||
|
keepAliveTimer = new QTimer(this);
|
||||||
|
socket = new QUdpSocket(this);
|
||||||
|
|
||||||
|
dns = new QDnsLookup();
|
||||||
|
dns->setType(QDnsLookup::A);
|
||||||
|
|
||||||
|
connect(socket, SIGNAL(connected()), SLOT(socketConnected()));
|
||||||
|
connect(socket, SIGNAL(disconnected()), SLOT(socketDisconnected()));
|
||||||
|
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError)));
|
||||||
|
connect(socket, SIGNAL(readyRead()), SLOT(read()));
|
||||||
|
connect(keepAliveTimer, SIGNAL(timeout()), this, SLOT(keepAliveTimerTimeout()));
|
||||||
|
connect(dns, SIGNAL(finished()), this, SLOT(hostLookupFinished()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QBERcon::Client::~Client() {
|
||||||
|
if(isConnected())
|
||||||
|
disconnectFromServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QBERcon::Client::connectToServer(QString password, QString hostname, quint16 port) {
|
||||||
|
if(hostname.isEmpty() || password.isEmpty()) {
|
||||||
|
emit error(QBERcon::ERROR_MISSING_LOGIN_DATA);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->hostname = hostname;
|
||||||
|
this->port = port;
|
||||||
|
this->password = password;
|
||||||
|
|
||||||
|
dns->setName(hostname);
|
||||||
|
dns->lookup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QBERcon::Client::hostLookupFinished() {
|
||||||
|
if (dns->error() != QDnsLookup::NoError) {
|
||||||
|
qDebug() << "DNS Lookup failed" << dns->error() << dns->errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(dns->hostAddressRecords().size() > 0) {
|
||||||
|
host = dns->hostAddressRecords().first().value();
|
||||||
|
qDebug() << "DNS Lookup OK:" << host;
|
||||||
|
socket->connectToHost(host, port, QAbstractSocket::ReadWrite);
|
||||||
|
} else {
|
||||||
|
qDebug() << "DNS Lookup failed, no records";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QBERcon::Client::keepAliveTimerTimeout() {
|
||||||
|
if(keepAliveReceived) {
|
||||||
|
keepAliveReceived = false;
|
||||||
|
sendCommand("");
|
||||||
|
} else {
|
||||||
|
emit error(QBERcon::ERROR_KEEPALIVE_EXCEEDED);
|
||||||
|
disconnectFromServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QBERcon::Client::read() {
|
||||||
|
QByteArray data;
|
||||||
|
data.resize(socket->pendingDatagramSize());
|
||||||
|
socket->readDatagram(data.data(), data.size());
|
||||||
|
handleData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QBERcon::Client::socketConnected() {
|
||||||
|
commandSequenceNumber = 0;
|
||||||
|
keepAliveReceived = true;
|
||||||
|
keepAliveTimer->start(keepAliveInterval);
|
||||||
|
sendPacket(QBERcon::PACKET_LOGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QBERcon::Client::disconnectFromServer() {
|
||||||
|
connectedToServer = false;
|
||||||
|
keepAliveTimer->stop();
|
||||||
|
socket->disconnectFromHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
quint8 QBERcon::Client::sendCommand(QString cmd) {
|
||||||
|
if(!connectedToServer) return 0;
|
||||||
|
sendPacket(QBERcon::PACKET_COMMAND, cmd);
|
||||||
|
return commandSequenceNumber - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QBERcon::Client::socketDisconnected() {
|
||||||
|
connectedToServer = false;
|
||||||
|
emit disconnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QBERcon::Client::socketError(QAbstractSocket::SocketError err) {
|
||||||
|
qDebug() << "QAbstractSocket::SocketError:" << err;
|
||||||
|
disconnectFromServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QBERcon::Client::addHeaderToPacket(QByteArray &dst) {
|
||||||
|
QByteArray result;
|
||||||
|
quint32 crc = qcrc32(dst);
|
||||||
|
result.append("BE");
|
||||||
|
for(int i = 0; i < 4; ++i) {
|
||||||
|
result.append((quint8)((crc >> (i * 8)) & 0xFF));
|
||||||
|
}
|
||||||
|
result.append(dst);
|
||||||
|
dst = result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void QBERcon::Client::handleData(QByteArray &data) {
|
||||||
|
if(!(data.size() > 7 && data.at(0) == 'B' && data.at(1) == 'E')) {
|
||||||
|
qDebug() << "Not a BE packet, ignoring";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 crc_msg = 0;
|
||||||
|
for(int i = 0; i < 4; ++i) {
|
||||||
|
quint8 b = data.at(2 + i);
|
||||||
|
crc_msg |= b << (i * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.remove(0, 6); // cut header and keep payload
|
||||||
|
|
||||||
|
quint32 crc_computed = qcrc32(data);
|
||||||
|
|
||||||
|
if(crc_msg != crc_computed) {
|
||||||
|
qDebug() << "Packet CRC missmatch, ignoring";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint8 packetType = data.at(1);
|
||||||
|
//qDebug() << "Packet type" << packetType;
|
||||||
|
|
||||||
|
switch (packetType) {
|
||||||
|
case QBERcon::PACKET_LOGIN: {
|
||||||
|
quint8 result = data.at(2);
|
||||||
|
if(result == 0x01) {
|
||||||
|
connectedToServer = true;
|
||||||
|
emit connected();
|
||||||
|
} else {
|
||||||
|
emit error(QBERcon::ERROR_LOGIN_FAILED);
|
||||||
|
disconnectFromServer();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QBERcon::PACKET_COMMAND: {
|
||||||
|
quint8 seqNumber = data.at(2);
|
||||||
|
if(data.length() < 4) { // ACK
|
||||||
|
keepAliveReceived = true;
|
||||||
|
} else {
|
||||||
|
// 3 4 5
|
||||||
|
// 0x00 | number of packets for this response | 0-based index of the current packet
|
||||||
|
if(data.at(3) == 0x00) { // multipart
|
||||||
|
//qDebug() << "Multipart";
|
||||||
|
quint8 messages_total = data.at(4);
|
||||||
|
quint8 message_current = data.at(5);
|
||||||
|
if(message_current == 0x00) {
|
||||||
|
multipartMessageData.clear();
|
||||||
|
}
|
||||||
|
if(messages_total > message_current) {
|
||||||
|
multipartMessageData.append(QString::fromUtf8(data.remove(0, 6)));
|
||||||
|
if((messages_total - 1) == message_current) {
|
||||||
|
emit commandReceived(multipartMessageData, seqNumber);
|
||||||
|
multipartMessageData.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QString msg = QString::fromUtf8(data.remove(0, 3));
|
||||||
|
emit commandReceived(msg, seqNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QBERcon::PACKET_MESSAGE: {
|
||||||
|
quint8 ret = data.at(2);
|
||||||
|
QString msg = QString::fromUtf8(data.remove(0, 3));
|
||||||
|
emit messageReceived(msg);
|
||||||
|
sendPacket(QBERcon::PACKET_MESSAGE, ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QBERcon::Client::sendPacket(QBERcon::PacketType type, QVariant data) {
|
||||||
|
QByteArray p;
|
||||||
|
p.append(0xFF);
|
||||||
|
p.append(type);
|
||||||
|
//qDebug() << "Sending packet type" << type;
|
||||||
|
switch (type) {
|
||||||
|
case QBERcon::PACKET_LOGIN:
|
||||||
|
p.append(password);
|
||||||
|
break;
|
||||||
|
case QBERcon::PACKET_MESSAGE:
|
||||||
|
p.append(data.toChar());
|
||||||
|
break;
|
||||||
|
case QBERcon::PACKET_COMMAND:
|
||||||
|
p.append(commandSequenceNumber++);
|
||||||
|
p.append(data.toByteArray());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
addHeaderToPacket(p);
|
||||||
|
socket->writeDatagram(p, host, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QBERcon::Client::isConnected() const {
|
||||||
|
return connectedToServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QBERcon::Client::setKeepAliveInterval(int value) {
|
||||||
|
keepAliveInterval = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// http://www.hackersdelight.org/hdcodetxt/crc.c.txt
|
||||||
|
/* This is derived from crc32b but does table lookup. First the table
|
||||||
|
itself is calculated, if it has not yet been set up.
|
||||||
|
Not counting the table setup (which would probably be a separate
|
||||||
|
function), when compiled to Cyclops with GCC, this function executes in
|
||||||
|
7 + 13n instructions, where n is the number of bytes in the input
|
||||||
|
message. It should be doable in 4 + 9n instructions. In any case, two
|
||||||
|
of the 13 or 9 instrucions are load byte.
|
||||||
|
This is Figure 14-7 in the text. */
|
||||||
|
quint32 QBERcon::Client::qcrc32(QByteArray data) {
|
||||||
|
int j;
|
||||||
|
quint32 byte, crc, mask;
|
||||||
|
static quint32 table[256];
|
||||||
|
/* Set up the table, if necessary. */
|
||||||
|
if (table[1] == 0) {
|
||||||
|
for (byte = 0; byte <= 255; byte++) {
|
||||||
|
crc = byte;
|
||||||
|
for (j = 7; j >= 0; j--) { // Do eight times.
|
||||||
|
mask = -(crc & 1);
|
||||||
|
crc = (crc >> 1) ^ (0xEDB88320 & mask);
|
||||||
|
}
|
||||||
|
table[byte] = crc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Through with table setup, now calculate the CRC. */
|
||||||
|
crc = 0xFFFFFFFF;
|
||||||
|
QByteArray::Iterator it = data.begin();
|
||||||
|
while (it != data.end()) {
|
||||||
|
byte = *it;
|
||||||
|
crc = (crc >> 8) ^ table[(crc ^ byte) & 0xFF];
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
return ~crc;
|
||||||
|
}
|
72
src/QBERcon.h
Normal file
72
src/QBERcon.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#ifndef QTBERCON_H
|
||||||
|
#define QTBERCON_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QUdpSocket>
|
||||||
|
#include <QDnsLookup> // QHostInfo::lookupHost slow for me some reason, so QT5
|
||||||
|
|
||||||
|
namespace QBERcon {
|
||||||
|
enum PacketType {
|
||||||
|
PACKET_LOGIN = 0x00,
|
||||||
|
PACKET_COMMAND = 0x01,
|
||||||
|
PACKET_MESSAGE = 0x02,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RconError {
|
||||||
|
ERROR_NONE = 0,
|
||||||
|
ERROR_LOGIN_FAILED,
|
||||||
|
ERROR_KEEPALIVE_EXCEEDED,
|
||||||
|
ERROR_MISSING_LOGIN_DATA,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Client : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit Client(QObject *parent = 0);
|
||||||
|
~Client();
|
||||||
|
void connectToServer(QString password, QString hostname, quint16 port = 2302);
|
||||||
|
void disconnectFromServer();
|
||||||
|
quint8 sendCommand(QString cmd);
|
||||||
|
bool isConnected() const;
|
||||||
|
void setKeepAliveInterval(int value);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void messageReceived(QString &message);
|
||||||
|
void commandReceived(QString message, quint8 seqNumber);
|
||||||
|
void connected();
|
||||||
|
void disconnected();
|
||||||
|
void error(QBERcon::RconError err);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void keepAliveTimerTimeout();
|
||||||
|
void read();
|
||||||
|
void socketConnected();
|
||||||
|
void socketDisconnected();
|
||||||
|
void socketError(QAbstractSocket::SocketError err);
|
||||||
|
void hostLookupFinished();
|
||||||
|
private:
|
||||||
|
void addHeaderToPacket(QByteArray &dst);
|
||||||
|
void handleData(QByteArray &data);
|
||||||
|
void sendPacket(QBERcon::PacketType type, QVariant data = QVariant());
|
||||||
|
quint32 qcrc32(QByteArray data);
|
||||||
|
|
||||||
|
QTimer *keepAliveTimer;
|
||||||
|
QUdpSocket *socket;
|
||||||
|
QDnsLookup *dns;
|
||||||
|
|
||||||
|
QString password;
|
||||||
|
quint16 port;
|
||||||
|
QString hostname;
|
||||||
|
QHostAddress host;
|
||||||
|
|
||||||
|
quint8 commandSequenceNumber;
|
||||||
|
bool connectedToServer;
|
||||||
|
int keepAliveInterval;
|
||||||
|
bool keepAliveReceived;
|
||||||
|
QString multipartMessageData;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif // QTBERCON_H
|
Loading…
x
Reference in New Issue
Block a user