diff --git a/.cmake-format.py b/.cmake-format.py new file mode 100644 index 00000000..2a46b909 --- /dev/null +++ b/.cmake-format.py @@ -0,0 +1,3 @@ +tab_size = 4 +enable_sort = True +autosort = True diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 54046d33..cb599308 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -32,11 +32,11 @@ jobs: # install EnTT git clone https://github.com/skypjack/entt.git cd entt/build - cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. + cmake -DCMAKE_BUILD_TYPE=Release .. sudo make install cd ../.. - name: Configure - run: cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DVOXELENGINE_BUILD_APPDIR=1 -DVOXELENGINE_BUILD_TESTS=ON + 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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b5c6fc8..abfd430e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,18 +8,20 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -if (CMAKE_SYSTEM_NAME STREQUAL "Windows") - # We use two types linking: for clang build is static (vcpkg triplet x64-windows-static) - # and for msvc build is dynamic linking (vcpkg triplet x64-windows) - # By default CMAKE_MSVC_RUNTIME_LIBRARY set by MultiThreaded$<$:Debug>DLL - if (VCPKG_TARGET_TRIPLET MATCHES "static") - set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - endif() +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + # We use two types linking: for clang build is static (vcpkg triplet + # x64-windows-static) and for msvc build is dynamic linking (vcpkg triplet + # x64-windows) By default CMAKE_MSVC_RUNTIME_LIBRARY set by + # MultiThreaded$<$:Debug>DLL + if(VCPKG_TARGET_TRIPLET MATCHES "static") + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() endif() add_subdirectory(src) add_executable(${PROJECT_NAME} src/main.cpp) -target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) +target_include_directories(${PROJECT_NAME} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) if(VOXELENGINE_BUILD_APPDIR) include(${CMAKE_CURRENT_SOURCE_DIR}/dev/cmake/BuildAppdir.cmake) @@ -27,33 +29,44 @@ endif() if(MSVC) if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) + set(CMAKE_BUILD_TYPE + Release + CACHE STRING "Build type" FORCE) endif() - if((CMAKE_BUILD_TYPE EQUAL "Release") OR (CMAKE_BUILD_TYPE EQUAL "RelWithDebInfo")) - target_compile_options(${PROJECT_NAME} PRIVATE /W4 /MT /O2) + if((CMAKE_BUILD_TYPE EQUAL "Release") OR (CMAKE_BUILD_TYPE EQUAL + "RelWithDebInfo")) + target_compile_options(${PROJECT_NAME} PRIVATE /W4 /MT /O2) else() - target_compile_options(${PROJECT_NAME} PRIVATE /W4) + target_compile_options(${PROJECT_NAME} PRIVATE /W4) endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /source-charset:UTF-8 /D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR") + set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} /source-charset:UTF-8 /D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR" + ) else() - target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra - # additional warnings - -Wformat-nonliteral -Wcast-align - -Wpointer-arith -Wundef - -Wwrite-strings -Wno-unused-parameter) - 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") - endif() + target_compile_options( + ${PROJECT_NAME} + PRIVATE -Wall + -Wextra + # additional warnings + -Wformat-nonliteral + -Wcast-align + -Wpointer-arith + -Wundef + -Wwrite-strings + -Wno-unused-parameter) + 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") + endif() endif() -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie -lstdc++fs") endif() -if (WIN32) +if(WIN32) target_link_libraries(${PROJECT_NAME} VoxelEngineSrc winmm) endif() @@ -61,14 +74,13 @@ target_link_libraries(${PROJECT_NAME} VoxelEngineSrc ${CMAKE_DL_LIBS}) # Deploy res to build dir add_custom_command( - TARGET ${PROJECT_NAME} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different - ${CMAKE_CURRENT_SOURCE_DIR}/res - $/res - ) + TARGET ${PROJECT_NAME} + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy_directory_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/res $/res) -if (VOXELENGINE_BUILD_TESTS) +if(VOXELENGINE_BUILD_TESTS) enable_testing() add_subdirectory(test) endif() diff --git a/dev/tests/world.lua b/dev/tests/world.lua index db1ffca5..cd9c21c3 100644 --- a/dev/tests/world.lua +++ b/dev/tests/world.lua @@ -5,6 +5,7 @@ app.reconfig_packs({"base"}, {}) app.new_world("demo", "2019", "core:default") assert(world.is_open()) assert(world.get_generator() == "core:default") +assert(app.is_content_loaded()) app.sleep(1) assert(world.get_total_time() > 0.0) print(world.get_total_time()) @@ -12,6 +13,7 @@ print(world.get_total_time()) -- Close app.close_world(true) assert(not world.is_open()) +assert(not app.is_content_loaded()) -- Reopen app.open_world("demo") diff --git a/doc/en/scripting/builtins/libapp.md b/doc/en/scripting/builtins/libapp.md index e3fed3d4..ffc4d923 100644 --- a/doc/en/scripting/builtins/libapp.md +++ b/doc/en/scripting/builtins/libapp.md @@ -69,6 +69,12 @@ app.config_packs( Updates the packs configuration, automatically removing unspecified ones, adding those missing in the previous configuration. Uses app.reconfig_packs. +```lua +app.is_content_loaded() -> bool +``` + +Checks if content is loaded. + ```lua app.new_world( -- world name diff --git a/doc/en/scripting/builtins/libworld.md b/doc/en/scripting/builtins/libworld.md index fc18773e..97c6fdee 100644 --- a/doc/en/scripting/builtins/libworld.md +++ b/doc/en/scripting/builtins/libworld.md @@ -47,4 +47,18 @@ world.is_night() -> bool -- Returns the total number of chunks loaded into memory world.count_chunks() -> int + +-- Returns the compressed chunk data to send. +-- Currently includes: +-- 1. Voxel data (id and state) +-- 2. Voxel metadata (fields) +world.get_chunk_data(x: int, z: int) -> Bytearray + +-- Modifies the chunk based on the compressed data. +-- Returns true if the chunk exists. +world.set_chunk_data( + x: int, z: int, + -- compressed chunk data + data: Bytearray +) -> bool ``` diff --git a/doc/en/scripting/events.md b/doc/en/scripting/events.md index cc820898..21e1cee5 100644 --- a/doc/en/scripting/events.md +++ b/doc/en/scripting/events.md @@ -126,6 +126,34 @@ function on_block_interact(blockid, x, y, z, playerid) -> bool Called on block RMB click interaction. Prevents block placing if **true** returned. +### Chunk Events (world.lua) + +```lua +function on_chunk_present(x: int, z: int, loaded: bool) +``` + +Called after a chunk is generated/loaded. If a previously saved chunk is loaded, `loaded` will be true. + +```lua +function on_chunk_remove(x: int, z: int) +``` + +Called when a chunk is unloaded from the world. + +### Inventory Events (world.lua) + +```lua +function on_inventory_open(invid: int, playerid: int) +``` + +Called when the inventory is opened. If the inventory was not opened directly by the player, playerid will be -1. + +```lua +function on_inventory_closed(invid: int, playerid: int) +``` + +Called when the inventory is closed. + ## Layout events Script *layouts/layout_name.xml.lua* events. diff --git a/doc/ru/scripting/builtins/libapp.md b/doc/ru/scripting/builtins/libapp.md index 7887bcdf..c0f3b741 100644 --- a/doc/ru/scripting/builtins/libapp.md +++ b/doc/ru/scripting/builtins/libapp.md @@ -69,6 +69,12 @@ app.config_packs( Обновляет конфигурацию паков, автоматически удаляя лишние, добавляя отсутствующие в прошлой конфигурации. Использует app.reconfig_packs. +```lua +app.is_content_loaded() -> bool +``` + +Проверяет, загружен ли контент. + ```lua app.new_world( -- название мира diff --git a/doc/ru/scripting/builtins/libworld.md b/doc/ru/scripting/builtins/libworld.md index 0467d784..a7e0e5a6 100644 --- a/doc/ru/scripting/builtins/libworld.md +++ b/doc/ru/scripting/builtins/libworld.md @@ -46,4 +46,18 @@ world.is_night() -> bool -- Возвращает общее количество загруженных в память чанков world.count_chunks() -> int + +-- Возвращает сжатые данные чанка для отправки. +-- На данный момент включает: +-- 1. Данные вокселей (id и состояние) +-- 2. Метаданные (поля) вокселей +world.get_chunk_data(x: int, z: int) -> Bytearray + +-- Изменяет чанк на основе сжатых данных. +-- Возвращает true если чанк существует. +world.set_chunk_data( + x: int, z: int, + -- сжатые данные чанка + data: Bytearray +) -> bool ``` diff --git a/doc/ru/scripting/events.md b/doc/ru/scripting/events.md index a5cc3e72..c7781879 100644 --- a/doc/ru/scripting/events.md +++ b/doc/ru/scripting/events.md @@ -126,6 +126,35 @@ function on_block_interact(blockid, x, y, z, playerid) -> bool Вызывается при нажатии на блок ПКМ. Предотвращает установку блоков, если возвращает `true` +### События чанков (world.lua) + +```lua +function on_chunk_present(x: int, z: int, loaded: bool) +``` + +Вызывается после генерации/загрузки чанка. В случае загрузки ранее сохраненного чанка `loaded` будет истинным. + +```lua +function on_chunk_remove(x: int, z: int) +``` + +Вызывается при выгрузке чанка из мира. + +### События инвентарей (world.lua) + +```lua +function on_inventory_open(invid: int, playerid: int) +``` + +Вызывается при открытии инвентаря. Если инвентарь был открыт не напрямую игроком, playerid будет равен -1. + +```lua +function on_inventory_closed(invid: int, playerid: int) +``` + +Вызывается при закрытии инвентаря. + + ## События макета События прописываются в файле `layouts/имя_макета.xml.lua`. diff --git a/res/config/bindings.toml b/res/config/bindings.toml index 426a580f..27f02342 100644 --- a/res/config/bindings.toml +++ b/res/config/bindings.toml @@ -19,3 +19,4 @@ player.pick="mouse:middle" player.drop="key:q" player.fast_interaction="key:x" hud.inventory="key:tab" +hud.chat="key:t" diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index f376e210..c9f38835 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -39,6 +39,7 @@ local function complete_app_lib(app) app.get_setting_info = core.get_setting_info app.load_content = core.load_content app.reset_content = core.reset_content + app.is_content_loaded = core.is_content_loaded function app.config_packs(packs_list) -- Check if packs are valid and add dependencies to the configuration @@ -376,6 +377,14 @@ function __vc_on_hud_open() hud.show_overlay("core:console", false, {"console"}) end) end) + input.add_callback("hud.chat", function() + if hud.is_paused() then + return + end + time.post_runnable(function() + hud.show_overlay("core:console", false, {"chat"}) + end) + end) end local RULES_FILE = "world:rules.toml" diff --git a/res/texts/en_US.txt b/res/texts/en_US.txt index e9b23897..732e2d66 100644 --- a/res/texts/en_US.txt +++ b/res/texts/en_US.txt @@ -33,6 +33,7 @@ movement.sprint=Sprint movement.crouch=Crouch movement.cheat=Cheat hud.inventory=Inventory +hud.chat=Chat player.pick=Pick Block player.attack=Attack player.destroy=Destroy diff --git a/res/texts/ru_RU.txt b/res/texts/ru_RU.txt index d3c40893..80a7ba6a 100644 --- a/res/texts/ru_RU.txt +++ b/res/texts/ru_RU.txt @@ -103,6 +103,7 @@ movement.sprint=Ускорение movement.crouch=Красться movement.cheat=Чит hud.inventory=Инвентарь +hud.chat=Чат player.pick=Подобрать Блок player.attack=Атаковать player.destroy=Сломать diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6350f4b8..09d3d0c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,7 +10,7 @@ add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS}) find_package(OpenGL REQUIRED) find_package(GLEW REQUIRED) -if (CMAKE_SYSTEM_NAME STREQUAL "Windows") +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") # specific for vcpkg find_package(OpenAL CONFIG REQUIRED) set(OPENAL_LIBRARY OpenAL::OpenAL) @@ -20,17 +20,19 @@ endif() find_package(ZLIB REQUIRED) find_package(PNG REQUIRED) find_package(CURL REQUIRED) -if (NOT APPLE) +if(NOT APPLE) find_package(EnTT REQUIRED) endif() set(LIBS "") -if (CMAKE_SYSTEM_NAME STREQUAL "Windows") - # Use directly linking to lib instead PkgConfig (because pkg-config dont install on windows as default) - # TODO: Do it with findLua. - if (MSVC) - set(LUA_INCLUDE_DIR "$ENV{VCPKG_ROOT}/packages/luajit_${VCPKG_TARGET_TRIPLET}/include/luajit") +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + # Use directly linking to lib instead PkgConfig (because pkg-config dont + # install on windows as default) TODO: Do it with findLua. + if(MSVC) + set(LUA_INCLUDE_DIR + "$ENV{VCPKG_ROOT}/packages/luajit_${VCPKG_TARGET_TRIPLET}/include/luajit" + ) find_package(Lua REQUIRED) else() # Used for mingw-clang cross compiling from msys2 @@ -49,7 +51,7 @@ elseif(APPLE) set(LUA_LIBRARIES "/opt/homebrew/lib/libluajit-5.1.a") message(STATUS "LUA Libraries: ${LUA_LIBRARIES}") message(STATUS "LUA Include Dir: ${LUA_INCLUDE_DIR}") - + set(VORBISLIB ${VORBIS_LDFLAGS}) message(STATUS "Vorbis Lib: ${VORBIS_LDFLAGS}") else() @@ -70,4 +72,16 @@ endif() include_directories(${LUA_INCLUDE_DIR}) include_directories(${CURL_INCLUDE_DIR}) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(${PROJECT_NAME} ${LIBS} glfw OpenGL::GL ${OPENAL_LIBRARY} GLEW::GLEW ZLIB::ZLIB PNG::PNG CURL::libcurl ${VORBISLIB} ${LUA_LIBRARIES} ${CMAKE_DL_LIBS}) +target_link_libraries( + ${PROJECT_NAME} + ${LIBS} + glfw + OpenGL::GL + ${OPENAL_LIBRARY} + GLEW::GLEW + ZLIB::ZLIB + PNG::PNG + CURL::libcurl + ${VORBISLIB} + ${LUA_LIBRARIES} + ${CMAKE_DL_LIBS}) diff --git a/src/content/ContentPack.hpp b/src/content/ContentPack.hpp index c8e0de9b..e4360bdc 100644 --- a/src/content/ContentPack.hpp +++ b/src/content/ContentPack.hpp @@ -109,6 +109,10 @@ struct WorldFuncsSet { bool onblockbroken; bool onblockinteract; bool onplayertick; + bool onchunkpresent; + bool onchunkremove; + bool oninventoryopen; + bool oninventoryclosed; }; class ContentPackRuntime { diff --git a/src/engine/ServerMainloop.cpp b/src/engine/ServerMainloop.cpp index eb570e7b..b97919be 100644 --- a/src/engine/ServerMainloop.cpp +++ b/src/engine/ServerMainloop.cpp @@ -70,7 +70,7 @@ void ServerMainloop::run() { begin = system_clock::now(); } } - logger.info() << "test finished"; + logger.info() << "script finished"; } void ServerMainloop::setLevel(std::unique_ptr level) { diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 3bf7cab2..01798503 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -413,6 +413,7 @@ std::shared_ptr Hud::openInventory( } secondInvView->bind(inv, &content); add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false)); + scripting::on_inventory_open(&player, *inv); return inv; } @@ -447,6 +448,8 @@ void Hud::openInventory( blockPos = block; currentblockid = chunks.require(block.x, block.y, block.z).id; add(HudElement(hud_element_mode::inventory_bound, doc, blockUI, false)); + + scripting::on_inventory_open(&player, *blockinv); } void Hud::showExchangeSlot() { @@ -461,7 +464,6 @@ void Hud::showExchangeSlot() { exchangeSlot->setInteractive(false); exchangeSlot->setZIndex(1); gui.store(SlotView::EXCHANGE_SLOT_NAME, exchangeSlot); - } void Hud::showOverlay( @@ -517,13 +519,19 @@ void Hud::dropExchangeSlot() { } void Hud::closeInventory() { + if (blockUI) { + scripting::on_inventory_closed(&player, *blockUI->getInventory()); + blockUI = nullptr; + } + if (secondInvView) { + scripting::on_inventory_closed(&player, *secondInvView->getInventory()); + } dropExchangeSlot(); gui.remove(SlotView::EXCHANGE_SLOT_NAME); exchangeSlot = nullptr; exchangeSlotInv = nullptr; inventoryOpen = false; inventoryView = nullptr; - blockUI = nullptr; secondUI = nullptr; for (auto& element : elements) { diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index da40d822..6e6b4bd3 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -135,7 +135,7 @@ const Mesh* ChunksRenderer::getOrRender( if (found == meshes.end()) { return render(chunk, important); } - if (chunk->flags.modified) { + if (chunk->flags.modified && chunk->flags.lighted) { render(chunk, important); } return found->second.mesh.get(); @@ -149,9 +149,17 @@ const Mesh* ChunksRenderer::retrieveChunk( size_t index, const Camera& camera, Shader& shader, bool culling ) { auto chunk = chunks.getChunks()[index]; - if (chunk == nullptr || !chunk->flags.lighted) { + if (chunk == nullptr) { return nullptr; } + if (!chunk->flags.lighted) { + const auto& found = meshes.find({chunk->x, chunk->z}); + if (found == meshes.end()) { + return nullptr; + } else { + return found->second.mesh.get(); + } + } float distance = glm::distance( camera.position, glm::vec3( diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index f83363cd..cefb61d5 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -89,7 +89,7 @@ WorldRenderer::WorldRenderer( ) { auto& settings = engine.getSettings(); level.events->listen( - EVT_CHUNK_HIDDEN, + LevelEventType::CHUNK_HIDDEN, [this](LevelEventType, Chunk* chunk) { chunks->unload(chunk); } ); auto assets = engine.getAssets(); diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 15ed2eb8..47a6384d 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -14,6 +14,7 @@ #include "scripting/scripting.hpp" #include "lighting/Lighting.hpp" #include "settings.hpp" +#include "world/LevelEvents.hpp" #include "world/Level.hpp" #include "world/World.hpp" @@ -26,6 +27,14 @@ LevelController::LevelController( level(std::move(levelPtr)), chunks(std::make_unique(*level)), playerTickClock(20, 3) { + + level->events->listen(LevelEventType::CHUNK_PRESENT, [](auto, Chunk* chunk) { + scripting::on_chunk_present(*chunk, chunk->flags.loaded); + }); + level->events->listen(LevelEventType::CHUNK_UNLOAD, [](auto, Chunk* chunk) { + scripting::on_chunk_remove(*chunk); + }); + if (clientPlayer) { chunks->lighting = std::make_unique( level->content, *clientPlayer->chunks @@ -99,6 +108,10 @@ void LevelController::update(float delta, bool pause) { void LevelController::saveWorld() { auto world = level->getWorld(); + if (world->isNameless()) { + logger.info() << "nameless world will not be saved"; + return; + } logger.info() << "writing world '" << world->getName() << "'"; world->wfile->createDirectories(); scripting::on_world_save(); diff --git a/src/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp index 80a924c5..3daebca5 100644 --- a/src/logic/scripting/lua/libs/libcore.cpp +++ b/src/logic/scripting/lua/libs/libcore.cpp @@ -43,6 +43,10 @@ static int l_reset_content(lua::State* L) { return 0; } +static int l_is_content_loaded(lua::State* L) { + return lua::pushboolean(L, content != nullptr); +} + /// @brief Creating new world /// @param name Name world /// @param seed Seed world @@ -51,6 +55,9 @@ static int l_new_world(lua::State* L) { auto name = lua::require_string(L, 1); auto seed = lua::require_string(L, 2); auto generator = lua::require_string(L, 3); + if (level != nullptr) { + throw std::runtime_error("world must be closed before"); + } auto controller = engine->getController(); controller->createWorld(name, seed, generator); return 0; @@ -60,7 +67,9 @@ static int l_new_world(lua::State* L) { /// @param name Name world static int l_open_world(lua::State* L) { auto name = lua::require_string(L, 1); - + if (level != nullptr) { + throw std::runtime_error("world must be closed before"); + } auto controller = engine->getController(); controller->openWorld(name, false); return 0; @@ -258,6 +267,7 @@ const luaL_Reg corelib[] = { {"get_version", lua::wrap}, {"load_content", lua::wrap}, {"reset_content", lua::wrap}, + {"is_content_loaded", lua::wrap}, {"new_world", lua::wrap}, {"open_world", lua::wrap}, {"reopen_world", lua::wrap}, diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 1acc1ad7..5d936842 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -4,8 +4,6 @@ #include "api_lua.hpp" #include "assets/AssetsLoader.hpp" -#include "coders/compression.hpp" -#include "coders/gzip.hpp" #include "coders/json.hpp" #include "engine/Engine.hpp" #include "files/engine_paths.hpp" @@ -14,6 +12,7 @@ #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" #include "voxels/GlobalChunks.hpp" +#include "voxels/compressed_chunks.hpp" #include "world/Level.hpp" #include "world/World.hpp" #include "logic/LevelController.hpp" @@ -123,122 +122,57 @@ 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); + int x = static_cast(lua::tointeger(L, 1)); + int z = static_cast(lua::tointeger(L, 2)); + const auto& chunk = level->chunks->getChunk(x, z); if (chunk == nullptr) { lua::pushnil(L); return 0; } + auto chunkData = compressed_chunks::encode(*chunk); + return lua::newuserdata(L, std::move(chunkData)); +} - bool compress = false; - if (lua::gettop(L) >= 3) { - compress = lua::toboolean(L, 3); +static void integrate_chunk_client(Chunk& chunk) { + int x = chunk.x; + int z = chunk.z; + auto chunksController = controller->getChunksController(); + Lighting& lighting = *chunksController->lighting; + chunk.flags.loadedLights = false; + chunk.flags.lighted = false; + + Lighting::prebuildSkyLight(chunk, *indices); + lighting.onChunkLoaded(x, z, true); + + for (int lz = -1; lz <= 1; lz++) { + for (int lx = -1; lx <= 1; lx++) { + if (std::abs(lx) + std::abs(lz) != 1) { + continue; + } + if (auto other = level->chunks->getChunk(x + lx, z + lz)) { + other->flags.modified = true; + lighting.onChunkLoaded(x - 1, z, true); + } + } } - std::vector chunk_data; - if (compress) { - size_t rle_compressed_size; - size_t gzip_compressed_size; - const auto& data_ptr = chunk->encode(); - ubyte* data = data_ptr.get(); - const auto& rle_compressed_data_ptr = compression::compress( - data, - CHUNK_DATA_LEN, - rle_compressed_size, - compression::Method::EXTRLE16 - ); - const auto& gzip_compressed_data = compression::compress( - rle_compressed_data_ptr.get(), - rle_compressed_size, - gzip_compressed_size, - compression::Method::GZIP - ); - auto tmp = dataio::h2le(rle_compressed_size); - chunk_data.reserve(gzip_compressed_size + sizeof(tmp)); - chunk_data.insert( - chunk_data.begin() + 0, (char*)&tmp, ((char*)&tmp) + sizeof(tmp) - ); - chunk_data.insert( - chunk_data.begin() + sizeof(tmp), - gzip_compressed_data.get(), - gzip_compressed_data.get() + gzip_compressed_size - ); - } else { - const auto& data = chunk->encode(); - chunk_data.reserve(CHUNK_DATA_LEN); - chunk_data.insert( - chunk_data.begin(), data.get(), data.get() + CHUNK_DATA_LEN - ); - } - return lua::newuserdata(L, chunk_data); } static int l_set_chunk_data(lua::State* L) { - int x = (int)lua::tointeger(L, 1); - int y = (int)lua::tointeger(L, 2); + int x = static_cast(lua::tointeger(L, 1)); + int z = static_cast(lua::tointeger(L, 2)); auto buffer = lua::touserdata(L, 3); - bool is_compressed = false; - if (lua::gettop(L) >= 4) { - is_compressed = lua::toboolean(L, 4); - } - auto chunk = level->chunks->getChunk(x, y); + auto chunk = level->chunks->getChunk(x, z); if (chunk == nullptr) { return 0; } - if (is_compressed) { - std::vector& raw_data = buffer->data(); - size_t gzip_decompressed_size = - dataio::le2h(*(size_t*)(raw_data.data())); - const auto& rle_data = compression::decompress( - raw_data.data() + sizeof(gzip_decompressed_size), - buffer->data().size() - sizeof(gzip_decompressed_size), - gzip_decompressed_size, - compression::Method::GZIP - ); - const auto& data = compression::decompress( - rle_data.get(), - gzip_decompressed_size, - CHUNK_DATA_LEN, - compression::Method::EXTRLE16 - ); - chunk->decode(data.get()); - } else { - chunk->decode(buffer->data().data()); + compressed_chunks::decode( + *chunk, buffer->data().data(), buffer->data().size() + ); + if (controller->getChunksController()->lighting == nullptr) { + return lua::pushboolean(L, true); } - - auto chunksController = controller->getChunksController(); - if (chunksController == nullptr) { - return 1; - } - - Lighting& lighting = *chunksController->lighting; - chunk->updateHeights(); - lighting.buildSkyLight(x, y); - chunk->flags.modified = true; - lighting.onChunkLoaded(x, y, true); - - chunk = level->chunks->getChunk(x - 1, y); - if (chunk != nullptr) { - chunk->flags.modified = true; - lighting.onChunkLoaded(x - 1, y, true); - } - chunk = level->chunks->getChunk(x + 1, y); - if (chunk != nullptr) { - chunk->flags.modified = true; - lighting.onChunkLoaded(x + 1, y, true); - } - chunk = level->chunks->getChunk(x, y - 1); - if (chunk != nullptr) { - chunk->flags.modified = true; - lighting.onChunkLoaded(x, y - 1, true); - } - chunk = level->chunks->getChunk(x, y + 1); - if (chunk != nullptr) { - chunk->flags.modified = true; - lighting.onChunkLoaded(x, y + 1, true); - } - - return 1; + integrate_chunk_client(*chunk); + return lua::pushboolean(L, true); } static int l_count_chunks(lua::State* L) { diff --git a/src/logic/scripting/lua/lua_util.cpp b/src/logic/scripting/lua/lua_util.cpp index 8c3adaea..842c7859 100644 --- a/src/logic/scripting/lua/lua_util.cpp +++ b/src/logic/scripting/lua/lua_util.cpp @@ -268,9 +268,9 @@ KeyCallback lua::create_simple_handler(State* L) { scripting::common_func lua::create_lambda(State* L) { auto funcptr = create_lambda_handler(L); return [=](const std::vector& args) -> dv::value { - int top = gettop(L) + 1; if (!get_from(L, LAMBDAS_TABLE, *funcptr, false)) return nullptr; + int top = gettop(L) + 1; for (const auto& arg : args) { pushvalue(L, arg); } @@ -290,9 +290,9 @@ scripting::common_func lua::create_lambda(State* L) { scripting::common_func lua::create_lambda_nothrow(State* L) { auto funcptr = create_lambda_handler(L); return [=](const std::vector& args) -> dv::value { - int top = gettop(L) - 1; if (!get_from(L, LAMBDAS_TABLE, *funcptr, false)) return nullptr; + int top = gettop(L) - 1; for (const auto& arg : args) { pushvalue(L, arg); } diff --git a/src/logic/scripting/lua/lua_wrapper.hpp b/src/logic/scripting/lua/lua_wrapper.hpp index ee184cb1..8637babc 100644 --- a/src/logic/scripting/lua/lua_wrapper.hpp +++ b/src/logic/scripting/lua/lua_wrapper.hpp @@ -21,6 +21,11 @@ namespace lua { } inline void pop(lua::State* L, int n = 1) { +#ifndef NDEBUG + if (lua_gettop(L) < n) { + abort(); + } +#endif lua_pop(L, n); } inline void insert(lua::State* L, int idx) { diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index b8f00d3f..d58879d3 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -24,6 +24,7 @@ #include "util/stringutil.hpp" #include "util/timeutil.hpp" #include "voxels/Block.hpp" +#include "voxels/Chunk.hpp" #include "world/Level.hpp" #include "interfaces/Process.hpp" @@ -411,6 +412,65 @@ bool scripting::on_block_interact( ); } +void scripting::on_chunk_present(const Chunk& chunk, bool loaded) { + auto args = [&chunk, loaded](lua::State* L) { + lua::pushvec_stack<2>(L, {chunk.x, chunk.z}); + lua::pushboolean(L, loaded); + return 3; + }; + for (auto& [packid, pack] : content->getPacks()) { + if (pack->worldfuncsset.onchunkpresent) { + lua::emit_event( + lua::get_main_state(), packid + ":.chunkpresent", args + ); + } + } +} + +void scripting::on_chunk_remove(const Chunk& chunk) { + auto args = [&chunk](lua::State* L) { + lua::pushvec_stack<2>(L, {chunk.x, chunk.z}); + return 2; + }; + for (auto& [packid, pack] : content->getPacks()) { + if (pack->worldfuncsset.onchunkremove) { + lua::emit_event( + lua::get_main_state(), packid + ":.chunkremove", args + ); + } + } +} + +void scripting::on_inventory_open(const Player* player, const Inventory& inventory) { + auto args = [player, &inventory](lua::State* L) { + lua::pushinteger(L, inventory.getId()); + lua::pushinteger(L, player ? player->getId() : -1); + return 2; + }; + for (auto& [packid, pack] : content->getPacks()) { + if (pack->worldfuncsset.oninventoryopen) { + lua::emit_event( + lua::get_main_state(), packid + ":.inventoryopen", args + ); + } + } +} + +void scripting::on_inventory_closed(const Player* player, const Inventory& inventory) { + auto args = [player, &inventory](lua::State* L) { + lua::pushinteger(L, inventory.getId()); + lua::pushinteger(L, player ? player->getId() : -1); + return 2; + }; + for (auto& [packid, pack] : content->getPacks()) { + if (pack->worldfuncsset.oninventoryclosed) { + lua::emit_event( + lua::get_main_state(), packid + ":.inventoryclosed", args + ); + } + } +} + void scripting::on_player_tick(Player* player, int tps) { auto args = [=](lua::State* L) { lua::pushinteger(L, player ? player->getId() : -1); @@ -837,6 +897,14 @@ void scripting::load_world_script( register_event(env, "on_block_interact", prefix + ":.blockinteract"); funcsset.onplayertick = register_event(env, "on_player_tick", prefix + ":.playertick"); + funcsset.onchunkpresent = + register_event(env, "on_chunk_present", prefix + ":.chunkpresent"); + funcsset.onchunkremove = + register_event(env, "on_chunk_remove", prefix + ":.chunkremove"); + funcsset.oninventoryopen = + register_event(env, "on_inventory_open", prefix + ":.inventoryopen"); + funcsset.oninventoryclosed = + register_event(env, "on_inventory_closed", prefix + ":.inventoryclosed"); } void scripting::load_layout_script( diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index fb552f66..b447de28 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -17,6 +17,7 @@ struct ContentPack; class ContentIndices; class Level; class Block; +class Chunk; class Player; struct ItemDef; class Inventory; @@ -84,6 +85,13 @@ namespace scripting { Player* player, const Block& block, const glm::ivec3& pos ); bool on_block_interact(Player* player, const Block& block, const glm::ivec3& pos); + + void on_chunk_present(const Chunk& chunk, bool loaded); + void on_chunk_remove(const Chunk& chunk); + + void on_inventory_open(const Player* player, const Inventory& inventory); + void on_inventory_closed(const Player* player, const Inventory& inventory); + void on_player_tick(Player* player, int tps); /// @brief Called on RMB click with the item selected diff --git a/src/util/HandlersList.hpp b/src/util/HandlersList.hpp index 5af5250d..737fdf36 100644 --- a/src/util/HandlersList.hpp +++ b/src/util/HandlersList.hpp @@ -1,7 +1,9 @@ #pragma once #include -#include +#include +#include +#include #include "typedefs.hpp" #include "delegates.hpp" diff --git a/src/voxels/Chunk.cpp b/src/voxels/Chunk.cpp index a32fc8ba..2c1f4fd8 100644 --- a/src/voxels/Chunk.cpp +++ b/src/voxels/Chunk.cpp @@ -13,19 +13,6 @@ Chunk::Chunk(int xpos, int zpos) : x(xpos), z(zpos) { top = CHUNK_H; } -bool Chunk::isEmpty() const { - int id = -1; - for (uint i = 0; i < CHUNK_VOL; i++) { - if (voxels[i].id != id) { - if (id != -1) - return false; - else - id = voxels[i].id; - } - } - return true; -} - void Chunk::updateHeights() { for (uint i = 0; i < CHUNK_VOL; i++) { if (voxels[i].id != 0) { diff --git a/src/voxels/Chunk.hpp b/src/voxels/Chunk.hpp index 55cdac21..3ce6e641 100644 --- a/src/voxels/Chunk.hpp +++ b/src/voxels/Chunk.hpp @@ -11,6 +11,7 @@ #include "maths/aabb.hpp" #include "voxel.hpp" +/// @brief Total bytes number of chunk voxel data inline constexpr int CHUNK_DATA_LEN = CHUNK_VOL * 4; class ContentReport; @@ -45,8 +46,7 @@ public: Chunk(int x, int z); - bool isEmpty() const; - + /// @brief Refresh `bottom` and `top` values void updateHeights(); // unused diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index f65c1863..bf9b2990 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -34,7 +34,7 @@ Chunks::Chunks( areaMap(w, d) { areaMap.setCenter(ox-w/2, oz-d/2); areaMap.setOutCallback([this](int, int, const auto& chunk) { - this->events->trigger(EVT_CHUNK_HIDDEN, chunk.get()); + this->events->trigger(LevelEventType::CHUNK_HIDDEN, chunk.get()); }); } @@ -323,7 +323,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 (events) { - events->trigger(LevelEventType::EVT_CHUNK_SHOWN, chunk.get()); + events->trigger(LevelEventType::CHUNK_SHOWN, chunk.get()); } return true; } diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp index 8f8e05bb..1e67a374 100644 --- a/src/voxels/GlobalChunks.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -12,6 +12,7 @@ #include "objects/Entities.hpp" #include "voxels/blocks_agent.hpp" #include "typedefs.hpp" +#include "world/LevelEvents.hpp" #include "world/Level.hpp" #include "world/World.hpp" #include "Block.hpp" @@ -125,6 +126,8 @@ std::shared_ptr GlobalChunks::create(int x, int z) { chunk->flags.loadedLights = true; } chunk->blocksMetadata = regions.getBlocksData(chunk->x, chunk->z); + + level.events->trigger(LevelEventType::CHUNK_PRESENT, chunk.get()); return chunk; } diff --git a/src/voxels/compressed_chunks.cpp b/src/voxels/compressed_chunks.cpp new file mode 100644 index 00000000..0e6772af --- /dev/null +++ b/src/voxels/compressed_chunks.cpp @@ -0,0 +1,61 @@ +#include "compressed_chunks.hpp" + +#include "coders/rle.hpp" +#include "coders/gzip.hpp" +#include "coders/byte_utils.hpp" +#include "voxels/Chunk.hpp" + +inline constexpr int HAS_VOXELS = 0x1; +inline constexpr int HAS_METADATA = 0x2; + +std::vector compressed_chunks::encode(const Chunk& chunk) { + auto data = chunk.encode(); + + /// world.get_chunk_data is only available in the main Lua state + static util::Buffer rleBuffer; + if (rleBuffer.size() < CHUNK_DATA_LEN * 2) { + rleBuffer = util::Buffer(CHUNK_DATA_LEN * 2); + } + size_t rleCompressedSize = + extrle::encode16(data.get(), CHUNK_DATA_LEN, rleBuffer.data()); + + const auto gzipCompressedData = gzip::compress( + rleBuffer.data(), rleCompressedSize + ); + auto metadataBytes = chunk.blocksMetadata.serialize(); + + ByteBuilder builder(2 + 8 + gzipCompressedData.size() + metadataBytes.size()); + builder.put(HAS_VOXELS | HAS_METADATA); // flags + builder.put(0); // reserved + builder.putInt32(gzipCompressedData.size()); + builder.put(gzipCompressedData.data(), gzipCompressedData.size()); + builder.putInt32(metadataBytes.size()); + builder.put(metadataBytes.data(), metadataBytes.size()); + return builder.build(); +} + +void compressed_chunks::decode(Chunk& chunk, const ubyte* src, size_t size) { + ByteReader reader(src, size); + + ubyte flags = reader.get(); + reader.skip(1); // reserved byte + + if (flags & HAS_VOXELS) { + size_t gzipCompressedSize = reader.getInt32(); + + auto rleData = gzip::decompress(reader.pointer(), gzipCompressedSize); + reader.skip(gzipCompressedSize); + + /// world.get_chunk_data is only available in the main Lua state + static util::Buffer voxelData (CHUNK_DATA_LEN); + extrle::decode16(rleData.data(), rleData.size(), voxelData.data()); + chunk.decode(voxelData.data()); + chunk.updateHeights(); + } + if (flags & HAS_METADATA) { + size_t metadataSize = reader.getInt32(); + chunk.blocksMetadata.deserialize(reader.pointer(), metadataSize); + reader.skip(metadataSize); + } + chunk.setModifiedAndUnsaved(); +} diff --git a/src/voxels/compressed_chunks.hpp b/src/voxels/compressed_chunks.hpp new file mode 100644 index 00000000..dc0bc5b6 --- /dev/null +++ b/src/voxels/compressed_chunks.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "typedefs.hpp" + +#include + +class Chunk; + +namespace compressed_chunks { + std::vector encode(const Chunk& chunk); + void decode(Chunk& chunk, const ubyte* src, size_t size); +} diff --git a/src/world/Level.cpp b/src/world/Level.cpp index 7ea871ee..460b34a4 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -52,13 +52,14 @@ Level::Level( entities->setNextID(worldInfo.nextEntityId); } - events->listen(LevelEventType::EVT_CHUNK_SHOWN, [this](LevelEventType, Chunk* chunk) { + events->listen(LevelEventType::CHUNK_SHOWN, [this](LevelEventType, Chunk* chunk) { chunks->incref(chunk); }); - events->listen(LevelEventType::EVT_CHUNK_HIDDEN, [this](LevelEventType, Chunk* chunk) { + events->listen(LevelEventType::CHUNK_HIDDEN, [this](LevelEventType, Chunk* chunk) { chunks->decref(chunk); }); - chunks->setOnUnload([this](const Chunk& chunk) { + chunks->setOnUnload([this](Chunk& chunk) { + events->trigger(LevelEventType::CHUNK_UNLOAD, &chunk); AABB aabb = chunk.getAABB(); entities->despawn(entities->getAllInside(aabb)); }); diff --git a/src/world/LevelEvents.hpp b/src/world/LevelEvents.hpp index f7bbaa7f..8096428b 100644 --- a/src/world/LevelEvents.hpp +++ b/src/world/LevelEvents.hpp @@ -6,9 +6,11 @@ class Chunk; -enum LevelEventType { - EVT_CHUNK_SHOWN, - EVT_CHUNK_HIDDEN, +enum class LevelEventType { + CHUNK_SHOWN, + CHUNK_HIDDEN, + CHUNK_PRESENT, + CHUNK_UNLOAD, }; using ChunkEventFunc = std::function; diff --git a/src/world/World.cpp b/src/world/World.cpp index a3b83742..54785862 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -94,7 +94,12 @@ std::unique_ptr World::create( content, packs ); - logger.info() << "created world '" << name << "' (" << directory.u8string() << ")"; + if (name.empty()) { + logger.info() << "created nameless world"; + } else { + logger.info() << "created world '" << name << "' (" + << directory.u8string() << ")"; + } logger.info() << "world seed: " << seed << " generator: " << generator; return std::make_unique(std::move(world), content, settings); } diff --git a/src/world/World.hpp b/src/world/World.hpp index 552f70ec..da1ebd81 100644 --- a/src/world/World.hpp +++ b/src/world/World.hpp @@ -140,6 +140,10 @@ public: /// @brief Get world generator id std::string getGenerator() const; + bool isNameless() const { + return info.name.empty(); + } + WorldInfo& getInfo() { return info; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0597d6e4..df2d1e17 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,23 +8,19 @@ find_package(GTest) add_executable(${PROJECT_NAME} ${SOURCES}) -target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src) -target_link_libraries( - ${PROJECT_NAME} - VoxelEngineSrc - GTest::gtest_main -) +target_include_directories(${PROJECT_NAME} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src) +target_link_libraries(${PROJECT_NAME} VoxelEngineSrc GTest::gtest_main) -# HACK: copy res to test/ folder for fixing problem compatibility MultiConfig and non -# MultiConfig builds. Delete in future and use only root res folder -# Also this resolve problem with ctests, because it set cwd to CMAKE_CURRENT_BINARY_DIR +# HACK: copy res to test/ folder for fixing problem compatibility MultiConfig +# and non MultiConfig builds. Delete in future and use only root res folder Also +# this resolve problem with ctests, because it set cwd to +# CMAKE_CURRENT_BINARY_DIR add_custom_command( - TARGET ${PROJECT_NAME} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different - ${CMAKE_SOURCE_DIR}/res - ${CMAKE_CURRENT_BINARY_DIR}/res - ) + TARGET ${PROJECT_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different + ${CMAKE_SOURCE_DIR}/res ${CMAKE_CURRENT_BINARY_DIR}/res) include(GoogleTest) gtest_discover_tests(${PROJECT_NAME}) diff --git a/vctest/CMakeLists.txt b/vctest/CMakeLists.txt index 6c1a3843..916eef31 100644 --- a/vctest/CMakeLists.txt +++ b/vctest/CMakeLists.txt @@ -8,24 +8,35 @@ add_executable(${PROJECT_NAME} ${SOURCES}) if(MSVC) if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) + 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) + 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) + target_compile_options(${PROJECT_NAME} PRIVATE /W4) endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + 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) + 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") +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}) +target_include_directories( + ${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src ${CMAKE_DL_LIBS})