#include #include "RemoteClient.hpp" #include "Common/Abstract.hpp" #include "Common/Net.hpp" #include "Server/Abstract.hpp" #include "Server/GameServer.hpp" #include "Server/World.hpp" #include #include #include #include #include #include namespace LV::Server { Net::Packet RemoteClient::makePacket_informateAssets_DK( const std::array< std::vector, static_cast(EnumAssets::MAX_ENUM) >& dkVector ) { Net::Packet pack; // Сжатие по дедубликации доменов std::unordered_map domainsToId; { std::unordered_set domains; for(size_t type = 0; type < static_cast(EnumAssets::MAX_ENUM); type++) { for(const auto& bind : dkVector[type]) { domains.insert(bind.Domain); } } pack << uint16_t(domains.size()); int counter = 0; for(const std::string& domain : domains) { pack << domain; domainsToId[domain] = counter++; } } // Запись связок домен+ключ for(size_t type = 0; type < static_cast(EnumAssets::MAX_ENUM); type++) { const std::vector& binds = dkVector[type]; pack << uint32_t(binds.size()); for(const auto& bind : binds) { auto iter = domainsToId.find(bind.Domain); assert(iter != domainsToId.end()); pack << iter->second << bind.Key; } } // Сжатие std::u8string compressed = compressLinear(pack.complite()); pack.clear(); pack << uint8_t(ToClient::AssetsBindDK) << (const std::string&) compressed; return pack; } Net::Packet RemoteClient::makePacket_informateAssets_HH( const std::array< std::vector, static_cast(EnumAssets::MAX_ENUM) >& hhVector, const std::array< std::vector, static_cast(EnumAssets::MAX_ENUM) >& lost ) { Net::Packet pack; pack << uint8_t(ToClient::AssetsBindHH); // Запись связок hash+header for(size_t type = 0; type < static_cast(EnumAssets::MAX_ENUM); type++) { const std::vector& binds = hhVector[type]; pack << uint32_t(binds.size()); for(const auto& bind : binds) { pack << bind.Id; pack.write((const std::byte*) bind.Hash.data(), bind.Hash.size()); pack << (const std::string&) bind.Header; } } return pack; } std::vector RemoteClient::makePackets_sendDefContentUpdate( std::array< std::vector< std::pair< ResourceId, // Идентификатор профиля std::u8string // Двоичный формат профиля > >, static_cast(EnumDefContent::MAX_ENUM) > newOrUpdate, // Новые или изменённые std::array< std::vector, static_cast(EnumDefContent::MAX_ENUM) > lost, // Потерянные профили std::array< std::vector>, static_cast(EnumDefContent::MAX_ENUM) > idToDK // Новые привязки ) { std::vector packets; Net::Packet pack; auto check = [&](size_t needSize) { if(pack.size()+needSize > 65500) { packets.emplace_back(std::move(pack)); pack.clear(); } }; pack << (uint8_t) ToClient::DefinitionsUpdate; pack << uint32_t(newOrUpdate.size()); for(size_t type = 0; type < static_cast(EnumDefContent::MAX_ENUM); type++) { pack << uint32_t(newOrUpdate[type].size()); for(const auto& [id, data] : newOrUpdate[type]) { check(data.size()); pack << id << (const std::string&) data; } } pack << uint32_t(lost.size()); for(size_t type = 0; type < static_cast(EnumDefContent::MAX_ENUM); type++) { pack << uint32_t(lost[type].size()); for(ResourceId id : lost[type]) { check(4); pack << id; } } pack << uint32_t(idToDK.size()); for(size_t type = 0; type < static_cast(EnumDefContent::MAX_ENUM); type++) { pack << uint32_t(idToDK[type].size()); for(const auto& [domain, key] : idToDK[type]) { check(domain.size() + key.size() + 8); pack << key << domain; } } if(pack.size()) packets.emplace_back(std::move(pack)); return packets; } namespace { const char* assetTypeName(EnumAssets type) { switch(type) { case EnumAssets::Nodestate: return "nodestate"; case EnumAssets::Model: return "model"; case EnumAssets::Texture: return "texture"; case EnumAssets::Particle: return "particle"; case EnumAssets::Animation: return "animation"; case EnumAssets::Sound: return "sound"; case EnumAssets::Font: return "font"; default: return "unknown"; } } } 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::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::prepareChunkUpdate_Voxels( // WorldId_t worldId, // Pos::GlobalChunk chunkPos, // const std::u8string& compressed_voxels // ) { // Pos::bvec4u localChunk = chunkPos & 0x3; // Pos::GlobalRegion regionPos = chunkPos >> 2; // packet << (uint8_t) ToClient::ChunkVoxels // << worldId << chunkPos.pack() << uint32_t(compressed_voxels.size()); // packet.write((const std::byte*) compressed_voxels.data(), compressed_voxels.size()); // } // void RemoteClient::prepareChunkUpdate_Nodes( // WorldId_t worldId, // Pos::GlobalChunk chunkPos, // const std::u8string& compressed_nodes // ) { // Pos::bvec4u localChunk = chunkPos & 0x3; // Pos::GlobalRegion regionPos = chunkPos >> 2; // packet << (uint8_t) ToClient::ChunkNodes // << worldId << chunkPos.pack() << uint32_t(compressed_nodes.size()); // packet.write((const std::byte*) compressed_nodes.data(), compressed_nodes.size()); // } void RemoteClient::NetworkAndResource_t::prepareRegionsRemove(WorldId_t worldId, std::vector regionPoses) { auto worldIter = ChunksToSend.find(worldId); if(worldIter != ChunksToSend.end()) { for(const Pos::GlobalRegion ®ionPos : regionPoses) worldIter->second.erase(regionPos); if(worldIter->second.empty()) ChunksToSend.erase(worldIter); } for(Pos::GlobalRegion regionPos : regionPoses) { checkPacketBorder(16); NextPacket << (uint8_t) ToClient::RemoveRegion << worldId << regionPos.pack(); } } void RemoteClient::NetworkAndResource_t::flushChunksToPackets() { for(auto &worldPair : ChunksToSend) { const WorldId_t worldId = worldPair.first; auto ®ions = worldPair.second; for(auto ®ionPair : regions) { const Pos::GlobalRegion regionPos = regionPair.first; auto &voxels = regionPair.second.first; auto &nodes = regionPair.second.second; for(auto &chunkPair : voxels) { const Pos::bvec4u chunkPos = chunkPair.first; const std::u8string &compressed = chunkPair.second; Pos::GlobalChunk globalPos = (Pos::GlobalChunk) regionPos; globalPos <<= 2; globalPos += (Pos::GlobalChunk) chunkPos; const size_t size = 1 + sizeof(WorldId_t) + sizeof(Pos::GlobalChunk::Pack) + sizeof(uint32_t) + compressed.size(); checkPacketBorder(static_cast(std::min(size, 64000))); NextPacket << (uint8_t) ToClient::ChunkVoxels << worldId << globalPos.pack() << uint32_t(compressed.size()); NextPacket.write((const std::byte*) compressed.data(), compressed.size()); } for(auto &chunkPair : nodes) { const Pos::bvec4u chunkPos = chunkPair.first; const std::u8string &compressed = chunkPair.second; Pos::GlobalChunk globalPos = (Pos::GlobalChunk) regionPos; globalPos <<= 2; globalPos += (Pos::GlobalChunk) chunkPos; const size_t size = 1 + sizeof(WorldId_t) + sizeof(Pos::GlobalChunk::Pack) + sizeof(uint32_t) + compressed.size(); checkPacketBorder(static_cast(std::min(size, 64000))); NextPacket << (uint8_t) ToClient::ChunkNodes << worldId << globalPos.pack() << uint32_t(compressed.size()); NextPacket.write((const std::byte*) compressed.data(), compressed.size()); } } } ChunksToSend.clear(); } void RemoteClient::NetworkAndResource_t::prepareEntitiesUpdate(const std::vector>& entities) { // for(auto& [entityId, entity] : entities) { // // Сопоставим с идентификатором клиента // ClientEntityId_t ceId = ReMapEntities.toClient(entityId); // checkPacketBorder(32); // NextPacket << (uint8_t) ToClient::Entity // << ceId // << (uint32_t) entity->getDefId() // << (uint32_t) entity->WorldId // << entity->Pos.x // << entity->Pos.y // << entity->Pos.z; // { // ToServer::PacketQuat q; // q.fromQuat(entity->Quat); // for(int iter = 0; iter < 5; iter++) // NextPacket << q.Data[iter]; // } // } } void RemoteClient::NetworkAndResource_t::prepareEntitiesUpdate_Dynamic(const std::vector>& entities) { prepareEntitiesUpdate(entities); } 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); // checkPacketBorder(16); // NextPacket << (uint8_t) ToClient::L1::Content // << (uint8_t) ToClient::L2Content::RemoveEntity // << cId; // } } void RemoteClient::NetworkAndResource_t::prepareWorldUpdate(WorldId_t worldId, World* world) { // TODO: отправить мир } void RemoteClient::NetworkAndResource_t::prepareWorldRemove(WorldId_t worldId) { } // void RemoteClient::NetworkAndResource_t::preparePortalUpdate(PortalId portalId, void* portal) {} // void RemoteClient::NetworkAndResource_t::preparePortalRemove(PortalId portalId) {} void RemoteClient::prepareCameraSetEntity(ServerEntityId_t entityId) { auto lock = NetworkAndResource.lock(); lock->checkPacketBorder(4); lock->NextPacket << (uint8_t) ToClient::TestLinkCameraToEntity; } ResourceRequest RemoteClient::pushPreparedPackets() { std::vector toSend; ResourceRequest nextRequest; { auto lock = NetworkAndResource.lock(); lock->flushChunksToPackets(); if(lock->NextPacket.size()) lock->SimplePackets.push_back(std::move(lock->NextPacket)); toSend = std::move(lock->SimplePackets); nextRequest = std::move(lock->NextRequest); } if(!AssetsInWork.AssetsPackets.empty()) { for(Net::Packet& packet : AssetsInWork.AssetsPackets) toSend.push_back(std::move(packet)); AssetsInWork.AssetsPackets.clear(); } if(AssetsInWork.AssetsPacket.size()) toSend.push_back(std::move(AssetsInWork.AssetsPacket)); { Net::Packet p; p << (uint8_t) ToClient::Tick; toSend.push_back(std::move(p)); } Socket.pushPackets(&toSend); toSend.clear(); nextRequest.uniq(); return std::move(nextRequest); } void RemoteClient::informateBinaryAssets(const std::vector& resources) { for(const AssetBinaryInfo& resource : resources) { auto lock = NetworkAndResource.lock(); auto iter = std::find(lock->ClientRequested.begin(), lock->ClientRequested.end(), resource.Hash); if(iter == lock->ClientRequested.end()) continue; lock->ClientRequested.erase(iter); lock.unlock(); auto it = std::lower_bound(AssetsInWork.OnClient.begin(), AssetsInWork.OnClient.end(), resource.Hash); if(it == AssetsInWork.OnClient.end() || *it != resource.Hash) { AssetsInWork.OnClient.insert(it, resource.Hash); AssetsInWork.ToSend.emplace_back(resource.Data, 0); } else { LOG.warn() << "Клиент повторно запросил имеющийся у него ресурс"; } } } 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: { static std::atomic debugRequestLogCount = 0; 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); if(debugRequestLogCount.fetch_add(1) < 64) { if(!hashes.empty()) { const auto& h = hashes.front(); LOG.debug() << "ResourceRequest count=" << count << " first=" << int(h[0]) << '.' << int(h[1]) << '.' << int(h[2]) << '.' << int(h[3]); } else { LOG.debug() << "ResourceRequest count=" << count; } } for(const auto& hash : hashes) { LOG.debug() << "Client requested hash=" << int(hash[0]) << '.' << int(hash[1]) << '.' << int(hash[2]) << '.' << int(hash[3]); } co_return; } case ToServer::L2System::ReloadMods: { if(Server) { Server->requestModsReload(); LOG.info() << "Запрос на перезагрузку модов"; } else { LOG.warn() << "Запрос на перезагрузку модов отклонён: сервер не назначен"; } co_return; } default: protocolError(); } } 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; constexpr uint16_t kMaxAssetPacketSize = 64000; const size_t maxChunkPayload = std::max(1, kMaxAssetPacketSize - 1 - 1 - 32 - 4); size_t chunkSize = std::max(1'024'000 / toSend.size(), 4096); chunkSize = std::min(chunkSize, maxChunkPayload); static std::atomic debugInitSendLogCount = 0; Net::Packet& p = AssetsInWork.AssetsPacket; auto flushAssetsPacket = [&]() { if(p.size() == 0) return; AssetsInWork.AssetsPackets.push_back(std::move(p)); }; bool hasFullSended = false; for(auto& [res, sended] : toSend) { if(sended == 0) { // Оповещаем о начале отправки ресурса const size_t initSize = 1 + 1 + 4 + 32 + 4 + 1; if(p.size() + initSize > kMaxAssetPacketSize) flushAssetsPacket(); p << (uint8_t) ToClient::AssetsInitSend << uint32_t(res.size()); p.write((const std::byte*) res.hash().data(), 32); } // Отправляем чанк size_t willSend = std::min(chunkSize, res.size()-sended); const size_t chunkMsgSize = 1 + 1 + 32 + 4 + willSend; if(p.size() + chunkMsgSize > kMaxAssetPacketSize) flushAssetsPacket(); p << (uint8_t) ToClient::AssetsNextSend; 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<0>(toSend[iter]).size() == std::get<1>(toSend[iter])) { toSend.erase(toSend.begin()+iter); } } } } } std::vector> RemoteClient::getViewPoints() { return {{0, CameraPos, 1}}; } }