move Chunks from Level to Player

This commit is contained in:
MihailRis
2024-12-17 19:40:00 +03:00
parent b7664b4188
commit 1c18c02092
40 changed files with 450 additions and 220 deletions
+60 -27
View File
@@ -1,5 +1,7 @@
#include "BlocksController.hpp"
#include <set>
#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<uint64_t> 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<CHUNK_W>(x), floordiv<CHUNK_D>(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<CHUNK_W>(x), floordiv<CHUNK_D>(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<CHUNK_W>(x), floordiv<CHUNK_D>(z)
);
if (chunk == nullptr) {
throw std::runtime_error("block does not exists");
}
+5 -4
View File
@@ -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<on_block_interaction> 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);
+23 -16
View File
@@ -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<Lighting>(level.content, level.chunks.get())),
padding(padding),
generator(std::make_unique<WorldGenerator>(
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<CHUNK_W>(position.x);
int centerY = floordiv<CHUNK_D>(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>& chunk) {
bool ChunksController::buildLights(const Player& player, const std::shared_ptr<Chunk>& 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) {
+5 -9
View File
@@ -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<WorldGenerator> generator;
/// @brief Process one chunk: load it or calculate lights for it
bool loadVisible();
bool buildLights(const std::shared_ptr<Chunk>& chunk);
void createChunk(int x, int y);
bool loadVisible(const Player& player) const;
bool buildLights(const Player& player, const std::shared_ptr<Chunk>& chunk) const;
void createChunk(const Player& player, int x, int y) const;
public:
std::unique_ptr<Lighting> 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();
+19 -6
View File
@@ -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<Level> levelPtr)
LevelController::LevelController(
Engine* engine, std::unique_ptr<Level> levelPtr, Player* clientPlayer
)
: settings(engine->getSettings()),
level(std::move(levelPtr)),
chunks(std::make_unique<ChunksController>(
*level, settings.chunks.padding.get()
)) {
if (clientPlayer) {
chunks->lighting = std::make_unique<Lighting>(
level->content, clientPlayer->chunks.get()
);
}
blocks = std::make_unique<BlocksController>(
*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<Level> 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) {
+1 -1
View File
@@ -18,7 +18,7 @@ class LevelController {
std::unique_ptr<BlocksController> blocks;
std::unique_ptr<ChunksController> chunks;
public:
LevelController(Engine* engine, std::unique_ptr<Level> level);
LevelController(Engine* engine, std::unique_ptr<Level> level, Player* clientPlayer);
/// @param delta time elapsed since the last update
/// @param pause is world and player simulation paused
+12 -12
View File
@@ -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) {
+3 -1
View File
@@ -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) {
+10 -2
View File
@@ -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;
@@ -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<lua::LuaVoxelFragment>(
L, std::shared_ptr<VoxelFragment>(std::move(fragment))
);
+2 -1
View File
@@ -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) + " " +
+7 -7
View File
@@ -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);
@@ -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;