Simple Spy V3 Script for Roblox
Simple Spy V3 Script for Roblox
SimpleSpyShutdown) == "function"
then
getgenv().SimpleSpyShutdown()
end
local realconfigs = {
logcheckcaller = false,
autoblock = false,
funcEnabled = true,
advancedinfo = false,
--logreturnvalues = false,
supersecretdevtoggle = false
}
configsmetatable.__index = function(self,index)
return realconfigs[index]
end
if hookfunction then
return hookfunction(old[metamethod],func)
else
local oldmetamethod = old[metamethod]
makewriteable(old)
old[metamethod] = func
makereadonly(old)
return oldmetamethod
end
end
return SearchTable(tbl)
end
if cachedstring then
local wasreadonly = isreadonly(rawmetatable)
if wasreadonly then
makewritable(rawmetatable)
end
rawset(rawmetatable, "__tostring", nil)
local safestring = tostring(userdata)
rawset(rawmetatable, "__tostring", cachedstring)
if wasreadonly then
makereadonly(rawmetatable)
end
return safestring
end
end
return tostring(userdata)
end
function ErrorPrompt(Message,state)
if getrenv then
local ErrorPrompt =
getrenv().require(CoreGui:WaitForChild("RobloxGui"):WaitForChild("Modules"):WaitFor
Child("ErrorPrompt")) -- File can be located in your roblox folder (C:\Users\
%Username%\AppData\Local\Roblox\Versions\whateverversionitis\ExtraContent\scripts\
CoreScripts\Modules)
local prompt = [Link]("Default",{HideErrorCode = true})
local ErrorStoarge = Create("ScreenGui",{Parent = CoreGui,ResetOnSpawn =
false})
local thread = state and running()
prompt:setParent(ErrorStoarge)
prompt:setErrorTitle("Simple Spy V3 Error")
prompt:updateButtons({{
Text = "Proceed",
Callback = function()
prompt:_close()
ErrorStoarge:Destroy()
if thread then
resume(thread)
end
end,
Primary = true
}}, 'Default')
prompt:_open(Message)
if thread then
yield(thread)
end
else
warn(Message)
end
end
-------------------------------------------------------------------------------
-- autoblock variables
local history = {}
local excluding = {}
local connections = {}
local DecompiledScripts = {}
local generation = {}
local running_threads = {}
local originalnamecall
xpcall(function()
if isfile and readfile and isfolder and makefolder then
local cachedconfigs = isfile("SimpleSpy//[Link]") and
jsond(readfile("SimpleSpy//[Link]"))
if cachedconfigs then
for i,v in next, realconfigs do
if cachedconfigs[i] == nil then
cachedconfigs[i] = v
end
end
realconfigs = cachedconfigs
end
configsmetatable.__newindex = function(self,index,newindex)
realconfigs[index] = newindex
writefile("SimpleSpy//[Link]",jsone(realconfigs))
end
else
configsmetatable.__newindex = function(self,index,newindex)
realconfigs[index] = newindex
end
end
end,function(err)
ErrorPrompt(("An error has occured: (%s)"):format(err))
end)
--- Prevents remote spam from causing lag (clears logs after
`getgenv().SIMPLESPYCONFIG_MaxRemotes` or 500 remotes)
function clean()
local max = getgenv().SIMPLESPYCONFIG_MaxRemotes
if not typeof(max) == "number" and [Link](max) ~= max then
max = 500
end
if #remoteLogs > max then
for i = 100, #remoteLogs do
local v = remoteLogs[i]
if typeof(v[1]) == "RBXScriptConnection" then
v[1]:Disconnect()
end
if typeof(v[2]) == "Instance" then
v[2]:Destroy()
end
end
local newLogs = {}
for i = 1, 100 do
[Link](newLogs, remoteLogs[i])
end
remoteLogs = newLogs
end
end
--- Reconnects bringBackOnResize if the current viewport changes and also connects
it initially
function connectResize()
if not [Link] then
workspace:GetPropertyChangedSignal("CurrentCamera"):Wait()
end
local lastCam =
[Link]:GetPropertyChangedSignal("ViewportSize"):Connect(bringBackO
nResize)
workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
lastCam:Disconnect()
if typeof(lastCam) == 'Connection' then
lastCam:Disconnect()
end
lastCam =
[Link]:GetPropertyChangedSignal("ViewportSize"):Connect(bringBackO
nResize)
end)
end
--- Brings gui back if it gets lost offscreen (connected to the camera viewport
changing)
function bringBackOnResize()
validateSize()
if sideClosed then
minimizeSize()
else
maximizeSize()
end
local currentX = [Link].X
local currentY = [Link].Y
local viewportSize = [Link]
if (currentX < 0) or (currentX > (viewportSize.X - (sideClosed and 131 or
[Link].X))) then
if currentX < 0 then
currentX = 0
else
currentX = viewportSize.X - (sideClosed and 131 or
[Link].X)
end
end
if (currentY < 0) or (currentY > (viewportSize.Y - (closed and 19 or
[Link].Y) - 36)) then
if currentY < 0 then
currentY = 0
else
currentY = viewportSize.Y - (closed and 19 or
[Link].Y) - 36
end
end
[Link](TweenService, Background, [Link](0.1), {Position =
[Link](0, currentX, 0, currentY)}):Play()
end
--- Fades out the table of elements (and makes them invisible), returns a function
to make them visible again
function fadeOut(elements)
local data = {}
for _, v in next, elements do
if typeof(v) == "Instance" and v:IsA("GuiObject") and [Link] then
spawn(function()
data[v] = {
BackgroundTransparency = [Link]
}
TweenService:Create(v, [Link](0.5), {BackgroundTransparency
= 1}):Play()
if v:IsA("TextBox") or v:IsA("TextButton") or v:IsA("TextLabel")
then
data[v].TextTransparency = [Link]
TweenService:Create(v, [Link](0.5), {TextTransparency =
1}):Play()
elseif v:IsA("ImageButton") or v:IsA("ImageLabel") then
data[v].ImageTransparency = [Link]
TweenService:Create(v, [Link](0.5), {ImageTransparency =
1}):Play()
end
delay(0.5,function()
[Link] = false
for i, x in next, data[v] do
v[i] = x
end
data[v] = true
end)
end)
end
end
return function()
for i, _ in next, data do
spawn(function()
local properties = {
BackgroundTransparency = [Link]
}
[Link] = 1
TweenService:Create(i, [Link](0.5), {BackgroundTransparency
= [Link]}):Play()
if i:IsA("TextBox") or i:IsA("TextButton") or i:IsA("TextLabel")
then
[Link] = [Link]
[Link] = 1
TweenService:Create(i, [Link](0.5), {TextTransparency =
[Link]}):Play()
elseif i:IsA("ImageButton") or i:IsA("ImageLabel") then
[Link] = [Link]
[Link] = 1
TweenService:Create(i, [Link](0.5), {ImageTransparency =
[Link]}):Play()
end
[Link] = true
end)
end
end
end
--- Expands and minimizes the gui (closed is the toggle boolean)
function toggleMinimize(override)
if mainClosing and not override or maximized then
return
end
mainClosing = true
closed = not closed
if closed then
if not sideClosed then
toggleSideTray(true)
end
[Link] = true
remotesFadeIn = fadeOut(LeftPanel:GetDescendants())
TweenService:Create(LeftPanel, [Link](0.5), {Size = [Link](0,
131, 0, 0)}):Play()
wait(0.5)
else
TweenService:Create(LeftPanel, [Link](0.5), {Size = [Link](0,
131, 0, 249)}):Play()
wait(0.5)
if remotesFadeIn then
remotesFadeIn()
remotesFadeIn = nil
end
bringBackOnResize()
end
mainClosing = false
end
--- Expands and minimizes the sidebar (sideClosed is the toggle boolean)
function toggleSideTray(override)
if sideClosing and not override or maximized then
return
end
sideClosing = true
sideClosed = not sideClosed
if sideClosed then
rightFadeIn = fadeOut(RightPanel:GetDescendants())
wait(0.5)
minimizeSize(0.5)
wait(0.5)
[Link] = false
else
if closed then
toggleMinimize(true)
end
[Link] = true
maximizeSize(0.5)
wait(0.5)
if rightFadeIn then
rightFadeIn()
end
bringBackOnResize()
end
sideClosing = false
end
--- Expands code box to fit screen for more convenient viewing
function toggleMaximize()
if not sideClosed and not maximized then
maximized = true
local disable = [Link]("TextButton")
local prevSize = [Link](0, [Link].X, 0,
[Link].Y)
local prevPos = [Link](0,[Link].X, 0,
[Link].Y)
[Link] = [Link](1, 0, 1, 0)
disable.BackgroundColor3 = [Link]()
[Link] = 0
[Link] = 0
[Link] = 3
[Link] = 1
[Link] = false
[Link] = 4
[Link] = prevPos
[Link] = prevSize
TweenService:Create(CodeBox, [Link](0.5), {Size = [Link](0.5, 0,
0.5, 0), Position = [Link](0.25, 0, 0.25, 0)}):Play()
TweenService:Create(disable, [Link](0.5), {BackgroundTransparency =
0.5}):Play()
disable.MouseButton1Click:Connect(function()
if UserInputService:GetMouseLocation().Y + 36 >=
[Link].Y and UserInputService:GetMouseLocation().Y + 36 <=
[Link].Y + [Link].Y and
UserInputService:GetMouseLocation().X >= [Link].X and
UserInputService:GetMouseLocation().X <= [Link].X +
[Link].X then
return
end
TweenService:Create(CodeBox, [Link](0.5), {Size = prevSize,
Position = prevPos}):Play()
TweenService:Create(disable, [Link](0.5),
{BackgroundTransparency = 1}):Play()
wait(0.5)
disable:Destroy()
[Link] = [Link](1, 0, 0.5, 0)
[Link] = [Link](0, 0, 0, 0)
[Link] = 0
maximized = false
end)
end
end
--- Updates the canvas size to fit the current amount of function buttons
function updateFunctionCanvas()
[Link] =
[Link]([Link].X,
[Link].Y)
end
--- Updates the canvas size to fit the amount of current remotes
function updateRemoteCanvas()
[Link] = [Link]([Link].X,
[Link].Y)
end
--- Allows for toggling of the tooltip and easy setting of le description
--- @param enable boolean
--- @param text string
function makeToolTip(enable, text)
if enable and text then
if [Link] then
[Link] = false
local tooltip = connections["ToolTip"]
if tooltip then
tooltip:Disconnect()
end
end
local first = true
connections["ToolTip"] = [Link]:Connect(function()
local MousePos = UserInputService:GetMouseLocation()
local topLeft = MousePos + [Link](20, -15)
local bottomRight = topLeft + [Link]
local ViewportSize = [Link]
local ViewportSizeX = ViewportSize.X
local ViewportSizeY = ViewportSize.Y
if topLeft.X < 0 then
topLeft = [Link](0, topLeft.Y)
elseif bottomRight.X > ViewportSizeX then
topLeft = [Link](ViewportSizeX - [Link].X,
topLeft.Y)
end
if topLeft.Y < 0 then
topLeft = [Link](topLeft.X, 0)
elseif bottomRight.Y > ViewportSizeY - 35 then
topLeft = [Link](topLeft.X, ViewportSizeY -
[Link].Y - 35)
end
if topLeft.X <= MousePos.X and topLeft.Y <= MousePos.Y then
topLeft = [Link](MousePos.X - [Link].X - 2,
MousePos.Y - [Link].Y - 2)
end
if first then
[Link] = [Link](topLeft.X, topLeft.Y)
first = false
else
ToolTip:TweenPosition([Link](topLeft.X, topLeft.Y),
"Out", "Linear", 0.1)
end
end)
[Link] = text
[Link] = true
[Link] = true
return
else
if [Link] then
[Link] = false
local tooltip = connections["ToolTip"]
if tooltip then
tooltip:Disconnect()
end
end
end
end
[Link]:Connect(function()
makeToolTip(true, description())
end)
[Link]:Connect(function()
makeToolTip(false)
end)
[Link]:Connect(function()
makeToolTip(false)
end)
Button.MouseButton1Click:Connect(function(...)
logthread(running())
onClick(FunctionTemplate, ...)
end)
updateFunctionCanvas()
end
local log = {
Name = [Link],
Function = [Link] or "--Function Info is disabled",
Remote = remote,
DebugId = [Link],
metamethod = [Link],
args = [Link],
Log = RemoteTemplate,
Button = Button,
Blocked = [Link],
Source = callingscript,
returnvalue = [Link],
GenScript = "-- Generating, please wait...\n-- (If this message persists,
the remote args are likely extremely long)"
}
logs[#logs + 1] = log
local connect = Button.MouseButton1Click:Connect(function()
logthread(running())
eventSelect(RemoteTemplate)
[Link] = genScript([Link], [Link])
if blocked then
[Link] = "-- THIS REMOTE WAS PREVENTED FROM FIRING TO THE SERVER
BY SIMPLESPY\n\n" .. [Link]
end
if selected == log and RemoteTemplate then
eventSelect(RemoteTemplate)
end
end)
layoutOrderNum -= 1
[Link](remoteLogs, 1, {connect, RemoteTemplate})
clean()
updateRemoteCanvas()
end
--- Generates a script from the provided arguments (first has to be remote path)
function genScript(remote, args)
prevTables = {}
local gen = ""
if #args > 0 then
xpcall(function()
gen = v2v({args = args}) .. "\n"
end,function(err)
gen ..= "-- An error has occured:\n--"..err.."\n-- TableToString
failure! Reverting to legacy functionality (results may vary)\nlocal args = {"
xpcall(function()
for i, v in next, args do
if type(i) ~= "Instance" and type(i) ~= "userdata" then
gen = gen .. "\n [object] = "
elseif type(i) == "string" then
gen = gen .. '\n ["' .. i .. '"] = '
elseif type(i) == "userdata" and typeof(i) ~= "Instance" then
gen = gen .. "\n [" .. [Link]("nil --[[%s]]",
typeof(v)) .. ")] = "
elseif type(i) == "userdata" then
gen = gen .. "\n [game." .. i:GetFullName() .. ")] = "
end
if type(v) ~= "Instance" and type(v) ~= "userdata" then
gen = gen .. "object"
elseif type(v) == "string" then
gen = gen .. '"' .. v .. '"'
elseif type(v) == "userdata" and typeof(v) ~= "Instance" then
gen = gen .. [Link]("nil --[[%s]]", typeof(v))
elseif type(v) == "userdata" then
gen = gen .. "game." .. v:GetFullName()
end
end
gen ..= "\n}\n\n"
end,function()
gen ..= "}\n-- Legacy tableToString failure! Unable to decompile."
end)
end)
if not remote:IsDescendantOf(game) and not getnilrequired then
gen = "function getNil(name,class) for _,v in next, getnilinstances()do
if [Link]==class and [Link]==name then return v;end end end\n\n" .. gen
end
if remote:IsA("RemoteEvent") then
gen ..= v2s(remote) .. ":FireServer(unpack(args))"
elseif remote:IsA("RemoteFunction") then
gen = gen .. v2s(remote) .. ":InvokeServer(unpack(args))"
end
else
if remote:IsA("RemoteEvent") then
gen ..= v2s(remote) .. ":FireServer()"
elseif remote:IsA("RemoteFunction") then
gen ..= v2s(remote) .. ":InvokeServer()"
end
end
prevTables = {}
return gen
end
--- value-to-string: value, string (out), level (indentation), parent table, var
name, is from tovar
local CustomGeneration = {
Vector3 = (function()
local temp = {}
for i,v in Vector3 do
if type(v) == "vector" then
temp[v] = `Vector3.{i}`
end
end
return temp
end)(),
Vector2 = (function()
local temp = {}
for i,v in Vector2 do
if type(v) == "userdata" then
temp[v] = `Vector2.{i}`
end
end
return temp
end)(),
CFrame = {
[[Link]] = "[Link]"
}
}
local number_table = {
["inf"] = "[Link]",
["-inf"] = "-[Link]",
["nan"] = "0/0"
}
local ufunctions
ufunctions = {
TweenInfo = function(u)
return `[Link]({[Link]}, {[Link]}, {[Link]},
{[Link]}, {[Link]}, {[Link]})`
end,
Ray = function(u)
local Vector3tostring = ufunctions["Vector3"]
return `[Link]({Vector3tostring([Link])},
{Vector3tostring([Link])})`
end,
BrickColor = function(u)
return `[Link]({[Link]})`
end,
NumberRange = function(u)
return `[Link]({[Link]}, {[Link]})`
end,
Region3 = function(u)
local center = [Link]
local centersize = [Link]/2
local Vector3tostring = ufunctions["Vector3"]
return `[Link]({Vector3tostring(center-centersize)},
{Vector3tostring(center+centersize)})`
end,
Faces = function(u)
local faces = {}
if [Link] then
[Link](faces, "Top")
end
if [Link] then
[Link](faces, "[Link]")
end
if [Link] then
[Link](faces, "[Link]")
end
if [Link] then
[Link](faces, "[Link]")
end
if [Link] then
[Link](faces, "[Link]")
end
if [Link] then
[Link](faces, "[Link]")
end
return `[Link]({[Link](faces, ", ")})`
end,
EnumItem = function(u)
return tostring(u)
end,
Enums = function(u)
return "Enum"
end,
Enum = function(u)
return `Enum.{u}`
end,
Vector3 = function(u)
return CustomGeneration.Vector3[u] or `[Link]({u})`
end,
Vector2 = function(u)
return CustomGeneration.Vector2[u] or `[Link]({u})`
end,
CFrame = function(u)
return [Link][u] or
`[Link]({[Link]({u:GetComponents()},", ")})`
end,
PathWaypoint = function(u)
return `[Link]({ufunctions["Vector3"]([Link])}, {[Link]},
"{[Link]}")`
end,
UDim = function(u)
return `[Link]({u})`
end,
UDim2 = function(u)
return `[Link]({u})`
end,
Rect = function(u)
local Vector2tostring = ufunctions["Vector2"]
return `[Link]({Vector2tostring([Link])}, {Vector2tostring([Link])})`
end,
Color3 = function(u)
return `[Link]({u.R}, {u.G}, {u.B})`
end,
RBXScriptSignal = function(u) -- The server doesnt recive this
return "RBXScriptSignal --[[RBXScriptSignal's are not supported]]"
end,
RBXScriptConnection = function(u) -- The server doesnt recive this
return "RBXScriptConnection --[[RBXScriptConnection's are not supported]]"
end,
}
local typeofv2sfunctions = {
number = function(v)
local number = tostring(v)
return number_table[number] or number
end,
boolean = function(v)
return tostring(v)
end,
string = function(v,l)
return formatstr(v, l)
end,
["function"] = function(v) -- The server doesnt recive this
return f2s(v)
end,
table = function(v, l, p, n, vtv, i, pt, path, tables, tI)
return t2s(v, l, p, n, vtv, i, pt, path, tables, tI)
end,
Instance = function(v)
local DebugId = OldDebugId(v)
return i2p(v,generation[DebugId])
end,
userdata = function(v) -- The server doesnt recive this
if [Link] then
if getrawmetatable(v) then
return "newproxy(true)"
end
return "newproxy(false)"
end
return "newproxy(true)"
end
}
local typev2sfunctions = {
userdata = function(v,vtypeof)
if ufunctions[vtypeof] then
return ufunctions[vtypeof](v)
end
return `{vtypeof}({rawtostring(v)}) --[[Generation Failure]]`
end,
vector = ufunctions["Vector3"]
}
if vtypeoffunc then
return vtypeoffunc(v, l, p, n, vtv, i, pt, path, tables, tI)
elseif vtypefunc then
return vtypefunc(v,vtypeof)
end
return `{vtypeof}({rawtostring(v)}) --[[Generation Failure]]`
end
--- value-to-variable
--- @param t any
function v2v(t)
topstr = ""
bottomstr = ""
getnilrequired = false
local ret = ""
local count = 1
for i, v in next, t do
if type(i) == "string" and i:match("^[%a_]+[%w_]*$") then
ret = ret .. "local " .. i .. " = " .. v2s(v, nil, nil, i, true) .. "\
n"
elseif rawtostring(i):match("^[%a_]+[%w_]*$") then
ret = ret .. "local " .. lower(rawtostring(i)) .. "_" ..
rawtostring(count) .. " = " .. v2s(v, nil, nil, lower(rawtostring(i)) .. "_" ..
rawtostring(count), true) .. "\n"
else
ret = ret .. "local " .. type(v) .. "_" .. rawtostring(count) .. " =
" .. v2s(v, nil, nil, type(v) .. "_" .. rawtostring(count), true) .. "\n"
end
count = count + 1
end
if getnilrequired then
topstr = "function getNil(name,class) for _,v in next, getnilinstances() do
if [Link]==class and [Link]==name then return v;end end end\n" .. topstr
end
if #topstr > 0 then
ret = topstr .. "\n" .. ret
end
if #bottomstr > 0 then
ret = ret .. bottomstr
end
return ret
end
end
--- table-to-string
--- @param t table
--- @param l number
--- @param p table
--- @param n string
--- @param vtv boolean
--- @param i any
--- @param pt table
--- @param path string
--- @param tables table
--- @param tI table
function t2s(t, l, p, n, vtv, i, pt, path, tables, tI)
local globalIndex = [Link](getgenv(), t) -- checks if table is a global
if type(globalIndex) == "string" then
return globalIndex
end
if not tI then
tI = {0}
end
if not path then -- sets path to empty string (so it doesn't have to manually
provided every time)
path = ""
end
if not l then -- sets the level to 0 (for indentation) and tables for logging
tables it already serialized
l = 0
tables = {}
end
if not p then -- p is the previous table but doesn't really matter if it's the
first
p = t
end
for _, v in next, tables do -- checks if the current table has been serialized
before
if n and rawequal(v, t) then
bottomstr = bottomstr .. "\n" .. rawtostring(n) .. rawtostring(path) ..
" = " .. rawtostring(n) .. rawtostring(({v2p(v, p)})[2])
return "{} --[[DUPLICATE]]"
end
end
[Link](tables, t) -- logs table to past tables
local s = "{" -- start of serialization
local size = 0
l += indent -- set indentation level
for k, v in next, t do -- iterates over table
size = size + 1 -- changes size for max limit
if size > (getgenv().SimpleSpyMaxTableSize or 1000) then
s = s .. "\n" .. [Link](" ", l) .. "-- MAXIMUM TABLE SIZE REACHED,
CHANGE 'getgenv().SimpleSpyMaxTableSize' TO ADJUST MAXIMUM SIZE "
break
end
if rawequal(k, t) then -- checks if the table being iterated over is being
used as an index within itself (yay, lua)
bottomstr ..= `\n{n}{path}[{n}{path}] = {(rawequal(v,k) and `{n}{path}`
or v2s(v, l, p, n, vtv, k, t, `{path}[{n}{path}]`, tables))}`
--bottomstr = bottomstr .. "\n" .. rawtostring(n) ..
rawtostring(path) .. "[" .. rawtostring(n) .. rawtostring(path) .. "]" .. " = " ..
(rawequal(v, k) and rawtostring(n) .. rawtostring(path) or v2s(v, l, p, n, vtv, k,
t, path .. "[" .. rawtostring(n) .. rawtostring(path) .. "]", tables))
size -= 1
continue
end
local currentPath = "" -- initializes the path of 'v' within 't'
if type(k) == "string" and k:match("^[%a_]+[%w_]*$") then -- cleanly
handles table path generation (for the first half)
currentPath = "." .. k
else
currentPath = "[" .. v2s(k, l, p, n, vtv, k, t, path .. currentPath,
tables, tI) .. "]"
end
if size % 100 == 0 then
scheduleWait()
end
-- actually serializes the member of the table
s = s .. "\n" .. [Link](" ", l) .. "[" .. v2s(k, l, p, n, vtv, k, t,
path .. currentPath, tables, tI) .. "] = " .. v2s(v, l, p, n, vtv, k, t, path ..
currentPath, tables, tI) .. ","
end
if #s > 1 then -- removes the last comma because it looks nicer (no way to tell
if it's done 'till it's done so...)
s = s:sub(1, #s - 1)
end
if size > 0 then -- cleanly indents the last curly bracket
s = s .. "\n" .. [Link](" ", l - indent)
end
return s .. "}"
end
--- function-to-string
function f2s(f)
for k, x in next, getgenv() do
local isgucci, gpath
if rawequal(x, f) then
isgucci, gpath = true, ""
elseif type(x) == "table" then
isgucci, gpath = v2p(f, x)
end
if isgucci and type(k) ~= "function" then
if type(k) == "string" and k:match("^[%a_]+[%w_]*$") then
return k .. gpath
else
return "getgenv()[" .. v2s(k) .. "]" .. gpath
end
end
end
if [Link] then
local funcname = info(f,"n")
--- instance-to-path
--- @param i userdata
function i2p(i,customgen)
if customgen then
return customgen
end
local player = getplayer(i)
local parent = i
local out = ""
if parent == nil then
return "nil"
elseif player then
while true do
if parent and parent == [Link] then
if player == [Link] then
return 'game:GetService("Players").[Link]' ..
out
else
return i2p(player) .. ".Character" .. out
end
else
if [Link]:match("[%a_]+[%w+]*") ~= [Link] then
out = ':FindFirstChild(' .. formatstr([Link]) .. ')' ..
out
else
out = "." .. [Link] .. out
end
end
[Link]()
parent = [Link]
end
elseif parent ~= game then
while true do
if parent and [Link] == game then
if SafeGetService([Link]) then
if lower([Link]) == "workspace" then
return `workspace{out}`
else
return 'game:GetService("' .. [Link] .. '")' ..
out
end
else
if [Link]:match("[%a_]+[%w_]*") then
return "game." .. [Link] .. out
else
return 'game:FindFirstChild(' .. formatstr([Link]) ..
')' .. out
end
end
elseif not [Link] then
getnilrequired = true
return 'getNil(' .. formatstr([Link]) .. ', "' ..
[Link] .. '")' .. out
else
if [Link]:match("[%a_]+[%w_]*") ~= [Link] then
out = ':WaitForChild(' .. formatstr([Link]) .. ')' .. out
else
out = ':WaitForChild("' .. [Link] .. '")'..out
end
end
if i:IsDescendantOf([Link]) then
return 'game:GetService("Players").LocalPlayer'..out
end
parent = [Link]
[Link]()
end
else
return "game"
end
end
--- Adds \'s to the text as a replacement to whitespace chars and other things
because [Link] can't yayeet
local specialstrings = {
["\n"] = function(thread,index)
resume(thread,index,"\\n")
end,
["\t"] = function(thread,index)
resume(thread,index,"\\t")
end,
["\\"] = function(thread,index)
resume(thread,index,"\\\\")
end,
['"'] = function(thread,index)
resume(thread,index,"\\\"")
end
}
if byte(char) then
timeout += 1
local c = create(coroutineFunc)
[Link](coroutines, c)
local specialfunc = specialstrings[char]
if specialfunc then
specialfunc(c,i)
i += 1
elseif byte(char) > 126 or byte(char) < 32 then
resume(c, i, "\\" .. byte(char))
-- s = s:sub(0, i - 1) .. "\\" .. byte(char) .. s:sub(i + 1, -1)
i += #rawtostring(byte(char))
end
if i >= n * 100 then
local extra = [Link]('" ..\n%s"', [Link](" ",
indentation + indent))
s = s:sub(0, i) .. extra .. s:sub(i + 1, -1)
i += #extra
n += 1
end
end
until char == "" or i > (getgenv().SimpleSpyMaxStringSize or 10000)
while not isFinished(coroutines) do
[Link]:Wait()
end
clear(coroutines)
if i > (getgenv().SimpleSpyMaxStringSize or 10000) then
s = [Link](s, 0, getgenv().SimpleSpyMaxStringSize or 10000)
return s, true
end
return s, false
end
--- finds script from 'src' from getinfo, returns nil if not found
--- @param src string
function getScriptFromSrc(src)
local realPath
local runningTest
--- @type number
local s, e
local match = false
if src:sub(1, 1) == "=" then
realPath = game
s = 2
else
runningTest = src:sub(2, e and e - 1 or -1)
for _, v in next, getnilinstances() do
if [Link] == runningTest then
realPath = v
break
end
end
s = #runningTest + 1
end
if realPath then
e = src:sub(s, -1):find("%.")
local i = 0
repeat
i += 1
if not e then
runningTest = src:sub(s, -1)
local test = [Link](realPath, runningTest)
if test then
realPath = test
end
match = true
else
runningTest = src:sub(s, e)
local test = [Link](realPath, runningTest)
local yeOld = e
if test then
realPath = test
s = e + 2
e = src:sub(e + 2, -1):find("%.")
e = e and e + yeOld or e
else
e = src:sub(e + 2, -1):find("%.")
e = e and e + yeOld or e
end
end
until match or i >= 50
end
return realPath
end
--- schedules the provided function (and calls it with any args after)
--- yields the current thread until the scheduler gives the ok
function scheduleWait()
local thread = running()
schedule(function()
resume(thread)
end)
yield()
end
--- the big (well tbh small now) boi task scheduler himself, handles p much
anything as quicc as possible
local function taskscheduler()
if not toggle then
scheduled = {}
return
end
if #scheduled > SIMPLESPYCONFIG_MaxRemotes + 100 then
[Link](scheduled, #scheduled)
end
if #scheduled > 0 then
local currentf = scheduled[1]
[Link](scheduled, 1)
if type(currentf) == "table" and type(currentf[1]) == "function" then
pcall(unpack(currentf))
end
end
end
function remoteHandler(data)
if [Link] then
local id = [Link]
if excluding[id] then
return
end
if not history[id] then
history[id] = {badOccurances = 0, lastCall = tick()}
end
if tick() - history[id].lastCall < 1 then
history[id].badOccurances += 1
return
else
history[id].badOccurances = 0
end
if history[id].badOccurances > 3 then
excluding[id] = true
return
end
history[id].lastCall = tick()
end
if [Link] then
[Link] = info(2,"f")
local calling = getcallingscript()
[Link] = calling and cloneref(calling) or nil
end
schedule(remoteHandler,data)
spawn(function()
setnamecallmethod(method)
returndata = originalnamecall(unpack(returnargs))
[Link] = returndata
if ThreadIsNotDead(thread) then
resume(thread)
end
end)
yield()
if not blockcheck then
return returndata
end
end]]
end
if blockcheck then return end
end
end
return originalfunction(...)
end
if [Link] then
[Link] = info(2,"f")
local calling = getcallingscript()
[Link] = calling and cloneref(calling) or nil
end
schedule(remoteHandler,data)
spawn(function()
setnamecallmethod(method)
returndata = originalnamecall(unpack(returnargs))
[Link] = returndata
if ThreadIsNotDead(thread) then
resume(thread)
end
end)
yield()
if not blockcheck then
return returndata
end
end]]
end
if blockcheck then return end
end
end
end
return originalnamecall(...)
end)
--- Toggles between the two remotespy methods (hookfunction currently = disabled)
function toggleSpyMethod()
toggleSpy()
toggle = not toggle
end
-- main
if not getgenv().SimpleSpyExecuted then
local succeeded,err = pcall(function()
if not RunService:IsClient() then
error("SimpleSpy cannot run on the server!")
end
getgenv().SimpleSpyShutdown = shutdown
onToggleButtonClick()
if not hookmetamethod then
ErrorPrompt("Simple Spy V3 will not function to it's fullest capablity
due to your executor not supporting hookmetamethod.",true)
end
codebox = [Link](CodeBox)
logthread(spawn(function()
local suc,err =
pcall([Link],game,"[Link]
[Link]")
codebox:setRaw((suc and err) or "")
end))
getgenv().SimpleSpy = SimpleSpy
getgenv().getNil = function(name,class)
for _,v in next, getnilinstances() do
if [Link] == class and [Link] == name then
return v;
end
end
end
[Link]:Connect(function(...)
mouseInGui = true
mouseEntered()
end)
[Link]:Connect(function(...)
mouseInGui = false
mouseEntered()
end)
TextLabel:GetPropertyChangedSignal("Text"):Connect(scaleToolTip)
-- [Link]:Connect(onBarInput)
MinimizeButton.MouseButton1Click:Connect(toggleMinimize)
MaximizeButton.MouseButton1Click:Connect(toggleSideTray)
Simple.MouseButton1Click:Connect(onToggleButtonClick)
[Link]:Connect(onXButtonHover)
[Link]:Connect(onXButtonUnhover)
[Link]:Connect(onToggleButtonHover)
[Link]:Connect(onToggleButtonUnhover)
CloseButton.MouseButton1Click:Connect(shutdown)
[Link](connections,
[Link]:Connect(backgroundUserInput))
connectResize()
[Link] = true
logthread(spawn(function()
delay(1,onToggleButtonUnhover)
end))
schedulerconnect = [Link]:Connect(taskscheduler)
bringBackOnResize()
[Link] = (gethui and gethui()) or (syn and syn.protect_gui and
syn.protect_gui(SimpleSpy3)) or CoreGui
logthread(spawn(function()
local lp = [Link] or
Players:GetPropertyChangedSignal("LocalPlayer"):Wait() or [Link]
generation = {
[OldDebugId(lp)] = 'game:GetService("Players").LocalPlayer',
[OldDebugId(lp:GetMouse())] =
'game:GetService("Players").LocalPlayer:GetMouse',
[OldDebugId(game)] = "game",
[OldDebugId(workspace)] = "workspace"
}
end))
end)
if succeeded then
getgenv().SimpleSpyExecuted = true
else
shutdown()
ErrorPrompt("An error has occured:\n"..rawtostring(err))
return
end
else
SimpleSpy3:Destroy()
return
end
----- ADD ONS ----- (easily add or remove additonal functionality to the
RemoteSpy!)
--[[
Some helpful things:
- add your function in here, and create buttons for them through the
'newButton' function
- the first argument provided is the TextButton the player clicks to run
the function
- generated scripts are generated when the namecall is initially fired and
saved in remoteFrame objects
- blacklisted remotes will be ignored directly in namecall (less lag)
- the properties of a 'remoteFrame' object:
{
Name: (string) The name of the Remote
GenScript: (string) The generated script that appears in the
codebox (generated when namecall fired)
Source: (Instance (LocalScript)) The script that fired/invoked the
remote
Remote: (Instance (RemoteEvent) | Instance (RemoteFunction)) The
remote that was fired/invoked
Log: (Instance (TextButton)) The button being used for the remote
(same as '[Link]')
}
- globals list: (contact @exx#9394 for more information or if you have
suggestions for more to be added)
- closed: (boolean) whether or not the GUI is currently minimized
- logs: (table[remoteFrame]) full of remoteFrame objects (properties
listed above)
- selected: (remoteFrame) the currently selected remoteFrame
(properties listed above)
- blacklist: (string[] | Instance[] (RemoteEvent) | Instance[]
(RemoteFunction)) an array of blacklisted names and remotes
- codebox: (Instance (TextBox)) the textbox that holds all the code-
cleared often
]]
-- Copies the contents of the codebox
newButton(
"Copy Code",
function() return "Click to copy code" end,
function()
setclipboard(codebox:getString())
[Link] = "Copied successfully!"
end
)
--- Gets the calling script (not super reliable but w/e)
newButton(
"Get Script",
function() return "Click to copy calling script to clipboard\nWARNING: Not
super reliable, nil == could not find" end,
function()
if selected then
if not [Link] then
[Link] = rawget(getfenv([Link]),"script")
end
setclipboard(v2s([Link]))
[Link] = "Done!"
end
end
)
--- Decompiles the script that fired the remote and puts it in the code box
newButton("Function Info",function() return "Click to view calling function
information" end,
function()
local func = selected and [Link]
if func then
local typeoffunc = typeof(func)
info = {
info = getinfo(func),
constants = lclosure and deepclone(getconstants(func)) or "N/A --
Lua Closure expected got C Closure",
upvalues = deepclone(getupvalues(func)),
script = {
SourceScript = SourceScript or 'nil',
CallingScript = CallingScript or 'nil'
}
}
if [Link] then
local Remote = [Link]
info["advancedinfo"] = {
Metamethod = [Link],
DebugId = {
SourceScriptDebugId = SourceScript and typeof(SourceScript)
== "Instance" and OldDebugId(SourceScript) or "N/A",
CallingScriptDebugId = CallingScript and
typeof(SourceScript) == "Instance" and OldDebugId(CallingScript) or "N/A",
RemoteDebugId = OldDebugId(Remote)
},
Protos = lclosure and getprotos(func) or "N/A --Lua Closure
expected got C Closure"
}
if Remote:IsA("RemoteFunction") then
info["advancedinfo"]["OnClientInvoke"] = getcallbackmember and
(getcallbackmember(Remote,"OnClientInvoke") or "N/A") or "N/A --Missing function
getcallbackmember"
elseif getconnections then
info["advancedinfo"]["OnClientEvents"] = {}
--- Excludes all Remotes that share the same name as the [Link] remote from
the RemoteSpy
newButton(
"Exclude (n)",
function() return "Click to exclude all remotes with this name.\nExcluding a
remote makes SimpleSpy ignore it, but it will continue to be usable." end,
function()
if selected then
blacklist[[Link]] = true
[Link] = "Excluded!"
end
end
)
--- Prevents the [Link] Remote from firing the server (still logged)
newButton(
"Block (i)",
function() return "Click to stop this remote from firing.\nBlocking a remote
won't remove it from SimpleSpy logs, but it will not continue to fire the server."
end,
function()
if selected then
blocklist[OldDebugId([Link])] = true
[Link] = "Excluded!"
end
end
)
--- Prevents all remotes from firing that share the same name as the [Link]
remote from the RemoteSpy (still logged)
newButton("Block (n)",function()
return "Click to stop remotes with this name from firing.\nBlocking a remote
won't remove it from SimpleSpy logs, but it will not continue to fire the server."
end,
function()
if selected then
blocklist[[Link]] = true
[Link] = "Excluded!"
end
end
)
xpcall(function()
local decompiledsource = decompile(Source):gsub("--
Decompiled with the Synapse X Luau decompiler.","")
local Sourcev2s = v2s(Source)
if (decompiledsource):find("script") and Sourcev2s then
DecompiledScripts[Source] = ("local script = %s\n
%s"):format(Sourcev2s,decompiledsource)
end
end,function(err)
return codebox:setRaw(("--[[\nAn error has occured\n%s\
n]]"):format(err))
end)
end
codebox:setRaw(DecompiledScripts[Source] or "--No Source Found")
[Link] = "Done!"
else
[Link] = "Source not found!"
end
else
[Link] = "Missing function (decompile)"
end
end
)
--[[newButton(
"returnvalue",
function() return "Get a Remote's return data" end,
function()
if selected then
local Remote = [Link]
if Remote and Remote:IsA("RemoteFunction") then
if [Link] and [Link] then
return codebox:setRaw(v2s([Link]))
end
return codebox:setRaw("No data was returned")
else
codebox:setRaw("RemoteFunction expected got "..(Remote and
[Link]))
end
end
end
)]]
newButton(
"Disable Info",
function() return [Link]("[%s] Toggle function info (because it can
cause lag in some games)", [Link] and "ENABLED" or "DISABLED") end,
function()
[Link] = not [Link]
[Link] = [Link]("[%s] Toggle function info (because it can
cause lag in some games)", [Link] and "ENABLED" or "DISABLED")
end
)
newButton(
"Autoblock",
function() return [Link]("[%s] [BETA] Intelligently detects and excludes
spammy remote calls from logs", [Link] and "ENABLED" or "DISABLED") end,
function()
[Link] = not [Link]
[Link] = [Link]("[%s] [BETA] Intelligently detects and
excludes spammy remote calls from logs", [Link] and "ENABLED" or
"DISABLED")
history = {}
excluding = {}
end
)
newButton("Logcheckcaller",function()
return ("[%s] Log remotes fired by the client"):format([Link]
and "ENABLED" or "DISABLED")
end,
function()
[Link] = not [Link]
[Link] = ("[%s] Log remotes fired by the
client"):format([Link] and "ENABLED" or "DISABLED")
end)
--[[newButton("Log returnvalues",function()
return ("[BETA] [%s] Log RemoteFunction's return
values"):format([Link] and "ENABLED" or "DISABLED")
end,
function()
[Link] = not [Link]
[Link] = ("[BETA] [%s] Log RemoteFunction's return
values"):format([Link] and "ENABLED" or "DISABLED")
end)]]
newButton("Advanced Info",function()
return ("[%s] Display more remoteinfo"):format([Link] and
"ENABLED" or "DISABLED")
end,
function()
[Link] = not [Link]
[Link] = ("[%s] Display more remoteinfo"):format([Link]
and "ENABLED" or "DISABLED")
end)
newButton("Join Discord",function()
return "Joins The Simple Spy Discord"
end,
function()
setclipboard("[Link]
[Link] = "Copied invite to your clipboard"
if request then
request({Url = '[Link] = 'POST',Headers =
{['Content-Type'] = 'application/json', Origin = '[Link] =
http:JSONEncode({cmd = 'INVITE_BROWSER',nonce = http:GenerateGUID(false),args =
{code = 'AWS6ez9'}})})
end
end)
if [Link] then
newButton("Load SSV2.2",function()
return "Load's Simple Spy V2.2"
end,
function()
loadstring(game:HttpGet("[Link]
SimpleSpySource/master/[Link]"))()
end)
newButton("Load SSV3",function()
return "Load's Simple Spy V3"
end,
function()
loadstring(game:HttpGet("[Link]
main/[Link]"))()
end)
local SuperSecretFolder = Create("Folder",{Parent = SimpleSpy3})
newButton("SUPER SECRET BUTTON",function()
return "You dont need a discription you already know what it does"
end,
function()
SuperSecretFolder:ClearAllChildren()
local random = listfiles("Music")
local NotSound = Create("Sound",{Parent = SuperSecretFolder,Looped =
false,Volume = [Link](1,5),SoundId =
getsynasset(random[[Link](1,#random)])})
NotSound:Play()
end)
end