summaryrefslogtreecommitdiff
path: root/mesecons_luacontroller
diff options
context:
space:
mode:
authorVanessa Ezekowitz <vanessaezekowitz@gmail.com>2016-04-01 21:00:20 -0400
committerVanessa Ezekowitz <vanessaezekowitz@gmail.com>2016-04-01 21:10:04 -0400
commit888b0ebfec8c2eff9015163549a7e47443cb8665 (patch)
tree915080159bfaa6ba6e226087c7ce0e8d5464b518 /mesecons_luacontroller
parentda66780a569712c23ae4f2996cfb4608a9f9d69d (diff)
downloaddreambuilder_modpack-888b0ebfec8c2eff9015163549a7e47443cb8665.tar
dreambuilder_modpack-888b0ebfec8c2eff9015163549a7e47443cb8665.tar.gz
dreambuilder_modpack-888b0ebfec8c2eff9015163549a7e47443cb8665.tar.bz2
dreambuilder_modpack-888b0ebfec8c2eff9015163549a7e47443cb8665.tar.xz
dreambuilder_modpack-888b0ebfec8c2eff9015163549a7e47443cb8665.zip
"explode" all modpacks into their individual components
(you can't have a modpack buried inside a modpack)
Diffstat (limited to 'mesecons_luacontroller')
-rw-r--r--mesecons_luacontroller/depends.txt1
-rw-r--r--mesecons_luacontroller/doc/luacontroller/description.html5
-rw-r--r--mesecons_luacontroller/doc/luacontroller/preview.pngbin0 -> 67795 bytes
-rw-r--r--mesecons_luacontroller/doc/luacontroller/recipe.pngbin0 -> 5560 bytes
-rw-r--r--mesecons_luacontroller/init.lua642
-rw-r--r--mesecons_luacontroller/textures/jeija_luac_background.pngbin0 -> 2016 bytes
-rw-r--r--mesecons_luacontroller/textures/jeija_luac_runbutton.pngbin0 -> 4262 bytes
-rw-r--r--mesecons_luacontroller/textures/jeija_luacontroller_LED_A.pngbin0 -> 3541 bytes
-rw-r--r--mesecons_luacontroller/textures/jeija_luacontroller_LED_B.pngbin0 -> 3537 bytes
-rw-r--r--mesecons_luacontroller/textures/jeija_luacontroller_LED_C.pngbin0 -> 3537 bytes
-rw-r--r--mesecons_luacontroller/textures/jeija_luacontroller_LED_D.pngbin0 -> 3537 bytes
-rw-r--r--mesecons_luacontroller/textures/jeija_luacontroller_burnt_top.pngbin0 -> 8706 bytes
-rw-r--r--mesecons_luacontroller/textures/jeija_luacontroller_top.pngbin0 -> 11913 bytes
13 files changed, 648 insertions, 0 deletions
diff --git a/mesecons_luacontroller/depends.txt b/mesecons_luacontroller/depends.txt
new file mode 100644
index 0000000..acaa924
--- /dev/null
+++ b/mesecons_luacontroller/depends.txt
@@ -0,0 +1 @@
+mesecons
diff --git a/mesecons_luacontroller/doc/luacontroller/description.html b/mesecons_luacontroller/doc/luacontroller/description.html
new file mode 100644
index 0000000..ca14615
--- /dev/null
+++ b/mesecons_luacontroller/doc/luacontroller/description.html
@@ -0,0 +1,5 @@
+The luacontroller is an advanced programmable component.
+You can simply code it in the language mesecons uses itself: Lua!
+All the code runs in a sandbox, so it's completely safe (but I won't guarantee that for absolute certainty!).
+
+<a href="http://mesecons.net/luacontroller/">Documentation is available here!</a>
diff --git a/mesecons_luacontroller/doc/luacontroller/preview.png b/mesecons_luacontroller/doc/luacontroller/preview.png
new file mode 100644
index 0000000..f16c9d0
--- /dev/null
+++ b/mesecons_luacontroller/doc/luacontroller/preview.png
Binary files differ
diff --git a/mesecons_luacontroller/doc/luacontroller/recipe.png b/mesecons_luacontroller/doc/luacontroller/recipe.png
new file mode 100644
index 0000000..529b66d
--- /dev/null
+++ b/mesecons_luacontroller/doc/luacontroller/recipe.png
Binary files differ
diff --git a/mesecons_luacontroller/init.lua b/mesecons_luacontroller/init.lua
new file mode 100644
index 0000000..839d150
--- /dev/null
+++ b/mesecons_luacontroller/init.lua
@@ -0,0 +1,642 @@
+-- ______
+-- |
+-- |
+-- | __ ___ _ __ _ _
+-- | | | | | |\ | | |_| | | | | |_ |_|
+-- |___| |______ |__| | \| | | \ |__| |_ |_ |_ |\
+-- |
+-- |
+--
+
+-- Reference
+-- ports = get_real_port_states(pos): gets if inputs are powered from outside
+-- 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_states(pos, ports): Applies new port states to a LuaController at pos
+-- run(pos): runs the code in the controller at pos
+-- reset_meta(pos, code, errmsg): performs a software-reset, installs new code and prints error messages
+-- resetn(pos): performs a hardware reset, turns off all ports
+--
+-- The Sandbox
+-- The whole code of the controller runs in a sandbox,
+-- a very restricted environment.
+-- However, as this does not prevent you from using e.g. loops,
+-- we need to check for these prohibited commands first.
+-- Actually the only way to damage the server is to
+-- use too much memory from the sandbox.
+-- You can add more functions to the environment
+-- (see where local env is defined)
+-- Something nice to play is is appending minetest.env to it.
+
+local BASENAME = "mesecons_luacontroller:luacontroller"
+
+local rules = {
+ a = {x = -1, y = 0, z = 0, name="A"},
+ b = {x = 0, y = 0, z = 1, name="B"},
+ c = {x = 1, y = 0, z = 0, name="C"},
+ d = {x = 0, y = 0, z = -1, name="D"},
+}
+
+
+------------------
+-- Action stuff --
+------------------
+-- These helpers are required to set the port states of the luacontroller
+
+local function update_real_port_states(pos, rule_name, new_state)
+ local meta = minetest.get_meta(pos)
+ if rule_name == nil then
+ meta:set_int("real_portstates", 1)
+ return
+ end
+ local n = meta:get_int("real_portstates") - 1
+ local L = {}
+ for i = 1, 4 do
+ L[i] = n % 2
+ n = math.floor(n / 2)
+ end
+ -- (0,-1) (-1,0) (1,0) (0,1)
+ local pos_to_side = { 4, 1, nil, 3, 2 }
+ if rule_name.x == nil then
+ for _, rname in ipairs(rule_name) do
+ local port = pos_to_side[rname.x + (2 * rname.z) + 3]
+ L[port] = (newstate == "on") and 1 or 0
+ end
+ else
+ local port = pos_to_side[rule_name.x + (2 * rule_name.z) + 3]
+ L[port] = (new_state == "on") and 1 or 0
+ end
+ meta:set_int("real_portstates",
+ 1 +
+ 1 * L[1] +
+ 2 * L[2] +
+ 4 * L[3] +
+ 8 * L[4])
+end
+
+
+local port_names = {"a", "b", "c", "d"}
+
+local function get_real_port_states(pos)
+ -- Determine if ports are powered (by itself or from outside)
+ local meta = minetest.get_meta(pos)
+ local L = {}
+ local n = meta:get_int("real_portstates") - 1
+ for _, name in ipairs(port_names) do
+ L[name] = ((n % 2) == 1)
+ n = math.floor(n / 2)
+ end
+ return L
+end
+
+
+local function merge_port_states(ports, vports)
+ return {
+ a = ports.a or vports.a,
+ b = ports.b or vports.b,
+ c = ports.c or vports.c,
+ d = ports.d or vports.d,
+ }
+end
+
+local function generate_name(ports)
+ local d = ports.d and 1 or 0
+ local c = ports.c and 1 or 0
+ local b = ports.b and 1 or 0
+ local a = ports.a and 1 or 0
+ return BASENAME..d..c..b..a
+end
+
+
+local function set_port(pos, rule, state)
+ if state then
+ mesecon.receptor_on(pos, {rule})
+ else
+ mesecon.receptor_off(pos, {rule})
+ end
+end
+
+
+local function clean_port_states(ports)
+ ports.a = ports.a and true or false
+ ports.b = ports.b and true or false
+ ports.c = ports.c and true or false
+ ports.d = ports.d and true or false
+end
+
+
+local function set_port_states(pos, ports)
+ local node = minetest.get_node(pos)
+ local name = node.name
+ clean_port_states(ports)
+ local vports = minetest.registered_nodes[name].virtual_portstates
+ local new_name = generate_name(ports)
+
+ if name ~= new_name and vports then
+ -- Problem:
+ -- We need to place the new node first so that when turning
+ -- off some port, it won't stay on because the rules indicate
+ -- there is an onstate output port there.
+ -- When turning the output off then, it will however cause feedback
+ -- so that the luacontroller will receive an "off" event by turning
+ -- its output off.
+ -- Solution / Workaround:
+ -- Remember which output was turned off and ignore next "off" event.
+ local meta = minetest.get_meta(pos)
+ local ign = minetest.deserialize(meta:get_string("ignore_offevents")) or {}
+ if ports.a and not vports.a and not mesecon.is_powered(pos, rules.a) then ign.A = true end
+ if ports.b and not vports.b and not mesecon.is_powered(pos, rules.b) then ign.B = true end
+ if ports.c and not vports.c and not mesecon.is_powered(pos, rules.c) then ign.C = true end
+ if ports.d and not vports.d and not mesecon.is_powered(pos, rules.d) then ign.D = true end
+ meta:set_string("ignore_offevents", minetest.serialize(ign))
+
+ minetest.swap_node(pos, {name = new_name, param2 = node.param2})
+
+ if ports.a ~= vports.a then set_port(pos, rules.a, ports.a) end
+ if ports.b ~= vports.b then set_port(pos, rules.b, ports.b) end
+ if ports.c ~= vports.c then set_port(pos, rules.c, ports.c) end
+ if ports.d ~= vports.d then set_port(pos, rules.d, ports.d) end
+ end
+end
+
+
+-----------------
+-- Overheating --
+-----------------
+local function burn_controller(pos)
+ local node = minetest.get_node(pos)
+ node.name = BASENAME.."_burnt"
+ minetest.swap_node(pos, node)
+ minetest.get_meta(pos):set_string("lc_memory", "");
+ -- Wait for pending operations
+ minetest.after(0.2, mesecon.receptor_off, pos, mesecon.rules.flat)
+end
+
+local function overheat(pos, meta)
+ if mesecon.do_overheat(pos) then -- If too hot
+ burn_controller(pos)
+ return true
+ end
+end
+
+------------------------
+-- Ignored off events --
+------------------------
+
+local function ignore_event(event, meta)
+ if event.type ~= "off" then return false end
+ local ignore_offevents = minetest.deserialize(meta:get_string("ignore_offevents")) or {}
+ if ignore_offevents[event.pin.name] then
+ ignore_offevents[event.pin.name] = nil
+ meta:set_string("ignore_offevents", minetest.serialize(ignore_offevents))
+ return true
+ end
+end
+
+-------------------------
+-- Parsing and running --
+-------------------------
+
+local function safe_print(param)
+ print(dump(param))
+end
+
+local function safe_date()
+ return(os.date("*t",os.time()))
+end
+
+local function remove_functions(x)
+ local tp = type(x)
+ if tp == "table" then
+ for key, value in pairs(x) do
+ local key_t, val_t = type(key), type(value)
+ if key_t == "function" or val_t == "function" then
+ x[key] = nil
+ else
+ if key_t == "table" then
+ remove_functions(key)
+ end
+ if val_t == "table" then
+ remove_functions(value)
+ end
+ end
+ end
+ elseif tp == "function" then
+ return nil
+ end
+ return x
+end
+
+local function get_interrupt(pos)
+ -- iid = interrupt id
+ local function interrupt(time, iid)
+ if type(time) ~= "number" then return end
+ local luac_id = minetest.get_meta(pos):get_int("luac_id")
+ mesecon.queue:add_action(pos, "lc_interrupt", {luac_id, iid}, time, iid, 1)
+ end
+ return interrupt
+end
+
+
+local function get_digiline_send(pos)
+ if not digiline then return end
+ return function(channel, msg)
+ minetest.after(0, function()
+ digiline:receptor_send(pos, digiline.rules.default, channel, msg)
+ end)
+ end
+end
+
+
+local safe_globals = {
+ "assert", "error", "ipairs", "next", "pairs", "pcall", "select",
+ "tonumber", "tostring", "type", "unpack", "_VERSION", "xpcall",
+}
+local function create_environment(pos, mem, event)
+ -- Gather variables for the environment
+ local vports = minetest.registered_nodes[minetest.get_node(pos).name].virtual_portstates
+ local vports_copy = {}
+ for k, v in pairs(vports) do vports_copy[k] = v end
+ local rports = get_real_port_states(pos)
+
+ -- Create new library tables on each call to prevent one LuaController
+ -- from breaking a library and messing up other LuaControllers.
+ local env = {
+ pin = merge_port_states(vports, rports),
+ port = vports_copy,
+ event = event,
+ mem = mem,
+ heat = minetest.get_meta(pos):get_int("heat"),
+ heat_max = mesecon.setting("overheat_max", 20),
+ print = safe_print,
+ interrupt = get_interrupt(pos),
+ digiline_send = get_digiline_send(pos),
+ string = {
+ byte = string.byte,
+ char = string.char,
+ format = string.format,
+ gsub = string.gsub,
+ len = string.len,
+ lower = string.lower,
+ upper = string.upper,
+ rep = string.rep,
+ reverse = string.reverse,
+ sub = string.sub,
+ },
+ math = {
+ abs = math.abs,
+ acos = math.acos,
+ asin = math.asin,
+ atan = math.atan,
+ atan2 = math.atan2,
+ ceil = math.ceil,
+ cos = math.cos,
+ cosh = math.cosh,
+ deg = math.deg,
+ exp = math.exp,
+ floor = math.floor,
+ fmod = math.fmod,
+ frexp = math.frexp,
+ huge = math.huge,
+ ldexp = math.ldexp,
+ log = math.log,
+ log10 = math.log10,
+ max = math.max,
+ min = math.min,
+ modf = math.modf,
+ pi = math.pi,
+ pow = math.pow,
+ rad = math.rad,
+ random = math.random,
+ sin = math.sin,
+ sinh = math.sinh,
+ sqrt = math.sqrt,
+ tan = math.tan,
+ tanh = math.tanh,
+ },
+ table = {
+ concat = table.concat,
+ insert = table.insert,
+ maxn = table.maxn,
+ remove = table.remove,
+ sort = table.sort,
+ },
+ os = {
+ clock = os.clock,
+ difftime = os.difftime,
+ time = os.time,
+ datetable = safe_date,
+ },
+ }
+ env._G = env
+
+ for _, name in pairs(safe_globals) do
+ env[name] = _G[name]
+ end
+
+ return env
+end
+
+
+local function timeout()
+ debug.sethook() -- Clear hook
+ error("Code timed out!")
+end
+
+
+local function code_prohibited(code)
+ -- LuaJIT doesn't increment the instruction counter when running
+ -- loops, so we have to sanitize inputs if we're using LuaJIT.
+ if not jit then
+ return false
+ end
+ local prohibited = {"while", "for", "do", "repeat", "until", "goto"}
+ code = " "..code.." "
+ for _, p in ipairs(prohibited) do
+ if string.find(code, "[^%w_]"..p.."[^%w_]") then
+ return "Prohibited command: "..p
+ end
+ end
+end
+
+
+local function create_sandbox(code, env)
+ if code:byte(1) == 27 then
+ return nil, "Binary code prohibited."
+ end
+ local f, msg = loadstring(code)
+ if not f then return nil, msg end
+ setfenv(f, env)
+
+ return function(...)
+ debug.sethook(timeout, "", 10000)
+ local ok, ret = pcall(f, ...)
+ debug.sethook() -- Clear hook
+ if not ok then error(ret) end
+ return ret
+ end
+end
+
+
+local function load_memory(meta)
+ return minetest.deserialize(meta:get_string("lc_memory")) or {}
+end
+
+
+local function save_memory(pos, meta, mem)
+ local memstring = minetest.serialize(remove_functions(mem))
+ local memsize_max = mesecon.setting("luacontroller_memsize", 100000)
+
+ if (#memstring <= memsize_max) then
+ meta:set_string("lc_memory", memstring)
+ else
+ print("Error: Luacontroller memory overflow. "..memsize_max.." bytes available, "
+ ..#memstring.." required. Controller overheats.")
+ burn_controller(pos)
+ end
+end
+
+
+local function run(pos, event)
+ local meta = minetest.get_meta(pos)
+ if overheat(pos) then return end
+ if ignore_event(event, meta) then return end
+
+ -- Load code & mem from meta
+ local mem = load_memory(meta)
+ local code = meta:get_string("code")
+
+ local err = code_prohibited(code)
+ if err then return err end
+
+ -- Create environment
+ local env = create_environment(pos, mem, event)
+
+ -- Create the sandbox and execute code
+ local f, msg = create_sandbox(code, env)
+ if not f then return msg end
+ local success, msg = pcall(f)
+ if not success then return msg end
+ if type(env.port) ~= "table" then
+ return "Ports set are invalid."
+ end
+
+ -- Actually set the ports
+ set_port_states(pos, env.port)
+
+ -- Save memory. This may burn the luacontroller if a memory overflow occurs.
+ save_memory(pos, meta, env.mem)
+end
+
+mesecon.queue:add_function("lc_interrupt", function (pos, luac_id, iid)
+ -- There is no luacontroller 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)
+
+local function reset_meta(pos, code, errmsg)
+ local meta = minetest.get_meta(pos)
+ meta:set_string("code", code)
+ code = minetest.formspec_escape(code or "")
+ errmsg = minetest.formspec_escape(errmsg or "")
+ meta:set_string("formspec", "size[10,8]"..
+ "background[-0.2,-0.25;10.4,8.75;jeija_luac_background.png]"..
+ "textarea[0.2,0.6;10.2,5;code;;"..code.."]"..
+ "image_button[3.75,6;2.5,1;jeija_luac_runbutton.png;program;]"..
+ "image_button_exit[9.72,-0.25;0.425,0.4;jeija_close_window.png;exit;]"..
+ "label[0.1,5;"..errmsg.."]")
+ meta:set_int("heat", 0)
+ meta:set_int("luac_id", math.random(1, 65535))
+end
+
+local function reset(pos)
+ set_port_states(pos, {a=false, b=false, c=false, d=false})
+end
+
+
+-----------------------
+-- Node Registration --
+-----------------------
+
+local output_rules = {}
+local input_rules = {}
+
+local node_box = {
+ type = "fixed",
+ fixed = {
+ {-8/16, -8/16, -8/16, 8/16, -7/16, 8/16}, -- Bottom slab
+ {-5/16, -7/16, -5/16, 5/16, -6/16, 5/16}, -- Circuit board
+ {-3/16, -6/16, -3/16, 3/16, -5/16, 3/16}, -- IC
+ }
+}
+
+local selection_box = {
+ type = "fixed",
+ fixed = { -8/16, -8/16, -8/16, 8/16, -5/16, 8/16 },
+}
+
+local digiline = {
+ receptor = {},
+ effector = {
+ action = function(pos, node, channel, msg)
+ run(pos, {type = "digiline", channel = channel, msg = msg})
+ end
+ }
+}
+local function on_receive_fields(pos, form_name, fields)
+ if not fields.program then
+ return
+ end
+ reset(pos)
+ reset_meta(pos, fields.code)
+ local err = run(pos, {type="program"})
+ if err then
+ print(err)
+ reset_meta(pos, fields.code, err)
+ end
+end
+
+for a = 0, 1 do -- 0 = off 1 = on
+for b = 0, 1 do
+for c = 0, 1 do
+for d = 0, 1 do
+ local cid = tostring(d)..tostring(c)..tostring(b)..tostring(a)
+ local node_name = BASENAME..cid
+ local top = "jeija_luacontroller_top.png"
+ if a == 1 then
+ top = top.."^jeija_luacontroller_LED_A.png"
+ end
+ if b == 1 then
+ top = top.."^jeija_luacontroller_LED_B.png"
+ end
+ if c == 1 then
+ top = top.."^jeija_luacontroller_LED_C.png"
+ end
+ if d == 1 then
+ top = top.."^jeija_luacontroller_LED_D.png"
+ end
+
+ local groups
+ if a + b + c + d ~= 0 then
+ groups = {dig_immediate=2, not_in_creative_inventory=1, overheat = 1}
+ else
+ groups = {dig_immediate=2, overheat = 1}
+ end
+
+ output_rules[cid] = {}
+ input_rules[cid] = {}
+ if a == 1 then table.insert(output_rules[cid], rules.a) end
+ if b == 1 then table.insert(output_rules[cid], rules.b) end
+ if c == 1 then table.insert(output_rules[cid], rules.c) end
+ if d == 1 then table.insert(output_rules[cid], rules.d) end
+
+ if a == 0 then table.insert( input_rules[cid], rules.a) end
+ if b == 0 then table.insert( input_rules[cid], rules.b) end
+ if c == 0 then table.insert( input_rules[cid], rules.c) end
+ if d == 0 then table.insert( input_rules[cid], rules.d) end
+
+ local mesecons = {
+ effector = {
+ rules = input_rules[cid],
+ action_change = function (pos, _, rule_name, new_state)
+ update_real_port_states(pos, rule_name, new_state)
+ run(pos, {type=new_state, pin=rule_name})
+ end,
+ },
+ receptor = {
+ state = mesecon.state.on,
+ rules = output_rules[cid]
+ }
+ }
+
+ minetest.register_node(node_name, {
+ description = "LuaController",
+ drawtype = "nodebox",
+ tiles = {
+ top,
+ "jeija_microcontroller_bottom.png",
+ "jeija_microcontroller_sides.png",
+ "jeija_microcontroller_sides.png",
+ "jeija_microcontroller_sides.png",
+ "jeija_microcontroller_sides.png"
+ },
+ inventory_image = top,
+ paramtype = "light",
+ groups = groups,
+ drop = BASENAME.."0000",
+ sunlight_propagates = true,
+ selection_box = selection_box,
+ node_box = node_box,
+ on_construct = reset_meta,
+ on_receive_fields = on_receive_fields,
+ sounds = default.node_sound_stone_defaults(),
+ mesecons = mesecons,
+ digiline = digiline,
+ -- Virtual portstates are the ports that
+ -- the node shows as powered up (light up).
+ virtual_portstates = {
+ a = a == 1,
+ b = b == 1,
+ c = c == 1,
+ d = d == 1,
+ },
+ after_dig_node = function (pos, node)
+ mesecon.receptor_off(pos, output_rules)
+ end,
+ is_luacontroller = true,
+ })
+end
+end
+end
+end
+
+------------------------------
+-- Overheated LuaController --
+------------------------------
+
+minetest.register_node(BASENAME .. "_burnt", {
+ drawtype = "nodebox",
+ tiles = {
+ "jeija_luacontroller_burnt_top.png",
+ "jeija_microcontroller_bottom.png",
+ "jeija_microcontroller_sides.png",
+ "jeija_microcontroller_sides.png",
+ "jeija_microcontroller_sides.png",
+ "jeija_microcontroller_sides.png"
+ },
+ inventory_image = "jeija_luacontroller_burnt_top.png",
+ is_burnt = true,
+ paramtype = "light",
+ groups = {dig_immediate=2, not_in_creative_inventory=1},
+ drop = BASENAME.."0000",
+ sunlight_propagates = true,
+ selection_box = selection_box,
+ node_box = node_box,
+ on_construct = reset_meta,
+ on_receive_fields = on_receive_fields,
+ sounds = default.node_sound_stone_defaults(),
+ virtual_portstates = {a = false, b = false, c = false, d = false},
+ mesecons = {
+ effector = {
+ rules = mesecon.rules.flat,
+ action_change = function(pos, _, rule_name, new_state)
+ update_real_port_states(pos, rule_name, new_state)
+ end,
+ },
+ },
+})
+
+------------------------
+-- Craft Registration --
+------------------------
+
+minetest.register_craft({
+ output = BASENAME.."0000 2",
+ recipe = {
+ {'mesecons_materials:silicon', 'mesecons_materials:silicon', 'group:mesecon_conductor_craftable'},
+ {'mesecons_materials:silicon', 'mesecons_materials:silicon', 'group:mesecon_conductor_craftable'},
+ {'group:mesecon_conductor_craftable', 'group:mesecon_conductor_craftable', ''},
+ }
+})
+
diff --git a/mesecons_luacontroller/textures/jeija_luac_background.png b/mesecons_luacontroller/textures/jeija_luac_background.png
new file mode 100644
index 0000000..40e316c
--- /dev/null
+++ b/mesecons_luacontroller/textures/jeija_luac_background.png
Binary files differ
diff --git a/mesecons_luacontroller/textures/jeija_luac_runbutton.png b/mesecons_luacontroller/textures/jeija_luac_runbutton.png
new file mode 100644
index 0000000..157507f
--- /dev/null
+++ b/mesecons_luacontroller/textures/jeija_luac_runbutton.png
Binary files differ
diff --git a/mesecons_luacontroller/textures/jeija_luacontroller_LED_A.png b/mesecons_luacontroller/textures/jeija_luacontroller_LED_A.png
new file mode 100644
index 0000000..a187e8e
--- /dev/null
+++ b/mesecons_luacontroller/textures/jeija_luacontroller_LED_A.png
Binary files differ
diff --git a/mesecons_luacontroller/textures/jeija_luacontroller_LED_B.png b/mesecons_luacontroller/textures/jeija_luacontroller_LED_B.png
new file mode 100644
index 0000000..738ba96
--- /dev/null
+++ b/mesecons_luacontroller/textures/jeija_luacontroller_LED_B.png
Binary files differ
diff --git a/mesecons_luacontroller/textures/jeija_luacontroller_LED_C.png b/mesecons_luacontroller/textures/jeija_luacontroller_LED_C.png
new file mode 100644
index 0000000..abe0fe6
--- /dev/null
+++ b/mesecons_luacontroller/textures/jeija_luacontroller_LED_C.png
Binary files differ
diff --git a/mesecons_luacontroller/textures/jeija_luacontroller_LED_D.png b/mesecons_luacontroller/textures/jeija_luacontroller_LED_D.png
new file mode 100644
index 0000000..cc10170
--- /dev/null
+++ b/mesecons_luacontroller/textures/jeija_luacontroller_LED_D.png
Binary files differ
diff --git a/mesecons_luacontroller/textures/jeija_luacontroller_burnt_top.png b/mesecons_luacontroller/textures/jeija_luacontroller_burnt_top.png
new file mode 100644
index 0000000..d1a17af
--- /dev/null
+++ b/mesecons_luacontroller/textures/jeija_luacontroller_burnt_top.png
Binary files differ
diff --git a/mesecons_luacontroller/textures/jeija_luacontroller_top.png b/mesecons_luacontroller/textures/jeija_luacontroller_top.png
new file mode 100644
index 0000000..3128230
--- /dev/null
+++ b/mesecons_luacontroller/textures/jeija_luacontroller_top.png
Binary files differ