Переработка интерфейса предоставления данных клиентам

This commit is contained in:
2026-01-04 13:36:52 +06:00
parent 2759073bb3
commit 83c4628995
11 changed files with 545 additions and 1133 deletions

View File

@@ -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& [regionPos, region] : world) {
for(auto& [chunkPos, chunk] : region.Voxels) {
CompressedVoxels cmp = compressVoxels(chunk);
Pos::GlobalChunk chunkPosR = (Pos::GlobalChunk(regionPos) << 2) + chunkPos;
std::u8string cmp = compressVoxels(chunk);
for(auto& ptr : region.NewCECs) {
bool accepted = ptr->maybe_prepareChunkUpdate_Voxels(worldId,
chunkPosR, cmp.Compressed, cmp.Defines);
if(!accepted) {
cecs.push_back(ptr.get());
}
ptr->prepareChunkUpdate_Voxels(worldId, regionPos, chunkPos, cmp);
}
if((region.IsChunkChanged_Voxels >> chunkPos.pack()) & 0x1) {
@@ -663,30 +641,16 @@ void GameServer::BackingChunkPressure_t::run(int id) {
if(skip)
continue;
bool accepted = ptr->maybe_prepareChunkUpdate_Voxels(worldId,
chunkPosR, cmp.Compressed, cmp.Defines);
if(!accepted) {
cecs.push_back(ptr.get());
}
ptr->prepareChunkUpdate_Voxels(worldId, regionPos, chunkPos, cmp);
}
}
if(!cecs.empty()) {
postponedVoxels.push_back({{worldId, chunkPosR, std::move(cmp)}, cecs});
cecs.clear();
}
}
for(auto& [chunkPos, chunk] : region.Nodes) {
CompressedNodes cmp = compressNodes(chunk.data());
Pos::GlobalChunk chunkPosR = (Pos::GlobalChunk(regionPos) << 2) + chunkPos;
std::u8string cmp = compressNodes(chunk.data());
for(auto& ptr : region.NewCECs) {
bool accepted = ptr->maybe_prepareChunkUpdate_Nodes(worldId,
chunkPosR, cmp.Compressed, cmp.Defines);
if(!accepted) {
cecs.push_back(ptr.get());
}
ptr->prepareChunkUpdate_Nodes(worldId, regionPos, chunkPos, cmp);
}
if((region.IsChunkChanged_Nodes >> chunkPos.pack()) & 0x1) {
@@ -702,60 +666,9 @@ void GameServer::BackingChunkPressure_t::run(int id) {
if(skip)
continue;
bool accepted = ptr->maybe_prepareChunkUpdate_Nodes(worldId,
chunkPosR, cmp.Compressed, cmp.Defines);
if(!accepted) {
cecs.push_back(ptr.get());
}
ptr->prepareChunkUpdate_Nodes(worldId, regionPos, chunkPos, cmp);
}
}
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() {
LOG.info() << "Перезагрузка модов: ассеты и зависимости";
std::vector<Net::Packet> packetsToSend;
AssetsPreloader::Out_reloadResources changes = Content.AM.reloadResources(AssetsInit);
AssetsPreloader::Out_applyResourceChange applied = Content.AM.applyResourceChange(changes);
LOG.info() << "Перезагрузка модов";
{
// TODO: перезагрузка модов
size_t changedCount = 0;
size_t lostCount = 0;
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) {
for(const auto& entry : applied.NewOrChange[type]) {
Content.OnContentChanges.AssetsInfo[type].push_back(entry.first);
changedCount++;
Content.CM.buildEndProfiles();
}
LOG.info() << "Перезагрузка ассетов";
{
{
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();
std::vector<ResourceId> nodeIds = Content.CM.collectProfileIds(EnumDefContent::Node);
if(!nodeIds.empty()) {
Content.OnContentChanges.Node.append_range(nodeIds);
// Отправка пакетов
for(std::shared_ptr<RemoteClient>& cec : Game.RemoteClients) {
auto copy = packetsToSend;
cec->pushPackets(&copy);
}
LOG.info() << "Перезагрузка завершена: обновлено ассетов=" << changedCount
<< " удалено=" << lostCount
<< " нод=" << nodeIds.size();
}
IWorldSaveBackend::TickSyncInfo_Out GameServer::stepDatabaseSync() {
@@ -2678,107 +2601,48 @@ void GameServer::stepSyncContent() {
}
// Сбор запросов на ресурсы и профили + отправка пакетов игрокам
// Сбор запросов на ресурсы + отправка пакетов игрокам
ResourceRequest full = std::move(Content.OnContentChanges);
for(std::shared_ptr<RemoteClient>& cec : Game.RemoteClients) {
full.insert(cec->pushPreparedPackets());
full.merge(cec->pushPreparedPackets());
}
full.uniq();
// Информируем о запрошенных ассетах
std::vector<std::tuple<EnumAssets, ResourceId, std::string, std::string, Resource, std::u8string>> resources;
for(int type = 0; type < (int) EnumAssets::MAX_ENUM; type++) {
for(ResourceId resId : full.AssetsInfo[type]) {
const AssetsPreloader::MediaResource* media = Content.AM.getResource((EnumAssets) type, resId);
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<Net::Packet> packetsToAll;
{
AssetsPreloader::Out_bakeId baked = Content.AM.bakeIdTables();
if(!baked.IdToDK.empty()) {
packetsToAll.push_back(RemoteClient::makePacket_informateAssets_DK(baked.IdToDK));
}
}
// Оповещаем о двоичных ресурсах по запросу
std::vector<AssetBinaryInfo> binaryResources;
for(const Hash_t& hash : full.Hashes) {
std::optional<std::tuple<EnumAssets, uint32_t, const AssetsPreloader::MediaResource*>> result = Content.AM.getResource(hash);
std::optional<
std::tuple<EnumAssets, uint32_t, const AssetsPreloader::MediaResource*>
> result = Content.AM.getResource(hash);
if(!result)
continue;
auto& [type, id, media] = *result;
Resource resource(*media->Resource);
resources.emplace_back(type, id, media->Domain, media->Key, std::move(resource), media->Header);
}
// Информируем о запрошенных профилях
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);
binaryResources.push_back(AssetBinaryInfo{
.Data = std::move(resource),
.Hash = media->Hash
});
}
for(std::shared_ptr<RemoteClient>& remoteClient : Game.RemoteClients) {
if(!resources.empty())
remoteClient->informateAssets(resources);
if(!voxels.empty())
remoteClient->informateDefVoxel(voxels);
if(!nodes.empty())
remoteClient->informateDefNode(nodes);
if(!worlds.empty())
remoteClient->informateDefWorld(worlds);
if(!portals.empty())
remoteClient->informateDefPortal(portals);
if(!entities.empty())
remoteClient->informateDefEntity(entities);
if(!items.empty())
remoteClient->informateDefItem(items);
if(!binaryResources.empty())
remoteClient->informateBinaryAssets(binaryResources);
if(!packetsToAll.empty()) {
auto copy = packetsToAll;
remoteClient->pushPackets(&copy);
}
}

View File

@@ -80,8 +80,8 @@ class GameServer : public AsyncObject {
ResourceRequest OnContentChanges;
ContentObj(asio::io_context& ioc)
: AM(ioc), CM(AM)
ContentObj(asio::io_context&)
: AM(), CM(AM)
{}
} Content;

View File

@@ -11,11 +11,153 @@
#include <boost/system/system_error.hpp>
#include <exception>
#include <Common/Packets.hpp>
#include "sha2.hpp"
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 {
const char* assetTypeName(EnumAssets type) {
@@ -71,8 +213,7 @@ void RemoteClient::shutdown(EnumDisconnect type, const std::string reason) {
IsGoingShutdown = true;
Net::Packet packet;
packet << (uint8_t) ToClient::L1::System
<< (uint8_t) ToClient::L2System::Disconnect
packet << (uint8_t) ToClient::Disconnect
<< (uint8_t) type << reason;
std::string info;
@@ -88,309 +229,64 @@ void RemoteClient::shutdown(EnumDisconnect type, const std::string reason) {
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>
newTypes, /* Новые типы вокселей */
lostTypes /* Потерянные типы вокселей */;
// packet << (uint8_t) ToClient::ChunkVoxels
// << worldId << chunkPos.pack() << uint32_t(compressed_voxels.size());
// packet.write((const std::byte*) compressed_voxels.data(), compressed_voxels.size());
// }
// Отметим использование этих вокселей
for(const DefVoxelId& id : uniq_sorted_defines) {
auto iter = ResUses.DefVoxel.find(id);
if(iter == ResUses.DefVoxel.end()) {
// Новый тип
newTypes.push_back(id);
ResUses.DefVoxel[id] = 1;
} else {
// Увеличиваем счётчик
iter->second++;
}
}
// void RemoteClient::prepareChunkUpdate_Nodes(
// WorldId_t worldId,
// Pos::GlobalChunk chunkPos,
// const std::u8string& compressed_nodes
// ) {
// Pos::bvec4u localChunk = chunkPos & 0x3;
// Pos::GlobalRegion regionPos = chunkPos >> 2;
auto iterWorld = ResUses.RefChunk.find(worldId);
if(iterWorld != ResUses.RefChunk.end())
// Исключим зависимости предыдущей версии чанка
{
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());
}
// packet << (uint8_t) ToClient::ChunkNodes
// << worldId << chunkPos.pack() << uint32_t(compressed_nodes.size());
// packet.write((const std::byte*) compressed_nodes.data(), compressed_nodes.size());
// }
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) {
checkPacketBorder(16);
NextPacket << (uint8_t) ToClient::L1::Content
<< (uint8_t) ToClient::L2Content::RemoveRegion
NextPacket << (uint8_t) ToClient::RemoveRegion
<< worldId << regionPos.pack();
}
}
void RemoteClient::NetworkAndResource_t::prepareEntitiesUpdate(const std::vector<std::tuple<ServerEntityId_t, const Entity*>>& entities)
{
for(auto& [entityId, entity] : entities) {
// Сопоставим с идентификатором клиента
ClientEntityId_t ceId = ReMapEntities.toClient(entityId);
// for(auto& [entityId, entity] : entities) {
// // Сопоставим с идентификатором клиента
// ClientEntityId_t ceId = ReMapEntities.toClient(entityId);
// Профиль новый
{
DefEntityId profile = entity->getDefId();
auto iter = ResUses.DefEntity.find(profile);
if(iter == ResUses.DefEntity.end()) {
// Клиенту неизвестен профиль
NextRequest.Entity.push_back(profile);
ResUses.DefEntity[profile] = 1;
} else
iter->second++;
}
// checkPacketBorder(32);
// NextPacket << (uint8_t) ToClient::Entity
// << ceId
// << (uint32_t) entity->getDefId()
// << (uint32_t) entity->WorldId
// << entity->Pos.x
// << entity->Pos.y
// << entity->Pos.z;
// Добавление модификационных зависимостей
// incrementBinary({}, {}, {}, {}, {});
// Старые данные
{
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);
}
// {
// ToServer::PacketQuat q;
// q.fromQuat(entity->Quat);
// for(int iter = 0; iter < 5; iter++)
// NextPacket << q.Data[iter];
// }
// }
}
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)
{
for(ServerEntityId_t entityId : entityIds) {
ClientEntityId_t cId = ReMapEntities.erase(entityId);
// for(ServerEntityId_t entityId : entityIds) {
// ClientEntityId_t cId = ReMapEntities.erase(entityId);
// Убавляем старые данные
{
auto iterEntity = ResUses.RefEntity.find(entityId);
assert(iterEntity != ResUses.RefEntity.end()); // Зависимости должны быть
// Убавляем модификационные заависимости
//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;
}
// checkPacketBorder(16);
// NextPacket << (uint8_t) ToClient::L1::Content
// << (uint8_t) ToClient::L2Content::RemoveEntity
// << cId;
// }
}
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: отправить мир
}
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) {}
@@ -522,8 +327,7 @@ void RemoteClient::prepareCameraSetEntity(ServerEntityId_t entityId) {
auto lock = NetworkAndResource.lock();
ClientEntityId_t cId = lock->ReMapEntities.toClient(entityId);
lock->checkPacketBorder(8);
lock->NextPacket << (uint8_t) ToClient::L1::System
<< (uint8_t) ToClient::L2System::LinkCameraToEntity
lock->NextPacket << (uint8_t) ToClient::TestLinkCameraToEntity
<< cId;
}
@@ -551,7 +355,7 @@ ResourceRequest RemoteClient::pushPreparedPackets() {
{
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));
}
@@ -563,192 +367,27 @@ ResourceRequest RemoteClient::pushPreparedPackets() {
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;
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());
for(const AssetBinaryInfo& resource : resources) {
auto lock = NetworkAndResource.lock();
// Проверка запрашиваемых клиентом ресурсов
{
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))
auto iter = std::find(lock->ClientRequested.begin(), lock->ClientRequested.end(), resource.Hash);
if(iter == lock->ClientRequested.end())
continue;
NextPacket << (uint8_t) ToClient::L1::Definition
<< (uint8_t) ToClient::L2Definition::Voxel
<< id;
}
}
lock->ClientRequested.erase(iter);
lock.unlock();
void RemoteClient::NetworkAndResource_t::informateDefNode(const std::vector<std::pair<DefNodeId, DefNode*>>& nodes)
{
for(auto& [id, def] : nodes) {
if(!ResUses.DefNode.contains(id))
continue;
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);
}
auto it = std::lower_bound(AssetsInWork.OnClient.begin(), AssetsInWork.OnClient.end(), resource.Hash);
if(it == AssetsInWork.OnClient.end() || *it != resource.Hash) {
AssetsInWork.OnClient.insert(it, resource.Hash);
AssetsInWork.ToSend.emplace_back(resource.Data, 0);
} else {
LOG.warn() << "Клиент повторно запросил имеющийся у него ресурс";
}
}
}
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() {
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() {
Pos::Object cameraPos = CameraPos;
@@ -944,36 +541,15 @@ void RemoteClient::onUpdate() {
bool hasFullSended = false;
for(auto& [type, domain, key, id, res, sended] : toSend) {
for(auto& [res, sended] : toSend) {
if(sended == 0) {
// Оповещаем о начале отправки ресурса
const size_t initSize = 1 + 1 + 4 + 32 + 4 + 1
+ 2 + domain.size()
+ 2 + key.size();
const size_t initSize = 1 + 1 + 4 + 32 + 4 + 1;
if(p.size() + initSize > kMaxAssetPacketSize)
flushAssetsPacket();
p << (uint8_t) ToClient::L1::Resource
<< (uint8_t) ToClient::L2Resource::InitResSend
p << (uint8_t) ToClient::AssetsInitSend
<< uint32_t(res.size());
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;
if(p.size() + chunkMsgSize > kMaxAssetPacketSize)
flushAssetsPacket();
p << (uint8_t) ToClient::L1::Resource
<< (uint8_t) ToClient::L2Resource::ChunkSend;
p << (uint8_t) ToClient::AssetsNextSend;
p.write((const std::byte*) res.hash().data(), 32);
p << uint32_t(willSend);
p.write(res.data() + sended, willSend);
@@ -995,7 +570,7 @@ void RemoteClient::onUpdate() {
if(hasFullSended) {
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);
}
}

View File

@@ -11,6 +11,7 @@
#include <initializer_list>
#include <optional>
#include <queue>
#include <string>
#include <type_traits>
#include <unordered_map>
@@ -141,55 +142,26 @@ public:
Информация о двоичных ресурсах будет получена сразу же при их запросе.
Действительная отправка ресурсов будет только по запросу клиента.
*/
struct ResourceRequest {
std::vector<Hash_t> Hashes;
std::vector<ResourceId> AssetsInfo[(int) EnumAssets::MAX_ENUM];
std::vector<Hash_t> Hashes;
std::vector<DefVoxelId> Voxel;
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) {
void merge(const ResourceRequest &obj) {
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() {
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());
auto last = std::unique(Hashes.begin(), Hashes.end());
Hashes.erase(last, Hashes.end());
}
};
struct AssetBinaryInfo {
Resource Data;
Hash_t Hash;
};
// using EntityKey = std::tuple<WorldId_c, Pos::GlobalRegion>;
@@ -209,67 +181,25 @@ class RemoteClient {
bool IsConnected = true, IsGoingShutdown = false;
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;
// Накопленные чанки для отправки
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;
@@ -277,9 +207,6 @@ class RemoteClient {
/// TODO: здесь может быть засор
std::vector<Hash_t> ClientRequested;
void incrementAssets(const ResUses_t::RefAssets_t& bin);
void decrementAssets(ResUses_t::RefAssets_t&& bin);
Net::Packet NextPacket;
std::vector<Net::Packet> SimplePackets;
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,
const std::vector<DefVoxelId>& uniq_sorted_defines);
void prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes,
const std::vector<DefNodeId>& uniq_sorted_defines);
void prepareChunkUpdate_Voxels(
WorldId_t worldId,
Pos::GlobalRegion regionPos,
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 prepareRegionsRemove(WorldId_t worldId, std::vector<Pos::GlobalRegion> regionPoses);
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 prepareEntitySwap(ServerEntityId_t prevEntityId, ServerEntityId_t nextEntityId);
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 {
@@ -318,8 +253,8 @@ class RemoteClient {
// Ресурсы, отправленные на клиент в этой сессии
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;
Net::Packet AssetsPacket;
@@ -375,32 +310,27 @@ public:
// если возвращает false, то блокировка сейчас находится у другого потока
// и запрос не был обработан.
// В зоне видимости добавился чанк или изменились его воксели
bool maybe_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels,
const std::vector<DefVoxelId>& uniq_sorted_defines)
{
auto lock = NetworkAndResource.tryLock();
if(!lock)
return false;
lock->prepareChunkUpdate_Voxels(worldId, chunkPos, compressed_voxels, uniq_sorted_defines);
return true;
// Создаёт пакет отправки вокселей чанка
void prepareChunkUpdate_Voxels(
WorldId_t worldId,
Pos::GlobalRegion regionPos,
Pos::bvec4u chunkPos,
const std::u8string& compressed_voxels
) {
NetworkAndResource.lock()->prepareChunkUpdate_Voxels(worldId, regionPos, chunkPos, compressed_voxels);
}
// В зоне видимости добавился чанк или изменились его ноды
bool maybe_prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes,
const std::vector<DefNodeId>& uniq_sorted_defines)
{
auto lock = NetworkAndResource.tryLock();
if(!lock)
return false;
lock->prepareChunkUpdate_Nodes(worldId, chunkPos, compressed_nodes, uniq_sorted_defines);
return true;
// Создаёт пакет отправки нод чанка
void prepareChunkUpdate_Nodes(
WorldId_t worldId,
Pos::GlobalRegion regionPos,
Pos::bvec4u chunkPos,
const std::u8string& compressed_nodes
) {
NetworkAndResource.lock()->prepareChunkUpdate_Nodes(worldId, regionPos, chunkPos, compressed_nodes);
}
// 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 prepareRegionsRemove(WorldId_t worldId, std::vector<Pos::GlobalRegion> regionPoses) { NetworkAndResource.lock()->prepareRegionsRemove(worldId, regionPoses); }
@@ -426,29 +356,52 @@ public:
// Отправка подготовленных пакетов
ResourceRequest pushPreparedPackets();
// Сообщить о ресурсах
// Сюда приходят все обновления ресурсов движка
// Глобально их можно запросить в выдаче pushPreparedPackets()
// Создаёт пакет для всех игроков с оповещением о новых идентификаторах (id -> domain+key)
static Net::Packet makePacket_informateAssets_DK(
const std::array<
std::vector<AssetsPreloader::BindDomainKeyInfo>,
static_cast<size_t>(EnumAssets::MAX_ENUM)
>& dkVector
);
// Нужно передавать клиенту информацию о новых привязках
// id -> домен+ключ
// id -> hash+header
// Создаёт пакет для всех игроков с оповещением об изменении файлов ресурсов (id -> hash+header)
static Net::Packet 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
);
// По запросу клиента отправлять нужные ресурсы по hash
/// TODO: новый void informateAssets();
// Оповещение о запрошенных (и не только) ассетах
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); }
// Оповещение о двоичных ресурсах (стриминг по запросу)
void informateBinaryAssets(
const std::vector<AssetBinaryInfo>& resources
);
// Создаёт пакет об обновлении игровых профилей
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();
private: