diff --git a/DbMinecraft.pro b/DbMinecraft.pro index afc8a0d..e234c21 100644 --- a/DbMinecraft.pro +++ b/DbMinecraft.pro @@ -5,11 +5,21 @@ CONFIG += c++1z DEFINES += QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000 SOURCES += main.cpp \ - client.cpp \ + closedclient.cpp \ + handshakingclient.cpp \ + loginclient.cpp \ mcdatastream.cpp \ - packets.cpp + packets.cpp \ + playclient.cpp \ + server.cpp \ + statusclient.cpp HEADERS += \ - client.h \ + closedclient.h \ + handshakingclient.h \ + loginclient.h \ mcdatastream.h \ - packets.h + packets.h \ + playclient.h \ + server.h \ + statusclient.h diff --git a/client.h b/client.h deleted file mode 100644 index 3596f2d..0000000 --- a/client.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include - -#include "mcdatastream.h" -#include "packets.h" - -class QTcpSocket; - -class Client : public QObject -{ - Q_OBJECT - -public: - explicit Client(QTcpSocket *socket, QObject *parent = nullptr); - - SocketState state() const { return m_state; } - - void keepAlive(); - void sendChatMessage(); - void trialDisconnect(); - -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; - - const QDateTime m_connectedSince; - QDateTime m_lastKeepAliveSent; - QDateTime m_lastKeepAliveReceived; - QDateTime m_lastChatMessage; -}; diff --git a/closedclient.cpp b/closedclient.cpp new file mode 100644 index 0000000..8d82a93 --- /dev/null +++ b/closedclient.cpp @@ -0,0 +1,49 @@ +#include "closedclient.h" + +#include +#include + +#include "server.h" + +ClosedClient::ClosedClient(QTcpSocket &socket, Server &server) : + QObject{&server}, m_socket{socket}, m_server{server}, m_dataStream{&m_socket} +{ + m_socket.setParent(this); + + connect(&m_socket, &QIODevice::readyRead, this, &ClosedClient::readyRead); + connect(&m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater); + + QTimer::singleShot(1000, this, &QObject::deleteLater); +} + +void ClosedClient::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; + + readPacket(packets::closed::serverbound::PacketType(type), buffer); + } +} + +void ClosedClient::readPacket(packets::closed::serverbound::PacketType type, const QByteArray &buffer) +{ + qWarning() << "does not support receiving any packets" << type << buffer; +} diff --git a/closedclient.h b/closedclient.h new file mode 100644 index 0000000..e6f0b00 --- /dev/null +++ b/closedclient.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "mcdatastream.h" +#include "packets.h" + +class QTcpSocket; +class QByteArray; + +class Server; + +class ClosedClient : public QObject +{ + Q_OBJECT + +public: + explicit ClosedClient(QTcpSocket &socket, Server &server); + +private slots: + void readyRead(); + +private: + void readPacket(packets::closed::serverbound::PacketType type, const QByteArray &buffer); + + QTcpSocket &m_socket; + Server &m_server; + + McDataStream m_dataStream; + + qint32 m_packetSize{}; +}; diff --git a/handshakingclient.cpp b/handshakingclient.cpp new file mode 100644 index 0000000..0b08897 --- /dev/null +++ b/handshakingclient.cpp @@ -0,0 +1,76 @@ +#include "handshakingclient.h" + +#include + +#include "server.h" +#include "statusclient.h" +#include "loginclient.h" + +HandshakingClient::HandshakingClient(QTcpSocket &socket, Server &server) : + QObject{&server}, m_socket{socket}, m_server{server}, m_dataStream{&m_socket} +{ + m_socket.setParent(this); + + connect(&m_socket, &QIODevice::readyRead, this, &HandshakingClient::readyRead); + connect(&m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater); +} + +void HandshakingClient::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; + + readPacket(packets::handshaking::serverbound::PacketType(type), buffer); + } +} + +void HandshakingClient::readPacket(packets::handshaking::serverbound::PacketType type, const QByteArray &buffer) +{ + McDataStream dataStream{const_cast(&buffer), QIODevice::ReadOnly}; + + switch(type) + { + using namespace packets::handshaking; + case serverbound::PacketType::Handshake: + { + qDebug() << type; + serverbound::Handshake packet{dataStream}; + switch (packet.nextState) + { + using namespace serverbound; + case Handshake::SocketState::StatusState: + new StatusClient{m_socket, m_server}; + break; + case Handshake::SocketState::LoginState: + new LoginClient{m_socket, m_server}; + break; + default: + qCritical() << "client requested new state" << packet.nextState << "which is not allowed/invalid"; + } + + deleteLater(); + + break; + } + default: + qWarning() << "unknown packet type" << type; + } +} diff --git a/handshakingclient.h b/handshakingclient.h new file mode 100644 index 0000000..e39fae5 --- /dev/null +++ b/handshakingclient.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "mcdatastream.h" +#include "packets.h" + +class QTcpSocket; +class QByteArray; + +class Server; + +class HandshakingClient : public QObject +{ + Q_OBJECT + +public: + explicit HandshakingClient(QTcpSocket &socket, Server &parent); + +private slots: + void readyRead(); + +private: + void readPacket(packets::handshaking::serverbound::PacketType type, const QByteArray &buffer); + + QTcpSocket &m_socket; + Server &m_server; + + McDataStream m_dataStream; + + qint32 m_packetSize{}; +}; diff --git a/loginclient.cpp b/loginclient.cpp new file mode 100644 index 0000000..ea17f1a --- /dev/null +++ b/loginclient.cpp @@ -0,0 +1,74 @@ +#include "loginclient.h" + +#include + +#include "server.h" +#include "playclient.h" + +LoginClient::LoginClient(QTcpSocket &socket, Server &server) : + QObject{&server}, m_socket{socket}, m_server{server}, m_dataStream{&m_socket} +{ + m_socket.setParent(this); + + connect(&m_socket, &QIODevice::readyRead, this, &LoginClient::readyRead); + connect(&m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater); +} + +void LoginClient::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; + + readPacket(packets::login::serverbound::PacketType(type), buffer); + } +} + +void LoginClient::readPacket(packets::login::serverbound::PacketType type, const QByteArray &buffer) +{ + McDataStream dataStream(const_cast(&buffer), QIODevice::ReadOnly); + + switch(type) + { + using namespace packets::login; + case serverbound::PacketType::Login: + { + qDebug() << type; + 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); + } + new PlayClient{m_socket, m_server}; + deleteLater(); + break; + } + default: + qWarning() << "unknown packet type" << type; + } +} diff --git a/loginclient.h b/loginclient.h new file mode 100644 index 0000000..d338be9 --- /dev/null +++ b/loginclient.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "mcdatastream.h" +#include "packets.h" + +class QTcpSocket; +class QByteArray; + +class Server; + +class LoginClient : public QObject +{ + Q_OBJECT + +public: + explicit LoginClient(QTcpSocket &socket, Server &server); + +private slots: + void readyRead(); + +private: + void readPacket(packets::login::serverbound::PacketType type, const QByteArray &buffer); + + QTcpSocket &m_socket; + Server &m_server; + + McDataStream m_dataStream; + + qint32 m_packetSize{}; +}; diff --git a/main.cpp b/main.cpp index f791fb5..51f5969 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,7 @@ #include #include -#include -#include -#include -#include -#include "client.h" +#include "server.h" int main(int argc, char *argv[]) { @@ -22,43 +18,7 @@ int main(int argc, char *argv[]) "%{function}(): " "%{message}")); - QList> clients; - - QTimer timer; - timer.setInterval(100); - QObject::connect(&timer, &QTimer::timeout, [&clients](){ - const auto now = QDateTime::currentDateTime(); - for (auto iter = std::begin(clients); iter != std::end(clients); ) - { - if ((*iter).isNull()) - { - iter = clients.erase(iter); - continue; - } - - auto &client = **iter; - if (client.state() == PlayState) - { - client.keepAlive(); - client.sendChatMessage(); - client.trialDisconnect(); - } - - iter++; - } - }); - timer.start(); - - QTcpServer server; - - QObject::connect(&server, &QTcpServer::newConnection, [&server,&clients](){ - clients.append(new Client(server.nextPendingConnection())); - }); - - if(!server.listen(QHostAddress::Any, 25565)) - qFatal("could not start listening %s", server.errorString().toUtf8().constData()); - - qDebug() << "started listening"; + Server server; return a.exec(); } diff --git a/packets.cpp b/packets.cpp index d01d4c4..41b9607 100644 --- a/packets.cpp +++ b/packets.cpp @@ -1,5 +1,7 @@ #include "packets.h" +#include + #include "mcdatastream.h" packets::handshaking::serverbound::Handshake::Handshake(McDataStream &stream) @@ -79,6 +81,7 @@ packets::play::serverbound::InteractEntity::InteractEntity(McDataStream &stream) targetX = stream.readFloat(); targetY = stream.readFloat(); targetZ = stream.readFloat(); + [[fallthrough]]; case Interact: hand = Hand(stream.readVar()); } diff --git a/packets.h b/packets.h index b1340e8..94908e1 100644 --- a/packets.h +++ b/packets.h @@ -9,8 +9,6 @@ class McDataStream; -enum SocketState { HandshakingState, StatusState, LoginState, PlayState, ClosedState }; - namespace packets { namespace handshaking { namespace serverbound { @@ -20,16 +18,27 @@ namespace packets { Q_ENUM_NS(PacketType) struct Handshake { + Q_GADGET + + public: Handshake(McDataStream &stream); qint32 protocolVersion; QString serverAddress; quint16 serverPort; + + enum class SocketState { HandshakingState, StatusState, LoginState, PlayState, ClosedState }; + Q_ENUM(SocketState) + SocketState nextState; }; } namespace clientbound { + Q_NAMESPACE + + enum class PacketType {}; + Q_ENUM_NS(PacketType) } } @@ -220,4 +229,20 @@ namespace packets { }; } } + + namespace closed { + namespace serverbound { + Q_NAMESPACE + + enum class PacketType {}; + Q_ENUM_NS(PacketType) + } + + namespace clientbound { + Q_NAMESPACE + + enum class PacketType {}; + Q_ENUM_NS(PacketType) + } + } } diff --git a/playclient.cpp b/playclient.cpp new file mode 100644 index 0000000..af0b981 --- /dev/null +++ b/playclient.cpp @@ -0,0 +1,186 @@ +#include "playclient.h" + +#include +#include + +#include "server.h" +#include "closedclient.h" + +PlayClient::PlayClient(QTcpSocket &socket, Server &server) : + QObject{&server}, m_socket{socket}, m_server{server}, m_dataStream{&m_socket} +{ + m_server.add(*this); + + m_socket.setParent(this); + + connect(&m_socket, &QIODevice::readyRead, this, &PlayClient::readyRead); + connect(&m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater); + + { + packets::play::clientbound::JoinGame packet; + packet.entityid = 1; + packet.gamemode = packets::play::clientbound::JoinGame::Creative; + packet.dimension = packets::play::clientbound::JoinGame::Overworld; + 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); + } +} + +PlayClient::~PlayClient() +{ + m_server.remove(*this); +} + +void PlayClient::keepAlive() +{ + const auto now = QDateTime::currentDateTime(); + if (!m_lastKeepAliveSent.isValid() || m_lastKeepAliveSent.secsTo(now) >= 1) + { + packets::play::clientbound::KeepAlive packet; + packet.keepAliveId = 0; + packet.serialize(m_dataStream); + m_lastKeepAliveSent = now; + } +} + +void PlayClient::sendChatMessage() +{ + const auto now = QDateTime::currentDateTime(); + if (!m_lastChatMessage.isValid() || m_lastChatMessage.secsTo(now) >= 2) + { + packets::play::clientbound::ChatMessage packet; + packet.jsonData = "{" + "\"text\": \"Chat message\", " + "\"bold\": \"true\" " + "}"; + packet.position = packets::play::clientbound::ChatMessage::Chat; + packet.serialize(m_dataStream); + m_lastChatMessage = now; + } +} + +void PlayClient::trialDisconnect() +{ + const auto now = QDateTime::currentDateTime(); + if (m_connectedSince.secsTo(now) >= 20) + { + packets::play::clientbound::Disconnect packet; + packet.reason = "{" + "\"text\": \"Your trial has ended.\", " + "\"bold\": \"true\" " + "}"; + packet.serialize(m_dataStream); + m_socket.flush(); + new ClosedClient{m_socket, m_server}; + deleteLater(); + } +} + +void PlayClient::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; + + readPacket(packets::play::serverbound::PacketType(type), buffer); + } +} + +void PlayClient::readPacket(packets::play::serverbound::PacketType type, const QByteArray &buffer) +{ + McDataStream dataStream(const_cast(&buffer), QIODevice::ReadOnly); + + switch(type) + { + using namespace packets::play; + case serverbound::PacketType::ClientSettings: + { + qDebug() << type; + { + 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::PacketType::InteractEntity: + { + qDebug() << type; + serverbound::InteractEntity packet{dataStream}; + qDebug() << "entityId" << packet.entityId; + qDebug() << "type" << packet.type; +// qDebug() << "targetX" << packet.targetX; +// qDebug() << "targetY" << packet.targetY; +// qDebug() << "targetZ" << packet.targetZ; +// qDebug() << "hand" << packet.hand; + break; + } + case serverbound::PacketType::PluginMessage: + { + qDebug() << type; + serverbound::PluginMessage packet{dataStream}; + qDebug() << "channel" << packet.channel; + break; + } + default: + qWarning() << "unknown packet type" << type; + } +} diff --git a/playclient.h b/playclient.h new file mode 100644 index 0000000..203353a --- /dev/null +++ b/playclient.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include "mcdatastream.h" +#include "packets.h" + +class QTcpSocket; +class QByteArray; + +class Server; + +class PlayClient : public QObject +{ + Q_OBJECT + +public: + explicit PlayClient(QTcpSocket &socket, Server &server); + ~PlayClient() override; + + void keepAlive(); + void sendChatMessage(); + void trialDisconnect(); + +private slots: + void readyRead(); + +private: + void readPacket(packets::play::serverbound::PacketType type, const QByteArray &buffer); + + QTcpSocket &m_socket; + Server &m_server; + + McDataStream m_dataStream; + + qint32 m_packetSize{}; + + const QDateTime m_connectedSince{QDateTime::currentDateTime()}; + QDateTime m_lastKeepAliveSent; + QDateTime m_lastKeepAliveReceived; + QDateTime m_lastChatMessage; +}; diff --git a/server.cpp b/server.cpp new file mode 100644 index 0000000..94e19e6 --- /dev/null +++ b/server.cpp @@ -0,0 +1,47 @@ +#include "server.h" + +#include + +#include "playclient.h" +#include "handshakingclient.h" + +Server::Server(QObject *parent) : + QObject{parent} +{ + m_timer.setInterval(100); + connect(&m_timer, &QTimer::timeout, this, &Server::timeout); + m_timer.start(); + + connect(&m_server, &QTcpServer::newConnection, this, &Server::newConnection); + + if(!m_server.listen(QHostAddress::Any, 25565)) + qFatal("could not start listening %s", m_server.errorString().toUtf8().constData()); +} + +void Server::add(PlayClient &playClient) +{ + m_playClients.insert(&playClient); +} + +void Server::remove(PlayClient &playClient) +{ + m_playClients.erase(&playClient); +} + +void Server::timeout() +{ + for (auto client : m_playClients) + { + client->keepAlive(); + client->sendChatMessage(); + client->trialDisconnect(); + } +} + +void Server::newConnection() +{ + auto * const connection = m_server.nextPendingConnection(); + if (connection) + new HandshakingClient{*connection, *this}; + //clients.push_back(); +} diff --git a/server.h b/server.h new file mode 100644 index 0000000..afa1bf3 --- /dev/null +++ b/server.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include + +#include + +class PlayClient; + +class Server : public QObject +{ + Q_OBJECT + +public: + Server(QObject *parent = nullptr); + + void add(PlayClient &playClient); + void remove(PlayClient &playClient); + +private slots: + void timeout(); + void newConnection(); + +private: + QTimer m_timer; + QTcpServer m_server; + + std::set m_playClients; +}; diff --git a/client.cpp b/statusclient.cpp similarity index 70% rename from client.cpp rename to statusclient.cpp index 66e0481..30b165c 100644 --- a/client.cpp +++ b/statusclient.cpp @@ -1,69 +1,21 @@ -#include "client.h" +#include "statusclient.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_connectedSince(QDateTime::currentDateTime()) +#include "server.h" + +StatusClient::StatusClient(QTcpSocket &socket, Server &server) : + QObject{&server}, m_socket{socket}, m_server{server}, m_dataStream{&m_socket} { - m_socket->setParent(this); + m_socket.setParent(this); - connect(m_socket, &QIODevice::readyRead, this, &Client::readyRead); - connect(m_socket, &QAbstractSocket::disconnected, this, &Client::disconnected); - - qDebug() << m_socket->peerPort(); + connect(&m_socket, &QIODevice::readyRead, this, &StatusClient::readyRead); + connect(&m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater); } -void Client::keepAlive() +void StatusClient::readyRead() { - const auto now = QDateTime::currentDateTime(); - if (!m_lastKeepAliveSent.isValid() || m_lastKeepAliveSent.secsTo(now) >= 1) - { - packets::play::clientbound::KeepAlive packet; - packet.keepAliveId = 0; - packet.serialize(m_dataStream); - m_lastKeepAliveSent = now; - } -} - -void Client::sendChatMessage() -{ - const auto now = QDateTime::currentDateTime(); - if (!m_lastChatMessage.isValid() || m_lastChatMessage.secsTo(now) >= 2) - { - packets::play::clientbound::ChatMessage packet; - packet.jsonData = "{" - "\"text\": \"Chat message\", " - "\"bold\": \"true\" " - "}"; - packet.position = packets::play::clientbound::ChatMessage::Chat; - packet.serialize(m_dataStream); - m_lastChatMessage = now; - } -} - -void Client::trialDisconnect() -{ - const auto now = QDateTime::currentDateTime(); - if (m_connectedSince.secsTo(now) >= 20) - { - packets::play::clientbound::Disconnect packet; - packet.reason = "{" - "\"text\": \"Your trial has ended.\", " - "\"bold\": \"true\" " - "}"; - packet.serialize(m_dataStream); - m_socket->flush(); - deleteLater(); - } -} - -void Client::readyRead() -{ - while(m_socket->bytesAvailable()) + while(m_socket.bytesAvailable()) { if(!m_packetSize) { @@ -71,69 +23,25 @@ void Client::readyRead() qDebug() << "packet size" << m_packetSize; } - if(m_socket->bytesAvailable() < m_packetSize) + if(m_socket.bytesAvailable() < m_packetSize) { - qWarning() << "packet not fully available" << m_socket->bytesAvailable(); + 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); + 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; - } + readPacket(packets::status::serverbound::PacketType(type), buffer); } } -void Client::disconnected() +void StatusClient::readPacket(packets::status::serverbound::PacketType type, const QByteArray &buffer) { - 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::PacketType::Handshake: - { - 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) @@ -141,8 +49,9 @@ void Client::readPacketStatus(const packets::status::serverbound::PacketType typ using namespace packets::status; case serverbound::PacketType::Request: { + qDebug() << type; { - serverbound::Request packet(dataStream); + serverbound::Request packet{dataStream}; } { clientbound::Response packet; @@ -173,9 +82,10 @@ void Client::readPacketStatus(const packets::status::serverbound::PacketType typ } case serverbound::PacketType::Ping: { + qDebug() << type; qint64 payload; { - serverbound::Ping packet(dataStream); + serverbound::Ping packet{dataStream}; payload = packet.payload; } { @@ -186,127 +96,6 @@ void Client::readPacketStatus(const packets::status::serverbound::PacketType typ 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::PacketType::Login: - { - 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 = packets::play::clientbound::JoinGame::Creative; - packet.dimension = packets::play::clientbound::JoinGame::Overworld; - 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::PacketType::ClientSettings: - { - { - 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::PacketType::InteractEntity: - { - serverbound::InteractEntity packet(dataStream); - qDebug() << "entityId" << packet.entityId; - qDebug() << "type" << packet.type; -// qDebug() << "targetX" << packet.targetX; -// qDebug() << "targetY" << packet.targetY; -// qDebug() << "targetZ" << packet.targetZ; -// qDebug() << "hand" << packet.hand; - break; - } - case serverbound::PacketType::PluginMessage: - { - serverbound::PluginMessage packet(dataStream); - qDebug() << "channel" << packet.channel; - break; - } - default: - qWarning() << "unknown type!"; + qWarning() << "unknown packet type" << type; } } diff --git a/statusclient.h b/statusclient.h new file mode 100644 index 0000000..f335a55 --- /dev/null +++ b/statusclient.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "mcdatastream.h" +#include "packets.h" + +class QTcpSocket; +class QByteArray; + +class Server; + +class StatusClient : public QObject +{ + Q_OBJECT + +public: + explicit StatusClient(QTcpSocket &socket, Server &server); + +private slots: + void readyRead(); + +private: + void readPacket(packets::status::serverbound::PacketType type, const QByteArray &buffer); + + QTcpSocket &m_socket; + Server &m_server; + + McDataStream m_dataStream; + + qint32 m_packetSize{}; +};