From 518d5d5ae4bc407676b5f6bc5f71a6f576aa2753 Mon Sep 17 00:00:00 2001 From: Xertis <118364459+Xertis@users.noreply.github.com> Date: Sat, 4 Apr 2026 01:53:49 +0300 Subject: [PATCH] Add on_entity_spawned and on_entity_despawned events --- doc/en/scripting/ecs.md | 4 +++ doc/en/scripting/events.md | 11 ++++++++ doc/ru/scripting/ecs.md | 4 +++ doc/ru/scripting/events.md | 12 ++++++++ src/content/ContentPack.hpp | 2 ++ src/logic/scripting/scripting.cpp | 32 ++++++++++++++++++++++ src/logic/scripting/scripting.hpp | 3 ++ src/logic/scripting/scripting_entities.cpp | 8 +++++- 8 files changed, 75 insertions(+), 1 deletion(-) diff --git a/doc/en/scripting/ecs.md b/doc/en/scripting/ecs.md index 7b1f87a1a..878452928 100644 --- a/doc/en/scripting/ecs.md +++ b/doc/en/scripting/ecs.md @@ -180,6 +180,10 @@ rig:get_color() -> vec3 rig:set_color(color: vec3) ``` +> [!WARNING] +> When an entity moves outside the loaded area, it is removed, triggering the event. +> When it re-enters the loaded area, the entity is spawned again. + ## Component events ```lua diff --git a/doc/en/scripting/events.md b/doc/en/scripting/events.md index fea9de7df..f475dc1c1 100644 --- a/doc/en/scripting/events.md +++ b/doc/en/scripting/events.md @@ -152,6 +152,17 @@ function on_block_interact(blockid, x, y, z, playerid) -> bool Called on block RMB click interaction. Prevents block placing if **true** returned. +```lua +function on_entity_spawned(uid) +``` +Called after an entity is spawned. + +```lua +function on_entity_despawned(uid) +``` + +Called after an entity is removed. At the time of the call, the entity with the given uid has already been deleted. + ### Chunk Events (world.lua) ```lua diff --git a/doc/ru/scripting/ecs.md b/doc/ru/scripting/ecs.md index 4f9d45f08..1a5f6ccf3 100644 --- a/doc/ru/scripting/ecs.md +++ b/doc/ru/scripting/ecs.md @@ -180,6 +180,10 @@ rig:get_color() -> vec3 rig:set_color(color: vec3) ``` +> [!WARNING] +> При выходе сущности за пределы зоны прогрузки она удаляется, вызывая события. +> При повторном попадании в зону прогрузки сущность спавнится заново. + ## События компонента ```lua diff --git a/doc/ru/scripting/events.md b/doc/ru/scripting/events.md index 7dbe7bc07..fa20e4030 100644 --- a/doc/ru/scripting/events.md +++ b/doc/ru/scripting/events.md @@ -156,6 +156,18 @@ function on_block_interact(blockid, x, y, z, playerid) -> bool Вызывается при нажатии на блок ПКМ. Предотвращает установку блоков, если возвращает `true` +```lua +function on_entity_spawned(uid) +``` + +Вызывается после спавна сущности + +```lua +function on_entity_despawned(uid) +``` + +Вызывается после удаления сущности, на момент вызова события сущность с переданным uid уже удалена + ```lua function on_player_tick(playerid: int, tps: int) ``` diff --git a/src/content/ContentPack.hpp b/src/content/ContentPack.hpp index a4548d038..bd0ccc79f 100644 --- a/src/content/ContentPack.hpp +++ b/src/content/ContentPack.hpp @@ -127,6 +127,8 @@ struct WorldFuncsSet { bool onchunkremove; bool oninventoryopen; bool oninventoryclosed; + bool onentityspawned; + bool onentitydespawned; }; class ContentPackRuntime { diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index bba9f6837..232c72796 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -555,6 +555,34 @@ void scripting::on_player_tick(Player* player, int tps) { } } +void scripting::on_entity_spawned(entityid_t eid) { + auto L = lua::get_main_state(); + for (auto& pack : content_control->getAllContentPacks()) { + lua::emit_event( + L, + pack.id + ":.entityspawned", + [&](lua::State* L) { + lua::pushinteger(L, eid); + return 1; + } + ); + } +} + +void scripting::on_entity_despawned(entityid_t eid) { + auto L = lua::get_main_state(); + for (auto& pack : content_control->getAllContentPacks()) { + lua::emit_event( + L, + pack.id + ":.entitydespawned", + [&](lua::State* L) { + lua::pushinteger(L, eid); + return 1; + } + ); + } +} + bool scripting::on_item_use(Player* player, const ItemDef& item) { std::string name = item.name + ".use"; return lua::emit_event( @@ -781,6 +809,10 @@ void scripting::load_world_script( register_event(env, "on_inventory_open", prefix + ":.inventoryopen"); funcsset.oninventoryclosed = register_event(env, "on_inventory_closed", prefix + ":.inventoryclosed"); + funcsset.onentityspawned = + register_event(env, "on_entity_spawned", prefix + ":.entityspawned"); + funcsset.onentitydespawned = + register_event(env, "on_entity_despawned", prefix + ":.entitydespawned"); } void scripting::load_content_script( diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index 9d29528b8..3d9737c6a 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -112,6 +112,9 @@ namespace scripting { void on_player_tick(Player* player, int tps); + void on_entity_spawned(entityid_t eid); + void on_entity_despawned(entityid_t eid); + /// @brief Called on RMB click with the item selected /// @return true if prevents default action bool on_item_use(Player* player, const ItemDef& item); diff --git a/src/logic/scripting/scripting_entities.cpp b/src/logic/scripting/scripting_entities.cpp index c068d7007..0be289038 100644 --- a/src/logic/scripting/scripting_entities.cpp +++ b/src/logic/scripting/scripting_entities.cpp @@ -129,6 +129,8 @@ void scripting::on_entity_spawn( for (auto& component : components) { create_component(L, -1, *component, args, saved); } + + scripting::on_entity_spawned(eid); } static void process_entity_callback( @@ -171,9 +173,13 @@ void scripting::on_entity_despawn(const Entity& entity) { entity, "on_despawn", &EntityFuncsSet::on_despawn, nullptr ); auto L = lua::get_main_state(); + entityid_t uid = entity.getUID(); + lua::get_from(L, "stdcomp", "remove_Entity", true); - lua::pushinteger(L, entity.getUID()); + lua::pushinteger(L, uid); lua::call(L, 1, 0); + + scripting::on_entity_despawned(uid); } void scripting::on_entity_grounded(const Entity& entity, float force) {