Implemented basic chat functionality
This commit is contained in:
@@ -1,25 +1,36 @@
|
||||
#include "chathelper.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
|
||||
QJsonObject normalTextObj(const QString &text)
|
||||
QJsonObject ChatPart::toObject() const
|
||||
{
|
||||
return QJsonObject { { "text", text } };
|
||||
}
|
||||
|
||||
QJsonObject boldTextObj(const QString &text)
|
||||
{
|
||||
QJsonObject obj = normalTextObj(text);
|
||||
obj["bold"] = true;
|
||||
QJsonObject obj;
|
||||
if (text.has_value())
|
||||
obj["text"] = *text;
|
||||
if (bold.has_value())
|
||||
obj["bold"] = *bold;
|
||||
if (italic.has_value())
|
||||
obj["italic"] = *italic;
|
||||
if (underlined.has_value())
|
||||
obj["underlined"] = *underlined;
|
||||
if (strikethrough.has_value())
|
||||
obj["strikethrough"] = *strikethrough;
|
||||
if (obfuscated.has_value())
|
||||
obj["obfuscated"] = *obfuscated;
|
||||
if (color.has_value())
|
||||
obj["color"] = *color;
|
||||
if (!extra.empty())
|
||||
{
|
||||
QJsonArray arr;
|
||||
for (const auto &sub : extra)
|
||||
arr.append(sub.toObject());
|
||||
obj["extra"] = arr;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
QString normalText(const QString &text)
|
||||
QString ChatPart::toString() const
|
||||
{
|
||||
return QJsonDocument{normalTextObj(text)}.toJson();
|
||||
}
|
||||
|
||||
QString boldText(const QString &text)
|
||||
{
|
||||
return QJsonDocument{boldTextObj(text)}.toJson();
|
||||
return QJsonDocument{toObject()}.toJson(QJsonDocument::Compact);
|
||||
}
|
||||
|
19
chathelper.h
19
chathelper.h
@@ -1,10 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <QString>
|
||||
#include <QJsonObject>
|
||||
|
||||
QJsonObject normalTextObj(const QString &text);
|
||||
QJsonObject boldTextObj(const QString &text);
|
||||
struct ChatPart {
|
||||
std::optional<QString> text;
|
||||
std::optional<bool> bold;
|
||||
std::optional<bool> italic;
|
||||
std::optional<bool> underlined;
|
||||
std::optional<bool> strikethrough;
|
||||
std::optional<bool> obfuscated;
|
||||
std::optional<QString> color;
|
||||
std::vector<ChatPart> extra;
|
||||
|
||||
QString normalText(const QString &text);
|
||||
QString boldText(const QString &text);
|
||||
QJsonObject toObject() const;
|
||||
QString toString() const;
|
||||
};
|
||||
|
@@ -8,8 +8,6 @@
|
||||
ClosedClient::ClosedClient(std::unique_ptr<QTcpSocket> &&socket, Server &server) :
|
||||
QObject{&server}, m_socket{std::move(socket)}, m_server{server}, m_dataStream{m_socket.get()}
|
||||
{
|
||||
m_socket->setParent(this);
|
||||
|
||||
connect(m_socket.get(), &QIODevice::readyRead, this, &ClosedClient::readyRead);
|
||||
connect(m_socket.get(), &QAbstractSocket::disconnected, this, &QObject::deleteLater);
|
||||
|
||||
|
@@ -9,8 +9,6 @@
|
||||
HandshakingClient::HandshakingClient(std::unique_ptr<QTcpSocket> &&socket, Server &server) :
|
||||
QObject{&server}, m_socket{std::move(socket)}, m_server{server}, m_dataStream{m_socket.get()}
|
||||
{
|
||||
m_socket->setParent(this);
|
||||
|
||||
connect(m_socket.get(), &QIODevice::readyRead, this, &HandshakingClient::readyRead);
|
||||
connect(m_socket.get(), &QAbstractSocket::disconnected, this, &QObject::deleteLater);
|
||||
}
|
||||
|
@@ -8,8 +8,6 @@
|
||||
LoginClient::LoginClient(std::unique_ptr<QTcpSocket> &&socket, Server &server) :
|
||||
QObject{&server}, m_socket{std::move(socket)}, m_server{server}, m_dataStream{m_socket.get()}
|
||||
{
|
||||
m_socket->setParent(this);
|
||||
|
||||
connect(m_socket.get(), &QIODevice::readyRead, this, &LoginClient::readyRead);
|
||||
connect(m_socket.get(), &QAbstractSocket::disconnected, this, &QObject::deleteLater);
|
||||
}
|
||||
@@ -67,7 +65,7 @@ void LoginClient::readPacket(packets::login::serverbound::PacketType type, const
|
||||
packet.serialize(m_dataStream);
|
||||
}
|
||||
m_dataStream.setDevice({});
|
||||
new PlayClient{std::move(m_socket), m_server};
|
||||
new PlayClient{name, std::move(m_socket), m_server};
|
||||
deleteLater();
|
||||
break;
|
||||
}
|
||||
|
@@ -61,6 +61,11 @@ void packets::login::clientbound::LoginSuccess::serialize(McDataStream &stream)
|
||||
stream.writeRawData(buffer.constData(), buffer.length());
|
||||
}
|
||||
|
||||
packets::play::serverbound::ChatMessage::ChatMessage(McDataStream &stream)
|
||||
{
|
||||
message = stream.readVar<QString>();
|
||||
}
|
||||
|
||||
packets::play::serverbound::ClientSettings::ClientSettings(McDataStream &stream)
|
||||
{
|
||||
locale = stream.readVar<QString>();
|
||||
|
@@ -116,9 +116,15 @@ namespace packets {
|
||||
namespace serverbound {
|
||||
Q_NAMESPACE
|
||||
|
||||
enum class PacketType { ClientSettings = 0x04, InteractEntity = 0x0E, PluginMessage = 0x0A };
|
||||
enum class PacketType { ChatMessage = 0x02, ClientSettings = 0x04, InteractEntity = 0x0E, PluginMessage = 0x0A };
|
||||
Q_ENUM_NS(PacketType)
|
||||
|
||||
struct ChatMessage {
|
||||
ChatMessage(McDataStream &stream);
|
||||
|
||||
QString message;
|
||||
};
|
||||
|
||||
struct ClientSettings {
|
||||
ClientSettings(McDataStream &stream);
|
||||
|
||||
|
@@ -8,12 +8,12 @@
|
||||
#include "chathelper.h"
|
||||
#include "chunkhelper.h"
|
||||
|
||||
PlayClient::PlayClient(std::unique_ptr<QTcpSocket> &&socket, Server &server) :
|
||||
QObject{&server}, m_socket{std::move(socket)}, m_server{server}, m_dataStream{m_socket.get()}
|
||||
PlayClient::PlayClient(const QString &name, std::unique_ptr<QTcpSocket> &&socket, Server &server) :
|
||||
QObject{&server}, m_name{name}, m_socket{std::move(socket)}, m_server{server}, m_dataStream{m_socket.get()}
|
||||
{
|
||||
m_server.add(*this);
|
||||
|
||||
m_socket->setParent(this);
|
||||
emit distributeChatMessage(ChatPart{.extra={ChatPart{.text=m_name,.color="red"},ChatPart{.text=tr(" joined the game!")}}}.toString());
|
||||
|
||||
connect(m_socket.get(), &QIODevice::readyRead, this, &PlayClient::readyRead);
|
||||
connect(m_socket.get(), &QAbstractSocket::disconnected, this, &QObject::deleteLater);
|
||||
@@ -52,22 +52,22 @@ PlayClient::PlayClient(std::unique_ptr<QTcpSocket> &&socket, Server &server) :
|
||||
packet.fieldOfViewModifier = 60.;
|
||||
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
|
||||
// {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
@@ -88,10 +88,14 @@ PlayClient::PlayClient(std::unique_ptr<QTcpSocket> &&socket, Server &server) :
|
||||
|
||||
PlayClient::~PlayClient()
|
||||
{
|
||||
qDebug() << "called" << m_socket->readAll();
|
||||
|
||||
emit distributeChatMessage(ChatPart{.extra={ChatPart{.text=m_name,.color="red"},ChatPart{.text=tr(" left the game!")}}}.toString());
|
||||
|
||||
m_server.remove(*this);
|
||||
}
|
||||
|
||||
void PlayClient::keepAlive()
|
||||
void PlayClient::tick()
|
||||
{
|
||||
const auto now = QDateTime::currentDateTime();
|
||||
if (!m_lastKeepAliveSent.isValid() || m_lastKeepAliveSent.secsTo(now) >= 1)
|
||||
@@ -101,24 +105,13 @@ void PlayClient::keepAlive()
|
||||
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 = normalText(tr("Time chaged to %0").arg(QTime::currentTime().toString()));
|
||||
packet.position = packets::play::clientbound::ChatMessage::Chat;
|
||||
packet.serialize(m_dataStream);
|
||||
sendChatMessage(ChatPart{.text=tr("Time chaged to %0").arg(QTime::currentTime().toString())}.toString());
|
||||
m_lastChatMessage = now;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayClient::randomizeStats()
|
||||
{
|
||||
const auto now = QDateTime::currentDateTime();
|
||||
if (!m_lastStats.isValid() || m_lastStats.msecsTo(now) >= 500)
|
||||
{
|
||||
{
|
||||
@@ -138,15 +131,11 @@ void PlayClient::randomizeStats()
|
||||
|
||||
m_lastStats = now;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayClient::trialDisconnect()
|
||||
{
|
||||
const auto now = QDateTime::currentDateTime();
|
||||
if (m_connectedSince.secsTo(now) >= 30)
|
||||
{
|
||||
packets::play::clientbound::Disconnect packet;
|
||||
packet.reason = boldText("Your trial has ended.");
|
||||
packet.reason = ChatPart{.text="Your trial has ended.",.bold=true}.toString();
|
||||
packet.serialize(m_dataStream);
|
||||
m_socket->flush();
|
||||
m_dataStream.setDevice({});
|
||||
@@ -155,6 +144,14 @@ void PlayClient::trialDisconnect()
|
||||
}
|
||||
}
|
||||
|
||||
void PlayClient::sendChatMessage(const QString &message)
|
||||
{
|
||||
packets::play::clientbound::ChatMessage packet;
|
||||
packet.jsonData = message;
|
||||
packet.position = packets::play::clientbound::ChatMessage::Chat;
|
||||
packet.serialize(m_dataStream);
|
||||
}
|
||||
|
||||
void PlayClient::readyRead()
|
||||
{
|
||||
while(m_socket && m_socket->bytesAvailable())
|
||||
@@ -189,7 +186,20 @@ void PlayClient::readPacket(packets::play::serverbound::PacketType type, const Q
|
||||
switch(type)
|
||||
{
|
||||
using namespace packets::play;
|
||||
case serverbound::PacketType::ClientSettings:
|
||||
using namespace serverbound;
|
||||
case PacketType::ChatMessage:
|
||||
{
|
||||
qDebug() << type;
|
||||
{
|
||||
serverbound::ChatMessage packet{dataStream};
|
||||
qDebug() << "message" << packet.message;
|
||||
const auto formatted = ChatPart{.extra={ChatPart{.text=m_name,.color="red"},ChatPart{.text=": " + packet.message}}}.toString();
|
||||
emit distributeChatMessage(formatted);
|
||||
sendChatMessage(formatted);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PacketType::ClientSettings:
|
||||
{
|
||||
qDebug() << type;
|
||||
{
|
||||
|
14
playclient.h
14
playclient.h
@@ -15,13 +15,16 @@ class PlayClient : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PlayClient(std::unique_ptr<QTcpSocket> &&socket, Server &server);
|
||||
explicit PlayClient(const QString &name, std::unique_ptr<QTcpSocket> &&socket, Server &server);
|
||||
~PlayClient() override;
|
||||
|
||||
void keepAlive();
|
||||
void sendChatMessage();
|
||||
void randomizeStats();
|
||||
void trialDisconnect();
|
||||
void tick();
|
||||
|
||||
signals:
|
||||
void distributeChatMessage(const QString &message);
|
||||
|
||||
public slots:
|
||||
void sendChatMessage(const QString &message);
|
||||
|
||||
private slots:
|
||||
void readyRead();
|
||||
@@ -29,6 +32,7 @@ private slots:
|
||||
private:
|
||||
void readPacket(packets::play::serverbound::PacketType type, const QByteArray &buffer);
|
||||
|
||||
const QString m_name;
|
||||
std::unique_ptr<QTcpSocket> m_socket;
|
||||
Server &m_server;
|
||||
|
||||
|
13
server.cpp
13
server.cpp
@@ -23,6 +23,12 @@ Server::Server(QObject *parent) :
|
||||
|
||||
void Server::add(PlayClient &playClient)
|
||||
{
|
||||
for (auto otherClient : m_playClients)
|
||||
{
|
||||
connect(&playClient, &PlayClient::distributeChatMessage, otherClient, &PlayClient::sendChatMessage);
|
||||
connect(otherClient, &PlayClient::distributeChatMessage, &playClient, &PlayClient::sendChatMessage);
|
||||
}
|
||||
|
||||
m_playClients.insert(&playClient);
|
||||
}
|
||||
|
||||
@@ -34,12 +40,7 @@ void Server::remove(PlayClient &playClient)
|
||||
void Server::timeout()
|
||||
{
|
||||
for (auto client : m_playClients)
|
||||
{
|
||||
client->keepAlive();
|
||||
client->sendChatMessage();
|
||||
client->randomizeStats();
|
||||
client->trialDisconnect();
|
||||
}
|
||||
client->tick();
|
||||
}
|
||||
|
||||
void Server::newConnection()
|
||||
|
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user