Подстройки кодекса
This commit is contained in:
@@ -13,8 +13,8 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -DGL
|
|||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") # -rdynamic
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") # -rdynamic
|
||||||
|
|
||||||
# gprof
|
# gprof
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg")
|
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg")
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg")
|
# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg")
|
||||||
|
|
||||||
# sanitizer
|
# sanitizer
|
||||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||||
|
|||||||
@@ -163,6 +163,8 @@ struct AssetEntry {
|
|||||||
ResourceId Id;
|
ResourceId Id;
|
||||||
std::string Domain, Key;
|
std::string Domain, Key;
|
||||||
Resource Res;
|
Resource Res;
|
||||||
|
Hash_t Hash = {};
|
||||||
|
std::vector<uint8_t> Dependencies;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ AssetsManager::AssetsManager(boost::asio::io_context &ioc, const fs::path &cache
|
|||||||
size_t maxCacheDirectorySize, size_t maxLifeTime)
|
size_t maxCacheDirectorySize, size_t maxLifeTime)
|
||||||
: IAsyncDestructible(ioc), CachePath(cachePath)
|
: IAsyncDestructible(ioc), CachePath(cachePath)
|
||||||
{
|
{
|
||||||
|
NextId.fill(0);
|
||||||
{
|
{
|
||||||
auto lock = Changes.lock();
|
auto lock = Changes.lock();
|
||||||
lock->MaxCacheDatabaseSize = maxCacheDirectorySize;
|
lock->MaxCacheDatabaseSize = maxCacheDirectorySize;
|
||||||
@@ -172,6 +173,212 @@ AssetsManager::~AssetsManager() {
|
|||||||
LOG.info() << "Хранилище кеша закрыто";
|
LOG.info() << "Хранилище кеша закрыто";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResourceId AssetsManager::getId(EnumAssets type, const std::string& domain, const std::string& key) {
|
||||||
|
std::lock_guard lock(MapMutex);
|
||||||
|
auto& typeTable = DKToId[type];
|
||||||
|
auto& domainTable = typeTable[domain];
|
||||||
|
if(auto iter = domainTable.find(key); iter != domainTable.end())
|
||||||
|
return iter->second;
|
||||||
|
|
||||||
|
ResourceId id = NextId[(int) type]++;
|
||||||
|
domainTable[key] = id;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ResourceId> AssetsManager::getLocalIdFromServer(EnumAssets type, ResourceId serverId) const {
|
||||||
|
std::lock_guard lock(MapMutex);
|
||||||
|
auto iterType = ServerToLocal.find(type);
|
||||||
|
if(iterType == ServerToLocal.end())
|
||||||
|
return std::nullopt;
|
||||||
|
auto iter = iterType->second.find(serverId);
|
||||||
|
if(iter == iterType->second.end())
|
||||||
|
return std::nullopt;
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AssetsManager::BindInfo* AssetsManager::getBind(EnumAssets type, ResourceId localId) const {
|
||||||
|
std::lock_guard lock(MapMutex);
|
||||||
|
auto iterType = LocalBinds.find(type);
|
||||||
|
if(iterType == LocalBinds.end())
|
||||||
|
return nullptr;
|
||||||
|
auto iter = iterType->second.find(localId);
|
||||||
|
if(iter == iterType->second.end())
|
||||||
|
return nullptr;
|
||||||
|
return &iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetsManager::BindResult AssetsManager::bindServerResource(EnumAssets type, ResourceId serverId, const std::string& domain,
|
||||||
|
const std::string& key, const Hash_t& hash, std::vector<uint8_t> header)
|
||||||
|
{
|
||||||
|
BindResult result;
|
||||||
|
result.LocalId = getId(type, domain, key);
|
||||||
|
|
||||||
|
std::lock_guard lock(MapMutex);
|
||||||
|
ServerToLocal[type][serverId] = result.LocalId;
|
||||||
|
|
||||||
|
auto& binds = LocalBinds[type];
|
||||||
|
auto iter = binds.find(result.LocalId);
|
||||||
|
if(iter == binds.end()) {
|
||||||
|
result.Changed = true;
|
||||||
|
binds.emplace(result.LocalId, BindInfo{
|
||||||
|
.LocalId = result.LocalId,
|
||||||
|
.ServerId = serverId,
|
||||||
|
.Domain = domain,
|
||||||
|
.Key = key,
|
||||||
|
.Hash = hash,
|
||||||
|
.Header = std::move(header)
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
BindInfo& info = iter->second;
|
||||||
|
bool hashChanged = info.Hash != hash;
|
||||||
|
bool headerChanged = info.Header != header;
|
||||||
|
result.Changed = hashChanged || headerChanged || info.ServerId != serverId;
|
||||||
|
info.ServerId = serverId;
|
||||||
|
info.Domain = domain;
|
||||||
|
info.Key = key;
|
||||||
|
info.Hash = hash;
|
||||||
|
info.Header = std::move(header);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ResourceId> AssetsManager::unbindServerResource(EnumAssets type, ResourceId serverId) {
|
||||||
|
std::lock_guard lock(MapMutex);
|
||||||
|
auto iterType = ServerToLocal.find(type);
|
||||||
|
if(iterType == ServerToLocal.end())
|
||||||
|
return std::nullopt;
|
||||||
|
auto iter = iterType->second.find(serverId);
|
||||||
|
if(iter == iterType->second.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
ResourceId localId = iter->second;
|
||||||
|
iterType->second.erase(iter);
|
||||||
|
|
||||||
|
auto iterBindType = LocalBinds.find(type);
|
||||||
|
if(iterBindType != LocalBinds.end())
|
||||||
|
iterBindType->second.erase(localId);
|
||||||
|
|
||||||
|
return localId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetsManager::clearServerBindings() {
|
||||||
|
std::lock_guard lock(MapMutex);
|
||||||
|
ServerToLocal.clear();
|
||||||
|
LocalBinds.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<AssetsManager::ParsedHeader> AssetsManager::parseHeader(const std::vector<uint8_t>& data) {
|
||||||
|
size_t pos = 0;
|
||||||
|
auto readU8 = [&](uint8_t& out) -> bool {
|
||||||
|
if(pos + 1 > data.size())
|
||||||
|
return false;
|
||||||
|
out = data[pos++];
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
auto readU32 = [&](uint32_t& out) -> bool {
|
||||||
|
if(pos + 4 > data.size())
|
||||||
|
return false;
|
||||||
|
out = uint32_t(data[pos]) |
|
||||||
|
(uint32_t(data[pos + 1]) << 8) |
|
||||||
|
(uint32_t(data[pos + 2]) << 16) |
|
||||||
|
(uint32_t(data[pos + 3]) << 24);
|
||||||
|
pos += 4;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
ParsedHeader out;
|
||||||
|
uint8_t c0, c1, version, type;
|
||||||
|
if(!readU8(c0) || !readU8(c1) || !readU8(version) || !readU8(type))
|
||||||
|
return std::nullopt;
|
||||||
|
if(c0 != 'a' || c1 != 'h' || version != 1)
|
||||||
|
return std::nullopt;
|
||||||
|
out.Type = static_cast<EnumAssets>(type);
|
||||||
|
|
||||||
|
uint32_t count = 0;
|
||||||
|
if(!readU32(count))
|
||||||
|
return std::nullopt;
|
||||||
|
out.ModelDeps.reserve(count);
|
||||||
|
for(uint32_t i = 0; i < count; i++) {
|
||||||
|
uint32_t id;
|
||||||
|
if(!readU32(id))
|
||||||
|
return std::nullopt;
|
||||||
|
out.ModelDeps.push_back(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!readU32(count))
|
||||||
|
return std::nullopt;
|
||||||
|
out.TextureDeps.reserve(count);
|
||||||
|
for(uint32_t i = 0; i < count; i++) {
|
||||||
|
uint32_t id;
|
||||||
|
if(!readU32(id))
|
||||||
|
return std::nullopt;
|
||||||
|
out.TextureDeps.push_back(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t extraSize = 0;
|
||||||
|
if(!readU32(extraSize))
|
||||||
|
return std::nullopt;
|
||||||
|
if(pos + extraSize > data.size())
|
||||||
|
return std::nullopt;
|
||||||
|
out.Extra.assign(data.begin() + pos, data.begin() + pos + extraSize);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> AssetsManager::buildHeader(EnumAssets type, const std::vector<uint32_t>& modelDeps,
|
||||||
|
const std::vector<uint32_t>& textureDeps, const std::vector<uint8_t>& extra)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
data.reserve(4 + 4 + modelDeps.size() * 4 + 4 + textureDeps.size() * 4 + 4 + extra.size());
|
||||||
|
data.push_back('a');
|
||||||
|
data.push_back('h');
|
||||||
|
data.push_back(1);
|
||||||
|
data.push_back(static_cast<uint8_t>(type));
|
||||||
|
|
||||||
|
auto writeU32 = [&](uint32_t value) {
|
||||||
|
data.push_back(uint8_t(value & 0xff));
|
||||||
|
data.push_back(uint8_t((value >> 8) & 0xff));
|
||||||
|
data.push_back(uint8_t((value >> 16) & 0xff));
|
||||||
|
data.push_back(uint8_t((value >> 24) & 0xff));
|
||||||
|
};
|
||||||
|
|
||||||
|
writeU32(static_cast<uint32_t>(modelDeps.size()));
|
||||||
|
for(uint32_t id : modelDeps)
|
||||||
|
writeU32(id);
|
||||||
|
|
||||||
|
writeU32(static_cast<uint32_t>(textureDeps.size()));
|
||||||
|
for(uint32_t id : textureDeps)
|
||||||
|
writeU32(id);
|
||||||
|
|
||||||
|
writeU32(static_cast<uint32_t>(extra.size()));
|
||||||
|
if(!extra.empty())
|
||||||
|
data.insert(data.end(), extra.begin(), extra.end());
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> AssetsManager::rebindHeader(const std::vector<uint8_t>& header) const {
|
||||||
|
auto parsed = parseHeader(header);
|
||||||
|
if(!parsed)
|
||||||
|
return header;
|
||||||
|
|
||||||
|
std::vector<uint32_t> modelDeps;
|
||||||
|
modelDeps.reserve(parsed->ModelDeps.size());
|
||||||
|
for(uint32_t serverId : parsed->ModelDeps) {
|
||||||
|
auto localId = getLocalIdFromServer(EnumAssets::Model, serverId);
|
||||||
|
modelDeps.push_back(localId.value_or(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> textureDeps;
|
||||||
|
textureDeps.reserve(parsed->TextureDeps.size());
|
||||||
|
for(uint32_t serverId : parsed->TextureDeps) {
|
||||||
|
auto localId = getLocalIdFromServer(EnumAssets::Texture, serverId);
|
||||||
|
textureDeps.push_back(localId.value_or(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildHeader(parsed->Type, modelDeps, textureDeps, parsed->Extra);
|
||||||
|
}
|
||||||
|
|
||||||
coro<> AssetsManager::asyncDestructor() {
|
coro<> AssetsManager::asyncDestructor() {
|
||||||
NeedShutdown = true;
|
NeedShutdown = true;
|
||||||
co_await IAsyncDestructible::asyncDestructor();
|
co_await IAsyncDestructible::asyncDestructor();
|
||||||
|
|||||||
@@ -1,17 +1,22 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Common/Abstract.hpp"
|
#include "Common/Abstract.hpp"
|
||||||
|
#include <array>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
#include <TOSLib.hpp>
|
#include <TOSLib.hpp>
|
||||||
#include <TOSAsync.hpp>
|
#include <TOSAsync.hpp>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
namespace LV::Client {
|
namespace LV::Client {
|
||||||
@@ -91,6 +96,22 @@ class AssetsManager : public IAsyncDestructible {
|
|||||||
public:
|
public:
|
||||||
using Ptr = std::shared_ptr<AssetsManager>;
|
using Ptr = std::shared_ptr<AssetsManager>;
|
||||||
|
|
||||||
|
struct ParsedHeader {
|
||||||
|
EnumAssets Type = EnumAssets::MAX_ENUM;
|
||||||
|
std::vector<uint32_t> ModelDeps;
|
||||||
|
std::vector<uint32_t> TextureDeps;
|
||||||
|
std::vector<uint8_t> Extra;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BindInfo {
|
||||||
|
ResourceId LocalId = 0;
|
||||||
|
ResourceId ServerId = 0;
|
||||||
|
std::string Domain;
|
||||||
|
std::string Key;
|
||||||
|
Hash_t Hash = {};
|
||||||
|
std::vector<uint8_t> Header;
|
||||||
|
};
|
||||||
|
|
||||||
struct ResourceKey {
|
struct ResourceKey {
|
||||||
Hash_t Hash;
|
Hash_t Hash;
|
||||||
EnumAssets Type;
|
EnumAssets Type;
|
||||||
@@ -98,6 +119,11 @@ public:
|
|||||||
ResourceId Id;
|
ResourceId Id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BindResult {
|
||||||
|
ResourceId LocalId = 0;
|
||||||
|
bool Changed = false;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~AssetsManager();
|
virtual ~AssetsManager();
|
||||||
static std::shared_ptr<AssetsManager> Create(asio::io_context &ioc, const fs::path& cachePath,
|
static std::shared_ptr<AssetsManager> Create(asio::io_context &ioc, const fs::path& cachePath,
|
||||||
@@ -151,6 +177,20 @@ public:
|
|||||||
return IssuedAnError;
|
return IssuedAnError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Получить или создать локальный идентификатор ресурса
|
||||||
|
ResourceId getId(EnumAssets type, const std::string& domain, const std::string& key);
|
||||||
|
std::optional<ResourceId> getLocalIdFromServer(EnumAssets type, ResourceId serverId) const;
|
||||||
|
const BindInfo* getBind(EnumAssets type, ResourceId localId) const;
|
||||||
|
BindResult bindServerResource(EnumAssets type, ResourceId serverId, const std::string& domain, const std::string& key,
|
||||||
|
const Hash_t& hash, std::vector<uint8_t> header);
|
||||||
|
std::optional<ResourceId> unbindServerResource(EnumAssets type, ResourceId serverId);
|
||||||
|
void clearServerBindings();
|
||||||
|
|
||||||
|
static std::optional<ParsedHeader> parseHeader(const std::vector<uint8_t>& data);
|
||||||
|
static std::vector<uint8_t> buildHeader(EnumAssets type, const std::vector<uint32_t>& modelDeps,
|
||||||
|
const std::vector<uint32_t>& textureDeps, const std::vector<uint8_t>& extra);
|
||||||
|
std::vector<uint8_t> rebindHeader(const std::vector<uint8_t>& header) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Logger LOG = "Client>ResourceHandler";
|
Logger LOG = "Client>ResourceHandler";
|
||||||
const fs::path
|
const fs::path
|
||||||
@@ -197,6 +237,11 @@ private:
|
|||||||
|
|
||||||
bool NeedShutdown = false, IssuedAnError = false;
|
bool NeedShutdown = false, IssuedAnError = false;
|
||||||
std::thread OffThread;
|
std::thread OffThread;
|
||||||
|
mutable std::mutex MapMutex;
|
||||||
|
std::unordered_map<EnumAssets, std::unordered_map<std::string, std::unordered_map<std::string, ResourceId>>> DKToId;
|
||||||
|
std::unordered_map<EnumAssets, std::unordered_map<ResourceId, ResourceId>> ServerToLocal;
|
||||||
|
std::unordered_map<EnumAssets, std::unordered_map<ResourceId, BindInfo>> LocalBinds;
|
||||||
|
std::array<ResourceId, (int) EnumAssets::MAX_ENUM> NextId = {};
|
||||||
|
|
||||||
|
|
||||||
virtual coro<> asyncDestructor();
|
virtual coro<> asyncDestructor();
|
||||||
@@ -207,4 +252,4 @@ private:
|
|||||||
std::string hashToString(const Hash_t& hash);
|
std::string hashToString(const Hash_t& hash);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -358,6 +358,12 @@ void ServerSession::update(GlobalTime gTime, float dTime) {
|
|||||||
resources.reserve(assets.size());
|
resources.reserve(assets.size());
|
||||||
|
|
||||||
for(AssetEntry& entry : assets) {
|
for(AssetEntry& entry : assets) {
|
||||||
|
entry.Hash = entry.Res.hash();
|
||||||
|
if(const AssetsManager::BindInfo* bind = AM->getBind(entry.Type, entry.Id))
|
||||||
|
entry.Dependencies = AM->rebindHeader(bind->Header);
|
||||||
|
else
|
||||||
|
entry.Dependencies.clear();
|
||||||
|
|
||||||
resources.push_back(entry.Res);
|
resources.push_back(entry.Res);
|
||||||
AsyncContext.LoadedResources.emplace_back(std::move(entry));
|
AsyncContext.LoadedResources.emplace_back(std::move(entry));
|
||||||
|
|
||||||
@@ -430,12 +436,27 @@ void ServerSession::update(GlobalTime gTime, float dTime) {
|
|||||||
needRequest.push_back(key.Hash);
|
needRequest.push_back(key.Hash);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Hash_t actualHash = res->hash();
|
||||||
|
if(actualHash != key.Hash) {
|
||||||
|
auto iter = std::lower_bound(AsyncContext.AlreadyLoading.begin(), AsyncContext.AlreadyLoading.end(), key.Hash);
|
||||||
|
if(iter == AsyncContext.AlreadyLoading.end() || *iter != key.Hash) {
|
||||||
|
AsyncContext.AlreadyLoading.insert(iter, key.Hash);
|
||||||
|
needRequest.push_back(key.Hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> deps;
|
||||||
|
if(const AssetsManager::BindInfo* bind = AM->getBind(key.Type, key.Id))
|
||||||
|
deps = AM->rebindHeader(bind->Header);
|
||||||
|
|
||||||
AssetEntry entry {
|
AssetEntry entry {
|
||||||
.Type = key.Type,
|
.Type = key.Type,
|
||||||
.Id = key.Id,
|
.Id = key.Id,
|
||||||
.Domain = key.Domain,
|
.Domain = key.Domain,
|
||||||
.Key = key.Key,
|
.Key = key.Key,
|
||||||
.Res = *res
|
.Res = std::move(*res),
|
||||||
|
.Hash = actualHash,
|
||||||
|
.Dependencies = std::move(deps)
|
||||||
};
|
};
|
||||||
|
|
||||||
AsyncContext.LoadedResources.emplace_back(std::move(entry));
|
AsyncContext.LoadedResources.emplace_back(std::move(entry));
|
||||||
@@ -529,7 +550,7 @@ void ServerSession::update(GlobalTime gTime, float dTime) {
|
|||||||
auto iter = niubdk.find(dk);
|
auto iter = niubdk.find(dk);
|
||||||
if(iter != niubdk.end()) {
|
if(iter != niubdk.end()) {
|
||||||
// Есть ресурс
|
// Есть ресурс
|
||||||
needQuery = false;
|
needQuery = iter->second.first.Hash != bind.Hash;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -951,12 +972,21 @@ void ServerSession::update(GlobalTime gTime, float dTime) {
|
|||||||
AsyncContext.Binds.clear();
|
AsyncContext.Binds.clear();
|
||||||
|
|
||||||
for(AssetBindEntry& entry : abc.Binds) {
|
for(AssetBindEntry& entry : abc.Binds) {
|
||||||
|
std::vector<uint8_t> deps;
|
||||||
|
if(!entry.Header.empty())
|
||||||
|
deps = AM->rebindHeader(entry.Header);
|
||||||
|
|
||||||
MyAssets.ExistBinds[(int) entry.Type].insert(entry.Id);
|
MyAssets.ExistBinds[(int) entry.Type].insert(entry.Id);
|
||||||
result.Assets_ChangeOrAdd[entry.Type].push_back(entry.Id);
|
result.Assets_ChangeOrAdd[entry.Type].push_back(entry.Id);
|
||||||
|
|
||||||
|
auto iterLoaded = IServerSession::Assets[entry.Type].find(entry.Id);
|
||||||
|
if(iterLoaded != IServerSession::Assets[entry.Type].end())
|
||||||
|
iterLoaded->second.Dependencies = deps;
|
||||||
|
|
||||||
// Если ресурс был в кеше, то достаётся от туда
|
// Если ресурс был в кеше, то достаётся от туда
|
||||||
auto iter = MyAssets.NotInUse[(int) entry.Type].find(entry.Domain+':'+entry.Key);
|
auto iter = MyAssets.NotInUse[(int) entry.Type].find(entry.Domain+':'+entry.Key);
|
||||||
if(iter != MyAssets.NotInUse[(int) entry.Type].end()) {
|
if(iter != MyAssets.NotInUse[(int) entry.Type].end()) {
|
||||||
|
iter->second.first.Dependencies = deps;
|
||||||
IServerSession::Assets[entry.Type][entry.Id] = std::get<0>(iter->second);
|
IServerSession::Assets[entry.Type][entry.Id] = std::get<0>(iter->second);
|
||||||
result.Assets_ChangeOrAdd[entry.Type].push_back(entry.Id);
|
result.Assets_ChangeOrAdd[entry.Type].push_back(entry.Id);
|
||||||
MyAssets.NotInUse[(int) entry.Type].erase(iter);
|
MyAssets.NotInUse[(int) entry.Type].erase(iter);
|
||||||
@@ -1196,6 +1226,7 @@ void ServerSession::setRenderSession(IRenderSession* session) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ServerSession::resetResourceSyncState() {
|
void ServerSession::resetResourceSyncState() {
|
||||||
|
AM->clearServerBindings();
|
||||||
AsyncContext.AssetsLoading.clear();
|
AsyncContext.AssetsLoading.clear();
|
||||||
AsyncContext.AlreadyLoading.clear();
|
AsyncContext.AlreadyLoading.clear();
|
||||||
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++)
|
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++)
|
||||||
@@ -1300,11 +1331,27 @@ coro<> ServerSession::rP_Resource(Net::AsyncSocket &sock) {
|
|||||||
key = co_await sock.read<std::string>();
|
key = co_await sock.read<std::string>();
|
||||||
Hash_t hash;
|
Hash_t hash;
|
||||||
co_await sock.read((std::byte*) hash.data(), hash.size());
|
co_await sock.read((std::byte*) hash.data(), hash.size());
|
||||||
|
uint32_t headerSize = co_await sock.read<uint32_t>();
|
||||||
|
std::vector<uint8_t> header;
|
||||||
|
if(headerSize > 0) {
|
||||||
|
header.resize(headerSize);
|
||||||
|
co_await sock.read((std::byte*) header.data(), header.size());
|
||||||
|
}
|
||||||
|
|
||||||
binds.emplace_back(
|
AssetsManager::BindResult bindResult = AM->bindServerResource(
|
||||||
(EnumAssets) type, (ResourceId) id, std::move(domain),
|
(EnumAssets) type, (ResourceId) id, domain, key, hash, header);
|
||||||
std::move(key), hash
|
|
||||||
);
|
if(!bindResult.Changed)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
binds.emplace_back(AssetBindEntry{
|
||||||
|
.Type = (EnumAssets) type,
|
||||||
|
.Id = bindResult.LocalId,
|
||||||
|
.Domain = std::move(domain),
|
||||||
|
.Key = std::move(key),
|
||||||
|
.Hash = hash,
|
||||||
|
.Header = std::move(header)
|
||||||
|
});
|
||||||
|
|
||||||
if(binds.back().Domain == "test"
|
if(binds.back().Domain == "test"
|
||||||
&& (binds.back().Type == EnumAssets::Nodestate
|
&& (binds.back().Type == EnumAssets::Nodestate
|
||||||
@@ -1339,7 +1386,11 @@ coro<> ServerSession::rP_Resource(Net::AsyncSocket &sock) {
|
|||||||
if(type >= (int) EnumAssets::MAX_ENUM)
|
if(type >= (int) EnumAssets::MAX_ENUM)
|
||||||
protocolError();
|
protocolError();
|
||||||
|
|
||||||
abc.Lost[(int) type].push_back(id);
|
auto localId = AM->unbindServerResource((EnumAssets) type, id);
|
||||||
|
if(!localId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
abc.Lost[(int) type].push_back(*localId);
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncContext.AssetsBinds.lock()->emplace_back(std::move(abc));
|
AsyncContext.AssetsBinds.lock()->emplace_back(std::move(abc));
|
||||||
@@ -1358,6 +1409,13 @@ coro<> ServerSession::rP_Resource(Net::AsyncSocket &sock) {
|
|||||||
|
|
||||||
std::string domain = co_await sock.read<std::string>();
|
std::string domain = co_await sock.read<std::string>();
|
||||||
std::string key = co_await sock.read<std::string>();
|
std::string key = co_await sock.read<std::string>();
|
||||||
|
ResourceId localId = 0;
|
||||||
|
if(auto mapped = AM->getLocalIdFromServer(type, id)) {
|
||||||
|
localId = *mapped;
|
||||||
|
} else {
|
||||||
|
localId = AM->getId(type, domain, key);
|
||||||
|
AM->bindServerResource(type, id, domain, key, hash, {});
|
||||||
|
}
|
||||||
|
|
||||||
if(domain == "test"
|
if(domain == "test"
|
||||||
&& (type == EnumAssets::Nodestate
|
&& (type == EnumAssets::Nodestate
|
||||||
@@ -1367,14 +1425,14 @@ coro<> ServerSession::rP_Resource(Net::AsyncSocket &sock) {
|
|||||||
uint32_t idx = debugResourceLogCount.fetch_add(1);
|
uint32_t idx = debugResourceLogCount.fetch_add(1);
|
||||||
if(idx < 128) {
|
if(idx < 128) {
|
||||||
LOG.debug() << "InitResSend type=" << assetTypeName(type)
|
LOG.debug() << "InitResSend type=" << assetTypeName(type)
|
||||||
<< " id=" << id
|
<< " id=" << localId
|
||||||
<< " key=" << domain << ':' << key
|
<< " key=" << domain << ':' << key
|
||||||
<< " size=" << size;
|
<< " size=" << size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncContext.AssetsLoading[hash] = AssetLoading{
|
AsyncContext.AssetsLoading[hash] = AssetLoading{
|
||||||
type, id, std::move(domain), std::move(key),
|
type, localId, std::move(domain), std::move(key),
|
||||||
std::u8string(size, '\0'), 0
|
std::u8string(size, '\0'), 0
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1410,9 +1468,14 @@ coro<> ServerSession::rP_Resource(Net::AsyncSocket &sock) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncContext.LoadedAssets.lock()->emplace_back(
|
AsyncContext.LoadedAssets.lock()->emplace_back(AssetEntry{
|
||||||
al.Type, al.Id, std::move(al.Domain), std::move(al.Key), std::move(al.Data)
|
.Type = al.Type,
|
||||||
);
|
.Id = al.Id,
|
||||||
|
.Domain = std::move(al.Domain),
|
||||||
|
.Key = std::move(al.Key),
|
||||||
|
.Res = std::move(al.Data),
|
||||||
|
.Hash = hash
|
||||||
|
});
|
||||||
|
|
||||||
AsyncContext.AssetsLoading.erase(AsyncContext.AssetsLoading.find(hash));
|
AsyncContext.AssetsLoading.erase(AsyncContext.AssetsLoading.find(hash));
|
||||||
|
|
||||||
@@ -1461,7 +1524,11 @@ coro<> ServerSession::rP_Definition(Net::AsyncSocket &sock) {
|
|||||||
{
|
{
|
||||||
DefNode_t def;
|
DefNode_t def;
|
||||||
DefNodeId id = co_await sock.read<DefNodeId>();
|
DefNodeId id = co_await sock.read<DefNodeId>();
|
||||||
def.NodestateId = co_await sock.read<uint32_t>();
|
ResourceId serverNodestate = co_await sock.read<uint32_t>();
|
||||||
|
if(auto localId = AM->getLocalIdFromServer(EnumAssets::Nodestate, serverNodestate))
|
||||||
|
def.NodestateId = *localId;
|
||||||
|
else
|
||||||
|
def.NodestateId = 0;
|
||||||
def.TexId = id;
|
def.TexId = id;
|
||||||
|
|
||||||
if(id < 32) {
|
if(id < 32) {
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ private:
|
|||||||
ResourceId Id;
|
ResourceId Id;
|
||||||
std::string Domain, Key;
|
std::string Domain, Key;
|
||||||
Hash_t Hash;
|
Hash_t Hash;
|
||||||
|
std::vector<uint8_t> Header;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TickData {
|
struct TickData {
|
||||||
|
|||||||
@@ -1648,7 +1648,7 @@ void VulkanRenderSession::tickSync(const TickSyncData& data) {
|
|||||||
if(auto iter = data.Profiles_Lost.find(EnumDefContent::Voxel); iter != data.Profiles_Lost.end())
|
if(auto iter = data.Profiles_Lost.find(EnumDefContent::Voxel); iter != data.Profiles_Lost.end())
|
||||||
mcpData.ChangedVoxels.insert(mcpData.ChangedVoxels.end(), iter->second.begin(), iter->second.end());
|
mcpData.ChangedVoxels.insert(mcpData.ChangedVoxels.end(), iter->second.begin(), iter->second.end());
|
||||||
|
|
||||||
std::vector<std::tuple<AssetsModel, Resource>> modelResources;
|
std::vector<std::tuple<AssetsModel, Resource, const std::vector<uint8_t>*>> modelResources;
|
||||||
std::vector<AssetsModel> modelLost;
|
std::vector<AssetsModel> modelLost;
|
||||||
if(auto iter = data.Assets_ChangeOrAdd.find(EnumAssets::Model); iter != data.Assets_ChangeOrAdd.end()) {
|
if(auto iter = data.Assets_ChangeOrAdd.find(EnumAssets::Model); iter != data.Assets_ChangeOrAdd.end()) {
|
||||||
const auto& list = ServerSession->Assets[EnumAssets::Model];
|
const auto& list = ServerSession->Assets[EnumAssets::Model];
|
||||||
@@ -1657,7 +1657,7 @@ void VulkanRenderSession::tickSync(const TickSyncData& data) {
|
|||||||
if(entryIter == list.end())
|
if(entryIter == list.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
modelResources.emplace_back(id, entryIter->second.Res);
|
modelResources.emplace_back(id, entryIter->second.Res, &entryIter->second.Dependencies);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(auto iter = data.Assets_Lost.find(EnumAssets::Model); iter != data.Assets_Lost.end())
|
if(auto iter = data.Assets_Lost.find(EnumAssets::Model); iter != data.Assets_Lost.end())
|
||||||
@@ -1698,7 +1698,7 @@ void VulkanRenderSession::tickSync(const TickSyncData& data) {
|
|||||||
|
|
||||||
std::vector<AssetsNodestate> changedNodestates;
|
std::vector<AssetsNodestate> changedNodestates;
|
||||||
if(NSP) {
|
if(NSP) {
|
||||||
std::vector<std::tuple<AssetsNodestate, Resource>> nodestateResources;
|
std::vector<std::tuple<AssetsNodestate, Resource, const std::vector<uint8_t>*>> nodestateResources;
|
||||||
std::vector<AssetsNodestate> nodestateLost;
|
std::vector<AssetsNodestate> nodestateLost;
|
||||||
|
|
||||||
if(auto iter = data.Assets_ChangeOrAdd.find(EnumAssets::Nodestate); iter != data.Assets_ChangeOrAdd.end()) {
|
if(auto iter = data.Assets_ChangeOrAdd.find(EnumAssets::Nodestate); iter != data.Assets_ChangeOrAdd.end()) {
|
||||||
@@ -1708,7 +1708,7 @@ void VulkanRenderSession::tickSync(const TickSyncData& data) {
|
|||||||
if(entryIter == list.end())
|
if(entryIter == list.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
nodestateResources.emplace_back(id, entryIter->second.Res);
|
nodestateResources.emplace_back(id, entryIter->second.Res, &entryIter->second.Dependencies);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Client/AssetsManager.hpp"
|
||||||
#include "Client/Abstract.hpp"
|
#include "Client/Abstract.hpp"
|
||||||
#include "Common/Abstract.hpp"
|
#include "Common/Abstract.hpp"
|
||||||
#include <Client/Vulkan/Vulkan.hpp>
|
#include <Client/Vulkan/Vulkan.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -85,7 +87,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Применяет изменения, возвращая все затронутые модели
|
// Применяет изменения, возвращая все затронутые модели
|
||||||
std::vector<AssetsModel> onModelChanges(std::vector<std::tuple<AssetsModel, Resource>> newOrChanged,
|
std::vector<AssetsModel> onModelChanges(std::vector<std::tuple<AssetsModel, Resource, const std::vector<uint8_t>*>> newOrChanged,
|
||||||
std::vector<AssetsModel> lost,
|
std::vector<AssetsModel> lost,
|
||||||
const std::unordered_map<ResourceId, AssetEntry>* modelAssets) {
|
const std::unordered_map<ResourceId, AssetEntry>* modelAssets) {
|
||||||
std::vector<AssetsModel> result;
|
std::vector<AssetsModel> result;
|
||||||
@@ -140,12 +142,39 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const auto& [key, resource] : newOrChanged) {
|
for(const auto& [key, resource, deps] : newOrChanged) {
|
||||||
result.push_back(key);
|
result.push_back(key);
|
||||||
|
|
||||||
makeUnready(key);
|
makeUnready(key);
|
||||||
ModelObject model;
|
ModelObject model;
|
||||||
std::string type = "unknown";
|
std::string type = "unknown";
|
||||||
|
std::optional<AssetsManager::ParsedHeader> header;
|
||||||
|
if(deps && !deps->empty()) {
|
||||||
|
header = AssetsManager::parseHeader(*deps);
|
||||||
|
if(header && header->Type != EnumAssets::Model)
|
||||||
|
header.reset();
|
||||||
|
}
|
||||||
|
const std::vector<uint32_t>* textureDeps = header ? &header->TextureDeps : nullptr;
|
||||||
|
auto remapTextureId = [&](uint32_t placeholder) -> uint32_t {
|
||||||
|
if(!textureDeps || placeholder >= textureDeps->size())
|
||||||
|
return 0;
|
||||||
|
return (*textureDeps)[placeholder];
|
||||||
|
};
|
||||||
|
auto remapPipeline = [&](TexturePipeline pipe) {
|
||||||
|
if(textureDeps) {
|
||||||
|
for(auto& texId : pipe.BinTextures)
|
||||||
|
texId = remapTextureId(texId);
|
||||||
|
if(!pipe.Pipeline.empty()) {
|
||||||
|
std::vector<uint8_t> code;
|
||||||
|
code.resize(pipe.Pipeline.size());
|
||||||
|
std::memcpy(code.data(), pipe.Pipeline.data(), code.size());
|
||||||
|
TexturePipelineProgram::remapTexIds(code, *textureDeps, nullptr);
|
||||||
|
pipe.Pipeline.resize(code.size());
|
||||||
|
std::memcpy(pipe.Pipeline.data(), code.data(), code.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pipe;
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::u8string_view data((const char8_t*) resource.data(), resource.size());
|
std::u8string_view data((const char8_t*) resource.data(), resource.size());
|
||||||
@@ -153,7 +182,10 @@ public:
|
|||||||
type = "InternalBinary";
|
type = "InternalBinary";
|
||||||
// Компилированная модель внутреннего формата
|
// Компилированная модель внутреннего формата
|
||||||
LV::PreparedModel pm((std::u8string) data);
|
LV::PreparedModel pm((std::u8string) data);
|
||||||
model.TextureMap = pm.CompiledTextures;
|
model.TextureMap.clear();
|
||||||
|
model.TextureMap.reserve(pm.CompiledTextures.size());
|
||||||
|
for(auto& [tkey, pipe] : pm.CompiledTextures)
|
||||||
|
model.TextureMap.emplace(tkey, remapPipeline(std::move(pipe)));
|
||||||
model.TextureKeys = {};
|
model.TextureKeys = {};
|
||||||
|
|
||||||
for(const PreparedModel::Cuboid& cb : pm.Cuboids) {
|
for(const PreparedModel::Cuboid& cb : pm.Cuboids) {
|
||||||
@@ -825,7 +857,7 @@ public:
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
// Применяет изменения, возвращает изменённые описания состояний
|
// Применяет изменения, возвращает изменённые описания состояний
|
||||||
std::vector<AssetsNodestate> onNodestateChanges(std::vector<std::tuple<AssetsNodestate, Resource>> newOrChanged, std::vector<AssetsNodestate> lost, std::vector<AssetsModel> changedModels) {
|
std::vector<AssetsNodestate> onNodestateChanges(std::vector<std::tuple<AssetsNodestate, Resource, const std::vector<uint8_t>*>> newOrChanged, std::vector<AssetsNodestate> lost, std::vector<AssetsModel> changedModels) {
|
||||||
std::vector<AssetsNodestate> result;
|
std::vector<AssetsNodestate> result;
|
||||||
|
|
||||||
for(ResourceId lostId : lost) {
|
for(ResourceId lostId : lost) {
|
||||||
@@ -837,7 +869,7 @@ public:
|
|||||||
Nodestates.erase(iterNodestate);
|
Nodestates.erase(iterNodestate);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const auto& [key, resource] : newOrChanged) {
|
for(const auto& [key, resource, deps] : newOrChanged) {
|
||||||
result.push_back(key);
|
result.push_back(key);
|
||||||
|
|
||||||
PreparedNodeState nodestate;
|
PreparedNodeState nodestate;
|
||||||
@@ -867,6 +899,13 @@ public:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(deps && !deps->empty()) {
|
||||||
|
auto header = AssetsManager::parseHeader(*deps);
|
||||||
|
if(header && header->Type == EnumAssets::Nodestate) {
|
||||||
|
nodestate.LocalToModel.assign(header->ModelDeps.begin(), header->ModelDeps.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Nodestates.insert_or_assign(key, std::move(nodestate));
|
Nodestates.insert_or_assign(key, std::move(nodestate));
|
||||||
if(key < 64) {
|
if(key < 64) {
|
||||||
auto iter = Nodestates.find(key);
|
auto iter = Nodestates.find(key);
|
||||||
|
|||||||
@@ -499,6 +499,15 @@ void unCompressNodes(const std::u8string& compressed, Node* ptr);
|
|||||||
std::u8string compressLinear(const std::u8string& data);
|
std::u8string compressLinear(const std::u8string& data);
|
||||||
std::u8string unCompressLinear(const std::u8string& data);
|
std::u8string unCompressLinear(const std::u8string& data);
|
||||||
|
|
||||||
|
inline std::pair<std::string_view, std::string_view> parseDomainKey(const std::string_view value, const std::string_view defaultDomain = "core") {
|
||||||
|
size_t pos = value.find(':');
|
||||||
|
|
||||||
|
if(pos == std::string_view::npos)
|
||||||
|
return {defaultDomain, value};
|
||||||
|
else
|
||||||
|
return {value.substr(0, pos), value.substr(pos+1)};
|
||||||
|
}
|
||||||
|
|
||||||
inline std::pair<std::string, std::string> parseDomainKey(const std::string& value, const std::string_view defaultDomain = "core") {
|
inline std::pair<std::string, std::string> parseDomainKey(const std::string& value, const std::string_view defaultDomain = "core") {
|
||||||
auto regResult = TOS::Str::match(value, "(?:([\\w\\d_]+):)?([\\w\\d/_.]+)");
|
auto regResult = TOS::Str::match(value, "(?:([\\w\\d_]+):)?([\\w\\d/_.]+)");
|
||||||
if(!regResult)
|
if(!regResult)
|
||||||
@@ -608,11 +617,13 @@ struct NodeStateInfo {
|
|||||||
int Variations = 0;
|
int Variations = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using ResourceHeader = std::u8string;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Хранит распаршенное определение состояний нод.
|
Хранит распаршенное определение состояний нод.
|
||||||
Не привязано ни к какому окружению.
|
Не привязано ни к какому окружению.
|
||||||
*/
|
*/
|
||||||
struct PreparedNodeState {
|
struct HeadlessNodeState {
|
||||||
enum class Op {
|
enum class Op {
|
||||||
Add, Sub, Mul, Div, Mod,
|
Add, Sub, Mul, Div, Mod,
|
||||||
LT, LE, GT, GE, EQ, NE,
|
LT, LE, GT, GE, EQ, NE,
|
||||||
@@ -642,9 +653,7 @@ struct PreparedNodeState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Локальный идентификатор в именной ресурс
|
// Локальный идентификатор в именной ресурс
|
||||||
std::vector<std::pair<std::string, std::string>> LocalToModelKD;
|
std::vector<std::string> LocalToModelKD;
|
||||||
// Локальный идентификатор в глобальный идентификатор
|
|
||||||
std::vector<AssetsModel> LocalToModel;
|
|
||||||
// Ноды выражений
|
// Ноды выражений
|
||||||
std::vector<Node> Nodes;
|
std::vector<Node> Nodes;
|
||||||
// Условия -> вариации модели + веса
|
// Условия -> вариации модели + веса
|
||||||
@@ -657,18 +666,33 @@ struct PreparedNodeState {
|
|||||||
>
|
>
|
||||||
, 1> Routes;
|
, 1> Routes;
|
||||||
|
|
||||||
PreparedNodeState(const std::string_view modid, const js::object& profile);
|
HeadlessNodeState() = default;
|
||||||
PreparedNodeState(const std::string_view modid, const sol::table& profile);
|
HeadlessNodeState(const HeadlessNodeState&) = default;
|
||||||
PreparedNodeState(const std::u8string_view data);
|
HeadlessNodeState(HeadlessNodeState&&) = default;
|
||||||
|
|
||||||
PreparedNodeState() = default;
|
HeadlessNodeState& operator=(const HeadlessNodeState&) = default;
|
||||||
PreparedNodeState(const PreparedNodeState&) = default;
|
HeadlessNodeState& operator=(HeadlessNodeState&&) = default;
|
||||||
PreparedNodeState(PreparedNodeState&&) = default;
|
|
||||||
|
|
||||||
PreparedNodeState& operator=(const PreparedNodeState&) = default;
|
/*
|
||||||
PreparedNodeState& operator=(PreparedNodeState&&) = default;
|
Парсит json формат с выделением все зависимостей в заголовок.
|
||||||
|
Требуется ресолвер идентификаторов моделей.
|
||||||
|
*/
|
||||||
|
ResourceHeader parse(const js::object& profile, const std::function<AssetsModel(const std::string_view model)>& modelResolver);
|
||||||
|
|
||||||
// Пишет в сжатый двоичный формат
|
/*
|
||||||
|
Парсит lua формат с выделением зависимостей в заголовок.
|
||||||
|
Требуется ресолвер идентификаторов моделей.
|
||||||
|
*/
|
||||||
|
ResourceHeader parse(const sol::table& profile, const std::function<AssetsModel(const std::string_view model)>& modelResolver);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Загружает ресурс из двоичного формата.
|
||||||
|
*/
|
||||||
|
void load(std::u8string_view data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Транслирует в двоичный формат.
|
||||||
|
*/
|
||||||
std::u8string dump() const;
|
std::u8string dump() const;
|
||||||
|
|
||||||
// Если зависит от случайного распределения по миру
|
// Если зависит от случайного распределения по миру
|
||||||
@@ -761,7 +785,6 @@ struct PreparedNodeState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::move_only_function<int32_t(uint16_t nodeId)> calcNode;
|
std::move_only_function<int32_t(uint16_t nodeId)> calcNode;
|
||||||
|
|
||||||
calcNode = [&](uint16_t nodeId) -> int32_t {
|
calcNode = [&](uint16_t nodeId) -> int32_t {
|
||||||
@@ -860,7 +883,7 @@ enum class EnumFace {
|
|||||||
/*
|
/*
|
||||||
Парсит json модель
|
Парсит json модель
|
||||||
*/
|
*/
|
||||||
struct PreparedModel {
|
struct HeadlessModel {
|
||||||
enum class EnumGuiLight {
|
enum class EnumGuiLight {
|
||||||
Default
|
Default
|
||||||
};
|
};
|
||||||
@@ -905,23 +928,42 @@ struct PreparedModel {
|
|||||||
|
|
||||||
std::vector<SubModel> SubModels;
|
std::vector<SubModel> SubModels;
|
||||||
|
|
||||||
// Json
|
HeadlessModel() = default;
|
||||||
PreparedModel(const std::string_view modid, const js::object& profile);
|
HeadlessModel(const HeadlessModel&) = default;
|
||||||
PreparedModel(const std::string_view modid, const sol::table& profile);
|
HeadlessModel(HeadlessModel&&) = default;
|
||||||
PreparedModel(const std::u8string& data);
|
|
||||||
|
|
||||||
PreparedModel() = default;
|
HeadlessModel& operator=(const HeadlessModel&) = default;
|
||||||
PreparedModel(const PreparedModel&) = default;
|
HeadlessModel& operator=(HeadlessModel&&) = default;
|
||||||
PreparedModel(PreparedModel&&) = default;
|
|
||||||
|
|
||||||
PreparedModel& operator=(const PreparedModel&) = default;
|
/*
|
||||||
PreparedModel& operator=(PreparedModel&&) = default;
|
Парсит json формат с выделением все зависимостей в заголовок.
|
||||||
|
Требуется ресолвер идентификаторов моделей.
|
||||||
|
*/
|
||||||
|
ResourceHeader parse(
|
||||||
|
const js::object& profile,
|
||||||
|
const std::function<AssetsModel(const std::string_view model)>& modelResolver,
|
||||||
|
const std::function<std::vector<uint8_t>(const std::string_view texturePipelineSrc)>& textureResolver
|
||||||
|
);
|
||||||
|
|
||||||
// Пишет в сжатый двоичный формат
|
/*
|
||||||
std::u8string dump() const;
|
Парсит lua формат с выделением зависимостей в заголовок.
|
||||||
|
Требуется ресолвер идентификаторов моделей.
|
||||||
|
*/
|
||||||
|
ResourceHeader parse(
|
||||||
|
const sol::table& profile,
|
||||||
|
const std::function<AssetsModel(const std::string_view model)>& modelResolver,
|
||||||
|
const std::function<std::vector<uint8_t>(const std::string_view texturePipelineSrc)>& textureResolver
|
||||||
|
);
|
||||||
|
|
||||||
private:
|
/*
|
||||||
|
Загружает ресурс из двоичного формата.
|
||||||
|
*/
|
||||||
void load(std::u8string_view data);
|
void load(std::u8string_view data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Транслирует в двоичный формат.
|
||||||
|
*/
|
||||||
|
std::u8string dump() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PreparedGLTF {
|
struct PreparedGLTF {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
namespace LV::Server {
|
namespace LV::Server {
|
||||||
|
|
||||||
ContentManager::ContentManager(AssetsPreloader &am)
|
ContentManager::ContentManager(AssetsPreloader& am)
|
||||||
: AM(am)
|
: AM(am)
|
||||||
{
|
{
|
||||||
std::fill(std::begin(NextId), std::end(NextId), 1);
|
std::fill(std::begin(NextId), std::end(NextId), 1);
|
||||||
|
|||||||
@@ -1567,7 +1567,7 @@ void GameServer::init(fs::path worldPath) {
|
|||||||
AssetsInit.Assets.push_back(mlt.LoadChain[index].Path / "assets");
|
AssetsInit.Assets.push_back(mlt.LoadChain[index].Path / "assets");
|
||||||
}
|
}
|
||||||
|
|
||||||
Content.AM.applyResourceChange(Content.AM.recheckResourcesSync(AssetsInit));
|
Content.AM.applyResourceChange(Content.AM.reloadResources(AssetsInit));
|
||||||
|
|
||||||
LOG.info() << "Пре Инициализация";
|
LOG.info() << "Пре Инициализация";
|
||||||
|
|
||||||
@@ -1882,7 +1882,7 @@ void GameServer::stepModInitializations() {
|
|||||||
void GameServer::reloadMods() {
|
void GameServer::reloadMods() {
|
||||||
LOG.info() << "Перезагрузка модов: ассеты и зависимости";
|
LOG.info() << "Перезагрузка модов: ассеты и зависимости";
|
||||||
|
|
||||||
AssetsPreloader::ResourceChangeObj changes = Content.AM.recheckResourcesSync(AssetsInit);
|
AssetsPreloader::Out_reloadResources changes = Content.AM.reloadResources(AssetsInit);
|
||||||
AssetsPreloader::Out_applyResourceChange applied = Content.AM.applyResourceChange(changes);
|
AssetsPreloader::Out_applyResourceChange applied = Content.AM.applyResourceChange(changes);
|
||||||
|
|
||||||
size_t changedCount = 0;
|
size_t changedCount = 0;
|
||||||
@@ -2687,7 +2687,7 @@ void GameServer::stepSyncContent() {
|
|||||||
full.uniq();
|
full.uniq();
|
||||||
|
|
||||||
// Информируем о запрошенных ассетах
|
// Информируем о запрошенных ассетах
|
||||||
std::vector<std::tuple<EnumAssets, ResourceId, const std::string, const std::string, Resource>> resources;
|
std::vector<std::tuple<EnumAssets, ResourceId, std::string, std::string, Resource, std::u8string>> resources;
|
||||||
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) {
|
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) {
|
||||||
for(ResourceId resId : full.AssetsInfo[type]) {
|
for(ResourceId resId : full.AssetsInfo[type]) {
|
||||||
const AssetsPreloader::MediaResource* media = Content.AM.getResource((EnumAssets) type, resId);
|
const AssetsPreloader::MediaResource* media = Content.AM.getResource((EnumAssets) type, resId);
|
||||||
@@ -2695,7 +2695,7 @@ void GameServer::stepSyncContent() {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
Resource resource(media->Resource->data(), media->Resource->size());
|
Resource resource(media->Resource->data(), media->Resource->size());
|
||||||
resources.emplace_back((EnumAssets) type, resId, media->Domain, media->Key, std::move(resource));
|
resources.emplace_back((EnumAssets) type, resId, media->Domain, media->Key, std::move(resource), media->Dependencies);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2705,8 +2705,8 @@ void GameServer::stepSyncContent() {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto& [type, id, media] = *result;
|
auto& [type, id, media] = *result;
|
||||||
Resource resource(media->Resource->data(), media->Resource->size());
|
Resource resource(*media->Resource);
|
||||||
resources.emplace_back(type, id, media->Domain, media->Key, std::move(resource));
|
resources.emplace_back(type, id, media->Domain, media->Key, std::move(resource), media->Header);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Информируем о запрошенных профилях
|
// Информируем о запрошенных профилях
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <boost/system/system_error.hpp>
|
#include <boost/system/system_error.hpp>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <Common/Packets.hpp>
|
#include <Common/Packets.hpp>
|
||||||
|
#include "sha2.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace LV::Server {
|
namespace LV::Server {
|
||||||
@@ -562,13 +563,14 @@ ResourceRequest RemoteClient::pushPreparedPackets() {
|
|||||||
return std::move(nextRequest);
|
return std::move(nextRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteClient::informateAssets(const std::vector<std::tuple<EnumAssets, ResourceId, const std::string, const std::string, Resource>>& resources)
|
void RemoteClient::informateAssets(const std::vector<std::tuple<EnumAssets, ResourceId, std::string, std::string, Resource, std::vector<uint8_t>>>& resources)
|
||||||
{
|
{
|
||||||
std::vector<std::tuple<EnumAssets, ResourceId, const std::string, const std::string, Hash_t, size_t>> newForClient;
|
std::vector<std::tuple<EnumAssets, ResourceId, std::string, std::string, Hash_t, std::vector<uint8_t>>> newForClient;
|
||||||
static std::atomic<uint32_t> debugSendLogCount = 0;
|
static std::atomic<uint32_t> debugSendLogCount = 0;
|
||||||
|
|
||||||
for(auto& [type, resId, domain, key, resource] : resources) {
|
for(auto& [type, resId, domain, key, resource, header] : resources) {
|
||||||
auto hash = resource.hash();
|
auto hash = resource.hash();
|
||||||
|
Hash_t headerHash = sha2::sha256(header.data(), header.size());
|
||||||
auto lock = NetworkAndResource.lock();
|
auto lock = NetworkAndResource.lock();
|
||||||
|
|
||||||
// Проверка запрашиваемых клиентом ресурсов
|
// Проверка запрашиваемых клиентом ресурсов
|
||||||
@@ -613,12 +615,13 @@ void RemoteClient::informateAssets(const std::vector<std::tuple<EnumAssets, Reso
|
|||||||
// Посмотрим что известно клиенту
|
// Посмотрим что известно клиенту
|
||||||
if(auto iter = lock->ResUses.AssetsUse[(int) type].find(resId);
|
if(auto iter = lock->ResUses.AssetsUse[(int) type].find(resId);
|
||||||
iter != lock->ResUses.AssetsUse[(int) type].end()
|
iter != lock->ResUses.AssetsUse[(int) type].end()
|
||||||
&& std::get<Hash_t>(iter->second) != hash
|
&& (iter->second.second.Hash != hash || iter->second.second.HeaderHash != headerHash)
|
||||||
) {
|
) {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
// Требуется перепривязать идентификатор к новому хешу
|
// Требуется перепривязать идентификатор к новому хешу
|
||||||
newForClient.push_back({(EnumAssets) type, resId, domain, key, hash, resource.size()});
|
newForClient.push_back({(EnumAssets) type, resId, domain, key, hash, header});
|
||||||
std::get<Hash_t>(iter->second) = hash;
|
iter->second.second.Hash = hash;
|
||||||
|
iter->second.second.HeaderHash = headerHash;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -628,14 +631,19 @@ void RemoteClient::informateAssets(const std::vector<std::tuple<EnumAssets, Reso
|
|||||||
assert(newForClient.size() < 65535*4);
|
assert(newForClient.size() < 65535*4);
|
||||||
auto lock = NetworkAndResource.lock();
|
auto lock = NetworkAndResource.lock();
|
||||||
|
|
||||||
lock->checkPacketBorder(2+1+4+newForClient.size()*(1+4+64+32));
|
lock->checkPacketBorder(2+1+4);
|
||||||
lock->NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение
|
lock->NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение
|
||||||
<< ((uint8_t) ToClient::L2Resource::Bind) << uint32_t(newForClient.size());
|
<< ((uint8_t) ToClient::L2Resource::Bind) << uint32_t(newForClient.size());
|
||||||
|
|
||||||
for(auto& [type, resId, domain, key, hash, size] : newForClient) {
|
for(auto& [type, resId, domain, key, hash, header] : newForClient) {
|
||||||
// TODO: может внести ограничение на длину домена и ключа?
|
// TODO: может внести ограничение на длину домена и ключа?
|
||||||
|
const size_t entrySize = 1 + 4 + 2 + domain.size() + 2 + key.size() + 32 + 4 + header.size();
|
||||||
|
lock->checkPacketBorder(entrySize);
|
||||||
lock->NextPacket << uint8_t(type) << uint32_t(resId) << domain << key;
|
lock->NextPacket << uint8_t(type) << uint32_t(resId) << domain << key;
|
||||||
lock->NextPacket.write((const std::byte*) hash.data(), hash.size());
|
lock->NextPacket.write((const std::byte*) hash.data(), hash.size());
|
||||||
|
lock->NextPacket << uint32_t(header.size());
|
||||||
|
if(!header.empty())
|
||||||
|
lock->NextPacket.write((const std::byte*) header.data(), header.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,7 +212,11 @@ class RemoteClient {
|
|||||||
struct ResUses_t {
|
struct ResUses_t {
|
||||||
// Счётчики использования двоичных кэшируемых ресурсов + хэш привязанный к идентификатору
|
// Счётчики использования двоичных кэшируемых ресурсов + хэш привязанный к идентификатору
|
||||||
// Хэш используется для того, чтобы исключить повторные объявления неизменившихся ресурсов
|
// Хэш используется для того, чтобы исключить повторные объявления неизменившихся ресурсов
|
||||||
std::map<ResourceId, std::pair<uint32_t, Hash_t>> AssetsUse[(int) EnumAssets::MAX_ENUM];
|
struct AssetBindState {
|
||||||
|
Hash_t Hash;
|
||||||
|
Hash_t HeaderHash;
|
||||||
|
};
|
||||||
|
std::map<ResourceId, std::pair<uint32_t, AssetBindState>> AssetsUse[(int) EnumAssets::MAX_ENUM];
|
||||||
|
|
||||||
// Зависимость профилей контента от профилей ресурсов
|
// Зависимость профилей контента от профилей ресурсов
|
||||||
// Нужно чтобы пересчитать зависимости к профилям ресурсов
|
// Нужно чтобы пересчитать зависимости к профилям ресурсов
|
||||||
@@ -426,8 +430,16 @@ public:
|
|||||||
// Сюда приходят все обновления ресурсов движка
|
// Сюда приходят все обновления ресурсов движка
|
||||||
// Глобально их можно запросить в выдаче pushPreparedPackets()
|
// Глобально их можно запросить в выдаче pushPreparedPackets()
|
||||||
|
|
||||||
|
// Нужно передавать клиенту информацию о новых привязках
|
||||||
|
// id -> домен+ключ
|
||||||
|
// id -> hash+header
|
||||||
|
|
||||||
|
// По запросу клиента отправлять нужные ресурсы по hash
|
||||||
|
|
||||||
|
/// TODO: новый void informateAssets();
|
||||||
|
|
||||||
// Оповещение о запрошенных (и не только) ассетах
|
// Оповещение о запрошенных (и не только) ассетах
|
||||||
void informateAssets(const std::vector<std::tuple<EnumAssets, ResourceId, const std::string, const std::string, Resource>>& resources);
|
void informateAssets(const std::vector<std::tuple<EnumAssets, ResourceId, std::string, std::string, Resource, std::vector<uint8_t>>>& resources);
|
||||||
|
|
||||||
// Игровые определения
|
// Игровые определения
|
||||||
void informateDefVoxel(const std::vector<std::pair<DefVoxelId, DefVoxel*>>& voxels) { NetworkAndResource.lock()->informateDefVoxel(voxels); }
|
void informateDefVoxel(const std::vector<std::pair<DefVoxelId, DefVoxel*>>& voxels) { NetworkAndResource.lock()->informateDefVoxel(voxels); }
|
||||||
|
|||||||
Reference in New Issue
Block a user