#include "ServerSession.hpp" #include "Client/Abstract.hpp" #include "Client/AssetsManager.hpp" #include "Common/Abstract.hpp" #include "Common/Net.hpp" #include "TOSAsync.hpp" #include "TOSLib.hpp" #include "glm/ext/quaternion_geometric.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace LV::Client { 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"; } } const char* toClientPacketName(ToClient type) { switch(type) { case ToClient::Init: return "Init"; case ToClient::Disconnect: return "Disconnect"; case ToClient::AssetsBindDK: return "AssetsBindDK"; case ToClient::AssetsBindHH: return "AssetsBindHH"; case ToClient::AssetsInitSend: return "AssetsInitSend"; case ToClient::AssetsNextSend: return "AssetsNextSend"; case ToClient::DefinitionsUpdate: return "DefinitionsUpdate"; case ToClient::ChunkVoxels: return "ChunkVoxels"; case ToClient::ChunkNodes: return "ChunkNodes"; case ToClient::ChunkLightPrism: return "ChunkLightPrism"; case ToClient::RemoveRegion: return "RemoveRegion"; case ToClient::Tick: return "Tick"; case ToClient::TestLinkCameraToEntity: return "TestLinkCameraToEntity"; case ToClient::TestUnlinkCamera: return "TestUnlinkCamera"; default: return "Unknown"; } } } ServerSession::ServerSession(asio::io_context &ioc, std::unique_ptr&& socket) : IAsyncDestructible(ioc), Socket(std::move(socket)), AM(ioc, "Cache") { assert(Socket.get()); Profiles.DefNode[0] = {0}; Profiles.DefNode[1] = {1}; Profiles.DefNode[2] = {2}; Profiles.DefNode[3] = {3}; Profiles.DefNode[4] = {4}; try { asio::co_spawn(ioc, run(AUC.use()), asio::detached); // TODO: добавить оптимизацию для подключения клиента к внутреннему серверу } catch(const std::exception &exc) { MAKE_ERROR("Ошибка инициализации обработчика объекта подключения к серверу:\n" << exc.what()); } } coro<> ServerSession::asyncDestructor() { co_await IAsyncDestructible::asyncDestructor(); } using namespace TOS; ServerSession::~ServerSession() { } coro<> ServerSession::asyncAuthorizeWithServer(tcp::socket &socket, const std::string username, const std::string token, int a_ar_r, std::function onProgress) { assert(a_ar_r >= 0 && a_ar_r <= 2); std::string progress; auto addLog = [&](const std::string &msg) { progress += '\n'; progress += msg; if(onProgress) onProgress('\n'+msg); }; if(username.size() > 255) { addLog("Имя пользователя слишком велико (>255)"); MAKE_ERROR(progress); } if(token.size() > 255) { addLog("Пароль слишком велик (>255)"); MAKE_ERROR(progress); } Net::Packet packet; packet.write((const std::byte*) "AlterLuanti", 11); packet << uint8_t(0) << uint8_t(a_ar_r) << username << token; addLog("Отправляем первый пакет, авторизация или регистрация"); co_await packet.sendAndFastClear(socket); addLog("Ожидаем код ответа"); uint8_t code = co_await Net::AsyncSocket::read(socket); if(code == 0) { addLog("Код = Авторизированы"); } else if(code == 1) { addLog("Код = Зарегистрированы и авторизированы"); } else if(code == 2 || code == 3) { if(code == 2) addLog("Код = Не удалось зарегистрироваться"); else addLog("Код = Не удалось авторизоваться"); std::string reason = co_await Net::AsyncSocket::read(socket); addLog(reason); if(code == 2) MAKE_ERROR("Не удалось зарегистрироваться, причина: " << reason); else MAKE_ERROR("Не удалось авторизоваться, причина: " << reason); } else { addLog("Получен неизвестный код ответа (может это не игровой сервер?), прерываем"); MAKE_ERROR(progress); } } coro> ServerSession::asyncInitGameProtocol(asio::io_context &ioc, tcp::socket &&socket, std::function onProgress) { std::string progress; auto addLog = [&](const std::string &msg) { progress += '\n'; progress += msg; if(onProgress) onProgress('\n'+msg); }; addLog("Инициализируем игровой протокол"); uint8_t code = 0; co_await Net::AsyncSocket::write<>(socket, code); asio::deadline_timer timer(socket.get_executor()); while(true) { code = co_await Net::AsyncSocket::read(socket); if(code == 0) { addLog("Код = Успешно"); break; } else if(code == 1) { addLog("Код = Ошибка с причиной"); addLog(co_await Net::AsyncSocket::read(socket)); MAKE_ERROR(progress); } else if(code == 2) { addLog("Код = Подождать 4 секунды"); timer.expires_from_now(boost::posix_time::seconds(4)); co_await timer.async_wait(); addLog("Ожидаем новый код"); } else { addLog("Получен неизвестный код ответа (может это не игровой сервер?), прерываем"); MAKE_ERROR(progress); } } co_return std::make_unique(ioc, std::move(socket)); } void ServerSession::shutdown(EnumDisconnect type) { IsGoingShutdown = true; Socket->closeRead(); Net::Packet packet; packet << (uint8_t) ToServer::L1::System << (uint8_t) ToServer::L2System::Disconnect << (uint8_t) type; Socket->pushPacket(std::move(packet)); std::string reason; if(type == EnumDisconnect::ByInterface) reason = "по запросу интерфейса"; else if(type == EnumDisconnect::CriticalError) reason = "на сервере произошла критическая ошибка"; else if(type == EnumDisconnect::ProtocolError) reason = "ошибка протокола (клиент)"; LOG.info() << "Отключение от сервера: " << reason; } void ServerSession::requestModsReload() { if(!Socket || !isConnected()) return; Net::Packet packet; packet << (uint8_t) ToServer::L1::System << (uint8_t) ToServer::L2System::ReloadMods; Socket->pushPacket(std::move(packet)); LOG.info() << "Запрос на перезагрузку модов отправлен"; } void ServerSession::onResize(uint32_t width, uint32_t height) { } void ServerSession::onChangeFocusState(bool isFocused) { if(!isFocused) CursorMode = EnumCursorMoveMode::Default; } void ServerSession::onCursorPosChange(int32_t width, int32_t height) { } void ServerSession::onCursorMove(float xMove, float yMove) { xMove /= 10.f; yMove /= 10.f; glm::vec3 deltaPYR; static constexpr float PI = glm::pi(), PI2 = PI*2, PI_HALF = PI/2, PI_DEG = PI/180; deltaPYR.x = std::clamp(PYR.x - yMove*PI_DEG, -PI_HALF+PI_DEG, PI_HALF-PI_DEG)-PYR.x; deltaPYR.y = std::fmod(PYR.y - xMove*PI_DEG, PI2)-PYR.y; deltaPYR.z = 0; double gTime = GTime; float deltaTime = 1-std::min(gTime-PYR_At, 1/PYR_TIME_DELTA)*PYR_TIME_DELTA; PYR_At = GTime; PYR += deltaPYR; PYR_Offset = deltaPYR+deltaTime*PYR_Offset; } void ServerSession::onCursorBtn(ISurfaceEventListener::EnumCursorBtn btn, bool state) { if(!state) return; if(btn == EnumCursorBtn::Left) { Net::Packet packet; packet << (uint8_t) ToServer::L1::System << (uint8_t) ToServer::L2System::BlockChange << uint8_t(0); Socket->pushPacket(std::move(packet)); } else if(btn == EnumCursorBtn::Right) { Net::Packet packet; packet << (uint8_t) ToServer::L1::System << (uint8_t) ToServer::L2System::BlockChange << uint8_t(1); Socket->pushPacket(std::move(packet)); } } void ServerSession::onKeyboardBtn(int btn, int state) { if(btn == GLFW_KEY_TAB && !state) { CursorMode = CursorMode == EnumCursorMoveMode::Default ? EnumCursorMoveMode::MoveAndHidden : EnumCursorMoveMode::Default; Keys.clear(); } if(CursorMode == EnumCursorMoveMode::MoveAndHidden) { if(btn == GLFW_KEY_W) Keys.W = state; else if(btn == GLFW_KEY_A) Keys.A = state; else if(btn == GLFW_KEY_S) Keys.S = state; else if(btn == GLFW_KEY_D) Keys.D = state; else if(btn == GLFW_KEY_LEFT_SHIFT) Keys.SHIFT = state; else if(btn == GLFW_KEY_SPACE) Keys.SPACE = state; else if(btn == GLFW_KEY_LEFT_CONTROL) Keys.CTRL = state; } } void ServerSession::onJoystick() { } void ServerSession::update(GlobalTime gTime, float dTime) { // Если AssetsManager запрашивает ресурсы с сервера { std::vector needRequest = AM.pullNeededResources(); if(!needRequest.empty()) { Net::Packet pack; std::vector packets; auto check = [&]() { if(pack.size() > 64000) packets.emplace_back(std::move(pack)); }; pack << (uint8_t) ToServer::L1::System << (uint8_t) ToServer::L2System::ResourceRequest; pack << (uint16_t) needRequest.size(); for(const Hash_t& hash : needRequest) { pack.write((const std::byte*) hash.data(), 32); check(); } if(pack.size()) packets.emplace_back(std::move(pack)); Socket->pushPackets(&packets); } } if(!AsyncContext.TickSequence.get_read().empty()) { // Есть такты с сервера // Оповещаем о подготовке к обработке тактов if(RS) RS->prepareTickSync(); std::vector ticks = std::move(*AsyncContext.TickSequence.lock()); IRenderSession::TickSyncData result; // Перевариваем данные по тактам // Профили std::unordered_map profile_Voxel_AddOrChange; std::vector profile_Voxel_Lost; std::unordered_map profile_Node_AddOrChange; std::vector profile_Node_Lost; std::unordered_map profile_World_AddOrChange; std::vector profile_World_Lost; std::unordered_map profile_Portal_AddOrChange; std::vector profile_Portal_Lost; std::unordered_map profile_Entity_AddOrChange; std::vector profile_Entity_Lost; std::unordered_map profile_Item_AddOrChange; std::vector profile_Item_Lost; std::unordered_map entity_AddOrChange; std::vector entity_Lost; { for(TickData& data : ticks) { // Привязки Id -> Domain+Key for(const auto& binds : data.BindsDK) { AM.pushAssetsBindDK(binds.Domains, binds.Keys); } // Привязки Id -> Hash+Header for(auto& binds : data.BindsHH) { AM.pushAssetsBindHH(std::move(binds.HashAndHeaders)); } // Полученные ресурсы с сервера if(!data.ReceivedAssets.empty()) AM.pushNewResources(std::move(data.ReceivedAssets)); { for(auto& [id, profile] : data.Profile_Voxel_AddOrChange) { auto iter = std::lower_bound(profile_Voxel_Lost.begin(), profile_Voxel_Lost.end(), id); if(iter != profile_Voxel_Lost.end() && *iter == id) profile_Voxel_Lost.erase(iter); profile_Voxel_AddOrChange[id] = profile; } for(DefVoxelId id : data.Profile_Voxel_Lost) { profile_Voxel_AddOrChange.erase(id); } profile_Voxel_Lost.insert(profile_Voxel_Lost.end(), data.Profile_Voxel_Lost.begin(), data.Profile_Voxel_Lost.end()); std::sort(profile_Voxel_Lost.begin(), profile_Voxel_Lost.end()); auto eraseIter = std::unique(profile_Voxel_Lost.begin(), profile_Voxel_Lost.end()); profile_Voxel_Lost.erase(eraseIter, profile_Voxel_Lost.end()); } { for(auto& [id, profile] : data.Profile_Node_AddOrChange) { auto iter = std::lower_bound(profile_Node_Lost.begin(), profile_Node_Lost.end(), id); if(iter != profile_Node_Lost.end() && *iter == id) profile_Node_Lost.erase(iter); profile_Node_AddOrChange[id] = profile; } for(DefNodeId id : data.Profile_Node_Lost) { profile_Node_AddOrChange.erase(id); } profile_Node_Lost.insert(profile_Node_Lost.end(), data.Profile_Node_Lost.begin(), data.Profile_Node_Lost.end()); std::sort(profile_Node_Lost.begin(), profile_Node_Lost.end()); auto eraseIter = std::unique(profile_Node_Lost.begin(), profile_Node_Lost.end()); profile_Node_Lost.erase(eraseIter, profile_Node_Lost.end()); } { for(auto& [id, profile] : data.Profile_World_AddOrChange) { auto iter = std::lower_bound(profile_World_Lost.begin(), profile_World_Lost.end(), id); if(iter != profile_World_Lost.end() && *iter == id) profile_World_Lost.erase(iter); profile_World_AddOrChange[id] = profile; } for(DefWorldId id : data.Profile_World_Lost) { profile_World_AddOrChange.erase(id); } profile_World_Lost.insert(profile_World_Lost.end(), data.Profile_World_Lost.begin(), data.Profile_World_Lost.end()); std::sort(profile_World_Lost.begin(), profile_World_Lost.end()); auto eraseIter = std::unique(profile_World_Lost.begin(), profile_World_Lost.end()); profile_World_Lost.erase(eraseIter, profile_World_Lost.end()); } { for(auto& [id, profile] : data.Profile_Portal_AddOrChange) { auto iter = std::lower_bound(profile_Portal_Lost.begin(), profile_Portal_Lost.end(), id); if(iter != profile_Portal_Lost.end() && *iter == id) profile_Portal_Lost.erase(iter); profile_Portal_AddOrChange[id] = profile; } for(DefPortalId id : data.Profile_Portal_Lost) { profile_Portal_AddOrChange.erase(id); } profile_Portal_Lost.insert(profile_Portal_Lost.end(), data.Profile_Portal_Lost.begin(), data.Profile_Portal_Lost.end()); std::sort(profile_Portal_Lost.begin(), profile_Portal_Lost.end()); auto eraseIter = std::unique(profile_Portal_Lost.begin(), profile_Portal_Lost.end()); profile_Portal_Lost.erase(eraseIter, profile_Portal_Lost.end()); } { for(auto& [id, profile] : data.Profile_Entity_AddOrChange) { auto iter = std::lower_bound(profile_Entity_Lost.begin(), profile_Entity_Lost.end(), id); if(iter != profile_Entity_Lost.end() && *iter == id) profile_Entity_Lost.erase(iter); profile_Entity_AddOrChange[id] = profile; } for(DefEntityId id : data.Profile_Entity_Lost) { profile_Entity_AddOrChange.erase(id); } profile_Entity_Lost.insert(profile_Entity_Lost.end(), data.Profile_Entity_Lost.begin(), data.Profile_Entity_Lost.end()); std::sort(profile_Entity_Lost.begin(), profile_Entity_Lost.end()); auto eraseIter = std::unique(profile_Entity_Lost.begin(), profile_Entity_Lost.end()); profile_Entity_Lost.erase(eraseIter, profile_Entity_Lost.end()); } { for(auto& [id, profile] : data.Profile_Item_AddOrChange) { auto iter = std::lower_bound(profile_Item_Lost.begin(), profile_Item_Lost.end(), id); if(iter != profile_Item_Lost.end() && *iter == id) profile_Item_Lost.erase(iter); profile_Item_AddOrChange[id] = profile; } for(DefItemId id : data.Profile_Item_Lost) { profile_Item_AddOrChange.erase(id); } profile_Item_Lost.insert(profile_Item_Lost.end(), data.Profile_Item_Lost.begin(), data.Profile_Item_Lost.end()); std::sort(profile_Item_Lost.begin(), profile_Item_Lost.end()); auto eraseIter = std::unique(profile_Item_Lost.begin(), profile_Item_Lost.end()); profile_Item_Lost.erase(eraseIter, profile_Item_Lost.end()); } { for(auto& [id, info] : data.Entity_AddOrChange) { auto iter = std::lower_bound(entity_Lost.begin(), entity_Lost.end(), id); if(iter != entity_Lost.end() && *iter == id) entity_Lost.erase(iter); entity_AddOrChange[id] = info; } for(EntityId_t id : data.Entity_Lost) { entity_AddOrChange.erase(id); } entity_Lost.insert(entity_Lost.end(), data.Entity_Lost.begin(), data.Entity_Lost.end()); std::sort(entity_Lost.begin(), entity_Lost.end()); auto eraseIter = std::unique(entity_Lost.begin(), entity_Lost.end()); entity_Lost.erase(eraseIter, entity_Lost.end()); } } { AssetsManager::ResourceUpdates updates = AM.pullResourceUpdates(); if(!updates.Models.empty()) { auto& map = Assets[EnumAssets::Model]; for(auto& update : updates.Models) { AssetEntry entry; entry.Id = update.Id; entry.Model = std::move(update.Model); entry.ModelHeader = std::move(update.Header); map[entry.Id] = std::move(entry); result.Assets_ChangeOrAdd[EnumAssets::Model].push_back(update.Id); } } if(!updates.Nodestates.empty()) { auto& map = Assets[EnumAssets::Nodestate]; for(auto& update : updates.Nodestates) { AssetEntry entry; entry.Id = update.Id; entry.Nodestate = std::move(update.Nodestate); entry.NodestateHeader = std::move(update.Header); map[entry.Id] = std::move(entry); result.Assets_ChangeOrAdd[EnumAssets::Nodestate].push_back(update.Id); } } if(!updates.Textures.empty()) { auto& map = Assets[EnumAssets::Texture]; for(auto& update : updates.Textures) { AssetEntry entry; entry.Id = update.Id; entry.Domain = std::move(update.Domain); entry.Key = std::move(update.Key); entry.Width = update.Width; entry.Height = update.Height; entry.Pixels = std::move(update.Pixels); entry.Header = std::move(update.Header); map[entry.Id] = std::move(entry); result.Assets_ChangeOrAdd[EnumAssets::Texture].push_back(update.Id); } } if(!updates.Particles.empty()) { auto& map = Assets[EnumAssets::Particle]; for(auto& update : updates.Particles) { AssetEntry entry; entry.Id = update.Id; entry.Data = std::move(update.Data); map[entry.Id] = std::move(entry); result.Assets_ChangeOrAdd[EnumAssets::Particle].push_back(update.Id); } } if(!updates.Animations.empty()) { auto& map = Assets[EnumAssets::Animation]; for(auto& update : updates.Animations) { AssetEntry entry; entry.Id = update.Id; entry.Data = std::move(update.Data); map[entry.Id] = std::move(entry); result.Assets_ChangeOrAdd[EnumAssets::Animation].push_back(update.Id); } } if(!updates.Sounds.empty()) { auto& map = Assets[EnumAssets::Sound]; for(auto& update : updates.Sounds) { AssetEntry entry; entry.Id = update.Id; entry.Data = std::move(update.Data); map[entry.Id] = std::move(entry); result.Assets_ChangeOrAdd[EnumAssets::Sound].push_back(update.Id); } } if(!updates.Fonts.empty()) { auto& map = Assets[EnumAssets::Font]; for(auto& update : updates.Fonts) { AssetEntry entry; entry.Id = update.Id; entry.Data = std::move(update.Data); map[entry.Id] = std::move(entry); result.Assets_ChangeOrAdd[EnumAssets::Font].push_back(update.Id); } } } for(auto& [id, _] : profile_Voxel_AddOrChange) result.Profiles_ChangeOrAdd[EnumDefContent::Voxel].push_back(id); result.Profiles_Lost[EnumDefContent::Voxel] = profile_Voxel_Lost; for(auto& [id, _] : profile_Node_AddOrChange) result.Profiles_ChangeOrAdd[EnumDefContent::Node].push_back(id); result.Profiles_Lost[EnumDefContent::Node] = profile_Node_Lost; for(auto& [id, _] : profile_World_AddOrChange) result.Profiles_ChangeOrAdd[EnumDefContent::World].push_back(id); result.Profiles_Lost[EnumDefContent::World] = profile_World_Lost; for(auto& [id, _] : profile_Portal_AddOrChange) result.Profiles_ChangeOrAdd[EnumDefContent::Portal].push_back(id); result.Profiles_Lost[EnumDefContent::Portal] = profile_Portal_Lost; for(auto& [id, _] : profile_Entity_AddOrChange) result.Profiles_ChangeOrAdd[EnumDefContent::Entity].push_back(id); result.Profiles_Lost[EnumDefContent::Entity] = profile_Entity_Lost; for(auto& [id, _] : profile_Item_AddOrChange) result.Profiles_ChangeOrAdd[EnumDefContent::Item].push_back(id); result.Profiles_Lost[EnumDefContent::Item] = profile_Item_Lost; } // Чанки std::unordered_map>> chunks_AddOrChange_Voxel_Result; std::unordered_map>> chunks_AddOrChange_Node_Result; std::unordered_map> chunks_Changed; std::unordered_map> regions_Lost_Result; { std::unordered_map> chunks_AddOrChange_Voxel; std::unordered_map> chunks_AddOrChange_Node; std::unordered_map> regions_Lost; for(TickData& data : ticks) { for(auto& [wId, chunks] : data.Chunks_AddOrChange_Voxel) { if(auto iter = regions_Lost.find(wId); iter != regions_Lost.end()) { for(const auto& [pos, value] : chunks) { iter->second.erase(Pos::GlobalRegion(pos >> 2)); } } chunks_AddOrChange_Voxel[wId].merge(chunks); } data.Chunks_AddOrChange_Voxel.clear(); for(auto& [wId, chunks] : data.Chunks_AddOrChange_Node) { if(auto iter = regions_Lost.find(wId); iter != regions_Lost.end()) { for(const auto& [pos, value] : chunks) { iter->second.erase(Pos::GlobalRegion(pos >> 2)); } } chunks_AddOrChange_Node[wId].merge(chunks); } data.Chunks_AddOrChange_Node.clear(); for(auto& [wId, regions] : data.Regions_Lost) { std::sort(regions.begin(), regions.end()); if(auto iter = chunks_AddOrChange_Voxel.find(wId); iter != chunks_AddOrChange_Voxel.end()) { std::vector toDelete; for(auto& [pos, value] : iter->second) { if(std::binary_search(regions.begin(), regions.end(), Pos::GlobalRegion(pos >> 2))) { toDelete.push_back(pos); } } for(Pos::GlobalChunk pos : toDelete) iter->second.erase(iter->second.find(pos)); } if(auto iter = chunks_AddOrChange_Node.find(wId); iter != chunks_AddOrChange_Node.end()) { std::vector toDelete; for(auto& [pos, value] : iter->second) { if(std::binary_search(regions.begin(), regions.end(), Pos::GlobalRegion(pos >> 2))) { toDelete.push_back(pos); } } for(Pos::GlobalChunk pos : toDelete) iter->second.erase(iter->second.find(pos)); } regions_Lost[wId].insert_range(regions); } data.Regions_Lost.clear(); } for(auto& [wId, list] : chunks_AddOrChange_Voxel) { auto& caocvr = chunks_AddOrChange_Voxel_Result[wId]; auto& c = chunks_Changed[wId]; for(auto& [pos, val] : list) { auto& sizes = VisibleChunkCompressed[wId][pos]; VisibleChunkCompressedBytes -= sizes.Voxels; sizes.Voxels = val.size(); VisibleChunkCompressedBytes += sizes.Voxels; caocvr[pos] = unCompressVoxels(val); c.push_back(pos); } } for(auto& [wId, list] : chunks_AddOrChange_Node) { auto& caocvr = chunks_AddOrChange_Node_Result[wId]; auto& c = chunks_Changed[wId]; for(auto& [pos, val] : list) { auto& sizes = VisibleChunkCompressed[wId][pos]; VisibleChunkCompressedBytes -= sizes.Nodes; sizes.Nodes = val.size(); VisibleChunkCompressedBytes += sizes.Nodes; auto& chunkNodes = caocvr[pos]; unCompressNodes(val, chunkNodes.data()); c.push_back(pos); } } regions_Lost_Result = std::move(regions_Lost); for(auto& [wId, list] : chunks_Changed) { std::sort(list.begin(), list.end()); auto eraseIter = std::unique(list.begin(), list.end()); list.erase(eraseIter, list.end()); } } result.Chunks_ChangeOrAdd = std::move(chunks_Changed); for(auto& [wId, regions] : regions_Lost_Result) result.Chunks_Lost[wId] = std::vector(regions.begin(), regions.end()); { for(TickData& data : ticks) { } } if(RS) RS->pushStageTickSync(); // Применяем изменения по ресурсам, профилям и контенту // Определения { for(auto& [resId, def] : profile_Node_AddOrChange) { Profiles.DefNode[resId] = def; } for(auto& [resId, def] : profile_Entity_AddOrChange) { Profiles.DefEntity[resId] = def; } } // Чанки { for(auto& [wId, lost] : regions_Lost_Result) { auto iterWorld = Content.Worlds.find(wId); auto iterSizesWorld = VisibleChunkCompressed.find(wId); if(iterWorld != Content.Worlds.end()) { for(const Pos::GlobalRegion& rPos : lost) { auto iterRegion = iterWorld->second.Regions.find(rPos); if(iterRegion != iterWorld->second.Regions.end()) iterWorld->second.Regions.erase(iterRegion); } } if(iterSizesWorld == VisibleChunkCompressed.end()) continue; for(const Pos::GlobalRegion& rPos : lost) { for(auto iter = iterSizesWorld->second.begin(); iter != iterSizesWorld->second.end(); ) { if(Pos::GlobalRegion(iter->first >> 2) == rPos) { VisibleChunkCompressedBytes -= iter->second.Voxels; VisibleChunkCompressedBytes -= iter->second.Nodes; iter = iterSizesWorld->second.erase(iter); } else { ++iter; } } } if(iterSizesWorld->second.empty()) VisibleChunkCompressed.erase(iterSizesWorld); } for(auto& [wId, voxels] : chunks_AddOrChange_Voxel_Result) { auto& regions = Content.Worlds[wId].Regions; for(auto& [pos, data] : voxels) { regions[pos >> 2].Chunks[Pos::bvec4u(pos & 0x3).pack()].Voxels = std::move(data); } } for(auto& [wId, nodes] : chunks_AddOrChange_Node_Result) { auto& regions = Content.Worlds[wId].Regions; for(auto& [pos, data] : nodes) { regions[pos >> 2].Chunks[Pos::bvec4u(pos & 0x3).pack()].Nodes = std::move(data); } } } // Сущности { for(auto& [entityId, info] : entity_AddOrChange) { auto iter = Content.Entityes.find(entityId); if(iter != Content.Entityes.end() && iter->second.WorldId != info.WorldId) { auto iterWorld = Content.Worlds.find(iter->second.WorldId); if(iterWorld != Content.Worlds.end()) { auto &list = iterWorld->second.Entitys; list.erase(std::remove(list.begin(), list.end(), entityId), list.end()); } } Content.Entityes[entityId] = info; auto &list = Content.Worlds[info.WorldId].Entitys; if(std::find(list.begin(), list.end(), entityId) == list.end()) list.push_back(entityId); } for(EntityId_t entityId : entity_Lost) { auto iter = Content.Entityes.find(entityId); if(iter != Content.Entityes.end()) { auto iterWorld = Content.Worlds.find(iter->second.WorldId); if(iterWorld != Content.Worlds.end()) { auto &list = iterWorld->second.Entitys; list.erase(std::remove(list.begin(), list.end(), entityId), list.end()); } Content.Entityes.erase(iter); } else { for(auto& [wId, worldInfo] : Content.Worlds) { auto &list = worldInfo.Entitys; list.erase(std::remove(list.begin(), list.end(), entityId), list.end()); } } } } if(RS) RS->tickSync(result); } AM.tick(); // Здесь нужно обработать управляющие пакеты // Оповещение модуля рендера об изменениях ресурсов // std::unordered_map> changedResources; // std::unordered_map> lostResources; // Обработка полученных ресурсов // Получаем ресурсы, загруженные с дискового кеша GTime = gTime; Pos += glm::vec3(Speed) * dTime; Speed -= glm::dvec3(Speed) * double(dTime); glm::mat4 rot(1); float deltaTime = 1-std::min(gTime-PYR_At, 1/PYR_TIME_DELTA)*PYR_TIME_DELTA; rot = glm::rotate(rot, PYR.y-deltaTime*PYR_Offset.y, {0, 1, 0}); float mltpl = 16*dTime*Pos::Object_t::BS; if(Keys.CTRL) mltpl *= 16; Speed += glm::vec3(rot*glm::vec4(0, 0, -1, 1)*float(Keys.W))*mltpl; Speed += glm::vec3(rot*glm::vec4(-1, 0, 0, 1)*float(Keys.A))*mltpl; Speed += glm::vec3(rot*glm::vec4(0, 0, 1, 1)*float(Keys.S))*mltpl; Speed += glm::vec3(rot*glm::vec4(1, 0, 0, 1)*float(Keys.D))*mltpl; Speed += glm::vec3(0, -1, 0)*float(Keys.SHIFT)*mltpl; Speed += glm::vec3(0, 1, 0)*float(Keys.SPACE)*mltpl; // { // // Пакеты // ParsedPacket *pack; // while(NetInputPackets.pop(pack)) { // delete pack; // } // } // Расчёт камеры { float deltaTime = 1-std::min(gTime-PYR_At, 1/PYR_TIME_DELTA)*PYR_TIME_DELTA; glm::quat quat = glm::angleAxis(PYR.x-deltaTime*PYR_Offset.x, glm::vec3(1.f, 0.f, 0.f)) * glm::angleAxis(PYR.y-deltaTime*PYR_Offset.y, glm::vec3(0.f, -1.f, 0.f)); quat = glm::normalize(quat); if(RS) RS->setCameraPos(0, Pos, quat); // Отправка текущей позиции камеры if(gTime-LastSendPYR_POS > 1/20.f) { LastSendPYR_POS = gTime; Net::Packet packet; ToServer::PacketQuat q; q.fromQuat(glm::inverse(quat)); packet << (uint8_t) ToServer::L1::System << (uint8_t) ToServer::L2System::Test_CAM_PYR_POS << Pos.x << Pos.y << Pos.z; for(int iter = 0; iter < 5; iter++) packet << q.Data[iter]; Socket->pushPacket(std::move(packet)); } } } void ServerSession::setRenderSession(IRenderSession* session) { RS = session; } void ServerSession::resetResourceSyncState() { AsyncContext.AssetsLoading.clear(); AsyncContext.ThisTickEntry = {}; AsyncContext.TickSequence.lock()->clear(); } coro<> ServerSession::run(AsyncUseControl::Lock) { try { while(!IsGoingShutdown && IsConnected) { co_await readPacket(*Socket); } } catch(const std::exception &exc) { LOG.error() << "Ошибка обработки сокета:\n" << exc.what(); } IsConnected = false; resetResourceSyncState(); co_return; } void ServerSession::protocolError() { shutdown(EnumDisconnect::ProtocolError); } coro<> ServerSession::readPacket(Net::AsyncSocket &sock) { uint8_t first = co_await sock.read(); if(DebugLogPackets) { ToClient type = static_cast(first); LOG.debug() << "Recv packet=" << toClientPacketName(type) << " id=" << int(first); } switch((ToClient) first) { case ToClient::Init: co_return; case ToClient::Disconnect: co_await rP_Disconnect(sock); co_return; case ToClient::AssetsBindDK: co_await rP_AssetsBindDK(sock); co_return; case ToClient::AssetsBindHH: co_await rP_AssetsBindHH(sock); co_return; case ToClient::AssetsInitSend: co_await rP_AssetsInitSend(sock); co_return; case ToClient::AssetsNextSend: co_await rP_AssetsNextSend(sock); co_return; case ToClient::DefinitionsUpdate: co_await rP_DefinitionsUpdate(sock); co_return; case ToClient::ChunkVoxels: co_await rP_ChunkVoxels(sock); co_return; case ToClient::ChunkNodes: co_await rP_ChunkNodes(sock); co_return; case ToClient::ChunkLightPrism: co_await rP_ChunkLightPrism(sock); co_return; case ToClient::RemoveRegion: co_await rP_RemoveRegion(sock); co_return; case ToClient::Tick: co_await rP_Tick(sock); co_return; case ToClient::TestLinkCameraToEntity: co_await rP_TestLinkCameraToEntity(sock); co_return; case ToClient::TestUnlinkCamera: co_await rP_TestUnlinkCamera(sock); co_return; default: protocolError(); } } coro<> ServerSession::rP_Disconnect(Net::AsyncSocket &sock) { EnumDisconnect type = (EnumDisconnect) co_await sock.read(); std::string reason = co_await sock.read(); if(type == EnumDisconnect::ByInterface) reason = "по запросу интерфейса " + reason; else if(type == EnumDisconnect::CriticalError) reason = "на сервере произошла критическая ошибка " + reason; else if(type == EnumDisconnect::ProtocolError) reason = "ошибка протокола (сервер) " + reason; LOG.info() << "Отключение от сервера: " << reason; co_return; } coro<> ServerSession::rP_AssetsBindDK(Net::AsyncSocket &sock) { UpdateAssetsBindsDK update; std::string compressed = co_await sock.read(); std::u8string in((const char8_t*) compressed.data(), compressed.size()); std::u8string data = unCompressLinear(in); Net::LinearReader lr(data); uint16_t domainsCount = lr.read(); update.Domains.reserve(domainsCount); for(uint16_t i = 0; i < domainsCount; ++i) update.Domains.push_back(lr.read()); for(size_t type = 0; type < static_cast(EnumAssets::MAX_ENUM); ++type) { update.Keys[type].resize(update.Domains.size()); uint32_t count = lr.read(); for(uint32_t i = 0; i < count; ++i) { uint16_t domainId = lr.read(); std::string key = lr.read(); if(domainId >= update.Domains.size()) continue; update.Keys.at(type).at(domainId).push_back(key); } } AsyncContext.ThisTickEntry.BindsDK.emplace_back(std::move(update)); } coro<> ServerSession::rP_AssetsBindHH(Net::AsyncSocket &sock) { UpdateAssetsBindsHH update; for(size_t typeIndex = 0; typeIndex < static_cast(EnumAssets::MAX_ENUM); ++typeIndex) { uint32_t count = co_await sock.read(); if(count == 0) continue; for(size_t iter = 0; iter < count; ++iter) { uint32_t id = co_await sock.read(); ResourceFile::Hash_t hash; co_await sock.read((std::byte*) hash.data(), hash.size()); std::string headerStr = co_await sock.read(); update.HashAndHeaders[typeIndex].emplace_back(id, hash, std::u8string((const char8_t*) headerStr.data(), headerStr.size())); } } AsyncContext.ThisTickEntry.BindsHH.emplace_back(std::move(update)); } coro<> ServerSession::rP_AssetsInitSend(Net::AsyncSocket &sock) { uint32_t size = co_await sock.read(); Hash_t hash; co_await sock.read((std::byte*) hash.data(), hash.size()); AsyncContext.AssetsLoading[hash] = AssetLoading{ .Data = std::u8string(size, '\0'), .Offset = 0 }; } coro<> ServerSession::rP_AssetsNextSend(Net::AsyncSocket &sock) { Hash_t hash; co_await sock.read((std::byte*) hash.data(), hash.size()); uint32_t size = co_await sock.read(); if(!AsyncContext.AssetsLoading.contains(hash)) { std::vector discard(size); co_await sock.read(discard.data(), size); co_return; } AssetLoading& al = AsyncContext.AssetsLoading.at(hash); if(al.Data.size() - al.Offset < size) MAKE_ERROR("Несоответствие ожидаемого размера ресурса"); co_await sock.read((std::byte*) al.Data.data() + al.Offset, size); al.Offset += size; if(al.Offset != al.Data.size()) co_return; AsyncContext.ThisTickEntry.ReceivedAssets.emplace_back(hash, std::move(al.Data)); AsyncContext.AssetsLoading.erase(AsyncContext.AssetsLoading.find(hash)); } coro<> ServerSession::rP_DefinitionsUpdate(Net::AsyncSocket &sock) { static std::atomic debugDefLogCount = 0; uint32_t typeCount = co_await sock.read(); typeCount = std::min(typeCount, static_cast(EnumDefContent::MAX_ENUM)); for(uint32_t type = 0; type < typeCount; ++type) { uint32_t count = co_await sock.read(); for(uint32_t i = 0; i < count; ++i) { ResourceId id = co_await sock.read(); std::string dataStr = co_await sock.read(); (void)dataStr; if(type == static_cast(EnumDefContent::Node)) { DefNode_t def; def.NodestateId = 0; def.TexId = id; AsyncContext.ThisTickEntry.Profile_Node_AddOrChange.emplace_back(id, def); if(id < 32) { uint32_t idx = debugDefLogCount.fetch_add(1); if(idx < 64) { LOG.debug() << "DefNode id=" << id << " nodestate=" << def.NodestateId << " tex=" << def.TexId; } } } } } uint32_t lostCount = co_await sock.read(); lostCount = std::min(lostCount, static_cast(EnumDefContent::MAX_ENUM)); for(uint32_t type = 0; type < lostCount; ++type) { uint32_t count = co_await sock.read(); for(uint32_t i = 0; i < count; ++i) { ResourceId id = co_await sock.read(); if(type == static_cast(EnumDefContent::Node)) AsyncContext.ThisTickEntry.Profile_Node_Lost.push_back(id); } } uint32_t dkCount = co_await sock.read(); dkCount = std::min(dkCount, static_cast(EnumDefContent::MAX_ENUM)); for(uint32_t type = 0; type < dkCount; ++type) { uint32_t count = co_await sock.read(); for(uint32_t i = 0; i < count; ++i) { std::string key = co_await sock.read(); std::string domain = co_await sock.read(); (void)key; (void)domain; } } co_return; } coro<> ServerSession::rP_ChunkVoxels(Net::AsyncSocket &sock) { WorldId_t wcId = co_await sock.read(); Pos::GlobalChunk pos; pos.unpack(co_await sock.read()); uint32_t compressedSize = co_await sock.read(); assert(compressedSize <= std::pow(2, 24)); std::u8string compressed(compressedSize, '\0'); co_await sock.read((std::byte*) compressed.data(), compressedSize); AsyncContext.ThisTickEntry.Chunks_AddOrChange_Voxel[wcId].insert({pos, std::move(compressed)}); co_return; } coro<> ServerSession::rP_ChunkNodes(Net::AsyncSocket &sock) { WorldId_t wcId = co_await sock.read(); Pos::GlobalChunk pos; pos.unpack(co_await sock.read()); uint32_t compressedSize = co_await sock.read(); assert(compressedSize <= std::pow(2, 24)); std::u8string compressed(compressedSize, '\0'); co_await sock.read((std::byte*) compressed.data(), compressedSize); AsyncContext.ThisTickEntry.Chunks_AddOrChange_Node[wcId].insert({pos, std::move(compressed)}); co_return; } coro<> ServerSession::rP_ChunkLightPrism(Net::AsyncSocket &sock) { (void)sock; co_return; } coro<> ServerSession::rP_RemoveRegion(Net::AsyncSocket &sock) { WorldId_t wcId = co_await sock.read(); Pos::GlobalRegion pos; pos.unpack(co_await sock.read()); AsyncContext.ThisTickEntry.Regions_Lost[wcId].push_back(pos); co_return; } coro<> ServerSession::rP_Tick(Net::AsyncSocket &sock) { (void)sock; AsyncContext.TickSequence.lock()->push_back(std::move(AsyncContext.ThisTickEntry)); AsyncContext.ThisTickEntry = {}; co_return; } coro<> ServerSession::rP_TestLinkCameraToEntity(Net::AsyncSocket &sock) { (void) sock; co_return; } coro<> ServerSession::rP_TestUnlinkCamera(Net::AsyncSocket &sock) { (void) sock; co_return; } }