#pragma once #include #include #include #include #include #include #include #include #include #include "../src/network/ClientState.h" #define _USE_MATH_DEFINES #include namespace beast = boost::beast; namespace http = beast::http; namespace websocket = beast::websocket; namespace net = boost::asio; using tcp = net::ip::tcp; static constexpr float kWorldZOffset = 45000.0f; static const Eigen::Vector3f kWorldOffset(0.0f, 0.0f, kWorldZOffset); static constexpr float kShipRadius = 15.0f; static constexpr float kSpawnShipMargin = 25.0f; static constexpr float kSpawnBoxMargin = 15.0f; static constexpr float kSpawnZJitter = 60.0f; std::vector split(const std::string& s, char delimiter); struct DeathInfo { int targetId = -1; uint64_t serverTime = 0; Eigen::Vector3f position = Eigen::Vector3f::Zero(); int killerId = -1; }; struct ServerBox { Eigen::Vector3f position; Eigen::Matrix3f rotation; float collisionRadius = 2.0f; bool destroyed = false; }; struct Projectile { int shooterId = -1; uint64_t spawnMs = 0; Eigen::Vector3f pos; Eigen::Vector3f vel; float lifeMs = PROJECTILE_LIFE; }; struct BoxDestroyedInfo { int boxIndex = -1; uint64_t serverTime = 0; Eigen::Vector3f position = Eigen::Vector3f::Zero(); int destroyedBy = -1; }; class Server; class Session : public std::enable_shared_from_this { Server& server_; websocket::stream ws_; beast::flat_buffer buffer_; int id_; bool is_writing_ = false; std::queue> writeQueue_; std::mutex writeMutex_; public: ClientStateInterval timedClientStates; bool joined_ = false; std::chrono::system_clock::time_point lastReceivedTime_; bool hasReservedSpawn_ = false; Eigen::Vector3f reservedSpawn_ = Eigen::Vector3f(0.0f, 0.0f, kWorldZOffset); std::string nickname = "Player"; int shipType = 0; Session(Server& server, tcp::socket&& socket, int id); int get_id() const; bool hasSpawnReserved() const; const Eigen::Vector3f& reservedSpawn() const; bool fetchStateAtTime(std::chrono::system_clock::time_point targetTime, ClientState& outState) const; void send_message(const std::string& msg); void run(); bool IsMessageValid(const std::string& fullMessage); bool is_timed_out(std::chrono::system_clock::time_point now) const; void force_disconnect(); private: void sendBoxesToClient(); public: void init(); ClientState get_latest_state(std::chrono::system_clock::time_point now); void doWrite(); private: void do_read(); void process_message(const std::string& msg); }; class Server { public: tcp::acceptor& acceptor_; net::io_context& ioc_; net::steady_timer timer; std::vector g_boxDestructions; std::mutex g_boxDestructions_mutex; std::vector g_serverBoxes; std::mutex g_boxes_mutex; std::vector> g_sessions; std::mutex g_sessions_mutex; std::vector g_projectiles; std::mutex g_projectiles_mutex; std::unordered_set g_dead_players; std::mutex g_dead_mutex; int next_id = 1000; std::vector generateServerBoxes(int count); public: Server(tcp::acceptor& acceptor, net::io_context& ioc); void broadcastToAll(const std::string& message); void broadcastToAllExceptId(const std::string& message, int id); void createProjectile(int id, Eigen::Vector3f pos, Eigen::Quaternionf dir, float velocity); void update_world(); Eigen::Vector3f PickSafeSpawnPos(int forPlayerId); // Caller must hold g_boxes_mutex Eigen::Vector3f PickSafeBoxPos(int skipIdx); void init(); void accept(); };