refactor(gameplay): add base classes Chunk and World for shared logic

This commit is contained in:
2026-07-25 15:47:46 +08:00
parent df3ac5b5db
commit f9bf2f4b89
11 changed files with 202 additions and 67 deletions

View File

@@ -0,0 +1,27 @@
#pragma once
#include "Cubed/gameplay/chunk_pos.hpp"
#include <glm/glm.hpp>
#include <tuple>
namespace Cubed {
class Chunk {
public:
Chunk() = default;
Chunk(const Chunk&) = delete;
Chunk(Chunk&&) = delete;
Chunk& operator=(const Chunk&) = delete;
Chunk& operator=(Chunk&&) = delete;
virtual ~Chunk() = default;
static int index(int x, int y, int z);
static int index(const glm::vec3& pos);
static std::tuple<int, int, int> world_to_block(int world_x, int world_y,
int world_z, int chunk_x,
int chunk_z);
static std::tuple<int, int, int> world_to_block(const glm::ivec3& block_pos,
ChunkPos chunk_pos);
static std::tuple<int, int, int> block_to_world(int x, int y, int z,
int chunk_x, int chunk_z);
static std::tuple<int, int, int> block_to_world(const glm::ivec3& block_pos,
ChunkPos chunk_pos);
};
} // namespace Cubed

View File

@@ -2,6 +2,7 @@
#include "Cubed/constants.hpp" #include "Cubed/constants.hpp"
#include "Cubed/gameplay/biome.hpp" #include "Cubed/gameplay/biome.hpp"
#include "Cubed/gameplay/block.hpp" #include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/chunk.hpp"
#include "Cubed/gameplay/chunk_pos.hpp" #include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/vertex_data.hpp" #include "Cubed/gameplay/vertex_data.hpp"
#include "world/chunk_data.pb.h" #include "world/chunk_data.pb.h"
@@ -26,7 +27,7 @@ struct ChunkRenderSnapshot {
glm::vec3 center; glm::vec3 center;
glm::vec3 half_extents; glm::vec3 half_extents;
}; };
class ClientChunk { class ClientChunk : public Chunk {
public: public:
ClientChunk(ClientWorld& world); ClientChunk(ClientWorld& world);
~ClientChunk(); ~ClientChunk();
@@ -35,17 +36,6 @@ public:
ClientChunk(ClientChunk&&) noexcept; ClientChunk(ClientChunk&&) noexcept;
ClientChunk& operator=(ClientChunk&&) noexcept; ClientChunk& operator=(ClientChunk&&) noexcept;
static int index(int x, int y, int z);
static int index(const glm::vec3& pos);
static std::tuple<int, int, int> world_to_block(int world_x, int world_y,
int world_z, int chunk_x,
int chunk_z);
static std::tuple<int, int, int> world_to_block(const glm::ivec3& block_pos,
ChunkPos chunk_pos);
static std::tuple<int, int, int> block_to_world(int x, int y, int z,
int chunk_x, int chunk_z);
static std::tuple<int, int, int> block_to_world(const glm::ivec3& block_pos,
ChunkPos chunk_pos);
BiomeType get_biome() const; BiomeType get_biome() const;
ChunkPos get_chunk_pos() const; ChunkPos get_chunk_pos() const;
const std::vector<BlockType>& get_chunk_blocks() const; const std::vector<BlockType>& get_chunk_blocks() const;

View File

@@ -8,6 +8,7 @@
#include "Cubed/gameplay/client_player.hpp" #include "Cubed/gameplay/client_player.hpp"
#include "Cubed/gameplay/game_time.hpp" #include "Cubed/gameplay/game_time.hpp"
#include "Cubed/gameplay/network_client.hpp" #include "Cubed/gameplay/network_client.hpp"
#include "Cubed/gameplay/world.hpp"
#include "Cubed/input/event.hpp" #include "Cubed/input/event.hpp"
#include "Cubed/tools/cubed_random.hpp" #include "Cubed/tools/cubed_random.hpp"
#include "Cubed/tools/priority_thread_pool.hpp" #include "Cubed/tools/priority_thread_pool.hpp"
@@ -30,7 +31,7 @@ struct PlayerRenderData {
float angle; float angle;
}; };
class WorldScene; class WorldScene;
class ClientWorld { class ClientWorld : public World {
public: public:
ClientWorld(const ClientWorld&) = delete; ClientWorld(const ClientWorld&) = delete;
ClientWorld(ClientWorld&&) = delete; ClientWorld(ClientWorld&&) = delete;
@@ -46,10 +47,10 @@ public:
const std::optional<LookBlock>& get_look_block_pos() const; const std::optional<LookBlock>& get_look_block_pos() const;
ClientPlayer& get_player(); ClientPlayer& get_player();
const ClientPlayer& get_player() const; const ClientPlayer& get_player() const;
int get_block(const glm::ivec3& block_pos) const; int get_block(const glm::ivec3& block_pos) const override;
bool is_solid(const glm::ivec3& block_pos) const; bool is_solid(const glm::ivec3& block_pos) const override;
bool can_pass_block(const glm::ivec3& block_pos) const; bool can_pass_block(const glm::ivec3& block_pos) const override;
BlockType get_block_tpye(const glm::ivec3& block_pos) const; BlockType get_block_tpye(const glm::ivec3& block_pos) const override;
void rebuild_world(); void rebuild_world();

View File

@@ -2,6 +2,7 @@
#include "Cubed/constants.hpp" #include "Cubed/constants.hpp"
#include "Cubed/gameplay/biome.hpp" #include "Cubed/gameplay/biome.hpp"
#include "Cubed/gameplay/block.hpp" #include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/chunk.hpp"
#include "Cubed/gameplay/chunk_generator.hpp" #include "Cubed/gameplay/chunk_generator.hpp"
#include "Cubed/gameplay/chunk_pos.hpp" #include "Cubed/gameplay/chunk_pos.hpp"
@@ -11,7 +12,7 @@
#include <tuple> #include <tuple>
namespace Cubed { namespace Cubed {
class ServerWorld; class ServerWorld;
class ServerChunk { class ServerChunk : public Chunk {
public: public:
ServerChunk(ServerWorld& world, ChunkPos chunk_pos, ServerChunk(ServerWorld& world, ChunkPos chunk_pos,
bool temp_chunk = false); bool temp_chunk = false);

View File

@@ -8,6 +8,7 @@
#include "Cubed/gameplay/river_worm.hpp" #include "Cubed/gameplay/river_worm.hpp"
#include "Cubed/gameplay/server_chunk.hpp" #include "Cubed/gameplay/server_chunk.hpp"
#include "Cubed/gameplay/server_player.hpp" #include "Cubed/gameplay/server_player.hpp"
#include "Cubed/gameplay/world.hpp"
#include "Cubed/tools/priority_thread_pool.hpp" #include "Cubed/tools/priority_thread_pool.hpp"
#include "Cubed/tools/recent_queue.hpp" #include "Cubed/tools/recent_queue.hpp"
#include "Cubed/tools/sensitive_filter.hpp" #include "Cubed/tools/sensitive_filter.hpp"
@@ -25,7 +26,7 @@
#include <vector> #include <vector>
namespace Cubed { namespace Cubed {
class Session; class Session;
class ServerWorld { class ServerWorld : public World {
public: public:
enum class ThreadPoolKind { NET, GEN }; enum class ThreadPoolKind { NET, GEN };
ServerWorld(Config& config); ServerWorld(Config& config);
@@ -85,6 +86,12 @@ public:
void handle_chat_message(ChatMsg& msg); void handle_chat_message(ChatMsg& msg);
void handle_voice_message(VoiceMsg& msg); void handle_voice_message(VoiceMsg& msg);
int chunk_size() const; int chunk_size() const;
int get_block(const glm::ivec3& block_pos) const override;
bool is_solid(const glm::ivec3& block_pos) const override;
bool can_pass_block(const glm::ivec3& block_pos) const override;
BlockType get_block_tpye(const glm::ivec3& block_pos) const override;
template <typename Fn> template <typename Fn>
void register_timer(std::string_view id, TickType threshold, Fn&& f) { void register_timer(std::string_view id, TickType threshold, Fn&& f) {
m_timers.emplace(std::piecewise_construct, m_timers.emplace(std::piecewise_construct,
@@ -119,7 +126,7 @@ private:
using PlayerUUIDMap = tbb::concurrent_hash_map<std::string, std::string>; using PlayerUUIDMap = tbb::concurrent_hash_map<std::string, std::string>;
using chunk_acc = ChunkHashMap::accessor; using chunk_acc = ChunkHashMap::accessor;
using chunk_caac = ChunkHashMap::const_accessor; using chunk_cacc = ChunkHashMap::const_accessor;
using uuid_acc = PlayerUUIDMap::accessor; using uuid_acc = PlayerUUIDMap::accessor;
using uuid_cacc = PlayerUUIDMap::const_accessor; using uuid_cacc = PlayerUUIDMap::const_accessor;

View File

@@ -0,0 +1,20 @@
#pragma once
#include "Cubed/gameplay/block.hpp"
#include <glm/glm.hpp>
namespace Cubed {
class World {
public:
World() = default;
World(const World&) = delete;
World(World&&) = delete;
World& operator=(const World&) = delete;
World& operator=(World&&) = delete;
virtual ~World() = default;
virtual int get_block(const glm::ivec3& block_pos) const = 0;
virtual bool is_solid(const glm::ivec3& block_pos) const = 0;
virtual bool can_pass_block(const glm::ivec3& block_pos) const = 0;
virtual BlockType get_block_tpye(const glm::ivec3& block_pos) const = 0;
};
} // namespace Cubed

View File

@@ -95,4 +95,5 @@ target_sources(${PROJECT_NAME}
tools/model_loader.cpp tools/model_loader.cpp
render/model_manager.cpp render/model_manager.cpp
render/model_renderer.cpp render/model_renderer.cpp
gameplay/chunk.cpp
) )

49
src/gameplay/chunk.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include "Cubed/gameplay/chunk.hpp"
#include "Cubed/tools/cubed_assert.hpp"
namespace Cubed {
int Chunk::index(int x, int y, int z) {
ASSERT(!(x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y ||
z >= CHUNK_SIZE));
if ((x * WORLD_SIZE_Y + y) * CHUNK_SIZE + z < 0 ||
(x * WORLD_SIZE_Y + y) * CHUNK_SIZE + z >=
CHUNK_SIZE * CHUNK_SIZE * WORLD_SIZE_Y) {
Logger::error("block pos x {} y {} z {} range error", x, y, z);
ASSERT(0);
}
return (x * WORLD_SIZE_Y + y) * CHUNK_SIZE + z;
}
int Chunk::index(const glm::vec3& pos) {
return Chunk::index(pos.x, pos.y, pos.z);
}
std::tuple<int, int, int> Chunk::world_to_block(int world_x, int world_y,
int world_z, int chunk_x,
int chunk_z) {
int x, y, z;
y = world_y;
x = world_x - chunk_x * CHUNK_SIZE;
z = world_z - chunk_z * CHUNK_SIZE;
return {x, y, z};
}
std::tuple<int, int, int> Chunk::world_to_block(const glm::ivec3& block_pos,
ChunkPos chunk_pos) {
return world_to_block(block_pos.x, block_pos.y, block_pos.z, chunk_pos.x,
chunk_pos.z);
}
std::tuple<int, int, int> Chunk::block_to_world(int x, int y, int z,
int chunk_x, int chunk_z) {
int world_x = x + chunk_x * CHUNK_SIZE;
int world_z = z + chunk_z * CHUNK_SIZE;
int world_y = y;
return {world_x, world_y, world_z};
}
std::tuple<int, int, int> Chunk::block_to_world(const glm::ivec3& block_pos,
ChunkPos chunk_pos) {
return block_to_world(block_pos.x, block_pos.y, block_pos.z, chunk_pos.x,
chunk_pos.z);
}
} // namespace Cubed

View File

@@ -1,7 +1,5 @@
#include "Cubed/gameplay/client_chunk.hpp" #include "Cubed/gameplay/client_chunk.hpp"
#include "Cubed/tools/cubed_assert.hpp"
namespace Cubed { namespace Cubed {
using OptionalBlockVectorArray = using OptionalBlockVectorArray =
std::array<std::optional<std::vector<BlockType>>, 4>; std::array<std::optional<std::vector<BlockType>>, 4>;
@@ -128,49 +126,6 @@ ClientChunk& ClientChunk::operator=(ClientChunk&& other) noexcept {
return *this; return *this;
} }
int ClientChunk::index(int x, int y, int z) {
ASSERT(!(x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y ||
z >= CHUNK_SIZE));
if ((x * WORLD_SIZE_Y + y) * CHUNK_SIZE + z < 0 ||
(x * WORLD_SIZE_Y + y) * CHUNK_SIZE + z >=
CHUNK_SIZE * CHUNK_SIZE * WORLD_SIZE_Y) {
Logger::error("block pos x {} y {} z {} range error", x, y, z);
ASSERT(0);
}
return (x * WORLD_SIZE_Y + y) * CHUNK_SIZE + z;
}
int ClientChunk::index(const glm::vec3& pos) {
return ClientChunk::index(pos.x, pos.y, pos.z);
}
std::tuple<int, int, int> ClientChunk::world_to_block(int world_x, int world_y,
int world_z, int chunk_x,
int chunk_z) {
int x, y, z;
y = world_y;
x = world_x - chunk_x * CHUNK_SIZE;
z = world_z - chunk_z * CHUNK_SIZE;
return {x, y, z};
}
std::tuple<int, int, int>
ClientChunk::world_to_block(const glm::ivec3& block_pos, ChunkPos chunk_pos) {
return world_to_block(block_pos.x, block_pos.y, block_pos.z, chunk_pos.x,
chunk_pos.z);
}
std::tuple<int, int, int>
ClientChunk::block_to_world(int x, int y, int z, int chunk_x, int chunk_z) {
int world_x = x + chunk_x * CHUNK_SIZE;
int world_z = z + chunk_z * CHUNK_SIZE;
int world_y = y;
return {world_x, world_y, world_z};
}
std::tuple<int, int, int>
ClientChunk::block_to_world(const glm::ivec3& block_pos, ChunkPos chunk_pos) {
return block_to_world(block_pos.x, block_pos.y, block_pos.z, chunk_pos.x,
chunk_pos.z);
}
BiomeType ClientChunk::get_biome() const { return m_biome.load(); } BiomeType ClientChunk::get_biome() const { return m_biome.load(); }
ChunkPos ClientChunk::get_chunk_pos() const { return m_chunk_pos; } ChunkPos ClientChunk::get_chunk_pos() const { return m_chunk_pos; }

View File

@@ -136,7 +136,6 @@ void ClientWorld::rebuild_world() {
BlockType ClientWorld::get_block_tpye(const glm::ivec3& block_pos) const { BlockType ClientWorld::get_block_tpye(const glm::ivec3& block_pos) const {
auto [chunk_x, chunk_z] = get_chunk_pos(block_pos.x, block_pos.z); auto [chunk_x, chunk_z] = get_chunk_pos(block_pos.x, block_pos.z);
chunk_cacc cacc; chunk_cacc cacc;
;
if (!m_chunks.find(cacc, ChunkPos{chunk_x, chunk_z})) { if (!m_chunks.find(cacc, ChunkPos{chunk_x, chunk_z})) {
// Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y, // Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y,
@@ -737,6 +736,7 @@ AABB ClientWorld::get_block_aabb(const glm::ivec3& pos) {
static_cast<float>(pos.z) + 0.5f}, static_cast<float>(pos.z) + 0.5f},
glm::vec3{0.5f, 0.5f, 0.5f}}; glm::vec3{0.5f, 0.5f, 0.5f}};
} }
AudioEngine& ClientWorld::get_audio() { return m_audio; } AudioEngine& ClientWorld::get_audio() { return m_audio; }
const AudioEngine& ClientWorld::get_audio() const { return m_audio; } const AudioEngine& ClientWorld::get_audio() const { return m_audio; }
Config& ClientWorld::get_config() { return m_config; } Config& ClientWorld::get_config() { return m_config; }

View File

@@ -113,7 +113,7 @@ void ServerWorld::send_chunk(int task_id, const std::string& uuid,
rsq_pos->set_x(pos.x); rsq_pos->set_x(pos.x);
rsq_pos->set_z(pos.z); rsq_pos->set_z(pos.z);
{ {
chunk_caac cacc; chunk_cacc cacc;
if (!m_chunks.find(cacc, pos)) { if (!m_chunks.find(cacc, pos)) {
// No chunk found and not generating // No chunk found and not generating
Logger::error("Chunk {} {} neither pending nor ready", pos.x, Logger::error("Chunk {} {} neither pending nor ready", pos.x,
@@ -990,4 +990,88 @@ void ServerWorld::set_chunk_load_style(int id) {
int ServerWorld::chunk_size() const { return m_chunks.size(); } int ServerWorld::chunk_size() const { return m_chunks.size(); }
int ServerWorld::get_block(const glm::ivec3& block_pos) const {
auto [chunk_x, chunk_z] = get_chunk_pos(block_pos.x, block_pos.z);
chunk_cacc cacc;
if (!m_chunks.find(cacc, ChunkPos{chunk_x, chunk_z})) {
return 0;
}
if (cacc->second.state != ChunkState::READY) {
return 0;
}
const auto& chunk_blocks = cacc->second.chunk->get_chunk_blocks();
auto [x, y, z] = Chunk::world_to_block(block_pos, {chunk_x, chunk_z});
if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y ||
z >= CHUNK_SIZE) {
return 0;
}
return chunk_blocks[Chunk::index(x, y, z)];
}
bool ServerWorld::is_solid(const glm::ivec3& block_pos) const {
auto [chunk_x, chunk_z] = get_chunk_pos(block_pos.x, block_pos.z);
chunk_cacc cacc;
if (!m_chunks.find(cacc, ChunkPos{chunk_x, chunk_z})) {
return false;
}
if (cacc->second.state != ChunkState::READY) {
return 0;
}
const auto& chunk_blocks = cacc->second.chunk->get_chunk_blocks();
auto [x, y, z] = Chunk::world_to_block(block_pos, {chunk_x, chunk_z});
if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y ||
z >= CHUNK_SIZE) {
return false;
}
auto id = chunk_blocks[Chunk::index(x, y, z)];
if (BlockManager::is_gas(id) || BlockManager::is_liquid(id)) {
return false;
} else {
return true;
}
}
bool ServerWorld::can_pass_block(const glm::ivec3& block_pos) const {
auto [chunk_x, chunk_z] = get_chunk_pos(block_pos.x, block_pos.z);
chunk_cacc cacc;
if (!m_chunks.find(cacc, ChunkPos{chunk_x, chunk_z})) {
return true;
}
if (cacc->second.state != ChunkState::READY) {
return 0;
}
const auto& chunk_blocks = cacc->second.chunk->get_chunk_blocks();
auto [x, y, z] = Chunk::world_to_block(block_pos, {chunk_x, chunk_z});
if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y ||
z >= CHUNK_SIZE) {
return true;
}
auto id = chunk_blocks[Chunk::index(x, y, z)];
return BlockManager::is_passable(id);
}
BlockType ServerWorld::get_block_tpye(const glm::ivec3& block_pos) const {
auto [chunk_x, chunk_z] = get_chunk_pos(block_pos.x, block_pos.z);
chunk_cacc cacc;
if (!m_chunks.find(cacc, ChunkPos{chunk_x, chunk_z})) {
// Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y,
// block_pos.z);
return 0;
}
if (cacc->second.state != ChunkState::READY) {
return 0;
}
const auto& chunk_blocks = cacc->second.chunk->get_chunk_blocks();
auto [x, y, z] = Chunk::world_to_block(block_pos, {chunk_x, chunk_z});
if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y ||
z >= CHUNK_SIZE) {
// Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y,
// block_pos.z);
return 0;
}
return chunk_blocks[Chunk::index(x, y, z)];
}
} // namespace Cubed } // namespace Cubed