Переработка интерфейса предоставления данных клиентам
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user