From 201e215ad4cbeffe2dfdb32b1fc7eceb53629ea3 Mon Sep 17 00:00:00 2001 From: SX <50966843+S-S-X@users.noreply.github.com> Date: Thu, 10 Dec 2020 15:47:30 +0200 Subject: [PATCH] Add "Multimeter" network inspection tool (#137) * (#107) Add network inspection tool * (#107) Fix luacheck and crash if node does not have network * (#107) Add few rows to display when no network * (#107) Implement formspec functions, add remote start * Multimeter texture * Update formspec, textures and initial use behavior Co-authored-by: SX <50966843+S-S-X@users.noreply.github.com> --- technic/textures/technic_multimeter.png | Bin 0 -> 321 bytes technic/textures/technic_multimeter_bg.png | Bin 0 -> 108 bytes .../textures/technic_multimeter_button.png | Bin 0 -> 164 bytes .../technic_multimeter_button_pressed.png | Bin 0 -> 170 bytes technic/textures/technic_multimeter_logo.png | Bin 0 -> 299 bytes technic/tools/init.lua | 1 + technic/tools/multimeter.lua | 322 ++++++++++++++++++ 7 files changed, 323 insertions(+) create mode 100644 technic/textures/technic_multimeter.png create mode 100644 technic/textures/technic_multimeter_bg.png create mode 100644 technic/textures/technic_multimeter_button.png create mode 100644 technic/textures/technic_multimeter_button_pressed.png create mode 100644 technic/textures/technic_multimeter_logo.png create mode 100644 technic/tools/multimeter.lua diff --git a/technic/textures/technic_multimeter.png b/technic/textures/technic_multimeter.png new file mode 100644 index 0000000000000000000000000000000000000000..d5bd9a16f2e2d8ae5bf28cd0d4138cfe7aedc484 GIT binary patch literal 321 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&F%}28J29*~C-V}>kqGb!aRt(* z#%A|)&Ho?Z_wGwG$@ILri1*Vr{yC|vl0Z3*k|4iehW`+7^;C!vQ0%a$i(^Q|oVQo3 z`3@_HxH>-H|6g`_<1mdKI;Vst0Fg^` A#sB~S literal 0 HcmV?d00001 diff --git a/technic/textures/technic_multimeter_bg.png b/technic/textures/technic_multimeter_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..5f19322631e757d720f000edea85f5954d1a5342 GIT binary patch literal 108 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j<3?z5j>~{cCJOMr-t`=sNO&R(dx|IGO;OD=- zd=pSy+SA1`gyVX0#MY+7ERMr}I2BB?nGRoJXyfEz2(DtcT=<~L4ycU5)78&qol`;+ E08LjND*ylh literal 0 HcmV?d00001 diff --git a/technic/textures/technic_multimeter_button.png b/technic/textures/technic_multimeter_button.png new file mode 100644 index 0000000000000000000000000000000000000000..fdee91e895cd0278d9b8b83d446aec58e49add5d GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6vOQfKLp096ooL9_U?9Nq{KzRY zj(rF8m-P6bpIWCFYvp+9R@TH0M-lr6A;-^_Xp5{lUbyh{6n@L82Se;8dZ}G|>^6_n zMRfQ6Ob!<*yC2Oy-E8dVqt`58YCi7h9?z;R-Jmyl&iaRD--DjnF>Dl(ne=n()mEUj N44$rjF6*2UngDLAJZ1m@ literal 0 HcmV?d00001 diff --git a/technic/textures/technic_multimeter_button_pressed.png b/technic/textures/technic_multimeter_button_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..563e2d331de26d7ffca19bed5dc2f2e6bc70f919 GIT binary patch literal 170 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf63O!vMLp096ooLJF;2^^GT(I|r z%ckA}Ht_>z-?H9GTs*PEZK2}A51X~Q#0{>Vp8eA9p^NF1h?02O_Lx-(B{e?PX^W&z z@px2TVr1L0LbuveTGGbuM>Dhis_8YUN(u${jBSOPyZ0UE@|=^9uw|3An|8Tf%>DBMxAM$MwRS@uX70iRhA z0MxJ_CdP!ChepjBP_sngg^)doW-+dUgY~4PFHEz@PphM5jhaP{S@l%RL!)L57PA;( zh1q_}&4Ppa2PpL+Iq3^w)&~@`m 99.99 and "100" or ("%0.2f"):format(p) +end +-- Get network TTL +local function net_ttl(net) return type(net.timeout) == "number" and (net.timeout - minetest.get_us_time()) end +-- Microseconds to milliseconds +local function us2ms(val) return type(val) == "number" and (val / 1000) or 0 end +-- Microseconds to seconds +local function us2s(val) return type(val) == "number" and (val / 1000 / 1000) or 0 end + +local function formspec(data) + local tablerows = "" + for _,row in ipairs(data.rows) do + tablerows = tablerows .. ",1" .. + "," .. formspec_escape(row[1] or "-") .. + "," .. formspec_escape(row[2] or "-") .. + "," .. formspec_escape(row[3] or "-") + end + local base36_net = base36(data.network_id) or "N/A" + return formspec_format_string:format(base36_net, base36_net, tablerows) +end + +local function multimeter_inspect(itemstack, player, pos, fault) + local id = pos and technic.pos2network(pos) + local rows = {} + local data = { network_id = id, rows = rows } + local name = player:get_player_name() + if id and itemstack and not fault then + table.insert(rows, { "Ref. point", v2s(technic.network2pos(id)), "coord" }) + table.insert(rows, { "Activated", technic.active_networks[id] and "yes" or "no", "active" }) + local net = technic.networks[id] + if net then + table.insert(rows, { "Timeout", ("%0.1f"):format(us2s(net_ttl(net))), "s" }) + table.insert(rows, { "Lag", ("%0.2f"):format(us2ms(net.lag)), "ms" }) + table.insert(rows, { "Skip", net.skip, "cycles" }) + table.insert(rows, {}) + local PR = net.PR_nodes + local RE = net.RE_nodes + local BA = net.BA_nodes + local C = count(net.all_nodes) + table.insert(rows, { "Supply", net.supply, "EU" }) + table.insert(rows, { "Demand", net.demand, "EU" }) + table.insert(rows, { "Battery charge", net.battery_charge, "EU" }) + table.insert(rows, { "Battery charge", percent(net.battery_charge, net.battery_charge_max), "%" }) + table.insert(rows, { "Battery capacity", net.battery_charge_max, "EU" }) + table.insert(rows, {}) + table.insert(rows, { "Nodes", C, "count" }) + table.insert(rows, { "Cables", C - #PR - #RE - #BA, "count" }) -- FIXME: Do not count PR+RE duplicates + table.insert(rows, { "Generators", #PR, "count" }) + table.insert(rows, { "Consumers", #RE, "count" }) + table.insert(rows, { "Batteries", #BA, "count" }) + end + else + table.insert(rows, { "Operation failed", "", "" }) + if not id then + table.insert(rows, {}) + table.insert(rows, { "Bad contact", "No network", "Fault" }) + end + if fault then table.insert(rows, {}) end + if fault == "battery" then + table.insert(rows, { "Recharge", "Insufficient charge", "Fault" }) + elseif fault == "decode" then + table.insert(rows, { "Decoder error", "Net ID decode", "Fault" }) + elseif fault == "switchload" then + table.insert(rows, { "Remote load error", "Load switching station", "Fault" }) + elseif fault == "cableload" then + table.insert(rows, { "Remote load error", "Load ref. cable", "Fault" }) + elseif fault == "protected" then + table.insert(rows, { "Protection error", "Area is protected", "Access" }) + end + if not itemstack then + table.insert(rows, {}) + table.insert(rows, { "Missing FLUTE", "FLUTE not found", "Fault" }) + end + end + open_formspecs[name] = { pos = pos, itemstack = itemstack } + minetest.show_formspec(name, "technic:multimeter", formspec(data)) +end + +local function remote_start_net(player, pos) + local sw_pos = {x=pos.x,y=pos.y+1,z=pos.z} + -- Try to load switch network node with VoxelManip + local sw_node = technic.get_or_load_node(sw_pos) or minetest.get_node(sw_pos) + if sw_node.name ~= "technic:switching_station" then return "switchload" end + -- Try to load network node with VoxelManip + local tier = technic.sw_pos2tier(sw_pos, true) + if not tier then return "cableload" end + -- Check protections + if minetest.is_protected(pos, player:get_player_name()) then return "protected" end + -- All checks passed, start network + local network_id = technic.sw_pos2network(sw_pos) or technic.create_network(sw_pos) + technic.activate_network(network_id, 300) +end + +local function async_itemstack_use_charge(itemstack, player, multiplier) + local fault = nil + local inv, invindex, invstack = async_itemstack_get(player, itemstack) + if not inv or not invindex or not use_charge(invstack, multiplier) then + -- Multimeter battery empty + fault = "battery" + elseif invstack then + inv:set_stack('main', invindex, invstack) + end + return invstack, fault +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname ~= "technic:multimeter" then + -- Not our formspec, tell engine to continue with other registered handlers + return + end + local name = player:get_player_name() + local flute = open_formspecs[name] + if fields and name then + local pos = flute and flute.pos + if fields.up then + local itemstack = flute and flute.itemstack + local invstack, fault = async_itemstack_use_charge(itemstack, player) + multimeter_inspect(invstack, player, pos, fault) + return true + elseif fields.wp and pos then + local network_id = technic.pos2network(pos) + local encoded_net_id = base36(network_id) + if encoded_net_id then + local net_pos = technic.network2pos(network_id) + local id = player:hud_add({ + hud_elem_type = "waypoint", + name = ("Network %s"):format(encoded_net_id), + text = "m", + number = 0xE0B020, + world_pos = net_pos + }) + minetest.after(90, function() if player then player:hud_remove(id) end end) + end + elseif fields.rs and fields.net and fields.net ~= "" then + -- Use charge first before even attempting remote start + local itemstack = flute and flute.itemstack + local invstack, fault = async_itemstack_use_charge(itemstack, player, rs_charge_multiplier) + if not fault then + local net_id = tonumber(fields.net, 36) + local net_pos = net_id and technic.network2pos(net_id) + if net_pos then + fault = remote_start_net(player, net_pos) + else + fault = "decode" + end + end + multimeter_inspect(invstack, player, pos, fault) + return true + elseif fields.quit then + open_formspecs[name] = nil + end + end + -- Tell engine to skip rest of formspec handlers + return true +end) + +local function check_node(pos) + local name = minetest.get_node(pos).name + if technic.machine_tiers[name] or technic.get_cable_tier(name) or name == "technic:switching_station" then + return name + end +end + +technic.register_power_tool("technic:multimeter", max_charge) + +minetest.register_tool("technic:multimeter", { + description = S("Multimeter"), + inventory_image = texture, + wield_image = texture, + stack_max = 1, + liquids_pointable = false, + wear_represents = "technic_RE_charge", + on_refill = technic.refill_RE_charge, + on_use = function(itemstack, player, pointed_thing) + local pos = minetest.get_pointed_thing_position(pointed_thing, false) + if pos and pointed_thing.type == "node" then + local name = check_node(pos) + if name then + if name == "technic:switching_station" then + -- Switching station compatibility shim + pos.y = pos.y - 1 + end + open_formspecs[player:get_player_name()] = nil + multimeter_inspect(itemstack, player, pos, not use_charge(itemstack) and "battery") + end + end + return itemstack + end, +})