Memory management of socket using unique_ptr, many new util functions

This commit is contained in:
2020-07-18 00:24:47 +02:00
parent b39078eedc
commit fcadff65e2
20 changed files with 365 additions and 101 deletions

View File

@@ -5,6 +5,8 @@ CONFIG += c++1z
DEFINES += QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000 DEFINES += QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000
SOURCES += main.cpp \ SOURCES += main.cpp \
chathelper.cpp \
chunkhelper.cpp \
closedclient.cpp \ closedclient.cpp \
handshakingclient.cpp \ handshakingclient.cpp \
loginclient.cpp \ loginclient.cpp \
@@ -15,6 +17,8 @@ SOURCES += main.cpp \
statusclient.cpp statusclient.cpp
HEADERS += \ HEADERS += \
chathelper.h \
chunkhelper.h \
closedclient.h \ closedclient.h \
handshakingclient.h \ handshakingclient.h \
loginclient.h \ loginclient.h \

25
chathelper.cpp Normal file
View File

@@ -0,0 +1,25 @@
#include "chathelper.h"
#include <QJsonDocument>
QJsonObject normalTextObj(const QString &text)
{
return QJsonObject { { "text", text } };
}
QJsonObject boldTextObj(const QString &text)
{
QJsonObject obj = normalTextObj(text);
obj["bold"] = true;
return obj;
}
QString normalText(const QString &text)
{
return QJsonDocument{normalTextObj(text)}.toJson();
}
QString boldText(const QString &text)
{
return QJsonDocument{boldTextObj(text)}.toJson();
}

10
chathelper.h Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
#include <QString>
#include <QJsonObject>
QJsonObject normalTextObj(const QString &text);
QJsonObject boldTextObj(const QString &text);
QString normalText(const QString &text);
QString boldText(const QString &text);

38
chunkhelper.cpp Normal file
View File

@@ -0,0 +1,38 @@
#include "chunkhelper.h"
#include <algorithm>
#include <array>
#include "mcdatastream.h"
QByteArray createChunkSection()
{
quint8 bitsPerBlock = 8;
std::array<qint8, 2> palette { 0, 1 };
std::array<qint64, 4096> blocks;
QByteArray buffer;
McDataStream tempStream(&buffer, QIODevice::WriteOnly);
tempStream << bitsPerBlock;
tempStream.writeVar<qint32>(palette.size());
for (const auto &entry : palette)
tempStream.writeVar<qint32>(entry);
tempStream.writeVar<qint32>(blocks.size());
for (const auto &block : blocks)
tempStream << block;
for (int i = 0; i < blocks.size(); i++)
tempStream << '\xFF';
return buffer;
}
QByteArray createBiomes()
{
qint32 biomes[256];
std::fill(std::begin(biomes), std::end(biomes), 0);
QByteArray buffer;
McDataStream tempStream(&buffer, QIODevice::WriteOnly);
for (const auto &biome : biomes)
tempStream << biome;
return buffer;
}

7
chunkhelper.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
#include <QByteArray>
QByteArray createChunkSection();
QByteArray createBiomes();

View File

@@ -5,20 +5,22 @@
#include "server.h" #include "server.h"
ClosedClient::ClosedClient(QTcpSocket &socket, Server &server) : ClosedClient::ClosedClient(std::unique_ptr<QTcpSocket> &&socket, Server &server) :
QObject{&server}, m_socket{socket}, m_server{server}, m_dataStream{&m_socket} QObject{&server}, m_socket{std::move(socket)}, m_server{server}, m_dataStream{m_socket.get()}
{ {
m_socket.setParent(this); m_socket->setParent(this);
connect(&m_socket, &QIODevice::readyRead, this, &ClosedClient::readyRead); connect(m_socket.get(), &QIODevice::readyRead, this, &ClosedClient::readyRead);
connect(&m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater); connect(m_socket.get(), &QAbstractSocket::disconnected, this, &QObject::deleteLater);
QTimer::singleShot(1000, this, &QObject::deleteLater); QTimer::singleShot(1000, this, &QObject::deleteLater);
} }
ClosedClient::~ClosedClient() = default;
void ClosedClient::readyRead() void ClosedClient::readyRead()
{ {
while(m_socket.bytesAvailable()) while(m_socket && m_socket->bytesAvailable())
{ {
if(!m_packetSize) if(!m_packetSize)
{ {
@@ -26,16 +28,16 @@ void ClosedClient::readyRead()
qDebug() << "packet size" << m_packetSize; 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; return;
} }
qint32 bytesRead; qint32 bytesRead;
const auto type = m_dataStream.readVar<qint32>(bytesRead); const auto type = m_dataStream.readVar<qint32>(bytesRead);
m_packetSize -= 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); Q_ASSERT(buffer.size() == m_packetSize);
m_packetSize = 0; m_packetSize = 0;

View File

@@ -15,7 +15,8 @@ class ClosedClient : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit ClosedClient(QTcpSocket &socket, Server &server); explicit ClosedClient(std::unique_ptr<QTcpSocket> &&socket, Server &server);
~ClosedClient() override;
private slots: private slots:
void readyRead(); void readyRead();
@@ -23,7 +24,7 @@ private slots:
private: private:
void readPacket(packets::closed::serverbound::PacketType type, const QByteArray &buffer); void readPacket(packets::closed::serverbound::PacketType type, const QByteArray &buffer);
QTcpSocket &m_socket; std::unique_ptr<QTcpSocket> m_socket;
Server &m_server; Server &m_server;
McDataStream m_dataStream; McDataStream m_dataStream;

View File

@@ -6,18 +6,20 @@
#include "statusclient.h" #include "statusclient.h"
#include "loginclient.h" #include "loginclient.h"
HandshakingClient::HandshakingClient(QTcpSocket &socket, Server &server) : HandshakingClient::HandshakingClient(std::unique_ptr<QTcpSocket> &&socket, Server &server) :
QObject{&server}, m_socket{socket}, m_server{server}, m_dataStream{&m_socket} QObject{&server}, m_socket{std::move(socket)}, m_server{server}, m_dataStream{m_socket.get()}
{ {
m_socket.setParent(this); m_socket->setParent(this);
connect(&m_socket, &QIODevice::readyRead, this, &HandshakingClient::readyRead); connect(m_socket.get(), &QIODevice::readyRead, this, &HandshakingClient::readyRead);
connect(&m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater); connect(m_socket.get(), &QAbstractSocket::disconnected, this, &QObject::deleteLater);
} }
HandshakingClient::~HandshakingClient() = default;
void HandshakingClient::readyRead() void HandshakingClient::readyRead()
{ {
while(m_socket.bytesAvailable()) while(m_socket && m_socket->bytesAvailable())
{ {
if(!m_packetSize) if(!m_packetSize)
{ {
@@ -25,16 +27,16 @@ void HandshakingClient::readyRead()
qDebug() << "packet size" << m_packetSize; 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; return;
} }
qint32 bytesRead; qint32 bytesRead;
const auto type = m_dataStream.readVar<qint32>(bytesRead); const auto type = m_dataStream.readVar<qint32>(bytesRead);
m_packetSize -= 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); Q_ASSERT(buffer.size() == m_packetSize);
m_packetSize = 0; m_packetSize = 0;
@@ -57,10 +59,12 @@ void HandshakingClient::readPacket(packets::handshaking::serverbound::PacketType
{ {
using namespace serverbound; using namespace serverbound;
case Handshake::SocketState::StatusState: case Handshake::SocketState::StatusState:
new StatusClient{m_socket, m_server}; m_dataStream.setDevice({});
new StatusClient{std::move(m_socket), m_server};
break; break;
case Handshake::SocketState::LoginState: case Handshake::SocketState::LoginState:
new LoginClient{m_socket, m_server}; m_dataStream.setDevice({});
new LoginClient{std::move(m_socket), m_server};
break; break;
default: default:
qCritical() << "client requested new state" << packet.nextState << "which is not allowed/invalid"; qCritical() << "client requested new state" << packet.nextState << "which is not allowed/invalid";

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include <memory>
#include <QObject> #include <QObject>
#include "mcdatastream.h" #include "mcdatastream.h"
@@ -15,7 +17,8 @@ class HandshakingClient : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit HandshakingClient(QTcpSocket &socket, Server &parent); explicit HandshakingClient(std::unique_ptr<QTcpSocket> &&socket, Server &parent);
~HandshakingClient() override;
private slots: private slots:
void readyRead(); void readyRead();
@@ -23,7 +26,7 @@ private slots:
private: private:
void readPacket(packets::handshaking::serverbound::PacketType type, const QByteArray &buffer); void readPacket(packets::handshaking::serverbound::PacketType type, const QByteArray &buffer);
QTcpSocket &m_socket; std::unique_ptr<QTcpSocket> m_socket;
Server &m_server; Server &m_server;
McDataStream m_dataStream; McDataStream m_dataStream;

View File

@@ -5,18 +5,20 @@
#include "server.h" #include "server.h"
#include "playclient.h" #include "playclient.h"
LoginClient::LoginClient(QTcpSocket &socket, Server &server) : LoginClient::LoginClient(std::unique_ptr<QTcpSocket> &&socket, Server &server) :
QObject{&server}, m_socket{socket}, m_server{server}, m_dataStream{&m_socket} QObject{&server}, m_socket{std::move(socket)}, m_server{server}, m_dataStream{m_socket.get()}
{ {
m_socket.setParent(this); m_socket->setParent(this);
connect(&m_socket, &QIODevice::readyRead, this, &LoginClient::readyRead); connect(m_socket.get(), &QIODevice::readyRead, this, &LoginClient::readyRead);
connect(&m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater); connect(m_socket.get(), &QAbstractSocket::disconnected, this, &QObject::deleteLater);
} }
LoginClient::~LoginClient() = default;
void LoginClient::readyRead() void LoginClient::readyRead()
{ {
while(m_socket.bytesAvailable()) while(m_socket && m_socket->bytesAvailable())
{ {
if(!m_packetSize) if(!m_packetSize)
{ {
@@ -24,16 +26,16 @@ void LoginClient::readyRead()
qDebug() << "packet size" << m_packetSize; 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; return;
} }
qint32 bytesRead; qint32 bytesRead;
const auto type = m_dataStream.readVar<qint32>(bytesRead); const auto type = m_dataStream.readVar<qint32>(bytesRead);
m_packetSize -= 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); Q_ASSERT(buffer.size() == m_packetSize);
m_packetSize = 0; m_packetSize = 0;
@@ -64,7 +66,8 @@ void LoginClient::readPacket(packets::login::serverbound::PacketType type, const
packet.username = name; packet.username = name;
packet.serialize(m_dataStream); packet.serialize(m_dataStream);
} }
new PlayClient{m_socket, m_server}; m_dataStream.setDevice({});
new PlayClient{std::move(m_socket), m_server};
deleteLater(); deleteLater();
break; break;
} }

View File

@@ -15,7 +15,8 @@ class LoginClient : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit LoginClient(QTcpSocket &socket, Server &server); explicit LoginClient(std::unique_ptr<QTcpSocket> &&socket, Server &server);
~LoginClient() override;
private slots: private slots:
void readyRead(); void readyRead();
@@ -23,7 +24,7 @@ private slots:
private: private:
void readPacket(packets::login::serverbound::PacketType type, const QByteArray &buffer); void readPacket(packets::login::serverbound::PacketType type, const QByteArray &buffer);
QTcpSocket &m_socket; std::unique_ptr<QTcpSocket> m_socket;
Server &m_server; Server &m_server;
McDataStream m_dataStream; McDataStream m_dataStream;

View File

@@ -93,7 +93,8 @@ void McDataStream::writeUuid(const QUuid &uuid)
{ {
Q_UNUSED(uuid) Q_UNUSED(uuid)
writeRawData("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16); const char buf[16]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
writeRawData(buf, 16);
} }
template<> template<>

View File

@@ -93,6 +93,22 @@ packets::play::serverbound::PluginMessage::PluginMessage(McDataStream &stream)
//TODO read to end of buffer //TODO read to end of buffer
} }
void packets::play::clientbound::SpawnMob::serialize(McDataStream &stream)
{
QByteArray buffer;
McDataStream tempStream(&buffer, QIODevice::WriteOnly);
tempStream.writeVar<qint32>(qint32(PacketType::SpawnMob));
tempStream.writeVar<qint32>(entityId);
tempStream.writeUuid(uuid);
tempStream.writeVar<qint32>(type);
tempStream.writeDouble(x);
tempStream.writeDouble(y);
tempStream.writeDouble(z);
tempStream << yaw << pitch << headPitch << velocityX << velocityY << velocityZ;
stream.writeVar<qint32>(buffer.length());
stream.writeRawData(buffer.constData(), buffer.length());
}
void packets::play::clientbound::ServerDifficulty::serialize(McDataStream &stream) void packets::play::clientbound::ServerDifficulty::serialize(McDataStream &stream)
{ {
QByteArray buffer; QByteArray buffer;
@@ -179,6 +195,30 @@ void packets::play::clientbound::PlayerPositionAndLook::serialize(McDataStream &
stream.writeRawData(buffer.constData(), buffer.length()); stream.writeRawData(buffer.constData(), buffer.length());
} }
void packets::play::clientbound::SetExperience::serialize(McDataStream &stream)
{
QByteArray buffer;
McDataStream tempStream(&buffer, QIODevice::WriteOnly);
tempStream.writeVar<qint32>(qint32(PacketType::SetExperience));
tempStream.writeFloat(experienceBar);
tempStream.writeVar<qint32>(level);
tempStream.writeVar<qint32>(totalExperience);
stream.writeVar<qint32>(buffer.length());
stream.writeRawData(buffer.constData(), buffer.length());
}
void packets::play::clientbound::UpdateHealth::serialize(McDataStream &stream)
{
QByteArray buffer;
McDataStream tempStream(&buffer, QIODevice::WriteOnly);
tempStream.writeVar<qint32>(qint32(PacketType::UpdateHealth));
tempStream.writeFloat(health);
tempStream.writeVar<qint32>(food);
tempStream.writeFloat(foodSaturation);
stream.writeVar<qint32>(buffer.length());
stream.writeRawData(buffer.constData(), buffer.length());
}
void packets::play::clientbound::SpawnPosition::serialize(McDataStream &stream) void packets::play::clientbound::SpawnPosition::serialize(McDataStream &stream)
{ {
QByteArray buffer; QByteArray buffer;
@@ -198,3 +238,19 @@ void packets::play::clientbound::KeepAlive::serialize(McDataStream &stream)
stream.writeVar<qint32>(buffer.length()); stream.writeVar<qint32>(buffer.length());
stream.writeRawData(buffer.constData(), buffer.length()); stream.writeRawData(buffer.constData(), buffer.length());
} }
void packets::play::clientbound::ChunkData::serialize(McDataStream &stream)
{
QByteArray buffer;
McDataStream tempStream(&buffer, QIODevice::WriteOnly);
tempStream.writeVar<qint32>(qint32(PacketType::ChunkData));
tempStream << chunkX << chunkY << fullChunk;
tempStream.writeVar<qint32>(primaryBitMask);
tempStream.writeVar<qint32>(data.size());
tempStream.writeRawData(data.constData(), data.size());
tempStream.writeVar<qint32>(blockEntities.size());
for (const auto &blockEntitiy : blockEntities)
tempStream.writeRawData(blockEntitiy.constData(), blockEntitiy.size());
stream.writeVar<qint32>(buffer.length());
stream.writeRawData(buffer.constData(), buffer.length());
}

View File

@@ -1,15 +1,18 @@
#pragma once #pragma once
#include <tuple>
#include <optional>
#include <QtGlobal> #include <QtGlobal>
#include <QString> #include <QString>
#include <QDebug> #include <QDebug>
#include <QtCore> #include <QtCore>
#include <tuple>
class McDataStream; class McDataStream;
namespace packets { namespace packets {
using angle = quint8; // A rotation angle in steps of 1/256 of a full turn
namespace handshaking { namespace handshaking {
namespace serverbound { namespace serverbound {
Q_NAMESPACE Q_NAMESPACE
@@ -151,10 +154,29 @@ namespace packets {
namespace clientbound { namespace clientbound {
Q_NAMESPACE Q_NAMESPACE
enum class PacketType { ServerDifficulty = 0x0D, ChatMessage = 0x0E, PluginMessage = 0x19, Disconnect = 0x1B, KeepAlive = 0x21, enum class PacketType { SpawnMob = 0x03, ServerDifficulty = 0x0D, ChatMessage = 0x0E, PluginMessage = 0x19, Disconnect = 0x1B,
JoinGame = 0x25, PlayerAbilities = 0x2E, PlayerPositionAndLook = 0x32, SpawnPosition = 0x49 }; KeepAlive = 0x21, ChunkData = 0x22, JoinGame = 0x25, PlayerAbilities = 0x2E, PlayerPositionAndLook = 0x32,
SetExperience = 0x43, UpdateHealth = 0x44, SpawnPosition = 0x49 };
Q_ENUM_NS(PacketType) Q_ENUM_NS(PacketType)
// does not work yet, client shows exception
struct SpawnMob {
qint32 entityId;
QUuid uuid;
qint32 type;
double x;
double y;
double z;
angle yaw;
angle pitch;
angle headPitch;
qint16 velocityX;
qint16 velocityY;
qint16 velocityZ;
void serialize(McDataStream &stream);
};
struct ServerDifficulty { struct ServerDifficulty {
quint8 difficulty; quint8 difficulty;
@@ -216,6 +238,22 @@ namespace packets {
void serialize(McDataStream &stream); void serialize(McDataStream &stream);
}; };
struct SetExperience {
float experienceBar;
qint32 level;
qint32 totalExperience;
void serialize(McDataStream &stream);
};
struct UpdateHealth {
float health;
qint32 food;
float foodSaturation;
void serialize(McDataStream &stream);
};
struct SpawnPosition { struct SpawnPosition {
std::tuple<qint32, qint16, qint32> location; std::tuple<qint32, qint16, qint32> location;
@@ -227,6 +265,17 @@ namespace packets {
void serialize(McDataStream &stream); void serialize(McDataStream &stream);
}; };
struct ChunkData {
qint32 chunkX;
qint32 chunkY;
bool fullChunk;
qint32 primaryBitMask;
QByteArray data;
std::vector<QByteArray> blockEntities;
void serialize(McDataStream &stream);
};
} }
} }

View File

@@ -5,21 +5,23 @@
#include "server.h" #include "server.h"
#include "closedclient.h" #include "closedclient.h"
#include "chathelper.h"
#include "chunkhelper.h"
PlayClient::PlayClient(QTcpSocket &socket, Server &server) : PlayClient::PlayClient(std::unique_ptr<QTcpSocket> &&socket, Server &server) :
QObject{&server}, m_socket{socket}, m_server{server}, m_dataStream{&m_socket} QObject{&server}, m_socket{std::move(socket)}, m_server{server}, m_dataStream{m_socket.get()}
{ {
m_server.add(*this); m_server.add(*this);
m_socket.setParent(this); m_socket->setParent(this);
connect(&m_socket, &QIODevice::readyRead, this, &PlayClient::readyRead); connect(m_socket.get(), &QIODevice::readyRead, this, &PlayClient::readyRead);
connect(&m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater); connect(m_socket.get(), &QAbstractSocket::disconnected, this, &QObject::deleteLater);
{ {
packets::play::clientbound::JoinGame packet; packets::play::clientbound::JoinGame packet;
packet.entityid = 1; packet.entityid = 1;
packet.gamemode = packets::play::clientbound::JoinGame::Creative; packet.gamemode = packets::play::clientbound::JoinGame::Survival;
packet.dimension = packets::play::clientbound::JoinGame::Overworld; packet.dimension = packets::play::clientbound::JoinGame::Overworld;
packet.difficulty = 2; packet.difficulty = 2;
packet.maxPlayers = 255; packet.maxPlayers = 255;
@@ -50,6 +52,38 @@ PlayClient::PlayClient(QTcpSocket &socket, Server &server) :
packet.fieldOfViewModifier = 60.; packet.fieldOfViewModifier = 60.;
packet.serialize(m_dataStream); packet.serialize(m_dataStream);
} }
{
packets::play::clientbound::ChunkData packet;
packet.fullChunk = true;
packet.primaryBitMask = 0b11000; // send only 2 chunks
packet.data += createChunkSection();
packet.data += createChunkSection();
packet.data += createBiomes(); // we are in Overworld
for (int y = -3; y <= 3; y++)
for (int x = -3; x <= 3; x++)
{
packet.chunkX = x;
packet.chunkY = y;
packet.serialize(m_dataStream);
}
}
// {
// packets::play::clientbound::SpawnMob packet;
// packet.entityId = 2;
// packet.uuid = QUuid::createUuid();
// packet.type = 19;
// packet.x = 50.;
// packet.y = 50.;
// packet.z = 50.;
// packet.yaw = 0;
// packet.pitch = 0;
// packet.headPitch = 0;
// packet.velocityX = 0;
// packet.velocityY = 0;
// packet.velocityZ = 0;
// packet.serialize(m_dataStream);
// }
} }
PlayClient::~PlayClient() PlayClient::~PlayClient()
@@ -75,36 +109,55 @@ void PlayClient::sendChatMessage()
if (!m_lastChatMessage.isValid() || m_lastChatMessage.secsTo(now) >= 2) if (!m_lastChatMessage.isValid() || m_lastChatMessage.secsTo(now) >= 2)
{ {
packets::play::clientbound::ChatMessage packet; packets::play::clientbound::ChatMessage packet;
packet.jsonData = "{" packet.jsonData = normalText(tr("Time chaged to %0").arg(QTime::currentTime().toString()));
"\"text\": \"Chat message\", "
"\"bold\": \"true\" "
"}";
packet.position = packets::play::clientbound::ChatMessage::Chat; packet.position = packets::play::clientbound::ChatMessage::Chat;
packet.serialize(m_dataStream); packet.serialize(m_dataStream);
m_lastChatMessage = now; m_lastChatMessage = now;
} }
} }
void PlayClient::randomizeStats()
{
const auto now = QDateTime::currentDateTime();
if (!m_lastStats.isValid() || m_lastStats.msecsTo(now) >= 500)
{
{
packets::play::clientbound::SetExperience packet;
packet.experienceBar = QRandomGenerator::system()->generateDouble();
packet.level = QRandomGenerator::system()->bounded(50);
packet.totalExperience = QRandomGenerator::system()->bounded(500);
packet.serialize(m_dataStream);
}
{
packets::play::clientbound::UpdateHealth packet;
packet.health = QRandomGenerator::system()->generateDouble() * 20;
packet.food = QRandomGenerator::system()->bounded(20);
packet.foodSaturation = QRandomGenerator::system()->generateDouble() * 5;
packet.serialize(m_dataStream);
}
m_lastStats = now;
}
}
void PlayClient::trialDisconnect() void PlayClient::trialDisconnect()
{ {
const auto now = QDateTime::currentDateTime(); const auto now = QDateTime::currentDateTime();
if (m_connectedSince.secsTo(now) >= 20) if (m_connectedSince.secsTo(now) >= 30)
{ {
packets::play::clientbound::Disconnect packet; packets::play::clientbound::Disconnect packet;
packet.reason = "{" packet.reason = boldText("Your trial has ended.");
"\"text\": \"Your trial has ended.\", "
"\"bold\": \"true\" "
"}";
packet.serialize(m_dataStream); packet.serialize(m_dataStream);
m_socket.flush(); m_socket->flush();
new ClosedClient{m_socket, m_server}; m_dataStream.setDevice({});
new ClosedClient{std::move(m_socket), m_server};
deleteLater(); deleteLater();
} }
} }
void PlayClient::readyRead() void PlayClient::readyRead()
{ {
while(m_socket.bytesAvailable()) while(m_socket && m_socket->bytesAvailable())
{ {
if(!m_packetSize) if(!m_packetSize)
{ {
@@ -112,16 +165,16 @@ void PlayClient::readyRead()
qDebug() << "packet size" << m_packetSize; 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; return;
} }
qint32 bytesRead; qint32 bytesRead;
const auto type = m_dataStream.readVar<qint32>(bytesRead); const auto type = m_dataStream.readVar<qint32>(bytesRead);
m_packetSize -= 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); Q_ASSERT(buffer.size() == m_packetSize);
m_packetSize = 0; m_packetSize = 0;
@@ -181,6 +234,6 @@ void PlayClient::readPacket(packets::play::serverbound::PacketType type, const Q
break; break;
} }
default: default:
qWarning() << "unknown packet type" << type; qWarning() << "unknown packet type" << type << buffer;
} }
} }

View File

@@ -15,11 +15,12 @@ class PlayClient : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit PlayClient(QTcpSocket &socket, Server &server); explicit PlayClient(std::unique_ptr<QTcpSocket> &&socket, Server &server);
~PlayClient() override; ~PlayClient() override;
void keepAlive(); void keepAlive();
void sendChatMessage(); void sendChatMessage();
void randomizeStats();
void trialDisconnect(); void trialDisconnect();
private slots: private slots:
@@ -28,7 +29,7 @@ private slots:
private: private:
void readPacket(packets::play::serverbound::PacketType type, const QByteArray &buffer); void readPacket(packets::play::serverbound::PacketType type, const QByteArray &buffer);
QTcpSocket &m_socket; std::unique_ptr<QTcpSocket> m_socket;
Server &m_server; Server &m_server;
McDataStream m_dataStream; McDataStream m_dataStream;
@@ -39,4 +40,5 @@ private:
QDateTime m_lastKeepAliveSent; QDateTime m_lastKeepAliveSent;
QDateTime m_lastKeepAliveReceived; QDateTime m_lastKeepAliveReceived;
QDateTime m_lastChatMessage; QDateTime m_lastChatMessage;
QDateTime m_lastStats;
}; };

View File

@@ -1,6 +1,9 @@
#include "server.h" #include "server.h"
#include <memory>
#include <QDateTime> #include <QDateTime>
#include <QTcpSocket>
#include "playclient.h" #include "playclient.h"
#include "handshakingclient.h" #include "handshakingclient.h"
@@ -34,14 +37,15 @@ void Server::timeout()
{ {
client->keepAlive(); client->keepAlive();
client->sendChatMessage(); client->sendChatMessage();
client->randomizeStats();
client->trialDisconnect(); client->trialDisconnect();
} }
} }
void Server::newConnection() void Server::newConnection()
{ {
auto * const connection = m_server.nextPendingConnection(); auto connection = std::unique_ptr<QTcpSocket>(m_server.nextPendingConnection());
if (connection) if (connection)
new HandshakingClient{*connection, *this}; new HandshakingClient{std::move(connection), *this};
//clients.push_back(); //clients.push_back();
} }

View File

@@ -1,12 +1,12 @@
#pragma once #pragma once
#include <set>
#include <QObject> #include <QObject>
#include <QTimer> #include <QTimer>
#include <QTcpServer> #include <QTcpServer>
#include <QPointer> #include <QPointer>
#include <set>
class PlayClient; class PlayClient;
class Server : public QObject class Server : public QObject

File diff suppressed because one or more lines are too long

View File

@@ -15,7 +15,8 @@ class StatusClient : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit StatusClient(QTcpSocket &socket, Server &server); explicit StatusClient(std::unique_ptr<QTcpSocket> &&socket, Server &server);
~StatusClient() override;
private slots: private slots:
void readyRead(); void readyRead();
@@ -23,7 +24,7 @@ private slots:
private: private:
void readPacket(packets::status::serverbound::PacketType type, const QByteArray &buffer); void readPacket(packets::status::serverbound::PacketType type, const QByteArray &buffer);
QTcpSocket &m_socket; std::unique_ptr<QTcpSocket> m_socket;
Server &m_server; Server &m_server;
McDataStream m_dataStream; McDataStream m_dataStream;