From b39078eedc776a5f360b8f40eeb3b2619148f0c6 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Thu, 16 Jul 2020 23:18:19 +0100 Subject: [PATCH] Splitted different client states into classes --- DbMinecraft.pro | 18 ++- client.h | 45 ------ closedclient.cpp | 49 +++++++ closedclient.h | 32 +++++ handshakingclient.cpp | 76 ++++++++++ handshakingclient.h | 32 +++++ loginclient.cpp | 74 ++++++++++ loginclient.h | 32 +++++ main.cpp | 44 +----- packets.cpp | 3 + packets.h | 29 +++- playclient.cpp | 186 ++++++++++++++++++++++++ playclient.h | 42 ++++++ server.cpp | 47 ++++++ server.h | 31 ++++ client.cpp => statusclient.cpp | 251 +++------------------------------ statusclient.h | 32 +++++ 17 files changed, 699 insertions(+), 324 deletions(-) delete mode 100644 client.h create mode 100644 closedclient.cpp create mode 100644 closedclient.h create mode 100644 handshakingclient.cpp create mode 100644 handshakingclient.h create mode 100644 loginclient.cpp create mode 100644 loginclient.h create mode 100644 playclient.cpp create mode 100644 playclient.h create mode 100644 server.cpp create mode 100644 server.h rename client.cpp => statusclient.cpp (70%) create mode 100644 statusclient.h 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{}; +};