Page 1 of 1

Hooking a non-logistic entity up to the logistics network

Posted: Mon Sep 04, 2017 10:38 pm
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

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

Posted: Tue Sep 05, 2017 12:58 am
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.

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

Posted: Tue Sep 05, 2017 1:43 am
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?

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

Posted: Tue Sep 05, 2017 1:59 am
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.

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

Posted: Tue Sep 05, 2017 2:08 am
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?

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

Posted: Tue Sep 05, 2017 8:11 am
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

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

Posted: Tue Sep 05, 2017 1:10 pm
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

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

Posted: Wed Sep 06, 2017 9:57 am
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.

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

Posted: Wed Sep 06, 2017 1:03 pm
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.

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

Posted: Wed Sep 06, 2017 1:19 pm
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?

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

Posted: Wed Sep 06, 2017 2:31 pm
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.

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

Posted: Thu Sep 07, 2017 7:42 am
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.

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

Posted: Thu Sep 07, 2017 10:56 am
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.

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

Posted: Thu Sep 07, 2017 2:22 pm
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()