RyanHub – file viewer
filename: src/network/session/NetworkClient.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 NETWORKCLIENT_H
#define NETWORKCLIENT_H
#include "../io/Socket.h"
#include "../protocol/Packet.h"
#include "../protocol/PacketCodec.h"
#include "concurrentqueue.h"
#include "spdlog/spdlog.h"
#include "utils/crypto/CryptoUtils.h"

#include <memory>
#include <optional>

namespace stratos {
class WorkerThread;
using SessionId = ClientInfo;
class NetworkManager;

struct SessionInfo {
    std::string username;
    UUID uuid;
};

class NetworkConnection final : public TCPConnection {
  public:
    using TCPConnection::receive;
    using TCPConnection::send;

    NetworkConnection(const SocketFd socketFd, const std::string& address, const int& port, NetworkManager* network, std::shared_ptr<spdlog::logger> logger, const std::shared_ptr<WorkerThread>& eventLoop) : TCPConnection(socketFd, address, port), network(network), logger(std::move(logger)), eventLoop(eventLoop) { changeState(Handshaking); }
    ~NetworkConnection() override = default;

    std::optional<std::unique_ptr<ServerboundPacket>> receivePacket();
    void                                              sendPacket(std::unique_ptr<ClientboundPacket>&& packet);
    int                                               handleReceive();
    void changeState(ProtocolState newState);
    bool                                              disconnect();
    bool                                              disconnect(const std::string& reason);
    bool                                              close() override;

    [[nodiscard]] const NetworkManager* getNetwork() const { return network; }
    [[nodiscard]] const std::shared_ptr<spdlog::logger>& getLogger() const { return logger; }
    [[nodiscard]] bool hasSendData() const { return sendQueue.size_approx() > 0; }
    [[nodiscard]] bool isDisconnected() const { return disconnected.load(std::memory_order_acquire); }
    [[nodiscard]] ProtocolState getState() const { return state; }
    [[nodiscard]] HandshakeIntent getIntent() const { return intent; }
    [[nodiscard]] std::optional<std::reference_wrapper<SessionInfo>> getSessionInfo() const { return sessionInfo ? std::make_optional(std::ref(*sessionInfo)) : std::nullopt; }
    void updateSessionInfo(SessionInfo&& info);
    void createNetworkSession();

  private:
    NetworkManager* network;

    ByteVec receiveBuf;
    moodycamel::ConcurrentQueue<std::unique_ptr<ClientboundPacket>> sendQueue = moodycamel::ConcurrentQueue<std::unique_ptr<ClientboundPacket>>();
    moodycamel::ConcurrentQueue<std::unique_ptr<ServerboundPacket>> receiveQueue = moodycamel::ConcurrentQueue<std::unique_ptr<ServerboundPacket>>();
    std::atomic<bool> disconnected = false;

    ProtocolState state;
    HandshakeIntent intent = HandshakeIntent::None;
    std::unique_ptr<PacketHandler> packetHandler;
    std::unique_ptr<SessionInfo> sessionInfo = nullptr;

    std::shared_ptr<spdlog::logger> logger;
    std::atomic<bool> dirty = false; // Indicates if the connection has data to send
    std::shared_ptr<WorkerThread> eventLoop;

    bool encryptionEnabled = false;
    const EVPKeyPtr* encryptionKey;
    std::vector<uint8_t> verifyToken; // Used for encryption handshake
    std::vector<uint8_t> clientSecret;

    int flushReceive();
    int flushSend();

    bool handleLegacyPing();

    friend class WorkerThread;
    friend class LoginPacketHandler;
};

class NetworkSession {
  public:
    explicit NetworkSession(NetworkManager* networkManager, SessionId id, std::shared_ptr<NetworkConnection> connection)
        : networkManager(networkManager), sessionId(std::move(id)), connection(std::move(connection)), packetHandler(std::make_unique<ConfigurationPacketHandler>(this)) { beginConfiguration(); }
    ~NetworkSession() = default;

    [[nodiscard]] std::string getIp() const { return sessionId.ip; }
    [[nodiscard]] int         getPort() const { return sessionId.port; }
    [[nodiscard]] bool        isConnected() const { return !connection->isDisconnected(); }
    [[nodiscard]] bool        isStale() const { return !isConnected() && connection->isClosed(); }

    void tick();
    void beginConfiguration() const;
    void changeState(ProtocolState newState) const;
    void loginPlayer();

    template <typename T> void send(T& packet) const { send(std::move(packet)); }
    template <typename T> void send(T&& packet) const { connection->sendPacket(std::make_unique<T>(std::move(packet))); }

  private:
    NetworkManager* networkManager;
    SessionId       sessionId;
    std::shared_ptr<NetworkConnection>   connection;

    std::unique_ptr<PacketHandler> packetHandler;

    // Processes received packets
    void processReceived();
    void dispose() const;

    friend class NetworkManager;
};

} // stratos

#endif //NETWORKCLIENT_H