diff --git a/pkg/base/lib_fastload.lua b/pkg/base/lib_fastload.lua
index 6099019..82e4d12 100644
--- a/pkg/base/lib_fastload.lua
+++ b/pkg/base/lib_fastload.lua
@@ -135,6 +135,9 @@ function fastload_analyse_client()
state.e.pcall = pcall
state.e.pairs = pairs
state.e.ipairs = ipairs
+
+ -- Tell it there's a sandbox
+ state.e.sandbox = {}
-- client.wav_play_local/global
function state.e.client.wav_play_local(...) return 1 end
diff --git a/pkg/base/main_client.lua b/pkg/base/main_client.lua
index 77afa4e..8538b32 100644
--- a/pkg/base/main_client.lua
+++ b/pkg/base/main_client.lua
@@ -15,10 +15,16 @@
along with Ice Lua Components. If not, see .
]]
+SANDBOX_CLIENT = true
+
if common.version.num < 8388608 then
error("You need Iceball version 0.2 or later to connect to this server.")
end
+if SANDBOX_CLIENT and not sandbox then
+ return (loadfile("pkg/base/sandbox/main.lua"))(...)
+end
+
if common.mk_compat_disable then
common.mk_compat_disable()
client.mk_set_title("Iceball")
@@ -155,6 +161,7 @@ do
local last_keepalive = common.time()
function load_screen_fetch(ftype, fname)
if string.sub(fname, 1,6) == "clsave" then return client.fetch_block(ftype, fname) end
+ --if true then return client.fetch_block(ftype, fname) end
if widgets ~= nil then
scene = gui_create_scene(w, h)
img_loading_width, img_loading_height = client.img_get_dims(img_loading)
diff --git a/pkg/base/main_server.lua b/pkg/base/main_server.lua
index 1d76a76..a8e1ba9 100644
--- a/pkg/base/main_server.lua
+++ b/pkg/base/main_server.lua
@@ -15,10 +15,16 @@
along with Ice Lua Components. If not, see .
]]
+SANDBOX_SERVER = false
+
if common.version.num < 8388608 then
error("You need Iceball version 0.2 or later to run this code.")
end
+if SANDBOX_SERVER and not sandbox then
+ return (loadfile("pkg/base/sandbox/main.lua"))(...)
+end
+
dofile("pkg/base/lib_fastload.lua")
-- UDP port test.
diff --git a/pkg/base/network.lua b/pkg/base/network.lua
index 59fcb63..2860196 100644
--- a/pkg/base/network.lua
+++ b/pkg/base/network.lua
@@ -588,6 +588,12 @@ network.sys_handle_c2s(PKT_PLR_OFFER, "bbz", nwdec_plrset(function (neth, cli, p
plr.wpn = weapons[wpn](plr)
if plr.team ~= tidx then
plr.set_health_damage(0, 0xFF800000, plr.name.." changed teams", nil)
+ if not teams[tidx] then
+ print("HACK ATTEMPT from neth "..tostring(neth))
+ server.net_kick(neth, "Hack attempt")
+ return
+ end
+ print(plr, tidx, teams[tidx])
net_broadcast(nil, common.net_pack("BIz", PKT_CHAT_ADD_TEXT, 0xFF800000,
"* Player "..plr.name.." has joined the "..teams[tidx].name.." team"))
elseif plr.weapon ~= wpn then
diff --git a/pkg/base/preconf.lua b/pkg/base/preconf.lua
index a732bcd..b182c8b 100644
--- a/pkg/base/preconf.lua
+++ b/pkg/base/preconf.lua
@@ -20,6 +20,7 @@ MODE_NUB_KICKONJOIN = false
-- skins allowed
SKIN_ENABLE_SRC = {"pmf", "kv6", "tga", "png", "wav", "it"}
+--SKIN_ENABLE_SRC = {}
SKIN_ENABLE = {}
do
local i
diff --git a/pkg/base/sandbox/fetch.lua b/pkg/base/sandbox/fetch.lua
new file mode 100644
index 0000000..df580ef
--- /dev/null
+++ b/pkg/base/sandbox/fetch.lua
@@ -0,0 +1,150 @@
+--[[
+ 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 .
+]]
+
+-- Wrappers for fetching stuff.
+
+do
+
+return function(sb_list, sb_aux, sb_ctl, name)
+ local SG = sb_list[name]
+
+ -- Set up function wrapper
+ local function wrapfn(f)
+ setfenv(f, SG)
+ end
+
+ local s_fetch_start = common.fetch_start
+ local s_fetch_poll = common.fetch_poll
+ local s_fetch_block = common.fetch_block
+
+ local fetch_last_ftype = nil
+ local fetch_last_fname = nil
+ function SG.common.fetch_start(ftype, fname)
+ local obj = s_fetch_start(ftype, fname)
+
+ if obj ~= true and obj ~= nil then
+ if ftype == "lua" and obj ~= nil then
+ wrapfn(obj)
+ end
+ end
+
+ fetch_last_ftype = nil
+ fetch_last_fname = nil
+ if obj == true then
+ fetch_last_ftype = ftype
+ fetch_last_fname = fname
+ end
+
+ return obj
+ end
+
+ function SG.common.fetch_poll()
+ local oldmap = common.map_get()
+ sb_ctl.gfx_api_prerender()
+
+ local function ff(obj, ...)
+ if obj ~= false then
+ if fetch_last_ftype == "lua" and obj ~= nil then
+ wrapfn(obj)
+ end
+
+ fetch_last_ftype = nil
+ fetch_last_fname = nil
+ end
+
+ sb_ctl.gfx_api_postrender(name)
+
+ return obj, ...
+ end
+
+ return ff(s_fetch_poll())
+ end
+
+ function SG.common.fetch_block(ftype, fname)
+ local fetch_start = common.fetch_start
+ local fetch_poll = common.fetch_poll
+ common.fetch_start = SG.common.fetch_start
+ common.fetch_poll = SG.common.fetch_poll
+
+ --print("BLOCK", ftype, fname)
+ local obj = s_fetch_block(ftype, fname)
+ if ftype == "lua" and obj ~= nil then
+ wrapfn(obj)
+ end
+ --print("BLOCK END", ftype, fname, obj)
+
+ common.fetch_start = fetch_start
+ common.fetch_poll = fetch_poll
+
+ return obj
+ end
+
+ function SG.loadstring(...)
+ local f = loadstring(...)
+ wrapfn(f)
+ return f
+ end
+
+ function SG.loadfile(...)
+ return SG.common.fetch_block("lua", ...)
+ end
+
+ function SG.dofile(...)
+ return SG.loadfile(...)()
+ end
+
+ -- mirrors
+ if client then
+ SG.client.fetch_block = SG.common.fetch_block
+ SG.client.fetch_start = SG.common.fetch_start
+ SG.client.fetch_poll = SG.common.fetch_poll
+ end
+
+ if server then
+ SG.server.fetch_block = SG.common.fetch_block
+ SG.server.fetch_start = SG.common.fetch_start
+ SG.server.fetch_poll = SG.common.fetch_poll
+ end
+
+ -- other things that are basically fetch_block
+ function SG.common.bin_load(...) return SG.common.fetch_block("bin", ...) end
+ function SG.common.json_load(...) return SG.common.fetch_block("json", ...) end
+ function SG.common.wav_load(...) return SG.common.fetch_block("wav", ...) end
+ function SG.common.mus_load_it(...) return SG.common.fetch_block("it", ...) end
+ function SG.common.model_load_pmf(...) return SG.common.fetch_block("pmf", ...) end
+ function SG.common.map_load(fname, fmt)
+ if fmt == nil or fmt == "auto" then
+ fmt = "map"
+ end
+ return SG.common.fetch_block(fmt, fname)
+ end
+ function SG.common.img_load(fname, fmt)
+ print("IMG!", fmt, fname)
+ local img = SG.common.fetch_block(fmt or "tga", fname)
+ print("IMG!", img)
+ if not img then return nil, nil, nil end
+ return img, common.img_get_dims(img)
+ end
+
+ -- more aliases
+ if client then
+ SG.client.img_load = SG.common.img_load
+ end
+end
+
+end
+
diff --git a/pkg/base/sandbox/gfx.lua b/pkg/base/sandbox/gfx.lua
new file mode 100644
index 0000000..e126e26
--- /dev/null
+++ b/pkg/base/sandbox/gfx.lua
@@ -0,0 +1,175 @@
+
+--[[
+ 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 .
+]]
+
+-- Wrappers for graphical stuff. Also handles input.
+
+do
+
+local sb_list, sb_aux, sb_ctl = ...
+sb_ctl.gfx_select = "root"
+sb_ctl.gfx_map_stack = {idx = 0}
+
+-- Select sandbox to use for graphics and input.
+function sandbox.gfx_select(name)
+ if not sb_list[name] then
+ error("invalid VM \""..tostring(name).."\" for gfx_select")
+ end
+
+ sb_ctl.gfx_select = name
+
+ client.mouse_lock_set(sb_aux[name].gfx_mouse_grab)
+ client.mouse_visible_set(sb_aux[name].gfx_mouse_vis)
+ local l = sb_aux[name].gfx_map_fog
+ client.map_fog_set(l[1], l[2], l[3], l[4])
+end
+
+-- INTERNAL API: Begin / end of hooks
+function sb_ctl.gfx_api_push(name)
+ local map = nil
+ if sb_aux[name] then
+ map = sb_aux[name].gfx_map
+ end
+
+ sb_ctl.gfx_map_stack.idx = sb_ctl.gfx_map_stack.idx + 1
+ sb_ctl.gfx_map_stack[sb_ctl.gfx_map_stack.idx] = {name, common.map_get()}
+ common.map_set(map)
+end
+function sb_ctl.gfx_api_pop()
+ local name = sb_ctl.gfx_map_stack[sb_ctl.gfx_map_stack.idx][1]
+ if sb_aux[name] then
+ sb_aux[name].gfx_map = common.map_get()
+ end
+ common.map_set(sb_ctl.gfx_map_stack[sb_ctl.gfx_map_stack.idx][2])
+ sb_ctl.gfx_map_stack[sb_ctl.gfx_map_stack.idx] = nil
+ sb_ctl.gfx_map_stack.idx = sb_ctl.gfx_map_stack.idx - 1
+end
+function sb_ctl.gfx_api_prerender()
+ local map = nil
+ if sb_aux[sb_ctl.gfx_select] then
+ map = sb_aux[sb_ctl.gfx_select].gfx_map
+ end
+ common.map_set(map)
+end
+function sb_ctl.gfx_api_postrender(name)
+ local map = nil
+ if sb_aux[name] then
+ map = sb_aux[name].gfx_map
+ end
+ common.map_set(map)
+end
+
+function sb_ctl.gfx_kill(name)
+ if name == sb_ctl.gfx_select then
+ -- TODO: properly return to parent
+ if name ~= "root" then
+ sandbox.gfx_select("root")
+ end
+ end
+end
+
+return function(sb_list, sb_aux, sb_ctl, name)
+ local SG = sb_list[name]
+
+ sb_aux[name].gfx_mouse_vis = true
+ sb_aux[name].gfx_mouse_grab = false
+ sb_aux[name].gfx_map_fog = {208, 224, 255, 60.0}
+ sb_aux[name].gfx_map = map
+
+ if client then
+ local s_img_blit = SG.client.img_blit
+ function SG.client.img_blit(...)
+ if sb_ctl.gfx_select == name then
+ return s_img_blit(...)
+ end
+ end
+
+ local s_va_render_global = SG.client.va_render_global
+ function SG.client.va_render_global(...)
+ if sb_ctl.gfx_select == name then
+ return s_va_render_global(...)
+ end
+ end
+
+ local s_va_render_local = SG.client.va_render_local
+ function SG.client.va_render_local(...)
+ if sb_ctl.gfx_select == name then
+ return s_va_render_local(...)
+ end
+ end
+
+ local s_model_render_bone_global = SG.client.model_render_bone_global
+ function SG.client.model_render_bone_global(...)
+ if sb_ctl.gfx_select == name then
+ return s_model_render_bone_global(...)
+ end
+ end
+
+ local s_model_render_bone_local = SG.client.model_render_bone_local
+ function SG.client.model_render_bone_local(...)
+ if sb_ctl.gfx_select == name then
+ return s_model_render_bone_local(...)
+ end
+ end
+
+ local s_mouse_lock_set = SG.client.mouse_lock_set
+ function SG.client.mouse_lock_set(state)
+ sb_aux[name].gfx_mouse_grab = state
+ if sb_ctl.gfx_select == name then
+ return s_mouse_lock_set(state)
+ end
+ end
+
+ local s_mouse_visible_set = SG.client.mouse_visible_set
+ function SG.client.mouse_visible_set(state)
+ sb_aux[name].gfx_mouse_vis = state
+ if sb_ctl.gfx_select == name then
+ return s_mouse_visible_set(state)
+ end
+ end
+
+ local s_map_set = SG.common.map_set
+ function SG.common.map_set(map)
+ print("map", name, map)
+ sb_aux[name].gfx_map = map
+ return s_map_set(map)
+ end
+ SG.client.map_set = SG.common.map_set
+
+ local s_map_fog_set = SG.client.map_fog_set
+ function SG.client.map_fog_set(r, g, b, dist)
+ sb_aux[name].gfx_map_fog = {r, g, b, dist}
+ if sb_ctl.gfx_select == name then
+ return s_map_fog_set(r, g, b, dist)
+ end
+ end
+
+ local s_map_fog_get = SG.client.map_fog_get
+ function SG.client.map_fog_get()
+ if sb_ctl.gfx_select == name then
+ return s_map_fog_get()
+ else
+ local l = sb_aux[name].gfx_map_fog
+ return l[1], l[2], l[3], l[4]
+ end
+ end
+ end
+
+end
+
+end
+
diff --git a/pkg/base/sandbox/main.lua b/pkg/base/sandbox/main.lua
new file mode 100644
index 0000000..fb62e85
--- /dev/null
+++ b/pkg/base/sandbox/main.lua
@@ -0,0 +1,282 @@
+--[[
+ 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 .
+]]
+
+-- 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.string = string
+ SG.math = math
+ SG.table = table
+
+ SG.assert = assert
+ SG.collectgarbage = collectgarbage
+ SG.error = error
+ SG.getfenv = getfenv
+ 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)
+
+ -- 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()
+ 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_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
+
diff --git a/pkg/base/sandbox/wav.lua b/pkg/base/sandbox/wav.lua
new file mode 100644
index 0000000..a6059a4
--- /dev/null
+++ b/pkg/base/sandbox/wav.lua
@@ -0,0 +1,87 @@
+--[[
+ 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 .
+]]
+
+-- Wrappers for the audio system.
+
+do
+
+return function (sb_list, sb_aux, sb_ctl, name)
+ local SG = sb_list[name]
+
+ sb_aux[name].mus_ref = nil
+ sb_aux[name].mus_order = 0
+ sb_aux[name].mus_row = 0
+ sb_aux[name].mus_vol = 1.0
+ sb_aux[name].wav_enabled = true --(name == "root")
+ sb_aux[name].wav_cube_size = 1.0
+
+ if client then
+ function SG.client.wav_cube_size(size)
+ sb_aux[name].wav_cube_size = size
+ if sb_aux[name].wav_enabled then
+ return client.wav_cube_size(size)
+ end
+ end
+
+ function SG.client.wav_play_global(...)
+ if sb_aux[name].wav_enabled then
+ return client.wav_play_global(...)
+ end
+
+ return nil
+ end
+
+ function SG.client.wav_play_local(...)
+ if sb_aux[name].wav_enabled then
+ return client.wav_play_local(...)
+ end
+
+ return nil
+ end
+
+ function SG.client.mus_play(mus, order, row)
+ order = order or 0
+ row = row or 0
+
+ sb_aux[name].mus_ref = mus
+ sb_aux[name].mus_order = order
+ sb_aux[name].mus_row = row
+ if sb_aux[name].wav_enabled then
+ return client.mus_play(mus, order, row)
+ end
+ end
+
+ function SG.client.mus_stop()
+ sb_aux[name].mus_ref = nil
+ sb_aux[name].mus_order = 0
+ sb_aux[name].mus_row = 0
+ if sb_aux[name].wav_enabled then
+ return client.mus_stop()
+ end
+ end
+
+ function SG.client.mus_vol_set(vol)
+ sb_aux[name].mus_vol = vol
+ if sb_aux[name].wav_enabled then
+ return client.mus_vol_set(vol)
+ end
+ end
+ end
+end
+
+end
+
diff --git a/pkg/br/snake/mod.json b/pkg/br/snake/mod.json
new file mode 100644
index 0000000..cceba11
--- /dev/null
+++ b/pkg/br/snake/mod.json
@@ -0,0 +1,3 @@
+{
+ "load_client": ["mod_sandbox.lua"]
+}
diff --git a/pkg/br/snake/mod_sandbox.lua b/pkg/br/snake/mod_sandbox.lua
new file mode 100644
index 0000000..8b2f0cc
--- /dev/null
+++ b/pkg/br/snake/mod_sandbox.lua
@@ -0,0 +1,52 @@
+--[[
+ 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 .
+]]
+
+-- Simple test of the sandbox API.
+
+local super = new_player
+function new_player(...)
+ local this = super(...)
+
+ local s_create_hud = this.create_hud
+ local function f_create_hud(...)
+ local ret = s_create_hud(...)
+
+ local s_chat_on_return = this.typing_text.on_return
+ function this.typing_text.on_return(...)
+ if this.typing_text.text == "/snake" then
+ local snake = sandbox.new("snake", "pkg/br/snake/main_client.lua")
+ sandbox.gfx_select(snake)
+ this.typing_text.text = ""
+ end
+
+ return s_chat_on_return(...)
+ end
+ end
+
+ function this.create_hud(...)
+ local ret = s_create_hud(...)
+ f_create_hud(...)
+ return ret
+ end
+
+ if this.scene then
+ f_create_hud(...)
+ end
+
+ return this
+end
+