Turtle subroutine working!
Code is somewhat more organized. Fixing problem of it dropping then picking up item for no reason...master
parent
8bdfcdfd3f
commit
b7e5dc958c
192
Sandbox.lua
192
Sandbox.lua
|
@ -1,192 +0,0 @@
|
|||
function getSandbox()
|
||||
|
||||
local sandbox = {
|
||||
_VERSION = "sandbox 0.5",
|
||||
_DESCRIPTION = "A pure-lua solution for running untrusted Lua code.",
|
||||
_URL = "https://github.com/kikito/sandbox.lua",
|
||||
_LICENSE = [[
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2021 Enrique García Cota
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]],
|
||||
|
||||
}
|
||||
|
||||
-- quotas don't work in LuaJIT since debug.sethook works differently there
|
||||
local quota_supported = type(_G.jit) == "nil"
|
||||
sandbox.quota_supported = quota_supported
|
||||
|
||||
-- PUC-Rio Lua 5.1 does not support deactivation of bytecode
|
||||
--local bytecode_blocked = _ENV or type(_G.jit) == "table"
|
||||
local bytecode_blocked = false
|
||||
sandbox.bytecode_blocked = bytecode_blocked
|
||||
|
||||
-- The base environment is merged with the given env option (or an empty table, if no env provided)
|
||||
--
|
||||
local BASE_ENV = {}
|
||||
|
||||
-- List of unsafe packages/functions:
|
||||
--
|
||||
-- * string.rep: can be used to allocate millions of bytes in 1 operation
|
||||
-- * {set|get}metatable: can be used to modify the metatable of global objects (strings, integers)
|
||||
-- * collectgarbage: can affect performance of other systems
|
||||
-- * dofile: can access the server filesystem
|
||||
-- * _G: It has access to everything. It can be mocked to other things though.
|
||||
-- * load{file|string}: All unsafe because they can grant acces to global env
|
||||
-- * raw{get|set|equal}: Potentially unsafe
|
||||
-- * module|require|module: Can modify the host settings
|
||||
-- * string.dump: Can display confidential server info (implementation of functions)
|
||||
-- * math.randomseed: Can affect the host sytem
|
||||
-- * io.*, os.*: Most stuff there is unsafe, see below for exceptions
|
||||
|
||||
|
||||
-- Safe packages/functions below
|
||||
([[
|
||||
|
||||
_VERSION assert error ipairs next pairs
|
||||
pcall select tonumber tostring type unpack xpcall
|
||||
|
||||
coroutine.create coroutine.resume coroutine.running coroutine.status
|
||||
coroutine.wrap coroutine.yield
|
||||
|
||||
math.abs math.acos math.asin math.atan math.atan2 math.ceil
|
||||
math.cos math.cosh math.deg math.exp math.fmod math.floor
|
||||
math.frexp math.huge math.ldexp math.log math.log10 math.max
|
||||
math.min math.modf math.pi math.pow math.rad math.random
|
||||
math.sin math.sinh math.sqrt math.tan math.tanh
|
||||
|
||||
os.clock os.difftime os.time
|
||||
|
||||
string.byte string.char string.find string.format string.gmatch
|
||||
string.gsub string.len string.lower string.match string.reverse
|
||||
string.sub string.upper
|
||||
|
||||
table.insert table.maxn table.remove table.sort
|
||||
|
||||
]]):gsub('%S+', function(id)
|
||||
local module, method = id:match('([^%.]+)%.([^%.]+)')
|
||||
if module then
|
||||
BASE_ENV[module] = BASE_ENV[module] or {}
|
||||
BASE_ENV[module][method] = _G[module][method]
|
||||
else
|
||||
BASE_ENV[id] = _G[id]
|
||||
end
|
||||
end)
|
||||
|
||||
local function protect_module(module, module_name)
|
||||
return setmetatable({}, {
|
||||
__index = module,
|
||||
__newindex = function(_, attr_name, _)
|
||||
error('Can not modify ' .. module_name .. '.' .. attr_name .. '. Protected by the sandbox.')
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
('coroutine math os string table'):gsub('%S+', function(module_name)
|
||||
BASE_ENV[module_name] = protect_module(BASE_ENV[module_name], module_name)
|
||||
end)
|
||||
|
||||
-- auxiliary functions/variables
|
||||
|
||||
local string_rep = string.rep
|
||||
|
||||
local function sethook(f, key, quota)
|
||||
if type(debug) ~= 'table' or type(debug.sethook) ~= 'function' then return end
|
||||
debug.sethook(f, key, quota)
|
||||
end
|
||||
|
||||
local function cleanup()
|
||||
sethook()
|
||||
string.rep = string_rep -- luacheck: no global
|
||||
end
|
||||
|
||||
-- Public interface: sandbox.protect
|
||||
function sandbox.protect(code, options)
|
||||
options = options or {}
|
||||
|
||||
local quota = false
|
||||
if options.quota and not quota_supported then
|
||||
error("options.quota is not supported on this environment (usually LuaJIT). Please unset options.quota")
|
||||
end
|
||||
if options.quota ~= false then
|
||||
quota = options.quota or 500000
|
||||
end
|
||||
|
||||
assert(type(code) == 'string', "expected a string")
|
||||
|
||||
local passed_env = options.env or {}
|
||||
local env = {}
|
||||
for k, v in pairs(BASE_ENV) do
|
||||
local pv = passed_env[k]
|
||||
if pv ~= nil then
|
||||
env[k] = pv
|
||||
else
|
||||
env[k] = v
|
||||
end
|
||||
end
|
||||
setmetatable(env, { __index = options.env })
|
||||
env._G = env
|
||||
|
||||
local f
|
||||
if bytecode_blocked then
|
||||
f = assert(load(code, nil, 't', env))
|
||||
else
|
||||
f = (loadstring(code))
|
||||
if (not f) then
|
||||
return (function() return nil end)
|
||||
end
|
||||
|
||||
setfenv(f, env)
|
||||
end
|
||||
|
||||
return function(...)
|
||||
|
||||
if quota and quota_supported then
|
||||
local timeout = function()
|
||||
cleanup()
|
||||
error('Quota exceeded: ' .. tostring(quota))
|
||||
end
|
||||
sethook(timeout, "", quota)
|
||||
end
|
||||
|
||||
string.rep = nil -- luacheck: no global
|
||||
|
||||
local t = table.pack(pcall(f, ...))
|
||||
|
||||
cleanup()
|
||||
|
||||
if not t[1] then error(t[2]) end
|
||||
|
||||
return table.unpack(t, 2, t.n)
|
||||
end
|
||||
end
|
||||
|
||||
-- Public interface: sandbox.run
|
||||
function sandbox.run(code, options, ...)
|
||||
return sandbox.protect(code, options)(...)
|
||||
end
|
||||
|
||||
-- make sandbox(f) == sandbox.protect(f)
|
||||
setmetatable(sandbox, {__call = function(_,code,o) return sandbox.protect(code,o) end})
|
||||
|
||||
return sandbox
|
||||
end
|
|
@ -1,15 +0,0 @@
|
|||
--- Functions in this file are callable by user-code
|
||||
|
||||
function stuff()
|
||||
return "Success! I am result of stuff()"
|
||||
end
|
||||
|
||||
--function moveForward (turtle) turtle:turtle_move_withHeading( 1, 0, 0) end
|
||||
--function moveBackward (turtle) turtle:turtle_move_withHeading(-1, 0, 0) end
|
||||
--function moveRight (turtle) turtle:turtle_move_withHeading( 0, 1, 0) end
|
||||
--function moveLeft (turtle) turtle:turtle_move_withHeading( 0,-1, 0) end
|
||||
--function moveUp (turtle) turtle:turtle_move_withHeading( 0, 0, 1) end
|
||||
--function moveDown (turtle) turtle:turtle_move_withHeading( 0, 0,-1) end
|
||||
--
|
||||
--function turnRight (turtle) turtle:set_heading(turtle:get_heading()-1) end
|
||||
--function turnLeft (turtle) turtle:set_heading(turtle:get_heading()+1) end
|
|
@ -1,3 +1,6 @@
|
|||
--[[
|
||||
The turtle block is only used to spawn the turtle entity, then it deletes itself
|
||||
]]
|
||||
minetest.register_node("computertest:turtle", {
|
||||
tiles = {
|
||||
"computertest_top.png",
|
|
@ -11,19 +11,15 @@ local function sandbox(codeString)
|
|||
end
|
||||
|
||||
local function getTurtle(id) return computertest.turtles[id] end
|
||||
local function runTurtleCommand(turtle, command)
|
||||
if command==nil or command=="" then return nil end
|
||||
--TODO eventually replace this with some kinda lua sandbox
|
||||
command = "function init(turtle) return "..command.." end"
|
||||
minetest.log("COMMAND IS \""..command.."\"")
|
||||
sandbox(command)()
|
||||
local res = init(turtle)
|
||||
if (res==nil) then
|
||||
return "Returned nil"
|
||||
elseif (type(res)=="string") then
|
||||
return res
|
||||
local function upload_code_to_turtle(turtle, code_string,run_for_result)
|
||||
turtle.codeUncompiled = code_string
|
||||
turtle.code = sandbox(turtle.codeUncompiled)
|
||||
if (run_for_result) then
|
||||
--TODO run subroutine once, if it returns a value, return that here
|
||||
return "Ran"
|
||||
end
|
||||
return "Done. Didn't return string."
|
||||
|
||||
return turtle.code ~= nil
|
||||
end
|
||||
local function get_formspec_inventory(self)
|
||||
return "size[12,5;]"
|
||||
|
@ -58,7 +54,6 @@ local function get_formspec_upload(turtle)
|
|||
.."set_focus[upload;true]"
|
||||
;
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
local function isForm(name)
|
||||
return string.sub(formname,1,string.len(name))==name
|
||||
|
@ -77,7 +72,10 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|||
local id = tonumber(string.sub(formname,1+string.len(FORMNAME_TURTLE_TERMINAL)))
|
||||
local turtle = getTurtle(id)
|
||||
turtle.lastCommandRan = fields.terminal_in
|
||||
local commandResult = runTurtleCommand(turtle, fields.terminal_in)
|
||||
local command = fields.terminal_in
|
||||
if command==nil or command=="" then return nil end
|
||||
command = "function init(turtle) return "..command.." end"
|
||||
local commandResult = upload_code_to_turtle(turtle, command, true)
|
||||
if (commandResult==nil) then
|
||||
minetest.close_formspec(player:get_player_name(),FORMNAME_TURTLE_TERMINAL..id)
|
||||
return true
|
||||
|
@ -89,11 +87,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|||
local id = tonumber(string.sub(formname,1+string.len(FORMNAME_TURTLE_UPLOAD)))
|
||||
if (fields.button_upload == nil or fields.upload == nil) then return true end
|
||||
local turtle = getTurtle(id)
|
||||
--minetest.debug("CODE UPLOADED, NEAT",dump(id),dump(formname),dump(fields),dump(turtle))
|
||||
turtle.codeUncompiled = fields.upload
|
||||
turtle.code = sandbox(turtle.codeUncompiled)
|
||||
if turtle.code==nil then return true end--Given malformed code
|
||||
-- This turtle.code is used later
|
||||
return not upload_code_to_turtle(turtle, fields.upload,false)
|
||||
else
|
||||
return false--Unknown formname, input not processed
|
||||
end
|
||||
|
@ -163,9 +157,6 @@ minetest.register_entity("computertest:turtle", {
|
|||
---@returns true on success
|
||||
---
|
||||
turtle_move_withHeading = function (turtle,numForward,numRight,numUp)
|
||||
--minetest.log("YAW"..dump(turtle.object:get_yaw()))
|
||||
--minetest.log("NUMFORWARD"..dump(numForward))
|
||||
--minetest.log("NUMRIGHT"..dump(numRight))
|
||||
local new_pos = turtle:getNearbyPos(numForward,numRight,numUp)
|
||||
--Verify new pos is empty
|
||||
if (minetest.get_node(new_pos).name~="air") then
|
||||
|
@ -189,8 +180,9 @@ minetest.register_entity("computertest:turtle", {
|
|||
end,
|
||||
mine = function(turtle, nodeLocation)
|
||||
local node = minetest.get_node(nodeLocation)
|
||||
if (node.name=="air") then return false end
|
||||
local drops = minetest.get_node_drops(node)
|
||||
|
||||
minetest.remove_node(nodeLocation)
|
||||
for _, itemname in ipairs(drops) do
|
||||
local stack = ItemStack(itemname)
|
||||
--TODO This doesn't actually need to drop-then-undrop the item for no reason
|
||||
|
@ -208,16 +200,16 @@ minetest.register_entity("computertest:turtle", {
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.remove_node(nodeLocation)
|
||||
turtle:yield("Mining")
|
||||
|
||||
return true
|
||||
end,
|
||||
-- MAIN TURTLE INTERFACE ---------------------------------------
|
||||
-- TODO put turtle interface into a stack, so the turtle can't immediately mine hundreds of blocks (only one mine per second and move two blocks per second or something)
|
||||
-- Wouldn't work since the player couldn't use stateful functions such as getting the fuel level
|
||||
-- TODO The TurtleEntity thread would need to pause itself after calling any of these functions. This pause would then return state back to the
|
||||
-- TODO move this to a literal OO interface wrapper thingy
|
||||
interface = {
|
||||
yield,
|
||||
moveForward, moveBackward, moveRight, moveLeft, moveUp, moveDown,
|
||||
turnLeft, turnRight,
|
||||
mineForward, mineUp, mineDown,
|
||||
},
|
||||
yield = function(turtle,reason) if (coroutine.running() == turtle.coroutine) then coroutine.yield(reason) end end,
|
||||
moveForward = function(turtle) turtle:turtle_move_withHeading( 1, 0, 0) end,
|
||||
moveBackward = function(turtle) turtle:turtle_move_withHeading(-1, 0, 0) end,
|
||||
|
@ -237,7 +229,7 @@ local timer = 0
|
|||
minetest.register_globalstep(function(dtime)
|
||||
timer = timer + dtime
|
||||
if (timer >= computertest.config.globalstep_interval) then
|
||||
for id,turtle in pairs(computertest.turtles) do
|
||||
for _,turtle in pairs(computertest.turtles) do
|
||||
if turtle.coroutine then
|
||||
if coroutine.status(turtle.coroutine)=="dead" then
|
||||
--minetest.log("turtle #"..id.." has coroutine, but it's already done running")
|
|
@ -0,0 +1,7 @@
|
|||
function init(turtle)
|
||||
local x = 0;
|
||||
while true do
|
||||
turtle:mineForward()
|
||||
turtle:moveForward()
|
||||
end
|
||||
end
|
14
init.lua
14
init.lua
|
@ -6,15 +6,5 @@ computertest = {
|
|||
num_turtles = 0,
|
||||
}
|
||||
local modpath = minetest.get_modpath("computertest")
|
||||
dofile(modpath.."/Block/Turtle.lua")
|
||||
dofile(modpath.."/TurtleEntity.lua")
|
||||
dofile(modpath.."/TurtleInterface.lua")
|
||||
dofile(modpath.."/Sandbox.lua")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
dofile(modpath.."/block/turtle.lua")
|
||||
dofile(modpath.."/entity/turtle.lua")
|
||||
|
|
Loading…
Reference in New Issue