Merge branch 'MihailRis:main' into main
This commit is contained in:
commit
a83b63db6c
3
.cmake-format.py
Normal file
3
.cmake-format.py
Normal file
@ -0,0 +1,3 @@
|
||||
tab_size = 4
|
||||
enable_sort = True
|
||||
autosort = True
|
||||
4
.github/workflows/appimage.yml
vendored
4
.github/workflows/appimage.yml
vendored
@ -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
|
||||
|
||||
@ -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$<$<CONFIG:Debug>:Debug>DLL
|
||||
if (VCPKG_TARGET_TRIPLET MATCHES "static")
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>: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$<$<CONFIG:Debug>:Debug>DLL
|
||||
if(VCPKG_TARGET_TRIPLET MATCHES "static")
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>: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
|
||||
$<TARGET_FILE_DIR:${PROJECT_NAME}>/res
|
||||
)
|
||||
TARGET ${PROJECT_NAME}
|
||||
POST_BUILD
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E copy_directory_if_different
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/res $<TARGET_FILE_DIR:${PROJECT_NAME}>/res)
|
||||
|
||||
if (VOXELENGINE_BUILD_TESTS)
|
||||
if(VOXELENGINE_BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
```
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -69,6 +69,12 @@ app.config_packs(
|
||||
Обновляет конфигурацию паков, автоматически удаляя лишние, добавляя отсутствующие в прошлой конфигурации.
|
||||
Использует app.reconfig_packs.
|
||||
|
||||
```lua
|
||||
app.is_content_loaded() -> bool
|
||||
```
|
||||
|
||||
Проверяет, загружен ли контент.
|
||||
|
||||
```lua
|
||||
app.new_world(
|
||||
-- название мира
|
||||
|
||||
@ -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
|
||||
```
|
||||
|
||||
@ -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`.
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -103,6 +103,7 @@ movement.sprint=Ускорение
|
||||
movement.crouch=Красться
|
||||
movement.cheat=Чит
|
||||
hud.inventory=Инвентарь
|
||||
hud.chat=Чат
|
||||
player.pick=Подобрать Блок
|
||||
player.attack=Атаковать
|
||||
player.destroy=Сломать
|
||||
|
||||
@ -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})
|
||||
|
||||
@ -109,6 +109,10 @@ struct WorldFuncsSet {
|
||||
bool onblockbroken;
|
||||
bool onblockinteract;
|
||||
bool onplayertick;
|
||||
bool onchunkpresent;
|
||||
bool onchunkremove;
|
||||
bool oninventoryopen;
|
||||
bool oninventoryclosed;
|
||||
};
|
||||
|
||||
class ContentPackRuntime {
|
||||
|
||||
@ -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> level) {
|
||||
|
||||
@ -413,6 +413,7 @@ std::shared_ptr<Inventory> 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) {
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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<ChunksController>(*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<Lighting>(
|
||||
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();
|
||||
|
||||
@ -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<l_get_version>},
|
||||
{"load_content", lua::wrap<l_load_content>},
|
||||
{"reset_content", lua::wrap<l_reset_content>},
|
||||
{"is_content_loaded", lua::wrap<l_is_content_loaded>},
|
||||
{"new_world", lua::wrap<l_new_world>},
|
||||
{"open_world", lua::wrap<l_open_world>},
|
||||
{"reopen_world", lua::wrap<l_reopen_world>},
|
||||
|
||||
@ -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<int>(lua::tointeger(L, 1));
|
||||
int z = static_cast<int>(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<lua::LuaBytearray>(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<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) {
|
||||
int x = (int)lua::tointeger(L, 1);
|
||||
int y = (int)lua::tointeger(L, 2);
|
||||
int x = static_cast<int>(lua::tointeger(L, 1));
|
||||
int z = static_cast<int>(lua::tointeger(L, 2));
|
||||
auto buffer = lua::touserdata<lua::LuaBytearray>(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<ubyte>& 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) {
|
||||
|
||||
@ -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<dv::value>& 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<dv::value>& 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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "typedefs.hpp"
|
||||
#include "delegates.hpp"
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>& 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;
|
||||
}
|
||||
|
||||
@ -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<Chunk> 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;
|
||||
}
|
||||
|
||||
|
||||
61
src/voxels/compressed_chunks.cpp
Normal file
61
src/voxels/compressed_chunks.cpp
Normal 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
src/voxels/compressed_chunks.hpp
Normal file
12
src/voxels/compressed_chunks.hpp
Normal 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);
|
||||
}
|
||||
@ -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));
|
||||
});
|
||||
|
||||
@ -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<void(LevelEventType, Chunk*)>;
|
||||
|
||||
@ -94,7 +94,12 @@ std::unique_ptr<Level> 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<Level>(std::move(world), content, settings);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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})
|
||||
|
||||
@ -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$<$<CONFIG:Release>: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$<$<CONFIG:Release>: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})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user