#include #include "RemoteClient.hpp" #include "Common/Net.hpp" #include "Server/Abstract.hpp" #include #include #include #include #include #include #include "World.hpp" #include "boost/asio/steady_timer.hpp" #include namespace LV::Server { coro<> RemoteClient::asyncDestructor() { shutdown(EnumDisconnect::ByInterface, "~RemoteClient()"); asio::steady_timer deadline(IOC); deadline.expires_after(std::chrono::seconds(1)); Socket.closeRead(); co_await (deadline.async_wait(asio::use_awaitable) || RunCoro.async_wait()); Socket.close(); co_await deadline.async_wait(); } RemoteClient::~RemoteClient() = default; coro<> RemoteClient::run() { 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; NextPacket << (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; LOG.info() << "Игрок '" << Username << "' отключился " << info; } // Может прийти событие на чанк, про который ещё ничего не знаем void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::vector &voxels) { WorldId_c wcId = worldId ? ResRemap.Worlds.toClient(worldId) : 0; assert(wcId != WorldId_c(-1)); // Пока ожидается, что игрок не будет одновременно наблюдать 256 миров // Перебиндить идентификаторы вокселей std::vector NeedVoxels; NeedVoxels.reserve(voxels.size()); for(const VoxelCube &cube : voxels) { NeedVoxels.push_back(cube.VoxelId); } std::unordered_set NeedVoxelsSet(NeedVoxels.begin(), NeedVoxels.end()); // Собираем информацию о конвертации идентификаторов std::unordered_map LocalRemapper; for(DefVoxelId_t vId : NeedVoxelsSet) { LocalRemapper[vId] = ResRemap.DefVoxels.toClient(vId); } // Проверить новые и забытые определения вокселей { std::unordered_set &prevSet = ResUses.ChunkVoxels[{worldId, chunkPos}]; std::unordered_set &nextSet = NeedVoxelsSet; std::vector newVoxels, lostVoxels; for(DefVoxelId_t id : prevSet) { if(!nextSet.contains(id)) { if(--ResUses.DefVoxel[id] == 0) { // Определение больше не используется ResUses.DefVoxel.erase(ResUses.DefVoxel.find(id)); DefVoxelId_c cId = ResRemap.DefVoxels.erase(id); // TODO: отправить пакет потери идентификатора LOG.debug() << "Определение вокселя потеряно: " << id << " -> " << cId; } } } for(DefVoxelId_t id : nextSet) { if(!prevSet.contains(id)) { if(++ResUses.DefVoxel[id] == 1) { // Определение только появилось NextRequest.NewVoxels.push_back(id); DefVoxelId_c cId = ResRemap.DefVoxels.toClient(id); LOG.debug() << "Новое определение вокселя: " << id << " -> " << cId; } } } prevSet = std::move(nextSet); } checkPacketBorder(voxels.size()*(2+6)+16); NextPacket << (uint8_t) ToClient::L1::Content << (uint8_t) ToClient::L2Content::ChunkVoxels << wcId << Pos::GlobalChunk::Key(chunkPos); NextPacket << uint16_t(voxels.size()); // TODO: for(const VoxelCube &cube : voxels) { NextPacket << LocalRemapper[cube.VoxelId] << cube.Left.X << cube.Left.Y << cube.Left.Z << cube.Right.X << cube.Right.Y << cube.Right.Z; } } void RemoteClient::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::unordered_map &nodes) { } void RemoteClient::prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalChunk chunkPos, const LightPrism *lights) { } void RemoteClient::prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkPos) { // Понизим зависимости ресурсов std::unordered_set &prevSet = ResUses.ChunkVoxels[{worldId, chunkPos}]; for(DefVoxelId_t id : prevSet) { if(--ResUses.DefVoxel[id] == 0) { // Определение больше не используется ResUses.DefVoxel.erase(ResUses.DefVoxel.find(id)); DefVoxelId_c cId = ResRemap.DefVoxels.erase(id); // TODO: отправить пакет потери идентификатора LOG.debug() << "Определение вокселя потеряно: " << id << " -> " << cId; } } WorldId_c cwId = worldId ? ResRemap.Worlds.toClient(worldId) : worldId; checkPacketBorder(16); NextPacket << (uint8_t) ToClient::L1::Content << (uint8_t) ToClient::L2Content::RemoveChunk << cwId << Pos::GlobalChunk::Key(chunkPos); } void RemoteClient::prepareWorldUpdate(WorldId_t worldId, World* world) { ResUsesObj::WorldResourceUse &res = ResUses.Worlds[worldId]; auto iter = ResUses.Worlds.find(worldId); if(iter == ResUses.Worlds.end()) { // Новый мир WorldId_c cwId = ResRemap.Worlds.toClient(worldId); ResUsesObj::WorldResourceUse &res = iter->second; res.DefId = world->getDefId(); if(++ResUses.DefWorld[res.DefId] == 1) { // Новое определение NextRequest.NewWorlds.push_back(res.DefId); DefWorldId_c cdId = ResRemap.DefWorlds.toClient(res.DefId); LOG.debug() << "Новое определение мира: " << res.DefId << " -> " << int(cdId); NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::World << cdId; // incrementBinary(Textures, Sounds, Models); } incrementBinary(res.Textures, res.Sounds, res.Models); LOG.debug() << "Новый мир: " << worldId << " -> " << int(cwId); } else { if(res.DefId != world->getDefId()) { DefWorldId_t newDef = world->getDefId(); if(--ResUses.DefWorld[res.DefId] == 0) { // Определение больше не используется ResUses.DefWorld.erase(ResUses.DefWorld.find(res.DefId)); DefWorldId_c cdId = ResRemap.DefWorlds.erase(res.DefId); // TODO: отправить пакет потери идентификатора LOG.debug() << "Определение мира потеряно: " << res.DefId << " -> " << cdId; } if(++ResUses.DefWorld[newDef] == 1) { // Новое определение мира DefWorldId_c cdId = ResRemap.DefWorlds.toClient(newDef); NextRequest.NewWorlds.push_back(newDef); // incrementBinary(Textures, Sounds, Models); // TODO: отправить пакет о новом определении мира LOG.debug() << "Новое определение мира: " << newDef << " -> " << cdId; } res.DefId = newDef; } // TODO: определить различия между переопределением поверх определений std::unordered_set lostTextures, newTextures; std::unordered_set lostSounds, newSounds; std::unordered_set lostModels, newModels; decrementBinary(lostTextures, lostSounds, lostModels); decrementBinary(newTextures, newSounds, newModels); WorldId_c cId = worldId ? ResRemap.Worlds.toClient(worldId) : worldId; // TODO: отправить пакет об изменении мира LOG.debug() << "Изменение мира: " << worldId << " -> " << cId; } } void RemoteClient::prepareWorldRemove(WorldId_t worldId) { // Чанки уже удалены prepareChunkRemove // Понизим зависимости ресурсов ResUsesObj::WorldResourceUse &res = ResUses.Worlds[worldId]; WorldId_c cWorld = worldId ? ResUses.Worlds.erase(worldId) : worldId; LOG.debug() << "Мир потерян: " << worldId << " -> " << cWorld; NextPacket << (uint8_t) ToClient::L1::Content << (uint8_t) ToClient::L2Content::RemoveWorld << cWorld; if(--ResUses.DefWorld[res.DefId] == 0) { // Определение мира потеряно ResUses.DefWorld.erase(ResUses.DefWorld.find(res.DefId)); DefWorldId_c cdWorld = ResRemap.DefWorlds.erase(res.DefId); // TODO: отправить пакет о потере определения мира LOG.debug() << "Определение мира потеряно: " << res.DefId << " -> " << cdWorld; } decrementBinary(res.Textures, res.Sounds, res.Models); } void RemoteClient::prepareEntitySwap(GlobalEntityId_t prev, GlobalEntityId_t next) { ResRemap.Entityes.rebindClientKey(prev, next); LOG.debug() << "Ребинд сущности: " << std::get<0>(prev) << " / " << std::get<1>(prev).X << ":" << std::get<1>(prev).Y << ":" << std::get<1>(prev).Z << " / " << std::get<2>(prev) << " -> " << std::get<0>(next) << " / " << std::get<1>(next).X << ":" << std::get<1>(next).Y << ":" << std::get<1>(next).Z << " / " << std::get<2>(next); } void RemoteClient::prepareEntityUpdate(GlobalEntityId_t entityId, const Entity *entity) { // Может прийти событие на сущность, про которую ещё ничего не знаем // Сопоставим с идентификатором клиента EntityId_c ceId = ResRemap.Entityes.toClient(entityId); auto iter = ResUses.Entity.find(entityId); if(iter == ResUses.Entity.end()) { // Новая сущность // WorldId_c cwId = ResRemap.Worlds.toClient(std::get<0>(entityId)); ResUsesObj::EntityResourceUse &res = ResUses.Entity[entityId]; res.DefId = entity->getDefId(); if(++ResUses.DefEntity[res.DefId] == 1) { // Новое определение NextRequest.NewEntityes.push_back(res.DefId); DefEntityId_c cId = ResRemap.DefEntityes.toClient(res.DefId); LOG.debug() << "Новое определение сущности: " << res.DefId << " -> " << cId; // TODO: Отправить пакет о новом определении // incrementBinary(Textures, Sounds, Models); } incrementBinary(res.Textures, res.Sounds, res.Models); LOG.debug() << "Новая сущность: " << std::get<0>(entityId) << " / " << std::get<1>(entityId).X << ":" << std::get<1>(entityId).Y << ":" << std::get<1>(entityId).Z << " / " << std::get<2>(entityId) << " -> " << ceId; } else { ResUsesObj::EntityResourceUse &res = iter->second; LOG.debug() << "Обновление сущности: " << ceId; } } void RemoteClient::prepareEntityRemove(GlobalEntityId_t entityId) { EntityId_c cId = ResRemap.Entityes.erase(entityId); ResUsesObj::EntityResourceUse &res = ResUses.Entity[entityId]; if(--ResUses.DefEntity[res.DefId] == 0) { LOG.debug() << "Потеряли определение сущности: " << res.DefId << " -> " << cId; ResUses.DefEntity.erase(ResUses.DefEntity.find(res.DefId)); ResRemap.DefEntityes.erase(res.DefId); // decrementBinary(std::unordered_set &textures, std::unordered_set &sounds, std::unordered_set &models) } LOG.debug() << "Сущность потеряна: " << cId; checkPacketBorder(16); NextPacket << (uint8_t) ToClient::L1::Content << (uint8_t) ToClient::L2Content::RemoveEntity << cId; } void RemoteClient::preparePortalUpdate(PortalId_t portalId, void* portal) {} void RemoteClient::preparePortalRemove(PortalId_t portalId) {} void RemoteClient::prepareCameraSetEntity(GlobalEntityId_t entityId) { } ResourceRequest RemoteClient::pushPreparedPackets() { if(NextPacket.size()) SimplePackets.push_back(std::move(NextPacket)); Socket.pushPackets(&SimplePackets); SimplePackets.clear(); NextRequest.uniq(); return std::move(NextRequest); } void RemoteClient::informateDefTexture(const std::unordered_map> &textures) { for(auto pair : textures) { BinTextureId_t sId = pair.first; if(!ResUses.BinTexture.contains(sId)) continue; TextureId_c cId = ResRemap.BinTextures.toClient(sId); checkPacketBorder(0); NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение << (uint8_t) ToClient::L2Resource::Texture << cId; for(auto part : pair.second->Hash) NextPacket << part; NextPacket << (uint8_t) ToClient::L1::Resource // Принудительная отправка << (uint8_t) ToClient::L2Resource::InitResSend << uint8_t(0) << uint8_t(0) << cId << uint32_t(pair.second->Data.size()); for(auto part : pair.second->Hash) NextPacket << part; NextPacket << uint8_t(0) << uint32_t(pair.second->Data.size()); size_t pos = 0; while(pos < pair.second->Data.size()) { checkPacketBorder(0); size_t need = std::min(pair.second->Data.size()-pos, std::min(NextPacket.size(), 64000)); NextPacket.write(pair.second->Data.data()+pos, need); pos += need; } } } void RemoteClient::informateDefSound(const std::unordered_map> &sounds) { for(auto pair : sounds) { BinSoundId_t sId = pair.first; if(!ResUses.BinSound.contains(sId)) continue; SoundId_c cId = ResRemap.BinSounds.toClient(sId); checkPacketBorder(0); NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение << (uint8_t) ToClient::L2Resource::Sound << cId; for(auto part : pair.second->Hash) NextPacket << part; NextPacket << (uint8_t) ToClient::L1::Resource // Принудительная отправка << (uint8_t) ToClient::L2Resource::InitResSend << uint8_t(0) << uint8_t(1) << cId << uint32_t(pair.second->Data.size()); for(auto part : pair.second->Hash) NextPacket << part; NextPacket << uint8_t(0) << uint32_t(pair.second->Data.size()); size_t pos = 0; while(pos < pair.second->Data.size()) { if(NextPacket.size() >= 64000) { SimplePackets.push_back(std::move(NextPacket)); } size_t need = std::min(pair.second->Data.size()-pos, std::min(NextPacket.size(), 64000)); NextPacket.write(pair.second->Data.data()+pos, need); pos += need; } } } void RemoteClient::informateDefModel(const std::unordered_map> &models) { for(auto pair : models) { BinModelId_t sId = pair.first; if(!ResUses.BinModel.contains(sId)) continue; ModelId_c cId = ResRemap.BinModels.toClient(sId); NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение << (uint8_t) ToClient::L2Resource::Model << cId; for(auto part : pair.second->Hash) NextPacket << part; NextPacket << (uint8_t) ToClient::L1::Resource // Принудительная отправка << (uint8_t) ToClient::L2Resource::InitResSend << uint8_t(0) << uint8_t(2) << cId << uint32_t(pair.second->Data.size()); for(auto part : pair.second->Hash) NextPacket << part; NextPacket << uint8_t(0) << uint32_t(pair.second->Data.size()); size_t pos = 0; while(pos < pair.second->Data.size()) { if(NextPacket.size() >= 64000) { SimplePackets.push_back(std::move(NextPacket)); } size_t need = std::min(pair.second->Data.size()-pos, std::min(NextPacket.size(), 64000)); NextPacket.write(pair.second->Data.data()+pos, need); pos += need; } } } void RemoteClient::informateDefWorld(const std::unordered_map &worlds) { for(auto pair : worlds) { DefWorldId_t sId = pair.first; if(!ResUses.DefWorld.contains(sId)) continue; DefWorldId_c cId = ResRemap.DefWorlds.toClient(sId); NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::World << cId; } } void RemoteClient::informateDefVoxel(const std::unordered_map &voxels) { for(auto pair : voxels) { DefVoxelId_t sId = pair.first; if(!ResUses.DefWorld.contains(sId)) continue; DefVoxelId_c cId = ResRemap.DefVoxels.toClient(sId); NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::Voxel << cId; } } void RemoteClient::informateDefNode(const std::unordered_map &nodes) { for(auto pair : nodes) { DefNodeId_t sId = pair.first; if(!ResUses.DefNode.contains(sId)) continue; DefNodeId_c cId = ResRemap.DefNodes.toClient(sId); NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::Node << cId; } } void RemoteClient::informateDefEntityes(const std::unordered_map &entityes) { for(auto pair : entityes) { DefEntityId_t sId = pair.first; if(!ResUses.DefNode.contains(sId)) continue; DefEntityId_c cId = ResRemap.DefEntityes.toClient(sId); NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::Entity << cId; } } void RemoteClient::informateDefPortals(const std::unordered_map &portals) { for(auto pair : portals) { DefPortalId_t sId = pair.first; if(!ResUses.DefNode.contains(sId)) continue; DefPortalId_c cId = ResRemap.DefPortals.toClient(sId); NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::Portal << cId; } } void RemoteClient::checkPacketBorder(uint16_t size) { if(64000-NextPacket.size() < size || (NextPacket.size() != 0 && size == 0)) { SimplePackets.push_back(std::move(NextPacket)); } } 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: { CameraPos.x = co_await sock.read(); CameraPos.y = co_await sock.read(); CameraPos.z = co_await sock.read(); for(int iter = 0; iter < 5; iter++) CameraQuat.Data[iter] = co_await sock.read(); co_return; } default: protocolError(); } } void RemoteClient::incrementBinary(std::unordered_set &textures, std::unordered_set &sounds, std::unordered_set &models) { for(BinTextureId_t id : textures) { if(++ResUses.BinTexture[id] == 1) { TextureId_c cId = ResRemap.BinTextures.toClient(id); NextRequest.NewTextures.push_back(id); LOG.debug() << "Новое определение текстуры: " << id << " -> " << cId; } } for(BinSoundId_t id : sounds) { if(++ResUses.BinSound[id] == 1) { SoundId_c cId = ResRemap.BinSounds.toClient(id); NextRequest.NewSounds.push_back(id); LOG.debug() << "Новое определение звука: " << id << " -> " << cId; } } for(BinModelId_t id : sounds) { if(++ResUses.BinModel[id] == 1) { ModelId_c cId = ResRemap.BinModels.toClient(id); NextRequest.NewModels.push_back(id); LOG.debug() << "Новое определение модели: " << id << " -> " << cId; } } } void RemoteClient::decrementBinary(std::unordered_set &textures, std::unordered_set &sounds, std::unordered_set &models) { for(BinTextureId_t id : textures) { if(--ResUses.BinTexture[id] == 0) { ResUses.BinTexture.erase(ResUses.BinTexture.find(id)); TextureId_c cId = ResRemap.BinTextures.erase(id); LOG.debug() << "Потеряно определение текстуры: " << id << " -> " << cId; NextPacket << (uint8_t) ToClient::L1::Resource << (uint8_t) ToClient::L2Resource::FreeTexture << cId; } } for(BinSoundId_t id : sounds) { if(--ResUses.BinSound[id] == 0) { ResUses.BinSound.erase(ResUses.BinSound.find(id)); SoundId_c cId = ResRemap.BinSounds.erase(id); LOG.debug() << "Потеряно определение звука: " << id << " -> " << cId; NextPacket << (uint8_t) ToClient::L1::Resource << (uint8_t) ToClient::L2Resource::FreeSound << cId; } } for(BinModelId_t id : sounds) { if(--ResUses.BinModel[id] == 0) { ResUses.BinModel.erase(ResUses.BinModel.find(id)); ModelId_c cId = ResRemap.BinModels.erase(id); LOG.debug() << "Потеряно определение модели: " << id << " -> " << cId; NextPacket << (uint8_t) ToClient::L1::Resource << (uint8_t) ToClient::L2Resource::FreeModel << cId; } } } }