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

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

@@ -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: