Please add table_type to LuaEntity and LuaPlayer
Please add table_type to LuaEntity and LuaPlayer
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?
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?
- bobingabout
- Smart Inserter
- Posts: 7352
- Joined: Fri May 09, 2014 1:01 pm
- Contact:
Re: Please add table_type to LuaEntity and LuaPlayer
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
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.gui then
--player
else
--not player
end
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
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
entity.type on LuaPlayer throws an error, entity.gui on LuaEntity throws an error
- eradicator
- Smart Inserter
- Posts: 5206
- Joined: Tue Jul 12, 2016 9:03 am
- Contact:
Re: Please add table_type to LuaEntity and LuaPlayer
Try one of the function types, e.g. get_quickbar() as those explicitily should return nil when failing.
- bobingabout
- Smart Inserter
- Posts: 7352
- Joined: Fri May 09, 2014 1:01 pm
- Contact:
Re: Please add table_type to LuaEntity and LuaPlayer
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.
- eradicator
- Smart Inserter
- Posts: 5206
- Joined: Tue Jul 12, 2016 9:03 am
- Contact:
Re: Please add table_type to LuaEntity and LuaPlayer
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.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.
Re: Please add table_type to LuaEntity and LuaPlayer
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.
- eradicator
- Smart Inserter
- Posts: 5206
- Joined: Tue Jul 12, 2016 9:03 am
- Contact:
Re: Please add table_type to LuaEntity and LuaPlayer
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 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.
Re: Please add table_type to LuaEntity and LuaPlayer
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.
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
ugly but efficient hack: lua strings are atoms, equality testing is just pointer testing, test the help() string.
Trying that in x=1,1000 loops says it's dirt cheap.
Code: Select all
if entity.help() == stored_player_help then
Re: Please add table_type to LuaEntity and LuaPlayer
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.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.
Re: Please add table_type to LuaEntity and LuaPlayer
This hack wins the interwebsquyxkh wrote:ugly but efficient hack: lua strings are atoms, equality testing is just pointer testing, test the help() string.
Trying that in x=1,1000 loops says it's dirt cheap.Code: Select all
if entity.help() == stored_player_help then
- eradicator
- Smart Inserter
- Posts: 5206
- Joined: Tue Jul 12, 2016 9:03 am
- Contact:
Re: Please add table_type to LuaEntity and LuaPlayer
Is that the same cost as testing for nil?Nexela wrote:This hack wins the interwebs :)quyxkh wrote:ugly but efficient hack: lua strings are atoms, equality testing is just pointer testing, test the help() string.
Trying that in x=1,1000 loops says it's dirt cheap.Code: Select all
if entity.help() == stored_player_help then
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
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.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.
- eradicator
- Smart Inserter
- Posts: 5206
- Joined: Tue Jul 12, 2016 9:03 am
- Contact:
Re: Please add table_type to LuaEntity and LuaPlayer
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?)quyxkh wrote: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.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.
Re: Please add table_type to LuaEntity and LuaPlayer
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.
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.
- eradicator
- Smart Inserter
- Posts: 5206
- Joined: Tue Jul 12, 2016 9:03 am
- Contact:
Re: Please add table_type to LuaEntity and LuaPlayer
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.
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
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.
The problem is, unless my coffee has worn off it doesn't: in my tests it returns the quickbar in case B.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.
Here, paste this into your console:
console paste
and then `/c x=fef'player'[1] gpsl{x.type,x.get_quickbar()}`.- eradicator
- Smart Inserter
- Posts: 5206
- Joined: Tue Jul 12, 2016 9:03 am
- Contact:
Re: Please add table_type to LuaEntity and LuaPlayer
Heh. Not exactly the minimum viable demonstration code , but my non-coffee returns the characters quickbar too :/. Also out of a mood i tested the uniqueness of the .help() string...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 pasteand then `/c x=fef'player'[1] gpsl{x.type,x.get_quickbar()}`.
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 .@OP:
Btw, how do you even end up in a situation where you get arbitrary entity data ?
Re: Please add table_type to LuaEntity and LuaPlayer
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.eradicator wrote: @OP:
Btw, how do you even end up in a situation where you get arbitrary entity data ?
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.