#pragma once #include #include #include #include "Abstract.hpp" #include "Common/Packets.hpp" #include "Server/ContentEventController.hpp" #include #include #include #include #include #include #include namespace LV::Server { using HASH = std::array; template= sizeof(ClientKey), int> = 0> class CSChunkedMapper { std::unordered_map, std::array>> Chunks; public: ServerKey toServer(ClientKey cKey) { int chunkIndex = cKey >> 6; int subIndex = cKey & 0x3f; auto iChunk = Chunks.find(chunkIndex); assert(iChunk != Chunks.end() && "Идентификатор уже занят"); std::bitset<64> &bits = std::get<0>(iChunk.second); std::array &keys = std::get<1>(iChunk.second); assert(bits.test(subIndex) && "Идентификатор уже занят"); return keys[subIndex]; } void erase(ClientKey cKey) { int chunkIndex = cKey >> 6; int subIndex = cKey & 0x3f; auto iChunk = Chunks.find(chunkIndex); if(iChunk == Chunks.end()) MAKE_ERROR("Идентификатор не привязан"); std::bitset<64> &bits = std::get<0>(iChunk->second); std::array &keys = std::get<1>(iChunk->second); assert(bits.test(subIndex) && "Идентификатор уже занят"); bits.reset(subIndex); } void link(ClientKey cKey, ServerKey sKey) { int chunkIndex = cKey >> 6; int subIndex = cKey & 0x3f; std::tuple, std::array> &chunk = Chunks[chunkIndex]; std::bitset<64> &bits = std::get<0>(chunk); std::array &keys = std::get<1>(chunk); assert(!bits.test(subIndex) && "Идентификатор уже занят"); bits.set(subIndex); keys[subIndex] = sKey; } }; template= sizeof(ClientKey), int> = 0> class SCSKeyRemapper { std::bitset FreeClientKeys; std::map Map; CSChunkedMapper CSmapper; public: SCSKeyRemapper() { FreeClientKeys.set(); } // Соотнести идентификатор на стороне сервера с идентификатором на стороне клиента ClientKey toClient(ServerKey skey) { auto iter = Map.find(skey); if(iter == Map.end()) { // Идентификатор отсутствует, нужно его занять // Ищет позицию ближайшего бита 1 size_t pos = FreeClientKeys._Find_first(); if(pos == FreeClientKeys.size()) return ClientKey(0); // Свободные идентификаторы отсутствуют ClientKey ckey = ClientKey(pos+1); Map[skey] = ckey; CSmapper.link(ckey, skey); FreeClientKeys.reset(pos); return ClientKey(pos); } return iter->second; } // Соотнести идентификатор на стороне клиента с идентификатором на стороне сервера ServerKey toServer(ClientKey ckey) { return CSmapper.toServer(ckey); } // Удаляет серверный идентификатор, освобождая идентификатор клиента ClientKey erase(ServerKey skey) { auto iter = Map.find(skey); if(iter == Map.end()) return 0; ClientKey ckey = iter->second; CSmapper.erase(ckey); Map.erase(iter); FreeClientKeys.set(ckey-1); return ckey; } void rebindClientKey(ServerKey prev, ServerKey next) { auto iter = Map.find(prev); assert(iter != Map.end() && "Идентификатор не найден"); ClientKey ckey = iter->second; CSmapper.erase(ckey); CSmapper.link(ckey, next); Map.erase(iter); Map[next] = ckey; } }; /* Шаблоны игрового контента, которые необходимо поддерживать в актуальном состоянии для клиента и шаблоны, которые клиенту уже не нужны. Соответствующие менеджеры ресурсов будут следить за изменениями этих ресурсов и переотправлять их клиенту */ struct ResourceRequest { std::vector BinTexture; std::vector BinAnimation; std::vector BinModel; std::vector BinSound; std::vector BinFont; std::vector Voxel; std::vector Node; std::vector World; std::vector Portal; std::vector Entity; std::vector Item; void insert(const ResourceRequest &obj) { BinTexture.insert(BinTexture.end(), obj.BinTexture.begin(), obj.BinTexture.end()); BinAnimation.insert(BinAnimation.end(), obj.BinAnimation.begin(), obj.BinAnimation.end()); BinModel.insert(BinModel.end(), obj.BinModel.begin(), obj.BinModel.end()); BinSound.insert(BinSound.end(), obj.BinSound.begin(), obj.BinSound.end()); BinFont.insert(BinFont.end(), obj.BinFont.begin(), obj.BinFont.end()); Voxel.insert(Voxel.end(), obj.Voxel.begin(), obj.Voxel.end()); Node.insert(Node.end(), obj.Node.begin(), obj.Node.end()); World.insert(World.end(), obj.World.begin(), obj.World.end()); Portal.insert(Portal.end(), obj.Portal.begin(), obj.Portal.end()); Entity.insert(Entity.end(), obj.Entity.begin(), obj.Entity.end()); Item.insert(Item.end(), obj.Item.begin(), obj.Item.end()); } void uniq() { for(std::vector *vec : { &BinTexture, &BinAnimation, &BinModel, &BinSound, &BinFont, &Voxel, &Node, &World, &Portal, &Entity, &Item }) { std::sort(vec->begin(), vec->end()); auto last = std::unique(vec->begin(), vec->end()); vec->erase(last, vec->end()); } } }; // using EntityKey = std::tuple; /* Обработчик сокета клиента. Подписывает клиента на отслеживание необходимых ресурсов на основе передаваемых клиенту данных */ class RemoteClient { TOS::Logger LOG; DestroyLock UseLock; Net::AsyncSocket Socket; bool IsConnected = true, IsGoingShutdown = false; std::vector ClientBinaryCache; /* При обнаружении нового контента составляется запрос (ResourceRequest) на полное описание ресурса. Это описание отправляется клиенту и используется чтобы выстроить зависимость какие базовые ресурсы использует контент. Если базовые ресурсы не известны, то они также запрашиваются. */ struct ResUsesObj { // Счётчики использования двоичных кэшируемых ресурсов std::map BinTexture; std::map BinAnimation; std::map BinModel; std::map BinSound; std::map BinFont; // Счётчики использование профилей контента std::map DefVoxel; // Один чанк, одно использование std::map DefNode; std::map DefWorld; std::map DefPortal; std::map DefEntity; std::map DefItem; // При передаче инвентарей? // Зависимость профилей контента от профилей ресурсов // Нужно чтобы пересчитать зависимости к профилям ресурсов struct RefDefVoxel_t { std::vector Texture; std::vector Sound; }; std::map RefDefVoxel; struct RefDefNode_t { std::vector Model; std::vector Sound; }; std::map RefDefNode; struct RefDefWorld_t { std::vector Texture; std::vector Model; }; std::map RefDefWorld; struct RefDefPortal_t { std::vector Texture; std::vector Animation; std::vector Model; }; std::map RefDefPortal; struct RefDefEntity_t { std::vector Texture; std::vector Animation; std::vector Model; }; std::map RefDefEntity; struct RefDefItem_t { std::vector Texture; std::vector Animation; std::vector Model; }; std::map RefDefItem; // Модификационные зависимости экземпляров профилей контента struct ChunkRef { // Отсортированные списки уникальных вокселей std::vector Voxel; std::vector Node; }; std::map>> RefChunk; struct RefWorld_t { DefWorldId_t Profile; }; std::map RefWorld; struct RefPortal_t { DefPortalId_t Profile; }; std::map RefPortal; struct RefEntity_t { DefEntityId_t Profile; }; std::map RefEntity; } ResUses; // Смена идентификаторов сервера на клиентские struct { SCSKeyRemapper Entityes; SCSKeyRemapper FuncEntityes; } ResRemap; Net::Packet NextPacket; std::vector SimplePackets; ResourceRequest NextRequest; public: const std::string Username; Pos::Object CameraPos = {0, 0, 0}; ToServer::PacketQuat CameraQuat = {0}; public: RemoteClient(asio::io_context &ioc, tcp::socket socket, const std::string username, std::vector &&client_cache) : LOG("RemoteClient " + username), Socket(ioc, std::move(socket)), Username(username), ClientBinaryCache(std::move(client_cache)) { } ~RemoteClient(); coro<> run(); void shutdown(EnumDisconnect type, const std::string reason); bool isConnected() { return IsConnected; } void pushPackets(std::vector *simplePackets, std::vector *smartPackets = nullptr) { if(IsGoingShutdown) return; Socket.pushPackets(simplePackets, smartPackets); } // Функции подготавливают пакеты к отправке // Отслеживаемое игроком использование контента // В зоне видимости добавился чанк или изменились его воксели void prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::vector* voxels); // В зоне видимости добавился чанк или изменились его ноды void prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const Node* nodes); void prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::unordered_map &nodes); //void prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalChunk chunkPos, const LightPrism *lights); // Регион удалён из зоны видимости void prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regionPos); // В зоне видимости добавилась новая сущность или она изменилась void prepareEntityUpdate(ServerEntityId_t entityId, const Entity *entity); // Наблюдаемая сущность пересекла границы региона, у неё изменился серверный идентификатор void prepareEntitySwap(ServerEntityId_t prevEntityId, ServerEntityId_t nextEntityId); // Клиент перестал наблюдать за сущностью void prepareEntityRemove(ServerEntityId_t entityId); // В зоне видимости добавился мир или он изменился void prepareWorldUpdate(WorldId_t worldId, World* world); // Клиент перестал наблюдать за миром void prepareWorldRemove(WorldId_t worldId); // В зоне видимости добавился порта или он изменился void preparePortalUpdate(PortalId_t portalId, void* portal); // Клиент перестал наблюдать за порталом void preparePortalRemove(PortalId_t portalId); // Прочие моменты void prepareCameraSetEntity(ServerEntityId_t entityId); // Отправка подготовленных пакетов ResourceRequest pushPreparedPackets(); // Сообщить о ресурсах // Сюда приходят все обновления ресурсов движка // Глобально их можно запросить в выдаче pushPreparedPackets() // Двоичные файлы void informateBinTexture(const std::unordered_map> &textures); void informateBinAnimation(const std::unordered_map> &animations); void informateBinModel(const std::unordered_map> &models); void informateBinSound(const std::unordered_map> &sounds); void informateBinFont(const std::unordered_map> &fonts); // Игровые определения void informateDefVoxel(const std::unordered_map &voxels); void informateDefNode(const std::unordered_map &nodes); void informateDefWorld(const std::unordered_map &worlds); void informateDefPortal(const std::unordered_map &portals); void informateDefEntity(const std::unordered_map &entityes); void informateDefItem(const std::unordered_map &items); private: void checkPacketBorder(uint16_t size); void protocolError(); coro<> readPacket(Net::AsyncSocket &sock); coro<> rP_System(Net::AsyncSocket &sock); void incrementBinary(const std::vector &textures, const std::vector &animation, const std::vector &sounds, const std::vector &models, const std::vector &fonts ); void decrementBinary(std::vector&& textures, std::vector&& animation, std::vector&& sounds, std::vector&& models, std::vector&& fonts ); void informateBin(ToClient::L2Resource type, ResourceId_t id, const std::shared_ptr& pair); // void incrementProfile(const std::vector &textures, const std::vector &model, // const std::vector &sounds, const std::vector &font // ); // void decrementProfile(std::vector &&textures, std::vector &&model, // std::vector &&sounds, std::vector &&font // ); }; }