From 5bf56d1f649d0a5d03945d1e720fcee859d56039 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 15 Dec 2024 18:20:34 +0300 Subject: [PATCH 01/31] update workflows --- .github/workflows/appimage.yml | 4 ++-- .github/workflows/macos.yml | 2 +- .github/workflows/windows-clang.yml | 4 ++-- .github/workflows/windows.yml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index a72489f0..25ae572f 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ "main", "headless-mode" ] jobs: build-appimage: @@ -24,7 +24,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y build-essential libglfw3-dev libglfw3 libglew-dev \ - libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev libgtest-dev cmake squashfs-tools tree + libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev libgtest-dev cmake squashfs-tools # fix luajit paths sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a sudo ln -s /usr/include/luajit-2.1 /usr/include/lua diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 3efb909c..24832af9 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ "main", "headless-mode" ] jobs: build-dmg: diff --git a/.github/workflows/windows-clang.yml b/.github/workflows/windows-clang.yml index 102557e8..183f2d6d 100644 --- a/.github/workflows/windows-clang.yml +++ b/.github/workflows/windows-clang.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ "main", "headless-mode" ] jobs: build-windows: @@ -31,7 +31,7 @@ jobs: mingw-w64-clang-x86_64-cmake mingw-w64-clang-x86_64-make mingw-w64-clang-x86_64-luajit - git tree + git - name: Set up vcpkg shell: msys2 {0} run: | diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index de477e89..d417197c 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main", "release-**"] pull_request: - branches: [ "main" ] + branches: [ "main", "headless-mode" ] jobs: build-windows: From 6157f8193d94d6488c0e57df26d6ff1e2eb658ad Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Dec 2024 11:26:57 +0300 Subject: [PATCH 02/31] add blocks_agent templates & remove block.get_slow --- dev/tests/example.lua | 7 +- src/constants.hpp | 5 ++ src/logic/scripting/lua/libs/libblock.cpp | 56 +++++++-------- src/logic/scripting/lua/libs/libcore.cpp | 5 +- src/voxels/Chunks.cpp | 30 ++------ src/voxels/Chunks.hpp | 4 ++ src/voxels/GlobalChunks.cpp | 83 ++++++++++++----------- src/voxels/GlobalChunks.hpp | 14 +++- src/voxels/blocks_agent.hpp | 58 ++++++++++++++++ 9 files changed, 158 insertions(+), 104 deletions(-) create mode 100644 src/voxels/blocks_agent.hpp diff --git a/dev/tests/example.lua b/dev/tests/example.lua index c066c1ed..3dbe1197 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -8,11 +8,8 @@ assert(player.get_name(pid) == "Xerxes") test.sleep_until(function() return world.count_chunks() >= 9 end, 1000) print(world.count_chunks()) -for i=1,3 do - print("---") - timeit(1000000, block.get, 0, 0, 0) - timeit(1000000, block.get_slow, 0, 0, 0) -end +timeit(10000000, block.get, 0, 0, 0) +timeit(10000000, core.blank, 0, 0, 0) block.destruct(0, 0, 0, pid) assert(block.get(0, 0, 0) == 0) diff --git a/src/constants.hpp b/src/constants.hpp index c8de5a8d..6f4a6ce6 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -35,6 +35,11 @@ inline constexpr int CHUNK_D = 16; inline constexpr uint VOXEL_USER_BITS = 8; inline constexpr uint VOXEL_USER_BITS_OFFSET = sizeof(blockstate_t)*8-VOXEL_USER_BITS; +/// @brief % unordered map max average buckets load factor. +/// Low value gives significant performance impact by minimizing collisions and +/// lookup latency. Default value (1.0) shows x2 slower work. +inline constexpr float CHUNKS_MAP_MAX_LOAD_FACTOR = 0.1f; + /// @brief chunk volume (count of voxels per Chunk) inline constexpr int CHUNK_VOL = (CHUNK_W * CHUNK_H * CHUNK_D); diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index dbd87489..5d681e5f 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -8,6 +8,7 @@ #include "voxels/Chunks.hpp" #include "voxels/voxel.hpp" #include "voxels/GlobalChunks.hpp" +#include "voxels/blocks_agent.hpp" #include "world/Level.hpp" #include "maths/voxmaths.hpp" #include "data/StructLayout.hpp" @@ -15,13 +16,13 @@ using namespace scripting; -static const Block* require_block(lua::State* L) { +static inline const Block* require_block(lua::State* L) { auto indices = content->getIndices(); auto id = lua::tointeger(L, 1); return indices->blocks.get(id); } -static int l_get_def(lua::State* L) { +static inline int l_get_def(lua::State* L) { if (auto def = require_block(L)) { return lua::pushstring(L, def->name); } @@ -39,8 +40,7 @@ static int l_is_solid_at(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - - return lua::pushboolean(L, level->chunks->isSolidBlock(x, y, z)); + return lua::pushboolean(L, blocks_agent::is_solid_at(*level->chunks, x, y, z)); } static int l_count(lua::State* L) { @@ -110,16 +110,7 @@ static int l_get(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = level->chunks->get(x, y, z); - int id = vox == nullptr ? -1 : vox->id; - return lua::pushinteger(L, id); -} - -static int l_get_slow(lua::State* L) { - auto x = lua::tointeger(L, 1); - auto y = lua::tointeger(L, 2); - auto z = lua::tointeger(L, 3); - auto vox = level->chunksStorage->get(x, y, z); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); int id = vox == nullptr ? -1 : vox->id; return lua::pushinteger(L, id); } @@ -128,7 +119,7 @@ static int l_get_x(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = level->chunksStorage->get(x, y, z); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, glm::ivec3(1, 0, 0)); } @@ -145,7 +136,7 @@ static int l_get_y(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = level->chunksStorage->get(x, y, z); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, glm::ivec3(0, 1, 0)); } @@ -162,7 +153,7 @@ static int l_get_z(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = level->chunksStorage->get(x, y, z); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, glm::ivec3(0, 0, 1)); } @@ -179,7 +170,7 @@ static int l_get_rotation(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - voxel* vox = level->chunksStorage->get(x, y, z); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); int rotation = vox == nullptr ? 0 : vox->state.rotation; return lua::pushinteger(L, rotation); } @@ -197,7 +188,7 @@ static int l_get_states(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = level->chunksStorage->get(x, y, z); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); int states = vox == nullptr ? 0 : blockstate2int(vox->state); return lua::pushinteger(L, states); } @@ -226,7 +217,7 @@ static int l_get_user_bits(lua::State* L) { auto offset = lua::tointeger(L, 4) + VOXEL_USER_BITS_OFFSET; auto bits = lua::tointeger(L, 5); - auto vox = level->chunks->get(x, y, z); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); if (vox == nullptr) { return lua::pushinteger(L, 0); } @@ -352,7 +343,7 @@ static int l_place(lua::State* L) { if (static_cast(id) >= indices->blocks.count()) { return 0; } - if (!level->chunks->get(x, y, z)) { + if (!blocks_agent::get(*level->chunksStorage, x, y, z)) { return 0; } const auto def = level->content->getIndices()->blocks.get(id); @@ -373,11 +364,11 @@ static int l_destruct(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); auto playerid = lua::gettop(L) >= 4 ? lua::tointeger(L, 4) : -1; - auto voxel = level->chunks->get(x, y, z); - if (voxel == nullptr) { + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + if (vox == nullptr) { return 0; } - auto& def = level->content->getIndices()->blocks.require(voxel->id); + auto& def = level->content->getIndices()->blocks.require(vox->id); auto player = level->players->get(playerid); controller->getBlocksController()->breakBlock(player, def, x, y, z); return 0; @@ -504,12 +495,15 @@ static int l_get_field(lua::State* L) { } auto cx = floordiv(x, CHUNK_W); auto cz = floordiv(z, CHUNK_D); - auto chunk = level->chunks->getChunk(cx, cz); + auto chunk = blocks_agent::get_chunk(*level->chunksStorage, cx, cz); + if (chunk == nullptr || y < 0 || y >= CHUNK_H) { + return 0; + } auto lx = x - cx * CHUNK_W; auto lz = z - cz * CHUNK_W; size_t voxelIndex = vox_index(lx, y, lz); - const auto& vox = level->chunks->require(x, y, z); + const auto& vox = chunk->voxels[voxelIndex]; const auto& def = content->getIndices()->blocks.require(vox.id); if (def.dataStruct == nullptr) { return 0; @@ -568,15 +562,18 @@ static int l_set_field(lua::State* L) { if (lua::gettop(L) >= 6) { index = lua::tointeger(L, 6); } - auto vox = level->chunks->get(x, y, z); auto cx = floordiv(x, CHUNK_W); auto cz = floordiv(z, CHUNK_D); - auto chunk = level->chunks->getChunk(cx, cz); auto lx = x - cx * CHUNK_W; auto lz = z - cz * CHUNK_W; + auto chunk = blocks_agent::get_chunk(*level->chunksStorage, cx, cz); + if (chunk == nullptr || y < 0 || y >= CHUNK_H) { + return 0; + } size_t voxelIndex = vox_index(lx, y, lz); + const auto& vox = chunk->voxels[voxelIndex]; - const auto& def = content->getIndices()->blocks.require(vox->id); + const auto& def = content->getIndices()->blocks.require(vox.id); if (def.dataStruct == nullptr) { return 0; } @@ -608,7 +605,6 @@ const luaL_Reg blocklib[] = { {"is_replaceable_at", lua::wrap}, {"set", lua::wrap}, {"get", lua::wrap}, - {"get_slow", lua::wrap}, {"get_X", lua::wrap}, {"get_Y", lua::wrap}, {"get_Z", lua::wrap}, diff --git a/src/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp index 5ac9d93d..d2d0c80f 100644 --- a/src/logic/scripting/lua/libs/libcore.cpp +++ b/src/logic/scripting/lua/libs/libcore.cpp @@ -16,6 +16,7 @@ #include "logic/EngineController.hpp" #include "logic/LevelController.hpp" #include "util/listutil.hpp" +#include "util/platform.hpp" #include "window/Events.hpp" #include "window/Window.hpp" #include "world/Level.hpp" @@ -220,8 +221,6 @@ static int l_load_texture(lua::State* L) { return 0; } -#include "util/platform.hpp" - static int l_open_folder(lua::State* L) { auto path = engine->getPaths()->resolve(lua::require_string(L, 1)); platform::open_folder(path); @@ -239,7 +238,7 @@ static int l_blank(lua::State*) { } const luaL_Reg corelib[] = { - {"nop", lua::wrap}, + {"blank", lua::wrap}, {"get_version", lua::wrap}, {"new_world", lua::wrap}, {"open_world", lua::wrap}, diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 390ac16c..8bfe6166 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -23,6 +23,7 @@ #include "Block.hpp" #include "Chunk.hpp" #include "voxel.hpp" +#include "blocks_agent.hpp" Chunks::Chunks( int32_t w, @@ -33,7 +34,7 @@ Chunks::Chunks( Level* level ) : level(level), - indices(level->content->getIndices()), + indices(level ? level->content->getIndices() : nullptr), areaMap(w, d), worldFiles(wfile) { areaMap.setCenter(ox-w/2, oz-d/2); @@ -43,30 +44,11 @@ Chunks::Chunks( } voxel* Chunks::get(int32_t x, int32_t y, int32_t z) const { - if (y < 0 || y >= CHUNK_H) { - return nullptr; - } - int cx = floordiv(x); - int cz = floordiv(z); - auto ptr = areaMap.getIf(cx, cz); - if (ptr == nullptr) { - return nullptr; - } - Chunk* chunk = ptr->get(); - if (chunk == nullptr) { - return nullptr; - } - int lx = x - cx * CHUNK_W; - int lz = z - cz * CHUNK_D; - return &chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; + return blocks_agent::get(*this, x, y, z); } voxel& Chunks::require(int32_t x, int32_t y, int32_t z) const { - auto voxel = get(x, y, z); - if (voxel == nullptr) { - throw std::runtime_error("voxel does not exist"); - } - return *voxel; + return blocks_agent::require(*this, x, y, z); } const AABB* Chunks::isObstacleAt(float x, float y, float z) const { @@ -103,9 +85,7 @@ const AABB* Chunks::isObstacleAt(float x, float y, float z) const { } bool Chunks::isSolidBlock(int32_t x, int32_t y, int32_t z) { - voxel* v = get(x, y, z); - if (v == nullptr) return false; - return indices->blocks.require(v->id).rt.solid; + return blocks_agent::is_solid_at(*this, x, y, z); } bool Chunks::isReplaceableBlock(int32_t x, int32_t y, int32_t z) { diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index fd33eb9a..a39c8960 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -152,4 +152,8 @@ public: size_t getVolume() const { return areaMap.area(); } + + const ContentIndices& getContentIndices() const { + return *indices; + } }; diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp index 379ca328..38843398 100644 --- a/src/voxels/GlobalChunks.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -16,10 +16,10 @@ #include "Block.hpp" #include "Chunk.hpp" -inline long long keyfrom(int x, int z) { +inline uint64_t keyfrom(int32_t x, int32_t z) { union { - int pos[2]; - long long key; + int32_t pos[2]; + uint64_t key; } ekey; ekey.pos[0] = x; ekey.pos[1] = z; @@ -28,7 +28,9 @@ inline long long keyfrom(int x, int z) { static debug::Logger logger("chunks-storage"); -GlobalChunks::GlobalChunks(Level* level) : level(level) { +GlobalChunks::GlobalChunks(Level* level) + : level(level), indices(level ? level->content->getIndices() : nullptr) { + chunksMap.max_load_factor(CHUNKS_MAP_MAX_LOAD_FACTOR); } std::shared_ptr GlobalChunks::fetch(int x, int z) { @@ -67,6 +69,29 @@ void GlobalChunks::erase(int x, int z) { chunksMap.erase(keyfrom(x, z)); } +static inline auto load_inventories( + WorldRegions& regions, + const Chunk& chunk, + const ContentUnitIndices& defs +) { + auto invs = regions.fetchInventories(chunk.x, chunk.z); + auto iterator = invs.begin(); + while (iterator != invs.end()) { + uint index = iterator->first; + const auto& def = defs.require(chunk.voxels[index].id); + if (def.inventorySize == 0) { + iterator = invs.erase(iterator); + continue; + } + auto& inventory = iterator->second; + if (def.inventorySize != inventory->size()) { + inventory->resize(def.inventorySize); + } + ++iterator; + } + return invs; +} + std::shared_ptr GlobalChunks::create(int x, int z) { const auto& found = chunksMap.find(keyfrom(x, z)); if (found != chunksMap.end()) { @@ -84,22 +109,10 @@ std::shared_ptr GlobalChunks::create(int x, int z) { chunk->decode(data.get()); check_voxels(indices, *chunk); - auto invs = regions.fetchInventories(chunk->x, chunk->z); - auto iterator = invs.begin(); - while (iterator != invs.end()) { - uint index = iterator->first; - const auto& def = indices.blocks.require(chunk->voxels[index].id); - if (def.inventorySize == 0) { - iterator = invs.erase(iterator); - continue; - } - auto& inventory = iterator->second; - if (def.inventorySize != inventory->size()) { - inventory->resize(def.inventorySize); - } - ++iterator; - } - chunk->setBlockInventories(std::move(invs)); + + chunk->setBlockInventories( + load_inventories(regions, *chunk, indices.blocks) + ); auto entitiesData = regions.fetchEntities(chunk->x, chunk->z); if (entitiesData.getType() == dv::value_type::object) { @@ -132,24 +145,6 @@ size_t GlobalChunks::size() const { return chunksMap.size(); } -voxel* GlobalChunks::get(int x, int y, int z) const { - if (y < 0 || y >= CHUNK_H) { - return nullptr; - } - - int cx = floordiv(x); - int cz = floordiv(z); - - const auto& found = chunksMap.find(keyfrom(cx, cz)); - if (found == chunksMap.end()) { - return nullptr; - } - const auto& chunk = found->second; - int lx = x - cx * CHUNK_W; - int lz = z - cz * CHUNK_D; - return &chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; -} - void GlobalChunks::incref(Chunk* chunk) { auto key = reinterpret_cast(chunk); const auto& found = refCounters.find(key); @@ -209,3 +204,15 @@ void GlobalChunks::saveAll() { save(chunk.get()); } } + +void GlobalChunks::putChunk(std::shared_ptr chunk) { + chunksMap[keyfrom(chunk->x, chunk->z)] = std::move(chunk); +} + +Chunk* GlobalChunks::getChunk(int cx, int cz) const { + const auto& found = chunksMap.find(keyfrom(cx, cz)); + if (found == chunksMap.end()) { + return nullptr; + } + return found->second.get(); +} diff --git a/src/voxels/GlobalChunks.hpp b/src/voxels/GlobalChunks.hpp index b21e15e6..f7ce0e92 100644 --- a/src/voxels/GlobalChunks.hpp +++ b/src/voxels/GlobalChunks.hpp @@ -11,10 +11,12 @@ class Chunk; class Level; +class ContentIndices; class GlobalChunks { Level* level; - std::unordered_map> chunksMap; + const ContentIndices* indices; + std::unordered_map> chunksMap; std::unordered_map> pinnedChunks; std::unordered_map refCounters; public: @@ -27,8 +29,6 @@ public: void pinChunk(std::shared_ptr chunk); void unpinChunk(int x, int z); - voxel* get(int x, int y, int z) const; - size_t size() const; void incref(Chunk* chunk); @@ -38,4 +38,12 @@ public: void save(Chunk* chunk); void saveAll(); + + void putChunk(std::shared_ptr chunk); + + Chunk* getChunk(int cx, int cz) const; + + const ContentIndices& getContentIndices() const { + return *indices; + } }; diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp new file mode 100644 index 00000000..31ae79c5 --- /dev/null +++ b/src/voxels/blocks_agent.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include "voxel.hpp" +#include "typedefs.hpp" +#include "maths/voxmaths.hpp" + +class Chunk; +class Chunks; +class GlobalChunks; + +/// Using templates to minimize OOP overhead + +namespace blocks_agent { + +template +inline Chunk* get_chunk(const Storage& chunks, int cx, int cz) { + return chunks.getChunk(cx, cz); +} + +template +inline voxel* get(const Storage& chunks, int32_t x, int32_t y, int32_t z) { + if (y < 0 || y >= CHUNK_H) { + return nullptr; + } + int cx = floordiv(x); + int cz = floordiv(z); + Chunk* chunk = get_chunk(chunks, cx, cz); + if (chunk == nullptr) { + return nullptr; + } + int lx = x - cx * CHUNK_W; + int lz = z - cz * CHUNK_D; + return &chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; +} + +template +inline voxel& require(const Storage& chunks, int32_t x, int32_t y, int32_t z) { + auto vox = get(chunks, x, y, z); + if (vox == nullptr) { + throw std::runtime_error("voxel does not exist"); + } + return *vox; +} + +template +inline const Block& get_block_def(const Storage& chunks, blockid_t id) { + return chunks.getContentIndices().blocks.require(id); +} + +template +inline bool is_solid_at(const Storage& chunks, int32_t x, int32_t y, int32_t z) { + if (auto vox = get(chunks, x, y, z)) { + return get_block_def(chunks, vox->id).rt.solid; + } + return false; +} + +} // blocks_agent From 87c19d69dc07b094669958ec6af6d53696cb28f6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Dec 2024 20:46:06 +0300 Subject: [PATCH 03/31] move block set logic to the blocks_agent --- src/logic/scripting/lua/libs/libblock.cpp | 6 +- src/voxels/Chunks.cpp | 111 ++-------------------- src/voxels/GlobalChunks.cpp | 18 ---- src/voxels/GlobalChunks.hpp | 18 +++- src/voxels/blocks_agent.cpp | 95 ++++++++++++++++++ src/voxels/blocks_agent.hpp | 87 ++++++++++++++++- src/world/generator/WorldGenerator.cpp | 8 +- 7 files changed, 210 insertions(+), 133 deletions(-) create mode 100644 src/voxels/blocks_agent.cpp diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 5d681e5f..e454903c 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -98,7 +98,7 @@ static int l_set(lua::State* L) { if (!level->chunks->get(x, y, z)) { return 0; } - level->chunks->set(x, y, z, id, int2blockstate(state)); + blocks_agent::set(*level->chunksStorage, x, y, z, id, int2blockstate(state)); level->lighting->onBlockSet(x, y, z, id); if (!noupdate) { blocks->updateSides(x, y, z); @@ -269,7 +269,9 @@ static int l_is_replaceable_at(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - return lua::pushboolean(L, level->chunks->isReplaceableBlock(x, y, z)); + return lua::pushboolean( + L, blocks_agent::is_replaceable_at(*level->chunksStorage, x, y, z) + ); } static int l_caption(lua::State* L) { diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 8bfe6166..50812d07 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -89,9 +89,7 @@ bool Chunks::isSolidBlock(int32_t x, int32_t y, int32_t z) { } bool Chunks::isReplaceableBlock(int32_t x, int32_t y, int32_t z) { - voxel* v = get(x, y, z); - if (v == nullptr) return false; - return indices->blocks.require(v->id).replaceable; + return blocks_agent::is_replaceable_at(*this, x, y, z); } bool Chunks::isObstacleBlock(int32_t x, int32_t y, int32_t z) { @@ -184,50 +182,13 @@ glm::ivec3 Chunks::seekOrigin( void Chunks::eraseSegments( const Block& def, blockstate state, int x, int y, int z ) { - const auto& rotation = def.rotations.variants[state.rotation]; - for (int sy = 0; sy < def.size.y; sy++) { - for (int sz = 0; sz < def.size.z; sz++) { - for (int sx = 0; sx < def.size.x; sx++) { - if ((sx | sy | sz) == 0) { - continue; - } - glm::ivec3 pos(x, y, z); - pos += rotation.axisX * sx; - pos += rotation.axisY * sy; - pos += rotation.axisZ * sz; - set(pos.x, pos.y, pos.z, 0, {}); - } - } - } -} - -static constexpr uint8_t segment_to_int(int sx, int sy, int sz) { - return ((sx > 0) | ((sy > 0) << 1) | ((sz > 0) << 2)); + blocks_agent::erase_segments(*this, def, state, x, y, z); } void Chunks::repairSegments( const Block& def, blockstate state, int x, int y, int z ) { - const auto& rotation = def.rotations.variants[state.rotation]; - const auto id = def.rt.id; - const auto size = def.size; - for (int sy = 0; sy < size.y; sy++) { - for (int sz = 0; sz < size.z; sz++) { - for (int sx = 0; sx < size.x; sx++) { - if ((sx | sy | sz) == 0) { - continue; - } - blockstate segState = state; - segState.segment = segment_to_int(sx, sy, sz); - - glm::ivec3 pos(x, y, z); - pos += rotation.axisX * sx; - pos += rotation.axisY * sy; - pos += rotation.axisZ * sz; - set(pos.x, pos.y, pos.z, id, segState); - } - } - } + blocks_agent::repair_segments(*this, def, state, x, y, z); } bool Chunks::checkReplaceability( @@ -283,7 +244,7 @@ void Chunks::setRotationExtended( pos += rotation.axisZ * sz; blockstate segState = newstate; - segState.segment = segment_to_int(sx, sy, sz); + segState.segment = blocks_agent::segment_to_int(sx, sy, sz); auto vox = get(pos); // checked for nullptr by checkReplaceability @@ -344,68 +305,7 @@ void Chunks::setRotation(int32_t x, int32_t y, int32_t z, uint8_t index) { void Chunks::set( int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state ) { - if (y < 0 || y >= CHUNK_H) { - return; - } - int cx = floordiv(x); - int cz = floordiv(z); - auto ptr = areaMap.getIf(cx, cz); - if (ptr == nullptr) { - return; - } - Chunk* chunk = ptr->get(); - if (chunk == nullptr) { - return; - } - int lx = x - cx * CHUNK_W; - int lz = z - cz * CHUNK_D; - size_t index = vox_index(lx, y, lz); - - // block finalization - voxel& vox = chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; - const auto& prevdef = indices->blocks.require(vox.id); - if (prevdef.inventorySize != 0) { - chunk->removeBlockInventory(lx, y, lz); - } - if (prevdef.rt.extended && !vox.state.segment) { - eraseSegments(prevdef, vox.state, x, y, z); - } - if (prevdef.dataStruct) { - if (auto found = chunk->blocksMetadata.find(index)) { - chunk->blocksMetadata.free(found); - chunk->flags.unsaved = true; - chunk->flags.blocksData = true; - } - } - - // block initialization - const auto& newdef = indices->blocks.require(id); - vox.id = id; - vox.state = state; - chunk->setModifiedAndUnsaved(); - if (!state.segment && newdef.rt.extended) { - repairSegments(newdef, state, x, y, z); - } - - if (y < chunk->bottom) - chunk->bottom = y; - else if (y + 1 > chunk->top) - chunk->top = y + 1; - else if (id == 0) - chunk->updateHeights(); - - if (lx == 0 && (chunk = getChunk(cx - 1, cz))) { - chunk->flags.modified = true; - } - if (lz == 0 && (chunk = getChunk(cx, cz - 1))) { - chunk->flags.modified = true; - } - if (lx == CHUNK_W - 1 && (chunk = getChunk(cx + 1, cz))) { - chunk->flags.modified = true; - } - if (lz == CHUNK_D - 1 && (chunk = getChunk(cx, cz + 1))) { - chunk->flags.modified = true; - } + blocks_agent::set(*this, x, y, z, id, state); } voxel* Chunks::rayCast( @@ -652,6 +552,7 @@ void Chunks::resize(uint32_t newW, uint32_t newD) { bool Chunks::putChunk(const std::shared_ptr& chunk) { if (areaMap.set(chunk->x, chunk->z, chunk)) { + if (level) level->events->trigger(LevelEventType::EVT_CHUNK_SHOWN, chunk.get()); return true; } diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp index 38843398..98a2d40a 100644 --- a/src/voxels/GlobalChunks.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -16,16 +16,6 @@ #include "Block.hpp" #include "Chunk.hpp" -inline uint64_t keyfrom(int32_t x, int32_t z) { - union { - int32_t pos[2]; - uint64_t key; - } ekey; - ekey.pos[0] = x; - ekey.pos[1] = z; - return ekey.key; -} - static debug::Logger logger("chunks-storage"); GlobalChunks::GlobalChunks(Level* level) @@ -208,11 +198,3 @@ void GlobalChunks::saveAll() { void GlobalChunks::putChunk(std::shared_ptr chunk) { chunksMap[keyfrom(chunk->x, chunk->z)] = std::move(chunk); } - -Chunk* GlobalChunks::getChunk(int cx, int cz) const { - const auto& found = chunksMap.find(keyfrom(cx, cz)); - if (found == chunksMap.end()) { - return nullptr; - } - return found->second.get(); -} diff --git a/src/voxels/GlobalChunks.hpp b/src/voxels/GlobalChunks.hpp index f7ce0e92..afbdf67c 100644 --- a/src/voxels/GlobalChunks.hpp +++ b/src/voxels/GlobalChunks.hpp @@ -14,6 +14,16 @@ class Level; class ContentIndices; class GlobalChunks { + static inline uint64_t keyfrom(int32_t x, int32_t z) { + union { + int32_t pos[2]; + uint64_t key; + } ekey; + ekey.pos[0] = x; + ekey.pos[1] = z; + return ekey.key; + } + Level* level; const ContentIndices* indices; std::unordered_map> chunksMap; @@ -41,7 +51,13 @@ public: void putChunk(std::shared_ptr chunk); - Chunk* getChunk(int cx, int cz) const; + inline Chunk* getChunk(int cx, int cz) const { + const auto& found = chunksMap.find(keyfrom(cx, cz)); + if (found == chunksMap.end()) { + return nullptr; + } + return found->second.get(); + } const ContentIndices& getContentIndices() const { return *indices; diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp new file mode 100644 index 00000000..53cef38d --- /dev/null +++ b/src/voxels/blocks_agent.cpp @@ -0,0 +1,95 @@ +#include "blocks_agent.hpp" + +using namespace blocks_agent; + +template +static inline void set_block( + Storage& chunks, + int32_t x, + int32_t y, + int32_t z, + uint32_t id, + blockstate state +) { + if (y < 0 || y >= CHUNK_H) { + return; + } + const auto& indices = chunks.getContentIndices(); + int cx = floordiv(x); + int cz = floordiv(z); + Chunk* chunk = get_chunk(chunks, cx, cz); + if (chunk == nullptr) { + return; + } + int lx = x - cx * CHUNK_W; + int lz = z - cz * CHUNK_D; + size_t index = vox_index(lx, y, lz); + + // block finalization + voxel& vox = chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; + const auto& prevdef = indices.blocks.require(vox.id); + if (prevdef.inventorySize != 0) { + chunk->removeBlockInventory(lx, y, lz); + } + if (prevdef.rt.extended && !vox.state.segment) { + erase_segments(chunks, prevdef, vox.state, x, y, z); + } + if (prevdef.dataStruct) { + if (auto found = chunk->blocksMetadata.find(index)) { + chunk->blocksMetadata.free(found); + chunk->flags.unsaved = true; + chunk->flags.blocksData = true; + } + } + + // block initialization + const auto& newdef = indices.blocks.require(id); + vox.id = id; + vox.state = state; + chunk->setModifiedAndUnsaved(); + if (!state.segment && newdef.rt.extended) { + repair_segments(chunks, newdef, state, x, y, z); + } + + if (y < chunk->bottom) + chunk->bottom = y; + else if (y + 1 > chunk->top) + chunk->top = y + 1; + else if (id == 0) + chunk->updateHeights(); + + if (lx == 0 && (chunk = get_chunk(chunks, cx - 1, cz))) { + chunk->flags.modified = true; + } + if (lz == 0 && (chunk = get_chunk(chunks, cx, cz - 1))) { + chunk->flags.modified = true; + } + if (lx == CHUNK_W - 1 && (chunk = get_chunk(chunks, cx + 1, cz))) { + chunk->flags.modified = true; + } + if (lz == CHUNK_D - 1 && (chunk = get_chunk(chunks, cx, cz + 1))) { + chunk->flags.modified = true; + } +} + +void blocks_agent::set( + Chunks& chunks, + int32_t x, + int32_t y, + int32_t z, + uint32_t id, + blockstate state +) { + set_block(chunks, x, y, z, id, state); +} + +void blocks_agent::set( + GlobalChunks& chunks, + int32_t x, + int32_t y, + int32_t z, + uint32_t id, + blockstate state +) { + set_block(chunks, x, y, z, id, state); +} diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 31ae79c5..22850f40 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -1,12 +1,16 @@ #pragma once #include "voxel.hpp" +#include "Block.hpp" +#include "Chunk.hpp" +#include "Chunks.hpp" +#include "GlobalChunks.hpp" +#include "constants.hpp" #include "typedefs.hpp" +#include "content/Content.hpp" #include "maths/voxmaths.hpp" -class Chunk; -class Chunks; -class GlobalChunks; +#include /// Using templates to minimize OOP overhead @@ -55,4 +59,81 @@ inline bool is_solid_at(const Storage& chunks, int32_t x, int32_t y, int32_t z) return false; } +template +inline bool is_replaceable_at(const Storage& chunks, int32_t x, int32_t y, int32_t z) { + if (auto vox = get(chunks, x, y, z)) { + return get_block_def(chunks, vox->id).replaceable; + } + return false; +} + +void set( + Chunks& chunks, + int32_t x, + int32_t y, + int32_t z, + uint32_t id, + blockstate state +); + +void set( + GlobalChunks& chunks, + int32_t x, + int32_t y, + int32_t z, + uint32_t id, + blockstate state +); + +template +inline void erase_segments( + Storage& chunks, const Block& def, blockstate state, int x, int y, int z +) { + const auto& rotation = def.rotations.variants[state.rotation]; + for (int sy = 0; sy < def.size.y; sy++) { + for (int sz = 0; sz < def.size.z; sz++) { + for (int sx = 0; sx < def.size.x; sx++) { + if ((sx | sy | sz) == 0) { + continue; + } + glm::ivec3 pos(x, y, z); + pos += rotation.axisX * sx; + pos += rotation.axisY * sy; + pos += rotation.axisZ * sz; + set(chunks, pos.x, pos.y, pos.z, 0, {}); + } + } + } +} + +static constexpr uint8_t segment_to_int(int sx, int sy, int sz) { + return ((sx > 0) | ((sy > 0) << 1) | ((sz > 0) << 2)); +} + +template +inline void repair_segments( + Storage& chunks, const Block& def, blockstate state, int x, int y, int z +) { + const auto& rotation = def.rotations.variants[state.rotation]; + const auto id = def.rt.id; + const auto size = def.size; + for (int sy = 0; sy < size.y; sy++) { + for (int sz = 0; sz < size.z; sz++) { + for (int sx = 0; sx < size.x; sx++) { + if ((sx | sy | sz) == 0) { + continue; + } + blockstate segState = state; + segState.segment = segment_to_int(sx, sy, sz); + + glm::ivec3 pos(x, y, z); + pos += rotation.axisX * sx; + pos += rotation.axisY * sy; + pos += rotation.axisZ * sz; + set(chunks, pos.x, pos.y, pos.z, id, segState); + } + } + } +} + } // blocks_agent diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index bc851a07..281ac411 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -189,10 +189,10 @@ void WorldGenerator::placeLine(const LinePlacement& line, int priority) { aabb.fix(); aabb.a -= line.radius; aabb.b += line.radius; - int cxa = floordiv(aabb.a.x, CHUNK_W); - int cza = floordiv(aabb.a.z, CHUNK_D); - int cxb = floordiv(aabb.b.x, CHUNK_W); - int czb = floordiv(aabb.b.z, CHUNK_D); + int cxa = floordiv(aabb.a.x); + int cza = floordiv(aabb.a.z); + int cxb = floordiv(aabb.b.x); + int czb = floordiv(aabb.b.z); for (int cz = cza; cz <= czb; cz++) { for (int cx = cxa; cx <= cxb; cx++) { const auto& found = prototypes.find({cx, cz}); From c7c2fe29d2bb2f1fe89b9815cf013e239c99dbf0 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Dec 2024 22:37:55 +0300 Subject: [PATCH 04/31] move seekOrigin logic to the blocks_agent --- src/logic/scripting/lua/libs/libblock.cpp | 26 ++++++++++++++++------- src/voxels/Chunks.cpp | 18 +--------------- src/voxels/blocks_agent.hpp | 24 +++++++++++++++++++++ 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index e454903c..743c042b 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -70,7 +70,7 @@ static int l_is_segment(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - const auto& vox = level->chunks->require(x, y, z); + const auto& vox = blocks_agent::require(*level->chunksStorage, x, y, z); return lua::pushboolean(L, vox.state.segment); } @@ -78,10 +78,13 @@ static int l_seek_origin(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - const auto& vox = level->chunks->require(x, y, z); + const auto& vox = blocks_agent::require(*level->chunksStorage, x, y, z); auto& def = indices->blocks.require(vox.id); return lua::pushivec_stack( - L, level->chunks->seekOrigin({x, y, z}, def, vox.state) + L, + blocks_agent::seek_origin( + *level->chunksStorage, {x, y, z}, def, vox.state + ) ); } @@ -198,13 +201,18 @@ static int l_set_states(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); auto states = lua::tointeger(L, 4); - - auto chunk = level->chunks->getChunkByVoxel(x, y, z); + if (y < 0 || y >= CHUNK_H) { + return 0; + } + int cx = floordiv(x); + int cz = floordiv(z); + auto chunk = blocks_agent::get_chunk(*level->chunksStorage, cx, cz); if (chunk == nullptr) { return 0; } - auto vox = level->chunks->get(x, y, z); - vox->state = int2blockstate(states); + int lx = x - cx * CHUNK_W; + int lz = z - cz * CHUNK_D; + chunk->voxels[vox_index(lx, y, lz)].state = int2blockstate(states); chunk->setModifiedAndUnsaved(); return 0; } @@ -223,7 +231,9 @@ static int l_get_user_bits(lua::State* L) { } const auto& def = content->getIndices()->blocks.require(vox->id); if (def.rt.extended) { - auto origin = level->chunks->seekOrigin({x, y, z}, def, vox->state); + auto origin = blocks_agent::seek_origin( + *level->chunksStorage, {x, y, z}, def, vox->state + ); vox = level->chunks->get(origin.x, origin.y, origin.z); if (vox == nullptr) { return lua::pushinteger(L, 0); diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 50812d07..a56c3581 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -160,23 +160,7 @@ Chunk* Chunks::getChunk(int x, int z) const { glm::ivec3 Chunks::seekOrigin( const glm::ivec3& srcpos, const Block& def, blockstate state ) const { - auto pos = srcpos; - const auto& rotation = def.rotations.variants[state.rotation]; - auto segment = state.segment; - while (true) { - if (!segment) { - return pos; - } - if (segment & 1) pos -= rotation.axisX; - if (segment & 2) pos -= rotation.axisY; - if (segment & 4) pos -= rotation.axisZ; - - if (auto* voxel = get(pos.x, pos.y, pos.z)) { - segment = voxel->state.segment; - } else { - return pos; - } - } + return blocks_agent::seek_origin(*this, srcpos, def, state); } void Chunks::eraseSegments( diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 22850f40..3ce5b49b 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -11,6 +11,7 @@ #include "maths/voxmaths.hpp" #include +#include /// Using templates to minimize OOP overhead @@ -136,4 +137,27 @@ inline void repair_segments( } } +template +inline glm::ivec3 seek_origin( + Storage& chunks, const glm::ivec3& srcpos, const Block& def, blockstate state +) { + auto pos = srcpos; + const auto& rotation = def.rotations.variants[state.rotation]; + auto segment = state.segment; + while (true) { + if (!segment) { + return pos; + } + if (segment & 1) pos -= rotation.axisX; + if (segment & 2) pos -= rotation.axisY; + if (segment & 4) pos -= rotation.axisZ; + + if (auto* voxel = get(chunks, pos.x, pos.y, pos.z)) { + segment = voxel->state.segment; + } else { + return pos; + } + } +} + } // blocks_agent From fb3f201dfc2b9814c65d1fc6c02d5330d604f059 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Dec 2024 23:32:52 +0300 Subject: [PATCH 05/31] move setRotation, checkReplaceability, raycast logic to the blocks_agent --- src/logic/scripting/lua/libs/libblock.cpp | 39 ++-- src/voxels/Chunks.cpp | 229 +--------------------- src/voxels/blocks_agent.cpp | 172 ++++++++++++++++ src/voxels/blocks_agent.hpp | 155 +++++++++++++++ 4 files changed, 359 insertions(+), 236 deletions(-) diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 743c042b..c0bf1ef6 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -98,7 +98,9 @@ static int l_set(lua::State* L) { if (static_cast(id) >= indices->blocks.count()) { return 0; } - if (!level->chunks->get(x, y, z)) { + int cx = floordiv(x); + int cz = floordiv(z); + if (!blocks_agent::get_chunk(*level->chunksStorage, cx, cz)) { return 0; } blocks_agent::set(*level->chunksStorage, x, y, z, id, int2blockstate(state)); @@ -183,7 +185,7 @@ static int l_set_rotation(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); auto value = lua::tointeger(L, 4); - level->chunks->setRotation(x, y, z, value); + blocks_agent::set_rotation(*level->chunksStorage, x, y, z, value); return 0; } @@ -234,7 +236,9 @@ static int l_get_user_bits(lua::State* L) { auto origin = blocks_agent::seek_origin( *level->chunksStorage, {x, y, z}, def, vox->state ); - vox = level->chunks->get(origin.x, origin.y, origin.z); + vox = blocks_agent::get( + *level->chunksStorage, origin.x, origin.y, origin.z + ); if (vox == nullptr) { return lua::pushinteger(L, 0); } @@ -245,6 +249,7 @@ static int l_get_user_bits(lua::State* L) { } static int l_set_user_bits(lua::State* L) { + auto& chunks = *level->chunksStorage; auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); @@ -254,18 +259,19 @@ static int l_set_user_bits(lua::State* L) { size_t mask = ((1 << bits) - 1) << offset; auto value = (lua::tointeger(L, 6) << offset) & mask; - auto chunk = level->chunks->getChunkByVoxel(x, y, z); - if (chunk == nullptr) { - return 0; - } - auto vox = level->chunks->get(x, y, z); - if (vox == nullptr) { + int cx = floordiv(x); + int cz = floordiv(z); + auto chunk = blocks_agent::get_chunk(chunks, cx, cz); + if (chunk == nullptr || y < 0 || y >= CHUNK_H) { return 0; } + int lx = x - cx * CHUNK_W; + int lz = z - cz * CHUNK_D; + auto vox = &chunk->voxels[vox_index(lx, y, lz)]; const auto& def = content->getIndices()->blocks.require(vox->id); if (def.rt.extended) { - auto origin = level->chunks->seekOrigin({x, y, z}, def, vox->state); - vox = level->chunks->get(origin); + auto origin = blocks_agent::seek_origin(chunks, {x, y, z}, def, vox->state); + vox = blocks_agent::get(chunks, origin.x, origin.y, origin.z); if (vox == nullptr) { return 0; } @@ -410,8 +416,15 @@ static int l_raycast(lua::State* L) { glm::vec3 end; glm::ivec3 normal; glm::ivec3 iend; - if (auto voxel = level->chunks->rayCast( - start, dir, maxDistance, end, normal, iend, filteredBlocks + if (auto voxel = blocks_agent::raycast( + *level->chunksStorage, + start, + dir, + maxDistance, + end, + normal, + iend, + filteredBlocks )) { if (lua::gettop(L) >= 4 && !lua::isnil(L, 4)) { lua::pushvalue(L, 4); diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index a56c3581..8d5231a6 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -181,109 +181,17 @@ bool Chunks::checkReplaceability( const glm::ivec3& origin, blockid_t ignore ) { - const auto& rotation = def.rotations.variants[state.rotation]; - const auto size = def.size; - for (int sy = 0; sy < size.y; sy++) { - for (int sz = 0; sz < size.z; sz++) { - for (int sx = 0; sx < size.x; sx++) { - auto pos = origin; - pos += rotation.axisX * sx; - pos += rotation.axisY * sy; - pos += rotation.axisZ * sz; - if (auto vox = get(pos.x, pos.y, pos.z)) { - auto& target = indices->blocks.require(vox->id); - if (!target.replaceable && vox->id != ignore) { - return false; - } - } else { - return false; - } - } - } - } - return true; + return blocks_agent::check_replaceability(*this, def, state, origin, ignore); } void Chunks::setRotationExtended( const Block& def, blockstate state, const glm::ivec3& origin, uint8_t index ) { - auto newstate = state; - newstate.rotation = index; - - // unable to rotate block (cause: obstacles) - if (!checkReplaceability(def, newstate, origin, def.rt.id)) { - return; - } - - const auto& rotation = def.rotations.variants[index]; - const auto size = def.size; - std::vector segmentBlocks; - - for (int sy = 0; sy < size.y; sy++) { - for (int sz = 0; sz < size.z; sz++) { - for (int sx = 0; sx < size.x; sx++) { - auto pos = origin; - pos += rotation.axisX * sx; - pos += rotation.axisY * sy; - pos += rotation.axisZ * sz; - - blockstate segState = newstate; - segState.segment = blocks_agent::segment_to_int(sx, sy, sz); - - auto vox = get(pos); - // checked for nullptr by checkReplaceability - if (vox->id != def.rt.id) { - set(pos.x, pos.y, pos.z, def.rt.id, segState); - } else { - vox->state = segState; - auto chunk = getChunkByVoxel(pos.x, pos.y, pos.z); - assert(chunk != nullptr); - chunk->setModifiedAndUnsaved(); - segmentBlocks.emplace_back(pos); - } - } - } - } - const auto& prevRotation = def.rotations.variants[state.rotation]; - for (int sy = 0; sy < size.y; sy++) { - for (int sz = 0; sz < size.z; sz++) { - for (int sx = 0; sx < size.x; sx++) { - auto pos = origin; - pos += prevRotation.axisX * sx; - pos += prevRotation.axisY * sy; - pos += prevRotation.axisZ * sz; - if (std::find( - segmentBlocks.begin(), segmentBlocks.end(), pos - ) == segmentBlocks.end()) { - set(pos.x, pos.y, pos.z, 0, {}); - } - } - } - } + blocks_agent::set_rotation_extended(*this, def, state, origin, index); } void Chunks::setRotation(int32_t x, int32_t y, int32_t z, uint8_t index) { - if (index >= BlockRotProfile::MAX_COUNT) { - return; - } - auto vox = get(x, y, z); - if (vox == nullptr) { - return; - } - auto& def = indices->blocks.require(vox->id); - if (!def.rotatable || vox->state.rotation == index) { - return; - } - if (def.rt.extended) { - auto origin = seekOrigin({x, y, z}, def, vox->state); - vox = get(origin); - setRotationExtended(def, vox->state, origin, index); - } else { - vox->state.rotation = index; - auto chunk = getChunkByVoxel(x, y, z); - assert(chunk != nullptr); - chunk->setModifiedAndUnsaved(); - } + return blocks_agent::set_rotation(*this, x, y, z, index); } void Chunks::set( @@ -301,134 +209,9 @@ voxel* Chunks::rayCast( glm::ivec3& iend, std::set filter ) const { - float px = start.x; - float py = start.y; - float pz = start.z; - - float dx = dir.x; - float dy = dir.y; - float dz = dir.z; - - float t = 0.0f; - int ix = std::floor(px); - int iy = std::floor(py); - int iz = std::floor(pz); - - int stepx = (dx > 0.0f) ? 1 : -1; - int stepy = (dy > 0.0f) ? 1 : -1; - int stepz = (dz > 0.0f) ? 1 : -1; - - constexpr float infinity = std::numeric_limits::infinity(); - constexpr float epsilon = 1e-6f; // 0.000001 - float txDelta = (std::fabs(dx) < epsilon) ? infinity : std::fabs(1.0f / dx); - float tyDelta = (std::fabs(dy) < epsilon) ? infinity : std::fabs(1.0f / dy); - float tzDelta = (std::fabs(dz) < epsilon) ? infinity : std::fabs(1.0f / dz); - - float xdist = (stepx > 0) ? (ix + 1 - px) : (px - ix); - float ydist = (stepy > 0) ? (iy + 1 - py) : (py - iy); - float zdist = (stepz > 0) ? (iz + 1 - pz) : (pz - iz); - - float txMax = (txDelta < infinity) ? txDelta * xdist : infinity; - float tyMax = (tyDelta < infinity) ? tyDelta * ydist : infinity; - float tzMax = (tzDelta < infinity) ? tzDelta * zdist : infinity; - - int steppedIndex = -1; - - while (t <= maxDist) { - voxel* voxel = get(ix, iy, iz); - if (voxel == nullptr) { - return nullptr; - } - - const auto& def = indices->blocks.require(voxel->id); - if ((filter.empty() && def.selectable) || - (!filter.empty() && filter.find(def.rt.id) == filter.end())) { - end.x = px + t * dx; - end.y = py + t * dy; - end.z = pz + t * dz; - iend.x = ix; - iend.y = iy; - iend.z = iz; - - if (!def.rt.solid) { - const std::vector& hitboxes = - def.rotatable ? def.rt.hitboxes[voxel->state.rotation] - : def.hitboxes; - - scalar_t distance = maxDist; - Ray ray(start, dir); - - bool hit = false; - - glm::vec3 offset {}; - if (voxel->state.segment) { - offset = seekOrigin(iend, def, voxel->state) - iend; - } - - for (auto box : hitboxes) { - box.a += offset; - box.b += offset; - scalar_t boxDistance; - glm::ivec3 boxNorm; - if (ray.intersectAABB( - iend, box, maxDist, boxNorm, boxDistance - ) > RayRelation::None && - boxDistance < distance) { - hit = true; - distance = boxDistance; - norm = boxNorm; - end = start + (dir * glm::vec3(distance)); - } - } - - if (hit) return voxel; - } else { - iend.x = ix; - iend.y = iy; - iend.z = iz; - - norm.x = norm.y = norm.z = 0; - if (steppedIndex == 0) norm.x = -stepx; - if (steppedIndex == 1) norm.y = -stepy; - if (steppedIndex == 2) norm.z = -stepz; - return voxel; - } - } - if (txMax < tyMax) { - if (txMax < tzMax) { - ix += stepx; - t = txMax; - txMax += txDelta; - steppedIndex = 0; - } else { - iz += stepz; - t = tzMax; - tzMax += tzDelta; - steppedIndex = 2; - } - } else { - if (tyMax < tzMax) { - iy += stepy; - t = tyMax; - tyMax += tyDelta; - steppedIndex = 1; - } else { - iz += stepz; - t = tzMax; - tzMax += tzDelta; - steppedIndex = 2; - } - } - } - iend.x = ix; - iend.y = iy; - iend.z = iz; - - end.x = px + t * dx; - end.y = py + t * dy; - end.z = pz + t * dz; - norm.x = norm.y = norm.z = 0; - return nullptr; + return blocks_agent::raycast( + *this, start, dir, maxDist, end, norm, iend, std::move(filter) + ); } glm::vec3 Chunks::rayCastToObstacle( diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp index 53cef38d..ab39ea29 100644 --- a/src/voxels/blocks_agent.cpp +++ b/src/voxels/blocks_agent.cpp @@ -1,5 +1,9 @@ #include "blocks_agent.hpp" +#include "maths/rays.hpp" + +#include + using namespace blocks_agent; template @@ -93,3 +97,171 @@ void blocks_agent::set( ) { set_block(chunks, x, y, z, id, state); } + +template +static inline voxel* raycast_blocks( + const Storage& chunks, + const glm::vec3& start, + const glm::vec3& dir, + float maxDist, + glm::vec3& end, + glm::ivec3& norm, + glm::ivec3& iend, + std::set filter +) { + const auto& blocks = chunks.getContentIndices().blocks; + float px = start.x; + float py = start.y; + float pz = start.z; + + float dx = dir.x; + float dy = dir.y; + float dz = dir.z; + + float t = 0.0f; + int ix = std::floor(px); + int iy = std::floor(py); + int iz = std::floor(pz); + + int stepx = (dx > 0.0f) ? 1 : -1; + int stepy = (dy > 0.0f) ? 1 : -1; + int stepz = (dz > 0.0f) ? 1 : -1; + + constexpr float infinity = std::numeric_limits::infinity(); + constexpr float epsilon = 1e-6f; // 0.000001 + float txDelta = (std::fabs(dx) < epsilon) ? infinity : std::fabs(1.0f / dx); + float tyDelta = (std::fabs(dy) < epsilon) ? infinity : std::fabs(1.0f / dy); + float tzDelta = (std::fabs(dz) < epsilon) ? infinity : std::fabs(1.0f / dz); + + float xdist = (stepx > 0) ? (ix + 1 - px) : (px - ix); + float ydist = (stepy > 0) ? (iy + 1 - py) : (py - iy); + float zdist = (stepz > 0) ? (iz + 1 - pz) : (pz - iz); + + float txMax = (txDelta < infinity) ? txDelta * xdist : infinity; + float tyMax = (tyDelta < infinity) ? tyDelta * ydist : infinity; + float tzMax = (tzDelta < infinity) ? tzDelta * zdist : infinity; + + int steppedIndex = -1; + + while (t <= maxDist) { + voxel* voxel = get(chunks, ix, iy, iz); + if (voxel == nullptr) { + return nullptr; + } + + const auto& def = blocks.require(voxel->id); + if ((filter.empty() && def.selectable) || + (!filter.empty() && filter.find(def.rt.id) == filter.end())) { + end.x = px + t * dx; + end.y = py + t * dy; + end.z = pz + t * dz; + iend.x = ix; + iend.y = iy; + iend.z = iz; + + if (!def.rt.solid) { + const std::vector& hitboxes = + def.rotatable ? def.rt.hitboxes[voxel->state.rotation] + : def.hitboxes; + + scalar_t distance = maxDist; + Ray ray(start, dir); + + bool hit = false; + + glm::vec3 offset {}; + if (voxel->state.segment) { + offset = seek_origin(chunks, iend, def, voxel->state) - iend; + } + + for (auto box : hitboxes) { + box.a += offset; + box.b += offset; + scalar_t boxDistance; + glm::ivec3 boxNorm; + if (ray.intersectAABB( + iend, box, maxDist, boxNorm, boxDistance + ) > RayRelation::None && + boxDistance < distance) { + hit = true; + distance = boxDistance; + norm = boxNorm; + end = start + (dir * glm::vec3(distance)); + } + } + + if (hit) return voxel; + } else { + iend.x = ix; + iend.y = iy; + iend.z = iz; + + norm.x = norm.y = norm.z = 0; + if (steppedIndex == 0) norm.x = -stepx; + if (steppedIndex == 1) norm.y = -stepy; + if (steppedIndex == 2) norm.z = -stepz; + return voxel; + } + } + if (txMax < tyMax) { + if (txMax < tzMax) { + ix += stepx; + t = txMax; + txMax += txDelta; + steppedIndex = 0; + } else { + iz += stepz; + t = tzMax; + tzMax += tzDelta; + steppedIndex = 2; + } + } else { + if (tyMax < tzMax) { + iy += stepy; + t = tyMax; + tyMax += tyDelta; + steppedIndex = 1; + } else { + iz += stepz; + t = tzMax; + tzMax += tzDelta; + steppedIndex = 2; + } + } + } + iend.x = ix; + iend.y = iy; + iend.z = iz; + + end.x = px + t * dx; + end.y = py + t * dy; + end.z = pz + t * dz; + norm.x = norm.y = norm.z = 0; + return nullptr; +} + +voxel* blocks_agent::raycast( + const Chunks& chunks, + const glm::vec3& start, + const glm::vec3& dir, + float maxDist, + glm::vec3& end, + glm::ivec3& norm, + glm::ivec3& iend, + std::set filter +) { + return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter); +} + +voxel* blocks_agent::raycast( + const GlobalChunks& chunks, + const glm::vec3& start, + const glm::vec3& dir, + float maxDist, + glm::vec3& end, + glm::ivec3& norm, + glm::ivec3& iend, + std::set filter +) { + return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter); +} diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 3ce5b49b..3bad256f 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -10,6 +10,7 @@ #include "content/Content.hpp" #include "maths/voxmaths.hpp" +#include #include #include @@ -160,4 +161,158 @@ inline glm::ivec3 seek_origin( } } +template +inline bool check_replaceability( + const Storage& chunks, + const Block& def, + blockstate state, + const glm::ivec3& origin, + blockid_t ignore +) { + const auto& blocks = chunks.getContentIndices().blocks; + const auto& rotation = def.rotations.variants[state.rotation]; + const auto size = def.size; + for (int sy = 0; sy < size.y; sy++) { + for (int sz = 0; sz < size.z; sz++) { + for (int sx = 0; sx < size.x; sx++) { + auto pos = origin; + pos += rotation.axisX * sx; + pos += rotation.axisY * sy; + pos += rotation.axisZ * sz; + if (auto vox = get(chunks, pos.x, pos.y, pos.z)) { + auto& target = blocks.require(vox->id); + if (!target.replaceable && vox->id != ignore) { + return false; + } + } else { + return false; + } + } + } + } + return true; +} + +/// @brief Set rotation to an extended block +/// @tparam Storage chunks storage +/// @param def block definition +/// @param state current block state +/// @param origin extended block origin +/// @param index target rotation index +template +inline void set_rotation_extended( + Storage& chunks, + const Block& def, + blockstate state, + const glm::ivec3& origin, + uint8_t index +) { + auto newstate = state; + newstate.rotation = index; + + // unable to rotate block (cause: obstacles) + if (!check_replaceability(chunks, def, newstate, origin, def.rt.id)) { + return; + } + + const auto& rotation = def.rotations.variants[index]; + const auto size = def.size; + std::vector segmentBlocks; + + for (int sy = 0; sy < size.y; sy++) { + for (int sz = 0; sz < size.z; sz++) { + for (int sx = 0; sx < size.x; sx++) { + auto pos = origin; + pos += rotation.axisX * sx; + pos += rotation.axisY * sy; + pos += rotation.axisZ * sz; + + blockstate segState = newstate; + segState.segment = segment_to_int(sx, sy, sz); + + auto vox = get(chunks, pos.x, pos.y, pos.z); + // checked for nullptr by checkReplaceability + if (vox->id != def.rt.id) { + set(chunks, pos.x, pos.y, pos.z, def.rt.id, segState); + } else { + vox->state = segState; + int cx = floordiv(pos.x); + int cz = floordiv(pos.z); + auto chunk = get_chunk(chunks, cx, cz); + assert(chunk != nullptr); + chunk->setModifiedAndUnsaved(); + segmentBlocks.emplace_back(pos); + } + } + } + } + const auto& prevRotation = def.rotations.variants[state.rotation]; + for (int sy = 0; sy < size.y; sy++) { + for (int sz = 0; sz < size.z; sz++) { + for (int sx = 0; sx < size.x; sx++) { + auto pos = origin; + pos += prevRotation.axisX * sx; + pos += prevRotation.axisY * sy; + pos += prevRotation.axisZ * sz; + if (std::find( + segmentBlocks.begin(), segmentBlocks.end(), pos + ) == segmentBlocks.end()) { + set(chunks, pos.x, pos.y, pos.z, 0, {}); + } + } + } + } +} + +template +inline void set_rotation( + Storage& chunks, int32_t x, int32_t y, int32_t z, uint8_t index +) { + if (index >= BlockRotProfile::MAX_COUNT) { + return; + } + auto vox = get(chunks, x, y, z); + if (vox == nullptr) { + return; + } + const auto& def = chunks.getContentIndices().blocks.require(vox->id); + if (!def.rotatable || vox->state.rotation == index) { + return; + } + if (def.rt.extended) { + auto origin = seek_origin(chunks, {x, y, z}, def, vox->state); + vox = get(chunks, origin.x, origin.y, origin.z); + set_rotation_extended(chunks, def, vox->state, origin, index); + } else { + vox->state.rotation = index; + int cx = floordiv(x); + int cz = floordiv(z); + auto chunk = get_chunk(chunks, cx, cz); + assert(chunk != nullptr); + chunk->setModifiedAndUnsaved(); + } +} + +voxel* raycast( + const Chunks& chunks, + const glm::vec3& start, + const glm::vec3& dir, + float maxDist, + glm::vec3& end, + glm::ivec3& norm, + glm::ivec3& iend, + std::set filter +); + +voxel* raycast( + const GlobalChunks& chunks, + const glm::vec3& start, + const glm::vec3& dir, + float maxDist, + glm::vec3& end, + glm::ivec3& norm, + glm::ivec3& iend, + std::set filter +); + } // blocks_agent From 5030a65bc168d171b3ff2765005dc1e41cd39224 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Dec 2024 23:43:01 +0300 Subject: [PATCH 06/31] add docs to some blocks_agent functions --- src/voxels/blocks_agent.hpp | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 3bad256f..e6e0262c 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -138,6 +138,14 @@ inline void repair_segments( } } +/// @brief Get origin position for specified extended block. Returns srcpos +/// if block is not extended. +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param srcpos block segment position +/// @param def definition of the block at srcpos +/// @param state state of the block at srcpos +/// @return origin block position template inline glm::ivec3 seek_origin( Storage& chunks, const glm::ivec3& srcpos, const Block& def, blockstate state @@ -161,6 +169,14 @@ inline glm::ivec3 seek_origin( } } +/// @brief Check blocks replaceability with specified block +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param def block definition +/// @param state target block state +/// @param origin target block origin +/// @param ignore ignored block id +/// @return true if specified area may be replaced with the block/extended block template inline bool check_replaceability( const Storage& chunks, @@ -194,7 +210,8 @@ inline bool check_replaceability( } /// @brief Set rotation to an extended block -/// @tparam Storage chunks storage +/// @tparam Storage chunks storage class +/// @param chunks chunks storage /// @param def block definition /// @param state current block state /// @param origin extended block origin @@ -264,6 +281,13 @@ inline void set_rotation_extended( } } +/// @brief Set block rotation +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param x block X position +/// @param y block Y position +/// @param z block Z position +/// @param index target rotation index template inline void set_rotation( Storage& chunks, int32_t x, int32_t y, int32_t z, uint8_t index From 6ed0bc3e7e401ca0318e383c8bec1f9a05eb88e6 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 16 Dec 2024 23:56:52 +0300 Subject: [PATCH 07/31] add docs to some blocks_agent functions --- src/voxels/blocks_agent.hpp | 65 +++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index e6e0262c..10655cdc 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -1,5 +1,7 @@ #pragma once +/// blocks_agent is set of templates but not a class to minimize OOP overhead. + #include "voxel.hpp" #include "Block.hpp" #include "Chunk.hpp" @@ -11,18 +13,31 @@ #include "maths/voxmaths.hpp" #include +#include #include #include -/// Using templates to minimize OOP overhead - namespace blocks_agent { +/// @brief Get specified chunk. +/// @tparam Storage +/// @param chunks +/// @param cx chunk grid position X +/// @param cz chunk grid position Z +/// @return chunk or nullptr if does not exists template inline Chunk* get_chunk(const Storage& chunks, int cx, int cz) { return chunks.getChunk(cx, cz); } +/// @brief Get voxel at specified position. +/// Returns nullptr if voxel does not exists. +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param x position X +/// @param y position Y +/// @param z position Z +/// @return voxel pointer or nullptr template inline voxel* get(const Storage& chunks, int32_t x, int32_t y, int32_t z) { if (y < 0 || y >= CHUNK_H) { @@ -39,6 +54,14 @@ inline voxel* get(const Storage& chunks, int32_t x, int32_t y, int32_t z) { return &chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx]; } +/// @brief Get voxel at specified position. +/// @throws std::runtime_error if voxel does not exists +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param x position X +/// @param y position Y +/// @param z position Z +/// @return voxel reference template inline voxel& require(const Storage& chunks, int32_t x, int32_t y, int32_t z) { auto vox = get(chunks, x, y, z); @@ -53,6 +76,14 @@ inline const Block& get_block_def(const Storage& chunks, blockid_t id) { return chunks.getContentIndices().blocks.require(id); } + +/// @brief Check if block at specified position is solid. +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param x position X +/// @param y position Y +/// @param z position Z +/// @return true if block exists and solid template inline bool is_solid_at(const Storage& chunks, int32_t x, int32_t y, int32_t z) { if (auto vox = get(chunks, x, y, z)) { @@ -61,6 +92,13 @@ inline bool is_solid_at(const Storage& chunks, int32_t x, int32_t y, int32_t z) return false; } +/// @brief Check if block at specified position is replaceable. +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param x position X +/// @param y position Y +/// @param z position Z +/// @return true if block exists and replaceable template inline bool is_replaceable_at(const Storage& chunks, int32_t x, int32_t y, int32_t z) { if (auto vox = get(chunks, x, y, z)) { @@ -87,6 +125,14 @@ void set( blockstate state ); +/// @brief Erase extended block segments +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param def origin block definition +/// @param state origin block state +/// @param x origin position X +/// @param y origin position Y +/// @param z origin position Z template inline void erase_segments( Storage& chunks, const Block& def, blockstate state, int x, int y, int z @@ -108,10 +154,23 @@ inline void erase_segments( } } -static constexpr uint8_t segment_to_int(int sx, int sy, int sz) { +/// @brief Convert segment offset to segment bits +/// @param sx segment offset X +/// @param sy segment offset Y +/// @param sz segment offset Z +/// @return segment bits +static constexpr inline uint8_t segment_to_int(int sx, int sy, int sz) { return ((sx > 0) | ((sy > 0) << 1) | ((sz > 0) << 2)); } +/// @brief Restore invalid extended block segments +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param def origin block definition +/// @param state origin block state +/// @param x origin position X +/// @param y origin position Y +/// @param z origin position Z template inline void repair_segments( Storage& chunks, const Block& def, blockstate state, int x, int y, int z From 7ca91020e3b0c2648d52c13be217ee3b1468893d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 00:04:52 +0300 Subject: [PATCH 08/31] add docs to remaining public blocks_agent functions --- src/voxels/blocks_agent.hpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 10655cdc..e2c4266e 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -107,6 +107,13 @@ inline bool is_replaceable_at(const Storage& chunks, int32_t x, int32_t y, int32 return false; } +/// @brief Set block at specified position if voxel exists. +/// @param chunks chunks matrix +/// @param x block position X +/// @param y block position Y +/// @param z block position Z +/// @param id new block id +/// @param state new block state void set( Chunks& chunks, int32_t x, @@ -116,6 +123,13 @@ void set( blockstate state ); +/// @brief Set block at specified position if voxel exists. +/// @param chunks chunks storage +/// @param x block position X +/// @param y block position Y +/// @param z block position Z +/// @param id new block id +/// @param state new block state void set( GlobalChunks& chunks, int32_t x, @@ -376,6 +390,16 @@ inline void set_rotation( } } +/// @brief Cast ray to a selectable block with filter based on id. +/// @param chunks chunks matrix +/// @param start ray start position +/// @param dir normalized ray direction vector +/// @param maxDist maximum ray length +/// @param end [out] ray end position +/// @param norm [out] surface normal vector +/// @param iend [out] ray end integer position (voxel position + normal) +/// @param filter filtered ids +/// @return voxel pointer or nullptr voxel* raycast( const Chunks& chunks, const glm::vec3& start, @@ -387,6 +411,16 @@ voxel* raycast( std::set filter ); +/// @brief Cast ray to a selectable block with filter based on id. +/// @param chunks chunks storage +/// @param start ray start position +/// @param dir normalized ray direction vector +/// @param maxDist maximum ray length +/// @param end [out] ray end position +/// @param norm [out] surface normal vector +/// @param iend [out] ray end integer position (voxel position + normal) +/// @param filter filtered ids +/// @return voxel pointer or nullptr voxel* raycast( const GlobalChunks& chunks, const glm::vec3& start, From c5049d0e2448a2cf359cef019239d23afe307988 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 02:05:52 +0300 Subject: [PATCH 09/31] performance test again --- dev/tests/example.lua | 7 +++++-- src/logic/scripting/lua/libs/libblock.cpp | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 3dbe1197..7eccf874 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -8,8 +8,11 @@ assert(player.get_name(pid) == "Xerxes") test.sleep_until(function() return world.count_chunks() >= 9 end, 1000) print(world.count_chunks()) -timeit(10000000, block.get, 0, 0, 0) -timeit(10000000, core.blank, 0, 0, 0) +for i=1,10 do + print("--------------------") + timeit(10000000, block.get_fast, 0, 0, 0) + timeit(10000000, block.get, 0, 0, 0) +end block.destruct(0, 0, 0, pid) assert(block.get(0, 0, 0) == 0) diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index c0bf1ef6..fb2a5560 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -120,6 +120,15 @@ static int l_get(lua::State* L) { return lua::pushinteger(L, id); } +static int l_get_fast(lua::State* L) { + auto x = lua::tointeger(L, 1); + auto y = lua::tointeger(L, 2); + auto z = lua::tointeger(L, 3); + auto vox = blocks_agent::get(*level->chunks, x, y, z); + int id = vox == nullptr ? -1 : vox->id; + return lua::pushinteger(L, id); +} + static int l_get_x(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); @@ -629,6 +638,7 @@ const luaL_Reg blocklib[] = { {"is_solid_at", lua::wrap}, {"is_replaceable_at", lua::wrap}, {"set", lua::wrap}, + {"get_fast", lua::wrap}, {"get", lua::wrap}, {"get_X", lua::wrap}, {"get_Y", lua::wrap}, From b7664b4188584dfba1273a3267a127ecc1a35e0a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 05:13:49 +0300 Subject: [PATCH 10/31] move Lighting instance to ChunksController --- dev/tests/example.lua | 6 +----- src/logic/BlocksController.cpp | 4 ++-- src/logic/BlocksController.hpp | 2 +- src/logic/ChunksController.cpp | 6 +++--- src/logic/ChunksController.hpp | 3 ++- src/logic/LevelController.cpp | 6 +++--- src/logic/scripting/lua/libs/libblock.cpp | 18 +++++++---------- src/logic/scripting/lua/libs/libworld.cpp | 21 ++++++++++++++------ src/objects/Player.cpp | 3 +++ src/objects/Player.hpp | 2 ++ src/voxels/Chunks.cpp | 24 +++++++++-------------- src/voxels/Chunks.hpp | 12 +++++++----- src/world/Level.cpp | 10 ++++------ src/world/Level.hpp | 4 +--- 14 files changed, 60 insertions(+), 61 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 7eccf874..f7147d06 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -8,11 +8,7 @@ assert(player.get_name(pid) == "Xerxes") test.sleep_until(function() return world.count_chunks() >= 9 end, 1000) print(world.count_chunks()) -for i=1,10 do - print("--------------------") - timeit(10000000, block.get_fast, 0, 0, 0) - timeit(10000000, block.get, 0, 0, 0) -end +timeit(10000000, block.get, 0, 0, 0) block.destruct(0, 0, 0, pid) assert(block.get(0, 0, 0) == 0) diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp index 980c8bfe..bcc1f728 100644 --- a/src/logic/BlocksController.cpp +++ b/src/logic/BlocksController.cpp @@ -14,10 +14,10 @@ #include "world/Level.hpp" #include "world/World.hpp" -BlocksController::BlocksController(const Level& level, uint padding) +BlocksController::BlocksController(const Level& level, Lighting& lighting, uint padding) : level(level), chunks(*level.chunks), - lighting(*level.lighting), + lighting(lighting), randTickClock(20, 3), blocksTickClock(20, 1), worldTickClock(20, 1), diff --git a/src/logic/BlocksController.hpp b/src/logic/BlocksController.hpp index 0937b3f7..a8f286dc 100644 --- a/src/logic/BlocksController.hpp +++ b/src/logic/BlocksController.hpp @@ -34,7 +34,7 @@ class BlocksController { FastRandom random {}; std::vector blockInteractionCallbacks; public: - BlocksController(const Level& level, uint padding); + BlocksController(const Level& level, Lighting& lighting, uint padding); void updateSides(int x, int y, int z); void updateSides(int x, int y, int z, int w, int h, int d); diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 0d8d97b4..55ab262d 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -25,7 +25,7 @@ const uint MIN_SURROUNDING = 9; ChunksController::ChunksController(Level& level, uint padding) : level(level), chunks(*level.chunks), - lighting(*level.lighting), + lighting(std::make_unique(level.content, level.chunks.get())), padding(padding), generator(std::make_unique( level.content->generators.require(level.getWorld()->getGenerator()), @@ -107,9 +107,9 @@ bool ChunksController::buildLights(const std::shared_ptr& chunk) { if (surrounding == MIN_SURROUNDING) { bool lightsCache = chunk->flags.loadedLights; if (!lightsCache) { - lighting.buildSkyLight(chunk->x, chunk->z); + lighting->buildSkyLight(chunk->x, chunk->z); } - lighting.onChunkLoaded(chunk->x, chunk->z, !lightsCache); + lighting->onChunkLoaded(chunk->x, chunk->z, !lightsCache); chunk->flags.lighted = true; return true; } diff --git a/src/logic/ChunksController.hpp b/src/logic/ChunksController.hpp index e9f1ab62..71dada6f 100644 --- a/src/logic/ChunksController.hpp +++ b/src/logic/ChunksController.hpp @@ -15,7 +15,6 @@ class ChunksController { private: Level& level; Chunks& chunks; - Lighting& lighting; uint padding; std::unique_ptr generator; @@ -24,6 +23,8 @@ private: bool buildLights(const std::shared_ptr& chunk); void createChunk(int x, int y); public: + std::unique_ptr lighting; + ChunksController(Level& level, uint padding); ~ChunksController(); diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index a9c002b1..2f363dad 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -20,12 +20,12 @@ static debug::Logger logger("level-control"); LevelController::LevelController(Engine* engine, std::unique_ptr levelPtr) : settings(engine->getSettings()), level(std::move(levelPtr)), - blocks(std::make_unique( - *level, settings.chunks.padding.get() - )), chunks(std::make_unique( *level, settings.chunks.padding.get() )) { + blocks = std::make_unique( + *level, *chunks->lighting, settings.chunks.padding.get() + ); scripting::on_world_load(this); } diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index fb2a5560..24b00124 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -104,7 +104,13 @@ static int l_set(lua::State* L) { return 0; } blocks_agent::set(*level->chunksStorage, x, y, z, id, int2blockstate(state)); - level->lighting->onBlockSet(x, y, z, id); + + auto chunksController = controller->getChunksController(); + if (chunksController == nullptr) { + return 1; + } + Lighting& lighting = *chunksController->lighting; + lighting.onBlockSet(x, y, z, id); if (!noupdate) { blocks->updateSides(x, y, z); } @@ -120,15 +126,6 @@ static int l_get(lua::State* L) { return lua::pushinteger(L, id); } -static int l_get_fast(lua::State* L) { - auto x = lua::tointeger(L, 1); - auto y = lua::tointeger(L, 2); - auto z = lua::tointeger(L, 3); - auto vox = blocks_agent::get(*level->chunks, x, y, z); - int id = vox == nullptr ? -1 : vox->id; - return lua::pushinteger(L, id); -} - static int l_get_x(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); @@ -638,7 +635,6 @@ const luaL_Reg blocklib[] = { {"is_solid_at", lua::wrap}, {"is_replaceable_at", lua::wrap}, {"set", lua::wrap}, - {"get_fast", lua::wrap}, {"get", lua::wrap}, {"get_X", lua::wrap}, {"get_Y", lua::wrap}, diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 68004777..58e2bdc4 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -16,6 +16,8 @@ #include "voxels/GlobalChunks.hpp" #include "world/Level.hpp" #include "world/World.hpp" +#include "logic/LevelController.hpp" +#include "logic/ChunksController.hpp" using namespace scripting; namespace fs = std::filesystem; @@ -203,30 +205,37 @@ static int l_set_chunk_data(lua::State* L) { } else { chunk->decode(buffer->data().data()); } + + auto chunksController = controller->getChunksController(); + if (chunksController == nullptr) { + return 1; + } + + Lighting& lighting = *chunksController->lighting; chunk->updateHeights(); - level->lighting->buildSkyLight(x, y); + lighting.buildSkyLight(x, y); chunk->flags.modified = true; - level->lighting->onChunkLoaded(x, y, true); + lighting.onChunkLoaded(x, y, true); chunk = level->chunks->getChunk(x - 1, y); if (chunk != nullptr) { chunk->flags.modified = true; - level->lighting->onChunkLoaded(x - 1, y, true); + lighting.onChunkLoaded(x - 1, y, true); } chunk = level->chunks->getChunk(x + 1, y); if (chunk != nullptr) { chunk->flags.modified = true; - level->lighting->onChunkLoaded(x + 1, y, true); + lighting.onChunkLoaded(x + 1, y, true); } chunk = level->chunks->getChunk(x, y - 1); if (chunk != nullptr) { chunk->flags.modified = true; - level->lighting->onChunkLoaded(x, y - 1, true); + lighting.onChunkLoaded(x, y - 1, true); } chunk = level->chunks->getChunk(x, y + 1); if (chunk != nullptr) { chunk->flags.modified = true; - level->lighting->onChunkLoaded(x, y + 1, true); + lighting.onChunkLoaded(x, y + 1, true); } return 1; diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index 000d2c04..0dd58f91 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -44,6 +44,9 @@ Player::Player( position(position), inventory(std::move(inv)), eid(eid), + chunks(std::make_unique( + 3, 3, 0, 0, level->events.get(), level->content->getIndices() + )), fpCamera(level->getCamera("core:first-person")), spCamera(level->getCamera("core:third-person-front")), tpCamera(level->getCamera("core:third-person-back")), diff --git a/src/objects/Player.hpp b/src/objects/Player.hpp index 920e06e4..1f57c6f7 100644 --- a/src/objects/Player.hpp +++ b/src/objects/Player.hpp @@ -8,6 +8,7 @@ #include "settings.hpp" #include "voxels/voxel.hpp" +class Chunks; class Camera; class Inventory; class ContentReport; @@ -55,6 +56,7 @@ class Player : public Serializable { entityid_t eid; entityid_t selectedEid = 0; public: + std::unique_ptr chunks; // not in use yet std::shared_ptr fpCamera, spCamera, tpCamera; std::shared_ptr currentCamera; bool debug = false; diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 8d5231a6..91b05d69 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -1,6 +1,5 @@ #include "Chunks.hpp" -#include #include #include @@ -20,9 +19,6 @@ #include "world/Level.hpp" #include "world/LevelEvents.hpp" #include "VoxelsVolume.hpp" -#include "Block.hpp" -#include "Chunk.hpp" -#include "voxel.hpp" #include "blocks_agent.hpp" Chunks::Chunks( @@ -30,16 +26,15 @@ Chunks::Chunks( int32_t d, int32_t ox, int32_t oz, - WorldFiles* wfile, - Level* level + LevelEvents* events, + const ContentIndices* indices ) - : level(level), - indices(level ? level->content->getIndices() : nullptr), - areaMap(w, d), - worldFiles(wfile) { + : events(events), + indices(indices), + areaMap(w, d) { areaMap.setCenter(ox-w/2, oz-d/2); areaMap.setOutCallback([this](int, int, const auto& chunk) { - this->level->events->trigger(EVT_CHUNK_HIDDEN, chunk.get()); + this->events->trigger(EVT_CHUNK_HIDDEN, chunk.get()); }); } @@ -319,8 +314,9 @@ void Chunks::resize(uint32_t newW, uint32_t newD) { bool Chunks::putChunk(const std::shared_ptr& chunk) { if (areaMap.set(chunk->x, chunk->z, chunk)) { - if (level) - level->events->trigger(LevelEventType::EVT_CHUNK_SHOWN, chunk.get()); + if (events) { + events->trigger(LevelEventType::EVT_CHUNK_SHOWN, chunk.get()); + } return true; } return false; @@ -330,8 +326,6 @@ bool Chunks::putChunk(const std::shared_ptr& chunk) { // 25.06.2024: not now // 11.11.2024: not now void Chunks::getVoxels(VoxelsVolume* volume, bool backlight) const { - const Content* content = level->content; - auto indices = content->getIndices(); voxel* voxels = volume->getVoxels(); light_t* lights = volume->getLights(); int x = volume->getX(); diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index a39c8960..c1c75e9c 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -20,12 +20,11 @@ class Chunk; class WorldFiles; class LevelEvents; class Block; -class Level; class VoxelsVolume; /// Player-centred chunks matrix class Chunks { - Level* level; + LevelEvents* events; const ContentIndices* const indices; void eraseSegments(const Block& def, blockstate state, int x, int y, int z); @@ -40,15 +39,14 @@ class Chunks { ); util::AreaMap2D, int32_t> areaMap; - WorldFiles* worldFiles; public: Chunks( int32_t w, int32_t d, int32_t ox, int32_t oz, - WorldFiles* worldFiles, - Level* level + LevelEvents* events, + const ContentIndices* indices ); ~Chunks() = default; @@ -156,4 +154,8 @@ public: const ContentIndices& getContentIndices() const { return *indices; } + + static inline constexpr unsigned matrixSize(int loadDistance, int padding) { + return (loadDistance + padding) * 2; + } }; diff --git a/src/world/Level.cpp b/src/world/Level.cpp index 99f47d86..c289579f 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -23,14 +23,14 @@ Level::Level( const Content* content, EngineSettings& settings ) - : world(std::move(worldPtr)), + : settings(settings), + world(std::move(worldPtr)), content(content), chunksStorage(std::make_unique(this)), physics(std::make_unique(glm::vec3(0, -22.6f, 0))), events(std::make_unique()), entities(std::make_unique(this)), - players(std::make_unique(this)), - settings(settings) { + players(std::make_unique(this)) { const auto& worldInfo = world->getInfo(); auto& cameraIndices = content->getIndices(ResourceType::CAMERA); for (size_t i = 0; i < cameraIndices.size(); i++) { @@ -65,11 +65,9 @@ Level::Level( (settings.chunks.loadDistance.get() + settings.chunks.padding.get()) * 2; chunks = std::make_unique( - matrixSize, matrixSize, 0, 0, world->wfile.get(), this + matrixSize, matrixSize, 0, 0, events.get(), content->getIndices() ); - lighting = std::make_unique(content, chunks.get()); - inventories = std::make_unique(*this); } diff --git a/src/world/Level.hpp b/src/world/Level.hpp index 63274672..b30bde3b 100644 --- a/src/world/Level.hpp +++ b/src/world/Level.hpp @@ -22,6 +22,7 @@ struct EngineSettings; /// @brief A level, contains chunks and objects class Level { + const EngineSettings& settings; std::unique_ptr world; public: const Content* const content; @@ -31,14 +32,11 @@ public: std::unique_ptr inventories; std::unique_ptr physics; - std::unique_ptr lighting; std::unique_ptr events; std::unique_ptr entities; std::unique_ptr players; std::vector> cameras; // move somewhere? - const EngineSettings& settings; - Level( std::unique_ptr world, const Content* content, From 1c18c020923f44d58f1969d706625e90e1fecc81 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 19:40:00 +0300 Subject: [PATCH 11/31] move Chunks from Level to Player --- dev/tests/example.lua | 4 +- src/TestMainloop.cpp | 4 +- src/frontend/hud.cpp | 49 +++++---- src/frontend/hud.hpp | 4 +- src/frontend/screens/LevelScreen.cpp | 10 +- src/graphics/render/BlockWrapsRenderer.cpp | 11 +- src/graphics/render/BlockWrapsRenderer.hpp | 6 +- src/graphics/render/ChunksRenderer.cpp | 61 ++++++----- src/graphics/render/ChunksRenderer.hpp | 3 + src/graphics/render/Decorator.cpp | 4 +- src/graphics/render/ParticlesRenderer.cpp | 10 +- src/graphics/render/ParticlesRenderer.hpp | 3 + src/graphics/render/WorldRenderer.cpp | 13 ++- src/logic/BlocksController.cpp | 87 ++++++++++----- src/logic/BlocksController.hpp | 9 +- src/logic/ChunksController.cpp | 39 ++++--- src/logic/ChunksController.hpp | 14 +-- src/logic/LevelController.cpp | 25 +++-- src/logic/LevelController.hpp | 2 +- src/logic/PlayerController.cpp | 24 ++--- src/logic/scripting/lua/libs/libblock.cpp | 4 +- src/logic/scripting/lua/libs/libentity.cpp | 12 ++- .../scripting/lua/libs/libgeneration.cpp | 2 +- src/logic/scripting/lua/libs/libhud.cpp | 3 +- src/logic/scripting/lua/libs/libworld.cpp | 14 +-- .../lua/usertypes/lua_type_voxelfragment.cpp | 2 +- src/objects/Entities.cpp | 2 +- src/objects/Player.cpp | 6 +- src/physics/PhysicsSolver.cpp | 28 ++--- src/physics/PhysicsSolver.hpp | 6 +- src/voxels/Chunks.cpp | 8 ++ src/voxels/Chunks.hpp | 2 + src/voxels/GlobalChunks.cpp | 5 + src/voxels/GlobalChunks.hpp | 3 + src/voxels/blocks_agent.cpp | 102 ++++++++++++++++++ src/voxels/blocks_agent.hpp | 41 +++++++ src/world/Level.cpp | 22 ---- src/world/Level.hpp | 5 - src/world/generator/VoxelFragment.cpp | 15 +-- src/world/generator/VoxelFragment.hpp | 6 +- 40 files changed, 450 insertions(+), 220 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index f7147d06..0435b6cd 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -5,11 +5,9 @@ test.reconfig_packs({"base"}, {}) test.new_world("demo", "2019", "core:default") local pid = player.create("Xerxes") assert(player.get_name(pid) == "Xerxes") -test.sleep_until(function() return world.count_chunks() >= 9 end, 1000) +test.sleep_until(function() return block.get(0, 0, 0) >= 0 end, 1000) print(world.count_chunks()) -timeit(10000000, block.get, 0, 0, 0) - block.destruct(0, 0, 0, pid) assert(block.get(0, 0, 0) == 0) test.close_world(true) diff --git a/src/TestMainloop.cpp b/src/TestMainloop.cpp index 33487059..100c0b51 100644 --- a/src/TestMainloop.cpp +++ b/src/TestMainloop.cpp @@ -49,6 +49,8 @@ void TestMainloop::setLevel(std::unique_ptr level) { engine.getPaths()->setCurrentWorldFolder(fs::path()); controller = nullptr; } else { - controller = std::make_unique(&engine, std::move(level)); + controller = std::make_unique( + &engine, std::move(level), nullptr + ); } } diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 1a2d8315..e6b1ecb9 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -106,7 +106,7 @@ std::shared_ptr HudElement::getNode() const { std::shared_ptr Hud::createContentAccess() { auto content = frontend.getLevel().content; auto indices = content->getIndices(); - auto inventory = player->getInventory(); + auto inventory = player.getInventory(); size_t itemsCount = indices->items.count(); auto accessInventory = std::make_shared(0, itemsCount); @@ -120,7 +120,7 @@ std::shared_ptr Hud::createContentAccess() { inventory->move(copy, indices); }, [=](uint, ItemStack& item) { - inventory->getSlot(player->getChosenSlot()).set(item); + inventory->getSlot(player.getChosenSlot()).set(item); }); InventoryBuilder builder; @@ -132,7 +132,7 @@ std::shared_ptr Hud::createContentAccess() { } std::shared_ptr Hud::createHotbar() { - auto inventory = player->getInventory(); + auto inventory = player.getInventory(); auto content = frontend.getLevel().content; SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr); @@ -148,7 +148,7 @@ std::shared_ptr Hud::createHotbar() { static constexpr uint WORLDGEN_IMG_SIZE = 128U; -Hud::Hud(Engine* engine, LevelFrontend& frontend, Player* player) +Hud::Hud(Engine* engine, LevelFrontend& frontend, Player& player) : engine(engine), assets(engine->getAssets()), gui(engine->getGUI()), @@ -177,7 +177,7 @@ Hud::Hud(Engine* engine, LevelFrontend& frontend, Player* player) uicamera->flipped = true; debugPanel = create_debug_panel( - engine, frontend.getLevel(), *player, allowDebugCheats + engine, frontend.getLevel(), player, allowDebugCheats ); debugPanel->setZIndex(2); gui->add(debugPanel); @@ -246,12 +246,12 @@ void Hud::processInput(bool visible) { void Hud::updateHotbarControl() { if (!inventoryOpen && Events::scroll) { - int slot = player->getChosenSlot(); + int slot = player.getChosenSlot(); slot = (slot - Events::scroll) % 10; if (slot < 0) { slot += 10; } - player->setChosenSlot(slot); + player.setChosenSlot(slot); } for ( int i = static_cast(keycode::NUM_1); @@ -259,17 +259,17 @@ void Hud::updateHotbarControl() { i++ ) { if (Events::jpressed(i)) { - player->setChosenSlot(i - static_cast(keycode::NUM_1)); + player.setChosenSlot(i - static_cast(keycode::NUM_1)); } } if (Events::jpressed(keycode::NUM_0)) { - player->setChosenSlot(9); + player.setChosenSlot(9); } } void Hud::updateWorldGenDebugVisualization() { auto& level = frontend.getLevel(); - auto& chunks = *level.chunks; + const auto& chunks = *player.chunks; auto generator = frontend.getController()->getChunksController()->getGenerator(); auto debugInfo = generator->createDebugInfo(); @@ -318,9 +318,10 @@ void Hud::updateWorldGenDebugVisualization() { void Hud::update(bool visible) { const auto& level = frontend.getLevel(); + const auto& chunks = *player.chunks; auto menu = gui->getMenu(); - debugPanel->setVisible(player->debug && visible); + debugPanel->setVisible(player.debug && visible); if (!visible && inventoryOpen) { closeInventory(); @@ -337,7 +338,7 @@ void Hud::update(bool visible) { } if (blockUI) { - voxel* vox = level.chunks->get(blockPos.x, blockPos.y, blockPos.z); + voxel* vox = chunks.get(blockPos.x, blockPos.y, blockPos.z); if (vox == nullptr || vox->id != currentblockid) { closeInventory(); } @@ -355,7 +356,7 @@ void Hud::update(bool visible) { if (visible) { for (auto& element : elements) { - element.update(pause, inventoryOpen, player->debug); + element.update(pause, inventoryOpen, player.debug); if (element.isRemoved()) { onRemove(element); } @@ -363,8 +364,8 @@ void Hud::update(bool visible) { } cleanup(); - debugMinimap->setVisible(player->debug && showGeneratorMinimap); - if (player->debug && showGeneratorMinimap) { + debugMinimap->setVisible(player.debug && showGeneratorMinimap); + if (player.debug && showGeneratorMinimap) { updateWorldGenDebugVisualization(); } } @@ -375,7 +376,7 @@ void Hud::openInventory() { showExchangeSlot(); inventoryOpen = true; - auto inventory = player->getInventory(); + auto inventory = player.getInventory(); auto inventoryDocument = assets->get("core:inventory"); inventoryView = std::dynamic_pointer_cast(inventoryDocument->getRoot()); inventoryView->bind(inventory, content); @@ -422,7 +423,9 @@ void Hud::openInventory( closeInventory(); } auto& level = frontend.getLevel(); + const auto& chunks = *player.chunks; auto content = level.content; + blockUI = std::dynamic_pointer_cast(doc->getRoot()); if (blockUI == nullptr) { throw std::runtime_error("block UI root element must be 'inventory'"); @@ -436,10 +439,10 @@ void Hud::openInventory( if (blockinv == nullptr) { blockinv = level.inventories->createVirtual(blockUI->getSlotsCount()); } - level.chunks->getChunkByVoxel(block.x, block.y, block.z)->flags.unsaved = true; + chunks.getChunkByVoxel(block.x, block.y, block.z)->flags.unsaved = true; blockUI->bind(blockinv, content); blockPos = block; - currentblockid = level.chunks->get(block.x, block.y, block.z)->id; + currentblockid = chunks.require(block.x, block.y, block.z).id; add(HudElement(hud_element_mode::inventory_bound, doc, blockUI, false)); } @@ -481,7 +484,7 @@ void Hud::openPermanent(UiDocument* doc) { auto invview = std::dynamic_pointer_cast(root); if (invview) { - invview->bind(player->getInventory(), frontend.getLevel().content); + invview->bind(player.getInventory(), frontend.getLevel().content); } add(HudElement(hud_element_mode::permanent, doc, doc->getRoot(), false)); } @@ -571,7 +574,7 @@ void Hud::draw(const DrawContext& ctx){ uishader->uniformMatrix("u_projview", uicamera->getProjView()); // Crosshair - if (!pause && !inventoryOpen && !player->debug) { + if (!pause && !inventoryOpen && !player.debug) { DrawContext chctx = ctx.sub(batch); chctx.setBlendMode(BlendMode::inversion); auto texture = assets->get("gui/crosshair"); @@ -629,7 +632,7 @@ void Hud::updateElementsPosition(const Viewport& viewport) { exchangeSlot->setPos(glm::vec2(Events::cursor)); } hotbarView->setPos(glm::vec2(width/2, height-65)); - hotbarView->setSelected(player->getChosenSlot()); + hotbarView->setSelected(player.getChosenSlot()); } bool Hud::isInventoryOpen() const { @@ -661,7 +664,7 @@ void Hud::setPause(bool pause) { } Player* Hud::getPlayer() const { - return player; + return &player; } std::shared_ptr Hud::getBlockInventory() { @@ -684,7 +687,7 @@ void Hud::setDebugCheats(bool flag) { gui->remove(debugPanel); debugPanel = create_debug_panel( - engine, frontend.getLevel(), *player, allowDebugCheats + engine, frontend.getLevel(), player, allowDebugCheats ); debugPanel->setZIndex(2); gui->add(debugPanel); diff --git a/src/frontend/hud.hpp b/src/frontend/hud.hpp index 5594664a..6d9d3f92 100644 --- a/src/frontend/hud.hpp +++ b/src/frontend/hud.hpp @@ -75,7 +75,7 @@ class Hud : public util::ObjectsKeeper { std::unique_ptr uicamera; gui::GUI* gui; LevelFrontend& frontend; - Player* player; + Player& player; /// @brief Is any overlay/inventory open bool inventoryOpen = false; @@ -131,7 +131,7 @@ class Hud : public util::ObjectsKeeper { void showExchangeSlot(); void updateWorldGenDebugVisualization(); public: - Hud(Engine* engine, LevelFrontend& frontend, Player* player); + Hud(Engine* engine, LevelFrontend& frontend, Player& player); ~Hud(); void update(bool hudVisible); diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index 9470e7e0..e39d4736 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -44,9 +44,9 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr levelPtr) auto menu = engine->getGUI()->getMenu(); menu->reset(); - controller = std::make_unique(engine, std::move(levelPtr)); - auto player = level->players->get(0); + controller = + std::make_unique(engine, std::move(levelPtr), player); playerController = std::make_unique( settings, level, @@ -60,21 +60,21 @@ LevelScreen::LevelScreen(Engine* engine, std::unique_ptr levelPtr) worldRenderer = std::make_unique( engine, *frontend, player ); - hud = std::make_unique(engine, *frontend, player); + hud = std::make_unique(engine, *frontend, *player); decorator = std::make_unique( *engine, *controller, *worldRenderer, assets, *player ); keepAlive(settings.graphics.backlight.observe([=](bool) { - controller->getLevel()->chunks->saveAndClear(); + player->chunks->saveAndClear(); worldRenderer->clear(); })); keepAlive(settings.camera.fov.observe([=](double value) { player->fpCamera->setFov(glm::radians(value)); })); keepAlive(Events::getBinding(BIND_CHUNKS_RELOAD).onactived.add([=](){ - controller->getLevel()->chunks->saveAndClear(); + player->chunks->saveAndClear(); worldRenderer->clear(); })); diff --git a/src/graphics/render/BlockWrapsRenderer.cpp b/src/graphics/render/BlockWrapsRenderer.cpp index 3ad30ad8..d19b1f1e 100644 --- a/src/graphics/render/BlockWrapsRenderer.cpp +++ b/src/graphics/render/BlockWrapsRenderer.cpp @@ -14,15 +14,18 @@ #include "window/Window.hpp" #include "world/Level.hpp" -BlockWrapsRenderer::BlockWrapsRenderer(const Assets& assets, const Level& level) - : assets(assets), level(level), batch(std::make_unique(1024)) { +BlockWrapsRenderer::BlockWrapsRenderer( + const Assets& assets, const Level& level, const Chunks& chunks +) + : assets(assets), + level(level), + chunks(chunks), + batch(std::make_unique(1024)) { } BlockWrapsRenderer::~BlockWrapsRenderer() = default; void BlockWrapsRenderer::draw(const BlockWrapper& wrapper) { - const auto& chunks = *level.chunks; - auto textureRegion = util::get_texture_region(assets, wrapper.texture, ""); auto& shader = assets.require("entity"); diff --git a/src/graphics/render/BlockWrapsRenderer.hpp b/src/graphics/render/BlockWrapsRenderer.hpp index 728e664a..9d6184b9 100644 --- a/src/graphics/render/BlockWrapsRenderer.hpp +++ b/src/graphics/render/BlockWrapsRenderer.hpp @@ -10,6 +10,7 @@ class Assets; class Player; class Level; +class Chunks; class DrawContext; struct BlockWrapper { @@ -20,6 +21,7 @@ struct BlockWrapper { class BlockWrapsRenderer { const Assets& assets; const Level& level; + const Chunks& chunks; std::unique_ptr batch; std::unordered_map> wrappers; @@ -27,7 +29,9 @@ class BlockWrapsRenderer { void draw(const BlockWrapper& wrapper); public: - BlockWrapsRenderer(const Assets& assets, const Level& level); + BlockWrapsRenderer( + const Assets& assets, const Level& level, const Chunks& chunks + ); ~BlockWrapsRenderer(); void draw(const DrawContext& ctx, const Player& player); diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index f669cfea..fe955bee 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -20,19 +20,22 @@ size_t ChunksRenderer::visibleChunks = 0; class RendererWorker : public util::Worker, RendererResult> { const Level& level; + const Chunks& chunks; BlocksRenderer renderer; public: RendererWorker( const Level& level, + const Chunks& chunks, const ContentGfxCache& cache, const EngineSettings& settings - ) : level(level), + ) : level(level), + chunks(chunks), renderer(settings.graphics.chunkMaxVertices.get(), *level.content, cache, settings) {} RendererResult operator()(const std::shared_ptr& chunk) override { - renderer.build(chunk.get(), level.chunks.get()); + renderer.build(chunk.get(), &chunks); if (renderer.isCancelled()) { return RendererResult { glm::ivec2(chunk->x, chunk->z), true, MeshData()}; @@ -44,29 +47,36 @@ public: }; ChunksRenderer::ChunksRenderer( - const Level* level, + const Level* level, + const Chunks& chunks, const Assets& assets, const Frustum& frustum, - const ContentGfxCache& cache, + const ContentGfxCache& cache, const EngineSettings& settings -) : level(*level), - assets(assets), - frustum(frustum), - settings(settings), - threadPool( - "chunks-render-pool", - [&](){return std::make_shared(*level, cache, settings);}, - [&](RendererResult& result){ - if (!result.cancelled) { - auto meshData = std::move(result.meshData); - meshes[result.key] = ChunkMesh { - std::make_unique(meshData.mesh), - std::move(meshData.sortingMesh) - }; - } - inwork.erase(result.key); - }, settings.graphics.chunkMaxRenderers.get()) -{ +) + : level(*level), + chunks(chunks), + assets(assets), + frustum(frustum), + settings(settings), + threadPool( + "chunks-render-pool", + [&]() { + return std::make_shared( + *level, chunks, cache, settings + ); + }, + [&](RendererResult& result) { + if (!result.cancelled) { + auto meshData = std::move(result.meshData); + meshes[result.key] = ChunkMesh { + std::make_unique(meshData.mesh), + std::move(meshData.sortingMesh)}; + } + inwork.erase(result.key); + }, + settings.graphics.chunkMaxRenderers.get() + ) { threadPool.setStopOnFail(false); renderer = std::make_unique( settings.graphics.chunkMaxVertices.get(), @@ -83,7 +93,7 @@ const Mesh* ChunksRenderer::render( ) { chunk->flags.modified = false; if (important) { - auto mesh = renderer->render(chunk.get(), level.chunks.get()); + auto mesh = renderer->render(chunk.get(), &chunks); meshes[glm::ivec2(chunk->x, chunk->z)] = ChunkMesh { std::move(mesh.mesh), std::move(mesh.sortingMeshData) }; @@ -131,7 +141,7 @@ void ChunksRenderer::update() { const Mesh* ChunksRenderer::retrieveChunk( size_t index, const Camera& camera, Shader& shader, bool culling ) { - auto chunk = level.chunks->getChunks()[index]; + auto chunk = chunks.getChunks()[index]; if (chunk == nullptr || !chunk->flags.lighted) { return nullptr; } @@ -163,7 +173,6 @@ const Mesh* ChunksRenderer::retrieveChunk( void ChunksRenderer::drawChunks( const Camera& camera, Shader& shader ) { - const auto& chunks = *level.chunks; const auto& atlas = assets.require("blocks"); atlas.getTexture()->bind(); @@ -232,7 +241,7 @@ void ChunksRenderer::drawSortedMeshes(const Camera& camera, Shader& shader) { frameid++; bool culling = settings.graphics.frustumCulling.get(); - const auto& chunks = level.chunks->getChunks(); + const auto& chunks = this->chunks.getChunks(); const auto& cameraPos = camera.position; const auto& atlas = assets.require("blocks"); diff --git a/src/graphics/render/ChunksRenderer.hpp b/src/graphics/render/ChunksRenderer.hpp index 859bca9e..a25c4241 100644 --- a/src/graphics/render/ChunksRenderer.hpp +++ b/src/graphics/render/ChunksRenderer.hpp @@ -20,6 +20,7 @@ class Level; class Camera; class Shader; class Assets; +class Chunks; class Frustum; class BlocksRenderer; class ContentGfxCache; @@ -42,6 +43,7 @@ struct RendererResult { class ChunksRenderer { const Level& level; + const Chunks& chunks; const Assets& assets; const Frustum& frustum; const EngineSettings& settings; @@ -57,6 +59,7 @@ class ChunksRenderer { public: ChunksRenderer( const Level* level, + const Chunks& chunks, const Assets& assets, const Frustum& frustum, const ContentGfxCache& cache, diff --git a/src/graphics/render/Decorator.cpp b/src/graphics/render/Decorator.cpp index 2f01955f..a3ef140b 100644 --- a/src/graphics/render/Decorator.cpp +++ b/src/graphics/render/Decorator.cpp @@ -85,7 +85,7 @@ void Decorator::update( int index = currentIndex; currentIndex = (currentIndex + BIG_PRIME) % UPDATE_BLOCKS; - const auto& chunks = *level.chunks; + const auto& chunks = *player.chunks; const auto& indices = *level.content->getIndices(); int lx = index % UPDATE_AREA_DIAMETER; @@ -108,7 +108,7 @@ void Decorator::update(float delta, const Camera& camera) { for (int i = 0; i < ITERATIONS; i++) { update(delta, pos, camera.position); } - const auto& chunks = *level.chunks; + const auto& chunks = *player.chunks; const auto& indices = *level.content->getIndices(); auto iter = blockEmitters.begin(); while (iter != blockEmitters.end()) { diff --git a/src/graphics/render/ParticlesRenderer.cpp b/src/graphics/render/ParticlesRenderer.cpp index 0e2292a5..80057352 100644 --- a/src/graphics/render/ParticlesRenderer.cpp +++ b/src/graphics/render/ParticlesRenderer.cpp @@ -16,12 +16,17 @@ size_t ParticlesRenderer::visibleParticles = 0; size_t ParticlesRenderer::aliveEmitters = 0; ParticlesRenderer::ParticlesRenderer( - const Assets& assets, const Level& level, const GraphicsSettings* settings + const Assets& assets, + const Level& level, + const Chunks& chunks, + const GraphicsSettings* settings ) : batch(std::make_unique(4096)), level(level), + chunks(chunks), assets(assets), - settings(settings) {} + settings(settings) { +} ParticlesRenderer::~ParticlesRenderer() = default; @@ -44,7 +49,6 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) { const auto& right = camera.right; const auto& up = camera.up; - const auto& chunks = *level.chunks; bool backlight = settings->backlight.get(); std::vector unusedTextures; diff --git a/src/graphics/render/ParticlesRenderer.hpp b/src/graphics/render/ParticlesRenderer.hpp index 46509d96..3fe692e5 100644 --- a/src/graphics/render/ParticlesRenderer.hpp +++ b/src/graphics/render/ParticlesRenderer.hpp @@ -10,12 +10,14 @@ class Texture; class Assets; class Camera; +class Chunks; class MainBatch; class Level; struct GraphicsSettings; class ParticlesRenderer { const Level& level; + const Chunks& chunks; const Assets& assets; const GraphicsSettings* settings; std::unordered_map> particles; @@ -29,6 +31,7 @@ public: ParticlesRenderer( const Assets& assets, const Level& level, + const Chunks& chunks, const GraphicsSettings* settings ); ~ParticlesRenderer(); diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index a0fd4c83..4455e451 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -68,21 +68,24 @@ WorldRenderer::WorldRenderer( lineBatch(std::make_unique()), batch3d(std::make_unique(BATCH3D_CAPACITY)), modelBatch(std::make_unique( - MODEL_BATCH_CAPACITY, assets, *level.chunks, engine->getSettings() + MODEL_BATCH_CAPACITY, assets, *player->chunks, engine->getSettings() )), particles(std::make_unique( - assets, level, &engine->getSettings().graphics + assets, level, *player->chunks, &engine->getSettings().graphics )), texts(std::make_unique(*batch3d, assets, *frustumCulling)), guides(std::make_unique()), chunks(std::make_unique( &level, + *player->chunks, assets, *frustumCulling, frontend.getContentGfxCache(), engine->getSettings() )), - blockWraps(std::make_unique(assets, level)) { + blockWraps( + std::make_unique(assets, level, *player->chunks) + ) { auto& settings = engine->getSettings(); level.events->listen( EVT_CHUNK_HIDDEN, @@ -362,7 +365,7 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) { int x = std::floor(player->currentCamera->position.x); int y = std::floor(player->currentCamera->position.y); int z = std::floor(player->currentCamera->position.z); - auto block = level.chunks->get(x, y, z); + auto block = player->chunks->get(x, y, z); if (block && block->id) { const auto& def = level.content->getIndices()->blocks.require(block->id); @@ -381,7 +384,7 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) { batch3d->begin(); shader.uniformMatrix("u_projview", glm::mat4(1.0f)); shader.uniformMatrix("u_apply", glm::mat4(1.0f)); - auto light = level.chunks->getLight(x, y, z); + auto light = player->chunks->getLight(x, y, z); float s = Lightmap::extract(light, 3) / 15.0f; glm::vec4 tint( glm::min(1.0f, Lightmap::extract(light, 0) / 15.0f + s), diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp index bcc1f728..3c0b64c0 100644 --- a/src/logic/BlocksController.cpp +++ b/src/logic/BlocksController.cpp @@ -1,5 +1,7 @@ #include "BlocksController.hpp" +#include + #include "content/Content.hpp" #include "items/Inventories.hpp" #include "items/Inventory.hpp" @@ -11,12 +13,15 @@ #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" #include "voxels/voxel.hpp" +#include "voxels/blocks_agent.hpp" #include "world/Level.hpp" #include "world/World.hpp" +#include "objects/Player.hpp" +#include "objects/Players.hpp" -BlocksController::BlocksController(const Level& level, Lighting& lighting, uint padding) +BlocksController::BlocksController(const Level& level, Lighting* lighting, uint padding) : level(level), - chunks(*level.chunks), + chunks(*level.chunksStorage), lighting(lighting), randTickClock(20, 3), blocksTickClock(20, 1), @@ -34,7 +39,7 @@ void BlocksController::updateSides(int x, int y, int z) { } void BlocksController::updateSides(int x, int y, int z, int w, int h, int d) { - voxel* vox = chunks.get(x, y, z); + voxel* vox = blocks_agent::get(chunks, x, y, z); const auto& def = level.content->getIndices()->blocks.require(vox->id); const auto& rot = def.rotations.variants[vox->state.rotation]; const auto& xaxis = rot.axisX; @@ -62,8 +67,10 @@ void BlocksController::breakBlock( onBlockInteraction( player, glm::ivec3(x, y, z), def, BlockInteraction::destruction ); - chunks.set(x, y, z, 0, {}); - lighting.onBlockSet(x, y, z, 0); + blocks_agent::set(chunks, x, y, z, 0, {}); + if (lighting) { + lighting->onBlockSet(x, y, z, 0); + } scripting::on_block_broken(player, def, glm::ivec3(x, y, z)); if (def.rt.extended) { updateSides(x, y, z , def.size.x, def.size.y, def.size.z); @@ -75,7 +82,7 @@ void BlocksController::breakBlock( void BlocksController::placeBlock( Player* player, const Block& def, blockstate state, int x, int y, int z ) { - auto voxel = chunks.get(x, y, z); + auto voxel = blocks_agent::get(chunks, x, y, z); if (voxel == nullptr) { return; } @@ -85,8 +92,10 @@ void BlocksController::placeBlock( onBlockInteraction( player, glm::ivec3(x, y, z), def, BlockInteraction::placing ); - chunks.set(x, y, z, def.rt.id, state); - lighting.onBlockSet(x, y, z, def.rt.id); + blocks_agent::set(chunks, x, y, z, def.rt.id, state); + if (lighting) { + lighting->onBlockSet(x, y, z, def.rt.id); + } scripting::on_block_placed(player, def, glm::ivec3(x, y, z)); if (def.rt.extended) { updateSides(x, y, z , def.size.x, def.size.y, def.size.z); @@ -96,12 +105,12 @@ void BlocksController::placeBlock( } void BlocksController::updateBlock(int x, int y, int z) { - voxel* vox = chunks.get(x, y, z); + voxel* vox = blocks_agent::get(chunks, x, y, z); if (vox == nullptr) return; const auto& def = level.content->getIndices()->blocks.require(vox->id); if (def.grounded) { const auto& vec = get_ground_direction(def, vox->state.rotation); - if (!chunks.isSolidBlock(x + vec.x, y + vec.y, z + vec.z)) { + if (!blocks_agent::is_solid_at(chunks, x + vec.x, y + vec.y, z + vec.z)) { breakBlock(nullptr, def, x, y, z); return; } @@ -162,28 +171,48 @@ void BlocksController::randomTick( void BlocksController::randomTick(int tickid, int parts) { auto indices = level.content->getIndices(); - int width = chunks.getWidth(); - int height = chunks.getHeight(); - int segments = 4; - for (uint z = padding; z < height - padding; z++) { - for (uint x = padding; x < width - padding; x++) { - int index = z * width + x; - if ((index + tickid) % parts != 0) { - continue; + std::set chunksIterated; + + for (const auto& [pid, player] : *level.players) { + const auto& chunks = *player->chunks; + int offsetX = chunks.getOffsetX(); + int offsetY = chunks.getOffsetY(); + int width = chunks.getWidth(); + int height = chunks.getHeight(); + int segments = 4; + + for (uint z = padding; z < height - padding; z++) { + for (uint x = padding; x < width - padding; x++) { + int index = z * width + x; + if ((index + tickid) % parts != 0) { + continue; + } + auto& chunk = chunks.getChunks()[index]; + if (chunk == nullptr || !chunk->flags.lighted) { + continue; + } + union { + int32_t pos[2]; + uint64_t key; + } posU; + posU.pos[0] = x + offsetX; + posU.pos[1] = z + offsetY; + if (chunksIterated.find(posU.key) != chunksIterated.end()) { + continue; + } + chunksIterated.insert(posU.key); + randomTick(*chunk, segments, indices); } - auto& chunk = chunks.getChunks()[index]; - if (chunk == nullptr || !chunk->flags.lighted) { - continue; - } - randomTick(*chunk, segments, indices); } } } int64_t BlocksController::createBlockInventory(int x, int y, int z) { - auto chunk = chunks.getChunkByVoxel(x, y, z); - if (chunk == nullptr) { + auto chunk = blocks_agent::get_chunk( + chunks, floordiv(x), floordiv(z) + ); + if (chunk == nullptr || y < 0 || y >= CHUNK_H) { return 0; } int lx = x - chunk->x * CHUNK_W; @@ -203,7 +232,9 @@ int64_t BlocksController::createBlockInventory(int x, int y, int z) { } void BlocksController::bindInventory(int64_t invid, int x, int y, int z) { - auto chunk = chunks.getChunkByVoxel(x, y, z); + auto chunk = blocks_agent::get_chunk( + chunks, floordiv(x), floordiv(z) + ); if (chunk == nullptr) { throw std::runtime_error("block does not exists"); } @@ -216,7 +247,9 @@ void BlocksController::bindInventory(int64_t invid, int x, int y, int z) { } void BlocksController::unbindInventory(int x, int y, int z) { - auto chunk = chunks.getChunkByVoxel(x, y, z); + auto chunk = blocks_agent::get_chunk( + chunks, floordiv(x), floordiv(z) + ); if (chunk == nullptr) { throw std::runtime_error("block does not exists"); } diff --git a/src/logic/BlocksController.hpp b/src/logic/BlocksController.hpp index a8f286dc..1dd48983 100644 --- a/src/logic/BlocksController.hpp +++ b/src/logic/BlocksController.hpp @@ -14,19 +14,20 @@ class Level; class Chunk; class Chunks; class Lighting; +class GlobalChunks; class ContentIndices; enum class BlockInteraction { step, destruction, placing }; /// @brief Player argument is nullable using on_block_interaction = std::function< - void(Player*, const glm::ivec3&, const Block&, BlockInteraction type)>; + void(Player*, const glm::ivec3&, const Block&, BlockInteraction)>; /// BlocksController manages block updates and data (inventories, metadata) class BlocksController { const Level& level; - Chunks& chunks; - Lighting& lighting; + GlobalChunks& chunks; + Lighting* lighting; util::Clock randTickClock; util::Clock blocksTickClock; util::Clock worldTickClock; @@ -34,7 +35,7 @@ class BlocksController { FastRandom random {}; std::vector blockInteractionCallbacks; public: - BlocksController(const Level& level, Lighting& lighting, uint padding); + BlocksController(const Level& level, Lighting* lighting, uint padding); void updateSides(int x, int y, int z); void updateSides(int x, int y, int z, int w, int h, int d); diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 55ab262d..13c7ab86 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -11,6 +11,7 @@ #include "lighting/Lighting.hpp" #include "maths/voxmaths.hpp" #include "util/timeutil.hpp" +#include "objects/Player.hpp" #include "voxels/Block.hpp" #include "voxels/Chunk.hpp" #include "voxels/Chunks.hpp" @@ -24,8 +25,6 @@ const uint MIN_SURROUNDING = 9; ChunksController::ChunksController(Level& level, uint padding) : level(level), - chunks(*level.chunks), - lighting(std::make_unique(level.content, level.chunks.get())), padding(padding), generator(std::make_unique( level.content->generators.require(level.getWorld()->getGenerator()), @@ -36,15 +35,19 @@ ChunksController::ChunksController(Level& level, uint padding) ChunksController::~ChunksController() = default; void ChunksController::update( - int64_t maxDuration, int loadDistance, int centerX, int centerY -) { + int64_t maxDuration, int loadDistance, Player& player +) const { + const auto& position = player.getPosition(); + int centerX = floordiv(position.x); + int centerY = floordiv(position.z); + generator->update(centerX, centerY, loadDistance); int64_t mcstotal = 0; for (uint i = 0; i < MAX_WORK_PER_FRAME; i++) { timeutil::Timer timer; - if (loadVisible()) { + if (loadVisible(player)) { int64_t mcs = timer.stop(); if (mcstotal + mcs < maxDuration * 1000) { mcstotal += mcs; @@ -55,7 +58,8 @@ void ChunksController::update( } } -bool ChunksController::loadVisible() { +bool ChunksController::loadVisible(const Player& player) const { + const auto& chunks = *player.chunks; int sizeX = chunks.getWidth(); int sizeY = chunks.getHeight(); @@ -69,7 +73,7 @@ bool ChunksController::loadVisible() { auto& chunk = chunks.getChunks()[index]; if (chunk != nullptr) { if (chunk->flags.loaded && !chunk->flags.lighted) { - if (buildLights(chunk)) { + if (buildLights(player, chunk)) { return true; } } @@ -93,32 +97,35 @@ bool ChunksController::loadVisible() { } int offsetX = chunks.getOffsetX(); int offsetY = chunks.getOffsetY(); - createChunk(nearX + offsetX, nearZ + offsetY); + createChunk(player, nearX + offsetX, nearZ + offsetY); return true; } -bool ChunksController::buildLights(const std::shared_ptr& chunk) { +bool ChunksController::buildLights(const Player& player, const std::shared_ptr& chunk) const { int surrounding = 0; for (int oz = -1; oz <= 1; oz++) { for (int ox = -1; ox <= 1; ox++) { - if (chunks.getChunk(chunk->x + ox, chunk->z + oz)) surrounding++; + if (player.chunks->getChunk(chunk->x + ox, chunk->z + oz)) + surrounding++; } } if (surrounding == MIN_SURROUNDING) { - bool lightsCache = chunk->flags.loadedLights; - if (!lightsCache) { - lighting->buildSkyLight(chunk->x, chunk->z); + if (lighting) { + bool lightsCache = chunk->flags.loadedLights; + if (!lightsCache) { + lighting->buildSkyLight(chunk->x, chunk->z); + } + lighting->onChunkLoaded(chunk->x, chunk->z, !lightsCache); } - lighting->onChunkLoaded(chunk->x, chunk->z, !lightsCache); chunk->flags.lighted = true; return true; } return false; } -void ChunksController::createChunk(int x, int z) { +void ChunksController::createChunk(const Player& player, int x, int z) const { auto chunk = level.chunksStorage->create(x, z); - chunks.putChunk(chunk); + player.chunks->putChunk(chunk); auto& chunkFlags = chunk->flags; if (!chunkFlags.loaded) { diff --git a/src/logic/ChunksController.hpp b/src/logic/ChunksController.hpp index 71dada6f..6272fd01 100644 --- a/src/logic/ChunksController.hpp +++ b/src/logic/ChunksController.hpp @@ -7,6 +7,7 @@ class Level; class Chunk; class Chunks; +class Player; class Lighting; class WorldGenerator; @@ -14,14 +15,13 @@ class WorldGenerator; class ChunksController { private: Level& level; - Chunks& chunks; uint padding; std::unique_ptr generator; /// @brief Process one chunk: load it or calculate lights for it - bool loadVisible(); - bool buildLights(const std::shared_ptr& chunk); - void createChunk(int x, int y); + bool loadVisible(const Player& player) const; + bool buildLights(const Player& player, const std::shared_ptr& chunk) const; + void createChunk(const Player& player, int x, int y) const; public: std::unique_ptr lighting; @@ -29,11 +29,7 @@ public: ~ChunksController(); /// @param maxDuration milliseconds reserved for chunks loading - void update( - int64_t maxDuration, - int loadDistance, - int centerX, - int centerY); + void update(int64_t maxDuration, int loadDistance, Player& player) const; const WorldGenerator* getGenerator() const { return generator.get(); diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 2f363dad..409a81b4 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -10,21 +10,33 @@ #include "objects/Players.hpp" #include "objects/Player.hpp" #include "physics/Hitbox.hpp" +#include "voxels/Chunks.hpp" #include "scripting/scripting.hpp" +#include "lighting/Lighting.hpp" #include "settings.hpp" #include "world/Level.hpp" #include "world/World.hpp" static debug::Logger logger("level-control"); -LevelController::LevelController(Engine* engine, std::unique_ptr levelPtr) +LevelController::LevelController( + Engine* engine, std::unique_ptr levelPtr, Player* clientPlayer +) : settings(engine->getSettings()), level(std::move(levelPtr)), chunks(std::make_unique( *level, settings.chunks.padding.get() )) { + + if (clientPlayer) { + chunks->lighting = std::make_unique( + level->content, clientPlayer->chunks.get() + ); + } blocks = std::make_unique( - *level, *chunks->lighting, settings.chunks.padding.get() + *level, + chunks ? chunks->lighting.get() : nullptr, + settings.chunks.padding.get() ); scripting::on_world_load(this); } @@ -32,14 +44,15 @@ LevelController::LevelController(Engine* engine, std::unique_ptr levelPtr void LevelController::update(float delta, bool pause) { for (const auto& [uid, player] : *level->players) { glm::vec3 position = player->getPosition(); - level->loadMatrix( + player->chunks->configure( position.x, position.z, - settings.chunks.loadDistance.get() + settings.chunks.padding.get() * 2 + settings.chunks.loadDistance.get() + settings.chunks.padding.get() ); chunks->update( - settings.chunks.loadSpeed.get(), settings.chunks.loadDistance.get(), - floordiv(position.x, CHUNK_W), floordiv(position.z, CHUNK_D) + settings.chunks.loadSpeed.get(), + settings.chunks.loadDistance.get(), + *player ); } if (!pause) { diff --git a/src/logic/LevelController.hpp b/src/logic/LevelController.hpp index 6cfcf816..5363976b 100644 --- a/src/logic/LevelController.hpp +++ b/src/logic/LevelController.hpp @@ -18,7 +18,7 @@ class LevelController { std::unique_ptr blocks; std::unique_ptr chunks; public: - LevelController(Engine* engine, std::unique_ptr level); + LevelController(Engine* engine, std::unique_ptr level, Player* clientPlayer); /// @param delta time elapsed since the last update /// @param pause is world and player simulation paused diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index 827b42c1..6159a389 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -215,7 +215,7 @@ void PlayerController::onFootstep(const Hitbox& hitbox) { int x = std::floor(pos.x + half.x * offsetX); int y = std::floor(pos.y - half.y * 1.1f); int z = std::floor(pos.z + half.z * offsetZ); - auto vox = level->chunks->get(x, y, z); + auto vox = player->chunks->get(x, y, z); if (vox) { auto& def = level->content->getIndices()->blocks.require(vox->id); if (!def.obstacle) { @@ -274,7 +274,7 @@ void PlayerController::postUpdate(float delta, bool input, bool pause) { camControl.updateMouse(this->input); } player->postUpdate(); - camControl.update(this->input, pause ? 0.0f : delta, level->chunks.get()); + camControl.update(this->input, pause ? 0.0f : delta, player->chunks.get()); if (input) { updateInteraction(delta); } else { @@ -366,14 +366,14 @@ static void pick_block( voxel* PlayerController::updateSelection(float maxDistance) { auto indices = level->content->getIndices(); - auto chunks = level->chunks.get(); + auto& chunks = *player->chunks; auto camera = player->fpCamera.get(); auto& selection = player->selection; glm::vec3 end; glm::ivec3 iend; glm::ivec3 norm; - voxel* vox = chunks->rayCast( + voxel* vox = chunks.rayCast( camera->position, camera->front, maxDistance, end, norm, iend ); if (vox) { @@ -411,12 +411,12 @@ voxel* PlayerController::updateSelection(float maxDistance) { blockstate selectedState = vox->state; selection.vox = *vox; if (selectedState.segment) { - selection.position = chunks->seekOrigin( + selection.position = chunks.seekOrigin( iend, indices->blocks.require(selection.vox.id), selectedState ); - auto origin = chunks->get(selection.position); + auto origin = chunks.get(selection.position); if (origin && origin->id != vox->id) { - chunks->set(iend.x, iend.y, iend.z, 0, {}); + chunks.set(iend.x, iend.y, iend.z, 0, {}); return updateSelection(maxDistance); } } else { @@ -429,7 +429,7 @@ voxel* PlayerController::updateSelection(float maxDistance) { void PlayerController::processRightClick(const Block& def, const Block& target) { const auto& selection = player->selection; - auto chunks = level->chunks.get(); + auto& chunks = *player->chunks; auto camera = player->fpCamera.get(); blockstate state {}; @@ -458,16 +458,16 @@ void PlayerController::processRightClick(const Block& def, const Block& target) } } } - auto vox = chunks->get(coord); + auto vox = chunks.get(coord); if (vox == nullptr) { return; } - if (!chunks->checkReplaceability(def, state, coord)) { + if (!chunks.checkReplaceability(def, state, coord)) { return; } if (def.grounded) { const auto& vec = get_ground_direction(def, state.rotation); - if (!chunks->isSolidBlock( + if (!chunks.isSolidBlock( coord.x + vec.x, coord.y + vec.y, coord.z + vec.z )) { return; @@ -502,7 +502,7 @@ void PlayerController::updateEntityInteraction( void PlayerController::updateInteraction(float delta) { auto indices = level->content->getIndices(); - auto chunks = level->chunks.get(); + auto chunks = player->chunks.get(); const auto& selection = player->selection; if (interactionTimer > 0.0f) { diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 24b00124..180a959f 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -40,7 +40,9 @@ static int l_is_solid_at(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - return lua::pushboolean(L, blocks_agent::is_solid_at(*level->chunks, x, y, z)); + return lua::pushboolean( + L, blocks_agent::is_solid_at(*level->chunksStorage, x, y, z) + ); } static int l_count(lua::State* L) { diff --git a/src/logic/scripting/lua/libs/libentity.cpp b/src/logic/scripting/lua/libs/libentity.cpp index ba3bd349..bf547f95 100644 --- a/src/logic/scripting/lua/libs/libentity.cpp +++ b/src/logic/scripting/lua/libs/libentity.cpp @@ -9,6 +9,7 @@ #include "physics/Hitbox.hpp" #include "voxels/Chunks.hpp" #include "voxels/Block.hpp" +#include "voxels/blocks_agent.hpp" #include "window/Camera.hpp" using namespace scripting; @@ -149,8 +150,15 @@ static int l_raycast(lua::State* L) { blockid_t block = BLOCK_VOID; - if (auto voxel = level->chunks->rayCast( - start, dir, maxDistance, end, normal, iend, filteredBlocks + if (auto voxel = blocks_agent::raycast( + *level->chunksStorage, + start, + dir, + maxDistance, + end, + normal, + iend, + filteredBlocks )) { maxDistance = glm::distance(start, end); block = voxel->id; diff --git a/src/logic/scripting/lua/libs/libgeneration.cpp b/src/logic/scripting/lua/libs/libgeneration.cpp index 034f6597..00d767c2 100644 --- a/src/logic/scripting/lua/libs/libgeneration.cpp +++ b/src/logic/scripting/lua/libs/libgeneration.cpp @@ -28,7 +28,7 @@ static int l_create_fragment(lua::State* L) { bool saveEntities = lua::toboolean(L, 4); auto fragment = - VoxelFragment::create(level, pointA, pointB, crop, saveEntities); + VoxelFragment::create(*level, pointA, pointB, crop, saveEntities); return lua::newuserdata( L, std::shared_ptr(std::move(fragment)) ); diff --git a/src/logic/scripting/lua/libs/libhud.cpp b/src/logic/scripting/lua/libs/libhud.cpp index 7f5ec46d..f12ba0a7 100644 --- a/src/logic/scripting/lua/libs/libhud.cpp +++ b/src/logic/scripting/lua/libs/libhud.cpp @@ -14,6 +14,7 @@ #include "voxels/Block.hpp" #include "voxels/Chunks.hpp" #include "voxels/voxel.hpp" +#include "voxels/blocks_agent.hpp" #include "world/Level.hpp" #include "api_lua.hpp" @@ -59,7 +60,7 @@ static int l_open_block(lua::State* L) { auto z = lua::tointeger(L, 3); bool playerInventory = !lua::toboolean(L, 4); - auto vox = level->chunks->get(x, y, z); + auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); if (vox == nullptr) { throw std::runtime_error( "block does not exists at " + std::to_string(x) + " " + diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 58e2bdc4..16e81d56 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -125,7 +125,7 @@ static int l_get_generator(lua::State* L) { static int l_get_chunk_data(lua::State* L) { int x = (int)lua::tointeger(L, 1); int y = (int)lua::tointeger(L, 2); - const auto& chunk = level->chunks->getChunk(x, y); + const auto& chunk = level->chunksStorage->getChunk(x, y); if (chunk == nullptr) { lua::pushnil(L); return 0; @@ -181,8 +181,8 @@ static int l_set_chunk_data(lua::State* L) { if (lua::gettop(L) >= 4) { is_compressed = lua::toboolean(L, 4); } - auto chunk = level->chunks->getChunk(x, y); - if(chunk== nullptr){ + auto chunk = level->chunksStorage->getChunk(x, y); + if (chunk == nullptr) { return 0; } if (is_compressed) { @@ -217,22 +217,22 @@ static int l_set_chunk_data(lua::State* L) { chunk->flags.modified = true; lighting.onChunkLoaded(x, y, true); - chunk = level->chunks->getChunk(x - 1, y); + chunk = level->chunksStorage->getChunk(x - 1, y); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x - 1, y, true); } - chunk = level->chunks->getChunk(x + 1, y); + chunk = level->chunksStorage->getChunk(x + 1, y); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x + 1, y, true); } - chunk = level->chunks->getChunk(x, y - 1); + chunk = level->chunksStorage->getChunk(x, y - 1); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x, y - 1, true); } - chunk = level->chunks->getChunk(x, y + 1); + chunk = level->chunksStorage->getChunk(x, y + 1); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x, y + 1, true); diff --git a/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp b/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp index f123be6f..4a7af971 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp @@ -26,7 +26,7 @@ static int l_place(lua::State* L) { auto offset = tovec3(L, 2); int rotation = tointeger(L, 3) & 0b11; fragment->getFragment()->place( - *scripting::level->chunks, offset, rotation + *scripting::level->chunksStorage, offset, rotation ); } return 0; diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 79c0ba66..d2e5742d 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -459,7 +459,7 @@ void Entities::updatePhysics(float delta) { float vel = glm::length(prevVel); int substeps = static_cast(delta * vel * 20); substeps = std::min(100, std::max(2, substeps)); - physics->step(level->chunks.get(), &hitbox, delta, substeps, eid.uid); + physics->step(*level->chunksStorage, &hitbox, delta, substeps, eid.uid); hitbox.linearDamping = hitbox.grounded * 24; transform.setPos(hitbox.position); if (hitbox.grounded && !grounded) { diff --git a/src/objects/Player.cpp b/src/objects/Player.cpp index 0dd58f91..0a2106ed 100644 --- a/src/objects/Player.cpp +++ b/src/objects/Player.cpp @@ -205,12 +205,12 @@ void Player::attemptToFindSpawnpoint() { position.z + (rand() % 200 - 100) ); while (newpos.y > 0 && - !level->chunks->isObstacleBlock(newpos.x, newpos.y - 2, newpos.z)) { + !chunks->isObstacleBlock(newpos.x, newpos.y - 2, newpos.z)) { newpos.y--; } - voxel* headvox = level->chunks->get(newpos.x, newpos.y + 1, newpos.z); - if (level->chunks->isObstacleBlock(newpos.x, newpos.y, newpos.z) || + voxel* headvox = chunks->get(newpos.x, newpos.y + 1, newpos.z); + if (chunks->isObstacleBlock(newpos.x, newpos.y, newpos.z) || headvox == nullptr || headvox->id != 0) { return; } diff --git a/src/physics/PhysicsSolver.cpp b/src/physics/PhysicsSolver.cpp index 95954380..5f7824a1 100644 --- a/src/physics/PhysicsSolver.cpp +++ b/src/physics/PhysicsSolver.cpp @@ -3,7 +3,7 @@ #include "maths/aabb.hpp" #include "voxels/Block.hpp" -#include "voxels/Chunks.hpp" +#include "voxels/GlobalChunks.hpp" #include "voxels/voxel.hpp" #include @@ -18,7 +18,7 @@ PhysicsSolver::PhysicsSolver(glm::vec3 gravity) : gravity(gravity) { } void PhysicsSolver::step( - Chunks* chunks, + const GlobalChunks& chunks, Hitbox* hitbox, float delta, uint substeps, @@ -63,7 +63,7 @@ void PhysicsSolver::step( float x = (px-half.x+E) + ix * s; for (int iz = 0; iz <= (half.z-E)*2/s; iz++){ float z = (pos.z-half.z+E) + iz * s; - if (chunks->isObstacleAt(x,y,z)){ + if (chunks.isObstacleAt(x,y,z)){ hitbox->grounded = true; break; } @@ -77,7 +77,7 @@ void PhysicsSolver::step( float x = (pos.x-half.x+E) + ix * s; for (int iz = 0; iz <= (half.z-E)*2/s; iz++){ float z = (pz-half.z+E) + iz * s; - if (chunks->isObstacleAt(x,y,z)){ + if (chunks.isObstacleAt(x,y,z)){ hitbox->grounded = true; break; } @@ -119,8 +119,8 @@ void PhysicsSolver::step( } static float calc_step_height( - Chunks* chunks, - glm::vec3& pos, + const GlobalChunks& chunks, + const glm::vec3& pos, const glm::vec3& half, float stepHeight, float s @@ -130,7 +130,7 @@ static float calc_step_height( float x = (pos.x-half.x+E) + ix * s; for (int iz = 0; iz <= (half.z-E)*2/s; iz++) { float z = (pos.z-half.z+E) + iz * s; - if (chunks->isObstacleAt(x, pos.y+half.y+stepHeight, z)) { + if (chunks.isObstacleAt(x, pos.y+half.y+stepHeight, z)) { return 0.0f; } } @@ -141,7 +141,7 @@ static float calc_step_height( template static bool calc_collision_neg( - Chunks* chunks, + const GlobalChunks& chunks, glm::vec3& pos, glm::vec3& vel, const glm::vec3& half, @@ -159,7 +159,7 @@ static bool calc_collision_neg( coord[nz] = (pos[nz]-half[nz]+E) + iz * s; coord[nx] = (pos[nx]-half[nx]-E); - if (const auto aabb = chunks->isObstacleAt(coord.x, coord.y, coord.z)) { + if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z)) { vel[nx] = 0.0f; float newx = std::floor(coord[nx]) + aabb->max()[nx] + half[nx] + E; if (std::abs(newx-pos[nx]) <= MAX_FIX) { @@ -174,7 +174,7 @@ static bool calc_collision_neg( template static void calc_collision_pos( - Chunks* chunks, + const GlobalChunks& chunks, glm::vec3& pos, glm::vec3& vel, const glm::vec3& half, @@ -191,7 +191,7 @@ static void calc_collision_pos( for (int iz = 0; iz <= (half[nz]-E)*2/s; iz++) { coord[nz] = (pos[nz]-half[nz]+E) + iz * s; coord[nx] = (pos[nx]+half[nx]+E); - if (const auto aabb = chunks->isObstacleAt(coord.x, coord.y, coord.z)) { + if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z)) { vel[nx] = 0.0f; float newx = std::floor(coord[nx]) - half[nx] + aabb->min()[nx] - E; if (std::abs(newx-pos[nx]) <= MAX_FIX) { @@ -204,7 +204,7 @@ static void calc_collision_pos( } void PhysicsSolver::colisionCalc( - Chunks* chunks, + const GlobalChunks& chunks, Hitbox* hitbox, glm::vec3& vel, glm::vec3& pos, @@ -234,7 +234,7 @@ void PhysicsSolver::colisionCalc( for (int iz = 0; iz <= (half.z-E)*2/s; iz++) { float z = (pos.z-half.z+E) + iz * s; float y = (pos.y-half.y+E); - if ((aabb = chunks->isObstacleAt(x,y,z))){ + if ((aabb = chunks.isObstacleAt(x,y,z))){ vel.y = 0.0f; float newy = std::floor(y) + aabb->max().y + half.y; if (std::abs(newy-pos.y) <= MAX_FIX+stepHeight) { @@ -251,7 +251,7 @@ void PhysicsSolver::colisionCalc( for (int iz = 0; iz <= (half.z-E)*2/s; iz++) { float z = (pos.z-half.z+E) + iz * s; float y = (pos.y+half.y+E); - if ((aabb = chunks->isObstacleAt(x,y,z))){ + if ((aabb = chunks.isObstacleAt(x,y,z))){ vel.y = 0.0f; float newy = std::floor(y) - half.y + aabb->min().y - E; if (std::abs(newy-pos.y) <= MAX_FIX) { diff --git a/src/physics/PhysicsSolver.hpp b/src/physics/PhysicsSolver.hpp index 35f8158e..5fa154fc 100644 --- a/src/physics/PhysicsSolver.hpp +++ b/src/physics/PhysicsSolver.hpp @@ -9,7 +9,7 @@ #include class Block; -class Chunks; +class GlobalChunks; struct Sensor; class PhysicsSolver { @@ -18,14 +18,14 @@ class PhysicsSolver { public: PhysicsSolver(glm::vec3 gravity); void step( - Chunks* chunks, + const GlobalChunks& chunks, Hitbox* hitbox, float delta, uint substeps, entityid_t entity ); void colisionCalc( - Chunks* chunks, + const GlobalChunks& chunks, Hitbox* hitbox, glm::vec3& vel, glm::vec3& pos, diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 91b05d69..37eae563 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -38,6 +38,14 @@ Chunks::Chunks( }); } +void Chunks::configure(int32_t x, int32_t z, uint32_t radius) { + setCenter(x, z); + uint32_t diameter = radius * 2LL; + if (getWidth() != diameter) { + resize(diameter, diameter); + } +} + voxel* Chunks::get(int32_t x, int32_t y, int32_t z) const { return blocks_agent::get(*this, x, y, z); } diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index c1c75e9c..86e02255 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -50,6 +50,8 @@ public: ); ~Chunks() = default; + void configure(int32_t x, int32_t z, uint32_t radius); + bool putChunk(const std::shared_ptr& chunk); Chunk* getChunk(int32_t x, int32_t z) const; diff --git a/src/voxels/GlobalChunks.cpp b/src/voxels/GlobalChunks.cpp index 98a2d40a..a85be528 100644 --- a/src/voxels/GlobalChunks.cpp +++ b/src/voxels/GlobalChunks.cpp @@ -10,6 +10,7 @@ #include "lighting/Lightmap.hpp" #include "maths/voxmaths.hpp" #include "objects/Entities.hpp" +#include "voxels/blocks_agent.hpp" #include "typedefs.hpp" #include "world/Level.hpp" #include "world/World.hpp" @@ -198,3 +199,7 @@ void GlobalChunks::saveAll() { void GlobalChunks::putChunk(std::shared_ptr chunk) { chunksMap[keyfrom(chunk->x, chunk->z)] = std::move(chunk); } + +const AABB* GlobalChunks::isObstacleAt(float x, float y, float z) const { + return blocks_agent::is_obstacle_at(*this, x, y, z); +} diff --git a/src/voxels/GlobalChunks.hpp b/src/voxels/GlobalChunks.hpp index afbdf67c..b4bb45e9 100644 --- a/src/voxels/GlobalChunks.hpp +++ b/src/voxels/GlobalChunks.hpp @@ -11,6 +11,7 @@ class Chunk; class Level; +struct AABB; class ContentIndices; class GlobalChunks { @@ -51,6 +52,8 @@ public: void putChunk(std::shared_ptr chunk); + const AABB* isObstacleAt(float x, float y, float z) const; + inline Chunk* getChunk(int cx, int cz) const { const auto& found = chunksMap.find(keyfrom(cx, cz)); if (found == chunksMap.end()) { diff --git a/src/voxels/blocks_agent.cpp b/src/voxels/blocks_agent.cpp index ab39ea29..a23fa9ef 100644 --- a/src/voxels/blocks_agent.cpp +++ b/src/voxels/blocks_agent.cpp @@ -265,3 +265,105 @@ voxel* blocks_agent::raycast( ) { return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter); } + +// reduce nesting on next modification +// 25.06.2024: not now +// 11.11.2024: not now +template +inline void get_voxels_impl( + const Storage& chunks, VoxelsVolume* volume, bool backlight +) { + const auto& blocks = chunks.getContentIndices().blocks; + voxel* voxels = volume->getVoxels(); + light_t* lights = volume->getLights(); + int x = volume->getX(); + int y = volume->getY(); + int z = volume->getZ(); + + int w = volume->getW(); + int h = volume->getH(); + int d = volume->getD(); + + int scx = floordiv(x); + int scz = floordiv(z); + + int ecx = floordiv(x + w); + int ecz = floordiv(z + d); + + int cw = ecx - scx + 1; + int cd = ecz - scz + 1; + + // cw*cd chunks will be scanned + for (int cz = scz; cz < scz + cd; cz++) { + for (int cx = scx; cx < scx + cw; cx++) { + const auto chunk = get_chunk(chunks, cx, cz); + if (chunk == nullptr) { + // no chunk loaded -> filling with BLOCK_VOID + for (int ly = y; ly < y + h; ly++) { + for (int lz = std::max(z, cz * CHUNK_D); + lz < std::min(z + d, (cz + 1) * CHUNK_D); + lz++) { + for (int lx = std::max(x, cx * CHUNK_W); + lx < std::min(x + w, (cx + 1) * CHUNK_W); + lx++) { + uint idx = vox_index(lx - x, ly - y, lz - z, w, d); + voxels[idx].id = BLOCK_VOID; + lights[idx] = 0; + } + } + } + } else { + const voxel* cvoxels = chunk->voxels; + const light_t* clights = chunk->lightmap.getLights(); + for (int ly = y; ly < y + h; ly++) { + for (int lz = std::max(z, cz * CHUNK_D); + lz < std::min(z + d, (cz + 1) * CHUNK_D); + lz++) { + for (int lx = std::max(x, cx * CHUNK_W); + lx < std::min(x + w, (cx + 1) * CHUNK_W); + lx++) { + uint vidx = vox_index(lx - x, ly - y, lz - z, w, d); + uint cidx = vox_index( + lx - cx * CHUNK_W, + ly, + lz - cz * CHUNK_D, + CHUNK_W, + CHUNK_D + ); + voxels[vidx] = cvoxels[cidx]; + light_t light = clights[cidx]; + if (backlight) { + const auto block = blocks.get(voxels[vidx].id); + if (block && block->lightPassing) { + light = Lightmap::combine( + std::min(15, + Lightmap::extract(light, 0) + 1), + std::min(15, + Lightmap::extract(light, 1) + 1), + std::min(15, + Lightmap::extract(light, 2) + 1), + std::min(15, + static_cast(Lightmap::extract(light, 3))) + ); + } + } + lights[vidx] = light; + } + } + } + } + } + } +} + +void blocks_agent::get_voxels( + const Chunks& chunks, VoxelsVolume* volume, bool backlight +) { + get_voxels_impl(chunks, volume, backlight); +} + +void blocks_agent::get_voxels( + const GlobalChunks& chunks, VoxelsVolume* volume, bool backlight +) { + get_voxels_impl(chunks, volume, backlight); +} diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index e2c4266e..72cdd5ab 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -6,6 +6,7 @@ #include "Block.hpp" #include "Chunk.hpp" #include "Chunks.hpp" +#include "VoxelsVolume.hpp" #include "GlobalChunks.hpp" #include "constants.hpp" #include "typedefs.hpp" @@ -17,6 +18,8 @@ #include #include +struct AABB; + namespace blocks_agent { /// @brief Get specified chunk. @@ -432,4 +435,42 @@ voxel* raycast( std::set filter ); +void get_voxels(const Chunks& chunks, VoxelsVolume* volume, bool backlight=false); + +void get_voxels(const GlobalChunks& chunks, VoxelsVolume* volume, bool backlight=false); + +template +inline const AABB* is_obstacle_at(const Storage& chunks, float x, float y, float z) { + int ix = std::floor(x); + int iy = std::floor(y); + int iz = std::floor(z); + voxel* v = get(chunks, ix, iy, iz); + if (v == nullptr) { + if (iy >= CHUNK_H) { + return nullptr; + } else { + static const AABB empty; + return ∅ + } + } + const auto& def = chunks.getContentIndices().blocks.require(v->id); + if (def.obstacle) { + glm::ivec3 offset {}; + if (v->state.segment) { + glm::ivec3 point(ix, iy, iz); + offset = seek_origin(chunks, point, def, v->state) - point; + } + const auto& boxes = + def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes; + for (const auto& hitbox : boxes) { + if (hitbox.contains( + {x - ix - offset.x, y - iy - offset.y, z - iz - offset.z} + )) { + return &hitbox; + } + } + } + return nullptr; +} + } // blocks_agent diff --git a/src/world/Level.cpp b/src/world/Level.cpp index c289579f..3625f62e 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -4,7 +4,6 @@ #include "data/dv_util.hpp" #include "items/Inventories.hpp" #include "items/Inventory.hpp" -#include "lighting/Lighting.hpp" #include "objects/Entities.hpp" #include "objects/Player.hpp" #include "objects/Players.hpp" @@ -12,7 +11,6 @@ #include "physics/PhysicsSolver.hpp" #include "settings.hpp" #include "voxels/Chunk.hpp" -#include "voxels/Chunks.hpp" #include "voxels/GlobalChunks.hpp" #include "window/Camera.hpp" #include "LevelEvents.hpp" @@ -60,31 +58,11 @@ Level::Level( events->listen(LevelEventType::EVT_CHUNK_HIDDEN, [this](LevelEventType, Chunk* chunk) { chunksStorage->decref(chunk); }); - - uint matrixSize = - (settings.chunks.loadDistance.get() + settings.chunks.padding.get()) * - 2; - chunks = std::make_unique( - matrixSize, matrixSize, 0, 0, events.get(), content->getIndices() - ); - inventories = std::make_unique(*this); } Level::~Level() = default; -void Level::loadMatrix(int32_t x, int32_t z, uint32_t radius) { - chunks->setCenter(x, z); - uint32_t diameter = std::min( - radius * 2LL, - (settings.chunks.loadDistance.get() + settings.chunks.padding.get()) * - 2LL - ); - if (chunks->getWidth() != diameter) { - chunks->resize(diameter, diameter); - } -} - World* Level::getWorld() { return world.get(); } diff --git a/src/world/Level.hpp b/src/world/Level.hpp index b30bde3b..9994c01d 100644 --- a/src/world/Level.hpp +++ b/src/world/Level.hpp @@ -9,11 +9,9 @@ class Content; class World; -class Chunks; class Entities; class Inventories; class LevelEvents; -class Lighting; class PhysicsSolver; class GlobalChunks; class Camera; @@ -27,7 +25,6 @@ class Level { public: const Content* const content; - std::unique_ptr chunks; std::unique_ptr chunksStorage; std::unique_ptr inventories; @@ -44,8 +41,6 @@ public: ); ~Level(); - void loadMatrix(int32_t x, int32_t z, uint32_t radius); - World* getWorld(); const World* getWorld() const; diff --git a/src/world/generator/VoxelFragment.cpp b/src/world/generator/VoxelFragment.cpp index 2d7ab923..af92dc7e 100644 --- a/src/world/generator/VoxelFragment.cpp +++ b/src/world/generator/VoxelFragment.cpp @@ -10,11 +10,12 @@ #include "voxels/Block.hpp" #include "voxels/GlobalChunks.hpp" #include "voxels/VoxelsVolume.hpp" +#include "voxels/blocks_agent.hpp" #include "world/Level.hpp" #include "core_defs.hpp" std::unique_ptr VoxelFragment::create( - Level* level, + const Level& level, const glm::ivec3& a, const glm::ivec3& b, bool crop, @@ -26,7 +27,7 @@ std::unique_ptr VoxelFragment::create( if (crop) { VoxelsVolume volume(size.x, size.y, size.z); volume.setPosition(start.x, start.y, start.z); - level->chunks->getVoxels(&volume); + blocks_agent::get_voxels(*level.chunksStorage, &volume); auto end = start + size; @@ -51,14 +52,14 @@ std::unique_ptr VoxelFragment::create( VoxelsVolume volume(size.x, size.y, size.z); volume.setPosition(start.x, start.y, start.z); - level->chunks->getVoxels(&volume); + blocks_agent::get_voxels(*level.chunksStorage, &volume); auto volVoxels = volume.getVoxels(); std::vector voxels(size.x * size.y * size.z); std::vector blockNames {CORE_AIR}; std::unordered_map blocksRegistered {{0, 0}}; - auto contentIndices = level->content->getIndices(); + auto contentIndices = level.content->getIndices(); for (size_t i = 0 ; i < voxels.size(); i++) { blockid_t id = volVoxels[i].id; blockid_t index; @@ -170,7 +171,7 @@ void VoxelFragment::prepare(const Content& content) { } void VoxelFragment::place( - Chunks& chunks, const glm::ivec3& offset, ubyte rotation + GlobalChunks& chunks, const glm::ivec3& offset, ubyte rotation ) { auto& structVoxels = getRuntimeVoxels(); for (int y = 0; y < size.y; y++) { @@ -185,7 +186,9 @@ void VoxelFragment::place( const auto& structVoxel = structVoxels[vox_index(x, y, z, size.x, size.z)]; if (structVoxel.id) { - chunks.set(sx, sy, sz, structVoxel.id, structVoxel.state); + blocks_agent::set( + chunks, sx, sy, sz, structVoxel.id, structVoxel.state + ); } } } diff --git a/src/world/generator/VoxelFragment.hpp b/src/world/generator/VoxelFragment.hpp index 701cac0e..6efa1998 100644 --- a/src/world/generator/VoxelFragment.hpp +++ b/src/world/generator/VoxelFragment.hpp @@ -10,7 +10,7 @@ inline constexpr int STRUCTURE_FORMAT_VERSION = 1; class Level; class Content; -class Chunks; +class GlobalChunks; class VoxelFragment : public Serializable { glm::ivec3 size; @@ -45,13 +45,13 @@ public: /// @brief Place fragment to the world /// @param offset target location /// @param rotation rotation index - void place(Chunks& chunks, const glm::ivec3& offset, ubyte rotation); + void place(GlobalChunks& chunks, const glm::ivec3& offset, ubyte rotation); /// @brief Create structure copy rotated 90 deg. clockwise std::unique_ptr rotated(const Content& content) const; static std::unique_ptr create( - Level* level, + const Level& level, const glm::ivec3& a, const glm::ivec3& b, bool crop, From 49dd866c22fd8d167efecc9cb575d16afd4d3531 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 20:43:42 +0300 Subject: [PATCH 12/31] update Chunks::configure --- src/voxels/Chunks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 37eae563..960b8f6c 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -40,7 +40,7 @@ Chunks::Chunks( void Chunks::configure(int32_t x, int32_t z, uint32_t radius) { setCenter(x, z); - uint32_t diameter = radius * 2LL; + uint32_t diameter = radius * 2LL + 1; if (getWidth() != diameter) { resize(diameter, diameter); } From b1fc666448e66b63a06fe7320305fa9f1efa035d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 20:56:25 +0300 Subject: [PATCH 13/31] fix --- src/voxels/Chunks.cpp | 2 +- src/world/generator/WorldGenerator.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 960b8f6c..37eae563 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -40,7 +40,7 @@ Chunks::Chunks( void Chunks::configure(int32_t x, int32_t z, uint32_t radius) { setCenter(x, z); - uint32_t diameter = radius * 2LL + 1; + uint32_t diameter = radius * 2LL; if (getWidth() != diameter) { resize(diameter, diameter); } diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index 281ac411..ef3c3f7f 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -354,8 +354,8 @@ void WorldGenerator::generateHeightmap( void WorldGenerator::update(int centerX, int centerY, int loadDistance) { surroundMap.setCenter(centerX, centerY); - // 1 is safety padding preventing ChunksController rounding problem - surroundMap.resize(loadDistance + 1); + // 2 is safety padding preventing ChunksController rounding problem + surroundMap.resize(loadDistance + 2); surroundMap.setCenter(centerX, centerY); } From 09e641fd91e193b5f61525a76cb15c8d1765a85d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 21:18:05 +0300 Subject: [PATCH 14/31] advanced chunks loading test --- dev/tests/example.lua | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 0435b6cd..99d4e6f7 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,13 +1,18 @@ test.set_setting("chunks.load-distance", 3) -test.set_setting("chunks.load-speed", 16) +test.set_setting("chunks.load-speed", 32) test.reconfig_packs({"base"}, {}) test.new_world("demo", "2019", "core:default") local pid = player.create("Xerxes") assert(player.get_name(pid) == "Xerxes") -test.sleep_until(function() return block.get(0, 0, 0) >= 0 end, 1000) -print(world.count_chunks()) -block.destruct(0, 0, 0, pid) -assert(block.get(0, 0, 0) == 0) +for i=1,100 do + if i % 10 == 0 then + print(tostring(i).." % done") + print("chunks loaded", world.count_chunks()) + end + player.set_pos(pid, math.random() * 100 - 50, 100, math.random() * 100 - 50) + test.tick() +end + test.close_world(true) From 70370904dbdbdd615856adc9a0715b2a1570a15a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 22:38:08 +0300 Subject: [PATCH 15/31] two players test --- dev/tests/example.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 99d4e6f7..89302aa4 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,17 +1,20 @@ test.set_setting("chunks.load-distance", 3) -test.set_setting("chunks.load-speed", 32) +test.set_setting("chunks.load-speed", 1) test.reconfig_packs({"base"}, {}) test.new_world("demo", "2019", "core:default") -local pid = player.create("Xerxes") +local pid1 = player.create("Xerxes") assert(player.get_name(pid) == "Xerxes") +local pid2 = player.create("Undefined") + for i=1,100 do if i % 10 == 0 then print(tostring(i).." % done") print("chunks loaded", world.count_chunks()) end - player.set_pos(pid, math.random() * 100 - 50, 100, math.random() * 100 - 50) + player.set_pos(pid1, math.random() * 100 - 50, 100, math.random() * 100 - 50) + player.set_pos(pid2, math.random() * 200 - 100, 100, math.random() * 200 - 100) test.tick() end From 48d94036fdedad3c89ebbbfa342fac5e39aeb114 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 17 Dec 2024 23:57:00 +0300 Subject: [PATCH 16/31] rename Level.chunksStorage to Level.chunks --- src/frontend/debug_panel.cpp | 2 +- src/frontend/hud.cpp | 2 +- src/logic/BlocksController.cpp | 2 +- src/logic/ChunksController.cpp | 2 +- src/logic/scripting/lua/libs/libblock.cpp | 53 +++++++++---------- src/logic/scripting/lua/libs/libentity.cpp | 2 +- src/logic/scripting/lua/libs/libhud.cpp | 2 +- src/logic/scripting/lua/libs/libworld.cpp | 14 ++--- .../lua/usertypes/lua_type_voxelfragment.cpp | 2 +- src/objects/Entities.cpp | 2 +- src/world/Level.cpp | 6 +-- src/world/Level.hpp | 2 +- src/world/World.cpp | 2 +- src/world/generator/VoxelFragment.cpp | 4 +- 14 files changed, 46 insertions(+), 51 deletions(-) diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index 3470cdb1..d1ad6231 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -96,7 +96,7 @@ std::shared_ptr create_debug_panel( std::to_wstring(ParticlesRenderer::aliveEmitters); })); panel->add(create_label([&]() { - return L"chunks: "+std::to_wstring(level.chunksStorage->size())+ + return L"chunks: "+std::to_wstring(level.chunks->size())+ L" visible: "+std::to_wstring(ChunksRenderer::visibleChunks); })); panel->add(create_label([&]() { diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index e6b1ecb9..d078b1c6 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -296,7 +296,7 @@ void Hud::updateWorldGenDebugVisualization() { data[(flippedZ * width + x) * 4 + 1] = chunks.getChunk(ax + ox, az + oz) ? 255 : 0; data[(flippedZ * width + x) * 4 + 0] = - level.chunksStorage->fetch(ax + ox, az + oz) ? 255 : 0; + level.chunks->fetch(ax + ox, az + oz) ? 255 : 0; if (ax < 0 || az < 0 || ax >= areaWidth || az >= areaHeight) { diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp index 3c0b64c0..0a70910f 100644 --- a/src/logic/BlocksController.cpp +++ b/src/logic/BlocksController.cpp @@ -21,7 +21,7 @@ BlocksController::BlocksController(const Level& level, Lighting* lighting, uint padding) : level(level), - chunks(*level.chunksStorage), + chunks(*level.chunks), lighting(lighting), randTickClock(20, 3), blocksTickClock(20, 1), diff --git a/src/logic/ChunksController.cpp b/src/logic/ChunksController.cpp index 13c7ab86..a8041f8d 100644 --- a/src/logic/ChunksController.cpp +++ b/src/logic/ChunksController.cpp @@ -124,7 +124,7 @@ bool ChunksController::buildLights(const Player& player, const std::shared_ptrcreate(x, z); + auto chunk = level.chunks->create(x, z); player.chunks->putChunk(chunk); auto& chunkFlags = chunk->flags; diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 180a959f..8d1e0897 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -41,7 +41,7 @@ static int l_is_solid_at(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); return lua::pushboolean( - L, blocks_agent::is_solid_at(*level->chunksStorage, x, y, z) + L, blocks_agent::is_solid_at(*level->chunks, x, y, z) ); } @@ -72,7 +72,7 @@ static int l_is_segment(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - const auto& vox = blocks_agent::require(*level->chunksStorage, x, y, z); + const auto& vox = blocks_agent::require(*level->chunks, x, y, z); return lua::pushboolean(L, vox.state.segment); } @@ -80,13 +80,10 @@ static int l_seek_origin(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - const auto& vox = blocks_agent::require(*level->chunksStorage, x, y, z); + const auto& vox = blocks_agent::require(*level->chunks, x, y, z); auto& def = indices->blocks.require(vox.id); return lua::pushivec_stack( - L, - blocks_agent::seek_origin( - *level->chunksStorage, {x, y, z}, def, vox.state - ) + L, blocks_agent::seek_origin(*level->chunks, {x, y, z}, def, vox.state) ); } @@ -102,10 +99,10 @@ static int l_set(lua::State* L) { } int cx = floordiv(x); int cz = floordiv(z); - if (!blocks_agent::get_chunk(*level->chunksStorage, cx, cz)) { + if (!blocks_agent::get_chunk(*level->chunks, cx, cz)) { return 0; } - blocks_agent::set(*level->chunksStorage, x, y, z, id, int2blockstate(state)); + blocks_agent::set(*level->chunks, x, y, z, id, int2blockstate(state)); auto chunksController = controller->getChunksController(); if (chunksController == nullptr) { @@ -123,7 +120,7 @@ static int l_get(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); int id = vox == nullptr ? -1 : vox->id; return lua::pushinteger(L, id); } @@ -132,7 +129,7 @@ static int l_get_x(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, glm::ivec3(1, 0, 0)); } @@ -149,7 +146,7 @@ static int l_get_y(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, glm::ivec3(0, 1, 0)); } @@ -166,7 +163,7 @@ static int l_get_z(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); if (vox == nullptr) { return lua::pushivec_stack(L, glm::ivec3(0, 0, 1)); } @@ -183,7 +180,7 @@ static int l_get_rotation(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); int rotation = vox == nullptr ? 0 : vox->state.rotation; return lua::pushinteger(L, rotation); } @@ -193,7 +190,7 @@ static int l_set_rotation(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); auto value = lua::tointeger(L, 4); - blocks_agent::set_rotation(*level->chunksStorage, x, y, z, value); + blocks_agent::set_rotation(*level->chunks, x, y, z, value); return 0; } @@ -201,7 +198,7 @@ static int l_get_states(lua::State* L) { auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); int states = vox == nullptr ? 0 : blockstate2int(vox->state); return lua::pushinteger(L, states); } @@ -216,7 +213,7 @@ static int l_set_states(lua::State* L) { } int cx = floordiv(x); int cz = floordiv(z); - auto chunk = blocks_agent::get_chunk(*level->chunksStorage, cx, cz); + auto chunk = blocks_agent::get_chunk(*level->chunks, cx, cz); if (chunk == nullptr) { return 0; } @@ -235,18 +232,16 @@ static int l_get_user_bits(lua::State* L) { auto offset = lua::tointeger(L, 4) + VOXEL_USER_BITS_OFFSET; auto bits = lua::tointeger(L, 5); - auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); if (vox == nullptr) { return lua::pushinteger(L, 0); } const auto& def = content->getIndices()->blocks.require(vox->id); if (def.rt.extended) { auto origin = blocks_agent::seek_origin( - *level->chunksStorage, {x, y, z}, def, vox->state - ); - vox = blocks_agent::get( - *level->chunksStorage, origin.x, origin.y, origin.z + *level->chunks, {x, y, z}, def, vox->state ); + vox = blocks_agent::get(*level->chunks, origin.x, origin.y, origin.z); if (vox == nullptr) { return lua::pushinteger(L, 0); } @@ -257,7 +252,7 @@ static int l_get_user_bits(lua::State* L) { } static int l_set_user_bits(lua::State* L) { - auto& chunks = *level->chunksStorage; + auto& chunks = *level->chunks; auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); @@ -294,7 +289,7 @@ static int l_is_replaceable_at(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); return lua::pushboolean( - L, blocks_agent::is_replaceable_at(*level->chunksStorage, x, y, z) + L, blocks_agent::is_replaceable_at(*level->chunks, x, y, z) ); } @@ -369,7 +364,7 @@ static int l_place(lua::State* L) { if (static_cast(id) >= indices->blocks.count()) { return 0; } - if (!blocks_agent::get(*level->chunksStorage, x, y, z)) { + if (!blocks_agent::get(*level->chunks, x, y, z)) { return 0; } const auto def = level->content->getIndices()->blocks.get(id); @@ -390,7 +385,7 @@ static int l_destruct(lua::State* L) { auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); auto playerid = lua::gettop(L) >= 4 ? lua::tointeger(L, 4) : -1; - auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); if (vox == nullptr) { return 0; } @@ -425,7 +420,7 @@ static int l_raycast(lua::State* L) { glm::ivec3 normal; glm::ivec3 iend; if (auto voxel = blocks_agent::raycast( - *level->chunksStorage, + *level->chunks, start, dir, maxDistance, @@ -528,7 +523,7 @@ static int l_get_field(lua::State* L) { } auto cx = floordiv(x, CHUNK_W); auto cz = floordiv(z, CHUNK_D); - auto chunk = blocks_agent::get_chunk(*level->chunksStorage, cx, cz); + auto chunk = blocks_agent::get_chunk(*level->chunks, cx, cz); if (chunk == nullptr || y < 0 || y >= CHUNK_H) { return 0; } @@ -599,7 +594,7 @@ static int l_set_field(lua::State* L) { auto cz = floordiv(z, CHUNK_D); auto lx = x - cx * CHUNK_W; auto lz = z - cz * CHUNK_W; - auto chunk = blocks_agent::get_chunk(*level->chunksStorage, cx, cz); + auto chunk = blocks_agent::get_chunk(*level->chunks, cx, cz); if (chunk == nullptr || y < 0 || y >= CHUNK_H) { return 0; } diff --git a/src/logic/scripting/lua/libs/libentity.cpp b/src/logic/scripting/lua/libs/libentity.cpp index bf547f95..ce394fb5 100644 --- a/src/logic/scripting/lua/libs/libentity.cpp +++ b/src/logic/scripting/lua/libs/libentity.cpp @@ -151,7 +151,7 @@ static int l_raycast(lua::State* L) { blockid_t block = BLOCK_VOID; if (auto voxel = blocks_agent::raycast( - *level->chunksStorage, + *level->chunks, start, dir, maxDistance, diff --git a/src/logic/scripting/lua/libs/libhud.cpp b/src/logic/scripting/lua/libs/libhud.cpp index f12ba0a7..90cb57d4 100644 --- a/src/logic/scripting/lua/libs/libhud.cpp +++ b/src/logic/scripting/lua/libs/libhud.cpp @@ -60,7 +60,7 @@ static int l_open_block(lua::State* L) { auto z = lua::tointeger(L, 3); bool playerInventory = !lua::toboolean(L, 4); - auto vox = blocks_agent::get(*level->chunksStorage, x, y, z); + auto vox = blocks_agent::get(*level->chunks, x, y, z); if (vox == nullptr) { throw std::runtime_error( "block does not exists at " + std::to_string(x) + " " + diff --git a/src/logic/scripting/lua/libs/libworld.cpp b/src/logic/scripting/lua/libs/libworld.cpp index 16e81d56..e13d8c0f 100644 --- a/src/logic/scripting/lua/libs/libworld.cpp +++ b/src/logic/scripting/lua/libs/libworld.cpp @@ -125,7 +125,7 @@ static int l_get_generator(lua::State* L) { static int l_get_chunk_data(lua::State* L) { int x = (int)lua::tointeger(L, 1); int y = (int)lua::tointeger(L, 2); - const auto& chunk = level->chunksStorage->getChunk(x, y); + const auto& chunk = level->chunks->getChunk(x, y); if (chunk == nullptr) { lua::pushnil(L); return 0; @@ -181,7 +181,7 @@ static int l_set_chunk_data(lua::State* L) { if (lua::gettop(L) >= 4) { is_compressed = lua::toboolean(L, 4); } - auto chunk = level->chunksStorage->getChunk(x, y); + auto chunk = level->chunks->getChunk(x, y); if (chunk == nullptr) { return 0; } @@ -217,22 +217,22 @@ static int l_set_chunk_data(lua::State* L) { chunk->flags.modified = true; lighting.onChunkLoaded(x, y, true); - chunk = level->chunksStorage->getChunk(x - 1, y); + chunk = level->chunks->getChunk(x - 1, y); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x - 1, y, true); } - chunk = level->chunksStorage->getChunk(x + 1, y); + chunk = level->chunks->getChunk(x + 1, y); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x + 1, y, true); } - chunk = level->chunksStorage->getChunk(x, y - 1); + chunk = level->chunks->getChunk(x, y - 1); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x, y - 1, true); } - chunk = level->chunksStorage->getChunk(x, y + 1); + chunk = level->chunks->getChunk(x, y + 1); if (chunk != nullptr) { chunk->flags.modified = true; lighting.onChunkLoaded(x, y + 1, true); @@ -245,7 +245,7 @@ static int l_count_chunks(lua::State* L) { if (level == nullptr) { return 0; } - return lua::pushinteger(L, level->chunksStorage->size()); + return lua::pushinteger(L, level->chunks->size()); } const luaL_Reg worldlib[] = { diff --git a/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp b/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp index 4a7af971..f123be6f 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_voxelfragment.cpp @@ -26,7 +26,7 @@ static int l_place(lua::State* L) { auto offset = tovec3(L, 2); int rotation = tointeger(L, 3) & 0b11; fragment->getFragment()->place( - *scripting::level->chunksStorage, offset, rotation + *scripting::level->chunks, offset, rotation ); } return 0; diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index d2e5742d..1a27243f 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -459,7 +459,7 @@ void Entities::updatePhysics(float delta) { float vel = glm::length(prevVel); int substeps = static_cast(delta * vel * 20); substeps = std::min(100, std::max(2, substeps)); - physics->step(*level->chunksStorage, &hitbox, delta, substeps, eid.uid); + physics->step(*level->chunks, &hitbox, delta, substeps, eid.uid); hitbox.linearDamping = hitbox.grounded * 24; transform.setPos(hitbox.position); if (hitbox.grounded && !grounded) { diff --git a/src/world/Level.cpp b/src/world/Level.cpp index 3625f62e..28a8dab5 100644 --- a/src/world/Level.cpp +++ b/src/world/Level.cpp @@ -24,7 +24,7 @@ Level::Level( : settings(settings), world(std::move(worldPtr)), content(content), - chunksStorage(std::make_unique(this)), + chunks(std::make_unique(this)), physics(std::make_unique(glm::vec3(0, -22.6f, 0))), events(std::make_unique()), entities(std::make_unique(this)), @@ -53,10 +53,10 @@ Level::Level( } events->listen(LevelEventType::EVT_CHUNK_SHOWN, [this](LevelEventType, Chunk* chunk) { - chunksStorage->incref(chunk); + chunks->incref(chunk); }); events->listen(LevelEventType::EVT_CHUNK_HIDDEN, [this](LevelEventType, Chunk* chunk) { - chunksStorage->decref(chunk); + chunks->decref(chunk); }); inventories = std::make_unique(*this); } diff --git a/src/world/Level.hpp b/src/world/Level.hpp index 9994c01d..63a724c9 100644 --- a/src/world/Level.hpp +++ b/src/world/Level.hpp @@ -25,7 +25,7 @@ class Level { public: const Content* const content; - std::unique_ptr chunksStorage; + std::unique_ptr chunks; std::unique_ptr inventories; std::unique_ptr physics; diff --git a/src/world/World.cpp b/src/world/World.cpp index eb8c33bf..d8ef2ed5 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -66,7 +66,7 @@ void World::writeResources(const Content* content) { void World::write(Level* level) { const Content* content = level->content; - level->chunksStorage->saveAll(); + level->chunks->saveAll(); info.nextEntityId = level->entities->peekNextID(); wfile->write(this, content); diff --git a/src/world/generator/VoxelFragment.cpp b/src/world/generator/VoxelFragment.cpp index af92dc7e..9a739a2a 100644 --- a/src/world/generator/VoxelFragment.cpp +++ b/src/world/generator/VoxelFragment.cpp @@ -27,7 +27,7 @@ std::unique_ptr VoxelFragment::create( if (crop) { VoxelsVolume volume(size.x, size.y, size.z); volume.setPosition(start.x, start.y, start.z); - blocks_agent::get_voxels(*level.chunksStorage, &volume); + blocks_agent::get_voxels(*level.chunks, &volume); auto end = start + size; @@ -52,7 +52,7 @@ std::unique_ptr VoxelFragment::create( VoxelsVolume volume(size.x, size.y, size.z); volume.setPosition(start.x, start.y, start.z); - blocks_agent::get_voxels(*level.chunksStorage, &volume); + blocks_agent::get_voxels(*level.chunks, &volume); auto volVoxels = volume.getVoxels(); std::vector voxels(size.x * size.y * size.z); From fa1646a9bd676a3849d45040b098dd311043db2b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 18 Dec 2024 00:48:29 +0300 Subject: [PATCH 17/31] add '--script', '--version' command line arguments --- src/{TestMainloop.cpp => ServerMainloop.cpp} | 36 +++++++++++++++----- src/{TestMainloop.hpp => ServerMainloop.hpp} | 6 ++-- src/engine.cpp | 4 +-- src/engine.hpp | 3 +- src/logic/scripting/lua/lua_engine.cpp | 2 +- src/util/command_line.cpp | 15 ++++++-- src/world/Level.hpp | 1 - 7 files changed, 48 insertions(+), 19 deletions(-) rename src/{TestMainloop.cpp => ServerMainloop.cpp} (53%) rename src/{TestMainloop.hpp => ServerMainloop.hpp} (73%) diff --git a/src/TestMainloop.cpp b/src/ServerMainloop.cpp similarity index 53% rename from src/TestMainloop.cpp rename to src/ServerMainloop.cpp index 100c0b51..be013e7b 100644 --- a/src/TestMainloop.cpp +++ b/src/ServerMainloop.cpp @@ -1,4 +1,4 @@ -#include "TestMainloop.hpp" +#include "ServerMainloop.hpp" #include "logic/scripting/scripting.hpp" #include "logic/LevelController.hpp" @@ -6,22 +6,27 @@ #include "debug/Logger.hpp" #include "world/Level.hpp" #include "world/World.hpp" +#include "util/platform.hpp" #include "engine.hpp" +#include + +using namespace std::chrono; + static debug::Logger logger("mainloop"); inline constexpr int TPS = 20; -TestMainloop::TestMainloop(Engine& engine) : engine(engine) { +ServerMainloop::ServerMainloop(Engine& engine) : engine(engine) { } -TestMainloop::~TestMainloop() = default; +ServerMainloop::~ServerMainloop() = default; -void TestMainloop::run() { +void ServerMainloop::run() { const auto& coreParams = engine.getCoreParameters(); auto& time = engine.getTime(); - if (coreParams.testFile.empty()) { + if (coreParams.scriptFile.empty()) { logger.info() << "nothing to do"; return; } @@ -29,21 +34,34 @@ void TestMainloop::run() { setLevel(std::move(level)); }); - logger.info() << "starting test " << coreParams.testFile; - auto process = scripting::start_coroutine(coreParams.testFile); + logger.info() << "starting test " << coreParams.scriptFile; + auto process = scripting::start_coroutine(coreParams.scriptFile); + + double targetDelta = 1.0f / static_cast(TPS); + double delta = targetDelta; + auto begin = steady_clock::now(); while (process->isActive()) { - time.step(1.0f / static_cast(TPS)); + time.step(delta); process->update(); if (controller) { float delta = time.getDelta(); controller->getLevel()->getWorld()->updateTimers(delta); controller->update(glm::min(delta, 0.2f), false); } + + if (!coreParams.testMode) { + auto end = steady_clock::now(); + platform::sleep(targetDelta * 1000 - + duration_cast(end - begin).count() / 1000); + end = steady_clock::now(); + delta = duration_cast(end - begin).count() / 1e6; + begin = end; + } } logger.info() << "test finished"; } -void TestMainloop::setLevel(std::unique_ptr level) { +void ServerMainloop::setLevel(std::unique_ptr level) { if (level == nullptr) { controller->onWorldQuit(); engine.getPaths()->setCurrentWorldFolder(fs::path()); diff --git a/src/TestMainloop.hpp b/src/ServerMainloop.hpp similarity index 73% rename from src/TestMainloop.hpp rename to src/ServerMainloop.hpp index 06ffae25..28cfbd9d 100644 --- a/src/TestMainloop.hpp +++ b/src/ServerMainloop.hpp @@ -6,12 +6,12 @@ class Level; class LevelController; class Engine; -class TestMainloop { +class ServerMainloop { Engine& engine; std::unique_ptr controller; public: - TestMainloop(Engine& engine); - ~TestMainloop(); + ServerMainloop(Engine& engine); + ~ServerMainloop(); void run(); diff --git a/src/engine.cpp b/src/engine.cpp index 3c7d4bb5..46fd17c8 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -36,7 +36,7 @@ #include "window/Window.hpp" #include "world/Level.hpp" #include "Mainloop.hpp" -#include "TestMainloop.hpp" +#include "ServerMainloop.hpp" #include #include @@ -173,7 +173,7 @@ void Engine::saveScreenshot() { void Engine::run() { if (params.headless) { - TestMainloop(*this).run(); + ServerMainloop(*this).run(); } else { Mainloop(*this).run(); } diff --git a/src/engine.hpp b/src/engine.hpp index 63a78084..88a60865 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -48,9 +48,10 @@ public: struct CoreParameters { bool headless = false; + bool testMode = false; std::filesystem::path resFolder {"res"}; std::filesystem::path userFolder {"."}; - std::filesystem::path testFile; + std::filesystem::path scriptFile; }; class Engine : public util::ObjectsKeeper { diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 0a4e2259..b34b87a0 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -121,7 +121,7 @@ void lua::initialize(const EnginePaths& paths, const CoreParameters& params) { main_thread = create_state( paths, params.headless ? StateType::TEST : StateType::BASE ); - lua::pushstring(main_thread, params.testFile.stem().u8string()); + lua::pushstring(main_thread, params.scriptFile.stem().u8string()); lua::setglobal(main_thread, "__VC_TEST_NAME"); } diff --git a/src/util/command_line.cpp b/src/util/command_line.cpp index 768c6260..25d7b554 100644 --- a/src/util/command_line.cpp +++ b/src/util/command_line.cpp @@ -19,19 +19,30 @@ static bool perform_keyword( auto token = reader.next(); params.userFolder = fs::u8path(token); } else if (keyword == "--help" || keyword == "-h") { - std::cout << "VoxelEngine command-line arguments:\n"; + std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n"; + std::cout << "command-line arguments:\n"; std::cout << " --help - show help\n"; + std::cout << " --version - print engine version\n"; std::cout << " --res - set resources directory\n"; std::cout << " --dir - set userfiles directory\n"; std::cout << " --headless - run in headless mode\n"; std::cout << " --test - test script file\n"; + std::cout << " --script - main script file\n"; std::cout << std::endl; return false; + } else if (keyword == "--version") { + std::cout << ENGINE_VERSION_STRING << std::endl; + return false; } else if (keyword == "--headless") { params.headless = true; } else if (keyword == "--test") { auto token = reader.next(); - params.testFile = fs::u8path(token); + params.testMode = true; + params.scriptFile = fs::u8path(token); + } else if (keyword == "--script") { + auto token = reader.next(); + params.testMode = false; + params.scriptFile = fs::u8path(token); } else { throw std::runtime_error("unknown argument " + keyword); } diff --git a/src/world/Level.hpp b/src/world/Level.hpp index 63a724c9..121fecc1 100644 --- a/src/world/Level.hpp +++ b/src/world/Level.hpp @@ -27,7 +27,6 @@ public: std::unique_ptr chunks; std::unique_ptr inventories; - std::unique_ptr physics; std::unique_ptr events; std::unique_ptr entities; From 5e55e20ec4dd605fbf8f487132c36c4b5d951cd9 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 18 Dec 2024 02:25:28 +0300 Subject: [PATCH 18/31] add test.quit, test.reopen_world --- dev/tests/example.lua | 2 ++ res/scripts/stdlib.lua | 29 +++++++++++++++++++++++- src/ServerMainloop.cpp | 5 ++++ src/engine.cpp | 11 +++++++++ src/engine.hpp | 5 ++++ src/logic/scripting/lua/libs/libcore.cpp | 5 +++- 6 files changed, 55 insertions(+), 2 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 89302aa4..1ebe6b06 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,6 +1,8 @@ test.set_setting("chunks.load-distance", 3) test.set_setting("chunks.load-speed", 1) +test.quit() + test.reconfig_packs({"base"}, {}) test.new_world("demo", "2019", "core:default") local pid1 = player.create("Xerxes") diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 0298a2b9..9ddda13c 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -9,16 +9,41 @@ function sleep(timesec) end end +function tb_frame_tostring(frame) + local s = frame.short_src + if frame.what ~= "C" then + s = s .. ":" .. tostring(frame.currentline) + end + if frame.what == "main" then + s = s .. ": in main chunk" + elseif frame.name then + s = s .. ": in function " .. utf8.escape(frame.name) + end + return s +end + if test then test.sleep = sleep test.name = __VC_TEST_NAME test.new_world = core.new_world test.open_world = core.open_world test.close_world = core.close_world + test.reopen_world = core.reopen_world test.reconfig_packs = core.reconfig_packs test.set_setting = core.set_setting test.tick = coroutine.yield + function test.quit() + local tb = debug.get_traceback(1) + local s = "test.quit() traceback:" + for i, frame in ipairs(tb) do + s = s .. "\n\t"..tb_frame_tostring(frame) + end + debug.log(s) + core.quit() + coroutine.yield() + end + function test.sleep_until(predicate, max_ticks) max_ticks = max_ticks or 1e9 local ticks = 0 @@ -382,7 +407,9 @@ end function __vc_stop_coroutine(id) local co = __vc_coroutines[id] if co then - coroutine.close(co) + if coroutine.close then + coroutine.close(co) + end __vc_coroutines[id] = nil end end diff --git a/src/ServerMainloop.cpp b/src/ServerMainloop.cpp index be013e7b..dd50f089 100644 --- a/src/ServerMainloop.cpp +++ b/src/ServerMainloop.cpp @@ -41,6 +41,11 @@ void ServerMainloop::run() { double delta = targetDelta; auto begin = steady_clock::now(); while (process->isActive()) { + if (engine.isQuitSignal()) { + process->terminate(); + logger.info() << "script has been terminated due to quit signal"; + break; + } time.step(delta); process->update(); if (controller) { diff --git a/src/engine.cpp b/src/engine.cpp index 46fd17c8..095d744c 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -461,6 +461,17 @@ void Engine::onWorldClosed() { levelConsumer(nullptr); } +void Engine::quit() { + quitSignal = true; + if (!isHeadless()) { + Window::setShouldClose(true); + } +} + +bool Engine::isQuitSignal() const { + return quitSignal; +} + gui::GUI* Engine::getGUI() { return gui.get(); } diff --git a/src/engine.hpp b/src/engine.hpp index 88a60865..39792624 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -74,6 +74,7 @@ class Engine : public util::ObjectsKeeper { std::unique_ptr gui; Time time; consumer> levelConsumer; + bool quitSignal = false; void loadControls(); void loadSettings(); @@ -138,6 +139,10 @@ public: void onWorldOpen(std::unique_ptr level); void onWorldClosed(); + void quit(); + + bool isQuitSignal() const; + /// @brief Get current Content instance const Content* getContent() const; diff --git a/src/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp index d2d0c80f..7b121be8 100644 --- a/src/logic/scripting/lua/libs/libcore.cpp +++ b/src/logic/scripting/lua/libs/libcore.cpp @@ -56,6 +56,9 @@ static int l_open_world(lua::State* L) { /// @brief Reopen world static int l_reopen_world(lua::State*) { auto controller = engine->getController(); + if (level == nullptr) { + throw std::runtime_error("no world open"); + } controller->reopenWorld(level->getWorld()); return 0; } @@ -229,7 +232,7 @@ static int l_open_folder(lua::State* L) { /// @brief Quit the game static int l_quit(lua::State*) { - Window::setShouldClose(true); + engine->quit(); return 0; } From d745a34657073de66e5c70aef8baabbfdfccf8f3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 18 Dec 2024 04:13:33 +0300 Subject: [PATCH 19/31] refactor --- dev/tests/example.lua | 2 - src/Mainloop.cpp | 6 +- src/ServerMainloop.cpp | 2 +- src/engine.cpp | 12 +- src/engine.hpp | 2 +- src/files/engine_paths.cpp | 4 +- src/files/engine_paths.hpp | 4 +- src/frontend/debug_panel.cpp | 10 +- src/frontend/hud.cpp | 26 ++-- src/frontend/hud.hpp | 6 +- src/frontend/menu.cpp | 54 +++++---- src/frontend/menu.hpp | 10 +- src/frontend/screens/LevelScreen.cpp | 30 ++--- src/frontend/screens/LevelScreen.hpp | 2 +- src/frontend/screens/MenuScreen.cpp | 8 +- src/frontend/screens/MenuScreen.hpp | 2 +- src/frontend/screens/Screen.cpp | 2 +- src/frontend/screens/Screen.hpp | 4 +- src/graphics/render/WorldRenderer.cpp | 67 +++++------ src/graphics/render/WorldRenderer.hpp | 6 +- src/graphics/ui/gui_xml.cpp | 2 +- src/logic/EngineController.cpp | 111 +++++++++--------- src/logic/EngineController.hpp | 4 +- src/logic/scripting/lua/libs/libcore.cpp | 2 +- src/logic/scripting/lua/libs/libfile.cpp | 4 +- .../scripting/lua/libs/libgeneration.cpp | 8 +- src/logic/scripting/lua/libs/libinput.cpp | 2 +- src/logic/scripting/lua/libs/libworld.cpp | 6 +- .../lua/usertypes/lua_type_heightmap.cpp | 4 +- src/logic/scripting/scripting.cpp | 6 +- src/logic/scripting/scripting_hud.cpp | 2 +- .../scripting/scripting_world_generation.cpp | 2 +- src/window/Window.cpp | 2 +- 33 files changed, 209 insertions(+), 205 deletions(-) diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 1ebe6b06..89302aa4 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,8 +1,6 @@ test.set_setting("chunks.load-distance", 3) test.set_setting("chunks.load-speed", 1) -test.quit() - test.reconfig_packs({"base"}, {}) test.new_world("demo", "2019", "core:default") local pid1 = player.create("Xerxes") diff --git a/src/Mainloop.cpp b/src/Mainloop.cpp index eae4c501..a12434ef 100644 --- a/src/Mainloop.cpp +++ b/src/Mainloop.cpp @@ -20,14 +20,14 @@ void Mainloop::run() { // destroy LevelScreen and run quit callbacks engine.setScreen(nullptr); // create and go to menu screen - engine.setScreen(std::make_shared(&engine)); + engine.setScreen(std::make_shared(engine)); } else { - engine.setScreen(std::make_shared(&engine, std::move(level))); + engine.setScreen(std::make_shared(engine, std::move(level))); } }); logger.info() << "starting menu screen"; - engine.setScreen(std::make_shared(&engine)); + engine.setScreen(std::make_shared(engine)); logger.info() << "main loop started"; while (!Window::isShouldClose()){ diff --git a/src/ServerMainloop.cpp b/src/ServerMainloop.cpp index dd50f089..3d05f55b 100644 --- a/src/ServerMainloop.cpp +++ b/src/ServerMainloop.cpp @@ -69,7 +69,7 @@ void ServerMainloop::run() { void ServerMainloop::setLevel(std::unique_ptr level) { if (level == nullptr) { controller->onWorldQuit(); - engine.getPaths()->setCurrentWorldFolder(fs::path()); + engine.getPaths().setCurrentWorldFolder(fs::path()); controller = nullptr; } else { controller = std::make_unique( diff --git a/src/engine.cpp b/src/engine.cpp index 095d744c..2f8466d6 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -87,7 +87,7 @@ Engine::Engine(CoreParameters coreParameters) auto resdir = paths.getResourcesFolder(); - controller = std::make_unique(this); + controller = std::make_unique(*this); if (!params.headless) { if (Window::initialize(&settings.display)){ throw initialize_error("could not initialize window"); @@ -101,7 +101,7 @@ Engine::Engine(CoreParameters coreParameters) gui = std::make_unique(); if (ENGINE_DEBUG_BUILD) { - menus::create_version_label(this); + menus::create_version_label(*this); } } audio::initialize(settings.audio.enabled.get() && !params.headless); @@ -118,7 +118,7 @@ Engine::Engine(CoreParameters coreParameters) paths.getResourcesFolder() )); } - keepAlive(settings.ui.language.observe([=](auto lang) { + keepAlive(settings.ui.language.observe([this](auto lang) { setLanguage(lang); }, true)); @@ -447,7 +447,7 @@ void Engine::setScreen(std::shared_ptr screen) { void Engine::setLanguage(std::string locale) { langs::setup(paths.getResourcesFolder(), std::move(locale), contentPacks); if (gui) { - gui->getMenu()->setPageLoader(menus::create_page_loader(this)); + gui->getMenu()->setPageLoader(menus::create_page_loader(*this)); } } @@ -502,8 +502,8 @@ std::vector& Engine::getBasePacks() { return basePacks; } -EnginePaths* Engine::getPaths() { - return &paths; +EnginePaths& Engine::getPaths() { + return paths; } ResPaths* Engine::getResPaths() { diff --git a/src/engine.hpp b/src/engine.hpp index 39792624..f10fb3a0 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -131,7 +131,7 @@ public: EngineSettings& getSettings(); /// @brief Get engine filesystem paths source - EnginePaths* getPaths(); + EnginePaths& getPaths(); /// @brief Get engine resource paths controller ResPaths* getResPaths(); diff --git a/src/files/engine_paths.cpp b/src/files/engine_paths.cpp index 26857331..0ecea7b8 100644 --- a/src/files/engine_paths.cpp +++ b/src/files/engine_paths.cpp @@ -132,7 +132,7 @@ std::filesystem::path EnginePaths::getSettingsFile() const { return userFilesFolder / SETTINGS_FILE; } -std::vector EnginePaths::scanForWorlds() { +std::vector EnginePaths::scanForWorlds() const { std::vector folders; auto folder = getWorldsFolder(); @@ -189,7 +189,7 @@ std::tuple EnginePaths::parsePath(std::string_view pat std::filesystem::path EnginePaths::resolve( const std::string& path, bool throwErr -) { +) const { auto [prefix, filename] = EnginePaths::parsePath(path); if (prefix.empty()) { throw files_access_error("no entry point specified"); diff --git a/src/files/engine_paths.hpp b/src/files/engine_paths.hpp index c5289243..61be1757 100644 --- a/src/files/engine_paths.hpp +++ b/src/files/engine_paths.hpp @@ -39,9 +39,9 @@ public: void setContentPacks(std::vector* contentPacks); - std::vector scanForWorlds(); + std::vector scanForWorlds() const; - std::filesystem::path resolve(const std::string& path, bool throwErr = true); + std::filesystem::path resolve(const std::string& path, bool throwErr = true) const; static std::tuple parsePath(std::string_view view); diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp index d1ad6231..a5607803 100644 --- a/src/frontend/debug_panel.cpp +++ b/src/frontend/debug_panel.cpp @@ -42,7 +42,7 @@ static std::shared_ptr