#pragma once #include #include #include #include "Abstract.hpp" #include #include #include #include #include #include namespace LV::Server { template && std::is_integral_v && sizeof(ServerKey) >= 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 && std::is_integral_v && sizeof(ServerKey) >= sizeof(ClientKey), int> = 0> class SCSKeyRemapper { std::bitset FreeClientKeys; std::map Map; CSChunkedMapper CSmapper; public: SCSKeyRemapper() { FreeClientKeys.set(); } // Соотнести идентификатор на стороне сервера с идентификатором на стороне клиента ClientKey toClient(ServerKey skey) { if(skey == ServerKey(0)) return ClientKey(0); 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, ckey); return ClientKey(pos); } return iter.second; } // Соотнести идентификатор на стороне клиента с идентификатором на стороне сервера ServerKey toServer(ClientKey ckey) { return CSmapper.toServer(ckey); } // Удаляет серверный идентификатор, освобождая идентификатор клиента ClientKey erase(ServerKey skey) { auto iter = Map.find(skey); assert(iter != Map.end() && "Идентификатор не существует"); ClientKey ckey = iter.second; CSmapper.erase(ckey); Map.erase(iter); return ckey; } }; /* Шаблоны игрового контента, которые необходимо поддерживать в актуальном состоянии для клиента и шаблоны, которые клиенту уже не нужны. Соответствующие менеджеры ресурсов будут следить за изменениями этих ресурсов и переотправлять их клиенту */ struct ResourceRequest { std::vector NewWorlds; std::vector NewVoxels; std::vector NewNodes; std::vector NewEntityes; std::vector NewTextures; std::vector NewModels; std::vector NewSounds; void insert(const ResourceRequest &obj) { NewWorlds.insert(NewWorlds.end(), obj.NewWorlds.begin(), obj.NewWorlds.end()); NewVoxels.insert(NewVoxels.end(), obj.NewVoxels.begin(), obj.NewVoxels.end()); NewNodes.insert(NewNodes.end(), obj.NewNodes.begin(), obj.NewNodes.end()); NewEntityes.insert(NewEntityes.end(), obj.NewEntityes.begin(), obj.NewEntityes.end()); NewTextures.insert(NewTextures.end(), obj.NewTextures.begin(), obj.NewTextures.end()); NewModels.insert(NewModels.end(), obj.NewModels.begin(), obj.NewModels.end()); NewSounds.insert(NewSounds.end(), obj.NewSounds.begin(), obj.NewSounds.end()); } void uniq() { for(std::vector *vec : {&NewWorlds, &NewVoxels, &NewNodes, &NewEntityes, &NewTextures, &NewModels, &NewSounds}) { 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 { DestroyLock UseLock; Net::AsyncSocket Socket; bool IsConnected = true, IsGoingShutdown = false; struct { std::bitset<(1 << sizeof(EntityId_c)*8) - 1> UsedEntityIdC; // 1 - идентификатор свободен, 0 - занят std::map TextureUses; std::map ModelUses; std::map SoundUses; std::map VoxelDefUses; std::map NodeDefUses; std::map EntityDefUses; std::unordered_map STC_Textures; std::unordered_map STC_Models; //std::unordered_map STC_Sounds; std::bitset<(1 << sizeof(VoxelId_c)*8) - 1> UsedVoxelIdC; // 1 - идентификатор свободен, 0 - занят std::unordered_map STC_Voxels; std::bitset<(1 << sizeof(NodeId_c)*8) - 1> UsedNodeIdC; // 1 - идентификатор свободен, 0 - занят std::unordered_map STC_Nodes; std::bitset<(1 << sizeof(VoxelId_c)*8) - 1> UsedWorldIdC; // 1 - идентификатор свободен, 0 - занят std::unordered_map STC_Worlds; } Remap; struct { //std::unordered_map EntityTextures; } BinaryResourceUsedIds; // Вести учёт использования идентификаторов struct { // Использованные идентификаторы вокселей чанками std::unordered_map>> Voxels; // Количество зависимостей к ресурсу std::unordered_map VoxelsUsedCount; // Использованные идентификаторы нод чанками std::unordered_map>> Nodes; // Количество зависимостей к ресурсу std::unordered_map NodesUsedCount; } ChunkUsedIds; struct { } WorldUsedIds; ResourceRequest NextRequest; std::vector SimplePackets; public: const std::string Username; public: RemoteClient(asio::io_context &ioc, tcp::socket socket, const std::string username) : Socket(ioc, std::move(socket)), Username(username) { } ~RemoteClient(); coro<> run(); void shutdown(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 prepareDefWorld(WorldId_t worldId, void* world); void prepareDefVoxel(VoxelId_t voxelId, void* voxel); void prepareDefNode(NodeId_t worldId, void* node); void prepareDefMediaStream(MediaStreamId_t modelId, void* mediaStream); // Отслеживаемое игроком использование контента void prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::vector &voxels); 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 prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkPos); void prepareWorldRemove(WorldId_t worldId); void prepareEntitySwap(WorldId_t prevWorldId, Pos::GlobalRegion prevRegionPos, EntityId_t prevEntityId, WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, EntityId_t newEntityId); void prepareEntityUpdate(WorldId_t worldId, Pos::GlobalRegion regionPos, EntityId_t entityId, const Entity *entity); void prepareEntityRemove(WorldId_t worldId, Pos::GlobalRegion regionPos, EntityId_t entityId); void preparePortalNew(PortalId_t portalId, void* portal); void preparePortalUpdate(PortalId_t portalId, void* portal); void preparePortalRemove(PortalId_t portalId); // Прочие моменты void prepareCameraSetEntity(WorldId_t worldId, Pos::GlobalChunk chunkPos, EntityId_t entityId); // Отправка подготовленных пакетов ResourceRequest pushPreparedPackets(); // Сообщить о ресурсах // Сюда приходят все обновления ресурсов движка // Глобально их можно запросить в выдаче pushPreparedPackets() void informateDefTexture(const std::unordered_map> &textures); void informateDefModel(const std::unordered_map> &models); void informateDefSound(const std::unordered_map> &sounds); private: WorldId_c rentWorldRemapId(WorldId_t wId); }; }