From 3c14a4ffe17b6be04c05a4540081330637bb3da1 Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Fri, 26 Jun 2026 13:48:07 +0800 Subject: [PATCH] feat(app): add client/server CLI arguments and init guards --- include/Cubed/app.hpp | 12 +++-- include/Cubed/gameplay/network_client.hpp | 2 + include/Cubed/gameplay/network_server.hpp | 3 +- include/Cubed/gameplay/server_world.hpp | 1 + include/Cubed/renderer.hpp | 3 ++ include/Cubed/texture_manager.hpp | 1 + include/Cubed/tools/arg_parser.hpp | 35 +++++++++++++ include/Cubed/window.hpp | 1 + src/app.cpp | 63 ++++++++++++++++++++--- src/gameplay/client_world.cpp | 3 ++ src/gameplay/network_client.cpp | 3 +- src/gameplay/network_server.cpp | 6 ++- src/gameplay/server_world.cpp | 4 ++ src/renderer.cpp | 37 +++++++------ src/texture_manager.cpp | 17 +++--- src/window.cpp | 12 +++-- 16 files changed, 164 insertions(+), 39 deletions(-) create mode 100644 include/Cubed/tools/arg_parser.hpp diff --git a/include/Cubed/app.hpp b/include/Cubed/app.hpp index fff53b0..6d41c1b 100644 --- a/include/Cubed/app.hpp +++ b/include/Cubed/app.hpp @@ -12,6 +12,12 @@ namespace Cubed { class App { public: + struct Argument { + bool is_client = false; + int port = 25530; + std::string ip{"127.0.0.1"}; + }; + App(); ~App(); static void cursor_position_callback(GLFWwindow* window, double xpos, @@ -60,9 +66,9 @@ private: inline static double fps_time_count = 0.0f; inline static int frame_count = 0; inline static int fps = 0; - - void init(); - + Argument m_argument; + void init(int argc, char** argv); + void handle_argument(int argc, char** argv); auto init_camera(); auto init_texture(); auto init_world(); diff --git a/include/Cubed/gameplay/network_client.hpp b/include/Cubed/gameplay/network_client.hpp index 758adf5..afc8073 100644 --- a/include/Cubed/gameplay/network_client.hpp +++ b/include/Cubed/gameplay/network_client.hpp @@ -17,6 +17,7 @@ public: void send(Packet packet); void start(std::string ip, int port = 25530); bool is_connected() const; + bool is_connect_error() const; private: asio::io_context m_io; @@ -32,6 +33,7 @@ private: std::atomic m_closed{false}; std::atomic m_connected{false}; + std::atomic m_connect_error{false}; // ClientWorld is managed by App ClientWorld& m_world; diff --git a/include/Cubed/gameplay/network_server.hpp b/include/Cubed/gameplay/network_server.hpp index 120fdab..5ed0ace 100644 --- a/include/Cubed/gameplay/network_server.hpp +++ b/include/Cubed/gameplay/network_server.hpp @@ -13,7 +13,7 @@ public: void stop(); // Run in another thread after initialization is complete - void start_server(); + void start_server(int port = 25530); int port() const; ServerWorld& server_world(); @@ -23,6 +23,7 @@ private: std::thread m_net_thread; int m_port = 25530; std::atomic m_stopped{false}; + std::atomic m_started{false}; ServerWorld m_world; std::mutex m_session_mutex; std::unordered_map> m_session; diff --git a/include/Cubed/gameplay/server_world.hpp b/include/Cubed/gameplay/server_world.hpp index 56b436a..30f1ad4 100644 --- a/include/Cubed/gameplay/server_world.hpp +++ b/include/Cubed/gameplay/server_world.hpp @@ -112,6 +112,7 @@ private: std::atomic m_gen_running{false}; std::atomic m_need_gen_chunk{false}; std::atomic m_is_rebuilding{false}; + std::atomic m_init{false}; std::atomic m_rendering_distance{24}; std::atomic m_pool_threads{0}; std::atomic m_max_threads{1}; diff --git a/include/Cubed/renderer.hpp b/include/Cubed/renderer.hpp index b10cd37..4d92bce 100644 --- a/include/Cubed/renderer.hpp +++ b/include/Cubed/renderer.hpp @@ -99,6 +99,9 @@ private: bool m_water_depth_fade = true; bool m_pbr = true; bool m_flip_y = false; + + bool m_init = false; + int m_shadow_mode = 0; int m_light_cull_face = 0; float m_aspect = 0.0f; diff --git a/include/Cubed/texture_manager.hpp b/include/Cubed/texture_manager.hpp index c62043a..7383378 100644 --- a/include/Cubed/texture_manager.hpp +++ b/include/Cubed/texture_manager.hpp @@ -8,6 +8,7 @@ namespace Cubed { class TextureManager { private: bool m_need_reload = false; + bool m_init = false; GLuint m_block_status_array = 0; GLuint m_texture_array = 0; GLuint m_cross_plane_array = 0; diff --git a/include/Cubed/tools/arg_parser.hpp b/include/Cubed/tools/arg_parser.hpp new file mode 100644 index 0000000..d9b2f9b --- /dev/null +++ b/include/Cubed/tools/arg_parser.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include + +namespace Cubed { +class ArgParser { +public: + ArgParser(int argc, char** argv) : m_args(argv, argc) {}; + ArgParser(std::span args) : m_args(args) {} + + bool has_next() const { return m_index < m_args.size(); } + + std::string_view next() { + if (!has_next()) { + throw std::runtime_error("No more arguments"); + } + return m_args[m_index++]; + } + + std::string_view require_next(std::string_view option) { + if (!has_next()) { + throw std::runtime_error( + std::format("{} requires an argument", option)); + } + return next(); + } + +private: + std::span m_args; + size_t m_index = 1; +}; +} // namespace Cubed diff --git a/include/Cubed/window.hpp b/include/Cubed/window.hpp index 6f8f95c..4d23be0 100644 --- a/include/Cubed/window.hpp +++ b/include/Cubed/window.hpp @@ -23,6 +23,7 @@ public: private: bool m_mouse_enable = false; + bool m_imgui_init = false; float m_aspect; GLFWwindow* m_window; int m_width; diff --git a/src/app.cpp b/src/app.cpp index 7ffbf70..e90927a 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -2,13 +2,13 @@ #include "Cubed/config.hpp" #include "Cubed/debug_collector.hpp" +#include "Cubed/tools/arg_parser.hpp" #include "Cubed/tools/cubed_assert.hpp" #include "Cubed/tools/log.hpp" #include "Cubed/tools/system_info.hpp" #include #include - namespace Cubed { App::App() {} @@ -33,9 +33,11 @@ void App::cursor_position_callback(GLFWwindow* window, double xpos, app->m_camera.update_cursor_position_camera(xpos, ypos); } } -void App::init() { +void App::init(int argc, char** argv) { + handle_argument(argc, argv); m_window.init(); m_window.imgui_init(); + Logger::info("Window Init Success"); glfwSetWindowUserPointer(m_window.get_glfw_window(), this); @@ -53,6 +55,7 @@ void App::init() { glfwSetCursorEnterCallback(m_window.get_glfw_window(), cursor_enter_callback); glfwSetCharCallback(m_window.get_glfw_window(), char_callback); + ChunkGenerator::init(); BlockManager::init(); m_renderer.init(); @@ -61,12 +64,13 @@ void App::init() { // MapTable::init_map(); m_texture_manager.init_texture(); Logger::info("Texture Load Success"); - - m_server.start_server(); + if (!m_argument.is_client) { + m_server.start_server(m_argument.port); + } m_client = std::make_shared(m_client_world); - m_client->start("127.0.0.1", 25530); + m_client->start(m_argument.ip, m_argument.port); // init will send packet m_client_world.init("test", m_client); @@ -76,6 +80,53 @@ void App::init() { m_dev_panel.init(); } +void App::handle_argument(int argc, char** argv) { + + static const std::unordered_map> + HANDLERS{ + + {"--client", [&](ArgParser&) { m_argument.is_client = true; }}, + + {"--host", [&](ArgParser&) { m_argument.is_client = false; }}, + + {"-p", + [&](ArgParser& p) { + auto arg = p.require_next("-p"); + + auto r = + std::from_chars(arg.begin(), arg.end(), m_argument.port); + + if (r.ec != std::errc{} || r.ptr != arg.end()) { + throw std::runtime_error( + std::format("Invalid port: {}", arg)); + } + + if (m_argument.port > 65535) { + throw std::runtime_error( + std::format("Port {} out of range", m_argument.port)); + } + }}, + + {"--ip", + [&](ArgParser& p) { + auto arg = p.require_next("--ip"); + m_argument.ip = arg; + }} + + }; + ArgParser parser(argc, argv); + + while (parser.has_next()) { + auto arg = parser.next(); + if (auto it = HANDLERS.find(arg); it != HANDLERS.end()) { + it->second(parser); + } else { + Logger::warn("Unknown argument: {}", arg); + } + } +} + void App::key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { ImGuiIO& io = ImGui::GetIO(); @@ -276,7 +327,7 @@ int App::start_cubed_application(int argc, char** argv) { App app; try { - app.init(); + app.init(argc, argv); Logger::info("Game Init Finish Start Run..."); app.run(); diff --git a/src/gameplay/client_world.cpp b/src/gameplay/client_world.cpp index abcaff2..eba82b6 100644 --- a/src/gameplay/client_world.cpp +++ b/src/gameplay/client_world.cpp @@ -200,6 +200,9 @@ void ClientWorld::init(std::string_view player_name, LoginReq req; req.set_name(m_player.get_name()); while (!client->is_connected()) { + if (client->is_connect_error()) { + throw std::runtime_error("Can't connect to the server"); + } std::this_thread::sleep_for(milliseconds(200)); } // request login diff --git a/src/gameplay/network_client.cpp b/src/gameplay/network_client.cpp index 1445a54..4cdc799 100644 --- a/src/gameplay/network_client.cpp +++ b/src/gameplay/network_client.cpp @@ -20,7 +20,7 @@ void NetworkClient::start(std::string ip, int port) { } bool NetworkClient::is_connected() const { return m_connected.load(); } - +bool NetworkClient::is_connect_error() const { return m_connect_error.load(); } asio::awaitable NetworkClient::connect(std::string ip, int port) { Logger::info("Connect Begin"); try { @@ -38,6 +38,7 @@ asio::awaitable NetworkClient::connect(std::string ip, int port) { } catch (const std::exception& e) { Logger::error("Client Error {}", e.what()); + m_connect_error = true; } } diff --git a/src/gameplay/network_server.cpp b/src/gameplay/network_server.cpp index 83079da..3cdc95b 100644 --- a/src/gameplay/network_server.cpp +++ b/src/gameplay/network_server.cpp @@ -9,6 +9,9 @@ NetworkServer::NetworkServer(int port) : m_port(port) {} NetworkServer::~NetworkServer() { stop(); } void NetworkServer::stop() { + if (!m_started) { + return; + } if (m_stopped.exchange(true)) { return; } @@ -79,9 +82,10 @@ void NetworkServer::net_run() { Logger::info("Server Started!"); } -void NetworkServer::start_server() { +void NetworkServer::start_server(int port) { m_world.init_world(); net_run(); + m_started = true; } int NetworkServer::port() const { return m_port; } diff --git a/src/gameplay/server_world.cpp b/src/gameplay/server_world.cpp index e0cc255..3ec116e 100644 --- a/src/gameplay/server_world.cpp +++ b/src/gameplay/server_world.cpp @@ -15,6 +15,9 @@ namespace Cubed { ServerWorld::ServerWorld() {} ServerWorld::~ServerWorld() { + if (!m_init) { + return; + } stop_gen_thread(); stop_server_thread(); wait_all_chunk_tasks(); @@ -60,6 +63,7 @@ void ServerWorld::init_world() { Logger::info("Chunk Block Init Finish, Time Consuming: {}", d); start_server_thread(); + m_init = true; } void ServerWorld::init_chunks() { hot_reload(); } diff --git a/src/renderer.cpp b/src/renderer.cpp index 4988cfb..fd2f146 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -27,25 +27,27 @@ Renderer::Renderer(const Camera& camera, ClientWorld& world, m_texture_manager(texture_manager), m_world(world) {} Renderer::~Renderer() { - glBindBuffer(GL_ARRAY_BUFFER, 0); - glDeleteBuffers(1, &m_outline_vbo); - glDeleteBuffers(1, &m_outline_indices_vbo); - glDeleteBuffers(1, &m_sky_vbo); - glDeleteBuffers(1, &m_ui_vbo); - glDeleteBuffers(1, &m_text_vbo); - glBindVertexArray(0); - glDeleteVertexArrays(NUM_VAO, m_vao.data()); - glDeleteFramebuffers(1, &m_fbo); - glDeleteTextures(1, &m_screen_texture); - glDeleteTextures(1, &m_screen_depth_texture); + if (m_init) { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glDeleteBuffers(1, &m_outline_vbo); + glDeleteBuffers(1, &m_outline_indices_vbo); + glDeleteBuffers(1, &m_sky_vbo); + glDeleteBuffers(1, &m_ui_vbo); + glDeleteBuffers(1, &m_text_vbo); + glBindVertexArray(0); + glDeleteVertexArrays(NUM_VAO, m_vao.data()); + glDeleteFramebuffers(1, &m_fbo); + glDeleteTextures(1, &m_screen_texture); + glDeleteTextures(1, &m_screen_depth_texture); - glDeleteFramebuffers(1, &m_oit_fbo); - glDeleteTextures(1, &m_accum_texture); - glDeleteTextures(1, &m_reveal_texture); - glDeleteTextures(1, &m_oit_depth_texture); + glDeleteFramebuffers(1, &m_oit_fbo); + glDeleteTextures(1, &m_accum_texture); + glDeleteTextures(1, &m_reveal_texture); + glDeleteTextures(1, &m_oit_depth_texture); - glDeleteFramebuffers(1, &m_depth_map_fbo); - glDeleteTextures(1, &m_depth_map_texture); + glDeleteFramebuffers(1, &m_depth_map_fbo); + glDeleteTextures(1, &m_depth_map_texture); + } } void Renderer::hot_reload() { @@ -171,6 +173,7 @@ void Renderer::init() { glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); + m_init = true; } const Shader& Renderer::get_shader(const std::string& name) const { diff --git a/src/texture_manager.cpp b/src/texture_manager.cpp index 24df2db..1b58e3e 100644 --- a/src/texture_manager.cpp +++ b/src/texture_manager.cpp @@ -36,14 +36,16 @@ TextureManager::TextureManager() {} TextureManager::~TextureManager() { delet_texture(); } void TextureManager::delet_texture() { - glDeleteTextures(1, &m_texture_array); - glDeleteTextures(1, &m_block_status_array); - glDeleteTextures(1, &m_cross_plane_array); - glDeleteTextures(1, &m_normal_texture_array); - for (auto& id : m_item_textures) { - glDeleteTextures(1, &id); + if (m_init) { + glDeleteTextures(1, &m_texture_array); + glDeleteTextures(1, &m_block_status_array); + glDeleteTextures(1, &m_cross_plane_array); + glDeleteTextures(1, &m_normal_texture_array); + for (auto& id : m_item_textures) { + glDeleteTextures(1, &id); + } + Logger::info("Successfully delete all texture"); } - Logger::info("Successfully delete all texture"); } GLuint TextureManager::get_block_status_array() const { @@ -313,6 +315,7 @@ void TextureManager::init_texture() { init_block(); init_block_status(); init_ui(); + m_init = true; } void TextureManager::update() { diff --git a/src/window.cpp b/src/window.cpp index a30069a..fcaa24f 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -18,10 +18,14 @@ static int windowed_width = 800, windowed_height = 600; Window::Window(Renderer& renderer) : m_renderer(renderer) {} Window::~Window() { + if (m_imgui_init) { + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + } - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplGlfw_Shutdown(); - ImGui::DestroyContext(); + if (ImGui::GetCurrentContext() != nullptr) { + ImGui::DestroyContext(); + } if (m_window) { glfwDestroyWindow(m_window); @@ -212,6 +216,8 @@ void Window::imgui_init() { // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOpenGL(m_window, false); ImGui_ImplOpenGL3_Init(); + + m_imgui_init = true; } } // namespace Cubed \ No newline at end of file