Add on_deactivate callback for luaentities (#10723)

This commit is contained in:
hecks 2021-01-02 15:14:29 +01:00 committed by GitHub
parent ad58fb2206
commit dd5a732fa9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 106 additions and 27 deletions

@ -160,6 +160,7 @@ local function init()
-- Simple iteration would ignore lookup via __index. -- Simple iteration would ignore lookup via __index.
local entity_instrumentation = { local entity_instrumentation = {
"on_activate", "on_activate",
"on_deactivate",
"on_step", "on_step",
"on_punch", "on_punch",
"on_rightclick", "on_rightclick",

@ -4207,6 +4207,8 @@ Callbacks:
* Called when the object is instantiated. * Called when the object is instantiated.
* `dtime_s` is the time passed since the object was unloaded, which can be * `dtime_s` is the time passed since the object was unloaded, which can be
used for updating the entity state. used for updating the entity state.
* `on_deactivate(self)
* Called when the object is about to get removed or unloaded.
* `on_step(self, dtime)` * `on_step(self, dtime)`
* Called on every server tick, after movement and collision processing. * Called on every server tick, after movement and collision processing.
`dtime` is usually 0.1 seconds, as per the `dedicated_server_step` setting `dtime` is usually 0.1 seconds, as per the `dedicated_server_step` setting

@ -31,6 +31,9 @@ minetest.register_entity("testentities:callback", {
on_activate = function(self, staticdata, dtime_s) on_activate = function(self, staticdata, dtime_s)
message("Callback entity: on_activate! pos="..spos(self).."; dtime_s="..dtime_s) message("Callback entity: on_activate! pos="..spos(self).."; dtime_s="..dtime_s)
end, end,
on_deactivate = function(self)
message("Callback entity: on_deactivate! pos="..spos(self))
end,
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage) on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
local name = get_object_name(puncher) local name = get_object_name(puncher)
message( message(

@ -103,6 +103,32 @@ void ScriptApiEntity::luaentity_Activate(u16 id,
lua_pop(L, 2); // Pop object and error handler lua_pop(L, 2); // Pop object and error handler
} }
void ScriptApiEntity::luaentity_Deactivate(u16 id)
{
SCRIPTAPI_PRECHECKHEADER
verbosestream << "scriptapi_luaentity_deactivate: id=" << id << std::endl;
int error_handler = PUSH_ERROR_HANDLER(L);
// Get the entity
luaentity_get(L, id);
int object = lua_gettop(L);
// Get on_deactivate
lua_getfield(L, -1, "on_deactivate");
if (!lua_isnil(L, -1)) {
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushvalue(L, object);
setOriginFromTable(object);
PCALL_RES(lua_pcall(L, 1, 0, error_handler));
} else {
lua_pop(L, 1);
}
lua_pop(L, 2); // Pop object and error handler
}
void ScriptApiEntity::luaentity_Remove(u16 id) void ScriptApiEntity::luaentity_Remove(u16 id)
{ {
SCRIPTAPI_PRECHECKHEADER SCRIPTAPI_PRECHECKHEADER

@ -33,6 +33,7 @@ public:
bool luaentity_Add(u16 id, const char *name); bool luaentity_Add(u16 id, const char *name);
void luaentity_Activate(u16 id, void luaentity_Activate(u16 id,
const std::string &staticdata, u32 dtime_s); const std::string &staticdata, u32 dtime_s);
void luaentity_Deactivate(u16 id);
void luaentity_Remove(u16 id); void luaentity_Remove(u16 id);
std::string luaentity_GetStaticdata(u16 id); std::string luaentity_GetStaticdata(u16 id);
void luaentity_GetProperties(u16 id, void luaentity_GetProperties(u16 id,

@ -110,7 +110,7 @@ int ObjectRef::l_remove(lua_State *L)
sao->clearParentAttachment(); sao->clearParentAttachment();
verbosestream << "ObjectRef::l_remove(): id=" << sao->getId() << std::endl; verbosestream << "ObjectRef::l_remove(): id=" << sao->getId() << std::endl;
sao->m_pending_removal = true; sao->markForRemoval();
return 0; return 0;
} }

@ -112,6 +112,15 @@ void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
} }
} }
void LuaEntitySAO::dispatchScriptDeactivate()
{
// Ensure that this is in fact a registered entity,
// and that it isn't already gone.
// The latter also prevents this from ever being called twice.
if (m_registered && !isGone())
m_env->getScriptIface()->luaentity_Deactivate(m_id);
}
void LuaEntitySAO::step(float dtime, bool send_recommended) void LuaEntitySAO::step(float dtime, bool send_recommended)
{ {
if(!m_properties_sent) if(!m_properties_sent)
@ -302,7 +311,7 @@ u16 LuaEntitySAO::punch(v3f dir,
{ {
if (!m_registered) { if (!m_registered) {
// Delete unknown LuaEntities when punched // Delete unknown LuaEntities when punched
m_pending_removal = true; markForRemoval();
return 0; return 0;
} }
@ -335,7 +344,7 @@ u16 LuaEntitySAO::punch(v3f dir,
clearParentAttachment(); clearParentAttachment();
clearChildAttachments(); clearChildAttachments();
m_env->getScriptIface()->luaentity_on_death(m_id, puncher); m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
m_pending_removal = true; markForRemoval();
} }
actionstream << puncher->getDescription() << " (id=" << puncher->getId() << actionstream << puncher->getDescription() << " (id=" << puncher->getId() <<

@ -71,6 +71,11 @@ public:
bool getSelectionBox(aabb3f *toset) const; bool getSelectionBox(aabb3f *toset) const;
bool collideWithObjects() const; bool collideWithObjects() const;
protected:
void dispatchScriptDeactivate();
virtual void onMarkedForDeactivation() { dispatchScriptDeactivate(); }
virtual void onMarkedForRemoval() { dispatchScriptDeactivate(); }
private: private:
std::string getPropertyPacket(); std::string getPropertyPacket();
void sendPosition(bool do_interpolate, bool is_movement_end); void sendPosition(bool do_interpolate, bool is_movement_end);

@ -531,7 +531,7 @@ bool PlayerSAO::setWieldedItem(const ItemStack &item)
void PlayerSAO::disconnected() void PlayerSAO::disconnected()
{ {
m_peer_id = PEER_ID_INEXISTENT; m_peer_id = PEER_ID_INEXISTENT;
m_pending_removal = true; markForRemoval();
} }
void PlayerSAO::unlinkPlayerSessionAndSave() void PlayerSAO::unlinkPlayerSessionAndSave()

@ -73,3 +73,19 @@ void ServerActiveObject::dumpAOMessagesToQueue(std::queue<ActiveObjectMessage> &
m_messages_out.pop(); m_messages_out.pop();
} }
} }
void ServerActiveObject::markForRemoval()
{
if (!m_pending_removal) {
onMarkedForRemoval();
m_pending_removal = true;
}
}
void ServerActiveObject::markForDeactivation()
{
if (!m_pending_deactivation) {
onMarkedForDeactivation();
m_pending_deactivation = true;
}
}

@ -70,6 +70,10 @@ public:
virtual bool environmentDeletes() const virtual bool environmentDeletes() const
{ return true; } { return true; }
// Safely mark the object for removal or deactivation
void markForRemoval();
void markForDeactivation();
// Create a certain type of ServerActiveObject // Create a certain type of ServerActiveObject
static ServerActiveObject* create(ActiveObjectType type, static ServerActiveObject* create(ActiveObjectType type,
ServerEnvironment *env, u16 id, v3f pos, ServerEnvironment *env, u16 id, v3f pos,
@ -213,25 +217,6 @@ public:
*/ */
u16 m_known_by_count = 0; u16 m_known_by_count = 0;
/*
- Whether this object is to be removed when nobody knows about
it anymore.
- Removal is delayed to preserve the id for the time during which
it could be confused to some other object by some client.
- This is usually set to true by the step() method when the object wants
to be deleted but can be set by anything else too.
*/
bool m_pending_removal = false;
/*
Same purpose as m_pending_removal but for deactivation.
deactvation = save static data in block, remove active object
If this is set alongside with m_pending_removal, removal takes
priority.
*/
bool m_pending_deactivation = false;
/* /*
A getter that unifies the above to answer the question: A getter that unifies the above to answer the question:
"Can the environment still interact with this object?" "Can the environment still interact with this object?"
@ -239,6 +224,9 @@ public:
inline bool isGone() const inline bool isGone() const
{ return m_pending_removal || m_pending_deactivation; } { return m_pending_removal || m_pending_deactivation; }
inline bool isPendingRemoval() const
{ return m_pending_removal; }
/* /*
Whether the object's static data has been stored to a block Whether the object's static data has been stored to a block
*/ */
@ -250,6 +238,9 @@ public:
v3s16 m_static_block = v3s16(1337,1337,1337); v3s16 m_static_block = v3s16(1337,1337,1337);
protected: protected:
virtual void onMarkedForDeactivation() {}
virtual void onMarkedForRemoval() {}
virtual void onAttach(int parent_id) {} virtual void onAttach(int parent_id) {}
virtual void onDetach(int parent_id) {} virtual void onDetach(int parent_id) {}
@ -257,6 +248,27 @@ protected:
v3f m_base_position; v3f m_base_position;
std::unordered_set<u32> m_attached_particle_spawners; std::unordered_set<u32> m_attached_particle_spawners;
/*
Same purpose as m_pending_removal but for deactivation.
deactvation = save static data in block, remove active object
If this is set alongside with m_pending_removal, removal takes
priority.
Note: Do not assign this directly, use markForDeactivation() instead.
*/
bool m_pending_deactivation = false;
/*
- Whether this object is to be removed when nobody knows about
it anymore.
- Removal is delayed to preserve the id for the time during which
it could be confused to some other object by some client.
- This is usually set to true by the step() method when the object wants
to be deleted but can be set by anything else too.
Note: Do not assign this directly, use markForRemoval() instead.
*/
bool m_pending_removal = false;
/* /*
Queue of messages to be sent to the client Queue of messages to be sent to the client
*/ */

@ -1164,7 +1164,7 @@ void ServerEnvironment::clearObjects(ClearObjectsMode mode)
// If known by some client, don't delete immediately // If known by some client, don't delete immediately
if (obj->m_known_by_count > 0) { if (obj->m_known_by_count > 0) {
obj->m_pending_removal = true; obj->markForRemoval();
return false; return false;
} }
@ -1792,7 +1792,7 @@ void ServerEnvironment::removeRemovedObjects()
/* /*
Delete static data from block if removed Delete static data from block if removed
*/ */
if (obj->m_pending_removal) if (obj->isPendingRemoval())
deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false); deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
// If still known by clients, don't actually remove. On some future // If still known by clients, don't actually remove. On some future
@ -1803,7 +1803,7 @@ void ServerEnvironment::removeRemovedObjects()
/* /*
Move static data from active to stored if deactivated Move static data from active to stored if deactivated
*/ */
if (!obj->m_pending_removal && obj->m_static_exists) { if (!obj->isPendingRemoval() && obj->m_static_exists) {
MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
if (block) { if (block) {
const auto i = block->m_static_objects.m_active.find(id); const auto i = block->m_static_objects.m_active.find(id);
@ -1991,6 +1991,7 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete)
if (!force_delete && obj->m_static_exists && if (!force_delete && obj->m_static_exists &&
!m_active_blocks.contains(obj->m_static_block) && !m_active_blocks.contains(obj->m_static_block) &&
m_active_blocks.contains(blockpos_o)) { m_active_blocks.contains(blockpos_o)) {
// Delete from block where object was located // Delete from block where object was located
deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false); deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
@ -2068,6 +2069,10 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete)
force_delete = true; force_delete = true;
} }
// Regardless of what happens to the object at this point, deactivate it first.
// This ensures that LuaEntity on_deactivate is always called.
obj->markForDeactivation();
/* /*
If known by some client, set pending deactivation. If known by some client, set pending deactivation.
Otherwise delete it immediately. Otherwise delete it immediately.
@ -2077,7 +2082,6 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete)
<< "object id=" << id << " is known by clients" << "object id=" << id << " is known by clients"
<< "; not deleting yet" << std::endl; << "; not deleting yet" << std::endl;
obj->m_pending_deactivation = true;
return false; return false;
} }