Hooking a non-logistic entity up to the logistics network

Place to get help with not working mods / modding interface.
Alex99
Burner Inserter
Burner Inserter
Posts: 6
Joined: Sat Aug 26, 2017 2:51 am
Contact:

Hooking a non-logistic entity up to the logistics network

Post by Alex99 »

Lets say you have a regular burner furnace entity. How would one go about adding that to the logistics network as a requester, then having it always request 10 coal that would go into it's fuel and have the player be unable to modify this request.

Thanks in advance :D
User avatar
DaveMcW
Smart Inserter
Smart Inserter
Posts: 3724
Joined: Tue May 13, 2014 11:06 am
Contact:

Re: Hooking a non-logistic entity up to the logistics network

Post by DaveMcW »

Code: Select all

/c
request_fuel = function(entity)
  if not entity or entity.type ~= "furnace" then return end
  local item = "coal"
  local add_more = true
  for fuel,amount in pairs(entity.get_inventory(defines.inventory.fuel).get_contents()) do
    if amount < 10 then
      item = fuel
    else
      add_more = false
    end
  end
  if add_more and entity.surface.count_entities_filtered{name="item-request-proxy", force=entity.force, area=entity.bounding_box} == 0 then
    entity.surface.create_entity{name="item-request-proxy", target=entity, force=entity.force, position=entity.position, modules={{item=item, count=10}}}
  end
end

request_fuel(game.player.selected)
This adds a temporary request for a single furnace. You need to add more logic to always update every furnace on the map.
Alex99
Burner Inserter
Burner Inserter
Posts: 6
Joined: Sat Aug 26, 2017 2:51 am
Contact:

Re: Hooking a non-logistic entity up to the logistics network

Post by Alex99 »

DaveMcW wrote:

Code: Select all

/c
request_fuel = function(entity)
  if not entity or entity.type ~= "furnace" then return end
  local item = "coal"
  local add_more = true
  for fuel,amount in pairs(entity.get_inventory(defines.inventory.fuel).get_contents()) do
    if amount < 10 then
      item = fuel
    else
      add_more = false
    end
  end
  if add_more and entity.surface.count_entities_filtered{name="item-request-proxy", force=entity.force, area=entity.bounding_box} == 0 then
    entity.surface.create_entity{name="item-request-proxy", target=entity, force=entity.force, position=entity.position, modules={{item=item, count=10}}}
  end
end

request_fuel(game.player.selected)
This adds a temporary request for a single furnace. You need to add more logic to always update every furnace on the map.
Thanks for the answer

So if I understand this correctly this creates a ghost entity that requires 10 coal to be constructed? then that coal is transferred into the furnace in question.

Also is there any way to use logistic bots instead of construction?
User avatar
DaveMcW
Smart Inserter
Smart Inserter
Posts: 3724
Joined: Tue May 13, 2014 11:06 am
Contact:

Re: Hooking a non-logistic entity up to the logistics network

Post by DaveMcW »

Alex99 wrote:Also is there any way to use logistic bots instead of construction?
Not easily. The only way is to create a hidden logistic entity that requests coal (see defines.events.on_built_entity), then code to transfer it to your furnace.
Alex99
Burner Inserter
Burner Inserter
Posts: 6
Joined: Sat Aug 26, 2017 2:51 am
Contact:

Re: Hooking a non-logistic entity up to the logistics network

Post by Alex99 »

DaveMcW wrote:Not easily. The only way is to create a hidden logistic entity that requests coal (see defines.events.on_built_entity), then code to transfer it to your furnace.
OK I think I can do that, is there an event that I could use that isn't on_tick as that would be pretty bad for ups?
User avatar
bobingabout
Smart Inserter
Smart Inserter
Posts: 7352
Joined: Fri May 09, 2014 1:01 pm
Contact:

Re: Hooking a non-logistic entity up to the logistics network

Post by bobingabout »

No, something that is constantly updated must be on the on_tick event. This is one reason why I personally do not include features of this nature in my mods.

There are however some guides around on how to attempt to offset the UPS disruption, such as only checking each chest for requests once a minute, but iterate through this list so that something is checked every tick.

something like

Code: Select all

for i, entity in pairs(list) do
if game.tick % 3600 == i % 3600 do
Creator of Bob's mods. Expanding your gameplay since version 0.9.8.
I also have a Patreon.
User avatar
Optera
Smart Inserter
Smart Inserter
Posts: 2920
Joined: Sat Jun 11, 2016 6:41 am
Contact:

Re: Hooking a non-logistic entity up to the logistics network

Post by Optera »

bobingabout wrote:No, something that is constantly updated must be on the on_tick event. This is one reason why I personally do not include features of this nature in my mods.

There are however some guides around on how to attempt to offset the UPS disruption, such as only checking each chest for requests once a minute, but iterate through this list so that something is checked every tick.

something like

Code: Select all

for i, entity in pairs(list) do
if game.tick % 3600 == i % 3600 do
modulo is expensive in to run twice for every entity every tick.
It's better to run it only once:

Code: Select all

function OnTick(event)
	local tick = game.tick
	for i=1, #global.VoidChests do
		if (i + tick) % update_interval == 0 then
			global.VoidChests[i].get_inventory(defines.inventory.chest).clear()
		end
	end
end
Even better, don't iterate over the whole array every tick instead step through it a few entites each tick spread out over whatever update cycle you choose.
I've benchmarked that to be 40% faster than above snippet in Void Chest +.

Code: Select all

function OnTick(event)
  global.tickCount = global.tickCount or 1
  global.chestIndex = global.chestIndex or 1

  -- only work if index is within bounds
  if global.chestIndex <= #global.VoidChests then
    local stopIndex = global.chestIndex + global.VoidChestStride - 1
    if stopIndex >= #global.VoidChests then
      stopIndex = #global.VoidChests
    end

    -- log("[VCP] "..global.tickCount.." / "..game.tick.." clearing chest ids "..global.chestIndex.." to "..stopIndex)
    for i=global.chestIndex, stopIndex do
      global.VoidChests[i].get_inventory(defines.inventory.chest).clear()
    end
    global.chestIndex = stopIndex + 1
  end

  -- reset clock and chest index
  if global.tickCount < update_interval then
    global.tickCount = global.tickCount + 1
  else
    global.tickCount = 1
    global.chestIndex = 1
  end
end
User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5211
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Hooking a non-logistic entity up to the logistics network

Post by eradicator »

Optera wrote: I've benchmarked that to be 40% faster than above snippet in Void Chest +
The last time i tried to to a tick-distrubuted update i used a construct like this:

Code: Select all

local UPDATEDELAY = 60

local function on_tick(event)
  local tock = tick % UPDATEDELAY
  for index=#global.mystuff-tock,1,-1*UPDATEDELAY do
    --stuff
    end
  end
Features:
  • one modulo per tick
  • only loops through entities that are actually updated
  • updates all entities in a fixed time window (aka does not get slower with more entities)
  • (con) if new entites are constructed during an updatecycle some will be skipped until the next cycle
It basically walks backwards through the array (because sometimes i need to delete stuff if it becomes invalid, and forward walking screws up the index in that case) and updates every n-th entity.

I only did some very short tests tho as in my case with very few entities (entity_count < UPDATEDELAY) the numbers seemed to suggest that doing them in bulk every n seconds was faster.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.
User avatar
Optera
Smart Inserter
Smart Inserter
Posts: 2920
Joined: Sat Jun 11, 2016 6:41 am
Contact:

Re: Hooking a non-logistic entity up to the logistics network

Post by Optera »

eradicator wrote:
Optera wrote: I've benchmarked that to be 40% faster than above snippet in Void Chest +
The last time i tried to to a tick-distrubuted update i used a construct like this:

Code: Select all

local UPDATEDELAY = 60

local function on_tick(event)
  local tock = tick % UPDATEDELAY
  for index=#global.mystuff-tock,1,-1*UPDATEDELAY do
    --stuff
    end
  end
Features:
  • one modulo per tick
  • only loops through entities that are actually updated
  • updates all entities in a fixed time window (aka does not get slower with more entities)
  • (con) if new entites are constructed during an updatecycle some will be skipped until the next cycle
It basically walks backwards through the array (because sometimes i need to delete stuff if it becomes invalid, and forward walking screws up the index in that case) and updates every n-th entity.

I only did some very short tests tho as in my case with very few entities (entity_count < UPDATEDELAY) the numbers seemed to suggest that doing them in bulk every n seconds was faster.
That's a nifty way of stepping with stride.
Benchmark results for 480 Void chests using either version as on_tick over 100k ticks:
My current version: avg 11805.281 ms
Your version: avg 11776.126ms

Conclusion:
Your version is 0.25% faster in this benchmark. Both versions may be too fast to be accurately benchmarked with "only" 480 void Chests.
Overall I'd say the wiki should be updated to use your version. It's far more simplistic and unless you have to call different functions depending on accurate execution order your stepping with stride will work perfectly fine.
Bilka
Factorio Staff
Factorio Staff
Posts: 3310
Joined: Sat Aug 13, 2016 9:20 am
Contact:

Re: Hooking a non-logistic entity up to the logistics network

Post by Bilka »

Optera wrote: Overall I'd say the wiki should be updated to use your version. It's far more simplistic and unless you have to call different functions depending on accurate execution order your stepping with stride will work perfectly fine.
Which wiki (page) talks about this?
I'm an admin over at https://wiki.factorio.com. Feel free to contact me if there's anything wrong (or right) with it.
User avatar
Optera
Smart Inserter
Smart Inserter
Posts: 2920
Joined: Sat Jun 11, 2016 6:41 am
Contact:

Re: Hooking a non-logistic entity up to the logistics network

Post by Optera »

Bilka wrote:
Optera wrote: Overall I'd say the wiki should be updated to use your version. It's far more simplistic and unless you have to call different functions depending on accurate execution order your stepping with stride will work perfectly fine.
Which wiki (page) talks about this?
Well if there isn't a guide or wiki page about how to properly create an on_tick handler there should be one.
I've seen way too many mods wasting a ton of performance due to bad on_tick iteration.
User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5211
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Hooking a non-logistic entity up to the logistics network

Post by eradicator »

Optera wrote: That's a nifty way of stepping with stride.
Benchmark results for 480 Void chests using either version as on_tick over 100k ticks:
My current version: avg 11805.281 ms
Your version: avg 11776.126ms
Thanks (also for the numbers). It's amazing that the difference would be so tiny despite your frequent access to the global table for index management. I guess my intuition about what is expensive and what isn't is pretty bad :D. Also btw in your code i think you can call .clear_items_inside() directly on the chest entity, which should save the get_inventory call and thus the passing of another reference object over the c++/lua barrier.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.
User avatar
Optera
Smart Inserter
Smart Inserter
Posts: 2920
Joined: Sat Jun 11, 2016 6:41 am
Contact:

Re: Hooking a non-logistic entity up to the logistics network

Post by Optera »

eradicator wrote: Thanks (also for the numbers). It's amazing that the difference would be so tiny despite your frequent access to the global table for index management. I guess my intuition about what is expensive and what isn't is pretty bad :D. Also btw in your code i think you can call .clear_items_inside() directly on the chest entity, which should save the get_inventory call and thus the passing of another reference object over the c++/lua barrier.
Thanks, I didn't even know a shortcut like clear_items_inside() existed.
Using that instead of get_inventory(defines.inventory.chest).clear() made a much more noticeable difference than the on_tick handlers.
clear_items_inside(): avg 10162.627 ms ~13% faster

I'm surprised how close they are together myself, but then again the slowest part in any of my mods is read or write to entities through the API.
Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

Re: Hooking a non-logistic entity up to the logistics network

Post by Nexela »

Want it to be even faster? Save the inventory and work directly on that

global.chest = {ent = ent, inv = ent.get_inventory(defines.inventory.chest)}

global.chest.inv.clear()
Post Reply

Return to “Modding help”