How to use .Insert if I already know the Unit_Number of the chest?

Place to get help with not working mods / modding interface.
Post Reply
Fiend
Burner Inserter
Burner Inserter
Posts: 11
Joined: Tue Sep 08, 2020 7:56 pm
Contact:

How to use .Insert if I already know the Unit_Number of the chest?

Post by Fiend »

I'm new to LUA and modding, assume I already created a chest in a previous save and stored its unit_number in a table, now I want to use the unit_number as a reference to .insert stuff into the chest, how do I do that?

User avatar
Stringweasel
Filter Inserter
Filter Inserter
Posts: 320
Joined: Thu Apr 27, 2017 8:22 pm
Contact:

Re: How to use .Insert if I already know the Unit_Number of the chest?

Post by Stringweasel »

There's no built-in way do get an entity (the chest) from the unit number. What modders usually do is to store a reference to the entity in global.

Something like

Code: Select all


function on_created(entity)
	global.my_chests[entity.unit_number] = entity
end

function insert_stuff(unit_number)
	local entity = global.my_chests[unit_number]
    
	-- Always make sure the entity is still valid.
	-- It might have been removed in the mean time
	if not entity or not entity.valid then return end
    
	-- insert stuff into the chest
end
Alt-F4 Author | Factorio Modder
Mods: Hall of Fame | Better Victory Screen | Fluidic Power | Biter Power | Space Spidertron | Spidertron Dock | Weasel's Demolition Derby

Fiend
Burner Inserter
Burner Inserter
Posts: 11
Joined: Tue Sep 08, 2020 7:56 pm
Contact:

Re: How to use .Insert if I already know the Unit_Number of the chest?

Post by Fiend »

Stringweasel wrote: ↑
Fri Jul 14, 2023 7:09 am
There's no built-in way do get an entity (the chest) from the unit number. What modders usually do is to store a reference to the entity in global.

Something like

Code: Select all


function on_created(entity)
	global.my_chests[entity.unit_number] = entity
end

function insert_stuff(unit_number)
	local entity = global.my_chests[unit_number]
    
	-- Always make sure the entity is still valid.
	-- It might have been removed in the mean time
	if not entity or not entity.valid then return end
    
	-- insert stuff into the chest
end


Thanks, this is why I see mods using find_entity every time they want to reference that entity.

Pi-C
Smart Inserter
Smart Inserter
Posts: 1654
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: How to use .Insert if I already know the Unit_Number of the chest?

Post by Pi-C »

Fiend wrote: ↑
Fri Jul 14, 2023 7:22 am
Thanks, this is why I see mods using find_entity every time they want to reference that entity.
Using find_entity, find_entities, and find_entities_filtered every time you want to reference an entity is bad practice: If you only have a unit_number, you don't know anything about the entity. However, find_entity etc. are methods of the class LuaSurface, which means you must (but really can't) know which surface it is on to search for the entity, or you must loop over all surfaces until you've found the entity you're looking for. Even with just one surface, searching the entire surface may take some time if many chunks have been generated; and mods like SE or Factorissimo may generate enough surfaces to make the following execute rather slowly:

Code: Select all

local function find_entity_by_id(unit_number)
	for s, surface in pairs(game.surfaces) do
		for e, entity in ipairs( surface.find_entities() ) do
			if entity.unit_number == unit_number then
				return entity
			end
		end
	end
end
Generally, if you know that you'll refer to the same entity repeatedly, you should store it in your global table (as demonstrated by Stringweasel) and only use find_entity etc. in migration files or in your handler for on_configuration_changed to create or rebuild the global table.
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

Fiend
Burner Inserter
Burner Inserter
Posts: 11
Joined: Tue Sep 08, 2020 7:56 pm
Contact:

Re: How to use .Insert if I already know the Unit_Number of the chest?

Post by Fiend »

Pi-C wrote: ↑
Fri Jul 14, 2023 8:02 am
Fiend wrote: ↑
Fri Jul 14, 2023 7:22 am
Thanks, this is why I see mods using find_entity every time they want to reference that entity.
Using find_entity, find_entities, and find_entities_filtered every time you want to reference an entity is bad practice: If you only have a unit_number, you don't know anything about the entity. However, find_entity etc. are methods of the class LuaSurface, which means you must (but really can't) know which surface it is on to search for the entity, or you must loop over all surfaces until you've found the entity you're looking for. Even with just one surface, searching the entire surface may take some time if many chunks have been generated; and mods like SE or Factorissimo may generate enough surfaces to make the following execute rather slowly:

Code: Select all

local function find_entity_by_id(unit_number)
	for s, surface in pairs(game.surfaces) do
		for e, entity in ipairs( surface.find_entities() ) do
			if entity.unit_number == unit_number then
				return entity
			end
		end
	end
end
Generally, if you know that you'll refer to the same entity repeatedly, you should store it in your global table (as demonstrated by Stringweasel) and only use find_entity etc. in migration files or in your handler for on_configuration_changed to create or rebuild the global table.
Thanks this was very helpful information, so in my case I am creating a new surface and placing a chest on it, so I know the surface name, and I know the chest number, what would be an efficient way to use those to insert inventory into that chest?

Xorimuth
Filter Inserter
Filter Inserter
Posts: 625
Joined: Sat Mar 02, 2019 9:39 pm
Contact:

Re: How to use .Insert if I already know the Unit_Number of the chest?

Post by Xorimuth »

Fiend wrote: ↑
Fri Jul 14, 2023 8:21 am
Pi-C wrote: ↑
Fri Jul 14, 2023 8:02 am
Fiend wrote: ↑
Fri Jul 14, 2023 7:22 am
Thanks, this is why I see mods using find_entity every time they want to reference that entity.
Using find_entity, find_entities, and find_entities_filtered every time you want to reference an entity is bad practice: If you only have a unit_number, you don't know anything about the entity. However, find_entity etc. are methods of the class LuaSurface, which means you must (but really can't) know which surface it is on to search for the entity, or you must loop over all surfaces until you've found the entity you're looking for. Even with just one surface, searching the entire surface may take some time if many chunks have been generated; and mods like SE or Factorissimo may generate enough surfaces to make the following execute rather slowly:

Code: Select all

local function find_entity_by_id(unit_number)
	for s, surface in pairs(game.surfaces) do
		for e, entity in ipairs( surface.find_entities() ) do
			if entity.unit_number == unit_number then
				return entity
			end
		end
	end
end
Generally, if you know that you'll refer to the same entity repeatedly, you should store it in your global table (as demonstrated by Stringweasel) and only use find_entity etc. in migration files or in your handler for on_configuration_changed to create or rebuild the global table.
Thanks this was very helpful information, so in my case I am creating a new surface and placing a chest on it, so I know the surface name, and I know the chest number, what would be an efficient way to use those to insert inventory into that chest?
Store it in global, exactly as they said.
My mods
Content: Freight Forwarding | Spidertron Patrols | Spidertron Enhancements | Power Overload
QoL: Factory Search | Remote Configuration | Module Inserter Simplified | Wire Shortcuts X | Ghost Warnings

Pi-C
Smart Inserter
Smart Inserter
Posts: 1654
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: How to use .Insert if I already know the Unit_Number of the chest?

Post by Pi-C »

Fiend wrote: ↑
Fri Jul 14, 2023 8:21 am
Thanks this was very helpful information, so in my case I am creating a new surface and placing a chest on it, so I know the surface name, and I know the chest number, what would be an efficient way to use those to insert inventory into that chest?
It depends on your use case. Suppose you want to place a chest on a hidden surface that collects all loot for the player who kills an enemy. In this case, you'd need not one chest, but one for every player. You would create the chest in response to on_player_created, remove it when the player is removed, create a new chest if the old one dies, and try to insert items whenever the player kills something. Then, it would be a good idea to make 2 tables (one for chests, one for players) and crossreference them:

Code: Select all

local function create_chest_for_player(p)
  if p and game.players[p] then
    local new_chest = your_surface.create_entity{
      -- Replace name and position with the values you need!
      name = "name-of-your-chest-prototype",
      position = {x, y},
      -- If not set, force will default to "enemy"
      force = game.players[p].force,
    }
    local id = new_chest and new_chest.valid and new_chest.unit_number
    
    global.chests[id] = { entity = new_chest, player = p}
    global.chests_by_player[p] = id
  end
end


script.event(defines.events.on_player_created, function(event)
  global.chests = global.chests or {}
  global.chests_by_player = global.chests_by_player or {}

  create_chest_for_player(event.player_index)
end)


script.event(defines.events.on_player_removed, function(event)
  local p = event.player_index
  local id = global.chests_by_player[p]

  -- Remove chest entity
  local chest = global.chests[id].entity 
  if chest and chest.valid then
    chest.destroy()
  end

  -- Clean tables
  global.chests[id] = nil
  global.chests_by_player[p] = nil
end)


script.event(defines.events.on_entity_died, function(event)
  -- If somehow one of your chests has been killed, replace it with a new empty chest
  if global.chests[event.entity.unit_number] then
    create_chest_for_player(global.chests[event.entity.unit_number].player)
    --Remove entry for old chest!
    global.chests[event.entity.unit_number] = nil
    return
  end
    

  -- A player killed something
  local player = event.cause and event.cause.valid and
                  event.cause.type == "character" and event.cause.player

  if player and player.valid then
    local id = global.chests_by_player[player.index]
    local chest = global.chests[id].entity
    local inventory = chest and chest.valid and
                      chest.get_inventory(defines.inventory.chest)

    local stack, inserted
    
    -- There will always be an inventory event.loot, but it may be an empty table
    for name, count in pairs(event.loot.get_contents()) do
      stack = { name = name, count = count }
      if inventory and inventory.valid and inventory.can_insert(stack) then
        -- We can insert at least one item, and perhaps even everything in the stack!
        inserted = inventory.insert(stack)
        -- So far, we've just read the loot inventory, but didn't change it!
        event.loot.remove(name = name, count = inserted)
      end
    end
  end
end)
Last edited by Pi-C on Tue Sep 12, 2023 4:00 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!

Nidan
Fast Inserter
Fast Inserter
Posts: 229
Joined: Sat Nov 21, 2015 1:40 am
Contact:

Re: How to use .Insert if I already know the Unit_Number of the chest?

Post by Nidan »

Fiend wrote: ↑
Fri Jul 14, 2023 8:21 am
Thanks this was very helpful information, so in my case I am creating a new surface and placing a chest on it, so I know the surface name, and I know the chest number, what would be an efficient way to use those to insert inventory into that chest?
Since you're placing the chest yourself, just remember the return value of create_entity since that is your chest. You don't need the unit_number in this case.

Fiend
Burner Inserter
Burner Inserter
Posts: 11
Joined: Tue Sep 08, 2020 7:56 pm
Contact:

Re: How to use .Insert if I already know the Unit_Number of the chest?

Post by Fiend »

Stringweasel wrote: ↑
Fri Jul 14, 2023 7:09 am
There's no built-in way do get an entity (the chest) from the unit number. What modders usually do is to store a reference to the entity in global.

Something like

Code: Select all


function on_created(entity)
	global.my_chests[entity.unit_number] = entity
end

function insert_stuff(unit_number)
	local entity = global.my_chests[unit_number]
    
	-- Always make sure the entity is still valid.
	-- It might have been removed in the mean time
	if not entity or not entity.valid then return end
    
	-- insert stuff into the chest
end
How do I count the items in the Global table? I have used this approach and was able to store values in the global table, I can also see these values using the GVV Mod (https://mods.factorio.com/mod/gvv) my problem now is that I can't figure out a way to count the items in the Global table, I tried using #global.my_table but it always returns the value Zero!

Pi-C
Smart Inserter
Smart Inserter
Posts: 1654
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: How to use .Insert if I already know the Unit_Number of the chest?

Post by Pi-C »

Fiend wrote: ↑
Sun Jul 16, 2023 12:43 pm
I tried using #global.my_table but it always returns the value Zero!
There are gaps in the table (non-consecutive index numbers), so #global.my_table won't work, and neither will ipairs(global.my_table). You can use these safely if you know that your table will be indexed with consecutive numbers, which is the case for tables returned by LuaSurface.find_entities() etc. Whenever you use unit_numbers or strings as table index, you must use table_size(global.my_table) or pairs(global.my_table) instead.

If you haven't done so yet, you should check out the libraries and functions that Factorio provides or changes.
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

Fiend
Burner Inserter
Burner Inserter
Posts: 11
Joined: Tue Sep 08, 2020 7:56 pm
Contact:

Re: How to use .Insert if I already know the Unit_Number of the chest?

Post by Fiend »

Pi-C wrote: ↑
Sun Jul 16, 2023 1:36 pm
Fiend wrote: ↑
Sun Jul 16, 2023 12:43 pm
I tried using #global.my_table but it always returns the value Zero!
There are gaps in the table (non-consecutive index numbers), so #global.my_table won't work, and neither will ipairs(global.my_table). You can use these safely if you know that your table will be indexed with consecutive numbers, which is the case for tables returned by LuaSurface.find_entities() etc. Whenever you use unit_numbers or strings as table index, you must use table_size(global.my_table) or pairs(global.my_table) instead.

If you haven't done so yet, you should check out the libraries and functions that Factorio provides or changes.
Thanks a ton, table_size worked like a charm, I've been fondling with the size thing for the past 2 days

Post Reply

Return to β€œModding help”