Extended search filters

Things that we aren't going to implement
Post Reply
Pi-C
Smart Inserter
Smart Inserter
Posts: 1648
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Extended search filters

Post by Pi-C »

Events can be filtered by combining different filters with AND and OR, like this:

Code: Select all

script.on_event(defines.events.on_entity_damaged, on_entity_damaged, {
  {filter = "final-damage-amount", comparison = ">", value = 0},
  {filter = "damage-type", type = WT.steam_damage_name, mode = "and"},

  {filter = "final-damage-amount", comparison = ">", value = 0, mode = "or"},
  {filter = "damage-type", type = WT.water_damage_name, mode = "and"},
})
This allows for some very precise pre-filtering of events so that they trigger only for things you're really interested in.

Now I've just found a bug in one of my mods. It didn't work as expected because I've messed up the conditions for a surface search:

Code: Select all

	local not_burning_types = { "character", "lamp", "electric-pole" }	-- Just some random prototype types…
	local e_type  -- Result of an operation that returns either a valid prototype type or nil
        local entities = surface.find_entities_filtered{
          force = force_name,
          type = e_type or not_burning_types, 
          limit = 1000,
          invert = e_type and false or true
        }
The goal was to find the entities that belong to a specific force and are of type e_type. If e_type is nil, the search would look for ALL entities of that force, but there are some entities that I want to ignore. So, if e_type is nil, I want to look for all types that are NOT listed in not_burning_types.

The code above gives me the expected results if e_type is not nil. But if it is nil, the filter is intrinsically broken: To get the correct types, I need to search for the types I want to ignore and invert the search. However, "invert" applies to all filters (see here: "If the filters should be inverted. These filters are: name, type, ghost_name, ghost_type, direction, collision_mask, force."), so this would give me all entities that are not listed in not_burning_types -- but none of these entities would belong to the correct force!

Of course, I could work around it, for example this way:

Code: Select all

	local inv_forces = { player = {"enemy", "neutral"}, "enemy" = {"player", "neutral"}, "neutral = {"player", "enemy"} }
	
	local not_burning_types = { "character", "lamp", "electric-pole" }	-- Just some random prototype types…
	local e_type  -- Result of an operation that returns either a valid prototype type or nil
        local entities = surface.find_entities_filtered{
          force = e_type and force_name or inv_forces[force_name],
          type = e_type or not_burning_types, 
          limit = 1000,
          invert = e_type and false or true
        }
But wouldn't it be more elegant if we could use sets of filters instead?

Code: Select all

        local entities = surface.find_entities_filtered{
          { filter = "force", force = force_name },
          { filter = "type", type = e_type or not_burning_types, invert = true, mode = "and" }
        }
This has several advantages:
  • It's easy and intuitive to combine even contradicting conditions.
  • Therefore it could encourage leaner code (e.g. instead of making two surface searches and filter out entities that are found by both searches, we could reduce that to just one search with clever filtering).
  • It would make the code more readable. (find_entities_filtered takes the arguments area, position, radius, to_be_upgraded, limit AND name, type, ghost_name, ghost_type, direction, collision_mask, force. Of these, only the latter are actual filters that can be inverted. It's easy to overlook that a particular argument is or isn't a filter, so it's easy to introduce bugs -- just as it happened to me.)
Would you consider adding the kind of filtering used for events to searches of all kinds (surface.find_*_filtered, game.get_filtered_*_prototypes etc.)?
Last edited by Pi-C on Sun Nov 15, 2020 1:18 pm, edited 1 time in total.
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

PFQNiet
Filter Inserter
Filter Inserter
Posts: 289
Joined: Sat Sep 05, 2020 7:48 pm
Contact:

Re: Extended search filters

Post by PFQNiet »

I too have encountered a case where I would love to apply "invert" to just one filter, not all filters. My workaround was to just use the most limiting filter and then manually `if...` to filter out the remaining unwanted entities. So in your example I'd probably filter on e_type first and then check `if entity.force == force_name` inside the loop. Which is a mess that could easily be avoided with the same kind of filter compositing that events already allow.

So +1 from me, would make things easier, and in theory shouldn't conflict with existing mods as you could have it so that `find_entities_filtered` accepts a filter or an array of filters.

Rseding91
Factorio Staff
Factorio Staff
Posts: 13204
Joined: Wed Jun 11, 2014 5:23 am
Contact:

Re: Extended search filters

Post by Rseding91 »

Sorry but I don't see this happening. The cost to parse, construct, and use all of those filters for each entity-search would end up about a wash compared to just searching and filtering lua-side in most cases.
If you want to get ahold of me I'm almost always on Discord.

Post Reply

Return to “Won't implement”