Using entities as table keys is behaving strangely

Place to get help with not working mods / modding interface.
User avatar
EntropySpark
Inserter
Inserter
Posts: 21
Joined: Wed Jan 31, 2018 7:06 am
Contact:

Using entities as table keys is behaving strangely

Post 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?
User avatar
Optera
Smart Inserter
Smart Inserter
Posts: 2920
Joined: Sat Jun 11, 2016 6:41 am
Contact:

Re: Using entities as table keys is behaving strangely

Post 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.
User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5211
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Using entities as table keys is behaving strangely

Post 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.
User avatar
EntropySpark
Inserter
Inserter
Posts: 21
Joined: Wed Jan 31, 2018 7:06 am
Contact:

Re: Using entities as table keys is behaving strangely

Post 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.
User avatar
Therax
Filter Inserter
Filter Inserter
Posts: 471
Joined: Sun May 21, 2017 6:28 pm
Contact:

Re: Using entities as table keys is behaving strangely

Post 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.
Miniloader — UPS-friendly 1x1 loaders
Bulk Rail Loaders — Rapid train loading and unloading
Beltlayer & Pipelayer — Route items and fluids freely underground
chrisgbk
Long Handed Inserter
Long Handed Inserter
Posts: 93
Joined: Mon Jan 02, 2017 4:31 am
Contact:

Re: Using entities as table keys is behaving strangely

Post 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.
Post Reply

Return to “Modding help”