Simple Spy V2.2.lua
Simple Spy V2.2.lua
Credits:
exx - basically everything
Frosty - GUI to Lua
]]
-- Instances:
--Properties:
SimpleSpy2.Name = "SimpleSpy2"
SimpleSpy2.ResetOnSpawn = false
Background.Name = "Background"
Background.Parent = SimpleSpy2
Background.BackgroundColor3 = Color3.new(1, 1, 1)
Background.BackgroundTransparency = 1
Background.Position = UDim2.new(0, 500, 0, 200)
Background.Size = UDim2.new(0, 450, 0, 268)
LeftPanel.Name = "LeftPanel"
LeftPanel.Parent = Background
LeftPanel.BackgroundColor3 = Color3.new(0.207843, 0.203922, 0.215686)
LeftPanel.BorderSizePixel = 0
LeftPanel.Position = UDim2.new(0, 0, 0, 19)
LeftPanel.Size = UDim2.new(0, 131, 0, 249)
LogList.Name = "LogList"
LogList.Parent = LeftPanel
LogList.Active = true
LogList.BackgroundColor3 = Color3.new(1, 1, 1)
LogList.BackgroundTransparency = 1
LogList.BorderSizePixel = 0
LogList.Position = UDim2.new(0, 0, 0, 9)
LogList.Size = UDim2.new(0, 131, 0, 232)
LogList.CanvasSize = UDim2.new(0, 0, 0, 0)
LogList.ScrollBarThickness = 4
UIListLayout.Parent = LogList
UIListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
UIListLayout.SortOrder = Enum.SortOrder.LayoutOrder
RemoteTemplate.Name = "RemoteTemplate"
RemoteTemplate.Parent = LogList
RemoteTemplate.BackgroundColor3 = Color3.new(1, 1, 1)
RemoteTemplate.BackgroundTransparency = 1
RemoteTemplate.Size = UDim2.new(0, 117, 0, 27)
ColorBar.Name = "ColorBar"
ColorBar.Parent = RemoteTemplate
ColorBar.BackgroundColor3 = Color3.new(1, 0.94902, 0)
ColorBar.BorderSizePixel = 0
ColorBar.Position = UDim2.new(0, 0, 0, 1)
ColorBar.Size = UDim2.new(0, 7, 0, 18)
ColorBar.ZIndex = 2
Text.Name = "Text"
Text.Parent = RemoteTemplate
Text.BackgroundColor3 = Color3.new(1, 1, 1)
Text.BackgroundTransparency = 1
Text.Position = UDim2.new(0, 12, 0, 1)
Text.Size = UDim2.new(0, 105, 0, 18)
Text.ZIndex = 2
Text.Font = Enum.Font.SourceSans
Text.Text = "TEXT"
Text.TextColor3 = Color3.new(1, 1, 1)
Text.TextSize = 14
Text.TextXAlignment = Enum.TextXAlignment.Left
Button.Name = "Button"
Button.Parent = RemoteTemplate
Button.BackgroundColor3 = Color3.new(0, 0, 0)
Button.BackgroundTransparency = 0.75
Button.BorderColor3 = Color3.new(1, 1, 1)
Button.Position = UDim2.new(0, 0, 0, 1)
Button.Size = UDim2.new(0, 117, 0, 18)
Button.AutoButtonColor = false
Button.Font = Enum.Font.SourceSans
Button.Text = ""
Button.TextColor3 = Color3.new(0, 0, 0)
Button.TextSize = 14
RightPanel.Name = "RightPanel"
RightPanel.Parent = Background
RightPanel.BackgroundColor3 = Color3.new(0.145098, 0.141176, 0.14902)
RightPanel.BorderSizePixel = 0
RightPanel.Position = UDim2.new(0, 131, 0, 19)
RightPanel.Size = UDim2.new(0, 319, 0, 249)
CodeBox.Name = "CodeBox"
CodeBox.Parent = RightPanel
CodeBox.BackgroundColor3 = Color3.new(0.0823529, 0.0745098, 0.0784314)
CodeBox.BorderSizePixel = 0
CodeBox.Size = UDim2.new(0, 319, 0, 119)
ScrollingFrame.Parent = RightPanel
ScrollingFrame.Active = true
ScrollingFrame.BackgroundColor3 = Color3.new(1, 1, 1)
ScrollingFrame.BackgroundTransparency = 1
ScrollingFrame.Position = UDim2.new(0, 0, 0.5, 0)
ScrollingFrame.Size = UDim2.new(1, 0, 0.5, -9)
ScrollingFrame.CanvasSize = UDim2.new(0, 0, 0, 0)
ScrollingFrame.ScrollBarThickness = 4
UIGridLayout.Parent = ScrollingFrame
UIGridLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
UIGridLayout.SortOrder = Enum.SortOrder.LayoutOrder
UIGridLayout.CellPadding = UDim2.new(0, 0, 0, 0)
UIGridLayout.CellSize = UDim2.new(0, 94, 0, 27)
FunctionTemplate.Name = "FunctionTemplate"
FunctionTemplate.Parent = ScrollingFrame
FunctionTemplate.BackgroundColor3 = Color3.new(1, 1, 1)
FunctionTemplate.BackgroundTransparency = 1
FunctionTemplate.Size = UDim2.new(0, 117, 0, 23)
ColorBar_2.Name = "ColorBar"
ColorBar_2.Parent = FunctionTemplate
ColorBar_2.BackgroundColor3 = Color3.new(1, 1, 1)
ColorBar_2.BorderSizePixel = 0
ColorBar_2.Position = UDim2.new(0, 7, 0, 10)
ColorBar_2.Size = UDim2.new(0, 7, 0, 18)
ColorBar_2.ZIndex = 3
Text_2.Name = "Text"
Text_2.Parent = FunctionTemplate
Text_2.BackgroundColor3 = Color3.new(1, 1, 1)
Text_2.BackgroundTransparency = 1
Text_2.Position = UDim2.new(0, 19, 0, 10)
Text_2.Size = UDim2.new(0, 69, 0, 18)
Text_2.ZIndex = 2
Text_2.Font = Enum.Font.SourceSans
Text_2.Text = "TEXT"
Text_2.TextColor3 = Color3.new(1, 1, 1)
Text_2.TextSize = 14
Text_2.TextStrokeColor3 = Color3.new(0.145098, 0.141176, 0.14902)
Text_2.TextXAlignment = Enum.TextXAlignment.Left
Button_2.Name = "Button"
Button_2.Parent = FunctionTemplate
Button_2.BackgroundColor3 = Color3.new(0, 0, 0)
Button_2.BackgroundTransparency = 0.69999998807907
Button_2.BorderColor3 = Color3.new(1, 1, 1)
Button_2.Position = UDim2.new(0, 7, 0, 10)
Button_2.Size = UDim2.new(0, 80, 0, 18)
Button_2.AutoButtonColor = false
Button_2.Font = Enum.Font.SourceSans
Button_2.Text = ""
Button_2.TextColor3 = Color3.new(0, 0, 0)
Button_2.TextSize = 14
TopBar.Name = "TopBar"
TopBar.Parent = Background
TopBar.BackgroundColor3 = Color3.new(0.145098, 0.141176, 0.14902)
TopBar.BorderSizePixel = 0
TopBar.Size = UDim2.new(0, 450, 0, 19)
Simple.Name = "Simple"
Simple.Parent = TopBar
Simple.BackgroundColor3 = Color3.new(1, 1, 1)
Simple.AutoButtonColor = false
Simple.BackgroundTransparency = 1
Simple.Position = UDim2.new(0, 5, 0, 0)
Simple.Size = UDim2.new(0, 57, 0, 18)
Simple.Font = Enum.Font.SourceSansBold
Simple.Text = "SimpleSpy"
Simple.TextColor3 = Color3.new(1, 1, 1)
Simple.TextSize = 14
Simple.TextXAlignment = Enum.TextXAlignment.Left
CloseButton.Name = "CloseButton"
CloseButton.Parent = TopBar
CloseButton.BackgroundColor3 = Color3.new(0.145098, 0.141176, 0.14902)
CloseButton.BorderSizePixel = 0
CloseButton.Position = UDim2.new(1, -19, 0, 0)
CloseButton.Size = UDim2.new(0, 19, 0, 19)
CloseButton.Font = Enum.Font.SourceSans
CloseButton.Text = ""
CloseButton.TextColor3 = Color3.new(0, 0, 0)
CloseButton.TextSize = 14
ImageLabel.Parent = CloseButton
ImageLabel.BackgroundColor3 = Color3.new(1, 1, 1)
ImageLabel.BackgroundTransparency = 1
ImageLabel.Position = UDim2.new(0, 5, 0, 5)
ImageLabel.Size = UDim2.new(0, 9, 0, 9)
ImageLabel.Image = "http://www.roblox.com/asset/?id=5597086202"
MaximizeButton.Name = "MaximizeButton"
MaximizeButton.Parent = TopBar
MaximizeButton.BackgroundColor3 = Color3.new(0.145098, 0.141176, 0.14902)
MaximizeButton.BorderSizePixel = 0
MaximizeButton.Position = UDim2.new(1, -38, 0, 0)
MaximizeButton.Size = UDim2.new(0, 19, 0, 19)
MaximizeButton.Font = Enum.Font.SourceSans
MaximizeButton.Text = ""
MaximizeButton.TextColor3 = Color3.new(0, 0, 0)
MaximizeButton.TextSize = 14
ImageLabel_2.Parent = MaximizeButton
ImageLabel_2.BackgroundColor3 = Color3.new(1, 1, 1)
ImageLabel_2.BackgroundTransparency = 1
ImageLabel_2.Position = UDim2.new(0, 5, 0, 5)
ImageLabel_2.Size = UDim2.new(0, 9, 0, 9)
ImageLabel_2.Image = "http://www.roblox.com/asset/?id=5597108117"
MinimizeButton.Name = "MinimizeButton"
MinimizeButton.Parent = TopBar
MinimizeButton.BackgroundColor3 = Color3.new(0.145098, 0.141176, 0.14902)
MinimizeButton.BorderSizePixel = 0
MinimizeButton.Position = UDim2.new(1, -57, 0, 0)
MinimizeButton.Size = UDim2.new(0, 19, 0, 19)
MinimizeButton.Font = Enum.Font.SourceSans
MinimizeButton.Text = ""
MinimizeButton.TextColor3 = Color3.new(0, 0, 0)
MinimizeButton.TextSize = 14
ImageLabel_3.Parent = MinimizeButton
ImageLabel_3.BackgroundColor3 = Color3.new(1, 1, 1)
ImageLabel_3.BackgroundTransparency = 1
ImageLabel_3.Position = UDim2.new(0, 5, 0, 5)
ImageLabel_3.Size = UDim2.new(0, 9, 0, 9)
ImageLabel_3.Image = "http://www.roblox.com/asset/?id=5597105827"
ToolTip.Name = "ToolTip"
ToolTip.Parent = SimpleSpy2
ToolTip.BackgroundColor3 = Color3.fromRGB(26, 26, 26)
ToolTip.BackgroundTransparency = 0.1
ToolTip.BorderColor3 = Color3.new(1, 1, 1)
ToolTip.Size = UDim2.new(0, 200, 0, 50)
ToolTip.ZIndex = 3
ToolTip.Visible = false
TextLabel.Parent = ToolTip
TextLabel.BackgroundColor3 = Color3.new(1, 1, 1)
TextLabel.BackgroundTransparency = 1
TextLabel.Position = UDim2.new(0, 2, 0, 2)
TextLabel.Size = UDim2.new(0, 196, 0, 46)
TextLabel.ZIndex = 3
TextLabel.Font = Enum.Font.SourceSans
TextLabel.Text = "This is some slightly longer text."
TextLabel.TextColor3 = Color3.new(1, 1, 1)
TextLabel.TextSize = 14
TextLabel.TextWrapped = true
TextLabel.TextXAlignment = Enum.TextXAlignment.Left
TextLabel.TextYAlignment = Enum.TextYAlignment.Top
-------------------------------------------------------------------------------
-- init
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")
local ContentProvider = game:GetService("ContentProvider")
local TextService = game:GetService("TextService")
local Mouse = game:GetService("Players").LocalPlayer:GetMouse()
-- autoblock variables
local autoblock = false
local history = {}
local excluding = {}
-- functions
--- Converts arguments to a string and generates code that calls the specified
method with them, recommended to be used in conjunction with ValueToString (method
must be a string, e.g. `game:GetService("ReplicatedStorage").Remote:FireServer`)
--- @param method string
--- @param args any[]
--- @return string
function SimpleSpy:ArgsToString(method, args)
assert(typeof(method) == "string", "string expected, got " .. typeof(method))
assert(typeof(args) == "table", "table expected, got " .. typeof(args))
return v2v({args = args}) .. "\n\n" .. method .. "(unpack(args))"
end
--- Converts a value to variables with the specified index as the variable name (if
nil/invalid then the name will be assigned automatically)
--- @param t any[]
--- @return string
function SimpleSpy:TableToVars(t)
assert(typeof(t) == "table", "table expected, got " .. typeof(t))
return v2v(t)
end
--- Allows for direct hooking of remotes **THIS CAN BE VERY DANGEROUS**
--- @param remote Instance
--- @param f function
function SimpleSpy:HookRemote(remote, f)
assert(typeof(remote) == "Instance", "Instance expected, got " ..
typeof(remote))
assert(typeof(f) == "function", "function expected, got " .. typeof(f))
remoteHooks[remote] = f
end
--- Prevents remote spam from causing lag (clears logs after
`_G.SIMPLESPYCONFIG_MaxRemotes` or 500 remotes)
function clean()
local max = _G.SIMPLESPYCONFIG_MaxRemotes
if not typeof(max) == "number" and math.floor(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
table.insert(newLogs, remoteLogs[i])
end
remoteLogs = newLogs
end
end
--- Executed when the toggle button (the SimpleSpy logo) is hovered over
function onToggleButtonHover()
if not toggle then
TweenService:Create(Simple, TweenInfo.new(0.5), {TextColor3 =
Color3.fromRGB(252, 51, 51)}):Play()
else
TweenService:Create(Simple, TweenInfo.new(0.5), {TextColor3 =
Color3.fromRGB(68, 206, 91)}):Play()
end
end
--- Reconnects bringBackOnResize if the current viewport changes and also connects
it initially
function connectResize()
local lastCam =
workspace.CurrentCamera:GetPropertyChangedSignal("ViewportSize"):Connect(bringBackO
nResize)
workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
lastCam:Disconnect()
if workspace.CurrentCamera then
lastCam =
workspace.CurrentCamera:GetPropertyChangedSignal("ViewportSize"):Connect(bringBackO
nResize)
end
end)
end
--- Brings gui back if it gets lost offscreen (connected to the camera viewport
changing)
function bringBackOnResize()
local currentX = Background.AbsolutePosition.X
local currentY = Background.AbsolutePosition.Y
local viewportSize = workspace.CurrentCamera.ViewportSize
if (currentX < 0) or (currentX > (viewportSize.X - (sideClosed and 131 or
TopBar.AbsoluteSize.X))) then
if currentX < 0 then
currentX = 0
else
currentX = viewportSize.X - (sideClosed and 131 or
TopBar.AbsoluteSize.X)
end
end
if (currentY < 0) or (currentY > (viewportSize.Y - (closed and 19 or
Background.AbsoluteSize.Y) - 36)) then
if currentY < 0 then
currentY = 0
else
currentY = viewportSize.Y - (closed and 19 or
Background.AbsoluteSize.Y) - 36
end
end
TweenService.Create(TweenService, Background, TweenInfo.new(0.1), {Position =
UDim2.new(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 pairs(elements) do
if typeof(v) == "Instance" and v:IsA("GuiObject") and v.Visible then
coroutine.wrap(function()
data[v] = {
BackgroundTransparency = v.BackgroundTransparency
}
TweenService:Create(v, TweenInfo.new(0.5), {BackgroundTransparency
= 1}):Play()
if v:IsA("TextBox") or v:IsA("TextButton") or v:IsA("TextLabel")
then
data[v].TextTransparency = v.TextTransparency
TweenService:Create(v, TweenInfo.new(0.5), {TextTransparency =
1}):Play()
elseif v:IsA("ImageButton") or v:IsA("ImageLabel") then
data[v].ImageTransparency = v.ImageTransparency
TweenService:Create(v, TweenInfo.new(0.5), {ImageTransparency =
1}):Play()
end
wait(0.5)
v.Visible = false
for i, x in pairs(data[v]) do
v[i] = x
end
data[v] = true
end)()
end
end
return function()
for i, _ in pairs(data) do
coroutine.wrap(function()
local properties = {
BackgroundTransparency = i.BackgroundTransparency
}
i.BackgroundTransparency = 1
TweenService:Create(i, TweenInfo.new(0.5), {BackgroundTransparency
= properties.BackgroundTransparency}):Play()
if i:IsA("TextBox") or i:IsA("TextButton") or i:IsA("TextLabel")
then
properties.TextTransparency = i.TextTransparency
i.TextTransparency = 1
TweenService:Create(i, TweenInfo.new(0.5), {TextTransparency =
properties.TextTransparency}):Play()
elseif i:IsA("ImageButton") or i:IsA("ImageLabel") then
properties.ImageTransparency = i.ImageTransparency
i.ImageTransparency = 1
TweenService:Create(i, TweenInfo.new(0.5), {ImageTransparency =
properties.ImageTransparency}):Play()
end
i.Visible = 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
LeftPanel.Visible = true
TweenService:Create(LeftPanel, TweenInfo.new(0.5), {Size = UDim2.new(0,
131, 0, 0)}):Play()
wait(0.5)
remotesFadeIn = fadeOut(LeftPanel:GetDescendants())
wait(0.5)
else
TweenService:Create(LeftPanel, TweenInfo.new(0.5), {Size = UDim2.new(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)
RightPanel.Visible = false
else
if closed then
toggleMinimize(true)
end
RightPanel.Visible = 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 = Instance.new("TextButton")
local prevSize = UDim2.new(0, CodeBox.AbsoluteSize.X, 0,
CodeBox.AbsoluteSize.Y)
local prevPos = UDim2.new(0,CodeBox.AbsolutePosition.X, 0,
CodeBox.AbsolutePosition.Y)
disable.Size = UDim2.new(1, 0, 1, 0)
disable.BackgroundColor3 = Color3.new()
disable.BorderSizePixel = 0
disable.Text = 0
disable.ZIndex = 3
disable.BackgroundTransparency = 1
disable.AutoButtonColor = false
CodeBox.ZIndex = 4
CodeBox.Position = prevPos
CodeBox.Size = prevSize
TweenService:Create(CodeBox, TweenInfo.new(0.5), {Size = UDim2.new(0.5, 0,
0.5, 0), Position = UDim2.new(0.25, 0, 0.25, 0)}):Play()
TweenService:Create(disable, TweenInfo.new(0.5), {BackgroundTransparency =
0.5}):Play()
disable.MouseButton1Click:Connect(function()
if UserInputService:GetMouseLocation().Y + 36 >=
CodeBox.AbsolutePosition.Y and UserInputService:GetMouseLocation().Y + 36 <=
CodeBox.AbsolutePosition.Y + CodeBox.AbsoluteSize.Y
and UserInputService:GetMouseLocation().X >=
CodeBox.AbsolutePosition.X and UserInputService:GetMouseLocation().X <=
CodeBox.AbsolutePosition.X + CodeBox.AbsoluteSize.X then
return
end
TweenService:Create(CodeBox, TweenInfo.new(0.5), {Size = prevSize,
Position = prevPos}):Play()
TweenService:Create(disable, TweenInfo.new(0.5),
{BackgroundTransparency = 1}):Play()
wait(0.5)
disable:Destroy()
CodeBox.Size = UDim2.new(1, 0, 0.5, 0)
CodeBox.Position = UDim2.new(0, 0, 0, 0)
CodeBox.ZIndex = 0
maximized = false
end)
end
end
--- Updates the canvas size to fit the current amount of function buttons
function updateFunctionCanvas()
ScrollingFrame.CanvasSize =
UDim2.fromOffset(UIGridLayout.AbsoluteContentSize.X,
UIGridLayout.AbsoluteContentSize.Y)
end
--- Updates the canvas size to fit the amount of current remotes
function updateRemoteCanvas()
LogList.CanvasSize = UDim2.fromOffset(UIListLayout.AbsoluteContentSize.X,
UIListLayout.AbsoluteContentSize.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 then
if ToolTip.Visible then
ToolTip.Visible = false
RunService:UnbindFromRenderStep("ToolTip")
end
local first = true
RunService:BindToRenderStep("ToolTip", 1, function()
local topLeft = Vector2.new(Mouse.X + 20, Mouse.Y + 20)
local bottomRight = topLeft + ToolTip.AbsoluteSize
if topLeft.X < 0 then
topLeft = Vector2.new(0, topLeft.Y)
elseif bottomRight.X > workspace.CurrentCamera.ViewportSize.X then
topLeft = Vector2.new(workspace.CurrentCamera.ViewportSize.X -
ToolTip.AbsoluteSize.X, topLeft.Y)
end
if topLeft.Y < 0 then
topLeft = Vector2.new(topLeft.X, 0)
elseif bottomRight.Y > workspace.CurrentCamera.ViewportSize.Y - 35 then
topLeft = Vector2.new(topLeft.X,
workspace.CurrentCamera.ViewportSize.Y - ToolTip.AbsoluteSize.Y - 35)
end
if topLeft.X <= Mouse.X and topLeft.Y <= Mouse.Y then
topLeft = Vector2.new(Mouse.X - ToolTip.AbsoluteSize.X - 2, Mouse.Y
- ToolTip.AbsoluteSize.Y - 2)
end
if first then
ToolTip.Position = UDim2.fromOffset(topLeft.X, topLeft.Y)
first = false
else
ToolTip:TweenPosition(UDim2.fromOffset(topLeft.X, topLeft.Y),
"Out", "Linear", 0.1)
end
end)
TextLabel.Text = text
ToolTip.Visible = true
else
if ToolTip.Visible then
ToolTip.Visible = false
RunService:UnbindFromRenderStep("ToolTip")
end
end
end
--- Generates a script from the provided arguments (first has to be remote path)
function genScript(remote, ...)
prevTables = {}
local gen = ""
local args = {...}
if #args > 0 then
if not pcall(function()
gen = v2v({args = args}) .. "\n"
end)
then
gen = gen .. "-- TableToString failure! Reverting to legacy
functionality (results may vary)\nlocal args = {"
if
not pcall(
function()
for i, v in pairs(args) do
if type(i) ~= "Instance" and type(i) ~= "userdata" then
gen = gen .. "\n [" .. tostring(i) .. "] = "
elseif type(i) == "string" then
gen = gen .. '\n ["' .. tostring(i) .. '"] = '
elseif type(i) == "userdata" and typeof(i) ~=
"Instance" then
gen = gen .. "\n [" .. typeof(i) .. ".new(" ..
tostring(i) .. ")] = "
elseif type(i) == "userdata" then
gen = gen .. "\n [game." .. i:GetFullName() ..
")] = "
end
if type(v) ~= "Instance" and type(v) ~= "userdata" then
gen = gen .. tostring(v)
elseif type(v) == "string" then
gen = gen .. '"' .. tostring(v) .. '"'
elseif type(v) == "userdata" and typeof(v) ~=
"Instance" then
gen = gen .. typeof(v) .. ".new(" .. tostring(v) ..
")"
elseif type(v) == "userdata" then
gen = gen .. "game." .. v:GetFullName()
end
end
gen = gen .. "\n}\n\n"
end
)
then
gen = 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
pairs(getnilinstances())do if v.ClassName==class and v.Name==name then return v;end
end end\n\n" .. gen
end
if remote:IsA("RemoteEvent") then
gen = 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 = gen .. v2s(remote) .. ":FireServer()"
elseif remote:IsA("RemoteFunction") then
gen = gen .. v2s(remote) .. ":InvokeServer()"
end
end
gen = "-- Script generated by SimpleSpy - credits to exx#9394\n\n" .. gen
prevTables = {}
return gen
end
--- value-to-string: value, string (out), level (indentation), parent table, var
name, is from tovar
function v2s(v, l, p, n, vtv, i, pt, path, tables, tI)
if not tI then
tI = {0}
else
tI[1] += 1
end
if typeof(v) == "number" then
if v == math.huge then
return "math.huge"
elseif tostring(v):match("nan") then
return "0/0 --[[NaN]]"
end
return tostring(v)
elseif typeof(v) == "boolean" then
return tostring(v)
elseif typeof(v) == "string" then
return formatstr(v)
elseif typeof(v) == "function" then
return f2s(v)
elseif typeof(v) == "table" then
return t2s(v, l, p, n, vtv, i, pt, path, tables, tI)
elseif typeof(v) == "Instance" then
return i2p(v)
elseif typeof(v) == "userdata" then
return "newproxy(true)"
elseif type(v) == "userdata" then
return u2s(v)
else
return "nil --[[" .. typeof(v) .. "]]"
end
end
--- value-to-variable
--- @param t any
function v2v(t)
topstr = ""
bottomstr = ""
getnilrequired = false
local ret = ""
local count = 1
for i, v in pairs(t) do
if type(i) == "string" and i:match("^[%a_]+[%w_]*$") then
ret = ret .. "local " .. i .. " = " .. v2s(v, nil, nil, i, true) .. "\
n"
elseif tostring(i):match("^[%a_]+[%w_]*$") then
ret = ret .. "local " .. tostring(i):lower() .. "_" ..
tostring(count) .. " = " .. v2s(v, nil, nil, tostring(i):lower() .. "_" ..
tostring(count), true) .. "\n"
else
ret = ret .. "local " .. type(v) .. "_" .. tostring(count) .. " = " ..
v2s(v, nil, nil, type(v) .. "_" .. tostring(count), true) .. "\n"
end
count = count + 1
end
if getnilrequired then
topstr = "function getNil(name,class) for _,v in pairs(getnilinstances())do
if v.ClassName==class and v.Name==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
--- 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)
for k, x in pairs(getrenv()) do -- checks if table is actually just a global
local isgucci, gpath
if rawequal(x, t) then
isgucci, gpath = true, ""
elseif type(x) == "table" then
isgucci, gpath = v2p(t, x)
end
if isgucci then
if type(k) == "string" and k:match("^[%a_]+[%w_]*$") then
return k .. gpath
else
return "getrenv()[" .. v2s(k) .. "]" .. gpath
end
end
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 pairs(tables) do -- checks if the current table has been serialized
before
if n and rawequal(v, t) then
bottomstr = bottomstr .. "\n" .. tostring(n) .. tostring(path) .. " = "
.. tostring(n) .. tostring(({v2p(v, p)})[2])
return "{} --[[DUPLICATE]]"
end
end
table.insert(tables, t) -- logs table to past tables
local s = "{" -- start of serialization
local size = 0
l = l + indent -- set indentation level
for k, v in pairs(t) do -- iterates over table
size = size + 1 -- changes size for max limit
if tI[1] > (_G.SimpleSpyMaxTableSize and _G.SimpleSpyMaxTableSize or 5000)
then
s = s .. "\n" .. string.rep(" ", l) .. "-- MAXIMUM TABLE SIZE REACHED,
CHANGE '_G.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 = bottomstr .. "\n" .. tostring(n) .. tostring(path) ..
"[" .. tostring(n) .. tostring(path) .. "]" .. " = " .. (rawequal(v, k) and
tostring(n) .. tostring(path) or v2s(v, l, p, n, vtv, k, t, path .. "[" ..
tostring(n) .. tostring(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, nil, p, n, vtv, i, pt, path, nil, tI) ..
"]"
end
-- actually serializes the member of the table
s = s .. "\n" .. string.rep(" ", 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" .. string.rep(" ", l - indent)
end
return s .. "}"
end
--- function-to-string
function f2s(f)
for k, x in pairs(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 then
if type(k) == "string" and k:match("^[%a_]+[%w_]*$") then
return k .. gpath
else
return "getgenv()[" .. v2s(k) .. "]" .. gpath
end
end
end
-- uwu some cool stuff here once bork finishes up
-- if SimpleSpy.GetExternalLoader then
-- local ExternalLoader = SimpleSpy:GetExternalLoader()
-- local loaded, path = pcall(function()
ExternalLoader:LoadAsset("Bork_Functions") end)
-- if loaded then
-- local functions = loadfile(path .. "functions.lua")
-- local out = functions[f]
-- if out then
-- return out
-- end
-- end
-- end
-- local isgucci, gpath = v2p(f, getgc())
-- if isgucci then
-- return "getgc()" .. gpath
-- end
if funcEnabled and debug.getinfo(f).name:match("^[%a_]+[%w_]*$") then
return "function()end --[[" .. debug.getinfo(f).name .. "]]"
end
return "function()end --[[" .. tostring(f) .. "]]"
end
--- instance-to-path
--- @param i userdata
function i2p(i)
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 == player.Character then
if player == Players.LocalPlayer then
return 'game:GetService("Players").LocalPlayer.Character' ..
out
else
return i2p(player) .. ".Character" .. out
end
else
if parent.Name:match("[%a_]+[%w+]*") ~= parent.Name then
out = '[' .. formatstr(parent.Name) .. ']' .. out
else
out = "." .. parent.Name .. out
end
end
parent = parent.Parent
end
elseif parent ~= game then
while true do
if parent and parent.Parent == game then
if game:GetService(parent.ClassName) then
if parent.ClassName == "Workspace" then
return "workspace" .. out
else
return 'game:GetService("' .. parent.ClassName .. '")' ..
out
end
else
if parent.Name:match("[%a_]+[%w_]*") then
return "game." .. parent.Name .. out
else
return 'game[' .. formatstr(parent.Name) .. ']' .. out
end
end
elseif parent.Parent == nil then
getnilrequired = true
return 'getNil(' .. formatstr(parent.Name) .. ', "' ..
parent.ClassName .. '")' .. out
elseif parent == Players.LocalPlayer then
out = ".LocalPlayer" .. out
else
if parent.Name:match("[%a_]+[%w_]*") ~= parent.Name then
out = '[' .. formatstr(parent.Name) .. ']' .. out
else
out = "." .. parent.Name .. out
end
end
parent = parent.Parent
end
else
return "game"
end
end
--- Adds \'s to the text as a replacement to whitespace chars and other things
because string.format can't yayeet
function handlespecials(s)
local i = 0
local coroutines = {}
local coroutineFunc = function(i, r)
s = s:sub(0, i - 1) .. r .. s:sub(i + 1, -1)
end
local function isFinished()
for _, v in pairs(coroutines) do
if coroutine.status(v) == "running" then
return false
end
end
return true
end
repeat
i = i + 1
local char = s:sub(i, i)
if string.byte(char) then
local c = coroutine.create(coroutineFunc)
table.insert(coroutines, c)
if char == "\n" then
coroutine.resume(c, i, "\\n")
-- s = s:sub(0, i - 1) .. "\\n" .. s:sub(i + 1, -1)
i = i + 1
elseif char == "\t" then
coroutine.resume(c, i, "\\t")
-- s = s:sub(0, i - 1) .. "\\t" .. s:sub(i + 1, -1)
i = i + 1
elseif char == "\\" then
coroutine.resume(c, i, "\\\\")
-- s = s:sub(0, i - 1) .. "\\\\" .. s:sub(i + 1, -1)
i = i + 1
elseif char == '"' then
coroutine.resume(c, i, "\\\"")
-- s = s:sub(0, i - 1) .. '\\"' .. s:sub(i + 1, -1)
i = i + 1
elseif string.byte(char) > 126 or string.byte(char) < 32 then
coroutine.resume(c, i, "\\" .. string.byte(char))
-- s = s:sub(0, i - 1) .. "\\" .. string.byte(char) .. s:sub(i + 1,
-1)
i = i + #tostring(string.byte(char))
end
end
until char == "" or i > (_G.SimpleSpyMaxStringSize or 2000)
while not isFinished() do
RunService.Heartbeat:Wait()
end
if i > (_G.SimpleSpyMaxStringSize or 2000) then
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 pairs(getnilinstances()) do
if v.Name == 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 = realPath.FindFirstChild(realPath, runningTest)
if test then
realPath = test
end
match = true
else
runningTest = src:sub(s, e)
local test = realPath.FindFirstChild(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)
function schedule(f, ...)
table.insert(scheduled, {f, ...})
end
--- the big (well tbh small now) boi task scheduler himself, handles p much
anything as quicc as possible
function taskscheduler()
if not toggle then
scheduled = {}
return
end
if #scheduled > 1000 then
table.remove(scheduled, #scheduled)
end
if #scheduled > 0 then
local currentf = scheduled[1]
table.remove(scheduled, 1)
if type(currentf) == "table" and type(currentf[1]) == "function" then
pcall(unpack(currentf))
end
end
end
--- Toggles between the two remotespy methods (hookfunction currently = disabled)
function toggleSpyMethod()
toggleSpy()
toggle = not toggle
end
-- main
if not _G.SimpleSpyExecuted then
local succeeded, err = pcall(function()
_G.SimpleSpyShutdown = shutdown
ContentProvider:PreloadAsync({"rbxassetid://6065821980",
"rbxassetid://6065774948", "rbxassetid://6065821086", "rbxassetid://6065821596",
ImageLabel, ImageLabel_2, ImageLabel_3})
if gethui then funcEnabled = false end
onToggleButtonClick()
RemoteTemplate.Parent = nil
FunctionTemplate.Parent = nil
codebox = Highlight.new(CodeBox)
codebox:setRaw("")
getgenv().SimpleSpy = SimpleSpy
TextLabel:GetPropertyChangedSignal("Text"):Connect(scaleToolTip)
TopBar.InputBegan:Connect(onBarInput)
MinimizeButton.MouseButton1Click:Connect(toggleMinimize)
MaximizeButton.MouseButton1Click:Connect(toggleSideTray)
Simple.MouseButton1Click:Connect(onToggleButtonClick)
CloseButton.MouseEnter:Connect(onXButtonHover)
CloseButton.MouseLeave:Connect(onXButtonUnhover)
Simple.MouseEnter:Connect(onToggleButtonHover)
Simple.MouseLeave:Connect(onToggleButtonUnhover)
CloseButton.MouseButton1Click:Connect(shutdown)
table.insert(connections,
UserInputService.InputBegan:Connect(backgroundUserInput))
table.insert(connections, Mouse.Move:Connect(mouseMoved))
connectResize()
SimpleSpy2.Enabled = true
coroutine.wrap(function()
wait(1)
onToggleButtonUnhover()
end)()
schedulerconnect = RunService.Heartbeat:Connect(taskscheduler)
if syn and syn.protect_gui then pcall(syn.protect_gui, SimpleSpy2) end
SimpleSpy2.Parent = gethui and gethui() or CoreGui
end)
if succeeded then
_G.SimpleSpyExecuted = true
else
warn("A fatal error has occured, SimpleSpy was unable to launch properly.\
nPlease DM this error message to @exx#9394:\n\n" .. tostring(err))
SimpleSpy2:Destroy()
hookfunction(remoteEvent.FireServer, originalEvent)
hookfunction(remoteFunction.InvokeServer, originalFunction)
gm.__namecall = original
return
end
else
SimpleSpy2: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 'selected.Log')
}
- 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())
TextLabel.Text = "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
setclipboard(SimpleSpy:ValueToString(selected.Source))
TextLabel.Text = "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()
if selected then
if selected.Function then
codebox:setRaw("-- Calling function info\n-- Generated by the
SimpleSpy serializer\n\n" .. tostring(selected.Function))
end
TextLabel.Text = "Done! Function info generated by the SimpleSpy
Serializer."
end
end
)
--- Excludes all Remotes that share the same name as the selected.Log remote from
the RemoteSpy
newButton(
"Exclude (n)",
function() return "Click to exclude all remotes with this name" end,
function()
if selected then
blacklist[selected.Name] = true
TextLabel.Text = "Excluded!"
end
end
)
--- Prevents the selected.Log Remote from firing the server (still logged)
newButton(
"Block (i)",
function() return "Click to stop this remote from firing" end,
function()
if selected then
blocklist[selected.Remote] = true
TextLabel.Text = "Excluded!"
end
end
)
--- Prevents all remotes from firing that share the same name as the selected.Log
remote from the RemoteSpy (still logged)
newButton(
"Block (n)",
function() return "Click to stop remotes with this name from firing" end,
function()
if selected then
blocklist[selected.Name] = true
TextLabel.Text = "Excluded!"
end
end
)
newButton(
"Disable Info",
function() return string.format("%s[%s] Toggle function info (because it can
cause lag in some games)", gethui and "NOT WORKING IN KRNL, DISABLED BY DEFAULT "
or "", funcEnabled and "ENABLED" or "DISABLED") end,
function()
funcEnabled = not funcEnabled
TextLabel.Text = string.format("%s[%s] Toggle function info (because it can
cause lag in some games)", gethui and "NOT WORKING IN KRNL, DISABLED BY DEFAULT "
or "", funcEnabled and "ENABLED" or "DISABLED")
end
)
newButton(
"Autoblock",
function() return string.format("[%s] [BETA] Intelligently detects and excludes
spammy remote calls from logs", autoblock and "ENABLED" or "DISABLED") end,
function()
autoblock = not autoblock
TextLabel.Text = string.format("[%s] [BETA] Intelligently detects and
excludes spammy remote calls from logs", autoblock and "ENABLED" or "DISABLED")
history = {}
excluding = {}
end
)
newButton(
"CallingScript",
function() return string.format("[%s] [UNSAFE] Uses 'getcallingscript' to get
calling script for Decompile and GetScript. Much more reliable, but opens up
SimpleSpy to detection and/or instability.", useGetCallingScript and "ENABLED" or
"DISABLED") end,
function()
useGetCallingScript = not useGetCallingScript
TextLabel.Text = string.format("[%s] [UNSAFE] Uses 'getcallingscript' to
get calling script for Decompile and GetScript. Much more reliable, but opens up
SimpleSpy to detection and/or instability.", useGetCallingScript and "ENABLED" or
"DISABLED")
end
)