mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-08 17:57:02 +08:00
feat(ecs): add entity type separation and creature limits
Replace generic entity creation with typed creatures and items. Rename `add_entity` to `add_creature`, enforce per-player creature cap, and track entity/creature totals for metrics. Expose run mode to dev panel and show new counts.
This commit is contained in:
@@ -3,9 +3,12 @@
|
||||
namespace Cubed {
|
||||
using EntityID = uint64_t;
|
||||
|
||||
enum class EntityType { CREATURE, ITEM };
|
||||
|
||||
struct Entity {
|
||||
EntityID id;
|
||||
explicit Entity(EntityID id) : id(id) {}
|
||||
EntityType type;
|
||||
Entity(EntityID id, EntityType type) : id(id), type(type) {}
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -12,15 +12,20 @@ class ServerWorld;
|
||||
class Session;
|
||||
class ServerEntityManager {
|
||||
public:
|
||||
static constexpr size_t PER_CREATURE_LIMITS = 100;
|
||||
|
||||
ServerEntityManager(ServerWorld& world);
|
||||
|
||||
void init();
|
||||
void update();
|
||||
// not thread safe
|
||||
void add_entity(std::string_view name, const glm::vec3& world_pos);
|
||||
void add_creature(std::string_view name, const glm::vec3& world_pos);
|
||||
void destory(EntityID id);
|
||||
void handle_player_login(std::shared_ptr<Session> session);
|
||||
|
||||
size_t max_creature_sum() const;
|
||||
size_t creature_sum() const;
|
||||
size_t entity_sum() const;
|
||||
|
||||
private:
|
||||
enum class Command { CREATE, SEND_ALL_ENTITIES, DESTORY };
|
||||
struct EntityCreateElement {
|
||||
@@ -43,6 +48,8 @@ private:
|
||||
std::variant<std::shared_ptr<Session>, EntityCreateElement, EntityID>;
|
||||
using TaskPair = std::pair<Command, TaskElement>;
|
||||
ServerWorld& m_world;
|
||||
std::atomic<size_t> m_creature_sum{0};
|
||||
std::atomic<size_t> m_entity_sum{0};
|
||||
tbb::concurrent_queue<TaskPair> m_tasks;
|
||||
entt::registry m_registry;
|
||||
EntityID m_next = 0;
|
||||
|
||||
@@ -98,6 +98,8 @@ public:
|
||||
uint32_t get_chunk_ref_count(const glm::vec3& pos) const;
|
||||
ServerEntityManager& entity_manager();
|
||||
std::shared_ptr<ThreadPool> get_compute_pool();
|
||||
size_t player_sum() 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;
|
||||
|
||||
@@ -42,6 +42,8 @@ public:
|
||||
|
||||
bool is_recording() const;
|
||||
|
||||
RunMode runmode() const;
|
||||
|
||||
private:
|
||||
enum class PauseUI { PAUSE_MENU, INVENTORY };
|
||||
SceneManager& m_scene_manager;
|
||||
@@ -61,6 +63,7 @@ private:
|
||||
ErrorUI m_error_ui;
|
||||
const Argument& m_argument;
|
||||
VoiceInputType m_input_type;
|
||||
RunMode m_runmode = RunMode::HYBRID;
|
||||
bool handle_mouse_move_event(const MouseMoveEvent& e) override;
|
||||
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
|
||||
bool handle_window_resize_event(const WindowResizeEvent& e) override;
|
||||
|
||||
@@ -343,17 +343,20 @@ void DevPanel::show_world_tab_item() {
|
||||
if (ImGui::BeginTabItem("world")) {
|
||||
|
||||
if (ImGui::BeginTabBar("World Kind")) {
|
||||
auto& param = m_world_scene.scene_manager().world_scene_param();
|
||||
if (param.host_game) {
|
||||
auto mode = m_world_scene.runmode();
|
||||
if (mode == RunMode::HYBRID || mode == RunMode::SERVER_ONLY) {
|
||||
if (ImGui::BeginTabItem("ServerWorld")) {
|
||||
show_server_world_table_bar();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
if (ImGui::BeginTabItem("Client World")) {
|
||||
show_client_world_table_bar();
|
||||
ImGui::EndTabItem();
|
||||
if (mode == RunMode::HYBRID || mode == RunMode::CLIENT_ONLY) {
|
||||
if (ImGui::BeginTabItem("Client World")) {
|
||||
show_client_world_table_bar();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
@@ -366,47 +369,49 @@ void DevPanel::show_world_tab_item() {
|
||||
}
|
||||
|
||||
void DevPanel::show_server_world_table_bar() {
|
||||
|
||||
auto& world = m_world_scene.server_world();
|
||||
ImGui::Text("ChunkGenerator Seed: %u", ChunkGenerator::seed());
|
||||
|
||||
ImGui::Text("Pool Threads %d Max Support Threads %d Reserved Threads %d",
|
||||
m_world_scene.server_world().gen_pool_threads(),
|
||||
m_world_scene.server_world().max_threads(), RESERVED_THREADS);
|
||||
ImGui::SliderInt("Set Pool Threads", &m_threads, 1,
|
||||
m_world_scene.server_world().max_threads());
|
||||
world.gen_pool_threads(), world.max_threads(),
|
||||
RESERVED_THREADS);
|
||||
ImGui::SliderInt("Set Pool Threads", &m_threads, 1, world.max_threads());
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Set")) {
|
||||
m_world_scene.server_world().change_pool_threads(
|
||||
ServerWorld::ThreadPoolKind::GEN, m_threads);
|
||||
world.change_pool_threads(ServerWorld::ThreadPoolKind::GEN, m_threads);
|
||||
}
|
||||
if (m_threads >
|
||||
m_world_scene.server_world().max_threads() - RESERVED_THREADS) {
|
||||
if (m_threads > world.max_threads() - RESERVED_THREADS) {
|
||||
ImGui::TextColored(
|
||||
ImVec4(1.0f, 1.0f, 0.0f, 1.0f),
|
||||
"Waring: When the threads in the thread pool exceed \n(maximum "
|
||||
"threads minus reserved threads), \nit may cause stuttering.");
|
||||
}
|
||||
|
||||
m_chunk_style = m_world_scene.server_world().chunk_load_style();
|
||||
m_chunk_style = world.chunk_load_style();
|
||||
if (ImGui::Combo("ChunkLoadStyle", &m_chunk_style, CHUNK_LOAD_STYLE,
|
||||
IM_ARRAYSIZE(CHUNK_LOAD_STYLE))) {
|
||||
m_world_scene.server_world().set_chunk_load_style(m_chunk_style);
|
||||
world.set_chunk_load_style(m_chunk_style);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Request Chunk Build")) {
|
||||
m_world_scene.server_world().need_gen(m_player->get_uuid());
|
||||
world.need_gen(m_player->get_uuid());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Checkbox("Gen Thread", &m_gen_thread_running)) {
|
||||
if (m_gen_thread_running) {
|
||||
m_world_scene.server_world().start_gen_thread();
|
||||
world.start_gen_thread();
|
||||
} else {
|
||||
m_world_scene.server_world().stop_gen_thread();
|
||||
world.stop_gen_thread();
|
||||
}
|
||||
}
|
||||
ImGui::Text("Server Chunk Size %d",
|
||||
m_world_scene.server_world().chunk_size());
|
||||
ImGui::Text("Server Chunk Size %d", world.chunk_size());
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Server Player Sum %zu", world.player_sum());
|
||||
|
||||
ImGui::Text("Server Entity Sum %zu", world.entity_manager().entity_sum());
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Server Creature Sum %zu",
|
||||
world.entity_manager().creature_sum());
|
||||
if (ImGui::BeginTabBar("World Settings")) {
|
||||
if (ImGui::BeginTabItem("Time")) {
|
||||
show_time_table_bar();
|
||||
|
||||
@@ -829,7 +829,7 @@ void ChunkGenerator::spawn_creature() {
|
||||
}
|
||||
auto [world_x, world_y, world_z] = Chunk::block_to_world(
|
||||
x, y + 1, z, chunk_pos.x, chunk_pos.z);
|
||||
m_chunk.world().entity_manager().add_entity(
|
||||
m_chunk.world().entity_manager().add_creature(
|
||||
SpawnDefaults::PIG.name,
|
||||
glm::vec3{world_x, world_y, world_z});
|
||||
}
|
||||
|
||||
@@ -31,8 +31,9 @@ void ClientEntityManager::init() {
|
||||
m_factories.emplace("cubed:pig", [this](EntityID id) {
|
||||
BaseClientCreature c;
|
||||
c.model = ModelManager::instance().get_model_id("cubed:pig");
|
||||
create_entity_in_registry(id, Entity{id}, EntityInfo{"cubed:pig", ""},
|
||||
std::move(c), PigTag{}, RenderTransform{});
|
||||
create_entity_in_registry(id, Entity{id, EntityType::CREATURE},
|
||||
EntityInfo{"cubed:pig", ""}, std::move(c),
|
||||
PigTag{}, RenderTransform{});
|
||||
});
|
||||
}
|
||||
// not thread safe
|
||||
|
||||
@@ -27,8 +27,8 @@ void ServerEntityManager::init() {
|
||||
c.movement.deceleration = PigDefaults::DECELERATION;
|
||||
c.velocity.max.x = c.velocity.max.z = PigDefaults::MAX_SPEED;
|
||||
return create_entity_in_factory(
|
||||
Entity{m_next}, EntityInfo{"cubed:pig", ""}, std::move(c), PigTag{},
|
||||
AIBase{}, WanderAITag{}, MoveBoost{});
|
||||
Entity{m_next, EntityType::CREATURE}, EntityInfo{"cubed:pig", ""},
|
||||
std::move(c), PigTag{}, AIBase{}, WanderAITag{}, MoveBoost{});
|
||||
});
|
||||
}
|
||||
void ServerEntityManager::update() {
|
||||
@@ -134,8 +134,13 @@ void ServerEntityManager::handle_task() {
|
||||
}
|
||||
}
|
||||
|
||||
void ServerEntityManager::add_entity(std::string_view name,
|
||||
const glm::vec3& world_pos) {
|
||||
void ServerEntityManager::add_creature(std::string_view name,
|
||||
const glm::vec3& world_pos) {
|
||||
if (m_creature_sum.fetch_add(1) >= max_creature_sum()) {
|
||||
m_creature_sum.fetch_sub(1);
|
||||
return;
|
||||
}
|
||||
++m_entity_sum;
|
||||
m_tasks.emplace(Command::CREATE,
|
||||
EntityCreateElement{std::string(name), world_pos});
|
||||
}
|
||||
@@ -149,6 +154,15 @@ void ServerEntityManager::handle_player_login(
|
||||
m_tasks.emplace(Command::SEND_ALL_ENTITIES, std::move(session));
|
||||
}
|
||||
|
||||
size_t ServerEntityManager::max_creature_sum() const {
|
||||
return PER_CREATURE_LIMITS * m_world.player_sum();
|
||||
}
|
||||
|
||||
size_t ServerEntityManager::creature_sum() const {
|
||||
return m_creature_sum.load();
|
||||
}
|
||||
size_t ServerEntityManager::entity_sum() const { return m_entity_sum.load(); }
|
||||
|
||||
void ServerEntityManager::create_entity(std::string_view name,
|
||||
const glm::vec3& pos) {
|
||||
ASSERT(m_factories.contains(name));
|
||||
@@ -159,6 +173,7 @@ void ServerEntityManager::create_entity(std::string_view name,
|
||||
ASSERT(t);
|
||||
t->transform.position.value = pos;
|
||||
}
|
||||
|
||||
handle_entity_create(e, name, pos);
|
||||
}
|
||||
|
||||
@@ -193,12 +208,23 @@ void ServerEntityManager::handle_entity_create(EntityID id,
|
||||
}
|
||||
|
||||
void ServerEntityManager::handle_entity_destory(EntityID id) {
|
||||
if (m_entity_sum == 0) {
|
||||
Logger::error("entity sum is 0!");
|
||||
return;
|
||||
}
|
||||
acc a;
|
||||
if (!m_entities.find(a, id)) {
|
||||
return;
|
||||
}
|
||||
auto e = m_registry.try_get<Entity>(a->second);
|
||||
ASSERT(e);
|
||||
if (e->type == EntityType::CREATURE) {
|
||||
--m_creature_sum;
|
||||
}
|
||||
m_registry.destroy(a->second);
|
||||
m_entities.erase(a);
|
||||
|
||||
--m_entity_sum;
|
||||
auto sessions = m_world.get_all_session();
|
||||
Arena arena;
|
||||
auto* s2c = Arena::Create<S2CEntityDestory>(&arena);
|
||||
|
||||
@@ -896,7 +896,7 @@ void ServerWorld::handle_block_change(const BlockChangeReq& req) {
|
||||
|
||||
void ServerWorld::handle_entity_create(C2SEntityCreateRequest& req) {
|
||||
|
||||
m_entity_manager.add_entity(req.name(), Tools::get_net_vec3(req.pos()));
|
||||
m_entity_manager.add_creature(req.name(), Tools::get_net_vec3(req.pos()));
|
||||
}
|
||||
void ServerWorld::handle_entity_destory(C2SEntityDestoryRequest& req) {
|
||||
m_entity_manager.destory(req.id());
|
||||
@@ -1133,4 +1133,7 @@ ServerEntityManager& ServerWorld::entity_manager() { return m_entity_manager; }
|
||||
std::shared_ptr<ThreadPool> ServerWorld::get_compute_pool() {
|
||||
return m_compute_thread_pool.load();
|
||||
}
|
||||
|
||||
size_t ServerWorld::player_sum() const { return m_player_sum.load(); }
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -129,21 +129,20 @@ void WorldScene::on_enter() {
|
||||
load_config();
|
||||
m_error_ui.init();
|
||||
m_client = std::make_shared<NetworkClient>(m_client_world);
|
||||
RunMode mode = RunMode::HYBRID;
|
||||
if (m_argument.direct_enter) {
|
||||
if (m_argument.ip) {
|
||||
mode = RunMode::CLIENT_ONLY;
|
||||
m_runmode = RunMode::CLIENT_ONLY;
|
||||
}
|
||||
} else {
|
||||
if (!m_scene_manager.world_scene_param().host_game) {
|
||||
mode = RunMode::CLIENT_ONLY;
|
||||
m_runmode = RunMode::CLIENT_ONLY;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_argument.direct_enter) {
|
||||
if (!m_argument.ip) {
|
||||
ChunkGenerator::init();
|
||||
m_server.start_server(*m_argument.port, mode);
|
||||
m_server.start_server(*m_argument.port, m_runmode);
|
||||
m_client->start("127.0.0.1", *m_argument.port);
|
||||
} else {
|
||||
m_client->start(*m_argument.ip, *m_argument.port);
|
||||
@@ -159,7 +158,7 @@ void WorldScene::on_enter() {
|
||||
} else {
|
||||
ChunkGenerator::init();
|
||||
}
|
||||
m_server.start_server(param.port, mode);
|
||||
m_server.start_server(param.port, m_runmode);
|
||||
}
|
||||
|
||||
m_client->start(param.ip, param.port);
|
||||
@@ -169,7 +168,7 @@ void WorldScene::on_enter() {
|
||||
try {
|
||||
|
||||
m_client_world.init(m_argument.player.value_or("Unknown"), m_client,
|
||||
mode);
|
||||
m_runmode);
|
||||
|
||||
Logger::info("World Init Success");
|
||||
m_camera.camera_init(&m_client_world.get_player());
|
||||
@@ -461,6 +460,9 @@ void WorldScene::handle_chat_message(ChatMessage& message) {
|
||||
bool WorldScene::is_recording() const {
|
||||
return m_client_world.get_audio().audio_recording().is_recording();
|
||||
}
|
||||
|
||||
RunMode WorldScene::runmode() const { return m_runmode; }
|
||||
|
||||
void WorldScene::set_error(std::string_view error) {
|
||||
Logger::error("WorldScene Error Set {}", error);
|
||||
m_error_ui.set_error(error);
|
||||
|
||||
Reference in New Issue
Block a user