[Solved] Which event should be used for LuaEntity.destroy()?
- aubergine18
- Smart Inserter
- Posts: 1264
- Joined: Fri Jul 22, 2016 8:51 pm
- Contact:
Re: Which event should be used for LuaEntity.destroy()? When?
For me it's not so much about the .valid check. I don't mind doing it. It's more about keeping track of things that would otherwise not need any on_tick involvement.
I've put a lot of effort in to avoiding using on_tick, and my bridges have highlighted a scenario where another mod deleting them is not covered by the event system.
I guess my only choice is to start iterating all my bridges, and the segments (entities) within them, every X ticks to see if another mod deleted them, at which point I need to replace some fake water with real water or maybe destroy whole bridge (depends on bridge type).
If LuaEntity.destroy() fired an event with at least some static info about the destroyed entity (id, position, type, name, force, surface, etc) then I could avoid using on_tick completely.
I've put a lot of effort in to avoiding using on_tick, and my bridges have highlighted a scenario where another mod deleting them is not covered by the event system.
I guess my only choice is to start iterating all my bridges, and the segments (entities) within them, every X ticks to see if another mod deleted them, at which point I need to replace some fake water with real water or maybe destroy whole bridge (depends on bridge type).
If LuaEntity.destroy() fired an event with at least some static info about the destroyed entity (id, position, type, name, force, surface, etc) then I could avoid using on_tick completely.
Better forum search for modders: Enclose your search term in quotes, eg. "font_color" or "custom-input" - it prevents the forum search from splitting on hypens and underscores, resulting in much more accurate results.
Re: Which event should be used for LuaEntity.destroy()? When?
"x" after calling destroy on it is simply a nullptr on the C++ side. The equivalent to "nil" in Lua. You can compare it all day long and it will equal other things that are "nil" but you can't tell *what* it was before it was set to "nil".mknejp wrote:That just means it clears out all the invalid entities in one go which is even better.Rseding91 wrote:Equality checks don't work once an entity is invalid. It will always compare false with any other valid entity or compare true with any other invalid entity regardless of them actually being equal before they got destroyed.
The mod calling "x.destroy()" still has an "x" handle to the now destroyed entity after the function returns, so some form of metadata for equality comparison and validity checking still has to exist. More is not required, or even asked for, to be available inside the on_destroyed event. Critical properties like name, type, prototype, force, unit_number can be provided in addition to the entity handle for filtering to reduce the amount of work done inside the event handler.Rseding91 wrote:Because as I said before: once something is destroyed it's not valid for anything to hold on to a handle to that thing and as such all of the wrappers are cleared.
If you want to get ahold of me I'm almost always on Discord.
Re: Which event should be used for LuaEntity.destroy()? When?
Which is perfectly fine. Although at that point a.invalid is probably more efficient than a==b. More important would be providing the destroyed entity's unit_number if it had one (along with other static properties) since that is what's used as table key and for filtering. The critical thing here really is knowing when something was destroyed and what kind of thing it was, with unit_number allowing for faster processing where available.Rseding91 wrote:"x" after calling destroy on it is simply a nullptr on the C++ side. The equivalent to "nil" in Lua. You can compare it all day long and it will equal other things that are "nil" but you can't tell *what* it was before it was set to "nil".
Re: Which event should be used for LuaEntity.destroy()? When?
I think Rseding meant you won't get any data about the entity after destroy() because it is invalid. I have tried printing entity.name after destroy() and it threw error.mknejp wrote:Which is perfectly fine. Although at that point a.invalid is probably more efficient than a==b. More important would be providing the destroyed entity's unit_number if it had one (along with other static properties) since that is what's used as table key and for filtering. The critical thing here really is knowing when something was destroyed and what kind of thing it was, with unit_number allowing for faster processing where available.Rseding91 wrote:"x" after calling destroy on it is simply a nullptr on the C++ side. The equivalent to "nil" in Lua. You can compare it all day long and it will equal other things that are "nil" but you can't tell *what* it was before it was set to "nil".
Well, my previous suggestion about passing entity's name, force, surface, position, etc as the event parameters were rejected. I understand that it is too different from the other events....
How about on_pre_entity_destroyed? Like on_preplayer_mined_item.
![Mr. Green :mrgreen:](./images/smilies/icon_mrgreen.gif)
on_pre_entity_destroyed
Called after LuaEntity.destroy() is called, but before the entity becomes invalid. You can't cancel the destruction in this event.
Contains
entity :: LuaEntity: the entity that's going to be invalid.
- DedlySpyder
- Filter Inserter
- Posts: 254
- Joined: Fri Jun 20, 2014 11:42 am
- Contact:
Re: Which event should be used for LuaEntity.destroy()? When?
@Mooncat, couldn't you just use LuaEntity.die() and clean up any Loot at the position within a small time frame? If the entity gives loot then add it's position (+/- a small amount) to a table for deletion.
Or even no big deal for the loot, as this mod isn't really for normal play, correct?
Or even no big deal for the loot, as this mod isn't really for normal play, correct?
Re: Which event should be used for LuaEntity.destroy()? When?
Good question. I have struggled on this question too when I was implementing the magic wands. But then I knew that die() isn't always feasible.DedlySpyder wrote:@Mooncat, couldn't you just use LuaEntity.die() and clean up any Loot at the position within a small time frame? If the entity gives loot then add it's position (+/- a small amount) to a table for deletion.
Or even no big deal for the loot, as this mod isn't really for normal play, correct?
die() will cause explosions, death animations and corpses, which is not acceptable for instant deconstruction (the opposite of instant blueprint).
Instant Blueprint and Instant Deconstruction
Here is another magic wand that can "kill" the selected entities:
Die
It will be hilarious if I use die() for instant deconstruction. ![Laughing :lol:](./images/smilies/icon_lol.gif)
If I need to get rid of the explosions, particles, death animations, corpses, loot, ghosts, etc. there will be 2 problems:
1) I will need to use find_entities, which is bad for performance. Still possible, just bad.
2) I need to know the time when the entity explode. Based on the current API of LuaEntityPrototype, it is impossible.
The easiest and most elegant way to solve this problem that I can think of is the on_pre_entity_destroyed event.
![Smile :)](./images/smilies/icon_e_smile.gif)
It doesn't violate the rule that entity is invalid because the event is called right before it becomes invalid.
It can get rid of on_tick listeners for just checking entity.valid. Good for performance.
If it is rejected, I think we will need the reason.
- LuziferSenpai
- Filter Inserter
- Posts: 383
- Joined: Tue Jul 08, 2014 10:06 am
- Contact:
Re: Which event should be used for LuaEntity.destroy()? When?
This is easy i think.Mooncat wrote:Good question. I have struggled on this question too when I was implementing the magic wands. But then I knew that die() isn't always feasible.DedlySpyder wrote:@Mooncat, couldn't you just use LuaEntity.die() and clean up any Loot at the position within a small time frame? If the entity gives loot then add it's position (+/- a small amount) to a table for deletion.
Or even no big deal for the loot, as this mod isn't really for normal play, correct?
die() will cause explosions, death animations and corpses, which is not acceptable for instant deconstruction (the opposite of instant blueprint).Instant Blueprint and Instant DeconstructionHere is another magic wand that can "kill" the selected entities:DieIt will be hilarious if I use die() for instant deconstruction.![]()
If I need to get rid of the explosions, particles, death animations, corpses, loot, ghosts, etc. there will be 2 problems:
1) I will need to use find_entities, which is bad for performance. Still possible, just bad.
2) I need to know the time when the entity explode. Based on the current API of LuaEntityPrototype, it is impossible.
The easiest and most elegant way to solve this problem that I can think of is the on_pre_entity_destroyed event.![]()
It doesn't violate the rule that entity is invalid because the event is called right before it becomes invalid.
It can get rid of on_tick listeners for just checking entity.valid. Good for performance.
If it is rejected, I think we will need the reason.
I stole this code from Klonan's Lab Builder and i think this will work for you
![Wink ;)](./images/smilies/icon_e_wink.gif)
Code: Select all
script.on_event( defines.events.on_player_selected_area, function( event )
if event.item == wandname then
for i, q in pairs( game.players[ event.player_index ].surface.find_entities( event.area ) )
if q.name ~= "player" then
v.destroy()
end
end
end
end )
Code: Select all
script.on_event( defines.events.on_player_alt_selected_area, function( event )
if event.item == wandname then
for i, q in pairs( game.players[ event.player_index ].surface.find_entities( event.area ) )
if q.name ~= "player" then
v.destroy()
end
end
end
end )
Senpai
- aubergine18
- Smart Inserter
- Posts: 1264
- Joined: Fri Jul 22, 2016 8:51 pm
- Contact:
Re: Which event should be used for LuaEntity.destroy()? When?
Does the vanilla game work the same way? For example, when a mod creates or destroys an entity, the C code gets no notification of that happening, and just has to somehow work it out? I suspect this is not the case.
If the vanilla game needs to know about full lifecycle of entities, does it not stand to reason that mods kind of need that too? Specifically events fired when a mod uses LuaSurface.create_entity() or LuaEntity.destroy(). (I've not checked to see if LuaEntity.die() fires an event, if not, that would need adding to the list).
If the vanilla game needs to know about full lifecycle of entities, does it not stand to reason that mods kind of need that too? Specifically events fired when a mod uses LuaSurface.create_entity() or LuaEntity.destroy(). (I've not checked to see if LuaEntity.die() fires an event, if not, that would need adding to the list).
Better forum search for modders: Enclose your search term in quotes, eg. "font_color" or "custom-input" - it prevents the forum search from splitting on hypens and underscores, resulting in much more accurate results.
Re: Which event should be used for LuaEntity.destroy()? When?
LuziferSenpai wrote:This is easy i think.
I stole this code from Klonan's Lab Builder and i think this will work for you
And if you want a second use on the same wand then use:Code: Select all
script.on_event( defines.events.on_player_selected_area, function( event ) if event.item == wandname then for i, q in pairs( game.players[ event.player_index ].surface.find_entities( event.area ) ) if q.name ~= "player" then v.destroy() end end end end )
Greetz,Code: Select all
script.on_event( defines.events.on_player_alt_selected_area, function( event ) if event.item == wandname then for i, q in pairs( game.players[ event.player_index ].surface.find_entities( event.area ) ) if q.name ~= "player" then v.destroy() end end end end )
Senpai
This is the code I am using, in addition to some filtering algorithms.
![Smile :)](./images/smilies/icon_e_smile.gif)
The problem is, also the reason why this thread is here, that other mods don't know the entity is destroyed.
According to the doc:aubergine18 wrote:I've not checked to see if LuaEntity.die() fires an event, if not, that would need adding to the list).
I trust it.Unlike LuaEntity::destroy, die will trigger on_entity_died
![Very Happy :D](./images/smilies/icon_e_biggrin.gif)
That's another reason why destroy should trigger on_pre_entity_destroyed. For consistency.
Re: Which event should be used for LuaEntity.destroy()? When?
Mooncat wrote:LuziferSenpai wrote:This is easy i think.
I stole this code from Klonan's Lab Builder and i think this will work for you
And if you want a second use on the same wand then use:Code: Select all
script.on_event( defines.events.on_player_selected_area, function( event ) if event.item == wandname then for i, q in pairs( game.players[ event.player_index ].surface.find_entities( event.area ) ) if q.name ~= "player" then v.destroy() end end end end )
Greetz,Code: Select all
script.on_event( defines.events.on_player_alt_selected_area, function( event ) if event.item == wandname then for i, q in pairs( game.players[ event.player_index ].surface.find_entities( event.area ) ) if q.name ~= "player" then v.destroy() end end end end )
Senpai
This is the code I am using, in addition to some filtering algorithms.![]()
The problem is, also the reason why this thread is here, that other mods don't know the entity is destroyed.
According to the doc:aubergine18 wrote:I've not checked to see if LuaEntity.die() fires an event, if not, that would need adding to the list).I trust it.Unlike LuaEntity::destroy, die will trigger on_entity_died
That's another reason why destroy should trigger on_pre_entity_destroyed. For consistency.
Just use on_mined_entity/on_entity_died/on_robot_mined_entity,
When you do you code, like this
Code: Select all
function my_function(entity)
game.raise_event(on_died)
entity.destroy()
end
- aubergine18
- Smart Inserter
- Posts: 1264
- Joined: Fri Jul 22, 2016 8:51 pm
- Contact:
Re: Which event should be used for LuaEntity.destroy()? When?
And what happens when we try to destroy rail that turns out to have train on top and thus doesn't get destroyed? We've already sent the event, so other mods think rail is destroyed, only to find that we can't destroy it. This is why LuaEntity.destroy() should be sending event, would you agree?Klonan wrote:That way any mods which remove entities when they die/mined are notified, with a still valid entity reference, after which the entity is destroyed by your script.
Also, is it guaranteed that the event would be received by other mods (eg. in MP games) prior to destruction of the entity?
Better forum search for modders: Enclose your search term in quotes, eg. "font_color" or "custom-input" - it prevents the forum search from splitting on hypens and underscores, resulting in much more accurate results.
Re: Which event should be used for LuaEntity.destroy()? When?
It's not going to happen.
If you want to get ahold of me I'm almost always on Discord.
Re: Which event should be used for LuaEntity.destroy()? When?
Why?.... I thought you would be interested on it because it is about optimization.Rseding91 wrote:It's not going to happen.
![Sad :(](./images/smilies/icon_e_sad.gif)
I guess you should be able to see the difference between
1) we add listeners to on_tick to iterate all our entities in every frame for checking entity.valid, and
2) we add listeners to on_pre_entity_destroyed and do things only when our entities are destroyed?
-
- Filter Inserter
- Posts: 841
- Joined: Mon Sep 14, 2015 7:40 am
- Contact:
Re: Which event should be used for LuaEntity.destroy()? When?
Why do you need to add on_tick if you don't currently have one? Just stick some .valid checks before all spots in your existing code where you modify an entity, and you should be good to go, no?
- aubergine18
- Smart Inserter
- Posts: 1264
- Joined: Fri Jul 22, 2016 8:51 pm
- Contact:
Re: Which event should be used for LuaEntity.destroy()? When?
Ok, so for this .valid == false entity, can you tell me it's unit_number please? Or its position? Or get me the data I'd associated with it in a dictionary?Supercheese wrote:Why do you need to add on_tick if you don't currently have one? Just stick some .valid checks before all spots in your existing code where you modify an entity, and you should be good to go, no?
Better forum search for modders: Enclose your search term in quotes, eg. "font_color" or "custom-input" - it prevents the forum search from splitting on hypens and underscores, resulting in much more accurate results.
Re: Which event should be used for LuaEntity.destroy()? When?
You're talking about an edge case of an edge case...aubergine18 wrote:And what happens when we try to destroy rail that turns out to have train on top and thus doesn't get destroyed? We've already sent the event, so other mods think rail is destroyed, only to find that we can't destroy it. This is why LuaEntity.destroy() should be sending event, would you agree?Klonan wrote:That way any mods which remove entities when they die/mined are notified, with a still valid entity reference, after which the entity is destroyed by your script.
Also, is it guaranteed that the event would be received by other mods (eg. in MP games) prior to destruction of the entity?
Really i have seen entity.destroy() used in a huge variety of cases without ever raising an event, and haven't seen a single complaint
And yea, the event will be fired and processed by all mod scripts registered to the event, before continuing in the function
-
- Filter Inserter
- Posts: 841
- Joined: Mon Sep 14, 2015 7:40 am
- Contact:
Re: Which event should be used for LuaEntity.destroy()? When?
What I mean is, say you've got currently a table where you insert/remove entities to iterate:aubergine18 wrote:Ok, so for this .valid == false entity, can you tell me it's unit_number please? Or its position? Or get me the data I'd associated with it in a dictionary?
Code: Select all
for i = 1, #global.Entity_Table do
modify_Entity(global.Entity_Table[i])
end
Code: Select all
for i = #global.Entity_Table, 1, -1 do
if global.Entity_Table[i] and global.Entity_Table[i].valid then
modify_Entity(global.Entity_Table[i])
else
table.remove(global.Entity_Table, i)
end
end
- aubergine18
- Smart Inserter
- Posts: 1264
- Joined: Fri Jul 22, 2016 8:51 pm
- Contact:
Re: Which event should be used for LuaEntity.destroy()? When?
What if I need access to the data associated with the entity, and with that data I need to update other arrays or associated entities, etc? And, assuming I'm keying via entity object --> metadata, will that index in the table even persist a save-load cycle? Depending on when I next iterate the table, the entire record might have been nuked by save-load cycle, and I literally wouldn't even know the entity had been destroyed, thus potentially leaving orphan entities or orphan data in other tables/arrays etc.
How do I clean up data associated with the entity, particularly if that data is not just in the current table, but potentially in several other tables?
If the entity is created/destroyed by anything other than a mod, I get an event with the entity in it. Built? Event. Mined? Event. Died? Event.
Mod creates it? Nope, you're on your own. Mod destroys it? Nope, you're on your own.
Code: Select all
for i = #global.Entity_Table, 1, -1 do
if global.Entity_Table[i] and global.Entity_Table[i].valid then
modify_Entity(global.Entity_Table[i])
else
clean_up_destroyed_entity_metadata(global.Entity_Table[i]) -- error!
table.remove(global.Entity_Table, i)
end
end
If the entity is created/destroyed by anything other than a mod, I get an event with the entity in it. Built? Event. Mined? Event. Died? Event.
Mod creates it? Nope, you're on your own. Mod destroys it? Nope, you're on your own.
Better forum search for modders: Enclose your search term in quotes, eg. "font_color" or "custom-input" - it prevents the forum search from splitting on hypens and underscores, resulting in much more accurate results.
Re: Which event should be used for LuaEntity.destroy()? When?
@Supercheese You are just saying the same thing as Rseding before:
Problem is, what if "using something" doesn't exist in the first place?
When player places entity A, my mod places entity B. And then no further script to control the behaviours of A and B. They can act naturally.
So, there is no "modify_Entity".
When A is mined, killed or DESTROYED, B should be removed too. We have events to detect when A is mined or killed, but we don't know when it is destroyed. So the only solution will be checking entity.valid in on_tick. It is bad.
Anyway, I will use Klonan's solution for now. Thanks.
But we know it is not perfect. It has 2 major problems:
1) LuaEntity.destroy() returns a boolean, because, according to the doc, not all entities can be destroyed. Rails under trains are just an example. Who knows what new features we will have in the future, making there more examples.
2) Shouldn't expect modders to raise event for the built-in functions, unless there is some kind of modding protocol for Factorio.
So we still need to find a better solution to avoid conflicts between mods in long term.
And I really want to know why we can't have on_pre_entity_destroyed.![Confused :?](./images/smilies/icon_e_confused.gif)
and we did read that.You're expected to simply check "entity.valid" before using something (exactly as the game does) to ensure it still exists.
Problem is, what if "using something" doesn't exist in the first place?
When player places entity A, my mod places entity B. And then no further script to control the behaviours of A and B. They can act naturally.
So, there is no "modify_Entity".
When A is mined, killed or DESTROYED, B should be removed too. We have events to detect when A is mined or killed, but we don't know when it is destroyed. So the only solution will be checking entity.valid in on_tick. It is bad.
Anyway, I will use Klonan's solution for now. Thanks.
![Smile :)](./images/smilies/icon_e_smile.gif)
But we know it is not perfect. It has 2 major problems:
1) LuaEntity.destroy() returns a boolean, because, according to the doc, not all entities can be destroyed. Rails under trains are just an example. Who knows what new features we will have in the future, making there more examples.
![Laughing :lol:](./images/smilies/icon_lol.gif)
2) Shouldn't expect modders to raise event for the built-in functions, unless there is some kind of modding protocol for Factorio.
So we still need to find a better solution to avoid conflicts between mods in long term.
And I really want to know why we can't have on_pre_entity_destroyed.
![Confused :?](./images/smilies/icon_e_confused.gif)
- aubergine18
- Smart Inserter
- Posts: 1264
- Joined: Fri Jul 22, 2016 8:51 pm
- Contact:
Re: Which event should be used for LuaEntity.destroy()? When?
There's 4 mods in past week that have hit this issue (vehicle carriage mod, electric vehicles mod, creative mode, and my WIP bridges mod). I imagine the VW camper van mod also will suffer the same fate.
The mod ecosystem continues to grow, and now we are starting to see some quite complex mods emerge, a trend that is likely to continue. As we get more mods, interacting with each other, the lack of events on LuaSurface.create_entity() and LuaEntity.destroy() is going to become more of a thorn in the side of modders.
If we want to create custom event, it's likely that we'll need a library mod that other mods will have to use. Alternatively we could start setting up a labyrinth of remote interfaces (way too painful in this particular scenario IMO).
As for "just check .valid", it's not that simple for some of these mods - they need to know specifically which entity died, when it dies, and then do stuff based on that entity, which by time .valid == false it's too late in many cases. And .valid is not going to help us detect when another mod create_entity() one of our entities.
As modders, we'll find a way to automate our way out of this mess, but it's not going to be pretty.![Sad :(](./images/smilies/icon_e_sad.gif)
@Mooncat: note also that entity properties cannot be iterated with pairs(), so we can't even table.deepcopy() entity prior to .destroy() in order to conditionally send event containing static clone of props post .destroy(). That means before doing .destroy() we'll have to first determine *if* the entity can be destroyed within Lua script, and if so, send event, then destroy it.
The mod ecosystem continues to grow, and now we are starting to see some quite complex mods emerge, a trend that is likely to continue. As we get more mods, interacting with each other, the lack of events on LuaSurface.create_entity() and LuaEntity.destroy() is going to become more of a thorn in the side of modders.
If we want to create custom event, it's likely that we'll need a library mod that other mods will have to use. Alternatively we could start setting up a labyrinth of remote interfaces (way too painful in this particular scenario IMO).
As for "just check .valid", it's not that simple for some of these mods - they need to know specifically which entity died, when it dies, and then do stuff based on that entity, which by time .valid == false it's too late in many cases. And .valid is not going to help us detect when another mod create_entity() one of our entities.
As modders, we'll find a way to automate our way out of this mess, but it's not going to be pretty.
![Sad :(](./images/smilies/icon_e_sad.gif)
@Mooncat: note also that entity properties cannot be iterated with pairs(), so we can't even table.deepcopy() entity prior to .destroy() in order to conditionally send event containing static clone of props post .destroy(). That means before doing .destroy() we'll have to first determine *if* the entity can be destroyed within Lua script, and if so, send event, then destroy it.
Better forum search for modders: Enclose your search term in quotes, eg. "font_color" or "custom-input" - it prevents the forum search from splitting on hypens and underscores, resulting in much more accurate results.