How to use .Insert if I already know the Unit_Number of the chest?
How to use .Insert if I already know the Unit_Number of the chest?
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?
- Stringweasel
- Filter Inserter
- Posts: 324
- Joined: Thu Apr 27, 2017 8:22 pm
- Contact:
Re: How to use .Insert if I already know the Unit_Number of the chest?
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
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
Mods: Hall of Fame | Better Victory Screen | Fluidic Power | Biter Power | Space Spidertron | Spidertron Dock | Weasel's Demolition Derby
Re: How to use .Insert if I already know the Unit_Number of the chest?
Stringweasel wrote: βFri Jul 14, 2023 7:09 amThere'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 likeCode: 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.
Re: How to use .Insert if I already know the Unit_Number of the chest?
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
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
Re: How to use .Insert if I already know the Unit_Number of the chest?
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?Pi-C wrote: βFri Jul 14, 2023 8:02 amUsing 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: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.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
Re: How to use .Insert if I already know the Unit_Number of the chest?
Store it in global, exactly as they said.Fiend wrote: βFri Jul 14, 2023 8:21 amThanks 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?Pi-C wrote: βFri Jul 14, 2023 8:02 amUsing 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: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.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
My mods
Content: Lunar Landings | Freight Forwarding | Spidertron Patrols | Spidertron Enhancements | Power Overload
QoL: Factory Search | Remote Configuration | Module Inserter Simplified | Wire Shortcuts X | Ghost Warnings
Content: Lunar Landings | Freight Forwarding | Spidertron Patrols | Spidertron Enhancements | Power Overload
QoL: Factory Search | Remote Configuration | Module Inserter Simplified | Wire Shortcuts X | Ghost Warnings
Re: How to use .Insert if I already know the Unit_Number of the 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!
Re: How to use .Insert if I already know the Unit_Number of the 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.
Re: How to use .Insert if I already know the Unit_Number of the chest?
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!Stringweasel wrote: βFri Jul 14, 2023 7:09 amThere'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 likeCode: 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
Re: How to use .Insert if I already know the Unit_Number of the chest?
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!
Re: How to use .Insert if I already know the Unit_Number of the chest?
Thanks a ton, table_size worked like a charm, I've been fondling with the size thing for the past 2 daysPi-C wrote: βSun Jul 16, 2023 1:36 pmThere 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.