#ifndef SERVER_RESOURCES_HH_ #define SERVER_RESOURCES_HH_ #include #include #include #include #include #include #include "server/chunk_data.hh" #include "server/client.hh" #include "server/database.hh" #include "server/world.hh" #include "shared/player.hh" #include "shared/world.hh" namespace server { namespace resources { // Occasionally we need access to certain objects quickly. // These classes enable the server to take priority over the thread pool's work // when necessary. Construct a low_priority_lock when it can happen whenever, // and a high_priority_lock when it should happen ~ now. template class lock_base { private: T& obj; protected: static inline std::mutex data_mutex; static inline std::mutex next_mutex; static inline std::mutex low_priority_mutex; protected: lock_base(T& obj) noexcept : obj(obj) {} public: lock_base(const lock_base&) = delete; lock_base(lock_base&&) = default; virtual ~lock_base(){}; T& get() noexcept { return this->obj; } T& operator*() noexcept { return this->get(); } T* const operator->() noexcept { return &this->get(); } }; // https://stackoverflow.com/questions/11666610/how-to-give-priority-to-privileged-thread-in-mutex-locking // ty ecatmur! template class low_priority_lock : public lock_base { public: low_priority_lock(T& t) noexcept : lock_base(t) { lock_base::low_priority_mutex.lock(); lock_base::next_mutex.lock(); lock_base::data_mutex.lock(); lock_base::next_mutex.unlock(); } virtual ~low_priority_lock() noexcept { lock_base::data_mutex.unlock(); lock_base::low_priority_mutex.unlock(); } }; template class high_priority_lock : public lock_base { public: high_priority_lock(T& t) noexcept : lock_base(t) { lock_base::next_mutex.lock(); lock_base::data_mutex.lock(); lock_base::next_mutex.unlock(); } virtual ~high_priority_lock() noexcept { lock_base::data_mutex.unlock(); } }; using chunk_map_key = shared::math::coords; using chunk_map_value = std::unique_ptr; using chunk_map = std::unordered_map; using client_map_key = shared::player::index_t; using client_map_value = std::unique_ptr; using client_map = std::unordered_map; using pool_t = boost::asio::thread_pool; struct resources { client_map& clients; chunk_map& chunks; pool_t& pool; }; void init() noexcept; void quit() noexcept; low_priority_lock get_resources_lock() noexcept; high_priority_lock get_resources_lock_immediate() noexcept; inline void associate_client_chunk(const shared::math::coords& coords, client_map_value& client, chunk_map_value& chunk) noexcept { client->chunks.emplace(coords); chunk->players.emplace(client->index); } inline void disassociate_client_chunk(const shared::math::coords& coords, client_map_value& client, chunk_map_value& chunk) noexcept { client->chunks.erase(coords); chunk->players.erase(client->index); } } // namespace resources } // namespace server #endif