Reworked suppliers to be chests that passively supply items

This commit is contained in:
Zenon Seth 2023-11-10 00:12:07 +00:00
parent 3cc6cc7c88
commit 3c2d91b63d
18 changed files with 155 additions and 158 deletions

View File

@ -12,8 +12,6 @@ local function get_controller_formspec(pos)
"button[5.6,0.6;3,0.8;"..SET_BUTTON..";Set]"
end
local function show_controller_formspec(pos, playerName)
local pInfo = {}
pInfo.position = pos
@ -37,7 +35,12 @@ local function on_controller_receive_fields(player, formname, fields)
return true
end
local function after_controller_place(pos)
logistica.start_controller_timer(pos)
end
-- registration stuff
minetest.register_on_player_receive_fields(on_controller_receive_fields)
--[[
@ -52,7 +55,7 @@ minetest.register_on_player_receive_fields(on_controller_receive_fields)
tier may be `nil` which will result in the controller connecting to everything
]]
function logistica.register_controller(simpleName, def, tier)
function logistica.register_controller(name, def, tier)
local controller_group = nil
if not tier then
tier = logistica.TIER_ALL
@ -62,7 +65,7 @@ function logistica.register_controller(simpleName, def, tier)
logistica.tiers[ltier] = true
controller_group = logistica.get_machine_group(ltier)
end
local controller_name = "logistica:" .. string.lower(simpleName:gsub(" ", "_")) .. "_controller"
local controller_name = "logistica:" .. string.lower(name:gsub(" ", "_"))
logistica.controllers[controller_name] = tier
local on_construct = function(pos)
@ -70,15 +73,16 @@ function logistica.register_controller(simpleName, def, tier)
logistica.on_controller_change(pos, nil)
end
local after_destruct = logistica.on_controller_change
local on_timer = logistica.on_controller_timer
if not def.groups then
def.groups = {}
end
def.groups[controller_group] = 1
def.groups[logistica.TIER_CONTROLLER] = 1
def.on_timer = logistica.on_controller_timer
def.on_construct = on_construct
def.after_destruct = after_destruct
def.on_timer = logistica.on_timer_powered(on_timer)
def.after_place_node = after_controller_place
def.drop = controller_name
def.on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
if clicker and clicker:is_player() then
@ -101,7 +105,7 @@ function logistica.register_controller(simpleName, def, tier)
minetest.register_node(controller_name.."_disabled", def_disabled)
end
logistica.register_controller("Simple Controller", {
logistica.register_controller("simple_controller", {
description = "Simple Controller",
tiles = { "logistica_silver_cable.png" },
groups = {

View File

@ -1,24 +1,21 @@
local NUM_SUPPLY_SLOTS = 8
local PULL_LIST_PICKER = "pull_pick"
local ON_OFF_BUTTON = "on_off_btn"
local FORMSPEC_NAME = "logistica_supplier"
local ON_OFF_BUTTON = "on_off_btn"
local supplierForms = {}
local function get_supplier_formspec(pos)
local posForm = "nodemeta:"..pos.x..","..pos.y..","..pos.z
local pushPos = logistica.get_supplier_target(pos)
local selectedList = logistica.get_supplier_target_list(pos)
local isOn = logistica.is_machine_on(pos)
return "formspec_version[4]" ..
"size[10.6,8]" ..
"size[10.5,12]" ..
logistica.ui.background..
logistica.ui.pull_list_picker(PULL_LIST_PICKER, 6.7, 0.7, pushPos, selectedList, "Supply from:")..
logistica.ui.on_off_btn(isOn, 9.3, 0.5, ON_OFF_BUTTON, "Enable")..
"label[0.6,1.2;Items to make available as Supply. If left empty, nothing is supplied]"..
"list["..posForm..";filter;0.5,1.5;"..NUM_SUPPLY_SLOTS..",1;0]"..
"list[current_player;main;0.5,3;8,4;0]"
logistica.ui.on_off_btn(isOn, 9.0, 0.3, ON_OFF_BUTTON, "Enable")..
"label[0.6,1.0;Passive Supplier: Items become available to network requests.]"..
"list["..posForm..";main;0.4,1.4;8,4;0]"..
"list[current_player;main;0.4,7.0;8,4;0]"..
"listring[]"
end
local function show_supplier_formspec(playerName, pos)
@ -40,13 +37,6 @@ local function on_player_receive_fields(player, formname, fields)
if not pos then return false end
logistica.toggle_machine_on_off(pos)
show_supplier_formspec(player:get_player_name(), pos)
elseif fields[PULL_LIST_PICKER] then
local selected = fields[PULL_LIST_PICKER]
if logistica.is_allowed_pull_list(selected) then
local pos = supplierForms[playerName].position
if not pos then return false end
logistica.set_supplier_target_list(pos, selected)
end
end
return true
end
@ -56,40 +46,32 @@ local function on_supplier_rightclick(pos, node, clicker, itemstack, pointed_thi
show_supplier_formspec(clicker:get_player_name(), pos)
end
local function after_place_supplier(pos, placer, itemstack, numRequestSlots)
local function after_place_supplier(pos, placer, itemstack)
local meta = minetest.get_meta(pos)
if placer and placer:is_player() then
meta:set_string("owner", placer:get_player_name())
end
logistica.toggle_machine_on_off(pos)
logistica.set_supplier_target_list(pos, "dst")
local inv = meta:get_inventory()
inv:set_size("filter", numRequestSlots)
inv:set_size("main", logistica.get_supplier_inv_size(pos))
logistica.set_node_tooltip_from_state(pos)
logistica.on_supplier_change(pos)
end
local function allow_supplier_storage_inv_put(pos, listname, index, stack, player)
if listname ~= "filter" then return 0 end
local inv = minetest.get_meta(pos):get_inventory()
local newStack = ItemStack(stack)
newStack:set_count(1)
inv:set_stack(listname, index, newStack)
logistica.update_cache_at_pos(pos, LOG_CACHE_SUPPLIER)
return 0
return stack:get_count()
end
local function allow_supplier_inv_take(pos, listname, index, stack, player)
if listname ~= "filter" then return 0 end
local inv = minetest.get_meta(pos):get_inventory()
local slotStack = inv:get_stack(listname, index)
slotStack:take_item(stack:get_count())
inv:set_stack(listname, index, slotStack)
logistica.update_cache_at_pos(pos, LOG_CACHE_SUPPLIER)
return 0
return stack:get_count()
end
local function allow_supplier_inv_move(pos, from_list, from_index, to_list, to_index, count, player)
return 0
return count
end
local function on_suppler_inventory_put(pos, listname, index, stack, player)
logistica.update_cache_at_pos(pos, LOG_CACHE_SUPPLIER)
end
local function on_suppler_inventory_take(pos, listname, index, stack, player)
logistica.update_cache_at_pos(pos, LOG_CACHE_SUPPLIER)
end
----------------------------------------------------------------
@ -102,41 +84,34 @@ minetest.register_on_player_receive_fields(on_player_receive_fields)
-- Public Registration API
----------------------------------------------------------------
-- `simpleName` is used for the description and for the name (can contain spaces)
-- `maxTransferRate` indicates how many nodes at a time this supplier can take when asked
function logistica.register_supplier(simpleName, maxTransferRate)
local lname = string.lower(simpleName:gsub(" ", "_"))
local supplier_name = "logistica:supplier_"..lname
-- `inventorySize` should be 32 at max (aka regular chest)
function logistica.register_supplier(desc, name, inventorySize, tiles)
local lname = string.lower(name:gsub(" ", "_"))
local supplier_name = "logistica:passive_supplier_"..lname
logistica.suppliers[supplier_name] = true
local grps = {oddly_breakable_by_hand = 3, cracky = 3 }
grps[logistica.TIER_ALL] = 1
local def = {
description = simpleName.." Supplier",
description = desc,
drawtype = "normal",
tiles = {
"logistica_"..lname.."_supplier_side.png^[transformR270",
"logistica_"..lname.."_supplier_side.png^[transformR90",
"logistica_"..lname.."_supplier_side.png^[transformR180",
"logistica_"..lname.."_supplier_side.png",
"logistica_"..lname.."_supplier_back.png",
"logistica_"..lname.."_supplier_front.png",
},
tiles = tiles,
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
groups = grps,
drop = supplier_name,
sounds = logistica.node_sound_metallic(),
after_place_node = function (pos, placer, itemstack)
after_place_supplier(pos, placer, itemstack, NUM_SUPPLY_SLOTS)
end,
after_place_node = after_place_supplier,
after_destruct = logistica.on_supplier_change,
on_rightclick = on_supplier_rightclick,
allow_metadata_inventory_put = allow_supplier_storage_inv_put,
allow_metadata_inventory_take = allow_supplier_inv_take,
allow_metadata_inventory_move = allow_supplier_inv_move,
on_metadata_inventory_put = on_suppler_inventory_put,
on_metadata_inventory_take = on_suppler_inventory_take,
logistica = {
supplier_transfer_rate = maxTransferRate,
on_power = function() end, -- necessary for power toggle to work
inventory_size = inventorySize,
on_power = function(pos, power) logistica.set_node_tooltip_from_state(pos, nil, power) end
}
}
@ -159,5 +134,11 @@ function logistica.register_supplier(simpleName, maxTransferRate)
end
logistica.register_supplier("Item", 1)
logistica.register_supplier("Stack", 99)
logistica.register_supplier("Passive Supplier", "simple", 32, {
"logistica_passive_supplier_top.png",
"logistica_passive_supplier_bottom.png",
"logistica_passive_supplier_side.png^[transformFX",
"logistica_passive_supplier_side.png",
"logistica_passive_supplier_side.png",
"logistica_passive_supplier_front.png",
}) -- default supplier

View File

@ -1,42 +1,19 @@
--[[
Outline of controller tick/s:
1. Gather request from each requester, add them to queue
- smart queue needed, unify request per requester
2. For the first N requests, check each supplier and if applicable fulfil request
3. Gather all storage slots
- cached, hopefully
4. For each storage slot, check each supplier, and pull up to S items per slot into storage
]]
local TIMER_DURATION_SHORT = 0.5
local TIMER_DURATION_LONG = 2.0
local TIMER_DURATION_LONG = 5
function logistica.start_controller_timer(pos, duration)
if duration == nil then duration = TIMER_DURATION_LONG end
local timer = minetest.get_node_timer(pos)
timer:start(duration)
logistica.start_node_timer(pos, duration)
end
function logistica.on_controller_timer(pos, elapsed)
if pos then return false end --
local node = minetest.get_node(pos)
if not node then return false end -- what?
if node.name:find("_disabled") then return false end -- disabled controllers don't do anything
local had_demand = false
local node = minetest.get_node_or_nil(pos)
if not node then return true end -- what?
if not logistica.is_controller(node.name) then return false end
local network = logistica.get_network_or_nil(pos)
if not network then
logistica.on_controller_change(pos, nil) -- this should re-scan the network
logistica.on_controller_change(pos) -- this should re-scan the network
end
network = logistica.get_network_or_nil(pos)
if not network then return true end -- something went wrong, retry again
if had_demand then
logistica.start_controller_timer(pos, TIMER_DURATION_SHORT)
else
logistica.start_controller_timer(pos, TIMER_DURATION_LONG)
end
return false
return true
end

View File

@ -10,6 +10,7 @@ logistica.item_storage = {}
logistica.tiers = {}
logistica.TIER_ALL = "logistica_all_tiers"
logistica.GROUP_ALL = "group:" .. logistica.TIER_ALL
logistica.TIER_CONTROLLER = "controller"
function logistica.get_cable_group(tier)
return "logistica_" .. tier .. "_cable"

View File

@ -38,12 +38,9 @@ function logistica.start_injector_timer(pos)
end
function logistica.on_injector_timer(pos, elapsed)
if not logistica.is_machine_on(pos) then return false end
local networkId = logistica.get_network_id_or_nil(pos)
if not networkId then
logistica.toggle_machine_on_off(pos)
logistica.set_node_tooltip_from_state(pos)
return false
end
if not networkId then return false end
logistica.set_node_tooltip_from_state(pos)
local node = minetest.get_node_or_nil(pos)

View File

@ -10,7 +10,7 @@ LOG_CACHE_MASS_STORAGE = {
nodes = function (network) return network.mass_storage end,
}
LOG_CACHE_SUPPLIER = {
listName = "filter",
listName = "main",
clear = function (network) network.supplier_cache = {} end,
cache = function (network) return network.supplier_cache end,
nodes = function (network) return network.suppliers end,
@ -38,7 +38,11 @@ end
local function list_to_cache_nameset(list)
local ret = {}
for i, item in ipairs(list) do ret[item:get_name()] = true end
for i, item in ipairs(list) do
if item:get_name() ~= "" then
ret[item:get_name()] = true
end
end
return ret
end
@ -90,10 +94,12 @@ local function update_network_cache(network, cacheOps)
logistica.load_position(storagePos)
local nodeMeta = get_meta(storagePos)
local list = nodeMeta:get_inventory():get_list(listName) or {}
for _, itemStack in pairs(list) do
for _, itemStack in ipairs(list) do
local name = itemStack:get_name()
if not cache[name] then cache[name] = {} end
cache[name][hash] = true
if name ~= "" then
if not cache[name] then cache[name] = {} end
cache[name][hash] = true
end
end
save_prev_cache(nodeMeta, listName, list_to_cache_nameset(list))
end
@ -112,12 +118,12 @@ local function update_network_cache_for_pos(pos, cacheOps)
local cache = cacheOps.cache(network)
local remAndAdd = diff(prevCacheItems, currCacheItems)
for name, _ in pairs(remAndAdd[1]) do
local posCache = cache[name]
if posCache then posCache[hash] = nil end
if cache[name] then cache[name][hash] = nil end
if logistica.table_is_empty(cache[name]) then cache[name] = nil end
end
for name, _ in pairs(remAndAdd[2]) do
local posCache = cache[name]
if posCache then posCache[hash] = true end
if not cache[name] then cache[name] = {} end
if cache[name] then cache[name][hash] = true end
end
save_prev_cache(meta, listName, currCacheItems)
end

View File

@ -1,17 +1,30 @@
local MASS_STORAGE_LIST_NAME = "storage"
local ITEM_STORAGE_LIST_NAME = "main"
local SUPPLIER_LIST_NAME = "main"
local function get_meta(pos)
logistica.load_position(pos)
return minetest.get_meta(pos)
end
local function updateSupplierCacheFor(supplierPosList)
for _, pos in ipairs(supplierPosList) do
logistica.update_cache_at_pos(pos, LOG_CACHE_SUPPLIER)
end
end
-- tries to take a stack from the network locations
-- calls the collectorFunc with the stack - collectorFunc needs to return how many were left-over<br>
-- `collectorFunc = function(stackToInsert)`<br>
-- note that it may be called multiple times as the itemstack is gathered from mass storage
function logistica.take_stack_from_network(stackToTake, network, collectorFunc, isAutomatedRequest)
if not network then return false end
-- first check suppliers
if logistica.take_stack_from_suppliers(stackToTake, network, collectorFunc, isAutomatedRequest) then
return
end
-- then check storages
if stackToTake:get_stack_max() <= 1 then
logistica.take_stack_from_item_storage(stackToTake, network, collectorFunc, isAutomatedRequest)
else
@ -19,6 +32,52 @@ function logistica.take_stack_from_network(stackToTake, network, collectorFunc,
end
end
-- tries to take the given stack from the passive suppliers on the network
-- calls the collectorFunc with the stack when necessary
-- note that it may be called multiple times as the itemstack is gathered from mass storage
function logistica.take_stack_from_suppliers(stackToTake, network, collectorFunc, isAutomatedRequest)
local requestedAmount = stackToTake:get_count()
local remaining = requestedAmount
local stackName = stackToTake:get_name()
local validSupplers = network.supplier_cache[stackName] or {}
local modifiedPos = {}
for hash, _ in pairs(validSupplers) do
local supplierPos = minetest.get_position_from_hash(hash)
local supplierInv = get_meta(supplierPos):get_inventory()
local machineIsOn = logistica.is_machine_on(supplierPos)
local supplyList = (machineIsOn and supplierInv:get_list(SUPPLIER_LIST_NAME)) or {}
for i, supplyStack in ipairs(supplyList) do
if supplyStack:get_name() == stackName then
table.insert(modifiedPos, supplierPos)
local supplyCount = supplyStack:get_count()
if supplyCount >= remaining then -- enough to fulfil requested
local toSend = ItemStack(supplyStack) ; toSend:set_count(remaining)
local leftover = collectorFunc(toSend)
supplyStack:set_count(supplyCount - remaining + leftover)
supplierInv:set_stack(SUPPLIER_LIST_NAME, i, supplyStack)
updateSupplierCacheFor(modifiedPos)
return true
else -- not enough to fulfil requested
local toSend = ItemStack(supplyStack)
local leftover = collectorFunc(toSend)
remaining = remaining - (supplyCount - leftover)
supplyStack:set_count(leftover)
if leftover > 0 then -- for some reason we could not insert all - exit early
supplierInv:set_stack(SUPPLIER_LIST_NAME, i, supplyStack)
updateSupplierCacheFor(modifiedPos)
return true
end
end
end
end
-- if we get there, we did not fulfil the request from this supplier
-- but some items still may have been inserted
if machineIsOn then supplierInv:set_list(SUPPLIER_LIST_NAME, supplyList) end
end
updateSupplierCacheFor(modifiedPos)
return false
end
-- calls the collectorFunc with the stack - collectorFunc needs to return how many were left-over<br>
-- `collectorFunc = function(stackToInsert)`<br>
-- returns true if item successfully found and given to collector, false otherwise

View File

@ -139,12 +139,9 @@ function logistica.start_requester_timer(pos, duration)
end
function logistica.on_requester_timer(pos, elapsed)
if not logistica.is_machine_on(pos) then return false end
local network = logistica.get_network_or_nil(pos)
if not network then
logistica.toggle_machine_on_off(pos)
logistica.set_node_tooltip_from_state(pos)
return false
end
if not network then return false end
logistica.set_node_tooltip_from_state(pos)
update_requester_actual_request(pos)
if take_requested_items_from_network(pos, network) then

View File

@ -1,34 +1,12 @@
local META_SUPPLIER_LISTNAME = "suptarlist"
local META_SUPPLIER_LIST = "main"
function logistica.get_supplier_target(pos)
local node = minetest.get_node_or_nil(pos)
if not node then return nil end
local shift = logistica.get_rot_directions(node.param2).backward
if not shift then return nil end
return {x = (pos.x + shift.x),
y = (pos.y + shift.y),
z = (pos.z + shift.z)}
end
function logistica.get_supplier_target_list(pos)
logistica.load_position(pos)
local meta = minetest.get_meta(pos)
return meta:get_string(META_SUPPLIER_LISTNAME)
end
function logistica.set_supplier_target_list(pos, listName)
logistica.load_position(pos)
local meta = minetest.get_meta(pos)
meta:set_string(META_SUPPLIER_LISTNAME, listName)
end
function logistica.get_supplier_max_item_transfer(pos)
function logistica.get_supplier_inv_size(pos)
local node = minetest.get_node_or_nil(pos)
if not node then return 0 end
local def = minetest.registered_nodes[node.name]
if def and def.logistica and def.logistica.supplier_transfer_rate then
return def.logistica.supplier_transfer_rate
if def and def.logistica and def.logistica.inventory_size then
return def.logistica.inventory_size
else
return 0
end
@ -39,15 +17,6 @@ function logistica.take_item_from_supplier(pos, stack)
logistica.load_position(pos)
if not logistica.is_machine_on(pos) then return ItemStack("") end
local meta = minetest.get_meta(pos)
local canTake = math.min(stack:get_count(), logistica.get_supplier_max_item_transfer(pos))
local copyStack = ItemStack(stack)
copyStack:set_count(canTake)
local targetListName = meta:get_string(META_SUPPLIER_LISTNAME)
local targetPos = logistica.get_supplier_target(pos)
logistica.load_position(targetPos)
local targetInv = minetest.get_meta(targetPos):get_inventory()
local targetList = targetInv:get_list(targetListName)
if not targetList then copyStack:set_count(0); return copyStack end
return targetInv:remove_item(targetListName, copyStack)
local inv = meta:get_inventory()
return inv:remove_item(META_SUPPLIER_LIST, stack)
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -99,11 +99,13 @@ function logistica.toggle_machine_on_off(pos)
return nil
end
-- isOn is optional
-- extraText is optional
function logistica.set_node_tooltip_from_state(pos, extraText)
-- `isOn` is optional
-- `extraText` is optional
-- `overrideState` is optional
function logistica.set_node_tooltip_from_state(pos, extraText, overrideState)
if extraText == nil then extraText = "" else extraText = "\n"..extraText end
local isOn = logistica.is_machine_on(pos)
local isOn = overrideState
if isOn == nil then isOn = logistica.is_machine_on(pos) end
logistica.load_position(pos)
local meta = minetest.get_meta(pos)
local node = minetest.get_node(pos)
@ -178,4 +180,8 @@ function logistica.on_timer_powered(func)
return function(pos, elapsed)
if logistica.is_machine_on(pos) then func(pos, elapsed) end
end
end
function logistica.table_is_empty(table)
return table == nil or (next(table) == nil)
end

View File

@ -10,7 +10,7 @@ dofile(path.."/sound.lua")
-- bad debug
d = {}
d.ttos = logistica.ttos
d.print = minetest.chat_send_all
d.log = minetest.chat_send_all
function table.map(self, f)
local t = {}
for k,v in pairs(self) do