Please add table_type to LuaEntity and LuaPlayer

User avatar
Earendel
Factorio Staff
Factorio Staff
Posts: 711
Joined: Sun Nov 23, 2014 11:57 am
Contact:

Please add table_type to LuaEntity and LuaPlayer

Post by Earendel »

Say a function returns either a LuaEntity or LuaPlayer, currently I can't find to distinguish them that doesn't result in an error. I'd prefer not to have to use pcall.

What would be useful is if LuaEntity and LuaPlayer had a property called something like "table_type " that return either "LuaEntity" or "LuaPlayer".

Maybe there's a better way to distinguish them already?

User avatar
bobingabout
Smart Inserter
Smart Inserter
Posts: 7352
Joined: Fri May 09, 2014 1:01 pm
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by bobingabout »

I guess you could check to see if the returned entity has a tag that only exists on one of them and not the other is present, like this

Code: Select all

if entity.gui then
--player
else
--not player
end
It also has to be a non-Boolean tag as false would fail into not a player, and not a function, but even a number that can return zero will fail too, so the best ones to check are tables.
gui for example is a table, and is unique to LuaPlayer. as long as you don't go deeper than the initial flag, it shouldn't fail.
http://lua-api.factorio.com/latest/LuaPlayer.html

Of course, player itself isn't an entity, it's a controller, the little man running around that you control is a player type entity, known as a character (Yes, confusing, data.raw.player.player is CLEARLY of type player, but in the control phase it's known as a character instead.)

if this is what you're after, try the following

Code: Select all

if entity.type == "player" then
--player character
else
--not player character
end
Creator of Bob's mods. Expanding your gameplay since version 0.9.8.
I also have a Patreon.

User avatar
Earendel
Factorio Staff
Factorio Staff
Posts: 711
Joined: Sun Nov 23, 2014 11:57 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by Earendel »

That's the problem, there's not a property (that I can find) that can be checked on both to return a definitive value without causing an error on the other.

entity.type on LuaPlayer throws an error, entity.gui on LuaEntity throws an error

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by eradicator »

Try one of the function types, e.g. get_quickbar() as those explicitily should return nil when failing.

User avatar
bobingabout
Smart Inserter
Smart Inserter
Posts: 7352
Joined: Fri May 09, 2014 1:01 pm
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by bobingabout »

Riiiiiight.... it shouldn't, because in theory if you do it as I did in the example, you're checking to see if .gui exists, so it should just return nil.
Creator of Bob's mods. Expanding your gameplay since version 0.9.8.
I also have a Patreon.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by eradicator »

bobingabout wrote:Riiiiiight.... it shouldn't, because in theory if you do it as I did in the example, you're checking to see if .gui exists, so it should just return nil.
I don't think this has ever been true. Reading values that an entity doesn't have has always been an error. I remember being annoyed about this years ago already, when i wanted to implement a generic "view all properties of this entity" gui. They probably do it on purpose so it's easier to notice when you're doing something wrong.

Zanthra
Fast Inserter
Fast Inserter
Posts: 207
Joined: Fri Mar 25, 2016 8:18 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by Zanthra »

In Lua it is not an error to access an undefined variable. It will simply be a "nil" value. The issue is when you try to access keys in a nil value.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by eradicator »

Zanthra wrote:In Lua it is not an error to access an undefined variable. It will simply be a "nil" value. The issue is when you try to access keys in a nil value.
In pure lua yes. This is however not true for userdata objects (like entities) that come from the c side. The devs can do anything they want with those. Even erroring on reading undefined values :P. This can be clearly deduced from the existance and nessicity of the .valid field on all userdata objects.

Zanthra
Fast Inserter
Fast Inserter
Posts: 207
Joined: Fri Mar 25, 2016 8:18 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by Zanthra »

I see now, interesting. How expensive is pcall as far as performance goes compared to directly calling a function? I have been warned away from using errors as control flow, but in this case it may be your best option.

I did some quick tests, and pcall with an error seems to take about 40 microseconds, and whithout an error, it has very little performance impact. So as long as you don't end up with more than 10 errors per tick, the performance impact should be fairly low.

quyxkh
Smart Inserter
Smart Inserter
Posts: 1028
Joined: Sun May 08, 2016 9:01 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by quyxkh »

ugly but efficient hack: lua strings are atoms, equality testing is just pointer testing, test the help() string.

Code: Select all

    if entity.help() == stored_player_help then 
Trying that in x=1,1000 loops says it's dirt cheap.

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by Nexela »

Zanthra wrote:I see now, interesting. How expensive is pcall as far as performance goes compared to directly calling a function? I have been warned away from using errors as control flow, but in this case it may be your best option.

I did some quick tests, and pcall with an error seems to take about 40 microseconds, and whithout an error, it has very little performance impact. So as long as you don't end up with more than 10 errors per tick, the performance impact should be fairly low.
In an of itself pcall is just like any other function call. And this is one of those cases where pcall would be an acceptable use, I.E I know the result from doing this can be an error and if it is I want to do X. what is not Ok is wrapping everything in pcall and hoping for the best as this is a very good way to make flaws in your code hard to track down.

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by Nexela »

quyxkh wrote:ugly but efficient hack: lua strings are atoms, equality testing is just pointer testing, test the help() string.

Code: Select all

    if entity.help() == stored_player_help then 
Trying that in x=1,1000 loops says it's dirt cheap.
This hack wins the interwebs :)

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by eradicator »

Nexela wrote:
quyxkh wrote:ugly but efficient hack: lua strings are atoms, equality testing is just pointer testing, test the help() string.

Code: Select all

    if entity.help() == stored_player_help then 
Trying that in x=1,1000 loops says it's dirt cheap.
This hack wins the interwebs :)
Is that the same cost as testing for nil?
eradicator wrote:Try one of the function types, e.g. get_quickbar() as those explicitily should return nil when failing.

quyxkh
Smart Inserter
Smart Inserter
Posts: 1028
Joined: Sun May 08, 2016 9:01 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by quyxkh »

eradicator wrote:Is that the same cost as testing for nil?
eradicator wrote:Try one of the function types, e.g. get_quickbar() as those explicitily should return nil when failing.
Nope, it's not, testing says doing `do x=1,1e6 if unsub.get_quickbar()==nil then end end` is cheaper than `…unsub.help()==saved_player_help…`no matter what the unsub is, the only downside is, it won't distinguish a LuaPlayer from a player's LuaEntity.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by eradicator »

quyxkh wrote:
eradicator wrote:Is that the same cost as testing for nil?
eradicator wrote:Try one of the function types, e.g. get_quickbar() as those explicitily should return nil when failing.
Nope, it's not, testing says doing `do x=1,1e6 if unsub.get_quickbar()==nil then end end` is cheaper than `…unsub.help()==saved_player_help…`no matter what the unsub is, the only downside is, it won't distinguish a LuaPlayer from a player's LuaEntity.
Oh. The OP didn't mention what kind of LuaEntity he's dealing with, so i kind of assumed it would be something not player related and not the LuaPlayer :oops: (also strictly LuaPlayer is not a LuaEntity, but i'm not sure that is of any benefit here). Good catch though. But i'm still not very fond of having to store the help string because i'm not sure how often that changes (hopefully only in on_config_changed?)

User avatar
Earendel
Factorio Staff
Factorio Staff
Posts: 711
Joined: Sun Nov 23, 2014 11:57 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by Earendel »

Yeah to be clear, the potential input types are:
A: Player: A LuaPlayer
B: Player Character: A LuaEntity of the "player", but is the player.character.
C: Non-player character: A LuaEntity of the type "player" but one spawned via script and has no associated player.
D: Some other LuaEntity, like a stone furnace or a piece of wall.

If you know it's a LuaEntity then entity.type and entity.name get all the info you need, but trying that on a LuaPlayer causes an error. Checking for a player first with get_quickbar() does seem to work as it returns nil for a character. I'm actually surprised by this because i'd expect the quickbar to be part of the character's inventory.

In the future we'll hopefully have LuaControl::is_player() as a more dedicated solution.

The entity.help() comparison seems like a VERY clever solution because presumably that could help identify a wide range of unknown table types.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by eradicator »

What do you need to distinguish out of those 4 thought, from the OP it sounds like you want:
A or (B or C or D)
So if .get_quickbar() returns nil on all of BCD (you just confirmed B) then wouldn't that still be the optimal solution? It makes sense btw that characters don't have quickbars, as god controllers can have a quickbar too.

quyxkh
Smart Inserter
Smart Inserter
Posts: 1028
Joined: Sun May 08, 2016 9:01 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by quyxkh »

Earendel wrote:A: Player: A LuaPlayer
B: Player Character: A LuaEntity of the "player", but is the player.character.
C: Non-player character: A LuaEntity of the type "player" but one spawned via script and has no associated player.
D: Some other LuaEntity, like a stone furnace or a piece of wall.
eradicator wrote:if .get_quickbar() returns nil on all of BCD (you just confirmed B) then wouldn't that still be the optimal solution? It makes sense btw that characters don't have quickbars, as god controllers can have a quickbar too.
The problem is, unless my coffee has worn off it doesn't: in my tests it returns the quickbar in case B.

Here, paste this into your console:
console paste
and then `/c x=fef'player'[1] gpsl{x.type,x.get_quickbar()}`.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by eradicator »

quyxkh wrote:The problem is, unless my coffee has worn off it doesn't: in my tests it returns the quickbar in case B.

Here, paste this into your console:
console paste
and then `/c x=fef'player'[1] gpsl{x.type,x.get_quickbar()}`.
Heh. Not exactly the minimum viable demonstration code :twisted:, but my non-coffee returns the characters quickbar too :/. Also out of a mood i tested the uniqueness of the .help() string...
code
And there's exactly two help() strings, one for LuaPlayer and one for every other prototype. So for avoiding pcall using that string does currently seem like the only option. With pcall (i.e. without having to worry about storing and updating some arbitrary string) .index might be a good option for something only LuaPlayer has. Also btw there's one property shared between every prototype and LuaPlayer which is .name, so if you're in the mood to store arbitray comparison data a player-name lookup table would work too :roll: .

@OP:
Btw, how do you even end up in a situation where you get arbitrary entity data :D?

User avatar
Earendel
Factorio Staff
Factorio Staff
Posts: 711
Joined: Sun Nov 23, 2014 11:57 am
Contact:

Re: Please add table_type to LuaEntity and LuaPlayer

Post by Earendel »

eradicator wrote: @OP:
Btw, how do you even end up in a situation where you get arbitrary entity data :D?
It's mainly that Car.get_driver() returns a LuaPlayer or a LuaEntity (or nil) depending on whether the driving character has an attached player.

Also I have a function teleport_near(entity, position) that does a collision check using can_place_entity. It would have been nice to pass anything that can be teleported into the function, but currently I'm just passing in the player.character if it's a player.

I think it would be generally useful if tables that cause errors when properties are accessed could self-identify what they are. This could be especially useful when dealing with information passed across remote interfaces and old data in global tables. It's not that the situation is unworkable now, but it would be a nice improvement.

Post Reply

Return to “Implemented mod requests”