Сеть, со стороны сервера

This commit is contained in:
2025-02-09 12:05:10 +06:00
parent 42f6869afd
commit 871b03632e
12 changed files with 814 additions and 244 deletions

View File

@@ -7,6 +7,8 @@
#include <exception>
#include <unordered_map>
#include <unordered_set>
#include "World.hpp"
#include <Common/Packets.hpp>
namespace LV::Server {
@@ -43,77 +45,81 @@ void RemoteClient::shutdown(const std::string reason) {
return;
IsGoingShutdown = true;
// Отправить пакет о завершении работы
NextPacket << (uint8_t) ToClient::L1::System
<< (uint8_t) ToClient::L2System::Disconnect
<< reason;
}
void RemoteClient::prepareDefWorld(WorldId_t worldId, void* world) {
}
void RemoteClient::prepareDefVoxel(VoxelId_t voxelId, void* voxel) {
}
void RemoteClient::prepareDefNode(NodeId_t worldId, void* node) {
}
void RemoteClient::prepareDefMediaStream(MediaStreamId_t modelId, void* mediaStream) {
}
// Может прийти событие на чанк, про который ещё ничего не знаем
void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos,
const std::vector<VoxelCube> &voxels)
{
WorldId_c wcId = rentWorldRemapId(worldId);
if(wcId == WorldId_c(-1))
return;
WorldId_c wcId = ResRemap.Worlds.toClient(worldId);
assert(wcId != WorldId_c(-1));
// Перебиндить идентификаторы вокселей
std::vector<VoxelId_t> NeedVoxels;
std::vector<DefVoxelId_t> NeedVoxels;
NeedVoxels.reserve(voxels.size());
for(const VoxelCube &cube : voxels) {
NeedVoxels.push_back(cube.Material);
NeedVoxels.push_back(cube.VoxelId);
}
std::unordered_set<VoxelId_t> NeedVoxelsSet(NeedVoxels.begin(), NeedVoxels.end());
std::unordered_set<DefVoxelId_t> NeedVoxelsSet(NeedVoxels.begin(), NeedVoxels.end());
// Собираем информацию о конвертации идентификаторов
std::unordered_map<VoxelId_t, VoxelId_c> LocalRemapper;
for(VoxelId_t vId : NeedVoxelsSet) {
auto cvId = Remap.STC_Voxels.find(vId);
if(cvId == Remap.STC_Voxels.end()) {
// Нужно забронировать идентификатор
VoxelId_c cvnId = Remap.UsedVoxelIdC._Find_first();
if(cvnId == VoxelId_c(-1))
// Нет свободных идентификаторов
LocalRemapper[vId] = 0;
else {
NextRequest.NewVoxels.push_back(vId);
Remap.UsedVoxelIdC.reset(cvnId);
Remap.STC_Voxels[vId] = cvnId;
LocalRemapper[vId] = cvnId;
std::unordered_map<DefVoxelId_t, VoxelId_c> LocalRemapper;
for(DefVoxelId_t vId : NeedVoxelsSet) {
LocalRemapper[vId] = ResRemap.DefVoxels.toClient(vId);
}
// Проверить новые и забытые определения вокселей
{
std::unordered_set<DefVoxelId_t> &prevSet = ResUses.ChunkVoxels[{worldId, chunkPos}];
std::unordered_set<DefVoxelId_t> &nextSet = NeedVoxelsSet;
std::vector<DefVoxelId_t> newVoxels, lostVoxels;
for(DefVoxelId_t id : prevSet) {
if(!nextSet.contains(id)) {
if(--ResUses.DefVoxel[id] == 0) {
// Определение больше не используется
ResUses.DefVoxel.erase(ResUses.DefVoxel.find(id));
VoxelId_c cId = ResRemap.DefVoxels.erase(id);
// TODO: отправить пакет потери идентификатора
LOG.debug() << "Определение вокселя потеряно: " << id << " -> " << cId;
}
}
} else {
LocalRemapper[vId] = cvId->second;
}
for(DefVoxelId_t id : nextSet) {
if(!prevSet.contains(id)) {
if(++ResUses.DefVoxel[id] == 1) {
// Определение только появилось
NextRequest.NewVoxels.push_back(id);
VoxelId_c cId = ResRemap.DefVoxels.toClient(id);
LOG.debug() << "Новое определение вокселя: " << id << " -> " << cId;
}
}
}
prevSet = std::move(nextSet);
}
Net::Packet packet;
// TODO: отправить новую информацию о расположении вокселей
LOG.debug() << "Новый чанк: " << worldId << " / " << chunkPos.X << ":" << chunkPos.Y << ":" << chunkPos.Z;
// Packet Id
packet << uint16_t(0);
packet << wcId << Pos::GlobalChunk::Key(chunkPos);
packet << uint16_t(voxels.size());
// NextPacket << uint16_t(0);
// NextPacket << wcId << Pos::GlobalChunk::Key(chunkPos);
// NextPacket << uint16_t(voxels.size());
for(const VoxelCube &cube : voxels) {
packet << LocalRemapper[cube.Material]
<< cube.Left.X << cube.Left.Y << cube.Left.Z
<< cube.Right.X << cube.Right.Y << cube.Right.Z;
}
SimplePackets.push_back(std::move(packet));
// for(const VoxelCube &cube : voxels) {
// NextPacket << LocalRemapper[cube.VoxelId]
// << cube.Left.X << cube.Left.Y << cube.Left.Z
// << cube.Right.X << cube.Right.Y << cube.Right.Z;
// }
}
void RemoteClient::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos,
@@ -131,144 +137,456 @@ void RemoteClient::prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalC
void RemoteClient::prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkPos)
{
// Понизим зависимости ресурсов
std::unordered_set<DefVoxelId_t> &prevSet = ResUses.ChunkVoxels[{worldId, chunkPos}];
for(DefVoxelId_t id : prevSet) {
if(--ResUses.DefVoxel[id] == 0) {
// Определение больше не используется
ResUses.DefVoxel.erase(ResUses.DefVoxel.find(id));
VoxelId_c cId = ResRemap.DefVoxels.erase(id);
// TODO: отправить пакет потери идентификатора
LOG.debug() << "Определение вокселя потеряно: " << id << " -> " << cId;
}
}
LOG.debug() << "Чанк потерян: " << worldId << " / " << chunkPos.X << ":" << chunkPos.Y << ":" << chunkPos.Z;
WorldId_c cwId = ResRemap.Worlds.toClient(worldId);
NextPacket << (uint8_t) ToClient::L1::Content
<< (uint8_t) ToClient::L2Content::RemoveChunk
<< cwId << chunkPos.X << chunkPos.Y << chunkPos.Z;
}
void RemoteClient::prepareWorldNew(WorldId_t worldId, World* world)
{
ResUsesObj::WorldResourceUse &res = ResUses.Worlds[worldId];
res.DefId = world->getDefId();
if(++ResUses.DefWorld[res.DefId] == 1) {
// Новое определение мира
DefWorldId_c cdId = ResRemap.DefWorlds.toClient(res.DefId);
NextRequest.NewWorlds.push_back(res.DefId);
LOG.debug() << "Новое определение мира: " << res.DefId << " -> " << cdId;
NextPacket << (uint8_t) ToClient::L1::Definition
<< (uint8_t) ToClient::L2Definition::World
<< cdId;
}
incrementBinary(res.Textures, res.Sounds, res.Models);
WorldId_c cId = ResRemap.Worlds.toClient(worldId);
LOG.debug() << "Новый мир: " << worldId << " -> " << int(cId);
NextPacket << (uint8_t) ToClient::L1::Content
<< (uint8_t) ToClient::L2Content::World
<< cId;
}
void RemoteClient::prepareWorldUpdate(WorldId_t worldId, World* world)
{
ResUsesObj::WorldResourceUse &res = ResUses.Worlds[worldId];
if(res.DefId != world->getDefId()) {
DefWorldId_t newDef = world->getDefId();
if(--ResUses.DefWorld[res.DefId] == 0) {
// Определение больше не используется
ResUses.DefWorld.erase(ResUses.DefWorld.find(res.DefId));
DefWorldId_c cdId = ResRemap.DefWorlds.erase(res.DefId);
// TODO: отправить пакет потери идентификатора
LOG.debug() << "Определение мира потеряно: " << res.DefId << " -> " << cdId;
}
if(++ResUses.DefWorld[newDef] == 1) {
// Новое определение мира
DefWorldId_c cdId = ResRemap.DefWorlds.toClient(newDef);
NextRequest.NewWorlds.push_back(newDef);
// incrementBinary(Textures, Sounds, Models);
// TODO: отправить пакет о новом определении мира
LOG.debug() << "Новое определение мира: " << newDef << " -> " << cdId;
}
res.DefId = newDef;
}
// TODO: определить различия между переопределением поверх определений
std::unordered_set<BinTextureId_t> lostTextures, newTextures;
std::unordered_set<BinSoundId_t> lostSounds, newSounds;
std::unordered_set<BinModelId_t> lostModels, newModels;
decrementBinary(lostTextures, lostSounds, lostModels);
decrementBinary(newTextures, newSounds, newModels);
WorldId_c cId = ResRemap.Worlds.toClient(worldId);
// TODO: отправить пакет об изменении мира
LOG.debug() << "Изменение мира: " << worldId << " -> " << cId;
}
void RemoteClient::prepareWorldRemove(WorldId_t worldId)
{
// Чанки уже удалены prepareChunkRemove
// Понизим зависимости ресурсов
ResUsesObj::WorldResourceUse &res = ResUses.Worlds[worldId];
WorldId_c cWorld = ResUses.Worlds.erase(worldId);
LOG.debug() << "Мир потерян: " << worldId << " -> " << cWorld;
NextPacket << (uint8_t) ToClient::L1::Content
<< (uint8_t) ToClient::L2Content::RemoveWorld
<< cWorld;
if(--ResUses.DefWorld[res.DefId] == 0) {
// Определение мира потеряно
ResUses.DefWorld.erase(ResUses.DefWorld.find(res.DefId));
DefWorldId_c cdWorld = ResRemap.DefWorlds.erase(res.DefId);
// TODO: отправить пакет о потере определения мира
LOG.debug() << "Определение мира потеряно: " << res.DefId << " -> " << cdWorld;
}
decrementBinary(res.Textures, res.Sounds, res.Models);
}
void RemoteClient::prepareEntitySwap(WorldId_t prevWorldId, Pos::GlobalRegion prevRegionPos, EntityId_t prevEntityId,
WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, EntityId_t newEntityId)
void RemoteClient::prepareEntitySwap(GlobalEntityId_t prev, GlobalEntityId_t next)
{
ResRemap.Entityes.rebindClientKey(prev, next);
LOG.debug() << "Ребинд сущности: " << std::get<0>(prev) << " / " << std::get<1>(prev).X << ":"
<< std::get<1>(prev).Y << ":" << std::get<1>(prev).Z << " / " << std::get<2>(prev)
<< " -> " << std::get<0>(next) << " / " << std::get<1>(next).X << ":"
<< std::get<1>(next).Y << ":" << std::get<1>(next).Z << " / " << std::get<2>(next);
}
void RemoteClient::prepareEntityUpdate(WorldId_t worldId, Pos::GlobalRegion regionPos,
EntityId_t entityId, const Entity *entity)
void RemoteClient::prepareEntityUpdate(GlobalEntityId_t entityId, const Entity *entity)
{
// Может прийти событие на сущность, про которую ещё ничего не знаем
// Сопоставим с идентификатором клиента
EntityId_c ceId = -1;
EntityId_c ceId = ResRemap.Entityes.toClient(entityId);
auto pWorld = Remap.STC_Entityes.find(worldId);
if(pWorld != Remap.STC_Entityes.end()) {
auto pRegion = pWorld->second.find(regionPos);
if(pRegion != pWorld->second.end()) {
auto pId = pRegion->second.find(entityId);
if(pId != pRegion->second.end()) {
ceId = pId->second;
}
}
}
auto iter = ResUses.Entity.find(entityId);
if(iter == ResUses.Entity.end()) {
// Новая сущность
if(ceId == EntityId_c(-1)) {
// Клиент ещё не знает о сущности
// Выделяем идентификатор на стороне клиента для сущностей
ceId = Remap.UsedEntityIdC._Find_first();
if(ceId != EntityId_c(-1)) {
Remap.UsedEntityIdC.reset(ceId);
Remap.CTS_Entityes[ceId] = {worldId, regionPos, entityId};
Remap.STC_Entityes[worldId][regionPos][entityId] = ceId;
// WorldId_c cwId = ResRemap.Worlds.toClient(std::get<0>(entityId));
ResUsesObj::EntityResourceUse &res = ResUses.Entity[entityId];
res.DefId = entity->getDefId();
if(++ResUses.DefEntity[res.DefId] == 1) {
// Новое определение
NextRequest.NewEntityes.push_back(res.DefId);
DefEntityId_c cId = ResRemap.DefEntityes.toClient(res.DefId);
LOG.debug() << "Новое определение сущности: " << res.DefId << " -> " << cId;
// TODO: Отправить пакет о новом определении
// incrementBinary(Textures, Sounds, Models);
}
incrementBinary(res.Textures, res.Sounds, res.Models);
LOG.debug() << "Новая сущность: " << std::get<0>(entityId) << " / " << std::get<1>(entityId).X << ":"
<< std::get<1>(entityId).Y << ":" << std::get<1>(entityId).Z << " / " << std::get<2>(entityId)
<< " -> " << ceId;
} else {
ResUsesObj::EntityResourceUse &res = iter->second;
LOG.debug() << "Обновление сущности: " << ceId;
}
if(ceId == EntityId_c(-1))
return; // У клиента закончились идентификаторы
// Перебиндить ресурсы скомпилированных конвейеров
// Отправить информацию о сущности
// entity ceId
}
void RemoteClient::prepareEntityRemove(WorldId_t worldId, Pos::GlobalRegion regionPos, EntityId_t entityId)
void RemoteClient::prepareEntityRemove(GlobalEntityId_t entityId)
{
// Освобождаем идентификатор на стороне клиента
auto pWorld = Remap.STC_Entityes.find(worldId);
if(pWorld == Remap.STC_Entityes.end())
return;
EntityId_c cId = ResRemap.Entityes.erase(entityId);
ResUsesObj::EntityResourceUse &res = ResUses.Entity[entityId];
auto pRegion = pWorld->second.find(regionPos);
if(pRegion == pWorld->second.end())
return;
if(--ResUses.DefEntity[res.DefId] == 0) {
LOG.debug() << "Потеряли определение сущности: " << res.DefId << " -> " << cId;
ResUses.DefEntity.erase(ResUses.DefEntity.find(res.DefId));
ResRemap.DefEntityes.erase(res.DefId);
auto pId = pRegion->second.find(entityId);
if(pId == pRegion->second.end())
return;
EntityId_c ceId = pId->second;
Remap.UsedEntityIdC.set(ceId);
{
auto pceid = Remap.CTS_Entityes.find(ceId);
if(pceid != Remap.CTS_Entityes.end())
Remap.CTS_Entityes.erase(pceid);
// decrementBinary(std::unordered_set<BinTextureId_t> &textures, std::unordered_set<BinSoundId_t> &sounds, std::unordered_set<BinModelId_t> &models)
}
pRegion->second.erase(pId);
LOG.debug() << "Сущность потеряна: " << cId;
if(pRegion->second.empty()) {
pWorld->second.erase(pRegion);
if(pWorld->second.empty())
Remap.STC_Entityes.erase(pWorld);
}
// Пакет об удалении сущности
// ceId
NextPacket << (uint8_t) ToClient::L1::Content
<< (uint8_t) ToClient::L2Content::RemoveEntity
<< cId;
}
void RemoteClient::preparePortalNew(PortalId_t portalId, void* portal) {}
void RemoteClient::preparePortalUpdate(PortalId_t portalId, void* portal) {}
void RemoteClient::preparePortalRemove(PortalId_t portalId) {}
void RemoteClient::prepareCameraSetEntity(WorldId_t worldId, Pos::GlobalChunk chunkPos, EntityId_t entityId) {}
void RemoteClient::prepareCameraSetEntity(GlobalEntityId_t entityId) {
}
ResourceRequest RemoteClient::pushPreparedPackets() {
if(NextPacket.size())
SimplePackets.push_back(std::move(NextPacket));
Socket.pushPackets(&SimplePackets);
SimplePackets.clear();
NextRequest.uniq();
return std::move(NextRequest);
return std::move(NextRequest);
}
void RemoteClient::informateDefTexture(const std::unordered_map<TextureId_t, std::shared_ptr<ResourceFile>> &textures) {
}
void RemoteClient::informateDefModel(const std::unordered_map<ModelId_t, std::shared_ptr<ResourceFile>> &models) {
}
void RemoteClient::informateDefSound(const std::unordered_map<SoundId_t, std::shared_ptr<ResourceFile>> &sounds) {
}
WorldId_c RemoteClient::rentWorldRemapId(WorldId_t wId)
void RemoteClient::informateDefTexture(const std::unordered_map<BinTextureId_t, std::shared_ptr<ResourceFile>> &textures)
{
WorldId_c wcId;
for(auto pair : textures) {
BinTextureId_t sId = pair.first;
if(!ResUses.BinTexture.contains(sId))
continue;
auto cwId = Remap.STC_Worlds.find(wId);
if(cwId == Remap.STC_Worlds.end()) {
// Нужно забронировать идентификатор
wcId = Remap.UsedWorldIdC._Find_first();
if(wcId == WorldId_c(-1))
// Нет свободных идентификаторов
return wcId;
else {
NextRequest.NewWorlds.push_back(wId);
Remap.UsedWorldIdC.reset(wcId);
Remap.STC_Worlds[wId] = wcId;
TextureId_c cId = ResRemap.BinTextures.toClient(sId);
NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение
<< (uint8_t) ToClient::L2Resource::Texture << cId;
for(auto part : pair.second->Hash)
NextPacket << part;
NextPacket << (uint8_t) ToClient::L1::Resource // Принудительная отправка
<< (uint8_t) ToClient::L2Resource::InitResSend
<< uint8_t(0) << uint8_t(0) << cId
<< uint32_t(pair.second->Data.size());
for(auto part : pair.second->Hash)
NextPacket << part;
NextPacket << uint8_t(0) << uint32_t(pair.second->Data.size());
size_t pos = 0;
while(pos < pair.second->Data.size()) {
if(NextPacket.size() > 64000) {
SimplePackets.push_back(std::move(NextPacket));
}
size_t need = std::min(pair.second->Data.size()-pos, std::min<size_t>(NextPacket.size(), 64000));
NextPacket.write(pair.second->Data.data()+pos, need);
pos += need;
}
} else {
wcId = cwId->second;
}
return wcId;
if(NextPacket.size())
SimplePackets.push_back(std::move(NextPacket));
}
void RemoteClient::informateDefSound(const std::unordered_map<BinSoundId_t, std::shared_ptr<ResourceFile>> &sounds)
{
for(auto pair : sounds) {
BinSoundId_t sId = pair.first;
if(!ResUses.BinSound.contains(sId))
continue;
SoundId_c cId = ResRemap.BinSounds.toClient(sId);
NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение
<< (uint8_t) ToClient::L2Resource::Sound << cId;
for(auto part : pair.second->Hash)
NextPacket << part;
NextPacket << (uint8_t) ToClient::L1::Resource // Принудительная отправка
<< (uint8_t) ToClient::L2Resource::InitResSend
<< uint8_t(0) << uint8_t(1) << cId
<< uint32_t(pair.second->Data.size());
for(auto part : pair.second->Hash)
NextPacket << part;
NextPacket << uint8_t(0) << uint32_t(pair.second->Data.size());
size_t pos = 0;
while(pos < pair.second->Data.size()) {
if(NextPacket.size() >= 64000) {
SimplePackets.push_back(std::move(NextPacket));
}
size_t need = std::min(pair.second->Data.size()-pos, std::min<size_t>(NextPacket.size(), 64000));
NextPacket.write(pair.second->Data.data()+pos, need);
pos += need;
}
}
}
void RemoteClient::informateDefModel(const std::unordered_map<BinModelId_t, std::shared_ptr<ResourceFile>> &models)
{
for(auto pair : models) {
BinModelId_t sId = pair.first;
if(!ResUses.BinModel.contains(sId))
continue;
ModelId_c cId = ResRemap.BinModels.toClient(sId);
NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение
<< (uint8_t) ToClient::L2Resource::Model << cId;
for(auto part : pair.second->Hash)
NextPacket << part;
NextPacket << (uint8_t) ToClient::L1::Resource // Принудительная отправка
<< (uint8_t) ToClient::L2Resource::InitResSend
<< uint8_t(0) << uint8_t(2) << cId
<< uint32_t(pair.second->Data.size());
for(auto part : pair.second->Hash)
NextPacket << part;
NextPacket << uint8_t(0) << uint32_t(pair.second->Data.size());
size_t pos = 0;
while(pos < pair.second->Data.size()) {
if(NextPacket.size() >= 64000) {
SimplePackets.push_back(std::move(NextPacket));
}
size_t need = std::min(pair.second->Data.size()-pos, std::min<size_t>(NextPacket.size(), 64000));
NextPacket.write(pair.second->Data.data()+pos, need);
pos += need;
}
}
}
void RemoteClient::informateDefWorld(const std::unordered_map<DefWorldId_t, World*> &worlds)
{
for(auto pair : worlds) {
DefWorldId_t sId = pair.first;
if(!ResUses.DefWorld.contains(sId))
continue;
DefWorldId_c cId = ResRemap.DefWorlds.toClient(sId);
NextPacket << (uint8_t) ToClient::L1::Definition
<< (uint8_t) ToClient::L2Definition::World
<< cId;
}
}
void RemoteClient::informateDefVoxel(const std::unordered_map<DefVoxelId_t, void*> &voxels)
{
for(auto pair : voxels) {
DefVoxelId_t sId = pair.first;
if(!ResUses.DefWorld.contains(sId))
continue;
VoxelId_c cId = ResRemap.DefVoxels.toClient(sId);
NextPacket << (uint8_t) ToClient::L1::Definition
<< (uint8_t) ToClient::L2Definition::Voxel
<< cId;
}
}
void RemoteClient::informateDefNode(const std::unordered_map<DefNodeId_t, void*> &nodes)
{
for(auto pair : nodes) {
DefNodeId_t sId = pair.first;
if(!ResUses.DefNode.contains(sId))
continue;
NodeId_c cId = ResRemap.DefNodes.toClient(sId);
NextPacket << (uint8_t) ToClient::L1::Definition
<< (uint8_t) ToClient::L2Definition::Node
<< cId;
}
}
void RemoteClient::informateDefEntityes(const std::unordered_map<DefEntityId_t, void*> &entityes)
{
for(auto pair : entityes) {
DefEntityId_t sId = pair.first;
if(!ResUses.DefNode.contains(sId))
continue;
DefEntityId_c cId = ResRemap.DefEntityes.toClient(sId);
NextPacket << (uint8_t) ToClient::L1::Definition
<< (uint8_t) ToClient::L2Definition::Entity
<< cId;
}
}
void RemoteClient::informateDefPortals(const std::unordered_map<DefPortalId_t, void*> &portals)
{
for(auto pair : portals) {
DefPortalId_t sId = pair.first;
if(!ResUses.DefNode.contains(sId))
continue;
DefPortalId_c cId = ResRemap.DefPortals.toClient(sId);
NextPacket << (uint8_t) ToClient::L1::Definition
<< (uint8_t) ToClient::L2Definition::Portal
<< cId;
}
}
void RemoteClient::incrementBinary(std::unordered_set<BinTextureId_t> &textures, std::unordered_set<BinSoundId_t> &sounds,
std::unordered_set<BinModelId_t> &models)
{
for(BinTextureId_t id : textures) {
if(++ResUses.BinTexture[id] == 1) {
TextureId_c cId = ResRemap.BinTextures.toClient(id);
NextRequest.NewTextures.push_back(id);
LOG.debug() << "Новое определение текстуры: " << id << " -> " << cId;
}
}
for(BinSoundId_t id : sounds) {
if(++ResUses.BinSound[id] == 1) {
SoundId_c cId = ResRemap.BinSounds.toClient(id);
NextRequest.NewSounds.push_back(id);
LOG.debug() << "Новое определение звука: " << id << " -> " << cId;
}
}
for(BinModelId_t id : sounds) {
if(++ResUses.BinModel[id] == 1) {
ModelId_c cId = ResRemap.BinModels.toClient(id);
NextRequest.NewModels.push_back(id);
LOG.debug() << "Новое определение модели: " << id << " -> " << cId;
}
}
}
void RemoteClient::decrementBinary(std::unordered_set<BinTextureId_t> &textures, std::unordered_set<BinSoundId_t> &sounds,
std::unordered_set<BinModelId_t> &models)
{
for(BinTextureId_t id : textures) {
if(--ResUses.BinTexture[id] == 0) {
ResUses.BinTexture.erase(ResUses.BinTexture.find(id));
TextureId_c cId = ResRemap.BinTextures.erase(id);
LOG.debug() << "Потеряно определение текстуры: " << id << " -> " << cId;
NextPacket << (uint8_t) ToClient::L1::Resource
<< (uint8_t) ToClient::L2Resource::FreeTexture
<< cId;
}
}
for(BinSoundId_t id : sounds) {
if(--ResUses.BinSound[id] == 0) {
ResUses.BinSound.erase(ResUses.BinSound.find(id));
SoundId_c cId = ResRemap.BinSounds.erase(id);
LOG.debug() << "Потеряно определение звука: " << id << " -> " << cId;
NextPacket << (uint8_t) ToClient::L1::Resource
<< (uint8_t) ToClient::L2Resource::FreeSound
<< cId;
}
}
for(BinModelId_t id : sounds) {
if(--ResUses.BinModel[id] == 0) {
ResUses.BinModel.erase(ResUses.BinModel.find(id));
ModelId_c cId = ResRemap.BinModels.erase(id);
LOG.debug() << "Потеряно определение модели: " << id << " -> " << cId;
NextPacket << (uint8_t) ToClient::L1::Resource
<< (uint8_t) ToClient::L2Resource::FreeModel
<< cId;
}
}
}