Продолжение передачи ресурсов на клиент

This commit is contained in:
2025-08-27 14:44:56 +06:00
parent cfbbfa286a
commit d60405cd18
10 changed files with 222 additions and 38 deletions

View File

@@ -60,11 +60,20 @@ public:
// states
};
struct AssetEntry {
EnumAssets Type;
ResourceId Id;
std::string Domain, Key;
Resource Res;
};
/* Интерфейс рендера текущего подключения к серверу */
class IRenderSession {
public:
// Подгрузка двоичных ресурсов
virtual void onBinaryResourceAdd(std::vector<Hash_t>) = 0;
// Изменённые ресурсы (кроме звуков)
virtual void onAssetsChanges(std::unordered_map<EnumAssets, std::vector<AssetEntry>> resources) = 0;
// Потерянные ресурсы
virtual void onAssetsLost(std::unordered_map<EnumAssets, std::vector<ResourceId>> resources) = 0;
virtual void onContentDefinesAdd(std::unordered_map<EnumDefContent, std::vector<ResourceId>>) = 0;
virtual void onContentDefinesLost(std::unordered_map<EnumDefContent, std::vector<ResourceId>>) = 0;
@@ -147,21 +156,6 @@ class IServerSession {
// };
public:
struct AssetEntry {
EnumAssets Type;
ResourceId Id;
std::string Domain, Key;
Resource Res;
};
static constexpr uint64_t TIME_BEFORE_UNLOAD_RESOURCE = 180;
struct {
// Оперируемые ресурсы
std::unordered_map<Hash_t, AssetEntry> Assets;
// Недавно использованные ресурсы, пока хранятся здесь в течении TIME_BEFORE_UNLOAD_RESOURCE секунд
std::unordered_map<Hash_t, std::pair<AssetEntry, uint64_t>> NotInUseAssets;
} Binary;
struct {
std::unordered_map<DefVoxelId, DefVoxel_t> DefVoxel;
std::unordered_map<DefNodeId, DefNode_t> DefNode;

View File

@@ -301,7 +301,7 @@ void AssetsManager::readWriteThread(AsyncUseControl::Lock lock) {
}
finded = true;
ReadyQueue.lock()->emplace_back(rk.Hash, PathFiles / hashKey.substr(0, 2) / hashKey.substr(2));
ReadyQueue.lock()->emplace_back(rk, PathFiles / hashKey.substr(0, 2) / hashKey.substr(2));
} else if(errc != SQLITE_DONE) {
sqlite3_reset(STMT_DISK_CONTAINS);
MAKE_ERROR("Не удалось выполнить подготовленный запрос STMT_DISK_CONTAINS: " << sqlite3_errmsg(DB));

View File

@@ -95,6 +95,7 @@ public:
Hash_t Hash;
EnumAssets Type;
std::string Domain, Key;
ResourceId Id;
};
public:
@@ -115,7 +116,7 @@ public:
}
// Получить считанные данные
std::vector<std::pair<Hash_t, std::optional<Resource>>> pullReads() {
std::vector<std::pair<ResourceKey, std::optional<Resource>>> pullReads() {
return std::move(*ReadyQueue.lock());
}
@@ -181,7 +182,7 @@ private:
// Очередь на запись ресурсов
TOS::SpinlockObject<std::queue<Resource>> WriteQueue;
// Очередь на выдачу результатов чтения
TOS::SpinlockObject<std::vector<std::pair<Hash_t, std::optional<Resource>>>> ReadyQueue;
TOS::SpinlockObject<std::vector<std::pair<ResourceKey, std::optional<Resource>>>> ReadyQueue;
struct Changes_t {
std::vector<fs::path> Assets;

View File

@@ -321,6 +321,147 @@ void ServerSession::onJoystick() {
}
void ServerSession::atFreeDrawTime(GlobalTime gTime, float dTime) {
// Оповещение модуля рендера об изменениях ресурсов
std::unordered_map<EnumAssets, std::unordered_map<ResourceId, AssetEntry>> changedResources;
std::unordered_map<EnumAssets, std::unordered_set<ResourceId>> lostResources;
// Обработка полученных ресурсов
if(!AsyncContext.LoadedAssets.get_read().empty()) {
std::vector<AssetEntry> assets = std::move(*AsyncContext.LoadedAssets.lock());
// Для сохранения ресурсов в кеше
std::vector<Resource> resources;
resources.reserve(assets.size());
for(AssetEntry& entry : assets) {
resources.push_back(entry.Res);
// Проверяем используется ли сейчас ресурс
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));
}
// Обработка полученных тактов
while(!AsyncContext.TickSequence.get_read().empty()) {
TickData tick;
{
auto lock = AsyncContext.TickSequence.lock();
tick = lock->front();
lock->pop();
}
// Потерянные привязки ресурсов
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) {
for(ResourceId id : tick.AssetsLost[type]) {
Assets.ExistBinds[type].erase(id);
changedResources[(EnumAssets) type].erase(id);
}
// Assets.ExistBinds[type].erase(tick.AssetsLost[type].begin(), tick.AssetsLost[type].end());
lostResources[(EnumAssets) type].insert_range(tick.AssetsLost[type]);
}
// Запрос к дисковому кешу
std::vector<AssetsManager::ResourceKey> needToLoad;
// Новые привязки ресурсов
for(const AssetBindEntry& bind : tick.AssetsBinds) {
Assets.ExistBinds[(int) bind.Type].insert(bind.Id);
// Проверить 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()) {
// Есть ресурс
Assets.InUse[(int) bind.Type][bind.Id] = std::get<0>(iter->second);
changedResources[bind.Type].insert({bind.Id, std::get<0>(iter->second)});
lostResources[bind.Type].erase(bind.Id);
continue;
}
}
// Под рукой нет ресурса, отправим на проверку в AssetsManager
needToLoad.emplace_back(bind.Hash, bind.Type, bind.Domain, bind.Key, bind.Id);
}
if(!needToLoad.empty())
AM->pushReads(std::move(needToLoad));
}
// Получаем ресурсы, загруженные с дискового кеша
{
std::vector<Hash_t> request;
std::vector<std::pair<AssetsManager::ResourceKey, std::optional<Resource>>> resources = AM->pullReads();
for(auto& [key, res] : resources) {
if(!res) {
// Нужно запросить ресурс с сервера
request.push_back(key.Hash);
} else {
auto& a = Assets.ExistBinds[(int) key.Type];
AssetEntry entry = {key.Type, key.Id, key.Domain, key.Key, *res};
if(a.contains(key.Id)) {
// Ресурс ещё нужен
Assets.InUse[(int) key.Type][key.Id] = entry;
changedResources[key.Type].insert({key.Id, entry});
} else {
// Ресурс уже не нужен
Assets.NotInUse[(int) key.Type][key.Domain + ':' + key.Key] = {entry, TIME_BEFORE_UNLOAD_RESOURCE+time(nullptr)};
}
}
}
if(!request.empty()) {
assert(request.size() < (1 << 16));
Net::Packet p;
p << (uint8_t) ToServer::L1::System << (uint8_t) ToServer::L2System::ResourceRequest
<< uint16_t(request.size());
for(Hash_t& hash : request)
p.write((const std::byte*) hash.data(), 32);
Socket->pushPacket(std::move(p));
}
}
if(RS) {
// Уведомляем рендер опотерянных и изменённых ресурсах
if(!lostResources.empty()) {
std::unordered_map<EnumAssets, std::vector<ResourceId>> lostResources2;
for(auto& [type, list] : lostResources)
lostResources2[type].append_range(list);
lostResources.clear();
RS->onAssetsLost(std::move(lostResources2));
}
if(!changedResources.empty()) {
std::unordered_map<EnumAssets, std::vector<AssetEntry>> changedResources2;
for(auto& [type, list] : changedResources) {
auto& a = changedResources2[type];
for(auto& [key, val] : list)
a.push_back(val);
}
changedResources.clear();
RS->onAssetsChanges(std::move(changedResources2));
}
}
GTime = gTime;
Pos += glm::vec3(Speed) * dTime;
@@ -539,6 +680,9 @@ coro<> ServerSession::rP_System(Net::AsyncSocket &sock) {
co_return;
case ToClient::L2System::UnlinkCamera:
co_return;
case ToClient::L2System::SyncTick:
AsyncContext.TickSequence.lock()->push(std::move(AsyncContext.ThisTickEntry));
co_return;
default:
protocolError();
@@ -572,13 +716,12 @@ coro<> ServerSession::rP_Resource(Net::AsyncSocket &sock) {
case ToClient::L2Resource::Lost:
{
uint32_t count = co_await sock.read<uint32_t>();
AsyncContext.ThisTickEntry.AssetsLost.reserve(AsyncContext.ThisTickEntry.AssetsLost.size()+count);
for(size_t iter = 0; iter < count; iter++) {
// uint8_t type = co_await sock.read<uint8_t>();
uint8_t type = co_await sock.read<uint8_t>();
uint32_t id = co_await sock.read<uint32_t>();
AsyncContext.ThisTickEntry.AssetsLost.push_back(id);
AsyncContext.ThisTickEntry.AssetsLost[(int) type].push_back(id);
}
}
case ToClient::L2Resource::InitResSend:

View File

@@ -76,6 +76,16 @@ private:
// Обработчик кеша ресурсов сервера
AssetsManager::Ptr AM;
static constexpr uint64_t TIME_BEFORE_UNLOAD_RESOURCE = 180;
struct {
// Существующие привязки ресурсов
std::unordered_set<ResourceId> ExistBinds[(int) EnumAssets::MAX_ENUM];
// Используемые в данных момент ресурсы (определяется по действующей привязке)
std::unordered_map<ResourceId, AssetEntry> InUse[(int) EnumAssets::MAX_ENUM];
// Недавно использованные ресурсы, пока хранятся здесь в течении TIME_BEFORE_UNLOAD_RESOURCE секунд
std::unordered_map<std::string, std::pair<AssetEntry, uint64_t>> NotInUse[(int) EnumAssets::MAX_ENUM];
} Assets;
struct AssetLoading {
EnumAssets Type;
ResourceId Id;
@@ -95,10 +105,10 @@ private:
std::vector<WorldId_t> LostWorld;
// std::vector<std::pair<WorldId_t, DefWorld>>
// Потерянные из видимости ресурсы
std::vector<ResourceId> AssetsLost;
// Новые привязки ресурсов
std::vector<AssetBindEntry> AssetsBinds;
// Потерянные из видимости ресурсы
std::vector<ResourceId> AssetsLost[(int) EnumAssets::MAX_ENUM];
};
struct {
@@ -111,8 +121,6 @@ private:
TickData ThisTickEntry;
// Обменный пункт
// Привязки ресурсов
TOS::SpinlockObject<std::vector<AssetEntry>> AssetsBindings;
// Полученные ресурсы с сервера
TOS::SpinlockObject<std::vector<AssetEntry>> LoadedAssets;
// Пакеты обновлений игрового мира

View File

@@ -987,7 +987,11 @@ void VulkanRenderSession::init(Vulkan *instance) {
}
}
void VulkanRenderSession::onBinaryResourceAdd(std::vector<Hash_t>) {
void VulkanRenderSession::onAssetsChanges(std::unordered_map<EnumAssets, std::vector<AssetEntry>> resources) {
}
void VulkanRenderSession::onAssetsLost(std::unordered_map<EnumAssets, std::vector<ResourceId>> resources) {
}

View File

@@ -340,7 +340,8 @@ public:
assert(serverSession);
}
virtual void onBinaryResourceAdd(std::vector<Hash_t>) override;
virtual void onAssetsChanges(std::unordered_map<EnumAssets, std::vector<AssetEntry>> resources) override;
virtual void onAssetsLost(std::unordered_map<EnumAssets, std::vector<ResourceId>> resources) override;
virtual void onContentDefinesAdd(std::unordered_map<EnumDefContent, std::vector<ResourceId>>) override;
virtual void onContentDefinesLost(std::unordered_map<EnumDefContent, std::vector<ResourceId>>) override;
virtual void onChunksChange(WorldId_t worldId, const std::unordered_set<Pos::GlobalChunk>& changeOrAddList, const std::unordered_set<Pos::GlobalRegion>& remove) override;