OgelGames 5ad0a8c229
Use minetest.load_area everywhere (#287)
also change `technic.get_or_load_node` to return loaded node instead of nil
2022-10-31 12:40:17 +11:00

769 lines
26 KiB
Lua
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--
-- 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, vm, run_stage, network)
for _, pos in ipairs(list) do
local node = minetest.get_node_or_nil(pos)
if not node then
vm:read_from_map(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
local vm = VoxelManip()
run_nodes(PR_nodes, vm, technic.producer, network)
run_nodes(RE_nodes, vm, technic.receiver, network)
run_nodes(BA_nodes, vm, 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