Add mods: technic, moreores, paintings, Nyancat (Pbj_pup). Small fix: sandwiches

This commit is contained in:
N-Nachtigal 2025-06-05 16:15:56 +02:00
parent 15e8e696a2
commit fb09deddc1
1404 changed files with 156555 additions and 211 deletions

View file

@ -0,0 +1,767 @@
--
-- Power network specific functions and data should live here
--
local S = technic.getter
local off_delay_seconds = tonumber(technic.config:get("switch_off_delay_seconds"))
local network_node_arrays = {"PR_nodes","BA_nodes","RE_nodes"}
technic.active_networks = {}
local networks = {}
technic.networks = networks
local technic_cables = {}
technic.cables = technic_cables
local poshash = minetest.hash_node_position
local hashpos = minetest.get_position_from_hash
function technic.create_network(sw_pos)
local network_id = poshash({x=sw_pos.x,y=sw_pos.y-1,z=sw_pos.z})
technic.build_network(network_id)
return network_id
end
local function pos_in_array(pos, array)
for _,pos2 in ipairs(array) do
if pos.x == pos2.x and pos.y == pos2.y and pos.z == pos2.z then
return true
end
end
return false
end
function technic.merge_networks(net1, net2)
-- TODO: Optimize for merging small network into larger by first checking network
-- node counts for both networks and keep network id with most nodes.
assert(type(net1) == "table", "Invalid net1 for technic.merge_networks")
assert(type(net2) == "table", "Invalid net2 for technic.merge_networks")
assert(net1 ~= net2, "Deadlock recipe: net1 & net2 equals for technic.merge_networks")
-- Move data in cables table
for node_id,cable_net_id in pairs(technic_cables) do
if cable_net_id == net2.id then
technic_cables[node_id] = net1.id
end
end
-- Move data in machine tables
for _,tablename in ipairs(network_node_arrays) do
for _,pos in ipairs(net2[tablename]) do
table.insert(net1[tablename], pos)
end
end
-- Move data in all_nodes table
for node_id,pos in pairs(net2.all_nodes) do
net1.all_nodes[node_id] = pos
end
-- Merge queues for incomplete networks
if net1.queue and net2.queue then
for _,pos in ipairs(net2.queue) do
if not pos_in_array(pos, net1.queue) then
table.insert(net1.queue, pos)
end
end
else
net1.queue = net1.queue or net2.queue
end
-- Remove links to net2
networks[net2.id] = nil
technic.active_networks[net2.id] = nil
end
function technic.activate_network(network_id, timeout)
-- timeout is optional ttl for network in seconds, if not specified use default
local network = networks[network_id]
if network then
-- timeout is absolute time in microseconds
network.timeout = minetest.get_us_time() + ((timeout or off_delay_seconds) * 1000 * 1000)
technic.active_networks[network_id] = network
end
end
function technic.sw_pos2tier(pos, load_node)
-- Get cable tier for switching station or nil if no cable
-- load_node true to use minetest.load_area to load node
local cable_pos = {x=pos.x,y=pos.y-1,z=pos.z}
if load_node then
minetest.load_area(cable_pos)
end
return technic.get_cable_tier(minetest.get_node(cable_pos).name)
end
-- Destroy network data
function technic.remove_network(network_id)
for pos_hash,cable_net_id in pairs(technic_cables) do
if cable_net_id == network_id then
technic_cables[pos_hash] = nil
end
end
networks[network_id] = nil
technic.active_networks[network_id] = nil
end
local function switch_index(pos, net)
for index, spos in ipairs(net.swpos) do
if pos.x == spos.x and pos.y == spos.y and pos.z == spos.z then
return index
end
end
end
function technic.switch_insert(pos, net)
if not switch_index(pos, net) then
table.insert(net.swpos, table.copy(pos))
end
return #net.swpos
end
function technic.switch_remove(pos, net)
local swindex = switch_index(pos, net)
if swindex then
table.remove(net.swpos, swindex)
end
return #net.swpos
end
function technic.sw_pos2network(pos)
return technic_cables[poshash({x=pos.x,y=pos.y-1,z=pos.z})]
end
function technic.pos2network(pos)
return technic_cables[poshash(pos)]
end
function technic.network2pos(network_id)
return hashpos(network_id)
end
function technic.network2sw_pos(network_id)
-- Return switching station position for network.
-- It is not guaranteed that position actually contains switching station.
local sw_pos = hashpos(network_id)
sw_pos.y = sw_pos.y + 1
return sw_pos
end
function technic.network_infotext(network_id, text)
local network = networks[network_id]
if network then
if text then
network.infotext = text
elseif network.queue then
local count = 0
for _ in pairs(network.all_nodes) do count = count + 1 end
return S("Building Network: @1 Nodes", count)
else
return network.infotext
end
end
end
local node_timeout = {}
local default_timeout = 2
function technic.set_default_timeout(timeout)
default_timeout = timeout or 2
end
function technic.get_timeout(tier, pos)
if node_timeout[tier] == nil then
-- it is normal that some multi tier nodes always drop here when checking all LV, MV and HV tiers
return 0
end
return node_timeout[tier][poshash(pos)] or 0
end
local function touch_node(tier, pos, timeout)
if node_timeout[tier] == nil then
-- this should get built up during registration
node_timeout[tier] = {}
end
node_timeout[tier][poshash(pos)] = timeout or default_timeout
end
technic.touch_node = touch_node
function technic.disable_machine(pos, node)
local nodedef = minetest.registered_nodes[node.name]
if nodedef then
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("@1 Has No Network", nodedef.description))
end
if nodedef and nodedef.technic_disabled_machine_name then
node.name = nodedef.technic_disabled_machine_name
minetest.swap_node(pos, node)
end
if nodedef and nodedef.technic_on_disable then
nodedef.technic_on_disable(pos, node)
end
local node_id = poshash(pos)
for _,nodes in pairs(node_timeout) do
nodes[node_id] = nil
end
end
local function match_cable_tier_filter(name, tiers)
-- Helper to check for set of cable tiers
if tiers then
for _, tier in ipairs(tiers) do if technic.is_tier_cable(name, tier) then return true end end
return false
end
return technic.get_cable_tier(name) ~= nil
end
local function get_neighbors(pos, tiers)
local tier_machines = tiers and technic.machines[tiers[1]]
local is_cable = match_cable_tier_filter(minetest.get_node(pos).name, tiers)
local network = is_cable and technic.networks[technic.pos2network(pos)]
local cables = {}
local machines = {}
local positions = {
{x=pos.x+1, y=pos.y, z=pos.z},
{x=pos.x-1, y=pos.y, z=pos.z},
{x=pos.x, y=pos.y+1, z=pos.z},
{x=pos.x, y=pos.y-1, z=pos.z},
{x=pos.x, y=pos.y, z=pos.z+1},
{x=pos.x, y=pos.y, z=pos.z-1},
}
for _,connected_pos in ipairs(positions) do
local name = minetest.get_node(connected_pos).name
if tier_machines and tier_machines[name] then
table.insert(machines, connected_pos)
elseif match_cable_tier_filter(name, tiers) then
local cable_network = technic.networks[technic.pos2network(connected_pos)]
table.insert(cables,{
pos = connected_pos,
network = cable_network,
})
if not network then network = cable_network end
end
end
return network, cables, machines
end
function technic.place_network_node(pos, tiers, name)
-- Get connections and primary network if there's any
local network, cables, machines = get_neighbors(pos, tiers)
if not network then
-- We're evidently not on a network, nothing to add ourselves to
return
end
-- Attach to primary network, this must be done before building branches from this position
technic.add_network_node(pos, network)
if not match_cable_tier_filter(name, tiers) then
if technic.machines[tiers[1]][name] == technic.producer_receiver then
-- FIXME: Multi tier machine like supply converter should also attach to other networks around pos.
-- Preferably also with connection rules defined for machine.
-- nodedef.connect_sides could be used to generate these rules.
-- For now, assume that all multi network machines belong to technic.producer_receiver group:
-- Get cables and networks around PR_RE machine
local _, machine_cables, _ = get_neighbors(pos)
for _,connection in ipairs(machine_cables) do
if connection.network and connection.network.id ~= network.id then
-- Attach PR_RE machine to secondary networks (last added is primary until above note is resolved)
technic.add_network_node(pos, connection.network)
end
end
else
-- Check connected cables for foreign networks, overload if machine was connected to multiple networks
for _, connection in ipairs(cables) do
if connection.network and connection.network.id ~= network.id then
technic.overload_network(connection.network.id)
technic.overload_network(network.id)
end
end
end
-- Machine added, skip all network building
return
end
-- Attach neighbor machines if cable was added
for _,machine_pos in ipairs(machines) do
technic.add_network_node(machine_pos, network)
end
-- Attach neighbor cables
for _,connection in ipairs(cables) do
if connection.network then
if connection.network.id ~= network.id then
-- Remove network if position belongs to another network
-- FIXME: Network requires partial rebuild but avoid doing it here if possible.
-- This might cause problems when merging two active networks into one
technic.remove_network(network.id)
technic.remove_network(connection.network.id)
connection.network = nil
end
else
-- There's cable that does not belong to any network, attach whole branch
technic.add_network_node(connection.pos, network)
technic.add_network_branch({connection.pos}, network)
end
end
end
-- Remove machine or cable from network
local function remove_network_node(network_id, pos)
local network = networks[network_id]
if not network then return end
-- Clear hash tables, cannot use table.remove
local node_id = poshash(pos)
technic_cables[node_id] = nil
network.all_nodes[node_id] = nil
-- TODO: All following things can be skipped if node is not machine
-- check here if it is or is not cable
-- or add separate function to remove cables and move responsibility to caller
-- Clear indexed arrays, do NOT leave holes
local machine_removed = false
for _,tblname in ipairs(network_node_arrays) do
local tbl = network[tblname]
for i=#tbl,1,-1 do
local mpos = tbl[i]
if mpos.x == pos.x and mpos.y == pos.y and mpos.z == pos.z then
table.remove(tbl, i)
machine_removed = true
break
end
end
end
if machine_removed then
-- Machine can still be in world, just not connected to any network. If so then disable it.
local node = minetest.get_node(pos)
technic.disable_machine(pos, node)
end
end
function technic.remove_network_node(pos, tiers, name)
-- Get the network and neighbors
local network, cables, machines = get_neighbors(pos, tiers)
if not network then return end
if not match_cable_tier_filter(name, tiers) then
-- Machine removed, skip cable checks to prevent unnecessary network cleanups
for _,connection in ipairs(cables) do
if connection.network then
-- Remove machine from all networks around it
remove_network_node(connection.network.id, pos)
end
end
return
end
if #cables == 1 then
-- Dead end cable removed, remove it from the network
remove_network_node(network.id, pos)
-- Remove neighbor machines from network if cable was removed
if match_cable_tier_filter(name, tiers) then
for _,machine_pos in ipairs(machines) do
local net, _, _ = get_neighbors(machine_pos, tiers)
if not net then
-- Remove machine from network if it does not have other connected cables
remove_network_node(network.id, machine_pos)
end
end
end
else
-- TODO: Check branches around and switching stations for branches:
-- remove branches that do not have switching station. Switching stations not tracked but could be easily tracked.
-- remove branches not connected to another branch. Individual branches not tracked, requires simple AI heuristics.
-- move branches that have switching station to new networks without checking or loading actual nodes in world.
-- To do all this network must be aware of individual branches and switching stations, might not be worth it...
-- For now remove whole network and let ABM rebuild it
technic.remove_network(network.id)
end
end
--
-- Functions to traverse the electrical network
--
-- Add a machine node to the LV/MV/HV network
local function add_network_machine(nodes, pos, network_id, all_nodes, multitier)
local node_id = poshash(pos)
local net_id_old = technic_cables[node_id]
if net_id_old == nil or (multitier and net_id_old ~= network_id and all_nodes[node_id] == nil) then
-- Add machine to network only if it is not already added
table.insert(nodes, pos)
-- FIXME: Machines connecting to multiple networks should have way to store multiple network ids
technic_cables[node_id] = network_id
all_nodes[node_id] = pos
return true
elseif not multitier and net_id_old ~= network_id then
-- Do not allow running from multiple networks, trigger overload
technic.overload_network(network_id)
technic.overload_network(net_id_old)
local meta = minetest.get_meta(pos)
meta:set_string("infotext",S("Network Overloaded"))
end
end
-- Add a wire node to the LV/MV/HV network
local function add_cable_node(pos, network)
local node_id = poshash(pos)
if not technic_cables[node_id] then
technic_cables[node_id] = network.id
network.all_nodes[node_id] = pos
if network.queue then
table.insert(network.queue, pos)
end
elseif technic_cables[node_id] ~= network.id then
-- Conflicting network connected, merge networks if both are still in building stage
local net2 = networks[technic_cables[node_id]]
if net2 and net2.queue then
technic.merge_networks(network, net2)
end
end
end
-- Generic function to add found connected nodes to the right classification array
local function add_network_node(network, pos, machines)
local name = technic.get_or_load_node(pos).name
if technic.get_cable_tier(name) == network.tier then
add_cable_node(pos, network)
elseif machines[name] then
if machines[name] == technic.producer then
add_network_machine(network.PR_nodes, pos, network.id, network.all_nodes)
elseif machines[name] == technic.receiver then
add_network_machine(network.RE_nodes, pos, network.id, network.all_nodes)
elseif machines[name] == technic.producer_receiver then
if add_network_machine(network.PR_nodes, pos, network.id, network.all_nodes, true) then
table.insert(network.RE_nodes, pos)
end
elseif machines[name] == technic.battery then
add_network_machine(network.BA_nodes, pos, network.id, network.all_nodes)
end
end
end
-- Generic function to add single nodes to the right classification array of existing network
function technic.add_network_node(pos, network)
add_network_node(network, pos, technic.machines[network.tier])
end
-- Traverse a network given a list of machines and a cable type name
local function traverse_network(network, pos, machines)
local positions = {
{x=pos.x+1, y=pos.y, z=pos.z},
{x=pos.x-1, y=pos.y, z=pos.z},
{x=pos.x, y=pos.y+1, z=pos.z},
{x=pos.x, y=pos.y-1, z=pos.z},
{x=pos.x, y=pos.y, z=pos.z+1},
{x=pos.x, y=pos.y, z=pos.z-1}}
for i, cur_pos in pairs(positions) do
if not network.all_nodes[poshash(cur_pos)] then
add_network_node(network, cur_pos, machines)
end
end
end
local function touch_nodes(list, tier)
for _, pos in ipairs(list) do
touch_node(tier, pos) -- Touch node
end
end
local function get_network(network_id, tier)
local cached = networks[network_id]
if cached and not cached.queue and cached.tier == tier then
touch_nodes(cached.PR_nodes, tier)
touch_nodes(cached.BA_nodes, tier)
touch_nodes(cached.RE_nodes, tier)
return cached.PR_nodes, cached.BA_nodes, cached.RE_nodes
end
return technic.build_network(network_id)
end
function technic.add_network_branch(queue, network)
-- Adds whole branch to network, queue positions can be used to bypass sub branches
local machines = technic.machines[network.tier]
--print(string.format("technic.add_network_branch(%s, %s, %.17g)",queue,minetest.pos_to_string(sw_pos),network.id))
local t1 = minetest.get_us_time()
while next(queue) do
local to_visit = {}
for _, pos in ipairs(queue) do
network.queue = to_visit
traverse_network(network, pos, machines)
end
queue = to_visit
if minetest.get_us_time() - t1 > 10000 then
-- time limit exceeded
break
end
end
-- Set build queue for network if network build was not finished within time limits
network.queue = #queue > 0 and queue
end
-- Battery charge status updates for network
local function update_battery(self, charge, max_charge, supply, demand)
self.battery_charge = self.battery_charge + charge
self.battery_charge_max = self.battery_charge_max + max_charge
self.battery_supply = self.battery_supply + supply
self.battery_demand = self.battery_demand + demand
if demand ~= 0 then
self.BA_count_active = self.BA_count_active + 1
self.BA_charge_active = self.BA_charge_active + charge
end
end
-- Moving average function generator
local function sma(period)
local values = {}
local index = 1
local sum = 0
return function(n)
-- Add new value and return average
sum = sum - (values[index] or 0) + n
values[index] = n
index = index ~= period and index + 1 or 1
return sum / #values
end
end
function technic.build_network(network_id)
local network = networks[network_id]
if network and not network.queue then
-- Network exists complete and cached
return network.PR_nodes, network.BA_nodes, network.RE_nodes
elseif not network then
-- Build new network if network does not exist
technic.remove_network(network_id)
local sw_pos = technic.network2sw_pos(network_id)
local tier = sw_pos and technic.sw_pos2tier(sw_pos)
if not tier then
-- Failed to get initial cable node for network
return
end
network = {
-- Build queue
queue = {},
-- Basic network data and lookup table for attached nodes
id = network_id, tier = tier, all_nodes = {}, swpos = {},
-- Indexed arrays for iteration by machine type
PR_nodes = {}, RE_nodes = {}, BA_nodes = {},
-- Power generation, usage and capacity related variables
supply = 0, demand = 0, battery_charge = 0, battery_charge_max = 0,
BA_count_active = 0, BA_charge_active = 0, battery_supply = 0, battery_demand = 0,
-- Battery status update function
update_battery = update_battery,
-- Network activation and excution control
timeout = 0, skip = 0, lag = 0, average_lag = sma(5)
}
-- Add first cable (one that is holding network id) and build network
add_cable_node(technic.network2pos(network_id), network)
end
-- Continue building incomplete network
technic.add_network_branch(network.queue, network)
network.battery_count = #network.BA_nodes
-- Add newly built network to cache array
networks[network_id] = network
if not network.queue then
-- And return producers, batteries and receivers (should this simply return network?)
return network.PR_nodes, network.BA_nodes, network.RE_nodes
end
end
--
-- Execute technic power network
--
local node_technic_run = {}
minetest.register_on_mods_loaded(function()
for name, tiers in pairs(technic.machine_tiers) do
local nodedef = minetest.registered_nodes[name]
local on_construct = type(nodedef.on_construct) == "function" and nodedef.on_construct
local on_destruct = type(nodedef.on_destruct) == "function" and nodedef.on_destruct
local place_node = technic.place_network_node
local remove_node = technic.remove_network_node
minetest.override_item(name, {
on_construct = on_construct
and function(pos) on_construct(pos) place_node(pos, tiers, name) end
or function(pos) place_node(pos, tiers, name) end,
on_destruct = on_destruct
and function(pos) on_destruct(pos) remove_node(pos, tiers, name) end
or function(pos) remove_node(pos, tiers, name) end,
})
end
for name, _ in pairs(technic.machine_tiers) do
if type(minetest.registered_nodes[name].technic_run) == "function" then
node_technic_run[name] = minetest.registered_nodes[name].technic_run
end
end
end)
local function run_nodes(list, run_stage, network)
for _, pos in ipairs(list) do
local node = minetest.get_node_or_nil(pos)
if not node then
minetest.load_area(pos, pos)
node = minetest.get_node_or_nil(pos)
end
if node and node.name and node_technic_run[node.name] then
node_technic_run[node.name](pos, node, run_stage, network)
end
end
end
function technic.network_run(network_id)
--
-- !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
-- TODO: This function requires a lot of cleanup
-- It is moved here from switching_station.lua and still
-- contain a lot of switching station specific stuff which
-- should be removed and/or refactored.
--
local t0 = minetest.get_us_time()
local PR_nodes
local BA_nodes
local RE_nodes
local network = networks[network_id]
if network then
PR_nodes, BA_nodes, RE_nodes = get_network(network_id, network.tier)
if not PR_nodes or technic.is_overloaded(network_id) then return end
else
--dprint("Not connected to a network")
technic.network_infotext(network_id, S("@1 Has No Network", S("Switching Station")))
return
end
-- Reset battery data for updates
network.battery_charge = 0
network.battery_charge_max = 0
network.battery_supply = 0
network.battery_demand = 0
network.BA_count_active = 0
network.BA_charge_active = 0
run_nodes(PR_nodes, technic.producer, network)
run_nodes(RE_nodes, technic.receiver, network)
run_nodes(BA_nodes, technic.battery, network)
-- Strings for the meta data
local eu_demand_str = network.tier.."_EU_demand"
local eu_input_str = network.tier.."_EU_input"
local eu_supply_str = network.tier.."_EU_supply"
-- Distribute charge equally across multiple batteries.
local charge_distributed = math.floor(network.BA_charge_active / network.BA_count_active)
for n, pos1 in pairs(BA_nodes) do
local meta1 = minetest.get_meta(pos1)
if (meta1:get_int(eu_demand_str) ~= 0) then
meta1:set_int("internal_EU_charge", charge_distributed)
end
end
-- Get all the power from the PR nodes
local PR_eu_supply = 0 -- Total power
for _, pos1 in pairs(PR_nodes) do
local meta1 = minetest.get_meta(pos1)
PR_eu_supply = PR_eu_supply + meta1:get_int(eu_supply_str)
end
--dprint("Total PR supply:"..PR_eu_supply)
-- Get all the demand from the RE nodes
local RE_eu_demand = 0
for _, pos1 in pairs(RE_nodes) do
local meta1 = minetest.get_meta(pos1)
RE_eu_demand = RE_eu_demand + meta1:get_int(eu_demand_str)
end
--dprint("Total RE demand:"..RE_eu_demand)
technic.network_infotext(network_id, S("@1. Supply: @2 Demand: @3",
S("Switching Station"), technic.EU_string(PR_eu_supply),
technic.EU_string(RE_eu_demand)))
-- Data that will be used by the power monitor
network.supply = PR_eu_supply
network.demand = RE_eu_demand
network.battery_count = #BA_nodes
-- If the PR supply is enough for the RE demand supply them all
local BA_eu_demand = network.battery_demand
if PR_eu_supply >= RE_eu_demand then
--dprint("PR_eu_supply"..PR_eu_supply.." >= RE_eu_demand"..RE_eu_demand)
for _, pos1 in pairs(RE_nodes) do
local meta1 = minetest.get_meta(pos1)
local eu_demand = meta1:get_int(eu_demand_str)
meta1:set_int(eu_input_str, eu_demand)
end
-- We have a surplus, so distribute the rest equally to the BA nodes
-- Let's calculate the factor of the demand
PR_eu_supply = PR_eu_supply - RE_eu_demand
local charge_factor = 0 -- Assume all batteries fully charged
if BA_eu_demand > 0 then
charge_factor = PR_eu_supply / BA_eu_demand
end
-- TODO: EU input for all batteries: math.floor(BA_eu_demand * charge_factor * #BA_nodes)
for n, pos1 in pairs(BA_nodes) do
local meta1 = minetest.get_meta(pos1)
local eu_demand = meta1:get_int(eu_demand_str)
meta1:set_int(eu_input_str, math.floor(eu_demand * charge_factor))
--dprint("Charging battery:"..math.floor(eu_demand*charge_factor))
end
local t1 = minetest.get_us_time()
local diff = t1 - t0
if diff > 50000 then
minetest.log("warning", "[technic] [+supply] technic_run took " .. diff .. " us at "
.. minetest.pos_to_string(hashpos(network_id)))
end
return
end
-- If the PR supply is not enough for the RE demand we will discharge the batteries too
local BA_eu_supply = network.battery_supply
if PR_eu_supply + BA_eu_supply >= RE_eu_demand then
--dprint("PR_eu_supply "..PR_eu_supply.."+BA_eu_supply "..BA_eu_supply.." >= RE_eu_demand"..RE_eu_demand)
for _, pos1 in pairs(RE_nodes) do
local meta1 = minetest.get_meta(pos1)
local eu_demand = meta1:get_int(eu_demand_str)
meta1:set_int(eu_input_str, eu_demand)
end
-- We have a deficit, so distribute to the BA nodes
-- Let's calculate the factor of the supply
local charge_factor = 0 -- Assume all batteries depleted
if BA_eu_supply > 0 then
charge_factor = (PR_eu_supply - RE_eu_demand) / BA_eu_supply
end
for n,pos1 in pairs(BA_nodes) do
local meta1 = minetest.get_meta(pos1)
local eu_supply = meta1:get_int(eu_supply_str)
meta1:set_int(eu_input_str, math.floor(eu_supply * charge_factor))
--dprint("Discharging battery:"..math.floor(eu_supply*charge_factor))
end
local t1 = minetest.get_us_time()
local diff = t1 - t0
if diff > 50000 then
minetest.log("warning", "[technic] [-supply] technic_run took " .. diff .. " us at "
.. minetest.pos_to_string(hashpos(network_id)))
end
return
end
-- If the PR+BA supply is not enough for the RE demand: Power only the batteries
local charge_factor = 0 -- Assume all batteries fully charged
if BA_eu_demand > 0 then
charge_factor = PR_eu_supply / BA_eu_demand
end
for n, pos1 in pairs(BA_nodes) do
local meta1 = minetest.get_meta(pos1)
local eu_demand = meta1:get_int(eu_demand_str)
meta1:set_int(eu_input_str, math.floor(eu_demand * charge_factor))
end
for n, pos1 in pairs(RE_nodes) do
local meta1 = minetest.get_meta(pos1)
meta1:set_int(eu_input_str, 0)
end
local t1 = minetest.get_us_time()
local diff = t1 - t0
if diff > 50000 then
minetest.log("warning", "[technic] technic_run took " .. diff .. " us at "
.. minetest.pos_to_string(hashpos(network_id)))
end
end