Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local fakeToReal = setmetatable({},{__mode="k"})
- local realToFake = setmetatable({},{__mode="v"})
- local connections = setmetatable({},{__mode="k"})
- local Metafields = {"index","newindex","call","metatable","tostring","len","unm","add","sub","mul","div","mod","pow","concat","eq","lt","le"}
- local API,dead = {}
- local DeadTrap,Kill = newproxy(true)
- local metacount = 0
- local function safeprint(...)
- local e = getfenv(0)
- setfenv(0,getfenv())
- print(...) setfenv(0,e)
- end
- local kill = Instance.new("Folder")
- local function killf() kill:Destroy() end
- function Kill()
- -- safeprint("metacount:", metacount)
- if metacount > 0 then return DeadTrap end
- spawn(killf) kill.Changed:Wait()
- end
- do
- local deadMeta = getmetatable(DeadTrap)
- local function deadBringer() return DeadTrap end
- for k,v in pairs(Metafields) do
- deadMeta["__"..v] = deadBringer
- end deadMeta.__call = Kill
- end
- local sandbox,unsandbox
- local function tuple(...)
- return {n=select("#",...),...}
- end
- local function customConnect(e,f)
- local con = e:Connect(function(...)
- metacount = 0 f(...)
- end)
- connections[con] = true
- return con
- end
- local function customWait(e)
- local t = tuple(e:Wait())
- metacount = 0
- if dead then Kill() end
- return unpack(t,1,t.n)
- end
- local instanceMeta = {}
- instanceMeta.__metatable = getmetatable(game)
- function instanceMeta:__index(key)
- if dead then return DeadTrap end
- local real = fakeToReal[self]
- if key == "connect" or key == "Connect" and typeof(real) == "RBXScriptSignal" then
- return sandbox(customConnect)
- elseif key == "wait" or key == "Wait" and typeof(real) == "RBXScriptSignal" then
- return sandbox(customWait)
- end
- return sandbox(real[key])
- end
- function instanceMeta:__newindex(key,val)
- if dead then return DeadTrap end
- unsandbox(self)[key] = unsandbox(val)
- end
- function instanceMeta:__call(...)
- if ... == 123 then return true end -- Kind of an assert for "is this sandboxed?"
- if dead then return Kill() end
- return API.SandboxTuple(unsandbox(self)(API.UnsandboxTuple(...)))
- end
- function instanceMeta:__tostring()
- if dead then return DeadTrap end
- return "~"..tostring(unsandbox(self)) -- Easier to debug
- end
- function API.Kill()
- warn("RIP")
- dead = true
- for k in pairs(connections) do
- k:Disconnect()
- end
- end
- function API.Sandbox(real)
- local fake = realToFake[real]
- if fake then return fake end
- local t = typeof(real)
- if t == "Instance" or t == "RBXScriptSignal" then
- fake = newproxy(true)
- fakeToReal[fake] = real
- realToFake[real] = fake
- local meta = getmetatable(fake)
- for k,v in pairs(instanceMeta) do
- meta[k] = v
- end return fake
- elseif t == "function" then
- local f = function(...)
- if dead then return Kill() end
- return API.SandboxTuple(real(API.UnsandboxTuple(...)))
- end
- fakeToReal[f] = real
- realToFake[real] = f
- elseif t == "table" then
- fake = {}
- for k,v in pairs(real) do
- fake[sandbox(k)] = sandbox(v)
- end return fake
- end
- return real
- end sandbox = API.Sandbox
- function API.Unsandbox(fake)
- local real = fakeToReal[fake]
- if real then return real end
- local t = typeof(fake)
- if t == "function" then
- local f = function(...)
- if dead then return Kill() end
- return API.UnsandboxTuple(fake(API.SandboxTuple(...)))
- end
- fakeToReal[fake] = f
- realToFake[f] = fake
- elseif t == "table" then
- real = {}
- for k,v in pairs(fake) do
- real[unsandbox(k)] = unsandbox(v)
- end return real
- end
- return fake
- end unsandbox = API.Unsandbox
- function API.SandboxTuple(...)
- local n,res = select("#",...),{...}
- for i=1,n do
- res[i] = sandbox(res[i])
- end return unpack(res,1,n)
- end
- function API.UnsandboxTuple(...)
- local n,res = select("#",...),{...}
- for i=1,n do
- res[i] = unsandbox(res[i])
- end return unpack(res,1,n)
- end
- local Globals,GlobalEnv = {
- tostring = tostring;
- error = error;
- getfenv = getfenv;
- setfenv = setfenv;
- pairs = pairs;
- ipairs = ipairs;
- next = next;
- setmetatable = setmetatable;
- getmetatable = getmetatable;
- },getfenv()
- function Globals.spawn(f)
- spawn(function(...)
- metacount = 0 f(...)
- end)
- end Globals.Spawn = Globals.spawn
- function Globals.delay(d,f)
- delay(d,function(...)
- metacount = 0 f(...)
- end)
- end Globals.Delay = Globals.delay
- function Globals.wait(...)
- if dead then
- metacount = 0
- return Kill()
- end return wait(...)
- end Globals.Wait = Globals.wait
- function Globals.print(...)
- if dead then return Kill() end
- print(...)
- end
- function Globals.warn(...)
- if dead then return Kill() end
- warn(...)
- end
- local metas = setmetatable({},{__mode="k"})
- local function proxyMeta(meta)
- local res = {}
- for k,v in pairs(Metafields) do
- v = "__"..v
- res[v] = function(...)
- local m = meta[v]
- if not m then
- error("No metamethod "..v,0)
- end metacount = metacount + 1
- local t = tuple(m(...))
- metacount = metacount - 1
- return unpack(t,1,t.n)
- end
- end
- function res:__index(key)
- local m,t = meta.__index,{}
- metacount = metacount + 1
- if typeof(m) == "function" then
- t = tuple(m(self,key))
- else
- t = {n=1,m[key]}
- end
- metacount = metacount - 1
- return unpack(t,1,t.n)
- end
- function res:__newindex(key,val)
- local m,t = meta.__newindex,{}
- metacount = metacount + 1
- if typeof(m) == "function" then
- m(self,key,val)
- else
- m[key] = val
- end metacount = metacount - 1
- end
- local function e(a) return a end
- function res:__call(...)
- if dead then return Kill() end
- return e(meta.__call)(...)
- end return res
- end
- function Globals.getmetatable(val)
- local m = metas[val]
- if m and m.__metatable ~= nil then
- return m
- end return m or getmetatable(val)
- end
- function Globals.setmetatable(val,meta)
- local m = metas[val]
- if m and m.__metatable ~= nil then
- error("cannot change a protected metatable",2)
- end
- setmetatable(val,proxyMeta(meta))
- metas[val] = meta return val
- end
- function API.Create()
- return setmetatable({},{__index=function(s,k)
- return Globals[k] or dead and DeadTrap or sandbox(GlobalEnv[k])
- end})
- end
- local unboxedBE = Instance.new("BindableEvent",workspace)
- unboxedBE.Name = "SandboxEvent"
- spawn(function()
- while wait(0.1) do
- unboxedBE:Fire(game)
- end
- end)
- spawn(function()
- local env = API.Create()
- delay(0.5,API.Kill)
- setfenv(0,env)
- setfenv(1,env)
- game(123)
- local BE = Instance.new("BindableEvent")
- BE.Event:Connect(warn)
- BE:Fire("hi")
- local eh = setmetatable({},{__index=function(s,k)
- safeprint("eh.__index",k)
- print("Did __index with key",k)
- end})
- local function test(k)
- print(eh[k])
- end
- test()
- spawn(test)
- delay(1,test)
- spawn(function()
- while wait() do
- print("Still running")
- end
- end)
- local BE = workspace.SandboxEvent
- BE.Parent = nil -- otherwise every test leaves one behind
- BE.Event:Connect(function(g) print("fired",g,g(123)) end)
- spawn(function()
- local g = BE.Event:Wait()
- print("waited",g,g(123))
- end)
- delay(1,function()
- local g = BE.Event:Wait()
- print("waited",g,g(123))
- end)
- wait(0.5)
- warn("for realz")
- end)
- return API
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement