Page 1 of 2

Please add table_type to LuaEntity and LuaPlayer

Posted: Thu Feb 15, 2018 1:21 pm
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?

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Thu Feb 15, 2018 2:55 pm
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

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Fri Feb 16, 2018 12:23 am
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

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Fri Feb 16, 2018 10:10 am
by eradicator
Try one of the function types, e.g. get_quickbar() as those explicitily should return nil when failing.

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Fri Feb 16, 2018 10:54 am
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.

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Fri Feb 16, 2018 12:35 pm
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.

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Fri Feb 16, 2018 8:36 pm
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.

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Fri Feb 16, 2018 8:45 pm
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.

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Fri Feb 16, 2018 9:27 pm
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.

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Sat Feb 17, 2018 1:18 am
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.

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Sat Feb 17, 2018 4:11 am
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.

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Sat Feb 17, 2018 4:12 am
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 :)

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Sat Feb 17, 2018 10:01 am
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.

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Sat Feb 17, 2018 2:20 pm
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.

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Sat Feb 17, 2018 4:30 pm
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?)

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Sat Feb 17, 2018 6:19 pm
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.

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Sat Feb 17, 2018 7:48 pm
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.

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Sat Feb 17, 2018 9:40 pm
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()}`.

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Sun Feb 18, 2018 7:58 am
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?

Re: Please add table_type to LuaEntity and LuaPlayer

Posted: Sun Feb 18, 2018 10:20 am
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.