Рефакторинг кода работы с ресурсами игры на стороне сервера
This commit is contained in:
@@ -34,24 +34,6 @@ using PlayerId_t = ResourceId;
|
||||
using DefGeneratorId_t = ResourceId;
|
||||
|
||||
|
||||
/*
|
||||
Сервер загружает информацию о локальных текстурах
|
||||
Пересмотр списка текстур?
|
||||
Динамичные текстуры?
|
||||
|
||||
*/
|
||||
|
||||
struct ResourceFile {
|
||||
using Hash_t = sha2::sha256_hash; // boost::uuids::detail::sha1::digest_type;
|
||||
|
||||
Hash_t Hash;
|
||||
std::vector<std::byte> Data;
|
||||
|
||||
void calcHash() {
|
||||
Hash = sha2::sha256((const uint8_t*) Data.data(), Data.size());
|
||||
}
|
||||
};
|
||||
|
||||
struct ServerTime {
|
||||
uint32_t Seconds : 24, Sub : 8;
|
||||
};
|
||||
|
||||
107
Src/Server/AssetsManager.hpp
Normal file
107
Src/Server/AssetsManager.hpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common/Abstract.hpp"
|
||||
#include "Common/IdProvider.hpp"
|
||||
#include "Common/AssetsPreloader.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace LV::Server {
|
||||
|
||||
class AssetsManager : public IdProvider<EnumAssets>, protected AssetsPreloader {
|
||||
public:
|
||||
using BindHashHeaderInfo = AssetsManager::BindHashHeaderInfo;
|
||||
|
||||
struct Out_checkAndPrepareResourcesUpdate : public AssetsPreloader::Out_checkAndPrepareResourcesUpdate {
|
||||
Out_checkAndPrepareResourcesUpdate(AssetsPreloader::Out_checkAndPrepareResourcesUpdate&& obj)
|
||||
: AssetsPreloader::Out_checkAndPrepareResourcesUpdate(std::move(obj))
|
||||
{}
|
||||
|
||||
std::unordered_map<ResourceFile::Hash_t, std::u8string> NewHeadless;
|
||||
};
|
||||
|
||||
Out_checkAndPrepareResourcesUpdate checkAndPrepareResourcesUpdate(
|
||||
const AssetsRegister& instances,
|
||||
ReloadStatus* status = nullptr
|
||||
) {
|
||||
std::unordered_map<ResourceFile::Hash_t, std::u8string> newHeadless;
|
||||
|
||||
Out_checkAndPrepareResourcesUpdate result = AssetsPreloader::checkAndPrepareResourcesUpdate(
|
||||
instances,
|
||||
[&](EnumAssets type, std::string_view domain, std::string_view key) { return getId(type, domain, key); },
|
||||
[&](std::u8string&& resource, ResourceFile::Hash_t hash, fs::path resPath) { newHeadless.emplace(hash, std::move(resource)); },
|
||||
status
|
||||
);
|
||||
|
||||
result.NewHeadless = std::move(newHeadless);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct Out_applyResourcesUpdate : public AssetsPreloader::Out_applyResourcesUpdate {
|
||||
Out_applyResourcesUpdate(AssetsPreloader::Out_applyResourcesUpdate&& obj)
|
||||
: AssetsPreloader::Out_applyResourcesUpdate(std::move(obj))
|
||||
{}
|
||||
};
|
||||
|
||||
Out_applyResourcesUpdate applyResourcesUpdate(Out_checkAndPrepareResourcesUpdate& orr) {
|
||||
Out_applyResourcesUpdate result = AssetsPreloader::applyResourcesUpdate(orr);
|
||||
|
||||
for(auto& [hash, data] : orr.NewHeadless) {
|
||||
Resources.emplace(hash, ResourceHashData{0, std::make_shared<std::u8string>(std::move(data))});
|
||||
}
|
||||
|
||||
for(auto& [hash, pathes] : orr.HashToPathNew) {
|
||||
auto iter = Resources.find(hash);
|
||||
assert(iter != Resources.end());
|
||||
iter->second.RefCount += pathes.size();
|
||||
}
|
||||
|
||||
for(auto& [hash, pathes] : orr.HashToPathLost) {
|
||||
auto iter = Resources.find(hash);
|
||||
assert(iter != Resources.end());
|
||||
iter->second.RefCount -= pathes.size();
|
||||
|
||||
if(iter->second.RefCount == 0)
|
||||
Resources.erase(iter);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::tuple<ResourceFile::Hash_t, std::shared_ptr<const std::u8string>>>
|
||||
getResources(const std::vector<ResourceFile::Hash_t>& hashes) const
|
||||
{
|
||||
std::vector<std::tuple<ResourceFile::Hash_t, std::shared_ptr<const std::u8string>>> result;
|
||||
result.reserve(hashes.size());
|
||||
|
||||
for(const auto& hash : hashes) {
|
||||
auto iter = Resources.find(hash);
|
||||
if(iter == Resources.end())
|
||||
continue;
|
||||
|
||||
result.emplace_back(hash, iter->second.Data);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::array<
|
||||
std::vector<BindHashHeaderInfo>,
|
||||
static_cast<size_t>(EnumAssets::MAX_ENUM)
|
||||
> collectHashBindings() const {
|
||||
return AssetsPreloader::collectHashBindings();
|
||||
}
|
||||
|
||||
private:
|
||||
struct ResourceHashData {
|
||||
size_t RefCount;
|
||||
std::shared_ptr<std::u8string> Data;
|
||||
};
|
||||
|
||||
std::unordered_map<
|
||||
ResourceFile::Hash_t,
|
||||
ResourceHashData
|
||||
> Resources;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
namespace LV::Server {
|
||||
|
||||
ContentManager::ContentManager(AssetsPreloader& am)
|
||||
ContentManager::ContentManager(AssetsManager& am)
|
||||
: AM(am)
|
||||
{
|
||||
std::fill(std::begin(NextId), std::end(NextId), 1);
|
||||
@@ -158,20 +158,6 @@ ContentManager::Out_buildEndProfiles ContentManager::buildEndProfiles() {
|
||||
keys.erase(iterErase, keys.end());
|
||||
}
|
||||
|
||||
for(ResourceId id : ProfileChanges[(int) EnumDefContent::Node]) {
|
||||
std::optional<DefNode>& node = getEntry_Node(id);
|
||||
if(!node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto [nodestateId, assetsModel, assetsTexture]
|
||||
= AM.getNodeDependency(node->Domain, node->Key);
|
||||
|
||||
node->NodestateId = nodestateId;
|
||||
node->ModelDeps = std::move(assetsModel);
|
||||
node->TextureDeps = std::move(assetsTexture);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common/Abstract.hpp"
|
||||
#include "Server/Abstract.hpp"
|
||||
#include "Common/AssetsPreloader.hpp"
|
||||
#include "AssetsManager.hpp"
|
||||
#include <sol/table.hpp>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
|
||||
namespace LV::Server {
|
||||
@@ -135,7 +133,7 @@ class ContentManager {
|
||||
void registerBase_Entity(ResourceId id, const std::string& domain, const std::string& key, const sol::table& profile);
|
||||
|
||||
public:
|
||||
ContentManager(AssetsPreloader &am);
|
||||
ContentManager(AssetsManager &am);
|
||||
~ContentManager();
|
||||
|
||||
// Регистрирует определение контента
|
||||
@@ -215,7 +213,7 @@ public:
|
||||
|
||||
private:
|
||||
TOS::Logger LOG = "Server>ContentManager";
|
||||
AssetsPreloader& AM;
|
||||
AssetsManager& AM;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1345,7 +1345,8 @@ void GameServer::init(fs::path worldPath) {
|
||||
AssetsInit.Assets.push_back(mlt.LoadChain[index].Path / "assets");
|
||||
}
|
||||
|
||||
Content.AM.applyResourceChange(Content.AM.reloadResources(AssetsInit));
|
||||
auto capru = Content.AM.checkAndPrepareResourcesUpdate(AssetsInit);
|
||||
Content.AM.applyResourcesUpdate(capru);
|
||||
|
||||
LOG.info() << "Пре Инициализация";
|
||||
|
||||
@@ -1606,12 +1607,11 @@ void GameServer::stepConnections() {
|
||||
}
|
||||
|
||||
if(!newClients.empty()) {
|
||||
AssetsPreloader::Out_fullSync fullSync = Content.AM.collectFullSync();
|
||||
std::array<std::vector<ResourceId>, static_cast<size_t>(EnumAssets::MAX_ENUM)> lost{};
|
||||
|
||||
std::vector<Net::Packet> packets;
|
||||
packets.push_back(RemoteClient::makePacket_informateAssets_DK(fullSync.IdToDK));
|
||||
packets.push_back(RemoteClient::makePacket_informateAssets_HH(fullSync.HashHeaders, lost));
|
||||
packets.push_back(RemoteClient::makePacket_informateAssets_DK(Content.AM.idToDK()));
|
||||
packets.push_back(RemoteClient::makePacket_informateAssets_HH(Content.AM.collectHashBindings(), lost));
|
||||
|
||||
for(const std::shared_ptr<RemoteClient>& client : newClients) {
|
||||
if(!packets.empty()) {
|
||||
@@ -1688,23 +1688,27 @@ void GameServer::reloadMods() {
|
||||
LOG.info() << "Перезагрузка ассетов";
|
||||
{
|
||||
{
|
||||
AssetsPreloader::Out_applyResourceChange applied
|
||||
= Content.AM.applyResourceChange(Content.AM.reloadResources(AssetsInit));
|
||||
AssetsManager::Out_checkAndPrepareResourcesUpdate capru = Content.AM.checkAndPrepareResourcesUpdate(AssetsInit);
|
||||
AssetsManager::Out_applyResourcesUpdate aru = Content.AM.applyResourcesUpdate(capru);
|
||||
|
||||
if(!applied.NewOrChange.empty() || !applied.Lost.empty())
|
||||
if(!capru.ResourceUpdates.empty() || !capru.LostLinks.empty())
|
||||
packetsToSend.push_back(
|
||||
RemoteClient::makePacket_informateAssets_HH(
|
||||
applied.NewOrChange,
|
||||
applied.Lost
|
||||
aru.NewOrUpdates,
|
||||
capru.LostLinks
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
AssetsPreloader::Out_bakeId baked = Content.AM.bakeIdTables();
|
||||
if(hasAnyBindings(baked.IdToDK)) {
|
||||
packetsToSend.push_back(RemoteClient::makePacket_informateAssets_DK(baked.IdToDK));
|
||||
std::array<
|
||||
std::vector<AssetsManager::BindDomainKeyInfo>,
|
||||
static_cast<size_t>(EnumAssets::MAX_ENUM)
|
||||
> baked = Content.AM.bake();
|
||||
|
||||
if(hasAnyBindings(baked)) {
|
||||
packetsToSend.push_back(RemoteClient::makePacket_informateAssets_DK(baked));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2495,9 +2499,13 @@ void GameServer::stepSyncContent() {
|
||||
|
||||
std::vector<Net::Packet> packetsToAll;
|
||||
{
|
||||
AssetsPreloader::Out_bakeId baked = Content.AM.bakeIdTables();
|
||||
if(hasAnyBindings(baked.IdToDK)) {
|
||||
packetsToAll.push_back(RemoteClient::makePacket_informateAssets_DK(baked.IdToDK));
|
||||
std::array<
|
||||
std::vector<AssetsManager::BindDomainKeyInfo>,
|
||||
static_cast<size_t>(EnumAssets::MAX_ENUM)
|
||||
> baked = Content.AM.bake();
|
||||
|
||||
if(hasAnyBindings(baked)) {
|
||||
packetsToAll.push_back(RemoteClient::makePacket_informateAssets_DK(baked));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2515,30 +2523,8 @@ void GameServer::stepSyncContent() {
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<AssetBinaryInfo> binaryResources;
|
||||
for(const Hash_t& hash : full.Hashes) {
|
||||
std::optional<
|
||||
std::tuple<EnumAssets, uint32_t, const AssetsPreloader::MediaResource*>
|
||||
> result = Content.AM.getResource(hash);
|
||||
|
||||
if(!result)
|
||||
continue;
|
||||
|
||||
auto& [type, id, media] = *result;
|
||||
LOG.debug() << "Server sending type=" << assetTypeName(type)
|
||||
<< " id=" << id
|
||||
<< " key=" << media->Domain << ':' << media->Key
|
||||
<< " hash=" << int(media->Hash[0]) << '.'
|
||||
<< int(media->Hash[1]) << '.'
|
||||
<< int(media->Hash[2]) << '.'
|
||||
<< int(media->Hash[3])
|
||||
<< " size=" << media->Resource->size();
|
||||
Resource resource(*media->Resource);
|
||||
binaryResources.push_back(AssetBinaryInfo{
|
||||
.Data = std::move(resource),
|
||||
.Hash = media->Hash
|
||||
});
|
||||
}
|
||||
std::vector<std::tuple<ResourceFile::Hash_t, std::shared_ptr<const std::u8string>>> binaryResources
|
||||
= Content.AM.getResources(full.Hashes);
|
||||
|
||||
for(std::shared_ptr<RemoteClient>& remoteClient : Game.RemoteClients) {
|
||||
if(!binaryResources.empty())
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "RemoteClient.hpp"
|
||||
#include "Server/Abstract.hpp"
|
||||
#include <TOSLib.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
@@ -25,6 +24,7 @@
|
||||
|
||||
#include "WorldDefManager.hpp"
|
||||
#include "ContentManager.hpp"
|
||||
#include "AssetsManager.hpp"
|
||||
#include "World.hpp"
|
||||
|
||||
#include "SaveBackend.hpp"
|
||||
@@ -73,7 +73,7 @@ class GameServer : public AsyncObject {
|
||||
|
||||
struct ContentObj {
|
||||
public:
|
||||
AssetsPreloader AM;
|
||||
AssetsManager AM;
|
||||
ContentManager CM;
|
||||
|
||||
// Если контент был перерегистрирован (исключая двоичные ресурсы), то профили будут повторно разосланы
|
||||
|
||||
@@ -11,13 +11,14 @@
|
||||
#include <boost/system/system_error.hpp>
|
||||
#include <exception>
|
||||
#include <Common/Packets.hpp>
|
||||
#include <unordered_set>
|
||||
|
||||
|
||||
namespace LV::Server {
|
||||
|
||||
Net::Packet RemoteClient::makePacket_informateAssets_DK(
|
||||
const std::array<
|
||||
std::vector<AssetsPreloader::BindDomainKeyInfo>,
|
||||
std::vector<AssetsManager::BindDomainKeyInfo>,
|
||||
static_cast<size_t>(EnumAssets::MAX_ENUM)
|
||||
>& dkVector
|
||||
) {
|
||||
@@ -46,7 +47,7 @@ Net::Packet RemoteClient::makePacket_informateAssets_DK(
|
||||
|
||||
// Запись связок домен+ключ
|
||||
for(size_t type = 0; type < static_cast<size_t>(EnumAssets::MAX_ENUM); type++) {
|
||||
const std::vector<AssetsPreloader::BindDomainKeyInfo>& binds = dkVector[type];
|
||||
const std::vector<AssetsManager::BindDomainKeyInfo>& binds = dkVector[type];
|
||||
pack << uint32_t(binds.size());
|
||||
|
||||
for(const auto& bind : binds) {
|
||||
@@ -67,7 +68,7 @@ Net::Packet RemoteClient::makePacket_informateAssets_DK(
|
||||
|
||||
Net::Packet RemoteClient::makePacket_informateAssets_HH(
|
||||
const std::array<
|
||||
std::vector<AssetsPreloader::BindHashHeaderInfo>,
|
||||
std::vector<AssetsManager::BindHashHeaderInfo>,
|
||||
static_cast<size_t>(EnumAssets::MAX_ENUM)
|
||||
>& hhVector,
|
||||
const std::array<
|
||||
@@ -430,21 +431,21 @@ ResourceRequest RemoteClient::pushPreparedPackets() {
|
||||
return std::move(nextRequest);
|
||||
}
|
||||
|
||||
void RemoteClient::informateBinaryAssets(const std::vector<AssetBinaryInfo>& resources)
|
||||
void RemoteClient::informateBinaryAssets(const std::vector<std::tuple<ResourceFile::Hash_t, std::shared_ptr<const std::u8string>>>& resources)
|
||||
{
|
||||
for(const AssetBinaryInfo& resource : resources) {
|
||||
for(const auto& [hash, resource] : resources) {
|
||||
auto lock = NetworkAndResource.lock();
|
||||
auto iter = std::find(lock->ClientRequested.begin(), lock->ClientRequested.end(), resource.Hash);
|
||||
auto iter = std::find(lock->ClientRequested.begin(), lock->ClientRequested.end(), hash);
|
||||
if(iter == lock->ClientRequested.end())
|
||||
continue;
|
||||
|
||||
lock->ClientRequested.erase(iter);
|
||||
lock.unlock();
|
||||
|
||||
auto it = std::lower_bound(AssetsInWork.OnClient.begin(), AssetsInWork.OnClient.end(), resource.Hash);
|
||||
if(it == AssetsInWork.OnClient.end() || *it != resource.Hash) {
|
||||
AssetsInWork.OnClient.insert(it, resource.Hash);
|
||||
AssetsInWork.ToSend.emplace_back(resource.Data, 0);
|
||||
auto it = std::lower_bound(AssetsInWork.OnClient.begin(), AssetsInWork.OnClient.end(), hash);
|
||||
if(it == AssetsInWork.OnClient.end() || *it != hash) {
|
||||
AssetsInWork.OnClient.insert(it, hash);
|
||||
AssetsInWork.ToSend.emplace_back(hash, resource, 0);
|
||||
} else {
|
||||
LOG.warn() << "Клиент повторно запросил имеющийся у него ресурс";
|
||||
}
|
||||
@@ -611,36 +612,36 @@ void RemoteClient::onUpdate() {
|
||||
|
||||
bool hasFullSended = false;
|
||||
|
||||
for(auto& [res, sended] : toSend) {
|
||||
for(auto& [hash, res, sended] : toSend) {
|
||||
if(sended == 0) {
|
||||
// Оповещаем о начале отправки ресурса
|
||||
const size_t initSize = 1 + 1 + 4 + 32 + 4 + 1;
|
||||
if(p.size() + initSize > kMaxAssetPacketSize)
|
||||
flushAssetsPacket();
|
||||
p << (uint8_t) ToClient::AssetsInitSend
|
||||
<< uint32_t(res.size());
|
||||
p.write((const std::byte*) res.hash().data(), 32);
|
||||
<< uint32_t(res->size());
|
||||
p.write((const std::byte*) hash.data(), 32);
|
||||
}
|
||||
|
||||
// Отправляем чанк
|
||||
size_t willSend = std::min(chunkSize, res.size()-sended);
|
||||
size_t willSend = std::min(chunkSize, res->size()-sended);
|
||||
const size_t chunkMsgSize = 1 + 1 + 32 + 4 + willSend;
|
||||
if(p.size() + chunkMsgSize > kMaxAssetPacketSize)
|
||||
flushAssetsPacket();
|
||||
p << (uint8_t) ToClient::AssetsNextSend;
|
||||
p.write((const std::byte*) res.hash().data(), 32);
|
||||
p.write((const std::byte*) hash.data(), 32);
|
||||
p << uint32_t(willSend);
|
||||
p.write(res.data() + sended, willSend);
|
||||
p.write((const std::byte*) res->data() + sended, willSend);
|
||||
sended += willSend;
|
||||
|
||||
if(sended == res.size()) {
|
||||
if(sended == res->size()) {
|
||||
hasFullSended = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(hasFullSended) {
|
||||
for(ssize_t iter = toSend.size()-1; iter >= 0; iter--) {
|
||||
if(std::get<0>(toSend[iter]).size() == std::get<1>(toSend[iter])) {
|
||||
if(std::get<1>(toSend[iter])->size() == std::get<2>(toSend[iter])) {
|
||||
toSend.erase(toSend.begin()+iter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <Common/Net.hpp>
|
||||
#include "Abstract.hpp"
|
||||
#include "Common/Packets.hpp"
|
||||
#include "Server/AssetsManager.hpp"
|
||||
#include "Server/ContentManager.hpp"
|
||||
#include <Common/Abstract.hpp>
|
||||
#include <bitset>
|
||||
@@ -256,7 +257,7 @@ class RemoteClient {
|
||||
std::vector<Hash_t> OnClient;
|
||||
// Отправляемые на клиент ресурсы
|
||||
// Ресурс, количество отправленных байт
|
||||
std::vector<std::tuple<Resource, size_t>> ToSend;
|
||||
std::vector<std::tuple<ResourceFile::Hash_t, std::shared_ptr<const std::u8string>, size_t>> ToSend;
|
||||
// Пакет с ресурсами
|
||||
std::vector<Net::Packet> AssetsPackets;
|
||||
Net::Packet AssetsPacket;
|
||||
@@ -361,7 +362,7 @@ public:
|
||||
// Создаёт пакет для всех игроков с оповещением о новых идентификаторах (id -> domain+key)
|
||||
static Net::Packet makePacket_informateAssets_DK(
|
||||
const std::array<
|
||||
std::vector<AssetsPreloader::BindDomainKeyInfo>,
|
||||
std::vector<AssetsManager::BindDomainKeyInfo>,
|
||||
static_cast<size_t>(EnumAssets::MAX_ENUM)
|
||||
>& dkVector
|
||||
);
|
||||
@@ -369,7 +370,7 @@ public:
|
||||
// Создаёт пакет для всех игроков с оповещением об изменении файлов ресурсов (id -> hash+header)
|
||||
static Net::Packet makePacket_informateAssets_HH(
|
||||
const std::array<
|
||||
std::vector<AssetsPreloader::BindHashHeaderInfo>,
|
||||
std::vector<AssetsManager::BindHashHeaderInfo>,
|
||||
static_cast<size_t>(EnumAssets::MAX_ENUM)
|
||||
>& hhVector,
|
||||
const std::array<
|
||||
@@ -380,7 +381,7 @@ public:
|
||||
|
||||
// Оповещение о двоичных ресурсах (стриминг по запросу)
|
||||
void informateBinaryAssets(
|
||||
const std::vector<AssetBinaryInfo>& resources
|
||||
const std::vector<std::tuple<ResourceFile::Hash_t, std::shared_ptr<const std::u8string>>>& resources
|
||||
);
|
||||
|
||||
// Создаёт пакет об обновлении игровых профилей
|
||||
|
||||
Reference in New Issue
Block a user