#include #include "RemoteClient.hpp" #include "Common/Abstract.hpp" #include "Common/Net.hpp" #include "Server/Abstract.hpp" #include "Server/World.hpp" #include #include #include #include #include namespace LV::Server { RemoteClient::~RemoteClient() { shutdown(EnumDisconnect::ByInterface, "~RemoteClient()"); if(Socket.isAlive()) { Socket.closeRead(); } UseLock.wait_no_use(); } coro<> RemoteClient::run() { auto useLock = UseLock.lock(); try { while(!IsGoingShutdown && IsConnected) { co_await readPacket(Socket); } } catch(const std::exception &exc) { if(const auto *errc = dynamic_cast(&exc); errc && errc->code() == boost::asio::error::operation_aborted) { co_return; } TOS::Logger("PlayerSocket").warn() << Username << ": " << exc.what(); } IsConnected = false; co_return; } void RemoteClient::shutdown(EnumDisconnect type, const std::string reason) { if(IsGoingShutdown) return; IsGoingShutdown = true; Net::Packet packet; packet << (uint8_t) ToClient::L1::System << (uint8_t) ToClient::L2System::Disconnect << (uint8_t) type << reason; std::string info; if(type == EnumDisconnect::ByInterface) info = "по запросу интерфейса " + reason; else if(type == EnumDisconnect::CriticalError) info = "на сервере произошла критическая ошибка " + reason; else if(type == EnumDisconnect::ProtocolError) info = "ошибка протокола (сервер) " + reason; Socket.pushPacket(std::move(packet)); LOG.info() << "Игрок '" << Username << "' отключился " << info; } void RemoteClient::NetworkAndResource_t::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels, const std::vector& uniq_sorted_defines) { Pos::bvec4u localChunk = chunkPos & 0x3; Pos::GlobalRegion regionPos = chunkPos >> 2; /* Обновить зависимости Запросить недостающие Отправить всё клиенту */ std::vector newTypes, /* Новые типы вокселей */ lostTypes /* Потерянные типы вокселей */; // Отметим использование этих вокселей for(const DefVoxelId& id : uniq_sorted_defines) { auto iter = ResUses.DefVoxel.find(id); if(iter == ResUses.DefVoxel.end()) { // Новый тип newTypes.push_back(id); ResUses.DefVoxel[id] = 1; } else { // Увеличиваем счётчик iter->second++; } } auto iterWorld = ResUses.RefChunk.find(worldId); if(iterWorld != ResUses.RefChunk.end()) // Исключим зависимости предыдущей версии чанка { auto iterRegion = iterWorld->second.find(regionPos); if(iterRegion != iterWorld->second.end()) { // Уменьшим счётчик зависимостей for(const DefVoxelId& id : iterRegion->second[localChunk.pack()].Voxel) { auto iter = ResUses.DefVoxel.find(id); assert(iter != ResUses.DefVoxel.end()); // Воксель должен быть в зависимостях if(--iter->second == 0) { // Вокселя больше нет в зависимостях lostTypes.push_back(id); ResUses.DefVoxel.erase(iter); } } } } else { ResUses.RefChunk[worldId] = {}; iterWorld = ResUses.RefChunk.find(worldId); } iterWorld->second[regionPos][localChunk.pack()].Voxel = uniq_sorted_defines; if(!newTypes.empty()) { // Добавляем новые типы в запрос NextRequest.Voxel.insert(NextRequest.Voxel.end(), newTypes.begin(), newTypes.end()); for(DefVoxelId voxel : newTypes) ResUses.RefDefVoxel[voxel] = {}; } if(!lostTypes.empty()) { for(const DefVoxelId& id : lostTypes) { auto iter = ResUses.RefDefVoxel.find(id); assert(iter != ResUses.RefDefVoxel.end()); // Должны быть описаны зависимости вокселя decrementAssets(std::move(iter->second)); ResUses.RefDefVoxel.erase(iter); checkPacketBorder(16); NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::FreeVoxel << id; } } checkPacketBorder(4+4+8+2+4+compressed_voxels.size()); NextPacket << (uint8_t) ToClient::L1::Content << (uint8_t) ToClient::L2Content::ChunkVoxels << worldId << chunkPos.pack() << uint32_t(compressed_voxels.size()); NextPacket.write((const std::byte*) compressed_voxels.data(), compressed_voxels.size()); } void RemoteClient::NetworkAndResource_t::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes, const std::vector& uniq_sorted_defines) { Pos::bvec4u localChunk = chunkPos & 0x3; Pos::GlobalRegion regionPos = chunkPos >> 2; std::vector newTypes, /* Новые типы нод */ lostTypes /* Потерянные типы нод */; // Отметим использование этих нод for(const DefNodeId& id : uniq_sorted_defines) { auto iter = ResUses.DefNode.find(id); if(iter == ResUses.DefNode.end()) { // Новый тип newTypes.push_back(id); ResUses.DefNode[id] = 1; } else { // Увеличиваем счётчик iter->second++; } } auto iterWorld = ResUses.RefChunk.find(worldId); if(iterWorld != ResUses.RefChunk.end()) // Исключим зависимости предыдущей версии чанка { auto iterRegion = iterWorld->second.find(regionPos); if(iterRegion != iterWorld->second.end()) { // Уменьшим счётчик зависимостей for(const DefNodeId& id : iterRegion->second[localChunk.pack()].Node) { auto iter = ResUses.DefNode.find(id); assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях if(--iter->second == 0) { // Ноды больше нет в зависимостях lostTypes.push_back(id); ResUses.DefNode.erase(iter); } } } } else { ResUses.RefChunk[worldId] = {}; iterWorld = ResUses.RefChunk.find(worldId); } iterWorld->second[regionPos][localChunk.pack()].Node = uniq_sorted_defines; if(!newTypes.empty()) { // Добавляем новые типы в запрос NextRequest.Node.insert(NextRequest.Node.end(), newTypes.begin(), newTypes.end()); for(DefNodeId node : newTypes) ResUses.RefDefNode[node] = {}; } if(!lostTypes.empty()) { for(const DefNodeId& id : lostTypes) { auto iter = ResUses.RefDefNode.find(id); assert(iter != ResUses.RefDefNode.end()); // Должны быть описаны зависимости ноды decrementAssets(std::move(iter->second)); ResUses.RefDefNode.erase(iter); checkPacketBorder(16); NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::FreeNode << id; } } checkPacketBorder(4+4+8+4+compressed_nodes.size()); NextPacket << (uint8_t) ToClient::L1::Content << (uint8_t) ToClient::L2Content::ChunkNodes << worldId << chunkPos.pack() << uint32_t(compressed_nodes.size()); NextPacket.write((const std::byte*) compressed_nodes.data(), compressed_nodes.size()); } void RemoteClient::NetworkAndResource_t::prepareRegionsRemove(WorldId_t worldId, std::vector regionPoses) { std::vector lostTypesV /* Потерянные типы вокселей */; std::vector lostTypesN /* Потерянные типы нод */; for(Pos::GlobalRegion regionPos : regionPoses) // Уменьшаем зависимости вокселей и нод { auto iterWorld = ResUses.RefChunk.find(worldId); if(iterWorld == ResUses.RefChunk.end()) return; auto iterRegion = iterWorld->second.find(regionPos); if(iterRegion == iterWorld->second.end()) return; for(const auto &iterChunk : iterRegion->second) { for(const DefVoxelId& id : iterChunk.Voxel) { auto iter = ResUses.DefVoxel.find(id); assert(iter != ResUses.DefVoxel.end()); // Воксель должен быть в зависимостях if(--iter->second == 0) { // Вокселя больше нет в зависимостях lostTypesV.push_back(id); ResUses.DefVoxel.erase(iter); } } for(const DefNodeId& id : iterChunk.Node) { auto iter = ResUses.DefNode.find(id); assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях if(--iter->second == 0) { // Ноды больше нет в зависимостях lostTypesN.push_back(id); ResUses.DefNode.erase(iter); } } } iterWorld->second.erase(iterRegion); } if(!lostTypesV.empty()) { for(const DefVoxelId& id : lostTypesV) { auto iter = ResUses.RefDefVoxel.find(id); assert(iter != ResUses.RefDefVoxel.end()); // Должны быть описаны зависимости вокселя decrementAssets(std::move(iter->second)); ResUses.RefDefVoxel.erase(iter); checkPacketBorder(16); NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::FreeVoxel << id; } } if(!lostTypesN.empty()) { for(const DefNodeId& id : lostTypesN) { auto iter = ResUses.RefDefNode.find(id); assert(iter != ResUses.RefDefNode.end()); // Должны быть описаны зависимости ноды decrementAssets(std::move(iter->second)); ResUses.RefDefNode.erase(iter); checkPacketBorder(16); NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::FreeNode << id; } } for(Pos::GlobalRegion regionPos : regionPoses) { checkPacketBorder(16); NextPacket << (uint8_t) ToClient::L1::Content << (uint8_t) ToClient::L2Content::RemoveRegion << worldId << regionPos.pack(); } } void RemoteClient::NetworkAndResource_t::prepareEntitiesUpdate(const std::vector>& entities) { for(auto& [entityId, entity] : entities) { // Сопоставим с идентификатором клиента ClientEntityId_t ceId = ReMapEntities.toClient(entityId); // Профиль новый { DefEntityId profile = entity->getDefId(); auto iter = ResUses.DefEntity.find(profile); if(iter == ResUses.DefEntity.end()) { // Клиенту неизвестен профиль NextRequest.Entity.push_back(profile); ResUses.DefEntity[profile] = 1; } else iter->second++; } // Добавление модификационных зависимостей // incrementBinary({}, {}, {}, {}, {}); // Старые данные { auto iterEntity = ResUses.RefEntity.find(entityId); if(iterEntity != ResUses.RefEntity.end()) { // Убавляем зависимость к старому профилю auto iterProfile = ResUses.DefEntity.find(iterEntity->second.Profile); assert(iterProfile != ResUses.DefEntity.end()); // Старый профиль должен быть if(--iterProfile->second == 0) { // Старый профиль больше не нужен auto iterProfileRef = ResUses.RefDefEntity.find(iterEntity->second.Profile); decrementAssets(std::move(iterProfileRef->second)); ResUses.DefEntity.erase(iterProfile); } // Убавляем зависимость к модификационным данным // iterEntity->second. // decrementBinary({}, {}, {}, {}, {}); } } // TODO: отправить клиенту } } void RemoteClient::NetworkAndResource_t::prepareEntitySwap(ServerEntityId_t prev, ServerEntityId_t next) { ReMapEntities.rebindClientKey(prev, next); } void RemoteClient::NetworkAndResource_t::prepareEntitiesRemove(const std::vector& entityIds) { for(ServerEntityId_t entityId : entityIds) { ClientEntityId_t cId = ReMapEntities.erase(entityId); // Убавляем старые данные { auto iterEntity = ResUses.RefEntity.find(entityId); assert(iterEntity != ResUses.RefEntity.end()); // Зависимости должны быть // Убавляем модификационные заависимости //decrementBinary(std::vector &&textures, std::vector &&animation, std::vector &&sounds, std::vector &&models, std::vector &&fonts) // Убавляем зависимость к профилю auto iterProfile = ResUses.DefEntity.find(iterEntity->second.Profile); assert(iterProfile != ResUses.DefEntity.end()); // Профиль должен быть if(--iterProfile->second == 0) { // Профиль больше не используется auto iterProfileRef = ResUses.RefDefEntity.find(iterEntity->second.Profile); decrementAssets(std::move(iterProfileRef->second)); ResUses.RefDefEntity.erase(iterProfileRef); ResUses.DefEntity.erase(iterProfile); } } checkPacketBorder(16); NextPacket << (uint8_t) ToClient::L1::Content << (uint8_t) ToClient::L2Content::RemoveEntity << cId; } } void RemoteClient::NetworkAndResource_t::prepareWorldUpdate(WorldId_t worldId, World* world) { // Добавление зависимостей ResUses.RefChunk[worldId]; // Профиль { DefWorldId defWorld = world->getDefId(); auto iterWorldProf = ResUses.DefWorld.find(defWorld); if(iterWorldProf == ResUses.DefWorld.end()) { // Профиль мира неизвестен клиенту ResUses.DefWorld[defWorld] = 1; NextRequest.World.push_back(defWorld); } else { iterWorldProf->second++; } } // Если есть предыдущая версия мира { auto iterWorld = ResUses.RefWorld.find(worldId); if(iterWorld != ResUses.RefWorld.end()) { // Мир известен клиенту // Убавляем модицикационные зависимости предыдущей версии мира // iterWorld->second. // Убавляем зависимости старого профиля auto iterWorldProf = ResUses.DefWorld.find(iterWorld->second.Profile); assert(iterWorldProf != ResUses.DefWorld.end()); // Старый профиль должен быть известен if(--iterWorldProf->second == 0) { // Старый профиль более ни кем не используется ResUses.DefWorld.erase(iterWorldProf); auto iterWorldProfRef = ResUses.RefDefWorld.find(iterWorld->second.Profile); assert(iterWorldProfRef != ResUses.RefDefWorld.end()); // Зависимости предыдущего профиля также должны быть decrementAssets(std::move(iterWorldProfRef->second)); ResUses.RefDefWorld.erase(iterWorldProfRef); } } } // Указываем модификационные зависимости текущей версии мира ResUses.RefWorld[worldId] = {world->getDefId()}; // TODO: отправить мир } void RemoteClient::NetworkAndResource_t::prepareWorldRemove(WorldId_t worldId) { // Чанки уже удалены prepareChunkRemove // Обновление зависимостей auto iterWorld = ResUses.RefWorld.find(worldId); assert(iterWorld != ResUses.RefWorld.end()); // Убавляем модификационные зависимости // decrementBinary(std::move(iterWorld->second.Texture), std::move(iterWorld->second.Model), {}, {}); auto iterWorldProf = ResUses.DefWorld.find(iterWorld->second.Profile); assert(iterWorldProf != ResUses.DefWorld.end()); // Профиль мира должен быть if(--iterWorldProf->second == 0) { // Профиль мира более не используется ResUses.DefWorld.erase(iterWorldProf); // Убавляем зависимости профиля auto iterWorldProfDef = ResUses.RefDefWorld.find(iterWorld->second.Profile); assert(iterWorldProfDef != ResUses.RefDefWorld.end()); // Зависимости профиля должны быть decrementAssets(std::move(iterWorldProfDef->second)); ResUses.RefDefWorld.erase(iterWorldProfDef); } ResUses.RefWorld.erase(iterWorld); auto iter = ResUses.RefChunk.find(worldId); assert(iter->second.empty()); ResUses.RefChunk.erase(iter); } // void RemoteClient::NetworkAndResource_t::preparePortalUpdate(PortalId portalId, void* portal) {} // void RemoteClient::NetworkAndResource_t::preparePortalRemove(PortalId portalId) {} void RemoteClient::prepareCameraSetEntity(ServerEntityId_t entityId) { } ResourceRequest RemoteClient::pushPreparedPackets() { std::vector toSend; ResourceRequest nextRequest; { auto lock = NetworkAndResource.lock(); if(lock->NextPacket.size()) lock->SimplePackets.push_back(std::move(lock->NextPacket)); toSend = std::move(lock->SimplePackets); nextRequest = std::move(lock->NextRequest); } if(AssetsInWork.AssetsPacket.size()) { toSend.push_back(std::move(AssetsInWork.AssetsPacket)); } { Net::Packet p; p << (uint8_t) ToClient::L1::System << (uint8_t) ToClient::L2System::SyncTick; toSend.push_back(std::move(p)); } Socket.pushPackets(&toSend); toSend.clear(); nextRequest.uniq(); return std::move(nextRequest); } void RemoteClient::informateAssets(const std::vector>& resources) { std::vector> newForClient; for(auto& [type, resId, domain, key, resource] : resources) { auto hash = resource.hash(); auto lock = NetworkAndResource.lock(); // Проверка запрашиваемых клиентом ресурсов { auto iter = std::find(lock->ClientRequested.begin(), lock->ClientRequested.end(), hash); if(iter != lock->ClientRequested.end()) { lock->ClientRequested.erase(iter); lock.unlock(); auto it = std::lower_bound(AssetsInWork.OnClient.begin(), AssetsInWork.OnClient.end(), hash); if(it == AssetsInWork.OnClient.end() || *it != hash) { AssetsInWork.OnClient.insert(it, hash); AssetsInWork.ToSend.emplace_back(type, domain, key, resId, resource, 0); } else { LOG.warn() << "Клиент повторно запросил имеющийся у него ресурс"; } lock = NetworkAndResource.lock(); } } // Информирование клиента о привязках ресурсов к идентификатору { // Посмотрим что известно клиенту if(auto iter = lock->ResUses.AssetsUse[(int) type].find(resId); iter != lock->ResUses.AssetsUse[(int) type].end() && std::get(iter->second) != hash ) { lock.unlock(); // Требуется перепривязать идентификатор к новому хешу newForClient.push_back({(EnumAssets) type, resId, domain, key, hash, resource.size()}); std::get(iter->second) = hash; } } } // Отправляем новые привязки ресурсов if(!newForClient.empty()) { assert(newForClient.size() < 65535*4); auto lock = NetworkAndResource.lock(); lock->checkPacketBorder(2+1+4+newForClient.size()*(1+4+64+32)); lock->NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение << ((uint8_t) ToClient::L2Resource::Bind) << uint32_t(newForClient.size()); for(auto& [type, resId, domain, key, hash, size] : newForClient) { // TODO: может внести ограничение на длину домена и ключа? lock->NextPacket << uint8_t(type) << uint32_t(resId) << domain << key; lock->NextPacket.write((const std::byte*) hash.data(), hash.size()); } } } void RemoteClient::NetworkAndResource_t::informateDefVoxel(const std::vector>& voxels) { for(auto pair : voxels) { DefVoxelId id = pair.first; if(!ResUses.DefVoxel.contains(id)) continue; NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::Voxel << id; } } void RemoteClient::NetworkAndResource_t::informateDefNode(const std::vector>& nodes) { for(auto& [id, def] : nodes) { if(!ResUses.DefNode.contains(id)) continue; checkPacketBorder(1+1+4+4); NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::Node << id << (uint32_t) def->NodestateId; ResUses_t::RefAssets_t refs; { refs.Resources[(uint8_t) EnumAssets::Nodestate].push_back(def->NodestateId); refs.Resources[(uint8_t) EnumAssets::Texture] = def->TextureDeps; refs.Resources[(uint8_t) EnumAssets::Model] = def->ModelDeps; incrementAssets(refs); } { auto iterDefRef = ResUses.RefDefNode.find(id); if(iterDefRef != ResUses.RefDefNode.end()) { decrementAssets(std::move(iterDefRef->second)); iterDefRef->second = std::move(refs); } else { ResUses.RefDefNode[id] = std::move(refs); } } } } void RemoteClient::NetworkAndResource_t::informateDefWorld(const std::vector>& worlds) { // for(auto pair : worlds) { // DefWorldId_t id = pair.first; // if(!ResUses.DefWorld.contains(id)) // continue; // NextPacket << (uint8_t) ToClient::L1::Definition // << (uint8_t) ToClient::L2Definition::World // << id; // } } void RemoteClient::NetworkAndResource_t::informateDefPortal(const std::vector>& portals) { // for(auto pair : portals) { // DefPortalId_t id = pair.first; // if(!ResUses.DefPortal.contains(id)) // continue; // NextPacket << (uint8_t) ToClient::L1::Definition // << (uint8_t) ToClient::L2Definition::Portal // << id; // } } void RemoteClient::NetworkAndResource_t::informateDefEntity(const std::vector>& entityes) { // for(auto pair : entityes) { // DefEntityId_t id = pair.first; // if(!ResUses.DefEntity.contains(id)) // continue; // NextPacket << (uint8_t) ToClient::L1::Definition // << (uint8_t) ToClient::L2Definition::Entity // << id; // } } void RemoteClient::NetworkAndResource_t::informateDefItem(const std::vector>& items) { // for(auto pair : items) { // DefItemId_t id = pair.first; // if(!ResUses.DefNode.contains(id)) // continue; // NextPacket << (uint8_t) ToClient::L1::Definition // << (uint8_t) ToClient::L2Definition::FuncEntity // << id; // } } void RemoteClient::protocolError() { shutdown(EnumDisconnect::ProtocolError, "Ошибка протокола"); } coro<> RemoteClient::readPacket(Net::AsyncSocket &sock) { uint8_t first = co_await sock.read(); switch((ToServer::L1) first) { case ToServer::L1::System: co_await rP_System(sock); co_return; default: protocolError(); } } coro<> RemoteClient::rP_System(Net::AsyncSocket &sock) { uint8_t second = co_await sock.read(); switch((ToServer::L2System) second) { case ToServer::L2System::InitEnd: co_return; case ToServer::L2System::Disconnect: { EnumDisconnect type = (EnumDisconnect) co_await sock.read(); shutdown(EnumDisconnect::ByInterface, "Вы были отключены от игры"); std::string reason; if(type == EnumDisconnect::CriticalError) reason = ": Критическая ошибка"; else reason = ": Ошибка протокола (клиент)"; LOG.info() << "Игрок '" << Username << "' отключился" << reason; co_return; } case ToServer::L2System::Test_CAM_PYR_POS: { Pos::Object newPos; newPos.x = co_await sock.read(); newPos.y = co_await sock.read(); newPos.z = co_await sock.read(); CameraPos = newPos; for(int iter = 0; iter < 5; iter++) CameraQuat.Data[iter] = co_await sock.read(); co_return; } case ToServer::L2System::BlockChange: { uint8_t action = co_await sock.read(); Actions.lock()->push(action); co_return; } case ToServer::L2System::ResourceRequest: { uint16_t count = co_await sock.read(); std::vector hashes; hashes.reserve(count); for(int iter = 0; iter < count; iter++) { Hash_t hash; co_await sock.read((std::byte*) hash.data(), 32); hashes.push_back(hash); } auto lock = NetworkAndResource.lock(); lock->NextRequest.Hashes.append_range(hashes); lock->ClientRequested.append_range(hashes); co_return; } default: protocolError(); } } void RemoteClient::NetworkAndResource_t::incrementAssets(const ResUses_t::RefAssets_t& bin) { for(int iter = 0; iter < 5; iter++) { auto &use = ResUses.AssetsUse[iter]; for(ResourceId id : bin.Resources[iter]) { if(++std::get<0>(use[id]) == 1) { NextRequest.AssetsInfo[iter].push_back(id); // LOG.debug() << "Новое определение (тип " << iter << ") -> " << id; } } } } void RemoteClient::NetworkAndResource_t::decrementAssets(ResUses_t::RefAssets_t&& bin) { std::vector> lost; for(int iter = 0; iter < (int) EnumAssets::MAX_ENUM; iter++) { auto &use = ResUses.AssetsUse[iter]; for(ResourceId id : bin.Resources[iter]) { if(--std::get<0>(use[id]) == 0) { use.erase(use.find(id)); lost.push_back({(EnumAssets) iter, id}); // LOG.debug() << "Потеряно определение (тип " << iter << ") -> " << id; } } } if(!lost.empty()) { assert(lost.size() < 65535*4); checkPacketBorder(1+1+4+lost.size()*(1+4)); NextPacket << (uint8_t) ToClient::L1::Resource << (uint8_t) ToClient::L2Resource::Lost << uint32_t(lost.size()); for(auto& [type, id] : lost) NextPacket << uint8_t(type) << uint32_t(id); } } void RemoteClient::onUpdate() { Pos::Object cameraPos = CameraPos; Pos::GlobalRegion r1 = LastPos >> 12 >> 4 >> 2; Pos::GlobalRegion r2 = cameraPos >> 12 >> 4 >> 2; if(r1 != r2) { CrossedRegion = true; } if(!Actions.get_read().empty()) { auto lock = Actions.lock(); while(!lock->empty()) { uint8_t action = lock->front(); lock->pop(); glm::quat q = CameraQuat.toQuat(); glm::vec4 v = glm::mat4(q)*glm::vec4(0, 0, -6, 1); Pos::GlobalNode pos = (Pos::GlobalNode) (glm::vec3) v; pos += cameraPos >> Pos::Object_t::BS_Bit; if(action == 0) { // Break Break.push(pos); } else if(action == 1) { // Build Build.push(pos); } } } LastPos = cameraPos; // Отправка ресурсов if(!AssetsInWork.ToSend.empty()) { auto& toSend = AssetsInWork.ToSend; size_t chunkSize = std::max(1'024'000 / toSend.size(), 4096); Net::Packet& p = AssetsInWork.AssetsPacket; bool hasFullSended = false; for(auto& [type, domain, key, id, res, sended] : toSend) { if(sended == 0) { // Оповещаем о начале отправки ресурса p << (uint8_t) ToClient::L1::Resource << (uint8_t) ToClient::L2Resource::InitResSend << uint32_t(res.size()); p.write((const std::byte*) res.hash().data(), 32); p << uint32_t(id) << uint8_t(type) << domain << key; } // Отправляем чанк size_t willSend = std::min(chunkSize, res.size()-sended); p << (uint8_t) ToClient::L1::Resource << (uint8_t) ToClient::L2Resource::ChunkSend; p.write((const std::byte*) res.hash().data(), 32); p << uint32_t(willSend); p.write(res.data() + sended, willSend); sended += willSend; if(sended == res.size()) { hasFullSended = true; } } if(hasFullSended) { for(ssize_t iter = toSend.size()-1; iter >= 0; iter--) { if(std::get<4>(toSend[iter]).size() == std::get<5>(toSend[iter])) { toSend.erase(toSend.begin()+iter); } } } } } std::vector> RemoteClient::getViewPoints() { return {{0, CameraPos, 1}}; } }