RyanHub – file viewer
filename: src/network/protocol/Packet.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 PACKET_H
#define PACKET_H
#include <functional>
#include <memory>

namespace stratos {
class PacketHandler;
class NetworkSession;
class PacketBuffer;

enum ProtocolState { Handshaking = 0x00, Status = 0x01, Login = 0x02, Configuration = 0x03, Play = 0x04 };
enum PacketDirection { Clientbound = 0x00, Serverbound = 0x01 };

class Packet {
public:
    explicit Packet(const int id) : id(id) {}
    virtual ~Packet() = default;

    int getId() const { return id; }
    virtual void decrypt(PacketBuffer& buffer) = 0;
    virtual void encrypt(PacketBuffer& buffer) = 0;
    virtual bool accept(PacketHandler& handler) = 0;

    int id;
};



class ClientboundPacket : virtual public Packet {
public:
    explicit ClientboundPacket(const int id) : Packet(id) {}
    ~ClientboundPacket() override = default;
    void decrypt(PacketBuffer& buffer) override {}
    bool accept(PacketHandler& handler) override { return false; } // Clientbound packets are not handled by handlers
};

class ServerboundPacket : virtual public Packet {
public:
    explicit ServerboundPacket(const int id) : Packet(id) {}
    ~ServerboundPacket() override = default;
    void encrypt(PacketBuffer& buffer) override {}
};

struct PacketKey {
    int state;
    int direction;
    int id;

    bool operator==(const PacketKey& other) const;
};

inline bool PacketKey::operator==(const PacketKey& other) const {
    return state == other.state && direction == other.direction && id == other.id;
}
}

template <>
struct std::hash<stratos::PacketKey> {
    std::size_t operator()(const stratos::PacketKey& key) const noexcept {
        const std::size_t h1 = std::hash<int>()(key.state);
        const std::size_t h2 = std::hash<int>()(key.direction);
        const std::size_t h3 = std::hash<int>()(key.id);

        std::size_t seed = h1;
        seed ^= h2 + 0x9e3779b9 + (seed << 6) + (seed >> 2);
        seed ^= h3 + 0x9e3779b9 + (seed << 6) + (seed >> 2);
        return seed;
    }
};

namespace stratos {
class PacketRegistry final {
  public:
    typedef std::function<std::unique_ptr<Packet>()> PacketFactory;

    PacketRegistry();

    static PacketRegistry& instance() {
        static PacketRegistry registry;
        return registry;
    }

    std::unique_ptr<Packet> create(const PacketKey& key);

  private:
    std::unordered_map<PacketKey, PacketFactory> factories ;

    void setup();
    void registerPacket(const PacketKey& key, PacketFactory factory);
};

inline PacketRegistry::PacketRegistry() : factories() {
    setup();
}
inline std::unique_ptr<Packet> PacketRegistry::create(const PacketKey& key) {
    if (const auto it = factories.find(key); it != factories.end()) return it->second();
    return nullptr;
}
inline void PacketRegistry::registerPacket(const PacketKey& key, PacketFactory factory) {
    factories[key] = std::move(factory);
}

} // namespace stratos

#endif //PACKET_H