From 3e01a399f9c66b63516d69b9651c8d4bfeb4c2cd Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 00:04:56 +0300 Subject: [PATCH 001/246] 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 166400c6..9ed192b5 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 857f4fe0..a3b40211 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 c62900f7..fb8540ba 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 9f26b1c8..38160dac 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 129dc68d..6641057d 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 04a221df..efa0ff6d 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 @@ public: }; 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 99501c12..88caf426 100644 --- a/src/util/command_line.cpp +++ b/src/util/command_line.cpp @@ -40,7 +40,7 @@ public: } }; -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 c6a2ec7e..49ac502e 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 002/246] 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 a56457bd..9e84d36d 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 7eecaef9..bc5bc04c 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 003/246] update locale log messages --- src/frontend/locale.cpp | 58 +++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/frontend/locale.cpp b/src/frontend/locale.cpp index de040a11..2af78076 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())) { +/// @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) { + } + + 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(); } } - } -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)); - 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 004/246] 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 0dc0f591..522e3705 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 @@ public: 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 6641057d..3d0b1c21 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 (!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); + } } - if (auto icon = load_icon(resdir)) { - icon->flipY(); - Window::setIcon(icon.get()); - } - 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 efa0ff6d..aad32100 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -45,10 +45,17 @@ public: 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 d9251897..23de64a7 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 49ac502e..a11020de 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 88caf426..e5a76197 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 @@ public: }; 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 9715c52d..ebb6741d 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 005/246] 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 250fd001..b264c95c 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 006/246] 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 b264c95c..4986c63b 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 007/246] 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 4986c63b..5d4cf976 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 008/246] 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 5d4cf976..d7c2a57f 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 009/246] 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 d7c2a57f..9c26a805 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 3d0b1c21..5b0c6803 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 aad32100..f05a4607 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 23de64a7..26857331 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 9b43cf28..ded9a970 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 000c4fc1..74a5a36b 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 010/246] 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 5b0c6803..d7942072 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 011/246] 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 e4ddf55a..0d76f5f4 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 b14c32d0..57338d34 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 d7942072..422637b8 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 012/246] 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 9c26a805..bac80b19 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 0d76f5f4..d3adc67f 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 57338d34..c11d0da4 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 9e84d36d..57f19747 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 00000000..3b724d47 --- /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 9cf23cc3..d5f8eed1 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 422637b8..49386b65 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 f05a4607..c3559fa8 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 @@ public: /// 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 00000000..b62b1e0f --- /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 ded9a970..9abe534c 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 0ab3c035..4ccba2cb 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 a11020de..b0d03412 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 00000000..90d159f5 --- /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 e5a76197..768c6260 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 00000000..6c1a3843 --- /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 00000000..f6d3e773 --- /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 013/246] 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 bac80b19..7eb6b6e3 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 014/246] 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 7eb6b6e3..dba47c3d 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 015/246] 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 dba47c3d..c491e399 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 016/246] 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 c491e399..257e81e6 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 d3adc67f..1cd0c7dc 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 017/246] 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 1cd0c7dc..ab12bfa1 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 018/246] 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 ab12bfa1..aa5b1d0a 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 019/246] 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 c11d0da4..8b9222c8 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 020/246] 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 8b9222c8..bac73b83 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 021/246] 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 bac73b83..1f7978dd 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 f6d3e773..f8cd1767 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 022/246] update vctest --- vctest/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vctest/main.cpp b/vctest/main.cpp index f8cd1767..afea3451 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 023/246] update vctest --- vctest/main.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/vctest/main.cpp b/vctest/main.cpp index afea3451..1bc82545 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 024/246] update vctest --- vctest/main.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vctest/main.cpp b/vctest/main.cpp index 1bc82545..3f0629e8 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 025/246] 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 3f0629e8..35253bce 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 026/246] 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 35253bce..8af97967 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 027/246] update vctest (MSVC) --- vctest/main.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/vctest/main.cpp b/vctest/main.cpp index 8af97967..6e6cb1c3 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 028/246] 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 6e6cb1c3..e0de9685 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 029/246] 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 1f7978dd..dd1974c7 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 e0de9685..7757041e 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 030/246] 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 3b724d47..f030222d 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 d5f8eed1..9a5aa08a 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 49386b65..8f970892 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 c3559fa8..a0d4a8e9 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -125,6 +125,8 @@ public: /// @brief Get current frame delta-time double getDelta() const; + double getUptime() const; + /// @brief Get active assets storage instance Assets* getAssets(); @@ -166,4 +168,6 @@ public: 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 a3b2d152..23f3db66 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 00000000..034a72fb --- /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 93708dda..45ed4288 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 8f477710..0a4e2259 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 dd1e794d..a70f5948 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 9abe534c..ec26bfcf 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 031/246] 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 bb941a81..8c431da6 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 032/246] 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 96fed03a..6f701346 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 033/246] 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 | 143 +++++++++-------------- 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, 212 insertions(+), 126 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 f030222d..e69de29b 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 00000000..180e75f5 --- /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 00000000..3cd2f608 --- /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 00000000..617a65cd --- /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 00000000..b0acc9a7 --- /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 00000000..57fb5c22 --- /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 9ed192b5..72d4540f 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 8f970892..622a2fdb 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(); - - Window::swapBuffers(); - Events::pollEvents(); - } +void Engine::updateFrontend() { + double delta = time.getDelta(); + updateHotkeys(); + audio::update(delta); + gui->act(delta, Viewport(Window::width, Window::height)); + screen->update(delta); } -void Engine::renderFrame(Batch2D& batch) { - screen->draw(delta); +void Engine::nextFrame() { + Window::setFramerate( + Window::isIconified() && settings.display.limitFpsIconified.get() + ? 20 + : settings.display.framerate.get() + ); + Window::swapBuffers(); + Events::pollEvents(); +} + +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 a0d4a8e9..fee92e81 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 @@ public: /// @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 @@ public: /// @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 @@ public: 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 9043f5b9..c9f08203 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 7062ad56..936de95b 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 736b053e..6c20f37f 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 38bc2257..49a362e3 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 c0dec649..851a49f7 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 4c38fc78..edd6efb7 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 45ed4288..daac093f 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 034/246] fix libworld docs --- doc/en/scripting/builtins/libworld.md | 16 +++++++--------- doc/ru/scripting/builtins/libworld.md | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/doc/en/scripting/builtins/libworld.md b/doc/en/scripting/builtins/libworld.md index 5bf01abb..5a1214f3 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 that it is the current time at night --- from 0.833(8 pm) to 0.333(8 am). -world.is_night() -> bool - -- Checks the existence of a world by name. -world.exists() -> bool +world.exists(name: str) -> bool + +-- Checks if the current time is daytime. From 0.333(8am) to 0.833(8pm). +world.is_day() -> 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 9d7d635e..49ac46fa 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 035/246] 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 e69de29b..6c9546a8 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 9a5aa08a..d8a593b0 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 180e75f5..102aa7cd 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 617a65cd..955f4b26 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 b0acc9a7..06ffae25 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 622a2fdb..072bd5f6 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 fee92e81..55104cb1 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 @@ public: /// @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 @@ public: 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 edd6efb7..27e254f3 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 4eb2631a..cde3b3ce 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 036/246] 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 6c9546a8..8baf6afb 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 d8a593b0..451f07c8 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 102aa7cd..eae4c501 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 955f4b26..32f31627 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 072bd5f6..3c7d4bb5 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 55104cb1..63a78084 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -135,6 +135,7 @@ public: 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 5d2ddd9b..5b24bbbb 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 9b048e44..811b8d53 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 037/246] 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 257e81e6..c491e399 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 aa5b1d0a..c0800c64 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 dd1974c7..9c1f7f96 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 038/246] 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 8baf6afb..beb9fc0e 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 451f07c8..3687d6fc 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 32f31627..ddf0ca17 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 57fb5c22..9f914b1f 100644 --- a/src/Time.hpp +++ b/src/Time.hpp @@ -18,6 +18,7 @@ public: 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 811b8d53..90d8d958 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 039/246] 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 beb9fc0e..4c4c2e5d 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 27e254f3..8c649e8a 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 5b24bbbb..87c2b6ad 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 6f701346..9a2c02d8 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 8d82a3f9..9ae8d727 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 90031991..b174d08a 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 f66d181f..a7bda524 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 cde3b3ce..bec61d3a 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 040/246] 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 4c4c2e5d..6262e369 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 3687d6fc..a732fde6 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 14479c7d..49ddec00 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 daac093f..78952ede 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 041/246] 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 6262e369..a1a0c369 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 a732fde6..3d21def6 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 bec61d3a..812f6d91 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 042/246] 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 272332fb..410448c0 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 043/246] 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 a1a0c369..8cde7829 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 00000000..3e7353e3 --- /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 21111e18..20199f0a 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 410448c0..26b4fbd3 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 35fff6c1..78bc03d0 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 ed370d08..402c8e5f 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 044/246] 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 9a2c02d8..827b42c1 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 045/246] 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 34d1db67..4436cf48 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 614adf41..79c0ba66 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 046/246] 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 c491e399..25432d30 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 c0800c64..3efb909c 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 047/246] 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 ddf0ca17..33487059 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 4436cf48..98b21613 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 db0f04ff..1810359b 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 b7bd4218..2f01955f 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 fbc196aa..d19ed45f 100644 --- a/src/graphics/render/Decorator.hpp +++ b/src/graphics/render/Decorator.hpp @@ -39,7 +39,8 @@ public: 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 87c2b6ad..9e30db48 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 94e0ac2c..6cfcf816 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 048/246] 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 cb077b8b..24581e24 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 00000000..0d206d65 --- /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 26b4fbd3..60e3b544 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 78bc03d0..62cccaf3 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 049/246] 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 60e3b544..c19803f4 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 050/246] 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 8cde7829..27833cd1 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 3d21def6..0298a2b9 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 98b21613..9470e7e0 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 6aad3049..8d496b47 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 9e30db48..a9c002b1 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 0628c5af..00c4585d 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 99d2fec3..747c68a4 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 1a6c97ce..b7eda1fb 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 c19803f4..d3dd2546 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 62cccaf3..cda81bcb 100644 --- a/src/voxels/ChunksStorage.hpp +++ b/src/voxels/ChunksStorage.hpp @@ -17,4 +17,6 @@ public: 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 051/246] 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 5a1214f3..fc18773e 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 49ac46fa..0467d784 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 052/246] 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 d4d39925..061c78e2 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 1283bcfe..6793411e 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 053/246] 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 ae67c678..8725ed97 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 f7ab3b1a..63308578 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 054/246] 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 90d8d958..5ac9d93d 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 055/246] 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 | 134 +++++++++++++++++++--- 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, 228 insertions(+), 145 deletions(-) delete mode 100644 src/util/WeakPtrsMap.hpp diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 27833cd1..ae6c05f2 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 62d111ae..3f69dc44 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 c9f08203..f78fc6a8 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 20199f0a..55449ad9 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 19d6d232..a0fd4c83 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 2f24e0fe..bf61549e 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 07b42e22..feeb9c8f 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 0d206d65..00000000 --- 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 94324964..390ac16c 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 01648301..fd33eb9a 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -124,8 +124,6 @@ public: 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 d3dd2546..8a872eea 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; } -size_t ChunksStorage::size() const { - return chunksMap->size(); +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(); +} + +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 cda81bcb..9d8d0332 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 @@ public: 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 402c8e5f..a89dc2e6 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 00017cc6..cb98cb9d 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 d0518b5c..f7bbaa7f 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 812f6d91..0067964c 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 056/246] 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 bf61549e..c8499e71 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 057/246] test --- src/voxels/ChunksStorage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index 8a872eea..45c74fee 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 058/246] 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 45c74fee..8a872eea 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 059/246] 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 00000000..102557e8 --- /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 bc5bc04c..4e8ae348 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 ff94ade7..f272dbf0 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 060/246] 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 25432d30..a72489f0 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 9c1f7f96..de477e89 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 061/246] 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 ae6c05f2..c066c1ed 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 062/246] static link libstdc++ (mingw) --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 57f19747..a2415ad9 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 063/246] 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 f78fc6a8..3470cdb1 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 55449ad9..1a2d8315 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 f61a2755..0d8d97b4 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 c8499e71..dbd87489 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 747c68a4..68004777 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 8a872eea..379ca328 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 9d8d0332..b21e15e6 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 a89dc2e6..99f47d86 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 1f3ef183..63274672 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 @@ public: 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 0067964c..eb8c33bf 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 f36824b0..2d7ab923 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 ea23ed2e00c79539e5195c628c43d05fa1f9ee8b Mon Sep 17 00:00:00 2001 From: Stepanov Igor Date: Sun, 15 Dec 2024 01:55:13 +0300 Subject: [PATCH 064/246] add cmake presets --- CMakePresets.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 CMakePresets.json diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 00000000..61c1ab30 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,17 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "windows-default", + "condition": { + "type": "equals", + "rhs": "${hostSystemName}", + "lhs": "Windows" + }, + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + } + } + ] +} \ No newline at end of file From dccda9936482dd342ed1c91afd58dacf28ed3f24 Mon Sep 17 00:00:00 2001 From: Stepanov Igor Date: Sun, 15 Dec 2024 01:56:02 +0300 Subject: [PATCH 065/246] Fix usage vcpkg for windows and fix readme, remove vcpkg usage flag from cmake --- .gitignore | 4 ---- CMakeLists.txt | 41 ++++++++++------------------------------- README.md | 25 ++++++++++++++++++------- src/CMakeLists.txt | 33 +++++++++++++-------------------- 4 files changed, 41 insertions(+), 62 deletions(-) diff --git a/.gitignore b/.gitignore index a1ce6e33..9056cce5 100644 --- a/.gitignore +++ b/.gitignore @@ -36,10 +36,6 @@ Debug/voxel_engine AppDir appimage-build/ -# for vcpkg -/vcpkg/ -.gitmodules - # macOS folder attributes *.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index a2415ad9..c357b46a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,8 @@ -option(VOXELENGINE_BUILD_WINDOWS_VCPKG ON) -if(VOXELENGINE_BUILD_WINDOWS_VCPKG AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake") - set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake" CACHE STRING "") -endif() - cmake_minimum_required(VERSION 3.15) project(VoxelEngine) -option(VOXELENGINE_BUILD_APPDIR OFF) -option(VOXELENGINE_BUILD_TESTS OFF) +option(VOXELENGINE_BUILD_APPDIR "" OFF) +option(VOXELENGINE_BUILD_TESTS "" OFF) set(CMAKE_CXX_STANDARD 17) @@ -44,30 +39,14 @@ else() endif() endif() -if(VOXELENGINE_BUILD_WINDOWS_VCPKG AND WIN32) - if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/.git") - find_package(Git QUIET) - if(GIT_FOUND) - message(STATUS "Adding vcpkg as a git submodule...") - execute_process(COMMAND ${GIT_EXECUTABLE} submodule add https://github.com/microsoft/vcpkg.git vcpkg WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - else() - message(FATAL_ERROR "Git not found, cannot add vcpkg submodule.") - endif() - endif() - - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/.git") - message(STATUS "Initializing and updating vcpkg submodule...") - execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - execute_process(COMMAND ${CMAKE_COMMAND} -E chdir vcpkg ./bootstrap-vcpkg.bat WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - endif() - - foreach(CONFIG_TYPE ${CMAKE_CONFIGURATION_TYPES}) - string(TOUPPER ${CONFIG_TYPE} CONFIG_TYPE_UPPER) - add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_SOURCE_DIR}/res ${CMAKE_BINARY_DIR}/${CONFIG_TYPE_UPPER}/res) - endforeach() -endif() +# if(VOXELENGINE_BUILD_WINDOWS_VCPKG AND WIN32) +# foreach(CONFIG_TYPE ${CMAKE_CONFIGURATION_TYPES}) +# string(TOUPPER ${CONFIG_TYPE} CONFIG_TYPE_UPPER) +# add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD +# COMMAND ${CMAKE_COMMAND} -E copy_directory +# ${CMAKE_SOURCE_DIR}/res ${CMAKE_BINARY_DIR}/${CONFIG_TYPE_UPPER}/res) +# endforeach() +# endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie -lstdc++fs") diff --git a/README.md b/README.md index 418d372c..e7f67193 100644 --- a/README.md +++ b/README.md @@ -108,17 +108,28 @@ cmake --build . >[!NOTE] > Requirement: > -> vcpkg, CMake +> vcpkg, CMake, Git +If you want use vcpkg, install vcpkg from git to you system: +```PowerShell +cd C:/ +git clone https://github.com/microsoft/vcpkg.git +cd vcpkg +.\bootstrap-vcpkg.bat +``` +After installing vcpkg, setup env variable VCPKG_ROOT and add it to PATH: +```PowerShell +$env:VCPKG_ROOT = "C:\path\to\vcpkg" +$env:PATH = "$env:VCPKG_ROOT;$env:PATH" +``` +For troubleshooting you can read full [documentation](https://learn.microsoft.com/ru-ru/vcpkg/get_started/get-started?pivots=shell-powershell) for vcpkg -```sh +After installing vcpkg you can build project: +```PowerShell git clone --recursive https://github.com/MihailRis/VoxelEngine-Cpp.git cd VoxelEngine-Cpp mkdir build cd build -cmake -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_WINDOWS_VCPKG=ON .. -del CMakeCache.txt -rmdir /s /q CMakeFiles -cmake -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_WINDOWS_VCPKG=ON .. +cmake -DCMAKE_BUILD_TYPE=Release .. cmake --build . --config Release ``` @@ -128,7 +139,7 @@ cmake --build . --config Release > [!WARNING] > If you have issues during the vcpkg integration, try navigate to ```vcpkg\downloads``` > and extract PowerShell-[version]-win-x86 to ```vcpkg\downloads\tools``` as powershell-core-[version]-windows. -> Then rerun ```cmake -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_WINDOWS_VCPKG=ON ..``` +> Then rerun ```cmake -DCMAKE_BUILD_TYPE=Release ..``` ## Build using Docker diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4e8ae348..1310bf28 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,8 +8,6 @@ list(REMOVE_ITEM SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp) add_library(${PROJECT_NAME} ${SOURCES} ${HEADERS}) -option(VOXELENGINE_BUILD_WINDOWS_VCPKG ON) - find_package(OpenGL REQUIRED) find_package(GLEW REQUIRED) find_package(OpenAL REQUIRED) @@ -22,26 +20,21 @@ endif() set(LIBS "") -if (WIN32) - if(VOXELENGINE_BUILD_WINDOWS_VCPKG) - 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) - set(VORBISLIB Vorbis::vorbis Vorbis::vorbisfile) +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + if (MSVC) + set(LUA_LIBRARIES "${CMAKE_SOURCE_DIR}/vcpkg/packages/luajit_x64-windows/lib/lua51.lib") + set(LUA_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/vcpkg/packages/luajit_x64-windows/include/luajit") else() - find_package(Lua REQUIRED) - set(VORBISLIB vorbis vorbisfile) # not tested - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/libs/glfw) + 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(STATUS ${OPENAL_LIBRARY}) endif() + find_package(glfw3 REQUIRED) + find_package(glm REQUIRED) + find_package(vorbis REQUIRED) + set(VORBISLIB Vorbis::vorbis Vorbis::vorbisfile) + elseif(APPLE) find_package(PkgConfig) pkg_check_modules(LUAJIT REQUIRED luajit) From 7547152ab2c626adaffc024154af4ef418dbc8c0 Mon Sep 17 00:00:00 2001 From: Stepanov Igor Date: Sun, 15 Dec 2024 02:17:27 +0300 Subject: [PATCH 066/246] Fix resources linking --- CMakeLists.txt | 24 +++++++++++++++--------- src/CMakeLists.txt | 4 ++-- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c357b46a..0ec3fa62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,14 +39,14 @@ else() endif() endif() -# if(VOXELENGINE_BUILD_WINDOWS_VCPKG AND WIN32) -# foreach(CONFIG_TYPE ${CMAKE_CONFIGURATION_TYPES}) -# string(TOUPPER ${CONFIG_TYPE} CONFIG_TYPE_UPPER) -# add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD -# COMMAND ${CMAKE_COMMAND} -E copy_directory -# ${CMAKE_SOURCE_DIR}/res ${CMAKE_BINARY_DIR}/${CONFIG_TYPE_UPPER}/res) -# endforeach() -# endif() +if(VOXELENGINE_BUILD_WINDOWS_VCPKG AND WIN32) + foreach(CONFIG_TYPE ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${CONFIG_TYPE} CONFIG_TYPE_UPPER) + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_SOURCE_DIR}/res ${CMAKE_BINARY_DIR}/${CONFIG_TYPE_UPPER}/res) + endforeach() +endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie -lstdc++fs") @@ -58,7 +58,13 @@ endif() target_link_libraries(${PROJECT_NAME} VoxelEngineSrc ${CMAKE_DL_LIBS}) -file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/res DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +add_custom_command( + TARGET ${PROJECT_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/res + $/res + ) if (VOXELENGINE_BUILD_TESTS) enable_testing() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1310bf28..b4fba12c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,8 +22,8 @@ set(LIBS "") if (CMAKE_SYSTEM_NAME STREQUAL "Windows") if (MSVC) - set(LUA_LIBRARIES "${CMAKE_SOURCE_DIR}/vcpkg/packages/luajit_x64-windows/lib/lua51.lib") - set(LUA_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/vcpkg/packages/luajit_x64-windows/include/luajit") + set(LUA_LIBRARIES "$ENV{VCPKG_ROOT}/packages/luajit_x64-windows/lib/lua51.lib") + set(LUA_INCLUDE_DIR "$ENV{VCPKG_ROOT}/packages/luajit_x64-windows/include/luajit") else() find_package(PkgConfig) pkg_check_modules(OpenAL REQUIRED IMPORTED_TARGET openal) From 326630ef1d14c01b300e1265c68d77aa591fd922 Mon Sep 17 00:00:00 2001 From: Stepanov Igor Date: Sun, 15 Dec 2024 02:29:59 +0300 Subject: [PATCH 067/246] Check config working --- .github/workflows/windows-clang.yml | 7 ++++--- CMakePresets.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/windows-clang.yml b/.github/workflows/windows-clang.yml index 102557e8..b224d098 100644 --- a/.github/workflows/windows-clang.yml +++ b/.github/workflows/windows-clang.yml @@ -1,8 +1,8 @@ name: Windows Build (CLang) on: - push: - branches: [ "main", "release-**"] + # push: + # branches: [ "main", "release-**"] pull_request: branches: [ "main" ] @@ -43,11 +43,12 @@ jobs: - name: Configure project with CMake and vcpkg shell: msys2 {0} run: | + export VCPKG_ROOT=./vcpkg 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 --preset default-windows -G "MinGW Makefiles" -DVCPKG_TARGET_TRIPLET=x64-mingw-static -DCMAKE_BUILD_TYPE=Release .. cmake --build . --config Release - name: Package for Windows run: | diff --git a/CMakePresets.json b/CMakePresets.json index 61c1ab30..7c5a3d37 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -2,7 +2,7 @@ "version": 6, "configurePresets": [ { - "name": "windows-default", + "name": "default-windows", "condition": { "type": "equals", "rhs": "${hostSystemName}", From 9c53cd329600743267ae9a89d4b65730693cbc79 Mon Sep 17 00:00:00 2001 From: Stepanov Igor Date: Sun, 15 Dec 2024 02:32:02 +0300 Subject: [PATCH 068/246] undo --- .github/workflows/windows-clang.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows-clang.yml b/.github/workflows/windows-clang.yml index b224d098..89fc272d 100644 --- a/.github/workflows/windows-clang.yml +++ b/.github/workflows/windows-clang.yml @@ -1,8 +1,8 @@ name: Windows Build (CLang) on: - # push: - # branches: [ "main", "release-**"] + push: + branches: [ "main", "release-**"] pull_request: branches: [ "main" ] From dd09e3fb1ca134f613a4b0f385c75ed730a5ced4 Mon Sep 17 00:00:00 2001 From: Stepanov Igor Date: Sun, 15 Dec 2024 02:43:25 +0300 Subject: [PATCH 069/246] Cleanup --- CMakeLists.txt | 9 --------- 1 file changed, 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ec3fa62..7f38ff19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,15 +39,6 @@ else() endif() endif() -if(VOXELENGINE_BUILD_WINDOWS_VCPKG AND WIN32) - foreach(CONFIG_TYPE ${CMAKE_CONFIGURATION_TYPES}) - string(TOUPPER ${CONFIG_TYPE} CONFIG_TYPE_UPPER) - add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_SOURCE_DIR}/res ${CMAKE_BINARY_DIR}/${CONFIG_TYPE_UPPER}/res) - endforeach() -endif() - if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie -lstdc++fs") endif() From 5bf56d1f649d0a5d03945d1e720fcee859d56039 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 15 Dec 2024 18:20:34 +0300 Subject: [PATCH 070/246] 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 a72489f0..25ae572f 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 3efb909c..24832af9 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 102557e8..183f2d6d 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 de477e89..d417197c 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: From 6157f8193d94d6488c0e57df26d6ff1e2eb658ad Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Dec 2024 11:26:57 +0300 Subject: [PATCH 071/246] add blocks_agent templates & remove block.get_slow --- dev/tests/example.lua | 7 +- src/constants.hpp | 5 ++ src/logic/scripting/lua/libs/libblock.cpp | 56 +++++++-------- src/logic/scripting/lua/libs/libcore.cpp | 5 +- src/voxels/Chunks.cpp | 30 ++------ src/voxels/Chunks.hpp | 4 ++ src/voxels/GlobalChunks.cpp | 83 ++++++++++++----------- src/voxels/GlobalChunks.hpp | 14 +++- src/voxels/blocks_agent.hpp | 58 ++++++++++++++++ 9 files changed, 158 insertions(+), 104 deletions(-) create mode 100644 src/voxels/blocks_agent.hpp diff --git a/dev/tests/example.lua b/dev/tests/example.lua index c066c1ed..3dbe1197 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -8,11 +8,8 @@ assert(player.get_name(pid) == "Xerxes") test.sleep_until(function() return world.count_chunks() >= 9 end, 1000) print(world.count_chunks()) -for i=1,3 do - print("---") - timeit(1000000, block.get, 0, 0, 0) - timeit(1000000, block.get_slow, 0, 0, 0) -end +timeit(10000000, block.get, 0, 0, 0) +timeit(10000000, core.blank, 0, 0, 0) block.destruct(0, 0, 0, pid) assert(block.get(0, 0, 0) == 0) diff --git a/src/constants.hpp b/src/constants.hpp index c8de5a8d..6f4a6ce6 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -35,6 +35,11 @@ inline constexpr int CHUNK_D = 16; inline constexpr uint VOXEL_USER_BITS = 8; inline constexpr uint VOXEL_USER_BITS_OFFSET = sizeof(blockstate_t)*8-VOXEL_USER_BITS; +/// @brief % unordered map max average buckets load factor. +/// Low value gives significant performance impact by minimizing collisions and +/// lookup latency. Default value (1.0) shows x2 slower work. +inline constexpr float CHUNKS_MAP_MAX_LOAD_FACTOR = 0.1f; + /// @brief chunk volume (count of voxels per Chunk) inline constexpr int CHUNK_VOL = (CHUNK_W * CHUNK_H * CHUNK_D); diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index dbd87489..5d681e5f 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -8,6 +8,7 @@ #include "voxels/Chunks.hpp" #include "voxels/voxel.hpp" #include "voxels/GlobalChunks.hpp" +#include "voxels/blocks_agent.hpp" #include "world/Level.hpp" #include "maths/voxmaths.hpp" #include "data/StructLayout.hpp" @@ -15,13 +16,13 @@ using namespace scripting; -static const Block* require_block(lua::State* L) { +static inline const Block* require_block(lua::State* L) { auto indices = content->getIndices(); auto id = lua::tointeger(L, 1); return indices->blocks.get(id); } -static int l_get_def(lua::State* L) { +static inline int l_get_def(lua::State* L) { if (auto def = require_block(L)) { return lua::pushstring(L, def->name); } @@ -39,8 +40,7 @@ static int l_is_solid_at(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - - return lua::pushboolean(L, level->chunks->isSolidBlock(x, y, z)); + return lua::pushboolean(L, blocks_agent::is_solid_at(*level->chunks, x, y, z)); } static int l_count(lua::State* L) { @@ -110,16 +110,7 @@ static int l_get(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); - int id = vox == nullptr ? -1 : vox->id; - 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); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); int id = vox == nullptr ? -1 : vox->id; return lua::pushinteger(L, id); } @@ -128,7 +119,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->chunksStorage->get(x, y, z); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, glm::ivec3(1, 0, 0)); } @@ -145,7 +136,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->chunksStorage->get(x, y, z); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, glm::ivec3(0, 1, 0)); } @@ -162,7 +153,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->chunksStorage->get(x, y, z); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, glm::ivec3(0, 0, 1)); } @@ -179,7 +170,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->chunksStorage->get(x, y, z); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); int rotation = vox == nullptr ? 0 : vox->state.rotation; return lua::pushinteger(L, rotation); } @@ -197,7 +188,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->chunksStorage->get(x, y, z); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); int states = vox == nullptr ? 0 : blockstate2int(vox->state); return lua::pushinteger(L, states); } @@ -226,7 +217,7 @@ static int l_get_user_bits(lua::State* L) { auto offset = lua::tointeger(L, 4) + VOXEL_USER_BITS_OFFSET; auto bits = lua::tointeger(L, 5); - auto vox = level->chunks->get(x, y, z); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); if (vox == nullptr) { return lua::pushinteger(L, 0); } @@ -352,7 +343,7 @@ static int l_place(lua::State* L) { if (static_cast(id) >= indices->blocks.count()) { return 0; } - if (!level->chunks->get(x, y, z)) { + if (!blocks_agent::get(*level->chunksStorage, x, y, z)) { return 0; } const auto def = level->content->getIndices()->blocks.get(id); @@ -373,11 +364,11 @@ static int l_destruct(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); auto playerid = lua::gettop(L) >= 4 ? lua::tointeger(L, 4) : -1; - auto voxel = level->chunks->get(x, y, z); - if (voxel == nullptr) { + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + if (vox == nullptr) { return 0; } - auto& def = level->content->getIndices()->blocks.require(voxel->id); + auto& def = level->content->getIndices()->blocks.require(vox->id); auto player = level->players->get(playerid); controller->getBlocksController()->breakBlock(player, def, x, y, z); return 0; @@ -504,12 +495,15 @@ static int l_get_field(lua::State* L) { } auto cx = floordiv(x, CHUNK_W); auto cz = floordiv(z, CHUNK_D); - auto chunk = level->chunks->getChunk(cx, cz); + auto chunk = blocks_agent::get_chunk(*level->chunksStorage, cx, cz); + if (chunk == nullptr || y < 0 || y >= CHUNK_H) { + return 0; + } auto lx = x - cx * CHUNK_W; auto lz = z - cz * CHUNK_W; size_t voxelIndex = vox_index(lx, y, lz); - const auto& vox = level->chunks->require(x, y, z); + const auto& vox = chunk->voxels[voxelIndex]; const auto& def = content->getIndices()->blocks.require(vox.id); if (def.dataStruct == nullptr) { return 0; @@ -568,15 +562,18 @@ static int l_set_field(lua::State* L) { if (lua::gettop(L) >= 6) { index = lua::tointeger(L, 6); } - auto vox = level->chunks->get(x, y, z); auto cx = floordiv(x, CHUNK_W); auto cz = floordiv(z, CHUNK_D); - auto chunk = level->chunks->getChunk(cx, cz); auto lx = x - cx * CHUNK_W; auto lz = z - cz * CHUNK_W; + auto chunk = blocks_agent::get_chunk(*level->chunksStorage, cx, cz); + if (chunk == nullptr || y < 0 || y >= CHUNK_H) { + return 0; + } size_t voxelIndex = vox_index(lx, y, lz); + const auto& vox = chunk->voxels[voxelIndex]; - const auto& def = content->getIndices()->blocks.require(vox->id); + const auto& def = content->getIndices()->blocks.require(vox.id); if (def.dataStruct == nullptr) { return 0; } @@ -608,7 +605,6 @@ 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/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp index 5ac9d93d..d2d0c80f 100644 --- a/src/logic/scripting/lua/libs/libcore.cpp +++ b/src/logic/scripting/lua/libs/libcore.cpp @@ -16,6 +16,7 @@ #include "logic/EngineController.hpp" #include "logic/LevelController.hpp" #include "util/listutil.hpp" +#include "util/platform.hpp" #include "window/Events.hpp" #include "window/Window.hpp" #include "world/Level.hpp" @@ -220,8 +221,6 @@ static int l_load_texture(lua::State* L) { return 0; } -#include "util/platform.hpp" - static int l_open_folder(lua::State* L) { auto path = engine->getPaths()->resolve(lua::require_string(L, 1)); platform::open_folder(path); @@ -239,7 +238,7 @@ static int l_blank(lua::State*) { } const luaL_Reg corelib[] = { - {"nop", lua::wrap}, + {"blank", lua::wrap}, {"get_version", lua::wrap}, {"new_world", lua::wrap}, {"open_world", lua::wrap}, diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 390ac16c..8bfe6166 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -23,6 +23,7 @@ #include "Block.hpp" #include "Chunk.hpp" #include "voxel.hpp" +#include "blocks_agent.hpp" Chunks::Chunks( int32_t w, @@ -33,7 +34,7 @@ Chunks::Chunks( Level* level ) : level(level), - indices(level->content->getIndices()), + indices(level ? level->content->getIndices() : nullptr), areaMap(w, d), worldFiles(wfile) { areaMap.setCenter(ox-w/2, oz-d/2); @@ -43,30 +44,11 @@ Chunks::Chunks( } 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); - int cz = floordiv(z); - auto ptr = areaMap.getIf(cx, cz); - if (ptr == nullptr) { - return nullptr; - } - Chunk* chunk = ptr->get(); - if (chunk == nullptr) { - return nullptr; - } - int lx = x - cx * CHUNK_W; - int lz = z - cz * CHUNK_D; - return &chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; + return blocks_agent::get(*this, x, y, z); } voxel& Chunks::require(int32_t x, int32_t y, int32_t z) const { - auto voxel = get(x, y, z); - if (voxel == nullptr) { - throw std::runtime_error("voxel does not exist"); - } - return *voxel; + return blocks_agent::require(*this, x, y, z); } const AABB* Chunks::isObstacleAt(float x, float y, float z) const { @@ -103,9 +85,7 @@ 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.require(v->id).rt.solid; + return blocks_agent::is_solid_at(*this, x, y, z); } bool Chunks::isReplaceableBlock(int32_t x, int32_t y, int32_t z) { diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index fd33eb9a..a39c8960 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -152,4 +152,8 @@ public: size_t getVolume() const { return areaMap.area(); } + + const ContentIndices& getContentIndices() const { + return *indices; + } }; diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp index 379ca328..38843398 100644 --- a/src/voxels/GlobalChunks.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -16,10 +16,10 @@ #include "Block.hpp" #include "Chunk.hpp" -inline long long keyfrom(int x, int z) { +inline uint64_t keyfrom(int32_t x, int32_t z) { union { - int pos[2]; - long long key; + int32_t pos[2]; + uint64_t key; } ekey; ekey.pos[0] = x; ekey.pos[1] = z; @@ -28,7 +28,9 @@ inline long long keyfrom(int x, int z) { static debug::Logger logger("chunks-storage"); -GlobalChunks::GlobalChunks(Level* level) : level(level) { +GlobalChunks::GlobalChunks(Level* level) + : level(level), indices(level ? level->content->getIndices() : nullptr) { + chunksMap.max_load_factor(CHUNKS_MAP_MAX_LOAD_FACTOR); } std::shared_ptr GlobalChunks::fetch(int x, int z) { @@ -67,6 +69,29 @@ void GlobalChunks::erase(int x, int z) { chunksMap.erase(keyfrom(x, z)); } +static inline auto load_inventories( + WorldRegions& regions, + const Chunk& chunk, + const ContentUnitIndices& defs +) { + auto invs = regions.fetchInventories(chunk.x, chunk.z); + auto iterator = invs.begin(); + while (iterator != invs.end()) { + uint index = iterator->first; + const auto& def = defs.require(chunk.voxels[index].id); + if (def.inventorySize == 0) { + iterator = invs.erase(iterator); + continue; + } + auto& inventory = iterator->second; + if (def.inventorySize != inventory->size()) { + inventory->resize(def.inventorySize); + } + ++iterator; + } + return invs; +} + std::shared_ptr GlobalChunks::create(int x, int z) { const auto& found = chunksMap.find(keyfrom(x, z)); if (found != chunksMap.end()) { @@ -84,22 +109,10 @@ std::shared_ptr GlobalChunks::create(int x, int z) { chunk->decode(data.get()); check_voxels(indices, *chunk); - auto invs = regions.fetchInventories(chunk->x, chunk->z); - auto iterator = invs.begin(); - while (iterator != invs.end()) { - uint index = iterator->first; - const auto& def = indices.blocks.require(chunk->voxels[index].id); - if (def.inventorySize == 0) { - iterator = invs.erase(iterator); - continue; - } - auto& inventory = iterator->second; - if (def.inventorySize != inventory->size()) { - inventory->resize(def.inventorySize); - } - ++iterator; - } - chunk->setBlockInventories(std::move(invs)); + + chunk->setBlockInventories( + load_inventories(regions, *chunk, indices.blocks) + ); auto entitiesData = regions.fetchEntities(chunk->x, chunk->z); if (entitiesData.getType() == dv::value_type::object) { @@ -132,24 +145,6 @@ size_t GlobalChunks::size() const { return chunksMap.size(); } -voxel* GlobalChunks::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 GlobalChunks::incref(Chunk* chunk) { auto key = reinterpret_cast(chunk); const auto& found = refCounters.find(key); @@ -209,3 +204,15 @@ void GlobalChunks::saveAll() { save(chunk.get()); } } + +void GlobalChunks::putChunk(std::shared_ptr chunk) { + chunksMap[keyfrom(chunk->x, chunk->z)] = std::move(chunk); +} + +Chunk* GlobalChunks::getChunk(int cx, int cz) const { + const auto& found = chunksMap.find(keyfrom(cx, cz)); + if (found == chunksMap.end()) { + return nullptr; + } + return found->second.get(); +} diff --git a/src/voxels/GlobalChunks.hpp b/src/voxels/GlobalChunks.hpp index b21e15e6..f7ce0e92 100644 --- a/src/voxels/GlobalChunks.hpp +++ b/src/voxels/GlobalChunks.hpp @@ -11,10 +11,12 @@ class Chunk; class Level; +class ContentIndices; class GlobalChunks { Level* level; - std::unordered_map> chunksMap; + const ContentIndices* indices; + std::unordered_map> chunksMap; std::unordered_map> pinnedChunks; std::unordered_map refCounters; public: @@ -27,8 +29,6 @@ public: 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); @@ -38,4 +38,12 @@ public: void save(Chunk* chunk); void saveAll(); + + void putChunk(std::shared_ptr chunk); + + Chunk* getChunk(int cx, int cz) const; + + const ContentIndices& getContentIndices() const { + return *indices; + } }; diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp new file mode 100644 index 00000000..31ae79c5 --- /dev/null +++ b/src/voxels/blocks_agent.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include "voxel.hpp" +#include "typedefs.hpp" +#include "maths/voxmaths.hpp" + +class Chunk; +class Chunks; +class GlobalChunks; + +/// Using templates to minimize OOP overhead + +namespace blocks_agent { + +template +inline Chunk* get_chunk(const Storage& chunks, int cx, int cz) { + return chunks.getChunk(cx, cz); +} + +template +inline voxel* get(const Storage& chunks, int32_t x, int32_t y, int32_t z) { + if (y < 0 || y >= CHUNK_H) { + return nullptr; + } + int cx = floordiv(x); + int cz = floordiv(z); + Chunk* chunk = get_chunk(chunks, cx, cz); + if (chunk == nullptr) { + return nullptr; + } + int lx = x - cx * CHUNK_W; + int lz = z - cz * CHUNK_D; + return &chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; +} + +template +inline voxel& require(const Storage& chunks, int32_t x, int32_t y, int32_t z) { + auto vox = get(chunks, x, y, z); + if (vox == nullptr) { + throw std::runtime_error("voxel does not exist"); + } + return *vox; +} + +template +inline const Block& get_block_def(const Storage& chunks, blockid_t id) { + return chunks.getContentIndices().blocks.require(id); +} + +template +inline bool is_solid_at(const Storage& chunks, int32_t x, int32_t y, int32_t z) { + if (auto vox = get(chunks, x, y, z)) { + return get_block_def(chunks, vox->id).rt.solid; + } + return false; +} + +} // blocks_agent From 87c19d69dc07b094669958ec6af6d53696cb28f6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Dec 2024 20:46:06 +0300 Subject: [PATCH 072/246] move block set logic to the blocks_agent --- src/logic/scripting/lua/libs/libblock.cpp | 6 +- src/voxels/Chunks.cpp | 111 ++-------------------- src/voxels/GlobalChunks.cpp | 18 ---- src/voxels/GlobalChunks.hpp | 18 +++- src/voxels/blocks_agent.cpp | 95 ++++++++++++++++++ src/voxels/blocks_agent.hpp | 87 ++++++++++++++++- src/world/generator/WorldGenerator.cpp | 8 +- 7 files changed, 210 insertions(+), 133 deletions(-) create mode 100644 src/voxels/blocks_agent.cpp diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 5d681e5f..e454903c 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -98,7 +98,7 @@ static int l_set(lua::State* L) { if (!level->chunks->get(x, y, z)) { return 0; } - level->chunks->set(x, y, z, id, int2blockstate(state)); + blocks_agent::set(*level->chunksStorage, x, y, z, id, int2blockstate(state)); level->lighting->onBlockSet(x, y, z, id); if (!noupdate) { blocks->updateSides(x, y, z); @@ -269,7 +269,9 @@ static int l_is_replaceable_at(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - return lua::pushboolean(L, level->chunks->isReplaceableBlock(x, y, z)); + return lua::pushboolean( + L, blocks_agent::is_replaceable_at(*level->chunksStorage, x, y, z) + ); } static int l_caption(lua::State* L) { diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 8bfe6166..50812d07 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -89,9 +89,7 @@ bool Chunks::isSolidBlock(int32_t x, int32_t y, int32_t z) { } 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.require(v->id).replaceable; + return blocks_agent::is_replaceable_at(*this, x, y, z); } bool Chunks::isObstacleBlock(int32_t x, int32_t y, int32_t z) { @@ -184,50 +182,13 @@ glm::ivec3 Chunks::seekOrigin( void Chunks::eraseSegments( const Block& def, blockstate state, int x, int y, int z ) { - const auto& rotation = def.rotations.variants[state.rotation]; - for (int sy = 0; sy < def.size.y; sy++) { - for (int sz = 0; sz < def.size.z; sz++) { - for (int sx = 0; sx < def.size.x; sx++) { - if ((sx | sy | sz) == 0) { - continue; - } - glm::ivec3 pos(x, y, z); - pos += rotation.axisX * sx; - pos += rotation.axisY * sy; - pos += rotation.axisZ * sz; - set(pos.x, pos.y, pos.z, 0, {}); - } - } - } -} - -static constexpr uint8_t segment_to_int(int sx, int sy, int sz) { - return ((sx > 0) | ((sy > 0) << 1) | ((sz > 0) << 2)); + blocks_agent::erase_segments(*this, def, state, x, y, z); } void Chunks::repairSegments( const Block& def, blockstate state, int x, int y, int z ) { - const auto& rotation = def.rotations.variants[state.rotation]; - const auto id = def.rt.id; - const auto size = def.size; - for (int sy = 0; sy < size.y; sy++) { - for (int sz = 0; sz < size.z; sz++) { - for (int sx = 0; sx < size.x; sx++) { - if ((sx | sy | sz) == 0) { - continue; - } - blockstate segState = state; - segState.segment = segment_to_int(sx, sy, sz); - - glm::ivec3 pos(x, y, z); - pos += rotation.axisX * sx; - pos += rotation.axisY * sy; - pos += rotation.axisZ * sz; - set(pos.x, pos.y, pos.z, id, segState); - } - } - } + blocks_agent::repair_segments(*this, def, state, x, y, z); } bool Chunks::checkReplaceability( @@ -283,7 +244,7 @@ void Chunks::setRotationExtended( pos += rotation.axisZ * sz; blockstate segState = newstate; - segState.segment = segment_to_int(sx, sy, sz); + segState.segment = blocks_agent::segment_to_int(sx, sy, sz); auto vox = get(pos); // checked for nullptr by checkReplaceability @@ -344,68 +305,7 @@ void Chunks::setRotation(int32_t x, int32_t y, int32_t z, uint8_t index) { void Chunks::set( int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state ) { - if (y < 0 || y >= CHUNK_H) { - return; - } - int cx = floordiv(x); - int cz = floordiv(z); - auto ptr = areaMap.getIf(cx, cz); - if (ptr == nullptr) { - return; - } - Chunk* chunk = ptr->get(); - if (chunk == nullptr) { - return; - } - int lx = x - cx * CHUNK_W; - int lz = z - cz * CHUNK_D; - size_t index = vox_index(lx, y, lz); - - // block finalization - voxel& vox = chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; - const auto& prevdef = indices->blocks.require(vox.id); - if (prevdef.inventorySize != 0) { - chunk->removeBlockInventory(lx, y, lz); - } - if (prevdef.rt.extended && !vox.state.segment) { - eraseSegments(prevdef, vox.state, x, y, z); - } - if (prevdef.dataStruct) { - if (auto found = chunk->blocksMetadata.find(index)) { - chunk->blocksMetadata.free(found); - chunk->flags.unsaved = true; - chunk->flags.blocksData = true; - } - } - - // block initialization - const auto& newdef = indices->blocks.require(id); - vox.id = id; - vox.state = state; - chunk->setModifiedAndUnsaved(); - if (!state.segment && newdef.rt.extended) { - repairSegments(newdef, state, x, y, z); - } - - if (y < chunk->bottom) - chunk->bottom = y; - else if (y + 1 > chunk->top) - chunk->top = y + 1; - else if (id == 0) - chunk->updateHeights(); - - if (lx == 0 && (chunk = getChunk(cx - 1, cz))) { - chunk->flags.modified = true; - } - if (lz == 0 && (chunk = getChunk(cx, cz - 1))) { - chunk->flags.modified = true; - } - if (lx == CHUNK_W - 1 && (chunk = getChunk(cx + 1, cz))) { - chunk->flags.modified = true; - } - if (lz == CHUNK_D - 1 && (chunk = getChunk(cx, cz + 1))) { - chunk->flags.modified = true; - } + blocks_agent::set(*this, x, y, z, id, state); } voxel* Chunks::rayCast( @@ -652,6 +552,7 @@ void Chunks::resize(uint32_t newW, uint32_t newD) { bool Chunks::putChunk(const std::shared_ptr& chunk) { if (areaMap.set(chunk->x, chunk->z, chunk)) { + if (level) level->events->trigger(LevelEventType::EVT_CHUNK_SHOWN, chunk.get()); return true; } diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp index 38843398..98a2d40a 100644 --- a/src/voxels/GlobalChunks.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -16,16 +16,6 @@ #include "Block.hpp" #include "Chunk.hpp" -inline uint64_t keyfrom(int32_t x, int32_t z) { - union { - int32_t pos[2]; - uint64_t key; - } ekey; - ekey.pos[0] = x; - ekey.pos[1] = z; - return ekey.key; -} - static debug::Logger logger("chunks-storage"); GlobalChunks::GlobalChunks(Level* level) @@ -208,11 +198,3 @@ void GlobalChunks::saveAll() { void GlobalChunks::putChunk(std::shared_ptr chunk) { chunksMap[keyfrom(chunk->x, chunk->z)] = std::move(chunk); } - -Chunk* GlobalChunks::getChunk(int cx, int cz) const { - const auto& found = chunksMap.find(keyfrom(cx, cz)); - if (found == chunksMap.end()) { - return nullptr; - } - return found->second.get(); -} diff --git a/src/voxels/GlobalChunks.hpp b/src/voxels/GlobalChunks.hpp index f7ce0e92..afbdf67c 100644 --- a/src/voxels/GlobalChunks.hpp +++ b/src/voxels/GlobalChunks.hpp @@ -14,6 +14,16 @@ class Level; class ContentIndices; class GlobalChunks { + static inline uint64_t keyfrom(int32_t x, int32_t z) { + union { + int32_t pos[2]; + uint64_t key; + } ekey; + ekey.pos[0] = x; + ekey.pos[1] = z; + return ekey.key; + } + Level* level; const ContentIndices* indices; std::unordered_map> chunksMap; @@ -41,7 +51,13 @@ public: void putChunk(std::shared_ptr chunk); - Chunk* getChunk(int cx, int cz) const; + inline Chunk* getChunk(int cx, int cz) const { + const auto& found = chunksMap.find(keyfrom(cx, cz)); + if (found == chunksMap.end()) { + return nullptr; + } + return found->second.get(); + } const ContentIndices& getContentIndices() const { return *indices; diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp new file mode 100644 index 00000000..53cef38d --- /dev/null +++ b/src/voxels/blocks_agent.cpp @@ -0,0 +1,95 @@ +#include "blocks_agent.hpp" + +using namespace blocks_agent; + +template +static inline void set_block( + Storage& chunks, + int32_t x, + int32_t y, + int32_t z, + uint32_t id, + blockstate state +) { + if (y < 0 || y >= CHUNK_H) { + return; + } + const auto& indices = chunks.getContentIndices(); + int cx = floordiv(x); + int cz = floordiv(z); + Chunk* chunk = get_chunk(chunks, cx, cz); + if (chunk == nullptr) { + return; + } + int lx = x - cx * CHUNK_W; + int lz = z - cz * CHUNK_D; + size_t index = vox_index(lx, y, lz); + + // block finalization + voxel& vox = chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; + const auto& prevdef = indices.blocks.require(vox.id); + if (prevdef.inventorySize != 0) { + chunk->removeBlockInventory(lx, y, lz); + } + if (prevdef.rt.extended && !vox.state.segment) { + erase_segments(chunks, prevdef, vox.state, x, y, z); + } + if (prevdef.dataStruct) { + if (auto found = chunk->blocksMetadata.find(index)) { + chunk->blocksMetadata.free(found); + chunk->flags.unsaved = true; + chunk->flags.blocksData = true; + } + } + + // block initialization + const auto& newdef = indices.blocks.require(id); + vox.id = id; + vox.state = state; + chunk->setModifiedAndUnsaved(); + if (!state.segment && newdef.rt.extended) { + repair_segments(chunks, newdef, state, x, y, z); + } + + if (y < chunk->bottom) + chunk->bottom = y; + else if (y + 1 > chunk->top) + chunk->top = y + 1; + else if (id == 0) + chunk->updateHeights(); + + if (lx == 0 && (chunk = get_chunk(chunks, cx - 1, cz))) { + chunk->flags.modified = true; + } + if (lz == 0 && (chunk = get_chunk(chunks, cx, cz - 1))) { + chunk->flags.modified = true; + } + if (lx == CHUNK_W - 1 && (chunk = get_chunk(chunks, cx + 1, cz))) { + chunk->flags.modified = true; + } + if (lz == CHUNK_D - 1 && (chunk = get_chunk(chunks, cx, cz + 1))) { + chunk->flags.modified = true; + } +} + +void blocks_agent::set( + Chunks& chunks, + int32_t x, + int32_t y, + int32_t z, + uint32_t id, + blockstate state +) { + set_block(chunks, x, y, z, id, state); +} + +void blocks_agent::set( + GlobalChunks& chunks, + int32_t x, + int32_t y, + int32_t z, + uint32_t id, + blockstate state +) { + set_block(chunks, x, y, z, id, state); +} diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 31ae79c5..22850f40 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -1,12 +1,16 @@ #pragma once #include "voxel.hpp" +#include "Block.hpp" +#include "Chunk.hpp" +#include "Chunks.hpp" +#include "GlobalChunks.hpp" +#include "constants.hpp" #include "typedefs.hpp" +#include "content/Content.hpp" #include "maths/voxmaths.hpp" -class Chunk; -class Chunks; -class GlobalChunks; +#include /// Using templates to minimize OOP overhead @@ -55,4 +59,81 @@ inline bool is_solid_at(const Storage& chunks, int32_t x, int32_t y, int32_t z) return false; } +template +inline bool is_replaceable_at(const Storage& chunks, int32_t x, int32_t y, int32_t z) { + if (auto vox = get(chunks, x, y, z)) { + return get_block_def(chunks, vox->id).replaceable; + } + return false; +} + +void set( + Chunks& chunks, + int32_t x, + int32_t y, + int32_t z, + uint32_t id, + blockstate state +); + +void set( + GlobalChunks& chunks, + int32_t x, + int32_t y, + int32_t z, + uint32_t id, + blockstate state +); + +template +inline void erase_segments( + Storage& chunks, const Block& def, blockstate state, int x, int y, int z +) { + const auto& rotation = def.rotations.variants[state.rotation]; + for (int sy = 0; sy < def.size.y; sy++) { + for (int sz = 0; sz < def.size.z; sz++) { + for (int sx = 0; sx < def.size.x; sx++) { + if ((sx | sy | sz) == 0) { + continue; + } + glm::ivec3 pos(x, y, z); + pos += rotation.axisX * sx; + pos += rotation.axisY * sy; + pos += rotation.axisZ * sz; + set(chunks, pos.x, pos.y, pos.z, 0, {}); + } + } + } +} + +static constexpr uint8_t segment_to_int(int sx, int sy, int sz) { + return ((sx > 0) | ((sy > 0) << 1) | ((sz > 0) << 2)); +} + +template +inline void repair_segments( + Storage& chunks, const Block& def, blockstate state, int x, int y, int z +) { + const auto& rotation = def.rotations.variants[state.rotation]; + const auto id = def.rt.id; + const auto size = def.size; + for (int sy = 0; sy < size.y; sy++) { + for (int sz = 0; sz < size.z; sz++) { + for (int sx = 0; sx < size.x; sx++) { + if ((sx | sy | sz) == 0) { + continue; + } + blockstate segState = state; + segState.segment = segment_to_int(sx, sy, sz); + + glm::ivec3 pos(x, y, z); + pos += rotation.axisX * sx; + pos += rotation.axisY * sy; + pos += rotation.axisZ * sz; + set(chunks, pos.x, pos.y, pos.z, id, segState); + } + } + } +} + } // blocks_agent diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index bc851a07..281ac411 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -189,10 +189,10 @@ void WorldGenerator::placeLine(const LinePlacement& line, int priority) { aabb.fix(); aabb.a -= line.radius; aabb.b += line.radius; - int cxa = floordiv(aabb.a.x, CHUNK_W); - int cza = floordiv(aabb.a.z, CHUNK_D); - int cxb = floordiv(aabb.b.x, CHUNK_W); - int czb = floordiv(aabb.b.z, CHUNK_D); + int cxa = floordiv(aabb.a.x); + int cza = floordiv(aabb.a.z); + int cxb = floordiv(aabb.b.x); + int czb = floordiv(aabb.b.z); for (int cz = cza; cz <= czb; cz++) { for (int cx = cxa; cx <= cxb; cx++) { const auto& found = prototypes.find({cx, cz}); From c7c2fe29d2bb2f1fe89b9815cf013e239c99dbf0 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Dec 2024 22:37:55 +0300 Subject: [PATCH 073/246] move seekOrigin logic to the blocks_agent --- src/logic/scripting/lua/libs/libblock.cpp | 26 ++++++++++++++++------- src/voxels/Chunks.cpp | 18 +--------------- src/voxels/blocks_agent.hpp | 24 +++++++++++++++++++++ 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index e454903c..743c042b 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -70,7 +70,7 @@ static int l_is_segment(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - const auto& vox = level->chunks->require(x, y, z); + const auto& vox = blocks_agent::require(*level->chunksStorage, x, y, z); return lua::pushboolean(L, vox.state.segment); } @@ -78,10 +78,13 @@ static int l_seek_origin(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - const auto& vox = level->chunks->require(x, y, z); + const auto& vox = blocks_agent::require(*level->chunksStorage, x, y, z); auto& def = indices->blocks.require(vox.id); return lua::pushivec_stack( - L, level->chunks->seekOrigin({x, y, z}, def, vox.state) + L, + blocks_agent::seek_origin( + *level->chunksStorage, {x, y, z}, def, vox.state + ) ); } @@ -198,13 +201,18 @@ static int l_set_states(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); auto states = lua::tointeger(L, 4); - - auto chunk = level->chunks->getChunkByVoxel(x, y, z); + if (y < 0 || y >= CHUNK_H) { + return 0; + } + int cx = floordiv(x); + int cz = floordiv(z); + auto chunk = blocks_agent::get_chunk(*level->chunksStorage, cx, cz); if (chunk == nullptr) { return 0; } - auto vox = level->chunks->get(x, y, z); - vox->state = int2blockstate(states); + int lx = x - cx * CHUNK_W; + int lz = z - cz * CHUNK_D; + chunk->voxels[vox_index(lx, y, lz)].state = int2blockstate(states); chunk->setModifiedAndUnsaved(); return 0; } @@ -223,7 +231,9 @@ static int l_get_user_bits(lua::State* L) { } const auto& def = content->getIndices()->blocks.require(vox->id); if (def.rt.extended) { - auto origin = level->chunks->seekOrigin({x, y, z}, def, vox->state); + auto origin = blocks_agent::seek_origin( + *level->chunksStorage, {x, y, z}, def, vox->state + ); vox = level->chunks->get(origin.x, origin.y, origin.z); if (vox == nullptr) { return lua::pushinteger(L, 0); diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 50812d07..a56c3581 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -160,23 +160,7 @@ Chunk* Chunks::getChunk(int x, int z) const { glm::ivec3 Chunks::seekOrigin( const glm::ivec3& srcpos, const Block& def, blockstate state ) const { - auto pos = srcpos; - const auto& rotation = def.rotations.variants[state.rotation]; - auto segment = state.segment; - while (true) { - if (!segment) { - return pos; - } - if (segment & 1) pos -= rotation.axisX; - if (segment & 2) pos -= rotation.axisY; - if (segment & 4) pos -= rotation.axisZ; - - if (auto* voxel = get(pos.x, pos.y, pos.z)) { - segment = voxel->state.segment; - } else { - return pos; - } - } + return blocks_agent::seek_origin(*this, srcpos, def, state); } void Chunks::eraseSegments( diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 22850f40..3ce5b49b 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -11,6 +11,7 @@ #include "maths/voxmaths.hpp" #include +#include /// Using templates to minimize OOP overhead @@ -136,4 +137,27 @@ inline void repair_segments( } } +template +inline glm::ivec3 seek_origin( + Storage& chunks, const glm::ivec3& srcpos, const Block& def, blockstate state +) { + auto pos = srcpos; + const auto& rotation = def.rotations.variants[state.rotation]; + auto segment = state.segment; + while (true) { + if (!segment) { + return pos; + } + if (segment & 1) pos -= rotation.axisX; + if (segment & 2) pos -= rotation.axisY; + if (segment & 4) pos -= rotation.axisZ; + + if (auto* voxel = get(chunks, pos.x, pos.y, pos.z)) { + segment = voxel->state.segment; + } else { + return pos; + } + } +} + } // blocks_agent From fb3f201dfc2b9814c65d1fc6c02d5330d604f059 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Dec 2024 23:32:52 +0300 Subject: [PATCH 074/246] move setRotation, checkReplaceability, raycast logic to the blocks_agent --- src/logic/scripting/lua/libs/libblock.cpp | 39 ++-- src/voxels/Chunks.cpp | 229 +--------------------- src/voxels/blocks_agent.cpp | 172 ++++++++++++++++ src/voxels/blocks_agent.hpp | 155 +++++++++++++++ 4 files changed, 359 insertions(+), 236 deletions(-) diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 743c042b..c0bf1ef6 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -98,7 +98,9 @@ static int l_set(lua::State* L) { if (static_cast(id) >= indices->blocks.count()) { return 0; } - if (!level->chunks->get(x, y, z)) { + int cx = floordiv(x); + int cz = floordiv(z); + if (!blocks_agent::get_chunk(*level->chunksStorage, cx, cz)) { return 0; } blocks_agent::set(*level->chunksStorage, x, y, z, id, int2blockstate(state)); @@ -183,7 +185,7 @@ static int l_set_rotation(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); auto value = lua::tointeger(L, 4); - level->chunks->setRotation(x, y, z, value); + blocks_agent::set_rotation(*level->chunksStorage, x, y, z, value); return 0; } @@ -234,7 +236,9 @@ static int l_get_user_bits(lua::State* L) { auto origin = blocks_agent::seek_origin( *level->chunksStorage, {x, y, z}, def, vox->state ); - vox = level->chunks->get(origin.x, origin.y, origin.z); + vox = blocks_agent::get( + *level->chunksStorage, origin.x, origin.y, origin.z + ); if (vox == nullptr) { return lua::pushinteger(L, 0); } @@ -245,6 +249,7 @@ static int l_get_user_bits(lua::State* L) { } static int l_set_user_bits(lua::State* L) { + auto& chunks = *level->chunksStorage; auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); @@ -254,18 +259,19 @@ static int l_set_user_bits(lua::State* L) { size_t mask = ((1 << bits) - 1) << offset; auto value = (lua::tointeger(L, 6) << offset) & mask; - auto chunk = level->chunks->getChunkByVoxel(x, y, z); - if (chunk == nullptr) { - return 0; - } - auto vox = level->chunks->get(x, y, z); - if (vox == nullptr) { + int cx = floordiv(x); + int cz = floordiv(z); + auto chunk = blocks_agent::get_chunk(chunks, cx, cz); + if (chunk == nullptr || y < 0 || y >= CHUNK_H) { return 0; } + int lx = x - cx * CHUNK_W; + int lz = z - cz * CHUNK_D; + auto vox = &chunk->voxels[vox_index(lx, y, lz)]; const auto& def = content->getIndices()->blocks.require(vox->id); if (def.rt.extended) { - auto origin = level->chunks->seekOrigin({x, y, z}, def, vox->state); - vox = level->chunks->get(origin); + auto origin = blocks_agent::seek_origin(chunks, {x, y, z}, def, vox->state); + vox = blocks_agent::get(chunks, origin.x, origin.y, origin.z); if (vox == nullptr) { return 0; } @@ -410,8 +416,15 @@ static int l_raycast(lua::State* L) { glm::vec3 end; glm::ivec3 normal; glm::ivec3 iend; - if (auto voxel = level->chunks->rayCast( - start, dir, maxDistance, end, normal, iend, filteredBlocks + if (auto voxel = blocks_agent::raycast( + *level->chunksStorage, + start, + dir, + maxDistance, + end, + normal, + iend, + filteredBlocks )) { if (lua::gettop(L) >= 4 && !lua::isnil(L, 4)) { lua::pushvalue(L, 4); diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index a56c3581..8d5231a6 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -181,109 +181,17 @@ bool Chunks::checkReplaceability( const glm::ivec3& origin, blockid_t ignore ) { - const auto& rotation = def.rotations.variants[state.rotation]; - const auto size = def.size; - for (int sy = 0; sy < size.y; sy++) { - for (int sz = 0; sz < size.z; sz++) { - for (int sx = 0; sx < size.x; sx++) { - auto pos = origin; - pos += rotation.axisX * sx; - pos += rotation.axisY * sy; - pos += rotation.axisZ * sz; - if (auto vox = get(pos.x, pos.y, pos.z)) { - auto& target = indices->blocks.require(vox->id); - if (!target.replaceable && vox->id != ignore) { - return false; - } - } else { - return false; - } - } - } - } - return true; + return blocks_agent::check_replaceability(*this, def, state, origin, ignore); } void Chunks::setRotationExtended( const Block& def, blockstate state, const glm::ivec3& origin, uint8_t index ) { - auto newstate = state; - newstate.rotation = index; - - // unable to rotate block (cause: obstacles) - if (!checkReplaceability(def, newstate, origin, def.rt.id)) { - return; - } - - const auto& rotation = def.rotations.variants[index]; - const auto size = def.size; - std::vector segmentBlocks; - - for (int sy = 0; sy < size.y; sy++) { - for (int sz = 0; sz < size.z; sz++) { - for (int sx = 0; sx < size.x; sx++) { - auto pos = origin; - pos += rotation.axisX * sx; - pos += rotation.axisY * sy; - pos += rotation.axisZ * sz; - - blockstate segState = newstate; - segState.segment = blocks_agent::segment_to_int(sx, sy, sz); - - auto vox = get(pos); - // checked for nullptr by checkReplaceability - if (vox->id != def.rt.id) { - set(pos.x, pos.y, pos.z, def.rt.id, segState); - } else { - vox->state = segState; - auto chunk = getChunkByVoxel(pos.x, pos.y, pos.z); - assert(chunk != nullptr); - chunk->setModifiedAndUnsaved(); - segmentBlocks.emplace_back(pos); - } - } - } - } - const auto& prevRotation = def.rotations.variants[state.rotation]; - for (int sy = 0; sy < size.y; sy++) { - for (int sz = 0; sz < size.z; sz++) { - for (int sx = 0; sx < size.x; sx++) { - auto pos = origin; - pos += prevRotation.axisX * sx; - pos += prevRotation.axisY * sy; - pos += prevRotation.axisZ * sz; - if (std::find( - segmentBlocks.begin(), segmentBlocks.end(), pos - ) == segmentBlocks.end()) { - set(pos.x, pos.y, pos.z, 0, {}); - } - } - } - } + blocks_agent::set_rotation_extended(*this, def, state, origin, index); } void Chunks::setRotation(int32_t x, int32_t y, int32_t z, uint8_t index) { - if (index >= BlockRotProfile::MAX_COUNT) { - return; - } - auto vox = get(x, y, z); - if (vox == nullptr) { - return; - } - auto& def = indices->blocks.require(vox->id); - if (!def.rotatable || vox->state.rotation == index) { - return; - } - if (def.rt.extended) { - auto origin = seekOrigin({x, y, z}, def, vox->state); - vox = get(origin); - setRotationExtended(def, vox->state, origin, index); - } else { - vox->state.rotation = index; - auto chunk = getChunkByVoxel(x, y, z); - assert(chunk != nullptr); - chunk->setModifiedAndUnsaved(); - } + return blocks_agent::set_rotation(*this, x, y, z, index); } void Chunks::set( @@ -301,134 +209,9 @@ voxel* Chunks::rayCast( glm::ivec3& iend, std::set filter ) const { - float px = start.x; - float py = start.y; - float pz = start.z; - - float dx = dir.x; - float dy = dir.y; - float dz = dir.z; - - float t = 0.0f; - int ix = std::floor(px); - int iy = std::floor(py); - int iz = std::floor(pz); - - int stepx = (dx > 0.0f) ? 1 : -1; - int stepy = (dy > 0.0f) ? 1 : -1; - int stepz = (dz > 0.0f) ? 1 : -1; - - constexpr float infinity = std::numeric_limits::infinity(); - constexpr float epsilon = 1e-6f; // 0.000001 - float txDelta = (std::fabs(dx) < epsilon) ? infinity : std::fabs(1.0f / dx); - float tyDelta = (std::fabs(dy) < epsilon) ? infinity : std::fabs(1.0f / dy); - float tzDelta = (std::fabs(dz) < epsilon) ? infinity : std::fabs(1.0f / dz); - - float xdist = (stepx > 0) ? (ix + 1 - px) : (px - ix); - float ydist = (stepy > 0) ? (iy + 1 - py) : (py - iy); - float zdist = (stepz > 0) ? (iz + 1 - pz) : (pz - iz); - - float txMax = (txDelta < infinity) ? txDelta * xdist : infinity; - float tyMax = (tyDelta < infinity) ? tyDelta * ydist : infinity; - float tzMax = (tzDelta < infinity) ? tzDelta * zdist : infinity; - - int steppedIndex = -1; - - while (t <= maxDist) { - voxel* voxel = get(ix, iy, iz); - if (voxel == nullptr) { - return nullptr; - } - - const auto& def = indices->blocks.require(voxel->id); - if ((filter.empty() && def.selectable) || - (!filter.empty() && filter.find(def.rt.id) == filter.end())) { - end.x = px + t * dx; - end.y = py + t * dy; - end.z = pz + t * dz; - iend.x = ix; - iend.y = iy; - iend.z = iz; - - if (!def.rt.solid) { - const std::vector& hitboxes = - def.rotatable ? def.rt.hitboxes[voxel->state.rotation] - : def.hitboxes; - - scalar_t distance = maxDist; - Ray ray(start, dir); - - bool hit = false; - - glm::vec3 offset {}; - if (voxel->state.segment) { - offset = seekOrigin(iend, def, voxel->state) - iend; - } - - for (auto box : hitboxes) { - box.a += offset; - box.b += offset; - scalar_t boxDistance; - glm::ivec3 boxNorm; - if (ray.intersectAABB( - iend, box, maxDist, boxNorm, boxDistance - ) > RayRelation::None && - boxDistance < distance) { - hit = true; - distance = boxDistance; - norm = boxNorm; - end = start + (dir * glm::vec3(distance)); - } - } - - if (hit) return voxel; - } else { - iend.x = ix; - iend.y = iy; - iend.z = iz; - - norm.x = norm.y = norm.z = 0; - if (steppedIndex == 0) norm.x = -stepx; - if (steppedIndex == 1) norm.y = -stepy; - if (steppedIndex == 2) norm.z = -stepz; - return voxel; - } - } - if (txMax < tyMax) { - if (txMax < tzMax) { - ix += stepx; - t = txMax; - txMax += txDelta; - steppedIndex = 0; - } else { - iz += stepz; - t = tzMax; - tzMax += tzDelta; - steppedIndex = 2; - } - } else { - if (tyMax < tzMax) { - iy += stepy; - t = tyMax; - tyMax += tyDelta; - steppedIndex = 1; - } else { - iz += stepz; - t = tzMax; - tzMax += tzDelta; - steppedIndex = 2; - } - } - } - iend.x = ix; - iend.y = iy; - iend.z = iz; - - end.x = px + t * dx; - end.y = py + t * dy; - end.z = pz + t * dz; - norm.x = norm.y = norm.z = 0; - return nullptr; + return blocks_agent::raycast( + *this, start, dir, maxDist, end, norm, iend, std::move(filter) + ); } glm::vec3 Chunks::rayCastToObstacle( diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp index 53cef38d..ab39ea29 100644 --- a/src/voxels/blocks_agent.cpp +++ b/src/voxels/blocks_agent.cpp @@ -1,5 +1,9 @@ #include "blocks_agent.hpp" +#include "maths/rays.hpp" + +#include + using namespace blocks_agent; template @@ -93,3 +97,171 @@ void blocks_agent::set( ) { set_block(chunks, x, y, z, id, state); } + +template +static inline voxel* raycast_blocks( + const Storage& chunks, + const glm::vec3& start, + const glm::vec3& dir, + float maxDist, + glm::vec3& end, + glm::ivec3& norm, + glm::ivec3& iend, + std::set filter +) { + const auto& blocks = chunks.getContentIndices().blocks; + float px = start.x; + float py = start.y; + float pz = start.z; + + float dx = dir.x; + float dy = dir.y; + float dz = dir.z; + + float t = 0.0f; + int ix = std::floor(px); + int iy = std::floor(py); + int iz = std::floor(pz); + + int stepx = (dx > 0.0f) ? 1 : -1; + int stepy = (dy > 0.0f) ? 1 : -1; + int stepz = (dz > 0.0f) ? 1 : -1; + + constexpr float infinity = std::numeric_limits::infinity(); + constexpr float epsilon = 1e-6f; // 0.000001 + float txDelta = (std::fabs(dx) < epsilon) ? infinity : std::fabs(1.0f / dx); + float tyDelta = (std::fabs(dy) < epsilon) ? infinity : std::fabs(1.0f / dy); + float tzDelta = (std::fabs(dz) < epsilon) ? infinity : std::fabs(1.0f / dz); + + float xdist = (stepx > 0) ? (ix + 1 - px) : (px - ix); + float ydist = (stepy > 0) ? (iy + 1 - py) : (py - iy); + float zdist = (stepz > 0) ? (iz + 1 - pz) : (pz - iz); + + float txMax = (txDelta < infinity) ? txDelta * xdist : infinity; + float tyMax = (tyDelta < infinity) ? tyDelta * ydist : infinity; + float tzMax = (tzDelta < infinity) ? tzDelta * zdist : infinity; + + int steppedIndex = -1; + + while (t <= maxDist) { + voxel* voxel = get(chunks, ix, iy, iz); + if (voxel == nullptr) { + return nullptr; + } + + const auto& def = blocks.require(voxel->id); + if ((filter.empty() && def.selectable) || + (!filter.empty() && filter.find(def.rt.id) == filter.end())) { + end.x = px + t * dx; + end.y = py + t * dy; + end.z = pz + t * dz; + iend.x = ix; + iend.y = iy; + iend.z = iz; + + if (!def.rt.solid) { + const std::vector& hitboxes = + def.rotatable ? def.rt.hitboxes[voxel->state.rotation] + : def.hitboxes; + + scalar_t distance = maxDist; + Ray ray(start, dir); + + bool hit = false; + + glm::vec3 offset {}; + if (voxel->state.segment) { + offset = seek_origin(chunks, iend, def, voxel->state) - iend; + } + + for (auto box : hitboxes) { + box.a += offset; + box.b += offset; + scalar_t boxDistance; + glm::ivec3 boxNorm; + if (ray.intersectAABB( + iend, box, maxDist, boxNorm, boxDistance + ) > RayRelation::None && + boxDistance < distance) { + hit = true; + distance = boxDistance; + norm = boxNorm; + end = start + (dir * glm::vec3(distance)); + } + } + + if (hit) return voxel; + } else { + iend.x = ix; + iend.y = iy; + iend.z = iz; + + norm.x = norm.y = norm.z = 0; + if (steppedIndex == 0) norm.x = -stepx; + if (steppedIndex == 1) norm.y = -stepy; + if (steppedIndex == 2) norm.z = -stepz; + return voxel; + } + } + if (txMax < tyMax) { + if (txMax < tzMax) { + ix += stepx; + t = txMax; + txMax += txDelta; + steppedIndex = 0; + } else { + iz += stepz; + t = tzMax; + tzMax += tzDelta; + steppedIndex = 2; + } + } else { + if (tyMax < tzMax) { + iy += stepy; + t = tyMax; + tyMax += tyDelta; + steppedIndex = 1; + } else { + iz += stepz; + t = tzMax; + tzMax += tzDelta; + steppedIndex = 2; + } + } + } + iend.x = ix; + iend.y = iy; + iend.z = iz; + + end.x = px + t * dx; + end.y = py + t * dy; + end.z = pz + t * dz; + norm.x = norm.y = norm.z = 0; + return nullptr; +} + +voxel* blocks_agent::raycast( + const Chunks& chunks, + const glm::vec3& start, + const glm::vec3& dir, + float maxDist, + glm::vec3& end, + glm::ivec3& norm, + glm::ivec3& iend, + std::set filter +) { + return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter); +} + +voxel* blocks_agent::raycast( + const GlobalChunks& chunks, + const glm::vec3& start, + const glm::vec3& dir, + float maxDist, + glm::vec3& end, + glm::ivec3& norm, + glm::ivec3& iend, + std::set filter +) { + return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter); +} diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 3ce5b49b..3bad256f 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -10,6 +10,7 @@ #include "content/Content.hpp" #include "maths/voxmaths.hpp" +#include #include #include @@ -160,4 +161,158 @@ inline glm::ivec3 seek_origin( } } +template +inline bool check_replaceability( + const Storage& chunks, + const Block& def, + blockstate state, + const glm::ivec3& origin, + blockid_t ignore +) { + const auto& blocks = chunks.getContentIndices().blocks; + const auto& rotation = def.rotations.variants[state.rotation]; + const auto size = def.size; + for (int sy = 0; sy < size.y; sy++) { + for (int sz = 0; sz < size.z; sz++) { + for (int sx = 0; sx < size.x; sx++) { + auto pos = origin; + pos += rotation.axisX * sx; + pos += rotation.axisY * sy; + pos += rotation.axisZ * sz; + if (auto vox = get(chunks, pos.x, pos.y, pos.z)) { + auto& target = blocks.require(vox->id); + if (!target.replaceable && vox->id != ignore) { + return false; + } + } else { + return false; + } + } + } + } + return true; +} + +/// @brief Set rotation to an extended block +/// @tparam Storage chunks storage +/// @param def block definition +/// @param state current block state +/// @param origin extended block origin +/// @param index target rotation index +template +inline void set_rotation_extended( + Storage& chunks, + const Block& def, + blockstate state, + const glm::ivec3& origin, + uint8_t index +) { + auto newstate = state; + newstate.rotation = index; + + // unable to rotate block (cause: obstacles) + if (!check_replaceability(chunks, def, newstate, origin, def.rt.id)) { + return; + } + + const auto& rotation = def.rotations.variants[index]; + const auto size = def.size; + std::vector segmentBlocks; + + for (int sy = 0; sy < size.y; sy++) { + for (int sz = 0; sz < size.z; sz++) { + for (int sx = 0; sx < size.x; sx++) { + auto pos = origin; + pos += rotation.axisX * sx; + pos += rotation.axisY * sy; + pos += rotation.axisZ * sz; + + blockstate segState = newstate; + segState.segment = segment_to_int(sx, sy, sz); + + auto vox = get(chunks, pos.x, pos.y, pos.z); + // checked for nullptr by checkReplaceability + if (vox->id != def.rt.id) { + set(chunks, pos.x, pos.y, pos.z, def.rt.id, segState); + } else { + vox->state = segState; + int cx = floordiv(pos.x); + int cz = floordiv(pos.z); + auto chunk = get_chunk(chunks, cx, cz); + assert(chunk != nullptr); + chunk->setModifiedAndUnsaved(); + segmentBlocks.emplace_back(pos); + } + } + } + } + const auto& prevRotation = def.rotations.variants[state.rotation]; + for (int sy = 0; sy < size.y; sy++) { + for (int sz = 0; sz < size.z; sz++) { + for (int sx = 0; sx < size.x; sx++) { + auto pos = origin; + pos += prevRotation.axisX * sx; + pos += prevRotation.axisY * sy; + pos += prevRotation.axisZ * sz; + if (std::find( + segmentBlocks.begin(), segmentBlocks.end(), pos + ) == segmentBlocks.end()) { + set(chunks, pos.x, pos.y, pos.z, 0, {}); + } + } + } + } +} + +template +inline void set_rotation( + Storage& chunks, int32_t x, int32_t y, int32_t z, uint8_t index +) { + if (index >= BlockRotProfile::MAX_COUNT) { + return; + } + auto vox = get(chunks, x, y, z); + if (vox == nullptr) { + return; + } + const auto& def = chunks.getContentIndices().blocks.require(vox->id); + if (!def.rotatable || vox->state.rotation == index) { + return; + } + if (def.rt.extended) { + auto origin = seek_origin(chunks, {x, y, z}, def, vox->state); + vox = get(chunks, origin.x, origin.y, origin.z); + set_rotation_extended(chunks, def, vox->state, origin, index); + } else { + vox->state.rotation = index; + int cx = floordiv(x); + int cz = floordiv(z); + auto chunk = get_chunk(chunks, cx, cz); + assert(chunk != nullptr); + chunk->setModifiedAndUnsaved(); + } +} + +voxel* raycast( + const Chunks& chunks, + const glm::vec3& start, + const glm::vec3& dir, + float maxDist, + glm::vec3& end, + glm::ivec3& norm, + glm::ivec3& iend, + std::set filter +); + +voxel* raycast( + const GlobalChunks& chunks, + const glm::vec3& start, + const glm::vec3& dir, + float maxDist, + glm::vec3& end, + glm::ivec3& norm, + glm::ivec3& iend, + std::set filter +); + } // blocks_agent From 5030a65bc168d171b3ff2765005dc1e41cd39224 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Dec 2024 23:43:01 +0300 Subject: [PATCH 075/246] add docs to some blocks_agent functions --- src/voxels/blocks_agent.hpp | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 3bad256f..e6e0262c 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -138,6 +138,14 @@ inline void repair_segments( } } +/// @brief Get origin position for specified extended block. Returns srcpos +/// if block is not extended. +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param srcpos block segment position +/// @param def definition of the block at srcpos +/// @param state state of the block at srcpos +/// @return origin block position template inline glm::ivec3 seek_origin( Storage& chunks, const glm::ivec3& srcpos, const Block& def, blockstate state @@ -161,6 +169,14 @@ inline glm::ivec3 seek_origin( } } +/// @brief Check blocks replaceability with specified block +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param def block definition +/// @param state target block state +/// @param origin target block origin +/// @param ignore ignored block id +/// @return true if specified area may be replaced with the block/extended block template inline bool check_replaceability( const Storage& chunks, @@ -194,7 +210,8 @@ inline bool check_replaceability( } /// @brief Set rotation to an extended block -/// @tparam Storage chunks storage +/// @tparam Storage chunks storage class +/// @param chunks chunks storage /// @param def block definition /// @param state current block state /// @param origin extended block origin @@ -264,6 +281,13 @@ inline void set_rotation_extended( } } +/// @brief Set block rotation +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param x block X position +/// @param y block Y position +/// @param z block Z position +/// @param index target rotation index template inline void set_rotation( Storage& chunks, int32_t x, int32_t y, int32_t z, uint8_t index From 6ed0bc3e7e401ca0318e383c8bec1f9a05eb88e6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Dec 2024 23:56:52 +0300 Subject: [PATCH 076/246] add docs to some blocks_agent functions --- src/voxels/blocks_agent.hpp | 65 +++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index e6e0262c..10655cdc 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -1,5 +1,7 @@ #pragma once +/// blocks_agent is set of templates but not a class to minimize OOP overhead. + #include "voxel.hpp" #include "Block.hpp" #include "Chunk.hpp" @@ -11,18 +13,31 @@ #include "maths/voxmaths.hpp" #include +#include #include #include -/// Using templates to minimize OOP overhead - namespace blocks_agent { +/// @brief Get specified chunk. +/// @tparam Storage +/// @param chunks +/// @param cx chunk grid position X +/// @param cz chunk grid position Z +/// @return chunk or nullptr if does not exists template inline Chunk* get_chunk(const Storage& chunks, int cx, int cz) { return chunks.getChunk(cx, cz); } +/// @brief Get voxel at specified position. +/// Returns nullptr if voxel does not exists. +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param x position X +/// @param y position Y +/// @param z position Z +/// @return voxel pointer or nullptr template inline voxel* get(const Storage& chunks, int32_t x, int32_t y, int32_t z) { if (y < 0 || y >= CHUNK_H) { @@ -39,6 +54,14 @@ inline voxel* get(const Storage& chunks, int32_t x, int32_t y, int32_t z) { return &chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; } +/// @brief Get voxel at specified position. +/// @throws std::runtime_error if voxel does not exists +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param x position X +/// @param y position Y +/// @param z position Z +/// @return voxel reference template inline voxel& require(const Storage& chunks, int32_t x, int32_t y, int32_t z) { auto vox = get(chunks, x, y, z); @@ -53,6 +76,14 @@ inline const Block& get_block_def(const Storage& chunks, blockid_t id) { return chunks.getContentIndices().blocks.require(id); } + +/// @brief Check if block at specified position is solid. +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param x position X +/// @param y position Y +/// @param z position Z +/// @return true if block exists and solid template inline bool is_solid_at(const Storage& chunks, int32_t x, int32_t y, int32_t z) { if (auto vox = get(chunks, x, y, z)) { @@ -61,6 +92,13 @@ inline bool is_solid_at(const Storage& chunks, int32_t x, int32_t y, int32_t z) return false; } +/// @brief Check if block at specified position is replaceable. +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param x position X +/// @param y position Y +/// @param z position Z +/// @return true if block exists and replaceable template inline bool is_replaceable_at(const Storage& chunks, int32_t x, int32_t y, int32_t z) { if (auto vox = get(chunks, x, y, z)) { @@ -87,6 +125,14 @@ void set( blockstate state ); +/// @brief Erase extended block segments +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param def origin block definition +/// @param state origin block state +/// @param x origin position X +/// @param y origin position Y +/// @param z origin position Z template inline void erase_segments( Storage& chunks, const Block& def, blockstate state, int x, int y, int z @@ -108,10 +154,23 @@ inline void erase_segments( } } -static constexpr uint8_t segment_to_int(int sx, int sy, int sz) { +/// @brief Convert segment offset to segment bits +/// @param sx segment offset X +/// @param sy segment offset Y +/// @param sz segment offset Z +/// @return segment bits +static constexpr inline uint8_t segment_to_int(int sx, int sy, int sz) { return ((sx > 0) | ((sy > 0) << 1) | ((sz > 0) << 2)); } +/// @brief Restore invalid extended block segments +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param def origin block definition +/// @param state origin block state +/// @param x origin position X +/// @param y origin position Y +/// @param z origin position Z template inline void repair_segments( Storage& chunks, const Block& def, blockstate state, int x, int y, int z From 7ca91020e3b0c2648d52c13be217ee3b1468893d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 00:04:52 +0300 Subject: [PATCH 077/246] add docs to remaining public blocks_agent functions --- src/voxels/blocks_agent.hpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 10655cdc..e2c4266e 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -107,6 +107,13 @@ inline bool is_replaceable_at(const Storage& chunks, int32_t x, int32_t y, int32 return false; } +/// @brief Set block at specified position if voxel exists. +/// @param chunks chunks matrix +/// @param x block position X +/// @param y block position Y +/// @param z block position Z +/// @param id new block id +/// @param state new block state void set( Chunks& chunks, int32_t x, @@ -116,6 +123,13 @@ void set( blockstate state ); +/// @brief Set block at specified position if voxel exists. +/// @param chunks chunks storage +/// @param x block position X +/// @param y block position Y +/// @param z block position Z +/// @param id new block id +/// @param state new block state void set( GlobalChunks& chunks, int32_t x, @@ -376,6 +390,16 @@ inline void set_rotation( } } +/// @brief Cast ray to a selectable block with filter based on id. +/// @param chunks chunks matrix +/// @param start ray start position +/// @param dir normalized ray direction vector +/// @param maxDist maximum ray length +/// @param end [out] ray end position +/// @param norm [out] surface normal vector +/// @param iend [out] ray end integer position (voxel position + normal) +/// @param filter filtered ids +/// @return voxel pointer or nullptr voxel* raycast( const Chunks& chunks, const glm::vec3& start, @@ -387,6 +411,16 @@ voxel* raycast( std::set filter ); +/// @brief Cast ray to a selectable block with filter based on id. +/// @param chunks chunks storage +/// @param start ray start position +/// @param dir normalized ray direction vector +/// @param maxDist maximum ray length +/// @param end [out] ray end position +/// @param norm [out] surface normal vector +/// @param iend [out] ray end integer position (voxel position + normal) +/// @param filter filtered ids +/// @return voxel pointer or nullptr voxel* raycast( const GlobalChunks& chunks, const glm::vec3& start, From c5049d0e2448a2cf359cef019239d23afe307988 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 02:05:52 +0300 Subject: [PATCH 078/246] performance test again --- dev/tests/example.lua | 7 +++++-- src/logic/scripting/lua/libs/libblock.cpp | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 3dbe1197..7eccf874 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(10000000, block.get, 0, 0, 0) -timeit(10000000, core.blank, 0, 0, 0) +for i=1,10 do + print("--------------------") + timeit(10000000, block.get_fast, 0, 0, 0) + timeit(10000000, block.get, 0, 0, 0) +end block.destruct(0, 0, 0, pid) assert(block.get(0, 0, 0) == 0) diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index c0bf1ef6..fb2a5560 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -120,6 +120,15 @@ static int l_get(lua::State* L) { return lua::pushinteger(L, id); } +static int l_get_fast(lua::State* L) { + auto x = lua::tointeger(L, 1); + auto y = lua::tointeger(L, 2); + auto z = lua::tointeger(L, 3); + auto vox = blocks_agent::get(*level->chunks, 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); @@ -629,6 +638,7 @@ const luaL_Reg blocklib[] = { {"is_solid_at", lua::wrap}, {"is_replaceable_at", lua::wrap}, {"set", lua::wrap}, + {"get_fast", lua::wrap}, {"get", lua::wrap}, {"get_X", lua::wrap}, {"get_Y", lua::wrap}, From b7664b4188584dfba1273a3267a127ecc1a35e0a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 05:13:49 +0300 Subject: [PATCH 079/246] move Lighting instance to ChunksController --- dev/tests/example.lua | 6 +----- src/logic/BlocksController.cpp | 4 ++-- src/logic/BlocksController.hpp | 2 +- src/logic/ChunksController.cpp | 6 +++--- src/logic/ChunksController.hpp | 3 ++- src/logic/LevelController.cpp | 6 +++--- src/logic/scripting/lua/libs/libblock.cpp | 18 +++++++---------- src/logic/scripting/lua/libs/libworld.cpp | 21 ++++++++++++++------ src/objects/Player.cpp | 3 +++ src/objects/Player.hpp | 2 ++ src/voxels/Chunks.cpp | 24 +++++++++-------------- src/voxels/Chunks.hpp | 12 +++++++----- src/world/Level.cpp | 10 ++++------ src/world/Level.hpp | 4 +--- 14 files changed, 60 insertions(+), 61 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 7eccf874..f7147d06 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -8,11 +8,7 @@ assert(player.get_name(pid) == "Xerxes") test.sleep_until(function() return world.count_chunks() >= 9 end, 1000) print(world.count_chunks()) -for i=1,10 do - print("--------------------") - timeit(10000000, block.get_fast, 0, 0, 0) - timeit(10000000, block.get, 0, 0, 0) -end +timeit(10000000, block.get, 0, 0, 0) block.destruct(0, 0, 0, pid) assert(block.get(0, 0, 0) == 0) diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp index 980c8bfe..bcc1f728 100644 --- a/src/logic/BlocksController.cpp +++ b/src/logic/BlocksController.cpp @@ -14,10 +14,10 @@ #include "world/Level.hpp" #include "world/World.hpp" -BlocksController::BlocksController(const Level& level, uint padding) +BlocksController::BlocksController(const Level& level, Lighting& lighting, uint padding) : level(level), chunks(*level.chunks), - lighting(*level.lighting), + lighting(lighting), randTickClock(20, 3), blocksTickClock(20, 1), worldTickClock(20, 1), diff --git a/src/logic/BlocksController.hpp b/src/logic/BlocksController.hpp index 0937b3f7..a8f286dc 100644 --- a/src/logic/BlocksController.hpp +++ b/src/logic/BlocksController.hpp @@ -34,7 +34,7 @@ class BlocksController { FastRandom random {}; std::vector blockInteractionCallbacks; public: - BlocksController(const Level& level, uint padding); + BlocksController(const Level& level, Lighting& lighting, uint padding); void updateSides(int x, int y, int z); void updateSides(int x, int y, int z, int w, int h, int d); diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 0d8d97b4..55ab262d 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -25,7 +25,7 @@ const uint MIN_SURROUNDING = 9; ChunksController::ChunksController(Level& level, uint padding) : level(level), chunks(*level.chunks), - lighting(*level.lighting), + lighting(std::make_unique(level.content, level.chunks.get())), padding(padding), generator(std::make_unique( level.content->generators.require(level.getWorld()->getGenerator()), @@ -107,9 +107,9 @@ bool ChunksController::buildLights(const std::shared_ptr& chunk) { if (surrounding == MIN_SURROUNDING) { bool lightsCache = chunk->flags.loadedLights; if (!lightsCache) { - lighting.buildSkyLight(chunk->x, chunk->z); + lighting->buildSkyLight(chunk->x, chunk->z); } - lighting.onChunkLoaded(chunk->x, chunk->z, !lightsCache); + lighting->onChunkLoaded(chunk->x, chunk->z, !lightsCache); chunk->flags.lighted = true; return true; } diff --git a/src/logic/ChunksController.hpp b/src/logic/ChunksController.hpp index e9f1ab62..71dada6f 100644 --- a/src/logic/ChunksController.hpp +++ b/src/logic/ChunksController.hpp @@ -15,7 +15,6 @@ class ChunksController { private: Level& level; Chunks& chunks; - Lighting& lighting; uint padding; std::unique_ptr generator; @@ -24,6 +23,8 @@ private: bool buildLights(const std::shared_ptr& chunk); void createChunk(int x, int y); public: + std::unique_ptr lighting; + ChunksController(Level& level, uint padding); ~ChunksController(); diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index a9c002b1..2f363dad 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -20,12 +20,12 @@ static debug::Logger logger("level-control"); LevelController::LevelController(Engine* engine, std::unique_ptr levelPtr) : settings(engine->getSettings()), level(std::move(levelPtr)), - blocks(std::make_unique( - *level, settings.chunks.padding.get() - )), chunks(std::make_unique( *level, settings.chunks.padding.get() )) { + blocks = std::make_unique( + *level, *chunks->lighting, settings.chunks.padding.get() + ); scripting::on_world_load(this); } diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index fb2a5560..24b00124 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -104,7 +104,13 @@ static int l_set(lua::State* L) { return 0; } blocks_agent::set(*level->chunksStorage, x, y, z, id, int2blockstate(state)); - level->lighting->onBlockSet(x, y, z, id); + + auto chunksController = controller->getChunksController(); + if (chunksController == nullptr) { + return 1; + } + Lighting& lighting = *chunksController->lighting; + lighting.onBlockSet(x, y, z, id); if (!noupdate) { blocks->updateSides(x, y, z); } @@ -120,15 +126,6 @@ static int l_get(lua::State* L) { return lua::pushinteger(L, id); } -static int l_get_fast(lua::State* L) { - auto x = lua::tointeger(L, 1); - auto y = lua::tointeger(L, 2); - auto z = lua::tointeger(L, 3); - auto vox = blocks_agent::get(*level->chunks, 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); @@ -638,7 +635,6 @@ const luaL_Reg blocklib[] = { {"is_solid_at", lua::wrap}, {"is_replaceable_at", lua::wrap}, {"set", lua::wrap}, - {"get_fast", lua::wrap}, {"get", lua::wrap}, {"get_X", lua::wrap}, {"get_Y", lua::wrap}, diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 68004777..58e2bdc4 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -16,6 +16,8 @@ #include "voxels/GlobalChunks.hpp" #include "world/Level.hpp" #include "world/World.hpp" +#include "logic/LevelController.hpp" +#include "logic/ChunksController.hpp" using namespace scripting; namespace fs = std::filesystem; @@ -203,30 +205,37 @@ static int l_set_chunk_data(lua::State* L) { } else { chunk->decode(buffer->data().data()); } + + auto chunksController = controller->getChunksController(); + if (chunksController == nullptr) { + return 1; + } + + Lighting& lighting = *chunksController->lighting; chunk->updateHeights(); - level->lighting->buildSkyLight(x, y); + lighting.buildSkyLight(x, y); chunk->flags.modified = true; - level->lighting->onChunkLoaded(x, y, true); + lighting.onChunkLoaded(x, y, true); chunk = level->chunks->getChunk(x - 1, y); if (chunk != nullptr) { chunk->flags.modified = true; - level->lighting->onChunkLoaded(x - 1, y, true); + lighting.onChunkLoaded(x - 1, y, true); } chunk = level->chunks->getChunk(x + 1, y); if (chunk != nullptr) { chunk->flags.modified = true; - level->lighting->onChunkLoaded(x + 1, y, true); + lighting.onChunkLoaded(x + 1, y, true); } chunk = level->chunks->getChunk(x, y - 1); if (chunk != nullptr) { chunk->flags.modified = true; - level->lighting->onChunkLoaded(x, y - 1, true); + lighting.onChunkLoaded(x, y - 1, true); } chunk = level->chunks->getChunk(x, y + 1); if (chunk != nullptr) { chunk->flags.modified = true; - level->lighting->onChunkLoaded(x, y + 1, true); + lighting.onChunkLoaded(x, y + 1, true); } return 1; diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index 000d2c04..0dd58f91 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -44,6 +44,9 @@ Player::Player( position(position), inventory(std::move(inv)), eid(eid), + chunks(std::make_unique( + 3, 3, 0, 0, level->events.get(), level->content->getIndices() + )), fpCamera(level->getCamera("core:first-person")), spCamera(level->getCamera("core:third-person-front")), tpCamera(level->getCamera("core:third-person-back")), diff --git a/src/objects/Player.hpp b/src/objects/Player.hpp index 920e06e4..1f57c6f7 100644 --- a/src/objects/Player.hpp +++ b/src/objects/Player.hpp @@ -8,6 +8,7 @@ #include "settings.hpp" #include "voxels/voxel.hpp" +class Chunks; class Camera; class Inventory; class ContentReport; @@ -55,6 +56,7 @@ class Player : public Serializable { entityid_t eid; entityid_t selectedEid = 0; public: + std::unique_ptr chunks; // not in use yet std::shared_ptr fpCamera, spCamera, tpCamera; std::shared_ptr currentCamera; bool debug = false; diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 8d5231a6..91b05d69 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -1,6 +1,5 @@ #include "Chunks.hpp" -#include #include #include @@ -20,9 +19,6 @@ #include "world/Level.hpp" #include "world/LevelEvents.hpp" #include "VoxelsVolume.hpp" -#include "Block.hpp" -#include "Chunk.hpp" -#include "voxel.hpp" #include "blocks_agent.hpp" Chunks::Chunks( @@ -30,16 +26,15 @@ Chunks::Chunks( int32_t d, int32_t ox, int32_t oz, - WorldFiles* wfile, - Level* level + LevelEvents* events, + const ContentIndices* indices ) - : level(level), - indices(level ? level->content->getIndices() : nullptr), - areaMap(w, d), - worldFiles(wfile) { + : events(events), + indices(indices), + areaMap(w, d) { areaMap.setCenter(ox-w/2, oz-d/2); areaMap.setOutCallback([this](int, int, const auto& chunk) { - this->level->events->trigger(EVT_CHUNK_HIDDEN, chunk.get()); + this->events->trigger(EVT_CHUNK_HIDDEN, chunk.get()); }); } @@ -319,8 +314,9 @@ void Chunks::resize(uint32_t newW, uint32_t newD) { bool Chunks::putChunk(const std::shared_ptr& chunk) { if (areaMap.set(chunk->x, chunk->z, chunk)) { - if (level) - level->events->trigger(LevelEventType::EVT_CHUNK_SHOWN, chunk.get()); + if (events) { + events->trigger(LevelEventType::EVT_CHUNK_SHOWN, chunk.get()); + } return true; } return false; @@ -330,8 +326,6 @@ bool Chunks::putChunk(const std::shared_ptr& chunk) { // 25.06.2024: not now // 11.11.2024: not now void Chunks::getVoxels(VoxelsVolume* volume, bool backlight) const { - const Content* content = level->content; - auto indices = content->getIndices(); voxel* voxels = volume->getVoxels(); light_t* lights = volume->getLights(); int x = volume->getX(); diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index a39c8960..c1c75e9c 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -20,12 +20,11 @@ class Chunk; class WorldFiles; class LevelEvents; class Block; -class Level; class VoxelsVolume; /// Player-centred chunks matrix class Chunks { - Level* level; + LevelEvents* events; const ContentIndices* const indices; void eraseSegments(const Block& def, blockstate state, int x, int y, int z); @@ -40,15 +39,14 @@ class Chunks { ); util::AreaMap2D, int32_t> areaMap; - WorldFiles* worldFiles; public: Chunks( int32_t w, int32_t d, int32_t ox, int32_t oz, - WorldFiles* worldFiles, - Level* level + LevelEvents* events, + const ContentIndices* indices ); ~Chunks() = default; @@ -156,4 +154,8 @@ public: const ContentIndices& getContentIndices() const { return *indices; } + + static inline constexpr unsigned matrixSize(int loadDistance, int padding) { + return (loadDistance + padding) * 2; + } }; diff --git a/src/world/Level.cpp b/src/world/Level.cpp index 99f47d86..c289579f 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -23,14 +23,14 @@ Level::Level( const Content* content, EngineSettings& settings ) - : world(std::move(worldPtr)), + : settings(settings), + world(std::move(worldPtr)), content(content), chunksStorage(std::make_unique(this)), physics(std::make_unique(glm::vec3(0, -22.6f, 0))), events(std::make_unique()), entities(std::make_unique(this)), - players(std::make_unique(this)), - settings(settings) { + players(std::make_unique(this)) { const auto& worldInfo = world->getInfo(); auto& cameraIndices = content->getIndices(ResourceType::CAMERA); for (size_t i = 0; i < cameraIndices.size(); i++) { @@ -65,11 +65,9 @@ Level::Level( (settings.chunks.loadDistance.get() + settings.chunks.padding.get()) * 2; chunks = std::make_unique( - matrixSize, matrixSize, 0, 0, world->wfile.get(), this + matrixSize, matrixSize, 0, 0, events.get(), content->getIndices() ); - lighting = std::make_unique(content, chunks.get()); - inventories = std::make_unique(*this); } diff --git a/src/world/Level.hpp b/src/world/Level.hpp index 63274672..b30bde3b 100644 --- a/src/world/Level.hpp +++ b/src/world/Level.hpp @@ -22,6 +22,7 @@ struct EngineSettings; /// @brief A level, contains chunks and objects class Level { + const EngineSettings& settings; std::unique_ptr world; public: const Content* const content; @@ -31,14 +32,11 @@ public: std::unique_ptr inventories; std::unique_ptr physics; - std::unique_ptr lighting; std::unique_ptr events; std::unique_ptr entities; std::unique_ptr players; std::vector> cameras; // move somewhere? - const EngineSettings& settings; - Level( std::unique_ptr world, const Content* content, From 1c18c020923f44d58f1969d706625e90e1fecc81 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 19:40:00 +0300 Subject: [PATCH 080/246] move Chunks from Level to Player --- dev/tests/example.lua | 4 +- src/TestMainloop.cpp | 4 +- src/frontend/hud.cpp | 49 +++++---- src/frontend/hud.hpp | 4 +- src/frontend/screens/LevelScreen.cpp | 10 +- src/graphics/render/BlockWrapsRenderer.cpp | 11 +- src/graphics/render/BlockWrapsRenderer.hpp | 6 +- src/graphics/render/ChunksRenderer.cpp | 61 ++++++----- src/graphics/render/ChunksRenderer.hpp | 3 + src/graphics/render/Decorator.cpp | 4 +- src/graphics/render/ParticlesRenderer.cpp | 10 +- src/graphics/render/ParticlesRenderer.hpp | 3 + src/graphics/render/WorldRenderer.cpp | 13 ++- src/logic/BlocksController.cpp | 87 ++++++++++----- src/logic/BlocksController.hpp | 9 +- src/logic/ChunksController.cpp | 39 ++++--- src/logic/ChunksController.hpp | 14 +-- src/logic/LevelController.cpp | 25 +++-- src/logic/LevelController.hpp | 2 +- src/logic/PlayerController.cpp | 24 ++--- src/logic/scripting/lua/libs/libblock.cpp | 4 +- src/logic/scripting/lua/libs/libentity.cpp | 12 ++- .../scripting/lua/libs/libgeneration.cpp | 2 +- src/logic/scripting/lua/libs/libhud.cpp | 3 +- src/logic/scripting/lua/libs/libworld.cpp | 14 +-- .../lua/usertypes/lua_type_voxelfragment.cpp | 2 +- src/objects/Entities.cpp | 2 +- src/objects/Player.cpp | 6 +- src/physics/PhysicsSolver.cpp | 28 ++--- src/physics/PhysicsSolver.hpp | 6 +- src/voxels/Chunks.cpp | 8 ++ src/voxels/Chunks.hpp | 2 + src/voxels/GlobalChunks.cpp | 5 + src/voxels/GlobalChunks.hpp | 3 + src/voxels/blocks_agent.cpp | 102 ++++++++++++++++++ src/voxels/blocks_agent.hpp | 41 +++++++ src/world/Level.cpp | 22 ---- src/world/Level.hpp | 5 - src/world/generator/VoxelFragment.cpp | 15 +-- src/world/generator/VoxelFragment.hpp | 6 +- 40 files changed, 450 insertions(+), 220 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index f7147d06..0435b6cd 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -5,11 +5,9 @@ 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) +test.sleep_until(function() return block.get(0, 0, 0) >= 0 end, 1000) print(world.count_chunks()) -timeit(10000000, block.get, 0, 0, 0) - block.destruct(0, 0, 0, pid) assert(block.get(0, 0, 0) == 0) test.close_world(true) diff --git a/src/TestMainloop.cpp b/src/TestMainloop.cpp index 33487059..100c0b51 100644 --- a/src/TestMainloop.cpp +++ b/src/TestMainloop.cpp @@ -49,6 +49,8 @@ void TestMainloop::setLevel(std::unique_ptr level) { engine.getPaths()->setCurrentWorldFolder(fs::path()); controller = nullptr; } else { - controller = std::make_unique(&engine, std::move(level)); + controller = std::make_unique( + &engine, std::move(level), nullptr + ); } } diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 1a2d8315..e6b1ecb9 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -106,7 +106,7 @@ std::shared_ptr HudElement::getNode() const { std::shared_ptr Hud::createContentAccess() { auto content = frontend.getLevel().content; auto indices = content->getIndices(); - auto inventory = player->getInventory(); + auto inventory = player.getInventory(); size_t itemsCount = indices->items.count(); auto accessInventory = std::make_shared(0, itemsCount); @@ -120,7 +120,7 @@ std::shared_ptr Hud::createContentAccess() { inventory->move(copy, indices); }, [=](uint, ItemStack& item) { - inventory->getSlot(player->getChosenSlot()).set(item); + inventory->getSlot(player.getChosenSlot()).set(item); }); InventoryBuilder builder; @@ -132,7 +132,7 @@ std::shared_ptr Hud::createContentAccess() { } std::shared_ptr Hud::createHotbar() { - auto inventory = player->getInventory(); + auto inventory = player.getInventory(); auto content = frontend.getLevel().content; SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr); @@ -148,7 +148,7 @@ std::shared_ptr Hud::createHotbar() { static constexpr uint WORLDGEN_IMG_SIZE = 128U; -Hud::Hud(Engine* engine, LevelFrontend& frontend, Player* player) +Hud::Hud(Engine* engine, LevelFrontend& frontend, Player& player) : engine(engine), assets(engine->getAssets()), gui(engine->getGUI()), @@ -177,7 +177,7 @@ Hud::Hud(Engine* engine, LevelFrontend& frontend, Player* player) uicamera->flipped = true; debugPanel = create_debug_panel( - engine, frontend.getLevel(), *player, allowDebugCheats + engine, frontend.getLevel(), player, allowDebugCheats ); debugPanel->setZIndex(2); gui->add(debugPanel); @@ -246,12 +246,12 @@ void Hud::processInput(bool visible) { void Hud::updateHotbarControl() { if (!inventoryOpen && Events::scroll) { - int slot = player->getChosenSlot(); + int slot = player.getChosenSlot(); slot = (slot - Events::scroll) % 10; if (slot < 0) { slot += 10; } - player->setChosenSlot(slot); + player.setChosenSlot(slot); } for ( int i = static_cast(keycode::NUM_1); @@ -259,17 +259,17 @@ void Hud::updateHotbarControl() { i++ ) { if (Events::jpressed(i)) { - player->setChosenSlot(i - static_cast(keycode::NUM_1)); + player.setChosenSlot(i - static_cast(keycode::NUM_1)); } } if (Events::jpressed(keycode::NUM_0)) { - player->setChosenSlot(9); + player.setChosenSlot(9); } } void Hud::updateWorldGenDebugVisualization() { auto& level = frontend.getLevel(); - auto& chunks = *level.chunks; + const auto& chunks = *player.chunks; auto generator = frontend.getController()->getChunksController()->getGenerator(); auto debugInfo = generator->createDebugInfo(); @@ -318,9 +318,10 @@ void Hud::updateWorldGenDebugVisualization() { void Hud::update(bool visible) { const auto& level = frontend.getLevel(); + const auto& chunks = *player.chunks; auto menu = gui->getMenu(); - debugPanel->setVisible(player->debug && visible); + debugPanel->setVisible(player.debug && visible); if (!visible && inventoryOpen) { closeInventory(); @@ -337,7 +338,7 @@ void Hud::update(bool visible) { } if (blockUI) { - voxel* vox = level.chunks->get(blockPos.x, blockPos.y, blockPos.z); + voxel* vox = chunks.get(blockPos.x, blockPos.y, blockPos.z); if (vox == nullptr || vox->id != currentblockid) { closeInventory(); } @@ -355,7 +356,7 @@ void Hud::update(bool visible) { if (visible) { for (auto& element : elements) { - element.update(pause, inventoryOpen, player->debug); + element.update(pause, inventoryOpen, player.debug); if (element.isRemoved()) { onRemove(element); } @@ -363,8 +364,8 @@ void Hud::update(bool visible) { } cleanup(); - debugMinimap->setVisible(player->debug && showGeneratorMinimap); - if (player->debug && showGeneratorMinimap) { + debugMinimap->setVisible(player.debug && showGeneratorMinimap); + if (player.debug && showGeneratorMinimap) { updateWorldGenDebugVisualization(); } } @@ -375,7 +376,7 @@ void Hud::openInventory() { showExchangeSlot(); inventoryOpen = true; - auto inventory = player->getInventory(); + auto inventory = player.getInventory(); auto inventoryDocument = assets->get("core:inventory"); inventoryView = std::dynamic_pointer_cast(inventoryDocument->getRoot()); inventoryView->bind(inventory, content); @@ -422,7 +423,9 @@ void Hud::openInventory( closeInventory(); } auto& level = frontend.getLevel(); + const auto& chunks = *player.chunks; auto content = level.content; + blockUI = std::dynamic_pointer_cast(doc->getRoot()); if (blockUI == nullptr) { throw std::runtime_error("block UI root element must be 'inventory'"); @@ -436,10 +439,10 @@ void Hud::openInventory( if (blockinv == nullptr) { blockinv = level.inventories->createVirtual(blockUI->getSlotsCount()); } - level.chunks->getChunkByVoxel(block.x, block.y, block.z)->flags.unsaved = true; + chunks.getChunkByVoxel(block.x, block.y, block.z)->flags.unsaved = true; blockUI->bind(blockinv, content); blockPos = block; - currentblockid = level.chunks->get(block.x, block.y, block.z)->id; + currentblockid = chunks.require(block.x, block.y, block.z).id; add(HudElement(hud_element_mode::inventory_bound, doc, blockUI, false)); } @@ -481,7 +484,7 @@ void Hud::openPermanent(UiDocument* doc) { auto invview = std::dynamic_pointer_cast(root); if (invview) { - invview->bind(player->getInventory(), frontend.getLevel().content); + invview->bind(player.getInventory(), frontend.getLevel().content); } add(HudElement(hud_element_mode::permanent, doc, doc->getRoot(), false)); } @@ -571,7 +574,7 @@ void Hud::draw(const DrawContext& ctx){ uishader->uniformMatrix("u_projview", uicamera->getProjView()); // Crosshair - if (!pause && !inventoryOpen && !player->debug) { + if (!pause && !inventoryOpen && !player.debug) { DrawContext chctx = ctx.sub(batch); chctx.setBlendMode(BlendMode::inversion); auto texture = assets->get("gui/crosshair"); @@ -629,7 +632,7 @@ void Hud::updateElementsPosition(const Viewport& viewport) { exchangeSlot->setPos(glm::vec2(Events::cursor)); } hotbarView->setPos(glm::vec2(width/2, height-65)); - hotbarView->setSelected(player->getChosenSlot()); + hotbarView->setSelected(player.getChosenSlot()); } bool Hud::isInventoryOpen() const { @@ -661,7 +664,7 @@ void Hud::setPause(bool pause) { } Player* Hud::getPlayer() const { - return player; + return &player; } std::shared_ptr Hud::getBlockInventory() { @@ -684,7 +687,7 @@ void Hud::setDebugCheats(bool flag) { gui->remove(debugPanel); debugPanel = create_debug_panel( - engine, frontend.getLevel(), *player, allowDebugCheats + engine, frontend.getLevel(), player, allowDebugCheats ); debugPanel->setZIndex(2); gui->add(debugPanel); diff --git a/src/frontend/hud.hpp b/src/frontend/hud.hpp index 5594664a..6d9d3f92 100644 --- a/src/frontend/hud.hpp +++ b/src/frontend/hud.hpp @@ -75,7 +75,7 @@ class Hud : public util::ObjectsKeeper { std::unique_ptr uicamera; gui::GUI* gui; LevelFrontend& frontend; - Player* player; + Player& player; /// @brief Is any overlay/inventory open bool inventoryOpen = false; @@ -131,7 +131,7 @@ class Hud : public util::ObjectsKeeper { void showExchangeSlot(); void updateWorldGenDebugVisualization(); public: - Hud(Engine* engine, LevelFrontend& frontend, Player* player); + Hud(Engine* engine, LevelFrontend& frontend, Player& player); ~Hud(); void update(bool hudVisible); diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index 9470e7e0..e39d4736 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -44,9 +44,9 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr levelPtr) auto menu = engine->getGUI()->getMenu(); menu->reset(); - controller = std::make_unique(engine, std::move(levelPtr)); - auto player = level->players->get(0); + controller = + std::make_unique(engine, std::move(levelPtr), player); playerController = std::make_unique( settings, level, @@ -60,21 +60,21 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr levelPtr) worldRenderer = std::make_unique( engine, *frontend, player ); - hud = std::make_unique(engine, *frontend, player); + hud = std::make_unique(engine, *frontend, *player); decorator = std::make_unique( *engine, *controller, *worldRenderer, assets, *player ); keepAlive(settings.graphics.backlight.observe([=](bool) { - controller->getLevel()->chunks->saveAndClear(); + player->chunks->saveAndClear(); worldRenderer->clear(); })); keepAlive(settings.camera.fov.observe([=](double value) { player->fpCamera->setFov(glm::radians(value)); })); keepAlive(Events::getBinding(BIND_CHUNKS_RELOAD).onactived.add([=](){ - controller->getLevel()->chunks->saveAndClear(); + player->chunks->saveAndClear(); worldRenderer->clear(); })); diff --git a/src/graphics/render/BlockWrapsRenderer.cpp b/src/graphics/render/BlockWrapsRenderer.cpp index 3ad30ad8..d19b1f1e 100644 --- a/src/graphics/render/BlockWrapsRenderer.cpp +++ b/src/graphics/render/BlockWrapsRenderer.cpp @@ -14,15 +14,18 @@ #include "window/Window.hpp" #include "world/Level.hpp" -BlockWrapsRenderer::BlockWrapsRenderer(const Assets& assets, const Level& level) - : assets(assets), level(level), batch(std::make_unique(1024)) { +BlockWrapsRenderer::BlockWrapsRenderer( + const Assets& assets, const Level& level, const Chunks& chunks +) + : assets(assets), + level(level), + chunks(chunks), + batch(std::make_unique(1024)) { } BlockWrapsRenderer::~BlockWrapsRenderer() = default; void BlockWrapsRenderer::draw(const BlockWrapper& wrapper) { - const auto& chunks = *level.chunks; - auto textureRegion = util::get_texture_region(assets, wrapper.texture, ""); auto& shader = assets.require("entity"); diff --git a/src/graphics/render/BlockWrapsRenderer.hpp b/src/graphics/render/BlockWrapsRenderer.hpp index 728e664a..9d6184b9 100644 --- a/src/graphics/render/BlockWrapsRenderer.hpp +++ b/src/graphics/render/BlockWrapsRenderer.hpp @@ -10,6 +10,7 @@ class Assets; class Player; class Level; +class Chunks; class DrawContext; struct BlockWrapper { @@ -20,6 +21,7 @@ struct BlockWrapper { class BlockWrapsRenderer { const Assets& assets; const Level& level; + const Chunks& chunks; std::unique_ptr batch; std::unordered_map> wrappers; @@ -27,7 +29,9 @@ class BlockWrapsRenderer { void draw(const BlockWrapper& wrapper); public: - BlockWrapsRenderer(const Assets& assets, const Level& level); + BlockWrapsRenderer( + const Assets& assets, const Level& level, const Chunks& chunks + ); ~BlockWrapsRenderer(); void draw(const DrawContext& ctx, const Player& player); diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index f669cfea..fe955bee 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -20,19 +20,22 @@ size_t ChunksRenderer::visibleChunks = 0; class RendererWorker : public util::Worker, RendererResult> { const Level& level; + const Chunks& chunks; BlocksRenderer renderer; public: RendererWorker( const Level& level, + const Chunks& chunks, const ContentGfxCache& cache, const EngineSettings& settings - ) : level(level), + ) : level(level), + chunks(chunks), renderer(settings.graphics.chunkMaxVertices.get(), *level.content, cache, settings) {} RendererResult operator()(const std::shared_ptr& chunk) override { - renderer.build(chunk.get(), level.chunks.get()); + renderer.build(chunk.get(), &chunks); if (renderer.isCancelled()) { return RendererResult { glm::ivec2(chunk->x, chunk->z), true, MeshData()}; @@ -44,29 +47,36 @@ public: }; ChunksRenderer::ChunksRenderer( - const Level* level, + const Level* level, + const Chunks& chunks, const Assets& assets, const Frustum& frustum, - const ContentGfxCache& cache, + const ContentGfxCache& cache, const EngineSettings& settings -) : level(*level), - assets(assets), - frustum(frustum), - settings(settings), - threadPool( - "chunks-render-pool", - [&](){return std::make_shared(*level, cache, settings);}, - [&](RendererResult& result){ - if (!result.cancelled) { - auto meshData = std::move(result.meshData); - meshes[result.key] = ChunkMesh { - std::make_unique(meshData.mesh), - std::move(meshData.sortingMesh) - }; - } - inwork.erase(result.key); - }, settings.graphics.chunkMaxRenderers.get()) -{ +) + : level(*level), + chunks(chunks), + assets(assets), + frustum(frustum), + settings(settings), + threadPool( + "chunks-render-pool", + [&]() { + return std::make_shared( + *level, chunks, cache, settings + ); + }, + [&](RendererResult& result) { + if (!result.cancelled) { + auto meshData = std::move(result.meshData); + meshes[result.key] = ChunkMesh { + std::make_unique(meshData.mesh), + std::move(meshData.sortingMesh)}; + } + inwork.erase(result.key); + }, + settings.graphics.chunkMaxRenderers.get() + ) { threadPool.setStopOnFail(false); renderer = std::make_unique( settings.graphics.chunkMaxVertices.get(), @@ -83,7 +93,7 @@ const Mesh* ChunksRenderer::render( ) { chunk->flags.modified = false; if (important) { - auto mesh = renderer->render(chunk.get(), level.chunks.get()); + auto mesh = renderer->render(chunk.get(), &chunks); meshes[glm::ivec2(chunk->x, chunk->z)] = ChunkMesh { std::move(mesh.mesh), std::move(mesh.sortingMeshData) }; @@ -131,7 +141,7 @@ void ChunksRenderer::update() { const Mesh* ChunksRenderer::retrieveChunk( size_t index, const Camera& camera, Shader& shader, bool culling ) { - auto chunk = level.chunks->getChunks()[index]; + auto chunk = chunks.getChunks()[index]; if (chunk == nullptr || !chunk->flags.lighted) { return nullptr; } @@ -163,7 +173,6 @@ const Mesh* ChunksRenderer::retrieveChunk( void ChunksRenderer::drawChunks( const Camera& camera, Shader& shader ) { - const auto& chunks = *level.chunks; const auto& atlas = assets.require("blocks"); atlas.getTexture()->bind(); @@ -232,7 +241,7 @@ void ChunksRenderer::drawSortedMeshes(const Camera& camera, Shader& shader) { frameid++; bool culling = settings.graphics.frustumCulling.get(); - const auto& chunks = level.chunks->getChunks(); + const auto& chunks = this->chunks.getChunks(); const auto& cameraPos = camera.position; const auto& atlas = assets.require("blocks"); diff --git a/src/graphics/render/ChunksRenderer.hpp b/src/graphics/render/ChunksRenderer.hpp index 859bca9e..a25c4241 100644 --- a/src/graphics/render/ChunksRenderer.hpp +++ b/src/graphics/render/ChunksRenderer.hpp @@ -20,6 +20,7 @@ class Level; class Camera; class Shader; class Assets; +class Chunks; class Frustum; class BlocksRenderer; class ContentGfxCache; @@ -42,6 +43,7 @@ struct RendererResult { class ChunksRenderer { const Level& level; + const Chunks& chunks; const Assets& assets; const Frustum& frustum; const EngineSettings& settings; @@ -57,6 +59,7 @@ class ChunksRenderer { public: ChunksRenderer( const Level* level, + const Chunks& chunks, const Assets& assets, const Frustum& frustum, const ContentGfxCache& cache, diff --git a/src/graphics/render/Decorator.cpp b/src/graphics/render/Decorator.cpp index 2f01955f..a3ef140b 100644 --- a/src/graphics/render/Decorator.cpp +++ b/src/graphics/render/Decorator.cpp @@ -85,7 +85,7 @@ void Decorator::update( int index = currentIndex; currentIndex = (currentIndex + BIG_PRIME) % UPDATE_BLOCKS; - const auto& chunks = *level.chunks; + const auto& chunks = *player.chunks; const auto& indices = *level.content->getIndices(); int lx = index % UPDATE_AREA_DIAMETER; @@ -108,7 +108,7 @@ void Decorator::update(float delta, const Camera& camera) { for (int i = 0; i < ITERATIONS; i++) { update(delta, pos, camera.position); } - const auto& chunks = *level.chunks; + const auto& chunks = *player.chunks; const auto& indices = *level.content->getIndices(); auto iter = blockEmitters.begin(); while (iter != blockEmitters.end()) { diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index 0e2292a5..80057352 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -16,12 +16,17 @@ size_t ParticlesRenderer::visibleParticles = 0; size_t ParticlesRenderer::aliveEmitters = 0; ParticlesRenderer::ParticlesRenderer( - const Assets& assets, const Level& level, const GraphicsSettings* settings + const Assets& assets, + const Level& level, + const Chunks& chunks, + const GraphicsSettings* settings ) : batch(std::make_unique(4096)), level(level), + chunks(chunks), assets(assets), - settings(settings) {} + settings(settings) { +} ParticlesRenderer::~ParticlesRenderer() = default; @@ -44,7 +49,6 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { const auto& right = camera.right; const auto& up = camera.up; - const auto& chunks = *level.chunks; bool backlight = settings->backlight.get(); std::vector unusedTextures; diff --git a/src/graphics/render/ParticlesRenderer.hpp b/src/graphics/render/ParticlesRenderer.hpp index 46509d96..3fe692e5 100644 --- a/src/graphics/render/ParticlesRenderer.hpp +++ b/src/graphics/render/ParticlesRenderer.hpp @@ -10,12 +10,14 @@ class Texture; class Assets; class Camera; +class Chunks; class MainBatch; class Level; struct GraphicsSettings; class ParticlesRenderer { const Level& level; + const Chunks& chunks; const Assets& assets; const GraphicsSettings* settings; std::unordered_map> particles; @@ -29,6 +31,7 @@ public: ParticlesRenderer( const Assets& assets, const Level& level, + const Chunks& chunks, const GraphicsSettings* settings ); ~ParticlesRenderer(); diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index a0fd4c83..4455e451 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -68,21 +68,24 @@ WorldRenderer::WorldRenderer( lineBatch(std::make_unique()), batch3d(std::make_unique(BATCH3D_CAPACITY)), modelBatch(std::make_unique( - MODEL_BATCH_CAPACITY, assets, *level.chunks, engine->getSettings() + MODEL_BATCH_CAPACITY, assets, *player->chunks, engine->getSettings() )), particles(std::make_unique( - assets, level, &engine->getSettings().graphics + assets, level, *player->chunks, &engine->getSettings().graphics )), texts(std::make_unique(*batch3d, assets, *frustumCulling)), guides(std::make_unique()), chunks(std::make_unique( &level, + *player->chunks, assets, *frustumCulling, frontend.getContentGfxCache(), engine->getSettings() )), - blockWraps(std::make_unique(assets, level)) { + blockWraps( + std::make_unique(assets, level, *player->chunks) + ) { auto& settings = engine->getSettings(); level.events->listen( EVT_CHUNK_HIDDEN, @@ -362,7 +365,7 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) { int x = std::floor(player->currentCamera->position.x); int y = std::floor(player->currentCamera->position.y); int z = std::floor(player->currentCamera->position.z); - auto block = level.chunks->get(x, y, z); + auto block = player->chunks->get(x, y, z); if (block && block->id) { const auto& def = level.content->getIndices()->blocks.require(block->id); @@ -381,7 +384,7 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) { batch3d->begin(); shader.uniformMatrix("u_projview", glm::mat4(1.0f)); shader.uniformMatrix("u_apply", glm::mat4(1.0f)); - auto light = level.chunks->getLight(x, y, z); + auto light = player->chunks->getLight(x, y, z); float s = Lightmap::extract(light, 3) / 15.0f; glm::vec4 tint( glm::min(1.0f, Lightmap::extract(light, 0) / 15.0f + s), diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp index bcc1f728..3c0b64c0 100644 --- a/src/logic/BlocksController.cpp +++ b/src/logic/BlocksController.cpp @@ -1,5 +1,7 @@ #include "BlocksController.hpp" +#include + #include "content/Content.hpp" #include "items/Inventories.hpp" #include "items/Inventory.hpp" @@ -11,12 +13,15 @@ #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" #include "voxels/voxel.hpp" +#include "voxels/blocks_agent.hpp" #include "world/Level.hpp" #include "world/World.hpp" +#include "objects/Player.hpp" +#include "objects/Players.hpp" -BlocksController::BlocksController(const Level& level, Lighting& lighting, uint padding) +BlocksController::BlocksController(const Level& level, Lighting* lighting, uint padding) : level(level), - chunks(*level.chunks), + chunks(*level.chunksStorage), lighting(lighting), randTickClock(20, 3), blocksTickClock(20, 1), @@ -34,7 +39,7 @@ void BlocksController::updateSides(int x, int y, int z) { } void BlocksController::updateSides(int x, int y, int z, int w, int h, int d) { - voxel* vox = chunks.get(x, y, z); + voxel* vox = blocks_agent::get(chunks, x, y, z); const auto& def = level.content->getIndices()->blocks.require(vox->id); const auto& rot = def.rotations.variants[vox->state.rotation]; const auto& xaxis = rot.axisX; @@ -62,8 +67,10 @@ void BlocksController::breakBlock( onBlockInteraction( player, glm::ivec3(x, y, z), def, BlockInteraction::destruction ); - chunks.set(x, y, z, 0, {}); - lighting.onBlockSet(x, y, z, 0); + blocks_agent::set(chunks, x, y, z, 0, {}); + if (lighting) { + lighting->onBlockSet(x, y, z, 0); + } scripting::on_block_broken(player, def, glm::ivec3(x, y, z)); if (def.rt.extended) { updateSides(x, y, z , def.size.x, def.size.y, def.size.z); @@ -75,7 +82,7 @@ void BlocksController::breakBlock( void BlocksController::placeBlock( Player* player, const Block& def, blockstate state, int x, int y, int z ) { - auto voxel = chunks.get(x, y, z); + auto voxel = blocks_agent::get(chunks, x, y, z); if (voxel == nullptr) { return; } @@ -85,8 +92,10 @@ void BlocksController::placeBlock( onBlockInteraction( player, glm::ivec3(x, y, z), def, BlockInteraction::placing ); - chunks.set(x, y, z, def.rt.id, state); - lighting.onBlockSet(x, y, z, def.rt.id); + blocks_agent::set(chunks, x, y, z, def.rt.id, state); + if (lighting) { + lighting->onBlockSet(x, y, z, def.rt.id); + } scripting::on_block_placed(player, def, glm::ivec3(x, y, z)); if (def.rt.extended) { updateSides(x, y, z , def.size.x, def.size.y, def.size.z); @@ -96,12 +105,12 @@ void BlocksController::placeBlock( } void BlocksController::updateBlock(int x, int y, int z) { - voxel* vox = chunks.get(x, y, z); + voxel* vox = blocks_agent::get(chunks, x, y, z); if (vox == nullptr) return; const auto& def = level.content->getIndices()->blocks.require(vox->id); if (def.grounded) { const auto& vec = get_ground_direction(def, vox->state.rotation); - if (!chunks.isSolidBlock(x + vec.x, y + vec.y, z + vec.z)) { + if (!blocks_agent::is_solid_at(chunks, x + vec.x, y + vec.y, z + vec.z)) { breakBlock(nullptr, def, x, y, z); return; } @@ -162,28 +171,48 @@ void BlocksController::randomTick( void BlocksController::randomTick(int tickid, int parts) { auto indices = level.content->getIndices(); - int width = chunks.getWidth(); - int height = chunks.getHeight(); - int segments = 4; - for (uint z = padding; z < height - padding; z++) { - for (uint x = padding; x < width - padding; x++) { - int index = z * width + x; - if ((index + tickid) % parts != 0) { - continue; + std::set chunksIterated; + + for (const auto& [pid, player] : *level.players) { + const auto& chunks = *player->chunks; + int offsetX = chunks.getOffsetX(); + int offsetY = chunks.getOffsetY(); + int width = chunks.getWidth(); + int height = chunks.getHeight(); + int segments = 4; + + for (uint z = padding; z < height - padding; z++) { + for (uint x = padding; x < width - padding; x++) { + int index = z * width + x; + if ((index + tickid) % parts != 0) { + continue; + } + auto& chunk = chunks.getChunks()[index]; + if (chunk == nullptr || !chunk->flags.lighted) { + continue; + } + union { + int32_t pos[2]; + uint64_t key; + } posU; + posU.pos[0] = x + offsetX; + posU.pos[1] = z + offsetY; + if (chunksIterated.find(posU.key) != chunksIterated.end()) { + continue; + } + chunksIterated.insert(posU.key); + randomTick(*chunk, segments, indices); } - auto& chunk = chunks.getChunks()[index]; - if (chunk == nullptr || !chunk->flags.lighted) { - continue; - } - randomTick(*chunk, segments, indices); } } } int64_t BlocksController::createBlockInventory(int x, int y, int z) { - auto chunk = chunks.getChunkByVoxel(x, y, z); - if (chunk == nullptr) { + auto chunk = blocks_agent::get_chunk( + chunks, floordiv(x), floordiv(z) + ); + if (chunk == nullptr || y < 0 || y >= CHUNK_H) { return 0; } int lx = x - chunk->x * CHUNK_W; @@ -203,7 +232,9 @@ int64_t BlocksController::createBlockInventory(int x, int y, int z) { } void BlocksController::bindInventory(int64_t invid, int x, int y, int z) { - auto chunk = chunks.getChunkByVoxel(x, y, z); + auto chunk = blocks_agent::get_chunk( + chunks, floordiv(x), floordiv(z) + ); if (chunk == nullptr) { throw std::runtime_error("block does not exists"); } @@ -216,7 +247,9 @@ void BlocksController::bindInventory(int64_t invid, int x, int y, int z) { } void BlocksController::unbindInventory(int x, int y, int z) { - auto chunk = chunks.getChunkByVoxel(x, y, z); + auto chunk = blocks_agent::get_chunk( + chunks, floordiv(x), floordiv(z) + ); if (chunk == nullptr) { throw std::runtime_error("block does not exists"); } diff --git a/src/logic/BlocksController.hpp b/src/logic/BlocksController.hpp index a8f286dc..1dd48983 100644 --- a/src/logic/BlocksController.hpp +++ b/src/logic/BlocksController.hpp @@ -14,19 +14,20 @@ class Level; class Chunk; class Chunks; class Lighting; +class GlobalChunks; class ContentIndices; enum class BlockInteraction { step, destruction, placing }; /// @brief Player argument is nullable using on_block_interaction = std::function< - void(Player*, const glm::ivec3&, const Block&, BlockInteraction type)>; + void(Player*, const glm::ivec3&, const Block&, BlockInteraction)>; /// BlocksController manages block updates and data (inventories, metadata) class BlocksController { const Level& level; - Chunks& chunks; - Lighting& lighting; + GlobalChunks& chunks; + Lighting* lighting; util::Clock randTickClock; util::Clock blocksTickClock; util::Clock worldTickClock; @@ -34,7 +35,7 @@ class BlocksController { FastRandom random {}; std::vector blockInteractionCallbacks; public: - BlocksController(const Level& level, Lighting& lighting, uint padding); + BlocksController(const Level& level, Lighting* lighting, uint padding); void updateSides(int x, int y, int z); void updateSides(int x, int y, int z, int w, int h, int d); diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 55ab262d..13c7ab86 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -11,6 +11,7 @@ #include "lighting/Lighting.hpp" #include "maths/voxmaths.hpp" #include "util/timeutil.hpp" +#include "objects/Player.hpp" #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" @@ -24,8 +25,6 @@ const uint MIN_SURROUNDING = 9; ChunksController::ChunksController(Level& level, uint padding) : level(level), - chunks(*level.chunks), - lighting(std::make_unique(level.content, level.chunks.get())), padding(padding), generator(std::make_unique( level.content->generators.require(level.getWorld()->getGenerator()), @@ -36,15 +35,19 @@ ChunksController::ChunksController(Level& level, uint padding) ChunksController::~ChunksController() = default; void ChunksController::update( - int64_t maxDuration, int loadDistance, int centerX, int centerY -) { + int64_t maxDuration, int loadDistance, Player& player +) const { + const auto& position = player.getPosition(); + int centerX = floordiv(position.x); + int centerY = floordiv(position.z); + generator->update(centerX, centerY, loadDistance); int64_t mcstotal = 0; for (uint i = 0; i < MAX_WORK_PER_FRAME; i++) { timeutil::Timer timer; - if (loadVisible()) { + if (loadVisible(player)) { int64_t mcs = timer.stop(); if (mcstotal + mcs < maxDuration * 1000) { mcstotal += mcs; @@ -55,7 +58,8 @@ void ChunksController::update( } } -bool ChunksController::loadVisible() { +bool ChunksController::loadVisible(const Player& player) const { + const auto& chunks = *player.chunks; int sizeX = chunks.getWidth(); int sizeY = chunks.getHeight(); @@ -69,7 +73,7 @@ bool ChunksController::loadVisible() { auto& chunk = chunks.getChunks()[index]; if (chunk != nullptr) { if (chunk->flags.loaded && !chunk->flags.lighted) { - if (buildLights(chunk)) { + if (buildLights(player, chunk)) { return true; } } @@ -93,32 +97,35 @@ bool ChunksController::loadVisible() { } int offsetX = chunks.getOffsetX(); int offsetY = chunks.getOffsetY(); - createChunk(nearX + offsetX, nearZ + offsetY); + createChunk(player, nearX + offsetX, nearZ + offsetY); return true; } -bool ChunksController::buildLights(const std::shared_ptr& chunk) { +bool ChunksController::buildLights(const Player& player, const std::shared_ptr& chunk) const { int surrounding = 0; for (int oz = -1; oz <= 1; oz++) { for (int ox = -1; ox <= 1; ox++) { - if (chunks.getChunk(chunk->x + ox, chunk->z + oz)) surrounding++; + if (player.chunks->getChunk(chunk->x + ox, chunk->z + oz)) + surrounding++; } } if (surrounding == MIN_SURROUNDING) { - bool lightsCache = chunk->flags.loadedLights; - if (!lightsCache) { - lighting->buildSkyLight(chunk->x, chunk->z); + if (lighting) { + bool lightsCache = chunk->flags.loadedLights; + if (!lightsCache) { + lighting->buildSkyLight(chunk->x, chunk->z); + } + lighting->onChunkLoaded(chunk->x, chunk->z, !lightsCache); } - lighting->onChunkLoaded(chunk->x, chunk->z, !lightsCache); chunk->flags.lighted = true; return true; } return false; } -void ChunksController::createChunk(int x, int z) { +void ChunksController::createChunk(const Player& player, int x, int z) const { auto chunk = level.chunksStorage->create(x, z); - chunks.putChunk(chunk); + player.chunks->putChunk(chunk); auto& chunkFlags = chunk->flags; if (!chunkFlags.loaded) { diff --git a/src/logic/ChunksController.hpp b/src/logic/ChunksController.hpp index 71dada6f..6272fd01 100644 --- a/src/logic/ChunksController.hpp +++ b/src/logic/ChunksController.hpp @@ -7,6 +7,7 @@ class Level; class Chunk; class Chunks; +class Player; class Lighting; class WorldGenerator; @@ -14,14 +15,13 @@ class WorldGenerator; class ChunksController { private: Level& level; - Chunks& chunks; uint padding; std::unique_ptr generator; /// @brief Process one chunk: load it or calculate lights for it - bool loadVisible(); - bool buildLights(const std::shared_ptr& chunk); - void createChunk(int x, int y); + bool loadVisible(const Player& player) const; + bool buildLights(const Player& player, const std::shared_ptr& chunk) const; + void createChunk(const Player& player, int x, int y) const; public: std::unique_ptr lighting; @@ -29,11 +29,7 @@ public: ~ChunksController(); /// @param maxDuration milliseconds reserved for chunks loading - void update( - int64_t maxDuration, - int loadDistance, - int centerX, - int centerY); + void update(int64_t maxDuration, int loadDistance, Player& player) const; const WorldGenerator* getGenerator() const { return generator.get(); diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 2f363dad..409a81b4 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -10,21 +10,33 @@ #include "objects/Players.hpp" #include "objects/Player.hpp" #include "physics/Hitbox.hpp" +#include "voxels/Chunks.hpp" #include "scripting/scripting.hpp" +#include "lighting/Lighting.hpp" #include "settings.hpp" #include "world/Level.hpp" #include "world/World.hpp" static debug::Logger logger("level-control"); -LevelController::LevelController(Engine* engine, std::unique_ptr levelPtr) +LevelController::LevelController( + Engine* engine, std::unique_ptr levelPtr, Player* clientPlayer +) : settings(engine->getSettings()), level(std::move(levelPtr)), chunks(std::make_unique( *level, settings.chunks.padding.get() )) { + + if (clientPlayer) { + chunks->lighting = std::make_unique( + level->content, clientPlayer->chunks.get() + ); + } blocks = std::make_unique( - *level, *chunks->lighting, settings.chunks.padding.get() + *level, + chunks ? chunks->lighting.get() : nullptr, + settings.chunks.padding.get() ); scripting::on_world_load(this); } @@ -32,14 +44,15 @@ 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( + player->chunks->configure( position.x, position.z, - settings.chunks.loadDistance.get() + settings.chunks.padding.get() * 2 + settings.chunks.loadDistance.get() + settings.chunks.padding.get() ); chunks->update( - settings.chunks.loadSpeed.get(), settings.chunks.loadDistance.get(), - floordiv(position.x, CHUNK_W), floordiv(position.z, CHUNK_D) + settings.chunks.loadSpeed.get(), + settings.chunks.loadDistance.get(), + *player ); } if (!pause) { diff --git a/src/logic/LevelController.hpp b/src/logic/LevelController.hpp index 6cfcf816..5363976b 100644 --- a/src/logic/LevelController.hpp +++ b/src/logic/LevelController.hpp @@ -18,7 +18,7 @@ class LevelController { std::unique_ptr blocks; std::unique_ptr chunks; public: - LevelController(Engine* engine, std::unique_ptr level); + LevelController(Engine* engine, std::unique_ptr level, Player* clientPlayer); /// @param delta time elapsed since the last update /// @param pause is world and player simulation paused diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index 827b42c1..6159a389 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -215,7 +215,7 @@ void PlayerController::onFootstep(const Hitbox& hitbox) { int x = std::floor(pos.x + half.x * offsetX); int y = std::floor(pos.y - half.y * 1.1f); int z = std::floor(pos.z + half.z * offsetZ); - auto vox = level->chunks->get(x, y, z); + auto vox = player->chunks->get(x, y, z); if (vox) { auto& def = level->content->getIndices()->blocks.require(vox->id); if (!def.obstacle) { @@ -274,7 +274,7 @@ void PlayerController::postUpdate(float delta, bool input, bool pause) { camControl.updateMouse(this->input); } player->postUpdate(); - camControl.update(this->input, pause ? 0.0f : delta, level->chunks.get()); + camControl.update(this->input, pause ? 0.0f : delta, player->chunks.get()); if (input) { updateInteraction(delta); } else { @@ -366,14 +366,14 @@ static void pick_block( voxel* PlayerController::updateSelection(float maxDistance) { auto indices = level->content->getIndices(); - auto chunks = level->chunks.get(); + auto& chunks = *player->chunks; auto camera = player->fpCamera.get(); auto& selection = player->selection; glm::vec3 end; glm::ivec3 iend; glm::ivec3 norm; - voxel* vox = chunks->rayCast( + voxel* vox = chunks.rayCast( camera->position, camera->front, maxDistance, end, norm, iend ); if (vox) { @@ -411,12 +411,12 @@ voxel* PlayerController::updateSelection(float maxDistance) { blockstate selectedState = vox->state; selection.vox = *vox; if (selectedState.segment) { - selection.position = chunks->seekOrigin( + selection.position = chunks.seekOrigin( iend, indices->blocks.require(selection.vox.id), selectedState ); - auto origin = chunks->get(selection.position); + auto origin = chunks.get(selection.position); if (origin && origin->id != vox->id) { - chunks->set(iend.x, iend.y, iend.z, 0, {}); + chunks.set(iend.x, iend.y, iend.z, 0, {}); return updateSelection(maxDistance); } } else { @@ -429,7 +429,7 @@ voxel* PlayerController::updateSelection(float maxDistance) { void PlayerController::processRightClick(const Block& def, const Block& target) { const auto& selection = player->selection; - auto chunks = level->chunks.get(); + auto& chunks = *player->chunks; auto camera = player->fpCamera.get(); blockstate state {}; @@ -458,16 +458,16 @@ void PlayerController::processRightClick(const Block& def, const Block& target) } } } - auto vox = chunks->get(coord); + auto vox = chunks.get(coord); if (vox == nullptr) { return; } - if (!chunks->checkReplaceability(def, state, coord)) { + if (!chunks.checkReplaceability(def, state, coord)) { return; } if (def.grounded) { const auto& vec = get_ground_direction(def, state.rotation); - if (!chunks->isSolidBlock( + if (!chunks.isSolidBlock( coord.x + vec.x, coord.y + vec.y, coord.z + vec.z )) { return; @@ -502,7 +502,7 @@ void PlayerController::updateEntityInteraction( void PlayerController::updateInteraction(float delta) { auto indices = level->content->getIndices(); - auto chunks = level->chunks.get(); + auto chunks = player->chunks.get(); const auto& selection = player->selection; if (interactionTimer > 0.0f) { diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 24b00124..180a959f 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -40,7 +40,9 @@ static int l_is_solid_at(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - return lua::pushboolean(L, blocks_agent::is_solid_at(*level->chunks, x, y, z)); + return lua::pushboolean( + L, blocks_agent::is_solid_at(*level->chunksStorage, x, y, z) + ); } static int l_count(lua::State* L) { diff --git a/src/logic/scripting/lua/libs/libentity.cpp b/src/logic/scripting/lua/libs/libentity.cpp index ba3bd349..bf547f95 100644 --- a/src/logic/scripting/lua/libs/libentity.cpp +++ b/src/logic/scripting/lua/libs/libentity.cpp @@ -9,6 +9,7 @@ #include "physics/Hitbox.hpp" #include "voxels/Chunks.hpp" #include "voxels/Block.hpp" +#include "voxels/blocks_agent.hpp" #include "window/Camera.hpp" using namespace scripting; @@ -149,8 +150,15 @@ static int l_raycast(lua::State* L) { blockid_t block = BLOCK_VOID; - if (auto voxel = level->chunks->rayCast( - start, dir, maxDistance, end, normal, iend, filteredBlocks + if (auto voxel = blocks_agent::raycast( + *level->chunksStorage, + start, + dir, + maxDistance, + end, + normal, + iend, + filteredBlocks )) { maxDistance = glm::distance(start, end); block = voxel->id; diff --git a/src/logic/scripting/lua/libs/libgeneration.cpp b/src/logic/scripting/lua/libs/libgeneration.cpp index 034f6597..00d767c2 100644 --- a/src/logic/scripting/lua/libs/libgeneration.cpp +++ b/src/logic/scripting/lua/libs/libgeneration.cpp @@ -28,7 +28,7 @@ static int l_create_fragment(lua::State* L) { bool saveEntities = lua::toboolean(L, 4); auto fragment = - VoxelFragment::create(level, pointA, pointB, crop, saveEntities); + VoxelFragment::create(*level, pointA, pointB, crop, saveEntities); return lua::newuserdata( L, std::shared_ptr(std::move(fragment)) ); diff --git a/src/logic/scripting/lua/libs/libhud.cpp b/src/logic/scripting/lua/libs/libhud.cpp index 7f5ec46d..f12ba0a7 100644 --- a/src/logic/scripting/lua/libs/libhud.cpp +++ b/src/logic/scripting/lua/libs/libhud.cpp @@ -14,6 +14,7 @@ #include "voxels/Block.hpp" #include "voxels/Chunks.hpp" #include "voxels/voxel.hpp" +#include "voxels/blocks_agent.hpp" #include "world/Level.hpp" #include "api_lua.hpp" @@ -59,7 +60,7 @@ static int l_open_block(lua::State* L) { auto z = lua::tointeger(L, 3); bool playerInventory = !lua::toboolean(L, 4); - auto vox = level->chunks->get(x, y, z); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); if (vox == nullptr) { throw std::runtime_error( "block does not exists at " + std::to_string(x) + " " + diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 58e2bdc4..16e81d56 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -125,7 +125,7 @@ static int l_get_generator(lua::State* L) { static int l_get_chunk_data(lua::State* L) { int x = (int)lua::tointeger(L, 1); int y = (int)lua::tointeger(L, 2); - const auto& chunk = level->chunks->getChunk(x, y); + const auto& chunk = level->chunksStorage->getChunk(x, y); if (chunk == nullptr) { lua::pushnil(L); return 0; @@ -181,8 +181,8 @@ static int l_set_chunk_data(lua::State* L) { if (lua::gettop(L) >= 4) { is_compressed = lua::toboolean(L, 4); } - auto chunk = level->chunks->getChunk(x, y); - if(chunk== nullptr){ + auto chunk = level->chunksStorage->getChunk(x, y); + if (chunk == nullptr) { return 0; } if (is_compressed) { @@ -217,22 +217,22 @@ static int l_set_chunk_data(lua::State* L) { chunk->flags.modified = true; lighting.onChunkLoaded(x, y, true); - chunk = level->chunks->getChunk(x - 1, y); + chunk = level->chunksStorage->getChunk(x - 1, y); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x - 1, y, true); } - chunk = level->chunks->getChunk(x + 1, y); + chunk = level->chunksStorage->getChunk(x + 1, y); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x + 1, y, true); } - chunk = level->chunks->getChunk(x, y - 1); + chunk = level->chunksStorage->getChunk(x, y - 1); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x, y - 1, true); } - chunk = level->chunks->getChunk(x, y + 1); + chunk = level->chunksStorage->getChunk(x, y + 1); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x, y + 1, true); diff --git a/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp b/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp index f123be6f..4a7af971 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp @@ -26,7 +26,7 @@ static int l_place(lua::State* L) { auto offset = tovec3(L, 2); int rotation = tointeger(L, 3) & 0b11; fragment->getFragment()->place( - *scripting::level->chunks, offset, rotation + *scripting::level->chunksStorage, offset, rotation ); } return 0; diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 79c0ba66..d2e5742d 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -459,7 +459,7 @@ void Entities::updatePhysics(float delta) { float vel = glm::length(prevVel); int substeps = static_cast(delta * vel * 20); substeps = std::min(100, std::max(2, substeps)); - physics->step(level->chunks.get(), &hitbox, delta, substeps, eid.uid); + physics->step(*level->chunksStorage, &hitbox, delta, substeps, eid.uid); hitbox.linearDamping = hitbox.grounded * 24; transform.setPos(hitbox.position); if (hitbox.grounded && !grounded) { diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index 0dd58f91..0a2106ed 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -205,12 +205,12 @@ void Player::attemptToFindSpawnpoint() { position.z + (rand() % 200 - 100) ); while (newpos.y > 0 && - !level->chunks->isObstacleBlock(newpos.x, newpos.y - 2, newpos.z)) { + !chunks->isObstacleBlock(newpos.x, newpos.y - 2, newpos.z)) { newpos.y--; } - voxel* headvox = level->chunks->get(newpos.x, newpos.y + 1, newpos.z); - if (level->chunks->isObstacleBlock(newpos.x, newpos.y, newpos.z) || + voxel* headvox = chunks->get(newpos.x, newpos.y + 1, newpos.z); + if (chunks->isObstacleBlock(newpos.x, newpos.y, newpos.z) || headvox == nullptr || headvox->id != 0) { return; } diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 95954380..5f7824a1 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -3,7 +3,7 @@ #include "maths/aabb.hpp" #include "voxels/Block.hpp" -#include "voxels/Chunks.hpp" +#include "voxels/GlobalChunks.hpp" #include "voxels/voxel.hpp" #include @@ -18,7 +18,7 @@ PhysicsSolver::PhysicsSolver(glm::vec3 gravity) : gravity(gravity) { } void PhysicsSolver::step( - Chunks* chunks, + const GlobalChunks& chunks, Hitbox* hitbox, float delta, uint substeps, @@ -63,7 +63,7 @@ void PhysicsSolver::step( float x = (px-half.x+E) + ix * s; for (int iz = 0; iz <= (half.z-E)*2/s; iz++){ float z = (pos.z-half.z+E) + iz * s; - if (chunks->isObstacleAt(x,y,z)){ + if (chunks.isObstacleAt(x,y,z)){ hitbox->grounded = true; break; } @@ -77,7 +77,7 @@ void PhysicsSolver::step( float x = (pos.x-half.x+E) + ix * s; for (int iz = 0; iz <= (half.z-E)*2/s; iz++){ float z = (pz-half.z+E) + iz * s; - if (chunks->isObstacleAt(x,y,z)){ + if (chunks.isObstacleAt(x,y,z)){ hitbox->grounded = true; break; } @@ -119,8 +119,8 @@ void PhysicsSolver::step( } static float calc_step_height( - Chunks* chunks, - glm::vec3& pos, + const GlobalChunks& chunks, + const glm::vec3& pos, const glm::vec3& half, float stepHeight, float s @@ -130,7 +130,7 @@ static float calc_step_height( float x = (pos.x-half.x+E) + ix * s; for (int iz = 0; iz <= (half.z-E)*2/s; iz++) { float z = (pos.z-half.z+E) + iz * s; - if (chunks->isObstacleAt(x, pos.y+half.y+stepHeight, z)) { + if (chunks.isObstacleAt(x, pos.y+half.y+stepHeight, z)) { return 0.0f; } } @@ -141,7 +141,7 @@ static float calc_step_height( template static bool calc_collision_neg( - Chunks* chunks, + const GlobalChunks& chunks, glm::vec3& pos, glm::vec3& vel, const glm::vec3& half, @@ -159,7 +159,7 @@ static bool calc_collision_neg( coord[nz] = (pos[nz]-half[nz]+E) + iz * s; coord[nx] = (pos[nx]-half[nx]-E); - if (const auto aabb = chunks->isObstacleAt(coord.x, coord.y, coord.z)) { + if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z)) { vel[nx] = 0.0f; float newx = std::floor(coord[nx]) + aabb->max()[nx] + half[nx] + E; if (std::abs(newx-pos[nx]) <= MAX_FIX) { @@ -174,7 +174,7 @@ static bool calc_collision_neg( template static void calc_collision_pos( - Chunks* chunks, + const GlobalChunks& chunks, glm::vec3& pos, glm::vec3& vel, const glm::vec3& half, @@ -191,7 +191,7 @@ static void calc_collision_pos( for (int iz = 0; iz <= (half[nz]-E)*2/s; iz++) { coord[nz] = (pos[nz]-half[nz]+E) + iz * s; coord[nx] = (pos[nx]+half[nx]+E); - if (const auto aabb = chunks->isObstacleAt(coord.x, coord.y, coord.z)) { + if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z)) { vel[nx] = 0.0f; float newx = std::floor(coord[nx]) - half[nx] + aabb->min()[nx] - E; if (std::abs(newx-pos[nx]) <= MAX_FIX) { @@ -204,7 +204,7 @@ static void calc_collision_pos( } void PhysicsSolver::colisionCalc( - Chunks* chunks, + const GlobalChunks& chunks, Hitbox* hitbox, glm::vec3& vel, glm::vec3& pos, @@ -234,7 +234,7 @@ void PhysicsSolver::colisionCalc( for (int iz = 0; iz <= (half.z-E)*2/s; iz++) { float z = (pos.z-half.z+E) + iz * s; float y = (pos.y-half.y+E); - if ((aabb = chunks->isObstacleAt(x,y,z))){ + if ((aabb = chunks.isObstacleAt(x,y,z))){ vel.y = 0.0f; float newy = std::floor(y) + aabb->max().y + half.y; if (std::abs(newy-pos.y) <= MAX_FIX+stepHeight) { @@ -251,7 +251,7 @@ void PhysicsSolver::colisionCalc( for (int iz = 0; iz <= (half.z-E)*2/s; iz++) { float z = (pos.z-half.z+E) + iz * s; float y = (pos.y+half.y+E); - if ((aabb = chunks->isObstacleAt(x,y,z))){ + if ((aabb = chunks.isObstacleAt(x,y,z))){ vel.y = 0.0f; float newy = std::floor(y) - half.y + aabb->min().y - E; if (std::abs(newy-pos.y) <= MAX_FIX) { diff --git a/src/physics/PhysicsSolver.hpp b/src/physics/PhysicsSolver.hpp index 35f8158e..5fa154fc 100644 --- a/src/physics/PhysicsSolver.hpp +++ b/src/physics/PhysicsSolver.hpp @@ -9,7 +9,7 @@ #include class Block; -class Chunks; +class GlobalChunks; struct Sensor; class PhysicsSolver { @@ -18,14 +18,14 @@ class PhysicsSolver { public: PhysicsSolver(glm::vec3 gravity); void step( - Chunks* chunks, + const GlobalChunks& chunks, Hitbox* hitbox, float delta, uint substeps, entityid_t entity ); void colisionCalc( - Chunks* chunks, + const GlobalChunks& chunks, Hitbox* hitbox, glm::vec3& vel, glm::vec3& pos, diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 91b05d69..37eae563 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -38,6 +38,14 @@ Chunks::Chunks( }); } +void Chunks::configure(int32_t x, int32_t z, uint32_t radius) { + setCenter(x, z); + uint32_t diameter = radius * 2LL; + if (getWidth() != diameter) { + resize(diameter, diameter); + } +} + voxel* Chunks::get(int32_t x, int32_t y, int32_t z) const { return blocks_agent::get(*this, x, y, z); } diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index c1c75e9c..86e02255 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -50,6 +50,8 @@ public: ); ~Chunks() = default; + void configure(int32_t x, int32_t z, uint32_t radius); + bool putChunk(const std::shared_ptr& chunk); Chunk* getChunk(int32_t x, int32_t z) const; diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp index 98a2d40a..a85be528 100644 --- a/src/voxels/GlobalChunks.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -10,6 +10,7 @@ #include "lighting/Lightmap.hpp" #include "maths/voxmaths.hpp" #include "objects/Entities.hpp" +#include "voxels/blocks_agent.hpp" #include "typedefs.hpp" #include "world/Level.hpp" #include "world/World.hpp" @@ -198,3 +199,7 @@ void GlobalChunks::saveAll() { void GlobalChunks::putChunk(std::shared_ptr chunk) { chunksMap[keyfrom(chunk->x, chunk->z)] = std::move(chunk); } + +const AABB* GlobalChunks::isObstacleAt(float x, float y, float z) const { + return blocks_agent::is_obstacle_at(*this, x, y, z); +} diff --git a/src/voxels/GlobalChunks.hpp b/src/voxels/GlobalChunks.hpp index afbdf67c..b4bb45e9 100644 --- a/src/voxels/GlobalChunks.hpp +++ b/src/voxels/GlobalChunks.hpp @@ -11,6 +11,7 @@ class Chunk; class Level; +struct AABB; class ContentIndices; class GlobalChunks { @@ -51,6 +52,8 @@ public: void putChunk(std::shared_ptr chunk); + const AABB* isObstacleAt(float x, float y, float z) const; + inline Chunk* getChunk(int cx, int cz) const { const auto& found = chunksMap.find(keyfrom(cx, cz)); if (found == chunksMap.end()) { diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp index ab39ea29..a23fa9ef 100644 --- a/src/voxels/blocks_agent.cpp +++ b/src/voxels/blocks_agent.cpp @@ -265,3 +265,105 @@ voxel* blocks_agent::raycast( ) { return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter); } + +// reduce nesting on next modification +// 25.06.2024: not now +// 11.11.2024: not now +template +inline void get_voxels_impl( + const Storage& chunks, VoxelsVolume* volume, bool backlight +) { + const auto& blocks = chunks.getContentIndices().blocks; + voxel* voxels = volume->getVoxels(); + light_t* lights = volume->getLights(); + int x = volume->getX(); + int y = volume->getY(); + int z = volume->getZ(); + + int w = volume->getW(); + int h = volume->getH(); + int d = volume->getD(); + + int scx = floordiv(x); + int scz = floordiv(z); + + int ecx = floordiv(x + w); + int ecz = floordiv(z + d); + + int cw = ecx - scx + 1; + int cd = ecz - scz + 1; + + // cw*cd chunks will be scanned + for (int cz = scz; cz < scz + cd; cz++) { + for (int cx = scx; cx < scx + cw; cx++) { + const auto chunk = get_chunk(chunks, cx, cz); + if (chunk == nullptr) { + // no chunk loaded -> filling with BLOCK_VOID + for (int ly = y; ly < y + h; ly++) { + for (int lz = std::max(z, cz * CHUNK_D); + lz < std::min(z + d, (cz + 1) * CHUNK_D); + lz++) { + for (int lx = std::max(x, cx * CHUNK_W); + lx < std::min(x + w, (cx + 1) * CHUNK_W); + lx++) { + uint idx = vox_index(lx - x, ly - y, lz - z, w, d); + voxels[idx].id = BLOCK_VOID; + lights[idx] = 0; + } + } + } + } else { + const voxel* cvoxels = chunk->voxels; + const light_t* clights = chunk->lightmap.getLights(); + for (int ly = y; ly < y + h; ly++) { + for (int lz = std::max(z, cz * CHUNK_D); + lz < std::min(z + d, (cz + 1) * CHUNK_D); + lz++) { + for (int lx = std::max(x, cx * CHUNK_W); + lx < std::min(x + w, (cx + 1) * CHUNK_W); + lx++) { + uint vidx = vox_index(lx - x, ly - y, lz - z, w, d); + uint cidx = vox_index( + lx - cx * CHUNK_W, + ly, + lz - cz * CHUNK_D, + CHUNK_W, + CHUNK_D + ); + voxels[vidx] = cvoxels[cidx]; + light_t light = clights[cidx]; + if (backlight) { + const auto block = blocks.get(voxels[vidx].id); + if (block && block->lightPassing) { + light = Lightmap::combine( + std::min(15, + Lightmap::extract(light, 0) + 1), + std::min(15, + Lightmap::extract(light, 1) + 1), + std::min(15, + Lightmap::extract(light, 2) + 1), + std::min(15, + static_cast(Lightmap::extract(light, 3))) + ); + } + } + lights[vidx] = light; + } + } + } + } + } + } +} + +void blocks_agent::get_voxels( + const Chunks& chunks, VoxelsVolume* volume, bool backlight +) { + get_voxels_impl(chunks, volume, backlight); +} + +void blocks_agent::get_voxels( + const GlobalChunks& chunks, VoxelsVolume* volume, bool backlight +) { + get_voxels_impl(chunks, volume, backlight); +} diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index e2c4266e..72cdd5ab 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -6,6 +6,7 @@ #include "Block.hpp" #include "Chunk.hpp" #include "Chunks.hpp" +#include "VoxelsVolume.hpp" #include "GlobalChunks.hpp" #include "constants.hpp" #include "typedefs.hpp" @@ -17,6 +18,8 @@ #include #include +struct AABB; + namespace blocks_agent { /// @brief Get specified chunk. @@ -432,4 +435,42 @@ voxel* raycast( std::set filter ); +void get_voxels(const Chunks& chunks, VoxelsVolume* volume, bool backlight=false); + +void get_voxels(const GlobalChunks& chunks, VoxelsVolume* volume, bool backlight=false); + +template +inline const AABB* is_obstacle_at(const Storage& chunks, float x, float y, float z) { + int ix = std::floor(x); + int iy = std::floor(y); + int iz = std::floor(z); + voxel* v = get(chunks, ix, iy, iz); + if (v == nullptr) { + if (iy >= CHUNK_H) { + return nullptr; + } else { + static const AABB empty; + return ∅ + } + } + const auto& def = chunks.getContentIndices().blocks.require(v->id); + if (def.obstacle) { + glm::ivec3 offset {}; + if (v->state.segment) { + glm::ivec3 point(ix, iy, iz); + offset = seek_origin(chunks, point, def, v->state) - point; + } + const auto& boxes = + def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes; + for (const auto& hitbox : boxes) { + if (hitbox.contains( + {x - ix - offset.x, y - iy - offset.y, z - iz - offset.z} + )) { + return &hitbox; + } + } + } + return nullptr; +} + } // blocks_agent diff --git a/src/world/Level.cpp b/src/world/Level.cpp index c289579f..3625f62e 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -4,7 +4,6 @@ #include "data/dv_util.hpp" #include "items/Inventories.hpp" #include "items/Inventory.hpp" -#include "lighting/Lighting.hpp" #include "objects/Entities.hpp" #include "objects/Player.hpp" #include "objects/Players.hpp" @@ -12,7 +11,6 @@ #include "physics/PhysicsSolver.hpp" #include "settings.hpp" #include "voxels/Chunk.hpp" -#include "voxels/Chunks.hpp" #include "voxels/GlobalChunks.hpp" #include "window/Camera.hpp" #include "LevelEvents.hpp" @@ -60,31 +58,11 @@ Level::Level( 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, events.get(), content->getIndices() - ); - inventories = std::make_unique(*this); } Level::~Level() = default; -void Level::loadMatrix(int32_t x, int32_t z, uint32_t radius) { - chunks->setCenter(x, z); - uint32_t diameter = std::min( - radius * 2LL, - (settings.chunks.loadDistance.get() + settings.chunks.padding.get()) * - 2LL - ); - if (chunks->getWidth() != diameter) { - chunks->resize(diameter, diameter); - } -} - World* Level::getWorld() { return world.get(); } diff --git a/src/world/Level.hpp b/src/world/Level.hpp index b30bde3b..9994c01d 100644 --- a/src/world/Level.hpp +++ b/src/world/Level.hpp @@ -9,11 +9,9 @@ class Content; class World; -class Chunks; class Entities; class Inventories; class LevelEvents; -class Lighting; class PhysicsSolver; class GlobalChunks; class Camera; @@ -27,7 +25,6 @@ class Level { public: const Content* const content; - std::unique_ptr chunks; std::unique_ptr chunksStorage; std::unique_ptr inventories; @@ -44,8 +41,6 @@ public: ); ~Level(); - void loadMatrix(int32_t x, int32_t z, uint32_t radius); - World* getWorld(); const World* getWorld() const; diff --git a/src/world/generator/VoxelFragment.cpp b/src/world/generator/VoxelFragment.cpp index 2d7ab923..af92dc7e 100644 --- a/src/world/generator/VoxelFragment.cpp +++ b/src/world/generator/VoxelFragment.cpp @@ -10,11 +10,12 @@ #include "voxels/Block.hpp" #include "voxels/GlobalChunks.hpp" #include "voxels/VoxelsVolume.hpp" +#include "voxels/blocks_agent.hpp" #include "world/Level.hpp" #include "core_defs.hpp" std::unique_ptr VoxelFragment::create( - Level* level, + const Level& level, const glm::ivec3& a, const glm::ivec3& b, bool crop, @@ -26,7 +27,7 @@ std::unique_ptr VoxelFragment::create( if (crop) { VoxelsVolume volume(size.x, size.y, size.z); volume.setPosition(start.x, start.y, start.z); - level->chunks->getVoxels(&volume); + blocks_agent::get_voxels(*level.chunksStorage, &volume); auto end = start + size; @@ -51,14 +52,14 @@ std::unique_ptr VoxelFragment::create( VoxelsVolume volume(size.x, size.y, size.z); volume.setPosition(start.x, start.y, start.z); - level->chunks->getVoxels(&volume); + blocks_agent::get_voxels(*level.chunksStorage, &volume); auto volVoxels = volume.getVoxels(); std::vector voxels(size.x * size.y * size.z); std::vector blockNames {CORE_AIR}; std::unordered_map blocksRegistered {{0, 0}}; - auto contentIndices = level->content->getIndices(); + auto contentIndices = level.content->getIndices(); for (size_t i = 0 ; i < voxels.size(); i++) { blockid_t id = volVoxels[i].id; blockid_t index; @@ -170,7 +171,7 @@ void VoxelFragment::prepare(const Content& content) { } void VoxelFragment::place( - Chunks& chunks, const glm::ivec3& offset, ubyte rotation + GlobalChunks& chunks, const glm::ivec3& offset, ubyte rotation ) { auto& structVoxels = getRuntimeVoxels(); for (int y = 0; y < size.y; y++) { @@ -185,7 +186,9 @@ void VoxelFragment::place( const auto& structVoxel = structVoxels[vox_index(x, y, z, size.x, size.z)]; if (structVoxel.id) { - chunks.set(sx, sy, sz, structVoxel.id, structVoxel.state); + blocks_agent::set( + chunks, sx, sy, sz, structVoxel.id, structVoxel.state + ); } } } diff --git a/src/world/generator/VoxelFragment.hpp b/src/world/generator/VoxelFragment.hpp index 701cac0e..6efa1998 100644 --- a/src/world/generator/VoxelFragment.hpp +++ b/src/world/generator/VoxelFragment.hpp @@ -10,7 +10,7 @@ inline constexpr int STRUCTURE_FORMAT_VERSION = 1; class Level; class Content; -class Chunks; +class GlobalChunks; class VoxelFragment : public Serializable { glm::ivec3 size; @@ -45,13 +45,13 @@ public: /// @brief Place fragment to the world /// @param offset target location /// @param rotation rotation index - void place(Chunks& chunks, const glm::ivec3& offset, ubyte rotation); + void place(GlobalChunks& chunks, const glm::ivec3& offset, ubyte rotation); /// @brief Create structure copy rotated 90 deg. clockwise std::unique_ptr rotated(const Content& content) const; static std::unique_ptr create( - Level* level, + const Level& level, const glm::ivec3& a, const glm::ivec3& b, bool crop, From 49dd866c22fd8d167efecc9cb575d16afd4d3531 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 20:43:42 +0300 Subject: [PATCH 081/246] update Chunks::configure --- src/voxels/Chunks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 37eae563..960b8f6c 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -40,7 +40,7 @@ Chunks::Chunks( void Chunks::configure(int32_t x, int32_t z, uint32_t radius) { setCenter(x, z); - uint32_t diameter = radius * 2LL; + uint32_t diameter = radius * 2LL + 1; if (getWidth() != diameter) { resize(diameter, diameter); } From b1fc666448e66b63a06fe7320305fa9f1efa035d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 20:56:25 +0300 Subject: [PATCH 082/246] fix --- src/voxels/Chunks.cpp | 2 +- src/world/generator/WorldGenerator.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 960b8f6c..37eae563 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -40,7 +40,7 @@ Chunks::Chunks( void Chunks::configure(int32_t x, int32_t z, uint32_t radius) { setCenter(x, z); - uint32_t diameter = radius * 2LL + 1; + uint32_t diameter = radius * 2LL; if (getWidth() != diameter) { resize(diameter, diameter); } diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 281ac411..ef3c3f7f 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -354,8 +354,8 @@ void WorldGenerator::generateHeightmap( void WorldGenerator::update(int centerX, int centerY, int loadDistance) { surroundMap.setCenter(centerX, centerY); - // 1 is safety padding preventing ChunksController rounding problem - surroundMap.resize(loadDistance + 1); + // 2 is safety padding preventing ChunksController rounding problem + surroundMap.resize(loadDistance + 2); surroundMap.setCenter(centerX, centerY); } From 09e641fd91e193b5f61525a76cb15c8d1765a85d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 21:18:05 +0300 Subject: [PATCH 083/246] advanced chunks loading test --- dev/tests/example.lua | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 0435b6cd..99d4e6f7 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,13 +1,18 @@ test.set_setting("chunks.load-distance", 3) -test.set_setting("chunks.load-speed", 16) +test.set_setting("chunks.load-speed", 32) 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 block.get(0, 0, 0) >= 0 end, 1000) -print(world.count_chunks()) -block.destruct(0, 0, 0, pid) -assert(block.get(0, 0, 0) == 0) +for i=1,100 do + if i % 10 == 0 then + print(tostring(i).." % done") + print("chunks loaded", world.count_chunks()) + end + player.set_pos(pid, math.random() * 100 - 50, 100, math.random() * 100 - 50) + test.tick() +end + test.close_world(true) From 70370904dbdbdd615856adc9a0715b2a1570a15a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 22:38:08 +0300 Subject: [PATCH 084/246] two players test --- dev/tests/example.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 99d4e6f7..89302aa4 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,17 +1,20 @@ test.set_setting("chunks.load-distance", 3) -test.set_setting("chunks.load-speed", 32) +test.set_setting("chunks.load-speed", 1) test.reconfig_packs({"base"}, {}) test.new_world("demo", "2019", "core:default") -local pid = player.create("Xerxes") +local pid1 = player.create("Xerxes") assert(player.get_name(pid) == "Xerxes") +local pid2 = player.create("Undefined") + for i=1,100 do if i % 10 == 0 then print(tostring(i).." % done") print("chunks loaded", world.count_chunks()) end - player.set_pos(pid, math.random() * 100 - 50, 100, math.random() * 100 - 50) + player.set_pos(pid1, math.random() * 100 - 50, 100, math.random() * 100 - 50) + player.set_pos(pid2, math.random() * 200 - 100, 100, math.random() * 200 - 100) test.tick() end From 48d94036fdedad3c89ebbbfa342fac5e39aeb114 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 23:57:00 +0300 Subject: [PATCH 085/246] rename Level.chunksStorage to Level.chunks --- src/frontend/debug_panel.cpp | 2 +- src/frontend/hud.cpp | 2 +- src/logic/BlocksController.cpp | 2 +- src/logic/ChunksController.cpp | 2 +- src/logic/scripting/lua/libs/libblock.cpp | 53 +++++++++---------- src/logic/scripting/lua/libs/libentity.cpp | 2 +- src/logic/scripting/lua/libs/libhud.cpp | 2 +- src/logic/scripting/lua/libs/libworld.cpp | 14 ++--- .../lua/usertypes/lua_type_voxelfragment.cpp | 2 +- src/objects/Entities.cpp | 2 +- src/world/Level.cpp | 6 +-- src/world/Level.hpp | 2 +- src/world/World.cpp | 2 +- src/world/generator/VoxelFragment.cpp | 4 +- 14 files changed, 46 insertions(+), 51 deletions(-) diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index 3470cdb1..d1ad6231 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -96,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.chunksStorage->size())+ + return L"chunks: "+std::to_wstring(level.chunks->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 e6b1ecb9..d078b1c6 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -296,7 +296,7 @@ void Hud::updateWorldGenDebugVisualization() { data[(flippedZ * width + x) * 4 + 1] = chunks.getChunk(ax + ox, az + oz) ? 255 : 0; data[(flippedZ * width + x) * 4 + 0] = - level.chunksStorage->fetch(ax + ox, az + oz) ? 255 : 0; + level.chunks->fetch(ax + ox, az + oz) ? 255 : 0; if (ax < 0 || az < 0 || ax >= areaWidth || az >= areaHeight) { diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp index 3c0b64c0..0a70910f 100644 --- a/src/logic/BlocksController.cpp +++ b/src/logic/BlocksController.cpp @@ -21,7 +21,7 @@ BlocksController::BlocksController(const Level& level, Lighting* lighting, uint padding) : level(level), - chunks(*level.chunksStorage), + chunks(*level.chunks), lighting(lighting), randTickClock(20, 3), blocksTickClock(20, 1), diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 13c7ab86..a8041f8d 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -124,7 +124,7 @@ bool ChunksController::buildLights(const Player& player, const std::shared_ptrcreate(x, z); + auto chunk = level.chunks->create(x, z); player.chunks->putChunk(chunk); auto& chunkFlags = chunk->flags; diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 180a959f..8d1e0897 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -41,7 +41,7 @@ static int l_is_solid_at(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); return lua::pushboolean( - L, blocks_agent::is_solid_at(*level->chunksStorage, x, y, z) + L, blocks_agent::is_solid_at(*level->chunks, x, y, z) ); } @@ -72,7 +72,7 @@ static int l_is_segment(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - const auto& vox = blocks_agent::require(*level->chunksStorage, x, y, z); + const auto& vox = blocks_agent::require(*level->chunks, x, y, z); return lua::pushboolean(L, vox.state.segment); } @@ -80,13 +80,10 @@ static int l_seek_origin(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - const auto& vox = blocks_agent::require(*level->chunksStorage, x, y, z); + const auto& vox = blocks_agent::require(*level->chunks, x, y, z); auto& def = indices->blocks.require(vox.id); return lua::pushivec_stack( - L, - blocks_agent::seek_origin( - *level->chunksStorage, {x, y, z}, def, vox.state - ) + L, blocks_agent::seek_origin(*level->chunks, {x, y, z}, def, vox.state) ); } @@ -102,10 +99,10 @@ static int l_set(lua::State* L) { } int cx = floordiv(x); int cz = floordiv(z); - if (!blocks_agent::get_chunk(*level->chunksStorage, cx, cz)) { + if (!blocks_agent::get_chunk(*level->chunks, cx, cz)) { return 0; } - blocks_agent::set(*level->chunksStorage, x, y, z, id, int2blockstate(state)); + blocks_agent::set(*level->chunks, x, y, z, id, int2blockstate(state)); auto chunksController = controller->getChunksController(); if (chunksController == nullptr) { @@ -123,7 +120,7 @@ static int l_get(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); int id = vox == nullptr ? -1 : vox->id; return lua::pushinteger(L, id); } @@ -132,7 +129,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 = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, glm::ivec3(1, 0, 0)); } @@ -149,7 +146,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 = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, glm::ivec3(0, 1, 0)); } @@ -166,7 +163,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 = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, glm::ivec3(0, 0, 1)); } @@ -183,7 +180,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); - auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); int rotation = vox == nullptr ? 0 : vox->state.rotation; return lua::pushinteger(L, rotation); } @@ -193,7 +190,7 @@ static int l_set_rotation(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); auto value = lua::tointeger(L, 4); - blocks_agent::set_rotation(*level->chunksStorage, x, y, z, value); + blocks_agent::set_rotation(*level->chunks, x, y, z, value); return 0; } @@ -201,7 +198,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 = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); int states = vox == nullptr ? 0 : blockstate2int(vox->state); return lua::pushinteger(L, states); } @@ -216,7 +213,7 @@ static int l_set_states(lua::State* L) { } int cx = floordiv(x); int cz = floordiv(z); - auto chunk = blocks_agent::get_chunk(*level->chunksStorage, cx, cz); + auto chunk = blocks_agent::get_chunk(*level->chunks, cx, cz); if (chunk == nullptr) { return 0; } @@ -235,18 +232,16 @@ static int l_get_user_bits(lua::State* L) { auto offset = lua::tointeger(L, 4) + VOXEL_USER_BITS_OFFSET; auto bits = lua::tointeger(L, 5); - auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); if (vox == nullptr) { return lua::pushinteger(L, 0); } const auto& def = content->getIndices()->blocks.require(vox->id); if (def.rt.extended) { auto origin = blocks_agent::seek_origin( - *level->chunksStorage, {x, y, z}, def, vox->state - ); - vox = blocks_agent::get( - *level->chunksStorage, origin.x, origin.y, origin.z + *level->chunks, {x, y, z}, def, vox->state ); + vox = blocks_agent::get(*level->chunks, origin.x, origin.y, origin.z); if (vox == nullptr) { return lua::pushinteger(L, 0); } @@ -257,7 +252,7 @@ static int l_get_user_bits(lua::State* L) { } static int l_set_user_bits(lua::State* L) { - auto& chunks = *level->chunksStorage; + auto& chunks = *level->chunks; auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); @@ -294,7 +289,7 @@ static int l_is_replaceable_at(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); return lua::pushboolean( - L, blocks_agent::is_replaceable_at(*level->chunksStorage, x, y, z) + L, blocks_agent::is_replaceable_at(*level->chunks, x, y, z) ); } @@ -369,7 +364,7 @@ static int l_place(lua::State* L) { if (static_cast(id) >= indices->blocks.count()) { return 0; } - if (!blocks_agent::get(*level->chunksStorage, x, y, z)) { + if (!blocks_agent::get(*level->chunks, x, y, z)) { return 0; } const auto def = level->content->getIndices()->blocks.get(id); @@ -390,7 +385,7 @@ static int l_destruct(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); auto playerid = lua::gettop(L) >= 4 ? lua::tointeger(L, 4) : -1; - auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); if (vox == nullptr) { return 0; } @@ -425,7 +420,7 @@ static int l_raycast(lua::State* L) { glm::ivec3 normal; glm::ivec3 iend; if (auto voxel = blocks_agent::raycast( - *level->chunksStorage, + *level->chunks, start, dir, maxDistance, @@ -528,7 +523,7 @@ static int l_get_field(lua::State* L) { } auto cx = floordiv(x, CHUNK_W); auto cz = floordiv(z, CHUNK_D); - auto chunk = blocks_agent::get_chunk(*level->chunksStorage, cx, cz); + auto chunk = blocks_agent::get_chunk(*level->chunks, cx, cz); if (chunk == nullptr || y < 0 || y >= CHUNK_H) { return 0; } @@ -599,7 +594,7 @@ static int l_set_field(lua::State* L) { auto cz = floordiv(z, CHUNK_D); auto lx = x - cx * CHUNK_W; auto lz = z - cz * CHUNK_W; - auto chunk = blocks_agent::get_chunk(*level->chunksStorage, cx, cz); + auto chunk = blocks_agent::get_chunk(*level->chunks, cx, cz); if (chunk == nullptr || y < 0 || y >= CHUNK_H) { return 0; } diff --git a/src/logic/scripting/lua/libs/libentity.cpp b/src/logic/scripting/lua/libs/libentity.cpp index bf547f95..ce394fb5 100644 --- a/src/logic/scripting/lua/libs/libentity.cpp +++ b/src/logic/scripting/lua/libs/libentity.cpp @@ -151,7 +151,7 @@ static int l_raycast(lua::State* L) { blockid_t block = BLOCK_VOID; if (auto voxel = blocks_agent::raycast( - *level->chunksStorage, + *level->chunks, start, dir, maxDistance, diff --git a/src/logic/scripting/lua/libs/libhud.cpp b/src/logic/scripting/lua/libs/libhud.cpp index f12ba0a7..90cb57d4 100644 --- a/src/logic/scripting/lua/libs/libhud.cpp +++ b/src/logic/scripting/lua/libs/libhud.cpp @@ -60,7 +60,7 @@ static int l_open_block(lua::State* L) { auto z = lua::tointeger(L, 3); bool playerInventory = !lua::toboolean(L, 4); - auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); if (vox == nullptr) { throw std::runtime_error( "block does not exists at " + std::to_string(x) + " " + diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 16e81d56..e13d8c0f 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -125,7 +125,7 @@ static int l_get_generator(lua::State* L) { static int l_get_chunk_data(lua::State* L) { int x = (int)lua::tointeger(L, 1); int y = (int)lua::tointeger(L, 2); - const auto& chunk = level->chunksStorage->getChunk(x, y); + const auto& chunk = level->chunks->getChunk(x, y); if (chunk == nullptr) { lua::pushnil(L); return 0; @@ -181,7 +181,7 @@ static int l_set_chunk_data(lua::State* L) { if (lua::gettop(L) >= 4) { is_compressed = lua::toboolean(L, 4); } - auto chunk = level->chunksStorage->getChunk(x, y); + auto chunk = level->chunks->getChunk(x, y); if (chunk == nullptr) { return 0; } @@ -217,22 +217,22 @@ static int l_set_chunk_data(lua::State* L) { chunk->flags.modified = true; lighting.onChunkLoaded(x, y, true); - chunk = level->chunksStorage->getChunk(x - 1, y); + chunk = level->chunks->getChunk(x - 1, y); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x - 1, y, true); } - chunk = level->chunksStorage->getChunk(x + 1, y); + chunk = level->chunks->getChunk(x + 1, y); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x + 1, y, true); } - chunk = level->chunksStorage->getChunk(x, y - 1); + chunk = level->chunks->getChunk(x, y - 1); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x, y - 1, true); } - chunk = level->chunksStorage->getChunk(x, y + 1); + chunk = level->chunks->getChunk(x, y + 1); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x, y + 1, true); @@ -245,7 +245,7 @@ static int l_count_chunks(lua::State* L) { if (level == nullptr) { return 0; } - return lua::pushinteger(L, level->chunksStorage->size()); + return lua::pushinteger(L, level->chunks->size()); } const luaL_Reg worldlib[] = { diff --git a/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp b/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp index 4a7af971..f123be6f 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp @@ -26,7 +26,7 @@ static int l_place(lua::State* L) { auto offset = tovec3(L, 2); int rotation = tointeger(L, 3) & 0b11; fragment->getFragment()->place( - *scripting::level->chunksStorage, offset, rotation + *scripting::level->chunks, offset, rotation ); } return 0; diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index d2e5742d..1a27243f 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -459,7 +459,7 @@ void Entities::updatePhysics(float delta) { float vel = glm::length(prevVel); int substeps = static_cast(delta * vel * 20); substeps = std::min(100, std::max(2, substeps)); - physics->step(*level->chunksStorage, &hitbox, delta, substeps, eid.uid); + physics->step(*level->chunks, &hitbox, delta, substeps, eid.uid); hitbox.linearDamping = hitbox.grounded * 24; transform.setPos(hitbox.position); if (hitbox.grounded && !grounded) { diff --git a/src/world/Level.cpp b/src/world/Level.cpp index 3625f62e..28a8dab5 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -24,7 +24,7 @@ Level::Level( : settings(settings), world(std::move(worldPtr)), content(content), - chunksStorage(std::make_unique(this)), + chunks(std::make_unique(this)), physics(std::make_unique(glm::vec3(0, -22.6f, 0))), events(std::make_unique()), entities(std::make_unique(this)), @@ -53,10 +53,10 @@ Level::Level( } events->listen(LevelEventType::EVT_CHUNK_SHOWN, [this](LevelEventType, Chunk* chunk) { - chunksStorage->incref(chunk); + chunks->incref(chunk); }); events->listen(LevelEventType::EVT_CHUNK_HIDDEN, [this](LevelEventType, Chunk* chunk) { - chunksStorage->decref(chunk); + chunks->decref(chunk); }); inventories = std::make_unique(*this); } diff --git a/src/world/Level.hpp b/src/world/Level.hpp index 9994c01d..63a724c9 100644 --- a/src/world/Level.hpp +++ b/src/world/Level.hpp @@ -25,7 +25,7 @@ class Level { public: const Content* const content; - std::unique_ptr chunksStorage; + std::unique_ptr chunks; std::unique_ptr inventories; std::unique_ptr physics; diff --git a/src/world/World.cpp b/src/world/World.cpp index eb8c33bf..d8ef2ed5 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->chunksStorage->saveAll(); + level->chunks->saveAll(); info.nextEntityId = level->entities->peekNextID(); wfile->write(this, content); diff --git a/src/world/generator/VoxelFragment.cpp b/src/world/generator/VoxelFragment.cpp index af92dc7e..9a739a2a 100644 --- a/src/world/generator/VoxelFragment.cpp +++ b/src/world/generator/VoxelFragment.cpp @@ -27,7 +27,7 @@ std::unique_ptr VoxelFragment::create( if (crop) { VoxelsVolume volume(size.x, size.y, size.z); volume.setPosition(start.x, start.y, start.z); - blocks_agent::get_voxels(*level.chunksStorage, &volume); + blocks_agent::get_voxels(*level.chunks, &volume); auto end = start + size; @@ -52,7 +52,7 @@ std::unique_ptr VoxelFragment::create( VoxelsVolume volume(size.x, size.y, size.z); volume.setPosition(start.x, start.y, start.z); - blocks_agent::get_voxels(*level.chunksStorage, &volume); + blocks_agent::get_voxels(*level.chunks, &volume); auto volVoxels = volume.getVoxels(); std::vector voxels(size.x * size.y * size.z); From fa1646a9bd676a3849d45040b098dd311043db2b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 18 Dec 2024 00:48:29 +0300 Subject: [PATCH 086/246] add '--script', '--version' command line arguments --- src/{TestMainloop.cpp => ServerMainloop.cpp} | 36 +++++++++++++++----- src/{TestMainloop.hpp => ServerMainloop.hpp} | 6 ++-- src/engine.cpp | 4 +-- src/engine.hpp | 3 +- src/logic/scripting/lua/lua_engine.cpp | 2 +- src/util/command_line.cpp | 15 ++++++-- src/world/Level.hpp | 1 - 7 files changed, 48 insertions(+), 19 deletions(-) rename src/{TestMainloop.cpp => ServerMainloop.cpp} (53%) rename src/{TestMainloop.hpp => ServerMainloop.hpp} (73%) diff --git a/src/TestMainloop.cpp b/src/ServerMainloop.cpp similarity index 53% rename from src/TestMainloop.cpp rename to src/ServerMainloop.cpp index 100c0b51..be013e7b 100644 --- a/src/TestMainloop.cpp +++ b/src/ServerMainloop.cpp @@ -1,4 +1,4 @@ -#include "TestMainloop.hpp" +#include "ServerMainloop.hpp" #include "logic/scripting/scripting.hpp" #include "logic/LevelController.hpp" @@ -6,22 +6,27 @@ #include "debug/Logger.hpp" #include "world/Level.hpp" #include "world/World.hpp" +#include "util/platform.hpp" #include "engine.hpp" +#include + +using namespace std::chrono; + static debug::Logger logger("mainloop"); inline constexpr int TPS = 20; -TestMainloop::TestMainloop(Engine& engine) : engine(engine) { +ServerMainloop::ServerMainloop(Engine& engine) : engine(engine) { } -TestMainloop::~TestMainloop() = default; +ServerMainloop::~ServerMainloop() = default; -void TestMainloop::run() { +void ServerMainloop::run() { const auto& coreParams = engine.getCoreParameters(); auto& time = engine.getTime(); - if (coreParams.testFile.empty()) { + if (coreParams.scriptFile.empty()) { logger.info() << "nothing to do"; return; } @@ -29,21 +34,34 @@ void TestMainloop::run() { setLevel(std::move(level)); }); - logger.info() << "starting test " << coreParams.testFile; - auto process = scripting::start_coroutine(coreParams.testFile); + logger.info() << "starting test " << coreParams.scriptFile; + auto process = scripting::start_coroutine(coreParams.scriptFile); + + double targetDelta = 1.0f / static_cast(TPS); + double delta = targetDelta; + auto begin = steady_clock::now(); while (process->isActive()) { - time.step(1.0f / static_cast(TPS)); + time.step(delta); process->update(); if (controller) { float delta = time.getDelta(); controller->getLevel()->getWorld()->updateTimers(delta); controller->update(glm::min(delta, 0.2f), false); } + + if (!coreParams.testMode) { + auto end = steady_clock::now(); + platform::sleep(targetDelta * 1000 - + duration_cast(end - begin).count() / 1000); + end = steady_clock::now(); + delta = duration_cast(end - begin).count() / 1e6; + begin = end; + } } logger.info() << "test finished"; } -void TestMainloop::setLevel(std::unique_ptr level) { +void ServerMainloop::setLevel(std::unique_ptr level) { if (level == nullptr) { controller->onWorldQuit(); engine.getPaths()->setCurrentWorldFolder(fs::path()); diff --git a/src/TestMainloop.hpp b/src/ServerMainloop.hpp similarity index 73% rename from src/TestMainloop.hpp rename to src/ServerMainloop.hpp index 06ffae25..28cfbd9d 100644 --- a/src/TestMainloop.hpp +++ b/src/ServerMainloop.hpp @@ -6,12 +6,12 @@ class Level; class LevelController; class Engine; -class TestMainloop { +class ServerMainloop { Engine& engine; std::unique_ptr controller; public: - TestMainloop(Engine& engine); - ~TestMainloop(); + ServerMainloop(Engine& engine); + ~ServerMainloop(); void run(); diff --git a/src/engine.cpp b/src/engine.cpp index 3c7d4bb5..46fd17c8 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -36,7 +36,7 @@ #include "window/Window.hpp" #include "world/Level.hpp" #include "Mainloop.hpp" -#include "TestMainloop.hpp" +#include "ServerMainloop.hpp" #include #include @@ -173,7 +173,7 @@ void Engine::saveScreenshot() { void Engine::run() { if (params.headless) { - TestMainloop(*this).run(); + ServerMainloop(*this).run(); } else { Mainloop(*this).run(); } diff --git a/src/engine.hpp b/src/engine.hpp index 63a78084..88a60865 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -48,9 +48,10 @@ public: struct CoreParameters { bool headless = false; + bool testMode = false; std::filesystem::path resFolder {"res"}; std::filesystem::path userFolder {"."}; - std::filesystem::path testFile; + std::filesystem::path scriptFile; }; class Engine : public util::ObjectsKeeper { diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 0a4e2259..b34b87a0 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -121,7 +121,7 @@ void lua::initialize(const EnginePaths& paths, const CoreParameters& params) { main_thread = create_state( paths, params.headless ? StateType::TEST : StateType::BASE ); - lua::pushstring(main_thread, params.testFile.stem().u8string()); + lua::pushstring(main_thread, params.scriptFile.stem().u8string()); lua::setglobal(main_thread, "__VC_TEST_NAME"); } diff --git a/src/util/command_line.cpp b/src/util/command_line.cpp index 768c6260..25d7b554 100644 --- a/src/util/command_line.cpp +++ b/src/util/command_line.cpp @@ -19,19 +19,30 @@ static bool perform_keyword( auto token = reader.next(); params.userFolder = fs::u8path(token); } else if (keyword == "--help" || keyword == "-h") { - std::cout << "VoxelEngine command-line arguments:\n"; + std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n"; + std::cout << "command-line arguments:\n"; std::cout << " --help - show help\n"; + std::cout << " --version - print engine version\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 << " --script - main script file\n"; std::cout << std::endl; return false; + } else if (keyword == "--version") { + std::cout << ENGINE_VERSION_STRING << std::endl; + return false; } else if (keyword == "--headless") { params.headless = true; } else if (keyword == "--test") { auto token = reader.next(); - params.testFile = fs::u8path(token); + params.testMode = true; + params.scriptFile = fs::u8path(token); + } else if (keyword == "--script") { + auto token = reader.next(); + params.testMode = false; + params.scriptFile = fs::u8path(token); } else { throw std::runtime_error("unknown argument " + keyword); } diff --git a/src/world/Level.hpp b/src/world/Level.hpp index 63a724c9..121fecc1 100644 --- a/src/world/Level.hpp +++ b/src/world/Level.hpp @@ -27,7 +27,6 @@ public: std::unique_ptr chunks; std::unique_ptr inventories; - std::unique_ptr physics; std::unique_ptr events; std::unique_ptr entities; From 5e55e20ec4dd605fbf8f487132c36c4b5d951cd9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 18 Dec 2024 02:25:28 +0300 Subject: [PATCH 087/246] add test.quit, test.reopen_world --- dev/tests/example.lua | 2 ++ res/scripts/stdlib.lua | 29 +++++++++++++++++++++++- src/ServerMainloop.cpp | 5 ++++ src/engine.cpp | 11 +++++++++ src/engine.hpp | 5 ++++ src/logic/scripting/lua/libs/libcore.cpp | 5 +++- 6 files changed, 55 insertions(+), 2 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 89302aa4..1ebe6b06 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,6 +1,8 @@ test.set_setting("chunks.load-distance", 3) test.set_setting("chunks.load-speed", 1) +test.quit() + test.reconfig_packs({"base"}, {}) test.new_world("demo", "2019", "core:default") local pid1 = player.create("Xerxes") diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 0298a2b9..9ddda13c 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -9,16 +9,41 @@ function sleep(timesec) end end +function tb_frame_tostring(frame) + local s = frame.short_src + if frame.what ~= "C" then + s = s .. ":" .. tostring(frame.currentline) + end + if frame.what == "main" then + s = s .. ": in main chunk" + elseif frame.name then + s = s .. ": in function " .. utf8.escape(frame.name) + end + return s +end + 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.reopen_world = core.reopen_world test.reconfig_packs = core.reconfig_packs test.set_setting = core.set_setting test.tick = coroutine.yield + function test.quit() + local tb = debug.get_traceback(1) + local s = "test.quit() traceback:" + for i, frame in ipairs(tb) do + s = s .. "\n\t"..tb_frame_tostring(frame) + end + debug.log(s) + core.quit() + coroutine.yield() + end + function test.sleep_until(predicate, max_ticks) max_ticks = max_ticks or 1e9 local ticks = 0 @@ -382,7 +407,9 @@ end function __vc_stop_coroutine(id) local co = __vc_coroutines[id] if co then - coroutine.close(co) + if coroutine.close then + coroutine.close(co) + end __vc_coroutines[id] = nil end end diff --git a/src/ServerMainloop.cpp b/src/ServerMainloop.cpp index be013e7b..dd50f089 100644 --- a/src/ServerMainloop.cpp +++ b/src/ServerMainloop.cpp @@ -41,6 +41,11 @@ void ServerMainloop::run() { double delta = targetDelta; auto begin = steady_clock::now(); while (process->isActive()) { + if (engine.isQuitSignal()) { + process->terminate(); + logger.info() << "script has been terminated due to quit signal"; + break; + } time.step(delta); process->update(); if (controller) { diff --git a/src/engine.cpp b/src/engine.cpp index 46fd17c8..095d744c 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -461,6 +461,17 @@ void Engine::onWorldClosed() { levelConsumer(nullptr); } +void Engine::quit() { + quitSignal = true; + if (!isHeadless()) { + Window::setShouldClose(true); + } +} + +bool Engine::isQuitSignal() const { + return quitSignal; +} + gui::GUI* Engine::getGUI() { return gui.get(); } diff --git a/src/engine.hpp b/src/engine.hpp index 88a60865..39792624 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -74,6 +74,7 @@ class Engine : public util::ObjectsKeeper { std::unique_ptr gui; Time time; consumer> levelConsumer; + bool quitSignal = false; void loadControls(); void loadSettings(); @@ -138,6 +139,10 @@ public: void onWorldOpen(std::unique_ptr level); void onWorldClosed(); + void quit(); + + bool isQuitSignal() const; + /// @brief Get current Content instance const Content* getContent() const; diff --git a/src/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp index d2d0c80f..7b121be8 100644 --- a/src/logic/scripting/lua/libs/libcore.cpp +++ b/src/logic/scripting/lua/libs/libcore.cpp @@ -56,6 +56,9 @@ static int l_open_world(lua::State* L) { /// @brief Reopen world static int l_reopen_world(lua::State*) { auto controller = engine->getController(); + if (level == nullptr) { + throw std::runtime_error("no world open"); + } controller->reopenWorld(level->getWorld()); return 0; } @@ -229,7 +232,7 @@ static int l_open_folder(lua::State* L) { /// @brief Quit the game static int l_quit(lua::State*) { - Window::setShouldClose(true); + engine->quit(); return 0; } From d745a34657073de66e5c70aef8baabbfdfccf8f3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 18 Dec 2024 04:13:33 +0300 Subject: [PATCH 088/246] refactor --- dev/tests/example.lua | 2 - src/Mainloop.cpp | 6 +- src/ServerMainloop.cpp | 2 +- src/engine.cpp | 12 +- src/engine.hpp | 2 +- src/files/engine_paths.cpp | 4 +- src/files/engine_paths.hpp | 4 +- src/frontend/debug_panel.cpp | 10 +- src/frontend/hud.cpp | 26 ++-- src/frontend/hud.hpp | 6 +- src/frontend/menu.cpp | 54 +++++---- src/frontend/menu.hpp | 10 +- src/frontend/screens/LevelScreen.cpp | 30 ++--- src/frontend/screens/LevelScreen.hpp | 2 +- src/frontend/screens/MenuScreen.cpp | 8 +- src/frontend/screens/MenuScreen.hpp | 2 +- src/frontend/screens/Screen.cpp | 2 +- src/frontend/screens/Screen.hpp | 4 +- src/graphics/render/WorldRenderer.cpp | 67 +++++------ src/graphics/render/WorldRenderer.hpp | 6 +- src/graphics/ui/gui_xml.cpp | 2 +- src/logic/EngineController.cpp | 111 +++++++++--------- src/logic/EngineController.hpp | 4 +- src/logic/scripting/lua/libs/libcore.cpp | 2 +- src/logic/scripting/lua/libs/libfile.cpp | 4 +- .../scripting/lua/libs/libgeneration.cpp | 8 +- src/logic/scripting/lua/libs/libinput.cpp | 2 +- src/logic/scripting/lua/libs/libworld.cpp | 6 +- .../lua/usertypes/lua_type_heightmap.cpp | 4 +- src/logic/scripting/scripting.cpp | 6 +- src/logic/scripting/scripting_hud.cpp | 2 +- .../scripting/scripting_world_generation.cpp | 2 +- src/window/Window.cpp | 2 +- 33 files changed, 209 insertions(+), 205 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 1ebe6b06..89302aa4 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,8 +1,6 @@ test.set_setting("chunks.load-distance", 3) test.set_setting("chunks.load-speed", 1) -test.quit() - test.reconfig_packs({"base"}, {}) test.new_world("demo", "2019", "core:default") local pid1 = player.create("Xerxes") diff --git a/src/Mainloop.cpp b/src/Mainloop.cpp index eae4c501..a12434ef 100644 --- a/src/Mainloop.cpp +++ b/src/Mainloop.cpp @@ -20,14 +20,14 @@ void Mainloop::run() { // destroy LevelScreen and run quit callbacks engine.setScreen(nullptr); // create and go to menu screen - engine.setScreen(std::make_shared(&engine)); + engine.setScreen(std::make_shared(engine)); } else { - engine.setScreen(std::make_shared(&engine, std::move(level))); + engine.setScreen(std::make_shared(engine, std::move(level))); } }); logger.info() << "starting menu screen"; - engine.setScreen(std::make_shared(&engine)); + engine.setScreen(std::make_shared(engine)); logger.info() << "main loop started"; while (!Window::isShouldClose()){ diff --git a/src/ServerMainloop.cpp b/src/ServerMainloop.cpp index dd50f089..3d05f55b 100644 --- a/src/ServerMainloop.cpp +++ b/src/ServerMainloop.cpp @@ -69,7 +69,7 @@ void ServerMainloop::run() { void ServerMainloop::setLevel(std::unique_ptr level) { if (level == nullptr) { controller->onWorldQuit(); - engine.getPaths()->setCurrentWorldFolder(fs::path()); + engine.getPaths().setCurrentWorldFolder(fs::path()); controller = nullptr; } else { controller = std::make_unique( diff --git a/src/engine.cpp b/src/engine.cpp index 095d744c..2f8466d6 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -87,7 +87,7 @@ Engine::Engine(CoreParameters coreParameters) auto resdir = paths.getResourcesFolder(); - controller = std::make_unique(this); + controller = std::make_unique(*this); if (!params.headless) { if (Window::initialize(&settings.display)){ throw initialize_error("could not initialize window"); @@ -101,7 +101,7 @@ Engine::Engine(CoreParameters coreParameters) gui = std::make_unique(); if (ENGINE_DEBUG_BUILD) { - menus::create_version_label(this); + menus::create_version_label(*this); } } audio::initialize(settings.audio.enabled.get() && !params.headless); @@ -118,7 +118,7 @@ Engine::Engine(CoreParameters coreParameters) paths.getResourcesFolder() )); } - keepAlive(settings.ui.language.observe([=](auto lang) { + keepAlive(settings.ui.language.observe([this](auto lang) { setLanguage(lang); }, true)); @@ -447,7 +447,7 @@ void Engine::setScreen(std::shared_ptr screen) { void Engine::setLanguage(std::string locale) { langs::setup(paths.getResourcesFolder(), std::move(locale), contentPacks); if (gui) { - gui->getMenu()->setPageLoader(menus::create_page_loader(this)); + gui->getMenu()->setPageLoader(menus::create_page_loader(*this)); } } @@ -502,8 +502,8 @@ std::vector& Engine::getBasePacks() { return basePacks; } -EnginePaths* Engine::getPaths() { - return &paths; +EnginePaths& Engine::getPaths() { + return paths; } ResPaths* Engine::getResPaths() { diff --git a/src/engine.hpp b/src/engine.hpp index 39792624..f10fb3a0 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -131,7 +131,7 @@ public: EngineSettings& getSettings(); /// @brief Get engine filesystem paths source - EnginePaths* getPaths(); + EnginePaths& getPaths(); /// @brief Get engine resource paths controller ResPaths* getResPaths(); diff --git a/src/files/engine_paths.cpp b/src/files/engine_paths.cpp index 26857331..0ecea7b8 100644 --- a/src/files/engine_paths.cpp +++ b/src/files/engine_paths.cpp @@ -132,7 +132,7 @@ std::filesystem::path EnginePaths::getSettingsFile() const { return userFilesFolder / SETTINGS_FILE; } -std::vector EnginePaths::scanForWorlds() { +std::vector EnginePaths::scanForWorlds() const { std::vector folders; auto folder = getWorldsFolder(); @@ -189,7 +189,7 @@ std::tuple EnginePaths::parsePath(std::string_view pat std::filesystem::path EnginePaths::resolve( const std::string& path, bool throwErr -) { +) const { auto [prefix, filename] = EnginePaths::parsePath(path); if (prefix.empty()) { throw files_access_error("no entry point specified"); diff --git a/src/files/engine_paths.hpp b/src/files/engine_paths.hpp index c5289243..61be1757 100644 --- a/src/files/engine_paths.hpp +++ b/src/files/engine_paths.hpp @@ -39,9 +39,9 @@ public: void setContentPacks(std::vector* contentPacks); - std::vector scanForWorlds(); + std::vector scanForWorlds() const; - std::filesystem::path resolve(const std::string& path, bool throwErr = true); + std::filesystem::path resolve(const std::string& path, bool throwErr = true) const; static std::tuple parsePath(std::string_view view); diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index d1ad6231..a5607803 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -42,7 +42,7 @@ static std::shared_ptr