Files
LuaVox/Src/Server/RemoteClient.hpp
2025-03-11 19:37:36 +06:00

364 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include <TOSLib.hpp>
#include <Common/Lockable.hpp>
#include <Common/Net.hpp>
#include <Common/Async.hpp>
#include "Abstract.hpp"
#include "Common/Packets.hpp"
#include "Server/ContentEventController.hpp"
#include "TOSAsync.hpp"
#include "boost/asio/detached.hpp"
#include "boost/asio/io_context.hpp"
#include "boost/asio/use_awaitable.hpp"
#include <Common/Abstract.hpp>
#include <bitset>
#include <initializer_list>
#include <memory>
#include <set>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
namespace LV::Server {
template<typename ServerKey, typename ClientKey, std::enable_if_t<sizeof(ServerKey) >= sizeof(ClientKey), int> = 0>
class CSChunkedMapper {
std::unordered_map<uint32_t, std::tuple<std::bitset<64>, std::array<ServerKey, 64>>> Chunks;
public:
ServerKey toServer(ClientKey cKey) {
int chunkIndex = cKey >> 6;
int subIndex = cKey & 0x3f;
auto iChunk = Chunks.find(chunkIndex);
assert(iChunk != Chunks.end() && "Идентификатор уже занят");
std::bitset<64> &bits = std::get<0>(iChunk.second);
std::array<ServerKey, 64> &keys = std::get<1>(iChunk.second);
assert(bits.test(subIndex) && "Идентификатор уже занят");
return keys[subIndex];
}
void erase(ClientKey cKey) {
int chunkIndex = cKey >> 6;
int subIndex = cKey & 0x3f;
auto iChunk = Chunks.find(chunkIndex);
if(iChunk == Chunks.end())
MAKE_ERROR("Идентификатор не привязан");
std::bitset<64> &bits = std::get<0>(iChunk->second);
std::array<ServerKey, 64> &keys = std::get<1>(iChunk->second);
assert(bits.test(subIndex) && "Идентификатор уже занят");
bits.reset(subIndex);
}
void link(ClientKey cKey, ServerKey sKey) {
int chunkIndex = cKey >> 6;
int subIndex = cKey & 0x3f;
std::tuple<std::bitset<64>, std::array<ServerKey, 64>> &chunk = Chunks[chunkIndex];
std::bitset<64> &bits = std::get<0>(chunk);
std::array<ServerKey, 64> &keys = std::get<1>(chunk);
assert(!bits.test(subIndex) && "Идентификатор уже занят");
bits.set(subIndex);
keys[subIndex] = sKey;
}
};
template<typename ServerKey, typename ClientKey, std::enable_if_t<sizeof(ServerKey) >= sizeof(ClientKey), int> = 0>
class SCSKeyRemapper {
std::bitset<sizeof(ClientKey)*8-1> FreeClientKeys;
std::map<ServerKey, ClientKey> Map;
CSChunkedMapper<ServerKey, ClientKey> CSmapper;
public:
SCSKeyRemapper() {
FreeClientKeys.set();
}
// Соотнести идентификатор на стороне сервера с идентификатором на стороне клиента
ClientKey toClient(ServerKey skey) {
auto iter = Map.find(skey);
if(iter == Map.end()) {
// Идентификатор отсутствует, нужно его занять
// Ищет позицию ближайшего бита 1
size_t pos = FreeClientKeys._Find_first();
if(pos == FreeClientKeys.size())
return ClientKey(0); // Свободные идентификаторы отсутствуют
ClientKey ckey = ClientKey(pos+1);
Map[skey] = ckey;
CSmapper.link(ckey, skey);
FreeClientKeys.reset(pos);
return ClientKey(pos);
}
return iter->second;
}
// Соотнести идентификатор на стороне клиента с идентификатором на стороне сервера
ServerKey toServer(ClientKey ckey) {
return CSmapper.toServer(ckey);
}
// Удаляет серверный идентификатор, освобождая идентификатор клиента
ClientKey erase(ServerKey skey) {
auto iter = Map.find(skey);
if(iter == Map.end())
return 0;
ClientKey ckey = iter->second;
CSmapper.erase(ckey);
Map.erase(iter);
FreeClientKeys.set(ckey-1);
return ckey;
}
void rebindClientKey(ServerKey prev, ServerKey next) {
auto iter = Map.find(prev);
assert(iter != Map.end() && "Идентификатор не найден");
ClientKey ckey = iter->second;
CSmapper.erase(ckey);
CSmapper.link(ckey, next);
Map.erase(iter);
Map[next] = ckey;
}
};
/*
Шаблоны игрового контента, которые необходимо поддерживать в актуальном
состоянии для клиента и шаблоны, которые клиенту уже не нужны.
Соответствующие менеджеры ресурсов будут следить за изменениями
этих ресурсов и переотправлять их клиенту
*/
struct ResourceRequest {
std::vector<BinTextureId_t> NewTextures;
std::vector<BinModelId_t> NewModels;
std::vector<BinSoundId_t> NewSounds;
std::vector<DefWorldId_t> NewWorlds;
std::vector<DefVoxelId_t> NewVoxels;
std::vector<DefNodeId_t> NewNodes;
std::vector<DefPortalId_t> NewPortals;
std::vector<DefEntityId_t> NewEntityes;
void insert(const ResourceRequest &obj) {
NewTextures.insert(NewTextures.end(), obj.NewTextures.begin(), obj.NewTextures.end());
NewModels.insert(NewModels.end(), obj.NewModels.begin(), obj.NewModels.end());
NewSounds.insert(NewSounds.end(), obj.NewSounds.begin(), obj.NewSounds.end());
NewWorlds.insert(NewWorlds.end(), obj.NewWorlds.begin(), obj.NewWorlds.end());
NewVoxels.insert(NewVoxels.end(), obj.NewVoxels.begin(), obj.NewVoxels.end());
NewNodes.insert(NewNodes.end(), obj.NewNodes.begin(), obj.NewNodes.end());
NewPortals.insert(NewPortals.end(), obj.NewPortals.begin(), obj.NewPortals.end());
NewEntityes.insert(NewEntityes.end(), obj.NewEntityes.begin(), obj.NewEntityes.end());
}
void uniq() {
for(std::vector<ResourceId_t> *vec : {&NewTextures, &NewModels, &NewSounds, &NewWorlds, &NewVoxels, &NewNodes, &NewPortals, &NewEntityes}) {
std::sort(vec->begin(), vec->end());
auto last = std::unique(vec->begin(), vec->end());
vec->erase(last, vec->end());
}
}
};
using EntityKey = std::tuple<WorldId_c, Pos::GlobalRegion>;
class RemoteClient;
using RemoteClient_ptr = std::unique_ptr<RemoteClient, std::function<void(RemoteClient*)>>;
/*
Обработчик сокета клиента.
Подписывает клиента на отслеживание необходимых ресурсов
на основе передаваемых клиенту данных
*/
class RemoteClient : public TOS::IAsyncDestructible {
TOS::Logger LOG;
Net::AsyncSocket Socket;
bool IsConnected = true, IsGoingShutdown = false;
struct ResUsesObj {
// Счётчики использования базовых ресурсов высшими объектами
std::map<BinTextureId_t, uint32_t> BinTexture;
std::map<BinSoundId_t, uint32_t> BinSound;
// Может использовать текстуры
std::map<BinModelId_t, uint32_t> BinModel;
// Будут использовать в своих определениях текстуры, звуки, модели
std::map<DefWorldId_t, uint32_t> DefWorld;
std::map<DefVoxelId_t, uint32_t> DefVoxel;
std::map<DefNodeId_t, uint32_t> DefNode;
std::map<DefPortalId_t, uint32_t> DefPortal;
std::map<DefEntityId_t, uint32_t> DefEntity;
// Переписываемый контент
// Сущности используют текстуры, звуки, модели
struct EntityResourceUse {
DefEntityId_t DefId;
std::unordered_set<BinTextureId_t> Textures;
std::unordered_set<BinSoundId_t> Sounds;
std::unordered_set<BinModelId_t> Models;
};
std::map<GlobalEntityId_t, EntityResourceUse> Entity;
// Чанки используют воксели, ноды
std::map<std::tuple<WorldId_t, Pos::GlobalChunk>, std::unordered_set<DefVoxelId_t>> ChunkVoxels;
std::map<std::tuple<WorldId_t, Pos::GlobalChunk>, std::unordered_set<DefNodeId_t>> ChunkNodes;
// Миры
struct WorldResourceUse {
DefWorldId_t DefId;
std::unordered_set<BinTextureId_t> Textures;
std::unordered_set<BinSoundId_t> Sounds;
std::unordered_set<BinModelId_t> Models;
};
std::map<WorldId_t, WorldResourceUse> Worlds;
// Порталы
struct PortalResourceUse {
DefPortalId_t DefId;
std::unordered_set<BinTextureId_t> Textures;
std::unordered_set<BinSoundId_t> Sounds;
std::unordered_set<BinModelId_t> Models;
};
std::map<PortalId_t, PortalResourceUse> Portals;
} ResUses;
struct {
SCSKeyRemapper<BinTextureId_t, TextureId_c> BinTextures;
SCSKeyRemapper<BinSoundId_t, SoundId_c> BinSounds;
SCSKeyRemapper<BinModelId_t, ModelId_c> BinModels;
SCSKeyRemapper<DefWorldId_t, DefWorldId_c> DefWorlds;
SCSKeyRemapper<DefVoxelId_t, DefVoxelId_c> DefVoxels;
SCSKeyRemapper<DefNodeId_t, DefNodeId_c> DefNodes;
SCSKeyRemapper<DefPortalId_t, DefPortalId_c> DefPortals;
SCSKeyRemapper<DefEntityId_t, DefEntityId_c> DefEntityes;
SCSKeyRemapper<WorldId_t, WorldId_c> Worlds;
SCSKeyRemapper<PortalId_t, PortalId_c> Portals;
SCSKeyRemapper<GlobalEntityId_t, EntityId_c> Entityes;
} ResRemap;
Net::Packet NextPacket;
ResourceRequest NextRequest;
std::vector<Net::Packet> SimplePackets;
TOS::WaitableCoro RunCoro;
public:
const std::string Username;
Pos::Object CameraPos = {0, 0, 0};
ToServer::PacketQuat CameraQuat = {0};
private:
RemoteClient(asio::io_context &ioc, tcp::socket socket, const std::string username)
: IAsyncDestructible(ioc), LOG("RemoteClient " + username), Socket(ioc, std::move(socket)),
Username(username), RunCoro(ioc)
{
RunCoro.co_spawn(run());
}
virtual coro<> asyncDestructor() override;
coro<> run();
public:
static RemoteClient_ptr Create(asio::io_context &ioc, tcp::socket socket, const std::string username) {
return createUnique<>(ioc, new RemoteClient(ioc, std::move(socket), username));
}
virtual ~RemoteClient();
void shutdown(EnumDisconnect type, const std::string reason);
bool isConnected() { return IsConnected; }
void pushPackets(std::vector<Net::Packet> *simplePackets, std::vector<Net::SmartPacket> *smartPackets = nullptr) {
if(IsGoingShutdown)
return;
Socket.pushPackets(simplePackets, smartPackets);
}
// Функции подготавливают пакеты к отправке
// Отслеживаемое игроком использование контента
// Maybe?
// Текущий список вокселей, определения нод, которые больше не используются в чанке, и определения нод, которые теперь используются
//void prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::vector<VoxelCube> &voxels, const std::vector<DefVoxelId_t> &noLongerInUseDefs, const std::vector<DefVoxelId_t> &nowUsed);
void prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::vector<VoxelCube> &voxels);
void prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::unordered_map<Pos::Local16_u, Node> &nodes);
void prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalChunk chunkPos, const LightPrism *lights);
void prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkPos);
void prepareEntitySwap(GlobalEntityId_t prevEntityId, GlobalEntityId_t nextEntityId);
void prepareEntityUpdate(GlobalEntityId_t entityId, const Entity *entity);
void prepareEntityRemove(GlobalEntityId_t entityId);
void prepareWorldUpdate(WorldId_t worldId, World* world);
void prepareWorldRemove(WorldId_t worldId);
void preparePortalUpdate(PortalId_t portalId, void* portal);
void preparePortalRemove(PortalId_t portalId);
// Прочие моменты
void prepareCameraSetEntity(GlobalEntityId_t entityId);
// Отправка подготовленных пакетов
ResourceRequest pushPreparedPackets();
// Сообщить о ресурсах
// Сюда приходят все обновления ресурсов движка
// Глобально их можно запросить в выдаче pushPreparedPackets()
// Двоичные файлы
void informateDefTexture(const std::unordered_map<BinTextureId_t, std::shared_ptr<ResourceFile>> &textures);
void informateDefSound(const std::unordered_map<BinSoundId_t, std::shared_ptr<ResourceFile>> &sounds);
void informateDefModel(const std::unordered_map<BinModelId_t, std::shared_ptr<ResourceFile>> &models);
// Игровые определения
void informateDefWorld(const std::unordered_map<DefWorldId_t, World*> &worlds);
void informateDefVoxel(const std::unordered_map<DefVoxelId_t, void*> &voxels);
void informateDefNode(const std::unordered_map<DefNodeId_t, void*> &nodes);
void informateDefEntityes(const std::unordered_map<DefEntityId_t, void*> &entityes);
void informateDefPortals(const std::unordered_map<DefPortalId_t, void*> &portals);
private:
void checkPacketBorder(uint16_t size);
void protocolError();
coro<> readPacket(Net::AsyncSocket &sock);
coro<> rP_System(Net::AsyncSocket &sock);
void incrementBinary(std::unordered_set<BinTextureId_t> &textures, std::unordered_set<BinSoundId_t> &sounds,
std::unordered_set<BinModelId_t> &models);
void decrementBinary(std::unordered_set<BinTextureId_t> &textures, std::unordered_set<BinSoundId_t> &sounds,
std::unordered_set<BinModelId_t> &models);
};
}