mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-08 09:47:01 +08:00
* refactor(config): replace singleton with dependency injection * refactor(client_player): return ChunkPosSet by value in non-const getter Copy the internal set under the mutex lock to avoid exposing a mutable reference that could be used unsafely after the lock is released. This fixes a potential data race when the caller holds the returned reference beyond the critical section. Also update the caller in `client_world` to remove the now-unnecessary `std::move`. * fix(gameplay): prevent use-after-free in pending delete queues during destruction * refactor(config): separate game and server configuration with explicit paths
414 lines
13 KiB
C++
414 lines
13 KiB
C++
#include "Cubed/app.hpp"
|
|
|
|
#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 "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_client_world(m_audio, m_game_config), m_dev_panel(*this),
|
|
m_renderer(m_camera, m_client_world, m_texture_manager, m_dev_panel,
|
|
m_game_config),
|
|
m_window(m_renderer, m_game_config) {}
|
|
|
|
App::~App() {
|
|
if (m_client) {
|
|
m_client->stop();
|
|
}
|
|
}
|
|
void App::cursor_position_callback(GLFWwindow* window, double xpos,
|
|
double ypos) {
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
|
|
|
|
ASSERT_MSG(app, "nullptr");
|
|
if (io.WantCaptureMouse && app->m_window.is_mouse_enable()) {
|
|
ImGui_ImplGlfw_CursorPosCallback(window, xpos, ypos);
|
|
return;
|
|
}
|
|
if (!app->m_window.is_mouse_enable()) {
|
|
app->m_camera.update_cursor_position_camera(xpos, ypos);
|
|
}
|
|
}
|
|
void App::init(int argc, char** argv) {
|
|
handle_toml();
|
|
handle_argument(argc, argv);
|
|
|
|
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);
|
|
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();
|
|
|
|
ChunkGenerator::init();
|
|
BlockManager::init();
|
|
m_renderer.init(m_argument.debug_on);
|
|
Logger::info("Renderer Init Success");
|
|
m_window.update_viewport();
|
|
// MapTable::init_map();
|
|
m_texture_manager.init_texture();
|
|
Logger::info("Texture Load Success");
|
|
if (!m_argument.is_client) {
|
|
m_server.start_server(m_argument.port);
|
|
}
|
|
|
|
m_client = std::make_shared<NetworkClient>(m_client_world);
|
|
|
|
m_client->start(m_argument.ip, m_argument.port);
|
|
// init will send packet
|
|
m_client_world.init(m_argument.player, m_client);
|
|
|
|
Logger::info("World Init Success");
|
|
|
|
m_camera.camera_init(&m_client_world.get_player());
|
|
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.data(), arg.data() + arg.size(),
|
|
m_argument.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");
|
|
}}
|
|
|
|
};
|
|
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::handle_toml() {
|
|
toml::table server;
|
|
try {
|
|
server = toml::parse_file("server.toml");
|
|
} catch (const toml::parse_error& e) {
|
|
// Logger::warn("Ip toml parse error {}", e.what());
|
|
return;
|
|
}
|
|
|
|
m_argument.ip =
|
|
*TOML::safe_get_value(server, "ip", std::string("127.0.01"));
|
|
m_argument.port = *TOML::safe_get_value(server, "port", 25530);
|
|
m_argument.is_client = *TOML::safe_get_value(server, "client", false);
|
|
}
|
|
|
|
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_mouse_enable()) {
|
|
if ((key == GLFW_KEY_LEFT_ALT || key == GLFW_KEY_ESCAPE) &&
|
|
action == GLFW_PRESS) {
|
|
app->m_window.toggle_mouse_able();
|
|
app->m_camera.reset_camera();
|
|
return;
|
|
}
|
|
ImGui_ImplGlfw_KeyCallback(window, key, scancode, action, mods);
|
|
return;
|
|
}
|
|
switch (key) {
|
|
case GLFW_KEY_Q:
|
|
if (action == GLFW_PRESS) {
|
|
}
|
|
break;
|
|
case GLFW_KEY_ESCAPE:
|
|
if (action == GLFW_PRESS) {
|
|
glfwSetWindowShouldClose(window, GLFW_TRUE);
|
|
}
|
|
break;
|
|
case GLFW_KEY_F11:
|
|
if (action == GLFW_PRESS) {
|
|
app->m_window.toggle_fullscreen();
|
|
}
|
|
break;
|
|
case GLFW_KEY_R:
|
|
if (action == GLFW_PRESS) {
|
|
app->m_texture_manager.need_reload();
|
|
}
|
|
break;
|
|
case GLFW_KEY_LEFT_ALT:
|
|
if (action == GLFW_PRESS) {
|
|
app->m_window.toggle_mouse_able();
|
|
app->m_camera.reset_camera();
|
|
}
|
|
break;
|
|
case GLFW_KEY_F5:
|
|
if (action == GLFW_PRESS) {
|
|
app->m_camera.change_perspective();
|
|
}
|
|
break;
|
|
}
|
|
|
|
app->m_client_world.get_player().update_player_move_state(key, action);
|
|
}
|
|
|
|
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_mouse_enable()) {
|
|
ImGui_ImplGlfw_MouseButtonCallback(window, button, action, mods);
|
|
return;
|
|
}
|
|
switch (button) {
|
|
case GLFW_MOUSE_BUTTON_LEFT:
|
|
if (action == GLFW_PRESS) {
|
|
if (app->m_window.is_mouse_enable()) {
|
|
app->m_window.toggle_mouse_able();
|
|
app->m_camera.reset_camera();
|
|
break;
|
|
;
|
|
}
|
|
Input::get_input_state().mouse_state.left = true;
|
|
}
|
|
if (action == GLFW_RELEASE) {
|
|
Input::get_input_state().mouse_state.left = false;
|
|
}
|
|
break;
|
|
case GLFW_MOUSE_BUTTON_RIGHT:
|
|
if (action == GLFW_PRESS) {
|
|
Input::get_input_state().mouse_state.right = true;
|
|
}
|
|
if (action == GLFW_RELEASE) {
|
|
Input::get_input_state().mouse_state.right = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
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_mouse_enable()) {
|
|
ImGui_ImplGlfw_WindowFocusCallback(window, focused);
|
|
return;
|
|
}
|
|
if (focused) {
|
|
app->m_camera.reset_camera();
|
|
}
|
|
}
|
|
|
|
void App::window_reshape_callback(GLFWwindow* window, int, int) {
|
|
|
|
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
|
|
ASSERT_MSG(app, "nullptr");
|
|
app->m_window.update_viewport();
|
|
}
|
|
|
|
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_mouse_enable()) {
|
|
ImGui_ImplGlfw_ScrollCallback(window, xoffset, yoffset);
|
|
return;
|
|
}
|
|
auto& player = app->m_client_world.get_player();
|
|
player.update_scroll(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_mouse_enable()) {
|
|
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_mouse_enable()) {
|
|
ImGui_ImplGlfw_CharCallback(window, c);
|
|
}
|
|
}
|
|
|
|
void App::render() {
|
|
|
|
if (glfwGetWindowAttrib(m_window.get_glfw_window(), GLFW_ICONIFIED) != 0) {
|
|
ImGui_ImplGlfw_Sleep(10);
|
|
return;
|
|
}
|
|
m_renderer.render();
|
|
|
|
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();
|
|
}
|
|
m_client_world.request_exit();
|
|
if (!m_argument.is_client) {
|
|
m_server.server_world().stop();
|
|
}
|
|
}
|
|
static Gait player_gait = Gait::WALK;
|
|
void App::update() {
|
|
glfwPollEvents();
|
|
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_client_world.update(dt);
|
|
m_camera.update_move_camera();
|
|
const auto& player = m_client_world.get_player();
|
|
if (player_gait != player.get_gait()) {
|
|
player_gait = player.get_gait();
|
|
float fov = m_game_config.get("player.fov", 70.0f);
|
|
if (player_gait == Gait::WALK) {
|
|
m_renderer.update_fov(fov);
|
|
}
|
|
if (player_gait == Gait::RUN) {
|
|
m_renderer.update_fov(fov + 5.0f);
|
|
}
|
|
}
|
|
m_audio.update_listener(m_camera.get_camera_pos(),
|
|
m_camera.get_camera_front(), glm::vec3(0, 1, 0));
|
|
m_audio.update();
|
|
m_renderer.update(dt);
|
|
}
|
|
|
|
int App::start_cubed_application(int argc, char** argv) {
|
|
|
|
App app;
|
|
|
|
try {
|
|
app.init(argc, argv);
|
|
Logger::info("Game 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; }
|
|
|
|
Camera& App::camera() { return m_camera; }
|
|
DevPanel& App::dev_panel() { return m_dev_panel; }
|
|
Renderer& App::renderer() { return m_renderer; }
|
|
TextureManager& App::texture_manager() { return m_texture_manager; }
|
|
Window& App::window() { return m_window; }
|
|
ClientWorld& App::client_world() { return m_client_world; }
|
|
ServerWorld& App::server_world() { return m_server.server_world(); }
|
|
Config& App::config() { return m_game_config; }
|
|
const App::Argument& App::argument() const { return m_argument; }
|
|
AudioEngine& App::audio() { return m_audio; }
|
|
} // namespace Cubed
|