Files
Cubed/src/app.cpp
zhenyan121 d6e1e4a50e refactor: argument (#33)
* refactor(config): remove TOML config loading and replace is_client with host_game

* feat(network): add net_error_message helper and use for error logging
2026-07-17 09:00:26 +08:00

773 lines
20 KiB
C++

#include "Cubed/app.hpp"
#include "Cubed/camera.hpp"
#include "Cubed/config.hpp"
#include "Cubed/debug_collector.hpp"
#include "Cubed/localization.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 "Cubed/tools/system_locate.hpp"
#include "Cubed/tools/text_tools.hpp"
#include "version.hpp"
#include <exception>
#include <imgui_impl_glfw.h>
namespace Cubed {
App::App()
: m_game_config(ASSETS_PATH "config.toml"),
m_texture_manager(m_game_config), m_audio(m_game_config),
m_renderer(m_texture_manager, m_game_config), m_window(m_game_config),
m_scene_manager(*this) {}
App::~App() {}
void App::cursor_position_callback(GLFWwindow* window, double xpos,
double ypos) {
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
ASSERT_MSG(app, "nullptr");
if (app->m_window.is_enable_imgui()) {
ImGui_ImplGlfw_CursorPosCallback(window, xpos, ypos);
return;
}
app->m_scene_manager.handle_event(
MouseMoveEvent{static_cast<float>(xpos), static_cast<float>(ypos)});
}
void App::init(int argc, char** argv) {
handle_argument(argc, argv);
if (m_argument.language) {
Localization::instance().load_language(*m_argument.language);
m_game_config.set("language", *m_argument.language);
} else {
auto locate = get_system_locale();
std::string default_value = "en_US";
if (locate.country == "CN") {
default_value = "zh_CN";
}
Localization::instance().load_language(
m_game_config.get("language", default_value));
}
m_window.init();
m_window.imgui_init();
Logger::info("Window Init Success");
glfwSetWindowUserPointer(m_window.get_glfw_window(), this);
glfwSetCursorPosCallback(m_window.get_glfw_window(),
cursor_position_callback);
glfwSetMouseButtonCallback(m_window.get_glfw_window(),
mouse_button_callback);
glfwSetWindowFocusCallback(m_window.get_glfw_window(),
window_focus_callback);
glfwSetWindowSizeCallback(m_window.get_glfw_window(),
window_reshape_callback);
glfwSetFramebufferSizeCallback(m_window.get_glfw_window(),
framebuffer_size_callback);
glfwSetKeyCallback(m_window.get_glfw_window(), key_callback);
glfwSetScrollCallback(m_window.get_glfw_window(), mouse_scroll_callback);
glfwSetCursorEnterCallback(m_window.get_glfw_window(),
cursor_enter_callback);
glfwSetCharCallback(m_window.get_glfw_window(), char_callback);
m_audio.init();
BlockManager::init();
if (m_argument.debug_on) {
m_renderer.init(*m_argument.debug_on);
}
Logger::info("Renderer Init Success");
// MapTable::init_map();
m_texture_manager.init_texture();
Logger::info("Texture Load Success");
m_scene_manager.request_push(SceneType::MAIN_MENU);
{
int w, h;
glfwGetWindowSize(m_window.get_glfw_window(), &w, &h);
window_reshape_callback(m_window.get_glfw_window(), w, h);
}
{
int w, h;
glfwGetFramebufferSize(m_window.get_glfw_window(), &w, &h);
framebuffer_size_callback(m_window.get_glfw_window(), w, h);
}
}
void App::handle_argument(int argc, char** argv) {
static const std::unordered_map<std::string_view,
std::function<void(ArgParser&)>>
HANDLERS{
{"-p",
[&](ArgParser& p) {
auto arg = p.require_next("-p");
int port;
auto r =
std::from_chars(arg.data(), arg.data() + arg.size(), port);
m_argument.port = port;
if (r.ec != std::errc{} || r.ptr != arg.data() + arg.size()) {
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;
}},
{"--player",
[&](ArgParser& p) {
auto arg = p.require_next("--player");
m_argument.player = arg;
}},
{"-V",
[&](ArgParser) {
std::cout << CUBED_VERSION << "\n";
exit(EXIT_SUCCESS);
}},
{"--no-debug",
[&](ArgParser) {
m_argument.debug_on = false;
Logger::info("Switch off opengl debug out put");
}},
{"--language",
[&](ArgParser& p) {
auto arg = p.require_next("--language");
m_argument.language = 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();
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
ASSERT_MSG(app, "nullptr");
// ImGui_ImplGlfw_CursorEnterCallback(window,
// !app->m_window.is_mouse_enable());
if (io.WantCaptureKeyboard && app->m_window.is_enable_imgui()) {
if ((key == GLFW_KEY_LEFT_ALT) && action == GLFW_PRESS) {
app->dispatch_event(KeyEvent{Key::LEFT_ALT, KeyAction::PRESS});
return;
}
ImGui_ImplGlfw_KeyCallback(window, key, scancode, action, mods);
return;
}
Key pkey;
KeyAction act;
switch (key) {
// Letter keys
case GLFW_KEY_A:
pkey = Key::A;
break;
case GLFW_KEY_B:
pkey = Key::B;
break;
case GLFW_KEY_C:
pkey = Key::C;
break;
case GLFW_KEY_D:
pkey = Key::D;
break;
case GLFW_KEY_E:
pkey = Key::E;
break;
case GLFW_KEY_F:
pkey = Key::F;
break;
case GLFW_KEY_G:
pkey = Key::G;
break;
case GLFW_KEY_H:
pkey = Key::H;
break;
case GLFW_KEY_I:
pkey = Key::I;
break;
case GLFW_KEY_J:
pkey = Key::J;
break;
case GLFW_KEY_K:
pkey = Key::K;
break;
case GLFW_KEY_L:
pkey = Key::L;
break;
case GLFW_KEY_M:
pkey = Key::M;
break;
case GLFW_KEY_N:
pkey = Key::N;
break;
case GLFW_KEY_O:
pkey = Key::O;
break;
case GLFW_KEY_P:
pkey = Key::P;
break;
case GLFW_KEY_Q:
pkey = Key::Q;
break;
case GLFW_KEY_R:
pkey = Key::R;
break;
case GLFW_KEY_S:
pkey = Key::S;
break;
case GLFW_KEY_T:
pkey = Key::T;
break;
case GLFW_KEY_U:
pkey = Key::U;
break;
case GLFW_KEY_V:
pkey = Key::V;
break;
case GLFW_KEY_W:
pkey = Key::W;
break;
case GLFW_KEY_X:
pkey = Key::X;
break;
case GLFW_KEY_Y:
pkey = Key::Y;
break;
case GLFW_KEY_Z:
pkey = Key::Z;
break;
// Digit keys (main keyboard area)
case GLFW_KEY_0:
pkey = Key::DIGIT_0;
break;
case GLFW_KEY_1:
pkey = Key::DIGIT_1;
break;
case GLFW_KEY_2:
pkey = Key::DIGIT_2;
break;
case GLFW_KEY_3:
pkey = Key::DIGIT_3;
break;
case GLFW_KEY_4:
pkey = Key::DIGIT_4;
break;
case GLFW_KEY_5:
pkey = Key::DIGIT_5;
break;
case GLFW_KEY_6:
pkey = Key::DIGIT_6;
break;
case GLFW_KEY_7:
pkey = Key::DIGIT_7;
break;
case GLFW_KEY_8:
pkey = Key::DIGIT_8;
break;
case GLFW_KEY_9:
pkey = Key::DIGIT_9;
break;
// Function keys
case GLFW_KEY_F1:
pkey = Key::F1;
break;
case GLFW_KEY_F2:
pkey = Key::F2;
break;
case GLFW_KEY_F3:
pkey = Key::F3;
break;
case GLFW_KEY_F4:
pkey = Key::F4;
break;
case GLFW_KEY_F5:
pkey = Key::F5;
break;
case GLFW_KEY_F6:
pkey = Key::F6;
break;
case GLFW_KEY_F7:
pkey = Key::F7;
break;
case GLFW_KEY_F8:
pkey = Key::F8;
break;
case GLFW_KEY_F9:
pkey = Key::F9;
break;
case GLFW_KEY_F10:
pkey = Key::F10;
break;
case GLFW_KEY_F11:
pkey = Key::F11;
break;
case GLFW_KEY_F12:
pkey = Key::F12;
break;
// Control keys
case GLFW_KEY_BACKSPACE:
pkey = Key::BACKSPACE;
break;
case GLFW_KEY_TAB:
pkey = Key::TAB;
break;
case GLFW_KEY_ENTER:
pkey = Key::ENTER;
break;
case GLFW_KEY_ESCAPE:
pkey = Key::ESCAPE;
break;
case GLFW_KEY_SPACE:
pkey = Key::SPACE;
break;
case GLFW_KEY_CAPS_LOCK:
pkey = Key::CAPS_LOCK;
break;
case GLFW_KEY_NUM_LOCK:
pkey = Key::NUM_LOCK;
break;
case GLFW_KEY_SCROLL_LOCK:
pkey = Key::SCROLL_LOCK;
break;
// Modifier keys
case GLFW_KEY_LEFT_SHIFT:
pkey = Key::LEFT_SHIFT;
break;
case GLFW_KEY_RIGHT_SHIFT:
pkey = Key::RIGHT_SHIFT;
break;
case GLFW_KEY_LEFT_CONTROL:
pkey = Key::LEFT_CTRL;
break;
case GLFW_KEY_RIGHT_CONTROL:
pkey = Key::RIGHT_CTRL;
break;
case GLFW_KEY_LEFT_ALT:
pkey = Key::LEFT_ALT;
break;
case GLFW_KEY_RIGHT_ALT:
pkey = Key::RIGHT_ALT;
break;
case GLFW_KEY_LEFT_SUPER:
pkey = Key::LEFT_SUPER;
break;
case GLFW_KEY_RIGHT_SUPER:
pkey = Key::RIGHT_SUPER;
break;
// Navigation keys
case GLFW_KEY_INSERT:
pkey = Key::INSERT;
break;
case GLFW_KEY_DELETE:
pkey = Key::DELETE;
break;
case GLFW_KEY_HOME:
pkey = Key::HOME;
break;
case GLFW_KEY_END:
pkey = Key::END;
break;
case GLFW_KEY_PAGE_UP:
pkey = Key::PAGE_UP;
break;
case GLFW_KEY_PAGE_DOWN:
pkey = Key::PAGE_DOWN;
break;
case GLFW_KEY_LEFT:
pkey = Key::LEFT;
break;
case GLFW_KEY_RIGHT:
pkey = Key::RIGHT;
break;
case GLFW_KEY_UP:
pkey = Key::UP;
break;
case GLFW_KEY_DOWN:
pkey = Key::DOWN;
break;
// Lock and system keys
case GLFW_KEY_PRINT_SCREEN:
pkey = Key::PRINT_SCREEN;
break;
case GLFW_KEY_PAUSE:
pkey = Key::PAUSE;
break;
// Main keyboard area symbol keys
case GLFW_KEY_GRAVE_ACCENT:
pkey = Key::GRAVE_ACCENT;
break;
case GLFW_KEY_MINUS:
pkey = Key::MINUS;
break;
case GLFW_KEY_EQUAL:
pkey = Key::EQUALS;
break;
case GLFW_KEY_LEFT_BRACKET:
pkey = Key::LEFT_BRACKET;
break;
case GLFW_KEY_RIGHT_BRACKET:
pkey = Key::RIGHT_BRACKET;
break;
case GLFW_KEY_BACKSLASH:
pkey = Key::BACKSLASH;
break;
case GLFW_KEY_SEMICOLON:
pkey = Key::SEMICOLON;
break;
case GLFW_KEY_APOSTROPHE:
pkey = Key::APOSTROPHE;
break;
case GLFW_KEY_COMMA:
pkey = Key::COMMA;
break;
case GLFW_KEY_PERIOD:
pkey = Key::PERIOD;
break;
case GLFW_KEY_SLASH:
pkey = Key::SLASH;
break;
// Numpad area
case GLFW_KEY_KP_0:
pkey = Key::NUMPAD_0;
break;
case GLFW_KEY_KP_1:
pkey = Key::NUMPAD_1;
break;
case GLFW_KEY_KP_2:
pkey = Key::NUMPAD_2;
break;
case GLFW_KEY_KP_3:
pkey = Key::NUMPAD_3;
break;
case GLFW_KEY_KP_4:
pkey = Key::NUMPAD_4;
break;
case GLFW_KEY_KP_5:
pkey = Key::NUMPAD_5;
break;
case GLFW_KEY_KP_6:
pkey = Key::NUMPAD_6;
break;
case GLFW_KEY_KP_7:
pkey = Key::NUMPAD_7;
break;
case GLFW_KEY_KP_8:
pkey = Key::NUMPAD_8;
break;
case GLFW_KEY_KP_9:
pkey = Key::NUMPAD_9;
break;
case GLFW_KEY_KP_ADD:
pkey = Key::NUMPAD_ADD;
break;
case GLFW_KEY_KP_SUBTRACT:
pkey = Key::NUMPAD_SUBTRACT;
break;
case GLFW_KEY_KP_MULTIPLY:
pkey = Key::NUMPAD_MULTIPLY;
break;
case GLFW_KEY_KP_DIVIDE:
pkey = Key::NUMPAD_DIVIDE;
break;
case GLFW_KEY_KP_DECIMAL:
pkey = Key::NUMPAD_DECIMAL;
break;
case GLFW_KEY_KP_ENTER:
pkey = Key::NUMPAD_ENTER;
break;
default:
Logger::error("Unknown Key {}", key);
return;
}
if (action == GLFW_PRESS) {
act = KeyAction::PRESS;
} else if (action == GLFW_RELEASE) {
act = KeyAction::RELEASE;
} else {
act = KeyAction::REPEAT;
}
app->dispatch_event(KeyEvent{pkey, act});
}
void App::mouse_button_callback(GLFWwindow* window, int button, int action,
int mods) {
ImGuiIO& io = ImGui::GetIO();
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
ASSERT_MSG(app, "nullptr");
if (io.WantCaptureMouse && app->m_window.is_enable_imgui()) {
ImGui_ImplGlfw_MouseButtonCallback(window, button, action, mods);
return;
}
MouseKey key;
KeyAction act;
switch (button) {
case GLFW_MOUSE_BUTTON_LEFT:
key = MouseKey::LEFT_BUTTON;
break;
case GLFW_MOUSE_BUTTON_RIGHT:
key = MouseKey::RIGHT_BUTTON;
break;
case GLFW_MOUSE_BUTTON_MIDDLE:
key = MouseKey::MIDDLE_BUTTON;
break;
case GLFW_MOUSE_BUTTON_4:
key = MouseKey::BACK_BUTTON;
break;
case GLFW_MOUSE_BUTTON_5:
key = MouseKey::FORWARD_BUTTON;
break;
case GLFW_MOUSE_BUTTON_6:
key = MouseKey::EXTRA_BUTTON_1;
break;
case GLFW_MOUSE_BUTTON_7:
key = MouseKey::EXTRA_BUTTON_2;
break;
case GLFW_MOUSE_BUTTON_8:
key = MouseKey::EXTRA_BUTTON_3;
break;
default:
Logger::error("Unknown Mouse Button {}", button);
return;
}
if (action == GLFW_PRESS) {
act = KeyAction::PRESS;
} else if (action == GLFW_RELEASE) {
act = KeyAction::RELEASE;
} else {
act = KeyAction::REPEAT;
}
app->dispatch_event(MouseButtonEvent{key, act});
}
void App::window_focus_callback(GLFWwindow* window, int focused) {
ImGuiIO& io = ImGui::GetIO();
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
ASSERT_MSG(app, "nullptr");
if (io.WantCaptureMouse && app->m_window.is_enable_imgui()) {
ImGui_ImplGlfw_WindowFocusCallback(window, focused);
return;
}
if (focused) {
auto camera = app->m_window.camera();
if (camera) {
camera->reset_camera();
}
}
}
void App::window_reshape_callback(GLFWwindow* window, int width, int height) {
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
ASSERT_MSG(app, "nullptr");
app->dispatch_event(WindowResizeEvent{width, height});
Logger::info("Window Reshape W: {} H: {}", width, height);
}
void App::framebuffer_size_callback(GLFWwindow* window, int width, int height) {
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
ASSERT_MSG(app, "nullptr");
app->dispatch_event(FrameBufferResizeEvent{width, height});
Logger::info("Frame Buffer Reshape W: {} H: {}", width, height);
}
void App::mouse_scroll_callback(GLFWwindow* window, double xoffset,
double yoffset) {
ImGuiIO& io = ImGui::GetIO();
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
ASSERT_MSG(app, "nullptr");
if (io.WantCaptureMouse && app->m_window.is_enable_imgui()) {
ImGui_ImplGlfw_ScrollCallback(window, xoffset, yoffset);
return;
}
app->dispatch_event(MouseWheelEvent(static_cast<float>(yoffset)));
}
void App::cursor_enter_callback(GLFWwindow* window, int entered) {
ImGuiIO& io = ImGui::GetIO();
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
ASSERT_MSG(app, "nullptr");
if (io.WantCaptureMouse && app->m_window.is_enable_imgui()) {
ImGui_ImplGlfw_CursorEnterCallback(window, entered);
return;
}
}
void App::char_callback(GLFWwindow* window, unsigned int c) {
ImGuiIO& io = ImGui::GetIO();
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
ASSERT_MSG(app, "nullptr");
if (io.WantCaptureKeyboard && app->m_window.is_enable_imgui()) {
ImGui_ImplGlfw_CharCallback(window, c);
return;
}
app->dispatch_event(TextInputEvent{codepoint_to_utf8(c)});
}
void App::render() {
if (glfwGetWindowAttrib(m_window.get_glfw_window(), GLFW_ICONIFIED) != 0) {
ImGui_ImplGlfw_Sleep(10);
return;
}
m_renderer.begin_frame();
m_scene_manager.render(m_renderer);
m_renderer.end_frame();
glfwSwapBuffers(m_window.get_glfw_window());
}
void App::run() {
last_time = glfwGetTime();
while (!glfwWindowShouldClose(m_window.get_glfw_window())) {
// if (m_client_world.is_receive_exit()) {
// break;
// }
update();
render();
}
}
// static Gait player_gait = Gait::WALK;
void App::update() {
glfwPollEvents();
{
int w, h;
glfwGetFramebufferSize(m_window.get_glfw_window(), &w, &h);
if (w != m_renderer.frame_width() || h != m_renderer.frame_height()) {
dispatch_event(FrameBufferResizeEvent{w, h});
}
}
{
int w, h;
glfwGetWindowSize(m_window.get_glfw_window(), &w, &h);
if (w != m_renderer.window_width() || h != m_renderer.window_height()) {
dispatch_event(WindowResizeEvent{w, h});
}
}
current_time = glfwGetTime();
dt = current_time - last_time;
last_time = current_time;
fps_time_count += dt;
frame_count++;
if (fps_time_count >= 1.0f) {
fps = static_cast<int>(frame_count / fps_time_count);
std::string title = "Cubed FPS: " + std::to_string(fps);
glfwSetWindowTitle(m_window.get_glfw_window(), title.c_str());
frame_count = 0;
fps_time_count = 0.0f;
DebugCollector::get().report(
"fps", std::string{"FPS: " + std::to_string(fps)});
DebugCollector::get().report(
"rss",
std::format("RSS: {}mb", Tools::get_current_rss() / (1024 * 1024)));
}
m_texture_manager.update();
m_audio.update();
DebugCollector::get().get_widget().update(dt);
m_renderer.update(dt);
m_scene_manager.update(dt);
}
void App::dispatch_event(const Event& e) {
if (m_window.handle_event(e)) {
return;
}
if (m_texture_manager.handle_event(e)) {
return;
}
if (m_renderer.handle_event(e)) {
return;
}
if (DebugCollector::get().handle_event(e)) {
return;
}
if (m_scene_manager.handle_event(e)) {
return;
}
}
int App::start_cubed_application(int argc, char** argv) {
App app;
try {
app.init(argc, argv);
Logger::info("Init Finish Start Run...");
app.run();
return 0;
} catch (std::exception& e) {
Logger::error("{}", e.what());
} catch (...) {
Logger::error("Unkown error");
}
return 1;
}
float App::delta_time() { return dt; }
float App::get_fps() { return fps; }
Renderer& App::renderer() { return m_renderer; }
TextureManager& App::texture_manager() { return m_texture_manager; }
Window& App::window() { return m_window; }
Config& App::config() { return m_game_config; }
const Argument& App::argument() const { return m_argument; }
AudioEngine& App::audio() { return m_audio; }
const char* App::get_clipboard_text() {
return glfwGetClipboardString(m_window.get_glfw_window());
}
} // namespace Cubed