#include #include "RemoteClient.hpp" #include "Common/Abstract.hpp" #include "Common/Net.hpp" #include "Server/Abstract.hpp" #include #include #include #include #include #include #include "World.hpp" #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; 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) { /* Обновить зависимости Запросить недостающие Отправить всё клиенту */ std::vector newTypes, /* Новые типы вокселей */ lostTypes /* Потерянные типы вокселей */; // Обновить зависимости вокселей std::vector v; if(voxels) { v.reserve(voxels->size()); for(const VoxelCube& value : *voxels) { v.push_back(value.VoxelId); } std::sort(v.begin(), v.end()); auto last = std::unique(v.begin(), v.end()); v.erase(last, v.end()); // В v отсортированный список уникальных вокселей в чанке // Отметим использование этих вокселей for(const DefVoxelId_t& id : v) { 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); assert(iterWorld != ResUses.RefChunk.end()); Pos::bvec4u lChunk = (chunkPos & 0xf); // Исключим зависимости предыдущей версии чанка { auto iterRegion = iterWorld->second.find(chunkPos); if(iterRegion != iterWorld->second.end()) { // Уменьшим счётчик зависимостей for(const DefVoxelId_t& id : iterRegion->second[lChunk.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); } } } } iterWorld->second[chunkPos][lChunk.pack()].Voxel = v; if(!newTypes.empty()) { // Добавляем новые типы в запрос NextRequest.Voxel.insert(NextRequest.Voxel.end(), newTypes.begin(), newTypes.end()); } if(!lostTypes.empty()) { for(const DefVoxelId_t& id : lostTypes) { auto iter = ResUses.RefDefVoxel.find(id); assert(iter != ResUses.RefDefVoxel.end()); // Должны быть описаны зависимости вокселя decrementBinary(std::move(iter->second.Texture), {}, std::move(iter->second.Sound), {}, {}); ResUses.RefDefVoxel.erase(iter); } } // TODO: отправить чанк } void RemoteClient::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const Node* nodes) { std::vector newTypes, /* Новые типы нод */ lostTypes /* Потерянные типы нод */; // Обновить зависимости нод std::vector n; { n.reserve(16*16*16); for(size_t iter = 0; iter < 16*16*16; iter++) { n.push_back(nodes[iter].NodeId); } std::sort(n.begin(), n.end()); auto last = std::unique(n.begin(), n.end()); n.erase(last, n.end()); // В n отсортированный список уникальных нод в чанке // Отметим использование этих нод for(const DefNodeId_t& id : n) { auto iter = ResUses.DefNode.find(id); if(iter == ResUses.DefNode.end()) { // Новый тип newTypes.push_back(id); ResUses.DefNode[id] = 1; } else { // Увеличиваем счётчик iter->second++; LOG.debug() << "id = " << id << ' ' << iter->second; } } } auto iterWorld = ResUses.RefChunk.find(worldId); assert(iterWorld != ResUses.RefChunk.end()); Pos::bvec4u lChunk = (chunkPos & 0xf); // Исключим зависимости предыдущей версии чанка { auto iterRegion = iterWorld->second.find(chunkPos); if(iterRegion != iterWorld->second.end()) { // Уменьшим счётчик зависимостей for(const DefNodeId_t& id : iterRegion->second[lChunk.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); } } } } iterWorld->second[chunkPos][lChunk.pack()].Node = n; if(!newTypes.empty()) { // Добавляем новые типы в запрос NextRequest.Node.insert(NextRequest.Node.end(), newTypes.begin(), newTypes.end()); } if(!lostTypes.empty()) { for(const DefNodeId_t& id : lostTypes) { auto iter = ResUses.RefDefNode.find(id); assert(iter != ResUses.RefDefNode.end()); // Должны быть описаны зависимости ноды decrementBinary({}, {}, std::move(iter->second.Sound), std::move(iter->second.Model), {}); ResUses.RefDefNode.erase(iter); } } // TODO: отправить чанк LOG.debug() << "Увидели " << chunkPos.x << ' ' << chunkPos.y << ' ' << chunkPos.z; } void RemoteClient::prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regionPos) { std::vector lostTypesV /* Потерянные типы вокселей */; std::vector lostTypesN /* Потерянные типы нод */; // Уменьшаем зависимости вокселей и нод { auto iterWorld = ResUses.RefChunk.find(worldId); assert(iterWorld != ResUses.RefChunk.end()); auto iterRegion = iterWorld->second.find(regionPos); assert(iterRegion != iterWorld->second.end()); for(const auto &iterChunk : iterRegion->second) { for(const DefVoxelId_t& 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_t& 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); } } } } if(!lostTypesV.empty()) { for(const DefVoxelId_t& id : lostTypesV) { auto iter = ResUses.RefDefVoxel.find(id); assert(iter != ResUses.RefDefVoxel.end()); // Должны быть описаны зависимости вокселя decrementBinary(std::move(iter->second.Texture), {}, std::move(iter->second.Sound), {}, {}); ResUses.RefDefVoxel.erase(iter); } } if(!lostTypesN.empty()) { for(const DefNodeId_t& id : lostTypesN) { auto iter = ResUses.RefDefNode.find(id); assert(iter != ResUses.RefDefNode.end()); // Должны быть описаны зависимости ноды decrementBinary({}, {}, std::move(iter->second.Sound), std::move(iter->second.Model), {}); ResUses.RefDefNode.erase(iter); } } checkPacketBorder(16); NextPacket << (uint8_t) ToClient::L1::Content << (uint8_t) ToClient::L2Content::RemoveRegion << worldId << regionPos.pack(); } void RemoteClient::prepareEntityUpdate(ServerEntityId_t entityId, const Entity *entity) { // Сопоставим с идентификатором клиента ClientEntityId_t ceId = ResRemap.Entityes.toClient(entityId); // Профиль новый { DefEntityId_t 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); decrementBinary(std::move(iterProfileRef->second.Texture), std::move(iterProfileRef->second.Animation), {}, std::move(iterProfileRef->second.Model), {}); ResUses.DefEntity.erase(iterProfile); } // Убавляем зависимость к модификационным данным // iterEntity->second. // decrementBinary({}, {}, {}, {}, {}); } } // TODO: отправить клиенту } void RemoteClient::prepareEntitySwap(ServerEntityId_t prev, ServerEntityId_t next) { ResRemap.Entityes.rebindClientKey(prev, next); } void RemoteClient::prepareEntityRemove(ServerEntityId_t entityId) { ClientEntityId_t cId = ResRemap.Entityes.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); decrementBinary(std::move(iterProfileRef->second.Texture), std::move(iterProfileRef->second.Animation), {}, std::move(iterProfileRef->second.Model), {}); ResUses.RefDefEntity.erase(iterProfileRef); ResUses.DefEntity.erase(iterProfile); } } checkPacketBorder(16); NextPacket << (uint8_t) ToClient::L1::Content << (uint8_t) ToClient::L2Content::RemoveEntity << cId; } void RemoteClient::prepareWorldUpdate(WorldId_t worldId, World* world) { // Добавление зависимостей ResUses.RefChunk[worldId]; // Профиль { DefWorldId_t 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()); // Зависимости предыдущего профиля также должны быть decrementBinary(std::move(iterWorldProfRef->second.Texture), {}, {}, std::move(iterWorldProfRef->second.Model), {}); ResUses.RefDefWorld.erase(iterWorldProfRef); } } } // Указываем модификационные зависимости текущей версии мира ResUses.RefWorld[worldId] = {world->getDefId()}; // TODO: отправить мир } void RemoteClient::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()); // Зависимости профиля должны быть decrementBinary(std::move(iterWorldProfDef->second.Texture), {}, {}, std::move(iterWorldProfDef->second.Model), {}); ResUses.RefDefWorld.erase(iterWorldProfDef); } ResUses.RefWorld.erase(iterWorld); auto iter = ResUses.RefChunk.find(worldId); assert(iter->second.empty()); ResUses.RefChunk.erase(iter); } void RemoteClient::preparePortalUpdate(PortalId_t portalId, void* portal) {} void RemoteClient::preparePortalRemove(PortalId_t portalId) {} void RemoteClient::prepareCameraSetEntity(ServerEntityId_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::informateBin(ToClient::L2Resource type, ResourceId_t id, const std::shared_ptr& data) { checkPacketBorder(0); NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение << (uint8_t) type << id; for(auto part : data->Hash) NextPacket << part; NextPacket << (uint8_t) ToClient::L1::Resource // Принудительная полная отправка << (uint8_t) ToClient::L2Resource::InitResSend << uint8_t(0) << uint8_t(0) << id << uint32_t(data->Data.size()); for(auto part : data->Hash) NextPacket << part; NextPacket << uint8_t(0) << uint32_t(data->Data.size()); size_t pos = 0; while(pos < data->Data.size()) { checkPacketBorder(0); size_t need = std::min(data->Data.size()-pos, std::min(NextPacket.size(), 64000)); NextPacket.write((const std::byte*) data->Data.data()+pos, need); pos += need; } } void RemoteClient::informateBinTexture(const std::unordered_map> &textures) { for(auto pair : textures) { BinTextureId_t id = pair.first; if(!ResUses.BinTexture.contains(id)) continue; // Клиент не наблюдает за этим объектом informateBin(ToClient::L2Resource::Texture, id, pair.second); } } void RemoteClient::informateBinAnimation(const std::unordered_map> &textures) { for(auto pair : textures) { BinTextureId_t id = pair.first; if(!ResUses.BinTexture.contains(id)) continue; // Клиент не наблюдает за этим объектом informateBin(ToClient::L2Resource::Animation, id, pair.second); } } void RemoteClient::informateBinModel(const std::unordered_map> &textures) { for(auto pair : textures) { BinTextureId_t id = pair.first; if(!ResUses.BinTexture.contains(id)) continue; // Клиент не наблюдает за этим объектом informateBin(ToClient::L2Resource::Model, id, pair.second); } } void RemoteClient::informateBinSound(const std::unordered_map> &textures) { for(auto pair : textures) { BinTextureId_t id = pair.first; if(!ResUses.BinTexture.contains(id)) continue; // Клиент не наблюдает за этим объектом informateBin(ToClient::L2Resource::Sound, id, pair.second); } } void RemoteClient::informateBinFont(const std::unordered_map> &textures) { for(auto pair : textures) { BinTextureId_t id = pair.first; if(!ResUses.BinTexture.contains(id)) continue; // Клиент не наблюдает за этим объектом informateBin(ToClient::L2Resource::Font, id, pair.second); } } void RemoteClient::informateDefVoxel(const std::unordered_map &voxels) { for(auto pair : voxels) { DefVoxelId_t id = pair.first; if(!ResUses.DefVoxel.contains(id)) continue; NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::Voxel << id; } } void RemoteClient::informateDefNode(const std::unordered_map &nodes) { for(auto pair : nodes) { DefNodeId_t id = pair.first; if(!ResUses.DefNode.contains(id)) continue; NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::Node << id; } } void RemoteClient::informateDefWorld(const std::unordered_map &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::informateDefPortal(const std::unordered_map &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::informateDefEntity(const std::unordered_map &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::informateDefItem(const std::unordered_map &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::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(const std::vector& textures, const std::vector& animation, const std::vector& sounds, const std::vector& models, const std::vector& fonts ) { for(BinTextureId_t id : textures) { if(++ResUses.BinTexture[id] == 1) { NextRequest.BinTexture.push_back(id); LOG.debug() << "Новое определение текстуры: " << id; } } for(BinAnimationId_t id : animation) { if(++ResUses.BinAnimation[id] == 1) { NextRequest.BinAnimation.push_back(id); LOG.debug() << "Новое определение анимации: " << id; } } for(BinSoundId_t id : sounds) { if(++ResUses.BinSound[id] == 1) { NextRequest.BinSound.push_back(id); LOG.debug() << "Новое определение звука: " << id; } } for(BinModelId_t id : models) { if(++ResUses.BinModel[id] == 1) { NextRequest.BinModel.push_back(id); LOG.debug() << "Новое определение модели: " << id; } } for(BinFontId_t id : fonts) { if(++ResUses.BinFont[id] == 1) { NextRequest.BinFont.push_back(id); LOG.debug() << "Новое определение шрифта: " << id; } } } void RemoteClient::decrementBinary(std::vector&& textures, std::vector&& animation, std::vector&& sounds, std::vector&& models, std::vector&& fonts ) { for(BinTextureId_t id : textures) { if(--ResUses.BinTexture[id] == 0) { ResUses.BinTexture.erase(ResUses.BinTexture.find(id)); LOG.debug() << "Потеряно определение текстуры: " << id; NextPacket << (uint8_t) ToClient::L1::Resource << (uint8_t) ToClient::L2Resource::FreeTexture << id; } } for(BinAnimationId_t id : animation) { if(--ResUses.BinAnimation[id] == 0) { ResUses.BinAnimation.erase(ResUses.BinAnimation.find(id)); LOG.debug() << "Потеряно определение анимации: " << id; NextPacket << (uint8_t) ToClient::L1::Resource << (uint8_t) ToClient::L2Resource::FreeAnimation << id; } } for(BinSoundId_t id : sounds) { if(--ResUses.BinSound[id] == 0) { ResUses.BinSound.erase(ResUses.BinSound.find(id)); LOG.debug() << "Потеряно определение звука: " << id; NextPacket << (uint8_t) ToClient::L1::Resource << (uint8_t) ToClient::L2Resource::FreeSound << id; } } for(BinModelId_t id : models) { if(--ResUses.BinModel[id] == 0) { ResUses.BinModel.erase(ResUses.BinModel.find(id)); LOG.debug() << "Потеряно определение модели: " << id; NextPacket << (uint8_t) ToClient::L1::Resource << (uint8_t) ToClient::L2Resource::FreeModel << id; } } for(BinFontId_t id : fonts) { if(--ResUses.BinFont[id] == 0) { ResUses.BinFont.erase(ResUses.BinFont.find(id)); LOG.debug() << "Потеряно определение шрифта: " << id; NextPacket << (uint8_t) ToClient::L1::Resource << (uint8_t) ToClient::L2Resource::FreeFont << id; } } } }