310 lines
6.4 KiB
Lua

--[[
This file is part of Ice Lua Components.
Ice Lua Components is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Ice Lua Components is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Ice Lua Components. If not, see <http://www.gnu.org/licenses/>.
]]
-- Simple, not necessarily secure sandbox.
-- More like a VM.
sandbox = {}
do
local sb_counter = 1
local sb_list = {}
local sb_aux = {}
local sb_ctl = {}
local sb_current = ""
local sb_wrap_fetch = loadfile("pkg/base/sandbox/fetch.lua")(sb_list, sb_aux, sb_ctl)
local sb_wrap_audio = loadfile("pkg/base/sandbox/wav.lua")(sb_list, sb_aux, sb_ctl)
local sb_wrap_gfx = loadfile("pkg/base/sandbox/gfx.lua")(sb_list, sb_aux, sb_ctl)
local function table_dup(S)
if S == nil then return nil end
local D = {}
local k, v
for k, v in pairs(S) do
if type(v) == "table" then
D[k] = table_dup(v)
else
D[k] = v
end
end
return D
end
-- Creates a new sandbox with a given name.
-- Returns actual name in case of collision.
function sandbox.new(name, fname, ...)
-- Pick a name
if sb_list[name] then
while sb_list[name.."-"..sb_counter] do
sb_counter = sb_counter + 1
end
name = name.."-"..sb_counter
sb_counter = sb_counter + 1
end
print("Creating sandbox \""..name.."\"")
-- Create new environment
local SG = {}
SG._G = SG
sb_list[name] = SG
sb_aux[name] = {}
sb_aux[name].tick_enabled = true
-- Copy some builtins
SG.coroutine = coroutine
SG.string = string
SG.math = math
SG.table = table
SG.assert = assert
SG.collectgarbage = collectgarbage
SG.error = error
SG.getmetatable = getmetatable
SG.ipairs = ipairs
SG.next = next
SG.pairs = pairs
SG.pcall = pcall
SG.print = print
SG.rawequal = rawequal
SG.rawget = rawget
SG.rawset = rawset
SG.select = select
SG.setfenv = setfenv
SG.setmetatable = setmetatable
SG.tonumber = tonumber
SG.tostring = tostring
SG.type = type
SG.unpack = unpack
SG._VERSION = _VERSION
SG.xpcall = xpcall
-- Copy main things
setfenv(table_dup, _G)
SG.client = table_dup(client)
SG.common = table_dup(common)
SG.server = table_dup(server)
SG.sandbox = table_dup(sandbox)
-- Remove hooks
local k,v
local clsv = (SG.client or SG.server)
for k,v in pairs(clsv) do
if k:sub(1,5) == "hook_" then
clsv[k] = nil
end
end
-- Enable wrappers
sb_wrap_fetch(sb_list, sb_aux, sb_ctl, name)
sb_wrap_audio(sb_list, sb_aux, sb_ctl, name)
sb_wrap_gfx(sb_list, sb_aux, sb_ctl, name)
function SG.getfenv(f)
local ret = getfenv(f)
if ret == _G then
ret = SG
end
return ret
end
function SG.sandbox.this()
return name
end
-- Do file
-- Return name
return name, (SG.loadfile(fname))(...)
end
-- Kills a sandbox.
function sandbox.kill(name)
if not sb_list[name] then
error("nonexistant sandbox \""..tostring(name).."\"")
end
if client then
sb_ctl.gfx_kill(name)
end
sb_list[name] = nil
sb_aux[name] = nil
end
if client then
function client.hook_tick(...)
local k, v
local hadf = false
local retacc = 1
local kill_list = {}
for k, v in pairs(sb_list) do
if sb_aux[k].tick_enabled then
local f = v.client.hook_tick
if f then
hadf = true
sb_ctl.gfx_api_push(k)
retacc = math.min(retacc, f(...))
sb_ctl.gfx_api_pop()
else
kill_list[1+#kill_list] = k
end
end
end
for k, v in pairs(kill_list) do
print("Killing sandbox \""..tostring(v).."\"")
sandbox.kill(v)
end
if not hadf then
client.hook_tick = nil
end
sb_ctl.gfx_api_prerender()
return retacc
end
function client.hook_key(...)
if not sb_list[sb_ctl.gfx_select] then return end
local f = sb_list[sb_ctl.gfx_select].client.hook_key
if f then
sb_ctl.gfx_api_push(sb_ctl.gfx_select)
f(...)
sb_ctl.gfx_api_pop()
end
sb_ctl.gfx_api_prerender()
end
function client.hook_text(...)
if not sb_list[sb_ctl.gfx_select] then return end
local f = sb_list[sb_ctl.gfx_select].client.hook_text
if f then
sb_ctl.gfx_api_push(sb_ctl.gfx_select)
f(...)
sb_ctl.gfx_api_pop()
end
sb_ctl.gfx_api_prerender()
end
function client.hook_mouse_button(...)
if not sb_list[sb_ctl.gfx_select] then return end
local f = sb_list[sb_ctl.gfx_select].client.hook_mouse_button
if f then
sb_ctl.gfx_api_push(sb_ctl.gfx_select)
f(...)
sb_ctl.gfx_api_pop()
end
sb_ctl.gfx_api_prerender()
end
function client.hook_mouse_motion(...)
if not sb_list[sb_ctl.gfx_select] then return end
local f = sb_list[sb_ctl.gfx_select].client.hook_mouse_motion
if f then
sb_ctl.gfx_api_push(sb_ctl.gfx_select)
f(...)
sb_ctl.gfx_api_pop()
end
sb_ctl.gfx_api_prerender()
end
function client.hook_render(...)
if not sb_list[sb_ctl.gfx_select] then return end
local f = sb_list[sb_ctl.gfx_select].client.hook_render
if f then
sb_ctl.gfx_api_push(sb_ctl.gfx_select)
f(...)
sb_ctl.gfx_api_pop()
end
sb_ctl.gfx_api_prerender()
end
function client.hook_kick(...)
local k, v
for k, v in pairs(sb_list) do
local f = v.client.hook_kick
if f then
f(...)
end
end
end
elseif server then
function server.hook_tick(...)
local k, v
local hadf = false
local retacc = 1
for k, v in pairs(sb_list) do
if sb_aux[k].tick_enabled then
local f = v.server.hook_tick
if f then
hadf = true
retacc = math.min(retacc, f(...))
end
end
end
if not hadf then
server.hook_tick = nil
end
end
function server.hook_file(...)
local f = sb_list[sb_current].server.hook_file
if f then
return f(...)
end
end
function server.hook_connect(...)
local f = sb_list[sb_current].server.hook_connect
if f then
return f(...)
end
end
function server.hook_disconnect(...)
local f = sb_list[sb_current].server.hook_disconnect
if f then
return f(...)
end
end
end
sb_current = "root" -- TO BE PHASED OUT
end
-- Do initial sandbox
if client then
sandbox.new("root", "pkg/base/main_client.lua", ...)
elseif server then
sandbox.new("root", "pkg/base/main_server.lua", ...)
else
error("Cannot determine if client or server!")
end