#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 remap(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); if(!bits.test(subIndex)) MAKE_ERROR("Идентификатор не привязан"); 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); if(!bits.test(subIndex)) MAKE_ERROR("Идентификатор не привязан"); bits.reset(subIndex); } void map(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); if(bits.test(subIndex)) { MAKE_ERROR("Идентификатор уже занят"); } bits.set(subIndex); keys[subIndex] = sKey; } }; template class SortedChunkedMap { struct Chunk { uint8_t bitset = 0; std::array, sizeof(bitset)> Data; }; public: }; template && std::is_integral_v && sizeof(ServerKey) >= sizeof(ClientKey), int> = 0> class SCMapper { public: }; template && std::is_integral_v, int> = 0> class SCKeyRemapper { std::bitset<(1 << sizeof(ClientKey)*8) - 1> UsedIdC; // 1 - идентификатор свободен, 0 - занят std::map SCTable; public: // Если аллоцировать идентификатор не получится, будет возвращено ClientKey(0) ClientKey toClient(ServerKey sKey) { }; }; /* Шаблоны игрового контента, которые необходимо поддерживать в актуальном состоянии для клиента и шаблоны, которые клиенту уже не нужны. Соответствующие менеджеры ресурсов будут следить за изменениями этих ресурсов и переотправлять их клиенту */ struct ResourceRequest { std::vector NewWorlds; std::vector NewVoxels; std::vector NewNodes; 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()); 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, &NewTextures, &NewModels, &NewSounds}) { std::sort(vec->begin(), vec->end()); auto last = std::unique(vec->begin(), vec->end()); vec->erase(last, vec->end()); } } }; /* Обработчик сокета клиента. Подписывает клиента на отслеживание необходимых ресурсов на основе передаваемых клиенту данных */ 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::unordered_map> CTS_Entityes; std::unordered_map>> STC_Entityes; 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); }; }