From be8fa7b1f831cdf2843c48c81c265066eca64355 Mon Sep 17 00:00:00 2001 From: Daniel Brunner <0xFEEDC0DE64@gmail.com> Date: Sun, 28 Oct 2018 14:55:38 +0100 Subject: [PATCH] Imported existing sources --- DbMinecraft.pro | 15 +++ client.cpp | 257 +++++++++++++++++++++++++++++++++++++++++++++++ client.h | 33 ++++++ main.cpp | 34 +++++++ mcdatastream.cpp | 173 +++++++++++++++++++++++++++++++ mcdatastream.h | 58 +++++++++++ packets.cpp | 150 +++++++++++++++++++++++++++ packets.h | 165 ++++++++++++++++++++++++++++++ 8 files changed, 885 insertions(+) create mode 100644 DbMinecraft.pro create mode 100644 client.cpp create mode 100644 client.h create mode 100644 main.cpp create mode 100644 mcdatastream.cpp create mode 100644 mcdatastream.h create mode 100644 packets.cpp create mode 100644 packets.h diff --git a/DbMinecraft.pro b/DbMinecraft.pro new file mode 100644 index 0000000..afc8a0d --- /dev/null +++ b/DbMinecraft.pro @@ -0,0 +1,15 @@ +QT = core network + +CONFIG += c++1z + +DEFINES += QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000 + +SOURCES += main.cpp \ + client.cpp \ + mcdatastream.cpp \ + packets.cpp + +HEADERS += \ + client.h \ + mcdatastream.h \ + packets.h diff --git a/client.cpp b/client.cpp new file mode 100644 index 0000000..5bf2e7a --- /dev/null +++ b/client.cpp @@ -0,0 +1,257 @@ +#include "client.h" + +#include +#include +#include +#include + +Client::Client(QTcpSocket *socket, QObject *parent) : + QObject(parent), m_socket(socket), m_dataStream(m_socket), + m_packetSize(0), m_state(HandshakingState) +{ + m_socket->setParent(this); + + connect(m_socket, &QIODevice::readyRead, this, &Client::readyRead); + connect(m_socket, &QAbstractSocket::disconnected, this, &Client::disconnected); + + qDebug() << m_socket->peerPort(); +} + +void Client::readyRead() +{ + while(m_socket->bytesAvailable()) + { + if(!m_packetSize) + { + m_packetSize = m_dataStream.readVar(); + qDebug() << "packet size" << m_packetSize; + } + + if(m_socket->bytesAvailable() < m_packetSize) + { + qWarning() << "packet not fully available" << m_socket->bytesAvailable(); + return; + } + + qint32 bytesRead; + const auto type = m_dataStream.readVar(bytesRead); + m_packetSize -= bytesRead; + const auto buffer = m_socket->read(m_packetSize); + Q_ASSERT(buffer.size() == m_packetSize); + m_packetSize = 0; + + switch(m_state) + { + case HandshakingState: + readPacketHandshaking(packets::handshaking::serverbound::PacketType(type), buffer); + break; + case StatusState: + readPacketStatus(packets::status::serverbound::PacketType(type), buffer); + break; + case LoginState: + readPacketLogin(packets::login::serverbound::PacketType(type), buffer); + break; + case PlayState: + readPacketPlay(packets::play::serverbound::PacketType(type), buffer); + break; + default: + qWarning() << "unhandled state" << m_state << type << buffer; + } + } +} + +void Client::disconnected() +{ + qDebug() << m_socket->peerPort(); + deleteLater(); +} + +void Client::readPacketHandshaking(packets::handshaking::serverbound::PacketType type, const QByteArray &buffer) +{ + qDebug() << type; + + McDataStream dataStream(const_cast(&buffer), QIODevice::ReadOnly); + + switch(type) + { + using namespace packets::handshaking; + case serverbound::PacketHandshake: + { + serverbound::Handshake packet(dataStream); + m_state = packet.nextState; + break; + } + default: + qWarning() << "unknown type!"; + } +} + +void Client::readPacketStatus(const packets::status::serverbound::PacketType type, const QByteArray &buffer) +{ + qDebug() << type; + + McDataStream dataStream(const_cast(&buffer), QIODevice::ReadOnly); + + switch(type) + { + using namespace packets::status; + case serverbound::PacketRequest: + { + { + serverbound::Request packet(dataStream); + } + { + clientbound::Response packet; + packet.jsonResponse = + "{" + " \"version\": {" + " \"name\": \"1.13.1\"," + " \"protocol\": 401" + " }," + " \"players\": {" + " \"max\": 1000," + " \"online\": 2000," + " \"sample\": [" + " {" + " \"name\": \"0xFEEDC0DE64\"," + " \"id\": \"6ebf7396-b6da-40b1-b7d9-9b7961450d5a\"" + " }" + " ]" + " }, " + " \"description\": {" + " \"text\": \"Mein monster server in C++\"" + " }," + " \"favicon\": \"\"" + "}"; + packet.serialize(m_dataStream); + } + break; + } + case serverbound::PacketPing: + { + qint64 payload; + { + serverbound::Ping packet(dataStream); + payload = packet.payload; + } + { + clientbound::Pong packet; + packet.payload = payload; + packet.serialize(m_dataStream); + } + break; + } + default: + qWarning() << "unknown type!"; + } +} + +void Client::readPacketLogin(const packets::login::serverbound::PacketType type, const QByteArray &buffer) +{ + qDebug() << type; + + McDataStream dataStream(const_cast(&buffer), QIODevice::ReadOnly); + + switch(type) + { + using namespace packets::login; + case serverbound::PacketLogin: + { + QString name; + { + serverbound::Login packet(dataStream); + name = packet.name; + } + qDebug() << "Name" << name; + { + clientbound::LoginSuccess packet; + const auto uuid = QUuid::createUuid().toString(); + packet.uuid = uuid.mid(1, uuid.length() - 2); + packet.username = name; + packet.serialize(m_dataStream); + } + m_state = PlayState; + { + packets::play::clientbound::JoinGame packet; + packet.entityid = 1; + packet.gamemode = 0; + packet.dimension = 0; + packet.difficulty = 2; + packet.maxPlayers = 255; + packet.levelType = QStringLiteral("default"); + packet.reducedDebugInfo = false; + packet.serialize(m_dataStream); + } + { + packets::play::clientbound::PluginMessage packet; + packet.channel = QStringLiteral("minecraft:brand"); + packet.data = QByteArrayLiteral("bullshit"); + packet.serialize(m_dataStream); + } + { + packets::play::clientbound::ServerDifficulty packet; + packet.difficulty = 2; + packet.serialize(m_dataStream); + } + { + packets::play::clientbound::SpawnPosition packet; + packet.location = std::make_tuple(100, 64, 100); + packet.serialize(m_dataStream); + } + { + packets::play::clientbound::PlayerAbilities packet; + packet.flags = 0x0F; + packet.flyingSpeed = 1.; + packet.fieldOfViewModifier = 60.; + packet.serialize(m_dataStream); + } + break; + } + default: + qWarning() << "unknown type!"; + } +} + +void Client::readPacketPlay(const packets::play::serverbound::PacketType type, const QByteArray &buffer) +{ + qDebug() << type; + + McDataStream dataStream(const_cast(&buffer), QIODevice::ReadOnly); + + switch(type) + { + using namespace packets::play; + case serverbound::PacketClientSettings: + { + { + serverbound::ClientSettings packet(dataStream); + qDebug() << "locale" << packet.locale; + qDebug() << "viewDistance" << packet.viewDistance; + qDebug() << "chatMode" << packet.chatMode; + qDebug() << "chatColors" << packet.chatColors; + qDebug() << "displayedSkinParts" << packet.displayedSkinParts; + qDebug() << "mainHand" << packet.mainHand; + } + { + clientbound::PlayerPositionAndLook packet; + packet.x = 50.; + packet.y = 64.; + packet.z = 50.; + packet.yaw = 0.; + packet.pitch = 0.; + packet.flags = 0; + packet.teleportId = 0; + packet.serialize(m_dataStream); + } + break; + } + case serverbound::PacketPluginMessage: + { + serverbound::PluginMessage packet(dataStream); + qDebug() << "channel" << packet.channel; + break; + } + default: + qWarning() << "unknown type!"; + } +} diff --git a/client.h b/client.h new file mode 100644 index 0000000..8973654 --- /dev/null +++ b/client.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include "mcdatastream.h" +#include "packets.h" + +class QTcpSocket; + +class Client : public QObject +{ + Q_OBJECT + +public: + explicit Client(QTcpSocket *socket, QObject *parent = nullptr); + +private Q_SLOTS: + void readyRead(); + void disconnected(); + +private: + void readPacketHandshaking(const packets::handshaking::serverbound::PacketType type, const QByteArray &buffer); + void readPacketStatus(const packets::status::serverbound::PacketType type, const QByteArray &buffer); + void readPacketLogin(const packets::login::serverbound::PacketType type, const QByteArray &buffer); + void readPacketPlay(const packets::play::serverbound::PacketType type, const QByteArray &buffer); + + QTcpSocket *m_socket; + McDataStream m_dataStream; + + qint32 m_packetSize; + + SocketState m_state; +}; diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..e1c3f29 --- /dev/null +++ b/main.cpp @@ -0,0 +1,34 @@ +#include +#include +#include + +#include "client.h" + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + + qSetMessagePattern(QStringLiteral("%{time dd.MM.yyyy HH:mm:ss.zzz} " + "[" + "%{if-debug}D%{endif}" + "%{if-info}I%{endif}" + "%{if-warning}W%{endif}" + "%{if-critical}C%{endif}" + "%{if-fatal}F%{endif}" + "] " + "%{function}(): " + "%{message}")); + + QTcpServer server; + + QObject::connect(&server, &QTcpServer::newConnection, [&server](){ + new Client(server.nextPendingConnection()); + }); + + if(!server.listen(QHostAddress::Any, 25565)) + qFatal("could not start listening %s", server.errorString().toUtf8().constData()); + + qDebug() << "started listening"; + + return a.exec(); +} diff --git a/mcdatastream.cpp b/mcdatastream.cpp new file mode 100644 index 0000000..cd5da16 --- /dev/null +++ b/mcdatastream.cpp @@ -0,0 +1,173 @@ +#include "mcdatastream.h" + +#include + +#include + +McDataStream::McDataStream() : + QDataStream() +{ + setByteOrder(QDataStream::BigEndian); +} + +McDataStream::McDataStream(QIODevice *device) : + QDataStream(device) +{ + setByteOrder(QDataStream::BigEndian); +} + +McDataStream::McDataStream(QByteArray *byteArray, QIODevice::OpenMode flags) : + QDataStream(byteArray, flags) +{ + setByteOrder(QDataStream::BigEndian); +} + +McDataStream::McDataStream(const QByteArray &byteArray) : + QDataStream(byteArray) +{ + setByteOrder(QDataStream::BigEndian); +} + +McDataStream::Position McDataStream::readPosition() +{ + quint64 val; + *this >> val; + const qint32 x = val >> 38; + const qint16 y = (val >> 26) & 0xFFF; + const qint32 z = val << 38 >> 38; + return std::make_tuple(x, y, z); +} + +void McDataStream::writePosition(const Position &position) +{ + const quint64 val = ((quint64(std::get<0>(position)) & 0x3FFFFFF) << 38) | + ((quint64(std::get<1>(position)) & 0xFFF) << 26) | + (quint64(std::get<2>(position)) & 0x3FFFFFF); + *this << val; +} + +float McDataStream::readFloat() +{ + const auto precision = floatingPointPrecision(); + setFloatingPointPrecision(SinglePrecision); + float value; + *this >> value; + setFloatingPointPrecision(precision); + return value; +} + +void McDataStream::writeFloat(float value) +{ + const auto precision = floatingPointPrecision(); + setFloatingPointPrecision(SinglePrecision); + *this << value; + setFloatingPointPrecision(precision); +} + +float McDataStream::readDouble() +{ + const auto precision = floatingPointPrecision(); + setFloatingPointPrecision(DoublePrecision); + double value; + *this >> value; + setFloatingPointPrecision(precision); + return value; +} + +void McDataStream::writeDouble(double value) +{ + const auto precision = floatingPointPrecision(); + setFloatingPointPrecision(DoublePrecision); + *this << value; + setFloatingPointPrecision(precision); +} + +template<> +qint32 McDataStream::readVar(qint32 &bytesRead) +{ + bytesRead = 0; + qint32 result = 0; + qint8 read; + do { + *this >> read; + qint32 value = read & 0b01111111; + result |= value << (7 * bytesRead); + + bytesRead++; + if (bytesRead > 5) { + qFatal("VarInt is too big"); + } + } while ((read & 0b10000000) != 0); + + return result; +} + +template<> +qint64 McDataStream::readVar(qint32 &bytesRead) +{ + bytesRead = 0; + qint64 result = 0; + qint8 read; + do { + *this >> read; + qint32 value = read & 0b01111111; + result |= value << (7 * bytesRead); + + bytesRead++; + if (bytesRead > 10) { + qFatal("VarInt is too big"); + } + } while ((read & 0b10000000) != 0); + + return result; +} + +template<> +QString McDataStream::readVar(qint32 &bytesRead) +{ + const auto length = readVar(bytesRead); + + auto data = std::unique_ptr(new char[length]); + + { + const auto rawLength = readRawData(data.get(), length); + bytesRead += rawLength; + Q_ASSERT(length == rawLength); + } + + return QString::fromUtf8(data.get(), length); +} + +template<> +void McDataStream::writeVar(qint32 value) +{ + do { + qint8 temp = value & 0b01111111; + value >>= 7; + if (value != 0) { + temp |= 0b10000000; + } + *this << temp; + } while (value != 0); +} + +template<> +void McDataStream::writeVar(qint64 value) +{ + do { + qint8 temp = value & 0b01111111; + value >>= 7; + if (value != 0) { + temp |= 0b10000000; + } + *this << temp; + } while (value != 0); +} + +template<> +void McDataStream::writeVar(QString value) +{ + const auto bytes = value.toUtf8(); + writeVar(bytes.length()); + writeRawData(bytes.constData(), bytes.length()); +} diff --git a/mcdatastream.h b/mcdatastream.h new file mode 100644 index 0000000..ee83ba6 --- /dev/null +++ b/mcdatastream.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include + +class McDataStream : public QDataStream +{ +public: + McDataStream(); + explicit McDataStream(QIODevice *device); + McDataStream(QByteArray *byteArray, QIODevice::OpenMode flags); + McDataStream(const QByteArray &byteArray); + + typedef std::tuple Position; + Position readPosition(); + void writePosition(const Position &position); + + float readFloat(); + void writeFloat(float value); + + float readDouble(); + void writeDouble(double value); + + template + T readVar(); + + template + T readVar(qint32 &bytesRead); + + template + void writeVar(T value); +}; + +template +T McDataStream::readVar() +{ + qint32 bytesRead; + return readVar(bytesRead); +} + +template<> +qint32 McDataStream::readVar(qint32 &bytesRead); + +template<> +qint64 McDataStream::readVar(qint32 &bytesRead); + +template<> +QString McDataStream::readVar(qint32 &bytesRead); + +template<> +void McDataStream::writeVar(qint32 value); + +template<> +void McDataStream::writeVar(qint64 value); + +template<> +void McDataStream::writeVar(QString value); diff --git a/packets.cpp b/packets.cpp new file mode 100644 index 0000000..9e92565 --- /dev/null +++ b/packets.cpp @@ -0,0 +1,150 @@ +#include "packets.h" + +#include "mcdatastream.h" + +packets::handshaking::serverbound::Handshake::Handshake(McDataStream &stream) +{ + protocolVersion = stream.readVar(); + serverAddress = stream.readVar(); + stream >> serverPort; + const auto socketState = stream.readVar(); + Q_ASSERT(socketState == 1 || socketState == 2); + nextState = SocketState(socketState); +} + +packets::status::serverbound::Request::Request(McDataStream &stream) +{ + Q_UNUSED(stream); +} + +packets::status::serverbound::Ping::Ping(McDataStream &stream) +{ + stream >> payload; +} + +void packets::status::clientbound::Response::serialize(McDataStream &stream) +{ + QByteArray buffer; + McDataStream tempStream(&buffer, QIODevice::WriteOnly); + tempStream.writeVar(PacketResponse); + tempStream.writeVar(jsonResponse); + stream.writeVar(buffer.length()); + stream.writeRawData(buffer.constData(), buffer.length()); +} + +void packets::status::clientbound::Pong::serialize(McDataStream &stream) +{ + QByteArray buffer; + McDataStream tempStream(&buffer, QIODevice::WriteOnly); + tempStream.writeVar(PacketPong); + tempStream << payload; + stream.writeVar(buffer.length()); + stream.writeRawData(buffer.constData(), buffer.length()); +} + +packets::login::serverbound::Login::Login(McDataStream &stream) +{ + name = stream.readVar(); +} + +void packets::login::clientbound::LoginSuccess::serialize(McDataStream &stream) +{ + QByteArray buffer; + McDataStream tempStream(&buffer, QIODevice::WriteOnly); + tempStream.writeVar(PacketLoginSuccess); + tempStream.writeVar(uuid); + tempStream.writeVar(username); + stream.writeVar(buffer.length()); + stream.writeRawData(buffer.constData(), buffer.length()); +} + +packets::play::serverbound::ClientSettings::ClientSettings(McDataStream &stream) +{ + locale = stream.readVar(); + stream >> viewDistance; + chatMode = stream.readVar(); + stream >> chatColors + >> displayedSkinParts; + mainHand = stream.readVar(); +} + +packets::play::serverbound::PluginMessage::PluginMessage(McDataStream &stream) +{ + channel = stream.readVar(); + //TODO read to end of buffer +} + +void packets::play::clientbound::ServerDifficulty::serialize(McDataStream &stream) +{ + QByteArray buffer; + McDataStream tempStream(&buffer, QIODevice::WriteOnly); + tempStream.writeVar(PacketServerDifficulty); + tempStream << difficulty; + stream.writeVar(buffer.length()); + stream.writeRawData(buffer.constData(), buffer.length()); +} + +void packets::play::clientbound::PluginMessage::serialize(McDataStream &stream) +{ + QByteArray buffer; + McDataStream tempStream(&buffer, QIODevice::WriteOnly); + tempStream.writeVar(PacketPluginMessage); + tempStream.writeVar(channel); + buffer.append(data); + stream.writeVar(buffer.length()); + stream.writeRawData(buffer.constData(), buffer.length()); +} + +void packets::play::clientbound::JoinGame::serialize(McDataStream &stream) +{ + QByteArray buffer; + McDataStream tempStream(&buffer, QIODevice::WriteOnly); + tempStream.writeVar(PacketJoinGame); + tempStream << entityid + << gamemode + << dimension + << difficulty + << maxPlayers; + tempStream.writeVar(levelType); + tempStream << reducedDebugInfo; + stream.writeVar(buffer.length()); + stream.writeRawData(buffer.constData(), buffer.length()); +} + +void packets::play::clientbound::PlayerAbilities::serialize(McDataStream &stream) +{ + QByteArray buffer; + McDataStream tempStream(&buffer, QIODevice::WriteOnly); + tempStream.writeVar(PacketPlayerAbilities); + tempStream << flags; + tempStream.writeFloat(flyingSpeed); + tempStream.writeFloat(fieldOfViewModifier); + stream.writeVar(buffer.length()); + stream.writeRawData(buffer.constData(), buffer.length()); +} + +void packets::play::clientbound::PlayerPositionAndLook::serialize(McDataStream &stream) +{ + QByteArray buffer; + McDataStream tempStream(&buffer, QIODevice::WriteOnly); + tempStream.writeVar(PacketPlayerPositionAndLook); + tempStream.writeDouble(x); + tempStream.writeDouble(y); + tempStream.writeDouble(z); + tempStream.writeFloat(yaw); + tempStream.writeFloat(pitch); + tempStream << flags; + tempStream.writeVar(teleportId); + stream.writeVar(buffer.length()); + stream.writeRawData(buffer.constData(), buffer.length()); +} + +void packets::play::clientbound::SpawnPosition::serialize(McDataStream &stream) +{ + QByteArray buffer; + McDataStream tempStream(&buffer, QIODevice::WriteOnly); + tempStream.writeVar(PacketSpawnPosition); + tempStream.writePosition(location); + stream.writeVar(buffer.length()); + stream.writeRawData(buffer.constData(), buffer.length()); +} diff --git a/packets.h b/packets.h new file mode 100644 index 0000000..ccaa68c --- /dev/null +++ b/packets.h @@ -0,0 +1,165 @@ +#pragma once + +#include +#include + +#include + +class McDataStream; + +enum SocketState { HandshakingState, StatusState, LoginState, PlayState, ClosedState }; + +namespace packets { + namespace handshaking { + namespace serverbound { + enum PacketType { PacketHandshake }; + + struct Handshake { + Handshake(McDataStream &stream); + + qint32 protocolVersion; + QString serverAddress; + quint16 serverPort; + SocketState nextState; + }; + } + + namespace clientbound { + } + } + + namespace status { + namespace serverbound { + enum PacketType { PacketRequest, PacketPing }; + + struct Request { + Request(McDataStream &stream); + }; + + struct Ping { + Ping(McDataStream &stream); + + qint64 payload; + }; + } + + namespace clientbound { + enum PacketType { PacketResponse, PacketPong }; + + struct Response { + QString jsonResponse; + + void serialize(McDataStream &stream); + }; + + struct Pong { + qint64 payload; + + void serialize(McDataStream &stream); + }; + } + } + + namespace login { + namespace serverbound { + enum PacketType { PacketLogin }; + + struct Login { + Login(McDataStream &stream); + + QString name; + }; + } + + namespace clientbound { + enum PacketType { PacketLoginSuccess = 0x02 }; + + struct LoginSuccess { + QString uuid; + QString username; + + void serialize(McDataStream &stream); + }; + } + } + + namespace play { + namespace serverbound { + enum PacketType { PacketClientSettings = 0x04, PacketPluginMessage = 0x0A }; + + struct ClientSettings { + ClientSettings(McDataStream &stream); + + QString locale; + qint8 viewDistance; + qint32 chatMode; + bool chatColors; + quint8 displayedSkinParts; + qint32 mainHand; + }; + + struct PluginMessage { + PluginMessage(McDataStream &stream); + + QString channel; + QByteArray data; + }; + } + + namespace clientbound { + enum PacketType { PacketServerDifficulty = 0x0D, PacketPluginMessage = 0x19, PacketJoinGame = 0x25, PacketPlayerAbilities = 0x2E, PacketPlayerPositionAndLook = 0x32, + PacketSpawnPosition = 0x49 }; + + struct ServerDifficulty { + quint8 difficulty; + + void serialize(McDataStream &stream); + }; + + struct PluginMessage { + QString channel; + QByteArray data; + + void serialize(McDataStream &stream); + }; + + struct JoinGame { + qint32 entityid; + quint8 gamemode; + qint32 dimension; + quint8 difficulty; + quint8 maxPlayers; + QString levelType; + bool reducedDebugInfo; + + void serialize(McDataStream &stream); + }; + + struct PlayerAbilities { + qint8 flags; + float flyingSpeed; + float fieldOfViewModifier; + + void serialize(McDataStream &stream); + }; + + struct PlayerPositionAndLook { + double x; + double y; + double z; + float yaw; + float pitch; + qint8 flags; + qint32 teleportId; + + void serialize(McDataStream &stream); + }; + + struct SpawnPosition { + std::tuple location; + + void serialize(McDataStream &stream); + }; + } + } +}