diff --git a/CMakeLists.txt b/CMakeLists.txt index edd7ed8..e57adae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.13) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -rdynamic") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") # -rdynamic project (LuaVox VERSION 0.0 DESCRIPTION "LuaVox Description") add_executable(${PROJECT_NAME}) @@ -65,8 +65,8 @@ add_custom_command(OUTPUT assets.o resources.cpp INPUT ${ASSETS} COMMAND cd "${CMAKE_CURRENT_SOURCE_DIR}/Work/assets" && ld -r -b binary -o '${CMAKE_CURRENT_BINARY_DIR}/assets.o' ${ASSETS} COMMAND objcopy --rename-section .data=.rodata,alloc,load,readonly,data,contents ${CMAKE_CURRENT_BINARY_DIR}/assets.o ${CMAKE_CURRENT_BINARY_DIR}/assets.o) -add_library(assets STATIC resources.cpp assets.o) SET_SOURCE_FILES_PROPERTIES(assets.o PROPERTIES EXTERNAL_OBJECT true GENERATED true) +add_library(assets STATIC resources.cpp assets.o) SET_TARGET_PROPERTIES(assets PROPERTIES LINKER_LANGUAGE C) target_link_libraries(${PROJECT_NAME} PUBLIC assets uring) diff --git a/Src/Client/Abstract.cpp b/Src/Client/Abstract.cpp index 14284f2..1c9c49b 100644 --- a/Src/Client/Abstract.cpp +++ b/Src/Client/Abstract.cpp @@ -1,25 +1,10 @@ #include "Abstract.hpp" +#include "Common/Abstract.hpp" namespace LV::Client { - void IRenderSession::onChunksChange(WorldId_t worldId, const std::vector &changeOrAddList, const std::vector &remove) {} - void IRenderSession::attachCameraToEntity(EntityId_t id) {} - void IRenderSession::onWorldAdd(WorldId_t id) {} - void IRenderSession::onWorldRemove(WorldId_t id) {} - void IRenderSession::onWorldChange(WorldId_t id) {} - void IRenderSession::onEntitysAdd(const std::vector &list) {} - void IRenderSession::onEntitysRemove(const std::vector &list) {} - void IRenderSession::onEntitysPosQuatChanges(const std::vector &list) {} - void IRenderSession::onEntitysStateChanges(const std::vector &list) {} - TextureId_t IRenderSession::allocateTexture() { return 0; } - void IRenderSession::freeTexture(TextureId_t id) {} - void IRenderSession::setTexture(TextureId_t id, TextureInfo info) {} - ModelId_t IRenderSession::allocateModel() { return 0; } - void IRenderSession::freeModel(ModelId_t id) {} - void IRenderSession::setModel(ModelId_t id, ModelInfo info) {} IRenderSession::~IRenderSession() = default; - IServerSession::~IServerSession() = default; void ISurfaceEventListener::onResize(uint32_t width, uint32_t height) {} diff --git a/Src/Client/Abstract.hpp b/Src/Client/Abstract.hpp index 21d574e..644e71c 100644 --- a/Src/Client/Abstract.hpp +++ b/Src/Client/Abstract.hpp @@ -9,17 +9,13 @@ namespace LV::Client { -using VoxelId_t = uint16_t; - struct VoxelCube { + DefVoxelId_c VoxelId; Pos::Local256 Left, Right; - VoxelId_t Material; }; -using NodeId_t = uint16_t; - struct Node { - NodeId_t NodeId; + DefNodeId_c NodeId; uint8_t Rotate : 6; }; @@ -34,15 +30,11 @@ struct Chunk { LightPrism Lights[16][16]; }; -using WorldId_t = uint8_t; -using PortalId_t = uint16_t; -using EntityId_t = uint16_t; - class Entity { public: // PosQuat - WorldId_t WorldId; - PortalId_t LastUsedPortal; + DefWorldId_c WorldId; + DefPortalId_c LastUsedPortal; Pos::Object Pos; glm::quat Quat; static constexpr uint16_t HP_BS = 4096, HP_BS_Bit = 12; @@ -54,51 +46,24 @@ public: // states }; -using TextureId_t = uint16_t; - -struct TextureInfo { - -}; - -using ModelId_t = uint16_t; - -struct ModelInfo { - -}; - /* Интерфейс рендера текущего подключения к серверу */ class IRenderSession { public: + virtual void onDefTexture(TextureId_c id, std::vector &&info) = 0; + virtual void onDefTextureLost(const std::vector &&lost) = 0; + virtual void onDefModel(ModelId_c id, std::vector &&info) = 0; + virtual void onDefModelLost(const std::vector &&lost) = 0; + + virtual void onDefWorldUpdates(const std::vector &updates) = 0; + virtual void onDefVoxelUpdates(const std::vector &updates) = 0; + virtual void onDefNodeUpdates(const std::vector &updates) = 0; + virtual void onDefPortalUpdates(const std::vector &updates) = 0; + virtual void onDefEntityUpdates(const std::vector &updates) = 0; + // Сообщаем об изменившихся чанках - virtual void onChunksChange(WorldId_t worldId, const std::vector &changeOrAddList, const std::vector &remove); - // Подключаем камеру к сущности - virtual void attachCameraToEntity(EntityId_t id); - - // - - // Мир уже есть в глобальном списке - virtual void onWorldAdd(WorldId_t id); - // Мира уже нет в списке - virtual void onWorldRemove(WorldId_t id); - // Изменение состояния мира - virtual void onWorldChange(WorldId_t id); - - // Сущности уже есть в глобальном списке - virtual void onEntitysAdd(const std::vector &list); - // - virtual void onEntitysRemove(const std::vector &list); - // - virtual void onEntitysPosQuatChanges(const std::vector &list); - // - virtual void onEntitysStateChanges(const std::vector &list); - - virtual TextureId_t allocateTexture(); - virtual void freeTexture(TextureId_t id); - virtual void setTexture(TextureId_t id, TextureInfo info); - - virtual ModelId_t allocateModel(); - virtual void freeModel(ModelId_t id); - virtual void setModel(ModelId_t id, ModelInfo info); + virtual void onChunksChange(WorldId_c worldId, const std::vector &changeOrAddList, const std::vector &remove) = 0; + // Установить позицию для камеры + virtual void setCameraPos(WorldId_c worldId, Pos::Object pos, glm::quat quat) = 0; virtual ~IRenderSession(); }; @@ -110,13 +75,25 @@ struct Region { struct World { - std::vector Entitys; + std::vector Entitys; std::unordered_map Regions; }; -class ChunksIterator { -public: +struct DefWorldInfo { + +}; + +struct DefPortalInfo { + +}; + +struct DefEntityInfo { + +}; + +struct WorldInfo { + }; struct VoxelInfo { @@ -127,13 +104,28 @@ struct NodeInfo { }; +struct PortalInfo { + +}; + +struct EntityInfo { + +}; + /* Интерфейс обработчика сессии с сервером */ class IServerSession { public: - std::unordered_map Entitys; - std::unordered_map Worlds; - std::unordered_map VoxelRegistry; - std::unordered_map NodeRegistry; + struct { + std::unordered_map DefWorlds; + std::unordered_map DefVoxels; + std::unordered_map DefNodes; + std::unordered_map DefPortals; + std::unordered_map DefEntityes; + + std::unordered_map Worlds; + std::unordered_map Portals; + std::unordered_map Entityes; + } Registry; virtual ~IServerSession(); }; diff --git a/Src/Client/ServerSession.cpp b/Src/Client/ServerSession.cpp index 44783f2..8e98ea9 100644 --- a/Src/Client/ServerSession.cpp +++ b/Src/Client/ServerSession.cpp @@ -1,17 +1,24 @@ #include "ServerSession.hpp" +#include "Client/Abstract.hpp" #include "Common/Net.hpp" #include #include #include #include #include +#include +#include +#include namespace LV::Client { using namespace TOS; -ServerSession::~ServerSession() = default; +ServerSession::~ServerSession() { + WorkDeadline.cancel(); + UseLock.wait_no_use(); +} coro<> ServerSession::asyncAuthorizeWithServer(tcp::socket &socket, const std::string username, const std::string token, int a_ar_r, std::function onProgress) { assert(a_ar_r >= 0 && a_ar_r <= 2); @@ -109,4 +116,270 @@ coro> ServerSession::asyncInitGameProtocol(asi co_return std::make_unique(ioc, std::move(socket)); } +void ServerSession::shutdown(EnumDisconnect type) { + IsGoingShutdown = true; + Net::Packet packet; + packet << (uint8_t) ToServer::L1::System + << (uint8_t) ToServer::L2System::Disconnect + << (uint8_t) type; + + Socket->pushPacket(std::move(packet)); + + std::string reason; + if(type == EnumDisconnect::ByInterface) + reason = "по запросу интерфейса"; + else if(type == EnumDisconnect::CriticalError) + reason = "на сервере произошла критическая ошибка"; + else if(type == EnumDisconnect::ProtocolError) + reason = "ошибка протокола (клиент)"; + + LOG.info() << "Отключение от сервера: " << reason; +} + +void ServerSession::onResize(uint32_t width, uint32_t height) { + +} + +void ServerSession::onChangeFocusState(bool isFocused) { + +} + +void ServerSession::onCursorPosChange(int32_t width, int32_t height) { + +} + +void ServerSession::onCursorMove(float xMove, float yMove) { + glm::vec3 front = Camera.Quat*glm::vec3(0.0f, 0.0f, -1.0f); +} + +void ServerSession::onFrameRendering() { + +} + +void ServerSession::onFrameRenderEnd() { + +} + +void ServerSession::onCursorBtn(ISurfaceEventListener::EnumCursorBtn btn, bool state) { + +} + +void ServerSession::onKeyboardBtn(int btn, int state) { + if(btn == GLFW_KEY_TAB && !state) { + CursorMode = CursorMode == EnumCursorMoveMode::Default ? EnumCursorMoveMode::MoveAndHidden : EnumCursorMoveMode::Default; + } +} + +void ServerSession::onJoystick() { + +} + +coro<> ServerSession::run() { + auto useLock = UseLock.lock(); + + try { + while(!IsGoingShutdown && IsConnected) { + co_await readPacket(*Socket); + } + } catch(const std::exception &exc) { + if(const auto *errc = dynamic_cast(&exc); + errc && errc->code() == boost::asio::error::operation_aborted) + { + co_return; + } + + TOS::Logger("ServerSession").warn() << exc.what(); + } + + IsConnected = false; + + co_return; +} + +void ServerSession::protocolError() { + shutdown(EnumDisconnect::ProtocolError); +} + +coro<> ServerSession::readPacket(Net::AsyncSocket &sock) { + uint8_t first = co_await sock.read(); + + switch((ToClient::L1) first) { + case ToClient::L1::System: co_await rP_System(sock); co_return; + case ToClient::L1::Resource: co_await rP_Resource(sock); co_return; + case ToClient::L1::Definition: co_await rP_Definition(sock); co_return; + case ToClient::L1::Content: co_await rP_Content(sock); co_return; + default: + protocolError(); + } +} + +coro<> ServerSession::rP_System(Net::AsyncSocket &sock) { + uint8_t second = co_await sock.read(); + + switch((ToClient::L2System) second) { + case ToClient::L2System::Init: + + co_return; + case ToClient::L2System::Disconnect: + { + EnumDisconnect type = (EnumDisconnect) co_await sock.read(); + std::string reason = co_await sock.read(); + + if(type == EnumDisconnect::ByInterface) + reason = "по запросу интерфейса " + reason; + else if(type == EnumDisconnect::CriticalError) + reason = "на сервере произошла критическая ошибка " + reason; + else if(type == EnumDisconnect::ProtocolError) + reason = "ошибка протокола (сервер) " + reason; + + LOG.info() << "Отключение от сервера: " << reason; + + co_return; + } + case ToClient::L2System::LinkCameraToEntity: + + co_return; + case ToClient::L2System::UnlinkCamera: + + co_return; + default: + protocolError(); + } +} + +coro<> ServerSession::rP_Resource(Net::AsyncSocket &sock) { + uint8_t second = co_await sock.read(); + + switch((ToClient::L2Resource) second) { + case ToClient::L2Resource::Texture: + + co_return; + case ToClient::L2Resource::FreeTexture: + + co_return; + case ToClient::L2Resource::Sound: + + co_return; + case ToClient::L2Resource::FreeSound: + + co_return; + case ToClient::L2Resource::Model: + + co_return; + case ToClient::L2Resource::FreeModel: + + co_return; + case ToClient::L2Resource::InitResSend: + + co_return; + case ToClient::L2Resource::ChunkSend: + + co_return; + case ToClient::L2Resource::SendCanceled: + + co_return; + default: + protocolError(); + } +} + +coro<> ServerSession::rP_Definition(Net::AsyncSocket &sock) { + uint8_t second = co_await sock.read(); + + switch((ToClient::L2Definition) second) { + case ToClient::L2Definition::World: + + co_return; + case ToClient::L2Definition::FreeWorld: + + co_return; + case ToClient::L2Definition::Voxel: + + co_return; + case ToClient::L2Definition::FreeVoxel: + + co_return; + case ToClient::L2Definition::Node: + + co_return; + case ToClient::L2Definition::FreeNode: + + co_return; + case ToClient::L2Definition::Portal: + + co_return; + case ToClient::L2Definition::FreePortal: + + co_return; + case ToClient::L2Definition::Entity: + + co_return; + case ToClient::L2Definition::FreeEntity: + + co_return; + default: + protocolError(); + } +} + +coro<> ServerSession::rP_Content(Net::AsyncSocket &sock) { + uint8_t second = co_await sock.read(); + + switch((ToClient::L2Content) second) { + case ToClient::L2Content::World: + + co_return; + case ToClient::L2Content::RemoveWorld: + + co_return; + case ToClient::L2Content::Portal: + + co_return; + case ToClient::L2Content::RemovePortal: + + co_return; + case ToClient::L2Content::Entity: + + co_return; + case ToClient::L2Content::RemoveEntity: + + co_return; + case ToClient::L2Content::ChunkVoxels: + { + WorldId_c wcId = co_await sock.read(); + Pos::GlobalChunk::Key posKey = co_await sock.read(); + Pos::GlobalChunk pos = *(Pos::GlobalChunk*) &posKey; + + std::vector cubes(co_await sock.read()); + + for(size_t iter = 0; iter < cubes.size(); iter++) { + VoxelCube &cube = cubes[iter]; + cube.VoxelId = co_await sock.read(); + cube.Left.X = co_await sock.read(); + cube.Left.Y = co_await sock.read(); + cube.Left.Z = co_await sock.read(); + cube.Right.X = co_await sock.read(); + cube.Right.Y = co_await sock.read(); + cube.Right.Z = co_await sock.read(); + } + + LOG.info() << "Приняты воксели чанка " << int(wcId) << " / " << pos.X << ":" << pos.Y << ":" << pos.Z << " Вокселей " << cubes.size(); + + co_return; + } + + case ToClient::L2Content::ChunkNodes: + + co_return; + case ToClient::L2Content::ChunkLightPrism: + + co_return; + case ToClient::L2Content::RemoveChunk: + + co_return; + default: + protocolError(); + } +} + } \ No newline at end of file diff --git a/Src/Client/ServerSession.hpp b/Src/Client/ServerSession.hpp index 0c58b12..d2ff2cd 100644 --- a/Src/Client/ServerSession.hpp +++ b/Src/Client/ServerSession.hpp @@ -1,7 +1,10 @@ #pragma once #include "Abstract.hpp" +#include "Common/Async.hpp" +#include "Common/Lockable.hpp" #include "Common/Net.hpp" +#include "Common/Packets.hpp" #include #include @@ -9,16 +12,23 @@ namespace LV::Client { class ServerSession : public AsyncObject, public IServerSession, public ISurfaceEventListener { - std::unique_ptr _Socket; - Net::AsyncSocket &Socket; + std::unique_ptr Socket; IRenderSession *RS = nullptr; + DestroyLock UseLock; + bool IsConnected = true, IsGoingShutdown = false; + + TOS::Logger LOG = "ServerSession"; + + struct { + glm::quat Quat; + } Camera; public: // Нужен сокет, на котором только что был согласован игровой протокол (asyncInitGameProtocol) ServerSession(asio::io_context &ioc, std::unique_ptr &&socket, IRenderSession *rs = nullptr) - : AsyncObject(ioc), _Socket(std::move(socket)), Socket(*socket), RS(rs) + : AsyncObject(ioc), Socket(std::move(socket)), RS(rs) { - assert(socket.get()); + assert(Socket.get()); co_spawn(run()); } @@ -29,23 +39,38 @@ public: // Начать игровой протокол в авторизированном сокете static coro> asyncInitGameProtocol(asio::io_context &ioc, tcp::socket &&socket, std::function onProgress = nullptr); + void shutdown(EnumDisconnect type); + + bool isConnected() { + return IsConnected; + } + + void waitShutdown() { + UseLock.wait_no_use(); + } // ISurfaceEventListener - // virtual void onResize(uint32_t width, uint32_t height) override; - // virtual void onChangeFocusState(bool isFocused) override; - // virtual void onCursorPosChange(int32_t width, int32_t height) override; - // virtual void onCursorMove(float xMove, float yMove) override; - // virtual void onFrameRendering() override; - // virtual void onFrameRenderEnd() override; + virtual void onResize(uint32_t width, uint32_t height) override; + virtual void onChangeFocusState(bool isFocused) override; + virtual void onCursorPosChange(int32_t width, int32_t height) override; + virtual void onCursorMove(float xMove, float yMove) override; + virtual void onFrameRendering() override; + virtual void onFrameRenderEnd() override; - // virtual void onCursorBtn(EnumCursorBtn btn, bool state) override; - // virtual void onKeyboardBtn(int btn, int state) override; - // virtual void onJoystick() override; + virtual void onCursorBtn(EnumCursorBtn btn, bool state) override; + virtual void onKeyboardBtn(int btn, int state) override; + virtual void onJoystick() override; private: coro<> run(); + void protocolError(); + coro<> readPacket(Net::AsyncSocket &sock); + coro<> rP_System(Net::AsyncSocket &sock); + coro<> rP_Resource(Net::AsyncSocket &sock); + coro<> rP_Definition(Net::AsyncSocket &sock); + coro<> rP_Content(Net::AsyncSocket &sock); }; } \ No newline at end of file diff --git a/Src/Client/Vulkan/Vulkan.cpp b/Src/Client/Vulkan/Vulkan.cpp index 99ac84a..fb915cf 100644 --- a/Src/Client/Vulkan/Vulkan.cpp +++ b/Src/Client/Vulkan/Vulkan.cpp @@ -1,8 +1,13 @@ +#include #include #include #include #include #include "Vulkan.hpp" +#include "Client/ServerSession.hpp" +#include "Common/Async.hpp" +#include "Common/Net.hpp" +#include "assets.hpp" #include "imgui.h" #include #ifdef HAS_IMGUI @@ -20,10 +25,22 @@ #include #include #include +#include "VulkanRenderSession.hpp" +#include extern void LoadSymbolsVulkan(TOS::DynamicLibrary &library); -namespace TOS::VK { +namespace LV::Client::VK { + +struct ServerObj { + Server::GameServer GS; + Net::Server LS; + + ServerObj(asio::io_context &ioc) + : GS(ioc, ""), LS(ioc, [&](tcp::socket sock) -> coro<> { co_await GS.pushSocketConnect(std::move(sock)); }, 7890) + { + } +}; ByteBuffer loadPNG(std::ifstream &&read, int &width, int &height, bool &hasAlpha, bool flipOver) { @@ -55,35 +72,38 @@ ByteBuffer loadPNG(std::istream &&read, int &width, int &height, bool &hasAlpha, return buff; } -IWindowCallbackListener::~IWindowCallbackListener() = default; - -void IWindowCallbackListener::onFrameBufferResize(uint32_t width, uint32_t height) {} -void IWindowCallbackListener::onScale(float x, float y) {} -void IWindowCallbackListener::onMouseClick(int btn, int state, int mods) {} -void IWindowCallbackListener::onMousePos(double x, double y) {} -void IWindowCallbackListener::onKeyboardClick(int key, int scancode, int action, int mods) {} -void IWindowCallbackListener::onFocus(int focused) {} - -Vulkan::Vulkan(uint16_t width, uint16_t height) +Vulkan::Vulkan(asio::io_context &ioc) + : AsyncObject(ioc), GuardLock(ioc.get_executor()) { - if(width) - Screen.Width = width; - if(height) - Screen.Height = height; + Screen.Width = 1920/2; + Screen.Height = 1080/2; getSettingsNext() = getBestSettings(); + reInit(); + + addImGUIFont(LV::getResource("default.ttf")->makeView()); + Game.ImGuiInterfaces.push_back(&Vulkan::gui_MainMenu); + + Game.MainThread = std::thread([&]() { + auto useLock = Game.UseLock.lock(); + run(); + GuardLock.reset(); + + if(Game.Session) { + Game.Session->shutdown(EnumDisconnect::ByInterface); + Game.Session = nullptr; + Game.RSession = nullptr; + } + + if(Game.Server) { + Game.Server->GS.shutdown("Завершение работы из-за остановки клиента"); + Game.Server = nullptr; + } + }); } Vulkan::~Vulkan() { - WindowEventListener = nullptr; - for(size_t watchdog = 0; watchdog < 100 && isAlive(); watchdog++) - { - glfwSetWindowShouldClose(Graphics.Window, 1); - Time::sleep3(100); - } - - if(isAlive()) - LOGGER.error() << "WatchDog: Не дождались завершения потока рендера в течении 10 секунд"; + Game.UseLock.wait_no_use(); for(std::shared_ptr dependent : ROS_Dependents) dependent->free(this); @@ -96,6 +116,8 @@ Vulkan::~Vulkan() glfwDestroyWindow(Graphics.Window); Graphics.Window = nullptr; } + + Game.MainThread.join(); } void Vulkan::run() @@ -122,8 +144,29 @@ void Vulkan::run() } } - if(CallBeforeDraw) - CallBeforeDraw(this); + if(glfwWindowShouldClose(Graphics.Window)) { + NeedShutdown = true; + } + + if(Game.Session) { + ServerSession &sobj = *Game.Session; + + // Спрятать или показать курсор + { + int mode = glfwGetInputMode(Graphics.Window, GLFW_CURSOR); + if(mode == GLFW_CURSOR_HIDDEN && sobj.CursorMode != ISurfaceEventListener::EnumCursorMoveMode::MoveAndHidden) + glfwSetInputMode(Graphics.Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + else if(mode == GLFW_CURSOR_NORMAL && sobj.CursorMode != ISurfaceEventListener::EnumCursorMoveMode::Default) { + glfwSetInputMode(Graphics.Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + glfwSetCursorPos(Graphics.Window, Screen.Width/2., Screen.Height/2.); + } + } + + + } + + // if(CallBeforeDraw) + // CallBeforeDraw(this); glfwPollEvents(); @@ -208,8 +251,8 @@ void Vulkan::run() vkCmdSetScissor(Graphics.CommandBufferRender, 0, 1, &scissor); } - if(CallOnDraw) - CallOnDraw(this, 0, Graphics.CommandBufferRender); + // if(CallOnDraw) + // CallOnDraw(this, 0, Graphics.CommandBufferRender); vkCmdEndRenderPass(Graphics.CommandBufferRender); @@ -254,7 +297,7 @@ void Vulkan::run() { const VkClearValue clear_values[2] = { - [0] = { .color = { .float32 = { 0.2f, 0.2f, 0.4f, 1.2f }}}, + [0] = { .color = { .float32 = { 0.1f, 0.1f, 0.1f, 1.0f }}}, [1] = { .depthStencil = { 1, 0 } }, }; @@ -279,12 +322,13 @@ void Vulkan::run() ImGui_ImplVulkan_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); - #endif - if(CallOnDraw) - CallOnDraw(this, 1, Graphics.CommandBufferRender); + ImGui::SetNextWindowPos({0, 0}); + ImGui::SetNextWindowSize({(float) Screen.Width, (float) Screen.Height}); + + assert(Game.ImGuiInterfaces.size()); + (this->*Game.ImGuiInterfaces.back())(); - #ifdef HAS_IMGUI ImGui::Render(); ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), Graphics.CommandBufferRender); #endif @@ -819,45 +863,52 @@ void Vulkan::glfwCallbackOnResize(GLFWwindow *window, int width, int height) handler->freeSwapchains(); handler->buildSwapchains(); - if(handler->WindowEventListener) - handler->WindowEventListener->onFrameBufferResize(width, height); + if(handler->Game.Session) + handler->Game.Session->onResize(width, height); } } void Vulkan::glfwCallbackOnMouseButton(GLFWwindow* window, int button, int action, int mods) { Vulkan *handler = (Vulkan*) glfwGetWindowUserPointer(window); - if(handler && handler->WindowEventListener) - handler->WindowEventListener->onMouseClick(button, action, mods); + + if(handler->Game.Session) + handler->Game.Session->onCursorBtn((ISurfaceEventListener::EnumCursorBtn) button, action); } void Vulkan::glfwCallbackOnCursorPos(GLFWwindow* window, double xpos, double ypos) { Vulkan *handler = (Vulkan*) glfwGetWindowUserPointer(window); - if(handler && handler->WindowEventListener) - handler->WindowEventListener->onMousePos(xpos, ypos); + + if(handler->Game.Session) { + ServerSession &sobj = *handler->Game.Session; + if(sobj.CursorMode == ISurfaceEventListener::EnumCursorMoveMode::Default) { + sobj.onCursorPosChange((int32_t) xpos, (int32_t) ypos); + } else { + glfwSetCursorPos(handler->Graphics.Window, handler->Screen.Width/2., handler->Screen.Height/2.); + sobj.onCursorMove(xpos-handler->Screen.Width/2., handler->Screen.Height/2.-ypos); + } + } } void Vulkan::glfwCallbackOnScale(GLFWwindow* window, float xscale, float yscale) { Vulkan *handler = (Vulkan*) glfwGetWindowUserPointer(window); - if(handler && handler->WindowEventListener) - handler->WindowEventListener->onScale(xscale, yscale); } void Vulkan::glfwCallbackOnKey(GLFWwindow* window, int key, int scancode, int action, int mods) { Vulkan *handler = (Vulkan*) glfwGetWindowUserPointer(window); - if(handler && handler->WindowEventListener) - handler->WindowEventListener->onKeyboardClick(key, scancode, action, mods); + if(handler->Game.Session) + handler->Game.Session->onKeyboardBtn(key, action); } void Vulkan::glfwCallbackOnFocus(GLFWwindow* window, int focused) { Vulkan *handler = (Vulkan*) glfwGetWindowUserPointer(window); - if(handler && handler->WindowEventListener) - handler->WindowEventListener->onFocus(focused); + if(handler->Game.Session) + handler->Game.Session->onChangeFocusState(focused); } void Vulkan::checkLibrary() @@ -1066,7 +1117,7 @@ void Vulkan::checkLibrary() glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); //glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); - Graphics.Window = glfwCreateWindow(Screen.Width, Screen.Height, "Duckout", nullptr, nullptr); + Graphics.Window = glfwCreateWindow(Screen.Width, Screen.Height, "LuaVox", nullptr, nullptr); if (!Graphics.Window) { const char *error_msg; @@ -1716,12 +1767,10 @@ void Vulkan::deInitVulkan() if(Graphics.ImGuiDescPool) vkDestroyDescriptorPool(Graphics.Device, Graphics.ImGuiDescPool, nullptr); - // Удаляем SwapChain if(Graphics.Swapchain) vkDestroySwapchainKHR(Graphics.Device, Graphics.Swapchain, nullptr); - // Очистка буферов команд if(Graphics.CommandBufferData) vkFreeCommandBuffers(Graphics.Device, Graphics.Pool, 1, &Graphics.CommandBufferData); @@ -1729,7 +1778,6 @@ void Vulkan::deInitVulkan() if(Graphics.CommandBufferRender) vkFreeCommandBuffers(Graphics.Device, Graphics.Pool, 1, &Graphics.CommandBufferRender); - // Освобождение пула команд if(Graphics.Pool) vkDestroyCommandPool(Graphics.Device, Graphics.Pool, nullptr); @@ -1739,8 +1787,8 @@ void Vulkan::deInitVulkan() vkDestroyRenderPass(Graphics.Device, Graphics.RenderPass, nullptr); // Освобождение виртуального устройства - if(Graphics.Device) - vkDestroyDevice(Graphics.Device, nullptr); + //if(Graphics.Device) + // vkDestroyDevice(Graphics.Device, nullptr); } if(Graphics.Surface) @@ -1837,7 +1885,7 @@ Vulkan& Vulkan::operator<<(std::shared_ptr dependent) return *this; } -void Vulkan::addImGUIFont(const ByteBuffer &font) { +void Vulkan::addImGUIFont(std::string_view view) { ImFontConfig fontConfig; fontConfig.MergeMode = false; fontConfig.PixelSnapH = true; @@ -1845,16 +1893,121 @@ void Vulkan::addImGUIFont(const ByteBuffer &font) { fontConfig.OversampleV = 1; auto &io = ImGui::GetIO(); - uint8_t *fontPtr = new uint8_t[font.size()]; - std::copy(font.begin(), font.end(), fontPtr); + uint8_t *fontPtr = new uint8_t[view.size()]; + std::copy(view.begin(), view.end(), fontPtr); try{ - io.Fonts->AddFontFromMemoryTTF(fontPtr, font.size(), 16.0f, &fontConfig, io.Fonts->GetGlyphRangesCyrillic()); + io.Fonts->AddFontFromMemoryTTF(fontPtr, view.size(), 16.0f, &fontConfig, io.Fonts->GetGlyphRangesCyrillic()); } catch(...) { delete[] fontPtr; throw; } } +void Vulkan::gui_MainMenu() { + if(!ImGui::Begin("MainMenu", nullptr, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) + return; + + static struct { + char Address[256] = "localhost", Username[256], Password[256]; + bool Cancel = false, InProgress = false; + std::string Progress; + + std::unique_ptr Socket; + + coro<> connect(asio::io_context &ioc) { + try { + std::string a(Address, strlen(Address)); + std::string u(Username, strlen(Username)); + std::string p(Password, strlen(Password)); + + tcp::socket sock = co_await Net::asyncConnectTo(a, [&](const std::string &text) { + Progress += text; + }); + + co_await Client::ServerSession::asyncAuthorizeWithServer(sock, u, p, 1, [&](const std::string &text) { + Progress += text; + }); + + Socket = co_await Client::ServerSession::asyncInitGameProtocol(ioc, std::move(sock), [&](const std::string &text) { + Progress += text; + }); + } catch(const std::exception &exc) { + Progress += "\n-> "; + Progress += exc.what(); + } + + InProgress = false; + + co_return; + } + } ConnectionProgress; + + ImGui::InputText("Address", ConnectionProgress.Address, sizeof(ConnectionProgress.Address)); + ImGui::InputText("Username", ConnectionProgress.Username, sizeof(ConnectionProgress.Username)); + ImGui::InputText("Password", ConnectionProgress.Password, sizeof(ConnectionProgress.Password), ImGuiInputTextFlags_Password); + + if(!ConnectionProgress.InProgress && !ConnectionProgress.Socket) { + if(ImGui::Button("Подключиться")) { + ConnectionProgress.InProgress = true; + ConnectionProgress.Cancel = false; + ConnectionProgress.Progress.clear(); + co_spawn(ConnectionProgress.connect(IOC)); + } + + if(!Game.Server) { + if(ImGui::Button("Запустить сервер")) { + try { + Game.Server = std::make_unique(IOC); + ConnectionProgress.Progress = "Сервер запущен на порту " + std::to_string(Game.Server->LS.getPort()); + } catch(const std::exception &exc) { + ConnectionProgress.Progress = "Не удалось запустить внутренний сервер: " + std::string(exc.what()); + } + } + } else { + if(!Game.Server->GS.isAlive()) + Game.Server = nullptr; + else if(ImGui::Button("Остановить сервер")) { + Game.Server->GS.shutdown("Сервер останавливается по запросу интерфейса"); + } + } + } + + if(ConnectionProgress.InProgress) { + if(ImGui::Button("Отмена")) + ConnectionProgress.Cancel = true; + } + + if(!ConnectionProgress.Progress.empty()) { + if(ImGui::BeginChild("Прогресс", {0, 0}, ImGuiChildFlags_Borders, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_HorizontalScrollbar)) { + ImGui::Text("Прогресс:\n%s", ConnectionProgress.Progress.c_str()); + ImGui::EndChild(); + } + } + + if(ConnectionProgress.Socket) { + std::unique_ptr sock = std::move(ConnectionProgress.Socket); + Game.RSession = std::make_unique(this); + Game.Session = std::make_unique(IOC, std::move(sock), Game.RSession.get()); + Game.RSession->setServerSession(Game.Session.get()); + Game.ImGuiInterfaces.push_back(&Vulkan::gui_ConnectedToServer); + } + + + ImGui::End(); +} + +void Vulkan::gui_ConnectedToServer() { + if(Game.Session) { + if(Game.Session->isConnected()) + return; + + Game.RSession = nullptr; + Game.Session = nullptr; + Game.ImGuiInterfaces.pop_back(); + } +} + + EnumRebuildType IVulkanDependent::needRebuild() { return EnumRebuildType::None; } IVulkanDependent::~IVulkanDependent() = default; @@ -1947,7 +2100,7 @@ Vulkan::vkInstance::~vkInstance() if(!Instance) return; - vkDestroyInstance(Instance, nullptr); + //vkDestroyInstance(Instance, nullptr); } diff --git a/Src/Client/Vulkan/Vulkan.hpp b/Src/Client/Vulkan/Vulkan.hpp index 39d325d..140f1e2 100644 --- a/Src/Client/Vulkan/Vulkan.hpp +++ b/Src/Client/Vulkan/Vulkan.hpp @@ -1,6 +1,10 @@ #pragma once +#include "Client/ServerSession.hpp" +#include "Common/Async.hpp" #include +#include +#include #include #include #include @@ -26,7 +30,10 @@ #define IMGUI_ENABLE_STB_TEXTEDIT_UNICODE -namespace TOS::VK { +namespace LV::Client::VK { + +class VulkanRenderSession; +using namespace TOS; ByteBuffer loadPNG(std::ifstream &&file, int &width, int &height, bool &hasAlpha, bool flipOver = true); ByteBuffer loadPNG(std::istream &&file, int &width, int &height, bool &hasAlpha, bool flipOver = true); @@ -49,21 +56,7 @@ struct Settings { } }; - -class IWindowCallbackListener { -public: - IWindowCallbackListener() = default; - virtual ~IWindowCallbackListener(); - - virtual void onFrameBufferResize(uint32_t width, uint32_t height); - virtual void onScale(float x, float y); - virtual void onMouseClick(int btn, int state, int mods); - virtual void onMousePos(double x, double y); - virtual void onKeyboardClick(int key, int scancode, int action, int mods); - virtual void onFocus(int focused); -}; - - +class ServerObj; class DescriptorLayout; class Pipeline; class DescriptorPool; @@ -76,7 +69,7 @@ class Buffer; Vulkan.reInit(); */ -class Vulkan { +class Vulkan : public AsyncObject { private: struct vkInstanceLayer { std::string LayerName = "nullptr", Description = "nullptr"; @@ -173,10 +166,9 @@ private: VkCommandBuffer Cmd = nullptr; VkFramebuffer FrameBuffer = nullptr; }; - - std::function CallOnDraw; - std::function CallBeforeDraw; + bool NeedShutdown = false; + asio::executor_work_guard GuardLock; public: struct { @@ -243,6 +235,16 @@ public: DrawState State = DrawState::Begin; } Screen; + struct { + DestroyLock UseLock; + std::thread MainThread; + std::unique_ptr RSession; + std::unique_ptr Session; + + std::list ImGuiInterfaces; + std::unique_ptr Server; + } Game; + private: Logger LOGGER = "Vulkan"; Settings @@ -256,7 +258,6 @@ private: std::optional LibraryVulkan; std::queue> VulkanContext; - std::shared_ptr WindowEventListener; // Объекты рисовки std::unordered_set> ROS_Dependents; @@ -298,7 +299,7 @@ private: void buildSwapchains(); public: - Vulkan(uint16_t width = 0, uint16_t height = 0); + Vulkan(asio::io_context &ioc); ~Vulkan(); Vulkan(const Vulkan&) = delete; @@ -339,12 +340,6 @@ public: Settings& getSettingsNext() { return SettingsNext; } bool isAlive() { return false; } - void start(std::function &&onDraw, std::function &&beforeDraw = {}) - { - CallOnDraw = std::move(onDraw); - CallBeforeDraw = std::move(beforeDraw); - run(); - } // Добавить обработчик перед началом рисовки кадра void beforeDraw(std::function &&callback) { @@ -356,10 +351,11 @@ public: return std::this_thread::get_id() == Graphics.ThisThread; } - void setWindowEventListener(std::shared_ptr listener) { WindowEventListener = listener; } void shutdown() { NeedShutdown = true; } + void addImGUIFont(std::string_view view); - void addImGUIFont(const ByteBuffer &font); + void gui_MainMenu(); + void gui_ConnectedToServer(); }; enum class EnumRebuildType { diff --git a/Src/Client/Vulkan/VulkanRenderSession.cpp b/Src/Client/Vulkan/VulkanRenderSession.cpp new file mode 100644 index 0000000..db7bc08 --- /dev/null +++ b/Src/Client/Vulkan/VulkanRenderSession.cpp @@ -0,0 +1,63 @@ +#include "VulkanRenderSession.hpp" + +namespace LV::Client::VK { + + +VulkanRenderSession::VulkanRenderSession(VK::Vulkan *vkInst) + : VkInst(vkInst), + MainAtlas(vkInst) +{ + assert(VkInst); +} + +VulkanRenderSession::~VulkanRenderSession() { + +} + +void VulkanRenderSession::onDefTexture(TextureId_c id, std::vector &&info) { + +} + +void VulkanRenderSession::onDefTextureLost(const std::vector &&lost) { + +} + +void VulkanRenderSession::onDefModel(ModelId_c id, std::vector &&info) { + +} + +void VulkanRenderSession::onDefModelLost(const std::vector &&lost) { + +} + +void VulkanRenderSession::onDefWorldUpdates(const std::vector &updates) { + +} + +void VulkanRenderSession::onDefVoxelUpdates(const std::vector &updates) { + +} + +void VulkanRenderSession::onDefNodeUpdates(const std::vector &updates) { + +} + +void VulkanRenderSession::onDefPortalUpdates(const std::vector &updates) { + +} + +void VulkanRenderSession::onDefEntityUpdates(const std::vector &updates) { + +} + +void VulkanRenderSession::onChunksChange(WorldId_c worldId, const std::vector &changeOrAddList, const std::vector &remove) { + +} + +void VulkanRenderSession::setCameraPos(WorldId_c worldId, Pos::Object pos, glm::quat quat) { + WorldId = worldId; + Pos = pos; + Quat = quat; +} + +} \ No newline at end of file diff --git a/Src/Client/Vulkan/VulkanRenderSession.hpp b/Src/Client/Vulkan/VulkanRenderSession.hpp new file mode 100644 index 0000000..a1667a6 --- /dev/null +++ b/Src/Client/Vulkan/VulkanRenderSession.hpp @@ -0,0 +1,43 @@ +#pragma once +#include "Client/Abstract.hpp" +#include + + +namespace LV::Client::VK { + +class VulkanRenderSession : public IRenderSession { + VK::Vulkan *VkInst; + IServerSession *ServerSession = nullptr; + + WorldId_c WorldId; + Pos::Object Pos; + glm::quat Quat; + + VK::AtlasImage MainAtlas; + std::map ServerToAtlas; + +public: + VulkanRenderSession(VK::Vulkan *vkInst); + virtual ~VulkanRenderSession(); + + void setServerSession(IServerSession *serverSession) { + ServerSession = serverSession; + assert(serverSession); + } + + virtual void onDefTexture(TextureId_c id, std::vector &&info) override; + virtual void onDefTextureLost(const std::vector &&lost) override; + virtual void onDefModel(ModelId_c id, std::vector &&info) override; + virtual void onDefModelLost(const std::vector &&lost) override; + + virtual void onDefWorldUpdates(const std::vector &updates) override; + virtual void onDefVoxelUpdates(const std::vector &updates) override; + virtual void onDefNodeUpdates(const std::vector &updates) override; + virtual void onDefPortalUpdates(const std::vector &updates) override; + virtual void onDefEntityUpdates(const std::vector &updates) override; + + virtual void onChunksChange(WorldId_c worldId, const std::vector &changeOrAddList, const std::vector &remove) override; + virtual void setCameraPos(WorldId_c worldId, Pos::Object pos, glm::quat quat) override; +}; + +} \ No newline at end of file diff --git a/Src/Common/Abstract.hpp b/Src/Common/Abstract.hpp index 233ba9e..db0fa00 100644 --- a/Src/Common/Abstract.hpp +++ b/Src/Common/Abstract.hpp @@ -32,7 +32,7 @@ struct Local16 { using Key = uint16_t; operator Key() const { - return Key(X) | (Key(Y) << 4) | (Key(Z) << 8); + return Key(uint8_t(X)) | (Key(uint8_t(Y) << 4)) | (Key(uint8_t(Z)) << 8); }; Local4_u left() const { return Local4_u{uint8_t(uint16_t(X) >> 2), uint8_t(uint16_t(Y) >> 2), uint8_t(uint16_t(Z) >> 2)}; } @@ -74,18 +74,18 @@ struct GlobalNode { using Key = uint64_t; operator Key() const { - return Key(X) | (Key(Y) << 20) | (Key(Z) << 40); + return Key(uint32_t(X)) | (Key(uint32_t(Y) << 20)) | (Key(uint32_t(Z)) << 40); }; auto operator<=>(const GlobalNode&) const = default; }; struct GlobalChunk { - int16_t X : 16, Y : 16, Z : 16; + int16_t X, Y, Z; using Key = uint64_t; operator Key() const { - return Key(X) | (Key(Y) << 16) | (Key(Z) << 32); + return Key(uint16_t(X)) | (Key(uint16_t(Y)) << 16) | (Key(uint16_t(Z)) << 32); }; auto operator<=>(const GlobalChunk&) const = default; @@ -96,7 +96,7 @@ struct GlobalRegion { using Key = uint64_t; operator Key() const { - return Key(X) | (Key(Y) << 12) | (Key(Z) << 24); + return Key(uint16_t(X)) | (Key(uint16_t(Y) << 12)) | (Key(uint16_t(Z)) << 24); }; auto operator<=>(const GlobalRegion&) const = default; @@ -118,6 +118,20 @@ struct LightPrism { uint8_t R : 2, G : 2, B : 2; }; +// Идентификаторы на стороне клиента +using TextureId_c = uint16_t; +using SoundId_c = uint16_t; +using ModelId_c = uint16_t; + +using DefWorldId_c = uint8_t; +using DefVoxelId_c = uint16_t; +using DefNodeId_c = uint16_t; +using DefPortalId_c = uint8_t; +using WorldId_c = uint8_t; +using PortalId_c = uint8_t; +using DefEntityId_c = uint16_t; +using EntityId_c = uint16_t; + } diff --git a/Src/Common/Async.hpp b/Src/Common/Async.hpp index faf4085..0e96959 100644 --- a/Src/Common/Async.hpp +++ b/Src/Common/Async.hpp @@ -112,7 +112,7 @@ public: return; std::atomic_bool flag = false; - Deadline.async_wait([&](boost::system::error_code errc) { flag.store(true); }); + Deadline.async_wait([&](boost::system::error_code errc) { flag.store(true); flag.notify_all(); }); lock.unlock(); flag.wait(false); } @@ -129,7 +129,7 @@ public: return; std::atomic_bool flag = false; - Deadline.async_wait([&](boost::system::error_code errc) { flag.store(true); }); + Deadline.async_wait([&](boost::system::error_code errc) { flag.store(true); flag.notify_all(); }); lock.unlock(); flag.wait(false); } diff --git a/Src/Common/Lockable.hpp b/Src/Common/Lockable.hpp index 10e2e53..f8d6005 100644 --- a/Src/Common/Lockable.hpp +++ b/Src/Common/Lockable.hpp @@ -110,10 +110,12 @@ public: : Lock(lock) { lock.UseCount++; + lock.UseCount.notify_all(); } ~Guard() { Lock.UseCount--; + Lock.UseCount.notify_all(); } private: diff --git a/Src/Common/Net.cpp b/Src/Common/Net.cpp index 21de8e1..134f2a9 100644 --- a/Src/Common/Net.cpp +++ b/Src/Common/Net.cpp @@ -1,6 +1,7 @@ #include "Net.hpp" #include #include +#include namespace LV::Net { @@ -17,15 +18,14 @@ bool Server::isStopped() { void Server::stop() { NeedClose = true; + NeedClose.notify_all(); if(Acceptor.is_open()) Acceptor.close(); } void Server::wait() { - if(!IsAlive) - return; - - Lock.wait(); + while(bool val = IsAlive) + IsAlive.wait(val); } coro Server::async_wait() { @@ -45,6 +45,7 @@ coro Server::run() { } IsAlive.store(false); + IsAlive.notify_all(); Lock.cancel(); } @@ -56,6 +57,7 @@ AsyncSocket::~AsyncSocket() { SendPackets.Context->NeedShutdown = true; SendPackets.SenderGuard.cancel(); + WorkDeadline.cancel(); } void AsyncSocket::pushPackets(std::vector *simplePackets, std::vector *smartPackets) { @@ -135,6 +137,10 @@ coro<> AsyncSocket::read(std::byte *data, uint32_t size) { } } +void AsyncSocket::closeRead() { + Socket.shutdown(boost::asio::socket_base::shutdown_receive); +} + coro<> AsyncSocket::waitForSend() { asio::deadline_timer waiter(IOC); @@ -249,8 +255,7 @@ coro asyncConnectTo(const std::string address, std::function asyncConnectTo(const std::string address, std::function *simplePackets, std::vector *smartPackets = nullptr); + + void pushPacket(Packet &&simplePacket) { + std::vector out(1); + out[0] = std::move(simplePacket); + pushPackets(&out); + } std::string getError() const; bool isAlive() const; coro<> read(std::byte *data, uint32_t size); + void closeRead(); template or std::is_same_v, int> = 0> coro read() { diff --git a/Src/Common/Packets.hpp b/Src/Common/Packets.hpp index 45afe1b..10892be 100644 --- a/Src/Common/Packets.hpp +++ b/Src/Common/Packets.hpp @@ -6,8 +6,14 @@ namespace LV { -namespace ToServer { +enum struct EnumDisconnect { + ByInterface, + CriticalError, + ProtocolError +}; +namespace ToServer { + struct PacketQuat { uint8_t Data[5]; @@ -51,6 +57,17 @@ struct PacketQuat { */ +// Первый уровень +enum struct L1 : uint8_t { + System, +}; + +// Второй уровень +enum struct L2System : uint8_t { + InitEnd, + Disconnect +}; + } namespace ToClient { diff --git a/Src/Server/Abstract.hpp b/Src/Server/Abstract.hpp index 65cd43d..44f50a4 100644 --- a/Src/Server/Abstract.hpp +++ b/Src/Server/Abstract.hpp @@ -8,20 +8,6 @@ namespace LV::Server { -// Идентификаторы на стороне клиента -using TextureId_c = uint16_t; -using SoundId_c = uint16_t; -using ModelId_c = uint16_t; - -using DefWorldId_c = uint8_t; -using WorldId_c = uint8_t; -using VoxelId_c = uint16_t; -using NodeId_c = uint16_t; -using DefPortalId_c = uint8_t; -using PortalId_c = uint8_t; -using DefEntityId_c = uint16_t; -using EntityId_c = uint16_t; - using ResourceId_t = uint32_t; // Двоичные данные diff --git a/Src/Server/GameServer.cpp b/Src/Server/GameServer.cpp index 72dfaf5..8d897f4 100644 --- a/Src/Server/GameServer.cpp +++ b/Src/Server/GameServer.cpp @@ -1,6 +1,7 @@ #include "GameServer.hpp" #include "Common/Abstract.hpp" #include "Common/Net.hpp" +#include "Common/Packets.hpp" #include "Server/Abstract.hpp" #include "Server/ContentEventController.hpp" #include @@ -248,7 +249,7 @@ void GameServer::run() { if(IsGoingShutdown) { // Отключить игроков for(std::unique_ptr &cec : Game.CECs) { - cec->Remote->shutdown(ShutdownReason); + cec->Remote->shutdown(EnumDisconnect::ByInterface, ShutdownReason); } // Сохранить данные @@ -259,7 +260,7 @@ void GameServer::run() { auto lock = External.NewConnectedPlayers.lock_write(); for(std::unique_ptr &client : *lock) { - client->shutdown(ShutdownReason); + client->shutdown(EnumDisconnect::ByInterface, ShutdownReason); } bool hasNewConnected = !lock->empty(); @@ -361,6 +362,14 @@ void GameServer::stepPlayers() { for(std::unique_ptr &cec : Game.CECs) { // Убрать отключившихся if(!cec->Remote->isConnected()) { + for(auto wPair : cec->SubscribedRegions) { + auto wIter = Expanse.Worlds.find(wPair.first); + if(wIter == Expanse.Worlds.end()) + continue; + + wIter->second->onCEC_RegionsLost(cec.get(), wPair.second); + } + cec.reset(); } diff --git a/Src/Server/GameServer.hpp b/Src/Server/GameServer.hpp index 2144eec..35f012c 100644 --- a/Src/Server/GameServer.hpp +++ b/Src/Server/GameServer.hpp @@ -114,7 +114,8 @@ public: void shutdown(const std::string reason) { - ShutdownReason = reason; + if(ShutdownReason.empty()) + ShutdownReason = reason; IsGoingShutdown = true; } diff --git a/Src/Server/RemoteClient.cpp b/Src/Server/RemoteClient.cpp index 92ac15d..1af42a2 100644 --- a/Src/Server/RemoteClient.cpp +++ b/Src/Server/RemoteClient.cpp @@ -14,7 +14,11 @@ namespace LV::Server { RemoteClient::~RemoteClient() { - shutdown("~RemoteClient()"); + shutdown(EnumDisconnect::ByInterface, "~RemoteClient()"); + if(Socket.isAlive()) { + Socket.closeRead(); + } + UseLock.wait_no_use(); } @@ -23,7 +27,7 @@ coro<> RemoteClient::run() { try { while(!IsGoingShutdown && IsConnected) { - co_await Socket.read(); + co_await readPacket(Socket); } } catch(const std::exception &exc) { if(const auto *errc = dynamic_cast(&exc); @@ -40,7 +44,7 @@ coro<> RemoteClient::run() { co_return; } -void RemoteClient::shutdown(const std::string reason) { +void RemoteClient::shutdown(EnumDisconnect type, const std::string reason) { if(IsGoingShutdown) return; @@ -48,7 +52,17 @@ void RemoteClient::shutdown(const std::string reason) { NextPacket << (uint8_t) ToClient::L1::System << (uint8_t) ToClient::L2System::Disconnect - << reason; + << (uint8_t) type << reason; + + std::string info; + if(type == EnumDisconnect::ByInterface) + info = "по запросу интерфейса " + reason; + else if(type == EnumDisconnect::CriticalError) + info = "на сервере произошла критическая ошибка " + reason; + else if(type == EnumDisconnect::ProtocolError) + info = "ошибка протокола (сервер) " + reason; + + LOG.info() << "Игрок '" << Username << "' отключился " << info; } @@ -70,7 +84,7 @@ void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk std::unordered_set NeedVoxelsSet(NeedVoxels.begin(), NeedVoxels.end()); // Собираем информацию о конвертации идентификаторов - std::unordered_map LocalRemapper; + std::unordered_map LocalRemapper; for(DefVoxelId_t vId : NeedVoxelsSet) { LocalRemapper[vId] = ResRemap.DefVoxels.toClient(vId); } @@ -87,7 +101,7 @@ void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk // Определение больше не используется ResUses.DefVoxel.erase(ResUses.DefVoxel.find(id)); - VoxelId_c cId = ResRemap.DefVoxels.erase(id); + DefVoxelId_c cId = ResRemap.DefVoxels.erase(id); // TODO: отправить пакет потери идентификатора LOG.debug() << "Определение вокселя потеряно: " << id << " -> " << cId; } @@ -99,7 +113,7 @@ void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk if(++ResUses.DefVoxel[id] == 1) { // Определение только появилось NextRequest.NewVoxels.push_back(id); - VoxelId_c cId = ResRemap.DefVoxels.toClient(id); + DefVoxelId_c cId = ResRemap.DefVoxels.toClient(id); LOG.debug() << "Новое определение вокселя: " << id << " -> " << cId; } } @@ -108,24 +122,23 @@ void RemoteClient::prepareChunkUpdate_Voxels(WorldId_t worldId, Pos::GlobalChunk prevSet = std::move(nextSet); } - // TODO: отправить новую информацию о расположении вокселей - LOG.debug() << "Новый чанк: " << worldId << " / " << chunkPos.X << ":" << chunkPos.Y << ":" << chunkPos.Z; - // Packet Id - // NextPacket << uint16_t(0); - // NextPacket << wcId << Pos::GlobalChunk::Key(chunkPos); - // NextPacket << uint16_t(voxels.size()); + LOG.debug() << "Воксели чанка: " << worldId << " / " << chunkPos.X << ":" << chunkPos.Y << ":" << chunkPos.Z; - // for(const VoxelCube &cube : voxels) { - // NextPacket << LocalRemapper[cube.VoxelId] - // << cube.Left.X << cube.Left.Y << cube.Left.Z - // << cube.Right.X << cube.Right.Y << cube.Right.Z; - // } + NextPacket << (uint8_t) ToClient::L1::Content + << (uint8_t) ToClient::L2Content::ChunkVoxels << wcId + << Pos::GlobalChunk::Key(chunkPos); + NextPacket << uint16_t(voxels.size()); + // TODO: + for(const VoxelCube &cube : voxels) { + NextPacket << LocalRemapper[cube.VoxelId] + << cube.Left.X << cube.Left.Y << cube.Left.Z + << cube.Right.X << cube.Right.Y << cube.Right.Z; + } } void RemoteClient::prepareChunkUpdate_Nodes(WorldId_t worldId, Pos::GlobalChunk chunkPos, const std::unordered_map &nodes) { - // Перебиндить идентификаторы нод } @@ -144,7 +157,7 @@ void RemoteClient::prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkP // Определение больше не используется ResUses.DefVoxel.erase(ResUses.DefVoxel.find(id)); - VoxelId_c cId = ResRemap.DefVoxels.erase(id); + DefVoxelId_c cId = ResRemap.DefVoxels.erase(id); // TODO: отправить пакет потери идентификатора LOG.debug() << "Определение вокселя потеряно: " << id << " -> " << cId; } @@ -154,7 +167,7 @@ void RemoteClient::prepareChunkRemove(WorldId_t worldId, Pos::GlobalChunk chunkP WorldId_c cwId = ResRemap.Worlds.toClient(worldId); NextPacket << (uint8_t) ToClient::L1::Content << (uint8_t) ToClient::L2Content::RemoveChunk - << cwId << chunkPos.X << chunkPos.Y << chunkPos.Z; + << cwId << Pos::GlobalChunk::Key(chunkPos); } void RemoteClient::prepareWorldNew(WorldId_t worldId, World* world) @@ -166,7 +179,7 @@ void RemoteClient::prepareWorldNew(WorldId_t worldId, World* world) DefWorldId_c cdId = ResRemap.DefWorlds.toClient(res.DefId); NextRequest.NewWorlds.push_back(res.DefId); - LOG.debug() << "Новое определение мира: " << res.DefId << " -> " << cdId; + LOG.debug() << "Новое определение мира: " << res.DefId << " -> " << int(cdId); NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::World << cdId; @@ -468,7 +481,7 @@ void RemoteClient::informateDefVoxel(const std::unordered_map if(!ResUses.DefNode.contains(sId)) continue; - NodeId_c cId = ResRemap.DefNodes.toClient(sId); + DefNodeId_c cId = ResRemap.DefNodes.toClient(sId); NextPacket << (uint8_t) ToClient::L1::Definition << (uint8_t) ToClient::L2Definition::Node @@ -521,6 +534,46 @@ void RemoteClient::informateDefPortals(const std::unordered_map RemoteClient::readPacket(Net::AsyncSocket &sock) { + uint8_t first = co_await sock.read(); + + switch((ToServer::L1) first) { + case ToServer::L1::System: co_await rP_System(sock); co_return; + default: + protocolError(); + } +} + +coro<> RemoteClient::rP_System(Net::AsyncSocket &sock) { + uint8_t second = co_await sock.read(); + + switch((ToServer::L2System) second) { + case ToServer::L2System::InitEnd: + + co_return; + case ToServer::L2System::Disconnect: + { + EnumDisconnect type = (EnumDisconnect) co_await sock.read(); + shutdown(EnumDisconnect::ByInterface, "Вы были отключены от игры"); + std::string reason; + if(type == EnumDisconnect::CriticalError) + reason = ": Критическая ошибка"; + else + reason = ": Ошибка протокола (клиент)"; + + LOG.info() << "Игрок '" << Username << "' отключился" << reason; + + co_return; + } + default: + protocolError(); + } +} + void RemoteClient::incrementBinary(std::unordered_set &textures, std::unordered_set &sounds, std::unordered_set &models) { diff --git a/Src/Server/RemoteClient.hpp b/Src/Server/RemoteClient.hpp index 333671a..af5c615 100644 --- a/Src/Server/RemoteClient.hpp +++ b/Src/Server/RemoteClient.hpp @@ -4,6 +4,7 @@ #include #include #include "Abstract.hpp" +#include "Common/Packets.hpp" #include "Server/ContentEventController.hpp" #include #include @@ -249,8 +250,8 @@ class RemoteClient { SCSKeyRemapper BinModels; SCSKeyRemapper DefWorlds; - SCSKeyRemapper DefVoxels; - SCSKeyRemapper DefNodes; + SCSKeyRemapper DefVoxels; + SCSKeyRemapper DefNodes; SCSKeyRemapper DefPortals; SCSKeyRemapper DefEntityes; @@ -275,7 +276,7 @@ public: ~RemoteClient(); coro<> run(); - void shutdown(const std::string reason); + void shutdown(EnumDisconnect type, const std::string reason); bool isConnected() { return IsConnected; } void pushPackets(std::vector *simplePackets, std::vector *smartPackets = nullptr) { @@ -330,6 +331,10 @@ public: void informateDefPortals(const std::unordered_map &portals); private: + void protocolError(); + coro<> readPacket(Net::AsyncSocket &sock); + coro<> rP_System(Net::AsyncSocket &sock); + void incrementBinary(std::unordered_set &textures, std::unordered_set &sounds, std::unordered_set &models); void decrementBinary(std::unordered_set &textures, std::unordered_set &sounds, diff --git a/Src/assets.cpp b/Src/assets.cpp index bfa11ad..06d9fa0 100644 --- a/Src/assets.cpp +++ b/Src/assets.cpp @@ -9,7 +9,7 @@ extern std::unordered_map> _binary_assets_symbols; namespace fs = std::filesystem; -namespace AL { +namespace LV { Resource::Resource() = default; Resource::~Resource() = default; diff --git a/Src/assets.hpp b/Src/assets.hpp index 6c500f4..b2bc5f5 100644 --- a/Src/assets.hpp +++ b/Src/assets.hpp @@ -3,7 +3,7 @@ #include #include -namespace AL { +namespace LV { namespace detail { struct membuf : std::streambuf { diff --git a/Src/main.cpp b/Src/main.cpp index f4ae01e..e35abcc 100644 --- a/Src/main.cpp +++ b/Src/main.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -9,6 +10,7 @@ #include #include "Client/ServerSession.hpp" #include "Common/Net.hpp" +#include "Common/Packets.hpp" #include "Server/GameServer.hpp" #include @@ -16,14 +18,14 @@ namespace LV { using namespace TOS; +std::unique_ptr session; + coro<> runClient(asio::io_context &ioc, uint16_t port) { try { tcp::socket sock = co_await Net::asyncConnectTo("localhost:"+std::to_string(port)); co_await Client::ServerSession::asyncAuthorizeWithServer(sock, "DrSocalkwe3n", "1password2", 1); std::unique_ptr asock = co_await Client::ServerSession::asyncInitGameProtocol(ioc, std::move(sock)); - asio::deadline_timer timer(ioc); - timer.expires_from_now(boost::posix_time::seconds(1)); - co_await timer.async_wait(); + session = std::make_unique(ioc, std::move(asock), nullptr); } catch(const std::exception &exc) { std::cout << exc.what() << std::endl; } @@ -31,33 +33,41 @@ coro<> runClient(asio::io_context &ioc, uint16_t port) { int main() { - VK::Vulkan VkInst; - VkInst.getSettingsNext() = VkInst.getBestSettings(); - VkInst.reInit(); - - auto ot = std::async([&](){ - VkInst.start([&](VK::Vulkan *instance, int subpass, VkCommandBuffer &renderCmd) - { - }); - }); - // LuaVox asio::io_context ioc; - Server::GameServer gs(ioc, ""); - - Net::Server server(ioc, [&](tcp::socket sock) -> coro<> { - server.stop(); - co_await gs.pushSocketConnect(std::move(sock)); - }, 6666); - - std::cout << server.getPort() << std::endl; - - asio::co_spawn(ioc, runClient(ioc, server.getPort()), asio::detached); - + LV::Client::VK::Vulkan vkInst(ioc); ioc.run(); - VkInst.shutdown(); + + // Server::GameServer gs(ioc, ""); + + // Net::Server server(ioc, [&](tcp::socket sock) -> coro<> { + // server.stop(); + // co_await gs.pushSocketConnect(std::move(sock)); + // }, 6666); + + // std::cout << server.getPort() << std::endl; + + // asio::co_spawn(ioc, runClient(ioc, server.getPort()), asio::detached); + + + // auto ot = std::async([&](){ + // VkInst.start([&](VK::Vulkan *instance, int subpass, VkCommandBuffer &renderCmd) + // { + // if(glfwWindowShouldClose(VkInst.Graphics.Window) || (session && !session->isConnected())) { + // VkInst.shutdown(); + + // if(glfwWindowShouldClose(VkInst.Graphics.Window) && session) + // session->shutdown(EnumDisconnect::ByInterface); + // } + // }); + + // session = nullptr; + // }); + + // ioc.run(); + // VkInst.shutdown(); return 0; } diff --git a/Work/assets/default.ttf b/Work/assets/default.ttf new file mode 100644 index 0000000..3c7ec0f Binary files /dev/null and b/Work/assets/default.ttf differ diff --git a/Work/assets/default2.ttf b/Work/assets/default2.ttf new file mode 100644 index 0000000..05ded64 Binary files /dev/null and b/Work/assets/default2.ttf differ diff --git a/Work/imgui.ini b/Work/imgui.ini index 9930887..cb4f8f5 100644 --- a/Work/imgui.ini +++ b/Work/imgui.ini @@ -1,4 +1,8 @@ [Window][Debug##Default] -Pos=60,60 +Pos=0,0 Size=400,400 +[Window][MainMenu] +Pos=0,0 +Size=960,540 +