Imported existing sources

This commit is contained in:
Daniel Brunner
2018-10-28 14:55:38 +01:00
parent f975682401
commit be8fa7b1f8
8 changed files with 885 additions and 0 deletions

15
DbMinecraft.pro Normal file
View File

@@ -0,0 +1,15 @@
QT = core network
CONFIG += c++1z
DEFINES += QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000
SOURCES += main.cpp \
client.cpp \
mcdatastream.cpp \
packets.cpp
HEADERS += \
client.h \
mcdatastream.h \
packets.h

257
client.cpp Normal file
View File

@@ -0,0 +1,257 @@
#include "client.h"
#include <QDebug>
#include <QTcpSocket>
#include <QUuid>
#include <QTimer>
Client::Client(QTcpSocket *socket, QObject *parent) :
QObject(parent), m_socket(socket), m_dataStream(m_socket),
m_packetSize(0), m_state(HandshakingState)
{
m_socket->setParent(this);
connect(m_socket, &QIODevice::readyRead, this, &Client::readyRead);
connect(m_socket, &QAbstractSocket::disconnected, this, &Client::disconnected);
qDebug() << m_socket->peerPort();
}
void Client::readyRead()
{
while(m_socket->bytesAvailable())
{
if(!m_packetSize)
{
m_packetSize = m_dataStream.readVar<qint32>();
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<qint32>(bytesRead);
m_packetSize -= bytesRead;
const auto buffer = m_socket->read(m_packetSize);
Q_ASSERT(buffer.size() == m_packetSize);
m_packetSize = 0;
switch(m_state)
{
case HandshakingState:
readPacketHandshaking(packets::handshaking::serverbound::PacketType(type), buffer);
break;
case StatusState:
readPacketStatus(packets::status::serverbound::PacketType(type), buffer);
break;
case LoginState:
readPacketLogin(packets::login::serverbound::PacketType(type), buffer);
break;
case PlayState:
readPacketPlay(packets::play::serverbound::PacketType(type), buffer);
break;
default:
qWarning() << "unhandled state" << m_state << type << buffer;
}
}
}
void Client::disconnected()
{
qDebug() << m_socket->peerPort();
deleteLater();
}
void Client::readPacketHandshaking(packets::handshaking::serverbound::PacketType type, const QByteArray &buffer)
{
qDebug() << type;
McDataStream dataStream(const_cast<QByteArray *>(&buffer), QIODevice::ReadOnly);
switch(type)
{
using namespace packets::handshaking;
case serverbound::PacketHandshake:
{
serverbound::Handshake packet(dataStream);
m_state = packet.nextState;
break;
}
default:
qWarning() << "unknown type!";
}
}
void Client::readPacketStatus(const packets::status::serverbound::PacketType type, const QByteArray &buffer)
{
qDebug() << type;
McDataStream dataStream(const_cast<QByteArray *>(&buffer), QIODevice::ReadOnly);
switch(type)
{
using namespace packets::status;
case serverbound::PacketRequest:
{
{
serverbound::Request packet(dataStream);
}
{
clientbound::Response packet;
packet.jsonResponse =
"{"
" \"version\": {"
" \"name\": \"1.13.1\","
" \"protocol\": 401"
" },"
" \"players\": {"
" \"max\": 1000,"
" \"online\": 2000,"
" \"sample\": ["
" {"
" \"name\": \"0xFEEDC0DE64\","
" \"id\": \"6ebf7396-b6da-40b1-b7d9-9b7961450d5a\""
" }"
" ]"
" }, "
" \"description\": {"
" \"text\": \"Mein monster server in C++\""
" },"
" \"favicon\": \"\""
"}";
packet.serialize(m_dataStream);
}
break;
}
case serverbound::PacketPing:
{
qint64 payload;
{
serverbound::Ping packet(dataStream);
payload = packet.payload;
}
{
clientbound::Pong packet;
packet.payload = payload;
packet.serialize(m_dataStream);
}
break;
}
default:
qWarning() << "unknown type!";
}
}
void Client::readPacketLogin(const packets::login::serverbound::PacketType type, const QByteArray &buffer)
{
qDebug() << type;
McDataStream dataStream(const_cast<QByteArray *>(&buffer), QIODevice::ReadOnly);
switch(type)
{
using namespace packets::login;
case serverbound::PacketLogin:
{
QString name;
{
serverbound::Login packet(dataStream);
name = packet.name;
}
qDebug() << "Name" << name;
{
clientbound::LoginSuccess packet;
const auto uuid = QUuid::createUuid().toString();
packet.uuid = uuid.mid(1, uuid.length() - 2);
packet.username = name;
packet.serialize(m_dataStream);
}
m_state = PlayState;
{
packets::play::clientbound::JoinGame packet;
packet.entityid = 1;
packet.gamemode = 0;
packet.dimension = 0;
packet.difficulty = 2;
packet.maxPlayers = 255;
packet.levelType = QStringLiteral("default");
packet.reducedDebugInfo = false;
packet.serialize(m_dataStream);
}
{
packets::play::clientbound::PluginMessage packet;
packet.channel = QStringLiteral("minecraft:brand");
packet.data = QByteArrayLiteral("bullshit");
packet.serialize(m_dataStream);
}
{
packets::play::clientbound::ServerDifficulty packet;
packet.difficulty = 2;
packet.serialize(m_dataStream);
}
{
packets::play::clientbound::SpawnPosition packet;
packet.location = std::make_tuple(100, 64, 100);
packet.serialize(m_dataStream);
}
{
packets::play::clientbound::PlayerAbilities packet;
packet.flags = 0x0F;
packet.flyingSpeed = 1.;
packet.fieldOfViewModifier = 60.;
packet.serialize(m_dataStream);
}
break;
}
default:
qWarning() << "unknown type!";
}
}
void Client::readPacketPlay(const packets::play::serverbound::PacketType type, const QByteArray &buffer)
{
qDebug() << type;
McDataStream dataStream(const_cast<QByteArray *>(&buffer), QIODevice::ReadOnly);
switch(type)
{
using namespace packets::play;
case serverbound::PacketClientSettings:
{
{
serverbound::ClientSettings packet(dataStream);
qDebug() << "locale" << packet.locale;
qDebug() << "viewDistance" << packet.viewDistance;
qDebug() << "chatMode" << packet.chatMode;
qDebug() << "chatColors" << packet.chatColors;
qDebug() << "displayedSkinParts" << packet.displayedSkinParts;
qDebug() << "mainHand" << packet.mainHand;
}
{
clientbound::PlayerPositionAndLook packet;
packet.x = 50.;
packet.y = 64.;
packet.z = 50.;
packet.yaw = 0.;
packet.pitch = 0.;
packet.flags = 0;
packet.teleportId = 0;
packet.serialize(m_dataStream);
}
break;
}
case serverbound::PacketPluginMessage:
{
serverbound::PluginMessage packet(dataStream);
qDebug() << "channel" << packet.channel;
break;
}
default:
qWarning() << "unknown type!";
}
}

33
client.h Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
#include <QObject>
#include "mcdatastream.h"
#include "packets.h"
class QTcpSocket;
class Client : public QObject
{
Q_OBJECT
public:
explicit Client(QTcpSocket *socket, QObject *parent = nullptr);
private Q_SLOTS:
void readyRead();
void disconnected();
private:
void readPacketHandshaking(const packets::handshaking::serverbound::PacketType type, const QByteArray &buffer);
void readPacketStatus(const packets::status::serverbound::PacketType type, const QByteArray &buffer);
void readPacketLogin(const packets::login::serverbound::PacketType type, const QByteArray &buffer);
void readPacketPlay(const packets::play::serverbound::PacketType type, const QByteArray &buffer);
QTcpSocket *m_socket;
McDataStream m_dataStream;
qint32 m_packetSize;
SocketState m_state;
};

34
main.cpp Normal file
View File

@@ -0,0 +1,34 @@
#include <QCoreApplication>
#include <qlogging.h>
#include <QTcpServer>
#include "client.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qSetMessagePattern(QStringLiteral("%{time dd.MM.yyyy HH:mm:ss.zzz} "
"["
"%{if-debug}D%{endif}"
"%{if-info}I%{endif}"
"%{if-warning}W%{endif}"
"%{if-critical}C%{endif}"
"%{if-fatal}F%{endif}"
"] "
"%{function}(): "
"%{message}"));
QTcpServer server;
QObject::connect(&server, &QTcpServer::newConnection, [&server](){
new Client(server.nextPendingConnection());
});
if(!server.listen(QHostAddress::Any, 25565))
qFatal("could not start listening %s", server.errorString().toUtf8().constData());
qDebug() << "started listening";
return a.exec();
}

173
mcdatastream.cpp Normal file
View File

@@ -0,0 +1,173 @@
#include "mcdatastream.h"
#include <QPoint>
#include <memory>
McDataStream::McDataStream() :
QDataStream()
{
setByteOrder(QDataStream::BigEndian);
}
McDataStream::McDataStream(QIODevice *device) :
QDataStream(device)
{
setByteOrder(QDataStream::BigEndian);
}
McDataStream::McDataStream(QByteArray *byteArray, QIODevice::OpenMode flags) :
QDataStream(byteArray, flags)
{
setByteOrder(QDataStream::BigEndian);
}
McDataStream::McDataStream(const QByteArray &byteArray) :
QDataStream(byteArray)
{
setByteOrder(QDataStream::BigEndian);
}
McDataStream::Position McDataStream::readPosition()
{
quint64 val;
*this >> val;
const qint32 x = val >> 38;
const qint16 y = (val >> 26) & 0xFFF;
const qint32 z = val << 38 >> 38;
return std::make_tuple(x, y, z);
}
void McDataStream::writePosition(const Position &position)
{
const quint64 val = ((quint64(std::get<0>(position)) & 0x3FFFFFF) << 38) |
((quint64(std::get<1>(position)) & 0xFFF) << 26) |
(quint64(std::get<2>(position)) & 0x3FFFFFF);
*this << val;
}
float McDataStream::readFloat()
{
const auto precision = floatingPointPrecision();
setFloatingPointPrecision(SinglePrecision);
float value;
*this >> value;
setFloatingPointPrecision(precision);
return value;
}
void McDataStream::writeFloat(float value)
{
const auto precision = floatingPointPrecision();
setFloatingPointPrecision(SinglePrecision);
*this << value;
setFloatingPointPrecision(precision);
}
float McDataStream::readDouble()
{
const auto precision = floatingPointPrecision();
setFloatingPointPrecision(DoublePrecision);
double value;
*this >> value;
setFloatingPointPrecision(precision);
return value;
}
void McDataStream::writeDouble(double value)
{
const auto precision = floatingPointPrecision();
setFloatingPointPrecision(DoublePrecision);
*this << value;
setFloatingPointPrecision(precision);
}
template<>
qint32 McDataStream::readVar<qint32>(qint32 &bytesRead)
{
bytesRead = 0;
qint32 result = 0;
qint8 read;
do {
*this >> read;
qint32 value = read & 0b01111111;
result |= value << (7 * bytesRead);
bytesRead++;
if (bytesRead > 5) {
qFatal("VarInt is too big");
}
} while ((read & 0b10000000) != 0);
return result;
}
template<>
qint64 McDataStream::readVar<qint64>(qint32 &bytesRead)
{
bytesRead = 0;
qint64 result = 0;
qint8 read;
do {
*this >> read;
qint32 value = read & 0b01111111;
result |= value << (7 * bytesRead);
bytesRead++;
if (bytesRead > 10) {
qFatal("VarInt is too big");
}
} while ((read & 0b10000000) != 0);
return result;
}
template<>
QString McDataStream::readVar<QString>(qint32 &bytesRead)
{
const auto length = readVar<qint32>(bytesRead);
auto data = std::unique_ptr<char[]>(new char[length]);
{
const auto rawLength = readRawData(data.get(), length);
bytesRead += rawLength;
Q_ASSERT(length == rawLength);
}
return QString::fromUtf8(data.get(), length);
}
template<>
void McDataStream::writeVar(qint32 value)
{
do {
qint8 temp = value & 0b01111111;
value >>= 7;
if (value != 0) {
temp |= 0b10000000;
}
*this << temp;
} while (value != 0);
}
template<>
void McDataStream::writeVar(qint64 value)
{
do {
qint8 temp = value & 0b01111111;
value >>= 7;
if (value != 0) {
temp |= 0b10000000;
}
*this << temp;
} while (value != 0);
}
template<>
void McDataStream::writeVar(QString value)
{
const auto bytes = value.toUtf8();
writeVar<qint32>(bytes.length());
writeRawData(bytes.constData(), bytes.length());
}

58
mcdatastream.h Normal file
View File

@@ -0,0 +1,58 @@
#pragma once
#include <QDataStream>
#include <tuple>
class McDataStream : public QDataStream
{
public:
McDataStream();
explicit McDataStream(QIODevice *device);
McDataStream(QByteArray *byteArray, QIODevice::OpenMode flags);
McDataStream(const QByteArray &byteArray);
typedef std::tuple<qint32, qint16, qint32> Position;
Position readPosition();
void writePosition(const Position &position);
float readFloat();
void writeFloat(float value);
float readDouble();
void writeDouble(double value);
template<typename T>
T readVar();
template<typename T>
T readVar(qint32 &bytesRead);
template<typename T>
void writeVar(T value);
};
template<typename T>
T McDataStream::readVar()
{
qint32 bytesRead;
return readVar<T>(bytesRead);
}
template<>
qint32 McDataStream::readVar<qint32>(qint32 &bytesRead);
template<>
qint64 McDataStream::readVar<qint64>(qint32 &bytesRead);
template<>
QString McDataStream::readVar<QString>(qint32 &bytesRead);
template<>
void McDataStream::writeVar(qint32 value);
template<>
void McDataStream::writeVar(qint64 value);
template<>
void McDataStream::writeVar(QString value);

150
packets.cpp Normal file
View File

@@ -0,0 +1,150 @@
#include "packets.h"
#include "mcdatastream.h"
packets::handshaking::serverbound::Handshake::Handshake(McDataStream &stream)
{
protocolVersion = stream.readVar<qint32>();
serverAddress = stream.readVar<QString>();
stream >> serverPort;
const auto socketState = stream.readVar<qint32>();
Q_ASSERT(socketState == 1 || socketState == 2);
nextState = SocketState(socketState);
}
packets::status::serverbound::Request::Request(McDataStream &stream)
{
Q_UNUSED(stream);
}
packets::status::serverbound::Ping::Ping(McDataStream &stream)
{
stream >> payload;
}
void packets::status::clientbound::Response::serialize(McDataStream &stream)
{
QByteArray buffer;
McDataStream tempStream(&buffer, QIODevice::WriteOnly);
tempStream.writeVar<qint32>(PacketResponse);
tempStream.writeVar<QString>(jsonResponse);
stream.writeVar<qint32>(buffer.length());
stream.writeRawData(buffer.constData(), buffer.length());
}
void packets::status::clientbound::Pong::serialize(McDataStream &stream)
{
QByteArray buffer;
McDataStream tempStream(&buffer, QIODevice::WriteOnly);
tempStream.writeVar<qint32>(PacketPong);
tempStream << payload;
stream.writeVar<qint32>(buffer.length());
stream.writeRawData(buffer.constData(), buffer.length());
}
packets::login::serverbound::Login::Login(McDataStream &stream)
{
name = stream.readVar<QString>();
}
void packets::login::clientbound::LoginSuccess::serialize(McDataStream &stream)
{
QByteArray buffer;
McDataStream tempStream(&buffer, QIODevice::WriteOnly);
tempStream.writeVar<qint32>(PacketLoginSuccess);
tempStream.writeVar<QString>(uuid);
tempStream.writeVar<QString>(username);
stream.writeVar<qint32>(buffer.length());
stream.writeRawData(buffer.constData(), buffer.length());
}
packets::play::serverbound::ClientSettings::ClientSettings(McDataStream &stream)
{
locale = stream.readVar<QString>();
stream >> viewDistance;
chatMode = stream.readVar<qint32>();
stream >> chatColors
>> displayedSkinParts;
mainHand = stream.readVar<qint32>();
}
packets::play::serverbound::PluginMessage::PluginMessage(McDataStream &stream)
{
channel = stream.readVar<QString>();
//TODO read to end of buffer
}
void packets::play::clientbound::ServerDifficulty::serialize(McDataStream &stream)
{
QByteArray buffer;
McDataStream tempStream(&buffer, QIODevice::WriteOnly);
tempStream.writeVar<qint32>(PacketServerDifficulty);
tempStream << difficulty;
stream.writeVar<qint32>(buffer.length());
stream.writeRawData(buffer.constData(), buffer.length());
}
void packets::play::clientbound::PluginMessage::serialize(McDataStream &stream)
{
QByteArray buffer;
McDataStream tempStream(&buffer, QIODevice::WriteOnly);
tempStream.writeVar<qint32>(PacketPluginMessage);
tempStream.writeVar<QString>(channel);
buffer.append(data);
stream.writeVar<qint32>(buffer.length());
stream.writeRawData(buffer.constData(), buffer.length());
}
void packets::play::clientbound::JoinGame::serialize(McDataStream &stream)
{
QByteArray buffer;
McDataStream tempStream(&buffer, QIODevice::WriteOnly);
tempStream.writeVar<qint32>(PacketJoinGame);
tempStream << entityid
<< gamemode
<< dimension
<< difficulty
<< maxPlayers;
tempStream.writeVar<QString>(levelType);
tempStream << reducedDebugInfo;
stream.writeVar<qint32>(buffer.length());
stream.writeRawData(buffer.constData(), buffer.length());
}
void packets::play::clientbound::PlayerAbilities::serialize(McDataStream &stream)
{
QByteArray buffer;
McDataStream tempStream(&buffer, QIODevice::WriteOnly);
tempStream.writeVar<qint32>(PacketPlayerAbilities);
tempStream << flags;
tempStream.writeFloat(flyingSpeed);
tempStream.writeFloat(fieldOfViewModifier);
stream.writeVar<qint32>(buffer.length());
stream.writeRawData(buffer.constData(), buffer.length());
}
void packets::play::clientbound::PlayerPositionAndLook::serialize(McDataStream &stream)
{
QByteArray buffer;
McDataStream tempStream(&buffer, QIODevice::WriteOnly);
tempStream.writeVar<qint32>(PacketPlayerPositionAndLook);
tempStream.writeDouble(x);
tempStream.writeDouble(y);
tempStream.writeDouble(z);
tempStream.writeFloat(yaw);
tempStream.writeFloat(pitch);
tempStream << flags;
tempStream.writeVar<qint32>(teleportId);
stream.writeVar<qint32>(buffer.length());
stream.writeRawData(buffer.constData(), buffer.length());
}
void packets::play::clientbound::SpawnPosition::serialize(McDataStream &stream)
{
QByteArray buffer;
McDataStream tempStream(&buffer, QIODevice::WriteOnly);
tempStream.writeVar<qint32>(PacketSpawnPosition);
tempStream.writePosition(location);
stream.writeVar<qint32>(buffer.length());
stream.writeRawData(buffer.constData(), buffer.length());
}

165
packets.h Normal file
View File

@@ -0,0 +1,165 @@
#pragma once
#include <QtGlobal>
#include <QString>
#include <tuple>
class McDataStream;
enum SocketState { HandshakingState, StatusState, LoginState, PlayState, ClosedState };
namespace packets {
namespace handshaking {
namespace serverbound {
enum PacketType { PacketHandshake };
struct Handshake {
Handshake(McDataStream &stream);
qint32 protocolVersion;
QString serverAddress;
quint16 serverPort;
SocketState nextState;
};
}
namespace clientbound {
}
}
namespace status {
namespace serverbound {
enum PacketType { PacketRequest, PacketPing };
struct Request {
Request(McDataStream &stream);
};
struct Ping {
Ping(McDataStream &stream);
qint64 payload;
};
}
namespace clientbound {
enum PacketType { PacketResponse, PacketPong };
struct Response {
QString jsonResponse;
void serialize(McDataStream &stream);
};
struct Pong {
qint64 payload;
void serialize(McDataStream &stream);
};
}
}
namespace login {
namespace serverbound {
enum PacketType { PacketLogin };
struct Login {
Login(McDataStream &stream);
QString name;
};
}
namespace clientbound {
enum PacketType { PacketLoginSuccess = 0x02 };
struct LoginSuccess {
QString uuid;
QString username;
void serialize(McDataStream &stream);
};
}
}
namespace play {
namespace serverbound {
enum PacketType { PacketClientSettings = 0x04, PacketPluginMessage = 0x0A };
struct ClientSettings {
ClientSettings(McDataStream &stream);
QString locale;
qint8 viewDistance;
qint32 chatMode;
bool chatColors;
quint8 displayedSkinParts;
qint32 mainHand;
};
struct PluginMessage {
PluginMessage(McDataStream &stream);
QString channel;
QByteArray data;
};
}
namespace clientbound {
enum PacketType { PacketServerDifficulty = 0x0D, PacketPluginMessage = 0x19, PacketJoinGame = 0x25, PacketPlayerAbilities = 0x2E, PacketPlayerPositionAndLook = 0x32,
PacketSpawnPosition = 0x49 };
struct ServerDifficulty {
quint8 difficulty;
void serialize(McDataStream &stream);
};
struct PluginMessage {
QString channel;
QByteArray data;
void serialize(McDataStream &stream);
};
struct JoinGame {
qint32 entityid;
quint8 gamemode;
qint32 dimension;
quint8 difficulty;
quint8 maxPlayers;
QString levelType;
bool reducedDebugInfo;
void serialize(McDataStream &stream);
};
struct PlayerAbilities {
qint8 flags;
float flyingSpeed;
float fieldOfViewModifier;
void serialize(McDataStream &stream);
};
struct PlayerPositionAndLook {
double x;
double y;
double z;
float yaw;
float pitch;
qint8 flags;
qint32 teleportId;
void serialize(McDataStream &stream);
};
struct SpawnPosition {
std::tuple<qint32, qint16, qint32> location;
void serialize(McDataStream &stream);
};
}
}
}