#pragma once #include #include "Abstract.hpp" #include #include #include #include namespace LV::Server { class RemoteClient; class GameServer; struct GlobalEntityId { WorldId_t WorldId; Pos::GlobalChunk ChunkPos; EntityId_t EntityId; }; struct ServerObjectPos { WorldId_t WorldId; Pos::Object ObjectPos; }; /* Сфера в которой отслеживаются события игроком */ struct ContentViewCircle { WorldId_t WorldId; // Позиция в чанках glm::i16vec3 Pos; // (Единица равна размеру чанку) в квадрате int32_t Range; inline int32_t sqrDistance(Pos::GlobalRegion regionPos) const { glm::i32vec3 vec = {Pos.x-((regionPos.X << 4) | 0b1000), Pos.y-((regionPos.Y << 4) | 0b1000), Pos.z-((regionPos.Z << 4) | 0b1000)}; return vec.x*vec.x+vec.y*vec.y+vec.z*vec.z; }; inline int32_t sqrDistance(Pos::GlobalChunk chunkPos) const { glm::i32vec3 vec = {Pos.x-chunkPos.X, Pos.y-chunkPos.Y, Pos.z-chunkPos.Z}; return vec.x*vec.x+vec.y*vec.y+vec.z*vec.z; }; inline int64_t sqrDistance(Pos::Object objectPos) const { glm::i32vec3 vec = {Pos.x-(objectPos.x >> 20), Pos.y-(objectPos.y >> 20), Pos.z-(objectPos.z >> 20)}; return vec.x*vec.x+vec.y*vec.y+vec.z*vec.z; }; bool isIn(Pos::GlobalRegion regionPos) const { return sqrDistance(regionPos) < Range+192; // (8×sqrt(3))^2 } bool isIn(Pos::GlobalChunk chunkPos) const { return sqrDistance(chunkPos) < Range+3; // (1×sqrt(3))^2 } bool isIn(Pos::Object objectPos, int32_t size = 0) const { return sqrDistance(objectPos) < Range+3+size; } }; /* Мост контента, для отслеживания событий из удалённх точек По типу портала, через который можно видеть контент на расстоянии */ struct ContentBridge { /* false -> Из точки From видно контент из точки To true -> Контент виден в обе стороны */ bool IsTwoWay = false; WorldId_t LeftWorld; // Позиция в чанках glm::i16vec3 LeftPos; WorldId_t RightWorld; // Позиция в чанках glm::i16vec3 RightPos; }; /* Игрок */ class ContentEventController { private: struct SubscribedObj { // Используется регионами std::vector Portals; std::unordered_map>> Chunks; std::unordered_map>> Entities; } Subscribed; public: // Управляется сервером std::unique_ptr Remote; // Регионы сюда заглядывают std::unordered_map> ContentViewCircles; std::unordered_map> SubscribedRegions; public: ContentEventController(std::unique_ptr &&remote); // Измеряется в чанках в длину uint8_t getViewRange() const; ServerObjectPos getLastPos() const; ServerObjectPos getPos() const; // Навешивается слушателем событий на регионы // Здесь приходят частично фильтрованные события // Регионы следят за чанками, которые видят игроки void onRegionsLost(WorldId_t worldId, const std::vector &lost); void onChunksEnterLost(WorldId_t worldId, Pos::GlobalRegion regionId, const std::unordered_set &enter, const std::unordered_set &lost); // Нужно фильтровать неотслеживаемые чанки void onChunksUpdate_Voxels(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map*> &chunks); void onChunksUpdate_Nodes(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map*> &chunks); void onChunksUpdate_LightPrism(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map &chunks); void onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_set &enter, const std::unordered_set &lost); void onEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegion lastRegionPos, EntityId_t lastId, WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, EntityId_t newId); void onEntityUpdates(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::vector &entities); void onPortalEnterLost(const std::vector &enter, const std::vector &lost); void onPortalUpdates(const std::vector &portals); inline const SubscribedObj& getSubscribed() { return Subscribed; }; }; } namespace std { template <> struct hash { std::size_t operator()(const LV::Server::ServerObjectPos& obj) const { return std::hash()(obj.WorldId) ^ std::hash()(obj.ObjectPos.x) ^ std::hash()(obj.ObjectPos.y) ^ std::hash()(obj.ObjectPos.z); } }; }