Merge branch 'MihailRis:main' into main

This commit is contained in:
Xertis
2025-01-14 00:05:07 +03:00
committed by GitHub
40 changed files with 491 additions and 209 deletions
+3
View File
@@ -0,0 +1,3 @@
tab_size = 4
enable_sort = True
autosort = True
+2 -2
View File
@@ -32,11 +32,11 @@ jobs:
# install EnTT # install EnTT
git clone https://github.com/skypjack/entt.git git clone https://github.com/skypjack/entt.git
cd entt/build cd entt/build
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. cmake -DCMAKE_BUILD_TYPE=Release ..
sudo make install sudo make install
cd ../.. cd ../..
- name: Configure - 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 - name: Build
run: cmake --build build -t install run: cmake --build build -t install
- name: Run tests - name: Run tests
+45 -33
View File
@@ -8,18 +8,20 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if (CMAKE_SYSTEM_NAME STREQUAL "Windows") if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
# We use two types linking: for clang build is static (vcpkg triplet x64-windows-static) # We use two types linking: for clang build is static (vcpkg triplet
# and for msvc build is dynamic linking (vcpkg triplet x64-windows) # x64-windows-static) and for msvc build is dynamic linking (vcpkg triplet
# By default CMAKE_MSVC_RUNTIME_LIBRARY set by MultiThreaded$<$<CONFIG:Debug>:Debug>DLL # x64-windows) By default CMAKE_MSVC_RUNTIME_LIBRARY set by
if (VCPKG_TARGET_TRIPLET MATCHES "static") # MultiThreaded$<$<CONFIG:Debug>:Debug>DLL
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") if(VCPKG_TARGET_TRIPLET MATCHES "static")
endif() set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
endif() endif()
add_subdirectory(src) add_subdirectory(src)
add_executable(${PROJECT_NAME} src/main.cpp) 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) if(VOXELENGINE_BUILD_APPDIR)
include(${CMAKE_CURRENT_SOURCE_DIR}/dev/cmake/BuildAppdir.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/dev/cmake/BuildAppdir.cmake)
@@ -27,33 +29,44 @@ endif()
if(MSVC) if(MSVC)
if(NOT CMAKE_BUILD_TYPE) 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() endif()
if((CMAKE_BUILD_TYPE EQUAL "Release") OR (CMAKE_BUILD_TYPE EQUAL "RelWithDebInfo")) if((CMAKE_BUILD_TYPE EQUAL "Release") OR (CMAKE_BUILD_TYPE EQUAL
target_compile_options(${PROJECT_NAME} PRIVATE /W4 /MT /O2) "RelWithDebInfo"))
target_compile_options(${PROJECT_NAME} PRIVATE /W4 /MT /O2)
else() else()
target_compile_options(${PROJECT_NAME} PRIVATE /W4) target_compile_options(${PROJECT_NAME} PRIVATE /W4)
endif() 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() else()
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra target_compile_options(
# additional warnings ${PROJECT_NAME}
-Wformat-nonliteral -Wcast-align PRIVATE -Wall
-Wpointer-arith -Wundef -Wextra
-Wwrite-strings -Wno-unused-parameter) # additional warnings
if (CMAKE_BUILD_TYPE MATCHES "Debug") -Wformat-nonliteral
target_compile_options(${PROJECT_NAME} PRIVATE -Og) -Wcast-align
endif() -Wpointer-arith
if (WIN32) -Wundef
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") -Wwrite-strings
endif() -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() 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") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie -lstdc++fs")
endif() endif()
if (WIN32) if(WIN32)
target_link_libraries(${PROJECT_NAME} VoxelEngineSrc winmm) target_link_libraries(${PROJECT_NAME} VoxelEngineSrc winmm)
endif() endif()
@@ -61,14 +74,13 @@ target_link_libraries(${PROJECT_NAME} VoxelEngineSrc ${CMAKE_DL_LIBS})
# Deploy res to build dir # Deploy res to build dir
add_custom_command( add_custom_command(
TARGET ${PROJECT_NAME} TARGET ${PROJECT_NAME}
POST_BUILD POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different COMMAND
${CMAKE_CURRENT_SOURCE_DIR}/res ${CMAKE_COMMAND} -E copy_directory_if_different
$<TARGET_FILE_DIR:${PROJECT_NAME}>/res ${CMAKE_CURRENT_SOURCE_DIR}/res $<TARGET_FILE_DIR:${PROJECT_NAME}>/res)
)
if (VOXELENGINE_BUILD_TESTS) if(VOXELENGINE_BUILD_TESTS)
enable_testing() enable_testing()
add_subdirectory(test) add_subdirectory(test)
endif() endif()
+2
View File
@@ -5,6 +5,7 @@ app.reconfig_packs({"base"}, {})
app.new_world("demo", "2019", "core:default") app.new_world("demo", "2019", "core:default")
assert(world.is_open()) assert(world.is_open())
assert(world.get_generator() == "core:default") assert(world.get_generator() == "core:default")
assert(app.is_content_loaded())
app.sleep(1) app.sleep(1)
assert(world.get_total_time() > 0.0) assert(world.get_total_time() > 0.0)
print(world.get_total_time()) print(world.get_total_time())
@@ -12,6 +13,7 @@ print(world.get_total_time())
-- Close -- Close
app.close_world(true) app.close_world(true)
assert(not world.is_open()) assert(not world.is_open())
assert(not app.is_content_loaded())
-- Reopen -- Reopen
app.open_world("demo") app.open_world("demo")
+6
View File
@@ -69,6 +69,12 @@ app.config_packs(
Updates the packs configuration, automatically removing unspecified ones, adding those missing in the previous configuration. Updates the packs configuration, automatically removing unspecified ones, adding those missing in the previous configuration.
Uses app.reconfig_packs. Uses app.reconfig_packs.
```lua
app.is_content_loaded() -> bool
```
Checks if content is loaded.
```lua ```lua
app.new_world( app.new_world(
-- world name -- world name
+14
View File
@@ -47,4 +47,18 @@ world.is_night() -> bool
-- Returns the total number of chunks loaded into memory -- Returns the total number of chunks loaded into memory
world.count_chunks() -> int 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
``` ```
+28
View File
@@ -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. 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 ## Layout events
Script *layouts/layout_name.xml.lua* events. Script *layouts/layout_name.xml.lua* events.
+6
View File
@@ -69,6 +69,12 @@ app.config_packs(
Обновляет конфигурацию паков, автоматически удаляя лишние, добавляя отсутствующие в прошлой конфигурации. Обновляет конфигурацию паков, автоматически удаляя лишние, добавляя отсутствующие в прошлой конфигурации.
Использует app.reconfig_packs. Использует app.reconfig_packs.
```lua
app.is_content_loaded() -> bool
```
Проверяет, загружен ли контент.
```lua ```lua
app.new_world( app.new_world(
-- название мира -- название мира
+14
View File
@@ -46,4 +46,18 @@ world.is_night() -> bool
-- Возвращает общее количество загруженных в память чанков -- Возвращает общее количество загруженных в память чанков
world.count_chunks() -> int 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
``` ```
+29
View File
@@ -126,6 +126,35 @@ function on_block_interact(blockid, x, y, z, playerid) -> bool
Вызывается при нажатии на блок ПКМ. Предотвращает установку блоков, если возвращает `true` Вызывается при нажатии на блок ПКМ. Предотвращает установку блоков, если возвращает `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`. События прописываются в файле `layouts/имя_макета.xml.lua`.
+1
View File
@@ -19,3 +19,4 @@ player.pick="mouse:middle"
player.drop="key:q" player.drop="key:q"
player.fast_interaction="key:x" player.fast_interaction="key:x"
hud.inventory="key:tab" hud.inventory="key:tab"
hud.chat="key:t"
+9
View File
@@ -39,6 +39,7 @@ local function complete_app_lib(app)
app.get_setting_info = core.get_setting_info app.get_setting_info = core.get_setting_info
app.load_content = core.load_content app.load_content = core.load_content
app.reset_content = core.reset_content app.reset_content = core.reset_content
app.is_content_loaded = core.is_content_loaded
function app.config_packs(packs_list) function app.config_packs(packs_list)
-- Check if packs are valid and add dependencies to the configuration -- 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"}) hud.show_overlay("core:console", false, {"console"})
end) end)
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 end
local RULES_FILE = "world:rules.toml" local RULES_FILE = "world:rules.toml"
+1
View File
@@ -33,6 +33,7 @@ movement.sprint=Sprint
movement.crouch=Crouch movement.crouch=Crouch
movement.cheat=Cheat movement.cheat=Cheat
hud.inventory=Inventory hud.inventory=Inventory
hud.chat=Chat
player.pick=Pick Block player.pick=Pick Block
player.attack=Attack player.attack=Attack
player.destroy=Destroy player.destroy=Destroy
+1
View File
@@ -103,6 +103,7 @@ movement.sprint=Ускорение
movement.crouch=Красться movement.crouch=Красться
movement.cheat=Чит movement.cheat=Чит
hud.inventory=Инвентарь hud.inventory=Инвентарь
hud.chat=Чат
player.pick=Подобрать Блок player.pick=Подобрать Блок
player.attack=Атаковать player.attack=Атаковать
player.destroy=Сломать player.destroy=Сломать
+22 -8
View File
@@ -10,7 +10,7 @@ add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
find_package(GLEW REQUIRED) find_package(GLEW REQUIRED)
if (CMAKE_SYSTEM_NAME STREQUAL "Windows") if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
# specific for vcpkg # specific for vcpkg
find_package(OpenAL CONFIG REQUIRED) find_package(OpenAL CONFIG REQUIRED)
set(OPENAL_LIBRARY OpenAL::OpenAL) set(OPENAL_LIBRARY OpenAL::OpenAL)
@@ -20,17 +20,19 @@ endif()
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
find_package(PNG REQUIRED) find_package(PNG REQUIRED)
find_package(CURL REQUIRED) find_package(CURL REQUIRED)
if (NOT APPLE) if(NOT APPLE)
find_package(EnTT REQUIRED) find_package(EnTT REQUIRED)
endif() endif()
set(LIBS "") set(LIBS "")
if (CMAKE_SYSTEM_NAME STREQUAL "Windows") if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
# Use directly linking to lib instead PkgConfig (because pkg-config dont install on windows as default) # Use directly linking to lib instead PkgConfig (because pkg-config dont
# TODO: Do it with findLua. # install on windows as default) TODO: Do it with findLua.
if (MSVC) if(MSVC)
set(LUA_INCLUDE_DIR "$ENV{VCPKG_ROOT}/packages/luajit_${VCPKG_TARGET_TRIPLET}/include/luajit") set(LUA_INCLUDE_DIR
"$ENV{VCPKG_ROOT}/packages/luajit_${VCPKG_TARGET_TRIPLET}/include/luajit"
)
find_package(Lua REQUIRED) find_package(Lua REQUIRED)
else() else()
# Used for mingw-clang cross compiling from msys2 # Used for mingw-clang cross compiling from msys2
@@ -70,4 +72,16 @@ endif()
include_directories(${LUA_INCLUDE_DIR}) include_directories(${LUA_INCLUDE_DIR})
include_directories(${CURL_INCLUDE_DIR}) include_directories(${CURL_INCLUDE_DIR})
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_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})
+4
View File
@@ -109,6 +109,10 @@ struct WorldFuncsSet {
bool onblockbroken; bool onblockbroken;
bool onblockinteract; bool onblockinteract;
bool onplayertick; bool onplayertick;
bool onchunkpresent;
bool onchunkremove;
bool oninventoryopen;
bool oninventoryclosed;
}; };
class ContentPackRuntime { class ContentPackRuntime {
+1 -1
View File
@@ -70,7 +70,7 @@ void ServerMainloop::run() {
begin = system_clock::now(); begin = system_clock::now();
} }
} }
logger.info() << "test finished"; logger.info() << "script finished";
} }
void ServerMainloop::setLevel(std::unique_ptr<Level> level) { void ServerMainloop::setLevel(std::unique_ptr<Level> level) {
+10 -2
View File
@@ -413,6 +413,7 @@ std::shared_ptr<Inventory> Hud::openInventory(
} }
secondInvView->bind(inv, &content); secondInvView->bind(inv, &content);
add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false)); add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false));
scripting::on_inventory_open(&player, *inv);
return inv; return inv;
} }
@@ -447,6 +448,8 @@ void Hud::openInventory(
blockPos = block; blockPos = block;
currentblockid = chunks.require(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)); add(HudElement(hud_element_mode::inventory_bound, doc, blockUI, false));
scripting::on_inventory_open(&player, *blockinv);
} }
void Hud::showExchangeSlot() { void Hud::showExchangeSlot() {
@@ -461,7 +464,6 @@ void Hud::showExchangeSlot() {
exchangeSlot->setInteractive(false); exchangeSlot->setInteractive(false);
exchangeSlot->setZIndex(1); exchangeSlot->setZIndex(1);
gui.store(SlotView::EXCHANGE_SLOT_NAME, exchangeSlot); gui.store(SlotView::EXCHANGE_SLOT_NAME, exchangeSlot);
} }
void Hud::showOverlay( void Hud::showOverlay(
@@ -517,13 +519,19 @@ void Hud::dropExchangeSlot() {
} }
void Hud::closeInventory() { void Hud::closeInventory() {
if (blockUI) {
scripting::on_inventory_closed(&player, *blockUI->getInventory());
blockUI = nullptr;
}
if (secondInvView) {
scripting::on_inventory_closed(&player, *secondInvView->getInventory());
}
dropExchangeSlot(); dropExchangeSlot();
gui.remove(SlotView::EXCHANGE_SLOT_NAME); gui.remove(SlotView::EXCHANGE_SLOT_NAME);
exchangeSlot = nullptr; exchangeSlot = nullptr;
exchangeSlotInv = nullptr; exchangeSlotInv = nullptr;
inventoryOpen = false; inventoryOpen = false;
inventoryView = nullptr; inventoryView = nullptr;
blockUI = nullptr;
secondUI = nullptr; secondUI = nullptr;
for (auto& element : elements) { for (auto& element : elements) {
+10 -2
View File
@@ -135,7 +135,7 @@ const Mesh* ChunksRenderer::getOrRender(
if (found == meshes.end()) { if (found == meshes.end()) {
return render(chunk, important); return render(chunk, important);
} }
if (chunk->flags.modified) { if (chunk->flags.modified && chunk->flags.lighted) {
render(chunk, important); render(chunk, important);
} }
return found->second.mesh.get(); return found->second.mesh.get();
@@ -149,9 +149,17 @@ const Mesh* ChunksRenderer::retrieveChunk(
size_t index, const Camera& camera, Shader& shader, bool culling size_t index, const Camera& camera, Shader& shader, bool culling
) { ) {
auto chunk = chunks.getChunks()[index]; auto chunk = chunks.getChunks()[index];
if (chunk == nullptr || !chunk->flags.lighted) { if (chunk == nullptr) {
return 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( float distance = glm::distance(
camera.position, camera.position,
glm::vec3( glm::vec3(
+1 -1
View File
@@ -89,7 +89,7 @@ WorldRenderer::WorldRenderer(
) { ) {
auto& settings = engine.getSettings(); auto& settings = engine.getSettings();
level.events->listen( level.events->listen(
EVT_CHUNK_HIDDEN, LevelEventType::CHUNK_HIDDEN,
[this](LevelEventType, Chunk* chunk) { chunks->unload(chunk); } [this](LevelEventType, Chunk* chunk) { chunks->unload(chunk); }
); );
auto assets = engine.getAssets(); auto assets = engine.getAssets();
+13
View File
@@ -14,6 +14,7 @@
#include "scripting/scripting.hpp" #include "scripting/scripting.hpp"
#include "lighting/Lighting.hpp" #include "lighting/Lighting.hpp"
#include "settings.hpp" #include "settings.hpp"
#include "world/LevelEvents.hpp"
#include "world/Level.hpp" #include "world/Level.hpp"
#include "world/World.hpp" #include "world/World.hpp"
@@ -26,6 +27,14 @@ LevelController::LevelController(
level(std::move(levelPtr)), level(std::move(levelPtr)),
chunks(std::make_unique<ChunksController>(*level)), chunks(std::make_unique<ChunksController>(*level)),
playerTickClock(20, 3) { 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) { if (clientPlayer) {
chunks->lighting = std::make_unique<Lighting>( chunks->lighting = std::make_unique<Lighting>(
level->content, *clientPlayer->chunks level->content, *clientPlayer->chunks
@@ -99,6 +108,10 @@ void LevelController::update(float delta, bool pause) {
void LevelController::saveWorld() { void LevelController::saveWorld() {
auto world = level->getWorld(); auto world = level->getWorld();
if (world->isNameless()) {
logger.info() << "nameless world will not be saved";
return;
}
logger.info() << "writing world '" << world->getName() << "'"; logger.info() << "writing world '" << world->getName() << "'";
world->wfile->createDirectories(); world->wfile->createDirectories();
scripting::on_world_save(); scripting::on_world_save();
+11 -1
View File
@@ -43,6 +43,10 @@ static int l_reset_content(lua::State* L) {
return 0; return 0;
} }
static int l_is_content_loaded(lua::State* L) {
return lua::pushboolean(L, content != nullptr);
}
/// @brief Creating new world /// @brief Creating new world
/// @param name Name world /// @param name Name world
/// @param seed Seed 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 name = lua::require_string(L, 1);
auto seed = lua::require_string(L, 2); auto seed = lua::require_string(L, 2);
auto generator = lua::require_string(L, 3); auto generator = lua::require_string(L, 3);
if (level != nullptr) {
throw std::runtime_error("world must be closed before");
}
auto controller = engine->getController(); auto controller = engine->getController();
controller->createWorld(name, seed, generator); controller->createWorld(name, seed, generator);
return 0; return 0;
@@ -60,7 +67,9 @@ static int l_new_world(lua::State* L) {
/// @param name Name world /// @param name Name world
static int l_open_world(lua::State* L) { static int l_open_world(lua::State* L) {
auto name = lua::require_string(L, 1); auto name = lua::require_string(L, 1);
if (level != nullptr) {
throw std::runtime_error("world must be closed before");
}
auto controller = engine->getController(); auto controller = engine->getController();
controller->openWorld(name, false); controller->openWorld(name, false);
return 0; return 0;
@@ -258,6 +267,7 @@ const luaL_Reg corelib[] = {
{"get_version", lua::wrap<l_get_version>}, {"get_version", lua::wrap<l_get_version>},
{"load_content", lua::wrap<l_load_content>}, {"load_content", lua::wrap<l_load_content>},
{"reset_content", lua::wrap<l_reset_content>}, {"reset_content", lua::wrap<l_reset_content>},
{"is_content_loaded", lua::wrap<l_is_content_loaded>},
{"new_world", lua::wrap<l_new_world>}, {"new_world", lua::wrap<l_new_world>},
{"open_world", lua::wrap<l_open_world>}, {"open_world", lua::wrap<l_open_world>},
{"reopen_world", lua::wrap<l_reopen_world>}, {"reopen_world", lua::wrap<l_reopen_world>},
+38 -104
View File
@@ -4,8 +4,6 @@
#include "api_lua.hpp" #include "api_lua.hpp"
#include "assets/AssetsLoader.hpp" #include "assets/AssetsLoader.hpp"
#include "coders/compression.hpp"
#include "coders/gzip.hpp"
#include "coders/json.hpp" #include "coders/json.hpp"
#include "engine/Engine.hpp" #include "engine/Engine.hpp"
#include "files/engine_paths.hpp" #include "files/engine_paths.hpp"
@@ -14,6 +12,7 @@
#include "voxels/Chunk.hpp" #include "voxels/Chunk.hpp"
#include "voxels/Chunks.hpp" #include "voxels/Chunks.hpp"
#include "voxels/GlobalChunks.hpp" #include "voxels/GlobalChunks.hpp"
#include "voxels/compressed_chunks.hpp"
#include "world/Level.hpp" #include "world/Level.hpp"
#include "world/World.hpp" #include "world/World.hpp"
#include "logic/LevelController.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) { static int l_get_chunk_data(lua::State* L) {
int x = (int)lua::tointeger(L, 1); int x = static_cast<int>(lua::tointeger(L, 1));
int y = (int)lua::tointeger(L, 2); int z = static_cast<int>(lua::tointeger(L, 2));
const auto& chunk = level->chunks->getChunk(x, y); const auto& chunk = level->chunks->getChunk(x, z);
if (chunk == nullptr) { if (chunk == nullptr) {
lua::pushnil(L); lua::pushnil(L);
return 0; return 0;
} }
auto chunkData = compressed_chunks::encode(*chunk);
return lua::newuserdata<lua::LuaBytearray>(L, std::move(chunkData));
}
bool compress = false; static void integrate_chunk_client(Chunk& chunk) {
if (lua::gettop(L) >= 3) { int x = chunk.x;
compress = lua::toboolean(L, 3); 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<ubyte> 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<lua::LuaBytearray>(L, chunk_data);
} }
static int l_set_chunk_data(lua::State* L) { static int l_set_chunk_data(lua::State* L) {
int x = (int)lua::tointeger(L, 1); int x = static_cast<int>(lua::tointeger(L, 1));
int y = (int)lua::tointeger(L, 2); int z = static_cast<int>(lua::tointeger(L, 2));
auto buffer = lua::touserdata<lua::LuaBytearray>(L, 3); auto buffer = lua::touserdata<lua::LuaBytearray>(L, 3);
bool is_compressed = false; auto chunk = level->chunks->getChunk(x, z);
if (lua::gettop(L) >= 4) {
is_compressed = lua::toboolean(L, 4);
}
auto chunk = level->chunks->getChunk(x, y);
if (chunk == nullptr) { if (chunk == nullptr) {
return 0; return 0;
} }
if (is_compressed) { compressed_chunks::decode(
std::vector<ubyte>& raw_data = buffer->data(); *chunk, buffer->data().data(), buffer->data().size()
size_t gzip_decompressed_size = );
dataio::le2h(*(size_t*)(raw_data.data())); if (controller->getChunksController()->lighting == nullptr) {
const auto& rle_data = compression::decompress( return lua::pushboolean(L, true);
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());
} }
integrate_chunk_client(*chunk);
auto chunksController = controller->getChunksController(); return lua::pushboolean(L, true);
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;
} }
static int l_count_chunks(lua::State* L) { static int l_count_chunks(lua::State* L) {
+2 -2
View File
@@ -268,9 +268,9 @@ KeyCallback lua::create_simple_handler(State* L) {
scripting::common_func lua::create_lambda(State* L) { scripting::common_func lua::create_lambda(State* L) {
auto funcptr = create_lambda_handler(L); auto funcptr = create_lambda_handler(L);
return [=](const std::vector<dv::value>& args) -> dv::value { return [=](const std::vector<dv::value>& args) -> dv::value {
int top = gettop(L) + 1;
if (!get_from(L, LAMBDAS_TABLE, *funcptr, false)) if (!get_from(L, LAMBDAS_TABLE, *funcptr, false))
return nullptr; return nullptr;
int top = gettop(L) + 1;
for (const auto& arg : args) { for (const auto& arg : args) {
pushvalue(L, arg); pushvalue(L, arg);
} }
@@ -290,9 +290,9 @@ scripting::common_func lua::create_lambda(State* L) {
scripting::common_func lua::create_lambda_nothrow(State* L) { scripting::common_func lua::create_lambda_nothrow(State* L) {
auto funcptr = create_lambda_handler(L); auto funcptr = create_lambda_handler(L);
return [=](const std::vector<dv::value>& args) -> dv::value { return [=](const std::vector<dv::value>& args) -> dv::value {
int top = gettop(L) - 1;
if (!get_from(L, LAMBDAS_TABLE, *funcptr, false)) if (!get_from(L, LAMBDAS_TABLE, *funcptr, false))
return nullptr; return nullptr;
int top = gettop(L) - 1;
for (const auto& arg : args) { for (const auto& arg : args) {
pushvalue(L, arg); pushvalue(L, arg);
} }
+5
View File
@@ -21,6 +21,11 @@ namespace lua {
} }
inline void pop(lua::State* L, int n = 1) { inline void pop(lua::State* L, int n = 1) {
#ifndef NDEBUG
if (lua_gettop(L) < n) {
abort();
}
#endif
lua_pop(L, n); lua_pop(L, n);
} }
inline void insert(lua::State* L, int idx) { inline void insert(lua::State* L, int idx) {
+68
View File
@@ -24,6 +24,7 @@
#include "util/stringutil.hpp" #include "util/stringutil.hpp"
#include "util/timeutil.hpp" #include "util/timeutil.hpp"
#include "voxels/Block.hpp" #include "voxels/Block.hpp"
#include "voxels/Chunk.hpp"
#include "world/Level.hpp" #include "world/Level.hpp"
#include "interfaces/Process.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) { void scripting::on_player_tick(Player* player, int tps) {
auto args = [=](lua::State* L) { auto args = [=](lua::State* L) {
lua::pushinteger(L, player ? player->getId() : -1); lua::pushinteger(L, player ? player->getId() : -1);
@@ -837,6 +897,14 @@ void scripting::load_world_script(
register_event(env, "on_block_interact", prefix + ":.blockinteract"); register_event(env, "on_block_interact", prefix + ":.blockinteract");
funcsset.onplayertick = funcsset.onplayertick =
register_event(env, "on_player_tick", prefix + ":.playertick"); 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( void scripting::load_layout_script(
+8
View File
@@ -17,6 +17,7 @@ struct ContentPack;
class ContentIndices; class ContentIndices;
class Level; class Level;
class Block; class Block;
class Chunk;
class Player; class Player;
struct ItemDef; struct ItemDef;
class Inventory; class Inventory;
@@ -84,6 +85,13 @@ namespace scripting {
Player* player, const Block& block, const glm::ivec3& pos Player* player, const Block& block, const glm::ivec3& pos
); );
bool on_block_interact(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); void on_player_tick(Player* player, int tps);
/// @brief Called on RMB click with the item selected /// @brief Called on RMB click with the item selected
+3 -1
View File
@@ -1,7 +1,9 @@
#pragma once #pragma once
#include <mutex> #include <mutex>
#include <iostream> #include <vector>
#include <algorithm>
#include <unordered_map>
#include "typedefs.hpp" #include "typedefs.hpp"
#include "delegates.hpp" #include "delegates.hpp"
-13
View File
@@ -13,19 +13,6 @@ Chunk::Chunk(int xpos, int zpos) : x(xpos), z(zpos) {
top = CHUNK_H; 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() { void Chunk::updateHeights() {
for (uint i = 0; i < CHUNK_VOL; i++) { for (uint i = 0; i < CHUNK_VOL; i++) {
if (voxels[i].id != 0) { if (voxels[i].id != 0) {
+2 -2
View File
@@ -11,6 +11,7 @@
#include "maths/aabb.hpp" #include "maths/aabb.hpp"
#include "voxel.hpp" #include "voxel.hpp"
/// @brief Total bytes number of chunk voxel data
inline constexpr int CHUNK_DATA_LEN = CHUNK_VOL * 4; inline constexpr int CHUNK_DATA_LEN = CHUNK_VOL * 4;
class ContentReport; class ContentReport;
@@ -45,8 +46,7 @@ public:
Chunk(int x, int z); Chunk(int x, int z);
bool isEmpty() const; /// @brief Refresh `bottom` and `top` values
void updateHeights(); void updateHeights();
// unused // unused
+2 -2
View File
@@ -34,7 +34,7 @@ Chunks::Chunks(
areaMap(w, d) { areaMap(w, d) {
areaMap.setCenter(ox-w/2, oz-d/2); areaMap.setCenter(ox-w/2, oz-d/2);
areaMap.setOutCallback([this](int, int, const auto& chunk) { 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>& chunk) { bool Chunks::putChunk(const std::shared_ptr<Chunk>& chunk) {
if (areaMap.set(chunk->x, chunk->z, chunk)) { if (areaMap.set(chunk->x, chunk->z, chunk)) {
if (events) { if (events) {
events->trigger(LevelEventType::EVT_CHUNK_SHOWN, chunk.get()); events->trigger(LevelEventType::CHUNK_SHOWN, chunk.get());
} }
return true; return true;
} }
+3
View File
@@ -12,6 +12,7 @@
#include "objects/Entities.hpp" #include "objects/Entities.hpp"
#include "voxels/blocks_agent.hpp" #include "voxels/blocks_agent.hpp"
#include "typedefs.hpp" #include "typedefs.hpp"
#include "world/LevelEvents.hpp"
#include "world/Level.hpp" #include "world/Level.hpp"
#include "world/World.hpp" #include "world/World.hpp"
#include "Block.hpp" #include "Block.hpp"
@@ -125,6 +126,8 @@ std::shared_ptr<Chunk> GlobalChunks::create(int x, int z) {
chunk->flags.loadedLights = true; chunk->flags.loadedLights = true;
} }
chunk->blocksMetadata = regions.getBlocksData(chunk->x, chunk->z); chunk->blocksMetadata = regions.getBlocksData(chunk->x, chunk->z);
level.events->trigger(LevelEventType::CHUNK_PRESENT, chunk.get());
return chunk; return chunk;
} }
+61
View File
@@ -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<ubyte> 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<ubyte> rleBuffer;
if (rleBuffer.size() < CHUNK_DATA_LEN * 2) {
rleBuffer = util::Buffer<ubyte>(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<ubyte> 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();
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include "typedefs.hpp"
#include <vector>
class Chunk;
namespace compressed_chunks {
std::vector<ubyte> encode(const Chunk& chunk);
void decode(Chunk& chunk, const ubyte* src, size_t size);
}
+4 -3
View File
@@ -52,13 +52,14 @@ Level::Level(
entities->setNextID(worldInfo.nextEntityId); 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); 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->decref(chunk);
}); });
chunks->setOnUnload([this](const Chunk& chunk) { chunks->setOnUnload([this](Chunk& chunk) {
events->trigger(LevelEventType::CHUNK_UNLOAD, &chunk);
AABB aabb = chunk.getAABB(); AABB aabb = chunk.getAABB();
entities->despawn(entities->getAllInside(aabb)); entities->despawn(entities->getAllInside(aabb));
}); });
+5 -3
View File
@@ -6,9 +6,11 @@
class Chunk; class Chunk;
enum LevelEventType { enum class LevelEventType {
EVT_CHUNK_SHOWN, CHUNK_SHOWN,
EVT_CHUNK_HIDDEN, CHUNK_HIDDEN,
CHUNK_PRESENT,
CHUNK_UNLOAD,
}; };
using ChunkEventFunc = std::function<void(LevelEventType, Chunk*)>; using ChunkEventFunc = std::function<void(LevelEventType, Chunk*)>;
+6 -1
View File
@@ -94,7 +94,12 @@ std::unique_ptr<Level> World::create(
content, content,
packs 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; logger.info() << "world seed: " << seed << " generator: " << generator;
return std::make_unique<Level>(std::move(world), content, settings); return std::make_unique<Level>(std::move(world), content, settings);
} }
+4
View File
@@ -140,6 +140,10 @@ public:
/// @brief Get world generator id /// @brief Get world generator id
std::string getGenerator() const; std::string getGenerator() const;
bool isNameless() const {
return info.name.empty();
}
WorldInfo& getInfo() { WorldInfo& getInfo() {
return info; return info;
} }
+11 -15
View File
@@ -8,23 +8,19 @@ find_package(GTest)
add_executable(${PROJECT_NAME} ${SOURCES}) add_executable(${PROJECT_NAME} ${SOURCES})
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src) target_include_directories(${PROJECT_NAME}
target_link_libraries( PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src)
${PROJECT_NAME} target_link_libraries(${PROJECT_NAME} VoxelEngineSrc GTest::gtest_main)
VoxelEngineSrc
GTest::gtest_main
)
# HACK: copy res to test/ folder for fixing problem compatibility MultiConfig and non # HACK: copy res to test/ folder for fixing problem compatibility MultiConfig
# MultiConfig builds. Delete in future and use only root res folder # and non MultiConfig builds. Delete in future and use only root res folder Also
# Also this resolve problem with ctests, because it set cwd to CMAKE_CURRENT_BINARY_DIR # this resolve problem with ctests, because it set cwd to
# CMAKE_CURRENT_BINARY_DIR
add_custom_command( add_custom_command(
TARGET ${PROJECT_NAME} TARGET ${PROJECT_NAME}
POST_BUILD POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different
${CMAKE_SOURCE_DIR}/res ${CMAKE_SOURCE_DIR}/res ${CMAKE_CURRENT_BINARY_DIR}/res)
${CMAKE_CURRENT_BINARY_DIR}/res
)
include(GoogleTest) include(GoogleTest)
gtest_discover_tests(${PROJECT_NAME}) gtest_discover_tests(${PROJECT_NAME})
+23 -12
View File
@@ -8,24 +8,35 @@ add_executable(${PROJECT_NAME} ${SOURCES})
if(MSVC) if(MSVC)
if(NOT CMAKE_BUILD_TYPE) 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() endif()
if((CMAKE_BUILD_TYPE EQUAL "Release") OR (CMAKE_BUILD_TYPE EQUAL "RelWithDebInfo")) if((CMAKE_BUILD_TYPE EQUAL "Release") OR (CMAKE_BUILD_TYPE EQUAL
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Release>:Release>") "RelWithDebInfo"))
target_compile_options(${PROJECT_NAME} PRIVATE /W4 /MT /O2) set(CMAKE_MSVC_RUNTIME_LIBRARY
"MultiThreaded$<$<CONFIG:Release>:Release>")
target_compile_options(${PROJECT_NAME} PRIVATE /W4 /MT /O2)
else() else()
target_compile_options(${PROJECT_NAME} PRIVATE /W4) target_compile_options(${PROJECT_NAME} PRIVATE /W4)
endif() endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else() else()
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra target_compile_options(
-Wformat-nonliteral -Wcast-align ${PROJECT_NAME}
-Wpointer-arith -Wundef PRIVATE -Wall
-Wwrite-strings -Wno-unused-parameter) -Wextra
-Wformat-nonliteral
-Wcast-align
-Wpointer-arith
-Wundef
-Wwrite-strings
-Wno-unused-parameter)
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") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie -lstdc++fs")
endif() 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})