30 Commits

Author SHA1 Message Date
a735d1a155 Merge branch 'm_small_fixes' into 'master'
Various small fixes

See merge request VanessaE/pipeworks!35
2021-02-07 12:46:16 +00:00
a2c0dd1130 Various small fixes
* Save the fakeplayer's wielded item in set_wielded_item
* pipeworks.luaentity: Return the found objects in get_objects_inside_radius
* Remove the invalid and unused on_blast return values in lua_tube.lua
  This should avoid a crash when a strong explosion happens next to a Lua Tube.
* Do not access a global "nodename" in a BUG message.
  This avoids a potential crash, in case the message actually appears in practice.
* Set the Flow Sensor and Fountain Head mesecons connection rules
* pipeworks.luaentity: Use the same function for move_to and set_pos
* (no functional change) Do not save the object returned by tube_inject_item into the "item1" local variable in filter-injector.lua
2021-02-07 11:58:18 +01:00
f0ef43823c Merge branch 'm_unused_loop_vars' into 'master'
Remove unused loop variables, trailing whitespace and fix mixed-whitespace indentations

See merge request VanessaE/pipeworks!34
2021-02-05 18:03:54 +00:00
17a602a5a1 Remove unused loop variables, trailing whitespace and fix mixed-whitespace indentations 2021-02-05 17:17:50 +01:00
2294a23582 Merge branch 'm_unused_code_removal' into 'master'
Remove or comment unused code and declare some variables local

See merge request VanessaE/pipeworks!33
2021-02-03 18:33:32 +00:00
b2d2ccbcba Remove or comment unused code and declare some variables local 2021-02-03 18:08:50 +01:00
8322f256c5 Merge branch 'master' into 'master'
Prevent the symptoms of Issue #33

See merge request VanessaE/pipeworks!31
2020-12-18 21:44:11 +00:00
ba7eb19317 Prevent the symptoms of Issue #33
Try to prevent the crash from issue #33 by
replaving nil velocity or acceleration with (0,0,0).
This does not fix the underlying cause of them being nil,
but should prevent the crash.
2020-12-18 19:28:07 +01:00
065c953eba use the right gear item in node breaker 2020-10-28 12:30:48 -04:00
9a63d17e4a Merge branch 'm_disable_print_message' into 'master'
Log the "Pipeworks loaded!" message to infostream instead of printing it

See merge request VanessaE/pipeworks!30
2020-10-17 18:55:24 +00:00
d814357ddf Merge branch 'm_table_extends_clean' into 'master'
Make pipeworks.table_extend easier to read

See merge request VanessaE/pipeworks!29
2020-10-17 18:55:01 +00:00
c2fe5fe956 Make pipeworks.table_extend easier to read
In my opinion this clarifies that tbl2 is attached at the end of tbl
2020-10-17 20:50:20 +02:00
9dbaa5f4f6 Log the "Pipeworks loaded!" message to infostream instead of printing it 2020-10-17 20:46:44 +02:00
d93396600f Merge branch 'protected-access-to-wielders' into 'master'
Allow protected access to wielder inventories.

Closes #40

See merge request VanessaE/pipeworks!27
2020-09-24 15:33:17 +00:00
c966a8a57d Allow players with the protection_bypass privilege or access to
the protection to access wielder node inventories.

Fixes #40.
2020-09-24 05:12:32 -06:00
61b061f669 Merge branch 'avoid-protection-check-on-chest-close' into 'master'
Avoid protection check on chest close.

Closes #23

See merge request VanessaE/pipeworks!26
2020-09-11 09:45:04 +00:00
fe91d5eb46 Avoid protection check on chest close.
Fixes #23.
2020-09-10 23:12:11 -06:00
eb1064ca6d Merge branch 'patch-1' into 'master'
Update pipeworks.zh_CN.tr

See merge request VanessaE/pipeworks!25
2020-09-05 07:14:04 +00:00
f54e25ec52 Update pipeworks.zh_CN.tr 2020-09-05 06:25:58 +00:00
dedb0dd54e Merge branch 'tptube-api' into 'master'
Expose teleport tube database API

See merge request VanessaE/pipeworks!24
2020-08-10 17:54:28 +00:00
SX
477a024034 Expose teleport tube database API 2020-08-09 23:09:31 +03:00
34cb0e7682 Merge branch 'master' into 'master'
Add "get_recipe" digiline command for querying the current autocrafter recipe.

See merge request VanessaE/pipeworks!23
2020-07-30 00:40:41 +00:00
505fc7cc49 Add "get_recipe" digiline command for querying the current autocrafter recipe.
Useful for having the player teach the luacontroller how to craft various items.
2020-07-23 01:49:37 +02:00
cb58a646cf Merge branch 'undefined' into 'master'
Add Chinese Translation

See merge request VanessaE/pipeworks!22
2020-07-19 03:55:39 +00:00
e3135c53f0 Add Chinese Translation 2020-07-19 02:22:12 +00:00
366d57f4da Merge branch 'fix_connect_sides' into 'master'
Consider connect_sides for item transport

See merge request VanessaE/pipeworks!21
2020-06-30 10:39:52 +00:00
c79e68a80c Consider connect_sides for item transport
Previously connect_sides was only used to choose the correct visual
model, but not during item transport. This allowed items to exit tubes
in directions without a visual connection and enter objects from sides
that should not be connectable according to connect_sides.
For example an item could enter a chest from the front, if a tube passed
there.

This change saves the connect_sides in the meta table of the object
whenever the visual representation is updated. When nothing is cached
yet, it uses the old behavior. That way it does not break existing
builds.
2020-06-30 10:11:22 +00:00
9338c109a6 Merge branch 'rebased_luacontroller' into 'master'
Rebase lua_tube onto upstream luacontroller

See merge request VanessaE/pipeworks!20
2020-06-30 09:41:01 +00:00
63bee98948 Fix typo in luatube update_real_port_states helper 2020-06-30 09:17:46 +00:00
ee6c9991b9 Rebase lua_tube onto upstream luacontroller
Adds:
 - various bug fixes
 - error label on the formspec
 - lightweight interrupts
2020-06-30 09:15:50 +00:00
20 changed files with 528 additions and 184 deletions

View File

@ -47,7 +47,7 @@ local function autocraft(inventory, craft)
end end
-- consume material -- consume material
for itemname, number in pairs(consumption) do for itemname, number in pairs(consumption) do
for i = 1, number do -- We have to do that since remove_item does not work if count > stack_max for _ = 1, number do -- We have to do that since remove_item does not work if count > stack_max
inventory:remove_item("src", ItemStack(itemname)) inventory:remove_item("src", ItemStack(itemname))
end end
end end
@ -67,16 +67,13 @@ local function run_autocrafter(pos, elapsed)
local inventory = meta:get_inventory() local inventory = meta:get_inventory()
local craft = get_craft(pos, inventory) local craft = get_craft(pos, inventory)
local output_item = craft.output.item local output_item = craft.output.item
-- NALC: existence de limitgroup ?
local limitcraft = minetest.get_item_group(output_item:get_name(), "limitcraft") or 0
-- only use crafts that have an actual result -- only use crafts that have an actual result
-- NALC: ou si l'item n'est pas dans le group limitcraft if output_item:is_empty() then
if output_item:is_empty() or limitcraft > 0 then
meta:set_string("infotext", S("unconfigured Autocrafter: unknown recipe")) meta:set_string("infotext", S("unconfigured Autocrafter: unknown recipe"))
return false return false
end end
for step = 1, math.floor(elapsed/craft_time) do for _ = 1, math.floor(elapsed/craft_time) do
local continue = autocraft(inventory, craft) local continue = autocraft(inventory, craft)
if not continue then return false end if not continue then return false end
end end
@ -109,7 +106,6 @@ local function after_recipe_change(pos, inventory)
inventory:set_stack("output", 1, "") inventory:set_stack("output", 1, "")
return return
end end
local recipe_changed = false
local recipe = inventory:get_list("recipe") local recipe = inventory:get_list("recipe")
local hash = minetest.hash_node_position(pos) local hash = minetest.hash_node_position(pos)
@ -392,6 +388,27 @@ minetest.register_node("pipeworks:autocrafter", {
end end
end end
after_recipe_change(pos,inv) after_recipe_change(pos,inv)
elseif msg == "get_recipe" then
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local recipe = {}
for y=0,2,1 do
local row = {}
for x=1,3,1 do
local slot = y*3+x
table.insert(row, inv:get_stack("recipe",slot):get_name())
end
table.insert(recipe, row)
end
local setchan = meta:get_string("channel")
local output = inv:get_stack("output", 1)
digiline:receptor_send(pos, digiline.rules.default, setchan, {
recipe = recipe,
result = {
name = output:get_name(),
count = output:get_count(),
}
})
elseif msg == "off" then elseif msg == "off" then
update_meta(meta, false) update_meta(meta, false)
minetest.get_node_timer(pos):stop() minetest.get_node_timer(pos):stop()

View File

@ -53,34 +53,35 @@ local function tube_autoroute(pos)
} }
-- xm = 1, xp = 2, ym = 3, yp = 4, zm = 5, zp = 6 -- xm = 1, xp = 2, ym = 3, yp = 4, zm = 5, zp = 6
local positions = {} local adjlist = {} -- this will be used in item_transport
local nodes = {}
for i, adj in ipairs(adjustments) do for i, adj in ipairs(adjustments) do
positions[i] = vector.add(pos, adj) local position = vector.add(pos, adj)
nodes[i] = minetest.get_node(positions[i]) local node = minetest.get_node(position)
end
for i, node in ipairs(nodes) do
local idef = minetest.registered_nodes[node.name] local idef = minetest.registered_nodes[node.name]
-- handle the tubes themselves -- handle the tubes themselves
if is_tube(node.name) then if is_tube(node.name) then
active[i] = 1 active[i] = 1
table.insert(adjlist, adj)
-- handle new style connectors -- handle new style connectors
elseif idef and idef.tube and idef.tube.connect_sides then elseif idef and idef.tube and idef.tube.connect_sides then
local dir = adjustments[i] if idef.tube.connect_sides[nodeside(node, vector.multiply(adj, -1))] then
if idef.tube.connect_sides[nodeside(node, vector.multiply(dir, -1))] then
active[i] = 1 active[i] = 1
table.insert(adjlist, adj)
end end
end end
end end
minetest.get_meta(pos):set_string("adjlist", minetest.serialize(adjlist))
-- all sides checked, now figure which tube to use. -- all sides checked, now figure which tube to use.
local nodedef = minetest.registered_nodes[nctr.name] local nodedef = minetest.registered_nodes[nctr.name]
local basename = nodedef.basename local basename = nodedef.basename
if nodedef.style == "old" then if nodedef.style == "old" then
local nsurround = "" local nsurround = ""
for i, n in ipairs(active) do for _, n in ipairs(active) do
nsurround = nsurround..n nsurround = nsurround..n
end end
nctr.name = basename.."_"..nsurround nctr.name = basename.."_"..nsurround

View File

@ -86,10 +86,9 @@ function pipeworks.table_contains(tbl, element)
end end
function pipeworks.table_extend(tbl, tbl2) function pipeworks.table_extend(tbl, tbl2)
local index = #tbl + 1 local oldlength = #tbl
for _, elt in ipairs(tbl2) do for i = 1,#tbl2 do
tbl[index] = elt tbl[oldlength + i] = tbl2[i]
index = index + 1
end end
end end
@ -115,7 +114,7 @@ local fs_helpers = {}
pipeworks.fs_helpers = fs_helpers pipeworks.fs_helpers = fs_helpers
function fs_helpers.on_receive_fields(pos, fields) function fs_helpers.on_receive_fields(pos, fields)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
for field, value in pairs(fields) do for field in pairs(fields) do
if pipeworks.string_startswith(field, "fs_helpers_cycling:") then if pipeworks.string_startswith(field, "fs_helpers_cycling:") then
local l = field:split(":") local l = field:split(":")
local new_value = tonumber(l[2]) local new_value = tonumber(l[2])
@ -224,7 +223,7 @@ function pipeworks.create_fake_player(def, is_dynamic)
return self._inventory:set_stack(def.wield_list, return self._inventory:set_stack(def.wield_list,
self._wield_index, item) self._wield_index, item)
end end
_wielded_item = ItemStack(item) self._wielded_item = ItemStack(item)
end, end,
get_wielded_item = function(self, item) get_wielded_item = function(self, item)
if self._inventory and def.wield_list then if self._inventory and def.wield_list then
@ -243,17 +242,18 @@ function pipeworks.create_fake_player(def, is_dynamic)
set_bone_position = delay(), set_bone_position = delay(),
hud_change = delay(), hud_change = delay(),
} }
local _trash
-- Getter & setter functions -- Getter & setter functions
p.get_inventory_formspec, p.set_inventory_formspec p.get_inventory_formspec, p.set_inventory_formspec
= get_set_wrap("formspec", is_dynamic) = get_set_wrap("formspec", is_dynamic)
p.get_breath, p.set_breath = get_set_wrap("breath", is_dynamic) p.get_breath, p.set_breath = get_set_wrap("breath", is_dynamic)
p.get_hp, p.set_hp = get_set_wrap("hp", is_dynamic) p.get_hp, p.set_hp = get_set_wrap("hp", is_dynamic)
p.get_pos, p.set_pos = get_set_wrap("pos", is_dynamic) p.get_pos, p.set_pos = get_set_wrap("pos", is_dynamic)
_trash, p.move_to = get_set_wrap("pos", is_dynamic)
p.get_wield_index, p.set_wield_index = get_set_wrap("wield_index", true) p.get_wield_index, p.set_wield_index = get_set_wrap("wield_index", true)
p.get_properties, p.set_properties = get_set_wrap("properties", false) p.get_properties, p.set_properties = get_set_wrap("properties", false)
-- For players, move_to and get_pos do the same
p.move_to = p.get_pos
-- Backwards compatibilty -- Backwards compatibilty
p.getpos = p.get_pos p.getpos = p.get_pos
p.setpos = p.set_pos p.setpos = p.set_pos

View File

@ -65,7 +65,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
open_chests[pn] = nil open_chests[pn] = nil
for k, v in pairs(open_chests) do for _, v in pairs(open_chests) do
if v.pos.x == pos.x and v.pos.y == pos.y and v.pos.z == pos.z then if v.pos.x == pos.x and v.pos.y == pos.y and v.pos.z == pos.z then
return true return true
end end
@ -77,10 +77,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
pipeworks.after_place(pos) pipeworks.after_place(pos)
end) end)
minetest.sound_play(sound, {gain = 0.3, pos = pos, max_hear_distance = 10}) minetest.sound_play(sound, {gain = 0.3, pos = pos, max_hear_distance = 10})
end elseif pipeworks.may_configure(pos, player) then
-- Pipeworks Switch -- Pipeworks Switch
if pipeworks.may_configure(pos, player) and not fields.quit then
fs_helpers.on_receive_fields(pos, fields) fs_helpers.on_receive_fields(pos, fields)
minetest.show_formspec(player:get_player_name(), "pipeworks:chest_formspec", get_chest_formspec(pos)) minetest.show_formspec(player:get_player_name(), "pipeworks:chest_formspec", get_chest_formspec(pos))
end end
@ -232,7 +230,7 @@ override.tiles = {
} }
-- Add the extra groups -- Add the extra groups
for i,v in ipairs({override_protected, override, override_open, override_protected_open}) do for _,v in ipairs({override_protected, override, override_open, override_protected_open}) do
v.groups.tubedevice = 1 v.groups.tubedevice = 1
v.groups.tubedevice_receiver = 1 v.groups.tubedevice_receiver = 1
end end

View File

@ -7,7 +7,7 @@ local DS = minetest.get_translator("default")
local fs_helpers = pipeworks.fs_helpers local fs_helpers = pipeworks.fs_helpers
tube_entry = "^pipeworks_tube_connection_stony.png" local tube_entry = "^pipeworks_tube_connection_stony.png"
local function active_formspec(fuel_percent, item_percent, pos, meta) local function active_formspec(fuel_percent, item_percent, pos, meta)
local formspec = local formspec =

View File

@ -10,7 +10,7 @@ function pipeworks.fix_after_rotation(pos, node, user, mode, new_param2)
if string.find(node.name, "spigot") then new_param2 = new_param2 % 4 end if string.find(node.name, "spigot") then new_param2 = new_param2 % 4 end
newnode = string.gsub(node.name, "_on", "_off") local newnode = string.gsub(node.name, "_on", "_off")
minetest.swap_node(pos, { name = newnode, param2 = new_param2 }) minetest.swap_node(pos, { name = newnode, param2 = new_param2 })
pipeworks.scan_for_pipe_objects(pos) pipeworks.scan_for_pipe_objects(pos)
@ -36,9 +36,7 @@ function pipeworks.rotate_on_place(itemstack, placer, pointed_thing)
local under = pointed_thing.under local under = pointed_thing.under
local fdir = minetest.dir_to_facedir(placer:get_look_dir()) local fdir = minetest.dir_to_facedir(placer:get_look_dir())
local undernode = minetest.get_node(under) local undernode = minetest.get_node(under)
local abovenode = minetest.get_node(above)
local uname = undernode.name local uname = undernode.name
local aname = abovenode.name
local isabove = (above.x == under.x) and (above.z == under.z) and (pitch > 0) local isabove = (above.x == under.x) and (above.z == under.z) and (pitch > 0)
local pos1 = above local pos1 = above
@ -105,6 +103,7 @@ if minetest.get_modpath("mesecons") then
} }
end end
--[[
local pipes_devicelist = { local pipes_devicelist = {
"pump", "pump",
"valve", "valve",
@ -120,14 +119,15 @@ local pipes_devicelist = {
"storage_tank_9", "storage_tank_9",
"storage_tank_10" "storage_tank_10"
} }
--]]
-- Now define the nodes. -- Now define the nodes.
local states = { "on", "off" } local states = { "on", "off" }
local dgroups = ""
for s in ipairs(states) do for s in ipairs(states) do
local dgroups
if states[s] == "off" then if states[s] == "off" then
dgroups = {snappy=3, pipe=1} dgroups = {snappy=3, pipe=1}
else else
@ -465,7 +465,7 @@ minetest.register_node(nodename_sensor_empty, {
end, end,
on_construct = function(pos) on_construct = function(pos)
if mesecon then if mesecon then
mesecon.receptor_off(pos, rules) mesecon.receptor_off(pos, pipeworks.mesecons_rules)
end end
end, end,
selection_box = { selection_box = {
@ -504,7 +504,7 @@ minetest.register_node(nodename_sensor_loaded, {
end, end,
on_construct = function(pos) on_construct = function(pos)
if mesecon then if mesecon then
mesecon.receptor_on(pos, rules) mesecon.receptor_on(pos, pipeworks.mesecons_rules)
end end
end, end,
selection_box = { selection_box = {
@ -626,7 +626,7 @@ minetest.register_node(nodename_fountain_empty, {
end, end,
on_construct = function(pos) on_construct = function(pos)
if mesecon then if mesecon then
mesecon.receptor_on(pos, rules) mesecon.receptor_on(pos, pipeworks.mesecons_rules)
end end
end, end,
selection_box = { selection_box = {
@ -661,7 +661,7 @@ minetest.register_node(nodename_fountain_loaded, {
end, end,
on_construct = function(pos) on_construct = function(pos)
if mesecon then if mesecon then
mesecon.receptor_on(pos, rules) mesecon.receptor_on(pos, pipeworks.mesecons_rules)
end end
end, end,
selection_box = { selection_box = {

View File

@ -1,10 +1,6 @@
local S = minetest.get_translator("pipeworks") local S = minetest.get_translator("pipeworks")
local fs_helpers = pipeworks.fs_helpers local fs_helpers = pipeworks.fs_helpers
local function delay(x)
return (function() return x end)
end
local function set_filter_infotext(data, meta) local function set_filter_infotext(data, meta)
local infotext = S("@1 Filter-Injector", data.wise_desc) local infotext = S("@1 Filter-Injector", data.wise_desc)
if meta:get_int("slotseq_mode") == 2 then if meta:get_int("slotseq_mode") == 2 then
@ -326,7 +322,8 @@ local function punch_filter(data, filtpos, filtnode, msg)
end end
local pos = vector.add(frompos, vector.multiply(dir, 1.4)) local pos = vector.add(frompos, vector.multiply(dir, 1.4))
local start_pos = vector.add(frompos, dir) local start_pos = vector.add(frompos, dir)
local item1 = pipeworks.tube_inject_item(pos, start_pos, dir, item, fakePlayer:get_player_name()) pipeworks.tube_inject_item(pos, start_pos, dir, item,
fakePlayer:get_player_name())
return true -- only fire one item, please return true -- only fire one item, please
end end
end end

View File

@ -6,8 +6,6 @@
pipeworks = {} pipeworks = {}
local DEBUG = false
pipeworks.worldpath = minetest.get_worldpath() pipeworks.worldpath = minetest.get_worldpath()
pipeworks.modpath = minetest.get_modpath("pipeworks") pipeworks.modpath = minetest.get_modpath("pipeworks")
local S = minetest.get_translator("pipeworks") local S = minetest.get_translator("pipeworks")
@ -71,8 +69,8 @@ function pipeworks.may_configure(pos, player)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local owner = meta:get_string("owner") local owner = meta:get_string("owner")
if owner ~= "" then -- wielders and filters if owner ~= "" and owner == name then -- wielders and filters
return owner == name return true
end end
return not minetest.is_protected(pos, name) return not minetest.is_protected(pos, name)
end end
@ -150,4 +148,4 @@ end
minetest.register_alias("pipeworks:pipe", "pipeworks:pipe_110000_empty") minetest.register_alias("pipeworks:pipe", "pipeworks:pipe_110000_empty")
print("Pipeworks loaded!") minetest.log("info", "Pipeworks loaded!")

View File

@ -25,7 +25,7 @@ end
-- both optional w/ sensible defaults and fallback to normal allow_* function -- both optional w/ sensible defaults and fallback to normal allow_* function
-- XXX: possibly change insert_object to insert_item -- XXX: possibly change insert_object to insert_item
local adjlist={{x=0,y=0,z=1},{x=0,y=0,z=-1},{x=0,y=1,z=0},{x=0,y=-1,z=0},{x=1,y=0,z=0},{x=-1,y=0,z=0}} local default_adjlist={{x=0,y=0,z=1},{x=0,y=0,z=-1},{x=0,y=1,z=0},{x=0,y=-1,z=0},{x=1,y=0,z=0},{x=-1,y=0,z=0}}
function pipeworks.notvel(tbl, vel) function pipeworks.notvel(tbl, vel)
local tbl2={} local tbl2={}
@ -42,7 +42,7 @@ minetest.register_globalstep(function(dtime)
return return
end end
tube_item_count = {} tube_item_count = {}
for id, entity in pairs(luaentity.entities) do for _, entity in pairs(luaentity.entities) do
if entity.name == "pipeworks:tubed_item" then if entity.name == "pipeworks:tubed_item" then
local h = minetest.hash_node_position(vector.round(entity._pos)) local h = minetest.hash_node_position(vector.round(entity._pos))
tube_item_count[h] = (tube_item_count[h] or 0) + 1 tube_item_count[h] = (tube_item_count[h] or 0) + 1
@ -80,6 +80,9 @@ local function go_next_compat(pos, cnode, cmeta, cycledir, vel, stack, owner)
if minetest.registered_nodes[cnode.name] and minetest.registered_nodes[cnode.name].tube and minetest.registered_nodes[cnode.name].tube.can_go then if minetest.registered_nodes[cnode.name] and minetest.registered_nodes[cnode.name].tube and minetest.registered_nodes[cnode.name].tube.can_go then
can_go = minetest.registered_nodes[cnode.name].tube.can_go(pos, cnode, vel, stack) can_go = minetest.registered_nodes[cnode.name].tube.can_go(pos, cnode, vel, stack)
else else
local adjlist_string = minetest.get_meta(pos):get_string("adjlist")
local adjlist = minetest.deserialize(adjlist_string) or default_adjlist -- backward compat: if not found, use old behavior: all directions
can_go = pipeworks.notvel(adjlist, vel) can_go = pipeworks.notvel(adjlist, vel)
end end
-- can_go() is expected to return an array-like table of candidate offsets. -- can_go() is expected to return an array-like table of candidate offsets.
@ -191,6 +194,7 @@ minetest.register_entity("pipeworks:tubed_item", {
from_data = function(self, itemstring) from_data = function(self, itemstring)
local stack = ItemStack(itemstring) local stack = ItemStack(itemstring)
--[[
local itemtable = stack:to_table() local itemtable = stack:to_table()
local itemname = nil local itemname = nil
if itemtable then if itemtable then
@ -202,6 +206,7 @@ minetest.register_entity("pipeworks:tubed_item", {
item_texture = minetest.registered_items[itemname].inventory_image item_texture = minetest.registered_items[itemname].inventory_image
item_type = minetest.registered_items[itemname].type item_type = minetest.registered_items[itemname].type
end end
--]]
self.object:set_properties({ self.object:set_properties({
is_visible = true, is_visible = true,
textures = {stack:get_name()} textures = {stack:get_name()}
@ -388,7 +393,7 @@ if minetest.get_modpath("mesecons_mvps") then
for _, n in ipairs(moved_nodes) do for _, n in ipairs(moved_nodes) do
moved[minetest.hash_node_position(n.oldpos)] = vector.subtract(n.pos, n.oldpos) moved[minetest.hash_node_position(n.oldpos)] = vector.subtract(n.pos, n.oldpos)
end end
for id, entity in pairs(luaentity.entities) do for _, entity in pairs(luaentity.entities) do
if entity.name == "pipeworks:tubed_item" then if entity.name == "pipeworks:tubed_item" then
local pos = entity:get_pos() local pos = entity:get_pos()
local rpos = vector.round(pos) local rpos = vector.round(pos)

110
locale/pipeworks.zh_CN.tr Normal file
View File

@ -0,0 +1,110 @@
# textdomain: pipeworks
# License: CC-by-SA 4.0
# Author: pevernow <3450354617@qq.com>
## digilines interfacing
Channel=频道
## init
Allow splitting incoming stacks from tubes=允许从管道中拆分传入堆栈
## autocrafter
Unknown item=通道
unconfigured Autocrafter: unknown recipe=未配置的自动工作台: 未知配方
unconfigured Autocrafter=未配置的自动工作台
'@1' Autocrafter (@2)=自动工作台 '@1' (@2)
Save=保存
paused '@1' Autocrafter=暂停的自动工作台
Autocrafter=自动工作台
## compat-furnaces
Allow splitting incoming material (not fuel) stacks from tubes=允许从管子中分离进来的材料(不是燃料)堆
## decorative tubes
Airtight steelblock embedded tube=密封嵌入式铁块管道
Airtight panel embedded tube=密封嵌入式片状管道
## devices
Pump/Intake Module=泵/进气模块
Valve=阀门
Decorative grating=Decorative grating
Spigot outlet=龙头
Airtight Pipe entry/exit=密闭管进/出
Flow Sensor=流量传感器
Flow sensor (on)=流量传感器(上)
empty=空的
@1% full=满的@1 %
Expansion Tank (@1)=扩展水箱 (@1)
Fluid Storage Tank (@1)=储液罐 (@1)
Fountainhead=源泉
Straight-only Pipe=直管
## filter-injector
(slot #@1 next)=(下一个插槽 : #@1)
@1 Filter-Injector=@1取物器
Sequence slots by Priority=优先顺序排列
Sequence slots Randomly=随机排列时隙
Sequence slots by Rotation=旋转顺序槽
Exact match - off=完全匹配-关闭
Exact match - on=完全匹配-开启
Prefer item types:=偏好物品类型 :
Itemwise=逐项
Stackwise=堆叠方式
Digiline=Digiline
## legacy
Auto-Tap=自动轴阀
## pipes
Pipe Segment=管道
Pipe Segment (legacy)=管道(旧版)
## routing tubes
Pneumatic tube segment=普通管道
Broken Tube=断管
High Priority Tube Segment=高优先级管段
Accelerating Pneumatic Tube Segment=加速管道
Crossing Pneumatic Tube Segment=定向管道
One way tube=单向管
## signal tubes
Detecting Pneumatic Tube Segment on=检测管道(运行中)
Detecting Pneumatic Tube Segment=检测管道
Digiline Detecting Pneumatic Tube Segment=Digiline检测管道
Digiline Detecting Tube=Digiline检测管
Conducting Pneumatic Tube Segment=传导管道
Conducting Pneumatic Tube Segment on=传导管道(运行中)
Digiline Conducting Pneumatic Tube Segment=Digiline传导式气动管道
Mesecon and Digiline Conducting Pneumatic Tube Segment=Mesecon和Digiline传导管道
Mesecon and Digiline Conducting Pneumatic Tube Segment on=Mesecon和Digiline传导管道运行中
## sorting tubes
Sorting Pneumatic Tube Segment=分类管道
Sorting pneumatic tube=分类管道
## teleport tube
Receive=接收
channels are public by default=频道默认为公开
use <player>:<channel> for fully private channels=将<player>:<channel>用于完全私人的频道
use <player>;<channel> for private receivers=使用<player>;<channel>作为私人接收器
Teleporting Pneumatic Tube Segment=传送管道
unconfigured Teleportation Tube=未配置的传送管道
Sorry, channel '@1' is reserved for exclusive use by @2=抱歉,频道‘@1保留供@2专用
Sorry, receiving from channel '@1' is reserved for @2=抱歉,从频道'@1'接收的内容已保留给'@2'
Teleportation Tube @1 on '@2'=传送管'@1'在'@2'上
## trashcan
Trash Can=垃圾箱
## tube registration
Pneumatic tube segment (legacy)=普通管道(旧式)
## vacuum tubes
Vacuuming Pneumatic Tube Segment=拾取管道
Adjustable Vacuuming Pneumatic Tube Segment=高级拾取管道
Adjustable Vacuuming Pneumatic Tube Segment (@1m)=高级拾取管道(@1m)
## wielder
Node Breaker=方块破坏器
Deployer=放置器
Dispenser=投掷器

View File

@ -13,8 +13,10 @@
-- newport = merge_port_states(state1, state2): just does result = state1 or state2 for every port -- newport = merge_port_states(state1, state2): just does result = state1 or state2 for every port
-- set_port(pos, rule, state): activates/deactivates the mesecons according to the port states -- set_port(pos, rule, state): activates/deactivates the mesecons according to the port states
-- set_port_states(pos, ports): Applies new port states to a Luacontroller at pos -- set_port_states(pos, ports): Applies new port states to a Luacontroller at pos
-- run(pos): runs the code in the controller at pos -- run_inner(pos, code, event): runs code on the controller at pos and event
-- reset_meta(pos, code, errmsg): performs a software-reset, installs new code and prints error messages -- reset_formspec(pos, code, errmsg): installs new code and prints error messages, without resetting LCID
-- reset_meta(pos, code, errmsg): performs a software-reset, installs new code and prints error message
-- run(pos, event): a wrapper for run_inner which gets code & handles errors via reset_meta
-- resetn(pos): performs a hardware reset, turns off all ports -- resetn(pos): performs a hardware reset, turns off all ports
-- --
-- The Sandbox -- The Sandbox
@ -77,7 +79,7 @@ local function update_real_port_states(pos, rule_name, new_state)
if rule_name.x == nil then if rule_name.x == nil then
for _, rname in ipairs(rule_name) do for _, rname in ipairs(rule_name) do
local port = pos_to_side[rname.x + (2 * rname.y) + (3 * rname.z) + 4] local port = pos_to_side[rname.x + (2 * rname.y) + (3 * rname.z) + 4]
L[port] = (newstate == "on") and 1 or 0 L[port] = (new_state == "on") and 1 or 0
end end
else else
local port = pos_to_side[rule_name.x + (2 * rule_name.y) + (3 * rule_name.z) + 4] local port = pos_to_side[rule_name.x + (2 * rule_name.y) + (3 * rule_name.z) + 4]
@ -168,7 +170,7 @@ local function set_port_states(pos, ports)
-- Solution / Workaround: -- Solution / Workaround:
-- Remember which output was turned off and ignore next "off" event. -- Remember which output was turned off and ignore next "off" event.
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local ign = minetest.deserialize(meta:get_string("ignore_offevents")) or {} local ign = minetest.deserialize(meta:get_string("ignore_offevents"), true) or {}
if ports.red and not vports.red and not mesecon.is_powered(pos, rules.red) then ign.red = true end if ports.red and not vports.red and not mesecon.is_powered(pos, rules.red) then ign.red = true end
if ports.blue and not vports.blue and not mesecon.is_powered(pos, rules.blue) then ign.blue = true end if ports.blue and not vports.blue and not mesecon.is_powered(pos, rules.blue) then ign.blue = true end
if ports.yellow and not vports.yellow and not mesecon.is_powered(pos, rules.yellow) then ign.yellow = true end if ports.yellow and not vports.yellow and not mesecon.is_powered(pos, rules.yellow) then ign.yellow = true end
@ -214,7 +216,7 @@ end
local function ignore_event(event, meta) local function ignore_event(event, meta)
if event.type ~= "off" then return false end if event.type ~= "off" then return false end
local ignore_offevents = minetest.deserialize(meta:get_string("ignore_offevents")) or {} local ignore_offevents = minetest.deserialize(meta:get_string("ignore_offevents"), true) or {}
if ignore_offevents[event.pin.name] then if ignore_offevents[event.pin.name] then
ignore_offevents[event.pin.name] = nil ignore_offevents[event.pin.name] = nil
meta:set_string("ignore_offevents", minetest.serialize(ignore_offevents)) meta:set_string("ignore_offevents", minetest.serialize(ignore_offevents))
@ -227,7 +229,11 @@ end
------------------------- -------------------------
local function safe_print(param) local function safe_print(param)
local string_meta = getmetatable("")
local sandbox = string_meta.__index
string_meta.__index = string -- Leave string sandbox temporarily
print(dump(param)) print(dump(param))
string_meta.__index = sandbox -- Restore string sandbox
end end
local function safe_date() local function safe_date()
@ -267,6 +273,7 @@ local function remove_functions(x)
local seen = {} local seen = {}
local function rfuncs(x) local function rfuncs(x)
if x == nil then return end
if seen[x] then return end if seen[x] then return end
seen[x] = true seen[x] = true
if type(x) ~= "table" then return end if type(x) ~= "table" then return end
@ -290,49 +297,174 @@ local function remove_functions(x)
return x return x
end end
local function get_interrupt(pos) -- The setting affects API so is not intended to be changeable at runtime
local get_interrupt
if mesecon.setting("luacontroller_lightweight_interrupts", false) then
-- use node timer
get_interrupt = function(pos, itbl, send_warning)
return (function(time, iid)
if type(time) ~= "number" then error("Delay must be a number") end
if iid ~= nil then send_warning("Interrupt IDs are disabled on this server") end
table.insert(itbl, function() minetest.get_node_timer(pos):start(time) end)
end)
end
else
-- use global action queue
-- itbl: Flat table of functions to run after sandbox cleanup, used to prevent various security hazards
get_interrupt = function(pos, itbl, send_warning)
-- iid = interrupt id -- iid = interrupt id
local function interrupt(time, iid) local function interrupt(time, iid)
if type(time) ~= "number" then return end -- NOTE: This runs within string metatable sandbox, so don't *rely* on anything of the form (""):y
-- Hence the values get moved out. Should take less time than original, so totally compatible
if type(time) ~= "number" then error("Delay must be a number") end
table.insert(itbl, function ()
-- Outside string metatable sandbox, can safely run this now
local luac_id = minetest.get_meta(pos):get_int("luac_id") local luac_id = minetest.get_meta(pos):get_int("luac_id")
-- Check if IID is dodgy, so you can't use interrupts to store an infinite amount of data.
-- Note that this is safe from alter-after-free because this code gets run after the sandbox has ended.
-- This runs outside of the timer and *shouldn't* harm perf. unless dodgy data is being sent in the first place
iid = remove_functions(iid)
local msg_ser = minetest.serialize(iid)
if #msg_ser <= mesecon.setting("luacontroller_interruptid_maxlen", 256) then
mesecon.queue:add_action(pos, "pipeworks:lc_tube_interrupt", {luac_id, iid}, time, iid, 1) mesecon.queue:add_action(pos, "pipeworks:lc_tube_interrupt", {luac_id, iid}, time, iid, 1)
else
send_warning("An interrupt ID was too large!")
end
end)
end end
return interrupt return interrupt
end
end
-- Given a message object passed to digiline_send, clean it up into a form
-- which is safe to transmit over the network and compute its "cost" (a very
-- rough estimate of its memory usage).
--
-- The cleaning comprises the following:
-- 1. Functions (and userdata, though user scripts ought not to get hold of
-- those in the first place) are removed, because they break the model of
-- Digilines as a network that carries basic data, and they could exfiltrate
-- references to mutable objects from one Luacontroller to another, allowing
-- inappropriate high-bandwidth, no-wires communication.
-- 2. Tables are duplicated because, being mutable, they could otherwise be
-- modified after the send is complete in order to change what data arrives
-- at the recipient, perhaps in violation of the previous cleaning rule or
-- in violation of the message size limit.
--
-- The cost indication is only approximate; its not a perfect measurement of
-- the number of bytes of memory used by the message object.
--
-- Parameters:
-- msg -- the message to clean
-- back_references -- for internal use only; do not provide
--
-- Returns:
-- 1. The cleaned object.
-- 2. The approximate cost of the object.
local function clean_and_weigh_digiline_message(msg, back_references)
local t = type(msg)
if t == "string" then
-- Strings are immutable so can be passed by reference, and cost their
-- length plus the size of the Lua object header (24 bytes on a 64-bit
-- platform) plus one byte for the NUL terminator.
return msg, #msg + 25
elseif t == "number" then
-- Numbers are passed by value so need not be touched, and cost 8 bytes
-- as all numbers in Lua are doubles.
return msg, 8
elseif t == "boolean" then
-- Booleans are passed by value so need not be touched, and cost 1
-- byte.
return msg, 1
elseif t == "table" then
-- Tables are duplicated. Check if this table has been seen before
-- (self-referential or shared table); if so, reuse the cleaned value
-- of the previous occurrence, maintaining table topology and avoiding
-- infinite recursion, and charge zero bytes for this as the object has
-- already been counted.
back_references = back_references or {}
local bref = back_references[msg]
if bref then
return bref, 0
end
-- Construct a new table by cleaning all the keys and values and adding
-- up their costs, plus 8 bytes as a rough estimate of table overhead.
local cost = 8
local ret = {}
back_references[msg] = ret
for k, v in pairs(msg) do
local k_cost, v_cost
k, k_cost = clean_and_weigh_digiline_message(k, back_references)
v, v_cost = clean_and_weigh_digiline_message(v, back_references)
if k ~= nil and v ~= nil then
-- Only include an element if its key and value are of legal
-- types.
ret[k] = v
end
-- If we only counted the cost of a table element when we actually
-- used it, we would be vulnerable to the following attack:
-- 1. Construct a huge table (too large to pass the cost limit).
-- 2. Insert it somewhere in a table, with a function as a key.
-- 3. Insert it somewhere in another table, with a number as a key.
-- 4. The first occurrence doesnt pay the cost because functions
-- are stripped and therefore the element is dropped.
-- 5. The second occurrence doesnt pay the cost because its in
-- back_references.
-- By counting the costs regardless of whether the objects will be
-- included, we avoid this attack; it may overestimate the cost of
-- some messages, but only those that wont be delivered intact
-- anyway because they contain illegal object types.
cost = cost + k_cost + v_cost
end
return ret, cost
else
return nil, 0
end
end end
local function get_digiline_send(pos) -- itbl: Flat table of functions to run after sandbox cleanup, used to prevent various security hazards
if not digiline then return end local function get_digiline_send(pos, itbl, send_warning)
if not minetest.global_exists("digilines") then return end
local chan_maxlen = mesecon.setting("luacontroller_digiline_channel_maxlen", 256)
local maxlen = mesecon.setting("luacontroller_digiline_maxlen", 50000)
return function(channel, msg) return function(channel, msg)
-- NOTE: This runs within string metatable sandbox, so don't *rely* on anything of the form (""):y
-- or via anything that could.
-- Make sure channel is string, number or boolean -- Make sure channel is string, number or boolean
if (type(channel) ~= "string" and type(channel) ~= "number" and type(channel) ~= "boolean") then if type(channel) == "string" then
if #channel > chan_maxlen then
send_warning("Channel string too long.")
return false
end
elseif (type(channel) ~= "string" and type(channel) ~= "number" and type(channel) ~= "boolean") then
send_warning("Channel must be string, number or boolean.")
return false return false
end end
-- It is technically possible to send functions over the wire since local msg_cost
-- the high performance impact of stripping those from the data has msg, msg_cost = clean_and_weigh_digiline_message(msg)
-- been decided to not be worth the added realism. if msg == nil or msg_cost > maxlen then
-- Make sure serialized version of the data is not insanely long to send_warning("Message was too complex, or contained invalid data.")
-- prevent DoS-like attacks
local msg_ser = minetest.serialize(msg)
if #msg_ser > mesecon.setting("luacontroller_digiline_maxlen", 50000) then
return false return false
end end
minetest.after(0, function() table.insert(itbl, function ()
digilines.receptor_send(pos, digiline_rules_luatube, channel, msg) -- Runs outside of string metatable sandbox
local luac_id = minetest.get_meta(pos):get_int("luac_id")
mesecon.queue:add_action(pos, "pipeworks:lt_digiline_relay", {channel, luac_id, msg})
end) end)
return true return true
end end
end end
local safe_globals = { local safe_globals = {
-- Don't add pcall/xpcall unless willing to deal with the consequences (unless very careful, incredibly likely to allow killing server indirectly)
"assert", "error", "ipairs", "next", "pairs", "select", "assert", "error", "ipairs", "next", "pairs", "select",
"tonumber", "tostring", "type", "unpack", "_VERSION" "tonumber", "tostring", "type", "unpack", "_VERSION"
} }
local function create_environment(pos, mem, event) local function create_environment(pos, mem, event, itbl, send_warning)
-- Make sure the tube hasn't broken. -- Make sure the tube hasn't broken.
local vports = minetest.registered_nodes[minetest.get_node(pos).name].virtual_portstates local vports = minetest.registered_nodes[minetest.get_node(pos).name].virtual_portstates
if not vports then return {} end if not vports then return {} end
@ -352,8 +484,8 @@ local function create_environment(pos, mem, event)
heat = mesecon.get_heat(pos), heat = mesecon.get_heat(pos),
heat_max = mesecon.setting("overheat_max", 20), heat_max = mesecon.setting("overheat_max", 20),
print = safe_print, print = safe_print,
interrupt = get_interrupt(pos), interrupt = get_interrupt(pos, itbl, send_warning),
digiline_send = get_digiline_send(pos), digiline_send = get_digiline_send(pos, itbl, send_warning),
string = { string = {
byte = string.byte, byte = string.byte,
char = string.char, char = string.char,
@ -441,10 +573,11 @@ local function create_sandbox(code, env)
jit.off(f, true) jit.off(f, true)
end end
local maxevents = mesecon.setting("luacontroller_maxevents", 10000)
return function(...) return function(...)
-- NOTE: This runs within string metatable sandbox, so the setting's been moved out for safety
-- Use instruction counter to stop execution -- Use instruction counter to stop execution
-- after luacontroller_maxevents -- after luacontroller_maxevents
local maxevents = mesecon.setting("luacontroller_maxevents", 10000)
debug.sethook(timeout, "", maxevents) debug.sethook(timeout, "", maxevents)
local ok, ret = pcall(f, ...) local ok, ret = pcall(f, ...)
debug.sethook() -- Clear hook debug.sethook() -- Clear hook
@ -455,7 +588,7 @@ end
local function load_memory(meta) local function load_memory(meta)
return minetest.deserialize(meta:get_string("lc_memory")) or {} return minetest.deserialize(meta:get_string("lc_memory"), true) or {}
end end
@ -465,6 +598,7 @@ local function save_memory(pos, meta, mem)
if (#memstring <= memsize_max) then if (#memstring <= memsize_max) then
meta:set_string("lc_memory", memstring) meta:set_string("lc_memory", memstring)
meta:mark_as_private("lc_memory")
else else
print("Error: lua_tube memory overflow. "..memsize_max.." bytes available, " print("Error: lua_tube memory overflow. "..memsize_max.." bytes available, "
..#memstring.." required. Controller overheats.") ..#memstring.." required. Controller overheats.")
@ -472,26 +606,42 @@ local function save_memory(pos, meta, mem)
end end
end end
-- Returns success (boolean), errmsg (string), retval(any, return value of the user supplied code)
local function run(pos, event) -- run (as opposed to run_inner) is responsible for setting up meta according to this output
local function run_inner(pos, code, event)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
if overheat(pos) then return end -- Note: These return success, presumably to avoid changing LC ID.
if ignore_event(event, meta) then return end if overheat(pos) then return true, "", nil end
if ignore_event(event, meta) then return true, "", nil end
-- Load code & mem from meta -- Load code & mem from meta
local mem = load_memory(meta) local mem = load_memory(meta)
local code = meta:get_string("code") local code = meta:get_string("code")
-- 'Last warning' label.
local warning = ""
local function send_warning(str)
warning = "Warning: " .. str
end
-- Create environment -- Create environment
local env = create_environment(pos, mem, event) local itbl = {}
local env = create_environment(pos, mem, event, itbl, send_warning)
-- Create the sandbox and execute code -- Create the sandbox and execute code
local f, msg = create_sandbox(code, env) local f, msg = create_sandbox(code, env)
if not f then return false, msg end if not f then return false, msg, nil end
local succ, msg = pcall(f) -- Start string true sandboxing
if not succ then return false, msg end local onetruestring = getmetatable("")
-- If a string sandbox is already up yet inconsistent, something is very wrong
assert(onetruestring.__index == string)
onetruestring.__index = env.string
local success, msg = pcall(f)
onetruestring.__index = string
-- End string true sandboxing
if not success then return false, msg, nil end
if type(env.port) ~= "table" then if type(env.port) ~= "table" then
return false, "Ports set are invalid." return false, "Ports set are invalid.", nil
end end
-- Actually set the ports -- Actually set the ports
@ -500,35 +650,81 @@ local function run(pos, event)
-- Save memory. This may burn the luacontroller if a memory overflow occurs. -- Save memory. This may burn the luacontroller if a memory overflow occurs.
save_memory(pos, meta, env.mem) save_memory(pos, meta, env.mem)
return succ, msg -- Execute deferred tasks
for _, v in ipairs(itbl) do
local failure = v()
if failure then
return false, failure, nil
end
end
return true, warning, msg
end end
mesecon.queue:add_function("pipeworks:lc_tube_interrupt", function (pos, luac_id, iid) local function reset_formspec(meta, code, errmsg)
-- There is no lua_tube anymore / it has been reprogrammed / replaced / burnt meta:set_string("code", code)
if (minetest.get_meta(pos):get_int("luac_id") ~= luac_id) then return end meta:mark_as_private("code")
if (minetest.registered_nodes[minetest.get_node(pos).name].is_burnt) then return end code = minetest.formspec_escape(code or "")
run(pos, {type = "interrupt", iid = iid}) errmsg = minetest.formspec_escape(tostring(errmsg or ""))
end) meta:set_string("formspec", "size[12,10]"
.."background[-0.2,-0.25;12.4,10.75;jeija_luac_background.png]"
.."label[0.1,8.3;"..errmsg.."]"
.."textarea[0.2,0.2;12.2,9.5;code;;"..code.."]"
.."image_button[4.75,8.75;2.5,1;jeija_luac_runbutton.png;program;]"
.."image_button_exit[11.72,-0.25;0.425,0.4;jeija_close_window.png;exit;]"
)
end
local function reset_meta(pos, code, errmsg) local function reset_meta(pos, code, errmsg)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
meta:set_string("code", code) reset_formspec(meta, code, errmsg)
code = minetest.formspec_escape(code or "")
errmsg = minetest.formspec_escape(tostring(errmsg or ""))
meta:set_string("formspec", "size[12,10]"..
"background[-0.2,-0.25;12.4,10.75;jeija_luac_background.png]"..
"textarea[0.2,0.2;12.2,9.5;code;;"..code.."]"..
"image_button[4.75,8.75;2.5,1;jeija_luac_runbutton.png;program;]"..
"image_button_exit[11.72,-0.25;0.425,0.4;jeija_close_window.png;exit;]"..
"label[0.1,9;"..errmsg.."]")
meta:set_int("luac_id", math.random(1, 65535)) meta:set_int("luac_id", math.random(1, 65535))
end end
-- Wraps run_inner with LC-reset-on-error
local function run(pos, event)
local meta = minetest.get_meta(pos)
local code = meta:get_string("code")
local ok, errmsg, retval = run_inner(pos, code, event)
if not ok then
reset_meta(pos, code, errmsg)
else
reset_formspec(meta, code, errmsg)
end
return ok, errmsg, retval
end
local function reset(pos) local function reset(pos)
set_port_states(pos, {red = false, blue = false, yellow = false, set_port_states(pos, {red = false, blue = false, yellow = false,
green = false, black = false, white = false}) green = false, black = false, white = false})
end end
local function node_timer(pos)
if minetest.registered_nodes[minetest.get_node(pos).name].is_burnt then
return false
end
run(pos, {type="interrupt"})
return false
end
-----------------------
-- A.Queue callbacks --
-----------------------
mesecon.queue:add_function("pipeworks:lc_tube_interrupt", function (pos, luac_id, iid)
-- There is no lua_tube anymore / it has been reprogrammed / replaced / burnt
if (minetest.get_meta(pos):get_int("luac_id") ~= luac_id) then return end
if (minetest.registered_nodes[minetest.get_node(pos).name].is_burnt) then return end
run(pos, {type="interrupt", iid = iid})
end)
mesecon.queue:add_function("pipeworks:lt_digiline_relay", function (pos, channel, luac_id, msg)
if not digiline then return end
-- This check is only really necessary because in case of server crash, old actions can be thrown into the future
if (minetest.get_meta(pos):get_int("luac_id") ~= luac_id) then return end
if (minetest.registered_nodes[minetest.get_node(pos).name].is_burnt) then return end
-- The actual work
digiline:receptor_send(pos, digiline_rules_luatube, channel, msg)
end)
----------------------- -----------------------
-- Node Registration -- -- Node Registration --
@ -558,6 +754,7 @@ local digiline = {
receptor = {}, receptor = {},
effector = { effector = {
action = function(pos, node, channel, msg) action = function(pos, node, channel, msg)
msg = clean_and_weigh_digiline_message(msg)
run(pos, {type = "digiline", channel = channel, msg = msg}) run(pos, {type = "digiline", channel = channel, msg = msg})
end end
}, },
@ -565,6 +762,18 @@ local digiline = {
rules = pipeworks.digilines_rules rules = pipeworks.digilines_rules
}, },
} }
local function get_program(pos)
local meta = minetest.get_meta(pos)
return meta:get_string("code")
end
local function set_program(pos, code)
reset(pos)
reset_meta(pos, code)
return run(pos, {type="program"})
end
local function on_receive_fields(pos, form_name, fields, sender) local function on_receive_fields(pos, form_name, fields, sender)
if not fields.program then if not fields.program then
return return
@ -574,12 +783,10 @@ local function on_receive_fields(pos, form_name, fields, sender)
minetest.record_protection_violation(pos, name) minetest.record_protection_violation(pos, name)
return return
end end
reset(pos) local ok, err = set_program(pos, fields.code)
reset_meta(pos, fields.code) if not ok then
local succ, err = run(pos, {type="program"}) -- it's not an error from the server perspective
if not succ then minetest.log("action", "Lua controller programming error: " .. tostring(err))
print(err)
reset_meta(pos, fields.code, err)
end end
end end
@ -715,7 +922,11 @@ for white = 0, 1 do
receptor = { receptor = {
state = mesecon.state.on, state = mesecon.state.on,
rules = output_rules[cid] rules = output_rules[cid]
} },
luacontroller = {
get_program = get_program,
set_program = set_program,
},
} }
minetest.register_node(node_name, { minetest.register_node(node_name, {
@ -723,6 +934,7 @@ for white = 0, 1 do
drawtype = "nodebox", drawtype = "nodebox",
tiles = tiles, tiles = tiles,
paramtype = "light", paramtype = "light",
is_ground_content = false,
groups = groups, groups = groups,
drop = BASENAME.."000000", drop = BASENAME.."000000",
sunlight_propagates = true, sunlight_propagates = true,
@ -749,6 +961,7 @@ for white = 0, 1 do
pipeworks.after_dig(pos, node) pipeworks.after_dig(pos, node)
end, end,
is_luacontroller = true, is_luacontroller = true,
on_timer = node_timer,
tubelike = 1, tubelike = 1,
tube = { tube = {
connect_sides = {front = 1, back = 1, left = 1, right = 1, top = 1, bottom = 1}, connect_sides = {front = 1, back = 1, left = 1, right = 1, top = 1, bottom = 1},
@ -756,13 +969,13 @@ for white = 0, 1 do
can_go = function(pos, node, velocity, stack) can_go = function(pos, node, velocity, stack)
local src = {name = nil} local src = {name = nil}
-- add color of the incoming tube explicitly; referring to rules, in case they change later -- add color of the incoming tube explicitly; referring to rules, in case they change later
for color, rule in pairs(rules) do for _, rule in pairs(rules) do
if (-velocity.x == rule.x and -velocity.y == rule.y and -velocity.z == rule.z) then if (-velocity.x == rule.x and -velocity.y == rule.y and -velocity.z == rule.z) then
src.name = rule.name src.name = rule.name
break break
end end
end end
local succ, msg = run(pos, { local succ, _, msg = run(pos, {
type = "item", type = "item",
pin = src, pin = src,
itemstring = stack:to_string(), itemstring = stack:to_string(),
@ -780,15 +993,7 @@ for white = 0, 1 do
on_blast = function(pos, intensity) on_blast = function(pos, intensity)
if not intensity or intensity > 1 + 3^0.5 then if not intensity or intensity > 1 + 3^0.5 then
minetest.remove_node(pos) minetest.remove_node(pos)
return {string.format("%s_%s", name, dropname)} return
end
minetest.swap_node(pos, {name = "pipeworks:broken_tube_1"})
pipeworks.scan_for_tube_objects(pos)
end,
on_blast = function(pos, intensity)
if not intensity or intensity > 1 + 3^0.5 then
minetest.remove_node(pos)
return {string.format("%s_%s", name, dropname)}
end end
minetest.swap_node(pos, {name = "pipeworks:broken_tube_1"}) minetest.swap_node(pos, {name = "pipeworks:broken_tube_1"})
pipeworks.scan_for_tube_objects(pos) pipeworks.scan_for_tube_objects(pos)
@ -836,6 +1041,7 @@ minetest.register_node(BASENAME .. "_burnt", {
tiles = tiles_burnt, tiles = tiles_burnt,
is_burnt = true, is_burnt = true,
paramtype = "light", paramtype = "light",
is_ground_content = false,
groups = {snappy = 3, tube = 1, tubedevice = 1, not_in_creative_inventory=1}, groups = {snappy = 3, tube = 1, tubedevice = 1, not_in_creative_inventory=1},
drop = BASENAME.."000000", drop = BASENAME.."000000",
sunlight_propagates = true, sunlight_propagates = true,
@ -864,7 +1070,7 @@ minetest.register_node(BASENAME .. "_burnt", {
on_blast = function(pos, intensity) on_blast = function(pos, intensity)
if not intensity or intensity > 1 + 3^0.5 then if not intensity or intensity > 1 + 3^0.5 then
minetest.remove_node(pos) minetest.remove_node(pos)
return {string.format("%s_%s", name, dropname)} return
end end
minetest.swap_node(pos, {name = "pipeworks:broken_tube_1"}) minetest.swap_node(pos, {name = "pipeworks:broken_tube_1"})
pipeworks.scan_for_tube_objects(pos) pipeworks.scan_for_tube_objects(pos)

View File

@ -318,19 +318,20 @@ end
function luaentity.get_objects_inside_radius(pos, radius) function luaentity.get_objects_inside_radius(pos, radius)
local objects = {} local objects = {}
local index = 1 local index = 1
for id, entity in pairs(luaentity.entities) do for _, entity in pairs(luaentity.entities) do
if vector.distance(pos, entity:get_pos()) <= radius then if vector.distance(pos, entity:get_pos()) <= radius then
objects[index] = entity objects[index] = entity
index = index + 1 index = index + 1
end end
end end
return objects
end end
local move_entities_globalstep_part2 = function(dtime) local move_entities_globalstep_part2 = function(dtime)
if not luaentity.entities then if not luaentity.entities then
luaentity.entities = read_entities() luaentity.entities = read_entities()
end end
for id, entity in pairs(luaentity.entities) do for _, entity in pairs(luaentity.entities) do
local master = entity._attached_entities_master local master = entity._attached_entities_master
local master_def = master and entity._attached_entities[master] local master_def = master and entity._attached_entities[master]
local master_entity = master_def and master_def.entity local master_entity = master_def and master_def.entity
@ -340,6 +341,8 @@ local move_entities_globalstep_part2 = function(dtime)
entity._velocity = master_entity:get_velocity() entity._velocity = master_entity:get_velocity()
entity._acceleration = master_entity:get_acceleration() entity._acceleration = master_entity:get_acceleration()
else else
entity._velocity = entity._velocity or vector.new(0,0,0)
entity._acceleration = entity._acceleration or vector.new(0,0,0)
entity._pos = vector.add(vector.add( entity._pos = vector.add(vector.add(
entity._pos, entity._pos,
vector.multiply(entity._velocity, dtime)), vector.multiply(entity._velocity, dtime)),

View File

@ -11,7 +11,7 @@ local new_flow_logic_register = pipeworks.flowables.register
local polys = "" local polys = ""
if pipeworks.enable_lowpoly then polys = "_lowpoly" end if pipeworks.enable_lowpoly then polys = "_lowpoly" end
local vti = {4, 3, 2, 1, 6, 5} --~ local vti = {4, 3, 2, 1, 6, 5}
local cconnects = {{}, {1}, {1, 2}, {1, 3}, {1, 3, 5}, {1, 2, 3}, {1, 2, 3, 5}, {1, 2, 3, 4}, {1, 2, 3, 4, 5}, {1, 2, 3, 4, 5, 6}} local cconnects = {{}, {1}, {1, 2}, {1, 3}, {1, 3, 5}, {1, 2, 3}, {1, 2, 3, 5}, {1, 2, 3, 4}, {1, 2, 3, 4, 5}, {1, 2, 3, 4, 5, 6}}
for index, connects in ipairs(cconnects) do for index, connects in ipairs(cconnects) do
local outsel = {} local outsel = {}
@ -30,10 +30,12 @@ for index, connects in ipairs(cconnects) do
table.insert(outsel, pipeworks.pipe_selectboxes[v]) table.insert(outsel, pipeworks.pipe_selectboxes[v])
end end
--[[
if #connects == 1 then if #connects == 1 then
local v = connects[1] local v = connects[1]
v = v-1 + 2*(v%2) -- Opposite side v = v-1 + 2*(v%2) -- Opposite side
end end
--]]
local pgroups = {snappy = 3, pipe = 1, not_in_creative_inventory = 1} local pgroups = {snappy = 3, pipe = 1, not_in_creative_inventory = 1}
local pipedesc = S("Pipe Segment").." "..dump(connects) local pipedesc = S("Pipe Segment").." "..dump(connects)

View File

@ -29,7 +29,7 @@ end
local formatvec = function(vec) local sep="," return "("..tostring(vec.x)..sep..tostring(vec.y)..sep..tostring(vec.z)..")" end --~ local formatvec = function(vec) local sep="," return "("..tostring(vec.x)..sep..tostring(vec.y)..sep..tostring(vec.z)..")" end
@ -39,7 +39,7 @@ local formatvec = function(vec) local sep="," return "("..tostring(vec.x)..sep..
local check_for_liquids_v2 = function(pos, limit) local check_for_liquids_v2 = function(pos, limit)
local coords = make_coords_offsets(pos, false) local coords = make_coords_offsets(pos, false)
local total = 0 local total = 0
for index, tpos in ipairs(coords) do for _, tpos in ipairs(coords) do
if total >= limit then break end if total >= limit then break end
local name = minetest.get_node(tpos).name local name = minetest.get_node(tpos).name
if name == "default:water_source" then if name == "default:water_source" then
@ -69,11 +69,13 @@ end
-- logging is unreliable when something is crashing... -- logging is unreliable when something is crashing...
--[[
local nilexplode = function(caller, label, value) local nilexplode = function(caller, label, value)
if value == nil then if value == nil then
error(caller..": "..label.." was nil") error(caller..": "..label.." was nil")
end end
end end
--]]
@ -151,7 +153,7 @@ local get_neighbour_positions = function(pos, node)
-- then, check each possible neighbour to see if they can be reached from this node. -- then, check each possible neighbour to see if they can be reached from this node.
local connections = {} local connections = {}
for index, offset in ipairs(candidates) do for _, offset in ipairs(candidates) do
local npos = vector.add(pos, offset) local npos = vector.add(pos, offset)
local neighbour = minetest.get_node(npos) local neighbour = minetest.get_node(npos)
local nodename = neighbour.name local nodename = neighbour.name
@ -185,13 +187,12 @@ flowlogic.balance_pressure = function(pos, node, currentpressure)
-- local dname = "flowlogic.balance_pressure()@"..formatvec(pos).." " -- local dname = "flowlogic.balance_pressure()@"..formatvec(pos).." "
-- check the pressure of all nearby flowable nodes, and average it out. -- check the pressure of all nearby flowable nodes, and average it out.
-- pressure handles to average over
local connections = {}
-- unconditionally include self in nodes to average over. -- unconditionally include self in nodes to average over.
-- result of averaging will be returned as new pressure for main flow logic callback -- result of averaging will be returned as new pressure for main flow logic callback
local totalv = currentpressure local totalv = currentpressure
local totalc = 1 local totalc = 1
-- pressure handles to average over
local connections = get_neighbour_positions(pos, node) local connections = get_neighbour_positions(pos, node)
-- for each neighbour, add neighbour's pressure to the total to balance out -- for each neighbour, add neighbour's pressure to the total to balance out
@ -287,7 +288,7 @@ flowlogic.run_output = function(pos, node, currentpressure, oldpressure, outputd
local upper = outputdef.upper local upper = outputdef.upper
local lower = outputdef.lower local lower = outputdef.lower
local result = currentpressure local result = currentpressure
local threshold = nil local threshold
if finitemode then threshold = lower else threshold = upper end if finitemode then threshold = lower else threshold = upper end
if currentpressure > threshold then if currentpressure > threshold then
local takenpressure = outputdef.outputfn(pos, node, currentpressure, finitemode) local takenpressure = outputdef.outputfn(pos, node, currentpressure, finitemode)
@ -316,7 +317,7 @@ flowlogic.run_transition = function(node, currentpressure)
local nodename_prev = simplesetdef[1].nodename local nodename_prev = simplesetdef[1].nodename
local result_nodename = node.name local result_nodename = node.name
for index, element in ipairs(simplesetdef) do for _, element in ipairs(simplesetdef) do
-- find the highest element that is below the current pressure. -- find the highest element that is below the current pressure.
local threshold = element.threshold local threshold = element.threshold
if threshold > currentpressure then if threshold > currentpressure then
@ -338,7 +339,9 @@ flowlogic.run_transition = function(node, currentpressure)
end end
if not found then if not found then
pipeworks.logger("flowlogic.run_transition() BUG no transition definitions found! nodename="..nodename.." currentpressure="..tostring(currentpressure)) pipeworks.logger("flowlogic.run_transition() BUG no transition " ..
"definitions found! node.name=" .. node.name ..
" currentpressure=" .. tostring(currentpressure))
end end
return result return result

View File

@ -26,8 +26,8 @@ local insertbase = function(nodename)
end end
local regwarning = function(kind, nodename) local regwarning = function(kind, nodename)
local tail = "" --~ local tail = ""
if pipeworks.toggles.pipe_mode ~= "pressure" then tail = " but pressure logic not enabled" end --~ if pipeworks.toggles.pipe_mode ~= "pressure" then tail = " but pressure logic not enabled" end
--pipeworks.logger(kind.." flow logic registry requested for "..nodename..tail) --pipeworks.logger(kind.." flow logic registry requested for "..nodename..tail)
end end
@ -106,7 +106,7 @@ register.directional_horizonal_rotate = function(nodename, doubleended)
end end
local directionfn = function(node, direction) local directionfn = function(node, direction)
local result = false local result = false
for index, endvec in ipairs(getends(node)) do for _, endvec in ipairs(getends(node)) do
if vector.equals(direction, endvec) then result = true end if vector.equals(direction, endvec) then result = true end
end end
return result return result

View File

@ -23,7 +23,7 @@ end
local function migrate_tube_db() local function migrate_tube_db()
local tmp_db = {} local tmp_db = {}
tp_tube_db.version = nil tp_tube_db.version = nil
for key, val in pairs(tp_tube_db) do for _, val in pairs(tp_tube_db) do
if(val.channel ~= "") then -- skip unconfigured tubes if(val.channel ~= "") then -- skip unconfigured tubes
tmp_db[hash(val)] = val tmp_db[hash(val)] = val
end end
@ -261,3 +261,11 @@ if minetest.get_modpath("mesecons_mvps") ~= nil then
end end
end) end)
end end
-- Expose teleport tube database API for other mods
pipeworks.tptube = {
hash = hash,
save_tube_db = save_tube_db,
get_db = function() return tp_tube_db or read_tube_db() end,
tp_tube_db_version = tp_tube_db_version
}

View File

@ -1,10 +1,6 @@
local S = minetest.get_translator("pipeworks") local S = minetest.get_translator("pipeworks")
local assumed_eye_pos = vector.new(0, 1.5, 0) local assumed_eye_pos = vector.new(0, 1.5, 0)
local function vector_copy(v)
return { x = v.x, y = v.y, z = v.z }
end
local function delay(x) local function delay(x)
return (function() return x end) return (function() return x end)
end end
@ -353,7 +349,7 @@ if pipeworks.enable_node_breaker then
{pos=pointed_thing.under, gain=sound.gain}) {pos=pointed_thing.under, gain=sound.gain})
end end
wieldstack = virtplayer:get_wielded_item() wieldstack = virtplayer:get_wielded_item()
else --~ else
--pipeworks.logger(dname.."couldn't dig node!") --pipeworks.logger(dname.."couldn't dig node!")
end end
end end
@ -378,7 +374,7 @@ if pipeworks.enable_node_breaker then
minetest.register_craft({ minetest.register_craft({
output = "pipeworks:nodebreaker_off", output = "pipeworks:nodebreaker_off",
recipe = { recipe = {
{ "pipeworks:gear", "pipeworks:gear", "pipeworks:gear" }, { "basic_materials:gear_steel", "basic_materials:gear_steel", "basic_materials:gear_steel" },
{ "default:stone", "mesecons:piston", "default:stone" }, { "default:stone", "mesecons:piston", "default:stone" },
{ "group:wood", "mesecons:mesecon", "group:wood" }, { "group:wood", "mesecons:mesecon", "group:wood" },
} }