Сеть, со стороны сервера
This commit is contained in:
158
Src/Common/Packets.hpp
Normal file
158
Src/Common/Packets.hpp
Normal 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
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,39 +9,45 @@
|
||||
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 NodeId_c = uint16_t;
|
||||
using WorldId_c = uint8_t;
|
||||
using DefPortalId_c = uint8_t;
|
||||
using PortalId_c = uint8_t;
|
||||
using DefEntityId_c = uint16_t;
|
||||
using EntityId_c = uint16_t;
|
||||
using TextureId_c = uint16_t;
|
||||
using ModelId_c = uint16_t;
|
||||
|
||||
using ResourceId_t = uint32_t;
|
||||
|
||||
// Двоичные данные
|
||||
using BinTextureId_t = ResourceId_t;
|
||||
using BinModelId_t = ResourceId_t;
|
||||
using BinSoundId_t = ResourceId_t;
|
||||
using BinModelId_t = ResourceId_t;
|
||||
|
||||
// Игровые определения
|
||||
using DefWorldId_t = ResourceId_t;
|
||||
using DefVoxelId_t = ResourceId_t;
|
||||
using DefNodeId_t = ResourceId_t;
|
||||
using DefWorldId_t = ResourceId_t;
|
||||
using DefPortalId_t = ResourceId_t;
|
||||
using DefEntityId_t = ResourceId_t;
|
||||
|
||||
// Конент, основанный на игровых определениях
|
||||
|
||||
// Контент, основанный на игровых определениях
|
||||
using WorldId_t = ResourceId_t;
|
||||
|
||||
// В одном регионе может быть максимум 2^16 сущностей. Клиенту адресуются сущности в формате <мир>+<позиция региона>+<uint16_t>
|
||||
// И если сущность перешла из одного региона в другой, идентификатор сущности на стороне клиента сохраняется
|
||||
using LocalEntityId_t = uint16_t;
|
||||
using GlobalEntityId_t = std::tuple<WorldId_t, Pos::GlobalRegion, LocalEntityId_t>;
|
||||
|
||||
|
||||
|
||||
using PortalId_t = uint16_t;
|
||||
|
||||
|
||||
|
||||
using MediaStreamId_t = uint16_t;
|
||||
using ContentBridgeId_t = uint16_t;
|
||||
using PlayerId_t = uint32_t;
|
||||
@@ -130,7 +136,7 @@ struct CollisionAABB : public AABB {
|
||||
|
||||
union {
|
||||
struct {
|
||||
EntityId_t Index;
|
||||
LocalEntityId_t Index;
|
||||
} Entity;
|
||||
|
||||
struct {
|
||||
@@ -161,6 +167,8 @@ struct CollisionAABB : public AABB {
|
||||
|
||||
|
||||
class Entity {
|
||||
DefEntityId_t DefId;
|
||||
|
||||
public:
|
||||
LocalAABB ABBOX;
|
||||
|
||||
@@ -185,11 +193,13 @@ public:
|
||||
IsRemoved = false;
|
||||
|
||||
public:
|
||||
Entity();
|
||||
Entity(DefEntityId_t defId);
|
||||
|
||||
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)};
|
||||
}
|
||||
|
||||
DefEntityId_t getDefId() const { return DefId; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "Common/Abstract.hpp"
|
||||
#include "RemoteClient.hpp"
|
||||
#include "Server/Abstract.hpp"
|
||||
#include "World.hpp"
|
||||
|
||||
|
||||
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];
|
||||
|
||||
chunks.insert(enter.begin(), enter.end());
|
||||
@@ -67,6 +72,11 @@ void ContentEventController::onChunksEnterLost(WorldId_t worldId, Pos::GlobalReg
|
||||
|
||||
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,
|
||||
@@ -145,7 +155,7 @@ void ContentEventController::onChunksUpdate_LightPrism(WorldId_t worldId, Pos::G
|
||||
}
|
||||
|
||||
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);
|
||||
if(pWorld == Subscribed.Entities.end()) {
|
||||
@@ -161,9 +171,9 @@ void ContentEventController::onEntityEnterLost(WorldId_t worldId, Pos::GlobalReg
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -177,13 +187,13 @@ void ContentEventController::onEntityEnterLost(WorldId_t worldId, Pos::GlobalReg
|
||||
}
|
||||
|
||||
// Сообщить Remote
|
||||
for(EntityId_t eId : lost) {
|
||||
Remote->prepareEntityRemove(worldId, regionPos, eId);
|
||||
for(LocalEntityId_t eId : lost) {
|
||||
Remote->prepareEntityRemove({worldId, regionPos, eId});
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -210,13 +220,13 @@ void ContentEventController::onEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegi
|
||||
lpRegion->second.erase(lpceId);
|
||||
npRegion->second.insert(newId);
|
||||
|
||||
Remote->prepareEntitySwap(lastWorldId, lastRegionPos, lastId, newWorldId, newRegionPos, newId);
|
||||
Remote->prepareEntitySwap({lastWorldId, lastRegionPos, lastId}, {newWorldId, newRegionPos, newId});
|
||||
|
||||
goto entitySwaped;
|
||||
}
|
||||
}
|
||||
|
||||
Remote->prepareEntityRemove(lastWorldId, lastRegionPos, lastId);
|
||||
Remote->prepareEntityRemove({lastWorldId, lastRegionPos, lastId});
|
||||
|
||||
entitySwaped:
|
||||
return;
|
||||
@@ -239,7 +249,7 @@ void ContentEventController::onEntityUpdates(WorldId_t worldId, Pos::GlobalRegio
|
||||
if(!lpRegion->second.contains(eId))
|
||||
continue;
|
||||
|
||||
Remote->prepareEntityUpdate(worldId, regionPos, eId, &entities[eId]);
|
||||
Remote->prepareEntityUpdate({worldId, regionPos, eId}, &entities[eId]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,7 @@ namespace LV::Server {
|
||||
|
||||
class RemoteClient;
|
||||
class GameServer;
|
||||
|
||||
struct GlobalEntityId {
|
||||
WorldId_t WorldId;
|
||||
Pos::GlobalChunk ChunkPos;
|
||||
EntityId_t EntityId;
|
||||
};
|
||||
class World;
|
||||
|
||||
|
||||
struct ServerObjectPos {
|
||||
@@ -92,7 +87,7 @@ private:
|
||||
// Используется регионами
|
||||
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<EntityId_t>>> Entities;
|
||||
std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalRegion, std::unordered_set<LocalEntityId_t>>> Entities;
|
||||
} Subscribed;
|
||||
|
||||
public:
|
||||
@@ -115,14 +110,14 @@ public:
|
||||
|
||||
// Регионы следят за чанками, которые видят игроки
|
||||
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_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 onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_set<EntityId_t> &enter, const std::unordered_set<EntityId_t> &lost);
|
||||
void onEntitySwap(WorldId_t lastWorldId, Pos::GlobalRegion lastRegionPos, EntityId_t lastId, WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, EntityId_t newId);
|
||||
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, 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 onPortalEnterLost(const std::vector<void*> &enter, const std::vector<void*> &lost);
|
||||
|
||||
@@ -19,6 +19,7 @@ GameServer::~GameServer() {
|
||||
RunThread.join();
|
||||
WorkDeadline.cancel();
|
||||
UseLock.wait_no_use();
|
||||
LOG.info() << "Сервер уничтожен";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
LOG.info() << "Сервер завершил работу";
|
||||
}
|
||||
|
||||
void GameServer::stepContent() {
|
||||
@@ -547,10 +550,10 @@ void GameServer::stepWorlds() {
|
||||
if(rPos != pRegion.first || pWorld.first != entity.WorldId) {
|
||||
|
||||
Region *toRegion = Expanse.Worlds[entity.WorldId]->forceLoadOrGetRegion(rPos);
|
||||
EntityId_t newId = toRegion->pushEntity(entity);
|
||||
LocalEntityId_t newId = toRegion->pushEntity(entity);
|
||||
// toRegion->Entityes[newId].WorldId = Если мир изменился
|
||||
|
||||
if(newId == EntityId_t(-1)) {
|
||||
if(newId == LocalEntityId_t(-1)) {
|
||||
// В другом регионе нет места
|
||||
} else {
|
||||
entity.IsRemoved = true;
|
||||
@@ -675,7 +678,7 @@ void GameServer::stepWorlds() {
|
||||
doesNotObserve:
|
||||
|
||||
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()) {
|
||||
@@ -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++) {
|
||||
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);
|
||||
@@ -738,7 +741,7 @@ void GameServer::stepWorlds() {
|
||||
goto doesNotObserveEntityes;
|
||||
|
||||
// Подходят ли уже наблюдаемые сущности под наблюдательные области
|
||||
for(EntityId_t eId : iterR_W_R->second) {
|
||||
for(LocalEntityId_t eId : iterR_W_R->second) {
|
||||
if(eId >= region.Entityes.size()) {
|
||||
lostEntityes.push_back(eId);
|
||||
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);
|
||||
}
|
||||
|
||||
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()));
|
||||
// Отправить полную информацию о новых наблюдаемых сущностях наблюдателю
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
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 << 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,103 +137,196 @@ 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()) {
|
||||
// Новая сущность
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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;
|
||||
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();
|
||||
|
||||
@@ -236,39 +335,258 @@ ResourceRequest RemoteClient::pushPreparedPackets() {
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
wcId = cwId->second;
|
||||
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));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,17 +4,18 @@
|
||||
#include <Common/Lockable.hpp>
|
||||
#include <Common/Net.hpp>
|
||||
#include "Abstract.hpp"
|
||||
#include "Server/ContentEventController.hpp"
|
||||
#include <Common/Abstract.hpp>
|
||||
#include <bitset>
|
||||
#include <initializer_list>
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace LV::Server {
|
||||
|
||||
|
||||
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 CSChunkedMapper {
|
||||
std::unordered_map<uint32_t, std::tuple<std::bitset<64>, std::array<ServerKey, 64>>> Chunks;
|
||||
|
||||
@@ -42,8 +43,8 @@ public:
|
||||
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);
|
||||
std::bitset<64> &bits = std::get<0>(iChunk->second);
|
||||
std::array<ServerKey, 64> &keys = std::get<1>(iChunk->second);
|
||||
|
||||
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 {
|
||||
std::bitset<sizeof(ClientKey)*8-1> FreeClientKeys;
|
||||
std::map<ServerKey, ClientKey> Map;
|
||||
@@ -78,9 +79,6 @@ public:
|
||||
|
||||
// Соотнести идентификатор на стороне сервера с идентификатором на стороне клиента
|
||||
ClientKey toClient(ServerKey skey) {
|
||||
if(skey == ServerKey(0))
|
||||
return ClientKey(0);
|
||||
|
||||
auto iter = Map.find(skey);
|
||||
if(iter == Map.end()) {
|
||||
// Идентификатор отсутствует, нужно его занять
|
||||
@@ -91,11 +89,12 @@ public:
|
||||
|
||||
ClientKey ckey = ClientKey(pos+1);
|
||||
Map[skey] = ckey;
|
||||
CSmapper.link(ckey, ckey);
|
||||
CSmapper.link(ckey, skey);
|
||||
FreeClientKeys.reset(pos);
|
||||
return ClientKey(pos);
|
||||
}
|
||||
|
||||
return iter.second;
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
// Соотнести идентификатор на стороне клиента с идентификатором на стороне сервера
|
||||
@@ -107,14 +106,27 @@ public:
|
||||
ClientKey erase(ServerKey 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);
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -124,27 +136,30 @@ public:
|
||||
этих ресурсов и переотправлять их клиенту
|
||||
*/
|
||||
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;
|
||||
|
||||
std::vector<TextureId_t> NewTextures;
|
||||
std::vector<ModelId_t> NewModels;
|
||||
std::vector<SoundId_t> NewSounds;
|
||||
|
||||
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());
|
||||
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 : {&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());
|
||||
auto last = std::unique(vec->begin(), vec->end());
|
||||
vec->erase(last, vec->end());
|
||||
@@ -154,42 +169,97 @@ struct ResourceRequest {
|
||||
|
||||
using EntityKey = std::tuple<WorldId_c, Pos::GlobalRegion>;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Обработчик сокета клиента.
|
||||
Подписывает клиента на отслеживание необходимых ресурсов
|
||||
на основе передаваемых клиенту данных
|
||||
*/
|
||||
class RemoteClient {
|
||||
TOS::Logger LOG;
|
||||
DestroyLock UseLock;
|
||||
Net::AsyncSocket Socket;
|
||||
bool IsConnected = true, IsGoingShutdown = false;
|
||||
|
||||
struct {
|
||||
struct ResUsesObj {
|
||||
// Счётчики использования базовых ресурсов высшими объектами
|
||||
std::map<TextureId_t, uint32_t> TextureUses;
|
||||
std::map<SoundId_t, uint32_t> SoundUses;
|
||||
std::map<BinTextureId_t, uint32_t> BinTexture;
|
||||
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<DefNodeId_t, uint32_t> NodeDefUses;
|
||||
std::map<DefEntityId_t, uint32_t> EntityDefUses;
|
||||
std::map<DefWorldId_t, uint32_t> WorldDefUses;
|
||||
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;
|
||||
|
||||
// Чанки используют воксели, ноды, миры
|
||||
// Сущности используют текстуры, модели, звуки, миры
|
||||
|
||||
} 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 {
|
||||
} 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;
|
||||
std::vector<Net::Packet> SimplePackets;
|
||||
|
||||
@@ -198,7 +268,7 @@ public:
|
||||
|
||||
public:
|
||||
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 prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkPos);
|
||||
|
||||
void prepareEntitySwap(WorldId_t prevWorldId, Pos::GlobalRegion prevRegionPos, EntityId_t prevEntityId,
|
||||
WorldId_t newWorldId, Pos::GlobalRegion newRegionPos, EntityId_t newEntityId);
|
||||
void prepareEntityUpdate(WorldId_t worldId, Pos::GlobalRegion regionPos, EntityId_t entityId, const Entity *entity);
|
||||
void prepareEntityRemove(WorldId_t worldId, Pos::GlobalRegion regionPos, EntityId_t entityId);
|
||||
void prepareEntitySwap(GlobalEntityId_t prevEntityId, GlobalEntityId_t nextEntityId);
|
||||
void prepareEntityUpdate(GlobalEntityId_t entityId, const Entity *entity);
|
||||
void prepareEntityRemove(GlobalEntityId_t entityId);
|
||||
|
||||
void prepareWorldNew(WorldId_t worldId, void* world);
|
||||
void prepareWorldUpdate(WorldId_t worldId, void* world);
|
||||
void prepareWorldNew(WorldId_t worldId, World* world);
|
||||
void prepareWorldUpdate(WorldId_t worldId, World* world);
|
||||
void prepareWorldRemove(WorldId_t worldId);
|
||||
|
||||
void preparePortalNew(PortalId_t portalId, void* portal);
|
||||
void preparePortalUpdate(PortalId_t portalId, void* portal);
|
||||
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();
|
||||
@@ -255,19 +318,22 @@ public:
|
||||
// Глобально их можно запросить в выдаче pushPreparedPackets()
|
||||
|
||||
// Двоичные файлы
|
||||
void informateDefTexture(const std::unordered_map<TextureId_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<SoundId_t, std::shared_ptr<ResourceFile>> &sounds);
|
||||
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, void*> &worlds);
|
||||
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:
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ namespace LV::Server {
|
||||
|
||||
struct SB_Region {
|
||||
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<NodeId_t, std::string> NodeMap;
|
||||
std::unordered_map<DefNodeId_t, std::string> NodeMap;
|
||||
std::vector<Entity> Entityes;
|
||||
};
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
for(js::value &jvVoxel : jaVoxels) {
|
||||
js::object &joVoxel = jvVoxel.as_object();
|
||||
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.Y = joVoxel.at("LeftY").as_uint64();
|
||||
cube.Left.Z = joVoxel.at("LeftZ").as_uint64();
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
js::array jaVoxels;
|
||||
for(const VoxelCube_Region &cube : data->Voxels) {
|
||||
js::object joVoxel;
|
||||
joVoxel["Material"] = cube.Material;
|
||||
joVoxel["Material"] = cube.VoxelId;
|
||||
joVoxel["LeftX"] = cube.Left.X;
|
||||
joVoxel["LeftY"] = cube.Left.Y;
|
||||
joVoxel["LeftZ"] = cube.Left.Z;
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
namespace LV::Server {
|
||||
|
||||
|
||||
World::World(WorldId_t id) {
|
||||
World::World(DefWorldId_t defId)
|
||||
: DefId(defId)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ public:
|
||||
.Voxel = {
|
||||
.Chunk = Pos::Local16_u(beg.x, beg.y, beg.z),
|
||||
.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++) {
|
||||
Entity &obj = Entityes[iter];
|
||||
|
||||
@@ -130,19 +130,19 @@ public:
|
||||
return Entityes.size()-1;
|
||||
}
|
||||
|
||||
return EntityId_t(-1);
|
||||
return LocalEntityId_t(-1);
|
||||
}
|
||||
};
|
||||
|
||||
class World {
|
||||
WorldId_t Id;
|
||||
DefWorldId_t DefId;
|
||||
|
||||
public:
|
||||
std::vector<Pos::GlobalRegion> NeedToLoad;
|
||||
std::unordered_map<Pos::GlobalRegion, std::unique_ptr<Region>> Regions;
|
||||
|
||||
public:
|
||||
World(WorldId_t id);
|
||||
World(DefWorldId_t defId);
|
||||
~World();
|
||||
|
||||
/*
|
||||
@@ -155,6 +155,8 @@ public:
|
||||
void onCEC_RegionsLost(ContentEventController *cec, const std::vector<Pos::GlobalRegion> &lost);
|
||||
|
||||
Region* forceLoadOrGetRegion(Pos::GlobalRegion pos);
|
||||
|
||||
DefWorldId_t getDefId() const { return DefId; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/deadline_timer.hpp>
|
||||
#include <boost/asio/detached.hpp>
|
||||
#include <boost/asio/detail/chrono.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <boost/chrono/duration.hpp>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#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));
|
||||
co_await Client::ServerSession::asyncAuthorizeWithServer(sock, "DrSocalkwe3n", "1password2", 1);
|
||||
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) {
|
||||
std::cout << exc.what() << std::endl;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user