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,37 +31,50 @@ 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);
for (auto& e : view) { if (!m_world.get_chunk_ref_count(c.transform.position.value)) {
auto [entity, creature] = view.get<Entity, BaseServerCreature>(e); continue;
Arena arena;
auto* p = Arena::Create<S2CEntityUpdate>(&arena);
p->set_id(entity.id);
Tools::set_net_pos(p, creature.transform.position.value);
Tools::set_net_vec3(p->mutable_direction(),
creature.transform.direction.value);
for (auto& s : sessions) {
s->send(make_packet(p));
} }
update_ai(e);
update_move(e);
update_send(e, sessions);
}
}
void ServerEntityManager::update_ai(entt::entity 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;
auto* p = Arena::Create<S2CEntityUpdate>(&arena);
p->set_id(entity.id);
Tools::set_net_pos(p, creature.transform.position.value);
Tools::set_net_vec3(p->mutable_direction(),
creature.transform.direction.value);
for (auto& s : sessions) {
s->send(make_packet(p));
} }
} }

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,61 +102,62 @@ 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 distance = get_move_distance(creature.velocity);
auto box = HitboxManager::hitbox(creature.hitbox);
auto& pos = creature.transform.position.value;
auto& v = creature.velocity;
bool ground =
!update_y(pos, glm::vec3{0.0f, -0.1f, 0.0f}, world, box.box);
bool stepped = false;
if (update_y(pos, distance, world, box.box)) {
pos.y += distance.y;
} else { auto& creature = registry.get<BaseServerCreature>(e);
v.value.y = 0.0f; auto distance = get_move_distance(creature.velocity);
} auto box = HitboxManager::hitbox(creature.hitbox);
if (update_x(pos, distance, world, box.box)) { auto& pos = creature.transform.position.value;
pos.x += distance.x; auto& v = creature.velocity;
bool ground = !update_y(pos, glm::vec3{0.0f, -0.1f, 0.0f}, world, box.box);
bool stepped = false;
if (update_y(pos, distance, world, box.box)) {
pos.y += distance.y;
} else { } else {
if (ground && !stepped) { v.value.y = 0.0f;
}
if (update_x(pos, distance, world, box.box)) {
pos.x += distance.x;
glm::vec3 step_pos = pos + glm::vec3{0.0f, 1.0f, 0.0f}; } else {
if (update_x(step_pos, distance, world, box.box)) { if (ground && !stepped) {
pos.x += distance.x;
pos.y += 1.0f; glm::vec3 step_pos = pos + glm::vec3{0.0f, 1.0f, 0.0f};
stepped = true; if (update_x(step_pos, distance, world, box.box)) {
} else { pos.x += distance.x;
v.value.x = 0.0f; pos.y += 1.0f;
} stepped = true;
} else { } else {
v.value.x = 0.0f; v.value.x = 0.0f;
} }
}
if (update_z(pos, distance, world, box.box)) {
pos.z += distance.z;
} else { } else {
if (ground && !stepped) { v.value.x = 0.0f;
}
}
glm::vec3 step_pos = pos + glm::vec3{0.0f, 1.0f, 0.0f}; if (update_z(pos, distance, world, box.box)) {
if (update_z(step_pos, distance, world, box.box)) { pos.z += distance.z;
pos.z += distance.z;
pos.y += 1.0f; } else {
stepped = true; if (ground && !stepped) {
glm::vec3 step_pos = pos + glm::vec3{0.0f, 1.0f, 0.0f};
if (update_z(step_pos, distance, world, box.box)) {
pos.z += distance.z;
pos.y += 1.0f;
stepped = true;
} else {
v.value.z = 0.0f;
}
} else { } else {
v.value.z = 0.0f; v.value.z = 0.0f;
} }
} else {
v.value.z = 0.0f;
} }
} }
} }

View File

@@ -5,38 +5,38 @@
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] = view.get<BaseServerCreature, MoveBoost>(e);
auto& v = creature.velocity.value;
if (moveboost.count <= moveboost.duration) {
++moveboost.count;
v += creature.transform.direction.value *
creature.movement.acceleration;
} else {
// Decelerated by friction in all directions
auto decay = [](float& c, float d) {
if (c > 0.0f)
c = std::max(0.0f, c - d);
else if (c < 0.0f)
c = std::min(0.0f, c + d);
};
decay(v.x, creature.movement.deceleration);
decay(v.z, creature.movement.deceleration);
}
v.y += -creature.gravity.value * dt;
auto v_clamp = [](float& c, float max) {
if (max < 0.0f) {
return; //-1 = unlimited
}
c = std::clamp(c, -max, max);
};
v_clamp(v.x, creature.velocity.max.x);
v_clamp(v.y, creature.velocity.max.y);
v_clamp(v.z, creature.velocity.max.z);
} }
auto [creature, moveboost] = registry.get<BaseServerCreature, MoveBoost>(e);
auto& v = creature.velocity.value;
if (moveboost.count <= moveboost.duration) {
++moveboost.count;
v +=
creature.transform.direction.value * creature.movement.acceleration;
} else {
// Decelerated by friction in all directions
auto decay = [](float& c, float d) {
if (c > 0.0f)
c = std::max(0.0f, c - d);
else if (c < 0.0f)
c = std::min(0.0f, c + d);
};
decay(v.x, creature.movement.deceleration);
decay(v.z, creature.movement.deceleration);
}
v.y += -creature.gravity.value * dt;
auto v_clamp = [](float& c, float max) {
if (max < 0.0f) {
return; //-1 = unlimited
}
c = std::clamp(c, -max, max);
};
v_clamp(v.x, creature.velocity.max.x);
v_clamp(v.y, creature.velocity.max.y);
v_clamp(v.z, creature.velocity.max.z);
} }
} // namespace Cubed } // namespace Cubed

View File

@@ -10,18 +10,18 @@ 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] = registry.get<AIBase, BaseServerCreature, MoveBoost>(e);
view.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);
}
} }
} }