feat(app): add client/server CLI arguments and init guards

This commit is contained in:
2026-06-26 13:48:07 +08:00
parent fbb6fe2fa6
commit 3c14a4ffe1
16 changed files with 164 additions and 39 deletions

View File

@@ -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();

View File

@@ -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<bool> m_closed{false};
std::atomic<bool> m_connected{false};
std::atomic<bool> m_connect_error{false};
// ClientWorld is managed by App
ClientWorld& m_world;

View File

@@ -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<bool> m_stopped{false};
std::atomic<bool> m_started{false};
ServerWorld m_world;
std::mutex m_session_mutex;
std::unordered_map<std::string, std::shared_ptr<Session>> m_session;

View File

@@ -112,6 +112,7 @@ private:
std::atomic<bool> m_gen_running{false};
std::atomic<bool> m_need_gen_chunk{false};
std::atomic<bool> m_is_rebuilding{false};
std::atomic<bool> m_init{false};
std::atomic<int> m_rendering_distance{24};
std::atomic<int> m_pool_threads{0};
std::atomic<int> m_max_threads{1};

View File

@@ -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;

View File

@@ -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;

View File

@@ -0,0 +1,35 @@
#pragma once
#include <format>
#include <span>
#include <stdexcept>
#include <string_view>
namespace Cubed {
class ArgParser {
public:
ArgParser(int argc, char** argv) : m_args(argv, argc) {};
ArgParser(std::span<char*> 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<char*> m_args;
size_t m_index = 1;
};
} // namespace Cubed

View File

@@ -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;

View File

@@ -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 <exception>
#include <imgui_impl_glfw.h>
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<NetworkClient>(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<std::string_view,
std::function<void(ArgParser&)>>
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();

View File

@@ -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

View File

@@ -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<void> NetworkClient::connect(std::string ip, int port) {
Logger::info("Connect Begin");
try {
@@ -38,6 +38,7 @@ asio::awaitable<void> NetworkClient::connect(std::string ip, int port) {
} catch (const std::exception& e) {
Logger::error("Client Error {}", e.what());
m_connect_error = true;
}
}

View File

@@ -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; }

View File

@@ -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(); }

View File

@@ -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 {

View File

@@ -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() {

View File

@@ -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