This commit is contained in:
2025-08-12 17:41:36 +06:00
parent e54f04f62e
commit c1b16949fa
7 changed files with 189 additions and 567 deletions

View File

@@ -499,7 +499,7 @@ enum struct TexturePipelineCMD : uint8_t {
};
struct TexturePipeline {
std::vector<BinTextureId_t> BinTextures;
std::vector<AssetsTexture> BinTextures;
std::u8string Pipeline;
};

View File

@@ -172,7 +172,7 @@ using NodeStateCondition = std::bitset<256>;
NodeStateCondition nodestateExpression(const std::vector<NodestateEntry>& entries, const std::string& expression);
struct ModelTransform {
std::vector<BinModelId_t> Ids;
std::vector<AssetsModel> Ids;
uint16_t Weight = 1;
bool UVLock = false;
};

View File

@@ -7,14 +7,55 @@
namespace LV::Server {
coro<AssetsManager::Resource> AssetsManager::loadResourceFromFile(EnumAssets type, fs::path path) const {
co_return AssetsManager::Resource(path);
}
coro<AssetsManager::Out_recheckResources> AssetsManager::recheckResources(AssetsRegister info) const {
coro<AssetsManager::Resource> AssetsManager::loadResourceFromLua(EnumAssets type, void*) const {
co_return AssetsManager::Resource("assets/null");
}
std::tuple<ResourceId_t, std::optional<AssetsManager::DataEntry>&> AssetsManager::Local::nextId(EnumAssets type) {
auto& table = Table[(int) type];
ResourceId_t id = -1;
std::optional<DataEntry> *data = nullptr;
for(size_t index = 0; index < table.size(); index++) {
auto& entry = *table[index];
if(entry.IsFull)
continue;
uint32_t pos = entry.Empty._Find_first();
entry.Empty.reset(pos);
if(entry.Empty._Find_first() == entry.Empty.size())
entry.IsFull = true;
id = index*TableEntry::ChunkSize + pos;
data = &entry.Entries[pos];
}
if(!data) {
table.emplace_back(std::make_unique<TableEntry>());
id = (table.size()-1)*TableEntry::ChunkSize;
data = &table.back()->Entries[0];
}
return {id, *data};
}
coro<AssetsManager::Out_recheckResources> AssetsManager::recheckResources(AssetsRegister info) {
Out_recheckResources result;
// Найти пропавшие ресурсы
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) {
for(auto& [domain, resources] : KeyToId[type]) {
auto lock = LocalObj.lock();
for(auto& [domain, resources] : lock->KeyToId[type]) {
for(auto& [key, id] : resources) {
if(!lock->Table[type][id / TableEntry::ChunkSize]->Entries[id % TableEntry::ChunkSize])
continue;
bool exists = false;
for(const fs::path& path : info.Assets) {
@@ -60,9 +101,10 @@ coro<AssetsManager::Out_recheckResources> AssetsManager::recheckResources(Assets
// Найти новые или изменённые ресурсы
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) {
const auto& keyToId = KeyToId[type];
for(auto& [domain, resources] : info.Custom[type]) {
auto lock = LocalObj.lock();
const auto& keyToId = lock->KeyToId[type];
auto iterDomain = keyToId.find(domain);
auto& findList = findedResources[type][domain];
@@ -72,7 +114,7 @@ coro<AssetsManager::Out_recheckResources> AssetsManager::recheckResources(Assets
for(auto& [key, id] : resources) {
// Подобрать идентификатор
// TODO: реализовать регистрации ресурсов из lua
domainList.emplace_back(key, Resource("assets/null"));
domainList.emplace_back(key, Resource("assets/null"), fs::file_time_type::min());
findList.insert(key);
}
} else {
@@ -82,9 +124,10 @@ coro<AssetsManager::Out_recheckResources> AssetsManager::recheckResources(Assets
continue;
else if(iterDomain->second.contains(key)) {
// Ресурс уже есть, TODO: нужно проверить его изменение
result.NewOrChange[type][domain].emplace_back(key, co_await loadResourceFromFile((EnumAssets) type, "assets/null"), fs::file_time_type::min());
} else {
// Ресурс не был известен
result.NewOrChange[type][domain].emplace_back(key, Resource("assets/null"));
result.NewOrChange[type][domain].emplace_back(key, co_await loadResourceFromFile((EnumAssets) type, "assets/null"), fs::file_time_type::min());
}
findList.insert(key);
@@ -117,7 +160,8 @@ coro<AssetsManager::Out_recheckResources> AssetsManager::recheckResources(Assets
}
auto& findList = findedResources[type][domain];
auto iterDomain = KeyToId[type].find(domain);
auto lock = LocalObj.lock();
auto iterDomain = lock->KeyToId[type].find(domain);
if(!fs::exists(resourcesPath) || !fs::is_directory(resourcesPath))
continue;
@@ -132,20 +176,19 @@ coro<AssetsManager::Out_recheckResources> AssetsManager::recheckResources(Assets
if(findList.contains(key))
// Ресурс уже был найден в вышестоящей директории
continue;
else if(iterDomain != KeyToId[type].end() && iterDomain->second.contains(key)) {
else if(iterDomain != lock->KeyToId[type].end() && iterDomain->second.contains(key)) {
// Ресурс уже есть, TODO: нужно проверить его изменение
ResourceId_t id = iterDomain->second.at(key);
DataEntry& entry = *Table[type][id / TableEntry::ChunkSize]->Entries[id % TableEntry::ChunkSize];
DataEntry& entry = *lock->Table[type][id / TableEntry::ChunkSize]->Entries[id % TableEntry::ChunkSize];
fs::file_time_type lwt = fs::last_write_time(file);
if(lwt <= entry.FileChangeTime)
continue;
if(lwt != entry.FileChangeTime)
// Будем считать что ресурс изменился
result.NewOrChange[type][domain].emplace_back(key, co_await loadResourceFromFile((EnumAssets) type, file), lwt);
} else {
// Ресурс не был известен
result.NewOrChange[type][domain].emplace_back(key, Resource("assets/null"));
fs::file_time_type lwt = fs::last_write_time(file);
result.NewOrChange[type][domain].emplace_back(key, co_await loadResourceFromFile((EnumAssets) type, file), lwt);
}
findList.insert(key);
@@ -158,4 +201,88 @@ coro<AssetsManager::Out_recheckResources> AssetsManager::recheckResources(Assets
co_return result;
}
AssetsManager::Out_applyResourceChange AssetsManager::applyResourceChange(const Out_recheckResources& orr) {
// Потерянные и обновлённые идентификаторы
Out_applyResourceChange result;
// Удаляем ресурсы
/*
Удаляются только ресурсы, при этом за ними остаётся бронь на идентификатор
Уже скомпилированные зависимости к ресурсам не будут
перекомпилироваться для смены идентификатора. Если нужный ресурс
появится, то привязка останется. Новые клиенты не получат ресурс
которого нет, но он может использоваться
*/
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) {
for(auto& [domain, resources] : orr.Lost[type]) {
auto lock = LocalObj.lock();
auto& keyToIdDomain = lock->KeyToId[type].at(domain);
for(const std::string& key : resources) {
auto iter = keyToIdDomain.find(key);
assert(iter != keyToIdDomain.end());
ResourceId_t resId = iter->second;
// keyToIdDomain.erase(iter);
// lost[type].push_back(resId);
uint32_t localId = resId % TableEntry::ChunkSize;
auto& chunk = lock->Table[type][resId / TableEntry::ChunkSize];
// chunk->IsFull = false;
// chunk->Empty.set(localId);
chunk->Entries[localId].reset();
}
}
}
// Добавляем
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) {
for(auto& [domain, resources] : orr.NewOrChange[type]) {
auto lock = LocalObj.lock();
auto& keyToIdDomain = lock->KeyToId[type][domain];
for(auto& [key, resource, lwt] : resources) {
ResourceId_t id = -1;
std::optional<DataEntry>* data = nullptr;
if(auto iterId = keyToIdDomain.find(key); iterId != keyToIdDomain.end()) {
id = iterId->second;
data = &lock->Table[(int) type][id / TableEntry::ChunkSize]->Entries[id % TableEntry::ChunkSize];
} else {
auto [_id, _data] = lock->nextId((EnumAssets) type);
id = _id;
data = &_data;
}
result.NewOrChange[type].push_back({id, resource});
keyToIdDomain[key] = id;
data->emplace(lwt, resource);
}
}
// Удалённые идентификаторы не считаются удалёнными, если были изменены
std::unordered_set<ResourceId_t> noc(result.NewOrChange[type].begin(), result.NewOrChange[type].end());
std::unordered_set<ResourceId_t> l(result.Lost[type].begin(), result.Lost[type].end());
result.Lost[type].clear();
std::set_difference(l.begin(), l.end(), noc.begin(), noc.end(), std::back_inserter(result.Lost[type]));
}
return result;
}
ResourceId_t AssetsManager::getId(EnumAssets type, const std::string& domain, const std::string& key) {
auto lock = LocalObj.lock();
auto& keyToId = lock->KeyToId[(int) type];
if(auto iterKTI = keyToId.find(domain); iterKTI != keyToId.end()) {
if(auto iterKey = iterKTI->second.find(key); iterKey != iterKTI->second.end()) {
return iterKey->second;
}
}
auto [id, entry] = lock->nextId(type);
keyToId[domain][key] = id;
return id;
}
}

View File

@@ -2,6 +2,7 @@
#include "Abstract.hpp"
#include "Common/Abstract.hpp"
#include "TOSLib.hpp"
#include "boost/asio/io_context.hpp"
#include "sha2.hpp"
#include <boost/interprocess/file_mapping.hpp>
@@ -14,6 +15,11 @@ namespace LV::Server {
namespace fs = std::filesystem;
/*
Работает с ресурсами из папок assets.
Использует папку server_cache/assets для хранения
преобразованных ресурсов
*/
class AssetsManager {
public:
struct Resource {
@@ -64,21 +70,27 @@ private:
std::array<std::optional<DataEntry>, ChunkSize> Entries;
TableEntry() {
Empty.reset();
Empty.set();
}
};
// Данные не меняются в процессе работы сервера.
// Изменения возможны при синхронизации всего сервера
// и перехода в режим перезагрузки модов
struct Local {
// Связь ресурсов по идентификаторам
std::vector<std::unique_ptr<TableEntry>> Table[(int) EnumAssets::MAX_ENUM];
// Связь домены -> {ключ -> идентификатор}
std::unordered_map<std::string, std::unordered_map<std::string, ResourceId_t>> KeyToId[(int) EnumAssets::MAX_ENUM];
std::tuple<ResourceId_t, std::optional<DataEntry>&> nextId(EnumAssets type);
};
// Связь ресурсов по идентификаторам
std::vector<std::unique_ptr<TableEntry>> Table[(int) EnumAssets::MAX_ENUM];
// Связь домены -> {ключ -> идентификатор}
std::unordered_map<std::string, std::unordered_map<std::string, ResourceId_t>> KeyToId[(int) EnumAssets::MAX_ENUM];
DataEntry& getEntry(EnumAssets type, ResourceId_t id);
TOS::SpinlockObject<Local> LocalObj;
/*
Загрузка ресурса с файла. При необходимости приводится
к внутреннему формату и сохраняется в кеше
*/
coro<Resource> loadResourceFromFile(EnumAssets type, fs::path path) const;
coro<Resource> loadResourceFromLua(EnumAssets type, void*) const;
public:
AssetsManager(asio::io_context& ioc);
@@ -111,10 +123,28 @@ public:
// Потерянные ресурсы
std::unordered_map<std::string, std::vector<std::string>> Lost[(int) EnumAssets::MAX_ENUM];
// Домен и ключ ресурса
std::unordered_map<std::string, std::vector<std::tuple<std::string, Resource>>> NewOrChange[(int) EnumAssets::MAX_ENUM];
std::unordered_map<std::string, std::vector<std::tuple<std::string, Resource, fs::file_time_type>>> NewOrChange[(int) EnumAssets::MAX_ENUM];
};
coro<Out_recheckResources> recheckResources(AssetsRegister) const;
coro<Out_recheckResources> recheckResources(AssetsRegister);
/*
Применяет расчитанные изменения.
Раздаёт идентификаторы ресурсам и записывает их в таблицу
*/
struct Out_applyResourceChange {
std::vector<ResourceId_t> Lost[(int) EnumAssets::MAX_ENUM];
std::vector<std::pair<ResourceId_t, Resource>> NewOrChange[(int) EnumAssets::MAX_ENUM];
};
Out_applyResourceChange applyResourceChange(const Out_recheckResources& orr);
/*
Выдаёт идентификатор ресурса, даже если он не существует или был удалён.
resource должен содержать домен и путь
*/
ResourceId_t getId(EnumAssets type, const std::string& domain, const std::string& key);
};
}

View File

@@ -1,383 +0,0 @@
#include "Common/Abstract.hpp"
#include "Server/Abstract.hpp"
#include <filesystem>
#include <unordered_map>
#define BOOST_ASIO_HAS_IO_URING 1
#include "BinaryResourceManager.hpp"
#include <memory>
#include <boost/system.hpp>
#include <boost/asio.hpp>
#include <boost/asio/stream_file.hpp>
#include <TOSLib.hpp>
#include <fstream>
namespace LV::Server {
struct UriParse {
std::string Protocol, Path;
std::string getFull() const {
return Protocol + "://" + Path;
}
};
UriParse parseUri(const std::string& uri) {
size_t pos = uri.find("://");
if(pos == std::string::npos)
return {"assets", uri};
else
return {uri.substr(0, pos), uri.substr(pos+3)};
}
struct Resource {
// Файл загруженный с диска
std::shared_ptr<ResourceFile> Loaded;
// Источник
Hash_t Hash;
size_t LastUsedTime = 0;
EnumBinResource Type;
ResourceId_t ResId;
UriParse Uri;
std::string LastError;
};
void BinaryResourceManager::run() {
TOS::Logger LOG = "BinaryResourceManager::run";
LOG.debug() << "Поток чтения двоичных ресурсов запущен";
// Ресурсы - кешированные в оперативную память или в процессе загрузки
std::unordered_map<ResourceId_t, std::shared_ptr<Resource>> knownResource[(int) EnumBinResource::MAX_ENUM];
// Трансляция идентификаторов в Uri (противоположность KnownResource)
std::vector<std::string> resIdToUri[(int) EnumBinResource::MAX_ENUM];
// Новые полученные идентификаторы и те, чьи ресурсы нужно снова загрузить
std::vector<ResourceId_t> newRes[(int) EnumBinResource::MAX_ENUM];
// Пути поиска ресурсов
std::vector<fs::path> assets;
// Запросы хешей
std::vector<ResourceId_t> binToHash[(int) EnumBinResource::MAX_ENUM];
//
std::unordered_map<Hash_t, std::shared_ptr<Resource>> hashToResource;
std::vector<Hash_t> hashToLoad;
auto lambdaLoadResource = [&](UriParse uri, int type) -> std::variant<std::shared_ptr<ResourceFile>, std::string>
{
// std::shared_ptr<Resource> resObj = std::make_shared<Resource>();
// knownResource[type][resId] = resObj;
if(uri.Protocol != "assets")
return "Протокол не поддерживается";
else {
auto var = loadFile(assets, uri.Path, (EnumBinResource) type);
if(var.index() == 0) {
std::shared_ptr<ResourceFile> resource = std::get<0>(var);
resource = convertFormate(resource, (EnumBinResource) type);
resource->calcHash();
return resource;
} else {
return std::get<1>(var);
}
}
};
try {
while(!NeedShutdown) {
bool hasWork = false;
auto lock = Local.lock();
for(int type = 0; type < (int) EnumBinResource::MAX_ENUM; type++) {
for(ResourceId_t iter = 0; iter < lock->ResIdToUri[type].size(); iter++) {
newRes[type].push_back(resIdToUri[type].size()+iter);
}
resIdToUri[type].insert(resIdToUri[type].end(), lock->ResIdToUri[type].begin(), lock->ResIdToUri[type].end());
resIdToUri[type].clear();
binToHash[type].insert(binToHash[type].end(), lock->BinToHash[type].begin(), lock->BinToHash[type].end());
lock->BinToHash[type].clear();
}
bool assetsUpdate = false;
if(!lock->Assets.empty()) {
// Требуется пересмотр всех ресурсов
assets = std::move(lock->Assets);
assetsUpdate = true;
}
std::vector<Hash_t> hashRequest = std::move(lock->Hashes);
lock.unlock();
if(!hashRequest.empty()) {
std::vector<std::shared_ptr<ResourceFile>> hashToResourceOut;
for(Hash_t hash : hashRequest) {
auto iter = hashToResource.find(hash);
if(iter == hashToResource.end())
continue;
if(!iter->second->Loaded) {
hashToLoad.push_back(hash);
continue;
}
iter->second->LastUsedTime = TOS::Time::getSeconds();
hashToResourceOut.push_back(iter->second->Loaded);
}
auto outLock = Out.lock();
outLock->HashToResource.insert(outLock->HashToResource.end(), hashToResourceOut.begin(), hashToResourceOut.end());
}
{
std::unordered_map<ResourceId_t, ResourceFile::Hash_t> binToHashOut[(int) EnumBinResource::MAX_ENUM];
for(int type = 0; type < (int) EnumBinResource::MAX_ENUM; type++) {
for(ResourceId_t resId : binToHash[type]) {
std::shared_ptr<Resource> resource = knownResource[type][resId];
if(!resource)
continue; // Идентификатор не известен
binToHashOut[type][resId] = resource->Hash;
}
}
}
// Загрузка ресурсов по новым идентификаторам
for(int type = 0; type < (int) EnumBinResource::MAX_ENUM; type++) {
if(newRes[type].empty())
continue;
hasWork = true;
while(!newRes[type].empty()) {
ResourceId_t resId = newRes[type].back();
newRes[type].pop_back();
assert(resId < resIdToUri[type].size());
UriParse uri = parseUri(resIdToUri[type][resId]);
std::shared_ptr<Resource> resObj = std::make_shared<Resource>();
resObj->LastUsedTime = TOS::Time::getSeconds();
resObj->Type = (EnumBinResource) type;
resObj->ResId = resId;
resObj->Uri = uri;
auto var = lambdaLoadResource(uri, type);
if(var.index() == 0) {
resObj->Loaded = std::get<0>(var);
resObj->Hash = resObj->Loaded->Hash;
hashToResource[resObj->Hash] = resObj;
} else {
std::fill(resObj->Hash.begin(), resObj->Hash.end(), 0);
resObj->LastError = std::get<1>(var);
}
knownResource[type][resId] = resObj;
}
}
while(!hashToLoad.empty()) {
Hash_t hash = hashToLoad.back();
hashToLoad.pop_back();
auto iter = hashToResource.find(hash);
if(iter == hashToResource.end())
continue;
std::shared_ptr<Resource> &res = iter->second;
if(res->Loaded) {
Out.lock()->HashToResource.push_back(res->Loaded);
} else {
if(!res->LastError.empty())
continue;
auto var = lambdaLoadResource(res->Uri, (int) res->Type);
if(var.index() == 0) {
hasWork = true;
res->Loaded = std::get<0>(var);
res->LastUsedTime = TOS::Time::getSeconds();
res->LastError.clear();
if(res->Hash != res->Loaded->Hash) {
// Хеш изменился
Out.lock()->BinToHash[(int) res->Type][res->ResId] = res->Loaded->Hash;
res->Hash = res->Loaded->Hash;
std::shared_ptr<Resource> resObj = res;
hashToResource.erase(iter);
hashToResource[hash] = resObj;
} else {
Out.lock()->HashToResource.push_back(res->Loaded);
}
} else {
res->LastError = std::get<1>(var);
}
}
}
// Удаляем долго не используемые ресурсы
{
size_t now = TOS::Time::getSeconds();
for(int type = 0; type < (int) EnumBinResource::MAX_ENUM; type++) {
for(auto& resObj : knownResource[type]) {
if(now - resObj.second->LastUsedTime > 30)
resObj.second->Loaded = nullptr;
}
}
}
if(assetsUpdate) {
hashToLoad.clear();
hashToResource.clear();
for(int type = 0; type < (int) EnumBinResource::MAX_ENUM; type++) {
for(auto& [resId, resObj] : knownResource[type]) {
auto var = lambdaLoadResource(resObj->Uri, type);
if(var.index() == 0) {
hasWork = true;
resObj->Loaded = std::get<0>(var);
resObj->LastUsedTime = TOS::Time::getSeconds();
resObj->LastError.clear();
if(resObj->Hash != resObj->Loaded->Hash) {
// Хеш изменился
Out.lock()->BinToHash[type][resId] = resObj->Loaded->Hash;
resObj->Hash = resObj->Loaded->Hash;
}
hashToResource[resObj->Hash] = resObj;
} else {
resObj->LastError = std::get<1>(var);
}
}
}
}
if(!hasWork)
TOS::Time::sleep3(10);
}
} catch(const std::exception& exc) {
LOG.error() << exc.what();
}
NeedShutdown = true;
LOG.debug() << "Поток чтения двоичных ресурсов остановлен";
}
std::variant<std::shared_ptr<ResourceFile>, std::string>
BinaryResourceManager::loadFile(const std::vector<fs::path>& assets, const std::string& path, EnumBinResource type)
{
try {
std::shared_ptr<ResourceFile> file = std::make_shared<ResourceFile>();
std::string firstPath;
switch(type) {
case EnumBinResource::Texture: firstPath = "texture"; break;
case EnumBinResource::Animation: firstPath = "animation"; break;
case EnumBinResource::Model: firstPath = "model"; break;
case EnumBinResource::Sound: firstPath = "sound"; break;
case EnumBinResource::Font: firstPath = "font"; break;
default: assert(false);
}
for(fs::path assetsPath : assets) {
fs::path p = assetsPath / firstPath / path;
if(!fs::exists(p))
continue;
std::ifstream fd(p);
if(!fd)
MAKE_ERROR("Не удалось открыть файл: " << p.string());
fd.seekg(0, std::ios::end);
std::streamsize size = fd.tellg();
fd.seekg(0, std::ios::beg);
file->Data.resize(size);
fd.read((char*) file->Data.data(), size);
return file;
}
MAKE_ERROR("Файл не найден");
} catch(const std::exception& exc) {
return exc.what();
}
}
std::shared_ptr<ResourceFile> convertFormate(std::shared_ptr<ResourceFile> file, EnumBinResource type) {
return file;
}
BinaryResourceManager::BinaryResourceManager(asio::io_context &ioc)
: AsyncObject(ioc), Thread(&BinaryResourceManager::run, this)
{
}
BinaryResourceManager::~BinaryResourceManager() {
NeedShutdown = true;
Thread.join();
}
void BinaryResourceManager::recheckResources(std::vector<fs::path> assets /* Пути до активных папок assets */) {
}
void BinaryResourceManager::needResourceResponse(const ResourceRequest& resources) {
auto lock = Local.lock();
for(int iter = 0; iter < (int) EnumBinResource::MAX_ENUM; iter++) {
lock->BinToHash[iter].insert(lock->BinToHash[iter].end(), resources.BinToHash[iter].begin(), resources.BinToHash[iter].end());
}
lock->Hashes.insert(lock->Hashes.end(), resources.Hashes.begin(), resources.Hashes.end());
}
void BinaryResourceManager::update(float dtime) {
// if(UpdatedResources.no_lock_readable().empty())
// return;
// auto lock = UpdatedResources.lock_write();
// for(ResourceId_t resId : *lock) {
// auto iterPI = PreparedInformation.find(resId);
// if(iterPI != PreparedInformation.end())
// continue;
// auto iterRI = ResourcesInfo.find(resId);
// if(iterRI != ResourcesInfo.end()) {
// PreparedInformation[resId] = iterRI->second->Loaded;
// }
// }
}
// coro<> BinaryResourceManager::checkResource_Assets(ResourceId_t id, fs::path path, std::shared_ptr<Resource> res) {
// try {
// asio::stream_file fd(IOC, path, asio::stream_file::flags::read_only);
// if(fd.size() > 1024*1024*16)
// MAKE_ERROR("Превышен лимит размера файла: " << fd.size() << " > " << 1024*1024*16);
// std::shared_ptr<ResourceFile> file = std::make_shared<ResourceFile>();
// file->Data.resize(fd.size());
// co_await asio::async_read(fd, asio::mutable_buffer(file->Data.data(), file->Data.size()));
// file->calcHash();
// res->LastError.clear();
// } catch(const std::exception &exc) {
// res->LastError = exc.what();
// res->IsLoading = false;
// if(const boost::system::system_error *errc = dynamic_cast<const boost::system::system_error*>(&exc); errc && errc->code() == asio::error::operation_aborted)
// co_return;
// }
// res->IsLoading = false;
// UpdatedResources.lock_write()->push_back(id);
// }
}

View File

@@ -1,153 +0,0 @@
#pragma once
#include "Common/Abstract.hpp"
#include "Common/Lockable.hpp"
#include "Server/RemoteClient.hpp"
#include <functional>
#include <optional>
#include <string>
#include <unordered_map>
#include <filesystem>
#include <variant>
#include <vector>
#include <Common/Async.hpp>
#include "Abstract.hpp"
#include "TOSLib.hpp"
namespace LV::Server {
namespace fs = std::filesystem;
/*
Может прийти множество запросов на один не загруженный ресурс
Чтение происходит отдельным потоком, переконвертацию пока предлагаю в realtime.
Хэш вычисляется после чтения и может быть иным чем при прошлом чтении (ресурс изменили наживую)
тогда обычным оповещением клиентам дойдёт новая версия
Подержать какое-то время ресурс в памяти
Ключи сопоставляются с идентификаторами и с хешеми. При появлении нового ключа,
ему выдаётся идентификатор и делается запрос на загрузку ресурса для вычисления хеша.
recheckResources делает повторную загрузку всех ресурсов для проверки изменения хешей.
*/
class BinaryResourceManager : public AsyncObject {
private:
// Поток сервера
// Последовательная регистрация ресурсов
ResourceId_t NextId[(int) EnumAssets::MAX_ENUM] = {0};
// Известные ресурсы, им присвоен идентификатор
// Нужно для потока загрузки
std::unordered_map<std::string, ResourceId_t> KnownResource[(int) EnumBinResource::MAX_ENUM];
// Местные потоки
struct LocalObj_t {
// Трансляция идентификаторов в Uri (противоположность KnownResource)
// Передаётся в отдельный поток
std::vector<std::string> ResIdToUri[(int) EnumBinResource::MAX_ENUM];
// Кому-то нужно сопоставить идентификаторы с хешами
std::vector<ResourceId_t> BinToHash[(int) EnumBinResource::MAX_ENUM];
// Запрос ресурсов, по которым потоки загружают ресурсы с диска
std::vector<Hash_t> Hashes;
// Передача новых путей поиска ресурсов в другой поток
std::vector<fs::path> Assets;
};
TOS::SpinlockObject<LocalObj_t> Local;
public:
// Подготовленные оповещения о ресурсах
struct OutObj_t {
std::unordered_map<ResourceId_t, ResourceFile::Hash_t> BinToHash[(int) EnumBinResource::MAX_ENUM];
std::vector<std::shared_ptr<ResourceFile>> HashToResource;
};
private:
TOS::SpinlockObject<OutObj_t> Out;
volatile bool NeedShutdown = false;
std::thread Thread;
void run();
std::variant<std::shared_ptr<ResourceFile>, std::string>
loadFile(const std::vector<fs::path>& assets, const std::string& path, EnumBinResource type);
std::shared_ptr<ResourceFile> convertFormate(std::shared_ptr<ResourceFile> file, EnumBinResource type);
public:
// Если ресурс будет обновлён или загружен будет вызвано onResourceUpdate
BinaryResourceManager(asio::io_context &ioc);
virtual ~BinaryResourceManager();
// Перепроверка изменений ресурсов
void recheckResources(std::vector<fs::path> assets /* Пути до активных папок assets */);
// Выдаёт или назначает идентификатор для ресурса
ResourceId_t getResource(const std::string& uri, EnumBinResource bin) {
std::string fullUri;
{
size_t pos = uri.find("://");
if(pos == std::string::npos)
fullUri = "assets://" + uri;
else
fullUri = uri;
}
int index = (int) bin;
auto &container = KnownResource[index];
auto iter = container.find(fullUri);
if(iter == container.end()) {
assert(NextId[index] != ResourceId_t(-1));
ResourceId_t nextId = NextId[index]++;
container.insert(iter, {fullUri, nextId});
auto lock = Local.lock();
lock->ResIdToUri[index].push_back(uri);
return nextId;
}
return iter->second;
}
BinTextureId_t getTexture(const std::string& uri) {
return getResource(uri, EnumBinResource::Texture);
}
BinAnimationId_t getAnimation(const std::string& uri) {
return getResource(uri, EnumBinResource::Animation);
}
BinModelId_t getModel(const std::string& uri) {
return getResource(uri, EnumBinResource::Model);
}
BinSoundId_t getSound(const std::string& uri) {
return getResource(uri, EnumBinResource::Sound);
}
BinFontId_t getFont(const std::string& uri) {
return getResource(uri, EnumBinResource::Font);
}
// Запросить ресурсы через pushPreparedPackets
void needResourceResponse(const ResourceRequest& resources);
// Получение обновлений или оповещений ресурсов
OutObj_t takePreparedInformation() {
return std::move(*Out.lock());
}
// Серверный такт
void update(float dtime);
};
}

View File

@@ -1,5 +1,6 @@
#pragma once
#include "Server/AssetsManager.hpp"
#define SOL_EXCEPTIONS_SAFE_PROPAGATION 1
#include <Common/Net.hpp>
@@ -23,7 +24,7 @@
#include "ContentEventController.hpp"
#include "WorldDefManager.hpp"
#include "BinaryResourceManager.hpp"
#include "AssetsManager.hpp"
#include "World.hpp"
#include "SaveBackend.hpp"
@@ -71,7 +72,7 @@ class GameServer : public AsyncObject {
struct ContentObj {
public:
BinaryResourceManager BRM;
AssetsManager AM;
ResourceId_t NextId[(int) EnumDefContent::MAX_ENUM] = {0};
std::unordered_map<std::string, ResourceId_t> ContentKeyToId[(int) EnumDefContent::MAX_ENUM]; // EnumDefContent
@@ -117,7 +118,7 @@ class GameServer : public AsyncObject {
ContentObj(asio::io_context& ioc)
: BRM(ioc)
: AM(ioc)
{}
} Content;