local FUEL_EFFICIENCY = 3 -- How many moves can the turtle do with a second fuel local TURTLES_FORCE_LOAD = true -- Useless for now, has to wait until force_load is merged --TODO: Change serialization so that it supports functions local safe_serialize = function(value) return minetest.serialize(value) end local safe_deserialize = minetest.deserialize local turtle_invs local turtle_updates local serialize_inv = function(l) local l2={} for _,i in pairs(l or {}) do l2[_]=i:to_table() end return l2 end local deserialize_inv = function(l) local l2={} for _,i in pairs(l or {}) do l2[_]=ItemStack(i) end return l2 end local wpath = minetest.get_worldpath() local function read_file(fn) local f = io.open(fn, "r") if f==nil then return {} end local t = f:read("*all") f:close() if t=="" or t==nil then return {} end return minetest.deserialize(t) end local function write_file(fn, tbl) local f = io.open(fn, "w") f:write(minetest.serialize(tbl)) f:close() end local get_turtle_info turtle_infos = read_file(wpath.."/turtle_infos") turtle_updates = read_file(wpath.."/turtle_updates") turtle_updates_to_add={} local tupdate minetest.register_globalstep(function(dtime) for _, timer in ipairs(turtle_updates_to_add) do table.insert(turtle_updates, timer) end turtle_updates_to_add = {} for index, timer in ipairs(turtle_updates) do local info = get_turtle_info(timer.update.turtle) if info["turtle"]~=nil then --turtle is loaded timer.time = timer.time - dtime if timer.time <= 0 then tupdate(timer.update) table.remove(turtle_updates,index) end end end end) local function turtle_add_update(time, update) table.insert(turtle_updates_to_add, {time=time, update=update}) end minetest.register_on_shutdown(function() for turtle,i in pairs(turtle_infos) do i["turtle"]=nil i["inventory"]=serialize_inv(turtle_invs:get_list(turtle)) end write_file(wpath.."/turtle_infos",turtle_infos) for _, timer in ipairs(turtle_updates_to_add) do table.insert(turtle_updates, timer) end write_file(wpath.."/turtle_updates",turtle_updates) end) get_turtle_info = function(turtle) if turtle_infos[turtle]==nil then turtle_infos[turtle]={} end return turtle_infos[turtle] end local function get_turtle_id() i=0 while true do if turtle_infos["turtle"..tostring(i)]==nil then return "turtle"..tostring(i) end i=i+1 end end local function round_pos(p) return {x=math.floor(p.x+0.5), y=math.floor(p.y+0.5), z=math.floor(p.z+0.5)} end local update_formspec = function(turtle, code, errmsg, filename, player, exit) local info = get_turtle_info(turtle) info["code"]=code or "" if minetest.formspec_escape then code = minetest.formspec_escape(code or "") errmsg = minetest.formspec_escape(errmsg or "") else code = string.gsub(code or "", "%[", "(") -- would otherwise code = string.gsub(code, "%]", ")") -- corrupt formspec errmsg = string.gsub(errmsg or "", "%[", "(") -- would otherwise errmsg = string.gsub(errmsg, "%]", ")") -- corrupt formspec end info["filename"] = filename info["formspec"]= "size[9,10]".. "textarea[0.3,0;4.7,5;code;;"..code.."]".. "list[detached:turtle:invs;"..turtle..";4.8,0;4,4;]".. "image_button[0,4.6;2.5,1;turtle_execute.png;program;]".. "image_button_exit[8.72,-0.25;0.425,0.4;turtle_close.png;exit;]".. "label[4.6,4;"..errmsg.."]".. "list[current_player;main;0.5,6;8,4;]".. "field[3,5;4,1;filename;Filename:;"..filename.."]".. "button[7,4.65;1,1;open;Open]".. "button[8,4.65;1,1;save;Save]" info["heat"]=0 if exit==nil then minetest.show_formspec(player:get_player_name(), turtle, info["formspec"]) end end -------------------- -- Overheat stuff -- -------------------- local heat = function (turtle) -- warm up local info = get_turtle_info(turtle) local h = info["heat"] if h ~= nil then info["heat"]=h+1 else info["heat"]=1 end end local cool = function (turtle) -- cool down after a while local info = get_turtle_info(turtle) local h = info["heat"] if h ~= nil then info["heat"]=h-1 end end local overheat = function (turtle) -- determine if too hot local info = get_turtle_info(turtle) local h = info["heat"] return h==nil or h>400 end ------------------- -- Parsing stuff -- ------------------- local code_prohibited = function(code) -- Clean code local prohibited = {"while", "for", "repeat", "until", "goto"}--, "function"} for _, p in ipairs(prohibited) do if string.find(code, "%A"..p.."%A") then return "Prohibited command: "..p end end end local safe_print = function(param) print(dump(param)) end local interrupt = function(params) turtle_update(params.turtle, {type="interrupt", iid = params.iid}) end local getinterrupt = function(turtle) local interrupt = function (time, iid) -- iid = interrupt id if type(time) ~= "number" then return end local iid = iid or math.random() local info = get_turtle_info(turtle) --local interrupts = safe_deserialize(info["interrupts"]) or {} local interrupts = info["interrupts"] or {} local found = false local search = safe_serialize(iid) for _, i in ipairs(interrupts) do if safe_serialize(i) == search then found = true break end end if not found then table.insert(interrupts, iid) --info["interrupts"]= safe_serialize(interrupts) info["interrupts"]=interrupts end turtle_add_update(time, {turtle=turtle, type="interrupt", iid = iid}) end return interrupt end local function getv(dir) if dir==0 then return {x=0,y=0,z=1} elseif dir==1 then return {x=1,y=0,z=0} elseif dir==2 then return {x=0,y=0,z=-1} elseif dir==3 then return {x=-1,y=0,z=0} end end local function v3add(v1,v2) return {x=v1.x+v2.x,y=v1.y+v2.y,z=v1.z+v2.z} end local function turtle_can_go(nname) return nname=="air" or minetest.registered_nodes[nname].liquidtype~="none" end local function stack_set_count(stack, count) stack = stack:to_table() if stack==nil then return nil end stack.count=count return ItemStack(stack) end -------------------------- -- /\ |--\ | -- -- /--\ |__/ | -- -- / \ | | -- -------------------------- tupdate = function(update) local turtle = update.turtle local t = update.type local info = get_turtle_info(turtle) if t=="failmove" then turtle_update(turtle,{type="failmove",iid=update.iid}) elseif t=="endmove" then info["moveint"]=nil info["spos"]=info["npos"] info["npos"]=nil local tobject = info["turtle"] tobject.object:setvelocity({x=0,y=0,z=0}) tobject.object:setpos(info["spos"]) turtle_update(turtle,{type="endmove",iid=update.iid}) elseif t=="endturn" then info["moveint"]=nil info["dir"]=info["ndir"] info["ndir"]=nil info["rotate"]=nil local tobject = info["turtle"] tobject.object:setyaw(info["dir"]*math.pi/2) turtle_update(turtle,{type="endmove",iid=update.iid}) elseif t=="interrupt" then interrupt(update) elseif t=="cool" then cool(turtle) end end local get_turtle_funcs = function(turtle) return { forward = function(iid) if iid==nil then iid="nil" end local info = get_turtle_info(turtle) local tobject = info["turtle"] if info["fuel"]==0 then turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) return end if info["moveint"]==nil then local spos = info["spos"] local dir = info["dir"] info["npos"] = v3add(spos, getv(dir)) if not turtle_can_go(minetest.env:get_node(info["npos"]).name) then info["npos"]=nil turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) return end info["fuel"]=info["fuel"]-1 info["moveint"]=iid tobject.object:setvelocity(getv(dir)) turtle_add_update(1,{turtle=turtle, type="endmove", iid=iid}) else turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) end end, back = function(iid) if iid==nil then iid="nil" end local info = get_turtle_info(turtle) local tobject = info["turtle"] if info["fuel"]==0 then turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) return end if info["moveint"]==nil then local spos = info["spos"] local dir = (info["dir"]+2)%4 info["npos"] = v3add(spos, getv(dir)) if not turtle_can_go(minetest.env:get_node(info["npos"]).name) then info["npos"]=nil turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) return end info["fuel"]=info["fuel"]-1 info["moveint"]=iid tobject.object:setvelocity(getv(dir)) turtle_add_update(1,{turtle=turtle, type="endmove", iid=iid}) else turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) end end, up = function(iid) if iid==nil then iid="nil" end local info = get_turtle_info(turtle) local tobject = info["turtle"] if info["fuel"]==0 then turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) return end if info["moveint"]==nil then local spos = info["spos"] info["npos"] = v3add(spos, {x=0,y=1,z=0}) if not turtle_can_go(minetest.env:get_node(info["npos"]).name) then info["npos"]=nil turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) return end info["fuel"]=info["fuel"]-1 info["moveint"]=iid tobject.object:setvelocity({x=0,y=1,z=0}) turtle_add_update(1,{turtle=turtle, type="endmove", iid=iid}) else turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) end end, down = function(iid) if iid==nil then iid="nil" end local info = get_turtle_info(turtle) local tobject = info["turtle"] if info["fuel"]==0 then turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) return end if info["moveint"]==nil then local spos = info["spos"] info["npos"] = v3add(spos, {x=0,y=-1,z=0}) if not turtle_can_go(minetest.env:get_node(info["npos"]).name) then info["npos"]=nil turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) return end info["fuel"]=info["fuel"]-1 info["moveint"]=iid tobject.object:setvelocity({x=0,y=-1,z=0}) turtle_add_update(1,{turtle=turtle, type="endmove", iid=iid}) else turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) end end, turnleft = function(iid) if iid==nil then iid="nil" end local info = get_turtle_info(turtle) local tobject = info["turtle"] if info["moveint"]==nil then local dir = info["dir"] info["ndir"]=(dir+3)%4 info["moveint"]=iid info["rotate"]=math.pi/2 turtle_add_update(1,{turtle=turtle, type="endturn", iid=iid}) else turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) end end, turnright = function(iid) if iid==nil then iid="nil" end local info = get_turtle_info(turtle) local tobject = info["turtle"] if info["moveint"]==nil then local dir = info["dir"] info["ndir"]=(dir+1)%4 info["moveint"]=iid info["rotate"]=-math.pi/2 turtle_add_update(1,{turtle=turtle, type="endturn", iid=iid}) else turtle_add_update(0,{turtle=turtle, type="failmove", iid=iid}) end end, detect = function() local info = get_turtle_info(turtle) local pos = v3add(info["spos"],getv(info["dir"])) return minetest.env:get_node(pos).name end, detectup = function() local info = get_turtle_info(turtle) local pos = v3add(info["spos"],{x=0,y=1,z=0}) return minetest.env:get_node(pos).name end, detectdown = function() local info = get_turtle_info(turtle) local pos = v3add(info["spos"],{x=0,y=-1,z=0}) return minetest.env:get_node(pos).name end, dig = function() local info = get_turtle_info(turtle) local dpos = v3add(info["spos"],getv(info["dir"])) local dnode = minetest.env:get_node(dpos) if turtle_can_go(dnode.name) or dnode.name=="ignore" then return false end local drops = minetest.get_node_drops(dnode.name, "default:pick_mese") local _, dropped_item for _, dropped_item in ipairs(drops) do local leftover = turtle_invs:add_item(turtle,dropped_item) minetest.env:add_item(info["spos"],leftover) end minetest.env:remove_node(dpos) return true end, digup = function() local info = get_turtle_info(turtle) local dpos = v3add(info["spos"],{x=0,y=1,z=0}) local dnode = minetest.env:get_node(dpos) if turtle_can_go(dnode.name) or dnode.name=="ignore" then return false end local drops = minetest.get_node_drops(dnode.name, "default:pick_mese") local _, dropped_item for _, dropped_item in ipairs(drops) do local leftover = turtle_invs:add_item(turtle,dropped_item) minetest.env:add_item(info["spos"],leftover) end minetest.env:remove_node(dpos) return true end, digdown = function() local info = get_turtle_info(turtle) local dpos = v3add(info["spos"],{x=0,y=-1,z=0}) local dnode = minetest.env:get_node(dpos) if turtle_can_go(dnode.name) or dnode.name=="ignore" then return false end local drops = minetest.get_node_drops(dnode.name, "default:pick_mese") local _, dropped_item for _, dropped_item in ipairs(drops) do local leftover = turtle_invs:add_item(turtle,dropped_item) minetest.env:add_item(info["spos"],leftover) end minetest.env:remove_node(dpos) return true end, place = function(slot) local info = get_turtle_info(turtle) local ppos = v3add(info["spos"],getv(info["dir"])) local dnode = minetest.env:get_node(ppos) if (not turtle_can_go(dnode.name)) or dnode.name=="ignore" then return false end local stack = turtle_invs:get_stack(turtle,slot) if stack:is_empty() or minetest.registered_nodes[stack:get_name()]==nil then return false end minetest.env:set_node(ppos, {name=stack:get_name()}) stack:take_item() turtle_invs:set_stack(turtle, slot, stack) return true end, placeup = function(slot) local info = get_turtle_info(turtle) local ppos = v3add(info["spos"],{x=0,y=1,z=0}) local dnode = minetest.env:get_node(ppos) if (not turtle_can_go(dnode.name)) or dnode.name=="ignore" then return false end local stack = turtle_invs:get_stack(turtle,slot) if stack:is_empty() or minetest.registered_nodes[stack:get_name()]==nil then return false end minetest.env:set_node(ppos, {name=stack:get_name()}) stack:take_item() turtle_invs:set_stack(turtle, slot, stack) return true end, placedown = function(slot) local info = get_turtle_info(turtle) local ppos = v3add(info["spos"],{x=0,y=-1,z=0}) local dnode = minetest.env:get_node(ppos) if (not turtle_can_go(dnode.name)) or dnode.name=="ignore" then return false end local stack = turtle_invs:get_stack(turtle,slot) if stack:is_empty() or minetest.registered_nodes[stack:get_name()]==nil then return false end minetest.env:set_node(ppos, {name=stack:get_name()}) stack:take_item() turtle_invs:set_stack(turtle, slot, stack) return true end, getstack = function(slot) local s = turtle_invs:get_stack(turtle,slot):to_table() if s==nil then return {name="", count=0} end return s end, moveto = function(slot1,slot2, nmax) local stack1 = turtle_invs:get_stack(turtle,slot1) local stack2 = turtle_invs:get_stack(turtle,slot2) local move if nmax==0 then nmax = stack1:get_count() end if stack2:is_empty() then move = math.min(stack1:get_count(), nmax) local taken = stack1:take_item(move) stack2:add_item(taken) else if stack1:get_name()~=stack2:get_name() then return 0 end move = math.min(stack1:get_count(), stack2:get_free_space(), nmax) local taken = stack1:take_item(move) stack2:add_item(taken) end turtle_invs:set_stack(turtle,slot1,stack1) turtle_invs:set_stack(turtle,slot2,stack2) return move end, refuel = function(slot, nmax) local info = get_turtle_info(turtle) local stack = turtle_invs:get_stack(turtle, slot) local fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = {stack}}) if fuel.time<=0 then return false end if nmax==nil then nmax=stack:get_count() end local count = math.min(stack:get_count(), nmax) local fs = stack:to_table() fs["count"]=1 local fstack = ItemStack(fs) local fuel, afterfuel fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = {fstack}}) info["fuel"]=info["fuel"]+FUEL_EFFICIENCY*count*fuel.time stack:take_item(count) if afterfuel~=nil then afterfuel = afterfuel.items[1] end if afterfuel~=nil then afterfuel = stack_set_count(afterfuel, afterfuel:get_count()*count) end if afterfuel~=nil then local leftover = stack:add_item(ItemStack(afterfuel)) turtle_invs:set_stack(turtle, slot, stack) local leftover2 = turtle_invs:add_item(turtle, leftover) minetest.env:add_item(info["spos"],leftover2) else turtle_invs:set_stack(turtle, slot, stack) end end, get_fuel_time = function() local info = get_turtle_info(turtle) return info["fuel"] end, craft = function(nmax) local info = get_turtle_info(turtle) local invl = turtle_invs:get_list(turtle) local recipe = {} local craftmax=nmax for i=1,16 do recipe[i]=ItemStack({name=invl[i]:get_name(),count=1}) if invl[i]:get_count()>0 then craftmax=math.min(craftmax, invl[i]:get_count()) end end local result,new=minetest.get_craft_result({method="normal",width=4,items=recipe}) if result.item:is_empty() then return 0 end result=result.item for i=1,16 do invl[i]:take_item(craftmax) turtle_invs:set_stack(turtle, i, invl[i]) end result = stack_set_count(result, result:get_count()*craftmax) local leftover = turtle_invs:add_item(turtle,result) minetest.env:add_item(info["spos"],leftover) for i=1,16 do local s=stack_set_count(new.items[i], new.items[i]:get_count()*craftmax) if s~=nil then local leftover = turtle_invs:add_item(turtle,s) minetest.env:add_item(info["spos"],leftover) end end end, drop = function(slot) local info = get_turtle_info(turtle) local stack = turtle_invs:get_stack(turtle, slot) turtle_invs:set_stack(turtle, slot, ItemStack("")) local spos = info["spos"] local item = tube_item({x=spos.x,y=spos.y,z=spos.z},stack) item:get_luaentity().start_pos = {x=spos.x,y=spos.y,z=spos.z} item:setvelocity(getv(info["dir"])) end, dropup = function(slot) local info = get_turtle_info(turtle) local stack = turtle_invs:get_stack(turtle, slot) turtle_invs:set_stack(turtle, slot, ItemStack("")) local spos = info["spos"] local item = tube_item({x=spos.x,y=spos.y,z=spos.z},stack) item:get_luaentity().start_pos = {x=spos.x,y=spos.y,z=spos.z} item:setvelocity({x=0,y=1,z=0}) end, dropdown = function(slot) local info = get_turtle_info(turtle) local stack = turtle_invs:get_stack(turtle, slot) turtle_invs:set_stack(turtle, slot, ItemStack("")) local spos = info["spos"] local item = tube_item({x=spos.x,y=spos.y,z=spos.z},stack) item:get_luaentity().start_pos = {x=spos.x,y=spos.y,z=spos.z} item:setvelocity({x=0,y=-1,z=0}) end, suck = function() local info = get_turtle_info(turtle) local frompos=v3add(info["spos"],getv(info["dir"])) local fromnode=minetest.env:get_node(frompos) local frominv if not (minetest.registered_nodes[fromnode.name].tube and minetest.registered_nodes[fromnode.name].tube.input_inventory) then for _,object in ipairs(minetest.env:get_objects_inside_radius(frompos, 1)) do if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" then if object:get_luaentity().itemstring ~= "" then local leftover = turtle_invs:add_item(turtle,ItemStack(object:get_luaentity().itemstring)) minetest.env:add_item(info["spos"],leftover) object:get_luaentity().itemstring = "" object:remove() return end end end return end local frommeta=minetest.env:get_meta(frompos) local frominvname=minetest.registered_nodes[fromnode.name].tube.input_inventory local frominv=frommeta:get_inventory() for spos,stack in ipairs(frominv:get_list(frominvname)) do if stack:get_name()~="" then local leftover = turtle_invs:add_item(turtle,stack) frominv:set_stack(frominvname, spos ,leftover) return end end end, suckup = function() local info = get_turtle_info(turtle) local frompos=v3add(info["spos"],{x=0,y=1,z=0}) local fromnode=minetest.env:get_node(frompos) local frominv if not (minetest.registered_nodes[fromnode.name].tube and minetest.registered_nodes[fromnode.name].tube.input_inventory) then for _,object in ipairs(minetest.env:get_objects_inside_radius(frompos, 1)) do if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" then if object:get_luaentity().itemstring ~= "" then local leftover = turtle_invs:add_item(turtle,ItemStack(object:get_luaentity().itemstring)) minetest.env:add_item(info["spos"],leftover) object:get_luaentity().itemstring = "" object:remove() return end end end return end local frommeta=minetest.env:get_meta(frompos) local frominvname=minetest.registered_nodes[fromnode.name].tube.input_inventory local frominv=frommeta:get_inventory() for spos,stack in ipairs(frominv:get_list(frominvname)) do if stack:get_name()~="" then local leftover = turtle_invs:add_item(turtle,stack) frominv:set_stack(frominvname, spos, leftover) return end end end, suckdown = function() local info = get_turtle_info(turtle) local frompos=v3add(info["spos"],{x=0,y=-1,z=0}) local fromnode=minetest.env:get_node(frompos) local frominv if not (minetest.registered_nodes[fromnode.name].tube and minetest.registered_nodes[fromnode.name].tube.input_inventory) then for _,object in ipairs(minetest.env:get_objects_inside_radius(frompos, 1)) do if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" then if object:get_luaentity().itemstring ~= "" then local leftover = turtle_invs:add_item(turtle,ItemStack(object:get_luaentity().itemstring)) minetest.env:add_item(info["spos"],leftover) object:get_luaentity().itemstring = "" object:remove() return end end end return end local frommeta=minetest.env:get_meta(frompos) local frominvname=minetest.registered_nodes[fromnode.name].tube.input_inventory local frominv=frommeta:get_inventory() for spos,stack in ipairs(frominv:get_list(frominvname)) do if stack:get_name()~="" then local leftover = turtle_invs:add_item(turtle,stack) frominv:set_stack(frominvname, spos, leftover) return end end end, } end local create_environment = function(turtle, mem, event) -- Gather variables for the environment return { print = safe_print, interrupt = getinterrupt(turtle), turtle = get_turtle_funcs(turtle), mem = mem, tostring = tostring, tonumber = tonumber, string = { byte = string.byte, char = string.char, find = string.find, format = string.format, gmatch = string.gmatch, gsub = string.gsub, len = string.len, lower = string.lower, match = string.match, 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 = { insert = table.insert, maxn = table.maxn, remove = table.remove, sort = table.sort }, event = event, } end local create_sandbox = function (code, env) -- Create Sandbox if code:byte(1) == 27 then return _, "You Hacker You! Don't use binary code!" end f, msg = loadstring(code) if not f then return _, msg end setfenv(f, env) return f end local do_overheat = function (turtle) -- Overheat protection heat(turtle) turtle_add_update(0.5,{turtle=turtle, type="cool"}) if overheat(turtle) then --TODO return true end end local load_memory = function(turtle) local info = get_turtle_info(turtle) return info["memory"] or {} end local save_memory = function(turtle, mem) local info = get_turtle_info(turtle) info["memory"] = mem end local interrupt_allow = function (turtle, event) if event.type ~= "interrupt" then return true end local info = get_turtle_info(turtle) local interrupts = info["interrupts"] or {} local search = safe_serialize(event.iid) for _, i in ipairs(interrupts) do if safe_serialize(i) == search then return true end end return false end ---------------------- -- Parsing function -- ---------------------- turtle_update = function (turtle, event) local info = get_turtle_info(turtle) if not interrupt_allow(turtle, event) then return end if do_overheat(turtle) then return end -- load code & mem from memory local mem = load_memory(turtle) local code = info["code"] -- make sure code is ok and create environment local prohibited = code_prohibited(code) if prohibited then return prohibited end local env = create_environment(turtle, mem, event) -- create the sandbox and execute code local chunk, msg = create_sandbox (code, env) if not chunk then return msg end local success, msg = pcall(f) if not success then print(msg) update_formspec(turtle, code, msg, info["filename"], nil, "") end save_memory(turtle, mem) end minetest.register_on_player_receive_fields(function(player, formname, fields) if formname:sub(1,6)~="turtle" then return end update_formspec(formname, fields.code, "", fields.filename, player, fields.exit) if fields.program~=nil or fields.exit~=nil then local err = turtle_update(formname, {type="program"}) if err then print(err) end update_formspec(formname, fields.code, err, fields.filename, player, fields.exit) end if fields.save then if fields.filename:sub(1,1)=="." then return end -- Not allowed to save because could change the user's files (including the mod's files, dangerous) local fn = minetest.get_modpath("turtle").."/progs/"..fields.filename..".lua" local f = io.open(fn, "w") f:write(fields.code) f:close() end if fields.open then local fn = minetest.get_modpath("turtle").."/progs/"..fields.filename..".lua" local f = io.open(fn, "r") local code if f==nil then code="" else code = f:read("*all") f:close() end if code==nil then code="" end update_formspec(formname, code, "", fields.filename, player) end end) minetest.register_craftitem("turtle:turtle",{ description="Turtle", image = "turtle_turtle_inv.png", on_place = function(itemstack, placer, pointed_thing) if pointed_thing.type~="node" then return end local obj = minetest.env:add_entity(pointed_thing.above, "turtle:turtle") itemstack:take_item() return itemstack end }) minetest.register_craft( { output = "turtle:turtle", recipe = { { "default:diamond", "default:pick_mese", "default:diamond" }, { "default:diamond", "default:mese", "default:diamond" }, { "default:mese", "default:diamond", "default:mese" }, }, }) turtle_invs = minetest.create_detached_inventory("turtle:invs") for turtle,i in pairs(turtle_infos) do turtle_invs:set_size(turtle,16) for l,stack in pairs(deserialize_inv(i["inventory"])) do turtle_invs:set_stack(turtle, l, stack) end end minetest.register_entity("turtle:turtle", { physical = true, force_load = TURTLES_FORCE_LOAD, collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5}, visual = "mesh", mesh="turtle.x", textures = {"default_wood.png","default_wood.png"}, visual_size = {x=1, y=1}, on_activate = function(self, staticdata) local info if staticdata==nil or staticdata=="" then self.n=get_turtle_id() info=get_turtle_info(self.n) turtle_invs:set_size(self.n,16) info["turtle"]=self info["spos"]=round_pos(self.object:getpos()) info["dir"]=0 info["fuel"]=0 info["formspec"]= "size[9,10]".. "textarea[0.3,0;4.7,5;code;;]".. "list[detached:turtle:invs;"..self.n..";4.8,0;4,4;]".. "image_button[0,4.6;2.5,1;turtle_execute.png;program;]".. "image_button_exit[8.72,-0.25;0.425,0.4;turtle_close.png;exit;]".. "label[4.6,4;]".. "list[current_player;main;0.5,6;8,4;]".. "field[3,5;4,1;filename;Filename:;]".. "button[7,4.65;1,1;open;Open]".. "button[8,4.65;1,1;save;Save]" else self.n=staticdata info=get_turtle_info(self.n) info["turtle"]=self end end, on_step = function(self, dtime) local info=get_turtle_info(self.n) if info["rotate"] then self.object:setyaw(self.object:getyaw()+info["rotate"]*dtime) end end, on_rightclick = function(self, clicker) minetest.show_formspec(clicker:get_player_name(), self.n, get_turtle_info(self.n)["formspec"]) end, on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir) self.object:remove() minetest.env:add_item(turtle_infos[self.n]["spos"],"turtle:turtle") for i=1,16 do turtle_invs:set_stack(self.n, i, ItemStack("")) end turtle_infos[self.n] = nil end, get_staticdata = function(self) return self.n end, })