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

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

158
Src/Common/Packets.hpp Normal file
View File

@@ -0,0 +1,158 @@
#pragma once
#include <cstdint>
#include <glm/ext/quaternion_float.hpp>
namespace LV {
namespace ToServer {
struct PacketQuat {
uint8_t Data[5];
void fromQuat(const glm::quat &quat) {
uint16_t
x = (quat.x+1)/2*0x3ff,
y = (quat.y+1)/2*0x3ff,
z = (quat.z+1)/2*0x3ff,
w = (quat.w+1)/2*0x3ff;
for(uint8_t &val : Data)
val = 0;
*(uint16_t*) Data |= x;
*(uint16_t*) (Data+1) |= y << 2;
*(uint16_t*) (Data+2) |= z << 4;
*(uint16_t*) (Data+3) |= w << 6;
}
glm::quat toQuat() const {
const uint64_t &data = (const uint64_t&) *Data;
uint16_t
x = data & 0x3ff,
y = (data >> 10) & 0x3ff,
z = (data >> 20) & 0x3ff,
w = (data >> 30) & 0x3ff;
float fx = (float(x)/0x3ff)*2-1;
float fy = (float(y)/0x3ff)*2-1;
float fz = (float(z)/0x3ff)*2-1;
float fw = (float(w)/0x3ff)*2-1;
return glm::quat(fx, fy, fz, fw);
}
};
/*
uint8_t+uint8_t
0 - Системное
0 - Новая позиция камеры WorldId_c+ObjectPos+PacketQuat
*/
}
namespace ToClient {
/*
uint8_t+uint8_t
0 - Системное
0 - Инициализация WorldId_c+ObjectPos
1 - Отключение от сервера String(Причина)
2 - Привязка камеры к сущности EntityId_c
3 - Отвязка камеры
1 - Оповещение о доступном ресурсе
0 - Текстура TextureId_c+Hash
1 - Освобождение текстуры TextureId_c
2 - Звук SoundId_c+Hash
3 - Освобождение звука SoundId_c
4 - Модель ModelId_c+Hash
5 - Освобождение модели ModelId_c
253 - Инициирование передачи ресурса StreamId+ResType+ResId+Size+Hash
254 - Передача чанка данных StreamId+Size+Data
255 - Передача отменена StreamId
2 - Новые определения
0 - Мир DefWorldId_c+определение
1 - Освобождение мира DefWorldId_c
2 - Воксель DefVoxelId_c+определение
3 - Освобождение вокселя DefVoxelId_c
4 - Нода DefNodeId_c+определение
5 - Освобождение ноды DefNodeId_c
6 - Портал DefPortalId_c+определение
7 - Освобождение портала DefPortalId_c
8 - Сущность DefEntityId_c+определение
9 - Освобождение сущности DefEntityId_c
3 - Новый контент
0 - Мир, новый/изменён WorldId_c+...
1 - Мир/Удалён WorldId_c
2 - Портал, новый/изменён PortalId_c+...
3 - Портал/Удалён PortalId_c
4 - Сущность, новый/изменён EntityId_c+...
5 - Сущность/Удалёна EntityId_c
6 - Чанк/Воксели WorldId_c+GlobalChunk+...
7 - Чанк/Ноды WorldId_c+GlobalChunk+...
8 - Чанк/Призмы освещения WorldId_c+GlobalChunk+...
9 - Чанк/Удалён WorldId_c+GlobalChunk
*/
// Первый уровень
enum struct L1 : uint8_t {
System,
Resource,
Definition,
Content
};
// Второй уровень
enum struct L2System : uint8_t {
Init,
Disconnect,
LinkCameraToEntity,
UnlinkCamera
};
enum struct L2Resource : uint8_t {
Texture,
FreeTexture,
Sound,
FreeSound,
Model,
FreeModel,
InitResSend = 253,
ChunkSend,
SendCanceled
};
enum struct L2Definition : uint8_t {
World,
FreeWorld,
Voxel,
FreeVoxel,
Node,
FreeNode,
Portal,
FreePortal,
Entity,
FreeEntity
};
enum struct L2Content : uint8_t {
World,
RemoveWorld,
Portal,
RemovePortal,
Entity,
RemoveEntity,
ChunkVoxels,
ChunkNodes,
ChunkLightPrism,
RemoveChunk
};
}
}

View File

@@ -9,39 +9,45 @@
namespace LV::Server { namespace LV::Server {
// Идентификаторы на стороне клиента // Идентификаторы на стороне клиента
using TextureId_c = uint16_t;
using SoundId_c = uint16_t;
using ModelId_c = uint16_t;
using DefWorldId_c = uint8_t;
using WorldId_c = uint8_t;
using VoxelId_c = uint16_t; using VoxelId_c = uint16_t;
using NodeId_c = uint16_t; using NodeId_c = uint16_t;
using WorldId_c = uint8_t; using DefPortalId_c = uint8_t;
using PortalId_c = uint8_t; using PortalId_c = uint8_t;
using DefEntityId_c = uint16_t;
using EntityId_c = uint16_t; using EntityId_c = uint16_t;
using TextureId_c = uint16_t;
using ModelId_c = uint16_t;
using ResourceId_t = uint32_t; using ResourceId_t = uint32_t;
// Двоичные данные // Двоичные данные
using BinTextureId_t = ResourceId_t; using BinTextureId_t = ResourceId_t;
using BinModelId_t = ResourceId_t;
using BinSoundId_t = ResourceId_t; using BinSoundId_t = ResourceId_t;
using BinModelId_t = ResourceId_t;
// Игровые определения // Игровые определения
using DefWorldId_t = ResourceId_t;
using DefVoxelId_t = ResourceId_t; using DefVoxelId_t = ResourceId_t;
using DefNodeId_t = ResourceId_t; using DefNodeId_t = ResourceId_t;
using DefWorldId_t = ResourceId_t;
using DefPortalId_t = ResourceId_t; using DefPortalId_t = ResourceId_t;
using DefEntityId_t = ResourceId_t; using DefEntityId_t = ResourceId_t;
// Конент, основанный на игровых определениях
// Контент, основанный на игровых определениях
using WorldId_t = ResourceId_t; using WorldId_t = ResourceId_t;
// В одном регионе может быть максимум 2^16 сущностей. Клиенту адресуются сущности в формате <мир>+<позиция региона>+<uint16_t> // В одном регионе может быть максимум 2^16 сущностей. Клиенту адресуются сущности в формате <мир>+<позиция региона>+<uint16_t>
// И если сущность перешла из одного региона в другой, идентификатор сущности на стороне клиента сохраняется // И если сущность перешла из одного региона в другой, идентификатор сущности на стороне клиента сохраняется
using LocalEntityId_t = uint16_t; using LocalEntityId_t = uint16_t;
using GlobalEntityId_t = std::tuple<WorldId_t, Pos::GlobalRegion, LocalEntityId_t>; using GlobalEntityId_t = std::tuple<WorldId_t, Pos::GlobalRegion, LocalEntityId_t>;
using PortalId_t = uint16_t; using PortalId_t = uint16_t;
using MediaStreamId_t = uint16_t; using MediaStreamId_t = uint16_t;
using ContentBridgeId_t = uint16_t; using ContentBridgeId_t = uint16_t;
using PlayerId_t = uint32_t; using PlayerId_t = uint32_t;
@@ -130,7 +136,7 @@ struct CollisionAABB : public AABB {
union { union {
struct { struct {
EntityId_t Index; LocalEntityId_t Index;
} Entity; } Entity;
struct { struct {
@@ -161,6 +167,8 @@ struct CollisionAABB : public AABB {
class Entity { class Entity {
DefEntityId_t DefId;
public: public:
LocalAABB ABBOX; LocalAABB ABBOX;
@@ -185,11 +193,13 @@ public:
IsRemoved = false; IsRemoved = false;
public: public:
Entity(); Entity(DefEntityId_t defId);
AABB aabbAtPos() { AABB aabbAtPos() {
return {Pos-Pos::Object(ABBOX.x/2, ABBOX.y/2, ABBOX.z/2), Pos+Pos::Object(ABBOX.x/2, ABBOX.y/2, ABBOX.z/2)}; return {Pos-Pos::Object(ABBOX.x/2, ABBOX.y/2, ABBOX.z/2), Pos+Pos::Object(ABBOX.x/2, ABBOX.y/2, ABBOX.z/2)};
} }
DefEntityId_t getDefId() const { return DefId; }
}; };

View File

@@ -2,6 +2,7 @@
#include "Common/Abstract.hpp" #include "Common/Abstract.hpp"
#include "RemoteClient.hpp" #include "RemoteClient.hpp"
#include "Server/Abstract.hpp" #include "Server/Abstract.hpp"
#include "World.hpp"
namespace LV::Server { namespace LV::Server {
@@ -51,7 +52,11 @@ void ContentEventController::onRegionsLost(WorldId_t worldId, const std::vector<
} }
} }
void ContentEventController::onChunksEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_set<Pos::Local16_u> &enter, const std::unordered_set<Pos::Local16_u> &lost) { void ContentEventController::onChunksEnterLost(WorldId_t worldId, World *worldObj, Pos::GlobalRegion regionPos, const std::unordered_set<Pos::Local16_u> &enter, const std::unordered_set<Pos::Local16_u> &lost) {
if(!Subscribed.Chunks.contains(worldId)) {
Remote->prepareWorldNew(worldId, worldObj);
}
std::unordered_set<Pos::Local16_u> &chunks = Subscribed.Chunks[worldId][regionPos]; std::unordered_set<Pos::Local16_u> &chunks = Subscribed.Chunks[worldId][regionPos];
chunks.insert(enter.begin(), enter.end()); chunks.insert(enter.begin(), enter.end());
@@ -67,6 +72,11 @@ void ContentEventController::onChunksEnterLost(WorldId_t worldId, Pos::GlobalReg
Remote->prepareChunkRemove(worldId, chunkPos); Remote->prepareChunkRemove(worldId, chunkPos);
} }
if(Subscribed.Chunks[worldId].empty()) {
Subscribed.Chunks.erase(Subscribed.Chunks.find(worldId));
Remote->prepareWorldRemove(worldId);
}
} }
void ContentEventController::onChunksUpdate_Voxels(WorldId_t worldId, Pos::GlobalRegion regionPos, void ContentEventController::onChunksUpdate_Voxels(WorldId_t worldId, Pos::GlobalRegion regionPos,
@@ -145,7 +155,7 @@ void ContentEventController::onChunksUpdate_LightPrism(WorldId_t worldId, Pos::G
} }
void ContentEventController::onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, void ContentEventController::onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos,
const std::unordered_set<EntityId_t> &enter, const std::unordered_set<EntityId_t> &lost) const std::unordered_set<LocalEntityId_t> &enter, const std::unordered_set<LocalEntityId_t> &lost)
{ {
auto pWorld = Subscribed.Entities.find(worldId); auto pWorld = Subscribed.Entities.find(worldId);
if(pWorld == Subscribed.Entities.end()) { if(pWorld == Subscribed.Entities.end()) {
@@ -161,9 +171,9 @@ void ContentEventController::onEntityEnterLost(WorldId_t worldId, Pos::GlobalReg
pRegion = pWorld->second.find(regionPos); pRegion = pWorld->second.find(regionPos);
} }
std::unordered_set<EntityId_t> &entityesId = pRegion->second; std::unordered_set<LocalEntityId_t> &entityesId = pRegion->second;
for(EntityId_t eId : lost) { for(LocalEntityId_t eId : lost) {
entityesId.erase(eId); entityesId.erase(eId);
} }
@@ -177,13 +187,13 @@ void ContentEventController::onEntityEnterLost(WorldId_t worldId, Pos::GlobalReg
} }
// Сообщить Remote // Сообщить Remote
for(EntityId_t eId : lost) { for(LocalEntityId_t eId : lost) {
Remote->prepareEntityRemove(worldId, regionPos, eId); Remote->prepareEntityRemove({worldId, regionPos, eId});
} }
} }
void ContentEventController::onEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegion lastRegionPos, void ContentEventController::onEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegion lastRegionPos,
EntityId_t lastId, WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, EntityId_t newId) LocalEntityId_t lastId, WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, LocalEntityId_t newId)
{ {
// Проверим отслеживается ли эта сущность нами // Проверим отслеживается ли эта сущность нами
auto lpWorld = Subscribed.Entities.find(lastWorldId); auto lpWorld = Subscribed.Entities.find(lastWorldId);
@@ -210,13 +220,13 @@ void ContentEventController::onEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegi
lpRegion->second.erase(lpceId); lpRegion->second.erase(lpceId);
npRegion->second.insert(newId); npRegion->second.insert(newId);
Remote->prepareEntitySwap(lastWorldId, lastRegionPos, lastId, newWorldId, newRegionPos, newId); Remote->prepareEntitySwap({lastWorldId, lastRegionPos, lastId}, {newWorldId, newRegionPos, newId});
goto entitySwaped; goto entitySwaped;
} }
} }
Remote->prepareEntityRemove(lastWorldId, lastRegionPos, lastId); Remote->prepareEntityRemove({lastWorldId, lastRegionPos, lastId});
entitySwaped: entitySwaped:
return; return;
@@ -239,7 +249,7 @@ void ContentEventController::onEntityUpdates(WorldId_t worldId, Pos::GlobalRegio
if(!lpRegion->second.contains(eId)) if(!lpRegion->second.contains(eId))
continue; continue;
Remote->prepareEntityUpdate(worldId, regionPos, eId, &entities[eId]); Remote->prepareEntityUpdate({worldId, regionPos, eId}, &entities[eId]);
} }
} }

View File

@@ -12,12 +12,7 @@ namespace LV::Server {
class RemoteClient; class RemoteClient;
class GameServer; class GameServer;
class World;
struct GlobalEntityId {
WorldId_t WorldId;
Pos::GlobalChunk ChunkPos;
EntityId_t EntityId;
};
struct ServerObjectPos { struct ServerObjectPos {
@@ -92,7 +87,7 @@ private:
// Используется регионами // Используется регионами
std::vector<PortalId_t> Portals; std::vector<PortalId_t> Portals;
std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalRegion, std::unordered_set<Pos::Local16_u>>> Chunks; std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalRegion, std::unordered_set<Pos::Local16_u>>> Chunks;
std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalRegion, std::unordered_set<EntityId_t>>> Entities; std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalRegion, std::unordered_set<LocalEntityId_t>>> Entities;
} Subscribed; } Subscribed;
public: public:
@@ -115,14 +110,14 @@ public:
// Регионы следят за чанками, которые видят игроки // Регионы следят за чанками, которые видят игроки
void onRegionsLost(WorldId_t worldId, const std::vector<Pos::GlobalRegion> &lost); void onRegionsLost(WorldId_t worldId, const std::vector<Pos::GlobalRegion> &lost);
void onChunksEnterLost(WorldId_t worldId, Pos::GlobalRegion regionId, const std::unordered_set<Pos::Local16_u> &enter, const std::unordered_set<Pos::Local16_u> &lost); void onChunksEnterLost(WorldId_t worldId, World *worldObj, Pos::GlobalRegion regionId, const std::unordered_set<Pos::Local16_u> &enter, const std::unordered_set<Pos::Local16_u> &lost);
// Нужно фильтровать неотслеживаемые чанки // Нужно фильтровать неотслеживаемые чанки
void onChunksUpdate_Voxels(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<Pos::Local16_u, const std::vector<VoxelCube>*> &chunks); void onChunksUpdate_Voxels(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<Pos::Local16_u, const std::vector<VoxelCube>*> &chunks);
void onChunksUpdate_Nodes(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<Pos::Local16_u, const std::unordered_map<Pos::Local16_u, Node>*> &chunks); void onChunksUpdate_Nodes(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<Pos::Local16_u, const std::unordered_map<Pos::Local16_u, Node>*> &chunks);
void onChunksUpdate_LightPrism(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<Pos::Local16_u, const LightPrism*> &chunks); void onChunksUpdate_LightPrism(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<Pos::Local16_u, const LightPrism*> &chunks);
void onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_set<EntityId_t> &enter, const std::unordered_set<EntityId_t> &lost); void onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_set<LocalEntityId_t> &enter, const std::unordered_set<LocalEntityId_t> &lost);
void onEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegion lastRegionPos, EntityId_t lastId, WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, EntityId_t newId); void onEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegion lastRegionPos, LocalEntityId_t lastId, WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, LocalEntityId_t newId);
void onEntityUpdates(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::vector<Entity> &entities); void onEntityUpdates(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::vector<Entity> &entities);
void onPortalEnterLost(const std::vector<void*> &enter, const std::vector<void*> &lost); void onPortalEnterLost(const std::vector<void*> &enter, const std::vector<void*> &lost);

View File

@@ -19,6 +19,7 @@ GameServer::~GameServer() {
RunThread.join(); RunThread.join();
WorkDeadline.cancel(); WorkDeadline.cancel();
UseLock.wait_no_use(); UseLock.wait_no_use();
LOG.info() << "Сервер уничтожен";
} }
static thread_local std::vector<ContentViewCircle> TL_Circles; static thread_local std::vector<ContentViewCircle> TL_Circles;
@@ -270,7 +271,7 @@ void GameServer::run() {
} }
// Конец // Конец
return; break;
} }
// //
@@ -313,6 +314,8 @@ void GameServer::run() {
CurrentTickDuration += PerTickAdjustment; CurrentTickDuration += PerTickAdjustment;
} }
} }
LOG.info() << "Сервер завершил работу";
} }
void GameServer::stepContent() { void GameServer::stepContent() {
@@ -547,10 +550,10 @@ void GameServer::stepWorlds() {
if(rPos != pRegion.first || pWorld.first != entity.WorldId) { if(rPos != pRegion.first || pWorld.first != entity.WorldId) {
Region *toRegion = Expanse.Worlds[entity.WorldId]->forceLoadOrGetRegion(rPos); Region *toRegion = Expanse.Worlds[entity.WorldId]->forceLoadOrGetRegion(rPos);
EntityId_t newId = toRegion->pushEntity(entity); LocalEntityId_t newId = toRegion->pushEntity(entity);
// toRegion->Entityes[newId].WorldId = Если мир изменился // toRegion->Entityes[newId].WorldId = Если мир изменился
if(newId == EntityId_t(-1)) { if(newId == LocalEntityId_t(-1)) {
// В другом регионе нет места // В другом регионе нет места
} else { } else {
entity.IsRemoved = true; entity.IsRemoved = true;
@@ -675,7 +678,7 @@ void GameServer::stepWorlds() {
doesNotObserve: doesNotObserve:
if(!newChunksSet.empty() || !lostChunks.empty()) if(!newChunksSet.empty() || !lostChunks.empty())
cec->onChunksEnterLost(pWorld.first, pRegion.first, newChunksSet, std::unordered_set<Pos::Local16_u>(lostChunks.begin(), lostChunks.end())); cec->onChunksEnterLost(pWorld.first, pWorld.second.get(), pRegion.first, newChunksSet, std::unordered_set<Pos::Local16_u>(lostChunks.begin(), lostChunks.end()));
// Нужно отправить полную информацию о новых наблюдаемых чанках наблюдателю // Нужно отправить полную информацию о новых наблюдаемых чанках наблюдателю
if(!newChunksSet.empty()) { if(!newChunksSet.empty()) {
@@ -706,7 +709,7 @@ void GameServer::stepWorlds() {
// Проверка отслеживания сущностей // Проверка отслеживания сущностей
{ {
std::vector<EntityId_t> newEntityes, lostEntityes; std::vector<LocalEntityId_t> newEntityes, lostEntityes;
for(size_t iter = 0; iter < region.Entityes.size(); iter++) { for(size_t iter = 0; iter < region.Entityes.size(); iter++) {
Entity &entity = region.Entityes[iter]; Entity &entity = region.Entityes[iter];
@@ -724,7 +727,7 @@ void GameServer::stepWorlds() {
} }
} }
std::unordered_set<EntityId_t> newEntityesSet(newEntityes.begin(), newEntityes.end()); std::unordered_set<LocalEntityId_t> newEntityesSet(newEntityes.begin(), newEntityes.end());
{ {
auto iterR_W = subs.Entities.find(pWorld.first); auto iterR_W = subs.Entities.find(pWorld.first);
@@ -738,7 +741,7 @@ void GameServer::stepWorlds() {
goto doesNotObserveEntityes; goto doesNotObserveEntityes;
// Подходят ли уже наблюдаемые сущности под наблюдательные области // Подходят ли уже наблюдаемые сущности под наблюдательные области
for(EntityId_t eId : iterR_W_R->second) { for(LocalEntityId_t eId : iterR_W_R->second) {
if(eId >= region.Entityes.size()) { if(eId >= region.Entityes.size()) {
lostEntityes.push_back(eId); lostEntityes.push_back(eId);
break; break;
@@ -762,13 +765,13 @@ void GameServer::stepWorlds() {
} }
// Удалим чанки которые наблюдатель уже видит // Удалим чанки которые наблюдатель уже видит
for(EntityId_t eId : iterR_W_R->second) for(LocalEntityId_t eId : iterR_W_R->second)
newEntityesSet.erase(eId); newEntityesSet.erase(eId);
} }
doesNotObserveEntityes: doesNotObserveEntityes:
cec->onEntityEnterLost(pWorld.first, pRegion.first, newEntityesSet, std::unordered_set<EntityId_t>(lostEntityes.begin(), lostEntityes.end())); cec->onEntityEnterLost(pWorld.first, pRegion.first, newEntityesSet, std::unordered_set<LocalEntityId_t>(lostEntityes.begin(), lostEntityes.end()));
// Отправить полную информацию о новых наблюдаемых сущностях наблюдателю // Отправить полную информацию о новых наблюдаемых сущностях наблюдателю
} }

View File

@@ -7,6 +7,8 @@
#include <exception> #include <exception>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include "World.hpp"
#include <Common/Packets.hpp>
namespace LV::Server { namespace LV::Server {
@@ -43,77 +45,81 @@ void RemoteClient::shutdown(const std::string reason) {
return; return;
IsGoingShutdown = true; 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, void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos,
const std::vector<VoxelCube> &voxels) const std::vector<VoxelCube> &voxels)
{ {
WorldId_c wcId = rentWorldRemapId(worldId); WorldId_c wcId = ResRemap.Worlds.toClient(worldId);
if(wcId == WorldId_c(-1)) assert(wcId != WorldId_c(-1));
return;
// Перебиндить идентификаторы вокселей // Перебиндить идентификаторы вокселей
std::vector<VoxelId_t> NeedVoxels; std::vector<DefVoxelId_t> NeedVoxels;
NeedVoxels.reserve(voxels.size()); NeedVoxels.reserve(voxels.size());
for(const VoxelCube &cube : voxels) { 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; std::unordered_map<DefVoxelId_t, VoxelId_c> LocalRemapper;
for(VoxelId_t vId : NeedVoxelsSet) { for(DefVoxelId_t vId : NeedVoxelsSet) {
auto cvId = Remap.STC_Voxels.find(vId); LocalRemapper[vId] = ResRemap.DefVoxels.toClient(vId);
if(cvId == Remap.STC_Voxels.end()) { }
// Нужно забронировать идентификатор
VoxelId_c cvnId = Remap.UsedVoxelIdC._Find_first(); // Проверить новые и забытые определения вокселей
if(cvnId == VoxelId_c(-1)) {
// Нет свободных идентификаторов std::unordered_set<DefVoxelId_t> &prevSet = ResUses.ChunkVoxels[{worldId, chunkPos}];
LocalRemapper[vId] = 0; std::unordered_set<DefVoxelId_t> &nextSet = NeedVoxelsSet;
else {
NextRequest.NewVoxels.push_back(vId); std::vector<DefVoxelId_t> newVoxels, lostVoxels;
Remap.UsedVoxelIdC.reset(cvnId); for(DefVoxelId_t id : prevSet) {
Remap.STC_Voxels[vId] = cvnId; if(!nextSet.contains(id)) {
LocalRemapper[vId] = cvnId; 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;
} }
} }
Net::Packet packet; 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);
}
// TODO: отправить новую информацию о расположении вокселей
LOG.debug() << "Новый чанк: " << worldId << " / " << chunkPos.X << ":" << chunkPos.Y << ":" << chunkPos.Z;
// Packet Id // Packet Id
packet << uint16_t(0); // NextPacket << uint16_t(0);
packet << wcId << Pos::GlobalChunk::Key(chunkPos); // NextPacket << wcId << Pos::GlobalChunk::Key(chunkPos);
packet << uint16_t(voxels.size()); // NextPacket << uint16_t(voxels.size());
for(const VoxelCube &cube : voxels) { // for(const VoxelCube &cube : voxels) {
packet << LocalRemapper[cube.Material] // NextPacket << LocalRemapper[cube.VoxelId]
<< cube.Left.X << cube.Left.Y << cube.Left.Z // << cube.Left.X << cube.Left.Y << cube.Left.Z
<< cube.Right.X << cube.Right.Y << cube.Right.Z; // << cube.Right.X << cube.Right.Y << cube.Right.Z;
} // }
SimplePackets.push_back(std::move(packet));
} }
void RemoteClient::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, void RemoteClient::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos,
@@ -131,103 +137,196 @@ void RemoteClient::prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalC
void RemoteClient::prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkPos) 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) 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;
} }
void RemoteClient::prepareEntitySwap(WorldId_t prevWorldId, Pos::GlobalRegion prevRegionPos, EntityId_t prevEntityId, decrementBinary(res.Textures, res.Sounds, res.Models);
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, void RemoteClient::prepareEntityUpdate(GlobalEntityId_t entityId, const Entity *entity)
EntityId_t entityId, const Entity *entity)
{ {
// Может прийти событие на сущность, про которую ещё ничего не знаем // Может прийти событие на сущность, про которую ещё ничего не знаем
// Сопоставим с идентификатором клиента // Сопоставим с идентификатором клиента
EntityId_c ceId = -1; EntityId_c ceId = ResRemap.Entityes.toClient(entityId);
auto pWorld = Remap.STC_Entityes.find(worldId); auto iter = ResUses.Entity.find(entityId);
if(pWorld != Remap.STC_Entityes.end()) { if(iter == ResUses.Entity.end()) {
auto pRegion = pWorld->second.find(regionPos); // Новая сущность
if(pRegion != pWorld->second.end()) {
auto pId = pRegion->second.find(entityId); // WorldId_c cwId = ResRemap.Worlds.toClient(std::get<0>(entityId));
if(pId != pRegion->second.end()) {
ceId = pId->second; 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)) { void RemoteClient::prepareEntityRemove(GlobalEntityId_t entityId)
// Клиент ещё не знает о сущности
// Выделяем идентификатор на стороне клиента для сущностей
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;
}
}
if(ceId == EntityId_c(-1))
return; // У клиента закончились идентификаторы
// Перебиндить ресурсы скомпилированных конвейеров
// Отправить информацию о сущности
// entity ceId
}
void RemoteClient::prepareEntityRemove(WorldId_t worldId, Pos::GlobalRegion regionPos, EntityId_t entityId)
{ {
// Освобождаем идентификатор на стороне клиента EntityId_c cId = ResRemap.Entityes.erase(entityId);
auto pWorld = Remap.STC_Entityes.find(worldId); ResUsesObj::EntityResourceUse &res = ResUses.Entity[entityId];
if(pWorld == Remap.STC_Entityes.end())
return;
auto pRegion = pWorld->second.find(regionPos); if(--ResUses.DefEntity[res.DefId] == 0) {
if(pRegion == pWorld->second.end()) LOG.debug() << "Потеряли определение сущности: " << res.DefId << " -> " << cId;
return; ResUses.DefEntity.erase(ResUses.DefEntity.find(res.DefId));
ResRemap.DefEntityes.erase(res.DefId);
auto pId = pRegion->second.find(entityId); // decrementBinary(std::unordered_set<BinTextureId_t> &textures, std::unordered_set<BinSoundId_t> &sounds, std::unordered_set<BinModelId_t> &models)
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);
} }
pRegion->second.erase(pId); LOG.debug() << "Сущность потеряна: " << cId;
if(pRegion->second.empty()) { NextPacket << (uint8_t) ToClient::L1::Content
pWorld->second.erase(pRegion); << (uint8_t) ToClient::L2Content::RemoveEntity
<< cId;
if(pWorld->second.empty())
Remap.STC_Entityes.erase(pWorld);
}
// Пакет об удалении сущности
// ceId
} }
void RemoteClient::preparePortalNew(PortalId_t portalId, void* portal) {} void RemoteClient::preparePortalNew(PortalId_t portalId, void* portal) {}
void RemoteClient::preparePortalUpdate(PortalId_t portalId, void* portal) {} void RemoteClient::preparePortalUpdate(PortalId_t portalId, void* portal) {}
void RemoteClient::preparePortalRemove(PortalId_t portalId) {} 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() { ResourceRequest RemoteClient::pushPreparedPackets() {
if(NextPacket.size())
SimplePackets.push_back(std::move(NextPacket));
Socket.pushPackets(&SimplePackets); Socket.pushPackets(&SimplePackets);
SimplePackets.clear(); SimplePackets.clear();
@@ -236,39 +335,258 @@ ResourceRequest RemoteClient::pushPreparedPackets() {
return std::move(NextRequest); return std::move(NextRequest);
} }
void RemoteClient::informateDefTexture(const std::unordered_map<TextureId_t, std::shared_ptr<ResourceFile>> &textures) { void RemoteClient::informateDefTexture(const std::unordered_map<BinTextureId_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)
{ {
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); TextureId_c cId = ResRemap.BinTextures.toClient(sId);
if(cwId == Remap.STC_Worlds.end()) {
// Нужно забронировать идентификатор NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение
wcId = Remap.UsedWorldIdC._Find_first(); << (uint8_t) ToClient::L2Resource::Texture << cId;
if(wcId == WorldId_c(-1)) for(auto part : pair.second->Hash)
// Нет свободных идентификаторов NextPacket << part;
return wcId;
else { NextPacket << (uint8_t) ToClient::L1::Resource // Принудительная отправка
NextRequest.NewWorlds.push_back(wId); << (uint8_t) ToClient::L2Resource::InitResSend
Remap.UsedWorldIdC.reset(wcId); << uint8_t(0) << uint8_t(0) << cId
Remap.STC_Worlds[wId] = wcId; << uint32_t(pair.second->Data.size());
} for(auto part : pair.second->Hash)
} else { NextPacket << part;
wcId = cwId->second;
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));
} }
return wcId; 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;
}
}
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;
}
}
} }

View File

@@ -4,17 +4,18 @@
#include <Common/Lockable.hpp> #include <Common/Lockable.hpp>
#include <Common/Net.hpp> #include <Common/Net.hpp>
#include "Abstract.hpp" #include "Abstract.hpp"
#include "Server/ContentEventController.hpp"
#include <Common/Abstract.hpp> #include <Common/Abstract.hpp>
#include <bitset> #include <bitset>
#include <initializer_list> #include <initializer_list>
#include <set>
#include <type_traits> #include <type_traits>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
namespace LV::Server { namespace LV::Server {
template<typename ServerKey, typename ClientKey, std::enable_if_t<sizeof(ServerKey) >= sizeof(ClientKey), int> = 0>
template<typename ServerKey, typename ClientKey, std::enable_if_t<std::is_integral_v<ServerKey> && std::is_integral_v<ClientKey> && sizeof(ServerKey) >= sizeof(ClientKey), int> = 0>
class CSChunkedMapper { class CSChunkedMapper {
std::unordered_map<uint32_t, std::tuple<std::bitset<64>, std::array<ServerKey, 64>>> Chunks; std::unordered_map<uint32_t, std::tuple<std::bitset<64>, std::array<ServerKey, 64>>> Chunks;
@@ -42,8 +43,8 @@ public:
if(iChunk == Chunks.end()) if(iChunk == Chunks.end())
MAKE_ERROR("Идентификатор не привязан"); MAKE_ERROR("Идентификатор не привязан");
std::bitset<64> &bits = std::get<0>(iChunk.second); std::bitset<64> &bits = std::get<0>(iChunk->second);
std::array<ServerKey, 64> &keys = std::get<1>(iChunk.second); std::array<ServerKey, 64> &keys = std::get<1>(iChunk->second);
assert(bits.test(subIndex) && "Идентификатор уже занят"); assert(bits.test(subIndex) && "Идентификатор уже занят");
@@ -65,7 +66,7 @@ public:
} }
}; };
template<typename ServerKey, typename ClientKey, std::enable_if_t<std::is_integral_v<ServerKey> && std::is_integral_v<ClientKey> && sizeof(ServerKey) >= sizeof(ClientKey), int> = 0> template<typename ServerKey, typename ClientKey, std::enable_if_t<sizeof(ServerKey) >= sizeof(ClientKey), int> = 0>
class SCSKeyRemapper { class SCSKeyRemapper {
std::bitset<sizeof(ClientKey)*8-1> FreeClientKeys; std::bitset<sizeof(ClientKey)*8-1> FreeClientKeys;
std::map<ServerKey, ClientKey> Map; std::map<ServerKey, ClientKey> Map;
@@ -78,9 +79,6 @@ public:
// Соотнести идентификатор на стороне сервера с идентификатором на стороне клиента // Соотнести идентификатор на стороне сервера с идентификатором на стороне клиента
ClientKey toClient(ServerKey skey) { ClientKey toClient(ServerKey skey) {
if(skey == ServerKey(0))
return ClientKey(0);
auto iter = Map.find(skey); auto iter = Map.find(skey);
if(iter == Map.end()) { if(iter == Map.end()) {
// Идентификатор отсутствует, нужно его занять // Идентификатор отсутствует, нужно его занять
@@ -91,11 +89,12 @@ public:
ClientKey ckey = ClientKey(pos+1); ClientKey ckey = ClientKey(pos+1);
Map[skey] = ckey; Map[skey] = ckey;
CSmapper.link(ckey, ckey); CSmapper.link(ckey, skey);
FreeClientKeys.reset(pos);
return ClientKey(pos); return ClientKey(pos);
} }
return iter.second; return iter->second;
} }
// Соотнести идентификатор на стороне клиента с идентификатором на стороне сервера // Соотнести идентификатор на стороне клиента с идентификатором на стороне сервера
@@ -107,14 +106,27 @@ public:
ClientKey erase(ServerKey skey) { ClientKey erase(ServerKey skey) {
auto iter = Map.find(skey); auto iter = Map.find(skey);
assert(iter != Map.end() && "Идентификатор не существует"); if(iter == Map.end())
return 0;
ClientKey ckey = iter.second; ClientKey ckey = iter->second;
CSmapper.erase(ckey); CSmapper.erase(ckey);
Map.erase(iter); Map.erase(iter);
FreeClientKeys.set(ckey-1);
return ckey; 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;
}
}; };
/* /*
@@ -124,27 +136,30 @@ public:
этих ресурсов и переотправлять их клиенту этих ресурсов и переотправлять их клиенту
*/ */
struct ResourceRequest { struct ResourceRequest {
std::vector<BinTextureId_t> NewTextures;
std::vector<BinModelId_t> NewModels;
std::vector<BinSoundId_t> NewSounds;
std::vector<DefWorldId_t> NewWorlds; std::vector<DefWorldId_t> NewWorlds;
std::vector<DefVoxelId_t> NewVoxels; std::vector<DefVoxelId_t> NewVoxels;
std::vector<DefNodeId_t> NewNodes; std::vector<DefNodeId_t> NewNodes;
std::vector<DefPortalId_t> NewPortals;
std::vector<DefEntityId_t> NewEntityes; std::vector<DefEntityId_t> NewEntityes;
std::vector<TextureId_t> NewTextures;
std::vector<ModelId_t> NewModels;
std::vector<SoundId_t> NewSounds;
void insert(const ResourceRequest &obj) { void insert(const ResourceRequest &obj) {
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());
NewEntityes.insert(NewEntityes.end(), obj.NewEntityes.begin(), obj.NewEntityes.end());
NewTextures.insert(NewTextures.end(), obj.NewTextures.begin(), obj.NewTextures.end()); NewTextures.insert(NewTextures.end(), obj.NewTextures.begin(), obj.NewTextures.end());
NewModels.insert(NewModels.end(), obj.NewModels.begin(), obj.NewModels.end()); NewModels.insert(NewModels.end(), obj.NewModels.begin(), obj.NewModels.end());
NewSounds.insert(NewSounds.end(), obj.NewSounds.begin(), obj.NewSounds.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() { void uniq() {
for(std::vector<ResourceId_t> *vec : {&NewWorlds, &NewVoxels, &NewNodes, &NewEntityes, &NewTextures, &NewModels, &NewSounds}) { for(std::vector<ResourceId_t> *vec : {&NewTextures, &NewModels, &NewSounds, &NewWorlds, &NewVoxels, &NewNodes, &NewPortals, &NewEntityes}) {
std::sort(vec->begin(), vec->end()); std::sort(vec->begin(), vec->end());
auto last = std::unique(vec->begin(), vec->end()); auto last = std::unique(vec->begin(), vec->end());
vec->erase(last, vec->end()); vec->erase(last, vec->end());
@@ -154,42 +169,97 @@ struct ResourceRequest {
using EntityKey = std::tuple<WorldId_c, Pos::GlobalRegion>; using EntityKey = std::tuple<WorldId_c, Pos::GlobalRegion>;
/* /*
Обработчик сокета клиента. Обработчик сокета клиента.
Подписывает клиента на отслеживание необходимых ресурсов Подписывает клиента на отслеживание необходимых ресурсов
на основе передаваемых клиенту данных на основе передаваемых клиенту данных
*/ */
class RemoteClient { class RemoteClient {
TOS::Logger LOG;
DestroyLock UseLock; DestroyLock UseLock;
Net::AsyncSocket Socket; Net::AsyncSocket Socket;
bool IsConnected = true, IsGoingShutdown = false; bool IsConnected = true, IsGoingShutdown = false;
struct { struct ResUsesObj {
// Счётчики использования базовых ресурсов высшими объектами // Счётчики использования базовых ресурсов высшими объектами
std::map<TextureId_t, uint32_t> TextureUses; std::map<BinTextureId_t, uint32_t> BinTexture;
std::map<SoundId_t, uint32_t> SoundUses; std::map<BinSoundId_t, uint32_t> BinSound;
// Может использовать текстуры // Может использовать текстуры
std::map<ModelId_t, uint32_t> ModelUses; std::map<BinModelId_t, uint32_t> BinModel;
// Будут использовать в своих определениях текстуры, звуки, модели // Будут использовать в своих определениях текстуры, звуки, модели
std::map<DefVoxelId_t, uint32_t> VoxelDefUses; std::map<DefWorldId_t, uint32_t> DefWorld;
std::map<DefNodeId_t, uint32_t> NodeDefUses; std::map<DefVoxelId_t, uint32_t> DefVoxel;
std::map<DefEntityId_t, uint32_t> EntityDefUses; std::map<DefNodeId_t, uint32_t> DefNode;
std::map<DefWorldId_t, uint32_t> WorldDefUses; std::map<DefPortalId_t, uint32_t> DefPortal;
std::map<DefEntityId_t, uint32_t> DefEntity;
// Чанки используют воксели, ноды, миры
// Сущности используют текстуры, модели, звуки, миры
} Remap; // Переписываемый контент
// Сущности используют текстуры, звуки, модели
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 { struct {
} BinaryResourceUsedIds; SCSKeyRemapper<BinTextureId_t, TextureId_c> BinTextures;
SCSKeyRemapper<BinSoundId_t, SoundId_c> BinSounds;
SCSKeyRemapper<BinModelId_t, ModelId_c> BinModels;
struct { SCSKeyRemapper<DefWorldId_t, DefWorldId_c> DefWorlds;
SCSKeyRemapper<DefVoxelId_t, VoxelId_c> DefVoxels;
SCSKeyRemapper<DefNodeId_t, NodeId_c> DefNodes;
SCSKeyRemapper<DefPortalId_t, DefPortalId_c> DefPortals;
SCSKeyRemapper<DefEntityId_t, DefEntityId_c> DefEntityes;
} WorldUsedIds; SCSKeyRemapper<WorldId_t, WorldId_c> Worlds;
SCSKeyRemapper<PortalId_t, PortalId_c> Portals;
SCSKeyRemapper<GlobalEntityId_t, EntityId_c> Entityes;
} ResRemap;
Net::Packet NextPacket;
ResourceRequest NextRequest; ResourceRequest NextRequest;
std::vector<Net::Packet> SimplePackets; std::vector<Net::Packet> SimplePackets;
@@ -198,7 +268,7 @@ public:
public: public:
RemoteClient(asio::io_context &ioc, tcp::socket socket, const std::string username) RemoteClient(asio::io_context &ioc, tcp::socket socket, const std::string username)
: Socket(ioc, std::move(socket)), Username(username) : LOG("RemoteClient " + username), Socket(ioc, std::move(socket)), Username(username)
{ {
} }
@@ -225,27 +295,20 @@ public:
void prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalChunk chunkPos, const LightPrism *lights); void prepareChunkUpdate_LightPrism(WorldId_t worldId, Pos::GlobalChunk chunkPos, const LightPrism *lights);
void prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkPos); void prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkPos);
void prepareEntitySwap(WorldId_t prevWorldId, Pos::GlobalRegion prevRegionPos, EntityId_t prevEntityId, void prepareEntitySwap(GlobalEntityId_t prevEntityId, GlobalEntityId_t nextEntityId);
WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, EntityId_t newEntityId); void prepareEntityUpdate(GlobalEntityId_t entityId, const Entity *entity);
void prepareEntityUpdate(WorldId_t worldId, Pos::GlobalRegion regionPos, EntityId_t entityId, const Entity *entity); void prepareEntityRemove(GlobalEntityId_t entityId);
void prepareEntityRemove(WorldId_t worldId, Pos::GlobalRegion regionPos, EntityId_t entityId);
void prepareWorldNew(WorldId_t worldId, void* world); void prepareWorldNew(WorldId_t worldId, World* world);
void prepareWorldUpdate(WorldId_t worldId, void* world); void prepareWorldUpdate(WorldId_t worldId, World* world);
void prepareWorldRemove(WorldId_t worldId); void prepareWorldRemove(WorldId_t worldId);
void preparePortalNew(PortalId_t portalId, void* portal); void preparePortalNew(PortalId_t portalId, void* portal);
void preparePortalUpdate(PortalId_t portalId, void* portal); void preparePortalUpdate(PortalId_t portalId, void* portal);
void preparePortalRemove(PortalId_t portalId); void preparePortalRemove(PortalId_t portalId);
// Необходимые определения шаблонов игрового контента
void prepareDefPortal(DefNodeId_t defWorldId, void* node);
void prepareDefMediaStream(MediaStreamId_t modelId, void* mediaStream);
// Прочие моменты // Прочие моменты
void prepareCameraSetEntity(WorldId_t worldId, Pos::GlobalChunk chunkPos, EntityId_t entityId); void prepareCameraSetEntity(GlobalEntityId_t entityId);
// Отправка подготовленных пакетов // Отправка подготовленных пакетов
ResourceRequest pushPreparedPackets(); ResourceRequest pushPreparedPackets();
@@ -255,19 +318,22 @@ public:
// Глобально их можно запросить в выдаче pushPreparedPackets() // Глобально их можно запросить в выдаче pushPreparedPackets()
// Двоичные файлы // Двоичные файлы
void informateDefTexture(const std::unordered_map<TextureId_t, std::shared_ptr<ResourceFile>> &textures); void informateDefTexture(const std::unordered_map<BinTextureId_t, std::shared_ptr<ResourceFile>> &textures);
void informateDefModel(const std::unordered_map<ModelId_t, std::shared_ptr<ResourceFile>> &models); void informateDefSound(const std::unordered_map<BinSoundId_t, std::shared_ptr<ResourceFile>> &sounds);
void informateDefSound(const std::unordered_map<SoundId_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, void*> &worlds); void informateDefWorld(const std::unordered_map<DefWorldId_t, World*> &worlds);
void informateDefVoxel(const std::unordered_map<DefVoxelId_t, void*> &voxels); void informateDefVoxel(const std::unordered_map<DefVoxelId_t, void*> &voxels);
void informateDefNode(const std::unordered_map<DefNodeId_t, void*> &nodes); void informateDefNode(const std::unordered_map<DefNodeId_t, void*> &nodes);
void informateDefEntityes(const std::unordered_map<DefEntityId_t, void*> &entityes); void informateDefEntityes(const std::unordered_map<DefEntityId_t, void*> &entityes);
void informateDefPortals(const std::unordered_map<DefPortalId_t, void*> &portals); void informateDefPortals(const std::unordered_map<DefPortalId_t, void*> &portals);
private: private:
WorldId_c rentWorldRemapId(WorldId_t wId); 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);
}; };

View File

@@ -12,9 +12,9 @@ namespace LV::Server {
struct SB_Region { struct SB_Region {
std::vector<VoxelCube_Region> Voxels; std::vector<VoxelCube_Region> Voxels;
std::unordered_map<VoxelId_t, std::string> VoxelsMap; std::unordered_map<DefVoxelId_t, std::string> VoxelsMap;
std::unordered_map<Pos::Local16_u, Node> Nodes; std::unordered_map<Pos::Local16_u, Node> Nodes;
std::unordered_map<NodeId_t, std::string> NodeMap; std::unordered_map<DefNodeId_t, std::string> NodeMap;
std::vector<Entity> Entityes; std::vector<Entity> Entityes;
}; };

View File

@@ -48,7 +48,7 @@ public:
for(js::value &jvVoxel : jaVoxels) { for(js::value &jvVoxel : jaVoxels) {
js::object &joVoxel = jvVoxel.as_object(); js::object &joVoxel = jvVoxel.as_object();
VoxelCube_Region cube; VoxelCube_Region cube;
cube.Material = joVoxel.at("Material").as_uint64(); cube.VoxelId = joVoxel.at("Material").as_uint64();
cube.Left.X = joVoxel.at("LeftX").as_uint64(); cube.Left.X = joVoxel.at("LeftX").as_uint64();
cube.Left.Y = joVoxel.at("LeftY").as_uint64(); cube.Left.Y = joVoxel.at("LeftY").as_uint64();
cube.Left.Z = joVoxel.at("LeftZ").as_uint64(); cube.Left.Z = joVoxel.at("LeftZ").as_uint64();
@@ -74,7 +74,7 @@ public:
js::array jaVoxels; js::array jaVoxels;
for(const VoxelCube_Region &cube : data->Voxels) { for(const VoxelCube_Region &cube : data->Voxels) {
js::object joVoxel; js::object joVoxel;
joVoxel["Material"] = cube.Material; joVoxel["Material"] = cube.VoxelId;
joVoxel["LeftX"] = cube.Left.X; joVoxel["LeftX"] = cube.Left.X;
joVoxel["LeftY"] = cube.Left.Y; joVoxel["LeftY"] = cube.Left.Y;
joVoxel["LeftZ"] = cube.Left.Z; joVoxel["LeftZ"] = cube.Left.Z;

View File

@@ -4,7 +4,9 @@
namespace LV::Server { namespace LV::Server {
World::World(WorldId_t id) { World::World(DefWorldId_t defId)
: DefId(defId)
{
} }

View File

@@ -99,7 +99,7 @@ public:
.Voxel = { .Voxel = {
.Chunk = Pos::Local16_u(beg.x, beg.y, beg.z), .Chunk = Pos::Local16_u(beg.x, beg.y, beg.z),
.Index = static_cast<uint32_t>(iter), .Index = static_cast<uint32_t>(iter),
.Id = cube.Material .Id = cube.VoxelId
} }
}; };
@@ -113,7 +113,7 @@ public:
} }
EntityId_t pushEntity(Entity &entity) { LocalEntityId_t pushEntity(Entity &entity) {
for(size_t iter = 0; iter < Entityes.size(); iter++) { for(size_t iter = 0; iter < Entityes.size(); iter++) {
Entity &obj = Entityes[iter]; Entity &obj = Entityes[iter];
@@ -130,19 +130,19 @@ public:
return Entityes.size()-1; return Entityes.size()-1;
} }
return EntityId_t(-1); return LocalEntityId_t(-1);
} }
}; };
class World { class World {
WorldId_t Id; DefWorldId_t DefId;
public: public:
std::vector<Pos::GlobalRegion> NeedToLoad; std::vector<Pos::GlobalRegion> NeedToLoad;
std::unordered_map<Pos::GlobalRegion, std::unique_ptr<Region>> Regions; std::unordered_map<Pos::GlobalRegion, std::unique_ptr<Region>> Regions;
public: public:
World(WorldId_t id); World(DefWorldId_t defId);
~World(); ~World();
/* /*
@@ -155,6 +155,8 @@ public:
void onCEC_RegionsLost(ContentEventController *cec, const std::vector<Pos::GlobalRegion> &lost); void onCEC_RegionsLost(ContentEventController *cec, const std::vector<Pos::GlobalRegion> &lost);
Region* forceLoadOrGetRegion(Pos::GlobalRegion pos); Region* forceLoadOrGetRegion(Pos::GlobalRegion pos);
DefWorldId_t getDefId() const { return DefId; }
}; };

View File

@@ -1,7 +1,10 @@
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/detached.hpp> #include <boost/asio/detached.hpp>
#include <boost/asio/detail/chrono.hpp>
#include <boost/asio/io_context.hpp> #include <boost/asio/io_context.hpp>
#include <boost/asio/write.hpp> #include <boost/asio/write.hpp>
#include <boost/chrono/duration.hpp>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include "Client/ServerSession.hpp" #include "Client/ServerSession.hpp"
@@ -18,6 +21,9 @@ coro<> runClient(asio::io_context &ioc, uint16_t port) {
tcp::socket sock = co_await Net::asyncConnectTo("localhost:"+std::to_string(port)); tcp::socket sock = co_await Net::asyncConnectTo("localhost:"+std::to_string(port));
co_await Client::ServerSession::asyncAuthorizeWithServer(sock, "DrSocalkwe3n", "1password2", 1); co_await Client::ServerSession::asyncAuthorizeWithServer(sock, "DrSocalkwe3n", "1password2", 1);
std::unique_ptr<Net::AsyncSocket> asock = co_await Client::ServerSession::asyncInitGameProtocol(ioc, std::move(sock)); std::unique_ptr<Net::AsyncSocket> asock = co_await Client::ServerSession::asyncInitGameProtocol(ioc, std::move(sock));
asio::deadline_timer timer(ioc);
timer.expires_from_now(boost::posix_time::seconds(1));
co_await timer.async_wait();
} catch(const std::exception &exc) { } catch(const std::exception &exc) {
std::cout << exc.what() << std::endl; std::cout << exc.what() << std::endl;
} }