Переработка интерфейса предоставления данных клиентам
This commit is contained in:
@@ -114,6 +114,10 @@ ServerSession::ServerSession(asio::io_context &ioc, std::unique_ptr<Net::AsyncSo
|
|||||||
Profiles.DefNode[3] = {3};
|
Profiles.DefNode[3] = {3};
|
||||||
Profiles.DefNode[4] = {4};
|
Profiles.DefNode[4] = {4};
|
||||||
|
|
||||||
|
std::fill(NextServerId.begin(), NextServerId.end(), 1);
|
||||||
|
for(auto& vec : ServerIdToDK)
|
||||||
|
vec.emplace_back();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
AM = AssetsManager::Create(ioc, "Cache");
|
AM = AssetsManager::Create(ioc, "Cache");
|
||||||
asio::co_spawn(ioc, run(AUC.use()), asio::detached);
|
asio::co_spawn(ioc, run(AUC.use()), asio::detached);
|
||||||
@@ -1231,6 +1235,11 @@ void ServerSession::resetResourceSyncState() {
|
|||||||
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++)
|
||||||
AsyncContext.ResourceWait[type].clear();
|
AsyncContext.ResourceWait[type].clear();
|
||||||
|
for(auto& vec : ServerIdToDK)
|
||||||
|
vec.clear();
|
||||||
|
std::fill(NextServerId.begin(), NextServerId.end(), 1);
|
||||||
|
for(auto& vec : ServerIdToDK)
|
||||||
|
vec.emplace_back();
|
||||||
AsyncContext.Binds.clear();
|
AsyncContext.Binds.clear();
|
||||||
AsyncContext.LoadedResources.clear();
|
AsyncContext.LoadedResources.clear();
|
||||||
AsyncContext.ThisTickEntry = {};
|
AsyncContext.ThisTickEntry = {};
|
||||||
@@ -1374,6 +1383,95 @@ coro<> ServerSession::rP_Resource(Net::AsyncSocket &sock) {
|
|||||||
AsyncContext.AssetsBinds.lock()->push_back(AssetsBindsChange(binds, {}));
|
AsyncContext.AssetsBinds.lock()->push_back(AssetsBindsChange(binds, {}));
|
||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
|
case ToClient::L2Resource::BindDK:
|
||||||
|
{
|
||||||
|
uint32_t count = co_await sock.read<uint32_t>();
|
||||||
|
for(size_t iter = 0; iter < count; iter++) {
|
||||||
|
uint8_t type = co_await sock.read<uint8_t>();
|
||||||
|
if(type >= (int) EnumAssets::MAX_ENUM)
|
||||||
|
protocolError();
|
||||||
|
|
||||||
|
std::string domain = co_await sock.read<std::string>();
|
||||||
|
std::string key = co_await sock.read<std::string>();
|
||||||
|
|
||||||
|
ResourceId serverId = NextServerId[type]++;
|
||||||
|
auto& table = ServerIdToDK[type];
|
||||||
|
if(table.size() <= serverId)
|
||||||
|
table.resize(serverId+1);
|
||||||
|
table[serverId] = {std::move(domain), std::move(key)};
|
||||||
|
}
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
case ToClient::L2Resource::BindHash:
|
||||||
|
{
|
||||||
|
uint32_t count = co_await sock.read<uint32_t>();
|
||||||
|
std::vector<AssetBindEntry> binds;
|
||||||
|
binds.reserve(count);
|
||||||
|
|
||||||
|
for(size_t iter = 0; iter < count; iter++) {
|
||||||
|
uint8_t type = co_await sock.read<uint8_t>();
|
||||||
|
if(type >= (int) EnumAssets::MAX_ENUM)
|
||||||
|
protocolError();
|
||||||
|
|
||||||
|
uint32_t id = co_await sock.read<uint32_t>();
|
||||||
|
Hash_t hash;
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& table = ServerIdToDK[type];
|
||||||
|
if(id >= table.size()) {
|
||||||
|
LOG.warn() << "BindHash without domain/key for id=" << id;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& [domain, key] = table[id];
|
||||||
|
if(domain.empty() && key.empty()) {
|
||||||
|
LOG.warn() << "BindHash missing domain/key for id=" << id;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetsManager::BindResult bindResult = AM->bindServerResource(
|
||||||
|
(EnumAssets) type, (ResourceId) id, domain, key, hash, header);
|
||||||
|
|
||||||
|
if(!bindResult.Changed)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
binds.emplace_back(AssetBindEntry{
|
||||||
|
.Type = (EnumAssets) type,
|
||||||
|
.Id = bindResult.LocalId,
|
||||||
|
.Domain = domain,
|
||||||
|
.Key = key,
|
||||||
|
.Hash = hash,
|
||||||
|
.Header = std::move(header)
|
||||||
|
});
|
||||||
|
|
||||||
|
if(binds.back().Domain == "test"
|
||||||
|
&& (binds.back().Type == EnumAssets::Nodestate
|
||||||
|
|| binds.back().Type == EnumAssets::Model
|
||||||
|
|| binds.back().Type == EnumAssets::Texture))
|
||||||
|
{
|
||||||
|
uint32_t idx = debugResourceLogCount.fetch_add(1);
|
||||||
|
if(idx < 128) {
|
||||||
|
LOG.debug() << "Bind asset type=" << assetTypeName(binds.back().Type)
|
||||||
|
<< " id=" << binds.back().Id
|
||||||
|
<< " key=" << binds.back().Domain << ':' << binds.back().Key
|
||||||
|
<< " hash=" << int(binds.back().Hash[0]) << '.'
|
||||||
|
<< int(binds.back().Hash[1]) << '.'
|
||||||
|
<< int(binds.back().Hash[2]) << '.'
|
||||||
|
<< int(binds.back().Hash[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!binds.empty())
|
||||||
|
AsyncContext.AssetsBinds.lock()->push_back(AssetsBindsChange(binds, {}));
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
case ToClient::L2Resource::Lost:
|
case ToClient::L2Resource::Lost:
|
||||||
{
|
{
|
||||||
uint32_t count = co_await sock.read<uint32_t>();
|
uint32_t count = co_await sock.read<uint32_t>();
|
||||||
|
|||||||
@@ -95,6 +95,9 @@ private:
|
|||||||
std::vector<uint8_t> Header;
|
std::vector<uint8_t> Header;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::array<std::vector<std::pair<std::string, std::string>>, (int) EnumAssets::MAX_ENUM> ServerIdToDK;
|
||||||
|
std::array<ResourceId, (int) EnumAssets::MAX_ENUM> NextServerId = {};
|
||||||
|
|
||||||
struct TickData {
|
struct TickData {
|
||||||
std::vector<std::pair<DefVoxelId, void*>> Profile_Voxel_AddOrChange;
|
std::vector<std::pair<DefVoxelId, void*>> Profile_Voxel_AddOrChange;
|
||||||
std::vector<DefVoxelId> Profile_Voxel_Lost;
|
std::vector<DefVoxelId> Profile_Voxel_Lost;
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ PrecompiledTexturePipeline compileTexturePipeline(const std::string &cmd, std::s
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CompressedVoxels compressVoxels_byte(const std::vector<VoxelCube>& voxels) {
|
std::u8string compressVoxels_byte(const std::vector<VoxelCube>& voxels) {
|
||||||
std::u8string compressed;
|
std::u8string compressed;
|
||||||
std::vector<DefVoxelId> defines;
|
std::vector<DefVoxelId> defines;
|
||||||
DefVoxelId maxValue = 0;
|
DefVoxelId maxValue = 0;
|
||||||
@@ -164,10 +164,10 @@ CompressedVoxels compressVoxels_byte(const std::vector<VoxelCube>& voxels) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {compressLinear(compressed), defines};
|
return compressLinear(compressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompressedVoxels compressVoxels_bit(const std::vector<VoxelCube>& voxels) {
|
std::u8string compressVoxels_bit(const std::vector<VoxelCube>& voxels) {
|
||||||
std::vector<DefVoxelId> profile;
|
std::vector<DefVoxelId> profile;
|
||||||
std::vector<DefVoxelId> one_byte[7];
|
std::vector<DefVoxelId> one_byte[7];
|
||||||
|
|
||||||
@@ -310,10 +310,10 @@ CompressedVoxels compressVoxels_bit(const std::vector<VoxelCube>& voxels) {
|
|||||||
for(size_t iter = 0; iter < buff.size(); iter++)
|
for(size_t iter = 0; iter < buff.size(); iter++)
|
||||||
compressed[iter / 8] |= (buff[iter] << (iter % 8));
|
compressed[iter / 8] |= (buff[iter] << (iter % 8));
|
||||||
|
|
||||||
return {compressLinear(compressed), profile};
|
return compressLinear(compressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompressedVoxels compressVoxels(const std::vector<VoxelCube>& voxels, bool fast) {
|
std::u8string compressVoxels(const std::vector<VoxelCube>& voxels, bool fast) {
|
||||||
if(fast)
|
if(fast)
|
||||||
return compressVoxels_byte(voxels);
|
return compressVoxels_byte(voxels);
|
||||||
else
|
else
|
||||||
@@ -697,24 +697,8 @@ CompressedNodes compressNodes_bit(const Node* nodes) {
|
|||||||
return {compressLinear(compressed), profiles};
|
return {compressLinear(compressed), profiles};
|
||||||
}
|
}
|
||||||
|
|
||||||
CompressedNodes compressNodes(const Node* nodes, bool fast) {
|
std::u8string compressNodes(const Node* nodes, bool fast) {
|
||||||
std::u8string data(16*16*16*sizeof(Node), '\0');
|
return compressLinear(std::u8string_view((const char8_t*) nodes, 16*16*16*sizeof(Node)));
|
||||||
const char8_t *ptr = (const char8_t*) nodes;
|
|
||||||
std::copy(ptr, ptr+16*16*16*4, data.data());
|
|
||||||
|
|
||||||
std::vector<DefNodeId> node(16*16*16);
|
|
||||||
for(int iter = 0; iter < 16*16*16; iter++) {
|
|
||||||
node[iter] = nodes[iter].NodeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
std::sort(node.begin(), node.end());
|
|
||||||
auto last = std::unique(node.begin(), node.end());
|
|
||||||
node.erase(last, node.end());
|
|
||||||
node.shrink_to_fit();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {compressLinear(data), std::move(node)};
|
|
||||||
|
|
||||||
// if(fast)
|
// if(fast)
|
||||||
// return compressNodes_byte(nodes);
|
// return compressNodes_byte(nodes);
|
||||||
@@ -854,7 +838,7 @@ void unCompressNodes(const std::u8string& compressed, Node* ptr) {
|
|||||||
// return unCompressNodes_bit(next, ptr);
|
// return unCompressNodes_bit(next, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::u8string compressLinear(const std::u8string& data) {
|
std::u8string compressLinear(std::u8string_view data) {
|
||||||
std::stringstream in;
|
std::stringstream in;
|
||||||
in.write((const char*) data.data(), data.size());
|
in.write((const char*) data.data(), data.size());
|
||||||
|
|
||||||
@@ -869,7 +853,7 @@ std::u8string compressLinear(const std::u8string& data) {
|
|||||||
return *(std::u8string*) &outString;
|
return *(std::u8string*) &outString;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::u8string unCompressLinear(const std::u8string& data) {
|
std::u8string unCompressLinear(std::u8string_view data) {
|
||||||
std::stringstream in;
|
std::stringstream in;
|
||||||
in.write((const char*) data.data(), data.size());
|
in.write((const char*) data.data(), data.size());
|
||||||
|
|
||||||
@@ -1488,7 +1472,7 @@ uint16_t PreparedNodeState::parseCondition(const std::string_view expression) {
|
|||||||
// };
|
// };
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<float, std::variant<PreparedNodeState::Model, PreparedNodeState::VectorModel>> PreparedNodeState::parseModel(const std::string_view modid, const js::object& obj) {
|
std::pair<float, std::variant<HeadlessNodeState::Model, HeadlessNodeState::VectorModel>> HeadlessNodeState::parseModel(const std::string_view modid, const js::object& obj) {
|
||||||
// ModelToLocalId
|
// ModelToLocalId
|
||||||
|
|
||||||
bool uvlock;
|
bool uvlock;
|
||||||
|
|||||||
@@ -469,13 +469,7 @@ struct VoxelCube {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CompressedVoxels {
|
std::u8string compressVoxels(const std::vector<VoxelCube>& voxels, bool fast = true);
|
||||||
std::u8string Compressed;
|
|
||||||
// Уникальный сортированный список идентификаторов вокселей
|
|
||||||
std::vector<DefVoxelId> Defines;
|
|
||||||
};
|
|
||||||
|
|
||||||
CompressedVoxels compressVoxels(const std::vector<VoxelCube>& voxels, bool fast = true);
|
|
||||||
std::vector<VoxelCube> unCompressVoxels(const std::u8string& compressed);
|
std::vector<VoxelCube> unCompressVoxels(const std::u8string& compressed);
|
||||||
|
|
||||||
struct Node {
|
struct Node {
|
||||||
@@ -493,11 +487,11 @@ struct CompressedNodes {
|
|||||||
std::vector<DefNodeId> Defines;
|
std::vector<DefNodeId> Defines;
|
||||||
};
|
};
|
||||||
|
|
||||||
CompressedNodes compressNodes(const Node* nodes, bool fast = true);
|
std::u8string compressNodes(const Node* nodes, bool fast = true);
|
||||||
void unCompressNodes(const std::u8string& compressed, Node* ptr);
|
void unCompressNodes(std::u8string_view compressed, Node* ptr);
|
||||||
|
|
||||||
std::u8string compressLinear(const std::u8string& data);
|
std::u8string compressLinear(std::u8string_view data);
|
||||||
std::u8string unCompressLinear(const std::u8string& data);
|
std::u8string unCompressLinear(std::u8string_view data);
|
||||||
|
|
||||||
inline std::pair<std::string_view, std::string_view> parseDomainKey(const std::string_view value, const std::string_view defaultDomain = "core") {
|
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(':');
|
size_t pos = value.find(':');
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ AssetsPreloader::Out_reloadResources AssetsPreloader::_reloadResources(const Ass
|
|||||||
// Домен неизвестен движку, все ресурсы в нём новые
|
// Домен неизвестен движку, все ресурсы в нём новые
|
||||||
for(const auto& [key, info] : table) {
|
for(const auto& [key, info] : table) {
|
||||||
PendingResource resource = buildResource(static_cast<AssetType>(type), domain, key, info);
|
PendingResource resource = buildResource(static_cast<AssetType>(type), domain, key, info);
|
||||||
result.NewOrChange[(int) type][domain].push_back(std::move(resource));
|
result.NewOrChange[type][domain].push_back(std::move(resource));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for(const auto& [key, info] : table) {
|
for(const auto& [key, info] : table) {
|
||||||
@@ -354,14 +354,14 @@ AssetsPreloader::Out_applyResourceChange AssetsPreloader::applyResourceChange(co
|
|||||||
// Связали с хешем
|
// Связали с хешем
|
||||||
HashToId[resource.Hash] = {static_cast<AssetType>(type), pending.Id};
|
HashToId[resource.Hash] = {static_cast<AssetType>(type), pending.Id};
|
||||||
// Осведомили о новом/изменённом ресурсе
|
// Осведомили о новом/изменённом ресурсе
|
||||||
result.NewOrChange[type].push_back({pending.Id, std::move(resource)});
|
result.NewOrChange[type].emplace_back(pending.Id, resource.Hash, std::move(resource.Header));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Не должно быть ресурсов, которые были помечены как потерянные
|
// Не должно быть ресурсов, которые были помечены как потерянные
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
std::unordered_set<uint32_t> changed;
|
std::unordered_set<uint32_t> changed;
|
||||||
for(const auto& [id, _] : result.NewOrChange[type])
|
for(const auto& [id, _, _] : result.NewOrChange[type])
|
||||||
changed.insert(id);
|
changed.insert(id);
|
||||||
|
|
||||||
auto& lost = result.Lost[type];
|
auto& lost = result.Lost[type];
|
||||||
@@ -421,4 +421,4 @@ AssetsPreloader::Out_bakeId AssetsPreloader::bakeIdTables() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,19 +145,40 @@ public:
|
|||||||
std::u8string Header;
|
std::u8string Header;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BindDomainKeyInfo {
|
||||||
|
std::string Domain;
|
||||||
|
std::string Key;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BindHashHeaderInfo {
|
||||||
|
ResourceId Id;
|
||||||
|
Hash_t Hash;
|
||||||
|
std::u8string Header;
|
||||||
|
};
|
||||||
|
|
||||||
struct Out_reloadResources {
|
struct Out_reloadResources {
|
||||||
std::unordered_map<std::string, std::vector<std::string>> Lost[(int) AssetType::MAX_ENUM];
|
|
||||||
std::unordered_map<std::string, std::vector<PendingResource>> NewOrChange[(int) AssetType::MAX_ENUM];
|
std::unordered_map<std::string, std::vector<PendingResource>> NewOrChange[(int) AssetType::MAX_ENUM];
|
||||||
|
std::unordered_map<std::string, std::vector<std::string>> Lost[(int) AssetType::MAX_ENUM];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Out_applyResourceChange {
|
struct Out_applyResourceChange {
|
||||||
std::vector<uint32_t> Lost[(int) AssetType::MAX_ENUM];
|
std::array<
|
||||||
std::vector<std::pair<uint32_t, MediaResource>> NewOrChange[(int) AssetType::MAX_ENUM];
|
std::vector<AssetsPreloader::BindHashHeaderInfo>,
|
||||||
|
static_cast<size_t>(AssetType::MAX_ENUM)
|
||||||
|
> NewOrChange;
|
||||||
|
|
||||||
|
std::array<
|
||||||
|
std::vector<ResourceId>,
|
||||||
|
static_cast<size_t>(AssetType::MAX_ENUM)
|
||||||
|
> Lost;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Out_bakeId {
|
struct Out_bakeId {
|
||||||
// Новые привязки
|
// Новые привязки
|
||||||
std::array<std::vector<std::pair<std::string, std::string>>, static_cast<size_t>(AssetType::MAX_ENUM)> IdToDK;
|
std::array<
|
||||||
|
std::vector<BindDomainKeyInfo>,
|
||||||
|
static_cast<size_t>(AssetType::MAX_ENUM)
|
||||||
|
> IdToDK;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ReloadStatus {
|
struct ReloadStatus {
|
||||||
@@ -297,7 +318,7 @@ private:
|
|||||||
Многопоточная таблица обратного резолва.
|
Многопоточная таблица обратного резолва.
|
||||||
Идентификатор -> домен+ключ
|
Идентификатор -> домен+ключ
|
||||||
*/
|
*/
|
||||||
std::array<std::vector<std::pair<std::string, std::string>>, static_cast<size_t>(AssetType::MAX_ENUM)> IdToDK;
|
std::array<std::vector<BindDomainKeyInfo>, static_cast<size_t>(AssetType::MAX_ENUM)> IdToDK;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Таблица в которой выделяются новые идентификаторы, которых не нашлось в DKToId.
|
Таблица в которой выделяются новые идентификаторы, которых не нашлось в DKToId.
|
||||||
@@ -313,7 +334,7 @@ private:
|
|||||||
Списки в которых пишутся новые привязки. Начала спиской исходят из LastSendId.
|
Списки в которых пишутся новые привязки. Начала спиской исходят из LastSendId.
|
||||||
Id + LastSendId -> домен+ключ
|
Id + LastSendId -> домен+ключ
|
||||||
*/
|
*/
|
||||||
std::array<TOS::SpinlockObject<std::vector<std::pair<std::string, std::string>>>, static_cast<size_t>(AssetType::MAX_ENUM)> NewIdToDK;
|
std::array<TOS::SpinlockObject<std::vector<BindDomainKeyInfo>>, static_cast<size_t>(AssetType::MAX_ENUM)> NewIdToDK;
|
||||||
|
|
||||||
// Загруженные ресурсы
|
// Загруженные ресурсы
|
||||||
std::array<std::unordered_map<ResourceId, MediaResource>, static_cast<size_t>(AssetType::MAX_ENUM)> MediaResources;
|
std::array<std::unordered_map<ResourceId, MediaResource>, static_cast<size_t>(AssetType::MAX_ENUM)> MediaResources;
|
||||||
@@ -370,7 +391,7 @@ inline ResourceId AssetsPreloader::_getIdNew(AssetType type, std::string_view do
|
|||||||
auto lock2 = NewIdToDK[static_cast<size_t>(type)].lock();
|
auto lock2 = NewIdToDK[static_cast<size_t>(type)].lock();
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
lock2->emplace_back(domain, key);
|
lock2->emplace_back((std::string) domain, (std::string) key);
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@@ -389,17 +410,17 @@ inline std::optional<std::tuple<AssetType, uint32_t, const AssetsPreloader::Medi
|
|||||||
AssetsPreloader::getResource(const ResourceFile::Hash_t& hash)
|
AssetsPreloader::getResource(const ResourceFile::Hash_t& hash)
|
||||||
{
|
{
|
||||||
auto iter = HashToId.find(hash);
|
auto iter = HashToId.find(hash);
|
||||||
if (iter == HashToId.end())
|
if(iter == HashToId.end())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
auto [type, id] = iter->second;
|
auto [type, id] = iter->second;
|
||||||
const MediaResource* res = getResource(type, id);
|
const MediaResource* res = getResource(type, id);
|
||||||
if (!res) {
|
if(!res) {
|
||||||
HashToId.erase(iter);
|
HashToId.erase(iter);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res->Hash != hash) {
|
if(res->Hash != hash) {
|
||||||
HashToId.erase(iter);
|
HashToId.erase(iter);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,106 +83,26 @@ enum struct L2System : uint8_t {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ToClient {
|
enum struct ToClient : uint8_t {
|
||||||
|
Init, // Первый пакет от сервера
|
||||||
|
Disconnect, // Отключаем клиента
|
||||||
|
|
||||||
/*
|
AssetsBindDK, // Привязка AssetsId к домен+ключ
|
||||||
uint8_t+uint8_t
|
AssetsBindHH, // Привязка AssetsId к hash+header
|
||||||
0 - Системное
|
AssetsInitSend, // Начало отправки запрошенного клиентом ресурса
|
||||||
0 - Инициализация WorldId_c+ObjectPos
|
AssetsNextSend, // Продолжение отправки ресурса
|
||||||
1 - Отключение от сервера String(Причина)
|
|
||||||
2 - Привязка камеры к сущности EntityId_c
|
|
||||||
3 - Отвязка камеры
|
|
||||||
1 - Оповещение о доступном ресурсе
|
|
||||||
0 - Текстура TextureId_c+Hash
|
|
||||||
1 - Освобождение текстуры TextureId_c
|
|
||||||
2 - Звук SoundId_c+Hash
|
|
||||||
3 - Освобождение звука SoundId_c
|
|
||||||
4 - Модель ModelId_c+Hash
|
|
||||||
5 - Освобождение модели ModelId_c
|
|
||||||
253 - Инициирование передачи ресурса StreamId+ResType+ResId+Size+Hash
|
|
||||||
254 - Передача чанка данных StreamId+Size+Data
|
|
||||||
255 - Передача отменена StreamId
|
|
||||||
2 - Новые определения
|
|
||||||
0 - Мир DefWorldId_c+определение
|
|
||||||
1 - Освобождение мира DefWorldId_c
|
|
||||||
2 - Воксель DefVoxelId_c+определение
|
|
||||||
3 - Освобождение вокселя DefVoxelId_c
|
|
||||||
4 - Нода DefNodeId_c+определение
|
|
||||||
5 - Освобождение ноды DefNodeId_c
|
|
||||||
6 - Портал DefPortalId_c+определение
|
|
||||||
7 - Освобождение портала DefPortalId_c
|
|
||||||
8 - Сущность DefEntityId_c+определение
|
|
||||||
9 - Освобождение сущности DefEntityId_c
|
|
||||||
3 - Новый контент
|
|
||||||
0 - Мир, новый/изменён WorldId_c+...
|
|
||||||
1 - Мир/Удалён WorldId_c
|
|
||||||
2 - Портал, новый/изменён PortalId_c+...
|
|
||||||
3 - Портал/Удалён PortalId_c
|
|
||||||
4 - Сущность, новый/изменён EntityId_c+...
|
|
||||||
5 - Сущность/Удалёна EntityId_c
|
|
||||||
6 - Чанк/Воксели WorldId_c+GlobalChunk+...
|
|
||||||
7 - Чанк/Ноды WorldId_c+GlobalChunk+...
|
|
||||||
8 - Чанк/Призмы освещения WorldId_c+GlobalChunk+...
|
|
||||||
9 - Чанк/Удалён WorldId_c+GlobalChunk
|
|
||||||
|
|
||||||
|
DefinitionsUpdate, // Обновление и потеря профилей контента (воксели, ноды, сущности, миры, ...)
|
||||||
|
|
||||||
|
ChunkVoxels, // Обновление вокселей чанка
|
||||||
|
ChunkNodes, // Обновление нод чанка
|
||||||
|
ChunkLightPrism, //
|
||||||
|
RemoveRegion, // Удаление региона из зоны видимости
|
||||||
|
|
||||||
*/
|
Tick, // Новые или потерянные игровые объекты (миры, сущности), динамичные данные такта (положение сущностей)
|
||||||
|
|
||||||
// Первый уровень
|
TestLinkCameraToEntity, // Привязываем камеру к сущности
|
||||||
enum struct L1 : uint8_t {
|
TestUnlinkCamera, // Отвязываем от сущности
|
||||||
System,
|
|
||||||
Resource,
|
|
||||||
Definition,
|
|
||||||
Content
|
|
||||||
};
|
|
||||||
|
|
||||||
// Второй уровень
|
|
||||||
enum struct L2System : uint8_t {
|
|
||||||
Init,
|
|
||||||
Disconnect,
|
|
||||||
LinkCameraToEntity,
|
|
||||||
UnlinkCamera,
|
|
||||||
SyncTick
|
|
||||||
};
|
|
||||||
|
|
||||||
enum struct L2Resource : uint8_t {
|
|
||||||
Bind, // Привязка идентификаторов ресурсов к хешам
|
|
||||||
Lost,
|
|
||||||
InitResSend = 253,
|
|
||||||
ChunkSend
|
|
||||||
};
|
|
||||||
|
|
||||||
enum struct L2Definition : uint8_t {
|
|
||||||
World,
|
|
||||||
FreeWorld,
|
|
||||||
Voxel,
|
|
||||||
FreeVoxel,
|
|
||||||
Node,
|
|
||||||
FreeNode,
|
|
||||||
Portal,
|
|
||||||
FreePortal,
|
|
||||||
Entity,
|
|
||||||
FreeEntity,
|
|
||||||
FuncEntity,
|
|
||||||
FreeFuncEntity,
|
|
||||||
Item,
|
|
||||||
FreeItem
|
|
||||||
};
|
|
||||||
|
|
||||||
enum struct L2Content : uint8_t {
|
|
||||||
World,
|
|
||||||
RemoveWorld,
|
|
||||||
Portal,
|
|
||||||
RemovePortal,
|
|
||||||
Entity,
|
|
||||||
RemoveEntity,
|
|
||||||
ChunkVoxels,
|
|
||||||
ChunkNodes,
|
|
||||||
ChunkLightPrism,
|
|
||||||
RemoveRegion
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -619,35 +619,13 @@ void GameServer::BackingChunkPressure_t::run(int id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Сжатие и отправка игрокам
|
// Сжатие и отправка игрокам
|
||||||
struct PostponedV {
|
|
||||||
WorldId_t WorldId;
|
|
||||||
Pos::GlobalChunk Chunk;
|
|
||||||
CompressedVoxels Data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PostponedN {
|
|
||||||
WorldId_t WorldId;
|
|
||||||
Pos::GlobalChunk Chunk;
|
|
||||||
CompressedNodes Data;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::list<std::pair<PostponedV, std::vector<RemoteClient*>>> postponedVoxels;
|
|
||||||
std::list<std::pair<PostponedN, std::vector<RemoteClient*>>> postponedNodes;
|
|
||||||
|
|
||||||
std::vector<RemoteClient*> cecs;
|
|
||||||
|
|
||||||
for(auto& [worldId, world] : dump) {
|
for(auto& [worldId, world] : dump) {
|
||||||
for(auto& [regionPos, region] : world) {
|
for(auto& [regionPos, region] : world) {
|
||||||
for(auto& [chunkPos, chunk] : region.Voxels) {
|
for(auto& [chunkPos, chunk] : region.Voxels) {
|
||||||
CompressedVoxels cmp = compressVoxels(chunk);
|
std::u8string cmp = compressVoxels(chunk);
|
||||||
Pos::GlobalChunk chunkPosR = (Pos::GlobalChunk(regionPos) << 2) + chunkPos;
|
|
||||||
|
|
||||||
for(auto& ptr : region.NewCECs) {
|
for(auto& ptr : region.NewCECs) {
|
||||||
bool accepted = ptr->maybe_prepareChunkUpdate_Voxels(worldId,
|
ptr->prepareChunkUpdate_Voxels(worldId, regionPos, chunkPos, cmp);
|
||||||
chunkPosR, cmp.Compressed, cmp.Defines);
|
|
||||||
if(!accepted) {
|
|
||||||
cecs.push_back(ptr.get());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if((region.IsChunkChanged_Voxels >> chunkPos.pack()) & 0x1) {
|
if((region.IsChunkChanged_Voxels >> chunkPos.pack()) & 0x1) {
|
||||||
@@ -663,30 +641,16 @@ void GameServer::BackingChunkPressure_t::run(int id) {
|
|||||||
if(skip)
|
if(skip)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bool accepted = ptr->maybe_prepareChunkUpdate_Voxels(worldId,
|
ptr->prepareChunkUpdate_Voxels(worldId, regionPos, chunkPos, cmp);
|
||||||
chunkPosR, cmp.Compressed, cmp.Defines);
|
|
||||||
if(!accepted) {
|
|
||||||
cecs.push_back(ptr.get());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!cecs.empty()) {
|
|
||||||
postponedVoxels.push_back({{worldId, chunkPosR, std::move(cmp)}, cecs});
|
|
||||||
cecs.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& [chunkPos, chunk] : region.Nodes) {
|
for(auto& [chunkPos, chunk] : region.Nodes) {
|
||||||
CompressedNodes cmp = compressNodes(chunk.data());
|
std::u8string cmp = compressNodes(chunk.data());
|
||||||
Pos::GlobalChunk chunkPosR = (Pos::GlobalChunk(regionPos) << 2) + chunkPos;
|
|
||||||
|
|
||||||
for(auto& ptr : region.NewCECs) {
|
for(auto& ptr : region.NewCECs) {
|
||||||
bool accepted = ptr->maybe_prepareChunkUpdate_Nodes(worldId,
|
ptr->prepareChunkUpdate_Nodes(worldId, regionPos, chunkPos, cmp);
|
||||||
chunkPosR, cmp.Compressed, cmp.Defines);
|
|
||||||
if(!accepted) {
|
|
||||||
cecs.push_back(ptr.get());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if((region.IsChunkChanged_Nodes >> chunkPos.pack()) & 0x1) {
|
if((region.IsChunkChanged_Nodes >> chunkPos.pack()) & 0x1) {
|
||||||
@@ -702,60 +666,9 @@ void GameServer::BackingChunkPressure_t::run(int id) {
|
|||||||
if(skip)
|
if(skip)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bool accepted = ptr->maybe_prepareChunkUpdate_Nodes(worldId,
|
ptr->prepareChunkUpdate_Nodes(worldId, regionPos, chunkPos, cmp);
|
||||||
chunkPosR, cmp.Compressed, cmp.Defines);
|
|
||||||
if(!accepted) {
|
|
||||||
cecs.push_back(ptr.get());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!cecs.empty()) {
|
|
||||||
postponedNodes.push_back({{worldId, chunkPosR, std::move(cmp)}, cecs});
|
|
||||||
cecs.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while(!postponedVoxels.empty() || !postponedNodes.empty()) {
|
|
||||||
{
|
|
||||||
auto begin = postponedVoxels.begin(), end = postponedVoxels.end();
|
|
||||||
while(begin != end) {
|
|
||||||
auto& [worldId, chunkPos, cmp] = begin->first;
|
|
||||||
for(RemoteClient* cec : begin->second) {
|
|
||||||
bool accepted = cec->maybe_prepareChunkUpdate_Voxels(worldId, chunkPos, cmp.Compressed, cmp.Defines);
|
|
||||||
if(!accepted)
|
|
||||||
cecs.push_back(cec);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cecs.empty()) {
|
|
||||||
begin = postponedVoxels.erase(begin);
|
|
||||||
} else {
|
|
||||||
begin->second = cecs;
|
|
||||||
cecs.clear();
|
|
||||||
begin++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto begin = postponedNodes.begin(), end = postponedNodes.end();
|
|
||||||
while(begin != end) {
|
|
||||||
auto& [worldId, chunkPos, cmp] = begin->first;
|
|
||||||
for(RemoteClient* cec : begin->second) {
|
|
||||||
bool accepted = cec->maybe_prepareChunkUpdate_Nodes(worldId, chunkPos, cmp.Compressed, cmp.Defines);
|
|
||||||
if(!accepted)
|
|
||||||
cecs.push_back(cec);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cecs.empty()) {
|
|
||||||
begin = postponedNodes.erase(begin);
|
|
||||||
} else {
|
|
||||||
begin->second = cecs;
|
|
||||||
cecs.clear();
|
|
||||||
begin++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1880,33 +1793,43 @@ void GameServer::stepModInitializations() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GameServer::reloadMods() {
|
void GameServer::reloadMods() {
|
||||||
LOG.info() << "Перезагрузка модов: ассеты и зависимости";
|
std::vector<Net::Packet> packetsToSend;
|
||||||
|
|
||||||
AssetsPreloader::Out_reloadResources changes = Content.AM.reloadResources(AssetsInit);
|
LOG.info() << "Перезагрузка модов";
|
||||||
AssetsPreloader::Out_applyResourceChange applied = Content.AM.applyResourceChange(changes);
|
{
|
||||||
|
// TODO: перезагрузка модов
|
||||||
|
|
||||||
size_t changedCount = 0;
|
Content.CM.buildEndProfiles();
|
||||||
size_t lostCount = 0;
|
}
|
||||||
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) {
|
|
||||||
for(const auto& entry : applied.NewOrChange[type]) {
|
LOG.info() << "Перезагрузка ассетов";
|
||||||
Content.OnContentChanges.AssetsInfo[type].push_back(entry.first);
|
{
|
||||||
changedCount++;
|
{
|
||||||
|
AssetsPreloader::Out_applyResourceChange applied
|
||||||
|
= Content.AM.applyResourceChange(Content.AM.reloadResources(AssetsInit));
|
||||||
|
|
||||||
|
if(!applied.NewOrChange.empty() || !applied.Lost.empty())
|
||||||
|
packetsToSend.push_back(
|
||||||
|
RemoteClient::makePacket_informateAssets_HH(
|
||||||
|
applied.NewOrChange,
|
||||||
|
applied.Lost
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
lostCount += applied.Lost[type].size();
|
{
|
||||||
|
AssetsPreloader::Out_bakeId baked = Content.AM.bakeIdTables();
|
||||||
|
if(!baked.IdToDK.empty())
|
||||||
|
packetsToSend.push_back(RemoteClient::makePacket_informateAssets_DK(baked.IdToDK));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Content.CM.markAllProfilesDirty(EnumDefContent::Node);
|
// Отправка пакетов
|
||||||
Content.CM.buildEndProfiles();
|
for(std::shared_ptr<RemoteClient>& cec : Game.RemoteClients) {
|
||||||
|
auto copy = packetsToSend;
|
||||||
std::vector<ResourceId> nodeIds = Content.CM.collectProfileIds(EnumDefContent::Node);
|
cec->pushPackets(©);
|
||||||
if(!nodeIds.empty()) {
|
|
||||||
Content.OnContentChanges.Node.append_range(nodeIds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.info() << "Перезагрузка завершена: обновлено ассетов=" << changedCount
|
|
||||||
<< " удалено=" << lostCount
|
|
||||||
<< " нод=" << nodeIds.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IWorldSaveBackend::TickSyncInfo_Out GameServer::stepDatabaseSync() {
|
IWorldSaveBackend::TickSyncInfo_Out GameServer::stepDatabaseSync() {
|
||||||
@@ -2678,107 +2601,48 @@ void GameServer::stepSyncContent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Сбор запросов на ресурсы и профили + отправка пакетов игрокам
|
// Сбор запросов на ресурсы + отправка пакетов игрокам
|
||||||
ResourceRequest full = std::move(Content.OnContentChanges);
|
ResourceRequest full = std::move(Content.OnContentChanges);
|
||||||
for(std::shared_ptr<RemoteClient>& cec : Game.RemoteClients) {
|
for(std::shared_ptr<RemoteClient>& cec : Game.RemoteClients) {
|
||||||
full.insert(cec->pushPreparedPackets());
|
full.merge(cec->pushPreparedPackets());
|
||||||
}
|
}
|
||||||
|
|
||||||
full.uniq();
|
full.uniq();
|
||||||
|
|
||||||
// Информируем о запрошенных ассетах
|
std::vector<Net::Packet> packetsToAll;
|
||||||
std::vector<std::tuple<EnumAssets, ResourceId, std::string, std::string, Resource, std::u8string>> resources;
|
{
|
||||||
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) {
|
AssetsPreloader::Out_bakeId baked = Content.AM.bakeIdTables();
|
||||||
for(ResourceId resId : full.AssetsInfo[type]) {
|
if(!baked.IdToDK.empty()) {
|
||||||
const AssetsPreloader::MediaResource* media = Content.AM.getResource((EnumAssets) type, resId);
|
packetsToAll.push_back(RemoteClient::makePacket_informateAssets_DK(baked.IdToDK));
|
||||||
if(!media)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Resource resource(media->Resource->data(), media->Resource->size());
|
|
||||||
resources.emplace_back((EnumAssets) type, resId, media->Domain, media->Key, std::move(resource), media->Dependencies);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Оповещаем о двоичных ресурсах по запросу
|
||||||
|
std::vector<AssetBinaryInfo> binaryResources;
|
||||||
for(const Hash_t& hash : full.Hashes) {
|
for(const Hash_t& hash : full.Hashes) {
|
||||||
std::optional<std::tuple<EnumAssets, uint32_t, const AssetsPreloader::MediaResource*>> result = Content.AM.getResource(hash);
|
std::optional<
|
||||||
|
std::tuple<EnumAssets, uint32_t, const AssetsPreloader::MediaResource*>
|
||||||
|
> result = Content.AM.getResource(hash);
|
||||||
|
|
||||||
if(!result)
|
if(!result)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto& [type, id, media] = *result;
|
auto& [type, id, media] = *result;
|
||||||
Resource resource(*media->Resource);
|
Resource resource(*media->Resource);
|
||||||
resources.emplace_back(type, id, media->Domain, media->Key, std::move(resource), media->Header);
|
binaryResources.push_back(AssetBinaryInfo{
|
||||||
}
|
.Data = std::move(resource),
|
||||||
|
.Hash = media->Hash
|
||||||
// Информируем о запрошенных профилях
|
});
|
||||||
std::vector<std::pair<DefVoxelId, DefVoxel*>> voxels;
|
|
||||||
for(DefVoxelId id : full.Voxel) {
|
|
||||||
auto value = Content.CM.getProfile_Voxel(id);
|
|
||||||
if(!value)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
voxels.emplace_back(id, *value);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::pair<DefNodeId, DefNode*>> nodes;
|
|
||||||
for(DefNodeId id : full.Node) {
|
|
||||||
auto value = Content.CM.getProfile_Node(id);
|
|
||||||
if(!value)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
nodes.emplace_back(id, *value);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::pair<DefWorldId, DefWorld*>> worlds;
|
|
||||||
for(DefWorldId id : full.World) {
|
|
||||||
auto value = Content.CM.getProfile_World(id);
|
|
||||||
if(!value)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
worlds.emplace_back(id, *value);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::pair<DefPortalId, DefPortal*>> portals;
|
|
||||||
for(DefPortalId id : full.Portal) {
|
|
||||||
auto value = Content.CM.getProfile_Portal(id);
|
|
||||||
if(!value)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
portals.emplace_back(id, *value);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::pair<DefEntityId, DefEntity*>> entities;
|
|
||||||
for(DefEntityId id : full.Entity) {
|
|
||||||
auto value = Content.CM.getProfile_Entity(id);
|
|
||||||
if(!value)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
entities.emplace_back(id, *value);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::pair<DefItemId, DefItem*>> items;
|
|
||||||
for(DefItemId id : full.Item) {
|
|
||||||
auto value = Content.CM.getProfile_Item(id);
|
|
||||||
if(!value)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
items.emplace_back(id, *value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for(std::shared_ptr<RemoteClient>& remoteClient : Game.RemoteClients) {
|
for(std::shared_ptr<RemoteClient>& remoteClient : Game.RemoteClients) {
|
||||||
if(!resources.empty())
|
if(!binaryResources.empty())
|
||||||
remoteClient->informateAssets(resources);
|
remoteClient->informateBinaryAssets(binaryResources);
|
||||||
if(!voxels.empty())
|
|
||||||
remoteClient->informateDefVoxel(voxels);
|
if(!packetsToAll.empty()) {
|
||||||
if(!nodes.empty())
|
auto copy = packetsToAll;
|
||||||
remoteClient->informateDefNode(nodes);
|
remoteClient->pushPackets(©);
|
||||||
if(!worlds.empty())
|
}
|
||||||
remoteClient->informateDefWorld(worlds);
|
|
||||||
if(!portals.empty())
|
|
||||||
remoteClient->informateDefPortal(portals);
|
|
||||||
if(!entities.empty())
|
|
||||||
remoteClient->informateDefEntity(entities);
|
|
||||||
if(!items.empty())
|
|
||||||
remoteClient->informateDefItem(items);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ class GameServer : public AsyncObject {
|
|||||||
ResourceRequest OnContentChanges;
|
ResourceRequest OnContentChanges;
|
||||||
|
|
||||||
|
|
||||||
ContentObj(asio::io_context& ioc)
|
ContentObj(asio::io_context&)
|
||||||
: AM(ioc), CM(AM)
|
: AM(), CM(AM)
|
||||||
{}
|
{}
|
||||||
} Content;
|
} Content;
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,153 @@
|
|||||||
#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 {
|
||||||
|
|
||||||
|
Net::Packet RemoteClient::makePacket_informateAssets_DK(
|
||||||
|
const std::array<
|
||||||
|
std::vector<AssetsPreloader::BindDomainKeyInfo>,
|
||||||
|
static_cast<size_t>(EnumAssets::MAX_ENUM)
|
||||||
|
>& dkVector
|
||||||
|
) {
|
||||||
|
Net::Packet pack;
|
||||||
|
|
||||||
|
// Сжатие по дедубликации доменов
|
||||||
|
std::unordered_map<std::string, uint16_t> domainsToId;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unordered_set<std::string> domains;
|
||||||
|
|
||||||
|
for(size_t type = 0; type < static_cast<size_t>(EnumAssets::MAX_ENUM); type++) {
|
||||||
|
for(const auto& bind : dkVector[type]) {
|
||||||
|
domains.insert(bind.Domain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pack << uint16_t(domains.size());
|
||||||
|
|
||||||
|
int counter = 0;
|
||||||
|
for(const std::string& domain : domains) {
|
||||||
|
pack << domain;
|
||||||
|
domainsToId[domain] = counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запись связок домен+ключ
|
||||||
|
for(size_t type = 0; type < static_cast<size_t>(EnumAssets::MAX_ENUM); type++) {
|
||||||
|
const std::vector<AssetsPreloader::BindDomainKeyInfo>& binds = dkVector[type];
|
||||||
|
pack << uint32_t(binds.size());
|
||||||
|
|
||||||
|
for(const auto& bind : binds) {
|
||||||
|
auto iter = domainsToId.find(bind.Domain);
|
||||||
|
assert(iter != domainsToId.end());
|
||||||
|
|
||||||
|
pack << iter->second << bind.Key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сжатие
|
||||||
|
std::u8string compressed = compressLinear(pack.complite());
|
||||||
|
pack << uint8_t(ToClient::AssetsBindDK) << (const std::string&) compressed;
|
||||||
|
|
||||||
|
return pack;
|
||||||
|
}
|
||||||
|
|
||||||
|
Net::Packet RemoteClient::makePacket_informateAssets_HH(
|
||||||
|
const std::array<
|
||||||
|
std::vector<AssetsPreloader::BindHashHeaderInfo>,
|
||||||
|
static_cast<size_t>(EnumAssets::MAX_ENUM)
|
||||||
|
>& hhVector,
|
||||||
|
const std::array<
|
||||||
|
std::vector<ResourceId>,
|
||||||
|
static_cast<size_t>(EnumAssets::MAX_ENUM)
|
||||||
|
>& lost
|
||||||
|
) {
|
||||||
|
Net::Packet pack;
|
||||||
|
pack << uint8_t(ToClient::AssetsBindHH);
|
||||||
|
|
||||||
|
// Запись связок hash+header
|
||||||
|
for(size_t type = 0; type < static_cast<size_t>(EnumAssets::MAX_ENUM); type++) {
|
||||||
|
const std::vector<AssetsPreloader::BindHashHeaderInfo>& binds = hhVector[type];
|
||||||
|
pack << uint32_t(binds.size());
|
||||||
|
|
||||||
|
for(const auto& bind : binds) {
|
||||||
|
pack << bind.Id;
|
||||||
|
pack.write((const std::byte*) bind.Hash.data(), bind.Hash.size());
|
||||||
|
pack << (const std::string&) bind.Header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pack;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Net::Packet> RemoteClient::makePackets_sendDefContentUpdate(
|
||||||
|
std::array<
|
||||||
|
std::vector<
|
||||||
|
std::pair<
|
||||||
|
ResourceId, // Идентификатор профиля
|
||||||
|
std::u8string // Двоичный формат профиля
|
||||||
|
>
|
||||||
|
>,
|
||||||
|
static_cast<size_t>(EnumDefContent::MAX_ENUM)
|
||||||
|
> newOrUpdate, // Новые или изменённые
|
||||||
|
std::array<
|
||||||
|
std::vector<ResourceId>,
|
||||||
|
static_cast<size_t>(EnumDefContent::MAX_ENUM)
|
||||||
|
> lost, // Потерянные профили
|
||||||
|
std::array<
|
||||||
|
std::vector<std::pair<std::string, std::string>>,
|
||||||
|
static_cast<size_t>(EnumDefContent::MAX_ENUM)
|
||||||
|
> idToDK // Новые привязки
|
||||||
|
) {
|
||||||
|
std::vector<Net::Packet> packets;
|
||||||
|
Net::Packet pack;
|
||||||
|
|
||||||
|
auto check = [&](size_t needSize) {
|
||||||
|
if(pack.size()+needSize > 65500) {
|
||||||
|
packets.emplace_back(std::move(pack));
|
||||||
|
pack.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pack << (uint8_t) ToClient::DefinitionsUpdate;
|
||||||
|
pack << uint32_t(newOrUpdate.size());
|
||||||
|
for(size_t type = 0; type < static_cast<size_t>(EnumDefContent::MAX_ENUM); type++) {
|
||||||
|
pack << uint32_t(newOrUpdate[type].size());
|
||||||
|
|
||||||
|
for(const auto& [id, data] : newOrUpdate[type]) {
|
||||||
|
check(data.size());
|
||||||
|
pack << id << (const std::string&) data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pack << uint32_t(lost.size());
|
||||||
|
for(size_t type = 0; type < static_cast<size_t>(EnumDefContent::MAX_ENUM); type++) {
|
||||||
|
pack << uint32_t(lost[type].size());
|
||||||
|
|
||||||
|
for(ResourceId id : lost[type]) {
|
||||||
|
check(4);
|
||||||
|
pack << id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pack << uint32_t(idToDK.size());
|
||||||
|
for(size_t type = 0; type < static_cast<size_t>(EnumDefContent::MAX_ENUM); type++) {
|
||||||
|
pack << uint32_t(idToDK[type].size());
|
||||||
|
|
||||||
|
for(const auto& [domain, key] : idToDK[type]) {
|
||||||
|
check(domain.size() + key.size() + 8);
|
||||||
|
pack << key << domain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pack.size())
|
||||||
|
packets.emplace_back(std::move(pack));
|
||||||
|
|
||||||
|
return packets;
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
const char* assetTypeName(EnumAssets type) {
|
const char* assetTypeName(EnumAssets type) {
|
||||||
@@ -71,8 +213,7 @@ void RemoteClient::shutdown(EnumDisconnect type, const std::string reason) {
|
|||||||
IsGoingShutdown = true;
|
IsGoingShutdown = true;
|
||||||
|
|
||||||
Net::Packet packet;
|
Net::Packet packet;
|
||||||
packet << (uint8_t) ToClient::L1::System
|
packet << (uint8_t) ToClient::Disconnect
|
||||||
<< (uint8_t) ToClient::L2System::Disconnect
|
|
||||||
<< (uint8_t) type << reason;
|
<< (uint8_t) type << reason;
|
||||||
|
|
||||||
std::string info;
|
std::string info;
|
||||||
@@ -88,309 +229,64 @@ void RemoteClient::shutdown(EnumDisconnect type, const std::string reason) {
|
|||||||
LOG.info() << "Игрок '" << Username << "' отключился " << info;
|
LOG.info() << "Игрок '" << Username << "' отключился " << info;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels,
|
|
||||||
const std::vector<DefVoxelId>& uniq_sorted_defines)
|
|
||||||
{
|
|
||||||
Pos::bvec4u localChunk = chunkPos & 0x3;
|
|
||||||
Pos::GlobalRegion regionPos = chunkPos >> 2;
|
|
||||||
|
|
||||||
/*
|
// void RemoteClient::prepareChunkUpdate_Voxels(
|
||||||
Обновить зависимости
|
// WorldId_t worldId,
|
||||||
Запросить недостающие
|
// Pos::GlobalChunk chunkPos,
|
||||||
Отправить всё клиенту
|
// const std::u8string& compressed_voxels
|
||||||
*/
|
// ) {
|
||||||
|
// Pos::bvec4u localChunk = chunkPos & 0x3;
|
||||||
|
// Pos::GlobalRegion regionPos = chunkPos >> 2;
|
||||||
|
|
||||||
std::vector<DefVoxelId>
|
// packet << (uint8_t) ToClient::ChunkVoxels
|
||||||
newTypes, /* Новые типы вокселей */
|
// << worldId << chunkPos.pack() << uint32_t(compressed_voxels.size());
|
||||||
lostTypes /* Потерянные типы вокселей */;
|
// packet.write((const std::byte*) compressed_voxels.data(), compressed_voxels.size());
|
||||||
|
// }
|
||||||
|
|
||||||
// Отметим использование этих вокселей
|
// void RemoteClient::prepareChunkUpdate_Nodes(
|
||||||
for(const DefVoxelId& id : uniq_sorted_defines) {
|
// WorldId_t worldId,
|
||||||
auto iter = ResUses.DefVoxel.find(id);
|
// Pos::GlobalChunk chunkPos,
|
||||||
if(iter == ResUses.DefVoxel.end()) {
|
// const std::u8string& compressed_nodes
|
||||||
// Новый тип
|
// ) {
|
||||||
newTypes.push_back(id);
|
// Pos::bvec4u localChunk = chunkPos & 0x3;
|
||||||
ResUses.DefVoxel[id] = 1;
|
// Pos::GlobalRegion regionPos = chunkPos >> 2;
|
||||||
} else {
|
|
||||||
// Увеличиваем счётчик
|
|
||||||
iter->second++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto iterWorld = ResUses.RefChunk.find(worldId);
|
// packet << (uint8_t) ToClient::ChunkNodes
|
||||||
|
// << worldId << chunkPos.pack() << uint32_t(compressed_nodes.size());
|
||||||
if(iterWorld != ResUses.RefChunk.end())
|
// packet.write((const std::byte*) compressed_nodes.data(), compressed_nodes.size());
|
||||||
// Исключим зависимости предыдущей версии чанка
|
// }
|
||||||
{
|
|
||||||
auto iterRegion = iterWorld->second.find(regionPos);
|
|
||||||
if(iterRegion != iterWorld->second.end()) {
|
|
||||||
// Уменьшим счётчик зависимостей
|
|
||||||
for(const DefVoxelId& id : iterRegion->second[localChunk.pack()].Voxel) {
|
|
||||||
auto iter = ResUses.DefVoxel.find(id);
|
|
||||||
assert(iter != ResUses.DefVoxel.end()); // Воксель должен быть в зависимостях
|
|
||||||
if(--iter->second == 0) {
|
|
||||||
// Вокселя больше нет в зависимостях
|
|
||||||
lostTypes.push_back(id);
|
|
||||||
ResUses.DefVoxel.erase(iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ResUses.RefChunk[worldId] = {};
|
|
||||||
iterWorld = ResUses.RefChunk.find(worldId);
|
|
||||||
}
|
|
||||||
|
|
||||||
iterWorld->second[regionPos][localChunk.pack()].Voxel = uniq_sorted_defines;
|
|
||||||
|
|
||||||
if(!newTypes.empty()) {
|
|
||||||
// Добавляем новые типы в запрос
|
|
||||||
NextRequest.Voxel.insert(NextRequest.Voxel.end(), newTypes.begin(), newTypes.end());
|
|
||||||
for(DefVoxelId voxel : newTypes)
|
|
||||||
ResUses.RefDefVoxel[voxel] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!lostTypes.empty()) {
|
|
||||||
for(const DefVoxelId& id : lostTypes) {
|
|
||||||
auto iter = ResUses.RefDefVoxel.find(id);
|
|
||||||
assert(iter != ResUses.RefDefVoxel.end()); // Должны быть описаны зависимости вокселя
|
|
||||||
decrementAssets(std::move(iter->second));
|
|
||||||
ResUses.RefDefVoxel.erase(iter);
|
|
||||||
|
|
||||||
checkPacketBorder(16);
|
|
||||||
NextPacket << (uint8_t) ToClient::L1::Definition
|
|
||||||
<< (uint8_t) ToClient::L2Definition::FreeVoxel
|
|
||||||
<< id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkPacketBorder(4+4+8+2+4+compressed_voxels.size());
|
|
||||||
NextPacket << (uint8_t) ToClient::L1::Content
|
|
||||||
<< (uint8_t) ToClient::L2Content::ChunkVoxels
|
|
||||||
<< worldId << chunkPos.pack() << uint32_t(compressed_voxels.size());
|
|
||||||
NextPacket.write((const std::byte*) compressed_voxels.data(), compressed_voxels.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes,
|
|
||||||
const std::vector<DefNodeId>& uniq_sorted_defines)
|
|
||||||
{
|
|
||||||
Pos::bvec4u localChunk = chunkPos & 0x3;
|
|
||||||
Pos::GlobalRegion regionPos = chunkPos >> 2;
|
|
||||||
|
|
||||||
std::vector<DefNodeId>
|
|
||||||
newTypes, /* Новые типы нод */
|
|
||||||
lostTypes /* Потерянные типы нод */;
|
|
||||||
|
|
||||||
// Отметим использование этих нод
|
|
||||||
for(const DefNodeId& id : uniq_sorted_defines) {
|
|
||||||
auto iter = ResUses.DefNode.find(id);
|
|
||||||
if(iter == ResUses.DefNode.end()) {
|
|
||||||
// Новый тип
|
|
||||||
newTypes.push_back(id);
|
|
||||||
ResUses.DefNode[id] = 1;
|
|
||||||
} else {
|
|
||||||
// Увеличиваем счётчик
|
|
||||||
iter->second++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto iterWorld = ResUses.RefChunk.find(worldId);
|
|
||||||
|
|
||||||
if(iterWorld != ResUses.RefChunk.end())
|
|
||||||
// Исключим зависимости предыдущей версии чанка
|
|
||||||
{
|
|
||||||
auto iterRegion = iterWorld->second.find(regionPos);
|
|
||||||
if(iterRegion != iterWorld->second.end()) {
|
|
||||||
// Уменьшим счётчик зависимостей
|
|
||||||
for(const DefNodeId& id : iterRegion->second[localChunk.pack()].Node) {
|
|
||||||
auto iter = ResUses.DefNode.find(id);
|
|
||||||
assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях
|
|
||||||
if(--iter->second == 0) {
|
|
||||||
// Ноды больше нет в зависимостях
|
|
||||||
lostTypes.push_back(id);
|
|
||||||
ResUses.DefNode.erase(iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ResUses.RefChunk[worldId] = {};
|
|
||||||
iterWorld = ResUses.RefChunk.find(worldId);
|
|
||||||
}
|
|
||||||
|
|
||||||
iterWorld->second[regionPos][localChunk.pack()].Node = uniq_sorted_defines;
|
|
||||||
|
|
||||||
if(!newTypes.empty()) {
|
|
||||||
// Добавляем новые типы в запрос
|
|
||||||
NextRequest.Node.insert(NextRequest.Node.end(), newTypes.begin(), newTypes.end());
|
|
||||||
for(DefNodeId node : newTypes)
|
|
||||||
ResUses.RefDefNode[node] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!lostTypes.empty()) {
|
|
||||||
for(const DefNodeId& id : lostTypes) {
|
|
||||||
auto iter = ResUses.RefDefNode.find(id);
|
|
||||||
assert(iter != ResUses.RefDefNode.end()); // Должны быть описаны зависимости ноды
|
|
||||||
decrementAssets(std::move(iter->second));
|
|
||||||
ResUses.RefDefNode.erase(iter);
|
|
||||||
|
|
||||||
checkPacketBorder(16);
|
|
||||||
NextPacket << (uint8_t) ToClient::L1::Definition
|
|
||||||
<< (uint8_t) ToClient::L2Definition::FreeNode
|
|
||||||
<< id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkPacketBorder(4+4+8+4+compressed_nodes.size());
|
|
||||||
NextPacket << (uint8_t) ToClient::L1::Content
|
|
||||||
<< (uint8_t) ToClient::L2Content::ChunkNodes
|
|
||||||
<< worldId << chunkPos.pack() << uint32_t(compressed_nodes.size());
|
|
||||||
NextPacket.write((const std::byte*) compressed_nodes.data(), compressed_nodes.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::prepareRegionsRemove(WorldId_t worldId, std::vector<Pos::GlobalRegion> regionPoses)
|
void RemoteClient::NetworkAndResource_t::prepareRegionsRemove(WorldId_t worldId, std::vector<Pos::GlobalRegion> regionPoses)
|
||||||
{
|
{
|
||||||
std::vector<DefVoxelId>
|
|
||||||
lostTypesV /* Потерянные типы вокселей */;
|
|
||||||
std::vector<DefNodeId>
|
|
||||||
lostTypesN /* Потерянные типы нод */;
|
|
||||||
|
|
||||||
for(Pos::GlobalRegion regionPos : regionPoses)
|
|
||||||
// Уменьшаем зависимости вокселей и нод
|
|
||||||
{
|
|
||||||
auto iterWorld = ResUses.RefChunk.find(worldId);
|
|
||||||
if(iterWorld == ResUses.RefChunk.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto iterRegion = iterWorld->second.find(regionPos);
|
|
||||||
if(iterRegion == iterWorld->second.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for(const auto &iterChunk : iterRegion->second) {
|
|
||||||
for(const DefVoxelId& id : iterChunk.Voxel) {
|
|
||||||
auto iter = ResUses.DefVoxel.find(id);
|
|
||||||
assert(iter != ResUses.DefVoxel.end()); // Воксель должен быть в зависимостях
|
|
||||||
if(--iter->second == 0) {
|
|
||||||
// Вокселя больше нет в зависимостях
|
|
||||||
lostTypesV.push_back(id);
|
|
||||||
ResUses.DefVoxel.erase(iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const DefNodeId& id : iterChunk.Node) {
|
|
||||||
auto iter = ResUses.DefNode.find(id);
|
|
||||||
assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях
|
|
||||||
if(--iter->second == 0) {
|
|
||||||
// Ноды больше нет в зависимостях
|
|
||||||
lostTypesN.push_back(id);
|
|
||||||
ResUses.DefNode.erase(iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iterWorld->second.erase(iterRegion);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!lostTypesV.empty()) {
|
|
||||||
for(const DefVoxelId& id : lostTypesV) {
|
|
||||||
auto iter = ResUses.RefDefVoxel.find(id);
|
|
||||||
assert(iter != ResUses.RefDefVoxel.end()); // Должны быть описаны зависимости вокселя
|
|
||||||
decrementAssets(std::move(iter->second));
|
|
||||||
ResUses.RefDefVoxel.erase(iter);
|
|
||||||
|
|
||||||
checkPacketBorder(16);
|
|
||||||
NextPacket << (uint8_t) ToClient::L1::Definition
|
|
||||||
<< (uint8_t) ToClient::L2Definition::FreeVoxel
|
|
||||||
<< id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!lostTypesN.empty()) {
|
|
||||||
for(const DefNodeId& id : lostTypesN) {
|
|
||||||
auto iter = ResUses.RefDefNode.find(id);
|
|
||||||
assert(iter != ResUses.RefDefNode.end()); // Должны быть описаны зависимости ноды
|
|
||||||
decrementAssets(std::move(iter->second));
|
|
||||||
ResUses.RefDefNode.erase(iter);
|
|
||||||
|
|
||||||
checkPacketBorder(16);
|
|
||||||
NextPacket << (uint8_t) ToClient::L1::Definition
|
|
||||||
<< (uint8_t) ToClient::L2Definition::FreeNode
|
|
||||||
<< id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for(Pos::GlobalRegion regionPos : regionPoses) {
|
for(Pos::GlobalRegion regionPos : regionPoses) {
|
||||||
checkPacketBorder(16);
|
checkPacketBorder(16);
|
||||||
NextPacket << (uint8_t) ToClient::L1::Content
|
NextPacket << (uint8_t) ToClient::RemoveRegion
|
||||||
<< (uint8_t) ToClient::L2Content::RemoveRegion
|
|
||||||
<< worldId << regionPos.pack();
|
<< worldId << regionPos.pack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::prepareEntitiesUpdate(const std::vector<std::tuple<ServerEntityId_t, const Entity*>>& entities)
|
void RemoteClient::NetworkAndResource_t::prepareEntitiesUpdate(const std::vector<std::tuple<ServerEntityId_t, const Entity*>>& entities)
|
||||||
{
|
{
|
||||||
for(auto& [entityId, entity] : entities) {
|
// for(auto& [entityId, entity] : entities) {
|
||||||
// Сопоставим с идентификатором клиента
|
// // Сопоставим с идентификатором клиента
|
||||||
ClientEntityId_t ceId = ReMapEntities.toClient(entityId);
|
// ClientEntityId_t ceId = ReMapEntities.toClient(entityId);
|
||||||
|
|
||||||
// Профиль новый
|
// checkPacketBorder(32);
|
||||||
{
|
// NextPacket << (uint8_t) ToClient::Entity
|
||||||
DefEntityId profile = entity->getDefId();
|
// << ceId
|
||||||
auto iter = ResUses.DefEntity.find(profile);
|
// << (uint32_t) entity->getDefId()
|
||||||
if(iter == ResUses.DefEntity.end()) {
|
// << (uint32_t) entity->WorldId
|
||||||
// Клиенту неизвестен профиль
|
// << entity->Pos.x
|
||||||
NextRequest.Entity.push_back(profile);
|
// << entity->Pos.y
|
||||||
ResUses.DefEntity[profile] = 1;
|
// << entity->Pos.z;
|
||||||
} else
|
|
||||||
iter->second++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Добавление модификационных зависимостей
|
// {
|
||||||
// incrementBinary({}, {}, {}, {}, {});
|
// ToServer::PacketQuat q;
|
||||||
|
// q.fromQuat(entity->Quat);
|
||||||
// Старые данные
|
// for(int iter = 0; iter < 5; iter++)
|
||||||
{
|
// NextPacket << q.Data[iter];
|
||||||
auto iterEntity = ResUses.RefEntity.find(entityId);
|
// }
|
||||||
if(iterEntity != ResUses.RefEntity.end()) {
|
// }
|
||||||
// Убавляем зависимость к старому профилю
|
|
||||||
auto iterProfile = ResUses.DefEntity.find(iterEntity->second.Profile);
|
|
||||||
assert(iterProfile != ResUses.DefEntity.end()); // Старый профиль должен быть
|
|
||||||
if(--iterProfile->second == 0) {
|
|
||||||
// Старый профиль больше не нужен
|
|
||||||
auto iterProfileRef = ResUses.RefDefEntity.find(iterEntity->second.Profile);
|
|
||||||
decrementAssets(std::move(iterProfileRef->second));
|
|
||||||
ResUses.DefEntity.erase(iterProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Убавляем зависимость к модификационным данным
|
|
||||||
// iterEntity->second.
|
|
||||||
// decrementBinary({}, {}, {}, {}, {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ResUses_t::RefEntity_t refEntity;
|
|
||||||
refEntity.Profile = entity->getDefId();
|
|
||||||
if(!ResUses.RefDefEntity.contains(refEntity.Profile))
|
|
||||||
ResUses.RefDefEntity[refEntity.Profile] = {};
|
|
||||||
|
|
||||||
checkPacketBorder(32);
|
|
||||||
NextPacket << (uint8_t) ToClient::L1::Content
|
|
||||||
<< (uint8_t) ToClient::L2Content::Entity
|
|
||||||
<< ceId
|
|
||||||
<< (uint32_t) refEntity.Profile
|
|
||||||
<< (uint32_t) entity->WorldId
|
|
||||||
<< entity->Pos.x
|
|
||||||
<< entity->Pos.y
|
|
||||||
<< entity->Pos.z;
|
|
||||||
|
|
||||||
{
|
|
||||||
ToServer::PacketQuat q;
|
|
||||||
q.fromQuat(entity->Quat);
|
|
||||||
for(int iter = 0; iter < 5; iter++)
|
|
||||||
NextPacket << q.Data[iter];
|
|
||||||
}
|
|
||||||
|
|
||||||
ResUses.RefEntity[entityId] = std::move(refEntity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::prepareEntitiesUpdate_Dynamic(const std::vector<std::tuple<ServerEntityId_t, const Entity*>>& entities)
|
void RemoteClient::NetworkAndResource_t::prepareEntitiesUpdate_Dynamic(const std::vector<std::tuple<ServerEntityId_t, const Entity*>>& entities)
|
||||||
@@ -405,114 +301,23 @@ void RemoteClient::NetworkAndResource_t::prepareEntitySwap(ServerEntityId_t prev
|
|||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::prepareEntitiesRemove(const std::vector<ServerEntityId_t>& entityIds)
|
void RemoteClient::NetworkAndResource_t::prepareEntitiesRemove(const std::vector<ServerEntityId_t>& entityIds)
|
||||||
{
|
{
|
||||||
for(ServerEntityId_t entityId : entityIds) {
|
// for(ServerEntityId_t entityId : entityIds) {
|
||||||
ClientEntityId_t cId = ReMapEntities.erase(entityId);
|
// ClientEntityId_t cId = ReMapEntities.erase(entityId);
|
||||||
|
|
||||||
// Убавляем старые данные
|
// checkPacketBorder(16);
|
||||||
{
|
// NextPacket << (uint8_t) ToClient::L1::Content
|
||||||
auto iterEntity = ResUses.RefEntity.find(entityId);
|
// << (uint8_t) ToClient::L2Content::RemoveEntity
|
||||||
assert(iterEntity != ResUses.RefEntity.end()); // Зависимости должны быть
|
// << cId;
|
||||||
|
// }
|
||||||
// Убавляем модификационные заависимости
|
|
||||||
//decrementBinary(std::vector<BinTextureId_t> &&textures, std::vector<BinAnimationId_t> &&animation, std::vector<BinSoundId_t> &&sounds, std::vector<BinModelId_t> &&models, std::vector<BinFontId_t> &&fonts)
|
|
||||||
|
|
||||||
// Убавляем зависимость к профилю
|
|
||||||
auto iterProfile = ResUses.DefEntity.find(iterEntity->second.Profile);
|
|
||||||
assert(iterProfile != ResUses.DefEntity.end()); // Профиль должен быть
|
|
||||||
if(--iterProfile->second == 0) {
|
|
||||||
// Профиль больше не используется
|
|
||||||
auto iterProfileRef = ResUses.RefDefEntity.find(iterEntity->second.Profile);
|
|
||||||
|
|
||||||
decrementAssets(std::move(iterProfileRef->second));
|
|
||||||
|
|
||||||
ResUses.RefDefEntity.erase(iterProfileRef);
|
|
||||||
ResUses.DefEntity.erase(iterProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResUses.RefEntity.erase(iterEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
checkPacketBorder(16);
|
|
||||||
NextPacket << (uint8_t) ToClient::L1::Content
|
|
||||||
<< (uint8_t) ToClient::L2Content::RemoveEntity
|
|
||||||
<< cId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::prepareWorldUpdate(WorldId_t worldId, World* world)
|
void RemoteClient::NetworkAndResource_t::prepareWorldUpdate(WorldId_t worldId, World* world)
|
||||||
{
|
{
|
||||||
// Добавление зависимостей
|
|
||||||
ResUses.RefChunk[worldId];
|
|
||||||
|
|
||||||
// Профиль
|
|
||||||
{
|
|
||||||
DefWorldId defWorld = world->getDefId();
|
|
||||||
auto iterWorldProf = ResUses.DefWorld.find(defWorld);
|
|
||||||
if(iterWorldProf == ResUses.DefWorld.end()) {
|
|
||||||
// Профиль мира неизвестен клиенту
|
|
||||||
ResUses.DefWorld[defWorld] = 1;
|
|
||||||
NextRequest.World.push_back(defWorld);
|
|
||||||
} else {
|
|
||||||
iterWorldProf->second++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Если есть предыдущая версия мира
|
|
||||||
{
|
|
||||||
auto iterWorld = ResUses.RefWorld.find(worldId);
|
|
||||||
if(iterWorld != ResUses.RefWorld.end()) {
|
|
||||||
// Мир известен клиенту
|
|
||||||
|
|
||||||
// Убавляем модицикационные зависимости предыдущей версии мира
|
|
||||||
// iterWorld->second.
|
|
||||||
|
|
||||||
// Убавляем зависимости старого профиля
|
|
||||||
auto iterWorldProf = ResUses.DefWorld.find(iterWorld->second.Profile);
|
|
||||||
assert(iterWorldProf != ResUses.DefWorld.end()); // Старый профиль должен быть известен
|
|
||||||
if(--iterWorldProf->second == 0) {
|
|
||||||
// Старый профиль более ни кем не используется
|
|
||||||
ResUses.DefWorld.erase(iterWorldProf);
|
|
||||||
auto iterWorldProfRef = ResUses.RefDefWorld.find(iterWorld->second.Profile);
|
|
||||||
assert(iterWorldProfRef != ResUses.RefDefWorld.end()); // Зависимости предыдущего профиля также должны быть
|
|
||||||
decrementAssets(std::move(iterWorldProfRef->second));
|
|
||||||
ResUses.RefDefWorld.erase(iterWorldProfRef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Указываем модификационные зависимости текущей версии мира
|
|
||||||
ResUses.RefWorld[worldId] = {world->getDefId()};
|
|
||||||
|
|
||||||
// TODO: отправить мир
|
// TODO: отправить мир
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::prepareWorldRemove(WorldId_t worldId)
|
void RemoteClient::NetworkAndResource_t::prepareWorldRemove(WorldId_t worldId)
|
||||||
{
|
{
|
||||||
// Чанки уже удалены prepareChunkRemove
|
|
||||||
// Обновление зависимостей
|
|
||||||
auto iterWorld = ResUses.RefWorld.find(worldId);
|
|
||||||
assert(iterWorld != ResUses.RefWorld.end());
|
|
||||||
|
|
||||||
// Убавляем модификационные зависимости
|
|
||||||
// decrementBinary(std::move(iterWorld->second.Texture), std::move(iterWorld->second.Model), {}, {});
|
|
||||||
|
|
||||||
auto iterWorldProf = ResUses.DefWorld.find(iterWorld->second.Profile);
|
|
||||||
assert(iterWorldProf != ResUses.DefWorld.end()); // Профиль мира должен быть
|
|
||||||
if(--iterWorldProf->second == 0) {
|
|
||||||
// Профиль мира более не используется
|
|
||||||
ResUses.DefWorld.erase(iterWorldProf);
|
|
||||||
// Убавляем зависимости профиля
|
|
||||||
auto iterWorldProfDef = ResUses.RefDefWorld.find(iterWorld->second.Profile);
|
|
||||||
assert(iterWorldProfDef != ResUses.RefDefWorld.end()); // Зависимости профиля должны быть
|
|
||||||
decrementAssets(std::move(iterWorldProfDef->second));
|
|
||||||
ResUses.RefDefWorld.erase(iterWorldProfDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResUses.RefWorld.erase(iterWorld);
|
|
||||||
|
|
||||||
auto iter = ResUses.RefChunk.find(worldId);
|
|
||||||
assert(iter->second.empty());
|
|
||||||
ResUses.RefChunk.erase(iter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// void RemoteClient::NetworkAndResource_t::preparePortalUpdate(PortalId portalId, void* portal) {}
|
// void RemoteClient::NetworkAndResource_t::preparePortalUpdate(PortalId portalId, void* portal) {}
|
||||||
@@ -522,8 +327,7 @@ void RemoteClient::prepareCameraSetEntity(ServerEntityId_t entityId) {
|
|||||||
auto lock = NetworkAndResource.lock();
|
auto lock = NetworkAndResource.lock();
|
||||||
ClientEntityId_t cId = lock->ReMapEntities.toClient(entityId);
|
ClientEntityId_t cId = lock->ReMapEntities.toClient(entityId);
|
||||||
lock->checkPacketBorder(8);
|
lock->checkPacketBorder(8);
|
||||||
lock->NextPacket << (uint8_t) ToClient::L1::System
|
lock->NextPacket << (uint8_t) ToClient::TestLinkCameraToEntity
|
||||||
<< (uint8_t) ToClient::L2System::LinkCameraToEntity
|
|
||||||
<< cId;
|
<< cId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,7 +355,7 @@ ResourceRequest RemoteClient::pushPreparedPackets() {
|
|||||||
|
|
||||||
{
|
{
|
||||||
Net::Packet p;
|
Net::Packet p;
|
||||||
p << (uint8_t) ToClient::L1::System << (uint8_t) ToClient::L2System::SyncTick;
|
p << (uint8_t) ToClient::Tick;
|
||||||
toSend.push_back(std::move(p));
|
toSend.push_back(std::move(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,192 +367,27 @@ ResourceRequest RemoteClient::pushPreparedPackets() {
|
|||||||
return std::move(nextRequest);
|
return std::move(nextRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteClient::informateAssets(const std::vector<std::tuple<EnumAssets, ResourceId, std::string, std::string, Resource, std::vector<uint8_t>>>& resources)
|
void RemoteClient::informateBinaryAssets(const std::vector<AssetBinaryInfo>& resources)
|
||||||
{
|
{
|
||||||
std::vector<std::tuple<EnumAssets, ResourceId, std::string, std::string, Hash_t, std::vector<uint8_t>>> newForClient;
|
for(const AssetBinaryInfo& resource : resources) {
|
||||||
static std::atomic<uint32_t> debugSendLogCount = 0;
|
|
||||||
|
|
||||||
for(auto& [type, resId, domain, key, resource, header] : resources) {
|
|
||||||
auto hash = resource.hash();
|
|
||||||
Hash_t headerHash = sha2::sha256(header.data(), header.size());
|
|
||||||
auto lock = NetworkAndResource.lock();
|
auto lock = NetworkAndResource.lock();
|
||||||
|
auto iter = std::find(lock->ClientRequested.begin(), lock->ClientRequested.end(), resource.Hash);
|
||||||
// Проверка запрашиваемых клиентом ресурсов
|
if(iter == lock->ClientRequested.end())
|
||||||
{
|
|
||||||
auto iter = std::find(lock->ClientRequested.begin(), lock->ClientRequested.end(), hash);
|
|
||||||
if(iter != lock->ClientRequested.end())
|
|
||||||
{
|
|
||||||
lock->ClientRequested.erase(iter);
|
|
||||||
lock.unlock();
|
|
||||||
|
|
||||||
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(type, domain, key, resId, resource, 0);
|
|
||||||
if(domain == "test"
|
|
||||||
&& (type == EnumAssets::Nodestate
|
|
||||||
|| type == EnumAssets::Model
|
|
||||||
|| type == EnumAssets::Texture))
|
|
||||||
{
|
|
||||||
if(debugSendLogCount.fetch_add(1) < 64) {
|
|
||||||
LOG.debug() << "Queue resource send type=" << assetTypeName(type)
|
|
||||||
<< " id=" << resId
|
|
||||||
<< " key=" << domain << ':' << key
|
|
||||||
<< " size=" << resource.size()
|
|
||||||
<< " hash=" << int(hash[0]) << '.'
|
|
||||||
<< int(hash[1]) << '.'
|
|
||||||
<< int(hash[2]) << '.'
|
|
||||||
<< int(hash[3]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG.warn() << "Клиент повторно запросил имеющийся у него ресурс";
|
|
||||||
}
|
|
||||||
|
|
||||||
lock = NetworkAndResource.lock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Информирование клиента о привязках ресурсов к идентификатору
|
|
||||||
{
|
|
||||||
// Посмотрим что известно клиенту
|
|
||||||
if(auto iter = lock->ResUses.AssetsUse[(int) type].find(resId);
|
|
||||||
iter != lock->ResUses.AssetsUse[(int) type].end()
|
|
||||||
&& (iter->second.second.Hash != hash || iter->second.second.HeaderHash != headerHash)
|
|
||||||
) {
|
|
||||||
lock.unlock();
|
|
||||||
// Требуется перепривязать идентификатор к новому хешу
|
|
||||||
newForClient.push_back({(EnumAssets) type, resId, domain, key, hash, header});
|
|
||||||
iter->second.second.Hash = hash;
|
|
||||||
iter->second.second.HeaderHash = headerHash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Отправляем новые привязки ресурсов
|
|
||||||
if(!newForClient.empty()) {
|
|
||||||
assert(newForClient.size() < 65535*4);
|
|
||||||
auto lock = NetworkAndResource.lock();
|
|
||||||
|
|
||||||
lock->checkPacketBorder(2+1+4);
|
|
||||||
lock->NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение
|
|
||||||
<< ((uint8_t) ToClient::L2Resource::Bind) << uint32_t(newForClient.size());
|
|
||||||
|
|
||||||
for(auto& [type, resId, domain, key, hash, header] : newForClient) {
|
|
||||||
// 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.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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::informateDefVoxel(const std::vector<std::pair<DefVoxelId, DefVoxel*>>& voxels)
|
|
||||||
{
|
|
||||||
for(auto pair : voxels) {
|
|
||||||
DefVoxelId id = pair.first;
|
|
||||||
if(!ResUses.DefVoxel.contains(id))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
NextPacket << (uint8_t) ToClient::L1::Definition
|
lock->ClientRequested.erase(iter);
|
||||||
<< (uint8_t) ToClient::L2Definition::Voxel
|
lock.unlock();
|
||||||
<< id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::informateDefNode(const std::vector<std::pair<DefNodeId, DefNode*>>& nodes)
|
auto it = std::lower_bound(AssetsInWork.OnClient.begin(), AssetsInWork.OnClient.end(), resource.Hash);
|
||||||
{
|
if(it == AssetsInWork.OnClient.end() || *it != resource.Hash) {
|
||||||
for(auto& [id, def] : nodes) {
|
AssetsInWork.OnClient.insert(it, resource.Hash);
|
||||||
if(!ResUses.DefNode.contains(id))
|
AssetsInWork.ToSend.emplace_back(resource.Data, 0);
|
||||||
continue;
|
} else {
|
||||||
|
LOG.warn() << "Клиент повторно запросил имеющийся у него ресурс";
|
||||||
checkPacketBorder(1+1+4+4);
|
|
||||||
NextPacket << (uint8_t) ToClient::L1::Definition
|
|
||||||
<< (uint8_t) ToClient::L2Definition::Node
|
|
||||||
<< id << (uint32_t) def->NodestateId;
|
|
||||||
|
|
||||||
ResUses_t::RefAssets_t refs;
|
|
||||||
{
|
|
||||||
refs.Resources[(uint8_t) EnumAssets::Nodestate].push_back(def->NodestateId);
|
|
||||||
refs.Resources[(uint8_t) EnumAssets::Texture] = def->TextureDeps;
|
|
||||||
refs.Resources[(uint8_t) EnumAssets::Model] = def->ModelDeps;
|
|
||||||
|
|
||||||
incrementAssets(refs);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto iterDefRef = ResUses.RefDefNode.find(id);
|
|
||||||
if(iterDefRef != ResUses.RefDefNode.end()) {
|
|
||||||
decrementAssets(std::move(iterDefRef->second));
|
|
||||||
iterDefRef->second = std::move(refs);
|
|
||||||
} else {
|
|
||||||
ResUses.RefDefNode[id] = std::move(refs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::informateDefWorld(const std::vector<std::pair<DefWorldId, DefWorld*>>& worlds)
|
|
||||||
{
|
|
||||||
// for(auto pair : worlds) {
|
|
||||||
// DefWorldId_t id = pair.first;
|
|
||||||
// if(!ResUses.DefWorld.contains(id))
|
|
||||||
// continue;
|
|
||||||
|
|
||||||
// NextPacket << (uint8_t) ToClient::L1::Definition
|
|
||||||
// << (uint8_t) ToClient::L2Definition::World
|
|
||||||
// << id;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::informateDefPortal(const std::vector<std::pair<DefPortalId, DefPortal*>>& portals)
|
|
||||||
{
|
|
||||||
// for(auto pair : portals) {
|
|
||||||
// DefPortalId_t id = pair.first;
|
|
||||||
// if(!ResUses.DefPortal.contains(id))
|
|
||||||
// continue;
|
|
||||||
|
|
||||||
// NextPacket << (uint8_t) ToClient::L1::Definition
|
|
||||||
// << (uint8_t) ToClient::L2Definition::Portal
|
|
||||||
// << id;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::informateDefEntity(const std::vector<std::pair<DefEntityId, DefEntity*>>& entityes)
|
|
||||||
{
|
|
||||||
for(auto pair : entityes) {
|
|
||||||
DefEntityId id = pair.first;
|
|
||||||
if(!ResUses.DefEntity.contains(id))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
checkPacketBorder(8);
|
|
||||||
NextPacket << (uint8_t) ToClient::L1::Definition
|
|
||||||
<< (uint8_t) ToClient::L2Definition::Entity
|
|
||||||
<< id;
|
|
||||||
|
|
||||||
if(!ResUses.RefDefEntity.contains(id))
|
|
||||||
ResUses.RefDefEntity[id] = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::informateDefItem(const std::vector<std::pair<DefItemId, DefItem*>>& items)
|
|
||||||
{
|
|
||||||
// for(auto pair : items) {
|
|
||||||
// DefItemId_t id = pair.first;
|
|
||||||
// if(!ResUses.DefNode.contains(id))
|
|
||||||
// continue;
|
|
||||||
|
|
||||||
// NextPacket << (uint8_t) ToClient::L1::Definition
|
|
||||||
// << (uint8_t) ToClient::L2Definition::FuncEntity
|
|
||||||
// << id;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteClient::protocolError() {
|
void RemoteClient::protocolError() {
|
||||||
shutdown(EnumDisconnect::ProtocolError, "Ошибка протокола");
|
shutdown(EnumDisconnect::ProtocolError, "Ошибка протокола");
|
||||||
}
|
}
|
||||||
@@ -850,48 +489,6 @@ coro<> RemoteClient::rP_System(Net::AsyncSocket &sock) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::incrementAssets(const ResUses_t::RefAssets_t& bin) {
|
|
||||||
for(int iter = 0; iter < 5; iter++) {
|
|
||||||
auto &use = ResUses.AssetsUse[iter];
|
|
||||||
|
|
||||||
for(ResourceId id : bin.Resources[iter]) {
|
|
||||||
if(++std::get<0>(use[id]) == 1) {
|
|
||||||
NextRequest.AssetsInfo[iter].push_back(id);
|
|
||||||
// LOG.debug() << "Новое определение (тип " << iter << ") -> " << id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteClient::NetworkAndResource_t::decrementAssets(ResUses_t::RefAssets_t&& bin) {
|
|
||||||
std::vector<std::tuple<EnumAssets, ResourceId>> lost;
|
|
||||||
|
|
||||||
for(int iter = 0; iter < (int) EnumAssets::MAX_ENUM; iter++) {
|
|
||||||
auto &use = ResUses.AssetsUse[iter];
|
|
||||||
|
|
||||||
for(ResourceId id : bin.Resources[iter]) {
|
|
||||||
if(--std::get<0>(use[id]) == 0) {
|
|
||||||
use.erase(use.find(id));
|
|
||||||
|
|
||||||
lost.push_back({(EnumAssets) iter, id});
|
|
||||||
// LOG.debug() << "Потеряно определение (тип " << iter << ") -> " << id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!lost.empty()) {
|
|
||||||
assert(lost.size() < 65535*4);
|
|
||||||
|
|
||||||
checkPacketBorder(1+1+4+lost.size()*(1+4));
|
|
||||||
NextPacket << (uint8_t) ToClient::L1::Resource
|
|
||||||
<< (uint8_t) ToClient::L2Resource::Lost
|
|
||||||
<< uint32_t(lost.size());
|
|
||||||
|
|
||||||
for(auto& [type, id] : lost)
|
|
||||||
NextPacket << uint8_t(type) << uint32_t(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteClient::onUpdate() {
|
void RemoteClient::onUpdate() {
|
||||||
Pos::Object cameraPos = CameraPos;
|
Pos::Object cameraPos = CameraPos;
|
||||||
|
|
||||||
@@ -944,36 +541,15 @@ void RemoteClient::onUpdate() {
|
|||||||
|
|
||||||
bool hasFullSended = false;
|
bool hasFullSended = false;
|
||||||
|
|
||||||
for(auto& [type, domain, key, id, res, sended] : toSend) {
|
for(auto& [res, sended] : toSend) {
|
||||||
if(sended == 0) {
|
if(sended == 0) {
|
||||||
// Оповещаем о начале отправки ресурса
|
// Оповещаем о начале отправки ресурса
|
||||||
const size_t initSize = 1 + 1 + 4 + 32 + 4 + 1
|
const size_t initSize = 1 + 1 + 4 + 32 + 4 + 1;
|
||||||
+ 2 + domain.size()
|
|
||||||
+ 2 + key.size();
|
|
||||||
if(p.size() + initSize > kMaxAssetPacketSize)
|
if(p.size() + initSize > kMaxAssetPacketSize)
|
||||||
flushAssetsPacket();
|
flushAssetsPacket();
|
||||||
p << (uint8_t) ToClient::L1::Resource
|
p << (uint8_t) ToClient::AssetsInitSend
|
||||||
<< (uint8_t) ToClient::L2Resource::InitResSend
|
|
||||||
<< uint32_t(res.size());
|
<< uint32_t(res.size());
|
||||||
p.write((const std::byte*) res.hash().data(), 32);
|
p.write((const std::byte*) res.hash().data(), 32);
|
||||||
p << uint32_t(id) << uint8_t(type) << domain << key;
|
|
||||||
if(domain == "test"
|
|
||||||
&& (type == EnumAssets::Nodestate
|
|
||||||
|| type == EnumAssets::Model
|
|
||||||
|| type == EnumAssets::Texture))
|
|
||||||
{
|
|
||||||
if(debugInitSendLogCount.fetch_add(1) < 64) {
|
|
||||||
const auto hash = res.hash();
|
|
||||||
LOG.debug() << "Send InitResSend type=" << assetTypeName(type)
|
|
||||||
<< " id=" << id
|
|
||||||
<< " key=" << domain << ':' << key
|
|
||||||
<< " size=" << res.size()
|
|
||||||
<< " hash=" << int(hash[0]) << '.'
|
|
||||||
<< int(hash[1]) << '.'
|
|
||||||
<< int(hash[2]) << '.'
|
|
||||||
<< int(hash[3]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Отправляем чанк
|
// Отправляем чанк
|
||||||
@@ -981,8 +557,7 @@ void RemoteClient::onUpdate() {
|
|||||||
const size_t chunkMsgSize = 1 + 1 + 32 + 4 + willSend;
|
const size_t chunkMsgSize = 1 + 1 + 32 + 4 + willSend;
|
||||||
if(p.size() + chunkMsgSize > kMaxAssetPacketSize)
|
if(p.size() + chunkMsgSize > kMaxAssetPacketSize)
|
||||||
flushAssetsPacket();
|
flushAssetsPacket();
|
||||||
p << (uint8_t) ToClient::L1::Resource
|
p << (uint8_t) ToClient::AssetsNextSend;
|
||||||
<< (uint8_t) ToClient::L2Resource::ChunkSend;
|
|
||||||
p.write((const std::byte*) res.hash().data(), 32);
|
p.write((const std::byte*) res.hash().data(), 32);
|
||||||
p << uint32_t(willSend);
|
p << uint32_t(willSend);
|
||||||
p.write(res.data() + sended, willSend);
|
p.write(res.data() + sended, willSend);
|
||||||
@@ -995,7 +570,7 @@ void RemoteClient::onUpdate() {
|
|||||||
|
|
||||||
if(hasFullSended) {
|
if(hasFullSended) {
|
||||||
for(ssize_t iter = toSend.size()-1; iter >= 0; iter--) {
|
for(ssize_t iter = toSend.size()-1; iter >= 0; iter--) {
|
||||||
if(std::get<4>(toSend[iter]).size() == std::get<5>(toSend[iter])) {
|
if(std::get<0>(toSend[iter]).size() == std::get<1>(toSend[iter])) {
|
||||||
toSend.erase(toSend.begin()+iter);
|
toSend.erase(toSend.begin()+iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@@ -141,55 +142,26 @@ public:
|
|||||||
|
|
||||||
Информация о двоичных ресурсах будет получена сразу же при их запросе.
|
Информация о двоичных ресурсах будет получена сразу же при их запросе.
|
||||||
Действительная отправка ресурсов будет только по запросу клиента.
|
Действительная отправка ресурсов будет только по запросу клиента.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
struct ResourceRequest {
|
struct ResourceRequest {
|
||||||
std::vector<Hash_t> Hashes;
|
std::vector<Hash_t> Hashes;
|
||||||
std::vector<ResourceId> AssetsInfo[(int) EnumAssets::MAX_ENUM];
|
|
||||||
|
|
||||||
std::vector<DefVoxelId> Voxel;
|
void merge(const ResourceRequest &obj) {
|
||||||
std::vector<DefNodeId> Node;
|
|
||||||
std::vector<DefWorldId> World;
|
|
||||||
std::vector<DefPortalId> Portal;
|
|
||||||
std::vector<DefEntityId> Entity;
|
|
||||||
std::vector<DefItemId> Item;
|
|
||||||
|
|
||||||
void insert(const ResourceRequest &obj) {
|
|
||||||
Hashes.insert(Hashes.end(), obj.Hashes.begin(), obj.Hashes.end());
|
Hashes.insert(Hashes.end(), obj.Hashes.begin(), obj.Hashes.end());
|
||||||
for(int iter = 0; iter < (int) EnumAssets::MAX_ENUM; iter++)
|
|
||||||
AssetsInfo[iter].insert(AssetsInfo[iter].end(), obj.AssetsInfo[iter].begin(), obj.AssetsInfo[iter].end());
|
|
||||||
|
|
||||||
Voxel.insert(Voxel.end(), obj.Voxel.begin(), obj.Voxel.end());
|
|
||||||
Node.insert(Node.end(), obj.Node.begin(), obj.Node.end());
|
|
||||||
World.insert(World.end(), obj.World.begin(), obj.World.end());
|
|
||||||
Portal.insert(Portal.end(), obj.Portal.begin(), obj.Portal.end());
|
|
||||||
Entity.insert(Entity.end(), obj.Entity.begin(), obj.Entity.end());
|
|
||||||
Item.insert(Item.end(), obj.Item.begin(), obj.Item.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void uniq() {
|
void uniq() {
|
||||||
for(std::vector<ResourceId> *vec : {&Voxel, &Node, &World,
|
|
||||||
&Portal, &Entity, &Item
|
|
||||||
})
|
|
||||||
{
|
|
||||||
std::sort(vec->begin(), vec->end());
|
|
||||||
auto last = std::unique(vec->begin(), vec->end());
|
|
||||||
vec->erase(last, vec->end());
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++)
|
|
||||||
{
|
|
||||||
std::sort(AssetsInfo[type].begin(), AssetsInfo[type].end());
|
|
||||||
auto last = std::unique(AssetsInfo[type].begin(), AssetsInfo[type].end());
|
|
||||||
AssetsInfo[type].erase(last, AssetsInfo[type].end());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(Hashes.begin(), Hashes.end());
|
std::sort(Hashes.begin(), Hashes.end());
|
||||||
auto last = std::unique(Hashes.begin(), Hashes.end());
|
auto last = std::unique(Hashes.begin(), Hashes.end());
|
||||||
Hashes.erase(last, Hashes.end());
|
Hashes.erase(last, Hashes.end());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AssetBinaryInfo {
|
||||||
|
Resource Data;
|
||||||
|
Hash_t Hash;
|
||||||
|
};
|
||||||
|
|
||||||
// using EntityKey = std::tuple<WorldId_c, Pos::GlobalRegion>;
|
// using EntityKey = std::tuple<WorldId_c, Pos::GlobalRegion>;
|
||||||
|
|
||||||
|
|
||||||
@@ -209,67 +181,25 @@ class RemoteClient {
|
|||||||
bool IsConnected = true, IsGoingShutdown = false;
|
bool IsConnected = true, IsGoingShutdown = false;
|
||||||
|
|
||||||
struct NetworkAndResource_t {
|
struct NetworkAndResource_t {
|
||||||
struct ResUses_t {
|
|
||||||
// Счётчики использования двоичных кэшируемых ресурсов + хэш привязанный к идентификатору
|
|
||||||
// Хэш используется для того, чтобы исключить повторные объявления неизменившихся ресурсов
|
|
||||||
struct AssetBindState {
|
|
||||||
Hash_t Hash;
|
|
||||||
Hash_t HeaderHash;
|
|
||||||
};
|
|
||||||
std::map<ResourceId, std::pair<uint32_t, AssetBindState>> AssetsUse[(int) EnumAssets::MAX_ENUM];
|
|
||||||
|
|
||||||
// Зависимость профилей контента от профилей ресурсов
|
|
||||||
// Нужно чтобы пересчитать зависимости к профилям ресурсов
|
|
||||||
struct RefAssets_t {
|
|
||||||
std::vector<ResourceId> Resources[(int) EnumAssets::MAX_ENUM];
|
|
||||||
};
|
|
||||||
|
|
||||||
std::map<DefVoxelId, RefAssets_t> RefDefVoxel;
|
|
||||||
std::map<DefNodeId, RefAssets_t> RefDefNode;
|
|
||||||
std::map<WorldId_t, RefAssets_t> RefDefWorld;
|
|
||||||
std::map<DefPortalId, RefAssets_t> RefDefPortal;
|
|
||||||
std::map<DefEntityId, RefAssets_t> RefDefEntity;
|
|
||||||
std::map<DefItemId, RefAssets_t> RefDefItem;
|
|
||||||
|
|
||||||
// Счётчики использование профилей контента
|
|
||||||
std::map<DefVoxelId, uint32_t> DefVoxel; // Один чанк, одно использование
|
|
||||||
std::map<DefNodeId, uint32_t> DefNode;
|
|
||||||
std::map<DefWorldId, uint32_t> DefWorld;
|
|
||||||
std::map<DefPortalId, uint32_t> DefPortal;
|
|
||||||
std::map<DefEntityId, uint32_t> DefEntity;
|
|
||||||
std::map<DefItemId, uint32_t> DefItem; // При передаче инвентарей?
|
|
||||||
|
|
||||||
|
|
||||||
// Зависимость наблюдаемых чанков от профилей нод и вокселей
|
|
||||||
struct ChunkRef {
|
|
||||||
// Отсортированные списки уникальных вокселей
|
|
||||||
std::vector<DefVoxelId> Voxel;
|
|
||||||
std::vector<DefNodeId> Node;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::map<WorldId_t, std::map<Pos::GlobalRegion, std::array<ChunkRef, 4*4*4>>> RefChunk;
|
|
||||||
|
|
||||||
// Модификационные зависимости экземпляров профилей контента
|
|
||||||
// У сущностей в мире могут дополнительно изменятся свойства, переписывая их профиль
|
|
||||||
struct RefWorld_t {
|
|
||||||
DefWorldId Profile;
|
|
||||||
RefAssets_t Assets;
|
|
||||||
};
|
|
||||||
std::map<WorldId_t, RefWorld_t> RefWorld;
|
|
||||||
struct RefPortal_t {
|
|
||||||
DefPortalId Profile;
|
|
||||||
RefAssets_t Assets;
|
|
||||||
};
|
|
||||||
// std::map<PortalId, RefPortal_t> RefPortal;
|
|
||||||
struct RefEntity_t {
|
|
||||||
DefEntityId Profile;
|
|
||||||
RefAssets_t Assets;
|
|
||||||
};
|
|
||||||
std::map<ServerEntityId_t, RefEntity_t> RefEntity;
|
|
||||||
} ResUses;
|
|
||||||
|
|
||||||
// Смена идентификаторов сервера на клиентские
|
// Смена идентификаторов сервера на клиентские
|
||||||
SCSKeyRemapper<ServerEntityId_t, ClientEntityId_t> ReMapEntities;
|
SCSKeyRemapper<ServerEntityId_t, ClientEntityId_t> ReMapEntities;
|
||||||
|
// Накопленные чанки для отправки
|
||||||
|
std::unordered_map<
|
||||||
|
WorldId_t, // Миры
|
||||||
|
std::unordered_map<
|
||||||
|
Pos::GlobalRegion, // Регионы
|
||||||
|
std::pair<
|
||||||
|
std::unordered_map< // Воксели
|
||||||
|
Pos::bvec4u, // Чанки
|
||||||
|
std::u8string
|
||||||
|
>,
|
||||||
|
std::unordered_map< // Ноды
|
||||||
|
Pos::bvec4u, // Чанки
|
||||||
|
std::u8string
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
> ChunksToSend;
|
||||||
|
|
||||||
// Запрос информации об ассетах и профилях контента
|
// Запрос информации об ассетах и профилях контента
|
||||||
ResourceRequest NextRequest;
|
ResourceRequest NextRequest;
|
||||||
@@ -277,9 +207,6 @@ class RemoteClient {
|
|||||||
/// TODO: здесь может быть засор
|
/// TODO: здесь может быть засор
|
||||||
std::vector<Hash_t> ClientRequested;
|
std::vector<Hash_t> ClientRequested;
|
||||||
|
|
||||||
void incrementAssets(const ResUses_t::RefAssets_t& bin);
|
|
||||||
void decrementAssets(ResUses_t::RefAssets_t&& bin);
|
|
||||||
|
|
||||||
Net::Packet NextPacket;
|
Net::Packet NextPacket;
|
||||||
std::vector<Net::Packet> SimplePackets;
|
std::vector<Net::Packet> SimplePackets;
|
||||||
void checkPacketBorder(uint16_t size) {
|
void checkPacketBorder(uint16_t size) {
|
||||||
@@ -288,10 +215,24 @@ class RemoteClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels,
|
void prepareChunkUpdate_Voxels(
|
||||||
const std::vector<DefVoxelId>& uniq_sorted_defines);
|
WorldId_t worldId,
|
||||||
void prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes,
|
Pos::GlobalRegion regionPos,
|
||||||
const std::vector<DefNodeId>& uniq_sorted_defines);
|
Pos::bvec4u chunkPos,
|
||||||
|
const std::u8string& compressed_voxels
|
||||||
|
) {
|
||||||
|
ChunksToSend[worldId][regionPos].first[chunkPos] = compressed_voxels;
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepareChunkUpdate_Nodes(
|
||||||
|
WorldId_t worldId,
|
||||||
|
Pos::GlobalRegion regionPos,
|
||||||
|
Pos::bvec4u chunkPos,
|
||||||
|
const std::u8string& compressed_nodes
|
||||||
|
) {
|
||||||
|
ChunksToSend[worldId][regionPos].second[chunkPos] = compressed_nodes;
|
||||||
|
}
|
||||||
|
|
||||||
void prepareEntitiesRemove(const std::vector<ServerEntityId_t>& entityId);
|
void prepareEntitiesRemove(const std::vector<ServerEntityId_t>& entityId);
|
||||||
void prepareRegionsRemove(WorldId_t worldId, std::vector<Pos::GlobalRegion> regionPoses);
|
void prepareRegionsRemove(WorldId_t worldId, std::vector<Pos::GlobalRegion> regionPoses);
|
||||||
void prepareWorldRemove(WorldId_t worldId);
|
void prepareWorldRemove(WorldId_t worldId);
|
||||||
@@ -299,12 +240,6 @@ class RemoteClient {
|
|||||||
void prepareEntitiesUpdate_Dynamic(const std::vector<std::tuple<ServerEntityId_t, const Entity*>>& entities);
|
void prepareEntitiesUpdate_Dynamic(const std::vector<std::tuple<ServerEntityId_t, const Entity*>>& entities);
|
||||||
void prepareEntitySwap(ServerEntityId_t prevEntityId, ServerEntityId_t nextEntityId);
|
void prepareEntitySwap(ServerEntityId_t prevEntityId, ServerEntityId_t nextEntityId);
|
||||||
void prepareWorldUpdate(WorldId_t worldId, World* world);
|
void prepareWorldUpdate(WorldId_t worldId, World* world);
|
||||||
void informateDefVoxel(const std::vector<std::pair<DefVoxelId, DefVoxel*>>& voxels);
|
|
||||||
void informateDefNode(const std::vector<std::pair<DefNodeId, DefNode*>>& nodes);
|
|
||||||
void informateDefWorld(const std::vector<std::pair<DefWorldId, DefWorld*>>& worlds);
|
|
||||||
void informateDefPortal(const std::vector<std::pair<DefPortalId, DefPortal*>>& portals);
|
|
||||||
void informateDefEntity(const std::vector<std::pair<DefEntityId, DefEntity*>>& entityes);
|
|
||||||
void informateDefItem(const std::vector<std::pair<DefItemId, DefItem*>>& items);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@@ -318,8 +253,8 @@ class RemoteClient {
|
|||||||
// Ресурсы, отправленные на клиент в этой сессии
|
// Ресурсы, отправленные на клиент в этой сессии
|
||||||
std::vector<Hash_t> OnClient;
|
std::vector<Hash_t> OnClient;
|
||||||
// Отправляемые на клиент ресурсы
|
// Отправляемые на клиент ресурсы
|
||||||
// Тип, домен, ключ, идентификатор, ресурс, количество отправленных байт
|
// Ресурс, количество отправленных байт
|
||||||
std::vector<std::tuple<EnumAssets, std::string, std::string, ResourceId, Resource, size_t>> ToSend;
|
std::vector<std::tuple<Resource, size_t>> ToSend;
|
||||||
// Пакет с ресурсами
|
// Пакет с ресурсами
|
||||||
std::vector<Net::Packet> AssetsPackets;
|
std::vector<Net::Packet> AssetsPackets;
|
||||||
Net::Packet AssetsPacket;
|
Net::Packet AssetsPacket;
|
||||||
@@ -375,32 +310,27 @@ public:
|
|||||||
// если возвращает false, то блокировка сейчас находится у другого потока
|
// если возвращает false, то блокировка сейчас находится у другого потока
|
||||||
// и запрос не был обработан.
|
// и запрос не был обработан.
|
||||||
|
|
||||||
// В зоне видимости добавился чанк или изменились его воксели
|
// Создаёт пакет отправки вокселей чанка
|
||||||
bool maybe_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels,
|
void prepareChunkUpdate_Voxels(
|
||||||
const std::vector<DefVoxelId>& uniq_sorted_defines)
|
WorldId_t worldId,
|
||||||
{
|
Pos::GlobalRegion regionPos,
|
||||||
auto lock = NetworkAndResource.tryLock();
|
Pos::bvec4u chunkPos,
|
||||||
if(!lock)
|
const std::u8string& compressed_voxels
|
||||||
return false;
|
) {
|
||||||
|
NetworkAndResource.lock()->prepareChunkUpdate_Voxels(worldId, regionPos, chunkPos, compressed_voxels);
|
||||||
lock->prepareChunkUpdate_Voxels(worldId, chunkPos, compressed_voxels, uniq_sorted_defines);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// В зоне видимости добавился чанк или изменились его ноды
|
// Создаёт пакет отправки нод чанка
|
||||||
bool maybe_prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes,
|
void prepareChunkUpdate_Nodes(
|
||||||
const std::vector<DefNodeId>& uniq_sorted_defines)
|
WorldId_t worldId,
|
||||||
{
|
Pos::GlobalRegion regionPos,
|
||||||
auto lock = NetworkAndResource.tryLock();
|
Pos::bvec4u chunkPos,
|
||||||
if(!lock)
|
const std::u8string& compressed_nodes
|
||||||
return false;
|
) {
|
||||||
|
NetworkAndResource.lock()->prepareChunkUpdate_Nodes(worldId, regionPos, chunkPos, compressed_nodes);
|
||||||
lock->prepareChunkUpdate_Nodes(worldId, chunkPos, compressed_nodes, uniq_sorted_defines);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
// void prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalChunk chunkPos, const LightPrism *lights);
|
|
||||||
|
// Клиент перестал наблюдать за сущностями
|
||||||
// Клиент перестал наблюдать за сущностью
|
|
||||||
void prepareEntitiesRemove(const std::vector<ServerEntityId_t>& entityId) { NetworkAndResource.lock()->prepareEntitiesRemove(entityId); }
|
void prepareEntitiesRemove(const std::vector<ServerEntityId_t>& entityId) { NetworkAndResource.lock()->prepareEntitiesRemove(entityId); }
|
||||||
// Регион удалён из зоны видимости
|
// Регион удалён из зоны видимости
|
||||||
void prepareRegionsRemove(WorldId_t worldId, std::vector<Pos::GlobalRegion> regionPoses) { NetworkAndResource.lock()->prepareRegionsRemove(worldId, regionPoses); }
|
void prepareRegionsRemove(WorldId_t worldId, std::vector<Pos::GlobalRegion> regionPoses) { NetworkAndResource.lock()->prepareRegionsRemove(worldId, regionPoses); }
|
||||||
@@ -426,29 +356,52 @@ public:
|
|||||||
// Отправка подготовленных пакетов
|
// Отправка подготовленных пакетов
|
||||||
ResourceRequest pushPreparedPackets();
|
ResourceRequest pushPreparedPackets();
|
||||||
|
|
||||||
// Сообщить о ресурсах
|
// Создаёт пакет для всех игроков с оповещением о новых идентификаторах (id -> domain+key)
|
||||||
// Сюда приходят все обновления ресурсов движка
|
static Net::Packet makePacket_informateAssets_DK(
|
||||||
// Глобально их можно запросить в выдаче pushPreparedPackets()
|
const std::array<
|
||||||
|
std::vector<AssetsPreloader::BindDomainKeyInfo>,
|
||||||
|
static_cast<size_t>(EnumAssets::MAX_ENUM)
|
||||||
|
>& dkVector
|
||||||
|
);
|
||||||
|
|
||||||
// Нужно передавать клиенту информацию о новых привязках
|
// Создаёт пакет для всех игроков с оповещением об изменении файлов ресурсов (id -> hash+header)
|
||||||
// id -> домен+ключ
|
static Net::Packet makePacket_informateAssets_HH(
|
||||||
// id -> hash+header
|
const std::array<
|
||||||
|
std::vector<AssetsPreloader::BindHashHeaderInfo>,
|
||||||
|
static_cast<size_t>(EnumAssets::MAX_ENUM)
|
||||||
|
>& hhVector,
|
||||||
|
const std::array<
|
||||||
|
std::vector<ResourceId>,
|
||||||
|
static_cast<size_t>(EnumAssets::MAX_ENUM)
|
||||||
|
>& lost
|
||||||
|
);
|
||||||
|
|
||||||
// По запросу клиента отправлять нужные ресурсы по hash
|
// Оповещение о двоичных ресурсах (стриминг по запросу)
|
||||||
|
void informateBinaryAssets(
|
||||||
/// TODO: новый void informateAssets();
|
const std::vector<AssetBinaryInfo>& 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 informateDefNode(const std::vector<std::pair<DefNodeId, DefNode*>>& nodes) { NetworkAndResource.lock()->informateDefNode(nodes); }
|
|
||||||
void informateDefWorld(const std::vector<std::pair<DefWorldId, DefWorld*>>& worlds) { NetworkAndResource.lock()->informateDefWorld(worlds); }
|
|
||||||
void informateDefPortal(const std::vector<std::pair<DefPortalId, DefPortal*>>& portals) { NetworkAndResource.lock()->informateDefPortal(portals); }
|
|
||||||
void informateDefEntity(const std::vector<std::pair<DefEntityId, DefEntity*>>& entityes) { NetworkAndResource.lock()->informateDefEntity(entityes); }
|
|
||||||
void informateDefItem(const std::vector<std::pair<DefItemId, DefItem*>>& items) { NetworkAndResource.lock()->informateDefItem(items); }
|
|
||||||
|
|
||||||
|
// Создаёт пакет об обновлении игровых профилей
|
||||||
|
static std::vector<Net::Packet> makePackets_sendDefContentUpdate(
|
||||||
|
std::array<
|
||||||
|
std::vector<
|
||||||
|
std::pair<
|
||||||
|
ResourceId, // Идентификатор профиля
|
||||||
|
std::u8string // Двоичный формат профиля
|
||||||
|
>
|
||||||
|
>,
|
||||||
|
static_cast<size_t>(EnumDefContent::MAX_ENUM)
|
||||||
|
> newOrUpdate, // Новые или изменённые
|
||||||
|
std::array<
|
||||||
|
std::vector<ResourceId>,
|
||||||
|
static_cast<size_t>(EnumDefContent::MAX_ENUM)
|
||||||
|
> lost, // Потерянные профили
|
||||||
|
std::array<
|
||||||
|
std::vector<std::pair<std::string, std::string>>,
|
||||||
|
static_cast<size_t>(EnumDefContent::MAX_ENUM)
|
||||||
|
> idToDK // Новые привязки
|
||||||
|
);
|
||||||
|
|
||||||
void onUpdate();
|
void onUpdate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
Reference in New Issue
Block a user