From cfec93957d12654bf67601e9caed8e16aeb10538 Mon Sep 17 00:00:00 2001 From: DrSocalkwe3n Date: Mon, 14 Jul 2025 09:50:26 +0600 Subject: [PATCH] * --- Src/Client/Abstract.hpp | 25 +- Src/Client/ServerSession.cpp | 134 +++++- Src/Client/Vulkan/Abstract.hpp | 2 +- Src/Client/Vulkan/VertexPool.hpp | 4 +- Src/Client/Vulkan/Vulkan.cpp | 2 +- Src/Client/Vulkan/VulkanRenderSession.cpp | 112 +++-- Src/Client/Vulkan/VulkanRenderSession.hpp | 5 +- Src/Common/Abstract.hpp | 22 + Src/Common/Packets.hpp | 21 +- Src/Server/Abstract.hpp | 14 +- Src/Server/BinaryResourceManager.cpp | 18 +- Src/Server/BinaryResourceManager.hpp | 56 ++- Src/Server/ContentEventController.cpp | 7 +- Src/Server/GameServer.cpp | 232 +++++++++- Src/Server/GameServer.hpp | 20 +- Src/Server/NodeDefManager.hpp | 13 +- Src/Server/RemoteClient.cpp | 366 ++++++++-------- Src/Server/RemoteClient.hpp | 105 ++--- Src/sha2.hpp | 499 ++++++++++++++++++++++ assets/shaders/chunk/node_opaque.frag | 2 + assets/shaders/chunk/node_opaque.frag.bin | Bin 6240 -> 6376 bytes assets/textures/frame.png | Bin 0 -> 138 bytes 22 files changed, 1266 insertions(+), 393 deletions(-) create mode 100644 Src/sha2.hpp create mode 100644 assets/textures/frame.png diff --git a/Src/Client/Abstract.hpp b/Src/Client/Abstract.hpp index 97b9f22..3063f76 100644 --- a/Src/Client/Abstract.hpp +++ b/Src/Client/Abstract.hpp @@ -64,10 +64,9 @@ public: class IRenderSession { public: // Подгрузка двоичных ресурсов - virtual void onBinaryResourceAdd(std::unordered_map>) = 0; - virtual void onBinaryResourceLost(std::unordered_map>) = 0; + virtual void onBinaryResourceAdd(std::vector) = 0; - virtual void onContentDefinesAdd(std::unordered_map>) = 0; + virtual void onContentDefinesAdd(std::unordered_map>) = 0; virtual void onContentDefinesLost(std::unordered_map>) = 0; // Сообщаем об изменившихся чанках @@ -90,10 +89,6 @@ struct DefVoxelInfo { }; -struct DefNodeInfo { - -}; - struct DefWorldInfo { }; @@ -142,10 +137,24 @@ struct DefItemInfo { /* Интерфейс обработчика сессии с сервером */ class IServerSession { + struct ArrayHasher { + std::size_t operator()(const Hash_t& a) const { + std::size_t h = 0; + for (auto e : a) + h ^= std::hash{}(e) + 0x9e3779b9 + (h << 6) + (h >> 2); + + return h; + } + }; + public: + struct { + std::unordered_map Resources; + } Binary; + struct { std::unordered_map DefVoxel; - std::unordered_map DefNode; + std::unordered_map DefNode; std::unordered_map DefWorld; std::unordered_map DefPortal; std::unordered_map DefEntity; diff --git a/Src/Client/ServerSession.cpp b/Src/Client/ServerSession.cpp index 9087e2a..d459825 100644 --- a/Src/Client/ServerSession.cpp +++ b/Src/Client/ServerSession.cpp @@ -3,6 +3,7 @@ #include "Common/Abstract.hpp" #include "Common/Net.hpp" #include "TOSLib.hpp" +#include "glm/ext/quaternion_geometric.hpp" #include #include #include @@ -24,8 +25,8 @@ struct PP_Content_ChunkVoxels : public ParsedPacket { Pos::GlobalChunk Pos; std::vector Cubes; - PP_Content_ChunkVoxels(ToClient::L1 l1, uint8_t l2, WorldId_t id, Pos::GlobalChunk pos, std::vector &&cubes) - : ParsedPacket(l1, l2), Id(id), Pos(pos), Cubes(std::move(cubes)) + PP_Content_ChunkVoxels(WorldId_t id, Pos::GlobalChunk pos, std::vector &&cubes) + : ParsedPacket(ToClient::L1::Content, (uint8_t) ToClient::L2Content::ChunkVoxels), Id(id), Pos(pos), Cubes(std::move(cubes)) {} }; @@ -34,8 +35,8 @@ struct PP_Content_ChunkNodes : public ParsedPacket { Pos::GlobalChunk Pos; std::array Nodes; - PP_Content_ChunkNodes(ToClient::L1 l1, uint8_t l2, WorldId_t id, Pos::GlobalChunk pos) - : ParsedPacket(l1, l2), Id(id), Pos(pos) + PP_Content_ChunkNodes(WorldId_t id, Pos::GlobalChunk pos) + : ParsedPacket(ToClient::L1::Content, (uint8_t) ToClient::L2Content::ChunkNodes), Id(id), Pos(pos) { } }; @@ -44,8 +45,36 @@ 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) + PP_Content_RegionRemove(WorldId_t id, Pos::GlobalRegion pos) + : ParsedPacket(ToClient::L1::Content, (uint8_t) ToClient::L2Content::RemoveRegion), Id(id), Pos(pos) + {} +}; + +struct PP_Definition_FreeNode : public ParsedPacket { + DefNodeId_t Id; + + PP_Definition_FreeNode(DefNodeId_t id) + : ParsedPacket(ToClient::L1::Definition, (uint8_t) ToClient::L2Definition::Node), + Id(id) + {} +}; + +struct PP_Definition_Node : public ParsedPacket { + DefNodeId_t Id; + DefNode_t Def; + + PP_Definition_Node(DefNodeId_t id, DefNode_t def) + : ParsedPacket(ToClient::L1::Definition, (uint8_t) ToClient::L2Definition::Node), + Id(id), Def(def) + {} +}; + +struct PP_Resource_InitResSend : public ParsedPacket { + Hash_t Hash; + BinaryResource Resource; + + PP_Resource_InitResSend(Hash_t hash, BinaryResource res) + : ParsedPacket(ToClient::L1::Resource, (uint8_t) ToClient::L2Resource::InitResSend), Hash(hash), Resource(res) {} }; @@ -283,11 +312,32 @@ void ServerSession::atFreeDrawTime(GlobalTime gTime, float dTime) { { std::unordered_map, std::unordered_set>> changeOrAddList_removeList; + std::unordered_map> onContentDefinesAdd; + std::unordered_map> onContentDefinesLost; // Пакеты ParsedPacket *pack; while(NetInputPackets.pop(pack)) { - if(pack->Level1 == ToClient::L1::Content) { + if(pack->Level1 == ToClient::L1::Definition) { + ToClient::L2Resource l2 = ToClient::L2Resource(pack->Level2); + if(l2 == ToClient::L2Resource::InitResSend) { + PP_Resource_InitResSend &p = *dynamic_cast(pack); + + } + + } else if(pack->Level1 == ToClient::L1::Definition) { + ToClient::L2Definition l2 = ToClient::L2Definition(pack->Level2); + if(l2 == ToClient::L2Definition::Node) { + PP_Definition_Node &p = *dynamic_cast(pack); + Registry.DefNode[p.Id] = p.Def; + onContentDefinesAdd[EnumDefContent::Node].push_back(p.Id); + } else if(l2 == ToClient::L2Definition::FreeNode) { + PP_Definition_FreeNode &p = *dynamic_cast(pack); + onContentDefinesLost[EnumDefContent::Node].push_back(p.Id); + } + + + } else if(pack->Level1 == ToClient::L1::Content) { ToClient::L2Content l2 = ToClient::L2Content(pack->Level2); if(l2 == ToClient::L2Content::ChunkVoxels) { PP_Content_ChunkVoxels &p = *dynamic_cast(pack); @@ -339,6 +389,14 @@ void ServerSession::atFreeDrawTime(GlobalTime gTime, float dTime) { RS->onChunksChange(pair.first, std::get<0>(pair.second), std::get<1>(pair.second)); } + + if(!onContentDefinesAdd.empty()) { + RS->onContentDefinesAdd(std::move(onContentDefinesAdd)); + } + + if(!onContentDefinesLost.empty()) { + RS->onContentDefinesLost(std::move(onContentDefinesLost)); + } } } @@ -348,7 +406,10 @@ void ServerSession::atFreeDrawTime(GlobalTime gTime, float dTime) { glm::quat quat = glm::angleAxis(PYR.x-deltaTime*PYR_Offset.x, glm::vec3(1.f, 0.f, 0.f)) - * glm::angleAxis(PYR.y-deltaTime*PYR_Offset.y, glm::vec3(0.f, -1.f, 0.f)); + * + glm::angleAxis(PYR.y-deltaTime*PYR_Offset.y, glm::vec3(0.f, -1.f, 0.f)); + + quat = glm::normalize(quat); if(RS) RS->setCameraPos(0, Pos, quat); @@ -360,7 +421,7 @@ void ServerSession::atFreeDrawTime(GlobalTime gTime, float dTime) { LastSendPYR_POS = gTime; Net::Packet packet; ToServer::PacketQuat q; - q.fromQuat(quat); + q.fromQuat(glm::inverse(quat)); packet << (uint8_t) ToServer::L1::System << (uint8_t) ToServer::L2System::Test_CAM_PYR_POS @@ -452,7 +513,7 @@ coro<> ServerSession::rP_Resource(Net::AsyncSocket &sock) { switch((ToClient::L2Resource) second) { case ToClient::L2Resource::Texture: - + co_return; case ToClient::L2Resource::FreeTexture: @@ -470,13 +531,27 @@ coro<> ServerSession::rP_Resource(Net::AsyncSocket &sock) { co_return; case ToClient::L2Resource::InitResSend: + { + uint32_t size = co_await sock.read(); + Hash_t hash; + co_await sock.read((std::byte*) hash.data(), hash.size()); + + uint32_t chunkSize = co_await sock.read(); + std::u8string data(size, '\0'); + + co_await sock.read((std::byte*) data.data(), data.size()); + + PP_Resource_InitResSend *packet = new PP_Resource_InitResSend( + hash, + std::make_shared(std::move(data)) + ); + + while(!NetInputPackets.push(packet)); co_return; + } case ToClient::L2Resource::ChunkSend: - co_return; - case ToClient::L2Resource::SendCanceled: - co_return; default: protocolError(); @@ -508,11 +583,38 @@ coro<> ServerSession::rP_Definition(Net::AsyncSocket &sock) { co_return; } case ToClient::L2Definition::Node: + { + DefNodeId_t id; + DefNode_t def; + id = co_await sock.read(); + def.DrawType = (DefNode_t::EnumDrawType) co_await sock.read(); + for(int iter = 0; iter < 6; iter++) { + auto &pl = def.Texs[iter].Pipeline; + pl.resize(co_await sock.read()); + co_await sock.read((std::byte*) pl.data(), pl.size()); + } + + PP_Definition_Node *packet = new PP_Definition_Node( + id, + def + ); + + while(!NetInputPackets.push(packet)); co_return; + } case ToClient::L2Definition::FreeNode: + { + DefNodeId_t id = co_await sock.read(); + PP_Definition_FreeNode *packet = new PP_Definition_FreeNode( + id + ); + + while(!NetInputPackets.push(packet)); + co_return; + } case ToClient::L2Definition::Portal: co_return; @@ -564,8 +666,6 @@ coro<> ServerSession::rP_Content(Net::AsyncSocket &sock) { co_await sock.read((std::byte*) compressed.data(), compressedSize); PP_Content_ChunkVoxels *packet = new PP_Content_ChunkVoxels( - ToClient::L1::Content, - (uint8_t) ToClient::L2Content::ChunkVoxels, wcId, pos, unCompressVoxels(compressed) // TODO: вынести в отдельный поток @@ -588,8 +688,6 @@ coro<> ServerSession::rP_Content(Net::AsyncSocket &sock) { co_await sock.read((std::byte*) compressed.data(), compressedSize); PP_Content_ChunkNodes *packet = new PP_Content_ChunkNodes( - ToClient::L1::Content, - (uint8_t) ToClient::L2Content::ChunkNodes, wcId, pos ); @@ -609,8 +707,6 @@ coro<> ServerSession::rP_Content(Net::AsyncSocket &sock) { pos.unpack(co_await sock.read()); PP_Content_RegionRemove *packet = new PP_Content_RegionRemove( - ToClient::L1::Content, - (uint8_t) ToClient::L2Content::RemoveRegion, wcId, pos ); diff --git a/Src/Client/Vulkan/Abstract.hpp b/Src/Client/Vulkan/Abstract.hpp index a598787..f51e4c3 100644 --- a/Src/Client/Vulkan/Abstract.hpp +++ b/Src/Client/Vulkan/Abstract.hpp @@ -32,7 +32,7 @@ struct VoxelVertexPoint { struct NodeVertexStatic { uint32_t - FX : 9, FY : 9, FZ : 9, // Позиция 15 -120 ~ 240 360 15 / 16 + FX : 9, FY : 9, FZ : 9, // Позиция -15 -120 ~ 240 360 +15 / 16 N1 : 4, // Не занято LS : 1, // Масштаб карты освещения (1м/16 или 1м) Tex : 18, // Текстура diff --git a/Src/Client/Vulkan/VertexPool.hpp b/Src/Client/Vulkan/VertexPool.hpp index 149f295..120e6e9 100644 --- a/Src/Client/Vulkan/VertexPool.hpp +++ b/Src/Client/Vulkan/VertexPool.hpp @@ -304,9 +304,9 @@ public: WritePos = 0; while(!postponed.empty()) { - Task& task = TasksWait.front(); + Task& task = postponed.front(); pushData(std::move(task.Data), task.PoolId, task.BlockId); - TasksWait.pop(); + postponed.pop(); } } }; diff --git a/Src/Client/Vulkan/Vulkan.cpp b/Src/Client/Vulkan/Vulkan.cpp index b33e204..03524b0 100644 --- a/Src/Client/Vulkan/Vulkan.cpp +++ b/Src/Client/Vulkan/Vulkan.cpp @@ -2695,7 +2695,7 @@ Buffer& Buffer::operator=(Buffer &&obj) { std::swap(Memory, obj.Memory); std::swap(Size, obj.Size); obj.Instance = nullptr; - + return *this; } diff --git a/Src/Client/Vulkan/VulkanRenderSession.cpp b/Src/Client/Vulkan/VulkanRenderSession.cpp index 070b7b7..1e2dbda 100644 --- a/Src/Client/Vulkan/VulkanRenderSession.cpp +++ b/Src/Client/Vulkan/VulkanRenderSession.cpp @@ -1,11 +1,13 @@ #include "VulkanRenderSession.hpp" #include "Client/Abstract.hpp" +#include "Client/Vulkan/Abstract.hpp" #include "Client/Vulkan/Vulkan.hpp" #include "Common/Abstract.hpp" #include "TOSLib.hpp" #include "assets.hpp" #include "glm/ext/matrix_transform.hpp" #include "glm/ext/scalar_constants.hpp" +#include "glm/matrix.hpp" #include "glm/trigonometric.hpp" #include #include @@ -211,7 +213,8 @@ void VulkanRenderSession::init(Vulkan *instance) { "willow_wood.png", "tropical_rainforest_wood.png", "xnether_blue_wood.png", - "xnether_purple_wood.png" + "xnether_purple_wood.png", + "frame.png" }) { ByteBuffer image = VK::loadPNG(getResource(std::string("textures/") + path)->makeStream().Stream, width, height, hasAlpha); uint16_t texId = VKCTX->MainTest.atlasAddTexture(width, height); @@ -229,35 +232,66 @@ void VulkanRenderSession::init(Vulkan *instance) { { NodeVertexStatic *array = (NodeVertexStatic*) VKCTX->TestQuad.mapMemory(); - array[0] = {135, 135, 135-64, 0, 0, 0, 0, 0, 0}; - array[1] = {135+16, 135, 135-64, 0, 0, 0, 0, 65535, 0}; - array[2] = {135+16, 135+16, 135-64, 0, 0, 0, 0, 65535, 65535}; - array[3] = {135, 135, 135-64, 0, 0, 0, 0, 0, 0}; - array[4] = {135+16, 135+16, 135-64, 0, 0, 0, 0, 65535, 65535}; - array[5] = {135, 135+16, 135-64, 0, 0, 0, 0, 0, 65535}; + array[0] = {135, 135, 135, 0, 0, 0, 0, 65535, 0}; + array[1] = {135, 135+16, 135, 0, 0, 0, 0, 0, 65535}; + array[2] = {135+16, 135+16, 135, 0, 0, 0, 0, 0, 65535}; + array[3] = {135, 135, 135, 0, 0, 0, 0, 65535, 0}; + array[4] = {135+16, 135+16, 135, 0, 0, 0, 0, 0, 65535}; + array[5] = {135+16, 135, 135, 0, 0, 0, 0, 0, 0}; + array[6] = {135, 135, 135+16, 0, 0, 0, 0, 0, 0}; + array[7] = {135+16, 135, 135+16, 0, 0, 0, 0, 65535, 0}; + array[8] = {135+16, 135+16, 135+16, 0, 0, 0, 0, 65535, 65535}; + array[9] = {135, 135, 135+16, 0, 0, 0, 0, 0, 0}; + array[10] = {135+16, 135+16, 135+16, 0, 0, 0, 0, 65535, 65535}; + array[11] = {135, 135+16, 135+16, 0, 0, 0, 0, 0, 65535}; - array[6] = {135, 135, 135-64-16, 0, 0, 0, 0, 0, 0}; - array[7] = {135, 135, 135-64, 0, 0, 0, 0, 65535, 0}; - array[8] = {135, 135+16, 135-64, 0, 0, 0, 0, 65535, 65535}; - array[9] = {135, 135, 135-64-16, 0, 0, 0, 0, 0, 0}; - array[10] = {135, 135+16, 135-64, 0, 0, 0, 0, 65535, 65535}; - array[11] = {135, 135+16, 135-64-16, 0, 0, 0, 0, 0, 65535}; + array[12] = {135, 135, 135, 0, 0, 0, 0, 0, 0}; + array[13] = {135, 135, 135+16, 0, 0, 0, 0, 65535, 0}; + array[14] = {135, 135+16, 135+16, 0, 0, 0, 0, 65535, 65535}; + array[15] = {135, 135, 135, 0, 0, 0, 0, 0, 0}; + array[16] = {135, 135+16, 135+16, 0, 0, 0, 0, 65535, 65535}; + array[17] = {135, 135+16, 135, 0, 0, 0, 0, 0, 65535}; + array[18] = {135+16, 135, 135+16, 0, 0, 0, 0, 0, 0}; + array[19] = {135+16, 135, 135, 0, 0, 0, 0, 65535, 0}; + array[20] = {135+16, 135+16, 135, 0, 0, 0, 0, 65535, 65535}; + array[21] = {135+16, 135, 135+16, 0, 0, 0, 0, 0, 0}; + array[22] = {135+16, 135+16, 135, 0, 0, 0, 0, 65535, 65535}; + array[23] = {135+16, 135+16, 135+16, 0, 0, 0, 0, 0, 65535}; - array[12] = {135, 135, 135-64, 0, 0, 0, 0, 0, 0}; - array[13] = {135+16, 135, 135-64, 0, 0, 0, 0, 65535, 0}; - array[14] = {135+16, 135, 135-64-16, 0, 0, 0, 0, 65535, 65535}; - array[15] = {135, 135, 135-64, 0, 0, 0, 0, 0, 0}; - array[16] = {135+16, 135, 135-64-16, 0, 0, 0, 0, 65535, 65535}; - array[17] = {135, 135, 135-64-16, 0, 0, 0, 0, 0, 65535}; + array[24] = {135, 135, 135, 0, 0, 0, 0, 0, 0}; + array[25] = {135+16, 135, 135, 0, 0, 0, 0, 65535, 0}; + array[26] = {135+16, 135, 135+16, 0, 0, 0, 0, 65535, 65535}; + array[27] = {135, 135, 135, 0, 0, 0, 0, 0, 0}; + array[28] = {135+16, 135, 135+16, 0, 0, 0, 0, 65535, 65535}; + array[29] = {135, 135, 135+16, 0, 0, 0, 0, 0, 65535}; - for(int iter = 0; iter < 18; iter++) { - array[18+iter] = array[iter]; - array[18+iter].FZ -= 32; + array[30] = {135, 135+16, 135+16, 0, 0, 0, 0, 0, 0}; + array[31] = {135+16, 135+16, 135+16, 0, 0, 0, 0, 65535, 0}; + array[32] = {135+16, 135+16, 135, 0, 0, 0, 0, 65535, 65535}; + array[33] = {135, 135+16, 135+16, 0, 0, 0, 0, 0, 0}; + array[34] = {135+16, 135+16, 135, 0, 0, 0, 0, 65535, 65535}; + array[35] = {135, 135+16, 135, 0, 0, 0, 0, 0, 65535}; + + for(int iter = 0; iter < 36; iter++) { + array[iter].Tex = 6; + if(array[iter].FX == 135) + array[iter].FX--; + else + array[iter].FX++; + + if(array[iter].FY == 135) + array[iter].FY--; + else + array[iter].FY++; + + if(array[iter].FZ == 135) + array[iter].FZ--; + else + array[iter].FZ++; } - VKCTX->TestQuad.unMapMemory(); } @@ -627,15 +661,11 @@ void VulkanRenderSession::init(Vulkan *instance) { } } -void VulkanRenderSession::onBinaryResourceAdd(std::unordered_map>) { +void VulkanRenderSession::onBinaryResourceAdd(std::vector) { } -void VulkanRenderSession::onBinaryResourceLost(std::unordered_map>) { - -} - -void VulkanRenderSession::onContentDefinesAdd(std::unordered_map>) { +void VulkanRenderSession::onContentDefinesAdd(std::unordered_map>) { } @@ -643,7 +673,6 @@ void VulkanRenderSession::onContentDefinesLost(std::unordered_map& changeOrAddList, const std::unordered_set& remove) { auto &table = External.ChunkVoxelMesh[worldId]; @@ -668,7 +697,6 @@ void VulkanRenderSession::onChunksChange(WorldId_t worldId, const std::unordered if(vertexs2.empty()) { VKCTX->VertexPool_Nodes.dropVertexs(std::get<1>(buffers)); } else { - changed++; auto &nodes = std::get<1>(buffers); VKCTX->VertexPool_Nodes.relocate(nodes, std::move(vertexs2)); } @@ -678,8 +706,6 @@ void VulkanRenderSession::onChunksChange(WorldId_t worldId, const std::unordered if(iter != table.end()) table.erase(iter); } - - TOS::Logger("Vul").debug() << "Обработано " << changed; } for(Pos::GlobalRegion pos : remove) { @@ -717,7 +743,7 @@ void VulkanRenderSession::beforeDraw() { void VulkanRenderSession::drawWorld(GlobalTime gTime, float dTime, VkCommandBuffer drawCmd) { { X64Offset = Pos & ~((1 << Pos::Object_t::BS_Bit << 4 << 2)-1); - X64Offset_f = glm::vec3(X64Offset) / float(Pos::Object_t::BS); + X64Offset_f = glm::vec3(X64Offset >> Pos::Object_t::BS_Bit); X64Delta = glm::vec3(Pos-X64Offset) / float(Pos::Object_t::BS); } @@ -817,7 +843,21 @@ void VulkanRenderSession::drawWorld(GlobalTime gTime, float dTime, VkCommandBuff Delta += dTime; PCO.Model = glm::mat4(1); - PCO.Model = glm::translate(PCO.Model, -X64Offset_f); + //PCO.Model = glm::translate(PCO.Model, -X64Offset_f); + // glm::quat quat = glm::inverse(Quat); + + { + + // auto *srv = (class ServerSession*) ServerSession; + + glm::vec4 v = glm::mat4(glm::inverse(Quat))*glm::vec4(0, 0, -6, 1); + + Pos::GlobalNode pos = (Pos::GlobalNode) (glm::vec3) v; + + pos += (Pos-X64Offset) >> Pos::Object_t::BS_Bit; + PCO.Model = glm::translate(PCO.Model, glm::vec3(pos)); + } + { glm::mat4 proj = glm::perspective(glm::radians(75.f), float(VkInst->Screen.Width)/float(VkInst->Screen.Height), 0.5, std::pow(2, 17)); @@ -832,7 +872,7 @@ void VulkanRenderSession::drawWorld(GlobalTime gTime, float dTime, VkCommandBuff // Смещаем мир относительно позиции игрока, чтобы игрок в пространстве рендера оказался в нулевых координатах view = glm::translate(view, -X64Delta); // Поворачиваем мир обратно взгляду игрока, чтобы его взгляд стал по направлению оси -z - view = glm::mat4(-Quat)*view; + view = glm::mat4(Quat)*view; // Сначала применяется матрица вида, потом проекции PCO.ProjView = proj*view; diff --git a/Src/Client/Vulkan/VulkanRenderSession.hpp b/Src/Client/Vulkan/VulkanRenderSession.hpp index c34088a..cf26769 100644 --- a/Src/Client/Vulkan/VulkanRenderSession.hpp +++ b/Src/Client/Vulkan/VulkanRenderSession.hpp @@ -141,9 +141,8 @@ public: assert(serverSession); } - virtual void onBinaryResourceAdd(std::unordered_map>) override; - virtual void onBinaryResourceLost(std::unordered_map>) override; - virtual void onContentDefinesAdd(std::unordered_map>) override; + virtual void onBinaryResourceAdd(std::vector) override; + virtual void onContentDefinesAdd(std::unordered_map>) override; virtual void onContentDefinesLost(std::unordered_map>) override; virtual void onChunksChange(WorldId_t worldId, const std::unordered_set& changeOrAddList, const std::unordered_set& remove) override; virtual void setCameraPos(WorldId_t worldId, Pos::Object pos, glm::quat quat) override; diff --git a/Src/Common/Abstract.hpp b/Src/Common/Abstract.hpp index ec53dcd..b76c0f5 100644 --- a/Src/Common/Abstract.hpp +++ b/Src/Common/Abstract.hpp @@ -485,6 +485,28 @@ void unCompressNodes(const std::u8string& compressed, Node* ptr); std::u8string compressLinear(const std::u8string& data); std::u8string unCompressLinear(const std::u8string& data); +enum struct TexturePipelineCMD : uint8_t { + Texture, // Указание текстуры + Combine, // Комбинирование + +}; + +struct TexturePipeline { + std::vector BinTextures; + std::u8string Pipeline; +}; + +struct DefNode_t { + enum struct EnumDrawType : uint8_t { + NoDraw, // Не рисуется + Simple, // Простая нода с текстурами на каждой стороне + } DrawType = EnumDrawType::Simple; + + TexturePipeline Texs[6]; +}; + +using Hash_t = std::array; + } diff --git a/Src/Common/Packets.hpp b/Src/Common/Packets.hpp index 64d65b0..543920a 100644 --- a/Src/Common/Packets.hpp +++ b/Src/Common/Packets.hpp @@ -32,14 +32,14 @@ struct PacketQuat { value |= uint64_t(w & 0x3ff) << 30; for(int iter = 0; iter < 5; iter++) - Data[iter] = (value >> (iter * 8)) & 0xff; + Data[iter] = (value >> (iter*8)) & 0xff; } glm::quat toQuat() const { uint64_t value = 0; for(int iter = 0; iter < 5; iter++) - value |= (Data[iter] >> (iter*10)) & 0x3ff; + value |= uint64_t(Data[iter]) << (iter*8); uint16_t x = value & 0x3ff, @@ -52,7 +52,7 @@ struct PacketQuat { float fz = (float(z)/0x3ff)*2-1; float fw = (float(w)/0x3ff)*2-1; - return glm::quat(fx, fy, fz, fw); + return glm::quat(fw, fx, fy, fz); } }; @@ -144,19 +144,10 @@ enum struct L2System : uint8_t { }; enum struct L2Resource : uint8_t { - Texture, - FreeTexture, - Animation, - FreeAnimation, - Sound, - FreeSound, - Model, - FreeModel, - Font, - FreeFont, + Bind, // Привязка идентификаторов ресурсов к хешам + Lost, InitResSend = 253, - ChunkSend, - SendCanceled + ChunkSend }; enum struct L2Definition : uint8_t { diff --git a/Src/Server/Abstract.hpp b/Src/Server/Abstract.hpp index 954da48..b5cb505 100644 --- a/Src/Server/Abstract.hpp +++ b/Src/Server/Abstract.hpp @@ -3,19 +3,13 @@ #include #include #include -#include +#include #include #include namespace LV::Server { -struct TexturePipeline { - std::vector BinTextures; - std::u8string Pipeline; -}; - - // В одном регионе может быть максимум 2^16 сущностей. Клиенту адресуются сущности в формате <мир>+<позиция региона>+ // И если сущность перешла из одного региона в другой, идентификатор сущности на стороне клиента сохраняется using RegionEntityId_t = uint16_t; @@ -39,15 +33,13 @@ using DefGeneratorId_t = ResourceId_t; */ struct ResourceFile { - using Hash_t = boost::uuids::detail::sha1::digest_type; + using Hash_t = sha2::sha256_hash; // boost::uuids::detail::sha1::digest_type; Hash_t Hash; std::vector Data; void calcHash() { - boost::uuids::detail::sha1 hash; - hash.process_bytes(Data.data(), Data.size()); - hash.get_digest(Hash); + Hash = sha2::sha256((const uint8_t*) Data.data(), Data.size()); } }; diff --git a/Src/Server/BinaryResourceManager.cpp b/Src/Server/BinaryResourceManager.cpp index 0e86789..2d3808b 100644 --- a/Src/Server/BinaryResourceManager.cpp +++ b/Src/Server/BinaryResourceManager.cpp @@ -11,9 +11,8 @@ namespace LV::Server { -BinaryResourceManager::BinaryResourceManager(asio::io_context &ioc, - std::shared_ptr zeroResource) - : AsyncObject(ioc), ZeroResource(std::move(zeroResource)) +BinaryResourceManager::BinaryResourceManager(asio::io_context &ioc) + : AsyncObject(ioc) { } @@ -43,15 +42,13 @@ void BinaryResourceManager::update(float dtime) { auto lock = UpdatedResources.lock_write(); for(ResourceId_t resId : *lock) { - std::shared_ptr &objRes = PreparedInformation[resId]; - if(objRes) + auto iterPI = PreparedInformation.find(resId); + if(iterPI != PreparedInformation.end()) continue; - auto iter = ResourcesInfo.find(resId); - if(iter == ResourcesInfo.end()) { - objRes = ZeroResource; - } else { - objRes = iter->second->Loaded; + auto iterRI = ResourcesInfo.find(resId); + if(iterRI != ResourcesInfo.end()) { + PreparedInformation[resId] = iterRI->second->Loaded; } } } @@ -81,7 +78,6 @@ ResourceId_t BinaryResourceManager::getResource_Assets(std::string path) { std::shared_ptr &res = ResourcesInfo[resId]; if(!res) { res = std::make_shared(); - res->Loaded = ZeroResource; auto iter = Domains.find("domain"); if(iter == Domains.end()) { diff --git a/Src/Server/BinaryResourceManager.hpp b/Src/Server/BinaryResourceManager.hpp index c38b532..94f43cc 100644 --- a/Src/Server/BinaryResourceManager.hpp +++ b/Src/Server/BinaryResourceManager.hpp @@ -1,5 +1,6 @@ #pragma once +#include "Common/Abstract.hpp" #include "Common/Lockable.hpp" #include "Server/RemoteClient.hpp" #include @@ -10,12 +11,24 @@ #include #include #include "Abstract.hpp" +#include "TOSLib.hpp" namespace LV::Server { namespace fs = std::filesystem; +/* + Может прийти множество запросов на один не загруженный ресурс + + Чтение происходит отдельным потоком, переконвертацию пока предлагаю в realtime. + Хэш вычисляется после чтения и может быть иным чем при прошлом чтении (ресурс изменили наживую) + тогда обычным оповещениям клиентам дойдёт новая версия + + Подержать какое-то время ресурс в памяти + +*/ + class BinaryResourceManager : public AsyncObject { public: @@ -26,49 +39,54 @@ private: // Источник std::string Uri; bool IsLoading = false; + std::string LastError; }; struct UriParse { std::string Orig, Protocol, Path; }; + + // Последовательная регистрация ресурсов + BinTextureId_t NextIdTexture = 0, NextIdAnimation = 0, NextIdModel = 0, + NextIdSound = 0, NextIdFont = 0; + + // Ресурсы - кешированные в оперативную память или в процессе загрузки + std::map> - // Нулевой ресурс - std::shared_ptr ZeroResource; - // Домены поиска ресурсов - std::unordered_map Domains; // Известные ресурсы std::map KnownResource; std::map> ResourcesInfo; - // Последовательная регистрация ресурсов - ResourceId_t NextId = 1; - // Накапливаем идентификаторы готовых ресурсов - Lockable> UpdatedResources; + // Сюда + TOS::SpinlockObject> UpdatedResources; // Подготовленая таблица оповещения об изменениях ресурсов // Должна забираться сервером и отчищаться std::unordered_map> PreparedInformation; public: // Если ресурс будет обновлён или загружен будет вызвано onResourceUpdate - BinaryResourceManager(asio::io_context &ioc, std::shared_ptr zeroResource); + BinaryResourceManager(asio::io_context &ioc); virtual ~BinaryResourceManager(); // Перепроверка изменений ресурсов - void recheckResources(); - // Домен мода -> путь к папке с ресурсами - void setAssetsDomain(std::unordered_map &&domains) { Domains = std::move(domains); } - // Идентификатор ресурса по его uri - ResourceId_t mapUriToId(const std::string &uri); + void recheckResources(std::vector assets /* Пути до активных папок assets */); + // Выдаёт или назначает идентификатор для ресурса + BinTextureId_t getTexture (const std::string& uri); + BinAnimationId_t getAnimation(const std::string& uri); + BinModelId_t getModel (const std::string& uri); + BinSoundId_t getSound (const std::string& uri); + BinFontId_t getFont (const std::string& uri); + // Запросить ресурсы через onResourceUpdate - void needResourceResponse(const std::vector &resources); - // Серверный такт - void update(float dtime); - bool hasPreparedInformation() { return !PreparedInformation.empty(); } - + void needResourceResponse(const ResourceRequest &&resources); + // Получение обновлений или оповещений ресурсов std::unordered_map> takePreparedInformation() { return std::move(PreparedInformation); } + // Серверный такт + void update(float dtime); + protected: UriParse parseUri(const std::string &uri); ResourceId_t getResource_Assets(std::string path); diff --git a/Src/Server/ContentEventController.cpp b/Src/Server/ContentEventController.cpp index a0ab52a..8684c08 100644 --- a/Src/Server/ContentEventController.cpp +++ b/Src/Server/ContentEventController.cpp @@ -3,6 +3,7 @@ #include "RemoteClient.hpp" #include "Server/Abstract.hpp" #include "World.hpp" +#include "glm/ext/quaternion_geometric.hpp" #include @@ -114,8 +115,10 @@ void ContentEventController::onUpdate() { uint8_t action = lock->front(); lock->pop(); - Pos::GlobalNode pos = (Pos::GlobalNode) (glm::vec3) (glm::mat4(Remote->CameraQuat.toQuat())*glm::vec4(0, 0, -1, 1)); - pos = Pos.ObjectPos >> Pos::Object_t::BS_Bit; + glm::quat q = Remote->CameraQuat.toQuat(); + glm::vec4 v = glm::mat4(q)*glm::vec4(0, 0, -6, 1); + Pos::GlobalNode pos = (Pos::GlobalNode) (glm::vec3) v; + pos += Pos.ObjectPos >> Pos::Object_t::BS_Bit; if(action == 0) { // Break diff --git a/Src/Server/GameServer.cpp b/Src/Server/GameServer.cpp index fecf24b..744fa39 100644 --- a/Src/Server/GameServer.cpp +++ b/Src/Server/GameServer.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -15,11 +17,17 @@ #include #include #include +#include #include "SaveBackends/Filesystem.hpp" #include "Server/SaveBackend.hpp" #include "Server/World.hpp" #include "TOSLib.hpp" #include "glm/gtc/noise.hpp" +#include + + +namespace js = boost::json; + namespace LV::Server { @@ -27,20 +35,21 @@ GameServer::GameServer(asio::io_context &ioc, fs::path worldPath) : AsyncObject(ioc), Content(ioc, nullptr, nullptr, nullptr, nullptr, nullptr) { + BackingChunkPressure.Threads.resize(4); + BackingNoiseGenerator.Threads.resize(4); + BackingAsyncLua.Threads.resize(4); + init(worldPath); - BackingChunkPressure.Threads.resize(4); BackingChunkPressure.Worlds = &Expanse.Worlds; for(size_t iter = 0; iter < BackingChunkPressure.Threads.size(); iter++) { BackingChunkPressure.Threads[iter] = std::thread(&BackingChunkPressure_t::run, &BackingChunkPressure, iter); } - BackingNoiseGenerator.Threads.resize(4); for(size_t iter = 0; iter < BackingNoiseGenerator.Threads.size(); iter++) { BackingNoiseGenerator.Threads[iter] = std::thread(&BackingNoiseGenerator_t::run, &BackingNoiseGenerator, iter); } - BackingAsyncLua.Threads.resize(4); for(size_t iter = 0; iter < BackingAsyncLua.Threads.size(); iter++) { BackingAsyncLua.Threads[iter] = std::thread(&BackingAsyncLua_t::run, &BackingAsyncLua, iter); } @@ -84,6 +93,9 @@ void GameServer::BackingChunkPressure_t::run(int id) { iteration = Iteration; } + assert(RunCollect > 0); + assert(RunCompress > 0); + // Сбор данных size_t pullSize = Threads.size(); size_t counter = 0; @@ -671,7 +683,7 @@ coro<> GameServer::pushSocketGameProtocol(tcp::socket socket, const std::string if(count > 262144) MAKE_ERROR("Не поддерживаемое количество ресурсов в кеше у клиента"); - std::vector clientCache; + std::vector clientCache; clientCache.resize(count); co_await Net::AsyncSocket::read(socket, (std::byte*) clientCache.data(), count*32); std::sort(clientCache.begin(), clientCache.end()); @@ -682,6 +694,149 @@ coro<> GameServer::pushSocketGameProtocol(tcp::socket socket, const std::string } } +TexturePipeline GameServer::buildTexturePipeline(const std::string& pl) { + /* + ^ объединение текстур, вторая поверх первой. + При наложении текстуры будут автоматически увеличины до размера + самой большой текстуры из участвующих. По умолчанию ближайший соседний + default:dirt.png^our_tech:machine.png + + Текстурные команды описываются в [] <- предоставляет текстуру. + Разделитель пробелом + default:dirt.png^[create 2 2 r ffaabbcc] + + Если перед командой будет использован $, то первым аргументом будет + предыдущая текстура, если это поддерживает команда + default:dirt.png$[resize 16 16] или [resize default:dirt.png 16 16] + + Группировка () + default:empty^(default:dirt.png^our_tech:machine.png) + */ + + std::map stbt; + std::unordered_set btis; + std::string alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + // Парсер группы. Возвращает позицию на которой закончил и скомпилированный код + std::move_only_function(size_t pos)> parse_obj; + std::move_only_function(size_t pos, std::u8string maybe)> parse_cmd; + + parse_cmd = [&](size_t pos, std::u8string maybe) -> std::tuple { + std::string cmd_name; + std::vector args; + size_t startPos = pos; + + for(pos++; pos < pl.size(); pos++) { + if(pl[pos] == ']') { + // Команда завершилась + // return {pos+1, cmd_name}; + } else if(pl[pos] == ' ') { + // Аргументы + // Здесь нужно получить либо кастомные значения, либо объект + auto [next_pos, subcmd] = parse_obj(pos+1); + args.push_back(subcmd); + if(next_pos == pl.size()) + MAKE_ERROR("Ожидался конец команды объявленной на " << startPos << ", наткнулись на конец потока"); + + pos = next_pos-1; + } else if(alpha.find(pl[pos]) == std::string::npos) { + MAKE_ERROR("Ошибка в имени команды"); + } else { + cmd_name += pl[pos]; + } + } + }; + + parse_obj = [&](size_t pos) -> std::pair { + std::u8string out; + + for(; pos < pl.size(); pos++) { + if(pl[pos] == '[') { + // Начало команды + if(!out.empty()) { + MAKE_ERROR("Отсутствует связь между текстурой и текущей командой " << pos); + } + + // out.push_back(TexturePipelineCMD::Combine); + auto [next_size, subcmd] = parse_cmd(pos+1, {}); + pos = next_size-1; + out = subcmd; + } else if(pl[pos] == '^') { + // Объединение + if(out.empty()) { + MAKE_ERROR("Отсутствует текстура для комбинирования " << pos); + + auto [next_pos, subcmd] = parse_obj(pos+1); + std::u8string cmd; + cmd.push_back(uint8_t(TexturePipelineCMD::Combine)); + cmd.insert(cmd.end(), out.begin(), out.end()); + cmd.insert(cmd.end(), subcmd.begin(), subcmd.end()); + + return {next_pos, cmd}; + } + } else if(pl[pos] == '$') { + // Готовый набор команд будет использован как аргумент + pos++; + if(pos >= pl.size() || pl[pos] != '[') + MAKE_ERROR("Ожидалось объявление команды " << pos); + auto [next_pos, subcmd] = parse_cmd(pos, out); + pos = next_pos-1; + out = subcmd; + } else if(pl[pos] == '(') { + if(!out.empty()) { + MAKE_ERROR("Начато определение группы после текстуры, вероятно пропущен знак объединения ^ " << pos); + } + + // Начало группы + auto [next_pos, subcmd] = parse_obj(pos+1); + pos = next_pos-1; + out = subcmd; + } else if(pl[pos] == ')') { + return {pos+1, out}; + } else { + // Это текстура, нужно её имя + if(!out.empty()) + MAKE_ERROR("Отсутствует связь между текстурой и текущим объявлением текстуры " << pos); + + out.push_back(uint8_t(TexturePipelineCMD::Texture)); + std::string texture_name; + for(; pos < pl.size(); pos++) { + if(pl[pos] == '^' || pl[pos] == ')' || pl[pos] == ']') + break; + else if(pl[pos] != '.' && pl[pos] != ':' && alpha.find_first_of(pl[pos]) != std::string::npos) + MAKE_ERROR("Недействительные символы в объявлении текстуры " << pos); + else + texture_name += pl[pos]; + } + + BinTextureId_t id = stbt[texture_name]; + btis.insert(id); + + for(int iter = 0; iter < 4; iter++) + out.push_back((id >> (iter * 8)) & 0xff); + + if(pos < pl.size()) + pos--; + } + } + + return {pos, out}; + }; + + auto [pos, cmd] = parse_obj(0); + + if(pos < pl.size()) { + MAKE_ERROR("Неожиданное продолжение " << pos); + } + + return {std::vector(btis.begin(), btis.end()), cmd}; +} + +std::string GameServer::deBuildTexturePipeline(const TexturePipeline& pipeline) { + return ""; +} + + void GameServer::init(fs::path worldPath) { Expanse.Worlds[0] = std::make_unique(0); @@ -778,6 +933,60 @@ void GameServer::run() { LOG.info() << "Сервер завершил работу"; } +std::vector GameServer::readModDataPath(const fs::path& modsDir) { + if(!fs::exists(modsDir)) + return {}; + + std::vector infos; + + try { + fs::directory_iterator begin(modsDir), end; + for(; begin != end; begin++) { + if(!begin->is_directory()) + continue; + + fs::path mod_conf = begin->path() / "mod.json"; + if(!fs::exists(mod_conf)) { + LOG.debug() << "Директория в папке с модами не содержит файл mod.json: " << begin->path().filename(); + continue; + } + + try { + std::ifstream fd(mod_conf); + js::object obj = js::parse(fd).as_object(); + + GameServer::ModInfo info; + + info.Id = obj.at("Id").as_string(); + info.Title = obj.contains("Title") ? obj["Title"].as_string() : ""; + info.Description = obj.contains("Description") ? obj["Description"].as_string() : ""; + + if(obj.contains("Dependencies")) { + js::array arr = obj["Dependencies"].as_array(); + for(auto& iter : arr) { + info.Dependencies.push_back((std::string) iter.as_string()); + } + } + + if(obj.contains("OptionalDependencies")) { + js::array arr = obj["OptionalDependencies"].as_array(); + for(auto& iter : arr) { + info.OptionalDependencies.push_back((std::string) iter.as_string()); + } + } + + } catch(const std::exception &exc) { + LOG.warn() << "Не удалось прочитать " << mod_conf.string(); + } + } + } catch(const std::exception &exc) { + LOG.warn() << "Не удалось прочитать моды из директории " << modsDir.string() << "\n" << exc.what(); + } + + + return infos; +} + void GameServer::stepConnections() { // Подключить новых игроков if(!External.NewConnectedPlayers.no_lock_readable().empty()) { @@ -1535,6 +1744,21 @@ void GameServer::stepSyncContent() { if(!full.BinFont.empty()) Content.Font.needResourceResponse(full.BinFont); + + if(!full.Node.empty()) { + std::unordered_map nodeDefines; + + for(DefNodeId_t id : full.Node) { + auto iter = Content.NodeDefines.find(id); + if(iter != Content.NodeDefines.end()) { + nodeDefines[id] = &iter->second; + } + } + + for(std::shared_ptr& cec : Game.CECs) { + cec->Remote->informateDefNode(nodeDefines); + } + } } diff --git a/Src/Server/GameServer.hpp b/Src/Server/GameServer.hpp index 301cc22..c12706c 100644 --- a/Src/Server/GameServer.hpp +++ b/Src/Server/GameServer.hpp @@ -72,6 +72,10 @@ class GameServer : public AsyncObject { Font(ioc, zeroFont) {} + + std::map NodeDefines; + std::map NodeKeys; + } Content; struct { @@ -157,6 +161,7 @@ class GameServer : public AsyncObject { RunCollect = Threads.size(); RunCompress = Threads.size(); Iteration += 1; + assert(RunCollect != 0); Symaphore.notify_all(); } @@ -181,7 +186,7 @@ class GameServer : public AsyncObject { thread.join(); } - void run(int id); + __attribute__((optimize("O3"))) void run(int id); } BackingChunkPressure; /* @@ -273,14 +278,23 @@ public: // Инициализация игрового протокола для сокета (onSocketAuthorized() может передать сокет в onSocketGame()) coro<> pushSocketGameProtocol(tcp::socket socket, const std::string username); - /* Загрузит, сгенерирует или просто выдаст регион из мира, который должен существовать */ - Region* forceGetRegion(WorldId_t worldId, Pos::GlobalRegion pos); + TexturePipeline buildTexturePipeline(const std::string& pipeline); + std::string deBuildTexturePipeline(const TexturePipeline& pipeline); private: void init(fs::path worldPath); void prerun(); void run(); + struct ModInfo { + std::string Id, Title, Description; + fs::path Path; + + std::vector Dependencies, OptionalDependencies; + }; + + std::vector readModDataPath(const fs::path& modsDir); + /* Подключение/отключение игроков */ diff --git a/Src/Server/NodeDefManager.hpp b/Src/Server/NodeDefManager.hpp index 7b9637e..bd78623 100644 --- a/Src/Server/NodeDefManager.hpp +++ b/Src/Server/NodeDefManager.hpp @@ -1 +1,12 @@ -#pragma once \ No newline at end of file +#pragma once + + +namespace LV::Server { + +class NodeDefManager { +public: + +}; + + +} \ No newline at end of file diff --git a/Src/Server/RemoteClient.cpp b/Src/Server/RemoteClient.cpp index c514b1e..b921bb3 100644 --- a/Src/Server/RemoteClient.cpp +++ b/Src/Server/RemoteClient.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -136,8 +137,13 @@ bool RemoteClient::maybe_prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::Globa for(const DefVoxelId_t& id : lostTypes) { auto iter = ResUses.RefDefVoxel.find(id); assert(iter != ResUses.RefDefVoxel.end()); // Должны быть описаны зависимости вокселя - decrementBinary(std::move(iter->second.Texture), {}, std::move(iter->second.Sound), {}, {}); + decrementBinary(std::move(iter->second)); ResUses.RefDefVoxel.erase(iter); + + checkPacketBorder(16); + NextPacket << (uint8_t) ToClient::L1::Definition + << (uint8_t) ToClient::L2Definition::FreeVoxel + << id; } } @@ -214,8 +220,13 @@ bool RemoteClient::maybe_prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::Global for(const DefNodeId_t& id : lostTypes) { auto iter = ResUses.RefDefNode.find(id); assert(iter != ResUses.RefDefNode.end()); // Должны быть описаны зависимости ноды - decrementBinary({}, {}, std::move(iter->second.Sound), std::move(iter->second.Model), {}); + decrementBinary(std::move(iter->second)); ResUses.RefDefNode.erase(iter); + + checkPacketBorder(16); + NextPacket << (uint8_t) ToClient::L1::Definition + << (uint8_t) ToClient::L2Definition::FreeNode + << id; } } @@ -238,42 +249,49 @@ void RemoteClient::prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regi // Уменьшаем зависимости вокселей и нод { auto iterWorld = ResUses.RefChunk.find(worldId); - assert(iterWorld != ResUses.RefChunk.end()); + if(iterWorld == ResUses.RefChunk.end()) + return; auto iterRegion = iterWorld->second.find(regionPos); - if(iterRegion != iterWorld->second.end()) { - for(const auto &iterChunk : iterRegion->second) { - for(const DefVoxelId_t& id : iterChunk.Voxel) { - auto iter = ResUses.DefVoxel.find(id); - assert(iter != ResUses.DefVoxel.end()); // Воксель должен быть в зависимостях - if(--iter->second == 0) { - // Вокселя больше нет в зависимостях - lostTypesV.push_back(id); - ResUses.DefVoxel.erase(iter); - } - } - - for(const DefNodeId_t& id : iterChunk.Node) { - auto iter = ResUses.DefNode.find(id); - assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях - if(--iter->second == 0) { - // Ноды больше нет в зависимостях - lostTypesN.push_back(id); - ResUses.DefNode.erase(iter); - } + if(iterRegion == iterWorld->second.end()) + return; + + for(const auto &iterChunk : iterRegion->second) { + for(const DefVoxelId_t& id : iterChunk.Voxel) { + auto iter = ResUses.DefVoxel.find(id); + assert(iter != ResUses.DefVoxel.end()); // Воксель должен быть в зависимостях + if(--iter->second == 0) { + // Вокселя больше нет в зависимостях + lostTypesV.push_back(id); + ResUses.DefVoxel.erase(iter); } } - iterWorld->second.erase(iterRegion); + for(const DefNodeId_t& id : iterChunk.Node) { + auto iter = ResUses.DefNode.find(id); + assert(iter != ResUses.DefNode.end()); // Нода должна быть в зависимостях + if(--iter->second == 0) { + // Ноды больше нет в зависимостях + lostTypesN.push_back(id); + ResUses.DefNode.erase(iter); + } + } } + + iterWorld->second.erase(iterRegion); } if(!lostTypesV.empty()) { for(const DefVoxelId_t& id : lostTypesV) { auto iter = ResUses.RefDefVoxel.find(id); assert(iter != ResUses.RefDefVoxel.end()); // Должны быть описаны зависимости вокселя - decrementBinary(std::move(iter->second.Texture), {}, std::move(iter->second.Sound), {}, {}); + decrementBinary(std::move(iter->second)); ResUses.RefDefVoxel.erase(iter); + + checkPacketBorder(16); + NextPacket << (uint8_t) ToClient::L1::Definition + << (uint8_t) ToClient::L2Definition::FreeVoxel + << id; } } @@ -281,8 +299,13 @@ void RemoteClient::prepareRegionRemove(WorldId_t worldId, Pos::GlobalRegion regi for(const DefNodeId_t& id : lostTypesN) { auto iter = ResUses.RefDefNode.find(id); assert(iter != ResUses.RefDefNode.end()); // Должны быть описаны зависимости ноды - decrementBinary({}, {}, std::move(iter->second.Sound), std::move(iter->second.Model), {}); + decrementBinary(std::move(iter->second)); ResUses.RefDefNode.erase(iter); + + checkPacketBorder(16); + NextPacket << (uint8_t) ToClient::L1::Definition + << (uint8_t) ToClient::L2Definition::FreeNode + << id; } } @@ -322,8 +345,7 @@ void RemoteClient::prepareEntityUpdate(ServerEntityId_t entityId, const Entity * if(--iterProfile->second == 0) { // Старый профиль больше не нужен auto iterProfileRef = ResUses.RefDefEntity.find(iterEntity->second.Profile); - decrementBinary(std::move(iterProfileRef->second.Texture), std::move(iterProfileRef->second.Animation), {}, - std::move(iterProfileRef->second.Model), {}); + decrementBinary(std::move(iterProfileRef->second)); ResUses.DefEntity.erase(iterProfile); } @@ -360,7 +382,7 @@ void RemoteClient::prepareEntityRemove(ServerEntityId_t entityId) // Профиль больше не используется auto iterProfileRef = ResUses.RefDefEntity.find(iterEntity->second.Profile); - decrementBinary(std::move(iterProfileRef->second.Texture), std::move(iterProfileRef->second.Animation), {}, std::move(iterProfileRef->second.Model), {}); + decrementBinary(std::move(iterProfileRef->second)); ResUses.RefDefEntity.erase(iterProfileRef); ResUses.DefEntity.erase(iterProfile); @@ -408,7 +430,7 @@ void RemoteClient::prepareWorldUpdate(WorldId_t worldId, World* world) ResUses.DefWorld.erase(iterWorldProf); auto iterWorldProfRef = ResUses.RefDefWorld.find(iterWorld->second.Profile); assert(iterWorldProfRef != ResUses.RefDefWorld.end()); // Зависимости предыдущего профиля также должны быть - decrementBinary(std::move(iterWorldProfRef->second.Texture), {}, {}, std::move(iterWorldProfRef->second.Model), {}); + decrementBinary(std::move(iterWorldProfRef->second)); ResUses.RefDefWorld.erase(iterWorldProfRef); } } @@ -438,7 +460,7 @@ void RemoteClient::prepareWorldRemove(WorldId_t worldId) // Убавляем зависимости профиля auto iterWorldProfDef = ResUses.RefDefWorld.find(iterWorld->second.Profile); assert(iterWorldProfDef != ResUses.RefDefWorld.end()); // Зависимости профиля должны быть - decrementBinary(std::move(iterWorldProfDef->second.Texture), {}, {}, std::move(iterWorldProfDef->second.Model), {}); + decrementBinary(std::move(iterWorldProfDef->second)); ResUses.RefDefWorld.erase(iterWorldProfDef); } @@ -468,83 +490,76 @@ ResourceRequest RemoteClient::pushPreparedPackets() { return std::move(NextRequest); } -void RemoteClient::informateBin(ToClient::L2Resource type, ResourceId_t id, const std::shared_ptr& data) { - checkPacketBorder(0); - NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение - << (uint8_t) type << id; - for(auto part : data->Hash) - NextPacket << part; +void RemoteClient::informateBinary(const std::vector>& resources) { + for(auto& resource : resources) { + auto &hash = resource->Hash; - NextPacket << (uint8_t) ToClient::L1::Resource // Принудительная полная отправка - << (uint8_t) ToClient::L2Resource::InitResSend - << uint8_t(0) << uint8_t(0) << id - << uint32_t(data->Data.size()); - for(auto part : data->Hash) - NextPacket << part; + auto iter = std::find(NeedToSend.begin(), NeedToSend.end(), hash); + if(iter == NeedToSend.end()) + continue; // Клиенту не требуется этот ресурс - NextPacket << uint8_t(0) << uint32_t(data->Data.size()); + { + auto it = std::lower_bound(ClientBinaryCache.begin(), ClientBinaryCache.end(), hash); - size_t pos = 0; - while(pos < data->Data.size()) { - checkPacketBorder(0); - size_t need = std::min(data->Data.size()-pos, std::min(NextPacket.size(), 64000)); - NextPacket.write((const std::byte*) data->Data.data()+pos, need); - pos += need; + if(it == ClientBinaryCache.end() || *it != hash) + ClientBinaryCache.insert(it, hash); + } + + // Полная отправка ресурса + checkPacketBorder(2+4+32+4); + NextPacket << (uint8_t) ToClient::L1::Resource // Принудительная полная отправка + << (uint8_t) ToClient::L2Resource::InitResSend + << uint32_t(resource->Data.size()); + for(auto part : hash) + NextPacket << part; + + NextPacket << uint32_t(resource->Data.size()); + + size_t pos = 0; + while(pos < resource->Data.size()) { + checkPacketBorder(0); + size_t need = std::min(resource->Data.size()-pos, std::min(NextPacket.size(), 64000)); + NextPacket.write((const std::byte*) resource->Data.data()+pos, need); + pos += need; + } + } } -void RemoteClient::informateBinTexture(const std::unordered_map> &textures) -{ - for(auto pair : textures) { - BinTextureId_t id = pair.first; - if(!ResUses.BinTexture.contains(id)) - continue; // Клиент не наблюдает за этим объектом +void RemoteClient::informateIdToHash(const std::vector>& resourcesLink) { + std::vector> newForClient; - informateBin(ToClient::L2Resource::Texture, id, pair.second); + for(auto& [type, id, hash] : resourcesLink) { + // Посмотрим что известно клиенту + auto iter = ResUses.BinUse[uint8_t(type)].find(id); + if(iter != ResUses.BinUse[uint8_t(type)].end()) { + if(std::get<1>(iter->second) != hash) { + // Требуется перепривязать идентификатор к новому хешу + newForClient.push_back({type, id, hash}); + std::get<1>(iter->second) = hash; + // Проверить есть ли хеш на стороне клиента + if(!std::binary_search(ClientBinaryCache.begin(), ClientBinaryCache.end(), hash)) { + NeedToSend.push_back(hash); + NextRequest.Hashes.push_back(hash); + } + } + } else { + // Ресурс не отслеживается клиентом + } } -} -void RemoteClient::informateBinAnimation(const std::unordered_map> &textures) -{ - for(auto pair : textures) { - BinTextureId_t id = pair.first; - if(!ResUses.BinTexture.contains(id)) - continue; // Клиент не наблюдает за этим объектом + // Отправляем новые привязки ресурсов + if(!newForClient.empty()) { + assert(newForClient.size() < 65535*4); - informateBin(ToClient::L2Resource::Animation, id, pair.second); - } -} + checkPacketBorder(2+4+newForClient.size()*(1+4+32)); + NextPacket << (uint8_t) ToClient::L1::Resource // Оповещение + << ((uint8_t) ToClient::L2Resource::Bind) << uint32_t(newForClient.size()); -void RemoteClient::informateBinModel(const std::unordered_map> &textures) -{ - for(auto pair : textures) { - BinTextureId_t id = pair.first; - if(!ResUses.BinTexture.contains(id)) - continue; // Клиент не наблюдает за этим объектом - - informateBin(ToClient::L2Resource::Model, id, pair.second); - } -} - -void RemoteClient::informateBinSound(const std::unordered_map> &textures) -{ - for(auto pair : textures) { - BinTextureId_t id = pair.first; - if(!ResUses.BinTexture.contains(id)) - continue; // Клиент не наблюдает за этим объектом - - informateBin(ToClient::L2Resource::Sound, id, pair.second); - } -} - -void RemoteClient::informateBinFont(const std::unordered_map> &textures) -{ - for(auto pair : textures) { - BinTextureId_t id = pair.first; - if(!ResUses.BinTexture.contains(id)) - continue; // Клиент не наблюдает за этим объектом - - informateBin(ToClient::L2Resource::Font, id, pair.second); + for(auto& [type, id, hash] : newForClient) { + NextPacket << uint8_t(type) << uint32_t(id); + NextPacket.write((const std::byte*) hash.data(), hash.size()); + } } } @@ -561,16 +576,51 @@ void RemoteClient::informateDefVoxel(const std::unordered_map &nodes) +void RemoteClient::informateDefNode(const std::unordered_map &nodes) { - for(auto pair : nodes) { - DefNodeId_t id = pair.first; + for(auto& [id, def] : nodes) { if(!ResUses.DefNode.contains(id)) continue; + + size_t reserve = 0; + for(int iter = 0; iter < 6; iter++) + reserve += def->Texs[iter].Pipeline.size(); + checkPacketBorder(1+1+4+1+2*6+reserve); NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::Node - << id; + << id << (uint8_t) def->DrawType; + + for(int iter = 0; iter < 6; iter++) { + NextPacket << (uint16_t) def->Texs[iter].Pipeline.size(); + NextPacket.write((const std::byte*) def->Texs[iter].Pipeline.data(), def->Texs[iter].Pipeline.size()); + } + + ResUsesObj::RefDefBin_t refs; + { + auto &array = refs.Resources[(uint8_t) EnumBinResource::Texture]; + for(int iter = 0; iter < 6; iter++) { + array.insert(array.end(), def->Texs[iter].BinTextures.begin(), def->Texs[iter].BinTextures.end()); + } + + std::sort(array.begin(), array.end()); + auto eraseLast = std::unique(array.begin(), array.end()); + array.erase(eraseLast, array.end()); + + incrementBinary(refs); + } + + + { + auto iterDefRef = ResUses.RefDefNode.find(id); + if(iterDefRef != ResUses.RefDefNode.end()) { + decrementBinary(std::move(iterDefRef->second)); + iterDefRef->second = std::move(refs); + } else { + ResUses.RefDefNode[id] = std::move(refs); + } + } + } } @@ -692,103 +742,45 @@ coro<> RemoteClient::rP_System(Net::AsyncSocket &sock) { } } -void RemoteClient::incrementBinary(const std::vector& textures, const std::vector& animation, - const std::vector& sounds, const std::vector& models, - const std::vector& fonts -) { - for(BinTextureId_t id : textures) { - if(++ResUses.BinTexture[id] == 1) { - NextRequest.BinTexture.push_back(id); - LOG.debug() << "Новое определение текстуры: " << id; - } - } +void RemoteClient::incrementBinary(const ResUsesObj::RefDefBin_t& bin) { + for(int iter = 0; iter < 5; iter++) { + auto &use = ResUses.BinUse[iter]; - for(BinAnimationId_t id : animation) { - if(++ResUses.BinAnimation[id] == 1) { - NextRequest.BinAnimation.push_back(id); - LOG.debug() << "Новое определение анимации: " << id; - } - } - - for(BinSoundId_t id : sounds) { - if(++ResUses.BinSound[id] == 1) { - NextRequest.BinSound.push_back(id); - LOG.debug() << "Новое определение звука: " << id; - } - } - - for(BinModelId_t id : models) { - if(++ResUses.BinModel[id] == 1) { - NextRequest.BinModel.push_back(id); - LOG.debug() << "Новое определение модели: " << id; - } - } - - for(BinFontId_t id : fonts) { - if(++ResUses.BinFont[id] == 1) { - NextRequest.BinFont.push_back(id); - LOG.debug() << "Новое определение шрифта: " << id; + for(ResourceId_t id : bin.Resources[iter]) { + if(++std::get<0>(use[id]) == 1) { + NextRequest.BinToHash[iter].push_back(id); + LOG.debug() << "Новое определение (тип " << iter << ") -> " << id; + } } } } -void RemoteClient::decrementBinary(std::vector&& textures, std::vector&& animation, - std::vector&& sounds, std::vector&& models, - std::vector&& fonts -) { - for(BinTextureId_t id : textures) { - if(--ResUses.BinTexture[id] == 0) { - ResUses.BinTexture.erase(ResUses.BinTexture.find(id)); - LOG.debug() << "Потеряно определение текстуры: " << id; +void RemoteClient::decrementBinary(ResUsesObj::RefDefBin_t&& bin) { + std::vector> lost; - NextPacket << (uint8_t) ToClient::L1::Resource - << (uint8_t) ToClient::L2Resource::FreeTexture - << id; + for(int iter = 0; iter < 5; iter++) { + auto &use = ResUses.BinUse[iter]; + + for(ResourceId_t id : bin.Resources[iter]) { + if(--std::get<0>(use[id]) == 0) { + use.erase(use.find(id)); + + lost.push_back({(EnumBinResource) iter, id}); + LOG.debug() << "Потеряно определение (тип " << iter << ") -> " << id; + } } } - for(BinAnimationId_t id : animation) { - if(--ResUses.BinAnimation[id] == 0) { - ResUses.BinAnimation.erase(ResUses.BinAnimation.find(id)); - LOG.debug() << "Потеряно определение анимации: " << id; + if(!lost.empty()) { + assert(lost.size() < 65535*4); - NextPacket << (uint8_t) ToClient::L1::Resource - << (uint8_t) ToClient::L2Resource::FreeAnimation - << id; - } - } + checkPacketBorder(1+1+4+lost.size()*(1+4)); + NextPacket << (uint8_t) ToClient::L1::Resource + << (uint8_t) ToClient::L2Resource::Lost + << uint32_t(lost.size()); - for(BinSoundId_t id : sounds) { - if(--ResUses.BinSound[id] == 0) { - ResUses.BinSound.erase(ResUses.BinSound.find(id)); - LOG.debug() << "Потеряно определение звука: " << id; - - NextPacket << (uint8_t) ToClient::L1::Resource - << (uint8_t) ToClient::L2Resource::FreeSound - << id; - } - } - - for(BinModelId_t id : models) { - if(--ResUses.BinModel[id] == 0) { - ResUses.BinModel.erase(ResUses.BinModel.find(id)); - LOG.debug() << "Потеряно определение модели: " << id; - - NextPacket << (uint8_t) ToClient::L1::Resource - << (uint8_t) ToClient::L2Resource::FreeModel - << id; - } - } - - for(BinFontId_t id : fonts) { - if(--ResUses.BinFont[id] == 0) { - ResUses.BinFont.erase(ResUses.BinFont.find(id)); - LOG.debug() << "Потеряно определение шрифта: " << id; - - NextPacket << (uint8_t) ToClient::L1::Resource - << (uint8_t) ToClient::L2Resource::FreeFont - << id; - } + for(auto& [type, id] : lost) + NextPacket << uint8_t(type) << uint32_t(id); } } diff --git a/Src/Server/RemoteClient.hpp b/Src/Server/RemoteClient.hpp index cf45b95..0b81396 100644 --- a/Src/Server/RemoteClient.hpp +++ b/Src/Server/RemoteClient.hpp @@ -17,7 +17,6 @@ #include namespace LV::Server { -using HASH = std::array; template= sizeof(ClientKey), int> = 0> class CSChunkedMapper { @@ -140,11 +139,8 @@ public: этих ресурсов и переотправлять их клиенту */ struct ResourceRequest { - std::vector BinTexture; - std::vector BinAnimation; - std::vector BinModel; - std::vector BinSound; - std::vector BinFont; + std::vector Hashes; + std::vector BinToHash[5]; std::vector Voxel; std::vector Node; @@ -154,11 +150,9 @@ struct ResourceRequest { std::vector Item; void insert(const ResourceRequest &obj) { - BinTexture.insert(BinTexture.end(), obj.BinTexture.begin(), obj.BinTexture.end()); - BinAnimation.insert(BinAnimation.end(), obj.BinAnimation.begin(), obj.BinAnimation.end()); - BinModel.insert(BinModel.end(), obj.BinModel.begin(), obj.BinModel.end()); - BinSound.insert(BinSound.end(), obj.BinSound.begin(), obj.BinSound.end()); - BinFont.insert(BinFont.end(), obj.BinFont.begin(), obj.BinFont.end()); + Hashes.insert(Hashes.end(), obj.Hashes.begin(), obj.Hashes.end()); + for(int iter = 0; iter < 5; iter++) + BinToHash[iter].insert(BinToHash[iter].end(), obj.BinToHash[iter].begin(), obj.BinToHash[iter].end()); Voxel.insert(Voxel.end(), obj.Voxel.begin(), obj.Voxel.end()); Node.insert(Node.end(), obj.Node.begin(), obj.Node.end()); @@ -169,9 +163,7 @@ struct ResourceRequest { } void uniq() { - for(std::vector *vec : { - &BinTexture, &BinAnimation, &BinModel, &BinSound, - &BinFont, &Voxel, &Node, &World, + for(std::vector *vec : {&BinToHash, &Voxel, &Node, &World, &Portal, &Entity, &Item }) { @@ -179,6 +171,10 @@ struct ResourceRequest { auto last = std::unique(vec->begin(), vec->end()); vec->erase(last, vec->end()); } + + std::sort(Hashes.begin(), Hashes.end()); + auto last = std::unique(Hashes.begin(), Hashes.end()); + Hashes.erase(last, Hashes.end()); } }; @@ -199,7 +195,9 @@ class RemoteClient { DestroyLock UseLock; Net::AsyncSocket Socket; bool IsConnected = true, IsGoingShutdown = false; - std::vector ClientBinaryCache; + + std::vector ClientBinaryCache, // Хеши ресурсов которые есть у клиента + NeedToSend; // Хеши которые нужно получить и отправить /* При обнаружении нового контента составляется запрос (ResourceRequest) @@ -209,12 +207,8 @@ class RemoteClient { */ struct ResUsesObj { - // Счётчики использования двоичных кэшируемых ресурсов - std::map BinTexture; - std::map BinAnimation; - std::map BinModel; - std::map BinSound; - std::map BinFont; + // Счётчики использования двоичных кэшируемых ресурсов + хэш привязанный к идентификатору + std::map> BinUse[5]; // Счётчики использование профилей контента std::map DefVoxel; // Один чанк, одно использование @@ -226,39 +220,17 @@ class RemoteClient { // Зависимость профилей контента от профилей ресурсов // Нужно чтобы пересчитать зависимости к профилям ресурсов - struct RefDefVoxel_t { - std::vector Texture; - std::vector Sound; + struct RefDefBin_t { + std::vector Resources[5]; }; - std::map RefDefVoxel; - struct RefDefNode_t { - std::vector Model; - std::vector Sound; - }; - std::map RefDefNode; - struct RefDefWorld_t { - std::vector Texture; - std::vector Model; - }; - std::map RefDefWorld; - struct RefDefPortal_t { - std::vector Texture; - std::vector Animation; - std::vector Model; - }; - std::map RefDefPortal; - struct RefDefEntity_t { - std::vector Texture; - std::vector Animation; - std::vector Model; - }; - std::map RefDefEntity; - struct RefDefItem_t { - std::vector Texture; - std::vector Animation; - std::vector Model; - }; - std::map RefDefItem; + + + std::map RefDefVoxel; + std::map RefDefNode; + std::map RefDefWorld; + std::map RefDefPortal; + std::map RefDefEntity; + std::map RefDefItem; // Модификационные зависимости экземпляров профилей контента struct ChunkRef { @@ -300,7 +272,7 @@ public: TOS::SpinlockObject> Actions; public: - RemoteClient(asio::io_context &ioc, tcp::socket socket, const std::string username, std::vector &&client_cache) + RemoteClient(asio::io_context &ioc, tcp::socket socket, const std::string username, std::vector &&client_cache) : LOG("RemoteClient " + username), Socket(ioc, std::move(socket)), Username(username), ClientBinaryCache(std::move(client_cache)) { } @@ -366,16 +338,16 @@ public: // Сюда приходят все обновления ресурсов движка // Глобально их можно запросить в выдаче pushPreparedPackets() - // Двоичные файлы - void informateBinTexture(const std::unordered_map> &textures); - void informateBinAnimation(const std::unordered_map> &animations); - void informateBinModel(const std::unordered_map> &models); - void informateBinSound(const std::unordered_map> &sounds); - void informateBinFont(const std::unordered_map> &fonts); + // Оповещение о ресурсе для отправки клиентам + void informateBinary(const std::vector>& resources); + + // Привязывает локальный идентификатор с хешем. Если его нет у клиента, + // то делается запрос на получение ресурсы для последующей отправки клиенту + void informateIdToHash(const std::vector>& resourcesLink); // Игровые определения void informateDefVoxel(const std::unordered_map &voxels); - void informateDefNode(const std::unordered_map &nodes); + void informateDefNode(const std::unordered_map &nodes); void informateDefWorld(const std::unordered_map &worlds); void informateDefPortal(const std::unordered_map &portals); void informateDefEntity(const std::unordered_map &entityes); @@ -387,15 +359,8 @@ private: coro<> readPacket(Net::AsyncSocket &sock); coro<> rP_System(Net::AsyncSocket &sock); - void incrementBinary(const std::vector &textures, const std::vector &animation, - const std::vector &sounds, const std::vector &models, - const std::vector &fonts - ); - void decrementBinary(std::vector&& textures, std::vector&& animation, - std::vector&& sounds, std::vector&& models, - std::vector&& fonts - ); - void informateBin(ToClient::L2Resource type, ResourceId_t id, const std::shared_ptr& pair); + void incrementBinary(const ResUsesObj::RefDefBin_t& bin); + void decrementBinary(ResUsesObj::RefDefBin_t&& bin); // void incrementProfile(const std::vector &textures, const std::vector &model, // const std::vector &sounds, const std::vector &font diff --git a/Src/sha2.hpp b/Src/sha2.hpp new file mode 100644 index 0000000..55db70a --- /dev/null +++ b/Src/sha2.hpp @@ -0,0 +1,499 @@ +// Copyright (c) 2018 Martyn Afford +// Licensed under the MIT licence + +#ifndef SHA2_HPP +#define SHA2_HPP + +#include +#include +#include + +namespace sha2 { + +template +using hash_array = std::array; + +using sha224_hash = hash_array<28>; +using sha256_hash = hash_array<32>; +using sha384_hash = hash_array<48>; +using sha512_hash = hash_array<64>; + +// SHA-2 uses big-endian integers. +inline void +write_u32(uint8_t* dest, uint32_t x) +{ + *dest++ = (x >> 24) & 0xff; + *dest++ = (x >> 16) & 0xff; + *dest++ = (x >> 8) & 0xff; + *dest++ = (x >> 0) & 0xff; +} + +inline void +write_u64(uint8_t* dest, uint64_t x) +{ + *dest++ = (x >> 56) & 0xff; + *dest++ = (x >> 48) & 0xff; + *dest++ = (x >> 40) & 0xff; + *dest++ = (x >> 32) & 0xff; + *dest++ = (x >> 24) & 0xff; + *dest++ = (x >> 16) & 0xff; + *dest++ = (x >> 8) & 0xff; + *dest++ = (x >> 0) & 0xff; +} + +inline uint32_t +read_u32(const uint8_t* src) +{ + return static_cast((src[0] << 24) | (src[1] << 16) | + (src[2] << 8) | src[3]); +} + +inline uint64_t +read_u64(const uint8_t* src) +{ + uint64_t upper = read_u32(src); + uint64_t lower = read_u32(src + 4); + return ((upper & 0xffffffff) << 32) | (lower & 0xffffffff); +} + +// A compiler-recognised implementation of rotate right that avoids the +// undefined behaviour caused by shifting by the number of bits of the left-hand +// type. See John Regehr's article https://blog.regehr.org/archives/1063 +inline uint32_t +ror(uint32_t x, uint32_t n) +{ + return (x >> n) | (x << (-n & 31)); +} + +inline uint64_t +ror(uint64_t x, uint64_t n) +{ + return (x >> n) | (x << (-n & 63)); +} + +// Utility function to truncate larger hashes. Assumes appropriate hash types +// (i.e., hash_array) for type T. +template +inline T +truncate(const hash_array& hash) +{ + T result; + memcpy(result.data(), hash.data(), sizeof(result)); + return result; +} + +// Both sha256_impl and sha512_impl are used by sha224/sha256 and +// sha384/sha512 respectively, avoiding duplication as only the initial hash +// values (s) and output hash length change. +inline sha256_hash +sha256_impl(const uint32_t* s, const uint8_t* data, uint64_t length) +{ + static_assert(sizeof(uint32_t) == 4, "sizeof(uint32_t) must be 4"); + static_assert(sizeof(uint64_t) == 8, "sizeof(uint64_t) must be 8"); + + constexpr size_t chunk_bytes = 64; + const uint64_t bit_length = length * 8; + + uint32_t hash[8] = {s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]}; + + constexpr uint32_t k[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; + + auto chunk = [&hash, &k](const uint8_t* chunk_data) { + uint32_t w[64] = {0}; + + for (int i = 0; i != 16; ++i) { + w[i] = read_u32(&chunk_data[i * 4]); + } + + for (int i = 16; i != 64; ++i) { + auto w15 = w[i - 15]; + auto w2 = w[i - 2]; + auto s0 = ror(w15, 7) ^ ror(w15, 18) ^ (w15 >> 3); + auto s1 = ror(w2, 17) ^ ror(w2, 19) ^ (w2 >> 10); + w[i] = w[i - 16] + s0 + w[i - 7] + s1; + } + + auto a = hash[0]; + auto b = hash[1]; + auto c = hash[2]; + auto d = hash[3]; + auto e = hash[4]; + auto f = hash[5]; + auto g = hash[6]; + auto h = hash[7]; + + for (int i = 0; i != 64; ++i) { + auto s1 = ror(e, 6) ^ ror(e, 11) ^ ror(e, 25); + auto ch = (e & f) ^ (~e & g); + auto temp1 = h + s1 + ch + k[i] + w[i]; + auto s0 = ror(a, 2) ^ ror(a, 13) ^ ror(a, 22); + auto maj = (a & b) ^ (a & c) ^ (b & c); + auto temp2 = s0 + maj; + + h = g; + g = f; + f = e; + e = d + temp1; + d = c; + c = b; + b = a; + a = temp1 + temp2; + } + + hash[0] += a; + hash[1] += b; + hash[2] += c; + hash[3] += d; + hash[4] += e; + hash[5] += f; + hash[6] += g; + hash[7] += h; + }; + + while (length >= chunk_bytes) { + chunk(data); + data += chunk_bytes; + length -= chunk_bytes; + } + + { + std::array buf; + memcpy(buf.data(), data, length); + + auto i = length; + buf[i++] = 0x80; + + if (i > chunk_bytes - 8) { + while (i < chunk_bytes) { + buf[i++] = 0; + } + + chunk(buf.data()); + i = 0; + } + + while (i < chunk_bytes - 8) { + buf[i++] = 0; + } + + write_u64(&buf[i], bit_length); + + chunk(buf.data()); + } + + sha256_hash result; + + for (uint8_t i = 0; i != 8; ++i) { + write_u32(&result[i * 4], hash[i]); + } + + return result; +} + +inline sha512_hash +sha512_impl(const uint64_t* s, const uint8_t* data, uint64_t length) +{ + static_assert(sizeof(uint32_t) == 4, "sizeof(uint32_t) must be 4"); + static_assert(sizeof(uint64_t) == 8, "sizeof(uint64_t) must be 8"); + + constexpr size_t chunk_bytes = 128; + const uint64_t bit_length_low = length << 3; + const uint64_t bit_length_high = length >> (64 - 3); + + uint64_t hash[8] = {s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]}; + + constexpr uint64_t k[80] = { + 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, + 0xe9b5dba58189dbbc, 0x3956c25bf348b538, 0x59f111f1b605d019, + 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, + 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, + 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, + 0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, + 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, 0x2de92c6f592b0275, + 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, + 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, + 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725, + 0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, + 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, + 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, + 0x92722c851482353b, 0xa2bfe8a14cf10364, 0xa81a664bbc423001, + 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218, + 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, + 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, + 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, + 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, + 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, + 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, + 0xc67178f2e372532b, 0xca273eceea26619c, 0xd186b8c721c0c207, + 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, + 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, + 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, + 0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, + 0x5fcb6fab3ad6faec, 0x6c44198c4a475817}; + + auto chunk = [&hash, &k](const uint8_t* chunk_data) { + uint64_t w[80] = {0}; + + for (int i = 0; i != 16; ++i) { + w[i] = read_u64(&chunk_data[i * 8]); + } + + for (int i = 16; i != 80; ++i) { + auto w15 = w[i - 15]; + auto w2 = w[i - 2]; + auto s0 = ror(w15, 1) ^ ror(w15, 8) ^ (w15 >> 7); + auto s1 = ror(w2, 19) ^ ror(w2, 61) ^ (w2 >> 6); + w[i] = w[i - 16] + s0 + w[i - 7] + s1; + } + + auto a = hash[0]; + auto b = hash[1]; + auto c = hash[2]; + auto d = hash[3]; + auto e = hash[4]; + auto f = hash[5]; + auto g = hash[6]; + auto h = hash[7]; + + for (int i = 0; i != 80; ++i) { + auto s1 = ror(e, 14) ^ ror(e, 18) ^ ror(e, 41); + auto ch = (e & f) ^ (~e & g); + auto temp1 = h + s1 + ch + k[i] + w[i]; + auto s0 = ror(a, 28) ^ ror(a, 34) ^ ror(a, 39); + auto maj = (a & b) ^ (a & c) ^ (b & c); + auto temp2 = s0 + maj; + + h = g; + g = f; + f = e; + e = d + temp1; + d = c; + c = b; + b = a; + a = temp1 + temp2; + } + + hash[0] += a; + hash[1] += b; + hash[2] += c; + hash[3] += d; + hash[4] += e; + hash[5] += f; + hash[6] += g; + hash[7] += h; + }; + + while (length >= chunk_bytes) { + chunk(data); + data += chunk_bytes; + length -= chunk_bytes; + } + + { + std::array buf; + memcpy(buf.data(), data, length); + + auto i = length; + buf[i++] = 0x80; + + if (i > chunk_bytes - 16) { + while (i < chunk_bytes) { + buf[i++] = 0; + } + + chunk(buf.data()); + i = 0; + } + + while (i < chunk_bytes - 16) { + buf[i++] = 0; + } + + write_u64(&buf[i + 0], bit_length_high); + write_u64(&buf[i + 8], bit_length_low); + + chunk(buf.data()); + } + + sha512_hash result; + + for (uint8_t i = 0; i != 8; ++i) { + write_u64(&result[i * 8], hash[i]); + } + + return result; +} + +inline sha224_hash +sha224(const uint8_t* data, uint64_t length) +{ + // Second 32 bits of the fractional parts of the square roots of the ninth + // through sixteenth primes 23..53 + const uint32_t initial_hash_values[8] = {0xc1059ed8, + 0x367cd507, + 0x3070dd17, + 0xf70e5939, + 0xffc00b31, + 0x68581511, + 0x64f98fa7, + 0xbefa4fa4}; + + auto hash = sha256_impl(initial_hash_values, data, length); + return truncate(hash); +} + +inline sha256_hash +sha256(const uint8_t* data, uint64_t length) +{ + // First 32 bits of the fractional parts of the square roots of the first + // eight primes 2..19: + const uint32_t initial_hash_values[8] = {0x6a09e667, + 0xbb67ae85, + 0x3c6ef372, + 0xa54ff53a, + 0x510e527f, + 0x9b05688c, + 0x1f83d9ab, + 0x5be0cd19}; + + return sha256_impl(initial_hash_values, data, length); +} + +inline sha384_hash +sha384(const uint8_t* data, uint64_t length) +{ + const uint64_t initial_hash_values[8] = {0xcbbb9d5dc1059ed8, + 0x629a292a367cd507, + 0x9159015a3070dd17, + 0x152fecd8f70e5939, + 0x67332667ffc00b31, + 0x8eb44a8768581511, + 0xdb0c2e0d64f98fa7, + 0x47b5481dbefa4fa4}; + + auto hash = sha512_impl(initial_hash_values, data, length); + return truncate(hash); +} + +inline sha512_hash +sha512(const uint8_t* data, uint64_t length) +{ + const uint64_t initial_hash_values[8] = {0x6a09e667f3bcc908, + 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, + 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, + 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, + 0x5be0cd19137e2179}; + + return sha512_impl(initial_hash_values, data, length); +} + +// SHA-512/t is a truncated version of SHA-512, where the result is truncated +// to t bits (in this implementation, t must be a multiple of eight). The two +// primariy variants of this are SHA-512/224 and SHA-512/256, both of which are +// provided through explicit functions (sha512_224 and sha512_256) below this +// function. On 64-bit platforms, SHA-512, and correspondingly SHA-512/t, +// should give a significant performance improvement over SHA-224 and SHA-256 +// due to the doubled block size. +template +inline hash_array +sha512_t(const uint8_t* data, uint64_t length) +{ + static_assert(bits % 8 == 0, "Bits must be a multiple of 8 (i.e., bytes)."); + static_assert(0 < bits && bits <= 512, "Bits must be between 8 and 512"); + static_assert(bits != 384, "NIST explicitly denies 384 bits, use SHA-384."); + + const uint64_t modified_initial_hash_values[8] = { + 0x6a09e667f3bcc908 ^ 0xa5a5a5a5a5a5a5a5, + 0xbb67ae8584caa73b ^ 0xa5a5a5a5a5a5a5a5, + 0x3c6ef372fe94f82b ^ 0xa5a5a5a5a5a5a5a5, + 0xa54ff53a5f1d36f1 ^ 0xa5a5a5a5a5a5a5a5, + 0x510e527fade682d1 ^ 0xa5a5a5a5a5a5a5a5, + 0x9b05688c2b3e6c1f ^ 0xa5a5a5a5a5a5a5a5, + 0x1f83d9abfb41bd6b ^ 0xa5a5a5a5a5a5a5a5, + 0x5be0cd19137e2179 ^ 0xa5a5a5a5a5a5a5a5}; + + // The SHA-512/t generation function uses a modified SHA-512 on the string + // "SHA-512/t" where t is the number of bits. The modified SHA-512 operates + // like the original but uses different initial hash values, as seen above. + // The hash is then used for the initial hash values sent to the original + // SHA-512. The sha512_224 and sha512_256 functions have this precalculated. + constexpr int buf_size = 12; + uint8_t buf[buf_size]; + + auto buf_ptr = reinterpret_cast(buf); + auto len = snprintf(buf_ptr, buf_size, "SHA-512/%d", bits); + auto ulen = static_cast(len); + + auto initial8 = sha512_impl(modified_initial_hash_values, buf, ulen); + + // To read the hash bytes back into 64-bit integers, we must convert back + // from big-endian. + uint64_t initial64[8]; + + for (uint8_t i = 0; i != 8; ++i) { + initial64[i] = read_u64(&initial8[i * 8]); + } + + // Once the initial hash is computed, use regular SHA-512 and copy the + // appropriate number of bytes. + auto hash = sha512_impl(initial64, data, length); + return truncate>(hash); +} + +// It is preferable to use either sha512_224 or sha512_256 in place of +// sha512_t<224> or sha512_t<256> for better performance (as the initial +// hashes are precalculated), for slightly less syntactic noise and for +// consistency with the other functions. +inline sha224_hash +sha512_224(const uint8_t* data, uint64_t length) +{ + // Precalculated initial hash (The hash of "SHA-512/224" using the modified + // SHA-512 generation function, described above in sha512_t). + const uint64_t initial_hash_values[8] = {0x8c3d37c819544da2, + 0x73e1996689dcd4d6, + 0x1dfab7ae32ff9c82, + 0x679dd514582f9fcf, + 0x0f6d2b697bd44da8, + 0x77e36f7304c48942, + 0x3f9d85a86a1d36c8, + 0x1112e6ad91d692a1}; + + auto hash = sha512_impl(initial_hash_values, data, length); + return truncate(hash); +} + +inline sha256_hash +sha512_256(const uint8_t* data, uint64_t length) +{ + // Precalculated initial hash (The hash of "SHA-512/256" using the modified + // SHA-512 generation function, described above in sha512_t). + const uint64_t initial_hash_values[8] = {0x22312194fc2bf72c, + 0x9f555fa3c84c64c2, + 0x2393b86b6f53b151, + 0x963877195940eabd, + 0x96283ee2a88effe3, + 0xbe5e1e2553863992, + 0x2b0199fc2c85b8aa, + 0x0eb72ddc81c52ca2}; + + auto hash = sha512_impl(initial_hash_values, data, length); + return truncate(hash); +} + +} // sha2 namespace + +#endif /* SHA2_HPP */ diff --git a/assets/shaders/chunk/node_opaque.frag b/assets/shaders/chunk/node_opaque.frag index d253531..708464c 100644 --- a/assets/shaders/chunk/node_opaque.frag +++ b/assets/shaders/chunk/node_opaque.frag @@ -71,4 +71,6 @@ vec4 atlasColor(uint texId, vec2 uv) void main() { Frame = atlasColor(Fragment.Texture, Fragment.UV); + if(Frame.w == 0) + discard; } \ No newline at end of file diff --git a/assets/shaders/chunk/node_opaque.frag.bin b/assets/shaders/chunk/node_opaque.frag.bin index 7c0aa2dbc1b90eaaa08bfbc19fcec5da697a8f82..8d4a86e1fd4d1e92b4b34dbd780501333f647dc0 100644 GIT binary patch delta 408 zcmXw!y-LGy6vc1)Poh{eNER21wS$9;&LYyO9V$)*K^!WGA0Rr^!Ku$6c?4a=N#Z;B z8ZHHAzZ%j;{GB#lIGlU#|K!}8x6Svh$W*$bRpl!yq@a;`#F4d3mzr$17uW z=lt}nzt_9&^lqPSy8?#OTDsZSu_2g9g|9KItU+KcLQ^X@dG%pivBG7d3YQB0P8qa4VRW5 z0C^|yBG61;=^{B=cs*%yrjowE5NN{=4yAqr&0p}W!$0M$29MZ~!Kxg;-bO&PjKkGW M_%ZcaTCeU#f4?e47XSbN delta 274 zcmaE1_`radnMs+Qft8Vgn}L_%+eF@I)=vx!3|}|qE@o!@G+B^ESoSlJ?~|FHQR16e z;FDOHUs~dylm!%HV_;?YJlT;&nDOi8P?pt<^&pLkKnw!z3@i*Fz7h~Kg89r0AcL5I z*aIjBVuOGL5PN_O0`nLcl%cdc12e-{s65Cl5F2DR#4HFOX0|ZURG_(=>)BQ^n_2@E rF99k9Sq=hnK!ZSrM*y(`Q0@zm<^f`m7)ULM|8;UL*YVA3xNUd=_TDaX diff --git a/assets/textures/frame.png b/assets/textures/frame.png new file mode 100644 index 0000000000000000000000000000000000000000..4a57b06c0a84f365f8026e93faa12aad0c297660 GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|&H|6fVg?31We{epSZZGe6twYl zaSX8#OiH=&`@cQ2CZeRw2HI}9Xvl@BGD^e`y% gu*FLRu1#QOICz(H_WE}FE}%IKp00i_>zopr01dw