#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 NewTextures; std::vector NewModels; std::vector NewSounds; std::vector NewWorlds; std::vector NewVoxels; std::vector NewNodes; std::vector NewPortals; std::vector NewEntityes; void insert(const ResourceRequest &obj) { 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()); 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()); NewPortals.insert(NewPortals.end(), obj.NewPortals.begin(), obj.NewPortals.end()); NewEntityes.insert(NewEntityes.end(), obj.NewEntityes.begin(), obj.NewEntityes.end()); } void uniq() { for(std::vector *vec : {&NewTextures, &NewModels, &NewSounds, &NewWorlds, &NewVoxels, &NewNodes, &NewPortals, &NewEntityes}) { 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 ClientCache; struct ResUsesObj { // Счётчики использования базовых ресурсов высшими объектами std::map BinTexture; std::map BinSound; // Может использовать текстуры std::map BinModel; // Будут использовать в своих определениях текстуры, звуки, модели std::map DefWorld; std::map DefVoxel; std::map DefNode; std::map DefPortal; std::map DefEntity; // Переписываемый контент // Сущности используют текстуры, звуки, модели struct EntityResourceUse { DefEntityId_t DefId; std::unordered_set Textures; std::unordered_set Sounds; std::unordered_set Models; }; std::map Entity; // Чанки используют воксели, ноды std::map, std::unordered_set> ChunkVoxels; std::map, std::unordered_set> ChunkNodes; // Миры struct WorldResourceUse { DefWorldId_t DefId; std::unordered_set Textures; std::unordered_set Sounds; std::unordered_set Models; }; std::map Worlds; // Порталы struct PortalResourceUse { DefPortalId_t DefId; std::unordered_set Textures; std::unordered_set Sounds; std::unordered_set Models; }; std::map Portals; } ResUses; struct { SCSKeyRemapper BinTextures; SCSKeyRemapper BinSounds; SCSKeyRemapper BinModels; SCSKeyRemapper DefWorlds; SCSKeyRemapper DefVoxels; SCSKeyRemapper DefNodes; SCSKeyRemapper DefPortals; SCSKeyRemapper DefEntityes; SCSKeyRemapper Worlds; SCSKeyRemapper Portals; SCSKeyRemapper Entityes; } ResRemap; Net::Packet NextPacket; ResourceRequest NextRequest; std::vector SimplePackets; 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), ClientCache(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); } // Функции подготавливают пакеты к отправке // Отслеживаемое игроком использование контента // Maybe? // Текущий список вокселей, определения нод, которые больше не используются в чанке, и определения нод, которые теперь используются //void prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::vector &voxels, const std::vector &noLongerInUseDefs, const std::vector &nowUsed); 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 prepareEntitySwap(GlobalEntityId_t prevEntityId, GlobalEntityId_t nextEntityId); void prepareEntityUpdate(GlobalEntityId_t entityId, const Entity *entity); void prepareEntityRemove(GlobalEntityId_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(GlobalEntityId_t entityId); // Отправка подготовленных пакетов ResourceRequest pushPreparedPackets(); // Сообщить о ресурсах // Сюда приходят все обновления ресурсов движка // Глобально их можно запросить в выдаче pushPreparedPackets() // Двоичные файлы void informateDefTexture(const std::unordered_map> &textures); void informateDefSound(const std::unordered_map> &sounds); void informateDefModel(const std::unordered_map> &models); // Игровые определения void informateDefWorld(const std::unordered_map &worlds); void informateDefVoxel(const std::unordered_map &voxels); void informateDefNode(const std::unordered_map &nodes); void informateDefEntityes(const std::unordered_map &entityes); void informateDefPortals(const std::unordered_map &portals); private: void checkPacketBorder(uint16_t size); void protocolError(); coro<> readPacket(Net::AsyncSocket &sock); coro<> rP_System(Net::AsyncSocket &sock); void incrementBinary(std::unordered_set &textures, std::unordered_set &sounds, std::unordered_set &models); void decrementBinary(std::unordered_set &textures, std::unordered_set &sounds, std::unordered_set &models); }; }