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

670 lines
24 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.

#include <TOSLib.hpp>
#include "RemoteClient.hpp"
#include "Common/Net.hpp"
#include "Server/Abstract.hpp"
#include <boost/asio/error.hpp>
#include <boost/chrono/duration.hpp>
#include <boost/system/system_error.hpp>
#include <exception>
#include <unordered_map>
#include <unordered_set>
#include "World.hpp"
#include "boost/asio/steady_timer.hpp"
#include <Common/Packets.hpp>
namespace LV::Server {
coro<> RemoteClient::asyncDestructor() {
shutdown(EnumDisconnect::ByInterface, "~RemoteClient()");
asio::steady_timer deadline(IOC);
deadline.expires_after(std::chrono::seconds(1));
Socket.closeRead();
co_await (deadline.async_wait(asio::use_awaitable) || RunCoro.async_wait());
Socket.close();
co_await deadline.async_wait();
}
RemoteClient::~RemoteClient() = default;
coro<> RemoteClient::run() {
try {
while(!IsGoingShutdown && IsConnected) {
co_await readPacket(Socket);
}
} catch(const std::exception &exc) {
if(const auto *errc = dynamic_cast<const boost::system::system_error*>(&exc);
errc && errc->code() == boost::asio::error::operation_aborted)
{
co_return;
}
TOS::Logger("PlayerSocket").warn() << Username << ": " << exc.what();
}
IsConnected = false;
co_return;
}
void RemoteClient::shutdown(EnumDisconnect type, const std::string reason) {
if(IsGoingShutdown)
return;
IsGoingShutdown = true;
NextPacket << (uint8_t) ToClient::L1::System
<< (uint8_t) ToClient::L2System::Disconnect
<< (uint8_t) type << reason;
std::string info;
if(type == EnumDisconnect::ByInterface)
info = "по запросу интерфейса " + reason;
else if(type == EnumDisconnect::CriticalError)
info = "на сервере произошла критическая ошибка " + reason;
else if(type == EnumDisconnect::ProtocolError)
info = "ошибка протокола (сервер) " + reason;
LOG.info() << "Игрок '" << Username << "' отключился " << info;
}
// Может прийти событие на чанк, про который ещё ничего не знаем
void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos,
const std::vector<VoxelCube> &voxels)
{
WorldId_c wcId = worldId ? ResRemap.Worlds.toClient(worldId) : 0;
assert(wcId != WorldId_c(-1)); // Пока ожидается, что игрок не будет одновременно наблюдать 256 миров
// Перебиндить идентификаторы вокселей
std::vector<DefVoxelId_t> NeedVoxels;
NeedVoxels.reserve(voxels.size());
for(const VoxelCube &cube : voxels) {
NeedVoxels.push_back(cube.VoxelId);
}
std::unordered_set<DefVoxelId_t> NeedVoxelsSet(NeedVoxels.begin(), NeedVoxels.end());
// Собираем информацию о конвертации идентификаторов
std::unordered_map<DefVoxelId_t, DefVoxelId_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));
DefVoxelId_c cId = ResRemap.DefVoxels.erase(id);
// TODO: отправить пакет потери идентификатора
LOG.debug() << "Определение вокселя потеряно: " << id << " -> " << cId;
}
}
}
for(DefVoxelId_t id : nextSet) {
if(!prevSet.contains(id)) {
if(++ResUses.DefVoxel[id] == 1) {
// Определение только появилось
NextRequest.NewVoxels.push_back(id);
DefVoxelId_c cId = ResRemap.DefVoxels.toClient(id);
LOG.debug() << "Новое определение вокселя: " << id << " -> " << cId;
}
}
}
prevSet = std::move(nextSet);
}
checkPacketBorder(voxels.size()*(2+6)+16);
NextPacket << (uint8_t) ToClient::L1::Content
<< (uint8_t) ToClient::L2Content::ChunkVoxels << wcId
<< Pos::GlobalChunk::Key(chunkPos);
NextPacket << uint16_t(voxels.size());
// TODO:
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,
const std::unordered_map<Pos::Local16_u, Node> &nodes)
{
}
void RemoteClient::prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalChunk chunkPos,
const LightPrism *lights)
{
}
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));
DefVoxelId_c cId = ResRemap.DefVoxels.erase(id);
// TODO: отправить пакет потери идентификатора
LOG.debug() << "Определение вокселя потеряно: " << id << " -> " << cId;
}
}
WorldId_c cwId = worldId ? ResRemap.Worlds.toClient(worldId) : worldId;
checkPacketBorder(16);
NextPacket << (uint8_t) ToClient::L1::Content
<< (uint8_t) ToClient::L2Content::RemoveChunk
<< cwId << Pos::GlobalChunk::Key(chunkPos);
}
void RemoteClient::prepareWorldUpdate(WorldId_t worldId, World* world)
{
ResUsesObj::WorldResourceUse &res = ResUses.Worlds[worldId];
auto iter = ResUses.Worlds.find(worldId);
if(iter == ResUses.Worlds.end()) {
// Новый мир
WorldId_c cwId = ResRemap.Worlds.toClient(worldId);
ResUsesObj::WorldResourceUse &res = iter->second;
res.DefId = world->getDefId();
if(++ResUses.DefWorld[res.DefId] == 1) {
// Новое определение
NextRequest.NewWorlds.push_back(res.DefId);
DefWorldId_c cdId = ResRemap.DefWorlds.toClient(res.DefId);
LOG.debug() << "Новое определение мира: " << res.DefId << " -> " << int(cdId);
NextPacket << (uint8_t) ToClient::L1::Definition
<< (uint8_t) ToClient::L2Definition::World
<< cdId;
// incrementBinary(Textures, Sounds, Models);
}
incrementBinary(res.Textures, res.Sounds, res.Models);
LOG.debug() << "Новый мир: " << worldId << " -> " << int(cwId);
} else {
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 = worldId ? ResRemap.Worlds.toClient(worldId) : worldId;
// TODO: отправить пакет об изменении мира
LOG.debug() << "Изменение мира: " << worldId << " -> " << cId;
}
}
void RemoteClient::prepareWorldRemove(WorldId_t worldId)
{
// Чанки уже удалены prepareChunkRemove
// Понизим зависимости ресурсов
ResUsesObj::WorldResourceUse &res = ResUses.Worlds[worldId];
WorldId_c cWorld = worldId ? ResUses.Worlds.erase(worldId) : 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(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(GlobalEntityId_t entityId, const Entity *entity)
{
// Может прийти событие на сущность, про которую ещё ничего не знаем
// Сопоставим с идентификатором клиента
EntityId_c ceId = ResRemap.Entityes.toClient(entityId);
auto iter = ResUses.Entity.find(entityId);
if(iter == ResUses.Entity.end()) {
// Новая сущность
// 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;
}
}
void RemoteClient::prepareEntityRemove(GlobalEntityId_t entityId)
{
EntityId_c cId = ResRemap.Entityes.erase(entityId);
ResUsesObj::EntityResourceUse &res = ResUses.Entity[entityId];
if(--ResUses.DefEntity[res.DefId] == 0) {
LOG.debug() << "Потеряли определение сущности: " << res.DefId << " -> " << cId;
ResUses.DefEntity.erase(ResUses.DefEntity.find(res.DefId));
ResRemap.DefEntityes.erase(res.DefId);
// decrementBinary(std::unordered_set<BinTextureId_t> &textures, std::unordered_set<BinSoundId_t> &sounds, std::unordered_set<BinModelId_t> &models)
}
LOG.debug() << "Сущность потеряна: " << cId;
checkPacketBorder(16);
NextPacket << (uint8_t) ToClient::L1::Content
<< (uint8_t) ToClient::L2Content::RemoveEntity
<< cId;
}
void RemoteClient::preparePortalUpdate(PortalId_t portalId, void* portal) {}
void RemoteClient::preparePortalRemove(PortalId_t portalId) {}
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);
}
void RemoteClient::informateDefTexture(const std::unordered_map<BinTextureId_t, std::shared_ptr<ResourceFile>> &textures)
{
for(auto pair : textures) {
BinTextureId_t sId = pair.first;
if(!ResUses.BinTexture.contains(sId))
continue;
TextureId_c cId = ResRemap.BinTextures.toClient(sId);
checkPacketBorder(0);
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()) {
checkPacketBorder(0);
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::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);
checkPacketBorder(0);
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;
DefVoxelId_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;
DefNodeId_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::checkPacketBorder(uint16_t size) {
if(64000-NextPacket.size() < size || (NextPacket.size() != 0 && size == 0)) {
SimplePackets.push_back(std::move(NextPacket));
}
}
void RemoteClient::protocolError() {
shutdown(EnumDisconnect::ProtocolError, "Ошибка протокола");
}
coro<> RemoteClient::readPacket(Net::AsyncSocket &sock) {
uint8_t first = co_await sock.read<uint8_t>();
switch((ToServer::L1) first) {
case ToServer::L1::System: co_await rP_System(sock); co_return;
default:
protocolError();
}
}
coro<> RemoteClient::rP_System(Net::AsyncSocket &sock) {
uint8_t second = co_await sock.read<uint8_t>();
switch((ToServer::L2System) second) {
case ToServer::L2System::InitEnd:
co_return;
case ToServer::L2System::Disconnect:
{
EnumDisconnect type = (EnumDisconnect) co_await sock.read<uint8_t>();
shutdown(EnumDisconnect::ByInterface, "Вы были отключены от игры");
std::string reason;
if(type == EnumDisconnect::CriticalError)
reason = ": Критическая ошибка";
else
reason = ": Ошибка протокола (клиент)";
LOG.info() << "Игрок '" << Username << "' отключился" << reason;
co_return;
}
case ToServer::L2System::Test_CAM_PYR_POS:
{
CameraPos.x = co_await sock.read<decltype(CameraPos.x)>();
CameraPos.y = co_await sock.read<decltype(CameraPos.x)>();
CameraPos.z = co_await sock.read<decltype(CameraPos.x)>();
for(int iter = 0; iter < 5; iter++)
CameraQuat.Data[iter] = co_await sock.read<uint8_t>();
co_return;
}
default:
protocolError();
}
}
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;
}
}
}
}