RyanHub – file viewer
filename: src/network/protocol/PacketSerialization.h
branch: feature/world
back to repo
/*
 *
 *               _____  _                 _
 *              /  ___|| |               | |
 *              \ `--. | |_  _ __   __ _ | |_   ___   ___
 *               `--. \| __|| '__| / _` || __| / _ \ / __|
 *              /\__/ /| |_ | |   | (_| || |_ | (_) |\__ \
 *              \____/  \__||_|    \__,_| \__| \___/ |___/
 *
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Copyright (C) 2025 Armen Deroian
 *
 */

#ifndef PACKETSERIALIZATION_H
#define PACKETSERIALIZATION_H
#include "math/Angle.h"
#include "math/Position.h"
#include "nbt/io/NBTBuffer.h"
#include "utils/Identifier.h"
#include "utils/Types.h"

#include <array>
#include <stdexcept>
#include <string>
#include <vector>

namespace stratos {
using ByteVec = std::vector<unsigned char>;
using UUID = std::array<uint8_t, 16>;

int8_t readByte(const ByteVec& buffer, size_t& offset);
uint8_t readUnsignedByte(const ByteVec& buffer, size_t& offset);
int readVarInt(const ByteVec& buffer, size_t& offset);

class PacketSerializationException final : public std::logic_error {
  public:
    explicit PacketSerializationException(const char* message) : logic_error(message) {};
    explicit PacketSerializationException(const std::string& message) : logic_error(message) {};
};

// Helper functions
inline bool isReadable(const ByteVec& buffer, const size_t& offset, const size_t& bytes) {
    if (offset + bytes > buffer.size()) throw PacketSerializationException("Buffer overflow: Tried reading beyond buffer size.");

    return true;
}

class PacketBuffer final : public nbt::NBTBuffer {
  public:
    PacketBuffer() = default;
    explicit PacketBuffer(const ByteVec& data) : NBTBuffer(data, 0) {}
    explicit PacketBuffer(ByteVec&& data) : NBTBuffer(std::move(data), 0) {}
    PacketBuffer(const ByteVec& data, const size_t offset) : NBTBuffer(data, offset) {}
    PacketBuffer(ByteVec&& data, const size_t offset) : NBTBuffer(std::move(data), offset) {}
    PacketBuffer(const PacketBuffer&) = delete;
    PacketBuffer& operator=(const PacketBuffer&) = delete;
    PacketBuffer(PacketBuffer&&) = default;
    PacketBuffer& operator=(PacketBuffer&&) = default;

    std::string           readStringUTF16BE();
    // TODO: readTextComponent
    // TODO: readJSONTextComponent
    stratos::utils::Identifier readIdentifier();
    // TODO: readEntityMetaData
    // TODO: readSlot
    // TODO: readHashedSlot
    // TODO: readNBT
    math::Position        readPosition();
    math::Angle           readAngle();
    UUID                  readUUID();
    template <typename T, typename ReadFunc>
    T readVarIntEnum(const ReadFunc& enumMapper) { return enumMapper(readVarInt()); }
    std::vector<uint8_t> readPrefixedByteArray();
    std::vector<LoginProperty> readLoginProperty();
    std::optional<std::string> readPrefixedOptionalString(int maxChars);
    std::vector<uint8_t> readInferredByteArray();
    ResourcePackHeader readResourcePackHeader();
    std::optional<std::vector<uint8_t>> readPrefixedOptionalInferredByteArray();
    std::optional<std::vector<uint8_t>> readPrefixedOptionalPrefixedByteArray();
    std::vector<ResourcePackHeader> readPrefixedResourcePackHeaderArray();

    void writeStringUTF16BE(const std::string& value);
    // TODO: writeTextComponent
    // TODO: writeJSONTextComponent
    void writeIdentifier(const utils::Identifier& identifier);
    // TODO: writeEntityMetaData
    // TODO: writeSlot
    // TODO: writeHashedSlot
    // TODO: writeNBT
    void writePosition(const math::Position& position);
    void writeAngle(const math::Angle& angle);
    void writeUUID(const UUID& uuid);
    void writePrefixedByteArray(const std::vector<uint8_t>& bytes);
    void writeLoginProperty(const std::vector<LoginProperty>& properties);
    void writeResourcePackHeader(const ResourcePackHeader& header);
    void writeRegistryEntry(const RegistryEntry& entry);
    void writeRegistryTagData(const RegistryTagData& data);
    void writeReportDetail(const ReportDetail& detail);
    void writeServerLink(const ServerLink& link);
    void writePrefixedOptionalString(const std::optional<std::string>& str, int maxChars);
    void writePrefixedOptionalUUID(const std::optional<UUID>& uuid);
    void writePrefixedOptionalByteArray(const std::optional<std::vector<uint8_t>>& bytes);
    void writePrefixedOptionalPrefixedByteArray(const std::optional<std::vector<uint8_t>>& bytes);
    void writePrefixedResourcePackHeaderArray(const std::vector<ResourcePackHeader>& headers);
    void writePrefixedRegistryEntryArray(const std::vector<RegistryEntry>& entries);
    void writePrefixedRegistryTagDataArray(const std::vector<RegistryTagData>& data);
    void writePrefixedIdentifierArray(const std::vector<utils::Identifier>& identifiers);
    void writePrefixedReportDetailArray(const std::vector<ReportDetail>& details);
    void writePrefixedServerLinkArray(const std::vector<ServerLink>& serverLinks);

    [[nodiscard]] const ByteVec& getBuffer() const { return buffer; }
    [[nodiscard]] size_t         getOffset() const { return offset; }
    [[nodiscard]] size_t         getSize() const { return buffer.size(); }
    [[nodiscard]] bool           isEmpty() const { return buffer.empty(); }
    [[nodiscard]] bool           isReadable() const { return offset < buffer.size(); }
    [[nodiscard]] bool           isReadable(const size_t& bytes) const { return offset + bytes < buffer.size(); }
};
} // namespace stratos

#endif