From 3e01a399f9c66b63516d69b9651c8d4bfeb4c2cd Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 00:04:56 +0300 Subject: [PATCH 01/64] refactor --- src/content/ContentPack.cpp | 4 ++-- src/content/ContentPack.hpp | 2 +- src/core_defs.cpp | 16 ++++++------- src/core_defs.hpp | 2 +- src/engine.cpp | 48 ++++++++++++++++++------------------- src/engine.hpp | 12 +++++----- src/util/command_line.cpp | 2 +- src/voxel_engine.cpp | 9 +------ 8 files changed, 43 insertions(+), 52 deletions(-) diff --git a/src/content/ContentPack.cpp b/src/content/ContentPack.cpp index 166400c6b..9ed192b5a 100644 --- a/src/content/ContentPack.cpp +++ b/src/content/ContentPack.cpp @@ -13,9 +13,9 @@ namespace fs = std::filesystem; -ContentPack ContentPack::createCore(const EnginePaths* paths) { +ContentPack ContentPack::createCore(const EnginePaths& paths) { return ContentPack { - "core", "Core", ENGINE_VERSION_STRING, "", "", paths->getResourcesFolder(), {} + "core", "Core", ENGINE_VERSION_STRING, "", "", paths.getResourcesFolder(), {} }; } diff --git a/src/content/ContentPack.hpp b/src/content/ContentPack.hpp index 857f4fe0a..a3b402118 100644 --- a/src/content/ContentPack.hpp +++ b/src/content/ContentPack.hpp @@ -71,7 +71,7 @@ struct ContentPack { const std::string& name ); - static ContentPack createCore(const EnginePaths*); + static ContentPack createCore(const EnginePaths&); static inline fs::path getFolderFor(ContentType type) { switch (type) { diff --git a/src/core_defs.cpp b/src/core_defs.cpp index c62900f7f..fb8540baa 100644 --- a/src/core_defs.cpp +++ b/src/core_defs.cpp @@ -11,9 +11,9 @@ #include "voxels/Block.hpp" // All in-game definitions (blocks, items, etc..) -void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) { +void corecontent::setup(const EnginePaths& paths, ContentBuilder& builder) { { - Block& block = builder->blocks.create(CORE_AIR); + Block& block = builder.blocks.create(CORE_AIR); block.replaceable = true; block.drawGroup = 1; block.lightPassing = true; @@ -24,11 +24,11 @@ void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) { block.pickingItem = CORE_EMPTY; } { - ItemDef& item = builder->items.create(CORE_EMPTY); + ItemDef& item = builder.items.create(CORE_EMPTY); item.iconType = ItemIconType::NONE; } - auto bindsFile = paths->getResourcesFolder()/fs::path("bindings.toml"); + auto bindsFile = paths.getResourcesFolder()/fs::path("bindings.toml"); if (fs::is_regular_file(bindsFile)) { Events::loadBindings( bindsFile.u8string(), files::read_string(bindsFile), BindType::BIND @@ -36,20 +36,20 @@ void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) { } { - Block& block = builder->blocks.create(CORE_OBSTACLE); + Block& block = builder.blocks.create(CORE_OBSTACLE); for (uint i = 0; i < 6; i++) { block.textureFaces[i] = "obstacle"; } block.hitboxes = {AABB()}; block.breakable = false; - ItemDef& item = builder->items.create(CORE_OBSTACLE+".item"); + ItemDef& item = builder.items.create(CORE_OBSTACLE+".item"); item.iconType = ItemIconType::BLOCK; item.icon = CORE_OBSTACLE; item.placingBlock = CORE_OBSTACLE; item.caption = block.caption; } { - Block& block = builder->blocks.create(CORE_STRUCT_AIR); + Block& block = builder.blocks.create(CORE_STRUCT_AIR); for (uint i = 0; i < 6; i++) { block.textureFaces[i] = "struct_air"; } @@ -58,7 +58,7 @@ void corecontent::setup(EnginePaths* paths, ContentBuilder* builder) { block.lightPassing = true; block.hitboxes = {AABB()}; block.obstacle = false; - ItemDef& item = builder->items.create(CORE_STRUCT_AIR+".item"); + ItemDef& item = builder.items.create(CORE_STRUCT_AIR+".item"); item.iconType = ItemIconType::BLOCK; item.icon = CORE_STRUCT_AIR; item.placingBlock = CORE_STRUCT_AIR; diff --git a/src/core_defs.hpp b/src/core_defs.hpp index 9f26b1c8a..38160dac8 100644 --- a/src/core_defs.hpp +++ b/src/core_defs.hpp @@ -35,5 +35,5 @@ class EnginePaths; class ContentBuilder; namespace corecontent { - void setup(EnginePaths* paths, ContentBuilder* builder); + void setup(const EnginePaths& paths, ContentBuilder& builder); } diff --git a/src/engine.cpp b/src/engine.cpp index 129dc68db..6641057de 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -15,7 +15,6 @@ #include "content/ContentLoader.hpp" #include "core_defs.hpp" #include "files/files.hpp" -#include "files/settings_io.hpp" #include "frontend/locale.hpp" #include "frontend/menu.hpp" #include "frontend/screens/Screen.hpp" @@ -37,7 +36,6 @@ #include "window/Events.hpp" #include "window/input.hpp" #include "window/Window.hpp" -#include "settings.hpp" #include #include @@ -71,15 +69,15 @@ static std::unique_ptr load_icon(const fs::path& resdir) { return nullptr; } -Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, EnginePaths* paths) - : settings(settings), settingsHandler(settingsHandler), paths(paths), +Engine::Engine(EnginePaths& paths) + : settings(), settingsHandler({settings}), paths(paths), interpreter(std::make_unique()), network(network::Network::create(settings.network)) { - paths->prepare(); + paths.prepare(); loadSettings(); - auto resdir = paths->getResourcesFolder(); + auto resdir = paths.getResourcesFolder(); controller = std::make_unique(this); if (Window::initialize(&this->settings.display)){ @@ -101,7 +99,7 @@ Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, Engin if (settings.ui.language.get() == "auto") { settings.ui.language.set(langs::locale_by_envlocale( platform::detect_locale(), - paths->getResourcesFolder() + paths.getResourcesFolder() )); } if (ENGINE_DEBUG_BUILD) { @@ -116,7 +114,7 @@ Engine::Engine(EngineSettings& settings, SettingsHandler& settingsHandler, Engin } void Engine::loadSettings() { - fs::path settings_file = paths->getSettingsFile(); + fs::path settings_file = paths.getSettingsFile(); if (fs::is_regular_file(settings_file)) { logger.info() << "loading settings"; std::string text = files::read_string(settings_file); @@ -130,7 +128,7 @@ void Engine::loadSettings() { } void Engine::loadControls() { - fs::path controls_file = paths->getControlsFile(); + fs::path controls_file = paths.getControlsFile(); if (fs::is_regular_file(controls_file)) { logger.info() << "loading controls"; std::string text = files::read_string(controls_file); @@ -162,7 +160,7 @@ void Engine::updateHotkeys() { void Engine::saveScreenshot() { auto image = Window::takeScreenshot(); image->flipY(); - fs::path filename = paths->getNewScreenshotFile("png"); + fs::path filename = paths.getNewScreenshotFile("png"); imageio::write(filename.string(), image.get()); logger.info() << "saved screenshot as " << filename.u8string(); } @@ -220,9 +218,9 @@ void Engine::processPostRunnables() { void Engine::saveSettings() { logger.info() << "saving settings"; - files::write_string(paths->getSettingsFile(), toml::stringify(settingsHandler)); + files::write_string(paths.getSettingsFile(), toml::stringify(settingsHandler)); logger.info() << "saving bindings"; - files::write_string(paths->getControlsFile(), Events::writeBindings()); + files::write_string(paths.getControlsFile(), Events::writeBindings()); } Engine::~Engine() { @@ -257,8 +255,8 @@ PacksManager Engine::createPacksManager(const fs::path& worldFolder) { PacksManager manager; manager.setSources({ worldFolder/fs::path("content"), - paths->getUserFilesFolder()/fs::path("content"), - paths->getResourcesFolder()/fs::path("content") + paths.getUserFilesFolder()/fs::path("content"), + paths.getResourcesFolder()/fs::path("content") }); return manager; } @@ -329,7 +327,7 @@ static void load_configs(const fs::path& root) { void Engine::loadContent() { scripting::cleanup(); - auto resdir = paths->getResourcesFolder(); + auto resdir = paths.getResourcesFolder(); std::vector names; for (auto& pack : contentPacks) { @@ -337,10 +335,10 @@ void Engine::loadContent() { } ContentBuilder contentBuilder; - corecontent::setup(paths, &contentBuilder); + corecontent::setup(paths, contentBuilder); - paths->setContentPacks(&contentPacks); - PacksManager manager = createPacksManager(paths->getCurrentWorldFolder()); + paths.setContentPacks(&contentPacks); + PacksManager manager = createPacksManager(paths.getCurrentWorldFolder()); manager.scan(); names = manager.assembly(names); contentPacks = manager.getAll(names); @@ -378,7 +376,7 @@ void Engine::loadContent() { void Engine::resetContent() { scripting::cleanup(); - auto resdir = paths->getResourcesFolder(); + auto resdir = paths.getResourcesFolder(); std::vector resRoots; { auto pack = ContentPack::createCore(paths); @@ -407,17 +405,17 @@ void Engine::loadWorldContent(const fs::path& folder) { PacksManager manager; manager.setSources({ folder/fs::path("content"), - paths->getUserFilesFolder()/fs::path("content"), - paths->getResourcesFolder()/fs::path("content") + paths.getUserFilesFolder()/fs::path("content"), + paths.getResourcesFolder()/fs::path("content") }); manager.scan(); contentPacks = manager.getAll(manager.assembly(packNames)); - paths->setCurrentWorldFolder(folder); + paths.setCurrentWorldFolder(folder); loadContent(); } void Engine::loadAllPacks() { - PacksManager manager = createPacksManager(paths->getCurrentWorldFolder()); + PacksManager manager = createPacksManager(paths.getCurrentWorldFolder()); manager.scan(); auto allnames = manager.getAllNames(); contentPacks = manager.getAll(manager.assembly(allnames)); @@ -435,7 +433,7 @@ void Engine::setScreen(std::shared_ptr screen) { } void Engine::setLanguage(std::string locale) { - langs::setup(paths->getResourcesFolder(), std::move(locale), contentPacks); + langs::setup(paths.getResourcesFolder(), std::move(locale), contentPacks); gui->getMenu()->setPageLoader(menus::create_page_loader(this)); } @@ -470,7 +468,7 @@ std::vector& Engine::getBasePacks() { } EnginePaths* Engine::getPaths() { - return paths; + return &paths; } ResPaths* Engine::getResPaths() { diff --git a/src/engine.hpp b/src/engine.hpp index 04a221dff..efa0ff6d0 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -2,12 +2,14 @@ #include "delegates.hpp" #include "typedefs.hpp" +#include "settings.hpp" #include "assets/Assets.hpp" #include "content/content_fwd.hpp" #include "content/ContentPack.hpp" #include "content/PacksManager.hpp" #include "files/engine_paths.hpp" +#include "files/settings_io.hpp" #include "util/ObjectsKeeper.hpp" #include @@ -26,8 +28,6 @@ class EngineController; class SettingsHandler; struct EngineSettings; -namespace fs = std::filesystem; - namespace gui { class GUI; } @@ -46,9 +46,9 @@ class initialize_error : public std::runtime_error { }; class Engine : public util::ObjectsKeeper { - EngineSettings& settings; - SettingsHandler& settingsHandler; - EnginePaths* paths; + EngineSettings settings; + SettingsHandler settingsHandler; + EnginePaths& paths; std::unique_ptr assets; std::shared_ptr screen; @@ -77,7 +77,7 @@ class Engine : public util::ObjectsKeeper { void processPostRunnables(); void loadAssets(); public: - Engine(EngineSettings& settings, SettingsHandler& settingsHandler, EnginePaths* paths); + Engine(EnginePaths& paths); ~Engine(); /// @brief Start main engine input/update/render loop. diff --git a/src/util/command_line.cpp b/src/util/command_line.cpp index 99501c12f..88caf426d 100644 --- a/src/util/command_line.cpp +++ b/src/util/command_line.cpp @@ -40,7 +40,7 @@ class ArgsReader { } }; -bool perform_keyword( +static bool perform_keyword( ArgsReader& reader, const std::string& keyword, EnginePaths& paths ) { if (keyword == "--res") { diff --git a/src/voxel_engine.cpp b/src/voxel_engine.cpp index c6a2ec7ed..49ac502eb 100644 --- a/src/voxel_engine.cpp +++ b/src/voxel_engine.cpp @@ -1,6 +1,4 @@ #include "engine.hpp" -#include "settings.hpp" -#include "files/settings_io.hpp" #include "files/engine_paths.hpp" #include "util/platform.hpp" #include "util/command_line.hpp" @@ -19,12 +17,7 @@ int main(int argc, char** argv) { platform::configure_encoding(); try { - EngineSettings settings; - SettingsHandler handler(settings); - - Engine engine(settings, handler, &paths); - - engine.mainloop(); + Engine(paths).mainloop(); } catch (const initialize_error& err) { logger.error() << "could not to initialize engine\n" << err.what(); From 27acc1d8b1aedf8bc1b00199581464a41ac23ef1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 00:12:42 +0300 Subject: [PATCH 02/64] rename voxel_engine.cpp to main.cpp --- CMakeLists.txt | 4 ++-- src/CMakeLists.txt | 2 +- src/{voxel_engine.cpp => main.cpp} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename src/{voxel_engine.cpp => main.cpp} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index a56457bd2..9e84d36d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ option(VOXELENGINE_BUILD_TESTS OFF) set(CMAKE_CXX_STANDARD 17) add_subdirectory(src) -add_executable(${PROJECT_NAME} src/voxel_engine.cpp) +add_executable(${PROJECT_NAME} src/main.cpp) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) if(VOXELENGINE_BUILD_APPDIR) @@ -81,4 +81,4 @@ file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/res DESTINATION ${CMAKE_CURRENT_BINARY_DIR if (VOXELENGINE_BUILD_TESTS) enable_testing() add_subdirectory(test) -endif() \ No newline at end of file +endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7eecaef99..bc5bc04cd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,7 +4,7 @@ set(CMAKE_CXX_STANDARD 17) file(GLOB_RECURSE HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) -list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/voxel_engine.cpp) +list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp) add_library(${PROJECT_NAME} ${SOURCES} ${HEADERS}) diff --git a/src/voxel_engine.cpp b/src/main.cpp similarity index 100% rename from src/voxel_engine.cpp rename to src/main.cpp From 1ab37073433c0655f54adb16ff57f6d3955e439f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 00:52:40 +0300 Subject: [PATCH 03/64] update locale log messages --- src/frontend/locale.cpp | 54 +++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/src/frontend/locale.cpp b/src/frontend/locale.cpp index de040a11d..2af780769 100644 --- a/src/frontend/locale.cpp +++ b/src/frontend/locale.cpp @@ -37,38 +37,40 @@ const std::string& langs::Lang::getId() const { return locale; } -// @brief Language key-value txt files parser -class Reader : BasicParser { - void skipWhitespace() override { - BasicParser::skipWhitespace(); - if (hasNext() && source[pos] == '#') { - skipLine(); - if (hasNext() && is_whitespace(peek())) { - skipWhitespace(); +/// @brief Language key-value txt files parser +namespace { + class Reader : BasicParser { + void skipWhitespace() override { + BasicParser::skipWhitespace(); + if (hasNext() && source[pos] == '#') { + skipLine(); + if (hasNext() && is_whitespace(peek())) { + skipWhitespace(); + } } } - } -public: - Reader(std::string_view file, std::string_view source) : BasicParser(file, source) { - } + public: + Reader(std::string_view file, std::string_view source) + : BasicParser(file, source) { + } - void read(langs::Lang& lang, const std::string &prefix) { - skipWhitespace(); - while (hasNext()) { - std::string key = parseString('=', true); - util::trim(key); - key = prefix + key; - std::string text = parseString('\n', false); - util::trim(text); - lang.put(util::str2wstr_utf8(key), - util::str2wstr_utf8(text)); + void read(langs::Lang& lang, const std::string &prefix) { skipWhitespace(); + while (hasNext()) { + std::string key = parseString('=', true); + util::trim(key); + key = prefix + key; + std::string text = parseString('\n', false); + util::trim(text); + lang.put(util::str2wstr_utf8(key), util::str2wstr_utf8(text)); + skipWhitespace(); + } } - } -}; + }; +} void langs::loadLocalesInfo(const fs::path& resdir, std::string& fallback) { - fs::path file = resdir/fs::path(langs::TEXTS_FOLDER)/fs::path("langs.json"); + auto file = resdir/fs::u8path(langs::TEXTS_FOLDER)/fs::u8path("langs.json"); auto root = files::read_json(file); langs::locales_info.clear(); @@ -85,7 +87,7 @@ void langs::loadLocalesInfo(const fs::path& resdir, std::string& fallback) { } else { continue; } - logline << "[" << key << " (" << name << ")] "; + logline << key << " "; langs::locales_info[key] = LocaleInfo {key, name}; } logline << "added"; From ee1a170376e8a2ab6cbf9a99f2cf03d6ae2f31fa Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 01:12:27 +0300 Subject: [PATCH 04/64] add headless mode (engine initialization and finalization) --- src/audio/audio.cpp | 9 ++++- src/engine.cpp | 70 ++++++++++++++++++++++++++------------ src/engine.hpp | 16 +++++++-- src/files/engine_paths.cpp | 11 ++++++ src/main.cpp | 12 +++---- src/util/command_line.cpp | 30 ++++++++-------- src/util/command_line.hpp | 4 +-- 7 files changed, 101 insertions(+), 51 deletions(-) diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index 0dc0f591c..522e37053 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -8,6 +8,9 @@ #include "coders/wav.hpp" #include "AL/ALAudio.hpp" #include "NoAudio.hpp" +#include "debug/Logger.hpp" + +static debug::Logger logger("audio"); namespace audio { static speakerid_t nextId = 1; @@ -147,10 +150,14 @@ class PCMVoidSource : public PCMStream { void audio::initialize(bool enabled) { if (enabled) { + logger.info() << "initializing ALAudio backend"; backend = ALAudio::create().release(); } if (backend == nullptr) { - std::cerr << "could not to initialize audio" << std::endl; + if (enabled) { + std::cerr << "could not to initialize audio" << std::endl; + } + logger.info() << "initializing NoAudio backend"; backend = NoAudio::create().release(); } create_channel("master"); diff --git a/src/engine.cpp b/src/engine.cpp index 6641057de..3d0b1c216 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -69,42 +69,51 @@ static std::unique_ptr load_icon(const fs::path& resdir) { return nullptr; } -Engine::Engine(EnginePaths& paths) - : settings(), settingsHandler({settings}), paths(paths), +Engine::Engine(CoreParameters coreParameters) + : params(std::move(coreParameters)), + settings(), + settingsHandler({settings}), interpreter(std::make_unique()), - network(network::Network::create(settings.network)) -{ + network(network::Network::create(settings.network)) { + if (params.headless) { + logger.info() << "headless mode is enabled"; + } + paths.setResourcesFolder(params.resFolder); + paths.setUserFilesFolder(params.userFolder); paths.prepare(); loadSettings(); auto resdir = paths.getResourcesFolder(); controller = std::make_unique(this); - if (Window::initialize(&this->settings.display)){ - throw initialize_error("could not initialize window"); - } - if (auto icon = load_icon(resdir)) { - icon->flipY(); - Window::setIcon(icon.get()); + if (!params.headless) { + if (Window::initialize(&settings.display)){ + throw initialize_error("could not initialize window"); + } + if (auto icon = load_icon(resdir)) { + icon->flipY(); + Window::setIcon(icon.get()); + } + loadControls(); + + gui = std::make_unique(); + if (ENGINE_DEBUG_BUILD) { + menus::create_version_label(this); + } } - loadControls(); - audio::initialize(settings.audio.enabled.get()); + audio::initialize(settings.audio.enabled.get() && !params.headless); create_channel(this, "master", settings.audio.volumeMaster); create_channel(this, "regular", settings.audio.volumeRegular); create_channel(this, "music", settings.audio.volumeMusic); create_channel(this, "ambient", settings.audio.volumeAmbient); create_channel(this, "ui", settings.audio.volumeUI); - gui = std::make_unique(); if (settings.ui.language.get() == "auto") { settings.ui.language.set(langs::locale_by_envlocale( platform::detect_locale(), paths.getResourcesFolder() )); } - if (ENGINE_DEBUG_BUILD) { - menus::create_version_label(this); - } keepAlive(settings.ui.language.observe([=](auto lang) { setLanguage(lang); }, true)); @@ -165,6 +174,14 @@ void Engine::saveScreenshot() { logger.info() << "saved screenshot as " << filename.u8string(); } +void Engine::run() { + if (params.headless) { + logger.info() << "nothing to do"; + } else { + mainloop(); + } +} + void Engine::mainloop() { logger.info() << "starting menu screen"; setScreen(std::make_shared(this)); @@ -219,8 +236,10 @@ void Engine::processPostRunnables() { void Engine::saveSettings() { logger.info() << "saving settings"; files::write_string(paths.getSettingsFile(), toml::stringify(settingsHandler)); - logger.info() << "saving bindings"; - files::write_string(paths.getControlsFile(), Events::writeBindings()); + if (!params.headless) { + logger.info() << "saving bindings"; + files::write_string(paths.getControlsFile(), Events::writeBindings()); + } } Engine::~Engine() { @@ -233,13 +252,18 @@ Engine::~Engine() { content.reset(); assets.reset(); interpreter.reset(); - gui.reset(); - logger.info() << "gui finished"; + if (gui) { + gui.reset(); + logger.info() << "gui finished"; + } audio::close(); network.reset(); scripting::close(); logger.info() << "scripting finished"; - Window::terminate(); + if (!params.headless) { + Window::terminate(); + logger.info() << "window closed"; + } logger.info() << "engine finished"; } @@ -434,7 +458,9 @@ void Engine::setScreen(std::shared_ptr screen) { void Engine::setLanguage(std::string locale) { langs::setup(paths.getResourcesFolder(), std::move(locale), contentPacks); - gui->getMenu()->setPageLoader(menus::create_page_loader(this)); + if (gui) { + gui->getMenu()->setPageLoader(menus::create_page_loader(this)); + } } gui::GUI* Engine::getGUI() { diff --git a/src/engine.hpp b/src/engine.hpp index efa0ff6d0..aad321000 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -45,10 +45,17 @@ class initialize_error : public std::runtime_error { initialize_error(const std::string& message) : std::runtime_error(message) {} }; +struct CoreParameters { + bool headless = false; + std::filesystem::path resFolder {"res"}; + std::filesystem::path userFolder {"."}; +}; + class Engine : public util::ObjectsKeeper { + CoreParameters params; EngineSettings settings; SettingsHandler settingsHandler; - EnginePaths& paths; + EnginePaths paths; std::unique_ptr assets; std::shared_ptr screen; @@ -77,9 +84,12 @@ class Engine : public util::ObjectsKeeper { void processPostRunnables(); void loadAssets(); public: - Engine(EnginePaths& paths); + Engine(CoreParameters coreParameters); ~Engine(); - + + /// @brief Start the engine + void run(); + /// @brief Start main engine input/update/render loop. /// Automatically sets MenuScreen void mainloop(); diff --git a/src/files/engine_paths.cpp b/src/files/engine_paths.cpp index d92518970..23de64a7d 100644 --- a/src/files/engine_paths.cpp +++ b/src/files/engine_paths.cpp @@ -48,6 +48,17 @@ static std::filesystem::path toCanonic(std::filesystem::path path) { } void EnginePaths::prepare() { + logger.info() << "resources folder: " << fs::canonical(resourcesFolder).u8string(); + logger.info() << "user files folder: " << fs::canonical(userFilesFolder).u8string(); + + if (!fs::is_directory(resourcesFolder)) { + throw std::runtime_error( + resourcesFolder.u8string() + " is not a directory" + ); + } + if (!fs::is_directory(userFilesFolder)) { + fs::create_directories(userFilesFolder); + } auto contentFolder = userFilesFolder / CONTENT_FOLDER; if (!fs::is_directory(contentFolder)) { fs::create_directories(contentFolder); diff --git a/src/main.cpp b/src/main.cpp index 49ac502eb..a11020de3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,4 @@ #include "engine.hpp" -#include "files/engine_paths.hpp" #include "util/platform.hpp" #include "util/command_line.hpp" #include "debug/Logger.hpp" @@ -11,15 +10,14 @@ static debug::Logger logger("main"); int main(int argc, char** argv) { debug::Logger::init("latest.log"); - EnginePaths paths; - if (!parse_cmdline(argc, argv, paths)) + CoreParameters coreParameters; + if (!parse_cmdline(argc, argv, coreParameters)) { return EXIT_SUCCESS; - + } platform::configure_encoding(); try { - Engine(paths).mainloop(); - } - catch (const initialize_error& err) { + Engine(std::move(coreParameters)).run(); + } catch (const initialize_error& err) { logger.error() << "could not to initialize engine\n" << err.what(); } #ifdef NDEBUG diff --git a/src/util/command_line.cpp b/src/util/command_line.cpp index 88caf426d..e5a761971 100644 --- a/src/util/command_line.cpp +++ b/src/util/command_line.cpp @@ -7,6 +7,7 @@ #include #include "files/engine_paths.hpp" +#include "engine.hpp" namespace fs = std::filesystem; @@ -41,27 +42,24 @@ class ArgsReader { }; static bool perform_keyword( - ArgsReader& reader, const std::string& keyword, EnginePaths& paths + ArgsReader& reader, const std::string& keyword, CoreParameters& params ) { if (keyword == "--res") { auto token = reader.next(); - if (!fs::is_directory(fs::path(token))) { - throw std::runtime_error(token + " is not a directory"); - } - paths.setResourcesFolder(fs::path(token)); - std::cout << "resources folder: " << token << std::endl; + params.resFolder = fs::u8path(token); } else if (keyword == "--dir") { auto token = reader.next(); - if (!fs::is_directory(fs::path(token))) { - fs::create_directories(fs::path(token)); - } - paths.setUserFilesFolder(fs::path(token)); - std::cout << "userfiles folder: " << token << std::endl; + params.userFolder = fs::u8path(token); } else if (keyword == "--help" || keyword == "-h") { - std::cout << "VoxelEngine command-line arguments:" << std::endl; - std::cout << " --res [path] - set resources directory" << std::endl; - std::cout << " --dir [path] - set userfiles directory" << std::endl; + std::cout << "VoxelEngine command-line arguments:\n"; + std::cout << " --help - show help\n"; + std::cout << " --res [path] - set resources directory\n"; + std::cout << " --dir [path] - set userfiles directory\n"; + std::cout << " --headless - run in headless mode\n"; + std::cout << std::endl; return false; + } else if (keyword == "--headless") { + params.headless = true; } else { std::cerr << "unknown argument " << keyword << std::endl; return false; @@ -69,13 +67,13 @@ static bool perform_keyword( return true; } -bool parse_cmdline(int argc, char** argv, EnginePaths& paths) { +bool parse_cmdline(int argc, char** argv, CoreParameters& params) { ArgsReader reader(argc, argv); reader.skip(); while (reader.hasNext()) { std::string token = reader.next(); if (reader.isKeywordArg()) { - if (!perform_keyword(reader, token, paths)) { + if (!perform_keyword(reader, token, params)) { return false; } } else { diff --git a/src/util/command_line.hpp b/src/util/command_line.hpp index 9715c52df..ebb6741d8 100644 --- a/src/util/command_line.hpp +++ b/src/util/command_line.hpp @@ -1,6 +1,6 @@ #pragma once -class EnginePaths; +struct CoreParameters; /// @return false if engine start can -bool parse_cmdline(int argc, char** argv, EnginePaths& paths); +bool parse_cmdline(int argc, char** argv, CoreParameters& params); From c80e2dfe3a8cc948709c5f6f3ce556f172ce5ba6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 01:26:30 +0300 Subject: [PATCH 05/64] add 'Run tests' step to appimage.yml workflow --- .github/workflows/appimage.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 250fd001a..b264c95c4 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -38,6 +38,8 @@ jobs: run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_APPDIR=1 - name: build run: cmake --build build -t install + - name: Run tests + run: ctest --test-dir ${{github.workspace}}/build - name: Build AppImage uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910 env: From d81718a0b3d979430e2a10fbeccf71d368bc2b18 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 01:32:33 +0300 Subject: [PATCH 06/64] update appimage workflow --- .github/workflows/appimage.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index b264c95c4..4986c63bd 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v2 with: submodules: 'true' - - name: install dependencies + - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y build-essential libglfw3-dev libglfw3 libglew-dev \ @@ -34,9 +34,9 @@ jobs: cmake -DCMAKE_BUILD_TYPE=Release .. sudo make install cd ../.. - - name: configure - run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_APPDIR=1 - - name: build + - name: Configure + run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_APPDIR=1 -DVOXELENGINE_BUILD_TESTS=ON + - name: Build run: cmake --build build -t install - name: Run tests run: ctest --test-dir ${{github.workspace}}/build From 1d1d5c330c13be6d3543874b2c6c39f8b8820c5b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 01:34:21 +0300 Subject: [PATCH 07/64] update appimage workflow --- .github/workflows/appimage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 4986c63bd..5d4cf9764 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -24,7 +24,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y build-essential libglfw3-dev libglfw3 libglew-dev \ - libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev cmake squashfs-tools + libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev libgtest-dev cmake squashfs-tools # fix luajit paths sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a sudo ln -s /usr/include/luajit-2.1 /usr/include/lua From e0670c11d50bfa9301f9b5dcc7da4d9625452117 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 01:44:28 +0300 Subject: [PATCH 08/64] add 'Run engine (headless)' step to appimage workflow & disable Build AppImage and upload --- .github/workflows/appimage.yml | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 5d4cf9764..d7c2a57fd 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -40,13 +40,18 @@ jobs: run: cmake --build build -t install - name: Run tests run: ctest --test-dir ${{github.workspace}}/build - - name: Build AppImage - uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910 - env: - UPDATE_INFO: gh-releases-zsync|MihailRis|VoxelEngine-Cpp|latest|*x86_64.AppImage.zsync - with: - recipe: dev/AppImageBuilder.yml - - uses: actions/upload-artifact@v4 - with: - name: AppImage - path: './*.AppImage*' + - name: Run engine (headless) + run: | + mkdir ${{github.workspace}}/userdir + chmod +x ${{github.workspace}}/build/VoxelEngine + ${{github.workspace}}/build/VoxelEngine --headless --dir ${{github.workspace}}/userdir +# - name: Build AppImage +# uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910 +# env: +# UPDATE_INFO: gh-releases-zsync|MihailRis|VoxelEngine-Cpp|latest|*x86_64.AppImage.zsync +# with: +# recipe: dev/AppImageBuilder.yml +# - uses: actions/upload-artifact@v4 +# with: +# name: AppImage +# path: './*.AppImage*' From d1f92c21d0bbdf2df0eb3b31c5637bdf7110444c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 02:10:36 +0300 Subject: [PATCH 09/64] fix on_block_interact & fix segfault after engine finished --- .github/workflows/appimage.yml | 1 - src/engine.cpp | 1 + src/engine.hpp | 3 +-- src/files/engine_paths.cpp | 7 ++++--- src/logic/scripting/scripting.cpp | 3 ++- src/util/ObjectsKeeper.hpp | 4 ++++ 6 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index d7c2a57fd..9c26a8058 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -42,7 +42,6 @@ jobs: run: ctest --test-dir ${{github.workspace}}/build - name: Run engine (headless) run: | - mkdir ${{github.workspace}}/userdir chmod +x ${{github.workspace}}/build/VoxelEngine ${{github.workspace}}/build/VoxelEngine --headless --dir ${{github.workspace}}/userdir # - name: Build AppImage diff --git a/src/engine.cpp b/src/engine.cpp index 3d0b1c216..5b0c6803a 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -258,6 +258,7 @@ Engine::~Engine() { } audio::close(); network.reset(); + clearKeepedObjects(); scripting::close(); logger.info() << "scripting finished"; if (!params.headless) { diff --git a/src/engine.hpp b/src/engine.hpp index aad321000..f05a4607d 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -68,12 +68,11 @@ class Engine : public util::ObjectsKeeper { std::unique_ptr interpreter; std::unique_ptr network; std::vector basePacks; + std::unique_ptr gui; uint64_t frame = 0; double lastTime = 0.0; double delta = 0.0; - - std::unique_ptr gui; void loadControls(); void loadSettings(); diff --git a/src/files/engine_paths.cpp b/src/files/engine_paths.cpp index 23de64a7d..268573313 100644 --- a/src/files/engine_paths.cpp +++ b/src/files/engine_paths.cpp @@ -48,9 +48,6 @@ static std::filesystem::path toCanonic(std::filesystem::path path) { } void EnginePaths::prepare() { - logger.info() << "resources folder: " << fs::canonical(resourcesFolder).u8string(); - logger.info() << "user files folder: " << fs::canonical(userFilesFolder).u8string(); - if (!fs::is_directory(resourcesFolder)) { throw std::runtime_error( resourcesFolder.u8string() + " is not a directory" @@ -59,6 +56,10 @@ void EnginePaths::prepare() { if (!fs::is_directory(userFilesFolder)) { fs::create_directories(userFilesFolder); } + + logger.info() << "resources folder: " << fs::canonical(resourcesFolder).u8string(); + logger.info() << "user files folder: " << fs::canonical(userFilesFolder).u8string(); + auto contentFolder = userFilesFolder / CONTENT_FOLDER; if (!fs::is_directory(contentFolder)) { fs::create_directories(contentFolder); diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 9b43cf28c..ded9a970f 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -368,7 +368,7 @@ bool scripting::on_block_interact( Player* player, const Block& block, const glm::ivec3& pos ) { std::string name = block.name + ".interact"; - return lua::emit_event(lua::get_main_state(), name, [pos, player](auto L) { + auto result = lua::emit_event(lua::get_main_state(), name, [pos, player](auto L) { lua::pushivec_stack(L, pos); lua::pushinteger(L, player->getId()); return 4; @@ -386,6 +386,7 @@ bool scripting::on_block_interact( ); } } + return result; } void scripting::on_player_tick(Player* player, int tps) { diff --git a/src/util/ObjectsKeeper.hpp b/src/util/ObjectsKeeper.hpp index 000c4fc10..74a5a36b4 100644 --- a/src/util/ObjectsKeeper.hpp +++ b/src/util/ObjectsKeeper.hpp @@ -14,5 +14,9 @@ namespace util { virtual void keepAlive(std::shared_ptr ptr) { ptrs.push_back(ptr); } + + virtual void clearKeepedObjects() { + ptrs.clear(); + } }; } From d2fbb83d786a9208d2a78115ec1a9941d6c9191b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 02:25:42 +0300 Subject: [PATCH 10/64] disable extra locale reload in headless mode --- src/engine.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index 5b0c6803a..d7942072f 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -108,7 +108,8 @@ Engine::Engine(CoreParameters coreParameters) create_channel(this, "ambient", settings.audio.volumeAmbient); create_channel(this, "ui", settings.audio.volumeUI); - if (settings.ui.language.get() == "auto") { + bool langNotSet = settings.ui.language.get() == "auto"; + if (langNotSet) { settings.ui.language.set(langs::locale_by_envlocale( platform::detect_locale(), paths.getResourcesFolder() @@ -116,7 +117,7 @@ Engine::Engine(CoreParameters coreParameters) } keepAlive(settings.ui.language.observe([=](auto lang) { setLanguage(lang); - }, true)); + }, !langNotSet)); scripting::initialize(this); basePacks = files::read_list(resdir/fs::path("config/builtins.list")); From d9bd60f473f390d6c2f0616765079453e36ce6fe Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 02:45:51 +0300 Subject: [PATCH 11/64] update windows and macos workflows & add engine version to log --- .github/workflows/macos.yml | 28 ++++++++++++++++------------ .github/workflows/windows.yml | 24 +++++++++++++----------- src/engine.cpp | 1 + 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index e4ddf55ab..0d76f5f47 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -39,16 +39,20 @@ jobs: - name: Run tests run: ctest --output-on-failure --test-dir build - - name: Create DMG + - name: Run engine (headless) run: | - mkdir VoxelEngineDmgContent - cp -r build/res VoxelEngineDmgContent/ - cp -r build/VoxelEngine VoxelEngineDmgContent/ - cp -r build/libs VoxelEngineDmgContent/libs - hdiutil create VoxelEngineMacApp.dmg -volname "VoxelEngine" -srcfolder VoxelEngineDmgContent -ov -format UDZO - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: VoxelEngineMacOs - path: VoxelEngineMacApp.dmg + chmod +x build/VoxelEngine + build/VoxelEngine --headless --dir userdir +# - name: Create DMG +# run: | +# mkdir VoxelEngineDmgContent +# cp -r build/res VoxelEngineDmgContent/ +# cp -r build/VoxelEngine VoxelEngineDmgContent/ +# cp -r build/libs VoxelEngineDmgContent/libs +# hdiutil create VoxelEngineMacApp.dmg -volname "VoxelEngine" -srcfolder VoxelEngineDmgContent -ov -format UDZO +# +# - name: Upload artifacts +# uses: actions/upload-artifact@v4 +# with: +# name: VoxelEngineMacOs +# path: VoxelEngineMacApp.dmg diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b14c32d0c..57338d342 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -34,16 +34,18 @@ jobs: cd build cmake -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_WINDOWS_VCPKG=ON -DVOXELENGINE_BUILD_TESTS=ON .. cmake --build . --config Release - - name: Package for Windows - run: | - mkdir packaged - cp -r build/* packaged/ - cp C:/Windows/System32/msvcp140.dll packaged/Release/msvcp140.dll - mv packaged/Release/VoxelEngine.exe packaged/Release/VoxelCore.exe - working-directory: ${{ github.workspace }} - name: Run tests run: ctest --output-on-failure --test-dir build - - uses: actions/upload-artifact@v4 - with: - name: Windows-Build - path: 'packaged/Release/*' + - name: Run engine (headless) + run: build/Release/VoxelEngine.exe --headless --dir userdir +# - name: Package for Windows +# run: | +# mkdir packaged +# cp -r build/* packaged/ +# cp C:/Windows/System32/msvcp140.dll packaged/Release/msvcp140.dll +# mv packaged/Release/VoxelEngine.exe packaged/Release/VoxelCore.exe +# working-directory: ${{ github.workspace }} +# - uses: actions/upload-artifact@v4 +# with: +# name: Windows-Build +# path: 'packaged/Release/*' diff --git a/src/engine.cpp b/src/engine.cpp index d7942072f..422637b8d 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -75,6 +75,7 @@ Engine::Engine(CoreParameters coreParameters) settingsHandler({settings}), interpreter(std::make_unique()), network(network::Network::create(settings.network)) { + logger.info() << "engine version: " << ENGINE_VERSION_STRING; if (params.headless) { logger.info() << "headless mode is enabled"; } From 5e8805f241682460f19cc42e5150f6b43deae15f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 15:49:23 +0300 Subject: [PATCH 12/64] add vctest (WIP) --- .github/workflows/appimage.yml | 1 + .github/workflows/macos.yml | 1 + .github/workflows/windows.yml | 1 + CMakeLists.txt | 2 + dev/tests/example.lua | 1 + res/scripts/stdlib.lua | 37 ++++++ src/engine.cpp | 16 ++- src/engine.hpp | 3 + src/interfaces/Process.hpp | 12 ++ src/logic/scripting/scripting.cpp | 54 ++++++++ src/logic/scripting/scripting.hpp | 19 +-- src/main.cpp | 14 +- src/util/ArgsReader.hpp | 37 ++++++ src/util/command_line.cpp | 49 ++----- vctest/CMakeLists.txt | 31 +++++ vctest/main.cpp | 213 ++++++++++++++++++++++++++++++ 16 files changed, 439 insertions(+), 52 deletions(-) create mode 100644 dev/tests/example.lua create mode 100644 src/interfaces/Process.hpp create mode 100644 src/util/ArgsReader.hpp create mode 100644 vctest/CMakeLists.txt create mode 100644 vctest/main.cpp diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 9c26a8058..bac80b19a 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -41,6 +41,7 @@ jobs: - name: Run tests run: ctest --test-dir ${{github.workspace}}/build - name: Run engine (headless) + timeout-minutes: 1 run: | chmod +x ${{github.workspace}}/build/VoxelEngine ${{github.workspace}}/build/VoxelEngine --headless --dir ${{github.workspace}}/userdir diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 0d76f5f47..d3adc67f5 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -43,6 +43,7 @@ jobs: run: | chmod +x build/VoxelEngine build/VoxelEngine --headless --dir userdir + timeout-minutes: 1 # - name: Create DMG # run: | # mkdir VoxelEngineDmgContent diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 57338d342..c11d0da47 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -38,6 +38,7 @@ jobs: run: ctest --output-on-failure --test-dir build - name: Run engine (headless) run: build/Release/VoxelEngine.exe --headless --dir userdir + timeout-minutes: 1 # - name: Package for Windows # run: | # mkdir packaged diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e84d36d2..57f19747b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,3 +82,5 @@ if (VOXELENGINE_BUILD_TESTS) enable_testing() add_subdirectory(test) endif() + +add_subdirectory(vctest) diff --git a/dev/tests/example.lua b/dev/tests/example.lua new file mode 100644 index 000000000..3b724d471 --- /dev/null +++ b/dev/tests/example.lua @@ -0,0 +1 @@ +print("Hello from the example test!") diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 9cf23cc3f..d5f8eed1c 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -328,6 +328,43 @@ function __vc_on_world_quit() _rules.clear() end +local __vc_coroutines = {} +local __vc_next_coroutine = 1 +local __vc_coroutine_error = nil + +function __vc_start_coroutine(chunk) + local co = coroutine.create(function() + local status, err = pcall(chunk) + if not status then + __vc_coroutine_error = err + end + end) + local id = __vc_next_coroutine + __vc_next_coroutine = __vc_next_coroutine + 1 + __vc_coroutines[id] = co + return id +end + +function __vc_resume_coroutine(id) + local co = __vc_coroutines[id] + if co then + coroutine.resume(co) + if __vc_coroutine_error then + error(__vc_coroutine_error) + end + return coroutine.status(co) ~= "dead" + end + return false +end + +function __vc_stop_coroutine(id) + local co = __vc_coroutines[id] + if co then + coroutine.close(co) + __vc_coroutines[id] = nil + end +end + assets = {} assets.load_texture = core.__load_texture diff --git a/src/engine.cpp b/src/engine.cpp index 422637b8d..49386b65b 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -36,6 +36,7 @@ #include "window/Events.hpp" #include "window/input.hpp" #include "window/Window.hpp" +#include "interfaces/Process.hpp" #include #include @@ -178,12 +179,25 @@ void Engine::saveScreenshot() { void Engine::run() { if (params.headless) { - logger.info() << "nothing to do"; + runTest(); } else { mainloop(); } } +void Engine::runTest() { + if (params.testFile.empty()) { + logger.info() << "nothing to do"; + return; + } + logger.info() << "starting test " << params.testFile; + auto process = scripting::start_coroutine(params.testFile); + while (process->isActive()) { + process->update(); + } + logger.info() << "test finished"; +} + void Engine::mainloop() { logger.info() << "starting menu screen"; setScreen(std::make_shared(this)); diff --git a/src/engine.hpp b/src/engine.hpp index f05a4607d..c3559fa82 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -49,6 +49,7 @@ struct CoreParameters { bool headless = false; std::filesystem::path resFolder {"res"}; std::filesystem::path userFolder {"."}; + std::filesystem::path testFile; }; class Engine : public util::ObjectsKeeper { @@ -93,6 +94,8 @@ class Engine : public util::ObjectsKeeper { /// Automatically sets MenuScreen void mainloop(); + void runTest(); + /// @brief Called after assets loading when all engine systems are initialized void onAssetsLoaded(); diff --git a/src/interfaces/Process.hpp b/src/interfaces/Process.hpp new file mode 100644 index 000000000..b62b1e0f0 --- /dev/null +++ b/src/interfaces/Process.hpp @@ -0,0 +1,12 @@ +#pragma once + +/// @brief Process interface. +class Process { +public: + virtual ~Process() {} + + virtual bool isActive() const = 0; + virtual void update() = 0; + virtual void waitForEnd() = 0; + virtual void terminate() = 0; +}; diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index ded9a970f..9abe534c8 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -25,6 +25,7 @@ #include "util/timeutil.hpp" #include "voxels/Block.hpp" #include "world/Level.hpp" +#include "interfaces/Process.hpp" using namespace scripting; @@ -71,6 +72,59 @@ void scripting::initialize(Engine* engine) { load_script(fs::path("classes.lua"), true); } +class LuaCoroutine : public Process { + lua::State* L; + int id; + bool alive = true; +public: + LuaCoroutine(lua::State* L, int id) : L(L), id(id) { + } + + bool isActive() const override { + return alive; + } + + void update() override { + if (lua::getglobal(L, "__vc_resume_coroutine")) { + lua::pushinteger(L, id); + if (lua::call(L, 1)) { + alive = lua::toboolean(L, -1); + lua::pop(L); + } + } + } + + void waitForEnd() override { + while (isActive()) { + update(); + } + } + + void terminate() override { + if (lua::getglobal(L, "__vc_stop_coroutine")) { + lua::pushinteger(L, id); + lua::pop(L, lua::call(L, 1)); + } + } +}; + +std::unique_ptr scripting::start_coroutine( + const std::filesystem::path& script +) { + auto L = lua::get_main_state(); + if (lua::getglobal(L, "__vc_start_coroutine")) { + auto source = files::read_string(script); + lua::loadbuffer(L, 0, source, script.filename().u8string()); + if (lua::call(L, 1)) { + int id = lua::tointeger(L, -1); + lua::pop(L, 2); + return std::make_unique(L, id); + } + lua::pop(L); + } + return nullptr; +} + [[nodiscard]] scriptenv scripting::get_root_environment() { return std::make_shared(0); } diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index 0ab3c0351..4ccba2cb0 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -11,8 +11,6 @@ #include "typedefs.hpp" #include "scripting_functional.hpp" -namespace fs = std::filesystem; - class Engine; class Content; struct ContentPack; @@ -34,6 +32,7 @@ class Entity; struct EntityDef; class GeneratorScript; struct GeneratorDef; +class Process; namespace scripting { extern Engine* engine; @@ -60,6 +59,10 @@ namespace scripting { void process_post_runnables(); + std::unique_ptr start_coroutine( + const std::filesystem::path& script + ); + void on_world_load(LevelController* controller); void on_world_tick(); void on_world_save(); @@ -136,7 +139,7 @@ namespace scripting { void load_content_script( const scriptenv& env, const std::string& prefix, - const fs::path& file, + const std::filesystem::path& file, const std::string& fileName, block_funcs_set& funcsset ); @@ -150,7 +153,7 @@ namespace scripting { void load_content_script( const scriptenv& env, const std::string& prefix, - const fs::path& file, + const std::filesystem::path& file, const std::string& fileName, item_funcs_set& funcsset ); @@ -161,13 +164,13 @@ namespace scripting { /// @param fileName script file path using the engine format void load_entity_component( const std::string& name, - const fs::path& file, + const std::filesystem::path& file, const std::string& fileName ); std::unique_ptr load_generator( const GeneratorDef& def, - const fs::path& file, + const std::filesystem::path& file, const std::string& dirPath ); @@ -179,7 +182,7 @@ namespace scripting { void load_world_script( const scriptenv& env, const std::string& packid, - const fs::path& file, + const std::filesystem::path& file, const std::string& fileName, world_funcs_set& funcsset ); @@ -193,7 +196,7 @@ namespace scripting { void load_layout_script( const scriptenv& env, const std::string& prefix, - const fs::path& file, + const std::filesystem::path& file, const std::string& fileName, uidocscript& script ); diff --git a/src/main.cpp b/src/main.cpp index a11020de3..b0d034123 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,17 +3,23 @@ #include "util/command_line.hpp" #include "debug/Logger.hpp" +#include #include static debug::Logger logger("main"); int main(int argc, char** argv) { - debug::Logger::init("latest.log"); - CoreParameters coreParameters; - if (!parse_cmdline(argc, argv, coreParameters)) { - return EXIT_SUCCESS; + try { + if (!parse_cmdline(argc, argv, coreParameters)) { + return EXIT_SUCCESS; + } + } catch (const std::runtime_error& err) { + std::cerr << err.what() << std::endl; + return EXIT_FAILURE; } + + debug::Logger::init(coreParameters.userFolder.string()+"/latest.log"); platform::configure_encoding(); try { Engine(std::move(coreParameters)).run(); diff --git a/src/util/ArgsReader.hpp b/src/util/ArgsReader.hpp new file mode 100644 index 000000000..90d159f50 --- /dev/null +++ b/src/util/ArgsReader.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include + +namespace util { + class ArgsReader { + const char* last = ""; + char** argv; + int argc; + int pos = 0; + public: + ArgsReader(int argc, char** argv) : argv(argv), argc(argc) { + } + + void skip() { + pos++; + } + + bool hasNext() const { + return pos < argc && std::strlen(argv[pos]); + } + + bool isKeywordArg() const { + return last[0] == '-'; + } + + std::string next() { + if (pos >= argc) { + throw std::runtime_error("unexpected end"); + } + last = argv[pos]; + return argv[pos++]; + } + }; +} diff --git a/src/util/command_line.cpp b/src/util/command_line.cpp index e5a761971..768c6260e 100644 --- a/src/util/command_line.cpp +++ b/src/util/command_line.cpp @@ -1,48 +1,16 @@ #include "command_line.hpp" -#include #include #include -#include -#include #include "files/engine_paths.hpp" +#include "util/ArgsReader.hpp" #include "engine.hpp" namespace fs = std::filesystem; -class ArgsReader { - const char* last = ""; - char** argv; - int argc; - int pos = 0; -public: - ArgsReader(int argc, char** argv) : argv(argv), argc(argc) { - } - - void skip() { - pos++; - } - - bool hasNext() const { - return pos < argc && strlen(argv[pos]); - } - - bool isKeywordArg() const { - return last[0] == '-'; - } - - std::string next() { - if (pos >= argc) { - throw std::runtime_error("unexpected end"); - } - last = argv[pos]; - return argv[pos++]; - } -}; - static bool perform_keyword( - ArgsReader& reader, const std::string& keyword, CoreParameters& params + util::ArgsReader& reader, const std::string& keyword, CoreParameters& params ) { if (keyword == "--res") { auto token = reader.next(); @@ -53,22 +21,25 @@ static bool perform_keyword( } else if (keyword == "--help" || keyword == "-h") { std::cout << "VoxelEngine command-line arguments:\n"; std::cout << " --help - show help\n"; - std::cout << " --res [path] - set resources directory\n"; - std::cout << " --dir [path] - set userfiles directory\n"; + std::cout << " --res - set resources directory\n"; + std::cout << " --dir - set userfiles directory\n"; std::cout << " --headless - run in headless mode\n"; + std::cout << " --test - test script file\n"; std::cout << std::endl; return false; } else if (keyword == "--headless") { params.headless = true; + } else if (keyword == "--test") { + auto token = reader.next(); + params.testFile = fs::u8path(token); } else { - std::cerr << "unknown argument " << keyword << std::endl; - return false; + throw std::runtime_error("unknown argument " + keyword); } return true; } bool parse_cmdline(int argc, char** argv, CoreParameters& params) { - ArgsReader reader(argc, argv); + util::ArgsReader reader(argc, argv); reader.skip(); while (reader.hasNext()) { std::string token = reader.next(); diff --git a/vctest/CMakeLists.txt b/vctest/CMakeLists.txt new file mode 100644 index 000000000..6c1a3843c --- /dev/null +++ b/vctest/CMakeLists.txt @@ -0,0 +1,31 @@ +project(vctest) + +set(CMAKE_CXX_STANDARD 17) + +file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) + +add_executable(${PROJECT_NAME} ${SOURCES}) + +if(MSVC) + if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) + endif() + if((CMAKE_BUILD_TYPE EQUAL "Release") OR (CMAKE_BUILD_TYPE EQUAL "RelWithDebInfo")) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Release>") + target_compile_options(${PROJECT_NAME} PRIVATE /W4 /MT /O2) + else() + target_compile_options(${PROJECT_NAME} PRIVATE /W4) + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +else() + target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra + -Wformat-nonliteral -Wcast-align + -Wpointer-arith -Wundef + -Wwrite-strings -Wno-unused-parameter) +endif() + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie -lstdc++fs") +endif() + +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src ${CMAKE_DL_LIBS}) diff --git a/vctest/main.cpp b/vctest/main.cpp new file mode 100644 index 000000000..f6d3e7733 --- /dev/null +++ b/vctest/main.cpp @@ -0,0 +1,213 @@ +#include +#include +#include +#include +#include +#include + +#include "util/ArgsReader.hpp" + +namespace fs = std::filesystem; + +inline fs::path TESTING_DIR = fs::u8path(".vctest"); + +struct Config { + fs::path executable; + fs::path directory; + fs::path resDir {"res"}; + fs::path workingDir {"."}; + bool outputAlways = false; +}; + +static bool perform_keyword( + util::ArgsReader& reader, const std::string& keyword, Config& config +) { + if (keyword == "--help" || keyword == "-h") { + std::cout << "Options\n\n"; + std::cout << " --help, -h = show help\n"; + std::cout << " --exe , -e = VoxelCore executable path\n"; + std::cout << " --tests , -d = tests directory path\n"; + std::cout << " --res , -r = 'res' directory path\n"; + std::cout << " --working-dir , -w = user directory path\n"; + std::cout << " --output-always = always show tests output\n"; + std::cout << std::endl; + return false; + } else if (keyword == "--exe" || keyword == "-e") { + config.executable = fs::path(reader.next()); + } else if (keyword == "--tests" || keyword == "-d") { + config.directory = fs::path(reader.next()); + } else if (keyword == "--res" || keyword == "-r") { + config.resDir = fs::path(reader.next()); + } else if (keyword == "--user" || keyword == "-u") { + config.workingDir = fs::path(reader.next()); + } else if (keyword == "--output-always") { + config.outputAlways = true; + } else { + std::cerr << "unknown argument " << keyword << std::endl; + return false; + } + return true; +} + +static bool parse_cmdline(int argc, char** argv, Config& config) { + util::ArgsReader reader(argc, argv); + while (reader.hasNext()) { + std::string token = reader.next(); + if (reader.isKeywordArg()) { + if (!perform_keyword(reader, token, config)) { + return false; + } + } + } + return true; +} + +static bool check_dir(const fs::path& dir) { + if (!fs::is_directory(dir)) { + std::cerr << dir << " is not a directory" << std::endl; + return false; + } + return true; +} + +static void print_separator(std::ostream& stream) { + for (int i = 0; i < 32; i++) { + stream << "="; + } + stream << "\n"; +} + +static bool check_config(const Config& config) { + if (!fs::exists(config.executable)) { + std::cerr << "file " << config.executable << " not found" << std::endl; + return true; + } + if (!check_dir(config.directory)) { + return true; + } + if (!check_dir(config.resDir)) { + return true; + } + if (!check_dir(config.workingDir)) { + return true; + } + return false; +} + +static void dump_config(const Config& config) { + std::cout << "paths:\n"; + std::cout << " VoxelCore executable = " << fs::canonical(config.executable).string() << "\n"; + std::cout << " Tests directory = " << fs::canonical(config.directory).string() << "\n"; + std::cout << " Resources directory = " << fs::canonical(config.resDir).string() << "\n"; + std::cout << " Working directory = " << fs::canonical(config.workingDir).string(); + std::cout << std::endl; +} + +static void cleanup(const fs::path& workingDir) { + auto dir = workingDir / TESTING_DIR; + std::cout << "cleaning up " << dir << std::endl; + fs::remove_all(dir); +} + +static void setup_working_dir(const fs::path& workingDir) { + auto dir = workingDir / TESTING_DIR; + std::cout << "setting up working directory " << dir << std::endl; + if (fs::is_directory(dir)) { + cleanup(workingDir); + } + fs::create_directories(dir); +} + +static void display_test_output(const fs::path& path, std::ostream& stream) { + stream << "[OUTPUT]" << std::endl; + if (fs::exists(path)) { + std::ifstream t(path); + stream << t.rdbuf(); + } +} + +static bool run_test(const Config& config, const fs::path& path) { + using std::chrono::duration_cast; + using std::chrono::high_resolution_clock; + using std::chrono::milliseconds; + + auto outputFile = config.workingDir / "output.txt"; + + auto name = path.stem(); + std::stringstream ss; + ss << config.executable << " --headless"; + ss << " --test " << path; + ss << " --res " << config.resDir; + ss << " --dir " << config.workingDir; + ss << " >" << (config.workingDir / "output.txt") << " 2>&1"; + auto command = ss.str(); + + print_separator(std::cout); + std::cout << "executing test " << name << "\ncommand: " << command << std::endl; + + auto start = high_resolution_clock::now(); + int code = system(command.c_str()); + auto testTime = + duration_cast(high_resolution_clock::now() - start) + .count(); + + if (code) { + display_test_output(outputFile, std::cerr); + std::cerr << "[FAILED] " << name << " in " << testTime << " ms" << std::endl; + fs::remove(outputFile); + return false; + } else { + if (config.outputAlways) { + display_test_output(outputFile, std::cout); + } + std::cout << "[PASSED] " << name << " in " << testTime << " ms" << std::endl; + fs::remove(outputFile); + return true; + } +} + +int main(int argc, char** argv) { + Config config; + try { + if (!parse_cmdline(argc, argv, config)) { + return 0; + } + } catch (const std::runtime_error& err) { + std::cerr << err.what() << std::endl; + throw; + } + if (check_config(config)) { + return 1; + } + dump_config(config); + + std::vector tests; + std::cout << "scanning for tests" << std::endl; + for (const auto& entry : fs::directory_iterator(config.directory)) { + auto path = entry.path(); + if (path.extension().string() != ".lua") { + std::cout << " " << entry.path() << " skipped" << std::endl; + continue; + } + std::cout << " " << entry.path() << " enqueued" << std::endl; + tests.push_back(path); + } + + setup_working_dir(config.workingDir); + config.workingDir /= TESTING_DIR; + + size_t passed = 0; + std::cout << "running " << tests.size() << " test(s)" << std::endl; + for (const auto& path : tests) { + passed += run_test(config, path); + } + print_separator(std::cout); + cleanup(config.workingDir); + std::cout << std::endl; + std::cout << passed << " test(s) passed, " << (tests.size() - passed) + << " test(s) failed" << std::endl; + if (passed < tests.size()) { + return 1; + } + return 0; +} From 95b848f44c3eb06f66148c617028933e3be0c8ba Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 16:15:09 +0300 Subject: [PATCH 13/64] replace 'Run engine (headless)' with 'Run engine tests' in AppImage workflow --- .github/workflows/appimage.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index bac80b19a..7eb6b6e30 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -39,12 +39,13 @@ jobs: - name: Build run: cmake --build build -t install - name: Run tests - run: ctest --test-dir ${{github.workspace}}/build - - name: Run engine (headless) + run: ctest --test-dir build + - name: Run engine tests timeout-minutes: 1 run: | - chmod +x ${{github.workspace}}/build/VoxelEngine - ${{github.workspace}}/build/VoxelEngine --headless --dir ${{github.workspace}}/userdir + chmod +x build/VoxelEngine + chmod +x build/vctest/vctest + build/vctest/vctest -e build/VoxelEngine -d dev/tests -u build --output-always # - name: Build AppImage # uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910 # env: From 7405caa47140a16d2a0a47c697aff893eceede59 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 16:28:17 +0300 Subject: [PATCH 14/64] update appimage.yml --- .github/workflows/appimage.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 7eb6b6e30..dba47c3d6 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -24,7 +24,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y build-essential libglfw3-dev libglfw3 libglew-dev \ - libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev libgtest-dev cmake squashfs-tools + libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev libgtest-dev cmake squashfs-tools tree # fix luajit paths sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a sudo ln -s /usr/include/luajit-2.1 /usr/include/lua @@ -43,9 +43,10 @@ jobs: - name: Run engine tests timeout-minutes: 1 run: | + tree chmod +x build/VoxelEngine - chmod +x build/vctest/vctest - build/vctest/vctest -e build/VoxelEngine -d dev/tests -u build --output-always +# chmod +x build/vctest/vctest +# build/vctest/vctest -e build/VoxelEngine -d dev/tests -u build --output-always # - name: Build AppImage # uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910 # env: From df4b4a382555993360e6cfb656c177c33249ca97 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 16:47:09 +0300 Subject: [PATCH 15/64] update appimage.yml --- .github/workflows/appimage.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index dba47c3d6..c491e3997 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -43,10 +43,9 @@ jobs: - name: Run engine tests timeout-minutes: 1 run: | - tree chmod +x build/VoxelEngine -# chmod +x build/vctest/vctest -# build/vctest/vctest -e build/VoxelEngine -d dev/tests -u build --output-always + chmod +x AppDir/usr/bin/vctest + AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build --output-always # - name: Build AppImage # uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910 # env: From 19b458d048667ee70cedf9a51af3fdb8deb478f1 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 17:00:20 +0300 Subject: [PATCH 16/64] replace 'Run engine (headless)' with 'Run engine tests' --- .github/workflows/appimage.yml | 2 +- .github/workflows/macos.yml | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index c491e3997..257e81e60 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -45,7 +45,7 @@ jobs: run: | chmod +x build/VoxelEngine chmod +x AppDir/usr/bin/vctest - AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build --output-always + AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build # - name: Build AppImage # uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910 # env: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index d3adc67f5..1cd0c7dc3 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -39,11 +39,12 @@ jobs: - name: Run tests run: ctest --output-on-failure --test-dir build - - name: Run engine (headless) + - name: Run engine tests + timeout-minutes: 1 run: | chmod +x build/VoxelEngine - build/VoxelEngine --headless --dir userdir - timeout-minutes: 1 + chmod +x build/vctest/vctest + build/vctest/vctest -e build/VoxelEngine -d dev/tests -u build --output-always # - name: Create DMG # run: | # mkdir VoxelEngineDmgContent From 2834fbec90dcfd8efc094feebbc989b2dc2a6ff9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 17:06:14 +0300 Subject: [PATCH 17/64] update macos.yml --- .github/workflows/macos.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 1cd0c7dc3..ab12bfa17 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -17,7 +17,7 @@ jobs: - name: Install dependencies from brew run: | - brew install glfw3 glew libpng openal-soft luajit libvorbis skypjack/entt/entt googletest + brew install glfw3 glew libpng openal-soft luajit libvorbis skypjack/entt/entt googletest tree - name: Install specific version of GLM run: | @@ -42,6 +42,7 @@ jobs: - name: Run engine tests timeout-minutes: 1 run: | + tree chmod +x build/VoxelEngine chmod +x build/vctest/vctest build/vctest/vctest -e build/VoxelEngine -d dev/tests -u build --output-always From 7528649ad9a8093695279aebd6941eaeda2f4cba Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 17:11:45 +0300 Subject: [PATCH 18/64] update macos.yml --- .github/workflows/macos.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index ab12bfa17..aa5b1d0a4 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -17,7 +17,7 @@ jobs: - name: Install dependencies from brew run: | - brew install glfw3 glew libpng openal-soft luajit libvorbis skypjack/entt/entt googletest tree + brew install glfw3 glew libpng openal-soft luajit libvorbis skypjack/entt/entt googletest - name: Install specific version of GLM run: | @@ -42,10 +42,9 @@ jobs: - name: Run engine tests timeout-minutes: 1 run: | - tree chmod +x build/VoxelEngine - chmod +x build/vctest/vctest - build/vctest/vctest -e build/VoxelEngine -d dev/tests -u build --output-always + chmod +x AppDir/usr/bin/vctest + AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build # - name: Create DMG # run: | # mkdir VoxelEngineDmgContent From c0309444c2e7e8ce9847c7e922592fe04f577875 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 17:14:48 +0300 Subject: [PATCH 19/64] replace 'Run engine (headless)' with 'Run engine tests' in windows workflow --- .github/workflows/windows.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c11d0da47..8b9222c85 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -36,8 +36,10 @@ jobs: cmake --build . --config Release - name: Run tests run: ctest --output-on-failure --test-dir build - - name: Run engine (headless) - run: build/Release/VoxelEngine.exe --headless --dir userdir + - name: Run engine tests + run: | + tree + build/Release/vctest/vctest.exe -e build/Release/VoxelEngine.exe -d dev/tests -u build timeout-minutes: 1 # - name: Package for Windows # run: | From ed4bfeecee89411349cab51bb5767f09ebbaa3c0 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 17:27:41 +0300 Subject: [PATCH 20/64] update windows.yml --- .github/workflows/windows.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 8b9222c85..bac73b836 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -38,8 +38,8 @@ jobs: run: ctest --output-on-failure --test-dir build - name: Run engine tests run: | - tree - build/Release/vctest/vctest.exe -e build/Release/VoxelEngine.exe -d dev/tests -u build + tree build + build/vctest/Release/vctest.exe -e build/Release/VoxelEngine.exe -d dev/tests -u build timeout-minutes: 1 # - name: Package for Windows # run: | From 95be0e74b8574fba3f83562e27c77bb3dddf8b58 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 17:42:15 +0300 Subject: [PATCH 21/64] update vctest --- .github/workflows/windows.yml | 1 - vctest/main.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index bac73b836..1f7978dd1 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -38,7 +38,6 @@ jobs: run: ctest --output-on-failure --test-dir build - name: Run engine tests run: | - tree build build/vctest/Release/vctest.exe -e build/Release/VoxelEngine.exe -d dev/tests -u build timeout-minutes: 1 # - name: Package for Windows diff --git a/vctest/main.cpp b/vctest/main.cpp index f6d3e7733..f8cd1767a 100644 --- a/vctest/main.cpp +++ b/vctest/main.cpp @@ -135,7 +135,7 @@ static bool run_test(const Config& config, const fs::path& path) { auto name = path.stem(); std::stringstream ss; - ss << config.executable << " --headless"; + ss << fs::canonical(config.executable) << " --headless"; ss << " --test " << path; ss << " --res " << config.resDir; ss << " --dir " << config.workingDir; From f86bbaa2cea93e31d6987fed3e10c82c702da367 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 18:21:38 +0300 Subject: [PATCH 22/64] update vctest --- vctest/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vctest/main.cpp b/vctest/main.cpp index f8cd1767a..afea34519 100644 --- a/vctest/main.cpp +++ b/vctest/main.cpp @@ -139,7 +139,7 @@ static bool run_test(const Config& config, const fs::path& path) { ss << " --test " << path; ss << " --res " << config.resDir; ss << " --dir " << config.workingDir; - ss << " >" << (config.workingDir / "output.txt") << " 2>&1"; + ss << " >" << (config.workingDir / "output.txt").string() << " 2>&1"; auto command = ss.str(); print_separator(std::cout); From c9207a8b151f80e3df28e1e32b7cdf2f8bb34fcd Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 18:47:29 +0300 Subject: [PATCH 23/64] update vctest --- vctest/main.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/vctest/main.cpp b/vctest/main.cpp index afea34519..1bc825454 100644 --- a/vctest/main.cpp +++ b/vctest/main.cpp @@ -126,6 +126,15 @@ static void display_test_output(const fs::path& path, std::ostream& stream) { } } +static std::string fix_path(std::string s) { + for (char& c : s) { + if (c == '\\') { + c = '/'; + } + } + return s; +} + static bool run_test(const Config& config, const fs::path& path) { using std::chrono::duration_cast; using std::chrono::high_resolution_clock; @@ -139,7 +148,7 @@ static bool run_test(const Config& config, const fs::path& path) { ss << " --test " << path; ss << " --res " << config.resDir; ss << " --dir " << config.workingDir; - ss << " >" << (config.workingDir / "output.txt").string() << " 2>&1"; + ss << " >" << fix_path((config.workingDir / "output.txt").string()) << " 2>&1"; auto command = ss.str(); print_separator(std::cout); From 798a86d692b6510cbce64689d92240cf8f959007 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 19:23:49 +0300 Subject: [PATCH 24/64] update vctest --- vctest/main.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vctest/main.cpp b/vctest/main.cpp index 1bc825454..3f0629e8c 100644 --- a/vctest/main.cpp +++ b/vctest/main.cpp @@ -103,8 +103,7 @@ static void dump_config(const Config& config) { std::cout << std::endl; } -static void cleanup(const fs::path& workingDir) { - auto dir = workingDir / TESTING_DIR; +static void cleanup(const fs::path& dir) { std::cout << "cleaning up " << dir << std::endl; fs::remove_all(dir); } @@ -113,7 +112,7 @@ static void setup_working_dir(const fs::path& workingDir) { auto dir = workingDir / TESTING_DIR; std::cout << "setting up working directory " << dir << std::endl; if (fs::is_directory(dir)) { - cleanup(workingDir); + cleanup(dir); } fs::create_directories(dir); } @@ -148,7 +147,7 @@ static bool run_test(const Config& config, const fs::path& path) { ss << " --test " << path; ss << " --res " << config.resDir; ss << " --dir " << config.workingDir; - ss << " >" << fix_path((config.workingDir / "output.txt").string()) << " 2>&1"; + ss << " >" << fix_path(outputFile.string()) << " 2>&1"; auto command = ss.str(); print_separator(std::cout); @@ -189,6 +188,8 @@ int main(int argc, char** argv) { return 1; } dump_config(config); + + system("cd"); std::vector tests; std::cout << "scanning for tests" << std::endl; From bc102075f2d5d33368577bfa38380abd14a2aa45 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 19:36:47 +0300 Subject: [PATCH 25/64] update vctest (MSVC) --- vctest/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vctest/main.cpp b/vctest/main.cpp index 3f0629e8c..35253bce8 100644 --- a/vctest/main.cpp +++ b/vctest/main.cpp @@ -189,7 +189,8 @@ int main(int argc, char** argv) { } dump_config(config); - system("cd"); + system("tree build"); + return 0; std::vector tests; std::cout << "scanning for tests" << std::endl; From edc3d473d89a6670ecdab8714f504c1f57af2c25 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 19:56:02 +0300 Subject: [PATCH 26/64] update vctest (MSVC) --- vctest/main.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/vctest/main.cpp b/vctest/main.cpp index 35253bce8..8af979670 100644 --- a/vctest/main.cpp +++ b/vctest/main.cpp @@ -147,9 +147,11 @@ static bool run_test(const Config& config, const fs::path& path) { ss << " --test " << path; ss << " --res " << config.resDir; ss << " --dir " << config.workingDir; - ss << " >" << fix_path(outputFile.string()) << " 2>&1"; + //ss << " >" << fix_path(outputFile.string()) << " 2>&1"; auto command = ss.str(); + return true; + print_separator(std::cout); std::cout << "executing test " << name << "\ncommand: " << command << std::endl; @@ -189,8 +191,7 @@ int main(int argc, char** argv) { } dump_config(config); - system("tree build"); - return 0; + system("tree build/Release"); std::vector tests; std::cout << "scanning for tests" << std::endl; From eb53ccfa3e03ed69cf37e77c3d06b479f9de95d8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 20:01:03 +0300 Subject: [PATCH 27/64] update vctest (MSVC) --- vctest/main.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/vctest/main.cpp b/vctest/main.cpp index 8af979670..6e6cb1c3a 100644 --- a/vctest/main.cpp +++ b/vctest/main.cpp @@ -150,8 +150,6 @@ static bool run_test(const Config& config, const fs::path& path) { //ss << " >" << fix_path(outputFile.string()) << " 2>&1"; auto command = ss.str(); - return true; - print_separator(std::cout); std::cout << "executing test " << name << "\ncommand: " << command << std::endl; From 6e5df533edc9c06166c387fadb94adf4dfdc6e90 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 20:14:48 +0300 Subject: [PATCH 28/64] update vctest (MSVC) --- vctest/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vctest/main.cpp b/vctest/main.cpp index 6e6cb1c3a..e0de96856 100644 --- a/vctest/main.cpp +++ b/vctest/main.cpp @@ -143,7 +143,7 @@ static bool run_test(const Config& config, const fs::path& path) { auto name = path.stem(); std::stringstream ss; - ss << fs::canonical(config.executable) << " --headless"; + ss << fix_path(config.executable.string()) << " --headless"; ss << " --test " << path; ss << " --res " << config.resDir; ss << " --dir " << config.workingDir; From bbb99871403baa625f088850e6bb3a5c59ee195a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 21:42:39 +0300 Subject: [PATCH 29/64] windows (#405) --- .github/workflows/windows.yml | 24 ++++++++++++------------ vctest/main.cpp | 12 +++++------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 1f7978dd1..dd1974c7b 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ "main", "headless-mode" ] jobs: build-windows: @@ -40,14 +40,14 @@ jobs: run: | build/vctest/Release/vctest.exe -e build/Release/VoxelEngine.exe -d dev/tests -u build timeout-minutes: 1 -# - name: Package for Windows -# run: | -# mkdir packaged -# cp -r build/* packaged/ -# cp C:/Windows/System32/msvcp140.dll packaged/Release/msvcp140.dll -# mv packaged/Release/VoxelEngine.exe packaged/Release/VoxelCore.exe -# working-directory: ${{ github.workspace }} -# - uses: actions/upload-artifact@v4 -# with: -# name: Windows-Build -# path: 'packaged/Release/*' + - name: Package for Windows + run: | + mkdir packaged + cp -r build/* packaged/ + cp C:/Windows/System32/msvcp140.dll packaged/Release/msvcp140.dll + mv packaged/Release/VoxelEngine.exe packaged/Release/VoxelCore.exe + working-directory: ${{ github.workspace }} + - uses: actions/upload-artifact@v4 + with: + name: Windows-Build + path: 'build/Release/*' diff --git a/vctest/main.cpp b/vctest/main.cpp index e0de96856..7757041e1 100644 --- a/vctest/main.cpp +++ b/vctest/main.cpp @@ -143,11 +143,11 @@ static bool run_test(const Config& config, const fs::path& path) { auto name = path.stem(); std::stringstream ss; - ss << fix_path(config.executable.string()) << " --headless"; - ss << " --test " << path; - ss << " --res " << config.resDir; - ss << " --dir " << config.workingDir; - //ss << " >" << fix_path(outputFile.string()) << " 2>&1"; + ss << fs::canonical(config.executable) << " --headless"; + ss << " --test " << fix_path(path.string()); + ss << " --res " << fix_path(config.resDir.string()); + ss << " --dir " << fix_path(config.workingDir.string()); + ss << " >" << fix_path(outputFile.string()) << " 2>&1"; auto command = ss.str(); print_separator(std::cout); @@ -189,8 +189,6 @@ int main(int argc, char** argv) { } dump_config(config); - system("tree build/Release"); - std::vector tests; std::cout << "scanning for tests" << std::endl; for (const auto& entry : fs::directory_iterator(config.directory)) { From 59402b6607cbbd391dcd7e58a6e10cb6e2d33978 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 22:16:50 +0300 Subject: [PATCH 30/64] add 'test' library --- dev/tests/example.lua | 2 ++ res/scripts/stdlib.lua | 5 +++++ src/engine.cpp | 14 ++++++++++++++ src/engine.hpp | 4 ++++ src/logic/scripting/lua/libs/api_lua.hpp | 1 + src/logic/scripting/lua/libs/libtest.cpp | 5 +++++ src/logic/scripting/lua/libs/libtime.cpp | 17 ++++++++++------- src/logic/scripting/lua/lua_engine.cpp | 14 +++++++++++--- src/logic/scripting/lua/lua_engine.hpp | 4 +++- src/logic/scripting/scripting.cpp | 2 +- 10 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 src/logic/scripting/lua/libs/libtest.cpp diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 3b724d471..f030222db 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1 +1,3 @@ print("Hello from the example test!") +test.sleep(1) +print("2") diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index d5f8eed1c..9a5aa08ae 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -9,6 +9,11 @@ function sleep(timesec) end end +if test then + test.sleep = sleep + test.name = __VC_TEST_NAME +end + ------------------------------------------------ ------------------- Events --------------------- ------------------------------------------------ diff --git a/src/engine.cpp b/src/engine.cpp index 49386b65b..8f9708921 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -190,9 +190,15 @@ void Engine::runTest() { logger.info() << "nothing to do"; return; } + int tps = 20; + logger.info() << "starting test " << params.testFile; auto process = scripting::start_coroutine(params.testFile); while (process->isActive()) { + frame++; + delta = 1.0f / static_cast(tps); + lastTime += delta; + process->update(); } logger.info() << "test finished"; @@ -466,6 +472,10 @@ double Engine::getDelta() const { return delta; } +double Engine::getUptime() const { + return lastTime; +} + void Engine::setScreen(std::shared_ptr screen) { // reset audio channels (stop all sources) audio::reset_channel(audio::get_channel_index("regular")); @@ -534,3 +544,7 @@ SettingsHandler& Engine::getSettingsHandler() { network::Network& Engine::getNetwork() { return *network; } + +const CoreParameters& Engine::getCoreParameters() const { + return params; +} diff --git a/src/engine.hpp b/src/engine.hpp index c3559fa82..a0d4a8e9e 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -125,6 +125,8 @@ class Engine : public util::ObjectsKeeper { /// @brief Get current frame delta-time double getDelta() const; + double getUptime() const; + /// @brief Get active assets storage instance Assets* getAssets(); @@ -166,4 +168,6 @@ class Engine : public util::ObjectsKeeper { SettingsHandler& getSettingsHandler(); network::Network& getNetwork(); + + const CoreParameters& getCoreParameters() const; }; diff --git a/src/logic/scripting/lua/libs/api_lua.hpp b/src/logic/scripting/lua/libs/api_lua.hpp index a3b2d152c..23f3db66a 100644 --- a/src/logic/scripting/lua/libs/api_lua.hpp +++ b/src/logic/scripting/lua/libs/api_lua.hpp @@ -37,6 +37,7 @@ extern const luaL_Reg packlib[]; extern const luaL_Reg particleslib[]; // gfx.particles extern const luaL_Reg playerlib[]; extern const luaL_Reg quatlib[]; +extern const luaL_Reg testlib[]; extern const luaL_Reg text3dlib[]; // gfx.text3d extern const luaL_Reg timelib[]; extern const luaL_Reg tomllib[]; diff --git a/src/logic/scripting/lua/libs/libtest.cpp b/src/logic/scripting/lua/libs/libtest.cpp new file mode 100644 index 000000000..034a72fb4 --- /dev/null +++ b/src/logic/scripting/lua/libs/libtest.cpp @@ -0,0 +1,5 @@ +#include "api_lua.hpp" + +const luaL_Reg testlib[] = { + {NULL, NULL} +}; diff --git a/src/logic/scripting/lua/libs/libtime.cpp b/src/logic/scripting/lua/libs/libtime.cpp index 93708dda5..45ed4288c 100644 --- a/src/logic/scripting/lua/libs/libtime.cpp +++ b/src/logic/scripting/lua/libs/libtime.cpp @@ -2,15 +2,18 @@ #include "window/Window.hpp" #include "api_lua.hpp" -static int l_time_uptime(lua::State* L) { - return lua::pushnumber(L, Window::time()); +using namespace scripting; + +static int l_uptime(lua::State* L) { + return lua::pushnumber(L, engine->getUptime()); } -static int l_time_delta(lua::State* L) { - return lua::pushnumber(L, scripting::engine->getDelta()); +static int l_delta(lua::State* L) { + return lua::pushnumber(L, engine->getDelta()); } const luaL_Reg timelib[] = { - {"uptime", lua::wrap}, - {"delta", lua::wrap}, - {NULL, NULL}}; + {"uptime", lua::wrap}, + {"delta", lua::wrap}, + {NULL, NULL} +}; diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 8f4777108..0a4e22597 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -9,6 +9,7 @@ #include "util/stringutil.hpp" #include "libs/api_lua.hpp" #include "lua_custom_types.hpp" +#include "engine.hpp" static debug::Logger logger("lua-state"); static lua::State* main_thread = nullptr; @@ -57,7 +58,10 @@ static void create_libs(State* L, StateType stateType) { openlib(L, "vec3", vec3lib); openlib(L, "vec4", vec4lib); - if (stateType == StateType::BASE) { + if (stateType == StateType::TEST) { + openlib(L, "test", testlib); + } + if (stateType == StateType::BASE || stateType == StateType::TEST) { openlib(L, "gui", guilib); openlib(L, "input", inputlib); openlib(L, "inventory", inventorylib); @@ -110,11 +114,15 @@ void lua::init_state(State* L, StateType stateType) { newusertype(L); } -void lua::initialize(const EnginePaths& paths) { +void lua::initialize(const EnginePaths& paths, const CoreParameters& params) { logger.info() << LUA_VERSION; logger.info() << LUAJIT_VERSION; - main_thread = create_state(paths, StateType::BASE); + main_thread = create_state( + paths, params.headless ? StateType::TEST : StateType::BASE + ); + lua::pushstring(main_thread, params.testFile.stem().u8string()); + lua::setglobal(main_thread, "__VC_TEST_NAME"); } void lua::finalize() { diff --git a/src/logic/scripting/lua/lua_engine.hpp b/src/logic/scripting/lua/lua_engine.hpp index dd1e794d4..a70f59481 100644 --- a/src/logic/scripting/lua/lua_engine.hpp +++ b/src/logic/scripting/lua/lua_engine.hpp @@ -8,14 +8,16 @@ #include "lua_util.hpp" class EnginePaths; +struct CoreParameters; namespace lua { enum class StateType { BASE, + TEST, GENERATOR, }; - void initialize(const EnginePaths& paths); + void initialize(const EnginePaths& paths, const CoreParameters& params); void finalize(); bool emit_event( diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 9abe534c8..ec26bfcfc 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -66,7 +66,7 @@ int scripting::load_script( void scripting::initialize(Engine* engine) { scripting::engine = engine; - lua::initialize(*engine->getPaths()); + lua::initialize(*engine->getPaths(), engine->getCoreParameters()); load_script(fs::path("stdlib.lua"), true); load_script(fs::path("classes.lua"), true); From a036c5e383135dc0f9b086e244188d1ceb3f0bf2 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 8 Dec 2024 15:25:24 +0300 Subject: [PATCH 31/64] fix base:bazalt durability --- res/content/base/blocks/bazalt.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/content/base/blocks/bazalt.json b/res/content/base/blocks/bazalt.json index bb941a812..8c431da67 100644 --- a/res/content/base/blocks/bazalt.json +++ b/res/content/base/blocks/bazalt.json @@ -1,4 +1,5 @@ { "texture": "bazalt", - "breakable": false + "breakable": false, + "base:durability": 1e9 } From 5fe5c6b27a9ba264faa06cd2689a8f090f4a9587 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 8 Dec 2024 15:38:03 +0300 Subject: [PATCH 32/64] fix player tick rate --- src/logic/PlayerController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index 96fed03ab..6f7013463 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -197,7 +197,7 @@ PlayerController::PlayerController( player(level->players->get(0)), camControl(player, settings.camera), blocksController(blocksController), - playerTickClock(60, 1) { + playerTickClock(20, 3) { } void PlayerController::onFootstep(const Hitbox& hitbox) { From 5ffc054d75f5ef36c5f878a743f492a7710c9926 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 9 Dec 2024 01:12:41 +0300 Subject: [PATCH 33/64] refactor --- dev/tests/example.lua | 3 - src/Mainloop.cpp | 30 +++++ src/Mainloop.hpp | 11 ++ src/TestMainloop.cpp | 31 +++++ src/TestMainloop.hpp | 11 ++ src/Time.hpp | 34 ++++++ src/content/ContentPack.cpp | 4 +- src/engine.cpp | 141 +++++++++-------------- src/engine.hpp | 26 ++--- src/frontend/debug_panel.cpp | 2 +- src/graphics/core/DrawContext.cpp | 3 + src/graphics/core/DrawContext.hpp | 2 +- src/graphics/ui/GUI.cpp | 10 +- src/graphics/ui/GUI.hpp | 2 + src/logic/EngineController.cpp | 22 +++- src/logic/scripting/lua/libs/libtime.cpp | 4 +- 16 files changed, 211 insertions(+), 125 deletions(-) create mode 100644 src/Mainloop.cpp create mode 100644 src/Mainloop.hpp create mode 100644 src/TestMainloop.cpp create mode 100644 src/TestMainloop.hpp create mode 100644 src/Time.hpp diff --git a/dev/tests/example.lua b/dev/tests/example.lua index f030222db..e69de29bb 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,3 +0,0 @@ -print("Hello from the example test!") -test.sleep(1) -print("2") diff --git a/src/Mainloop.cpp b/src/Mainloop.cpp new file mode 100644 index 000000000..180e75f5d --- /dev/null +++ b/src/Mainloop.cpp @@ -0,0 +1,30 @@ +#include "Mainloop.hpp" + +#include "debug/Logger.hpp" +#include "engine.hpp" +#include "frontend/screens/MenuScreen.hpp" +#include "window/Window.hpp" + +static debug::Logger logger("mainloop"); + +Mainloop::Mainloop(Engine& engine) : engine(engine) { +} + +void Mainloop::run() { + auto& time = engine.getTime(); + + logger.info() << "starting menu screen"; + engine.setScreen(std::make_shared(&engine)); + + logger.info() << "main loop started"; + while (!Window::isShouldClose()){ + time.update(Window::time()); + engine.updateFrontend(); + if (!Window::isIconified()) { + engine.renderFrame(); + } + engine.postUpdate(); + engine.nextFrame(); + } + logger.info() << "main loop stopped"; +} diff --git a/src/Mainloop.hpp b/src/Mainloop.hpp new file mode 100644 index 000000000..3cd2f608f --- /dev/null +++ b/src/Mainloop.hpp @@ -0,0 +1,11 @@ +#pragma once + +class Engine; + +class Mainloop { + Engine& engine; +public: + Mainloop(Engine& engine); + + void run(); +}; diff --git a/src/TestMainloop.cpp b/src/TestMainloop.cpp new file mode 100644 index 000000000..617a65cd6 --- /dev/null +++ b/src/TestMainloop.cpp @@ -0,0 +1,31 @@ +#include "TestMainloop.hpp" + +#include "logic/scripting/scripting.hpp" +#include "interfaces/Process.hpp" +#include "debug/Logger.hpp" +#include "engine.hpp" + +static debug::Logger logger("mainloop"); + +inline constexpr int TPS = 20; + +TestMainloop::TestMainloop(Engine& engine) : engine(engine) { +} + +void TestMainloop::run() { + const auto& coreParams = engine.getCoreParameters(); + auto& time = engine.getTime(); + + if (coreParams.testFile.empty()) { + logger.info() << "nothing to do"; + return; + } + + logger.info() << "starting test " << coreParams.testFile; + auto process = scripting::start_coroutine(coreParams.testFile); + while (process->isActive()) { + time.step(1.0f / static_cast(TPS)); + process->update(); + } + logger.info() << "test finished"; +} diff --git a/src/TestMainloop.hpp b/src/TestMainloop.hpp new file mode 100644 index 000000000..b0acc9a7d --- /dev/null +++ b/src/TestMainloop.hpp @@ -0,0 +1,11 @@ +#pragma once + +class Engine; + +class TestMainloop { + Engine& engine; +public: + TestMainloop(Engine& engine); + + void run(); +}; diff --git a/src/Time.hpp b/src/Time.hpp new file mode 100644 index 000000000..57fb5c228 --- /dev/null +++ b/src/Time.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +class Time { + uint64_t frame = 0; + double lastTime = 0.0; + double delta = 0.0; +public: + Time() {} + + void update(double currentTime) { + frame++; + delta = currentTime - lastTime; + lastTime = currentTime; + } + + void step(double delta) { + frame++; + lastTime += delta; + } + + void set(double currentTime) { + lastTime = currentTime; + } + + double getDelta() const { + return delta; + } + + double getTime() const { + return lastTime; + } +}; diff --git a/src/content/ContentPack.cpp b/src/content/ContentPack.cpp index 9ed192b5a..72d4540f4 100644 --- a/src/content/ContentPack.cpp +++ b/src/content/ContentPack.cpp @@ -146,9 +146,7 @@ void ContentPack::scanFolder( std::vector ContentPack::worldPacksList(const fs::path& folder) { fs::path listfile = folder / fs::path("packs.list"); if (!fs::is_regular_file(listfile)) { - std::cerr << "warning: packs.list not found (will be created)"; - std::cerr << std::endl; - files::write_string(listfile, "# autogenerated, do not modify\nbase\n"); + throw std::runtime_error("missing file 'packs.list'"); } return files::read_list(listfile); } diff --git a/src/engine.cpp b/src/engine.cpp index 8f9708921..622a2fdb1 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -20,7 +20,6 @@ #include "frontend/screens/Screen.hpp" #include "frontend/screens/MenuScreen.hpp" #include "graphics/render/ModelsGenerator.hpp" -#include "graphics/core/Batch2D.hpp" #include "graphics/core/DrawContext.hpp" #include "graphics/core/ImageData.hpp" #include "graphics/core/Shader.hpp" @@ -36,7 +35,8 @@ #include "window/Events.hpp" #include "window/input.hpp" #include "window/Window.hpp" -#include "interfaces/Process.hpp" +#include "Mainloop.hpp" +#include "TestMainloop.hpp" #include #include @@ -92,6 +92,7 @@ Engine::Engine(CoreParameters coreParameters) if (Window::initialize(&settings.display)){ throw initialize_error("could not initialize window"); } + time.set(Window::time()); if (auto icon = load_icon(resdir)) { icon->flipY(); Window::setIcon(icon.get()); @@ -153,13 +154,6 @@ void Engine::onAssetsLoaded() { gui->onAssetsLoad(assets.get()); } -void Engine::updateTimers() { - frame++; - double currentTime = Window::time(); - delta = currentTime - lastTime; - lastTime = currentTime; -} - void Engine::updateHotkeys() { if (Events::jpressed(keycode::F2)) { saveScreenshot(); @@ -179,70 +173,40 @@ void Engine::saveScreenshot() { void Engine::run() { if (params.headless) { - runTest(); + TestMainloop(*this).run(); } else { - mainloop(); + Mainloop(*this).run(); } } -void Engine::runTest() { - if (params.testFile.empty()) { - logger.info() << "nothing to do"; - return; - } - int tps = 20; - - logger.info() << "starting test " << params.testFile; - auto process = scripting::start_coroutine(params.testFile); - while (process->isActive()) { - frame++; - delta = 1.0f / static_cast(tps); - lastTime += delta; - - process->update(); - } - logger.info() << "test finished"; +void Engine::postUpdate() { + network->update(); + processPostRunnables(); } -void Engine::mainloop() { - logger.info() << "starting menu screen"; - setScreen(std::make_shared(this)); - - Batch2D batch(1024); - lastTime = Window::time(); - - logger.info() << "engine started"; - while (!Window::isShouldClose()){ - assert(screen != nullptr); - updateTimers(); - updateHotkeys(); - audio::update(delta); - - gui->act(delta, Viewport(Window::width, Window::height)); - screen->update(delta); - - if (!Window::isIconified()) { - renderFrame(batch); - } - Window::setFramerate( - Window::isIconified() && settings.display.limitFpsIconified.get() - ? 20 - : settings.display.framerate.get() - ); - - network->update(); - processPostRunnables(); +void Engine::updateFrontend() { + double delta = time.getDelta(); + updateHotkeys(); + audio::update(delta); + gui->act(delta, Viewport(Window::width, Window::height)); + screen->update(delta); +} - Window::swapBuffers(); - Events::pollEvents(); - } +void Engine::nextFrame() { + Window::setFramerate( + Window::isIconified() && settings.display.limitFpsIconified.get() + ? 20 + : settings.display.framerate.get() + ); + Window::swapBuffers(); + Events::pollEvents(); } -void Engine::renderFrame(Batch2D& batch) { - screen->draw(delta); +void Engine::renderFrame() { + screen->draw(time.getDelta()); Viewport viewport(Window::width, Window::height); - DrawContext ctx(nullptr, viewport, &batch); + DrawContext ctx(nullptr, viewport, nullptr); gui->draw(ctx, *assets); } @@ -333,32 +297,31 @@ void Engine::loadAssets() { } } assets = std::move(new_assets); - - if (content) { - for (auto& [name, def] : content->blocks.getDefs()) { - if (def->model == BlockModel::custom) { - if (def->modelName.empty()) { - assets->store( - std::make_unique( - ModelsGenerator::loadCustomBlockModel( - def->customModelRaw, *assets, !def->shadeless - ) - ), - name + ".model" - ); - def->modelName = def->name + ".model"; - } - } - } - for (auto& [name, def] : content->items.getDefs()) { + + if (content == nullptr) { + return; + } + for (auto& [name, def] : content->blocks.getDefs()) { + if (def->model == BlockModel::custom && def->modelName.empty()) { assets->store( std::make_unique( - ModelsGenerator::generate(*def, *content, *assets) + ModelsGenerator::loadCustomBlockModel( + def->customModelRaw, *assets, !def->shadeless + ) ), name + ".model" ); + def->modelName = def->name + ".model"; } } + for (auto& [name, def] : content->items.getDefs()) { + assets->store( + std::make_unique( + ModelsGenerator::generate(*def, *content, *assets) + ), + name + ".model" + ); + } } static void load_configs(const fs::path& root) { @@ -468,14 +431,6 @@ void Engine::loadAllPacks() { contentPacks = manager.getAll(manager.assembly(allnames)); } -double Engine::getDelta() const { - return delta; -} - -double Engine::getUptime() const { - return lastTime; -} - void Engine::setScreen(std::shared_ptr screen) { // reset audio channels (stop all sources) audio::reset_channel(audio::get_channel_index("regular")); @@ -545,6 +500,14 @@ network::Network& Engine::getNetwork() { return *network; } +Time& Engine::getTime() { + return time; +} + const CoreParameters& Engine::getCoreParameters() const { return params; } + +bool Engine::isHeadless() const { + return params.headless; +} diff --git a/src/engine.hpp b/src/engine.hpp index a0d4a8e9e..fee92e812 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -11,6 +11,7 @@ #include "files/engine_paths.hpp" #include "files/settings_io.hpp" #include "util/ObjectsKeeper.hpp" +#include "Time.hpp" #include #include @@ -23,7 +24,6 @@ class Screen; class EnginePaths; class ResPaths; -class Batch2D; class EngineController; class SettingsHandler; struct EngineSettings; @@ -70,17 +70,12 @@ class Engine : public util::ObjectsKeeper { std::unique_ptr network; std::vector basePacks; std::unique_ptr gui; - - uint64_t frame = 0; - double lastTime = 0.0; - double delta = 0.0; + Time time; void loadControls(); void loadSettings(); void saveSettings(); - void updateTimers(); void updateHotkeys(); - void renderFrame(Batch2D& batch); void processPostRunnables(); void loadAssets(); public: @@ -90,11 +85,11 @@ class Engine : public util::ObjectsKeeper { /// @brief Start the engine void run(); - /// @brief Start main engine input/update/render loop. - /// Automatically sets MenuScreen - void mainloop(); + void postUpdate(); - void runTest(); + void updateFrontend(); + void renderFrame(); + void nextFrame(); /// @brief Called after assets loading when all engine systems are initialized void onAssetsLoaded(); @@ -122,11 +117,6 @@ class Engine : public util::ObjectsKeeper { /// @brief Collect all available content-packs from res/content void loadAllPacks(); - /// @brief Get current frame delta-time - double getDelta() const; - - double getUptime() const; - /// @brief Get active assets storage instance Assets* getAssets(); @@ -169,5 +159,9 @@ class Engine : public util::ObjectsKeeper { network::Network& getNetwork(); + Time& getTime(); + const CoreParameters& getCoreParameters() const; + + bool isHeadless() const; }; diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index 9043f5b9d..c9f08203b 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -56,7 +56,7 @@ std::shared_ptr create_debug_panel( static std::wstring fpsString = L""; panel->listenInterval(0.016f, [engine]() { - fps = 1.0f / engine->getDelta(); + fps = 1.0f / engine->getTime().getDelta(); fpsMin = std::min(fps, fpsMin); fpsMax = std::max(fps, fpsMax); }); diff --git a/src/graphics/core/DrawContext.cpp b/src/graphics/core/DrawContext.cpp index 7062ad56c..936de95b0 100644 --- a/src/graphics/core/DrawContext.cpp +++ b/src/graphics/core/DrawContext.cpp @@ -92,6 +92,9 @@ DrawContext DrawContext::sub(Flushable* flushable) const { ctx.parent = this; ctx.flushable = flushable; ctx.scissorsCount = 0; + if (auto batch2D = dynamic_cast(flushable)) { + ctx.g2d = batch2D; + } return ctx; } diff --git a/src/graphics/core/DrawContext.hpp b/src/graphics/core/DrawContext.hpp index 736b053ee..6c20f37f5 100644 --- a/src/graphics/core/DrawContext.hpp +++ b/src/graphics/core/DrawContext.hpp @@ -10,7 +10,7 @@ class Framebuffer; class DrawContext { const DrawContext* parent; Viewport viewport; - Batch2D* const g2d; + Batch2D* g2d; Flushable* flushable = nullptr; Framebuffer* fbo = nullptr; bool depthMask = true; diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index 38bc22573..49a362e3e 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -23,7 +23,7 @@ using namespace gui; -GUI::GUI() { +GUI::GUI() : batch2D(std::make_unique(1024)) { container = std::make_shared(glm::vec2(1000)); uicamera = std::make_unique(glm::vec3(), Window::height); uicamera->perspective = false; @@ -198,7 +198,9 @@ void GUI::act(float delta, const Viewport& vp) { } void GUI::draw(const DrawContext& pctx, const Assets& assets) { - auto& viewport = pctx.getViewport(); + auto ctx = pctx.sub(batch2D.get()); + + auto& viewport = ctx.getViewport(); glm::vec2 wsize = viewport.size(); menu->setPos((wsize - menu->getSize()) / 2.0f); @@ -208,8 +210,8 @@ void GUI::draw(const DrawContext& pctx, const Assets& assets) { uishader->use(); uishader->uniformMatrix("u_projview", uicamera->getProjView()); - pctx.getBatch2D()->begin(); - container->draw(pctx, assets); + batch2D->begin(); + container->draw(ctx, assets); } std::shared_ptr GUI::getFocused() const { diff --git a/src/graphics/ui/GUI.hpp b/src/graphics/ui/GUI.hpp index c0dec6490..851a49f7d 100644 --- a/src/graphics/ui/GUI.hpp +++ b/src/graphics/ui/GUI.hpp @@ -13,6 +13,7 @@ class Viewport; class DrawContext; class Assets; class Camera; +class Batch2D; /* Some info about padding and margin. @@ -52,6 +53,7 @@ namespace gui { /// @brief The main UI controller class GUI { + std::unique_ptr batch2D; std::shared_ptr container; std::shared_ptr hover; std::shared_ptr pressed; diff --git a/src/logic/EngineController.cpp b/src/logic/EngineController.cpp index 4c38fc786..edd6efb7c 100644 --- a/src/logic/EngineController.cpp +++ b/src/logic/EngineController.cpp @@ -132,13 +132,18 @@ static void show_content_missing( menus::show(engine, "reports/missing_content", {std::move(root)}); } -static bool loadWorldContent(Engine* engine, const fs::path& folder) { - return menus::call(engine, [engine, folder]() { +static bool load_world_content(Engine* engine, const fs::path& folder) { + if (engine->isHeadless()) { engine->loadWorldContent(folder); - }); + return true; + } else { + return menus::call(engine, [engine, folder]() { + engine->loadWorldContent(folder); + }); + } } -static void loadWorld(Engine* engine, const std::shared_ptr& worldFiles) { +static void load_world(Engine* engine, const std::shared_ptr& worldFiles) { try { auto content = engine->getContent(); auto& packs = engine->getContentPacks(); @@ -160,7 +165,12 @@ static void loadWorld(Engine* engine, const std::shared_ptr& worldFi void EngineController::openWorld(const std::string& name, bool confirmConvert) { auto paths = engine->getPaths(); auto folder = paths->getWorldsFolder() / fs::u8path(name); - if (!loadWorldContent(engine, folder)) { + auto worldFile = folder / fs::u8path("world.json"); + if (!fs::exists(worldFile)) { + throw std::runtime_error(worldFile.u8string() + " does not exists"); + } + + if (!load_world_content(engine, folder)) { return; } @@ -192,7 +202,7 @@ void EngineController::openWorld(const std::string& name, bool confirmConvert) { } return; } - loadWorld(engine, std::move(worldFiles)); + load_world(engine, std::move(worldFiles)); } inline uint64_t str2seed(const std::string& seedstr) { diff --git a/src/logic/scripting/lua/libs/libtime.cpp b/src/logic/scripting/lua/libs/libtime.cpp index 45ed4288c..daac093f5 100644 --- a/src/logic/scripting/lua/libs/libtime.cpp +++ b/src/logic/scripting/lua/libs/libtime.cpp @@ -5,11 +5,11 @@ using namespace scripting; static int l_uptime(lua::State* L) { - return lua::pushnumber(L, engine->getUptime()); + return lua::pushnumber(L, engine->getTime().getTime()); } static int l_delta(lua::State* L) { - return lua::pushnumber(L, engine->getDelta()); + return lua::pushnumber(L, engine->getTime().getDelta()); } const luaL_Reg timelib[] = { From 8f37704530ac1ca8a54b0f5eb19e58505c6a13be Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 10 Dec 2024 18:19:08 +0300 Subject: [PATCH 34/64] fix libworld docs --- doc/en/scripting/builtins/libworld.md | 14 ++++++-------- doc/ru/scripting/builtins/libworld.md | 2 +- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/doc/en/scripting/builtins/libworld.md b/doc/en/scripting/builtins/libworld.md index 5bf01abb0..5a1214f3a 100644 --- a/doc/en/scripting/builtins/libworld.md +++ b/doc/en/scripting/builtins/libworld.md @@ -36,14 +36,12 @@ world.get_seed() -> int -- Returns generator name. world.get_generator() -> str --- Proves that this is the current time during the day --- from 0.333(8 am) to 0.833(8 pm). -world.is_day() -> boolean +-- Checks the existence of a world by name. +world.exists(name: str) -> bool --- Checks that it is the current time at night --- from 0.833(8 pm) to 0.333(8 am). -world.is_night() -> bool +-- Checks if the current time is daytime. From 0.333(8am) to 0.833(8pm). +world.is_day() -> bool --- Checks the existence of a world by name. -world.exists() -> bool +-- Checks if the current time is nighttime. From 0.833(8pm) to 0.333(8am). +world.is_night() -> bool ``` diff --git a/doc/ru/scripting/builtins/libworld.md b/doc/ru/scripting/builtins/libworld.md index 9d7d635ea..49ac46fad 100644 --- a/doc/ru/scripting/builtins/libworld.md +++ b/doc/ru/scripting/builtins/libworld.md @@ -36,7 +36,7 @@ world.get_seed() -> int world.get_generator() -> str -- Проверяет существование мира по имени. -world.exists() -> bool +world.exists(name: str) -> bool -- Проверяет является ли текущее время днём. От 0.333(8 утра) до 0.833(8 вечера). world.is_day() -> bool From 9b4dd8f65e80612b14a2e9940c0a2b5dbd4719d4 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 10 Dec 2024 18:41:39 +0300 Subject: [PATCH 35/64] refactor & add test.new_world --- dev/tests/example.lua | 2 ++ res/scripts/stdlib.lua | 1 + src/Mainloop.cpp | 6 ++++++ src/TestMainloop.cpp | 11 +++++++++++ src/TestMainloop.hpp | 8 ++++++++ src/engine.cpp | 18 +++++++++++++++--- src/engine.hpp | 6 ++++++ src/logic/EngineController.cpp | 11 ++++++----- src/world/World.cpp | 2 ++ 9 files changed, 57 insertions(+), 8 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index e69de29bb..6c9546a82 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -0,0 +1,2 @@ +test.new_world("demo", "2019", "core:default") +print(world.get_generator()) diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 9a5aa08ae..d8a593b07 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -12,6 +12,7 @@ end if test then test.sleep = sleep test.name = __VC_TEST_NAME + test.new_world = core.new_world end ------------------------------------------------ diff --git a/src/Mainloop.cpp b/src/Mainloop.cpp index 180e75f5d..102aa7cd1 100644 --- a/src/Mainloop.cpp +++ b/src/Mainloop.cpp @@ -3,7 +3,9 @@ #include "debug/Logger.hpp" #include "engine.hpp" #include "frontend/screens/MenuScreen.hpp" +#include "frontend/screens/LevelScreen.hpp" #include "window/Window.hpp" +#include "world/Level.hpp" static debug::Logger logger("mainloop"); @@ -13,6 +15,10 @@ Mainloop::Mainloop(Engine& engine) : engine(engine) { void Mainloop::run() { auto& time = engine.getTime(); + engine.setLevelConsumer([this](auto level) { + engine.setScreen(std::make_shared(&engine, std::move(level))); + }); + logger.info() << "starting menu screen"; engine.setScreen(std::make_shared(&engine)); diff --git a/src/TestMainloop.cpp b/src/TestMainloop.cpp index 617a65cd6..955f4b265 100644 --- a/src/TestMainloop.cpp +++ b/src/TestMainloop.cpp @@ -1,8 +1,10 @@ #include "TestMainloop.hpp" #include "logic/scripting/scripting.hpp" +#include "logic/LevelController.hpp" #include "interfaces/Process.hpp" #include "debug/Logger.hpp" +#include "world/Level.hpp" #include "engine.hpp" static debug::Logger logger("mainloop"); @@ -12,6 +14,8 @@ inline constexpr int TPS = 20; TestMainloop::TestMainloop(Engine& engine) : engine(engine) { } +TestMainloop::~TestMainloop() = default; + void TestMainloop::run() { const auto& coreParams = engine.getCoreParameters(); auto& time = engine.getTime(); @@ -20,6 +24,9 @@ void TestMainloop::run() { logger.info() << "nothing to do"; return; } + engine.setLevelConsumer([this](auto level) { + setLevel(std::move(level)); + }); logger.info() << "starting test " << coreParams.testFile; auto process = scripting::start_coroutine(coreParams.testFile); @@ -29,3 +36,7 @@ void TestMainloop::run() { } logger.info() << "test finished"; } + +void TestMainloop::setLevel(std::unique_ptr level) { + this->controller = std::make_unique(&engine, std::move(level)); +} diff --git a/src/TestMainloop.hpp b/src/TestMainloop.hpp index b0acc9a7d..06ffae25a 100644 --- a/src/TestMainloop.hpp +++ b/src/TestMainloop.hpp @@ -1,11 +1,19 @@ #pragma once +#include + +class Level; +class LevelController; class Engine; class TestMainloop { Engine& engine; + std::unique_ptr controller; public: TestMainloop(Engine& engine); + ~TestMainloop(); void run(); + + void setLevel(std::unique_ptr level); }; diff --git a/src/engine.cpp b/src/engine.cpp index 622a2fdb1..072bd5f6e 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -19,6 +19,7 @@ #include "frontend/menu.hpp" #include "frontend/screens/Screen.hpp" #include "frontend/screens/MenuScreen.hpp" +#include "frontend/screens/LevelScreen.hpp" #include "graphics/render/ModelsGenerator.hpp" #include "graphics/core/DrawContext.hpp" #include "graphics/core/ImageData.hpp" @@ -35,6 +36,7 @@ #include "window/Events.hpp" #include "window/input.hpp" #include "window/Window.hpp" +#include "world/Level.hpp" #include "Mainloop.hpp" #include "TestMainloop.hpp" @@ -120,7 +122,7 @@ Engine::Engine(CoreParameters coreParameters) } keepAlive(settings.ui.language.observe([=](auto lang) { setLanguage(lang); - }, !langNotSet)); + }, true)); scripting::initialize(this); basePacks = files::read_list(resdir/fs::path("config/builtins.list")); @@ -272,6 +274,10 @@ PacksManager Engine::createPacksManager(const fs::path& worldFolder) { return manager; } +void Engine::setLevelConsumer(consumer> levelConsumer) { + this->levelConsumer = std::move(levelConsumer); +} + void Engine::loadAssets() { logger.info() << "loading assets"; Shader::preprocessor->setPaths(resPaths.get()); @@ -380,8 +386,10 @@ void Engine::loadContent() { ContentLoader::loadScripts(*content); langs::setup(resdir, langs::current->getId(), contentPacks); - loadAssets(); - onAssetsLoaded(); + if (!isHeadless()) { + loadAssets(); + onAssetsLoaded(); + } } void Engine::resetContent() { @@ -445,6 +453,10 @@ void Engine::setLanguage(std::string locale) { } } +void Engine::onWorldOpen(std::unique_ptr level) { + levelConsumer(std::move(level)); +} + gui::GUI* Engine::getGUI() { return gui.get(); } diff --git a/src/engine.hpp b/src/engine.hpp index fee92e812..55104cb11 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -21,6 +21,7 @@ #include #include +class Level; class Screen; class EnginePaths; class ResPaths; @@ -71,6 +72,7 @@ class Engine : public util::ObjectsKeeper { std::vector basePacks; std::unique_ptr gui; Time time; + consumer> levelConsumer; void loadControls(); void loadSettings(); @@ -132,6 +134,8 @@ class Engine : public util::ObjectsKeeper { /// @brief Get engine resource paths controller ResPaths* getResPaths(); + void onWorldOpen(std::unique_ptr level); + /// @brief Get current Content instance const Content* getContent() const; @@ -155,6 +159,8 @@ class Engine : public util::ObjectsKeeper { PacksManager createPacksManager(const fs::path& worldFolder); + void setLevelConsumer(consumer> levelConsumer); + SettingsHandler& getSettingsHandler(); network::Network& getNetwork(); diff --git a/src/logic/EngineController.cpp b/src/logic/EngineController.cpp index edd6efb7c..27e254f3f 100644 --- a/src/logic/EngineController.cpp +++ b/src/logic/EngineController.cpp @@ -150,9 +150,7 @@ static void load_world(Engine* engine, const std::shared_ptr& worldF auto& settings = engine->getSettings(); auto level = World::load(worldFiles, settings, content, packs); - engine->setScreen( - std::make_shared(engine, std::move(level)) - ); + engine->onWorldOpen(std::move(level)); } catch (const world_load_error& error) { guiutil::alert( engine->getGUI(), @@ -229,7 +227,10 @@ void EngineController::createWorld( EnginePaths* paths = engine->getPaths(); auto folder = paths->getWorldsFolder() / fs::u8path(name); - if (!menus::call(engine, [this, paths, folder]() { + if (engine->isHeadless()) { + engine->loadContent(); + paths->setCurrentWorldFolder(folder); + } else if (!menus::call(engine, [this, paths, folder]() { engine->loadContent(); paths->setCurrentWorldFolder(folder); })) { @@ -244,7 +245,7 @@ void EngineController::createWorld( engine->getContent(), engine->getContentPacks() ); - engine->setScreen(std::make_shared(engine, std::move(level))); + engine->onWorldOpen(std::move(level)); } void EngineController::reopenWorld(World* world) { diff --git a/src/world/World.cpp b/src/world/World.cpp index 4eb2631a9..cde3b3ce8 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -95,6 +95,8 @@ std::unique_ptr World::create( content, packs ); + logger.info() << "created world '" << name << "' (" << directory.u8string() << ")"; + logger.info() << "world seed: " << seed << " generator: " << generator; auto level = std::make_unique(std::move(world), content, settings); level->players->create(); return level; From d3f74b56fa1cf62c5291da2ce9074127dffa5480 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 10 Dec 2024 19:14:51 +0300 Subject: [PATCH 36/64] add test.close_world(bool) --- dev/tests/example.lua | 4 +++- res/scripts/stdlib.lua | 1 + src/Mainloop.cpp | 9 ++++++++- src/TestMainloop.cpp | 8 +++++++- src/engine.cpp | 8 ++++++-- src/engine.hpp | 1 + src/logic/LevelController.cpp | 5 +++-- src/logic/scripting/lua/libs/libcore.cpp | 5 +---- 8 files changed, 30 insertions(+), 11 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 6c9546a82..8baf6afbd 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,2 +1,4 @@ test.new_world("demo", "2019", "core:default") -print(world.get_generator()) +assert(world.get_generator() == "core:default") +test.close_world(true) +assert(not world.is_open()) diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index d8a593b07..451f07c8e 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -13,6 +13,7 @@ if test then test.sleep = sleep test.name = __VC_TEST_NAME test.new_world = core.new_world + test.close_world = core.close_world end ------------------------------------------------ diff --git a/src/Mainloop.cpp b/src/Mainloop.cpp index 102aa7cd1..eae4c501c 100644 --- a/src/Mainloop.cpp +++ b/src/Mainloop.cpp @@ -16,7 +16,14 @@ void Mainloop::run() { auto& time = engine.getTime(); engine.setLevelConsumer([this](auto level) { - engine.setScreen(std::make_shared(&engine, std::move(level))); + if (level == nullptr) { + // destroy LevelScreen and run quit callbacks + engine.setScreen(nullptr); + // create and go to menu screen + engine.setScreen(std::make_shared(&engine)); + } else { + engine.setScreen(std::make_shared(&engine, std::move(level))); + } }); logger.info() << "starting menu screen"; diff --git a/src/TestMainloop.cpp b/src/TestMainloop.cpp index 955f4b265..32f316272 100644 --- a/src/TestMainloop.cpp +++ b/src/TestMainloop.cpp @@ -38,5 +38,11 @@ void TestMainloop::run() { } void TestMainloop::setLevel(std::unique_ptr level) { - this->controller = std::make_unique(&engine, std::move(level)); + if (level == nullptr) { + controller->onWorldQuit(); + engine.getPaths()->setCurrentWorldFolder(fs::path()); + controller = nullptr; + } else { + controller = std::make_unique(&engine, std::move(level)); + } } diff --git a/src/engine.cpp b/src/engine.cpp index 072bd5f6e..3c7d4bb5e 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -18,8 +18,6 @@ #include "frontend/locale.hpp" #include "frontend/menu.hpp" #include "frontend/screens/Screen.hpp" -#include "frontend/screens/MenuScreen.hpp" -#include "frontend/screens/LevelScreen.hpp" #include "graphics/render/ModelsGenerator.hpp" #include "graphics/core/DrawContext.hpp" #include "graphics/core/ImageData.hpp" @@ -454,9 +452,15 @@ void Engine::setLanguage(std::string locale) { } void Engine::onWorldOpen(std::unique_ptr level) { + logger.info() << "world open"; levelConsumer(std::move(level)); } +void Engine::onWorldClosed() { + logger.info() << "world closed"; + levelConsumer(nullptr); +} + gui::GUI* Engine::getGUI() { return gui.get(); } diff --git a/src/engine.hpp b/src/engine.hpp index 55104cb11..63a780845 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -135,6 +135,7 @@ class Engine : public util::ObjectsKeeper { ResPaths* getResPaths(); void onWorldOpen(std::unique_ptr level); + void onWorldClosed(); /// @brief Get current Content instance const Content* getContent() const; diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 5d2ddd9b5..5b24bbbb9 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -54,8 +54,9 @@ void LevelController::update(float delta, bool input, bool pause) { } void LevelController::saveWorld() { - level->getWorld()->wfile->createDirectories(); - logger.info() << "writing world"; + auto world = level->getWorld(); + logger.info() << "writing world '" << world->getName() << "'"; + world->wfile->createDirectories(); scripting::on_world_save(); level->onSave(); level->getWorld()->write(level.get()); diff --git a/src/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp index 9b048e443..811b8d53a 100644 --- a/src/logic/scripting/lua/libs/libcore.cpp +++ b/src/logic/scripting/lua/libs/libcore.cpp @@ -69,10 +69,7 @@ static int l_close_world(lua::State* L) { if (save_world) { controller->saveWorld(); } - // destroy LevelScreen and run quit callbacks - engine->setScreen(nullptr); - // create and go to menu screen - engine->setScreen(std::make_shared(engine)); + engine->onWorldClosed(); return 0; } From 9e18dccbbbb8aeb0c4f113be0208f48a95dbb669 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 10 Dec 2024 19:20:28 +0300 Subject: [PATCH 37/64] enable verbose engine tests output --- .github/workflows/appimage.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/windows.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 257e81e60..c491e3997 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -45,7 +45,7 @@ jobs: run: | chmod +x build/VoxelEngine chmod +x AppDir/usr/bin/vctest - AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build + AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build --output-always # - name: Build AppImage # uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910 # env: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index aa5b1d0a4..c0800c64c 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -44,7 +44,7 @@ jobs: run: | chmod +x build/VoxelEngine chmod +x AppDir/usr/bin/vctest - AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build + AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build --output-always # - name: Create DMG # run: | # mkdir VoxelEngineDmgContent diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index dd1974c7b..9c1f7f963 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -38,7 +38,7 @@ jobs: run: ctest --output-on-failure --test-dir build - name: Run engine tests run: | - build/vctest/Release/vctest.exe -e build/Release/VoxelEngine.exe -d dev/tests -u build + build/vctest/Release/vctest.exe -e build/Release/VoxelEngine.exe -d dev/tests -u build --output-always timeout-minutes: 1 - name: Package for Windows run: | From 3c2eb30a29b1847ea6402fa9023772e5576c6e4d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 10 Dec 2024 20:14:18 +0300 Subject: [PATCH 38/64] add test.reconfig_packs & fix world.get_total_time() & enable level controller --- dev/tests/example.lua | 4 ++++ res/scripts/stdlib.lua | 1 + src/TestMainloop.cpp | 6 ++++++ src/Time.hpp | 1 + src/logic/scripting/lua/libs/libcore.cpp | 4 ++-- 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 8baf6afbd..beb9fc0e3 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,4 +1,8 @@ +test.reconfig_packs({"base"}, {}) test.new_world("demo", "2019", "core:default") assert(world.get_generator() == "core:default") +coroutine.yield() +assert(world.get_total_time() > 0.0) +print(world.get_total_time()) test.close_world(true) assert(not world.is_open()) diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 451f07c8e..3687d6fc2 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -14,6 +14,7 @@ if test then test.name = __VC_TEST_NAME test.new_world = core.new_world test.close_world = core.close_world + test.reconfig_packs = core.reconfig_packs end ------------------------------------------------ diff --git a/src/TestMainloop.cpp b/src/TestMainloop.cpp index 32f316272..ddf0ca171 100644 --- a/src/TestMainloop.cpp +++ b/src/TestMainloop.cpp @@ -5,6 +5,7 @@ #include "interfaces/Process.hpp" #include "debug/Logger.hpp" #include "world/Level.hpp" +#include "world/World.hpp" #include "engine.hpp" static debug::Logger logger("mainloop"); @@ -33,6 +34,11 @@ void TestMainloop::run() { while (process->isActive()) { time.step(1.0f / static_cast(TPS)); process->update(); + if (controller) { + float delta = time.getDelta(); + controller->getLevel()->getWorld()->updateTimers(delta); + controller->update(glm::min(delta, 0.2f), false, false); + } } logger.info() << "test finished"; } diff --git a/src/Time.hpp b/src/Time.hpp index 57fb5c228..9f914b1fc 100644 --- a/src/Time.hpp +++ b/src/Time.hpp @@ -18,6 +18,7 @@ class Time { void step(double delta) { frame++; lastTime += delta; + this->delta = delta; } void set(double currentTime) { diff --git a/src/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp index 811b8d53a..90d8d958b 100644 --- a/src/logic/scripting/lua/libs/libcore.cpp +++ b/src/logic/scripting/lua/libs/libcore.cpp @@ -115,8 +115,8 @@ static int l_reconfig_packs(lua::State* L) { remPacks.emplace_back(lua::tostring(L, -1)); lua::pop(L); } - auto engine_controller = engine->getController(); - engine_controller->reconfigPacks(controller, addPacks, remPacks); + auto engineController = engine->getController(); + engineController->reconfigPacks(controller, addPacks, remPacks); return 0; } From c67f158e6221bac4cb776113a9b43155c62e1e5c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Dec 2024 00:31:08 +0300 Subject: [PATCH 39/64] remove default player from headless mode --- dev/tests/example.lua | 1 - src/logic/EngineController.cpp | 4 ++++ src/logic/LevelController.cpp | 39 +++++++++++++++++++++------------- src/logic/PlayerController.cpp | 35 +++++++++++++++--------------- src/logic/PlayerController.hpp | 16 ++++++++++++-- src/objects/Players.cpp | 6 +++--- src/objects/Players.hpp | 2 +- src/world/World.cpp | 4 +--- 8 files changed, 65 insertions(+), 42 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index beb9fc0e3..4c4c2e5d6 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,4 +1,3 @@ -test.reconfig_packs({"base"}, {}) test.new_world("demo", "2019", "core:default") assert(world.get_generator() == "core:default") coroutine.yield() diff --git a/src/logic/EngineController.cpp b/src/logic/EngineController.cpp index 27e254f3f..8c649e8ac 100644 --- a/src/logic/EngineController.cpp +++ b/src/logic/EngineController.cpp @@ -16,6 +16,7 @@ #include "frontend/screens/MenuScreen.hpp" #include "graphics/ui/elements/Menu.hpp" #include "graphics/ui/gui_util.hpp" +#include "objects/Players.hpp" #include "interfaces/Task.hpp" #include "util/stringutil.hpp" #include "world/Level.hpp" @@ -245,6 +246,9 @@ void EngineController::createWorld( engine->getContent(), engine->getContentPacks() ); + if (!engine->isHeadless()) { + level->players->create(); + } engine->onWorldOpen(std::move(level)); } diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 5b24bbbb9..87c2b6ad8 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -6,6 +6,7 @@ #include "engine.hpp" #include "files/WorldFiles.hpp" #include "objects/Entities.hpp" +#include "objects/Players.hpp" #include "physics/Hitbox.hpp" #include "settings.hpp" #include "world/Level.hpp" @@ -23,34 +24,42 @@ LevelController::LevelController(Engine* engine, std::unique_ptr levelPtr )), chunks(std::make_unique( *level, settings.chunks.padding.get() - )), - player(std::make_unique( - settings, level.get(), blocks.get() )) { + if (!engine->isHeadless()) { + player = std::make_unique( + settings, level.get(), level->players->get(0), blocks.get() + ); + } scripting::on_world_load(this); } void LevelController::update(float delta, bool input, bool pause) { - glm::vec3 position = player->getPlayer()->getPosition(); - level->loadMatrix( - position.x, - position.z, - settings.chunks.loadDistance.get() + settings.chunks.padding.get() * 2 - ); - chunks->update( - settings.chunks.loadSpeed.get(), settings.chunks.loadDistance.get(), - floordiv(position.x, CHUNK_W), floordiv(position.z, CHUNK_D) - ); + if (player) { + glm::vec3 position = player->getPlayer()->getPosition(); + level->loadMatrix( + position.x, + position.z, + settings.chunks.loadDistance.get() + settings.chunks.padding.get() * 2 + ); + chunks->update( + settings.chunks.loadSpeed.get(), settings.chunks.loadDistance.get(), + floordiv(position.x, CHUNK_W), floordiv(position.z, CHUNK_D) + ); + } if (!pause) { // update all objects that needed blocks->update(delta); - player->update(delta, input, pause); + if (player) { + player->update(delta, input); + } level->entities->updatePhysics(delta); level->entities->update(delta); } level->entities->clean(); - player->postUpdate(delta, input, pause); + if (player) { + player->postUpdate(delta, input, pause); + } } void LevelController::saveWorld() { diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index 6f7013463..9a2c02d82 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -190,11 +190,14 @@ void CameraControl::update(PlayerInput input, float delta, Chunks* chunks) { } PlayerController::PlayerController( - const EngineSettings& settings, Level* level, + const EngineSettings& settings, + Level* level, + Player* player, BlocksController* blocksController ) - : settings(settings), level(level), - player(level->players->get(0)), + : settings(settings), + level(level), + player(player), camControl(player, settings.camera), blocksController(blocksController), playerTickClock(20, 3) { @@ -242,21 +245,19 @@ void PlayerController::updateFootsteps(float delta) { } } -void PlayerController::update(float delta, bool input, bool pause) { - if (!pause) { - if (input) { - updateKeyboard(); - player->updateSelectedEntity(); - } else { - resetKeyboard(); - } - updatePlayer(delta); +void PlayerController::update(float delta, bool input) { + if (input) { + updateKeyboard(); + player->updateSelectedEntity(); + } else { + resetKeyboard(); + } + updatePlayer(delta); - if (playerTickClock.update(delta)) { - if (player->getId() % playerTickClock.getParts() == - playerTickClock.getPart()) { - scripting::on_player_tick(player, playerTickClock.getTickRate()); - } + if (playerTickClock.update(delta)) { + if (player->getId() % playerTickClock.getParts() == + playerTickClock.getPart()) { + scripting::on_player_tick(player, playerTickClock.getTickRate()); } } } diff --git a/src/logic/PlayerController.hpp b/src/logic/PlayerController.hpp index 8d82a3f99..9ae8d7271 100644 --- a/src/logic/PlayerController.hpp +++ b/src/logic/PlayerController.hpp @@ -71,9 +71,21 @@ class PlayerController { voxel* updateSelection(float maxDistance); public: PlayerController( - const EngineSettings& settings, Level* level, BlocksController* blocksController + const EngineSettings& settings, + Level* level, + Player* player, + BlocksController* blocksController ); - void update(float delta, bool input, bool pause); + + /// @brief Called after blocks update if not paused + /// @param delta delta time + /// @param input process user input + void update(float delta, bool input); + + /// @brief Called after whole level update + /// @param delta delta time + /// @param input process user input + /// @param pause is game paused void postUpdate(float delta, bool input, bool pause); Player* getPlayer(); }; diff --git a/src/objects/Players.cpp b/src/objects/Players.cpp index 900319917..b174d08aa 100644 --- a/src/objects/Players.cpp +++ b/src/objects/Players.cpp @@ -7,7 +7,7 @@ Players::Players(Level* level) : level(level) {} -void Players::addPlayer(std::unique_ptr player) { +void Players::add(std::unique_ptr player) { players[player->getId()] = std::move(player); } @@ -30,7 +30,7 @@ Player* Players::create() { 0 ); auto player = playerPtr.get(); - addPlayer(std::move(playerPtr)); + add(std::move(playerPtr)); level->inventories->store(player->getInventory()); return player; @@ -62,7 +62,7 @@ void Players::deserialize(const dv::value& src) { ); auto player = playerPtr.get(); player->deserialize(playerMap); - addPlayer(std::move(playerPtr)); + add(std::move(playerPtr)); auto& inventory = player->getInventory(); // invalid inventory id pre 0.25 if (inventory->getId() == 0) { diff --git a/src/objects/Players.hpp b/src/objects/Players.hpp index f66d181f9..a7bda5246 100644 --- a/src/objects/Players.hpp +++ b/src/objects/Players.hpp @@ -17,7 +17,7 @@ class Players : public Serializable { Level* level; std::unordered_map> players; - void addPlayer(std::unique_ptr player); + void add(std::unique_ptr player); public: Players(Level* level); diff --git a/src/world/World.cpp b/src/world/World.cpp index cde3b3ce8..bec61d3ac 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -97,9 +97,7 @@ std::unique_ptr World::create( ); logger.info() << "created world '" << name << "' (" << directory.u8string() << ")"; logger.info() << "world seed: " << seed << " generator: " << generator; - auto level = std::make_unique(std::move(world), content, settings); - level->players->create(); - return level; + return std::make_unique(std::move(world), content, settings); } std::unique_ptr World::load( From b05c2fc91138fc6a4f522ad9804da66542c99090 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Dec 2024 14:08:24 +0300 Subject: [PATCH 40/64] fix 'timeit' in headless mode --- dev/tests/example.lua | 3 ++- res/scripts/stdlib.lua | 1 + res/scripts/stdmin.lua | 4 ++-- src/logic/scripting/lua/libs/libtime.cpp | 1 - 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 4c4c2e5d6..6262e3699 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,6 +1,7 @@ test.new_world("demo", "2019", "core:default") +assert(world.is_open()) assert(world.get_generator() == "core:default") -coroutine.yield() +test.sleep(1) assert(world.get_total_time() > 0.0) print(world.get_total_time()) test.close_world(true) diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 3687d6fc2..a732fde61 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -15,6 +15,7 @@ if test then test.new_world = core.new_world test.close_world = core.close_world test.reconfig_packs = core.reconfig_packs + test.tick = coroutine.yield end ------------------------------------------------ diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 14479c7df..49ddec005 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -34,11 +34,11 @@ end function timeit(iters, func, ...) - local tm = time.uptime() + local tm = os.clock() for i=1,iters do func(...) end - print("[time mcs]", (time.uptime()-tm) * 1000000) + print("[time mcs]", (os.clock()-tm) * 1000000) end ---------------------------------------------- diff --git a/src/logic/scripting/lua/libs/libtime.cpp b/src/logic/scripting/lua/libs/libtime.cpp index daac093f5..78952ede8 100644 --- a/src/logic/scripting/lua/libs/libtime.cpp +++ b/src/logic/scripting/lua/libs/libtime.cpp @@ -1,5 +1,4 @@ #include "engine.hpp" -#include "window/Window.hpp" #include "api_lua.hpp" using namespace scripting; From cfc16679cb91d4a580d98b549782ee2af7682361 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Dec 2024 14:49:52 +0300 Subject: [PATCH 41/64] add test.open_world --- dev/tests/example.lua | 12 ++++++++++++ res/scripts/stdlib.lua | 1 + src/world/World.cpp | 6 ++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 6262e3699..a1a0c369f 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,8 +1,20 @@ +-- Create/close/open/close world + +-- Open test.new_world("demo", "2019", "core:default") assert(world.is_open()) assert(world.get_generator() == "core:default") test.sleep(1) assert(world.get_total_time() > 0.0) print(world.get_total_time()) + +-- Close test.close_world(true) assert(not world.is_open()) + +-- Reopen +test.open_world("demo") +assert(world.is_open()) +assert(world.get_total_time() > 0.0) +test.tick() +test.close_world(true) diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index a732fde61..3d21def6e 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -13,6 +13,7 @@ if test then test.sleep = sleep test.name = __VC_TEST_NAME test.new_world = core.new_world + test.open_world = core.open_world test.close_world = core.close_world test.reconfig_packs = core.reconfig_packs test.tick = coroutine.yield diff --git a/src/world/World.cpp b/src/world/World.cpp index bec61d3ac..812f6d919 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -133,8 +133,10 @@ std::unique_ptr World::load( auto playerRoot = files::read_json(file); level->players->deserialize(playerRoot); - if (!playerRoot["players"][0].has("id")) { - level->getWorld()->getInfo().nextPlayerId++; + if (!playerRoot["players"].empty()) { + if (!playerRoot["players"][0].has("id")) { + level->getWorld()->getInfo().nextPlayerId++; + } } } return level; From 91fa2838d6793df84e9024b3a34f7c31bd75b191 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Dec 2024 15:23:16 +0300 Subject: [PATCH 42/64] reduce 'corrupted block detected ...' spam if chunk data is corrupted --- src/voxels/ChunksStorage.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index 272332fb3..410448c08 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/ChunksStorage.cpp @@ -40,13 +40,23 @@ void ChunksStorage::remove(int x, int z) { } static void check_voxels(const ContentIndices& indices, Chunk* chunk) { + bool corrupted = false; for (size_t i = 0; i < CHUNK_VOL; i++) { blockid_t id = chunk->voxels[i].id; if (indices.blocks.get(id) == nullptr) { - auto logline = logger.error(); - logline << "corruped block detected at " << i << " of chunk "; - logline << chunk->x << "x" << chunk->z; - logline << " -> " << id; + if (!corrupted) { +#ifdef NDEBUG + // release + auto logline = logger.error(); + logline << "corruped blocks detected at " << i << " of chunk "; + logline << chunk->x << "x" << chunk->z; + logline << " -> " << id; + corrupted = true; +#else + // debug + abort(); +#endif + } chunk->voxels[i].id = BLOCK_AIR; } } From 436bfd24bacba7a99014896a29c1a72d1d59f3bf Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Dec 2024 16:30:19 +0300 Subject: [PATCH 43/64] update ChunksStorage --- dev/tests/example.lua | 21 +-------------------- dev/tests/world.lua | 23 +++++++++++++++++++++++ src/frontend/hud.cpp | 2 +- src/voxels/ChunksStorage.cpp | 29 +++++++++++++++++------------ src/voxels/ChunksStorage.hpp | 11 ++++------- src/world/Level.cpp | 4 ---- 6 files changed, 46 insertions(+), 44 deletions(-) create mode 100644 dev/tests/world.lua diff --git a/dev/tests/example.lua b/dev/tests/example.lua index a1a0c369f..8cde7829c 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,20 +1 @@ --- Create/close/open/close world - --- Open -test.new_world("demo", "2019", "core:default") -assert(world.is_open()) -assert(world.get_generator() == "core:default") -test.sleep(1) -assert(world.get_total_time() > 0.0) -print(world.get_total_time()) - --- Close -test.close_world(true) -assert(not world.is_open()) - --- Reopen -test.open_world("demo") -assert(world.is_open()) -assert(world.get_total_time() > 0.0) -test.tick() -test.close_world(true) +print("hello world") diff --git a/dev/tests/world.lua b/dev/tests/world.lua new file mode 100644 index 000000000..3e7353e39 --- /dev/null +++ b/dev/tests/world.lua @@ -0,0 +1,23 @@ +-- Create/close/open/close world + +-- Open +test.new_world("demo", "2019", "core:default") +assert(world.is_open()) +assert(world.get_generator() == "core:default") +test.sleep(1) +assert(world.get_total_time() > 0.0) +print(world.get_total_time()) + +-- Close +test.close_world(true) +assert(not world.is_open()) + +-- Reopen +test.open_world("demo") +assert(world.is_open()) +assert(world.get_total_time() > 0.0) +assert(world.get_seed() == 2019) +test.tick() + +-- Close +test.close_world(true) diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 21111e187..20199f0ae 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -295,7 +295,7 @@ void Hud::updateWorldGenDebugVisualization() { data[(flippedZ * width + x) * 4 + 1] = level.chunks->getChunk(ax + ox, az + oz) ? 255 : 0; data[(flippedZ * width + x) * 4 + 0] = - level.chunksStorage->get(ax + ox, az + oz) ? 255 : 0; + level.chunksStorage->fetch(ax + ox, az + oz) ? 255 : 0; if (ax < 0 || az < 0 || ax >= areaWidth || az >= areaHeight) { diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index 410448c08..26b4fbd3d 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/ChunksStorage.cpp @@ -20,23 +20,18 @@ static debug::Logger logger("chunks-storage"); ChunksStorage::ChunksStorage(Level* level) : level(level) { } -void ChunksStorage::store(const std::shared_ptr& chunk) { - chunksMap[glm::ivec2(chunk->x, chunk->z)] = chunk; -} +std::shared_ptr ChunksStorage::fetch(int x, int z) { + std::lock_guard lock(mutex); -std::shared_ptr ChunksStorage::get(int x, int z) const { auto found = chunksMap.find(glm::ivec2(x, z)); if (found == chunksMap.end()) { return nullptr; } - return found->second; -} - -void ChunksStorage::remove(int x, int z) { - auto found = chunksMap.find(glm::ivec2(x, z)); - if (found != chunksMap.end()) { - chunksMap.erase(found->first); + auto ptr = found->second.lock(); + if (ptr == nullptr) { + chunksMap.erase(found); } + return ptr; } static void check_voxels(const ContentIndices& indices, Chunk* chunk) { @@ -63,11 +58,21 @@ static void check_voxels(const ContentIndices& indices, Chunk* chunk) { } std::shared_ptr ChunksStorage::create(int x, int z) { + std::lock_guard lock(mutex); + + auto found = chunksMap.find(glm::ivec2(x, z)); + if (found != chunksMap.end()) { + auto chunk = found->second.lock(); + if (chunk) { + return chunk; + } + } + World* world = level->getWorld(); auto& regions = world->wfile.get()->getRegions(); auto chunk = std::make_shared(x, z); - store(chunk); + chunksMap[glm::ivec2(chunk->x, chunk->z)] = chunk; if (auto data = regions.getVoxels(chunk->x, chunk->z)) { const auto& indices = *level->content->getIndices(); diff --git a/src/voxels/ChunksStorage.hpp b/src/voxels/ChunksStorage.hpp index 35fff6c1d..78bc03d04 100644 --- a/src/voxels/ChunksStorage.hpp +++ b/src/voxels/ChunksStorage.hpp @@ -1,11 +1,9 @@ #pragma once +#include #include #include -#include "typedefs.hpp" -#include "voxel.hpp" - #define GLM_ENABLE_EXPERIMENTAL #include @@ -14,13 +12,12 @@ class Level; class ChunksStorage { Level* level; - std::unordered_map> chunksMap; + std::mutex mutex; + std::unordered_map> chunksMap; public: ChunksStorage(Level* level); ~ChunksStorage() = default; - std::shared_ptr get(int x, int z) const; - void store(const std::shared_ptr& chunk); - void remove(int x, int y); + std::shared_ptr fetch(int x, int z); std::shared_ptr create(int x, int z); }; diff --git a/src/world/Level.cpp b/src/world/Level.cpp index ed370d084..402c8e5fc 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -62,10 +62,6 @@ Level::Level( ); lighting = std::make_unique(content, chunks.get()); - events->listen(EVT_CHUNK_HIDDEN, [this](lvl_event_type, Chunk* chunk) { - this->chunksStorage->remove(chunk->x, chunk->z); - }); - inventories = std::make_unique(*this); } From 0d071ab0141edbf087f3ec03505792740023c01e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Dec 2024 20:42:59 +0300 Subject: [PATCH 44/64] fix camera-related bugs --- src/logic/PlayerController.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index 9a2c02d82..827b42c1e 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -170,18 +170,21 @@ void CameraControl::update(PlayerInput input, float delta, Chunks* chunks) { refresh(); + camera->updateVectors(); if (player->currentCamera == spCamera) { spCamera->position = chunks->rayCastToObstacle(camera->position, camera->front, 3.0f) - 0.4f * camera->front; spCamera->dir = -camera->dir; spCamera->front = -camera->front; + spCamera->right = -camera->right; } else if (player->currentCamera == tpCamera) { tpCamera->position = chunks->rayCastToObstacle(camera->position, -camera->front, 3.0f) + 0.4f * camera->front; tpCamera->dir = camera->dir; tpCamera->front = camera->front; + tpCamera->right = camera->right; } if (player->currentCamera == spCamera || player->currentCamera == tpCamera || player->currentCamera == camera) { From c1a7b7545a9f8ad3f22d36eaeb3e59221457766c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Dec 2024 21:07:38 +0300 Subject: [PATCH 45/64] change components on_render call timing --- src/frontend/screens/LevelScreen.cpp | 4 ++++ src/objects/Entities.cpp | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index 34d1db678..4436cf488 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -18,6 +18,7 @@ #include "graphics/ui/elements/Menu.hpp" #include "graphics/ui/GUI.hpp" #include "logic/LevelController.hpp" +#include "logic/scripting/scripting.hpp" #include "logic/scripting/scripting_hud.hpp" #include "util/stringutil.hpp" #include "physics/Hitbox.hpp" @@ -182,6 +183,9 @@ void LevelScreen::draw(float delta) { Viewport viewport(Window::width, Window::height); DrawContext ctx(nullptr, viewport, batch.get()); + if (!hud->isPause()) { + scripting::on_entities_render(engine->getTime().getDelta()); + } worldRenderer->draw( ctx, *camera, hudVisible, hud->isPause(), delta, postProcessing.get() ); diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 614adf417..79c0ba668 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -556,10 +556,6 @@ void Entities::render( float delta, bool pause ) { - if (!pause) { - scripting::on_entities_render(delta); - } - auto view = registry.view(); for (auto [entity, transform, skeleton] : view.each()) { if (transform.dirty) { From 9adaf6d527604a5a2df1e249123588e9b8f20a78 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Dec 2024 22:04:38 +0300 Subject: [PATCH 46/64] update workflows --- .github/workflows/appimage.yml | 20 ++++++++++---------- .github/workflows/macos.yml | 26 +++++++++++++------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index c491e3997..25432d30d 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -46,13 +46,13 @@ jobs: chmod +x build/VoxelEngine chmod +x AppDir/usr/bin/vctest AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build --output-always -# - name: Build AppImage -# uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910 -# env: -# UPDATE_INFO: gh-releases-zsync|MihailRis|VoxelEngine-Cpp|latest|*x86_64.AppImage.zsync -# with: -# recipe: dev/AppImageBuilder.yml -# - uses: actions/upload-artifact@v4 -# with: -# name: AppImage -# path: './*.AppImage*' + - name: Build AppImage + uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910 + env: + UPDATE_INFO: gh-releases-zsync|MihailRis|VoxelEngine-Cpp|latest|*x86_64.AppImage.zsync + with: + recipe: dev/AppImageBuilder.yml + - uses: actions/upload-artifact@v4 + with: + name: AppImage + path: './*.AppImage*' diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index c0800c64c..3efb909cd 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -45,16 +45,16 @@ jobs: chmod +x build/VoxelEngine chmod +x AppDir/usr/bin/vctest AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build --output-always -# - name: Create DMG -# run: | -# mkdir VoxelEngineDmgContent -# cp -r build/res VoxelEngineDmgContent/ -# cp -r build/VoxelEngine VoxelEngineDmgContent/ -# cp -r build/libs VoxelEngineDmgContent/libs -# hdiutil create VoxelEngineMacApp.dmg -volname "VoxelEngine" -srcfolder VoxelEngineDmgContent -ov -format UDZO -# -# - name: Upload artifacts -# uses: actions/upload-artifact@v4 -# with: -# name: VoxelEngineMacOs -# path: VoxelEngineMacApp.dmg + - name: Create DMG + run: | + mkdir VoxelEngineDmgContent + cp -r build/res VoxelEngineDmgContent/ + cp -r build/VoxelEngine VoxelEngineDmgContent/ + cp -r build/libs VoxelEngineDmgContent/libs + hdiutil create VoxelEngineMacApp.dmg -volname "VoxelEngine" -srcfolder VoxelEngineDmgContent -ov -format UDZO + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: VoxelEngineMacOs + path: VoxelEngineMacApp.dmg From 863146cf6bb498ed101619c31e7ace70b632148f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 11 Dec 2024 23:42:59 +0300 Subject: [PATCH 47/64] move PlayerController to LevelScreen --- src/TestMainloop.cpp | 2 +- src/frontend/screens/LevelScreen.cpp | 67 ++++++++++++++++++++-------- src/frontend/screens/LevelScreen.hpp | 2 + src/graphics/render/Decorator.cpp | 11 +++-- src/graphics/render/Decorator.hpp | 3 +- src/logic/LevelController.cpp | 38 ++-------------- src/logic/LevelController.hpp | 7 +-- 7 files changed, 66 insertions(+), 64 deletions(-) diff --git a/src/TestMainloop.cpp b/src/TestMainloop.cpp index ddf0ca171..334870591 100644 --- a/src/TestMainloop.cpp +++ b/src/TestMainloop.cpp @@ -37,7 +37,7 @@ void TestMainloop::run() { if (controller) { float delta = time.getDelta(); controller->getLevel()->getWorld()->updateTimers(delta); - controller->update(glm::min(delta, 0.2f), false, false); + controller->update(glm::min(delta, 0.2f), false); } } logger.info() << "test finished"; diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index 4436cf488..98b216133 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -1,27 +1,30 @@ #include "LevelScreen.hpp" -#include "core_defs.hpp" -#include "frontend/hud.hpp" -#include "frontend/LevelFrontend.hpp" #include "audio/audio.hpp" #include "coders/imageio.hpp" +#include "content/Content.hpp" +#include "core_defs.hpp" #include "debug/Logger.hpp" #include "engine.hpp" #include "files/files.hpp" -#include "content/Content.hpp" +#include "frontend/LevelFrontend.hpp" +#include "frontend/hud.hpp" #include "graphics/core/DrawContext.hpp" #include "graphics/core/ImageData.hpp" #include "graphics/core/PostProcessing.hpp" #include "graphics/core/Viewport.hpp" -#include "graphics/render/WorldRenderer.hpp" #include "graphics/render/Decorator.hpp" -#include "graphics/ui/elements/Menu.hpp" +#include "graphics/render/WorldRenderer.hpp" #include "graphics/ui/GUI.hpp" +#include "graphics/ui/elements/Menu.hpp" #include "logic/LevelController.hpp" +#include "logic/PlayerController.hpp" #include "logic/scripting/scripting.hpp" #include "logic/scripting/scripting_hud.hpp" -#include "util/stringutil.hpp" +#include "maths/voxmaths.hpp" +#include "objects/Players.hpp" #include "physics/Hitbox.hpp" +#include "util/stringutil.hpp" #include "voxels/Chunks.hpp" #include "window/Camera.hpp" #include "window/Events.hpp" @@ -42,16 +45,25 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr levelPtr) menu->reset(); controller = std::make_unique(engine, std::move(levelPtr)); + + auto player = level->players->get(0); + playerController = std::make_unique( + settings, + level, + player, + controller->getBlocksController() + ); + frontend = std::make_unique( - controller->getPlayer(), controller.get(), assets + player, controller.get(), assets ); worldRenderer = std::make_unique( - engine, *frontend, controller->getPlayer() + engine, *frontend, player ); - hud = std::make_unique(engine, *frontend, controller->getPlayer()); + hud = std::make_unique(engine, *frontend, player); decorator = std::make_unique( - *engine, *controller, *worldRenderer, assets + *engine, *controller, *worldRenderer, assets, *player ); keepAlive(settings.graphics.backlight.observe([=](bool) { @@ -59,7 +71,7 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr levelPtr) worldRenderer->clear(); })); keepAlive(settings.camera.fov.observe([=](double value) { - controller->getPlayer()->fpCamera->setFov(glm::radians(value)); + player->fpCamera->setFov(glm::radians(value)); })); keepAlive(Events::getBinding(BIND_CHUNKS_RELOAD).onactived.add([=](){ controller->getLevel()->chunks->saveAndClear(); @@ -106,7 +118,7 @@ void LevelScreen::saveWorldPreview() { try { logger.info() << "saving world preview"; auto paths = engine->getPaths(); - auto player = controller->getPlayer(); + auto player = playerController->getPlayer(); auto& settings = engine->getSettings(); int previewSize = settings.ui.worldPreviewSize.get(); @@ -129,6 +141,7 @@ void LevelScreen::saveWorldPreview() { } void LevelScreen::updateHotkeys() { + auto player = playerController->getPlayer(); auto& settings = engine->getSettings(); if (Events::jpressed(keycode::O)) { settings.graphics.frustumCulling.toggle(); @@ -137,7 +150,7 @@ void LevelScreen::updateHotkeys() { hudVisible = !hudVisible; } if (Events::jpressed(keycode::F3)) { - controller->getPlayer()->debug = !controller->getPlayer()->debug; + player->debug = !player->debug; } } @@ -151,7 +164,7 @@ void LevelScreen::update(float delta) { updateHotkeys(); } - auto player = controller->getPlayer(); + auto player = playerController->getPlayer(); auto camera = player->currentCamera; bool paused = hud->isPause(); @@ -167,18 +180,36 @@ void LevelScreen::update(float delta) { camera->dir, glm::vec3(0, 1, 0) ); + auto level = controller->getLevel(); + const auto& settings = engine->getSettings(); if (!hud->isPause()) { - controller->getLevel()->getWorld()->updateTimers(delta); + level->getWorld()->updateTimers(delta); animator->update(delta); } - controller->update(glm::min(delta, 0.2f), !inputLocked, hud->isPause()); + + glm::vec3 position = player->getPosition(); + level->loadMatrix( + position.x, + position.z, + settings.chunks.loadDistance.get() + settings.chunks.padding.get() * 2 + ); + controller->getChunksController()->update( + settings.chunks.loadSpeed.get(), settings.chunks.loadDistance.get(), + floordiv(position.x, CHUNK_W), floordiv(position.z, CHUNK_D) + ); + if (!hud->isPause()) { + playerController->update(delta, !inputLocked); + } + controller->update(glm::min(delta, 0.2f), hud->isPause()); + playerController->postUpdate(delta, !inputLocked, hud->isPause()); + hud->update(hudVisible); decorator->update(delta, *camera); } void LevelScreen::draw(float delta) { - auto camera = controller->getPlayer()->currentCamera; + auto camera = playerController->getPlayer()->currentCamera; Viewport viewport(Window::width, Window::height); DrawContext ctx(nullptr, viewport, batch.get()); diff --git a/src/frontend/screens/LevelScreen.hpp b/src/frontend/screens/LevelScreen.hpp index db0f04ff4..1810359b0 100644 --- a/src/frontend/screens/LevelScreen.hpp +++ b/src/frontend/screens/LevelScreen.hpp @@ -8,6 +8,7 @@ class Engine; class LevelFrontend; class Hud; class LevelController; +class PlayerController; class WorldRenderer; class TextureAnimator; class PostProcessing; @@ -18,6 +19,7 @@ class Level; class LevelScreen : public Screen { std::unique_ptr frontend; std::unique_ptr controller; + std::unique_ptr playerController; std::unique_ptr worldRenderer; std::unique_ptr animator; std::unique_ptr postProcessing; diff --git a/src/graphics/render/Decorator.cpp b/src/graphics/render/Decorator.cpp index b7bd42188..2f01955fb 100644 --- a/src/graphics/render/Decorator.cpp +++ b/src/graphics/render/Decorator.cpp @@ -10,6 +10,7 @@ #include "voxels/Block.hpp" #include "world/Level.hpp" #include "window/Camera.hpp" +#include "objects/Player.hpp" #include "objects/Players.hpp" #include "logic/LevelController.hpp" #include "util/stringutil.hpp" @@ -29,13 +30,17 @@ inline constexpr int ITERATIONS = 512; inline constexpr int BIG_PRIME = 666667; Decorator::Decorator( - Engine& engine, LevelController& controller, WorldRenderer& renderer, const Assets& assets + Engine& engine, + LevelController& controller, + WorldRenderer& renderer, + const Assets& assets, + Player& player ) : engine(engine), level(*controller.getLevel()), renderer(renderer), assets(assets), - player(*controller.getPlayer()) { + player(player) { controller.getBlocksController()->listenBlockInteraction( [this](auto player, const auto& pos, const auto& def, BlockInteraction type) { if (type == BlockInteraction::placing && def.particles) { @@ -43,7 +48,7 @@ Decorator::Decorator( } }); for (const auto& [id, player] : *level.players) { - if (id == controller.getPlayer()->getId()) { + if (id == this->player.getId()) { continue; } playerTexts[id] = renderer.texts->add(std::make_unique( diff --git a/src/graphics/render/Decorator.hpp b/src/graphics/render/Decorator.hpp index fbc196aa9..d19ed45f8 100644 --- a/src/graphics/render/Decorator.hpp +++ b/src/graphics/render/Decorator.hpp @@ -39,7 +39,8 @@ class Decorator { Engine& engine, LevelController& level, WorldRenderer& renderer, - const Assets& assets + const Assets& assets, + Player& player ); void update(float delta, const Camera& camera); diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 87c2b6ad8..9e30db489 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -5,14 +5,14 @@ #include "debug/Logger.hpp" #include "engine.hpp" #include "files/WorldFiles.hpp" +#include "maths/voxmaths.hpp" #include "objects/Entities.hpp" #include "objects/Players.hpp" #include "physics/Hitbox.hpp" +#include "scripting/scripting.hpp" #include "settings.hpp" #include "world/Level.hpp" #include "world/World.hpp" -#include "maths/voxmaths.hpp" -#include "scripting/scripting.hpp" static debug::Logger logger("level-control"); @@ -25,41 +25,17 @@ LevelController::LevelController(Engine* engine, std::unique_ptr levelPtr chunks(std::make_unique( *level, settings.chunks.padding.get() )) { - if (!engine->isHeadless()) { - player = std::make_unique( - settings, level.get(), level->players->get(0), blocks.get() - ); - } scripting::on_world_load(this); } -void LevelController::update(float delta, bool input, bool pause) { - if (player) { - glm::vec3 position = player->getPlayer()->getPosition(); - level->loadMatrix( - position.x, - position.z, - settings.chunks.loadDistance.get() + settings.chunks.padding.get() * 2 - ); - chunks->update( - settings.chunks.loadSpeed.get(), settings.chunks.loadDistance.get(), - floordiv(position.x, CHUNK_W), floordiv(position.z, CHUNK_D) - ); - } - +void LevelController::update(float delta, bool pause) { if (!pause) { // update all objects that needed blocks->update(delta); - if (player) { - player->update(delta, input); - } level->entities->updatePhysics(delta); level->entities->update(delta); } level->entities->clean(); - if (player) { - player->postUpdate(delta, input, pause); - } } void LevelController::saveWorld() { @@ -79,10 +55,6 @@ Level* LevelController::getLevel() { return level.get(); } -Player* LevelController::getPlayer() { - return player->getPlayer(); -} - BlocksController* LevelController::getBlocksController() { return blocks.get(); } @@ -90,7 +62,3 @@ BlocksController* LevelController::getBlocksController() { ChunksController* LevelController::getChunksController() { return chunks.get(); } - -PlayerController* LevelController::getPlayerController() { - return player.get(); -} diff --git a/src/logic/LevelController.hpp b/src/logic/LevelController.hpp index 94e0ac2c5..6cfcf8167 100644 --- a/src/logic/LevelController.hpp +++ b/src/logic/LevelController.hpp @@ -4,7 +4,6 @@ #include "BlocksController.hpp" #include "ChunksController.hpp" -#include "PlayerController.hpp" class Engine; class Level; @@ -18,23 +17,19 @@ class LevelController { // Sub-controllers std::unique_ptr blocks; std::unique_ptr chunks; - std::unique_ptr player; public: LevelController(Engine* engine, std::unique_ptr level); /// @param delta time elapsed since the last update - /// @param input is user input allowed to be handled /// @param pause is world and player simulation paused - void update(float delta, bool input, bool pause); + void update(float delta, bool pause); void saveWorld(); void onWorldQuit(); Level* getLevel(); - Player* getPlayer(); BlocksController* getBlocksController(); ChunksController* getChunksController(); - PlayerController* getPlayerController(); }; From 7e92a16016c622ea7f4d2ba6c55fd335813cdd9e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 12 Dec 2024 02:40:59 +0300 Subject: [PATCH 48/64] fix weak_ptr leak --- src/lighting/Lightmap.cpp | 7 ++--- src/util/WeakPtrsMap.hpp | 43 +++++++++++++++++++++++++++++ src/voxels/ChunksStorage.cpp | 53 ++++++++++++++++-------------------- src/voxels/ChunksStorage.hpp | 9 ++---- 4 files changed, 73 insertions(+), 39 deletions(-) create mode 100644 src/util/WeakPtrsMap.hpp diff --git a/src/lighting/Lightmap.cpp b/src/lighting/Lightmap.cpp index cb077b8b1..24581e246 100644 --- a/src/lighting/Lightmap.cpp +++ b/src/lighting/Lightmap.cpp @@ -2,16 +2,15 @@ #include "util/data_io.hpp" -#include +#include +#include void Lightmap::set(const Lightmap* lightmap) { set(lightmap->map); } void Lightmap::set(const light_t* map) { - for (size_t i = 0; i < CHUNK_VOL; i++) { - this->map[i] = map[i]; - } + std::memcpy(this->map, map, sizeof(light_t) * CHUNK_VOL); } static_assert(sizeof(light_t) == 2, "replace dataio calls to new light_t"); diff --git a/src/util/WeakPtrsMap.hpp b/src/util/WeakPtrsMap.hpp new file mode 100644 index 000000000..0d206d652 --- /dev/null +++ b/src/util/WeakPtrsMap.hpp @@ -0,0 +1,43 @@ +#include +#include +#include + +namespace util { + template + class WeakPtrsMap { + std::unordered_map> map; + std::mutex mutex; + public: + std::weak_ptr& operator[](const K& k) { + return map[k]; + } + + std::shared_ptr fetch(const K& k) { + auto found = map.find(k); + if (found == map.end()) { + return nullptr; + } + auto ptr = found->second.lock(); + if (ptr == nullptr) { + map.erase(found); + } + return ptr; + } + + void erase(const K& k) { + map.erase(k); + } + + size_t size() const { + return map.size(); + } + + void lock() { + mutex.lock(); + } + + void unlock() { + mutex.unlock(); + } + }; +} diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index 26b4fbd3d..60e3b5445 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/ChunksStorage.cpp @@ -17,34 +17,28 @@ static debug::Logger logger("chunks-storage"); -ChunksStorage::ChunksStorage(Level* level) : level(level) { +ChunksStorage::ChunksStorage(Level* level) + : level(level), + chunksMap(std::make_shared>()) { } std::shared_ptr ChunksStorage::fetch(int x, int z) { - std::lock_guard lock(mutex); - - auto found = chunksMap.find(glm::ivec2(x, z)); - if (found == chunksMap.end()) { - return nullptr; - } - auto ptr = found->second.lock(); - if (ptr == nullptr) { - chunksMap.erase(found); - } - return ptr; + std::lock_guard lock(*chunksMap); + return chunksMap->fetch({x, z}); } -static void check_voxels(const ContentIndices& indices, Chunk* chunk) { +static void check_voxels(const ContentIndices& indices, Chunk& chunk) { bool corrupted = false; + blockid_t defsCount = indices.blocks.count(); for (size_t i = 0; i < CHUNK_VOL; i++) { - blockid_t id = chunk->voxels[i].id; - if (indices.blocks.get(id) == nullptr) { + blockid_t id = chunk.voxels[i].id; + if (id >= defsCount) { if (!corrupted) { #ifdef NDEBUG // release auto logline = logger.error(); logline << "corruped blocks detected at " << i << " of chunk "; - logline << chunk->x << "x" << chunk->z; + logline << chunk.x << "x" << chunk.z; logline << " -> " << id; corrupted = true; #else @@ -52,33 +46,34 @@ static void check_voxels(const ContentIndices& indices, Chunk* chunk) { abort(); #endif } - chunk->voxels[i].id = BLOCK_AIR; + chunk.voxels[i].id = BLOCK_AIR; } } } std::shared_ptr ChunksStorage::create(int x, int z) { - std::lock_guard lock(mutex); - - auto found = chunksMap.find(glm::ivec2(x, z)); - if (found != chunksMap.end()) { - auto chunk = found->second.lock(); - if (chunk) { - return chunk; - } + if (auto ptr = chunksMap->fetch({x, z})) { + return ptr; } World* world = level->getWorld(); auto& regions = world->wfile.get()->getRegions(); - auto chunk = std::make_shared(x, z); - chunksMap[glm::ivec2(chunk->x, chunk->z)] = chunk; + auto& localChunksMap = chunksMap; + auto chunk = std::shared_ptr( + new Chunk(x, z), + [localChunksMap, x, z](auto ptr) { + std::lock_guard lock(*localChunksMap); + localChunksMap->erase({x, z}); + delete ptr; + } + ); + (*chunksMap)[glm::ivec2(chunk->x, chunk->z)] = chunk; if (auto data = regions.getVoxels(chunk->x, chunk->z)) { const auto& indices = *level->content->getIndices(); chunk->decode(data.get()); - check_voxels(indices, chunk.get()); - + check_voxels(indices, *chunk); auto invs = regions.fetchInventories(chunk->x, chunk->z); auto iterator = invs.begin(); while (iterator != invs.end()) { diff --git a/src/voxels/ChunksStorage.hpp b/src/voxels/ChunksStorage.hpp index 78bc03d04..62cccaf3a 100644 --- a/src/voxels/ChunksStorage.hpp +++ b/src/voxels/ChunksStorage.hpp @@ -1,19 +1,16 @@ #pragma once -#include -#include -#include - #define GLM_ENABLE_EXPERIMENTAL #include +#include "util/WeakPtrsMap.hpp" + class Chunk; class Level; class ChunksStorage { Level* level; - std::mutex mutex; - std::unordered_map> chunksMap; + std::shared_ptr> chunksMap; public: ChunksStorage(Level* level); ~ChunksStorage() = default; From 8a5042f2a22664aacb22241346894ae156fb2962 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 12 Dec 2024 02:54:56 +0300 Subject: [PATCH 49/64] fix msvc build --- src/voxels/ChunksStorage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index 60e3b5445..c19803f47 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/ChunksStorage.cpp @@ -62,7 +62,7 @@ std::shared_ptr ChunksStorage::create(int x, int z) { auto& localChunksMap = chunksMap; auto chunk = std::shared_ptr( new Chunk(x, z), - [localChunksMap, x, z](auto ptr) { + [localChunksMap, x, z](Chunk* ptr) { std::lock_guard lock(*localChunksMap); localChunksMap->erase({x, z}); delete ptr; From 9ec8788838773ad87b050a9d7c4f6656bcbc0901 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 12 Dec 2024 15:54:56 +0300 Subject: [PATCH 50/64] add player.create, test.set_setting, test.sleep_until, world.count_chunks --- dev/tests/example.lua | 13 ++++++- res/scripts/stdlib.lua | 12 +++++++ src/frontend/screens/LevelScreen.cpp | 11 ------ src/lighting/Lighting.cpp | 41 +++++++++++----------- src/logic/LevelController.cpp | 13 +++++++ src/logic/scripting/lua/libs/libplayer.cpp | 9 +++++ src/logic/scripting/lua/libs/libworld.cpp | 9 +++++ src/util/AreaMap2D.hpp | 4 +-- src/voxels/ChunksStorage.cpp | 4 +++ src/voxels/ChunksStorage.hpp | 2 ++ 10 files changed, 84 insertions(+), 34 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 8cde7829c..27833cd10 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1 +1,12 @@ -print("hello world") +test.set_setting("chunks.load-distance", 2) +test.set_setting("chunks.load-speed", 16) + +test.new_world("demo", "2019", "core:default") +local pid = player.create("Xerxes") +assert(player.get_name(pid) == "Xerxes") +test.sleep_until(function() return world.count_chunks() >= 9 end, 1000) +print(world.count_chunks()) +assert(block.get(0, 0, 0) == block.index("core:obstacle")) +block.destruct(0, 0, 0, pid) +assert(block.get(0, 0, 0) == 0) +test.close_world(true) diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 3d21def6e..0298a2b9b 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -16,7 +16,19 @@ if test then test.open_world = core.open_world test.close_world = core.close_world test.reconfig_packs = core.reconfig_packs + test.set_setting = core.set_setting test.tick = coroutine.yield + + function test.sleep_until(predicate, max_ticks) + max_ticks = max_ticks or 1e9 + local ticks = 0 + while ticks < max_ticks and not predicate() do + test.tick() + end + if ticks == max_ticks then + error("max ticks exceed") + end + end end ------------------------------------------------ diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index 98b216133..9470e7e08 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -187,17 +187,6 @@ void LevelScreen::update(float delta) { level->getWorld()->updateTimers(delta); animator->update(delta); } - - glm::vec3 position = player->getPosition(); - level->loadMatrix( - position.x, - position.z, - settings.chunks.loadDistance.get() + settings.chunks.padding.get() * 2 - ); - controller->getChunksController()->update( - settings.chunks.loadSpeed.get(), settings.chunks.loadDistance.get(), - floordiv(position.x, CHUNK_W), floordiv(position.z, CHUNK_D) - ); if (!hud->isPause()) { playerController->update(delta, !inputLocked); } diff --git a/src/lighting/Lighting.cpp b/src/lighting/Lighting.cpp index 6aad3049d..8d496b476 100644 --- a/src/lighting/Lighting.cpp +++ b/src/lighting/Lighting.cpp @@ -86,11 +86,12 @@ void Lighting::buildSkyLight(int cx, int cz){ solverS->solve(); } -void Lighting::onChunkLoaded(int cx, int cz, bool expand){ - LightSolver* solverR = this->solverR.get(); - LightSolver* solverG = this->solverG.get(); - LightSolver* solverB = this->solverB.get(); - LightSolver* solverS = this->solverS.get(); + +void Lighting::onChunkLoaded(int cx, int cz, bool expand) { + auto& solverR = *this->solverR; + auto& solverG = *this->solverG; + auto& solverB = *this->solverB; + auto& solverS = *this->solverS; auto blockDefs = content->getIndices()->blocks.getDefs(); auto chunk = chunks->getChunk(cx, cz); @@ -103,9 +104,9 @@ void Lighting::onChunkLoaded(int cx, int cz, bool expand){ int gx = x + cx * CHUNK_W; int gz = z + cz * CHUNK_D; if (block->rt.emissive){ - solverR->add(gx,y,gz,block->emission[0]); - solverG->add(gx,y,gz,block->emission[1]); - solverB->add(gx,y,gz,block->emission[2]); + solverR.add(gx,y,gz,block->emission[0]); + solverG.add(gx,y,gz,block->emission[1]); + solverB.add(gx,y,gz,block->emission[2]); } } } @@ -119,10 +120,10 @@ void Lighting::onChunkLoaded(int cx, int cz, bool expand){ int gz = z + cz * CHUNK_D; int rgbs = chunk->lightmap.get(x, y, z); if (rgbs){ - solverR->add(gx,y,gz, Lightmap::extract(rgbs, 0)); - solverG->add(gx,y,gz, Lightmap::extract(rgbs, 1)); - solverB->add(gx,y,gz, Lightmap::extract(rgbs, 2)); - solverS->add(gx,y,gz, Lightmap::extract(rgbs, 3)); + solverR.add(gx,y,gz, Lightmap::extract(rgbs, 0)); + solverG.add(gx,y,gz, Lightmap::extract(rgbs, 1)); + solverB.add(gx,y,gz, Lightmap::extract(rgbs, 2)); + solverS.add(gx,y,gz, Lightmap::extract(rgbs, 3)); } } } @@ -134,19 +135,19 @@ void Lighting::onChunkLoaded(int cx, int cz, bool expand){ int gz = z + cz * CHUNK_D; int rgbs = chunk->lightmap.get(x, y, z); if (rgbs){ - solverR->add(gx,y,gz, Lightmap::extract(rgbs, 0)); - solverG->add(gx,y,gz, Lightmap::extract(rgbs, 1)); - solverB->add(gx,y,gz, Lightmap::extract(rgbs, 2)); - solverS->add(gx,y,gz, Lightmap::extract(rgbs, 3)); + solverR.add(gx,y,gz, Lightmap::extract(rgbs, 0)); + solverG.add(gx,y,gz, Lightmap::extract(rgbs, 1)); + solverB.add(gx,y,gz, Lightmap::extract(rgbs, 2)); + solverS.add(gx,y,gz, Lightmap::extract(rgbs, 3)); } } } } } - solverR->solve(); - solverG->solve(); - solverB->solve(); - solverS->solve(); + solverR.solve(); + solverG.solve(); + solverB.solve(); + solverS.solve(); } void Lighting::onBlockSet(int x, int y, int z, blockid_t id){ diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 9e30db489..a9c002b19 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -8,6 +8,7 @@ #include "maths/voxmaths.hpp" #include "objects/Entities.hpp" #include "objects/Players.hpp" +#include "objects/Player.hpp" #include "physics/Hitbox.hpp" #include "scripting/scripting.hpp" #include "settings.hpp" @@ -29,6 +30,18 @@ LevelController::LevelController(Engine* engine, std::unique_ptr levelPtr } void LevelController::update(float delta, bool pause) { + for (const auto& [uid, player] : *level->players) { + glm::vec3 position = player->getPosition(); + level->loadMatrix( + position.x, + position.z, + settings.chunks.loadDistance.get() + settings.chunks.padding.get() * 2 + ); + chunks->update( + settings.chunks.loadSpeed.get(), settings.chunks.loadDistance.get(), + floordiv(position.x, CHUNK_W), floordiv(position.z, CHUNK_D) + ); + } if (!pause) { // update all objects that needed blocks->update(delta); diff --git a/src/logic/scripting/lua/libs/libplayer.cpp b/src/logic/scripting/lua/libs/libplayer.cpp index 0628c5afe..00c4585d9 100644 --- a/src/logic/scripting/lua/libs/libplayer.cpp +++ b/src/logic/scripting/lua/libs/libplayer.cpp @@ -250,6 +250,14 @@ static int l_set_name(lua::State* L) { return 0; } +static int l_create(lua::State* L) { + auto player = level->players->create(); + if (lua::isstring(L, 1)) { + player->setName(lua::require_string(L, 1)); + } + return lua::pushinteger(L, player->getId()); +} + const luaL_Reg playerlib[] = { {"get_pos", lua::wrap}, {"set_pos", lua::wrap}, @@ -277,5 +285,6 @@ const luaL_Reg playerlib[] = { {"set_camera", lua::wrap}, {"get_name", lua::wrap}, {"set_name", lua::wrap}, + {"create", lua::wrap}, {NULL, NULL} }; diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 99d2fec3b..747c68a42 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -13,6 +13,7 @@ #include "lighting/Lighting.hpp" #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" +#include "voxels/ChunksStorage.hpp" #include "world/Level.hpp" #include "world/World.hpp" @@ -231,6 +232,13 @@ static int l_set_chunk_data(lua::State* L) { return 1; } +static int l_count_chunks(lua::State* L) { + if (level == nullptr) { + return 0; + } + return lua::pushinteger(L, level->chunksStorage->size()); +} + const luaL_Reg worldlib[] = { {"is_open", lua::wrap}, {"get_list", lua::wrap}, @@ -246,5 +254,6 @@ const luaL_Reg worldlib[] = { {"exists", lua::wrap}, {"get_chunk_data", lua::wrap}, {"set_chunk_data", lua::wrap}, + {"count_chunks", lua::wrap}, {NULL, NULL} }; diff --git a/src/util/AreaMap2D.hpp b/src/util/AreaMap2D.hpp index 1a6c97ceb..b7eda1fb7 100644 --- a/src/util/AreaMap2D.hpp +++ b/src/util/AreaMap2D.hpp @@ -27,7 +27,7 @@ namespace util { std::fill(secondBuffer.begin(), secondBuffer.end(), T{}); for (TCoord y = 0; y < sizeY; y++) { for (TCoord x = 0; x < sizeX; x++) { - auto& value = firstBuffer[y * sizeX + x]; + auto value = std::move(firstBuffer[y * sizeX + x]); auto nx = x - dx; auto ny = y - dy; if (value == T{}) { @@ -40,7 +40,7 @@ namespace util { valuesCount--; continue; } - secondBuffer[ny * sizeX + nx] = value; + secondBuffer[ny * sizeX + nx] = std::move(value); } } std::swap(firstBuffer, secondBuffer); diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index c19803f47..d3dd25467 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/ChunksStorage.cpp @@ -109,3 +109,7 @@ std::shared_ptr ChunksStorage::create(int x, int z) { chunk->blocksMetadata = regions.getBlocksData(chunk->x, chunk->z); return chunk; } + +size_t ChunksStorage::size() const { + return chunksMap->size(); +} diff --git a/src/voxels/ChunksStorage.hpp b/src/voxels/ChunksStorage.hpp index 62cccaf3a..cda81bcbb 100644 --- a/src/voxels/ChunksStorage.hpp +++ b/src/voxels/ChunksStorage.hpp @@ -17,4 +17,6 @@ class ChunksStorage { std::shared_ptr fetch(int x, int z); std::shared_ptr create(int x, int z); + + size_t size() const; }; From 8e6fb1dfb4e662936d5c40ff5c170e05c06faa31 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 12 Dec 2024 16:24:17 +0300 Subject: [PATCH 51/64] update libworld docs --- doc/en/scripting/builtins/libworld.md | 3 +++ doc/ru/scripting/builtins/libworld.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/doc/en/scripting/builtins/libworld.md b/doc/en/scripting/builtins/libworld.md index 5a1214f3a..fc18773e5 100644 --- a/doc/en/scripting/builtins/libworld.md +++ b/doc/en/scripting/builtins/libworld.md @@ -44,4 +44,7 @@ world.is_day() -> bool -- Checks if the current time is nighttime. From 0.833(8pm) to 0.333(8am). world.is_night() -> bool + +-- Returns the total number of chunks loaded into memory +world.count_chunks() -> int ``` diff --git a/doc/ru/scripting/builtins/libworld.md b/doc/ru/scripting/builtins/libworld.md index 49ac46fad..0467d784c 100644 --- a/doc/ru/scripting/builtins/libworld.md +++ b/doc/ru/scripting/builtins/libworld.md @@ -43,4 +43,7 @@ world.is_day() -> bool -- Проверяет является ли текущее время ночью. От 0.833(8 вечера) до 0.333(8 утра). world.is_night() -> bool + +-- Возвращает общее количество загруженных в память чанков +world.count_chunks() -> int ``` From 913f942adbb19689e9e8c54a9430a00299cbc7a8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 12 Dec 2024 16:37:06 +0300 Subject: [PATCH 52/64] add platform::get_process_id --- src/util/platform.cpp | 15 +++++++++++++-- src/util/platform.hpp | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/util/platform.cpp b/src/util/platform.cpp index d4d399250..061c78e28 100644 --- a/src/util/platform.cpp +++ b/src/util/platform.cpp @@ -57,7 +57,14 @@ void platform::sleep(size_t millis) { // Reset the timer resolution back to the system default timeEndPeriod(periodMin); } -#else + +int platform::get_process_id() { + return GetCurrentProcessId(); +} + +#else // _WIN32 + +#include void platform::configure_encoding() { } @@ -74,7 +81,11 @@ std::string platform::detect_locale() { void platform::sleep(size_t millis) { std::this_thread::sleep_for(std::chrono::milliseconds(millis)); } -#endif + +int platform::get_process_id() { + return getpid(); +} +#endif // _WIN32 void platform::open_folder(const std::filesystem::path& folder) { if (!std::filesystem::is_directory(folder)) { diff --git a/src/util/platform.hpp b/src/util/platform.hpp index 1283bcfea..6793411ed 100644 --- a/src/util/platform.hpp +++ b/src/util/platform.hpp @@ -12,4 +12,5 @@ namespace platform { void open_folder(const std::filesystem::path& folder); /// Makes the current thread sleep for the specified amount of milliseconds. void sleep(size_t millis); + int get_process_id(); } From 086c20c37090b339cabaaa9619972f45c286def9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 12 Dec 2024 20:28:41 +0300 Subject: [PATCH 53/64] cache block.index, block.name, item.index, item.name --- res/scripts/post_content.lua | 23 ++++++++++++++++++++++- src/content/ContentBuilder.cpp | 8 ++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/res/scripts/post_content.lua b/res/scripts/post_content.lua index ae67c6789..8725ed974 100644 --- a/res/scripts/post_content.lua +++ b/res/scripts/post_content.lua @@ -7,7 +7,7 @@ local names = { "hidden", "draw-group", "picking-item", "surface-replacement", "script-name", "ui-layout", "inventory-size", "tick-interval", "overlay-texture", "translucent", "fields", "particles", "icon-type", "icon", "placing-block", - "stack-size" + "stack-size", "name" } for name, _ in pairs(user_props) do table.insert(names, name) @@ -40,3 +40,24 @@ make_read_only(block.properties) for k,v in pairs(block.properties) do make_read_only(v) end + +local function cache_names(library) + local indices = {} + local names = {} + for id=0,library.defs_count()-1 do + local name = library.properties[id].name + indices[name] = id + names[id] = name + end + + function library.name(id) + return names[id] + end + + function library.index(name) + return indices[name] + end +end + +cache_names(block) +cache_names(item) diff --git a/src/content/ContentBuilder.cpp b/src/content/ContentBuilder.cpp index f7ab3b1a7..633085788 100644 --- a/src/content/ContentBuilder.cpp +++ b/src/content/ContentBuilder.cpp @@ -91,10 +91,18 @@ std::unique_ptr ContentBuilder::build() { for (Block* def : blockDefsIndices) { def->rt.pickingItem = content->items.require(def->pickingItem).rt.id; def->rt.surfaceReplacement = content->blocks.require(def->surfaceReplacement).rt.id; + if (def->properties == nullptr) { + def->properties = dv::object(); + } + def->properties["name"] = def->name; } for (ItemDef* def : itemDefsIndices) { def->rt.placingBlock = content->blocks.require(def->placingBlock).rt.id; + if (def->properties == nullptr) { + def->properties = dv::object(); + } + def->properties["name"] = def->name; } for (auto& [name, def] : content->generators.getDefs()) { From 53cc8d3c7be93de628bde75da0c52d7a32315378 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 12 Dec 2024 23:40:57 +0300 Subject: [PATCH 54/64] add core.blank --- src/logic/scripting/lua/libs/libcore.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp index 90d8d958b..5ac9d93d0 100644 --- a/src/logic/scripting/lua/libs/libcore.cpp +++ b/src/logic/scripting/lua/libs/libcore.cpp @@ -234,7 +234,12 @@ static int l_quit(lua::State*) { return 0; } +static int l_blank(lua::State*) { + return 0; +} + const luaL_Reg corelib[] = { + {"nop", lua::wrap}, {"get_version", lua::wrap}, {"new_world", lua::wrap}, {"open_world", lua::wrap}, From e0b3425eff12bd8132bf551a8d03c96322536b51 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 13 Dec 2024 05:54:41 +0300 Subject: [PATCH 55/64] migrate blocks interaction (scripting) to global chunks storage (WIP) --- dev/tests/example.lua | 8 +- res/content/base/scripts/world.lua | 22 ++-- src/frontend/debug_panel.cpp | 3 +- src/frontend/hud.cpp | 3 +- src/graphics/render/WorldRenderer.cpp | 2 +- src/logic/scripting/lua/libs/libblock.cpp | 11 ++ src/maths/voxmaths.hpp | 19 +++- src/util/WeakPtrsMap.hpp | 43 ------- src/voxels/Chunks.cpp | 76 ++++--------- src/voxels/Chunks.hpp | 2 - src/voxels/ChunksStorage.cpp | 132 +++++++++++++++++++--- src/voxels/ChunksStorage.hpp | 23 +++- src/world/Level.cpp | 8 ++ src/world/LevelEvents.cpp | 6 +- src/world/LevelEvents.hpp | 11 +- src/world/World.cpp | 2 +- 16 files changed, 227 insertions(+), 144 deletions(-) delete mode 100644 src/util/WeakPtrsMap.hpp diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 27833cd10..ae6c05f22 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,12 +1,16 @@ -test.set_setting("chunks.load-distance", 2) +test.set_setting("chunks.load-distance", 3) test.set_setting("chunks.load-speed", 16) +test.reconfig_packs({"base"}, {}) test.new_world("demo", "2019", "core:default") local pid = player.create("Xerxes") assert(player.get_name(pid) == "Xerxes") test.sleep_until(function() return world.count_chunks() >= 9 end, 1000) print(world.count_chunks()) -assert(block.get(0, 0, 0) == block.index("core:obstacle")) + +timeit(1000000, block.get, 0, 0, 0) +timeit(1000000, block.get_slow, 0, 0, 0) + block.destruct(0, 0, 0, pid) assert(block.get(0, 0, 0) == 0) test.close_world(true) diff --git a/res/content/base/scripts/world.lua b/res/content/base/scripts/world.lua index 62d111ae3..3f69dc444 100644 --- a/res/content/base/scripts/world.lua +++ b/res/content/base/scripts/world.lua @@ -1,12 +1,14 @@ function on_block_broken(id, x, y, z, playerid) - gfx.particles.emit({x+0.5, y+0.5, z+0.5}, 64, { - lifetime=1.0, - spawn_interval=0.0001, - explosion={4, 4, 4}, - texture="blocks:"..block.get_textures(id)[1], - random_sub_uv=0.1, - size={0.1, 0.1, 0.1}, - spawn_shape="box", - spawn_spread={0.4, 0.4, 0.4} - }) + if gfx then + gfx.particles.emit({x+0.5, y+0.5, z+0.5}, 64, { + lifetime=1.0, + spawn_interval=0.0001, + explosion={4, 4, 4}, + texture="blocks:"..block.get_textures(id)[1], + random_sub_uv=0.1, + size={0.1, 0.1, 0.1}, + spawn_shape="box", + spawn_spread={0.4, 0.4, 0.4} + }) + end end diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index c9f08203b..f78fc6a8e 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -21,6 +21,7 @@ #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" +#include "voxels/ChunksStorage.hpp" #include "world/Level.hpp" #include "world/World.hpp" @@ -95,7 +96,7 @@ std::shared_ptr create_debug_panel( std::to_wstring(ParticlesRenderer::aliveEmitters); })); panel->add(create_label([&]() { - return L"chunks: "+std::to_wstring(level.chunks->getChunksCount())+ + return L"chunks: "+std::to_wstring(level.chunksStorage->size())+ L" visible: "+std::to_wstring(ChunksRenderer::visibleChunks); })); panel->add(create_label([&]() { diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 20199f0ae..55449ad9b 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -269,6 +269,7 @@ void Hud::updateHotbarControl() { void Hud::updateWorldGenDebugVisualization() { auto& level = frontend.getLevel(); + auto& chunks = *level.chunks; auto generator = frontend.getController()->getChunksController()->getGenerator(); auto debugInfo = generator->createDebugInfo(); @@ -293,7 +294,7 @@ void Hud::updateWorldGenDebugVisualization() { int az = z - (height - areaHeight) / 2; data[(flippedZ * width + x) * 4 + 1] = - level.chunks->getChunk(ax + ox, az + oz) ? 255 : 0; + chunks.getChunk(ax + ox, az + oz) ? 255 : 0; data[(flippedZ * width + x) * 4 + 0] = level.chunksStorage->fetch(ax + ox, az + oz) ? 255 : 0; diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index 19d6d232f..a0fd4c83d 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -86,7 +86,7 @@ WorldRenderer::WorldRenderer( auto& settings = engine->getSettings(); level.events->listen( EVT_CHUNK_HIDDEN, - [this](lvl_event_type, Chunk* chunk) { chunks->unload(chunk); } + [this](LevelEventType, Chunk* chunk) { chunks->unload(chunk); } ); auto assets = engine->getAssets(); skybox = std::make_unique( diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 2f24e0feb..bf61549e5 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -7,6 +7,7 @@ #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" #include "voxels/voxel.hpp" +#include "voxels/ChunksStorage.hpp" #include "world/Level.hpp" #include "maths/voxmaths.hpp" #include "data/StructLayout.hpp" @@ -114,6 +115,15 @@ static int l_get(lua::State* L) { return lua::pushinteger(L, id); } +static int l_get_slow(lua::State* L) { + auto x = lua::tointeger(L, 1); + auto y = lua::tointeger(L, 2); + auto z = lua::tointeger(L, 3); + auto vox = level->chunksStorage->get(x, y, z); + int id = vox == nullptr ? -1 : vox->id; + return lua::pushinteger(L, id); +} + static int l_get_x(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); @@ -598,6 +608,7 @@ const luaL_Reg blocklib[] = { {"is_replaceable_at", lua::wrap}, {"set", lua::wrap}, {"get", lua::wrap}, + {"get_slow", lua::wrap}, {"get_X", lua::wrap}, {"get_Y", lua::wrap}, {"get_Z", lua::wrap}, diff --git a/src/maths/voxmaths.hpp b/src/maths/voxmaths.hpp index 07b42e221..feeb9c8f3 100644 --- a/src/maths/voxmaths.hpp +++ b/src/maths/voxmaths.hpp @@ -1,7 +1,5 @@ #pragma once -#include "typedefs.hpp" - inline constexpr int floordiv(int a, int b) { if (a < 0 && a % b) { return (a / b) - 1; @@ -9,6 +7,23 @@ inline constexpr int floordiv(int a, int b) { return a / b; } +inline constexpr bool is_pot(int a) { + return (a > 0) && ((a & (a - 1)) == 0); +} + +inline constexpr unsigned floorlog2(unsigned x) { + return x == 1 ? 0 : 1 + floorlog2(x >> 1); +} + +template +inline constexpr int floordiv(int a) { + if constexpr (is_pot(b)) { + return a >> floorlog2(b); + } else { + return floordiv(a, b); + } +} + inline constexpr int ceildiv(int a, int b) { if (a > 0 && a % b) { return a / b + 1; diff --git a/src/util/WeakPtrsMap.hpp b/src/util/WeakPtrsMap.hpp deleted file mode 100644 index 0d206d652..000000000 --- a/src/util/WeakPtrsMap.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include - -namespace util { - template - class WeakPtrsMap { - std::unordered_map> map; - std::mutex mutex; - public: - std::weak_ptr& operator[](const K& k) { - return map[k]; - } - - std::shared_ptr fetch(const K& k) { - auto found = map.find(k); - if (found == map.end()) { - return nullptr; - } - auto ptr = found->second.lock(); - if (ptr == nullptr) { - map.erase(found); - } - return ptr; - } - - void erase(const K& k) { - map.erase(k); - } - - size_t size() const { - return map.size(); - } - - void lock() { - mutex.lock(); - } - - void unlock() { - mutex.unlock(); - } - }; -} diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 943249647..390ac16c6 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -9,7 +9,6 @@ #include "data/StructLayout.hpp" #include "coders/byte_utils.hpp" -#include "coders/json.hpp" #include "content/Content.hpp" #include "files/WorldFiles.hpp" #include "graphics/core/Mesh.hpp" @@ -39,7 +38,6 @@ Chunks::Chunks( worldFiles(wfile) { areaMap.setCenter(ox-w/2, oz-d/2); areaMap.setOutCallback([this](int, int, const auto& chunk) { - save(chunk.get()); this->level->events->trigger(EVT_CHUNK_HIDDEN, chunk.get()); }); } @@ -48,13 +46,13 @@ voxel* Chunks::get(int32_t x, int32_t y, int32_t z) const { if (y < 0 || y >= CHUNK_H) { return nullptr; } - int cx = floordiv(x, CHUNK_W); - int cz = floordiv(z, CHUNK_D); + int cx = floordiv(x); + int cz = floordiv(z); auto ptr = areaMap.getIf(cx, cz); if (ptr == nullptr) { return nullptr; } - Chunk* chunk = ptr->get(); // not thread safe + Chunk* chunk = ptr->get(); if (chunk == nullptr) { return nullptr; } @@ -107,27 +105,27 @@ const AABB* Chunks::isObstacleAt(float x, float y, float z) const { bool Chunks::isSolidBlock(int32_t x, int32_t y, int32_t z) { voxel* v = get(x, y, z); if (v == nullptr) return false; - return indices->blocks.get(v->id)->rt.solid; //-V522 + return indices->blocks.require(v->id).rt.solid; } bool Chunks::isReplaceableBlock(int32_t x, int32_t y, int32_t z) { voxel* v = get(x, y, z); if (v == nullptr) return false; - return indices->blocks.get(v->id)->replaceable; //-V522 + return indices->blocks.require(v->id).replaceable; } bool Chunks::isObstacleBlock(int32_t x, int32_t y, int32_t z) { voxel* v = get(x, y, z); if (v == nullptr) return false; - return indices->blocks.get(v->id)->obstacle; //-V522 + return indices->blocks.require(v->id).obstacle; } ubyte Chunks::getLight(int32_t x, int32_t y, int32_t z, int channel) const { if (y < 0 || y >= CHUNK_H) { return 0; } - int cx = floordiv(x, CHUNK_W); - int cz = floordiv(z, CHUNK_D); + int cx = floordiv(x); + int cz = floordiv(z); auto ptr = areaMap.getIf(cx, cz); if (ptr == nullptr) { @@ -146,8 +144,8 @@ light_t Chunks::getLight(int32_t x, int32_t y, int32_t z) const { if (y < 0 || y >= CHUNK_H) { return 0; } - int cx = floordiv(x, CHUNK_W); - int cz = floordiv(z, CHUNK_D); + int cx = floordiv(x); + int cz = floordiv(z); auto ptr = areaMap.getIf(cx, cz); if (ptr == nullptr) { @@ -166,8 +164,8 @@ Chunk* Chunks::getChunkByVoxel(int32_t x, int32_t y, int32_t z) const { if (y < 0 || y >= CHUNK_H) { return nullptr; } - int cx = floordiv(x, CHUNK_W); - int cz = floordiv(z, CHUNK_D); + int cx = floordiv(x); + int cz = floordiv(z); if (auto ptr = areaMap.getIf(cx, cz)) { return ptr->get(); } @@ -369,8 +367,8 @@ void Chunks::set( if (y < 0 || y >= CHUNK_H) { return; } - int cx = floordiv(x, CHUNK_W); - int cz = floordiv(z, CHUNK_D); + int cx = floordiv(x); + int cz = floordiv(z); auto ptr = areaMap.getIf(cx, cz); if (ptr == nullptr) { return; @@ -673,7 +671,11 @@ void Chunks::resize(uint32_t newW, uint32_t newD) { } bool Chunks::putChunk(const std::shared_ptr& chunk) { - return areaMap.set(chunk->x, chunk->z, chunk); + if (areaMap.set(chunk->x, chunk->z, chunk)) { + level->events->trigger(LevelEventType::EVT_CHUNK_SHOWN, chunk.get()); + return true; + } + return false; } // reduce nesting on next modification @@ -692,11 +694,11 @@ void Chunks::getVoxels(VoxelsVolume* volume, bool backlight) const { int h = volume->getH(); int d = volume->getD(); - int scx = floordiv(x, CHUNK_W); - int scz = floordiv(z, CHUNK_D); + int scx = floordiv(x); + int scz = floordiv(z); - int ecx = floordiv(x + w, CHUNK_W); - int ecz = floordiv(z + d, CHUNK_D); + int ecx = floordiv(x + w); + int ecz = floordiv(z + d); int cw = ecx - scx + 1; int cd = ecz - scz + 1; @@ -768,35 +770,3 @@ void Chunks::getVoxels(VoxelsVolume* volume, bool backlight) const { void Chunks::saveAndClear() { areaMap.clear(); } - -void Chunks::save(Chunk* chunk) { - if (chunk != nullptr) { - AABB aabb( - glm::vec3(chunk->x * CHUNK_W, -INFINITY, chunk->z * CHUNK_D), - glm::vec3( - (chunk->x + 1) * CHUNK_W, INFINITY, (chunk->z + 1) * CHUNK_D - ) - ); - auto entities = level->entities->getAllInside(aabb); - auto root = dv::object(); - root["data"] = level->entities->serialize(entities); - if (!entities.empty()) { - level->entities->despawn(std::move(entities)); - chunk->flags.entities = true; - } - worldFiles->getRegions().put( - chunk, - chunk->flags.entities ? json::to_binary(root, true) - : std::vector() - ); - } -} - -void Chunks::saveAll() { - const auto& chunks = areaMap.getBuffer(); - for (size_t i = 0; i < areaMap.area(); i++) { - if (auto& chunk = chunks[i]) { - save(chunk.get()); - } - } -} diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index 01648301a..fd33eb9a9 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -124,8 +124,6 @@ class Chunks { void resize(uint32_t newW, uint32_t newD); void saveAndClear(); - void save(Chunk* chunk); - void saveAll(); const std::vector>& getChunks() const { return areaMap.getBuffer(); diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index d3dd25467..8a872eea6 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/ChunksStorage.cpp @@ -3,6 +3,7 @@ #include #include "content/Content.hpp" +#include "coders/json.hpp" #include "debug/Logger.hpp" #include "files/WorldFiles.hpp" #include "items/Inventories.hpp" @@ -15,16 +16,27 @@ #include "Block.hpp" #include "Chunk.hpp" +inline long long keyfrom(int x, int z) { + union { + int pos[2]; + long long key; + } ekey; + ekey.pos[0] = x; + ekey.pos[1] = z; + return ekey.key; +} + static debug::Logger logger("chunks-storage"); -ChunksStorage::ChunksStorage(Level* level) - : level(level), - chunksMap(std::make_shared>()) { +ChunksStorage::ChunksStorage(Level* level) : level(level) { } std::shared_ptr ChunksStorage::fetch(int x, int z) { - std::lock_guard lock(*chunksMap); - return chunksMap->fetch({x, z}); + const auto& found = chunksMap.find(keyfrom(x, z)); + if (found == chunksMap.end()) { + return nullptr; + } + return found->second; } static void check_voxels(const ContentIndices& indices, Chunk& chunk) { @@ -51,24 +63,22 @@ static void check_voxels(const ContentIndices& indices, Chunk& chunk) { } } +void ChunksStorage::erase(int x, int z) { + chunksMap.erase(keyfrom(x, z)); +} + std::shared_ptr ChunksStorage::create(int x, int z) { - if (auto ptr = chunksMap->fetch({x, z})) { - return ptr; + const auto& found = chunksMap.find(keyfrom(x, z)); + if (found != chunksMap.end()) { + return found->second; } + auto chunk = std::make_shared(x, z); + chunksMap[keyfrom(x, z)] = chunk; + World* world = level->getWorld(); auto& regions = world->wfile.get()->getRegions(); - auto& localChunksMap = chunksMap; - auto chunk = std::shared_ptr( - new Chunk(x, z), - [localChunksMap, x, z](Chunk* ptr) { - std::lock_guard lock(*localChunksMap); - localChunksMap->erase({x, z}); - delete ptr; - } - ); - (*chunksMap)[glm::ivec2(chunk->x, chunk->z)] = chunk; if (auto data = regions.getVoxels(chunk->x, chunk->z)) { const auto& indices = *level->content->getIndices(); @@ -110,6 +120,92 @@ std::shared_ptr ChunksStorage::create(int x, int z) { return chunk; } +void ChunksStorage::pinChunk(std::shared_ptr chunk) { + pinnedChunks[{chunk->x, chunk->z}] = std::move(chunk); +} + +void ChunksStorage::unpinChunk(int x, int z) { + pinnedChunks.erase({x, z}); +} + size_t ChunksStorage::size() const { - return chunksMap->size(); + return chunksMap.size(); +} + +voxel* ChunksStorage::get(int x, int y, int z) const { + if (y < 0 || y >= CHUNK_H) { + return nullptr; + } + + int cx = floordiv(x); + int cz = floordiv(z); + + const auto& found = chunksMap.find(keyfrom(cx, cz)); + if (found == chunksMap.end()) { + return nullptr; + } + const auto& chunk = found->second; + int lx = x - cx * CHUNK_W; + int lz = z - cz * CHUNK_D; + return &chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; +} + +void ChunksStorage::incref(Chunk* chunk) { + auto key = reinterpret_cast(chunk); + const auto& found = refCounters.find(key); + if (found == refCounters.end()) { + refCounters[key] = 1; + return; + } + found->second++; +} + +void ChunksStorage::decref(Chunk* chunk) { + auto key = reinterpret_cast(chunk); + const auto& found = refCounters.find(key); + if (found == refCounters.end()) { + abort(); + } + if (--found->second == 0) { + union { + int pos[2]; + long long key; + } ekey; + ekey.pos[0] = chunk->x; + ekey.pos[1] = chunk->z; + + save(chunk); + chunksMap.erase(ekey.key); + refCounters.erase(found); + } +} + +void ChunksStorage::save(Chunk* chunk) { + if (chunk == nullptr) { + return; + } + AABB aabb( + glm::vec3(chunk->x * CHUNK_W, -INFINITY, chunk->z * CHUNK_D), + glm::vec3( + (chunk->x + 1) * CHUNK_W, INFINITY, (chunk->z + 1) * CHUNK_D + ) + ); + auto entities = level->entities->getAllInside(aabb); + auto root = dv::object(); + root["data"] = level->entities->serialize(entities); + if (!entities.empty()) { + level->entities->despawn(std::move(entities)); + chunk->flags.entities = true; + } + level->getWorld()->wfile->getRegions().put( + chunk, + chunk->flags.entities ? json::to_binary(root, true) + : std::vector() + ); +} + +void ChunksStorage::saveAll() { + for (const auto& [_, chunk] : chunksMap) { + save(chunk.get()); + } } diff --git a/src/voxels/ChunksStorage.hpp b/src/voxels/ChunksStorage.hpp index cda81bcbb..9d8d03323 100644 --- a/src/voxels/ChunksStorage.hpp +++ b/src/voxels/ChunksStorage.hpp @@ -1,16 +1,22 @@ #pragma once +#include +#include + #define GLM_ENABLE_EXPERIMENTAL +#include #include -#include "util/WeakPtrsMap.hpp" +#include "voxel.hpp" class Chunk; class Level; class ChunksStorage { Level* level; - std::shared_ptr> chunksMap; + std::unordered_map> chunksMap; + std::unordered_map> pinnedChunks; + std::unordered_map refCounters; public: ChunksStorage(Level* level); ~ChunksStorage() = default; @@ -18,5 +24,18 @@ class ChunksStorage { std::shared_ptr fetch(int x, int z); std::shared_ptr create(int x, int z); + void pinChunk(std::shared_ptr chunk); + void unpinChunk(int x, int z); + + voxel* get(int x, int y, int z) const; + size_t size() const; + + void incref(Chunk* chunk); + void decref(Chunk* chunk); + + void erase(int x, int z); + + void save(Chunk* chunk); + void saveAll(); }; diff --git a/src/world/Level.cpp b/src/world/Level.cpp index 402c8e5fc..a89dc2e60 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -54,12 +54,20 @@ Level::Level( entities->setNextID(worldInfo.nextEntityId); } + events->listen(LevelEventType::EVT_CHUNK_SHOWN, [this](LevelEventType, Chunk* chunk) { + chunksStorage->incref(chunk); + }); + events->listen(LevelEventType::EVT_CHUNK_HIDDEN, [this](LevelEventType, Chunk* chunk) { + chunksStorage->decref(chunk); + }); + uint matrixSize = (settings.chunks.loadDistance.get() + settings.chunks.padding.get()) * 2; chunks = std::make_unique( matrixSize, matrixSize, 0, 0, world->wfile.get(), this ); + lighting = std::make_unique(content, chunks.get()); inventories = std::make_unique(*this); diff --git a/src/world/LevelEvents.cpp b/src/world/LevelEvents.cpp index 00017cc66..cb98cb9dd 100644 --- a/src/world/LevelEvents.cpp +++ b/src/world/LevelEvents.cpp @@ -4,14 +4,14 @@ using std::vector; -void LevelEvents::listen(lvl_event_type type, const chunk_event_func& func) { +void LevelEvents::listen(LevelEventType type, const ChunkEventFunc& func) { auto& callbacks = chunk_callbacks[type]; callbacks.push_back(func); } -void LevelEvents::trigger(lvl_event_type type, Chunk* chunk) { +void LevelEvents::trigger(LevelEventType type, Chunk* chunk) { const auto& callbacks = chunk_callbacks[type]; - for (const chunk_event_func& func : callbacks) { + for (const ChunkEventFunc& func : callbacks) { func(type, chunk); } } diff --git a/src/world/LevelEvents.hpp b/src/world/LevelEvents.hpp index d0518b5ca..f7bbaa7f6 100644 --- a/src/world/LevelEvents.hpp +++ b/src/world/LevelEvents.hpp @@ -6,16 +6,17 @@ class Chunk; -enum lvl_event_type { +enum LevelEventType { + EVT_CHUNK_SHOWN, EVT_CHUNK_HIDDEN, }; -using chunk_event_func = std::function; +using ChunkEventFunc = std::function; class LevelEvents { - std::unordered_map> + std::unordered_map> chunk_callbacks; public: - void listen(lvl_event_type type, const chunk_event_func& func); - void trigger(lvl_event_type type, Chunk* chunk); + void listen(LevelEventType type, const ChunkEventFunc& func); + void trigger(LevelEventType type, Chunk* chunk); }; diff --git a/src/world/World.cpp b/src/world/World.cpp index 812f6d919..0067964cf 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -66,7 +66,7 @@ void World::writeResources(const Content* content) { void World::write(Level* level) { const Content* content = level->content; - level->chunks->saveAll(); + level->chunksStorage->saveAll(); info.nextEntityId = level->entities->peekNextID(); wfile->write(this, content); From bf43fd71bf29822d2e1e07ee0b3d7f3eb6a65a30 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 13 Dec 2024 07:16:13 +0300 Subject: [PATCH 56/64] update libblock --- src/logic/scripting/lua/libs/libblock.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index bf61549e5..c8499e713 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -128,7 +128,7 @@ static int l_get_x(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = level->chunks->get(x, y, z); + auto vox = level->chunksStorage->get(x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, glm::ivec3(1, 0, 0)); } @@ -145,7 +145,7 @@ static int l_get_y(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = level->chunks->get(x, y, z); + auto vox = level->chunksStorage->get(x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, glm::ivec3(0, 1, 0)); } @@ -162,7 +162,7 @@ static int l_get_z(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = level->chunks->get(x, y, z); + auto vox = level->chunksStorage->get(x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, glm::ivec3(0, 0, 1)); } @@ -179,7 +179,7 @@ static int l_get_rotation(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - voxel* vox = level->chunks->get(x, y, z); + voxel* vox = level->chunksStorage->get(x, y, z); int rotation = vox == nullptr ? 0 : vox->state.rotation; return lua::pushinteger(L, rotation); } @@ -197,7 +197,7 @@ static int l_get_states(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = level->chunks->get(x, y, z); + auto vox = level->chunksStorage->get(x, y, z); int states = vox == nullptr ? 0 : blockstate2int(vox->state); return lua::pushinteger(L, states); } From 251aca7abc13e269c1593745ce8ee08c7ccb1933 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 13 Dec 2024 07:33:55 +0300 Subject: [PATCH 57/64] test --- src/voxels/ChunksStorage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index 8a872eea6..45c74fee3 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/ChunksStorage.cpp @@ -29,6 +29,7 @@ inline long long keyfrom(int x, int z) { static debug::Logger logger("chunks-storage"); ChunksStorage::ChunksStorage(Level* level) : level(level) { + chunksMap.max_load_factor(0.2f); } std::shared_ptr ChunksStorage::fetch(int x, int z) { From 76559cc9ac1c4cec5b0c764e07094a1f4258b3d9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 13 Dec 2024 08:02:26 +0300 Subject: [PATCH 58/64] Revert "test" This reverts commit 251aca7abc13e269c1593745ce8ee08c7ccb1933. --- src/voxels/ChunksStorage.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index 45c74fee3..8a872eea6 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/ChunksStorage.cpp @@ -29,7 +29,6 @@ inline long long keyfrom(int x, int z) { static debug::Logger logger("chunks-storage"); ChunksStorage::ChunksStorage(Level* level) : level(level) { - chunksMap.max_load_factor(0.2f); } std::shared_ptr ChunksStorage::fetch(int x, int z) { From 12c10d1ad135168007cacc9ca02d3bc2c0238766 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Dec 2024 03:09:25 +0300 Subject: [PATCH 59/64] Add Windows build with Clang (#411) after 72 commits and 24 hours --- .github/workflows/windows-clang.yml | 75 +++++++++++++++++++++++++ src/CMakeLists.txt | 15 +++-- src/logic/scripting/lua/lua_commons.hpp | 3 +- 3 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/windows-clang.yml diff --git a/.github/workflows/windows-clang.yml b/.github/workflows/windows-clang.yml new file mode 100644 index 000000000..102557e88 --- /dev/null +++ b/.github/workflows/windows-clang.yml @@ -0,0 +1,75 @@ +name: Windows Build (CLang) + +on: + push: + branches: [ "main", "release-**"] + pull_request: + branches: [ "main" ] + +jobs: + build-windows: + + strategy: + matrix: + include: + - os: windows-latest + compiler: clang + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v2 + with: + submodules: 'true' + - uses: msys2/setup-msys2@v2 + id: msys2 + name: Setup MSYS2 + with: + msystem: clang64 + install: >- + mingw-w64-clang-x86_64-toolchain + mingw-w64-clang-x86_64-cmake + mingw-w64-clang-x86_64-make + mingw-w64-clang-x86_64-luajit + git tree + - name: Set up vcpkg + shell: msys2 {0} + run: | + git clone https://github.com/microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.bat + ./vcpkg integrate install + cd .. + - name: Configure project with CMake and vcpkg + shell: msys2 {0} + run: | + export VCPKG_DEFAULT_TRIPLET=x64-mingw-static + export VCPKG_DEFAULT_HOST_TRIPLET=x64-mingw-static + mkdir build + cd build + cmake -G "MinGW Makefiles" -DVCPKG_TARGET_TRIPLET=x64-mingw-static -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_WINDOWS_VCPKG=ON .. + cmake --build . --config Release + - name: Package for Windows + run: | + mkdir packaged + mkdir packaged/res + cp build/VoxelEngine.exe packaged/ + cp build/vctest/vctest.exe packaged/ + cp build/*.dll packaged/ + cp -r build/res/* packaged/res/ + mv packaged/VoxelEngine.exe packaged/VoxelCore.exe + - env: + MSYS2_LOCATION: ${{ steps.msys2.outputs.msys2-location }} + name: Add lua51.dll to the package + run: | + cp $env:MSYS2_LOCATION/clang64/bin/lua51.dll ${{ github.workspace }}/packaged/ + working-directory: ${{ github.workspace }} + - uses: actions/upload-artifact@v4 + with: + name: Windows-Build + path: 'packaged/*' + - name: Run engine tests + shell: msys2 {0} + working-directory: ${{ github.workspace }} + run: | + packaged/vctest.exe -e packaged/VoxelCore.exe -d dev/tests -u build --output-always diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bc5bc04cd..4e8ae3489 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,10 +20,19 @@ if (NOT APPLE) find_package(EnTT REQUIRED) endif() +set(LIBS "") + if (WIN32) if(VOXELENGINE_BUILD_WINDOWS_VCPKG) - set(LUA_LIBRARIES "${CMAKE_CURRENT_SOURCE_DIR}/../vcpkg/packages/luajit_x64-windows/lib/lua51.lib") - set(LUA_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../vcpkg/packages/luajit_x64-windows/include/luajit") + if (MSVC) + set(LUA_LIBRARIES "${CMAKE_CURRENT_SOURCE_DIR}/../vcpkg/packages/luajit_x64-windows/lib/lua51.lib") + set(LUA_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../vcpkg/packages/luajit_x64-windows/include/luajit") + else() + find_package(PkgConfig) + pkg_check_modules(OpenAL REQUIRED IMPORTED_TARGET openal) + set(LIBS ${LIBS} luajit-5.1 wsock32 ws2_32 pthread PkgConfig::OpenAL -static-libstdc++) + message(${OPENAL_LIBRARY}) + endif() find_package(glfw3 REQUIRED) find_package(glm REQUIRED) find_package(vorbis REQUIRED) @@ -53,8 +62,6 @@ else() set(VORBISLIB ${VORBIS_LDFLAGS}) endif() -set(LIBS "") - if(UNIX) find_package(glfw3 3.3 REQUIRED) find_package(Threads REQUIRED) diff --git a/src/logic/scripting/lua/lua_commons.hpp b/src/logic/scripting/lua/lua_commons.hpp index ff94ade7c..f272dbf03 100644 --- a/src/logic/scripting/lua/lua_commons.hpp +++ b/src/logic/scripting/lua/lua_commons.hpp @@ -3,9 +3,8 @@ #include "delegates.hpp" #include "logic/scripting/scripting.hpp" -#ifdef __linux__ +#if (defined __linux__) || (defined __MINGW32__) #include - #include #else #include From 59978f4c6e1cbf5e96a3ec32ce5370a6a9a4c72d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Dec 2024 03:10:59 +0300 Subject: [PATCH 60/64] rename workflows --- .github/workflows/appimage.yml | 2 +- .github/workflows/windows.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 25432d30d..a72489f0b 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -1,4 +1,4 @@ -name: C/C++ AppImage +name: x86-64 AppImage on: push: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9c1f7f963..de477e896 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -1,10 +1,10 @@ -name: Windows Build +name: MSVC Build on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main", "headless-mode" ] + branches: [ "main" ] jobs: build-windows: From 5e025b548feedf38b50fdb710870acb2e0a7498b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Dec 2024 04:59:57 +0300 Subject: [PATCH 61/64] update test --- dev/tests/example.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index ae6c05f22..c066c1ed4 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -8,8 +8,11 @@ assert(player.get_name(pid) == "Xerxes") test.sleep_until(function() return world.count_chunks() >= 9 end, 1000) print(world.count_chunks()) -timeit(1000000, block.get, 0, 0, 0) -timeit(1000000, block.get_slow, 0, 0, 0) +for i=1,3 do + print("---") + timeit(1000000, block.get, 0, 0, 0) + timeit(1000000, block.get_slow, 0, 0, 0) +end block.destruct(0, 0, 0, pid) assert(block.get(0, 0, 0) == 0) From 323acb25730c124da239c92d84fd023fb1bed963 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Dec 2024 16:27:17 +0300 Subject: [PATCH 62/64] static link libstdc++ (mingw) --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 57f19747b..a2415ad97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,9 @@ else() if (CMAKE_BUILD_TYPE MATCHES "Debug") target_compile_options(${PROJECT_NAME} PRIVATE -Og) endif() + if (WIN32) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++") + endif() endif() if(VOXELENGINE_BUILD_WINDOWS_VCPKG AND WIN32) From e713412a6d286c493d562bc58339bc834794c611 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Dec 2024 18:58:51 +0300 Subject: [PATCH 63/64] rename ChunksStorage to GlobalChunks --- src/frontend/debug_panel.cpp | 2 +- src/frontend/hud.cpp | 2 +- src/logic/ChunksController.cpp | 2 +- src/logic/scripting/lua/libs/libblock.cpp | 2 +- src/logic/scripting/lua/libs/libworld.cpp | 2 +- .../{ChunksStorage.cpp => GlobalChunks.cpp} | 26 +++++++++---------- .../{ChunksStorage.hpp => GlobalChunks.hpp} | 6 ++--- src/world/Level.cpp | 4 +-- src/world/Level.hpp | 4 +-- src/world/World.cpp | 2 +- src/world/generator/VoxelFragment.cpp | 2 +- 11 files changed, 27 insertions(+), 27 deletions(-) rename src/voxels/{ChunksStorage.cpp => GlobalChunks.cpp} (86%) rename src/voxels/{ChunksStorage.hpp => GlobalChunks.hpp} (90%) diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index f78fc6a8e..3470cdb14 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -21,7 +21,7 @@ #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" -#include "voxels/ChunksStorage.hpp" +#include "voxels/GlobalChunks.hpp" #include "world/Level.hpp" #include "world/World.hpp" diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 55449ad9b..1a2d83150 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -40,7 +40,7 @@ #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" -#include "voxels/ChunksStorage.hpp" +#include "voxels/GlobalChunks.hpp" #include "window/Camera.hpp" #include "window/Events.hpp" #include "window/input.hpp" diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index f61a27550..0d8d97b4a 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -14,7 +14,7 @@ #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" -#include "voxels/ChunksStorage.hpp" +#include "voxels/GlobalChunks.hpp" #include "world/Level.hpp" #include "world/World.hpp" #include "world/generator/WorldGenerator.hpp" diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index c8499e713..dbd87489a 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -7,7 +7,7 @@ #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" #include "voxels/voxel.hpp" -#include "voxels/ChunksStorage.hpp" +#include "voxels/GlobalChunks.hpp" #include "world/Level.hpp" #include "maths/voxmaths.hpp" #include "data/StructLayout.hpp" diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 747c68a42..680047778 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -13,7 +13,7 @@ #include "lighting/Lighting.hpp" #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" -#include "voxels/ChunksStorage.hpp" +#include "voxels/GlobalChunks.hpp" #include "world/Level.hpp" #include "world/World.hpp" diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/GlobalChunks.cpp similarity index 86% rename from src/voxels/ChunksStorage.cpp rename to src/voxels/GlobalChunks.cpp index 8a872eea6..379ca3288 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -1,4 +1,4 @@ -#include "ChunksStorage.hpp" +#include "GlobalChunks.hpp" #include @@ -28,10 +28,10 @@ inline long long keyfrom(int x, int z) { static debug::Logger logger("chunks-storage"); -ChunksStorage::ChunksStorage(Level* level) : level(level) { +GlobalChunks::GlobalChunks(Level* level) : level(level) { } -std::shared_ptr ChunksStorage::fetch(int x, int z) { +std::shared_ptr GlobalChunks::fetch(int x, int z) { const auto& found = chunksMap.find(keyfrom(x, z)); if (found == chunksMap.end()) { return nullptr; @@ -63,11 +63,11 @@ static void check_voxels(const ContentIndices& indices, Chunk& chunk) { } } -void ChunksStorage::erase(int x, int z) { +void GlobalChunks::erase(int x, int z) { chunksMap.erase(keyfrom(x, z)); } -std::shared_ptr ChunksStorage::create(int x, int z) { +std::shared_ptr GlobalChunks::create(int x, int z) { const auto& found = chunksMap.find(keyfrom(x, z)); if (found != chunksMap.end()) { return found->second; @@ -120,19 +120,19 @@ std::shared_ptr ChunksStorage::create(int x, int z) { return chunk; } -void ChunksStorage::pinChunk(std::shared_ptr chunk) { +void GlobalChunks::pinChunk(std::shared_ptr chunk) { pinnedChunks[{chunk->x, chunk->z}] = std::move(chunk); } -void ChunksStorage::unpinChunk(int x, int z) { +void GlobalChunks::unpinChunk(int x, int z) { pinnedChunks.erase({x, z}); } -size_t ChunksStorage::size() const { +size_t GlobalChunks::size() const { return chunksMap.size(); } -voxel* ChunksStorage::get(int x, int y, int z) const { +voxel* GlobalChunks::get(int x, int y, int z) const { if (y < 0 || y >= CHUNK_H) { return nullptr; } @@ -150,7 +150,7 @@ voxel* ChunksStorage::get(int x, int y, int z) const { return &chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; } -void ChunksStorage::incref(Chunk* chunk) { +void GlobalChunks::incref(Chunk* chunk) { auto key = reinterpret_cast(chunk); const auto& found = refCounters.find(key); if (found == refCounters.end()) { @@ -160,7 +160,7 @@ void ChunksStorage::incref(Chunk* chunk) { found->second++; } -void ChunksStorage::decref(Chunk* chunk) { +void GlobalChunks::decref(Chunk* chunk) { auto key = reinterpret_cast(chunk); const auto& found = refCounters.find(key); if (found == refCounters.end()) { @@ -180,7 +180,7 @@ void ChunksStorage::decref(Chunk* chunk) { } } -void ChunksStorage::save(Chunk* chunk) { +void GlobalChunks::save(Chunk* chunk) { if (chunk == nullptr) { return; } @@ -204,7 +204,7 @@ void ChunksStorage::save(Chunk* chunk) { ); } -void ChunksStorage::saveAll() { +void GlobalChunks::saveAll() { for (const auto& [_, chunk] : chunksMap) { save(chunk.get()); } diff --git a/src/voxels/ChunksStorage.hpp b/src/voxels/GlobalChunks.hpp similarity index 90% rename from src/voxels/ChunksStorage.hpp rename to src/voxels/GlobalChunks.hpp index 9d8d03323..b21e15e6b 100644 --- a/src/voxels/ChunksStorage.hpp +++ b/src/voxels/GlobalChunks.hpp @@ -12,14 +12,14 @@ class Chunk; class Level; -class ChunksStorage { +class GlobalChunks { Level* level; std::unordered_map> chunksMap; std::unordered_map> pinnedChunks; std::unordered_map refCounters; public: - ChunksStorage(Level* level); - ~ChunksStorage() = default; + GlobalChunks(Level* level); + ~GlobalChunks() = default; std::shared_ptr fetch(int x, int z); std::shared_ptr create(int x, int z); diff --git a/src/world/Level.cpp b/src/world/Level.cpp index a89dc2e60..99f47d866 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -13,7 +13,7 @@ #include "settings.hpp" #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" -#include "voxels/ChunksStorage.hpp" +#include "voxels/GlobalChunks.hpp" #include "window/Camera.hpp" #include "LevelEvents.hpp" #include "World.hpp" @@ -25,7 +25,7 @@ Level::Level( ) : world(std::move(worldPtr)), content(content), - chunksStorage(std::make_unique(this)), + chunksStorage(std::make_unique(this)), physics(std::make_unique(glm::vec3(0, -22.6f, 0))), events(std::make_unique()), entities(std::make_unique(this)), diff --git a/src/world/Level.hpp b/src/world/Level.hpp index 1f3ef183f..632746720 100644 --- a/src/world/Level.hpp +++ b/src/world/Level.hpp @@ -15,7 +15,7 @@ class Inventories; class LevelEvents; class Lighting; class PhysicsSolver; -class ChunksStorage; +class GlobalChunks; class Camera; class Players; struct EngineSettings; @@ -27,7 +27,7 @@ class Level { const Content* const content; std::unique_ptr chunks; - std::unique_ptr chunksStorage; + std::unique_ptr chunksStorage; std::unique_ptr inventories; std::unique_ptr physics; diff --git a/src/world/World.cpp b/src/world/World.cpp index 0067964cf..eb8c33bf7 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -15,7 +15,7 @@ #include "settings.hpp" #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" -#include "voxels/ChunksStorage.hpp" +#include "voxels/GlobalChunks.hpp" #include "world/generator/WorldGenerator.hpp" #include "world/generator/GeneratorDef.hpp" #include "Level.hpp" diff --git a/src/world/generator/VoxelFragment.cpp b/src/world/generator/VoxelFragment.cpp index f36824b0b..2d7ab9239 100644 --- a/src/world/generator/VoxelFragment.cpp +++ b/src/world/generator/VoxelFragment.cpp @@ -8,7 +8,7 @@ #include "content/Content.hpp" #include "voxels/Chunks.hpp" #include "voxels/Block.hpp" -#include "voxels/ChunksStorage.hpp" +#include "voxels/GlobalChunks.hpp" #include "voxels/VoxelsVolume.hpp" #include "world/Level.hpp" #include "core_defs.hpp" From 5bf56d1f649d0a5d03945d1e720fcee859d56039 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 15 Dec 2024 18:20:34 +0300 Subject: [PATCH 64/64] update workflows --- .github/workflows/appimage.yml | 4 ++-- .github/workflows/macos.yml | 2 +- .github/workflows/windows-clang.yml | 4 ++-- .github/workflows/windows.yml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index a72489f0b..25ae572fc 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ "main", "headless-mode" ] jobs: build-appimage: @@ -24,7 +24,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y build-essential libglfw3-dev libglfw3 libglew-dev \ - libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev libgtest-dev cmake squashfs-tools tree + libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev libgtest-dev cmake squashfs-tools # fix luajit paths sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a sudo ln -s /usr/include/luajit-2.1 /usr/include/lua diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 3efb909cd..24832af93 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ "main", "headless-mode" ] jobs: build-dmg: diff --git a/.github/workflows/windows-clang.yml b/.github/workflows/windows-clang.yml index 102557e88..183f2d6dc 100644 --- a/.github/workflows/windows-clang.yml +++ b/.github/workflows/windows-clang.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ "main", "headless-mode" ] jobs: build-windows: @@ -31,7 +31,7 @@ jobs: mingw-w64-clang-x86_64-cmake mingw-w64-clang-x86_64-make mingw-w64-clang-x86_64-luajit - git tree + git - name: Set up vcpkg shell: msys2 {0} run: | diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index de477e896..d417197c9 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ "main", "headless-mode" ] jobs: build-windows: