Page 1 of 1

Using entities as table keys is behaving strangely

Posted: Thu Feb 08, 2018 6:27 am
by EntropySpark

Code: Select all

local damaged_entities = {}

script.on_event({defines.events.on_entity_damaged},
   function (e)
		local damaged_entity = e.entity
		for entity, i in pairs(damaged_entities) do
			if entity == damaged_entity and damaged_entities[entity] ~= damaged_entities[damaged_entity] then
				game.players[1].print('broken')
			end
		end
		damaged_entities[damaged_entity] = true
   end
)
Here, I have a set of damaged entities. For some reason, if an entity is damaged twice, then the table treats it as a different entity even though it is the same entity as before, and "broken" is printed. Specifically, the fact that two keys can be equal (which should mean they're the same reference in Lua) yet yield different results from the table is very strange. Admittedly, my experience with Lua is limited compared to other languages, is there some hidden Lua quirk going on that explains this?

Re: Using entities as table keys is behaving strangely

Posted: Thu Feb 08, 2018 6:59 am
by Optera
Entity is a user_object for lua ofc it behaves wonky when used as key in a dictionary.
Best practice is to use entity.unique_identifier as key and entity as value:

Code: Select all

global.damaged_entities[e.entity.unique_identifier] = e.entity
You probably should also make this dictionary global or inconsistent data will cause desync in MP.

Re: Using entities as table keys is behaving strangely

Posted: Thu Feb 08, 2018 7:05 am
by eradicator
Optera wrote:You probably should must also make this dictionary global or inconsistent data will cause desync in MP.
FTFY. This is not some slight error. The mod will not work in MP ever with a non global table.

Also it's called .unit_number not .unique_identifier. Most entites have one (biters, buildings, anything with health anyway) but some don't, like resources and trees.

Re: Using entities as table keys is behaving strangely

Posted: Thu Feb 08, 2018 7:23 am
by EntropySpark
Thanks, unit_number is exactly what I need. And don't worry, I'm using a global, I simplified my code to get to the meat of the issue.

Re: Using entities as table keys is behaving strangely

Posted: Thu Feb 08, 2018 7:24 am
by Therax
EntropySpark wrote:Specifically, the fact that two keys can be equal (which should mean they're the same reference in Lua) yet yield different results from the table is very strange. Admittedly, my experience with Lua is limited compared to other languages, is there some hidden Lua quirk going on that explains this?
The == operator can be overridden by setting the __eq method in a metatable. You may be familiar with similar features in languages like Python. Table lookup is performed by raw reference identity, since it is usually a hash table lookup. Try rawequal(x, y) instead of x == y and you should see results you expect.

The entity references you get in events are just that, references, created on the fly each tick and with unpredictable addresses. They may have meta methods set which would explain the behavior you are seeing, but I haven't confirmed that myself. As stated, the unit_number makes a suitable table key in most cases.

Re: Using entities as table keys is behaving strangely

Posted: Fri Feb 09, 2018 12:24 pm
by chrisgbk
Optera wrote:Entity is a user_object for lua ofc it behaves wonky when used as key in a dictionary.
Best practice is to use entity.unique_identifier as key and entity as value:

Code: Select all

global.damaged_entities[e.entity.unique_identifier] = e.entity
You probably should also make this dictionary global or inconsistent data will cause desync in MP.
Lua 5.2 supports using it as a key; the problem is that lua 5.2 also has a bug affecting only(as far as I could tell from testing) the 64 bit builds that causes the code generator to generate incorrect code if you do in certain cases. Same thing as using a table as a key for another table. Lua 5.3 works fine, but I doubt we'll ever see Factorio move to 5.3.

Basically, only use simple types (numbers, strings) as keys unless you want bad random things to happen. See also this bug report.