#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 namespace LV::Client { ServerSession::ServerSession(asio::io_context &ioc, std::unique_ptr&& socket) : IAsyncDestructible(ioc), Socket(std::move(socket)) //, NetInputPackets(1024) { assert(Socket.get()); Profiles.DefNode[0] = {0}; Profiles.DefNode[1] = {1}; Profiles.DefNode[2] = {2}; Profiles.DefNode[3] = {3}; Profiles.DefNode[4] = {4}; try { AM = AssetsManager::Create(ioc, "Cache"); 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::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) { // Если были получены ресурсы, отправим их на запись в кеш if(!AsyncContext.LoadedAssets.get_read().empty()) { std::vector assets = std::move(*AsyncContext.LoadedAssets.lock()); std::vector resources; resources.reserve(assets.size()); for(AssetEntry& entry : assets) { resources.push_back(entry.Res); AsyncContext.LoadedResources.emplace_back(std::move(entry)); // // Проверяем используется ли сейчас ресурс // auto iter = Assets.ExistBinds[(int) entry.Type].find(entry.Id); // if(iter == Assets.ExistBinds[(int) entry.Type].end()) { // // Не используется // Assets.NotInUse[(int) entry.Type][entry.Domain + ':' + entry.Key] = {entry, TIME_BEFORE_UNLOAD_RESOURCE+time(nullptr)}; // } else { // // Используется // Assets.InUse[(int) entry.Type][entry.Id] = entry; // changedResources[entry.Type].insert({entry.Id, entry}); // } } AM->pushResources(std::move(resources)); } // Получить ресурсы с AssetsManager { std::vector>> resources = AM->pullReads(); std::vector needRequest; for(auto& [key, res] : resources) { if(!res) { // Проверить не был ли уже отправлен запрос на получение этого хеша auto iter = std::lower_bound(AsyncContext.AlreadyLoading.begin(), AsyncContext.AlreadyLoading.end(), res->hash()); if(iter == AsyncContext.AlreadyLoading.end()) { AsyncContext.AlreadyLoading.insert(iter, res->hash()); needRequest.push_back(res->hash()); } } else { AssetEntry entry { .Type = key.Type, .Id = key.Id, .Domain = key.Domain, .Key = key.Key, .Res = *res }; AsyncContext.LoadedResources.emplace_back(std::move(entry)); } } if(!needRequest.empty()) { assert(needRequest.size() < (1 << 16)); Net::Packet p; p << (uint8_t) ToServer::L1::System << (uint8_t) ToServer::L2System::ResourceRequest; p << (uint16_t) needRequest.size(); for(const Hash_t& hash : needRequest) p.write((const std::byte*) hash.data(), 32); Socket->pushPacket(std::move(p)); } } // Разбираемся с полученными меж тактами привязками ресурсов if(!AsyncContext.AssetsBinds.get_read().empty()) { AssetsBindsChange abc; // Нужно объеденить изменения в один AssetsBindsChange (abc) { std::vector list = std::move(*AsyncContext.AssetsBinds.lock()); for(AssetsBindsChange entry : list) { for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) std::sort(entry.Lost[type].begin(), entry.Lost[type].end()); // Если до этого была объявлена привязка, а теперь она потеряна, то просто сокращаем значения. // Иначе дописываем в lost for(ssize_t iter = abc.Binds.size()-1; iter >= 0; iter--) { const AssetBindEntry& abe = abc.Binds[iter]; auto& lost = entry.Lost[(int) abe.Type]; auto iterator = std::lower_bound(lost.begin(), lost.end(), abe.Id); if(iterator != lost.end()) { // Привязка будет удалена lost.erase(iterator); abc.Binds.erase(abc.Binds.begin()+iter); } } for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) { abc.Lost[type].append_range(entry.Lost[type]); entry.Lost[type].clear(); std::sort(abc.Lost[type].begin(), abc.Lost[type].end()); } for(AssetBindEntry& abe : entry.Binds) { auto iterator = std::lower_bound(entry.Lost[(int) abe.Type].begin(), entry.Lost[(int) abe.Type].end(), abe.Id); if(iterator != entry.Lost[(int) abe.Type].end()) { // Получили новую привязку, которая была удалена в предыдущем такте entry.Lost[(int) abe.Type].erase(iterator); } else { // Данная привязка не удалялась, может она была изменена? bool hasChanged = false; for(AssetBindEntry& abe2 : abc.Binds) { if(abe2.Type == abe.Type && abe2.Id == abe.Id) { // Привязка была изменена abe2 = std::move(abe); hasChanged = true; break; } } if(!hasChanged) // Изменения не было, это просто новая привязка abc.Binds.emplace_back(std::move(abe)); } } entry.Binds.clear(); } } // Запрос к дисковому кешу новых ресурсов std::vector needToLoad; for(const AssetBindEntry& bind : abc.Binds) { bool needQuery = false; // Проверить in memory кеш по домену+ключу { std::string dk = bind.Domain + ':' + bind.Key; auto &niubdk = Assets.NotInUse[(int) bind.Type]; auto iter = niubdk.find(dk); if(iter != niubdk.end()) { // Есть ресурс needQuery = true; } } // Проверить если такой запрос уже был отправлен в AssetsManager и ожидает ответа if(!needQuery) { auto& list = AsyncContext.ResourceWait[(int) bind.Type]; auto iterDomain = list.find(bind.Domain); if(iterDomain != list.end()) { for(const auto& [key, hash] : iterDomain->second) { if(key == bind.Key && hash == bind.Hash) { needQuery = true; break; } } } } // Под рукой нет ресурса, отправим на проверку в AssetsManager if(needQuery) { AsyncContext.ResourceWait[(int) bind.Type][bind.Domain].emplace_back(bind.Key, bind.Hash); needToLoad.emplace_back(bind.Hash, bind.Type, bind.Domain, bind.Key, bind.Id); } } // Отправляем запрос на получение ресурсов if(!needToLoad.empty()) AM->pushReads(std::move(needToLoad)); AsyncContext.Binds.push_back(std::move(abc)); } 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; { for(TickData& data : ticks) { { 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()) 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()) 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()) 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()) 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()) 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()) 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, _] : 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) { 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) { unCompressNodes(val, caocvr[pos].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(TickData& data : ticks) { } } if(RS) RS->pushStageTickSync(); // Применяем изменения по ресурсам, профилям и контенту // Разбираемся с изменениями в привязках ресурсов { AssetsBindsChange abc; for(AssetsBindsChange entry : AsyncContext.Binds) { for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) std::sort(entry.Lost[type].begin(), entry.Lost[type].end()); for(ssize_t iter = abc.Binds.size()-1; iter >= 0; iter--) { const AssetBindEntry& abe = abc.Binds[iter]; auto& lost = entry.Lost[(int) abe.Type]; auto iterator = std::lower_bound(lost.begin(), lost.end(), abe.Id); if(iterator != lost.end()) { lost.erase(iterator); abc.Binds.erase(abc.Binds.begin()+iter); } } for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) { abc.Lost[type].append_range(entry.Lost[type]); entry.Lost[type].clear(); std::sort(abc.Lost[type].begin(), abc.Lost[type].end()); } for(AssetBindEntry& abe : entry.Binds) { auto iterator = std::lower_bound(entry.Lost[(int) abe.Type].begin(), entry.Lost[(int) abe.Type].end(), abe.Id); if(iterator != entry.Lost[(int) abe.Type].end()) { // Получили новую привязку, которая была удалена в предыдущем такте entry.Lost[(int) abe.Type].erase(iterator); } else { // Данная привязка не удалялась, может она была изменена? bool hasChanged = false; for(AssetBindEntry& abe2 : abc.Binds) { if(abe2.Type == abe.Type && abe2.Id == abe.Id) { // Привязка была изменена abe2 = std::move(abe); hasChanged = true; break; } } if(!hasChanged) // Изменения не было, это просто новая привязка abc.Binds.emplace_back(std::move(abe)); } } entry.Binds.clear(); } AsyncContext.Binds.clear(); for(AssetBindEntry& entry : abc.Binds) { Assets.ExistBinds[(int) entry.Type].insert(entry.Id); result.Assets_ChangeOrAdd[entry.Type].push_back(entry.Id); // Если ресурс был в кеше, то достаётся от туда auto iter = Assets.NotInUse[(int) entry.Type].find(entry.Domain+':'+entry.Key); if(iter != Assets.NotInUse[(int) entry.Type].end()) { IServerSession::Assets[entry.Type][entry.Id] = std::get<0>(iter->second); Assets.NotInUse[(int) entry.Type].erase(iter); } } for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) { for(ResourceId id : abc.Lost[type]) { Assets.ExistBinds[type].erase(id); // Потерянные ресурсы уходят в кеш auto iter = IServerSession::Assets[(EnumAssets) type].find(id); if(iter != IServerSession::Assets[(EnumAssets) type].end()) { Assets.NotInUse[(int) iter->second.Type][iter->second.Domain+':'+iter->second.Key] = {iter->second, TIME_BEFORE_UNLOAD_RESOURCE+time(nullptr)}; IServerSession::Assets[(EnumAssets) type].erase(iter); } } result.Assets_Lost[(EnumAssets) type] = std::move(abc.Lost[type]); } } // Получаем ресурсы { for(AssetEntry& entry : AsyncContext.LoadedResources) { if(Assets.ExistBinds[(int) entry.Type].contains(entry.Id)) { // Ресурс ещё нужен IServerSession::Assets[entry.Type][entry.Id] = entry; } else { // Ресурс уже не нужен, отправляем в кеш Assets.NotInUse[(int) entry.Type][entry.Domain+':'+entry.Key] = {entry, TIME_BEFORE_UNLOAD_RESOURCE+time(nullptr)}; } } AsyncContext.LoadedResources.clear(); } // Чистим кеш ресурсов { uint64_t now = time(nullptr); for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) { std::vector toDelete; for(auto& [key, value] : Assets.NotInUse[type]) { if(std::get<1>(value) < now) toDelete.push_back(key); } for(std::string& key : toDelete) Assets.NotInUse[type].erase(Assets.NotInUse[type].find(key)); } } // Чанки { for(auto& [wId, lost] : regions_Lost_Result) { auto iterWorld = Content.Worlds.find(wId); if(iterWorld == Content.Worlds.end()) continue; for(const Pos::GlobalRegion& rPos : lost) { auto iterRegion = iterWorld->second.Regions.find(rPos); if(iterRegion != iterWorld->second.Regions.end()) iterWorld->second.Regions.erase(iterRegion); } } 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); } } } if(RS) RS->tickSync(result); } // Здесь нужно обработать управляющие пакеты // Оповещение модуля рендера об изменениях ресурсов 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; } 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; co_return; } void ServerSession::protocolError() { shutdown(EnumDisconnect::ProtocolError); } coro<> ServerSession::readPacket(Net::AsyncSocket &sock) { uint8_t first = co_await sock.read(); switch((ToClient::L1) first) { case ToClient::L1::System: co_await rP_System(sock); co_return; case ToClient::L1::Resource: co_await rP_Resource(sock); co_return; case ToClient::L1::Definition: co_await rP_Definition(sock); co_return; case ToClient::L1::Content: co_await rP_Content(sock); co_return; default: protocolError(); } } coro<> ServerSession::rP_System(Net::AsyncSocket &sock) { uint8_t second = co_await sock.read(); switch((ToClient::L2System) second) { case ToClient::L2System::Init: co_return; case ToClient::L2System::Disconnect: { 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; } case ToClient::L2System::LinkCameraToEntity: co_return; case ToClient::L2System::UnlinkCamera: co_return; case ToClient::L2System::SyncTick: AsyncContext.TickSequence.lock()->push_back(std::move(AsyncContext.ThisTickEntry)); co_return; default: protocolError(); } } coro<> ServerSession::rP_Resource(Net::AsyncSocket &sock) { uint8_t second = co_await sock.read(); switch((ToClient::L2Resource) second) { case ToClient::L2Resource::Bind: { uint32_t count = co_await sock.read(); std::vector binds; binds.reserve(count); for(size_t iter = 0; iter < count; iter++) { uint8_t type = co_await sock.read(); uint32_t id = co_await sock.read(); std::string domain, key; domain = co_await sock.read(); key = co_await sock.read(); Hash_t hash; co_await sock.read((std::byte*) hash.data(), hash.size()); binds.emplace_back( (EnumAssets) type, (ResourceId) id, std::move(domain), std::move(key), hash ); } AsyncContext.AssetsBinds.lock()->push_back(AssetsBindsChange(binds, {})); } case ToClient::L2Resource::Lost: { uint32_t count = co_await sock.read(); AssetsBindsChange abc; for(size_t iter = 0; iter < count; iter++) { uint8_t type = co_await sock.read(); uint32_t id = co_await sock.read(); abc.Lost[(int) type].push_back(id); } AsyncContext.AssetsBinds.lock()->emplace_back(std::move(abc)); } case ToClient::L2Resource::InitResSend: { uint32_t size = co_await sock.read(); Hash_t hash; co_await sock.read((std::byte*) hash.data(), hash.size()); ResourceId id = co_await sock.read(); EnumAssets type = (EnumAssets) co_await sock.read(); std::string domain = co_await sock.read(); std::string key = co_await sock.read(); AsyncContext.AssetsLoading[hash] = AssetLoading{ type, id, std::move(domain), std::move(key), std::u8string(size, '\0'), 0 }; co_return; } case ToClient::L2Resource::ChunkSend: { Hash_t hash; co_await sock.read((std::byte*) hash.data(), hash.size()); uint32_t size = co_await sock.read(); 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()) { // Ресурс полностью загружен AsyncContext.LoadedAssets.lock()->emplace_back( al.Type, al.Id, std::move(al.Domain), std::move(al.Key), std::move(al.Data) ); AsyncContext.AssetsLoading.erase(AsyncContext.AssetsLoading.find(hash)); } co_return; } default: protocolError(); } } coro<> ServerSession::rP_Definition(Net::AsyncSocket &sock) { uint8_t second = co_await sock.read(); switch((ToClient::L2Definition) second) { case ToClient::L2Definition::World: { DefWorldId cdId = co_await sock.read(); co_return; } case ToClient::L2Definition::FreeWorld: { DefWorldId cdId = co_await sock.read(); co_return; } case ToClient::L2Definition::Voxel: { DefVoxelId cdId = co_await sock.read(); co_return; } case ToClient::L2Definition::FreeVoxel: { DefVoxelId cdId = co_await sock.read(); co_return; } case ToClient::L2Definition::Node: { // DefNodeId id; // DefNode_t def; // id = co_await sock.read(); // def.DrawType = (DefNode_t::EnumDrawType) co_await sock.read(); // for(int iter = 0; iter < 6; iter++) { // auto &pl = def.Texs[iter].Pipeline; // pl.resize(co_await sock.read()); // co_await sock.read((std::byte*) pl.data(), pl.size()); // } // PP_Definition_Node *packet = new PP_Definition_Node( // id, // def // ); // while(!NetInputPackets.push(packet)); // co_return; } case ToClient::L2Definition::FreeNode: { DefNodeId id = co_await sock.read(); co_return; } case ToClient::L2Definition::Portal: co_return; case ToClient::L2Definition::FreePortal: co_return; case ToClient::L2Definition::Entity: co_return; case ToClient::L2Definition::FreeEntity: co_return; default: protocolError(); } } coro<> ServerSession::rP_Content(Net::AsyncSocket &sock) { uint8_t second = co_await sock.read(); switch((ToClient::L2Content) second) { case ToClient::L2Content::World: { WorldId_t wId = co_await sock.read(); AsyncContext.ThisTickEntry.Worlds_AddOrChange.emplace_back(wId, nullptr); co_return; } case ToClient::L2Content::RemoveWorld: { WorldId_t wId = co_await sock.read(); AsyncContext.ThisTickEntry.Worlds_Lost.push_back(wId); co_return; } case ToClient::L2Content::Portal: co_return; case ToClient::L2Content::RemovePortal: co_return; case ToClient::L2Content::Entity: co_return; case ToClient::L2Content::RemoveEntity: co_return; case ToClient::L2Content::ChunkVoxels: { 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; } case ToClient::L2Content::ChunkNodes: { 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; } case ToClient::L2Content::ChunkLightPrism: co_return; case ToClient::L2Content::RemoveRegion: { 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; } default: protocolError(); } } }