Переработка доставки вокселей и нод в чанках
This commit is contained in:
@@ -43,9 +43,12 @@ struct VoxelCube {
|
|||||||
Pos::bvec256u Left, Size;
|
Pos::bvec256u Left, Size;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Node {
|
union Node {
|
||||||
DefNodeId_t NodeId;
|
struct {
|
||||||
uint8_t Rotate : 6;
|
DefNodeId_t NodeId : 24, Meta : 8;
|
||||||
|
};
|
||||||
|
|
||||||
|
DefNodeId_t Data;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 16 метров ребро
|
// 16 метров ребро
|
||||||
@@ -54,7 +57,7 @@ struct Chunk {
|
|||||||
// Кубы вокселей в чанке
|
// Кубы вокселей в чанке
|
||||||
std::vector<VoxelCube> Voxels;
|
std::vector<VoxelCube> Voxels;
|
||||||
// Ноды
|
// Ноды
|
||||||
std::unordered_map<Pos::bvec16u, Node> Nodes;
|
Node Nodes[16][16][16];
|
||||||
// Ограничения прохождения света, идущего от солнца (от верха карты до верхней плоскости чанка)
|
// Ограничения прохождения света, идущего от солнца (от верха карты до верхней плоскости чанка)
|
||||||
// LightPrism Lights[16][16];
|
// LightPrism Lights[16][16];
|
||||||
};
|
};
|
||||||
@@ -86,7 +89,7 @@ public:
|
|||||||
virtual void onContentDefinesLost(std::unordered_map<EnumDefContent, std::vector<ResourceId_t>>) = 0;
|
virtual void onContentDefinesLost(std::unordered_map<EnumDefContent, std::vector<ResourceId_t>>) = 0;
|
||||||
|
|
||||||
// Сообщаем об изменившихся чанках
|
// Сообщаем об изменившихся чанках
|
||||||
virtual void onChunksChange(WorldId_t worldId, const std::unordered_set<Pos::GlobalChunk> &changeOrAddList, const std::unordered_set<Pos::GlobalChunk> &remove) = 0;
|
virtual void onChunksChange(WorldId_t worldId, const std::unordered_set<Pos::GlobalChunk> &changeOrAddList, const std::unordered_set<Pos::GlobalRegion> &remove) = 0;
|
||||||
// Установить позицию для камеры
|
// Установить позицию для камеры
|
||||||
virtual void setCameraPos(WorldId_t worldId, Pos::Object pos, glm::quat quat) = 0;
|
virtual void setCameraPos(WorldId_t worldId, Pos::Object pos, glm::quat quat) = 0;
|
||||||
|
|
||||||
@@ -94,7 +97,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Region {
|
struct Region {
|
||||||
std::unordered_map<Pos::bvec16u, Chunk> Chunks;
|
Chunk Chunks[4][4][4];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct World {
|
struct World {
|
||||||
|
|||||||
@@ -29,11 +29,23 @@ struct PP_Content_ChunkVoxels : public ParsedPacket {
|
|||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PP_Content_ChunkRemove : public ParsedPacket {
|
struct PP_Content_ChunkNodes : public ParsedPacket {
|
||||||
WorldId_t Id;
|
WorldId_t Id;
|
||||||
Pos::GlobalChunk Pos;
|
Pos::GlobalChunk Pos;
|
||||||
|
Node Nodes[16][16][16];
|
||||||
|
|
||||||
PP_Content_ChunkRemove(ToClient::L1 l1, uint8_t l2, WorldId_t id, Pos::GlobalChunk pos)
|
PP_Content_ChunkNodes(ToClient::L1 l1, uint8_t l2, WorldId_t id, Pos::GlobalChunk pos, Node* nodes)
|
||||||
|
: ParsedPacket(l1, l2), Id(id), Pos(pos)
|
||||||
|
{
|
||||||
|
std::copy(nodes, nodes+16*16*16, (Node*) Nodes);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PP_Content_RegionRemove : public ParsedPacket {
|
||||||
|
WorldId_t Id;
|
||||||
|
Pos::GlobalRegion Pos;
|
||||||
|
|
||||||
|
PP_Content_RegionRemove(ToClient::L1 l1, uint8_t l2, WorldId_t id, Pos::GlobalRegion pos)
|
||||||
: ParsedPacket(l1, l2), Id(id), Pos(pos)
|
: ParsedPacket(l1, l2), Id(id), Pos(pos)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
@@ -252,7 +264,7 @@ void ServerSession::atFreeDrawTime(GlobalTime gTime, float dTime) {
|
|||||||
Speed += glm::vec3(0, 1, 0)*float(Keys.SPACE)*mltpl;
|
Speed += glm::vec3(0, 1, 0)*float(Keys.SPACE)*mltpl;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unordered_map<WorldId_t, std::tuple<std::unordered_set<Pos::GlobalChunk>, std::unordered_set<Pos::GlobalChunk>>> changeOrAddList_removeList;
|
std::unordered_map<WorldId_t, std::tuple<std::unordered_set<Pos::GlobalChunk>, std::unordered_set<Pos::GlobalRegion>>> changeOrAddList_removeList;
|
||||||
|
|
||||||
// Пакеты
|
// Пакеты
|
||||||
ParsedPacket *pack;
|
ParsedPacket *pack;
|
||||||
@@ -264,19 +276,26 @@ void ServerSession::atFreeDrawTime(GlobalTime gTime, float dTime) {
|
|||||||
Pos::GlobalRegion rPos = p.Pos >> 2;
|
Pos::GlobalRegion rPos = p.Pos >> 2;
|
||||||
Pos::bvec4u cPos = p.Pos & 0x3;
|
Pos::bvec4u cPos = p.Pos & 0x3;
|
||||||
|
|
||||||
Data.Worlds[p.Id].Regions[rPos].Chunks[cPos].Voxels = std::move(p.Cubes);
|
Data.Worlds[p.Id].Regions[rPos].Chunks[cPos.x][cPos.y][cPos.z].Voxels = std::move(p.Cubes);
|
||||||
|
|
||||||
auto &pair = changeOrAddList_removeList[p.Id];
|
auto &pair = changeOrAddList_removeList[p.Id];
|
||||||
std::get<0>(pair).insert(p.Pos);
|
std::get<0>(pair).insert(p.Pos);
|
||||||
} else if(l2 == ToClient::L2Content::RemoveChunk) {
|
} else if(l2 == ToClient::L2Content::ChunkNodes) {
|
||||||
PP_Content_ChunkRemove &p = *dynamic_cast<PP_Content_ChunkRemove*>(pack);
|
PP_Content_ChunkNodes &p = *dynamic_cast<PP_Content_ChunkNodes*>(pack);
|
||||||
|
|
||||||
Pos::GlobalRegion rPos = p.Pos >> 2;
|
Pos::GlobalRegion rPos = p.Pos >> 2;
|
||||||
Pos::bvec4u cPos = p.Pos & 0x3;
|
Pos::bvec4u cPos = p.Pos & 0x3;
|
||||||
auto &obj = Data.Worlds[p.Id].Regions[rPos].Chunks;
|
|
||||||
auto iter = obj.find(cPos);
|
Node *nodes = (Node*) Data.Worlds[p.Id].Regions[rPos].Chunks[cPos.x][cPos.y][cPos.z].Nodes;
|
||||||
if(iter != obj.end())
|
std::copy((const Node*)p.Nodes, ((const Node*) p.Nodes)+16*16*16, nodes);
|
||||||
obj.erase(iter);
|
auto &pair = changeOrAddList_removeList[p.Id];
|
||||||
|
std::get<0>(pair).insert(p.Pos);
|
||||||
|
} else if(l2 == ToClient::L2Content::RemoveRegion) {
|
||||||
|
PP_Content_RegionRemove &p = *dynamic_cast<PP_Content_RegionRemove*>(pack);
|
||||||
|
|
||||||
|
auto ®ions = Data.Worlds[p.Id].Regions;
|
||||||
|
auto obj = regions.find(p.Pos);
|
||||||
|
assert(obj != regions.end());
|
||||||
|
regions.erase(obj);
|
||||||
|
|
||||||
auto &pair = changeOrAddList_removeList[p.Id];
|
auto &pair = changeOrAddList_removeList[p.Id];
|
||||||
std::get<1>(pair).insert(p.Pos);
|
std::get<1>(pair).insert(p.Pos);
|
||||||
@@ -514,11 +533,6 @@ coro<> ServerSession::rP_Content(Net::AsyncSocket &sock) {
|
|||||||
pos.unpack(co_await sock.read<Pos::GlobalChunk::Pack>());
|
pos.unpack(co_await sock.read<Pos::GlobalChunk::Pack>());
|
||||||
|
|
||||||
std::vector<VoxelCube> cubes(co_await sock.read<uint16_t>());
|
std::vector<VoxelCube> cubes(co_await sock.read<uint16_t>());
|
||||||
uint16_t debugCubesCount = cubes.size();
|
|
||||||
if(debugCubesCount > 1) {
|
|
||||||
int g = 0;
|
|
||||||
g++;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(size_t iter = 0; iter < cubes.size(); iter++) {
|
for(size_t iter = 0; iter < cubes.size(); iter++) {
|
||||||
VoxelCube &cube = cubes[iter];
|
VoxelCube &cube = cubes[iter];
|
||||||
@@ -545,19 +559,39 @@ coro<> ServerSession::rP_Content(Net::AsyncSocket &sock) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case ToClient::L2Content::ChunkNodes:
|
case ToClient::L2Content::ChunkNodes:
|
||||||
|
{
|
||||||
|
WorldId_t wcId = co_await sock.read<WorldId_t>();
|
||||||
|
Pos::GlobalChunk pos;
|
||||||
|
pos.unpack(co_await sock.read<Pos::GlobalChunk::Pack>());
|
||||||
|
std::array<Node, 16*16*16> nodes;
|
||||||
|
|
||||||
|
for(Node& node : nodes) {
|
||||||
|
node.Data = co_await sock.read<DefNodeId_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
PP_Content_ChunkNodes *packet = new PP_Content_ChunkNodes(
|
||||||
|
ToClient::L1::Content,
|
||||||
|
(uint8_t) ToClient::L2Content::ChunkVoxels,
|
||||||
|
wcId,
|
||||||
|
pos,
|
||||||
|
nodes.data()
|
||||||
|
);
|
||||||
|
|
||||||
|
while(!NetInputPackets.push(packet));
|
||||||
|
|
||||||
co_return;
|
co_return;
|
||||||
|
}
|
||||||
case ToClient::L2Content::ChunkLightPrism:
|
case ToClient::L2Content::ChunkLightPrism:
|
||||||
|
|
||||||
co_return;
|
co_return;
|
||||||
case ToClient::L2Content::RemoveChunk: {
|
case ToClient::L2Content::RemoveRegion: {
|
||||||
WorldId_t wcId = co_await sock.read<uint8_t>();
|
WorldId_t wcId = co_await sock.read<uint8_t>();
|
||||||
Pos::GlobalChunk pos;
|
Pos::GlobalChunk pos;
|
||||||
pos.unpack(co_await sock.read<Pos::GlobalChunk::Pack>());
|
pos.unpack(co_await sock.read<Pos::GlobalRegion::Pack>());
|
||||||
|
|
||||||
PP_Content_ChunkRemove *packet = new PP_Content_ChunkRemove(
|
PP_Content_RegionRemove *packet = new PP_Content_RegionRemove(
|
||||||
ToClient::L1::Content,
|
ToClient::L1::Content,
|
||||||
(uint8_t) ToClient::L2Content::RemoveChunk,
|
(uint8_t) ToClient::L2Content::RemoveRegion,
|
||||||
wcId,
|
wcId,
|
||||||
pos
|
pos
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -611,14 +611,14 @@ void VulkanRenderSession::onContentDefinesLost(std::unordered_map<EnumDefContent
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderSession::onChunksChange(WorldId_t worldId, const std::unordered_set<Pos::GlobalChunk> &changeOrAddList, const std::unordered_set<Pos::GlobalChunk> &remove) {
|
void VulkanRenderSession::onChunksChange(WorldId_t worldId, const std::unordered_set<Pos::GlobalChunk>& changeOrAddList, const std::unordered_set<Pos::GlobalRegion>& remove) {
|
||||||
auto &table = External.ChunkVoxelMesh[worldId];
|
auto &table = External.ChunkVoxelMesh[worldId];
|
||||||
|
|
||||||
for(Pos::GlobalChunk pos : changeOrAddList) {
|
for(Pos::GlobalChunk pos : changeOrAddList) {
|
||||||
Pos::GlobalRegion rPos = pos >> 4;
|
Pos::GlobalRegion rPos = pos >> 4;
|
||||||
Pos::bvec16u cPos = pos & 0xf;
|
Pos::bvec16u cPos = pos & 0xf;
|
||||||
|
|
||||||
const auto &voxels = ServerSession->Data.Worlds[worldId].Regions[rPos].Chunks[cPos].Voxels;
|
const auto &voxels = ServerSession->Data.Worlds[worldId].Regions[rPos].Chunks[cPos.x][cPos.y][cPos.z].Voxels;
|
||||||
|
|
||||||
if(voxels.empty()) {
|
if(voxels.empty()) {
|
||||||
auto iter = table.find(pos);
|
auto iter = table.find(pos);
|
||||||
@@ -639,10 +639,14 @@ auto &table = External.ChunkVoxelMesh[worldId];
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(Pos::GlobalChunk pos : remove) {
|
for(Pos::GlobalRegion pos : remove) {
|
||||||
auto iter = table.find(pos);
|
for(int z = 0; z < 4; z++)
|
||||||
if(iter != table.end())
|
for(int y = 0; y < 4; y++)
|
||||||
table.erase(iter);
|
for(int x = 0; x < 4; x++) {
|
||||||
|
auto iter = table.find((Pos::GlobalChunk(pos) << 2) + Pos::GlobalChunk(x, y, z));
|
||||||
|
if(iter != table.end())
|
||||||
|
table.erase(iter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(table.empty())
|
if(table.empty())
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ public:
|
|||||||
virtual void onBinaryResourceLost(std::unordered_map<EnumBinResource, std::vector<ResourceId_t>>) override;
|
virtual void onBinaryResourceLost(std::unordered_map<EnumBinResource, std::vector<ResourceId_t>>) override;
|
||||||
virtual void onContentDefinesAdd(std::unordered_map<EnumDefContent, std::unordered_map<ResourceId_t, std::u8string>>) override;
|
virtual void onContentDefinesAdd(std::unordered_map<EnumDefContent, std::unordered_map<ResourceId_t, std::u8string>>) override;
|
||||||
virtual void onContentDefinesLost(std::unordered_map<EnumDefContent, std::vector<ResourceId_t>>) override;
|
virtual void onContentDefinesLost(std::unordered_map<EnumDefContent, std::vector<ResourceId_t>>) override;
|
||||||
virtual void onChunksChange(WorldId_t worldId, const std::unordered_set<Pos::GlobalChunk> &changeOrAddList, const std::unordered_set<Pos::GlobalChunk> &remove) override;
|
virtual void onChunksChange(WorldId_t worldId, const std::unordered_set<Pos::GlobalChunk>& changeOrAddList, const std::unordered_set<Pos::GlobalRegion>& remove) override;
|
||||||
virtual void setCameraPos(WorldId_t worldId, Pos::Object pos, glm::quat quat) override;
|
virtual void setCameraPos(WorldId_t worldId, Pos::Object pos, glm::quat quat) override;
|
||||||
|
|
||||||
glm::mat4 calcViewMatrix(glm::quat quat, glm::vec3 camOffset = glm::vec3(0)) {
|
glm::mat4 calcViewMatrix(glm::quat quat, glm::vec3 camOffset = glm::vec3(0)) {
|
||||||
|
|||||||
763
Src/Common/Abstract.cpp
Normal file
763
Src/Common/Abstract.cpp
Normal file
@@ -0,0 +1,763 @@
|
|||||||
|
#include "Abstract.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace LV {
|
||||||
|
|
||||||
|
|
||||||
|
CompressedVoxels compressVoxels_byte(const std::vector<VoxelCube>& voxels) {
|
||||||
|
std::u8string compressed;
|
||||||
|
std::vector<DefVoxelId_t> defines;
|
||||||
|
DefVoxelId_t maxValue = 0;
|
||||||
|
defines.reserve(voxels.size());
|
||||||
|
|
||||||
|
compressed.push_back(1);
|
||||||
|
|
||||||
|
assert(voxels.size() <= 65535);
|
||||||
|
|
||||||
|
for(const VoxelCube& cube : voxels) {
|
||||||
|
defines.push_back(cube.VoxelId);
|
||||||
|
if(cube.VoxelId > maxValue)
|
||||||
|
maxValue = cube.VoxelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::sort(defines.begin(), defines.end());
|
||||||
|
auto last = std::unique(defines.begin(), defines.end());
|
||||||
|
defines.erase(last, defines.end());
|
||||||
|
defines.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Количество байт на идентификатор в сыром виде
|
||||||
|
uint8_t bytes_raw = std::ceil(std::log2(maxValue)/8);
|
||||||
|
assert(bytes_raw >= 1 && bytes_raw <= 3);
|
||||||
|
// Количество байт без таблицы индексов
|
||||||
|
size_t size_in_raw = (bytes_raw+6)*voxels.size();
|
||||||
|
// Количество байт на индекс
|
||||||
|
uint8_t bytes_per_define = std::ceil(std::log2(defines.size())/8);
|
||||||
|
assert(bytes_per_define == 1 || bytes_per_define == 2);
|
||||||
|
// Количество байт после таблицы индексов
|
||||||
|
size_t size_after_indices = (bytes_per_define+6)*voxels.size();
|
||||||
|
// Размер таблицы индексов
|
||||||
|
size_t indeces_size = 2+bytes_raw*defines.size();
|
||||||
|
|
||||||
|
if(indeces_size+size_after_indices < size_in_raw) {
|
||||||
|
// Выгодней писать с таблицей индексов
|
||||||
|
|
||||||
|
// Индексы, размер идентификатора ключа к таблице, размер значения таблицы
|
||||||
|
compressed.push_back(1 | (bytes_per_define << 1) | (bytes_raw << 2));
|
||||||
|
compressed.push_back(defines.size() & 0xff);
|
||||||
|
compressed.push_back((defines.size() >> 8) & 0xff);
|
||||||
|
|
||||||
|
// Таблица
|
||||||
|
for(DefVoxelId_t id : defines) {
|
||||||
|
compressed.push_back(id & 0xff);
|
||||||
|
if(bytes_raw > 1)
|
||||||
|
compressed.push_back((id >> 8) & 0xff);
|
||||||
|
if(bytes_raw > 2)
|
||||||
|
compressed.push_back((id >> 16) & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
compressed.push_back(voxels.size() & 0xff);
|
||||||
|
compressed.push_back((voxels.size() >> 8) & 0xff);
|
||||||
|
|
||||||
|
for(const VoxelCube& cube : voxels) {
|
||||||
|
size_t index = std::binary_search(defines.begin(), defines.end(), cube.VoxelId);
|
||||||
|
compressed.push_back(index & 0xff);
|
||||||
|
if(bytes_per_define > 1)
|
||||||
|
compressed.push_back((index >> 8) & 0xff);
|
||||||
|
|
||||||
|
compressed.push_back(cube.Meta);
|
||||||
|
compressed.push_back(cube.Pos.x);
|
||||||
|
compressed.push_back(cube.Pos.y);
|
||||||
|
compressed.push_back(cube.Pos.z);
|
||||||
|
compressed.push_back(cube.Size.x);
|
||||||
|
compressed.push_back(cube.Size.y);
|
||||||
|
compressed.push_back(cube.Size.z);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
compressed.push_back(0 | (0 << 1) | (bytes_raw << 2));
|
||||||
|
|
||||||
|
compressed.push_back(voxels.size() & 0xff);
|
||||||
|
compressed.push_back((voxels.size() >> 8) & 0xff);
|
||||||
|
|
||||||
|
for(const VoxelCube& cube : voxels) {
|
||||||
|
compressed.push_back(cube.VoxelId & 0xff);
|
||||||
|
if(bytes_raw > 1)
|
||||||
|
compressed.push_back((cube.VoxelId >> 8) & 0xff);
|
||||||
|
if(bytes_raw > 2)
|
||||||
|
compressed.push_back((cube.VoxelId >> 16) & 0xff);
|
||||||
|
|
||||||
|
compressed.push_back(cube.Meta);
|
||||||
|
compressed.push_back(cube.Pos.x);
|
||||||
|
compressed.push_back(cube.Pos.y);
|
||||||
|
compressed.push_back(cube.Pos.z);
|
||||||
|
compressed.push_back(cube.Size.x);
|
||||||
|
compressed.push_back(cube.Size.y);
|
||||||
|
compressed.push_back(cube.Size.z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {compressed, defines};
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressedVoxels compressVoxels_bit(const std::vector<VoxelCube>& voxels) {
|
||||||
|
std::vector<DefVoxelId_t> profile;
|
||||||
|
std::vector<DefVoxelId_t> one_byte[7];
|
||||||
|
|
||||||
|
DefVoxelId_t maxValueProfile = 0;
|
||||||
|
DefVoxelId_t maxValues[7] = {0};
|
||||||
|
|
||||||
|
profile.reserve(voxels.size());
|
||||||
|
for(int iter = 0; iter < 7; iter++)
|
||||||
|
one_byte[iter].reserve(voxels.size());
|
||||||
|
|
||||||
|
assert(voxels.size() <= 65535);
|
||||||
|
for(const VoxelCube& cube : voxels) {
|
||||||
|
profile.push_back(cube.VoxelId);
|
||||||
|
|
||||||
|
one_byte[0].push_back(cube.Meta);
|
||||||
|
one_byte[1].push_back(cube.Pos.x);
|
||||||
|
one_byte[2].push_back(cube.Pos.y);
|
||||||
|
one_byte[3].push_back(cube.Pos.z);
|
||||||
|
one_byte[4].push_back(cube.Size.x);
|
||||||
|
one_byte[5].push_back(cube.Size.y);
|
||||||
|
one_byte[6].push_back(cube.Size.z);
|
||||||
|
|
||||||
|
if(cube.VoxelId > maxValueProfile)
|
||||||
|
maxValueProfile = cube.VoxelId;
|
||||||
|
if(cube.Meta > maxValues[0])
|
||||||
|
maxValues[0] = cube.Meta;
|
||||||
|
if(cube.Pos.x > maxValues[1])
|
||||||
|
maxValues[1] = cube.Pos.x;
|
||||||
|
if(cube.Pos.y > maxValues[2])
|
||||||
|
maxValues[2] = cube.Pos.y;
|
||||||
|
if(cube.Pos.z > maxValues[3])
|
||||||
|
maxValues[3] = cube.Pos.z;
|
||||||
|
if(cube.Size.x > maxValues[4])
|
||||||
|
maxValues[4] = cube.Size.x;
|
||||||
|
if(cube.Size.y > maxValues[5])
|
||||||
|
maxValues[5] = cube.Size.y;
|
||||||
|
if(cube.Size.z > maxValues[6])
|
||||||
|
maxValues[6] = cube.Size.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::sort(profile.begin(), profile.end());
|
||||||
|
auto last = std::unique(profile.begin(), profile.end());
|
||||||
|
profile.erase(last, profile.end());
|
||||||
|
profile.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int iter = 0; iter < 7; iter++) {
|
||||||
|
std::sort(one_byte[iter].begin(), one_byte[iter].end());
|
||||||
|
auto last = std::unique(one_byte[iter].begin(), one_byte[iter].end());
|
||||||
|
one_byte[iter].erase(last, one_byte[iter].end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Количество бит на идентификатор в сыром виде
|
||||||
|
size_t bits_raw_profile = std::ceil(std::log2(maxValueProfile));
|
||||||
|
assert(bits_raw_profile >= 1 && bits_raw_profile <= 24);
|
||||||
|
size_t bits_index_profile = std::ceil(std::log2(profile.size()));
|
||||||
|
bool indices_profile = 16+bits_raw_profile*profile.size()+bits_index_profile*voxels.size() < bits_raw_profile*voxels.size();
|
||||||
|
|
||||||
|
size_t bits_raw[7];
|
||||||
|
size_t bits_index[7];
|
||||||
|
bool indices[7];
|
||||||
|
|
||||||
|
for(int iter = 0; iter < 7; iter++) {
|
||||||
|
bits_raw[iter] = std::ceil(std::log2(maxValues[iter]));
|
||||||
|
assert(bits_raw[iter] >= 1 && bits_raw[iter] <= 8);
|
||||||
|
bits_index[iter] = std::ceil(std::log2(one_byte[iter].size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<bool> buff;
|
||||||
|
|
||||||
|
buff.push_back(indices_profile);
|
||||||
|
for(int iter = 0; iter < 7; iter++)
|
||||||
|
buff.push_back(indices[iter]);
|
||||||
|
|
||||||
|
auto write = [&](size_t value, int count) {
|
||||||
|
for(int iter = 0; iter < count; iter++)
|
||||||
|
buff.push_back((value >> iter) & 0x1);
|
||||||
|
};
|
||||||
|
|
||||||
|
write(0, 8);
|
||||||
|
|
||||||
|
// Таблицы
|
||||||
|
if(indices_profile) {
|
||||||
|
write(profile.size(), 16);
|
||||||
|
write(bits_raw_profile, 5);
|
||||||
|
write(bits_index_profile, 4);
|
||||||
|
|
||||||
|
for(DefNodeId_t id : profile)
|
||||||
|
write(id, bits_raw_profile);
|
||||||
|
} else {
|
||||||
|
write(bits_raw_profile, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int iter = 0; iter < 7; iter++) {
|
||||||
|
if(indices[iter]) {
|
||||||
|
write(one_byte[iter].size(), 16);
|
||||||
|
write(bits_raw[iter], 3);
|
||||||
|
write(bits_index[iter], 3);
|
||||||
|
|
||||||
|
for(uint8_t id : one_byte[iter])
|
||||||
|
write(id, bits_raw[iter]);
|
||||||
|
} else {
|
||||||
|
write(bits_raw[iter], 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Данные
|
||||||
|
|
||||||
|
write(voxels.size(), 16);
|
||||||
|
|
||||||
|
for(const VoxelCube& cube : voxels) {
|
||||||
|
if(indices_profile)
|
||||||
|
write(std::binary_search(profile.begin(), profile.end(), cube.VoxelId), bits_index_profile);
|
||||||
|
else
|
||||||
|
write(cube.VoxelId, bits_raw_profile);
|
||||||
|
|
||||||
|
for(int iter = 0; iter < 7; iter++) {
|
||||||
|
uint8_t val;
|
||||||
|
if(iter == 0) val = cube.Meta;
|
||||||
|
else if(iter == 1) val = cube.Pos.x;
|
||||||
|
else if(iter == 2) val = cube.Pos.y;
|
||||||
|
else if(iter == 3) val = cube.Pos.z;
|
||||||
|
else if(iter == 4) val = cube.Size.x;
|
||||||
|
else if(iter == 5) val = cube.Size.y;
|
||||||
|
else if(iter == 6) val = cube.Size.z;
|
||||||
|
|
||||||
|
if(indices[iter])
|
||||||
|
write(std::binary_search(one_byte[iter].begin(), one_byte[iter].end(), val), bits_index[iter]);
|
||||||
|
else
|
||||||
|
write(val, bits_raw[iter]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::u8string compressed((buff.size()+7)/8, '\0');
|
||||||
|
|
||||||
|
for(int begin = 0, end = compressed.size()*8-buff.size(); begin < end; begin++)
|
||||||
|
compressed.push_back(0);
|
||||||
|
|
||||||
|
for(size_t iter = 0; iter < buff.size(); iter++)
|
||||||
|
compressed[iter / 8] |= (buff[iter] << (iter % 8));
|
||||||
|
|
||||||
|
return {compressed, profile};
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressedVoxels compressVoxels(const std::vector<VoxelCube>& voxels, bool fast) {
|
||||||
|
if(fast)
|
||||||
|
return compressVoxels_byte(voxels);
|
||||||
|
else
|
||||||
|
return compressVoxels_bit(voxels);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VoxelCube> unCompressVoxels_byte(const std::u8string& compressed) {
|
||||||
|
size_t pos = 1;
|
||||||
|
|
||||||
|
auto read = [&]() -> size_t {
|
||||||
|
assert(pos < compressed.size());
|
||||||
|
return compressed[pos++];
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t cmd = read();
|
||||||
|
|
||||||
|
if(cmd & 0x1) {
|
||||||
|
// Таблица
|
||||||
|
uint8_t bytes_per_define = (cmd >> 1) & 0x1;
|
||||||
|
uint8_t bytes_raw = (cmd >> 2) & 0x3;
|
||||||
|
|
||||||
|
std::vector<DefVoxelId_t> defines;
|
||||||
|
defines.resize(read() | (read() << 8));
|
||||||
|
|
||||||
|
for(size_t iter = 0; iter < defines.size(); iter++) {
|
||||||
|
DefVoxelId_t id = read();
|
||||||
|
if(bytes_raw > 1)
|
||||||
|
id |= read() << 8;
|
||||||
|
if(bytes_raw > 2)
|
||||||
|
id |= read() << 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VoxelCube> voxels;
|
||||||
|
voxels.resize(read() | (read() << 8));
|
||||||
|
|
||||||
|
for(size_t iter = 0; iter < voxels.size(); iter++) {
|
||||||
|
size_t index = read();
|
||||||
|
|
||||||
|
if(bytes_per_define > 1)
|
||||||
|
index |= read() << 8;
|
||||||
|
|
||||||
|
VoxelCube &cube = voxels[iter];
|
||||||
|
|
||||||
|
assert(index < defines.size());
|
||||||
|
|
||||||
|
cube.VoxelId = defines[index];
|
||||||
|
cube.Meta = read();
|
||||||
|
cube.Pos.x = read();
|
||||||
|
cube.Pos.y = read();
|
||||||
|
cube.Pos.z = read();
|
||||||
|
cube.Size.x = read();
|
||||||
|
cube.Size.y = read();
|
||||||
|
cube.Size.z = read();
|
||||||
|
}
|
||||||
|
|
||||||
|
return voxels;
|
||||||
|
} else {
|
||||||
|
uint8_t bytes_raw = (cmd >> 2) & 0x3;
|
||||||
|
|
||||||
|
std::vector<VoxelCube> voxels;
|
||||||
|
voxels.resize(read() | (read() << 8));
|
||||||
|
|
||||||
|
for(size_t iter = 0; iter < voxels.size(); iter++) {
|
||||||
|
VoxelCube &cube = voxels[iter];
|
||||||
|
|
||||||
|
cube.VoxelId = read();
|
||||||
|
if(bytes_raw > 1)
|
||||||
|
cube.VoxelId |= read() << 8;
|
||||||
|
if(bytes_raw > 2)
|
||||||
|
cube.VoxelId |= read() << 16;
|
||||||
|
|
||||||
|
cube.Meta = read();
|
||||||
|
cube.Pos.x = read();
|
||||||
|
cube.Pos.y = read();
|
||||||
|
cube.Pos.z = read();
|
||||||
|
cube.Size.x = read();
|
||||||
|
cube.Size.y = read();
|
||||||
|
cube.Size.z = read();
|
||||||
|
}
|
||||||
|
|
||||||
|
return voxels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VoxelCube> unCompressVoxels_bit(const std::u8string& compressed) {
|
||||||
|
size_t pos = 1;
|
||||||
|
|
||||||
|
auto read = [&](int bits) -> size_t {
|
||||||
|
size_t out = 0;
|
||||||
|
for(int iter = 0; iter < bits; iter++, pos++) {
|
||||||
|
assert(pos < compressed.size()*8);
|
||||||
|
|
||||||
|
out |= (compressed[pos / 8] >> (pos % 8)) << iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool indices_profile = read(1);
|
||||||
|
bool indices[7];
|
||||||
|
|
||||||
|
for(int iter = 0; iter < 7; iter++)
|
||||||
|
indices[iter] = read(1);
|
||||||
|
|
||||||
|
std::vector<DefVoxelId_t> profile;
|
||||||
|
std::vector<DefVoxelId_t> one_byte[7];
|
||||||
|
uint8_t bits_raw_profile;
|
||||||
|
uint8_t bits_index_profile;
|
||||||
|
size_t bits_raw[7];
|
||||||
|
size_t bits_index[7];
|
||||||
|
|
||||||
|
// Таблицы
|
||||||
|
if(indices_profile) {
|
||||||
|
profile.resize(read(16));
|
||||||
|
bits_raw_profile = read(5);
|
||||||
|
bits_index_profile = read(4);
|
||||||
|
|
||||||
|
for(size_t iter = 0; iter < profile.size(); iter++)
|
||||||
|
profile[iter] = read(bits_raw_profile);
|
||||||
|
} else {
|
||||||
|
bits_raw_profile = read(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int iter = 0; iter < 7; iter++) {
|
||||||
|
if(indices[iter]) {
|
||||||
|
one_byte[iter].resize(read(16));
|
||||||
|
bits_raw[iter] = read(3);
|
||||||
|
bits_index[iter] = read(3);
|
||||||
|
|
||||||
|
for(size_t iter2 = 0; iter2 < one_byte[iter].size(); iter2++)
|
||||||
|
one_byte[iter][iter2] = read(bits_raw[iter]);
|
||||||
|
} else {
|
||||||
|
bits_raw[iter] = read(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Данные
|
||||||
|
std::vector<VoxelCube> voxels;
|
||||||
|
voxels.resize(read(16));
|
||||||
|
|
||||||
|
for(size_t iter = 0; iter < voxels.size(); iter++) {
|
||||||
|
VoxelCube &cube = voxels[iter];
|
||||||
|
|
||||||
|
if(indices_profile)
|
||||||
|
cube.VoxelId = profile[read(bits_index_profile)];
|
||||||
|
else
|
||||||
|
cube.VoxelId = read(bits_raw_profile);
|
||||||
|
|
||||||
|
for(int iter = 0; iter < 7; iter++) {
|
||||||
|
uint8_t val;
|
||||||
|
|
||||||
|
if(indices[iter])
|
||||||
|
val = one_byte[iter][read(bits_index[iter])];
|
||||||
|
else
|
||||||
|
val = read(bits_raw[iter]);
|
||||||
|
|
||||||
|
if(iter == 0) cube.Meta = val;
|
||||||
|
else if(iter == 1) cube.Pos.x = val;
|
||||||
|
else if(iter == 2) cube.Pos.y = val;
|
||||||
|
else if(iter == 3) cube.Pos.z = val;
|
||||||
|
else if(iter == 4) cube.Size.x = val;
|
||||||
|
else if(iter == 5) cube.Size.y = val;
|
||||||
|
else if(iter == 6) cube.Size.z = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return voxels;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VoxelCube> unCompressVoxels(const std::u8string& compressed) {
|
||||||
|
if(compressed.front())
|
||||||
|
return unCompressVoxels_byte(compressed);
|
||||||
|
else
|
||||||
|
return unCompressVoxels_bit(compressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CompressedNodes compressNodes_byte(const Node* nodes) {
|
||||||
|
std::u8string compressed;
|
||||||
|
|
||||||
|
std::vector<DefNodeId_t> profiles;
|
||||||
|
|
||||||
|
profiles.reserve(16*16*16);
|
||||||
|
|
||||||
|
compressed.push_back(1);
|
||||||
|
|
||||||
|
DefNodeId_t maxValueProfile = 0;
|
||||||
|
|
||||||
|
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||||||
|
const Node &node = nodes[iter];
|
||||||
|
|
||||||
|
profiles.push_back(node.NodeId);
|
||||||
|
|
||||||
|
if(node.NodeId > maxValueProfile)
|
||||||
|
maxValueProfile = node.NodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::sort(profiles.begin(), profiles.end());
|
||||||
|
auto last = std::unique(profiles.begin(), profiles.end());
|
||||||
|
profiles.erase(last, profiles.end());
|
||||||
|
profiles.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Количество байт на идентификатор в сыром виде
|
||||||
|
uint8_t bytes_raw_profile = std::ceil(std::log2(maxValueProfile)/8);
|
||||||
|
assert(bytes_raw_profile >= 1 && bytes_raw_profile <= 3);
|
||||||
|
// Количество байт на индекс
|
||||||
|
uint8_t bytes_indices_profile = std::ceil(std::log2(profiles.size())/8);
|
||||||
|
assert(bytes_indices_profile >= 1 && bytes_indices_profile <= 2);
|
||||||
|
|
||||||
|
bool indices_profile = 3+bytes_raw_profile*profiles.size()+bytes_indices_profile*16*16*16 < bytes_raw_profile*16*16*16;
|
||||||
|
|
||||||
|
compressed.push_back(indices_profile | (bytes_raw_profile << 1) | (bytes_indices_profile << 3));
|
||||||
|
|
||||||
|
if(indices_profile) {
|
||||||
|
// Таблица
|
||||||
|
compressed.push_back(profiles.size() & 0xff);
|
||||||
|
compressed.push_back((profiles.size() >> 8) & 0xff);
|
||||||
|
compressed.push_back((profiles.size() >> 16) & 0xff);
|
||||||
|
|
||||||
|
for(DefNodeId_t id : profiles) {
|
||||||
|
compressed.push_back(id & 0xff);
|
||||||
|
if(bytes_raw_profile > 1)
|
||||||
|
compressed.push_back((id >> 8) & 0xff);
|
||||||
|
if(bytes_raw_profile > 2)
|
||||||
|
compressed.push_back((id >> 16) & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Данные
|
||||||
|
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||||||
|
const Node &node = nodes[iter];
|
||||||
|
|
||||||
|
size_t index = std::binary_search(profiles.begin(), profiles.end(), node.NodeId);
|
||||||
|
compressed.push_back(index & 0xff);
|
||||||
|
if(bytes_indices_profile > 1)
|
||||||
|
compressed.push_back((index >> 8) & 0xff);
|
||||||
|
|
||||||
|
compressed.push_back(node.Meta);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||||||
|
const Node &node = nodes[iter];
|
||||||
|
|
||||||
|
compressed.push_back(node.NodeId & 0xff);
|
||||||
|
if(bytes_raw_profile > 1)
|
||||||
|
compressed.push_back((node.NodeId >> 8) & 0xff);
|
||||||
|
if(bytes_raw_profile > 2)
|
||||||
|
compressed.push_back((node.NodeId >> 8) & 0xff);
|
||||||
|
|
||||||
|
compressed.push_back(node.Meta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
profiles.shrink_to_fit();
|
||||||
|
|
||||||
|
return {compressed, profiles};
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressedNodes compressNodes_bit(const Node* nodes) {
|
||||||
|
std::u8string compressed;
|
||||||
|
|
||||||
|
std::vector<DefNodeId_t> profiles;
|
||||||
|
std::vector<DefNodeId_t> meta;
|
||||||
|
|
||||||
|
profiles.reserve(16*16*16);
|
||||||
|
meta.reserve(16*16*16);
|
||||||
|
|
||||||
|
compressed.push_back(1);
|
||||||
|
|
||||||
|
DefNodeId_t maxValueProfile = 0,
|
||||||
|
maxValueMeta = 0;
|
||||||
|
|
||||||
|
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||||||
|
const Node &node = nodes[iter];
|
||||||
|
|
||||||
|
profiles.push_back(node.NodeId);
|
||||||
|
meta.push_back(node.Meta);
|
||||||
|
|
||||||
|
if(node.NodeId > maxValueProfile)
|
||||||
|
maxValueProfile = node.NodeId;
|
||||||
|
|
||||||
|
if(node.Meta > maxValueMeta)
|
||||||
|
maxValueMeta = node.Meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::sort(profiles.begin(), profiles.end());
|
||||||
|
auto last = std::unique(profiles.begin(), profiles.end());
|
||||||
|
profiles.erase(last, profiles.end());
|
||||||
|
profiles.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::sort(meta.begin(), meta.end());
|
||||||
|
auto last = std::unique(meta.begin(), meta.end());
|
||||||
|
meta.erase(last, meta.end());
|
||||||
|
meta.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Количество бит на идентификатор в сыром виде
|
||||||
|
uint8_t bits_raw_profile = std::ceil(std::log2(maxValueProfile));
|
||||||
|
assert(bits_raw_profile >= 1 && bits_raw_profile <= 24);
|
||||||
|
// Количество бит на индекс
|
||||||
|
uint8_t bits_indices_profile = std::ceil(std::log2(profiles.size()));
|
||||||
|
assert(bits_indices_profile >= 1 && bits_indices_profile <= 16);
|
||||||
|
|
||||||
|
bool indices_profile = 3*8+bits_raw_profile*profiles.size()+bits_indices_profile*16*16*16 < bits_raw_profile*16*16*16;
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<bool> buff;
|
||||||
|
|
||||||
|
auto write = [&](size_t value, int count) {
|
||||||
|
for(int iter = 0; iter < count; iter++)
|
||||||
|
buff.push_back((value >> iter) & 0x1);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
write(indices_profile, 1);
|
||||||
|
write(bits_raw_profile, 5);
|
||||||
|
write(bits_indices_profile, 4);
|
||||||
|
|
||||||
|
// Количество бит на идентификатор в сыром виде
|
||||||
|
uint8_t bits_raw_meta = std::ceil(std::log2(maxValueMeta));
|
||||||
|
assert(bits_raw_meta >= 1 && bits_raw_meta <= 8);
|
||||||
|
// Количество бит на индекс
|
||||||
|
uint8_t bits_indices_meta = std::ceil(std::log2(meta.size()));
|
||||||
|
assert(bits_indices_meta >= 1 && bits_indices_meta <= 8);
|
||||||
|
|
||||||
|
bool indices_meta = 3*8+bits_raw_meta*profiles.size()+bits_indices_meta*16*16*16 < bits_raw_meta*16*16*16;
|
||||||
|
|
||||||
|
write(indices_meta, 1);
|
||||||
|
write(bits_raw_meta, 3);
|
||||||
|
write(bits_indices_meta, 3);
|
||||||
|
|
||||||
|
// Таблицы
|
||||||
|
if(indices_profile) {
|
||||||
|
write(profiles.size(), 12);
|
||||||
|
|
||||||
|
for(DefNodeId_t id : profiles) {
|
||||||
|
write(id, bits_raw_profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(indices_meta) {
|
||||||
|
write(meta.size(), 8);
|
||||||
|
|
||||||
|
for(DefNodeId_t id : meta) {
|
||||||
|
write(id, bits_raw_meta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Данные
|
||||||
|
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||||||
|
const Node &node = nodes[iter];
|
||||||
|
|
||||||
|
if(indices_profile) {
|
||||||
|
size_t index = std::binary_search(profiles.begin(), profiles.end(), node.NodeId);
|
||||||
|
write(index, bits_indices_profile);
|
||||||
|
} else {
|
||||||
|
write(node.NodeId, bits_raw_profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(indices_meta) {
|
||||||
|
size_t index = std::binary_search(meta.begin(), meta.end(), node.Meta);
|
||||||
|
write(index, bits_indices_meta);
|
||||||
|
} else {
|
||||||
|
write(node.Meta, bits_raw_meta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {compressed, profiles};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CompressedNodes compressNodes(const Node* nodes, bool fast) {
|
||||||
|
if(fast)
|
||||||
|
return compressNodes_byte(nodes);
|
||||||
|
else
|
||||||
|
return compressNodes_bit(nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unCompressNodes_byte(const std::u8string& compressed, Node* ptr) {
|
||||||
|
size_t pos = 1;
|
||||||
|
|
||||||
|
auto read = [&]() -> size_t {
|
||||||
|
assert(pos < compressed.size());
|
||||||
|
return compressed[pos++];
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t value = read();
|
||||||
|
|
||||||
|
uint8_t bytes_raw_profile = (value >> 1) & 0x3;
|
||||||
|
uint8_t bytes_indices_profile = (value >> 3) & 0x3;
|
||||||
|
bool indices_profile = value & 0x1;
|
||||||
|
|
||||||
|
if(indices_profile) {
|
||||||
|
std::vector<DefNodeId_t> profiles;
|
||||||
|
profiles.resize(read() | (read() << 8) | (read() << 16));
|
||||||
|
|
||||||
|
for(size_t iter = 0; iter < profiles.size(); iter++) {
|
||||||
|
DefNodeId_t id = read();
|
||||||
|
|
||||||
|
if(bytes_raw_profile > 1)
|
||||||
|
id |= read() << 8;
|
||||||
|
if(bytes_raw_profile > 2)
|
||||||
|
id |= read() << 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||||||
|
Node &node = ptr[iter];
|
||||||
|
|
||||||
|
DefNodeId_t index = read();
|
||||||
|
if(bytes_indices_profile > 1)
|
||||||
|
index |= read() << 8;
|
||||||
|
|
||||||
|
node.NodeId = profiles[index];
|
||||||
|
node.Meta = read();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||||||
|
Node &node = ptr[iter];
|
||||||
|
|
||||||
|
node.NodeId = read();
|
||||||
|
|
||||||
|
if(bytes_raw_profile > 1)
|
||||||
|
node.NodeId |= read() << 8;
|
||||||
|
if(bytes_raw_profile > 2)
|
||||||
|
node.NodeId |= read() << 16;
|
||||||
|
|
||||||
|
node.Meta = read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unCompressNodes_bit(const std::u8string& compressed, Node* ptr) {
|
||||||
|
size_t pos = 1;
|
||||||
|
|
||||||
|
auto read = [&](int bits) -> size_t {
|
||||||
|
size_t out = 0;
|
||||||
|
for(int iter = 0; iter < bits; iter++, pos++) {
|
||||||
|
assert(pos < compressed.size()*8);
|
||||||
|
|
||||||
|
out |= (compressed[pos / 8] >> (pos % 8)) << iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<DefNodeId_t> meta;
|
||||||
|
|
||||||
|
|
||||||
|
bool indices_profile = read(1);
|
||||||
|
uint8_t bits_raw_profile = read(5);
|
||||||
|
uint8_t bits_indices_profile = read(4);
|
||||||
|
|
||||||
|
bool indices_meta = read(1);
|
||||||
|
uint8_t bits_raw_meta = read(3);
|
||||||
|
uint8_t bits_indices_meta = read(3);
|
||||||
|
|
||||||
|
std::vector<DefNodeId_t> profiles;
|
||||||
|
|
||||||
|
// Таблицы
|
||||||
|
if(indices_profile) {
|
||||||
|
profiles.resize(read(12));
|
||||||
|
|
||||||
|
for(size_t iter = 0; iter < profiles.size(); iter++) {
|
||||||
|
profiles[iter] = read(bits_raw_profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(indices_meta) {
|
||||||
|
meta.resize(read(8));
|
||||||
|
|
||||||
|
for(size_t iter = 0; iter < meta.size(); iter++) {
|
||||||
|
meta[iter] = read(bits_raw_meta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Данные
|
||||||
|
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
||||||
|
Node &node = ptr[iter];
|
||||||
|
|
||||||
|
if(indices_profile) {
|
||||||
|
node.NodeId = profiles[read(bits_indices_profile)];
|
||||||
|
} else {
|
||||||
|
node.NodeId = read(bits_raw_profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(indices_meta) {
|
||||||
|
node.Meta = meta[read(bits_indices_meta)];
|
||||||
|
} else {
|
||||||
|
node.Meta = read(bits_raw_meta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unCompressNodes(const std::u8string& compressed, Node* ptr) {
|
||||||
|
if(compressed.front())
|
||||||
|
return unCompressNodes_byte(compressed, ptr);
|
||||||
|
else
|
||||||
|
return unCompressNodes_bit(compressed, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/Net.hpp"
|
||||||
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <glm/ext.hpp>
|
#include <glm/ext.hpp>
|
||||||
|
#include <initializer_list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
namespace LV {
|
namespace LV {
|
||||||
@@ -422,6 +426,61 @@ using PortalId_t = ResourceId_t;
|
|||||||
// uint8_t R : 2, G : 2, B : 2;
|
// uint8_t R : 2, G : 2, B : 2;
|
||||||
// };
|
// };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct VoxelCube {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
DefVoxelId_t VoxelId : 24, Meta : 8;
|
||||||
|
};
|
||||||
|
|
||||||
|
DefVoxelId_t Data = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
Pos::bvec256u Pos, Size; // Размер+1, 0 это единичный размер
|
||||||
|
|
||||||
|
auto operator<=>(const VoxelCube& other) const {
|
||||||
|
if (auto cmp = Pos <=> other.Pos; cmp != 0)
|
||||||
|
return cmp;
|
||||||
|
|
||||||
|
if (auto cmp = Size <=> other.Size; cmp != 0)
|
||||||
|
return cmp;
|
||||||
|
|
||||||
|
return Data <=> other.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const VoxelCube& other) const {
|
||||||
|
return Pos == other.Pos && Size == other.Size && Data == other.Data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CompressedVoxels {
|
||||||
|
std::u8string Compressed;
|
||||||
|
// Уникальный сортированный список идентификаторов вокселей
|
||||||
|
std::vector<DefVoxelId_t> Defines;
|
||||||
|
};
|
||||||
|
|
||||||
|
CompressedVoxels compressVoxels(const std::vector<VoxelCube>& voxels, bool fast = true);
|
||||||
|
std::vector<VoxelCube> unCompressVoxels(const std::u8string& compressed);
|
||||||
|
|
||||||
|
struct Node {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
DefNodeId_t NodeId : 24, Meta : 8;
|
||||||
|
};
|
||||||
|
DefNodeId_t Data;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CompressedNodes {
|
||||||
|
std::u8string Compressed;
|
||||||
|
// Уникальный сортированный список идентификаторов нод
|
||||||
|
std::vector<DefNodeId_t> Defines;
|
||||||
|
};
|
||||||
|
|
||||||
|
CompressedNodes compressNodes(const Node* nodes, bool fast = true);
|
||||||
|
void unCompressNodes(const std::u8string& compressed, Node* ptr);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// TODO: Всё это надо переписать
|
||||||
|
|
||||||
|
|
||||||
#include "MemoryPool.hpp"
|
#include "MemoryPool.hpp"
|
||||||
#include "Async.hpp"
|
#include "Async.hpp"
|
||||||
#include "TOSLib.hpp"
|
#include "TOSLib.hpp"
|
||||||
|
|||||||
@@ -55,32 +55,6 @@ struct ServerTime {
|
|||||||
uint32_t Seconds : 24, Sub : 8;
|
uint32_t Seconds : 24, Sub : 8;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VoxelCube {
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
DefVoxelId_t VoxelId : 24, Meta : 8;
|
|
||||||
};
|
|
||||||
|
|
||||||
DefVoxelId_t Data = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
Pos::bvec256u Left, Right; // TODO: заменить на позицию и размер
|
|
||||||
|
|
||||||
auto operator<=>(const VoxelCube& other) const {
|
|
||||||
if (auto cmp = Left <=> other.Left; cmp != 0)
|
|
||||||
return cmp;
|
|
||||||
|
|
||||||
if (auto cmp = Right <=> other.Right; cmp != 0)
|
|
||||||
return cmp;
|
|
||||||
|
|
||||||
return Data <=> other.Data;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const VoxelCube& other) const {
|
|
||||||
return Left == other.Left && Right == other.Right && Data == other.Data;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VoxelCube_Region {
|
struct VoxelCube_Region {
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
@@ -107,15 +81,6 @@ struct VoxelCube_Region {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Node {
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
DefNodeId_t NodeId : 24, Meta : 8;
|
|
||||||
};
|
|
||||||
DefNodeId_t Data;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AABB {
|
struct AABB {
|
||||||
Pos::Object VecMin, VecMax;
|
Pos::Object VecMin, VecMax;
|
||||||
|
|
||||||
@@ -382,8 +347,8 @@ inline void convertChunkVoxelsToRegion(const std::unordered_map<Pos::bvec4u, std
|
|||||||
for (const auto& cube : voxels) {
|
for (const auto& cube : voxels) {
|
||||||
regions.push_back({
|
regions.push_back({
|
||||||
cube.VoxelId, cube.Meta,
|
cube.VoxelId, cube.Meta,
|
||||||
Pos::bvec1024u(left.x+cube.Left.x, left.y+cube.Left.y, left.z+cube.Left.z),
|
Pos::bvec1024u(left.x+cube.Pos.x, left.y+cube.Pos.y, left.z+cube.Pos.z),
|
||||||
Pos::bvec1024u(left.x+cube.Right.x, left.y+cube.Right.y, left.z+cube.Right.z)
|
Pos::bvec1024u(left.x+cube.Pos.x+cube.Size.x, left.y+cube.Pos.y+cube.Size.y, left.z+cube.Pos.z+cube.Size.z)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,60 +44,6 @@ void ContentEventController::onWorldUpdate(WorldId_t worldId, World *worldObj)
|
|||||||
Remote->prepareWorldUpdate(worldId, worldObj);
|
Remote->prepareWorldUpdate(worldId, worldObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContentEventController::onChunksUpdate_Voxels(WorldId_t worldId, Pos::GlobalRegion regionPos,
|
|
||||||
const std::unordered_map<Pos::bvec4u, const std::vector<VoxelCube>*>& chunks)
|
|
||||||
{
|
|
||||||
auto pWorld = ContentViewState.Regions.find(worldId);
|
|
||||||
if(pWorld == ContentViewState.Regions.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(!std::binary_search(pWorld->second.begin(), pWorld->second.end(), regionPos))
|
|
||||||
return;
|
|
||||||
|
|
||||||
for(auto pChunk : chunks) {
|
|
||||||
Pos::GlobalChunk chunkPos = (Pos::GlobalChunk(regionPos) << 2) + pChunk.first;
|
|
||||||
Remote->prepareChunkUpdate_Voxels(worldId, chunkPos, pChunk.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContentEventController::onChunksUpdate_Nodes(WorldId_t worldId, Pos::GlobalRegion regionPos,
|
|
||||||
const std::unordered_map<Pos::bvec4u, const Node*> &chunks)
|
|
||||||
{
|
|
||||||
auto pWorld = ContentViewState.Regions.find(worldId);
|
|
||||||
if(pWorld == ContentViewState.Regions.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(!std::binary_search(pWorld->second.begin(), pWorld->second.end(), regionPos))
|
|
||||||
return;
|
|
||||||
|
|
||||||
for(auto pChunk : chunks) {
|
|
||||||
Pos::GlobalChunk chunkPos = (Pos::GlobalChunk(regionPos) << 2) + Pos::GlobalChunk(pChunk.first);
|
|
||||||
Remote->prepareChunkUpdate_Nodes(worldId, chunkPos, pChunk.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// void ContentEventController::onChunksUpdate_LightPrism(WorldId_t worldId, Pos::GlobalRegion regionPos,
|
|
||||||
// const std::unordered_map<Pos::bvec4u, const LightPrism*> &chunks)
|
|
||||||
// {
|
|
||||||
// auto pWorld = ContentViewState.find(worldId);
|
|
||||||
// if(pWorld == ContentViewState.end())
|
|
||||||
// return;
|
|
||||||
|
|
||||||
// auto pRegion = pWorld->second.find(regionPos);
|
|
||||||
// if(pRegion == pWorld->second.end())
|
|
||||||
// return;
|
|
||||||
|
|
||||||
// const std::bitset<4096> &chunkBitset = pRegion->second;
|
|
||||||
|
|
||||||
// for(auto pChunk : chunks) {
|
|
||||||
// if(!chunkBitset.test(pChunk.first))
|
|
||||||
// continue;
|
|
||||||
|
|
||||||
// Pos::GlobalChunk chunkPos = regionPos.toChunk(pChunk.first);
|
|
||||||
// Remote->prepareChunkUpdate_LightPrism(worldId, chunkPos, pChunk.second);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
void ContentEventController::onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos,
|
void ContentEventController::onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos,
|
||||||
const std::unordered_set<RegionEntityId_t> &enter, const std::unordered_set<RegionEntityId_t> &lost)
|
const std::unordered_set<RegionEntityId_t> &enter, const std::unordered_set<RegionEntityId_t> &lost)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -151,11 +151,6 @@ public:
|
|||||||
// Фильтровать не отслеживаемые миры
|
// Фильтровать не отслеживаемые миры
|
||||||
void onWorldUpdate(WorldId_t worldId, World *worldObj);
|
void onWorldUpdate(WorldId_t worldId, World *worldObj);
|
||||||
|
|
||||||
// Нужно фильтровать неотслеживаемые чанки
|
|
||||||
void onChunksUpdate_Voxels(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<Pos::bvec4u, const std::vector<VoxelCube>*> &chunks);
|
|
||||||
void onChunksUpdate_Nodes(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<Pos::bvec4u, const Node*> &chunks);
|
|
||||||
//void onChunksUpdate_LightPrism(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<Pos::bvec4u, const LightPrism*> &chunks);
|
|
||||||
|
|
||||||
void onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_set<RegionEntityId_t> &enter, const std::unordered_set<RegionEntityId_t> &lost);
|
void onEntityEnterLost(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_set<RegionEntityId_t> &enter, const std::unordered_set<RegionEntityId_t> &lost);
|
||||||
void onEntitySwap(ServerEntityId_t prevId, ServerEntityId_t newId);
|
void onEntitySwap(ServerEntityId_t prevId, ServerEntityId_t newId);
|
||||||
void onEntityUpdates(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<RegionEntityId_t, Entity*> &entities);
|
void onEntityUpdates(WorldId_t worldId, Pos::GlobalRegion regionPos, const std::unordered_map<RegionEntityId_t, Entity*> &entities);
|
||||||
|
|||||||
@@ -11,24 +11,62 @@
|
|||||||
#include <glm/geometric.hpp>
|
#include <glm/geometric.hpp>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include "SaveBackends/Filesystem.hpp"
|
#include "SaveBackends/Filesystem.hpp"
|
||||||
#include "Server/SaveBackend.hpp"
|
#include "Server/SaveBackend.hpp"
|
||||||
#include "Server/World.hpp"
|
#include "Server/World.hpp"
|
||||||
|
#include "TOSLib.hpp"
|
||||||
#include "glm/gtc/noise.hpp"
|
#include "glm/gtc/noise.hpp"
|
||||||
|
|
||||||
namespace LV::Server {
|
namespace LV::Server {
|
||||||
|
|
||||||
GameServer::~GameServer() {
|
GameServer::~GameServer() {
|
||||||
shutdown("on ~GameServer");
|
shutdown("on ~GameServer");
|
||||||
|
Backing.NeedShutdown = true;
|
||||||
|
Backing.Symaphore.notify_all();
|
||||||
|
|
||||||
RunThread.join();
|
RunThread.join();
|
||||||
WorkDeadline.cancel();
|
WorkDeadline.cancel();
|
||||||
UseLock.wait_no_use();
|
UseLock.wait_no_use();
|
||||||
|
|
||||||
|
Backing.stop();
|
||||||
|
|
||||||
LOG.info() << "Сервер уничтожен";
|
LOG.info() << "Сервер уничтожен";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameServer::Backing_t::run(int id) {
|
||||||
|
LOG.debug() << "Старт фонового потока " << id;
|
||||||
|
|
||||||
|
try {
|
||||||
|
while(true) {
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(Mutex);
|
||||||
|
Symaphore.wait(lock, [&](){ return Run != 0 || NeedShutdown; });
|
||||||
|
if(NeedShutdown) {
|
||||||
|
LOG.debug() << "Завершение выполнения фонового потока " << id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Работа
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(Mutex);
|
||||||
|
Run--;
|
||||||
|
Symaphore.notify_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(const std::exception& exc) {
|
||||||
|
std::unique_lock<std::mutex> lock(Mutex);
|
||||||
|
NeedShutdown = true;
|
||||||
|
LOG.error() << "Ошибка выполнения фонового потока " << id << ":\n" << exc.what();
|
||||||
|
}
|
||||||
|
|
||||||
|
Symaphore.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
static thread_local std::vector<ContentViewCircle> TL_Circles;
|
static thread_local std::vector<ContentViewCircle> TL_Circles;
|
||||||
|
|
||||||
std::vector<ContentViewCircle> GameServer::Expanse_t::accumulateContentViewCircles(ContentViewCircle circle, int depth)
|
std::vector<ContentViewCircle> GameServer::Expanse_t::accumulateContentViewCircles(ContentViewCircle circle, int depth)
|
||||||
@@ -356,6 +394,8 @@ void GameServer::stepConnections() {
|
|||||||
lock->clear();
|
lock->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Backing.end();
|
||||||
|
|
||||||
// Отключение игроков
|
// Отключение игроков
|
||||||
for(std::unique_ptr<ContentEventController> &cec : Game.CECs) {
|
for(std::unique_ptr<ContentEventController> &cec : Game.CECs) {
|
||||||
// Убрать отключившихся
|
// Убрать отключившихся
|
||||||
@@ -425,7 +465,7 @@ IWorldSaveBackend::TickSyncInfo_Out GameServer::stepDatabaseSync() {
|
|||||||
auto iterWorld = Expanse.Worlds.find(worldId);
|
auto iterWorld = Expanse.Worlds.find(worldId);
|
||||||
assert(iterWorld != Expanse.Worlds.end());
|
assert(iterWorld != Expanse.Worlds.end());
|
||||||
|
|
||||||
std::vector<Pos::GlobalRegion> notLoaded = iterWorld->second->onCEC_RegionsEnter(cec.get(), regions);
|
std::vector<Pos::GlobalRegion> notLoaded = iterWorld->second->onCEC_RegionsEnter(cec.get(), regions, worldId);
|
||||||
if(!notLoaded.empty()) {
|
if(!notLoaded.empty()) {
|
||||||
// Добавляем к списку на загрузку
|
// Добавляем к списку на загрузку
|
||||||
std::vector<Pos::GlobalRegion> &tl = toDB.Load[worldId];
|
std::vector<Pos::GlobalRegion> &tl = toDB.Load[worldId];
|
||||||
@@ -483,6 +523,9 @@ void GameServer::stepGeneratorAndLuaAsync(IWorldSaveBackend::TickSyncInfo_Out db
|
|||||||
for(auto& [worldId, regions] : db.NotExisten) {
|
for(auto& [worldId, regions] : db.NotExisten) {
|
||||||
auto &r = calculatedNoise[worldId];
|
auto &r = calculatedNoise[worldId];
|
||||||
for(Pos::GlobalRegion pos : regions) {
|
for(Pos::GlobalRegion pos : regions) {
|
||||||
|
if(IsGoingShutdown)
|
||||||
|
break;
|
||||||
|
|
||||||
r.emplace_back();
|
r.emplace_back();
|
||||||
std::get<0>(r.back()) = pos;
|
std::get<0>(r.back()) = pos;
|
||||||
auto ®ion = std::get<1>(r.back());
|
auto ®ion = std::get<1>(r.back());
|
||||||
@@ -494,11 +537,14 @@ void GameServer::stepGeneratorAndLuaAsync(IWorldSaveBackend::TickSyncInfo_Out db
|
|||||||
for(int z = 0; z < 64; z++)
|
for(int z = 0; z < 64; z++)
|
||||||
for(int y = 0; y < 64; y++)
|
for(int y = 0; y < 64; y++)
|
||||||
for(int x = 0; x < 64; x++, ptr++) {
|
for(int x = 0; x < 64; x++, ptr++) {
|
||||||
*ptr = glm::perlin(glm::dvec3(posNode.x+x, posNode.y+y, posNode.z+z));
|
*ptr = TOS::genRand(); //glm::perlin(glm::vec3(posNode.x+x, posNode.y+y, posNode.z+z));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(IsGoingShutdown)
|
||||||
|
return;
|
||||||
|
|
||||||
std::unordered_map<WorldId_t, std::vector<std::pair<Pos::GlobalRegion, World::RegionIn>>> toLoadRegions;
|
std::unordered_map<WorldId_t, std::vector<std::pair<Pos::GlobalRegion, World::RegionIn>>> toLoadRegions;
|
||||||
|
|
||||||
// Синхронизация с контроллером асинхронных обработчиков луа
|
// Синхронизация с контроллером асинхронных обработчиков луа
|
||||||
@@ -565,7 +611,7 @@ void GameServer::stepGeneratorAndLuaAsync(IWorldSaveBackend::TickSyncInfo_Out db
|
|||||||
|
|
||||||
iterWorld->second->pushRegions(std::move(regions));
|
iterWorld->second->pushRegions(std::move(regions));
|
||||||
for(auto& [cec, poses] : toSubscribe) {
|
for(auto& [cec, poses] : toSubscribe) {
|
||||||
iterWorld->second->onCEC_RegionsEnter(cec, poses);
|
iterWorld->second->onCEC_RegionsEnter(cec, poses, worldId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1068,6 +1114,8 @@ void GameServer::stepSyncContent() {
|
|||||||
full.insert(cec->Remote->pushPreparedPackets());
|
full.insert(cec->Remote->pushPreparedPackets());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Backing.start();
|
||||||
|
|
||||||
full.uniq();
|
full.uniq();
|
||||||
|
|
||||||
if(!full.BinTexture.empty())
|
if(!full.BinTexture.empty())
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <Common/Lockable.hpp>
|
#include <Common/Lockable.hpp>
|
||||||
#include <boost/asio/any_io_executor.hpp>
|
#include <boost/asio/any_io_executor.hpp>
|
||||||
#include <boost/asio/io_context.hpp>
|
#include <boost/asio/io_context.hpp>
|
||||||
|
#include <condition_variable>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include "RemoteClient.hpp"
|
#include "RemoteClient.hpp"
|
||||||
#include "Server/Abstract.hpp"
|
#include "Server/Abstract.hpp"
|
||||||
@@ -116,12 +117,78 @@ class GameServer : public AsyncObject {
|
|||||||
std::unique_ptr<IModStorageSaveBackend> ModStorage;
|
std::unique_ptr<IModStorageSaveBackend> ModStorage;
|
||||||
} SaveBackend;
|
} SaveBackend;
|
||||||
|
|
||||||
|
enum class EnumBackingScenario {
|
||||||
|
ChunksChanges // Сжатие изменённых чанков и отправка клиентам
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Обязательно между тактами
|
||||||
|
После окончания такта пул копирует изменённые чанки
|
||||||
|
- синхронизация сбора в stepDatabaseSync -
|
||||||
|
сжимает их и отправляет клиентам
|
||||||
|
- синхронизация в начале stepPlayerProceed -
|
||||||
|
^ к этому моменту все данные должны быть отправлены
|
||||||
|
Далее при подписании на новые регионы они будут добавлены в пул на обработку
|
||||||
|
|
||||||
|
Генерация шума
|
||||||
|
OpenCL или пул
|
||||||
|
|
||||||
|
Конвертация ресурсов игры, их хранение в кеше и загрузка в память для отправки клиентам
|
||||||
|
io_uring или последовательное чтение
|
||||||
|
|
||||||
|
Исполнение асинхронного луа
|
||||||
|
Пул для постоянной работы и синхронизации времени с главным потоком
|
||||||
|
|
||||||
|
Сжатие/расжатие регионов в базе
|
||||||
|
Локальный поток должен собирать ключи профилей для базы
|
||||||
|
Остальное внутри базы
|
||||||
|
*/
|
||||||
|
struct Backing_t {
|
||||||
|
TOS::Logger LOG = "Backing";
|
||||||
|
bool NeedShutdown = false;
|
||||||
|
std::vector<std::thread> Threads;
|
||||||
|
std::mutex Mutex;
|
||||||
|
std::atomic_int Run = 0;
|
||||||
|
std::condition_variable Symaphore;
|
||||||
|
std::unordered_map<WorldId_t, std::unique_ptr<World>> *Worlds;
|
||||||
|
|
||||||
|
void start() {
|
||||||
|
std::lock_guard<std::mutex> lock(Mutex);
|
||||||
|
Run = Threads.size();
|
||||||
|
Symaphore.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void end() {
|
||||||
|
std::unique_lock<std::mutex> lock(Mutex);
|
||||||
|
Symaphore.wait(lock, [&](){ return Run == 0 || NeedShutdown; });
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(Mutex);
|
||||||
|
NeedShutdown = true;
|
||||||
|
Symaphore.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(std::thread& thread : Threads)
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(int id);
|
||||||
|
} Backing;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GameServer(asio::io_context &ioc, fs::path worldPath)
|
GameServer(asio::io_context &ioc, fs::path worldPath)
|
||||||
: AsyncObject(ioc),
|
: AsyncObject(ioc),
|
||||||
Content(ioc, nullptr, nullptr, nullptr, nullptr, nullptr)
|
Content(ioc, nullptr, nullptr, nullptr, nullptr, nullptr)
|
||||||
{
|
{
|
||||||
init(worldPath);
|
init(worldPath);
|
||||||
|
|
||||||
|
Backing.Threads.resize(4);
|
||||||
|
Backing.Worlds = &Expanse.Worlds;
|
||||||
|
for(size_t iter = 0; iter < Backing.Threads.size(); iter++) {
|
||||||
|
Backing.Threads[iter] = std::thread(Backing.Run, &Backing, iter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~GameServer();
|
virtual ~GameServer();
|
||||||
@@ -211,6 +278,7 @@ private:
|
|||||||
/*
|
/*
|
||||||
Обработка запросов двоичных ресурсов и определений
|
Обработка запросов двоичных ресурсов и определений
|
||||||
Отправка пакетов игрокам
|
Отправка пакетов игрокам
|
||||||
|
Запуск задачи ChunksChanges
|
||||||
*/
|
*/
|
||||||
void stepSyncContent();
|
void stepSyncContent();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -67,9 +67,13 @@ void RemoteClient::shutdown(EnumDisconnect type, const std::string reason) {
|
|||||||
LOG.info() << "Игрок '" << Username << "' отключился " << info;
|
LOG.info() << "Игрок '" << Username << "' отключился " << info;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos,
|
bool RemoteClient::maybe_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels,
|
||||||
const std::vector<VoxelCube>* voxels)
|
const std::vector<DefVoxelId_t>& uniq_sorted_defines)
|
||||||
{
|
{
|
||||||
|
bool lock = ResUses.RefChunkLock.exchange(1);
|
||||||
|
if(lock)
|
||||||
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Обновить зависимости
|
Обновить зависимости
|
||||||
Запросить недостающие
|
Запросить недостающие
|
||||||
@@ -80,39 +84,23 @@ void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk
|
|||||||
newTypes, /* Новые типы вокселей */
|
newTypes, /* Новые типы вокселей */
|
||||||
lostTypes /* Потерянные типы вокселей */;
|
lostTypes /* Потерянные типы вокселей */;
|
||||||
|
|
||||||
// Обновить зависимости вокселей
|
// Отметим использование этих вокселей
|
||||||
std::vector<DefVoxelId_t> v;
|
for(const DefVoxelId_t& id : uniq_sorted_defines) {
|
||||||
if(voxels) {
|
auto iter = ResUses.DefVoxel.find(id);
|
||||||
v.reserve(voxels->size());
|
if(iter == ResUses.DefVoxel.end()) {
|
||||||
for(const VoxelCube& value : *voxels) {
|
// Новый тип
|
||||||
v.push_back(value.VoxelId);
|
newTypes.push_back(id);
|
||||||
}
|
ResUses.DefVoxel[id] = 1;
|
||||||
|
} else {
|
||||||
std::sort(v.begin(), v.end());
|
// Увеличиваем счётчик
|
||||||
auto last = std::unique(v.begin(), v.end());
|
iter->second++;
|
||||||
v.erase(last, v.end());
|
|
||||||
|
|
||||||
// В v отсортированный список уникальных вокселей в чанке
|
|
||||||
|
|
||||||
|
|
||||||
// Отметим использование этих вокселей
|
|
||||||
for(const DefVoxelId_t& id : v) {
|
|
||||||
auto iter = ResUses.DefVoxel.find(id);
|
|
||||||
if(iter == ResUses.DefVoxel.end()) {
|
|
||||||
// Новый тип
|
|
||||||
newTypes.push_back(id);
|
|
||||||
ResUses.DefVoxel[id] = 1;
|
|
||||||
} else {
|
|
||||||
// Увеличиваем счётчик
|
|
||||||
iter->second++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Исключим зависимости предыдущей версии чанка
|
|
||||||
auto iterWorld = ResUses.RefChunk.find(worldId);
|
auto iterWorld = ResUses.RefChunk.find(worldId);
|
||||||
assert(iterWorld != ResUses.RefChunk.end());
|
|
||||||
Pos::bvec4u lChunk = (chunkPos & 0xf);
|
Pos::bvec4u lChunk = (chunkPos & 0xf);
|
||||||
|
|
||||||
|
if(iterWorld != ResUses.RefChunk.end())
|
||||||
// Исключим зависимости предыдущей версии чанка
|
// Исключим зависимости предыдущей версии чанка
|
||||||
{
|
{
|
||||||
auto iterRegion = iterWorld->second.find(chunkPos);
|
auto iterRegion = iterWorld->second.find(chunkPos);
|
||||||
@@ -128,9 +116,12 @@ void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ResUses.RefChunk[worldId] = {};
|
||||||
|
iterWorld = ResUses.RefChunk.find(worldId);
|
||||||
}
|
}
|
||||||
|
|
||||||
iterWorld->second[chunkPos][lChunk.pack()].Voxel = v;
|
iterWorld->second[chunkPos][lChunk.pack()].Voxel = uniq_sorted_defines;
|
||||||
|
|
||||||
if(!newTypes.empty()) {
|
if(!newTypes.empty()) {
|
||||||
// Добавляем новые типы в запрос
|
// Добавляем новые типы в запрос
|
||||||
@@ -146,46 +137,44 @@ void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: отправить чанк
|
checkPacketBorder(1+4+8+2+4+compressed_voxels.size());
|
||||||
|
NextPacket << (uint8_t) ToClient::L1::Content
|
||||||
|
<< (uint8_t) ToClient::L2Content::ChunkVoxels
|
||||||
|
<< worldId << chunkPos.pack() << uint32_t(compressed_voxels.size());
|
||||||
|
NextPacket.write((const std::byte*) compressed_voxels.data(), compressed_voxels.size());
|
||||||
|
|
||||||
|
ResUses.RefChunkLock.exchange(0);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteClient::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const Node* nodes) {
|
bool RemoteClient::maybe_prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes,
|
||||||
|
const std::vector<DefNodeId_t>& uniq_sorted_defines)
|
||||||
|
{
|
||||||
|
bool lock = ResUses.RefChunkLock.exchange(1);
|
||||||
|
if(lock)
|
||||||
|
return false;
|
||||||
|
|
||||||
std::vector<DefNodeId_t>
|
std::vector<DefNodeId_t>
|
||||||
newTypes, /* Новые типы нод */
|
newTypes, /* Новые типы нод */
|
||||||
lostTypes /* Потерянные типы нод */;
|
lostTypes /* Потерянные типы нод */;
|
||||||
|
|
||||||
// Обновить зависимости нод
|
// Отметим использование этих нод
|
||||||
std::vector<DefNodeId_t> n;
|
for(const DefNodeId_t& id : uniq_sorted_defines) {
|
||||||
{
|
auto iter = ResUses.DefNode.find(id);
|
||||||
n.reserve(16*16*16);
|
if(iter == ResUses.DefNode.end()) {
|
||||||
for(size_t iter = 0; iter < 16*16*16; iter++) {
|
// Новый тип
|
||||||
n.push_back(nodes[iter].NodeId);
|
newTypes.push_back(id);
|
||||||
}
|
ResUses.DefNode[id] = 1;
|
||||||
|
} else {
|
||||||
std::sort(n.begin(), n.end());
|
// Увеличиваем счётчик
|
||||||
auto last = std::unique(n.begin(), n.end());
|
iter->second++;
|
||||||
n.erase(last, n.end());
|
|
||||||
|
|
||||||
// В n отсортированный список уникальных нод в чанке
|
|
||||||
|
|
||||||
// Отметим использование этих нод
|
|
||||||
for(const DefNodeId_t& id : n) {
|
|
||||||
auto iter = ResUses.DefNode.find(id);
|
|
||||||
if(iter == ResUses.DefNode.end()) {
|
|
||||||
// Новый тип
|
|
||||||
newTypes.push_back(id);
|
|
||||||
ResUses.DefNode[id] = 1;
|
|
||||||
} else {
|
|
||||||
// Увеличиваем счётчик
|
|
||||||
iter->second++;
|
|
||||||
LOG.debug() << "id = " << id << ' ' << iter->second;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto iterWorld = ResUses.RefChunk.find(worldId);
|
auto iterWorld = ResUses.RefChunk.find(worldId);
|
||||||
assert(iterWorld != ResUses.RefChunk.end());
|
|
||||||
Pos::bvec4u lChunk = (chunkPos & 0xf);
|
Pos::bvec4u lChunk = (chunkPos & 0xf);
|
||||||
|
|
||||||
|
if(iterWorld != ResUses.RefChunk.end())
|
||||||
// Исключим зависимости предыдущей версии чанка
|
// Исключим зависимости предыдущей версии чанка
|
||||||
{
|
{
|
||||||
auto iterRegion = iterWorld->second.find(chunkPos);
|
auto iterRegion = iterWorld->second.find(chunkPos);
|
||||||
@@ -201,9 +190,12 @@ void RemoteClient::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ResUses.RefChunk[worldId] = {};
|
||||||
|
iterWorld = ResUses.RefChunk.find(worldId);
|
||||||
}
|
}
|
||||||
|
|
||||||
iterWorld->second[chunkPos][lChunk.pack()].Node = n;
|
iterWorld->second[chunkPos][lChunk.pack()].Node = uniq_sorted_defines;
|
||||||
|
|
||||||
if(!newTypes.empty()) {
|
if(!newTypes.empty()) {
|
||||||
// Добавляем новые типы в запрос
|
// Добавляем новые типы в запрос
|
||||||
@@ -219,8 +211,14 @@ void RemoteClient::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: отправить чанк
|
checkPacketBorder(1+4+8+4+compressed_nodes.size());
|
||||||
LOG.debug() << "Увидели " << chunkPos.x << ' ' << chunkPos.y << ' ' << chunkPos.z;
|
NextPacket << (uint8_t) ToClient::L1::Content
|
||||||
|
<< (uint8_t) ToClient::L2Content::ChunkNodes
|
||||||
|
<< worldId << chunkPos.pack() << uint32_t(compressed_nodes.size());
|
||||||
|
NextPacket.write((const std::byte*) compressed_nodes.data(), compressed_nodes.size());
|
||||||
|
|
||||||
|
ResUses.RefChunkLock.exchange(0);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteClient::prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regionPos) {
|
void RemoteClient::prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regionPos) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "Common/Packets.hpp"
|
#include "Common/Packets.hpp"
|
||||||
#include "Server/ContentEventController.hpp"
|
#include "Server/ContentEventController.hpp"
|
||||||
#include <Common/Abstract.hpp>
|
#include <Common/Abstract.hpp>
|
||||||
|
#include <atomic>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <set>
|
#include <set>
|
||||||
@@ -264,6 +265,7 @@ class RemoteClient {
|
|||||||
std::vector<DefVoxelId_t> Voxel;
|
std::vector<DefVoxelId_t> Voxel;
|
||||||
std::vector<DefNodeId_t> Node;
|
std::vector<DefNodeId_t> Node;
|
||||||
};
|
};
|
||||||
|
std::atomic_bool RefChunkLock = 0;
|
||||||
std::map<WorldId_t, std::map<Pos::GlobalRegion, std::array<ChunkRef, 4*4*4>>> RefChunk;
|
std::map<WorldId_t, std::map<Pos::GlobalRegion, std::array<ChunkRef, 4*4*4>>> RefChunk;
|
||||||
struct RefWorld_t {
|
struct RefWorld_t {
|
||||||
DefWorldId_t Profile;
|
DefWorldId_t Profile;
|
||||||
@@ -314,15 +316,24 @@ public:
|
|||||||
Socket.pushPackets(simplePackets, smartPackets);
|
Socket.pushPackets(simplePackets, smartPackets);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Функции подготавливают пакеты к отправке
|
/*
|
||||||
|
Сервер собирает изменения миров, сжимает их и раздаёт на отправку игрокам
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Функции подготавливают пакеты к отправке
|
||||||
// Отслеживаемое игроком использование контента
|
// Отслеживаемое игроком использование контента
|
||||||
|
|
||||||
|
// maybe созданны для использования в многопотоке, если ресурс сейчас занят вернёт false, потом нужно повторить запрос
|
||||||
// В зоне видимости добавился чанк или изменились его воксели
|
// В зоне видимости добавился чанк или изменились его воксели
|
||||||
void prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::vector<VoxelCube>* voxels);
|
bool maybe_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_voxels,
|
||||||
|
const std::vector<DefVoxelId_t>& uniq_sorted_defines);
|
||||||
// В зоне видимости добавился чанк или изменились его ноды
|
// В зоне видимости добавился чанк или изменились его ноды
|
||||||
void prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const Node* nodes);
|
bool maybe_prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::u8string& compressed_nodes,
|
||||||
void prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::unordered_map<Pos::bvec16u, Node> &nodes);
|
const std::vector<DefNodeId_t>& uniq_sorted_defines);
|
||||||
//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 prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regionPos);
|
void prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regionPos);
|
||||||
|
|
||||||
|
|||||||
@@ -33,81 +33,77 @@ public:
|
|||||||
return Dir / worldId / std::to_string(regionPos.x) / std::to_string(regionPos.y) / std::to_string(regionPos.z);
|
return Dir / worldId / std::to_string(regionPos.x) / std::to_string(regionPos.y) / std::to_string(regionPos.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool isAsync() { return false; };
|
virtual TickSyncInfo_Out tickSync(TickSyncInfo_In &&data) override {
|
||||||
|
TickSyncInfo_Out out;
|
||||||
virtual bool isExist(std::string worldId, Pos::GlobalRegion regionPos) {
|
out.NotExisten = std::move(data.Load);
|
||||||
return fs::exists(getPath(worldId, regionPos));
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void load(std::string worldId, Pos::GlobalRegion regionPos, SB_Region *data) {
|
virtual void changePreloadDistance(uint8_t value) override {
|
||||||
std::ifstream fd(getPath(worldId, regionPos));
|
|
||||||
js::object jobj = js::parse(fd).as_object();
|
|
||||||
|
|
||||||
{
|
|
||||||
js::array &jaVoxels = jobj.at("Voxels").as_array();
|
|
||||||
for(js::value &jvVoxel : jaVoxels) {
|
|
||||||
js::object &joVoxel = jvVoxel.as_object();
|
|
||||||
VoxelCube_Region cube;
|
|
||||||
cube.Data = joVoxel.at("Data").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();
|
|
||||||
cube.Right.x = joVoxel.at("RightX").as_uint64();
|
|
||||||
cube.Right.y = joVoxel.at("RightY").as_uint64();
|
|
||||||
cube.Right.z = joVoxel.at("RightZ").as_uint64();
|
|
||||||
data->Voxels.push_back(cube);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
js::object &joVoxelMap = jobj.at("VoxelsMap").as_object();
|
|
||||||
for(js::key_value_pair &jkvp : joVoxelMap) {
|
|
||||||
data->VoxelsMap[std::stoul(jkvp.key())] = jkvp.value().as_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void save(std::string worldId, Pos::GlobalRegion regionPos, const SB_Region *data) {
|
// virtual void load(std::string worldId, Pos::GlobalRegion regionPos, SB_Region *data) {
|
||||||
js::object jobj;
|
// std::ifstream fd(getPath(worldId, regionPos));
|
||||||
|
// js::object jobj = js::parse(fd).as_object();
|
||||||
|
|
||||||
{
|
// {
|
||||||
js::array jaVoxels;
|
// js::array &jaVoxels = jobj.at("Voxels").as_array();
|
||||||
for(const VoxelCube_Region &cube : data->Voxels) {
|
// for(js::value &jvVoxel : jaVoxels) {
|
||||||
js::object joVoxel;
|
// js::object &joVoxel = jvVoxel.as_object();
|
||||||
joVoxel["Data"] = cube.Data;
|
// VoxelCube_Region cube;
|
||||||
joVoxel["LeftX"] = cube.Left.x;
|
// cube.Data = joVoxel.at("Data").as_uint64();
|
||||||
joVoxel["LeftY"] = cube.Left.y;
|
// cube.Left.x = joVoxel.at("LeftX").as_uint64();
|
||||||
joVoxel["LeftZ"] = cube.Left.z;
|
// cube.Left.y = joVoxel.at("LeftY").as_uint64();
|
||||||
joVoxel["RightX"] = cube.Right.x;
|
// cube.Left.z = joVoxel.at("LeftZ").as_uint64();
|
||||||
joVoxel["RightY"] = cube.Right.y;
|
// cube.Right.x = joVoxel.at("RightX").as_uint64();
|
||||||
joVoxel["RightZ"] = cube.Right.z;
|
// cube.Right.y = joVoxel.at("RightY").as_uint64();
|
||||||
jaVoxels.push_back(std::move(joVoxel));
|
// cube.Right.z = joVoxel.at("RightZ").as_uint64();
|
||||||
}
|
// data->Voxels.push_back(cube);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
jobj["Voxels"] = std::move(jaVoxels);
|
// {
|
||||||
}
|
// js::object &joVoxelMap = jobj.at("VoxelsMap").as_object();
|
||||||
|
// for(js::key_value_pair &jkvp : joVoxelMap) {
|
||||||
|
// data->VoxelsMap[std::stoul(jkvp.key())] = jkvp.value().as_string();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
{
|
// virtual void save(std::string worldId, Pos::GlobalRegion regionPos, const SB_Region *data) {
|
||||||
js::object joVoxelMap;
|
// js::object jobj;
|
||||||
for(const auto &pair : data->VoxelsMap) {
|
|
||||||
joVoxelMap[std::to_string(pair.first)] = pair.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
jobj["VoxelsMap"] = std::move(joVoxelMap);
|
// {
|
||||||
}
|
// js::array jaVoxels;
|
||||||
|
// for(const VoxelCube_Region &cube : data->Voxels) {
|
||||||
|
// js::object joVoxel;
|
||||||
|
// joVoxel["Data"] = cube.Data;
|
||||||
|
// joVoxel["LeftX"] = cube.Left.x;
|
||||||
|
// joVoxel["LeftY"] = cube.Left.y;
|
||||||
|
// joVoxel["LeftZ"] = cube.Left.z;
|
||||||
|
// joVoxel["RightX"] = cube.Right.x;
|
||||||
|
// joVoxel["RightY"] = cube.Right.y;
|
||||||
|
// joVoxel["RightZ"] = cube.Right.z;
|
||||||
|
// jaVoxels.push_back(std::move(joVoxel));
|
||||||
|
// }
|
||||||
|
|
||||||
fs::create_directories(getPath(worldId, regionPos).parent_path());
|
// jobj["Voxels"] = std::move(jaVoxels);
|
||||||
std::ofstream fd(getPath(worldId, regionPos));
|
// }
|
||||||
fd << js::serialize(jobj);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void remove(std::string worldId, Pos::GlobalRegion regionPos) {
|
// {
|
||||||
fs::remove(getPath(worldId, regionPos));
|
// js::object joVoxelMap;
|
||||||
}
|
// for(const auto &pair : data->VoxelsMap) {
|
||||||
|
// joVoxelMap[std::to_string(pair.first)] = pair.second;
|
||||||
|
// }
|
||||||
|
|
||||||
virtual void remove(std::string worldId) {
|
// jobj["VoxelsMap"] = std::move(joVoxelMap);
|
||||||
fs::remove_all(Dir / worldId);
|
// }
|
||||||
}
|
|
||||||
|
// fs::create_directories(getPath(worldId, regionPos).parent_path());
|
||||||
|
// std::ofstream fd(getPath(worldId, regionPos));
|
||||||
|
// fd << js::serialize(jobj);
|
||||||
|
// }
|
||||||
};
|
};
|
||||||
|
|
||||||
class PSB_Filesystem : public IPlayerSaveBackend {
|
class PSB_Filesystem : public IPlayerSaveBackend {
|
||||||
@@ -163,35 +159,37 @@ public:
|
|||||||
|
|
||||||
virtual bool isAsync() { return false; };
|
virtual bool isAsync() { return false; };
|
||||||
|
|
||||||
virtual bool isExist(std::string playerId) {
|
virtual coro<bool> isExist(std::string useranme) override {
|
||||||
return fs::exists(getPath(playerId));
|
co_return fs::exists(getPath(useranme));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void rename(std::string fromPlayerId, std::string toPlayerId) {
|
virtual coro<> rename(std::string prevUsername, std::string newUsername) override {
|
||||||
fs::rename(getPath(fromPlayerId), getPath(toPlayerId));
|
fs::rename(getPath(prevUsername), getPath(newUsername));
|
||||||
|
co_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void load(std::string playerId, SB_Auth *data) {
|
virtual coro<bool> load(std::string useranme, SB_Auth& data) override {
|
||||||
std::ifstream fd(getPath(playerId));
|
std::ifstream fd(getPath(useranme));
|
||||||
js::object jobj = js::parse(fd).as_object();
|
js::object jobj = js::parse(fd).as_object();
|
||||||
|
|
||||||
data->Id = jobj.at("Id").as_uint64();
|
data.Id = jobj.at("Id").as_uint64();
|
||||||
data->PasswordHash = jobj.at("PasswordHash").as_string();
|
data.PasswordHash = jobj.at("PasswordHash").as_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void save(std::string playerId, const SB_Auth *data) {
|
virtual coro<> save(std::string playerId, const SB_Auth& data) override {
|
||||||
js::object jobj;
|
js::object jobj;
|
||||||
|
|
||||||
jobj["Id"] = data->Id;
|
jobj["Id"] = data.Id;
|
||||||
jobj["PasswordHash"] = data->PasswordHash;
|
jobj["PasswordHash"] = data.PasswordHash;
|
||||||
|
|
||||||
fs::create_directories(getPath(playerId).parent_path());
|
fs::create_directories(getPath(playerId).parent_path());
|
||||||
std::ofstream fd(getPath(playerId));
|
std::ofstream fd(getPath(playerId));
|
||||||
fd << js::serialize(jobj);
|
fd << js::serialize(jobj);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void remove(std::string playerId) {
|
virtual coro<> remove(std::string username) override {
|
||||||
fs::remove(getPath(playerId));
|
fs::remove(getPath(username));
|
||||||
|
co_return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#include "World.hpp"
|
#include "World.hpp"
|
||||||
|
#include "TOSLib.hpp"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
namespace LV::Server {
|
namespace LV::Server {
|
||||||
@@ -14,23 +16,40 @@ World::~World() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::onUpdate(GameServer *server, float dtime) {
|
std::vector<Pos::GlobalRegion> World::onCEC_RegionsEnter(ContentEventController *cec, const std::vector<Pos::GlobalRegion>& enter, WorldId_t wId) {
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Pos::GlobalRegion> World::onCEC_RegionsEnter(ContentEventController *cec, const std::vector<Pos::GlobalRegion> &enter) {
|
|
||||||
std::vector<Pos::GlobalRegion> out;
|
std::vector<Pos::GlobalRegion> out;
|
||||||
|
|
||||||
|
TOS::Logger("Test").debug() << "Start";
|
||||||
|
|
||||||
for(const Pos::GlobalRegion &pos : enter) {
|
for(const Pos::GlobalRegion &pos : enter) {
|
||||||
auto iterRegion = Regions.find(pos);
|
auto iterRegion = Regions.find(pos);
|
||||||
if(iterRegion == Regions.end()) {
|
if(iterRegion == Regions.end()) {
|
||||||
out.push_back(pos);
|
out.push_back(pos);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
iterRegion->second->CECs.push_back(cec);
|
auto ®ion = *iterRegion->second;
|
||||||
|
region.CECs.push_back(cec);
|
||||||
// Отправить клиенту информацию о чанках и сущностях
|
// Отправить клиенту информацию о чанках и сущностях
|
||||||
|
std::unordered_map<Pos::bvec4u, const std::vector<VoxelCube>*> voxels;
|
||||||
|
std::unordered_map<Pos::bvec4u, const Node*> nodes;
|
||||||
|
|
||||||
|
for(auto& [key, value] : region.Voxels) {
|
||||||
|
voxels[key] = &value;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int z = 0; z < 4; z++)
|
||||||
|
for(int y = 0; y < 4; y++)
|
||||||
|
for(int x = 0; x < 4; x++) {
|
||||||
|
nodes[Pos::bvec4u(x, y, z)] = (const Node*) ®ion.Nodes[0][0][0][x][y][z];
|
||||||
|
}
|
||||||
|
|
||||||
|
cec->onChunksUpdate_Voxels(wId, pos, voxels);
|
||||||
|
cec->onChunksUpdate_Nodes(wId, pos, nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TOS::Logger("Test").debug() << "End";
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,4 +69,26 @@ void World::onCEC_RegionsLost(ContentEventController *cec, const std::vector<Pos
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
World::SaveUnloadInfo World::onStepDatabaseSync() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void World::pushRegions(std::vector<std::pair<Pos::GlobalRegion, RegionIn>> regions) {
|
||||||
|
for(auto& [key, value] : regions) {
|
||||||
|
Region ®ion = *(Regions[key] = std::make_unique<Region>());
|
||||||
|
region.Voxels = std::move(value.Voxels);
|
||||||
|
|
||||||
|
Node *ptr = (Node*) region.Nodes;
|
||||||
|
for(std::array<Node, 16*16*16>& nodes : value.Nodes) {
|
||||||
|
|
||||||
|
std::copy(nodes.data(), nodes.data()+16*16*16, ptr);
|
||||||
|
ptr += 16*16*16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void World::onUpdate(GameServer *server, float dtime) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -85,13 +85,13 @@ public:
|
|||||||
aabbInfo.VecMin.set(axis, aabbInfo.VecMin[axis] & ~0xff00);
|
aabbInfo.VecMin.set(axis, aabbInfo.VecMin[axis] & ~0xff00);
|
||||||
aabbInfo.VecMax = aabbInfo.VecMin;
|
aabbInfo.VecMax = aabbInfo.VecMin;
|
||||||
|
|
||||||
aabbInfo.VecMin.x |= int(cube.Left.x) << 6;
|
aabbInfo.VecMin.x |= int(cube.Pos.x) << 6;
|
||||||
aabbInfo.VecMin.y |= int(cube.Left.y) << 6;
|
aabbInfo.VecMin.y |= int(cube.Pos.y) << 6;
|
||||||
aabbInfo.VecMin.z |= int(cube.Left.z) << 6;
|
aabbInfo.VecMin.z |= int(cube.Pos.z) << 6;
|
||||||
|
|
||||||
aabbInfo.VecMax.x |= int(cube.Right.x) << 6;
|
aabbInfo.VecMax.x |= int(cube.Pos.x+cube.Size.x+1) << 6;
|
||||||
aabbInfo.VecMax.y |= int(cube.Right.y) << 6;
|
aabbInfo.VecMax.y |= int(cube.Pos.y+cube.Size.y+1) << 6;
|
||||||
aabbInfo.VecMax.z |= int(cube.Right.z) << 6;
|
aabbInfo.VecMax.z |= int(cube.Pos.z+cube.Size.z+1) << 6;
|
||||||
|
|
||||||
if(aabb.isCollideWith(aabbInfo)) {
|
if(aabb.isCollideWith(aabbInfo)) {
|
||||||
aabbInfo = {
|
aabbInfo = {
|
||||||
@@ -149,8 +149,8 @@ public:
|
|||||||
Возвращает список не загруженных регионов, на которые соответственно игрока не получилось подписать
|
Возвращает список не загруженных регионов, на которые соответственно игрока не получилось подписать
|
||||||
При подписи происходит отправка всех чанков и сущностей региона
|
При подписи происходит отправка всех чанков и сущностей региона
|
||||||
*/
|
*/
|
||||||
std::vector<Pos::GlobalRegion> onCEC_RegionsEnter(ContentEventController *cec, const std::vector<Pos::GlobalRegion> &enter);
|
std::vector<Pos::GlobalRegion> onCEC_RegionsEnter(ContentEventController *cec, const std::vector<Pos::GlobalRegion> &enter, WorldId_t wId);
|
||||||
void onCEC_RegionsLost(ContentEventController *cec, const std::vector<Pos::GlobalRegion> &lost);
|
void onCEC_RegionsLost(ContentEventController *cec, const std::vector<Pos::GlobalRegion>& lost);
|
||||||
struct SaveUnloadInfo {
|
struct SaveUnloadInfo {
|
||||||
std::vector<Pos::GlobalRegion> ToUnload;
|
std::vector<Pos::GlobalRegion> ToUnload;
|
||||||
std::vector<std::pair<Pos::GlobalRegion, SB_Region_In>> ToSave;
|
std::vector<std::pair<Pos::GlobalRegion, SB_Region_In>> ToSave;
|
||||||
|
|||||||
Reference in New Issue
Block a user