refactor(gameplay): update systems per entity with chunk check

Refactor server entity update flow to process entities individually,
skipping those whose chunk is not loaded. System update methods now
accept a single entity instead of iterating the full registry view,
and `ServerWorld::get_chunk_ref_count` is added to determine if an
entity's chunk is active.
This commit is contained in:
2026-08-05 10:00:52 +08:00
parent abf1f4db7d
commit 0ffa2804d1
10 changed files with 145 additions and 118 deletions

View File

@@ -44,9 +44,10 @@ private:
void handle_entity_destory(EntityID id); void handle_entity_destory(EntityID id);
void handle_task(); void handle_task();
void send_all_entities(std::shared_ptr<Session>& session); void send_all_entities(std::shared_ptr<Session>& session);
void update_ai(); void update_ai(entt::entity e);
void update_move(); void update_move(entt::entity e);
void update_send(); void update_send(entt::entity e,
std::span<std::shared_ptr<Session>> sessions);
template <typename... Args> template <typename... Args>
EntityID create_entity_in_factory(Args&&... args) { EntityID create_entity_in_factory(Args&&... args) {
auto entity = m_registry.create(); auto entity = m_registry.create();

View File

@@ -94,6 +94,8 @@ public:
std::vector<std::shared_ptr<Session>> get_all_session() const; std::vector<std::shared_ptr<Session>> get_all_session() const;
uint32_t get_chunk_ref_count(const glm::vec3& pos) const;
int get_block(const glm::ivec3& block_pos) const override; int get_block(const glm::ivec3& block_pos) const override;
bool is_solid(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; bool can_pass_block(const glm::ivec3& block_pos) const override;

View File

@@ -8,7 +8,8 @@ class PhysicalSystem {
public: public:
static glm::vec3 get_move_distance(const TickVelocity& v); static glm::vec3 get_move_distance(const TickVelocity& v);
static void update(ServerWorld& world, entt::registry& registry); static void update(ServerWorld& world, entt::registry& registry,
entt::entity e);
private: private:
}; };

View File

@@ -4,7 +4,7 @@
namespace Cubed { namespace Cubed {
class SpeedSystem { class SpeedSystem {
public: public:
static void update(float dt, entt::registry& registry); static void update(float dt, entt::registry& registry, entt::entity e);
private: private:
}; };

View File

@@ -6,7 +6,7 @@
namespace Cubed { namespace Cubed {
class WanderAISystem { class WanderAISystem {
public: public:
static void update(entt::registry& registry); static void update(entt::registry& registry, entt::entity e);
private: private:
static void do_ai(BaseServerCreature& creature, MoveBoost& move_boost); static void do_ai(BaseServerCreature& creature, MoveBoost& move_boost);

View File

@@ -31,26 +31,40 @@ void ServerEntityManager::init() {
} }
void ServerEntityManager::update() { void ServerEntityManager::update() {
handle_task(); handle_task();
update_ai();
update_move();
update_send(); auto view = m_registry.view<BaseServerCreature>();
}
void ServerEntityManager::update_ai() { WanderAISystem::update(m_registry); }
void ServerEntityManager::update_move() {
SpeedSystem::update(
static_cast<float>(m_world.get_per_tick_time()) / 1000.0f, m_registry);
PhysicalSystem::update(m_world, m_registry);
}
void ServerEntityManager::update_send() {
auto sessions = m_world.get_all_session(); auto sessions = m_world.get_all_session();
auto view = m_registry.view<Entity, BaseServerCreature>(); for (auto e : view) {
const auto& c = view.get<BaseServerCreature>(e);
if (!m_world.get_chunk_ref_count(c.transform.position.value)) {
continue;
}
update_ai(e);
update_move(e);
update_send(e, sessions);
}
}
for (auto& e : view) { void ServerEntityManager::update_ai(entt::entity e) {
auto [entity, creature] = view.get<Entity, BaseServerCreature>(e); WanderAISystem::update(m_registry, e);
}
void ServerEntityManager::update_move(entt::entity e) {
SpeedSystem::update(static_cast<float>(m_world.get_per_tick_time()) /
1000.0f,
m_registry, e);
PhysicalSystem::update(m_world, m_registry, e);
}
void ServerEntityManager::update_send(
entt::entity e, std::span<std::shared_ptr<Session>> sessions) {
if (!m_registry.all_of<Entity, BaseServerCreature>(e)) {
return;
}
const auto [entity, creature] =
m_registry.get<Entity, BaseServerCreature>(e);
Arena arena; Arena arena;
auto* p = Arena::Create<S2CEntityUpdate>(&arena); auto* p = Arena::Create<S2CEntityUpdate>(&arena);
p->set_id(entity.id); p->set_id(entity.id);
@@ -62,7 +76,6 @@ void ServerEntityManager::update_send() {
for (auto& s : sessions) { for (auto& s : sessions) {
s->send(make_packet(p)); s->send(make_packet(p));
} }
}
} }
void ServerEntityManager::handle_task() { void ServerEntityManager::handle_task() {

View File

@@ -1020,6 +1020,15 @@ std::vector<std::shared_ptr<Session>> ServerWorld::get_all_session() const {
return sessions; return sessions;
} }
uint32_t ServerWorld::get_chunk_ref_count(const glm::vec3& pos) const {
ChunkPos p = get_chunk_pos(pos.x, pos.z);
chunk_cacc cacc;
if (!m_chunks.find(cacc, p)) {
return 0;
}
return cacc->second.ref_count;
}
int ServerWorld::get_block(const glm::ivec3& block_pos) const { int ServerWorld::get_block(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;

View File

@@ -102,17 +102,19 @@ bool update_z(const glm::vec3& pos, const glm::vec3& distance,
} // namespace } // namespace
void PhysicalSystem::update(ServerWorld& world, entt::registry& registry) { void PhysicalSystem::update(ServerWorld& world, entt::registry& registry,
entt::entity e) {
auto view = registry.view<BaseServerCreature>(); if (!registry.all_of<BaseServerCreature>(e)) {
for (auto e : view) { return;
auto& creature = view.get<BaseServerCreature>(e); }
auto& creature = registry.get<BaseServerCreature>(e);
auto distance = get_move_distance(creature.velocity); auto distance = get_move_distance(creature.velocity);
auto box = HitboxManager::hitbox(creature.hitbox); auto box = HitboxManager::hitbox(creature.hitbox);
auto& pos = creature.transform.position.value; auto& pos = creature.transform.position.value;
auto& v = creature.velocity; auto& v = creature.velocity;
bool ground = bool ground = !update_y(pos, glm::vec3{0.0f, -0.1f, 0.0f}, world, box.box);
!update_y(pos, glm::vec3{0.0f, -0.1f, 0.0f}, world, box.box);
bool stepped = false; bool stepped = false;
if (update_y(pos, distance, world, box.box)) { if (update_y(pos, distance, world, box.box)) {
pos.y += distance.y; pos.y += distance.y;
@@ -158,7 +160,6 @@ void PhysicalSystem::update(ServerWorld& world, entt::registry& registry) {
v.value.z = 0.0f; v.value.z = 0.0f;
} }
} }
}
} }
glm::vec3 PhysicalSystem::get_move_distance(const TickVelocity& v) { glm::vec3 PhysicalSystem::get_move_distance(const TickVelocity& v) {

View File

@@ -5,16 +5,17 @@
namespace Cubed { namespace Cubed {
void SpeedSystem::update(float dt, entt::registry& registry) { void SpeedSystem::update(float dt, entt::registry& registry, entt::entity e) {
auto view = registry.view<BaseServerCreature, MoveBoost>(); if (!registry.all_of<BaseServerCreature, MoveBoost>(e)) {
return;
}
for (auto e : view) { auto [creature, moveboost] = registry.get<BaseServerCreature, MoveBoost>(e);
auto [creature, moveboost] = view.get<BaseServerCreature, MoveBoost>(e);
auto& v = creature.velocity.value; auto& v = creature.velocity.value;
if (moveboost.count <= moveboost.duration) { if (moveboost.count <= moveboost.duration) {
++moveboost.count; ++moveboost.count;
v += creature.transform.direction.value * v +=
creature.movement.acceleration; creature.transform.direction.value * creature.movement.acceleration;
} else { } else {
// Decelerated by friction in all directions // Decelerated by friction in all directions
auto decay = [](float& c, float d) { auto decay = [](float& c, float d) {
@@ -36,7 +37,6 @@ void SpeedSystem::update(float dt, entt::registry& registry) {
v_clamp(v.x, creature.velocity.max.x); v_clamp(v.x, creature.velocity.max.x);
v_clamp(v.y, creature.velocity.max.y); v_clamp(v.y, creature.velocity.max.y);
v_clamp(v.z, creature.velocity.max.z); v_clamp(v.z, creature.velocity.max.z);
}
} }
} // namespace Cubed } // namespace Cubed

View File

@@ -10,19 +10,19 @@ constexpr double MOVE_PROBABILITY = 0.01;
} // namespace } // namespace
namespace Cubed { namespace Cubed {
void WanderAISystem::update(entt::registry& registry) { void WanderAISystem::update(entt::registry& registry, entt::entity e) {
auto view = if (!registry.all_of<AIBase, WanderAITag, BaseServerCreature, MoveBoost>(
registry.view<AIBase, WanderAITag, BaseServerCreature, MoveBoost>(); e)) {
return;
}
for (auto e : view) {
auto [ai, creature, move_boost] = auto [ai, creature, move_boost] =
view.get<AIBase, BaseServerCreature, MoveBoost>(e); registry.get<AIBase, BaseServerCreature, MoveBoost>(e);
++ai.count; ++ai.count;
if (ai.count >= ai.interval) { if (ai.count >= ai.interval) {
ai.count = 0; ai.count = 0;
do_ai(creature, move_boost); do_ai(creature, move_boost);
} }
}
} }
void WanderAISystem::do_ai(BaseServerCreature& creature, void WanderAISystem::do_ai(BaseServerCreature& creature,