csmit195

right

Jul 1st, 2024
756
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 99.00 KB | None | 0 0
  1. --[[
  2.     higlight.lua v0.0.1 by exxtremewa#9394
  3.    
  4.     Features:
  5.      - uses the power of fancy syntax detection algorithms to convert a frame into a syntax highlighted high quality code box
  6.      - is cool
  7. ]]
  8.  
  9. local TextService = game:GetService("TextService")
  10. local RunService = game:GetService("RunService")
  11. --- The Highlight class
  12. --- @class Highlight
  13. local Highlight = {}
  14.  
  15. -- PRIVATE METHODS/PROPERTIES --
  16.  
  17. --[[
  18.     Char Object:
  19.     {
  20.         Char: string -- A single character
  21.         Color: Color3 -- The intended color of the char
  22.         Line: number -- The line number
  23.     }
  24. ]]
  25.  
  26. local parentFrame
  27. local scrollingFrame
  28. local textFrame
  29. local lineNumbersFrame
  30. local lines = {}
  31.  
  32. --- Contents of the table- array of char objects
  33. local tableContents = {}
  34.  
  35. local line = 0
  36. local largestX = 0
  37.  
  38. local lineSpace = 15
  39. local font = Enum.Font.Ubuntu
  40. local textSize = 14
  41.  
  42. local backgroundColor = Color3.fromRGB(40, 44, 52)
  43. local operatorColor = Color3.fromRGB(187, 85, 255)
  44. local functionColor = Color3.fromRGB(97, 175, 239)
  45. local stringColor = Color3.fromRGB(152, 195, 121)
  46. local numberColor = Color3.fromRGB(209, 154, 102)
  47. local booleanColor = numberColor
  48. local objectColor = Color3.fromRGB(229, 192, 123)
  49. local defaultColor = Color3.fromRGB(224, 108, 117)
  50. local commentColor = Color3.fromRGB(148, 148, 148)
  51. local lineNumberColor = commentColor
  52. local genericColor = Color3.fromRGB(240, 240, 240)
  53.  
  54. local operators = {"^(function)[^%w_]", "^(local)[^%w_]", "^(if)[^%w_]", "^(for)[^%w_]", "^(while)[^%w_]", "^(then)[^%w_]", "^(do)[^%w_]", "^(else)[^%w_]", "^(elseif)[^%w_]", "^(return)[^%w_]", "^(end)[^%w_]", "^(continue)[^%w_]", "^(and)[^%w_]", "^(not)[^%w_]", "^(or)[^%w_]", "[^%w_](or)[^%w_]", "[^%w_](and)[^%w_]", "[^%w_](not)[^%w_]", "[^%w_](continue)[^%w_]", "[^%w_](function)[^%w_]", "[^%w_](local)[^%w_]", "[^%w_](if)[^%w_]", "[^%w_](for)[^%w_]", "[^%w_](while)[^%w_]", "[^%w_](then)[^%w_]", "[^%w_](do)[^%w_]", "[^%w_](else)[^%w_]", "[^%w_](elseif)[^%w_]", "[^%w_](return)[^%w_]", "[^%w_](end)[^%w_]"}
  55. --- In this case, patterns could not be used, so just the string characters are provided
  56. local strings = {{"\"", "\""}, {"'", "'"}, {"%[%[", "%]%]", true}}
  57. local comments = {"%-%-%[%[[^%]%]]+%]?%]?", "(%-%-[^\n]+)"}
  58. local functions = {"[^%w_]([%a_][%a%d_]*)%s*%(", "^([%a_][%a%d_]*)%s*%(", "[:%.%(%[%p]([%a_][%a%d_]*)%s*%("}
  59. local numbers = {"[^%w_](%d+[eE]?%d*)", "[^%w_](%.%d+[eE]?%d*)", "[^%w_](%d+%.%d+[eE]?%d*)", "^(%d+[eE]?%d*)", "^(%.%d+[eE]?%d*)", "^(%d+%.%d+[eE]?%d*)"}
  60. local booleans = {"[^%w_](true)", "^(true)", "[^%w_](false)", "^(false)", "[^%w_](nil)", "^(nil)"}
  61. local objects = {"[^%w_:]([%a_][%a%d_]*):", "^([%a_][%a%d_]*):"}
  62. local other = {"[^_%s%w=>~<%-%+%*]", ">", "~", "<", "%-", "%+", "=", "%*"}
  63. local offLimits = {}
  64.  
  65. --- Determines if index is in a string
  66. function isOffLimits(index)
  67.     for _, v in pairs(offLimits) do
  68.         if index >= v[1] and index <= v[2] then
  69.             return true
  70.         end
  71.     end
  72.     return false
  73. end
  74.  
  75. --- Find iterator
  76. -- function gfind(str, pattern)
  77. --     local start = 0
  78. --     return function()
  79. --         local findStart, findEnd = str:find(pattern, start)
  80. --         if findStart and findEnd ~= #str then
  81. --             start = findEnd + 1
  82. --             return findStart, findEnd
  83. --         else
  84. --             return nil
  85. --         end
  86. --     end
  87. -- end
  88.  
  89. --- Find iterator
  90. function gfind(str, pattern)
  91.     return coroutine.wrap(function()
  92.         local start = 0
  93.         while true do
  94.             local findStart, findEnd = str:find(pattern, start)
  95.             if findStart and findEnd ~= #str then
  96.                 start = findEnd + 1
  97.                 coroutine.yield(findStart, findEnd)
  98.             else
  99.                 return
  100.             end
  101.         end
  102.     end)
  103. end
  104.  
  105. --- Finds and highlights comments with `commentColor`
  106. function renderComments()
  107.     local str = Highlight:getRaw()
  108.     local step = 1
  109.     for _, pattern in pairs(comments) do
  110.         for commentStart, commentEnd in gfind(str, pattern) do
  111.             if step % 1000 == 0 then
  112.                 RunService.Heartbeat:Wait()
  113.             end
  114.             step = step + 1
  115.             if not isOffLimits(commentStart) then
  116.                 for i = commentStart, commentEnd do
  117.                     table.insert(offLimits, {commentStart, commentEnd})
  118.                     if tableContents[i] then
  119.                         tableContents[i].Color = commentColor
  120.                     end
  121.                 end
  122.             end
  123.         end
  124.     end
  125. end
  126.  
  127. -- Finds and highlights strings with `stringColor`
  128. function renderStrings()
  129.     local stringType
  130.     local stringEndType
  131.     local ignoreBackslashes
  132.     local stringStart
  133.     local stringEnd
  134.     local offLimitsIndex
  135.     local skip = false
  136.     for i, char in pairs(tableContents) do
  137.         if stringType then
  138.             char.Color = stringColor
  139.             local possibleString = ""
  140.             for k = stringStart, i do
  141.                 possibleString = possibleString .. tableContents[k].Char
  142.             end
  143.             if char.Char:match(stringEndType) and not not ignoreBackslashes or (possibleString:match("(\\*)" .. stringEndType .. "$") and #possibleString:match("(\\*)" .. stringEndType .. "$") % 2 == 0) then
  144.                 skip = true
  145.                 stringType = nil
  146.                 stringEndType = nil
  147.                 ignoreBackslashes = nil
  148.                 stringEnd = i
  149.                 offLimits[offLimitsIndex][2] = stringEnd
  150.             end
  151.         end
  152.         if not skip then
  153.             for _, v in pairs(strings) do
  154.                 if char.Char:match(v[1]) and not isOffLimits(i) then
  155.                     stringType = v[1]
  156.                     stringEndType = v[2]
  157.                     ignoreBackslashes = v[3]
  158.                     char.Color = stringColor
  159.                     stringStart = i
  160.                     offLimitsIndex = #offLimits + 1
  161.                     offLimits[offLimitsIndex] = {stringStart, math.huge}
  162.                 end
  163.             end
  164.         end
  165.         skip = false
  166.     end
  167. end
  168.  
  169. --- Highlights the specified patterns with the specified color
  170. --- @param patternArray string[]
  171. ---@param color userdata
  172. function highlightPattern(patternArray, color)
  173.     local str = Highlight:getRaw()
  174.     local step = 1
  175.     for _, pattern in pairs(patternArray) do
  176.         for findStart, findEnd in gfind(str, pattern) do
  177.             if step % 1000 == 0 then
  178.                 RunService.Heartbeat:Wait()
  179.             end
  180.             -- step += 1
  181.             step = step + 1
  182.             if not isOffLimits(findStart) and not isOffLimits(findEnd) then
  183.                 for i = findStart, findEnd do
  184.                     if tableContents[i] then
  185.                         tableContents[i].Color = color
  186.                     end
  187.                 end
  188.             end
  189.         end
  190.     end
  191. end
  192.  
  193. --- Automatically replaces reserved chars with escape chars
  194. --- @param s string
  195. function autoEscape(s)
  196.     for i = 0, #s do
  197.         local char = string.sub(s, i, i)
  198.         if char == "<" then
  199.             s = string.format("%s%s%s", string.sub(s, 0, i - 1), "&lt;", string.sub(s, i + 1, -1))
  200.             i = i + 3
  201.         elseif char == ">" then
  202.             s = string.format("%s%s%s", string.sub(s, 0, i - 1), "&gt;", string.sub(s, i + 1, -1))
  203.             -- i += 3
  204.             i = i + 3
  205.         elseif char == '"' then
  206.             s = string.format("%s%s%s", string.sub(s, 0, i - 1), "&quot;", string.sub(s, i + 1, -1))
  207.             -- i += 5
  208.             i = i + 5
  209.         elseif char == "'" then
  210.             s = string.format("%s%s%s", string.sub(s, 0, i - 1), "&apos;", string.sub(s, i + 1, -1))
  211.             -- i += 5
  212.             i = i + 5
  213.         elseif char == "&" then
  214.             s = string.format("%s%s%s", string.sub(s, 0, i - 1), "&amp;", string.sub(s, i + 1, -1))
  215.             -- i += 4
  216.             i = i + 4
  217.         end
  218.     end
  219.     -- s:gsub("<", "&lt;")
  220.     -- s:gsub(">", "&gt;")
  221.     -- s:gsub('"', "&quot;")
  222.     -- s:gsub("'", "&apos;")
  223.     -- s:gsub("&", "&amp;")
  224.     return s
  225. end
  226.  
  227. --- Main function for syntax highlighting tableContents
  228. function render()
  229.     offLimits = {}
  230.     lines = {}
  231.     textFrame:ClearAllChildren()
  232.     lineNumbersFrame:ClearAllChildren()
  233.  
  234.     highlightPattern(functions, functionColor)
  235.     highlightPattern(numbers, numberColor)
  236.     highlightPattern(operators, operatorColor)
  237.     highlightPattern(objects, objectColor)
  238.     highlightPattern(booleans, booleanColor)
  239.     highlightPattern(other, genericColor)
  240.     renderComments()
  241.     renderStrings()
  242.  
  243.     local lastColor
  244.     local lineStr = ""
  245.     local rawStr = ""
  246.     largestX = 0
  247.     line = 1
  248.  
  249.     for i = 1, #tableContents + 1 do
  250.         local char = tableContents[i]
  251.         if i == #tableContents + 1 or char.Char == "\n" then
  252.             lineStr = lineStr .. (lastColor and "</font>" or "")
  253.  
  254.             local lineText = Instance.new("TextLabel")
  255.             local x = TextService:GetTextSize(rawStr, textSize, font, Vector2.new(math.huge, math.huge)).X + 60
  256.  
  257.             if x > largestX then
  258.                 largestX = x
  259.             end
  260.  
  261.             lineText.TextXAlignment = Enum.TextXAlignment.Left
  262.             lineText.TextYAlignment = Enum.TextYAlignment.Top
  263.             lineText.Position = UDim2.new(0, 0, 0, line * lineSpace - lineSpace / 2)
  264.             lineText.Size = UDim2.new(0, x, 0, textSize)
  265.             lineText.RichText = true
  266.             lineText.Font = font
  267.             lineText.TextSize = textSize
  268.             lineText.BackgroundTransparency = 1
  269.             lineText.Text = lineStr
  270.             lineText.Parent = textFrame
  271.  
  272.             if i ~= #tableContents + 1 then
  273.                 local lineNumber = Instance.new("TextLabel")
  274.                 lineNumber.Text = line
  275.                 lineNumber.Font = font
  276.                 lineNumber.TextSize = textSize
  277.                 lineNumber.Size = UDim2.new(1, 0, 0, lineSpace)
  278.                 lineNumber.TextXAlignment = Enum.TextXAlignment.Right
  279.                 lineNumber.TextColor3 = lineNumberColor
  280.                 lineNumber.Position = UDim2.new(0, 0, 0, line * lineSpace - lineSpace / 2)
  281.                 lineNumber.BackgroundTransparency = 1
  282.                 lineNumber.Parent = lineNumbersFrame
  283.             end
  284.  
  285.             lineStr = ""
  286.             rawStr = ""
  287.             lastColor = nil
  288.             -- line += 1
  289.             line = line + 1
  290.             updateZIndex()
  291.             updateCanvasSize()
  292.             if line % 5 == 0 then
  293.                 RunService.Heartbeat:Wait()
  294.             end
  295.         elseif char.Char == " " then
  296.             lineStr = lineStr .. char.Char
  297.             rawStr = rawStr .. char.Char
  298.         elseif char.Char == "\t" then
  299.             lineStr = lineStr .. string.rep(" ", 4)
  300.             rawStr = rawStr .. char.Char
  301.         else
  302.             if char.Color == lastColor then
  303.                 lineStr = lineStr .. autoEscape(char.Char)
  304.             else
  305.                 lineStr = lineStr .. string.format('%s<font color="rgb(%d,%d,%d)">', lastColor and "</font>" or "", char.Color.R * 255, char.Color.G * 255, char.Color.B * 255)
  306.                 lineStr = lineStr .. autoEscape(char.Char)
  307.                 lastColor = char.Color
  308.             end
  309.             rawStr = rawStr .. char.Char
  310.         end
  311.  
  312.         -- local v = tableContents[i]
  313.         -- if not lines[v.Line] or #lines[v.Line] <= 200 then
  314.         --     local textBox = Instance.new("TextLabel")
  315.         --     local size = TextService:GetTextSize(v.Char, 14, Enum.Font.Arial, Vector2.new(math.huge, math.huge))
  316.         --     local lineSizeX = 0
  317.         --     if not lines[v.Line] then
  318.         --         lines[v.Line] = {}
  319.         --     end
  320.         --     if v.Char == "\n" then
  321.         --         textBox.Text = ""
  322.         --         game:GetService("RunService").Heartbeat:Wait()
  323.         --     elseif v.Char:match("\t") then
  324.         --         v.Char = "\t____"
  325.         --         textBox.Text = "\t____"
  326.         --         textBox.TextTransparency = 1
  327.         --     elseif v.Char:match(" ") then
  328.         --         v.Char = " |"
  329.         --         textBox.Text = " -"
  330.         --         textBox.TextTransparency = 1
  331.         --     else
  332.         --         textBox.Text = v.Char
  333.         --     end
  334.         --     if not lines[v.Line] then
  335.         --         lines[v.Line] = {}
  336.         --     end
  337.         --     for _, c in pairs(lines[v.Line]) do
  338.         --         lineSizeX = lineSizeX + TextService:GetTextSize(c.Char, 14, Enum.Font.Arial, Vector2.new(math.huge, math.huge)).X
  339.         --     end
  340.         --     textBox.TextColor3 = v.Color
  341.         --     textBox.Size = UDim2.new(0, size.X, 0, size.Y)
  342.         --     textBox.TextXAlignment = Enum.TextXAlignment.Left
  343.         --     textBox.TextYAlignment = Enum.TextYAlignment.Top
  344.         --     textBox.Position = UDim2.new(0, lineSizeX, 0, v.Line * lineSpace - lineSpace / 2)
  345.         --     textBox.BackgroundTransparency = 1
  346.         --     v.TextBox = textBox
  347.         --     table.insert(lines[v.Line], v)
  348.         --     textBox.Parent = textFrame
  349.         -- end
  350.     end
  351.     updateZIndex()
  352.     updateCanvasSize()
  353. end
  354.  
  355. function onFrameSizeChange()
  356.     local newSize = parentFrame.AbsoluteSize
  357.     scrollingFrame.Size = UDim2.new(0, newSize.X, 0, newSize.Y)
  358. end
  359.  
  360. function updateCanvasSize()
  361.     -- local codeSize = Vector2.new(TextService:GetTextSize(Highlight:getRaw(), textSize, font, Vector2.new(math.huge, math.huge)).X + 60, #lines * lineSpace + 60)
  362.     scrollingFrame.CanvasSize = UDim2.new(0, largestX, 0, line * lineSpace)
  363. end
  364.  
  365. function updateZIndex()
  366.     for _, v in pairs(parentFrame:GetDescendants()) do
  367.         if v:IsA("GuiObject") then
  368.             v.ZIndex = parentFrame.ZIndex
  369.         end
  370.     end
  371. end
  372.  
  373. -- PUBLIC METHODS --
  374.  
  375. --- Runs when a new object is instantiated
  376. --- @param frame userdata
  377. function Highlight:init(frame)
  378.     if typeof(frame) == "Instance" and frame:IsA("Frame") then
  379.         frame:ClearAllChildren()
  380.  
  381.         parentFrame = frame
  382.         scrollingFrame = Instance.new("ScrollingFrame")
  383.         textFrame = Instance.new("Frame")
  384.         lineNumbersFrame = Instance.new("Frame")
  385.  
  386.         local parentSize = frame.AbsoluteSize
  387.         scrollingFrame.Name = "HIGHLIGHT_IDE"
  388.         scrollingFrame.Size = UDim2.new(0, parentSize.X, 0, parentSize.Y)
  389.         scrollingFrame.BackgroundColor3 = backgroundColor
  390.         scrollingFrame.BorderSizePixel = 0
  391.         scrollingFrame.ScrollBarThickness = 4
  392.  
  393.         textFrame.Name = ""
  394.         textFrame.Size = UDim2.new(1, -40, 1, 0)
  395.         textFrame.Position = UDim2.new(0, 40, 0, 0)
  396.         textFrame.BackgroundTransparency = 1
  397.  
  398.         lineNumbersFrame.Name = ""
  399.         lineNumbersFrame.Size = UDim2.new(0, 25, 1, 0)
  400.         lineNumbersFrame.BackgroundTransparency = 1
  401.  
  402.         textFrame.Parent = scrollingFrame
  403.         lineNumbersFrame.Parent = scrollingFrame
  404.         scrollingFrame.Parent = parentFrame
  405.  
  406.         render()
  407.         parentFrame:GetPropertyChangedSignal("AbsoluteSize"):Connect(onFrameSizeChange)
  408.         parentFrame:GetPropertyChangedSignal("ZIndex"):Connect(updateZIndex)
  409.     else
  410.         error("Initialization error: argument " .. typeof(frame) .. " is not a Frame Instance")
  411.     end
  412. end
  413.  
  414. --- Sets the raw text of the code box (\n = new line, \t converted to spaces)
  415. --- @param raw string
  416. function Highlight:setRaw(raw)
  417.     raw = raw .. "\n"
  418.     tableContents = {}
  419.     local line = 1
  420.     for i = 1, #raw do
  421.         table.insert(tableContents, {
  422.             Char = raw:sub(i, i),
  423.             Color = defaultColor,
  424.             -- Line = line
  425.         })
  426.         if i % 1000 == 0 then
  427.             RunService.Heartbeat:Wait()
  428.         end
  429.         -- if raw:sub(i, i) == "\n" then
  430.         --     line = line + 1
  431.         -- end
  432.     end
  433.     render()
  434. end
  435.  
  436. --- Returns the (string) raw text of the code box (\n = new line). This includes placeholder characters so it should only be used internally.
  437. --- @return string
  438. function Highlight:getRaw()
  439.     local result = ""
  440.     for _, char in pairs(tableContents) do
  441.         result = result .. char.Char
  442.     end
  443.     return result
  444. end
  445.  
  446. --- Returns the (string) text of the code box (\n = new line)
  447. --- @return string
  448. function Highlight:getString()
  449.     local result = ""
  450.     for _, char in pairs(tableContents) do
  451.         result = result .. char.Char:sub(1, 1)
  452.     end
  453.     return result
  454. end
  455.  
  456. --- Returns the (char[]) array that holds all the lines in order as strings
  457. --- @return table[]
  458. function Highlight:getTable()
  459.     return tableContents
  460. end
  461.  
  462. --- Returns the (int) number of lines in the code box
  463. --- @return number
  464. function Highlight:getSize()
  465.     return #tableContents
  466. end
  467.  
  468. --- Returns the (string) line of the specified line number
  469. --- @param line number
  470. --- @return string
  471. function Highlight:getLine(line)
  472.     local currentline = 0
  473.     local rightLine = false
  474.     local result = ""
  475.     for _, v in pairs(tableContents) do
  476.         currentline = currentline + 1
  477.         if v.Char == "\n" and not rightLine then
  478.             rightLine = true
  479.         end
  480.         if rightLine and v.Char ~= "\n" then
  481.             result = result .. v.Char
  482.         elseif rightLine then
  483.             break
  484.         end
  485.     end
  486.     return result
  487. end
  488.  
  489. --- Replaces the specified line number with the specified string (\n will overwrite further lines)
  490. --- @param line number
  491. ---@param text string
  492. function Highlight:setLine(line, text)
  493.     if #tableContents and line >= tableContents[#tableContents].Line then
  494.         for i = tableContents[#tableContents].Line, line do
  495.             table.insert(tableContents, {
  496.                 Char = "\n",
  497.                 Line = i,
  498.                 Color = defaultColor
  499.             })
  500.             local str = Highlight:getRaw()
  501.             str = str:sub(0, #str) .. text
  502.             Highlight:setRaw(str)
  503.             return
  504.         end
  505.     elseif not #tableContents then
  506.         return
  507.     end
  508.     local str = Highlight:getRaw()
  509.     local lastStart = 0
  510.     local currentLine = 0
  511.     for i in gfind(str, "\n") do
  512.         currentLine = currentLine + 1
  513.         if line == currentLine then
  514.             str = str:sub(0, lastStart) .. text .. str:sub(i, #str)
  515.             Highlight:setRaw(str)
  516.             return
  517.         end
  518.     end
  519.     error("Unable to set line")
  520. end
  521.  
  522. --- Inserts a line made from the specified string and moves all existing lines down (\n will insert further lines)
  523. --- @param line number
  524. ---@param text string
  525. function Highlight:insertLine(line, text)
  526.     if #tableContents and line >= tableContents[#tableContents].Line then
  527.         Highlight:setLine(line, text)
  528.     elseif not #tableContents then
  529.         return
  530.     end
  531.     local str = Highlight:getRaw()
  532.     local lastStart = 0
  533.     local currentLine = 0
  534.     for i in gfind(str, "\n") do
  535.         currentLine = currentLine + 1
  536.         if line == currentLine then
  537.             str = str:sub(0, lastStart) .. "\n" .. text .. "\n" .. str:sub(i, #str)
  538.             Highlight:setRaw(str)
  539.             return
  540.         end
  541.     end
  542.     error("Unable to insert line")
  543. end
  544.  
  545. -- CONSTRUCTOR --
  546.  
  547. local Highlight = {}
  548. --- responsible for instantiation
  549. function Highlight.new(...)
  550.     local class = Highlight
  551.     local new = {}
  552.     class.__index = class
  553.     setmetatable(new, class)
  554.     new:init(...)
  555.     return new
  556. end
  557.  
  558. -- shuts down the previous instance of SimpleSpy
  559. if _G.SimpleSpyExecuted and type(_G.SimpleSpyShutdown) == "function" then
  560.     print(pcall(_G.SimpleSpyShutdown))
  561. end
  562.  
  563. local Players = game:GetService("Players")
  564. local CoreGui = game:GetService("CoreGui")
  565.  
  566. ---- GENERATED (kinda sorta mostly) BY GUI to LUA ----
  567.  
  568. -- Instances:
  569.  
  570. local SimpleSpy2 = Instance.new("ScreenGui")
  571. local Background = Instance.new("Frame")
  572. local LeftPanel = Instance.new("Frame")
  573. local LogList = Instance.new("ScrollingFrame")
  574. local UIListLayout = Instance.new("UIListLayout")
  575. local RemoteTemplate = Instance.new("Frame")
  576. local ColorBar = Instance.new("Frame")
  577. local Text = Instance.new("TextLabel")
  578. local Button = Instance.new("TextButton")
  579. local RightPanel = Instance.new("Frame")
  580. local CodeBox = Instance.new("Frame")
  581. local ScrollingFrame = Instance.new("ScrollingFrame")
  582. local UIGridLayout = Instance.new("UIGridLayout")
  583. local FunctionTemplate = Instance.new("Frame")
  584. local ColorBar_2 = Instance.new("Frame")
  585. local Text_2 = Instance.new("TextLabel")
  586. local Button_2 = Instance.new("TextButton")
  587. local TopBar = Instance.new("Frame")
  588. local Simple = Instance.new("TextButton")
  589. local CloseButton = Instance.new("TextButton")
  590. local ImageLabel = Instance.new("ImageLabel")
  591. local MaximizeButton = Instance.new("TextButton")
  592. local ImageLabel_2 = Instance.new("ImageLabel")
  593. local MinimizeButton = Instance.new("TextButton")
  594. local ImageLabel_3 = Instance.new("ImageLabel")
  595. local ToolTip = Instance.new("Frame")
  596. local TextLabel = Instance.new("TextLabel")
  597.  
  598. --Properties:
  599.  
  600. SimpleSpy2.Name = "SimpleSpy2"
  601. SimpleSpy2.ResetOnSpawn = false
  602.  
  603. Background.Name = "Background"
  604. Background.Parent = SimpleSpy2
  605. Background.BackgroundColor3 = Color3.new(1, 1, 1)
  606. Background.BackgroundTransparency = 1
  607. Background.Position = UDim2.new(0, 500, 0, 200)
  608. Background.Size = UDim2.new(0, 450, 0, 268)
  609.  
  610. LeftPanel.Name = "LeftPanel"
  611. LeftPanel.Parent = Background
  612. LeftPanel.BackgroundColor3 = Color3.fromRGB(53, 52, 55)
  613. LeftPanel.BorderSizePixel = 0
  614. LeftPanel.Position = UDim2.new(0, 0, 0, 19)
  615. LeftPanel.Size = UDim2.new(0, 131, 0, 249)
  616.  
  617. LogList.Name = "LogList"
  618. LogList.Parent = LeftPanel
  619. LogList.Active = true
  620. LogList.BackgroundColor3 = Color3.new(1, 1, 1)
  621. LogList.BackgroundTransparency = 1
  622. LogList.BorderSizePixel = 0
  623. LogList.Position = UDim2.new(0, 0, 0, 9)
  624. LogList.Size = UDim2.new(0, 131, 0, 232)
  625. LogList.CanvasSize = UDim2.new(0, 0, 0, 0)
  626. LogList.ScrollBarThickness = 4
  627.  
  628. UIListLayout.Parent = LogList
  629. UIListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
  630. UIListLayout.SortOrder = Enum.SortOrder.LayoutOrder
  631.  
  632. RemoteTemplate.Name = "RemoteTemplate"
  633. RemoteTemplate.Parent = LogList
  634. RemoteTemplate.BackgroundColor3 = Color3.new(1, 1, 1)
  635. RemoteTemplate.BackgroundTransparency = 1
  636. RemoteTemplate.Size = UDim2.new(0, 117, 0, 27)
  637.  
  638. ColorBar.Name = "ColorBar"
  639. ColorBar.Parent = RemoteTemplate
  640. ColorBar.BackgroundColor3 = Color3.fromRGB(255, 242, 0)
  641. ColorBar.BorderSizePixel = 0
  642. ColorBar.Position = UDim2.new(0, 0, 0, 1)
  643. ColorBar.Size = UDim2.new(0, 7, 0, 18)
  644. ColorBar.ZIndex = 2
  645.  
  646. Text.Name = "Text"
  647. Text.Parent = RemoteTemplate
  648. Text.BackgroundColor3 = Color3.new(1, 1, 1)
  649. Text.BackgroundTransparency = 1
  650. Text.Position = UDim2.new(0, 12, 0, 1)
  651. Text.Size = UDim2.new(0, 105, 0, 18)
  652. Text.ZIndex = 2
  653. Text.Font = Enum.Font.SourceSans
  654. Text.Text = "TEXT"
  655. Text.TextColor3 = Color3.new(1, 1, 1)
  656. Text.TextSize = 14
  657. Text.TextXAlignment = Enum.TextXAlignment.Left
  658. Text.TextWrapped = true
  659.  
  660. Button.Name = "Button"
  661. Button.Parent = RemoteTemplate
  662. Button.BackgroundColor3 = Color3.new(0, 0, 0)
  663. Button.BackgroundTransparency = 0.75
  664. Button.BorderColor3 = Color3.new(1, 1, 1)
  665. Button.Position = UDim2.new(0, 0, 0, 1)
  666. Button.Size = UDim2.new(0, 117, 0, 18)
  667. Button.AutoButtonColor = false
  668. Button.Font = Enum.Font.SourceSans
  669. Button.Text = ""
  670. Button.TextColor3 = Color3.new(0, 0, 0)
  671. Button.TextSize = 14
  672.  
  673. RightPanel.Name = "RightPanel"
  674. RightPanel.Parent = Background
  675. RightPanel.BackgroundColor3 = Color3.fromRGB(37, 36, 38)
  676. RightPanel.BorderSizePixel = 0
  677. RightPanel.Position = UDim2.new(0, 131, 0, 19)
  678. RightPanel.Size = UDim2.new(0, 319, 0, 249)
  679.  
  680. CodeBox.Name = "CodeBox"
  681. CodeBox.Parent = RightPanel
  682. CodeBox.BackgroundColor3 = Color3.new(0.0823529, 0.0745098, 0.0784314)
  683. CodeBox.BorderSizePixel = 0
  684. CodeBox.Size = UDim2.new(0, 319, 0, 119)
  685.  
  686. ScrollingFrame.Parent = RightPanel
  687. ScrollingFrame.Active = true
  688. ScrollingFrame.BackgroundColor3 = Color3.new(1, 1, 1)
  689. ScrollingFrame.BackgroundTransparency = 1
  690. ScrollingFrame.Position = UDim2.new(0, 0, 0.5, 0)
  691. ScrollingFrame.Size = UDim2.new(1, 0, 0.5, -9)
  692. ScrollingFrame.CanvasSize = UDim2.new(0, 0, 0, 0)
  693. ScrollingFrame.ScrollBarThickness = 4
  694.  
  695. UIGridLayout.Parent = ScrollingFrame
  696. UIGridLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
  697. UIGridLayout.SortOrder = Enum.SortOrder.LayoutOrder
  698. UIGridLayout.CellPadding = UDim2.new(0, 0, 0, 0)
  699. UIGridLayout.CellSize = UDim2.new(0, 94, 0, 27)
  700.  
  701. FunctionTemplate.Name = "FunctionTemplate"
  702. FunctionTemplate.Parent = ScrollingFrame
  703. FunctionTemplate.BackgroundColor3 = Color3.new(1, 1, 1)
  704. FunctionTemplate.BackgroundTransparency = 1
  705. FunctionTemplate.Size = UDim2.new(0, 117, 0, 23)
  706.  
  707. ColorBar_2.Name = "ColorBar"
  708. ColorBar_2.Parent = FunctionTemplate
  709. ColorBar_2.BackgroundColor3 = Color3.new(1, 1, 1)
  710. ColorBar_2.BorderSizePixel = 0
  711. ColorBar_2.Position = UDim2.new(0, 7, 0, 10)
  712. ColorBar_2.Size = UDim2.new(0, 7, 0, 18)
  713. ColorBar_2.ZIndex = 3
  714.  
  715. Text_2.Name = "Text"
  716. Text_2.Parent = FunctionTemplate
  717. Text_2.BackgroundColor3 = Color3.new(1, 1, 1)
  718. Text_2.BackgroundTransparency = 1
  719. Text_2.Position = UDim2.new(0, 19, 0, 10)
  720. Text_2.Size = UDim2.new(0, 69, 0, 18)
  721. Text_2.ZIndex = 2
  722. Text_2.Font = Enum.Font.SourceSans
  723. Text_2.Text = "TEXT"
  724. Text_2.TextColor3 = Color3.new(1, 1, 1)
  725. Text_2.TextSize = 14
  726. Text_2.TextStrokeColor3 = Color3.new(0.145098, 0.141176, 0.14902)
  727. Text_2.TextXAlignment = Enum.TextXAlignment.Left
  728. Text_2.TextWrapped = true
  729.  
  730. Button_2.Name = "Button"
  731. Button_2.Parent = FunctionTemplate
  732. Button_2.BackgroundColor3 = Color3.new(0, 0, 0)
  733. Button_2.BackgroundTransparency = 0.69999998807907
  734. Button_2.BorderColor3 = Color3.new(1, 1, 1)
  735. Button_2.Position = UDim2.new(0, 7, 0, 10)
  736. Button_2.Size = UDim2.new(0, 80, 0, 18)
  737. Button_2.AutoButtonColor = false
  738. Button_2.Font = Enum.Font.SourceSans
  739. Button_2.Text = ""
  740. Button_2.TextColor3 = Color3.new(0, 0, 0)
  741. Button_2.TextSize = 14
  742.  
  743. TopBar.Name = "TopBar"
  744. TopBar.Parent = Background
  745. TopBar.BackgroundColor3 = Color3.fromRGB(37, 35, 38)
  746. TopBar.BorderSizePixel = 0
  747. TopBar.Size = UDim2.new(0, 450, 0, 19)
  748.  
  749. Simple.Name = "Simple"
  750. Simple.Parent = TopBar
  751. Simple.BackgroundColor3 = Color3.new(1, 1, 1)
  752. Simple.AutoButtonColor = false
  753. Simple.BackgroundTransparency = 1
  754. Simple.Position = UDim2.new(0, 5, 0, 0)
  755. Simple.Size = UDim2.new(0, 57, 0, 18)
  756. Simple.Font = Enum.Font.SourceSansBold
  757. Simple.Text = "SimpleSpy"
  758. Simple.TextColor3 = Color3.new(1, 1, 1)
  759. Simple.TextSize = 14
  760. Simple.TextXAlignment = Enum.TextXAlignment.Left
  761.  
  762. CloseButton.Name = "CloseButton"
  763. CloseButton.Parent = TopBar
  764. CloseButton.BackgroundColor3 = Color3.new(0.145098, 0.141176, 0.14902)
  765. CloseButton.BorderSizePixel = 0
  766. CloseButton.Position = UDim2.new(1, -19, 0, 0)
  767. CloseButton.Size = UDim2.new(0, 19, 0, 19)
  768. CloseButton.Font = Enum.Font.SourceSans
  769. CloseButton.Text = ""
  770. CloseButton.TextColor3 = Color3.new(0, 0, 0)
  771. CloseButton.TextSize = 14
  772.  
  773. ImageLabel.Parent = CloseButton
  774. ImageLabel.BackgroundColor3 = Color3.new(1, 1, 1)
  775. ImageLabel.BackgroundTransparency = 1
  776. ImageLabel.Position = UDim2.new(0, 5, 0, 5)
  777. ImageLabel.Size = UDim2.new(0, 9, 0, 9)
  778. ImageLabel.Image = "http://www.roblox.com/asset/?id=5597086202"
  779.  
  780. MaximizeButton.Name = "MaximizeButton"
  781. MaximizeButton.Parent = TopBar
  782. MaximizeButton.BackgroundColor3 = Color3.new(0.145098, 0.141176, 0.14902)
  783. MaximizeButton.BorderSizePixel = 0
  784. MaximizeButton.Position = UDim2.new(1, -38, 0, 0)
  785. MaximizeButton.Size = UDim2.new(0, 19, 0, 19)
  786. MaximizeButton.Font = Enum.Font.SourceSans
  787. MaximizeButton.Text = ""
  788. MaximizeButton.TextColor3 = Color3.new(0, 0, 0)
  789. MaximizeButton.TextSize = 14
  790.  
  791. ImageLabel_2.Parent = MaximizeButton
  792. ImageLabel_2.BackgroundColor3 = Color3.new(1, 1, 1)
  793. ImageLabel_2.BackgroundTransparency = 1
  794. ImageLabel_2.Position = UDim2.new(0, 5, 0, 5)
  795. ImageLabel_2.Size = UDim2.new(0, 9, 0, 9)
  796. ImageLabel_2.Image = "http://www.roblox.com/asset/?id=5597108117"
  797.  
  798. MinimizeButton.Name = "MinimizeButton"
  799. MinimizeButton.Parent = TopBar
  800. MinimizeButton.BackgroundColor3 = Color3.new(0.145098, 0.141176, 0.14902)
  801. MinimizeButton.BorderSizePixel = 0
  802. MinimizeButton.Position = UDim2.new(1, -57, 0, 0)
  803. MinimizeButton.Size = UDim2.new(0, 19, 0, 19)
  804. MinimizeButton.Font = Enum.Font.SourceSans
  805. MinimizeButton.Text = ""
  806. MinimizeButton.TextColor3 = Color3.new(0, 0, 0)
  807. MinimizeButton.TextSize = 14
  808.  
  809. ImageLabel_3.Parent = MinimizeButton
  810. ImageLabel_3.BackgroundColor3 = Color3.new(1, 1, 1)
  811. ImageLabel_3.BackgroundTransparency = 1
  812. ImageLabel_3.Position = UDim2.new(0, 5, 0, 5)
  813. ImageLabel_3.Size = UDim2.new(0, 9, 0, 9)
  814. ImageLabel_3.Image = "http://www.roblox.com/asset/?id=5597105827"
  815.  
  816. ToolTip.Name = "ToolTip"
  817. ToolTip.Parent = SimpleSpy2
  818. ToolTip.BackgroundColor3 = Color3.fromRGB(26, 26, 26)
  819. ToolTip.BackgroundTransparency = 0.1
  820. ToolTip.BorderColor3 = Color3.new(1, 1, 1)
  821. ToolTip.Size = UDim2.new(0, 200, 0, 50)
  822. ToolTip.ZIndex = 3
  823. ToolTip.Visible = false
  824.  
  825. TextLabel.Parent = ToolTip
  826. TextLabel.BackgroundColor3 = Color3.new(1, 1, 1)
  827. TextLabel.BackgroundTransparency = 1
  828. TextLabel.Position = UDim2.new(0, 2, 0, 2)
  829. TextLabel.Size = UDim2.new(0, 196, 0, 46)
  830. TextLabel.ZIndex = 3
  831. TextLabel.Font = Enum.Font.SourceSans
  832. TextLabel.Text = "This is some slightly longer text."
  833. TextLabel.TextColor3 = Color3.new(1, 1, 1)
  834. TextLabel.TextSize = 14
  835. TextLabel.TextWrapped = true
  836. TextLabel.TextXAlignment = Enum.TextXAlignment.Left
  837. TextLabel.TextYAlignment = Enum.TextYAlignment.Top
  838.  
  839. -------------------------------------------------------------------------------
  840. -- init
  841. local RunService = game:GetService("RunService")
  842. local UserInputService = game:GetService("UserInputService")
  843. local TweenService = game:GetService("TweenService")
  844. local ContentProvider = game:GetService("ContentProvider")
  845. local TextService = game:GetService("TextService")
  846. local Mouse
  847.  
  848. local selectedColor = Color3.new(0.321569, 0.333333, 1)
  849. local deselectedColor = Color3.new(0.8, 0.8, 0.8)
  850. --- So things are descending
  851. local layoutOrderNum = 999999999
  852. --- Whether or not the gui is closing
  853. local mainClosing = false
  854. --- Whether or not the gui is closed (defaults to false)
  855. local closed = false
  856. --- Whether or not the sidebar is closing
  857. local sideClosing = false
  858. --- Whether or not the sidebar is closed (defaults to true but opens automatically on remote selection)
  859. local sideClosed = false
  860. --- Whether or not the code box is maximized (defaults to false)
  861. local maximized = false
  862. --- The event logs to be read from
  863. local logs = {}
  864. --- The event currently selected.Log (defaults to nil)
  865. local selected = nil
  866. --- The blacklist (can be a string name or the Remote Instance)
  867. local blacklist = {}
  868. --- The block list (can be a string name or the Remote Instance)
  869. local blocklist = {}
  870. --- Whether or not to add getNil function
  871. local getNil = false
  872. --- Array of remotes (and original functions) connected to
  873. local connectedRemotes = {}
  874. --- True = hookfunction, false = namecall
  875. local toggle = false
  876. local gm
  877. local original
  878. --- used to prevent recursives
  879. local prevTables = {}
  880. --- holds logs (for deletion)
  881. local remoteLogs = {}
  882. --- used for hookfunction
  883. local remoteEvent = Instance.new("RemoteEvent")
  884. --- used for hookfunction
  885. local remoteFunction = Instance.new("RemoteFunction")
  886. local originalEvent = remoteEvent.FireServer
  887. local originalFunction = remoteFunction.InvokeServer
  888. --- the maximum amount of remotes allowed in logs
  889. _G.SIMPLESPYCONFIG_MaxRemotes = 500
  890. --- how many spaces to indent
  891. local indent = 4
  892. --- used for task scheduler
  893. local scheduled = {}
  894. --- RBXScriptConnect of the task scheduler
  895. local schedulerconnect
  896. local SimpleSpy = {}
  897. local topstr = ""
  898. local bottomstr = ""
  899. local remotesFadeIn
  900. local rightFadeIn
  901. local codebox
  902. local p
  903. local getnilrequired = false
  904.  
  905. -- autoblock variables
  906. local autoblock = false
  907. local history = {}
  908. local excluding = {}
  909.  
  910. -- function info variables
  911. local funcEnabled = true
  912.  
  913. -- remote hooking/connecting api variables
  914. local remoteSignals = {}
  915. local remoteHooks = {}
  916.  
  917. -- original mouse icon
  918. local oldIcon
  919.  
  920. -- if mouse inside gui
  921. local mouseInGui = false
  922.  
  923. -- handy array of RBXScriptConnections to disconnect on shutdown
  924. local connections = {}
  925.  
  926. -- whether or not SimpleSpy uses 'getcallingscript()' to get the script (default is false because detection)
  927. local useGetCallingScript = false
  928.  
  929. --- used to enable/disable SimpleSpy's keyToString for remotes
  930. local keyToString = false
  931.  
  932. -- determines whether return values are recorded
  933. local recordReturnValues = false
  934.  
  935. -- functions
  936.  
  937. --- 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.remote:FireServer`)
  938. --- @param method string
  939. --- @param args any[]
  940. --- @return string
  941. function SimpleSpy:ArgsToString(method, args)
  942.     assert(typeof(method) == "string", "string expected, got " .. typeof(method))
  943.     assert(typeof(args) == "table", "table expected, got " .. typeof(args))
  944.     return v2v({ args = args }) .. "\n\n" .. method .. "(unpack(args))"
  945. end
  946.  
  947. --- Converts a value to variables with the specified index as the variable name (if nil/invalid then the name will be assigned automatically)
  948. --- @param t any[]
  949. --- @return string
  950. function SimpleSpy:TableToVars(t)
  951.     assert(typeof(t) == "table", "table expected, got " .. typeof(t))
  952.     return v2v(t)
  953. end
  954.  
  955. --- Converts a value to a variable with the specified `variablename` (if nil/invalid then the name will be assigned automatically)
  956. --- @param value any
  957. --- @return string
  958. function SimpleSpy:ValueToVar(value, variablename)
  959.     assert(variablename == nil or typeof(variablename) == "string", "string expected, got " .. typeof(variablename))
  960.     if not variablename then
  961.         variablename = 1
  962.     end
  963.     return v2v({ [variablename] = value })
  964. end
  965.  
  966. --- Converts any value to a string, cannot preserve function contents
  967. --- @param value any
  968. --- @return string
  969. function SimpleSpy:ValueToString(value)
  970.     return v2s(value)
  971. end
  972.  
  973. --- Generates the simplespy function info
  974. --- @param func function
  975. --- @return string
  976. function SimpleSpy:GetFunctionInfo(func)
  977.     assert(typeof(func) == "function", "Instance expected, got " .. typeof(func))
  978.     warn("Function info currently unavailable due to crashing in Synapse X")
  979.     return v2v({ functionInfo = {
  980.         info = debug.getinfo(func),
  981.         constants = debug.getconstants(func),
  982.     } })
  983. end
  984.  
  985. --- Gets the ScriptSignal for a specified remote being fired
  986. --- @param remote Instance
  987. function SimpleSpy:GetRemoteFiredSignal(remote)
  988.     assert(typeof(remote) == "Instance", "Instance expected, got " .. typeof(remote))
  989.     if not remoteSignals[remote] then
  990.         remoteSignals[remote] = newSignal()
  991.     end
  992.     return remoteSignals[remote]
  993. end
  994.  
  995. --- Allows for direct hooking of remotes **THIS CAN BE VERY DANGEROUS**
  996. --- @param remote Instance
  997. --- @param f function
  998. function SimpleSpy:HookRemote(remote, f)
  999.     assert(typeof(remote) == "Instance", "Instance expected, got " .. typeof(remote))
  1000.     assert(typeof(f) == "function", "function expected, got " .. typeof(f))
  1001.     remoteHooks[remote] = f
  1002. end
  1003.  
  1004. --- Blocks the specified remote instance/string
  1005. --- @param remote any
  1006. function SimpleSpy:BlockRemote(remote)
  1007.     assert(
  1008.         typeof(remote) == "Instance" or typeof(remote) == "string",
  1009.         "Instance | string expected, got " .. typeof(remote)
  1010.     )
  1011.     blocklist[remote] = true
  1012. end
  1013.  
  1014. --- Excludes the specified remote from logs (instance/string)
  1015. --- @param remote any
  1016. function SimpleSpy:ExcludeRemote(remote)
  1017.     assert(
  1018.         typeof(remote) == "Instance" or typeof(remote) == "string",
  1019.         "Instance | string expected, got " .. typeof(remote)
  1020.     )
  1021.     blacklist[remote] = true
  1022. end
  1023.  
  1024. --- Creates a new ScriptSignal that can be connected to and fired
  1025. --- @return table
  1026. function newSignal()
  1027.     local connected = {}
  1028.     return {
  1029.         Connect = function(self, f)
  1030.             assert(connected, "Signal is closed")
  1031.             connected[tostring(f)] = f
  1032.             return {
  1033.                 Connected = true,
  1034.                 Disconnect = function(self)
  1035.                     if not connected then
  1036.                         warn("Signal is already closed")
  1037.                     end
  1038.                     self.Connected = false
  1039.                     connected[tostring(f)] = nil
  1040.                 end,
  1041.             }
  1042.         end,
  1043.         Wait = function(self)
  1044.             local thread = coroutine.running()
  1045.             local connection
  1046.             connection = self:Connect(function()
  1047.                 connection:Disconnect()
  1048.                 if coroutine.status(thread) == "suspended" then
  1049.                     coroutine.resume(thread)
  1050.                 end
  1051.             end)
  1052.             coroutine.yield()
  1053.         end,
  1054.         Fire = function(self, ...)
  1055.             for _, f in pairs(connected) do
  1056.                 coroutine.wrap(f)(...)
  1057.             end
  1058.         end,
  1059.     }
  1060. end
  1061.  
  1062. --- Prevents remote spam from causing lag (clears logs after `_G.SIMPLESPYCONFIG_MaxRemotes` or 500 remotes)
  1063. function clean()
  1064.     local max = _G.SIMPLESPYCONFIG_MaxRemotes
  1065.     if not typeof(max) == "number" and math.floor(max) ~= max then
  1066.         max = 500
  1067.     end
  1068.     if #remoteLogs > max then
  1069.         for i = 100, #remoteLogs do
  1070.             local v = remoteLogs[i]
  1071.             if typeof(v[1]) == "RBXScriptConnection" then
  1072.                 v[1]:Disconnect()
  1073.             end
  1074.             if typeof(v[2]) == "Instance" then
  1075.                 v[2]:Destroy()
  1076.             end
  1077.         end
  1078.         local newLogs = {}
  1079.         for i = 1, 100 do
  1080.             table.insert(newLogs, remoteLogs[i])
  1081.         end
  1082.         remoteLogs = newLogs
  1083.     end
  1084. end
  1085.  
  1086. --- Scales the ToolTip to fit containing text
  1087. function scaleToolTip()
  1088.     local size = TextService:GetTextSize(
  1089.         TextLabel.Text,
  1090.         TextLabel.TextSize,
  1091.         TextLabel.Font,
  1092.         Vector2.new(196, math.huge)
  1093.     )
  1094.     TextLabel.Size = UDim2.new(0, size.X, 0, size.Y)
  1095.     ToolTip.Size = UDim2.new(0, size.X + 4, 0, size.Y + 4)
  1096. end
  1097.  
  1098. --- Executed when the toggle button (the SimpleSpy logo) is hovered over
  1099. function onToggleButtonHover()
  1100.     if not toggle then
  1101.         TweenService:Create(Simple, TweenInfo.new(0.5), { TextColor3 = Color3.fromRGB(252, 51, 51) }):Play()
  1102.     else
  1103.         TweenService:Create(Simple, TweenInfo.new(0.5), { TextColor3 = Color3.fromRGB(68, 206, 91) }):Play()
  1104.     end
  1105. end
  1106.  
  1107. --- Executed when the toggle button is unhovered over
  1108. function onToggleButtonUnhover()
  1109.     TweenService:Create(Simple, TweenInfo.new(0.5), { TextColor3 = Color3.fromRGB(255, 255, 255) }):Play()
  1110. end
  1111.  
  1112. --- Executed when the X button is hovered over
  1113. function onXButtonHover()
  1114.     TweenService:Create(CloseButton, TweenInfo.new(0.2), { BackgroundColor3 = Color3.fromRGB(255, 60, 60) }):Play()
  1115. end
  1116.  
  1117. --- Executed when the X button is unhovered over
  1118. function onXButtonUnhover()
  1119.     TweenService:Create(CloseButton, TweenInfo.new(0.2), { BackgroundColor3 = Color3.fromRGB(37, 36, 38) }):Play()
  1120. end
  1121.  
  1122. --- Toggles the remote spy method (when button clicked)
  1123. function onToggleButtonClick()
  1124.     if toggle then
  1125.         TweenService:Create(Simple, TweenInfo.new(0.5), { TextColor3 = Color3.fromRGB(252, 51, 51) }):Play()
  1126.     else
  1127.         TweenService:Create(Simple, TweenInfo.new(0.5), { TextColor3 = Color3.fromRGB(68, 206, 91) }):Play()
  1128.     end
  1129.     toggleSpyMethod()
  1130. end
  1131.  
  1132. --- Reconnects bringBackOnResize if the current viewport changes and also connects it initially
  1133. function connectResize()
  1134.     local lastCam = workspace.CurrentCamera:GetPropertyChangedSignal("ViewportSize"):Connect(bringBackOnResize)
  1135.     workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
  1136.         lastCam:Disconnect()
  1137.         if workspace.CurrentCamera then
  1138.             lastCam = workspace.CurrentCamera:GetPropertyChangedSignal("ViewportSize"):Connect(bringBackOnResize)
  1139.         end
  1140.     end)
  1141. end
  1142.  
  1143. --- Brings gui back if it gets lost offscreen (connected to the camera viewport changing)
  1144. function bringBackOnResize()
  1145.     validateSize()
  1146.     if sideClosed then
  1147.         minimizeSize()
  1148.     else
  1149.         maximizeSize()
  1150.     end
  1151.     local currentX = Background.AbsolutePosition.X
  1152.     local currentY = Background.AbsolutePosition.Y
  1153.     local viewportSize = workspace.CurrentCamera.ViewportSize
  1154.     if (currentX < 0) or (currentX > (viewportSize.X - (sideClosed and 131 or Background.AbsoluteSize.X))) then
  1155.         if currentX < 0 then
  1156.             currentX = 0
  1157.         else
  1158.             currentX = viewportSize.X - (sideClosed and 131 or Background.AbsoluteSize.X)
  1159.         end
  1160.     end
  1161.     if (currentY < 0) or (currentY > (viewportSize.Y - (closed and 19 or Background.AbsoluteSize.Y) - 36)) then
  1162.         if currentY < 0 then
  1163.             currentY = 0
  1164.         else
  1165.             currentY = viewportSize.Y - (closed and 19 or Background.AbsoluteSize.Y) - 36
  1166.         end
  1167.     end
  1168.     TweenService.Create(
  1169.         TweenService,
  1170.         Background,
  1171.         TweenInfo.new(0.1),
  1172.         { Position = UDim2.new(0, currentX, 0, currentY) }
  1173.     ):Play()
  1174. end
  1175.  
  1176. --- Drags gui (so long as mouse is held down)
  1177. --- @param input InputObject
  1178. function onBarInput(input)
  1179.     if input.UserInputType == Enum.UserInputType.MouseButton1 then
  1180.         local lastPos = UserInputService.GetMouseLocation(UserInputService)
  1181.         local mainPos = Background.AbsolutePosition
  1182.         local offset = mainPos - lastPos
  1183.         local currentPos = offset + lastPos
  1184.         RunService.BindToRenderStep(RunService, "drag", 1, function()
  1185.             local newPos = UserInputService.GetMouseLocation(UserInputService)
  1186.             if newPos ~= lastPos then
  1187.                 local currentX = (offset + newPos).X
  1188.                 local currentY = (offset + newPos).Y
  1189.                 local viewportSize = workspace.CurrentCamera.ViewportSize
  1190.                 if
  1191.                     (currentX < 0 and currentX < currentPos.X)
  1192.                     or (
  1193.                         currentX > (viewportSize.X - (sideClosed and 131 or TopBar.AbsoluteSize.X))
  1194.                         and currentX > currentPos.X
  1195.                     )
  1196.                 then
  1197.                     if currentX < 0 then
  1198.                         currentX = 0
  1199.                     else
  1200.                         currentX = viewportSize.X - (sideClosed and 131 or TopBar.AbsoluteSize.X)
  1201.                     end
  1202.                 end
  1203.                 if
  1204.                     (currentY < 0 and currentY < currentPos.Y)
  1205.                     or (
  1206.                         currentY > (viewportSize.Y - (closed and 19 or Background.AbsoluteSize.Y) - 36)
  1207.                         and currentY > currentPos.Y
  1208.                     )
  1209.                 then
  1210.                     if currentY < 0 then
  1211.                         currentY = 0
  1212.                     else
  1213.                         currentY = viewportSize.Y - (closed and 19 or Background.AbsoluteSize.Y) - 36
  1214.                     end
  1215.                 end
  1216.                 currentPos = Vector2.new(currentX, currentY)
  1217.                 lastPos = newPos
  1218.                 TweenService.Create(
  1219.                     TweenService,
  1220.                     Background,
  1221.                     TweenInfo.new(0.1),
  1222.                     { Position = UDim2.new(0, currentPos.X, 0, currentPos.Y) }
  1223.                 ):Play()
  1224.             end
  1225.             -- if input.UserInputState ~= Enum.UserInputState.Begin then
  1226.             --     RunService.UnbindFromRenderStep(RunService, "drag")
  1227.             -- end
  1228.         end)
  1229.         table.insert(
  1230.             connections,
  1231.             UserInputService.InputEnded:Connect(function(inputE)
  1232.                 if input == inputE then
  1233.                     RunService:UnbindFromRenderStep("drag")
  1234.                 end
  1235.             end)
  1236.         )
  1237.     end
  1238. end
  1239.  
  1240. --- Fades out the table of elements (and makes them invisible), returns a function to make them visible again
  1241. function fadeOut(elements)
  1242.     local data = {}
  1243.     for _, v in pairs(elements) do
  1244.         if typeof(v) == "Instance" and v:IsA("GuiObject") and v.Visible then
  1245.             coroutine.wrap(function()
  1246.                 data[v] = {
  1247.                     BackgroundTransparency = v.BackgroundTransparency,
  1248.                 }
  1249.                 TweenService:Create(v, TweenInfo.new(0.5), { BackgroundTransparency = 1 }):Play()
  1250.                 if v:IsA("TextBox") or v:IsA("TextButton") or v:IsA("TextLabel") then
  1251.                     data[v].TextTransparency = v.TextTransparency
  1252.                     TweenService:Create(v, TweenInfo.new(0.5), { TextTransparency = 1 }):Play()
  1253.                 elseif v:IsA("ImageButton") or v:IsA("ImageLabel") then
  1254.                     data[v].ImageTransparency = v.ImageTransparency
  1255.                     TweenService:Create(v, TweenInfo.new(0.5), { ImageTransparency = 1 }):Play()
  1256.                 end
  1257.                 wait(0.5)
  1258.                 v.Visible = false
  1259.                 for i, x in pairs(data[v]) do
  1260.                     v[i] = x
  1261.                 end
  1262.                 data[v] = true
  1263.             end)()
  1264.         end
  1265.     end
  1266.     return function()
  1267.         for i, _ in pairs(data) do
  1268.             coroutine.wrap(function()
  1269.                 local properties = {
  1270.                     BackgroundTransparency = i.BackgroundTransparency,
  1271.                 }
  1272.                 i.BackgroundTransparency = 1
  1273.                 TweenService
  1274.                     :Create(i, TweenInfo.new(0.5), { BackgroundTransparency = properties.BackgroundTransparency })
  1275.                     :Play()
  1276.                 if i:IsA("TextBox") or i:IsA("TextButton") or i:IsA("TextLabel") then
  1277.                     properties.TextTransparency = i.TextTransparency
  1278.                     i.TextTransparency = 1
  1279.                     TweenService
  1280.                         :Create(i, TweenInfo.new(0.5), { TextTransparency = properties.TextTransparency })
  1281.                         :Play()
  1282.                 elseif i:IsA("ImageButton") or i:IsA("ImageLabel") then
  1283.                     properties.ImageTransparency = i.ImageTransparency
  1284.                     i.ImageTransparency = 1
  1285.                     TweenService
  1286.                         :Create(i, TweenInfo.new(0.5), { ImageTransparency = properties.ImageTransparency })
  1287.                         :Play()
  1288.                 end
  1289.                 i.Visible = true
  1290.             end)()
  1291.         end
  1292.     end
  1293. end
  1294.  
  1295. --- Expands and minimizes the gui (closed is the toggle boolean)
  1296. function toggleMinimize(override)
  1297.     if mainClosing and not override or maximized then
  1298.         return
  1299.     end
  1300.     mainClosing = true
  1301.     closed = not closed
  1302.     if closed then
  1303.         if not sideClosed then
  1304.             toggleSideTray(true)
  1305.         end
  1306.         LeftPanel.Visible = true
  1307.         TweenService:Create(LeftPanel, TweenInfo.new(0.5), { Size = UDim2.new(0, 131, 0, 0) }):Play()
  1308.         wait(0.5)
  1309.         remotesFadeIn = fadeOut(LeftPanel:GetDescendants())
  1310.         wait(0.5)
  1311.     else
  1312.         TweenService:Create(LeftPanel, TweenInfo.new(0.5), { Size = UDim2.new(0, 131, 0, 249) }):Play()
  1313.         wait(0.5)
  1314.         if remotesFadeIn then
  1315.             remotesFadeIn()
  1316.             remotesFadeIn = nil
  1317.         end
  1318.         bringBackOnResize()
  1319.     end
  1320.     mainClosing = false
  1321. end
  1322.  
  1323. --- Expands and minimizes the sidebar (sideClosed is the toggle boolean)
  1324. function toggleSideTray(override)
  1325.     if sideClosing and not override or maximized then
  1326.         return
  1327.     end
  1328.     sideClosing = true
  1329.     sideClosed = not sideClosed
  1330.     if sideClosed then
  1331.         rightFadeIn = fadeOut(RightPanel:GetDescendants())
  1332.         wait(0.5)
  1333.         minimizeSize(0.5)
  1334.         wait(0.5)
  1335.         RightPanel.Visible = false
  1336.     else
  1337.         if closed then
  1338.             toggleMinimize(true)
  1339.         end
  1340.         RightPanel.Visible = true
  1341.         maximizeSize(0.5)
  1342.         wait(0.5)
  1343.         if rightFadeIn then
  1344.             rightFadeIn()
  1345.         end
  1346.         bringBackOnResize()
  1347.     end
  1348.     sideClosing = false
  1349. end
  1350.  
  1351. --- Expands code box to fit screen for more convenient viewing
  1352. function toggleMaximize()
  1353.     if not sideClosed and not maximized then
  1354.         maximized = true
  1355.         local disable = Instance.new("TextButton")
  1356.         local prevSize = UDim2.new(0, CodeBox.AbsoluteSize.X, 0, CodeBox.AbsoluteSize.Y)
  1357.         local prevPos = UDim2.new(0, CodeBox.AbsolutePosition.X, 0, CodeBox.AbsolutePosition.Y)
  1358.         disable.Size = UDim2.new(1, 0, 1, 0)
  1359.         disable.BackgroundColor3 = Color3.new()
  1360.         disable.BorderSizePixel = 0
  1361.         disable.Text = 0
  1362.         disable.ZIndex = 3
  1363.         disable.BackgroundTransparency = 1
  1364.         disable.AutoButtonColor = false
  1365.         CodeBox.ZIndex = 4
  1366.         CodeBox.Position = prevPos
  1367.         CodeBox.Size = prevSize
  1368.         TweenService
  1369.             :Create(
  1370.                 CodeBox,
  1371.                 TweenInfo.new(0.5),
  1372.                 { Size = UDim2.new(0.5, 0, 0.5, 0), Position = UDim2.new(0.25, 0, 0.25, 0) }
  1373.             )
  1374.             :Play()
  1375.         TweenService:Create(disable, TweenInfo.new(0.5), { BackgroundTransparency = 0.5 }):Play()
  1376.         disable.MouseButton1Click:Connect(function()
  1377.             if
  1378.                 UserInputService:GetMouseLocation().Y + 36 >= CodeBox.AbsolutePosition.Y
  1379.                 and UserInputService:GetMouseLocation().Y + 36 <= CodeBox.AbsolutePosition.Y + CodeBox.AbsoluteSize.Y
  1380.                 and UserInputService:GetMouseLocation().X >= CodeBox.AbsolutePosition.X
  1381.                 and UserInputService:GetMouseLocation().X <= CodeBox.AbsolutePosition.X + CodeBox.AbsoluteSize.X
  1382.             then
  1383.                 return
  1384.             end
  1385.             TweenService:Create(CodeBox, TweenInfo.new(0.5), { Size = prevSize, Position = prevPos }):Play()
  1386.             TweenService:Create(disable, TweenInfo.new(0.5), { BackgroundTransparency = 1 }):Play()
  1387.             maximized = false
  1388.             wait(0.5)
  1389.             disable:Destroy()
  1390.             CodeBox.Size = UDim2.new(1, 0, 0.5, 0)
  1391.             CodeBox.Position = UDim2.new(0, 0, 0, 0)
  1392.             CodeBox.ZIndex = 0
  1393.         end)
  1394.     end
  1395. end
  1396.  
  1397. --- Checks if cursor is within resize range
  1398. --- @param p Vector2
  1399. function isInResizeRange(p)
  1400.     local relativeP = p - Background.AbsolutePosition
  1401.     local range = 5
  1402.     if
  1403.         relativeP.X >= TopBar.AbsoluteSize.X - range
  1404.         and relativeP.Y >= Background.AbsoluteSize.Y - range
  1405.         and relativeP.X <= TopBar.AbsoluteSize.X
  1406.         and relativeP.Y <= Background.AbsoluteSize.Y
  1407.     then
  1408.         return true, "B"
  1409.     elseif relativeP.X >= TopBar.AbsoluteSize.X - range and relativeP.X <= Background.AbsoluteSize.X then
  1410.         return true, "X"
  1411.     elseif relativeP.Y >= Background.AbsoluteSize.Y - range and relativeP.Y <= Background.AbsoluteSize.Y then
  1412.         return true, "Y"
  1413.     end
  1414.     return false
  1415. end
  1416.  
  1417. --- Checks if cursor is within dragging range
  1418. --- @param p Vector2
  1419. function isInDragRange(p)
  1420.     local relativeP = p - Background.AbsolutePosition
  1421.     if
  1422.         relativeP.X <= TopBar.AbsoluteSize.X - CloseButton.AbsoluteSize.X * 3
  1423.         and relativeP.X >= 0
  1424.         and relativeP.Y <= TopBar.AbsoluteSize.Y
  1425.         and relativeP.Y >= 0
  1426.     then
  1427.         return true
  1428.     end
  1429.     return false
  1430. end
  1431.  
  1432. --- Called when mouse enters SimpleSpy
  1433. function mouseEntered()
  1434.     local existingCursor = SimpleSpy2:FindFirstChild("Cursor")
  1435.     while existingCursor do
  1436.         existingCursor:Destroy()
  1437.         existingCursor = SimpleSpy2:FindFirstChild("Cursor")
  1438.     end
  1439.     local customCursor = Instance.new("ImageLabel")
  1440.     customCursor.Name = "Cursor"
  1441.     customCursor.Size = UDim2.fromOffset(200, 200)
  1442.     customCursor.ZIndex = 1e5
  1443.     customCursor.BackgroundTransparency = 1
  1444.     customCursor.Image = ""
  1445.     customCursor.Parent = SimpleSpy2
  1446.     UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.ForceHide
  1447.     RunService:BindToRenderStep("SIMPLESPY_CURSOR", 1, function()
  1448.         if mouseInGui and _G.SimpleSpyExecuted then
  1449.             local mouseLocation = UserInputService:GetMouseLocation() - Vector2.new(0, 36)
  1450.             customCursor.Position = UDim2.fromOffset(
  1451.                 mouseLocation.X - customCursor.AbsoluteSize.X / 2,
  1452.                 mouseLocation.Y - customCursor.AbsoluteSize.Y / 2
  1453.             )
  1454.             local inRange, type = isInResizeRange(mouseLocation)
  1455.             if inRange and not sideClosed and not closed then
  1456.                 customCursor.Image = type == "B" and "rbxassetid://6065821980"
  1457.                     or type == "X" and "rbxassetid://6065821086"
  1458.                     or type == "Y" and "rbxassetid://6065821596"
  1459.             elseif inRange and not closed and type == "Y" or type == "B" then
  1460.                 customCursor.Image = "rbxassetid://6065821596"
  1461.             elseif customCursor.Image ~= "rbxassetid://6065775281" then
  1462.                 customCursor.Image = "rbxassetid://6065775281"
  1463.             end
  1464.         else
  1465.             UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.None
  1466.             customCursor:Destroy()
  1467.             RunService:UnbindFromRenderStep("SIMPLESPY_CURSOR")
  1468.         end
  1469.     end)
  1470. end
  1471.  
  1472. --- Called when mouse moves
  1473. function mouseMoved()
  1474.     local mousePos = UserInputService:GetMouseLocation() - Vector2.new(0, 36)
  1475.     if
  1476.         not closed
  1477.         and mousePos.X >= TopBar.AbsolutePosition.X
  1478.         and mousePos.X <= TopBar.AbsolutePosition.X + TopBar.AbsoluteSize.X
  1479.         and mousePos.Y >= Background.AbsolutePosition.Y
  1480.         and mousePos.Y <= Background.AbsolutePosition.Y + Background.AbsoluteSize.Y
  1481.     then
  1482.         if not mouseInGui then
  1483.             mouseInGui = true
  1484.             mouseEntered()
  1485.         end
  1486.     else
  1487.         mouseInGui = false
  1488.     end
  1489. end
  1490.  
  1491. --- Adjusts the ui elements to the 'Maximized' size
  1492. function maximizeSize(speed)
  1493.     if not speed then
  1494.         speed = 0.05
  1495.     end
  1496.     TweenService
  1497.         :Create(
  1498.             LeftPanel,
  1499.             TweenInfo.new(speed),
  1500.             { Size = UDim2.fromOffset(LeftPanel.AbsoluteSize.X, Background.AbsoluteSize.Y - TopBar.AbsoluteSize.Y) }
  1501.         )
  1502.         :Play()
  1503.     TweenService
  1504.         :Create(RightPanel, TweenInfo.new(speed), {
  1505.             Size = UDim2.fromOffset(
  1506.                 Background.AbsoluteSize.X - LeftPanel.AbsoluteSize.X,
  1507.                 Background.AbsoluteSize.Y - TopBar.AbsoluteSize.Y
  1508.             ),
  1509.         })
  1510.         :Play()
  1511.     TweenService
  1512.         :Create(
  1513.             TopBar,
  1514.             TweenInfo.new(speed),
  1515.             { Size = UDim2.fromOffset(Background.AbsoluteSize.X, TopBar.AbsoluteSize.Y) }
  1516.         )
  1517.         :Play()
  1518.     TweenService
  1519.         :Create(ScrollingFrame, TweenInfo.new(speed), {
  1520.             Size = UDim2.fromOffset(Background.AbsoluteSize.X - LeftPanel.AbsoluteSize.X, 110),
  1521.             Position = UDim2.fromOffset(0, Background.AbsoluteSize.Y - 119 - TopBar.AbsoluteSize.Y),
  1522.         })
  1523.         :Play()
  1524.     TweenService
  1525.         :Create(CodeBox, TweenInfo.new(speed), {
  1526.             Size = UDim2.fromOffset(
  1527.                 Background.AbsoluteSize.X - LeftPanel.AbsoluteSize.X,
  1528.                 Background.AbsoluteSize.Y - 119 - TopBar.AbsoluteSize.Y
  1529.             ),
  1530.         })
  1531.         :Play()
  1532.     TweenService
  1533.         :Create(
  1534.             LogList,
  1535.             TweenInfo.new(speed),
  1536.             { Size = UDim2.fromOffset(LogList.AbsoluteSize.X, Background.AbsoluteSize.Y - TopBar.AbsoluteSize.Y - 18) }
  1537.         )
  1538.         :Play()
  1539. end
  1540.  
  1541. --- Adjusts the ui elements to close the side
  1542. function minimizeSize(speed)
  1543.     if not speed then
  1544.         speed = 0.05
  1545.     end
  1546.     TweenService
  1547.         :Create(
  1548.             LeftPanel,
  1549.             TweenInfo.new(speed),
  1550.             { Size = UDim2.fromOffset(LeftPanel.AbsoluteSize.X, Background.AbsoluteSize.Y - TopBar.AbsoluteSize.Y) }
  1551.         )
  1552.         :Play()
  1553.     TweenService
  1554.         :Create(
  1555.             RightPanel,
  1556.             TweenInfo.new(speed),
  1557.             { Size = UDim2.fromOffset(0, Background.AbsoluteSize.Y - TopBar.AbsoluteSize.Y) }
  1558.         )
  1559.         :Play()
  1560.     TweenService
  1561.         :Create(
  1562.             TopBar,
  1563.             TweenInfo.new(speed),
  1564.             { Size = UDim2.fromOffset(LeftPanel.AbsoluteSize.X, TopBar.AbsoluteSize.Y) }
  1565.         )
  1566.         :Play()
  1567.     TweenService
  1568.         :Create(ScrollingFrame, TweenInfo.new(speed), {
  1569.             Size = UDim2.fromOffset(0, 119),
  1570.             Position = UDim2.fromOffset(0, Background.AbsoluteSize.Y - 119 - TopBar.AbsoluteSize.Y),
  1571.         })
  1572.         :Play()
  1573.     TweenService
  1574.         :Create(
  1575.             CodeBox,
  1576.             TweenInfo.new(speed),
  1577.             { Size = UDim2.fromOffset(0, Background.AbsoluteSize.Y - 119 - TopBar.AbsoluteSize.Y) }
  1578.         )
  1579.         :Play()
  1580.     TweenService
  1581.         :Create(
  1582.             LogList,
  1583.             TweenInfo.new(speed),
  1584.             { Size = UDim2.fromOffset(LogList.AbsoluteSize.X, Background.AbsoluteSize.Y - TopBar.AbsoluteSize.Y - 18) }
  1585.         )
  1586.         :Play()
  1587. end
  1588.  
  1589. --- Ensures size is within screensize limitations
  1590. function validateSize()
  1591.     local x, y = Background.AbsoluteSize.X, Background.AbsoluteSize.Y
  1592.     local screenSize = workspace.CurrentCamera.ViewportSize
  1593.     if x + Background.AbsolutePosition.X > screenSize.X then
  1594.         if screenSize.X - Background.AbsolutePosition.X >= 450 then
  1595.             x = screenSize.X - Background.AbsolutePosition.X
  1596.         else
  1597.             x = 450
  1598.         end
  1599.     elseif y + Background.AbsolutePosition.Y > screenSize.Y then
  1600.         if screenSize.X - Background.AbsolutePosition.Y >= 268 then
  1601.             y = screenSize.Y - Background.AbsolutePosition.Y
  1602.         else
  1603.             y = 268
  1604.         end
  1605.     end
  1606.     Background.Size = UDim2.fromOffset(x, y)
  1607. end
  1608.  
  1609. --- Called on user input while mouse in 'Background' frame
  1610. --- @param input InputObject
  1611. function backgroundUserInput(input)
  1612.     local mousePos = UserInputService:GetMouseLocation() - Vector2.new(0, 36)
  1613.     local inResizeRange, type = isInResizeRange(mousePos)
  1614.     if input.UserInputType == Enum.UserInputType.MouseButton1 and inResizeRange then
  1615.         local lastPos = UserInputService:GetMouseLocation()
  1616.         local offset = Background.AbsoluteSize - lastPos
  1617.         local currentPos = lastPos + offset
  1618.         RunService:BindToRenderStep("SIMPLESPY_RESIZE", 1, function()
  1619.             local newPos = UserInputService:GetMouseLocation()
  1620.             if newPos ~= lastPos then
  1621.                 local currentX = (newPos + offset).X
  1622.                 local currentY = (newPos + offset).Y
  1623.                 if currentX < 450 then
  1624.                     currentX = 450
  1625.                 end
  1626.                 if currentY < 268 then
  1627.                     currentY = 268
  1628.                 end
  1629.                 currentPos = Vector2.new(currentX, currentY)
  1630.                 Background.Size = UDim2.fromOffset(
  1631.                     (not sideClosed and not closed and (type == "X" or type == "B")) and currentPos.X
  1632.                         or Background.AbsoluteSize.X,
  1633.                     (--[[(not sideClosed or currentPos.X <= LeftPanel.AbsolutePosition.X + LeftPanel.AbsoluteSize.X) and]]not closed and (type == "Y" or type == "B"))
  1634.                             and currentPos.Y
  1635.                         or Background.AbsoluteSize.Y
  1636.                 )
  1637.                 validateSize()
  1638.                 if sideClosed then
  1639.                     minimizeSize()
  1640.                 else
  1641.                     maximizeSize()
  1642.                 end
  1643.                 lastPos = newPos
  1644.             end
  1645.         end)
  1646.         table.insert(
  1647.             connections,
  1648.             UserInputService.InputEnded:Connect(function(inputE)
  1649.                 if input == inputE then
  1650.                     RunService:UnbindFromRenderStep("SIMPLESPY_RESIZE")
  1651.                 end
  1652.             end)
  1653.         )
  1654.     elseif isInDragRange(mousePos) then
  1655.         onBarInput(input)
  1656.     end
  1657. end
  1658.  
  1659. --- Gets the player an instance is descended from
  1660. function getPlayerFromInstance(instance)
  1661.     for _, v in pairs(Players:GetPlayers()) do
  1662.         if v.Character and (instance:IsDescendantOf(v.Character) or instance == v.Character) then
  1663.             return v
  1664.         end
  1665.     end
  1666. end
  1667.  
  1668. --- Runs on MouseButton1Click of an event frame
  1669. function eventSelect(frame)
  1670.     if selected and selected.Log and selected.Log.Button then
  1671.         TweenService
  1672.             :Create(selected.Log.Button, TweenInfo.new(0.5), { BackgroundColor3 = Color3.fromRGB(0, 0, 0) })
  1673.             :Play()
  1674.         selected = nil
  1675.     end
  1676.     for _, v in pairs(logs) do
  1677.         if frame == v.Log then
  1678.             selected = v
  1679.         end
  1680.     end
  1681.     if selected and selected.Log then
  1682.         TweenService
  1683.             :Create(frame.Button, TweenInfo.new(0.5), { BackgroundColor3 = Color3.fromRGB(92, 126, 229) })
  1684.             :Play()
  1685.         codebox:setRaw(selected.GenScript)
  1686.     end
  1687.     if sideClosed then
  1688.         toggleSideTray()
  1689.     end
  1690. end
  1691.  
  1692. --- Updates the canvas size to fit the current amount of function buttons
  1693. function updateFunctionCanvas()
  1694.     ScrollingFrame.CanvasSize = UDim2.fromOffset(UIGridLayout.AbsoluteContentSize.X, UIGridLayout.AbsoluteContentSize.Y)
  1695. end
  1696.  
  1697. --- Updates the canvas size to fit the amount of current remotes
  1698. function updateRemoteCanvas()
  1699.     LogList.CanvasSize = UDim2.fromOffset(UIListLayout.AbsoluteContentSize.X, UIListLayout.AbsoluteContentSize.Y)
  1700. end
  1701.  
  1702. --- Allows for toggling of the tooltip and easy setting of le description
  1703. --- @param enable boolean
  1704. --- @param text string
  1705. function makeToolTip(enable, text)
  1706.     if enable then
  1707.         if ToolTip.Visible then
  1708.             ToolTip.Visible = false
  1709.             RunService:UnbindFromRenderStep("ToolTip")
  1710.         end
  1711.         local first = true
  1712.         RunService:BindToRenderStep("ToolTip", 1, function()
  1713.             local topLeft = Vector2.new(Mouse.X + 20, Mouse.Y + 20)
  1714.             local bottomRight = topLeft + ToolTip.AbsoluteSize
  1715.             if topLeft.X < 0 then
  1716.                 topLeft = Vector2.new(0, topLeft.Y)
  1717.             elseif bottomRight.X > workspace.CurrentCamera.ViewportSize.X then
  1718.                 topLeft = Vector2.new(workspace.CurrentCamera.ViewportSize.X - ToolTip.AbsoluteSize.X, topLeft.Y)
  1719.             end
  1720.             if topLeft.Y < 0 then
  1721.                 topLeft = Vector2.new(topLeft.X, 0)
  1722.             elseif bottomRight.Y > workspace.CurrentCamera.ViewportSize.Y - 35 then
  1723.                 topLeft = Vector2.new(topLeft.X, workspace.CurrentCamera.ViewportSize.Y - ToolTip.AbsoluteSize.Y - 35)
  1724.             end
  1725.             if topLeft.X <= Mouse.X and topLeft.Y <= Mouse.Y then
  1726.                 topLeft = Vector2.new(Mouse.X - ToolTip.AbsoluteSize.X - 2, Mouse.Y - ToolTip.AbsoluteSize.Y - 2)
  1727.             end
  1728.             if first then
  1729.                 ToolTip.Position = UDim2.fromOffset(topLeft.X, topLeft.Y)
  1730.                 first = false
  1731.             else
  1732.                 ToolTip:TweenPosition(UDim2.fromOffset(topLeft.X, topLeft.Y), "Out", "Linear", 0.1)
  1733.             end
  1734.         end)
  1735.         TextLabel.Text = text
  1736.         ToolTip.Visible = true
  1737.     else
  1738.         if ToolTip.Visible then
  1739.             ToolTip.Visible = false
  1740.             RunService:UnbindFromRenderStep("ToolTip")
  1741.         end
  1742.     end
  1743. end
  1744.  
  1745. --- Creates new function button (below codebox)
  1746. --- @param name string
  1747. ---@param description function
  1748. ---@param onClick function
  1749. function newButton(name, description, onClick)
  1750.     local button = FunctionTemplate:Clone()
  1751.     button.Text.Text = name
  1752.     button.Button.MouseEnter:Connect(function()
  1753.         makeToolTip(true, description())
  1754.     end)
  1755.     button.Button.MouseLeave:Connect(function()
  1756.         makeToolTip(false)
  1757.     end)
  1758.     button.AncestryChanged:Connect(function()
  1759.         makeToolTip(false)
  1760.     end)
  1761.     button.Button.MouseButton1Click:Connect(function(...)
  1762.         onClick(button, ...)
  1763.     end)
  1764.     button.Parent = ScrollingFrame
  1765.     updateFunctionCanvas()
  1766. end
  1767.  
  1768. --- Adds new Remote to logs
  1769. --- @param name string The name of the remote being logged
  1770. --- @param type string The type of the remote being logged (either 'function' or 'event')
  1771. --- @param args any
  1772. --- @param remote any
  1773. --- @param function_info string
  1774. --- @param blocked any
  1775. function newRemote(type, name, args, remote, function_info, blocked, src, returnValue)
  1776.     local remoteFrame = RemoteTemplate:Clone()
  1777.     remoteFrame.Text.Text = string.sub(name, 1, 50)
  1778.     remoteFrame.ColorBar.BackgroundColor3 = type == "event" and Color3.new(255, 242, 0) or Color3.fromRGB(99, 86, 245)
  1779.     local id = Instance.new("IntValue")
  1780.     id.Name = "ID"
  1781.     id.Value = #logs + 1
  1782.     id.Parent = remoteFrame
  1783.     local weakRemoteTable = setmetatable({ remote = remote }, { __mode = "v" })
  1784.     local log = {
  1785.         Name = name,
  1786.         Function = function_info,
  1787.         Remote = weakRemoteTable,
  1788.         Log = remoteFrame,
  1789.         Blocked = blocked,
  1790.         Source = src,
  1791.         GenScript = "-- Generating, please wait... (click to reload)\n-- (If this message persists, the remote args are likely extremely long)",
  1792.         ReturnValue = returnValue,
  1793.     }
  1794.     logs[#logs + 1] = log
  1795.     schedule(function()
  1796.         log.GenScript = genScript(remote, args)
  1797.         if blocked then
  1798.             logs[#logs].GenScript = "-- THIS REMOTE WAS PREVENTED FROM FIRING THE SERVER BY SIMPLESPY\n\n"
  1799.                 .. logs[#logs].GenScript
  1800.         end
  1801.     end)
  1802.     local connect = remoteFrame.Button.MouseButton1Click:Connect(function()
  1803.         eventSelect(remoteFrame)
  1804.     end)
  1805.     if layoutOrderNum < 1 then
  1806.         layoutOrderNum = 999999999
  1807.     end
  1808.     remoteFrame.LayoutOrder = layoutOrderNum
  1809.     layoutOrderNum = layoutOrderNum - 1
  1810.     remoteFrame.Parent = LogList
  1811.     table.insert(remoteLogs, 1, { connect, remoteFrame })
  1812.     clean()
  1813.     updateRemoteCanvas()
  1814. end
  1815.  
  1816. --- Generates a script from the provided arguments (first has to be remote path)
  1817. function genScript(remote, args)
  1818.     prevTables = {}
  1819.     local gen = ""
  1820.     if #args > 0 then
  1821.         if not pcall(function()
  1822.             gen = v2v({ args = args }) .. "\n"
  1823.         end) then
  1824.             gen = gen
  1825.                 .. "-- TableToString failure! Reverting to legacy functionality (results may vary)\nlocal args = {"
  1826.             if
  1827.                 not pcall(function()
  1828.                     for i, v in pairs(args) do
  1829.                         if type(i) ~= "Instance" and type(i) ~= "userdata" then
  1830.                             gen = gen .. "\n    [object] = "
  1831.                         elseif type(i) == "string" then
  1832.                             gen = gen .. '\n    ["' .. i .. '"] = '
  1833.                         elseif type(i) == "userdata" and typeof(i) ~= "Instance" then
  1834.                             gen = gen .. "\n    [" .. string.format("nil --[[%s]]", typeof(v)) .. ")] = "
  1835.                         elseif type(i) == "userdata" then
  1836.                             gen = gen .. "\n    [game." .. i:GetFullName() .. ")] = "
  1837.                         end
  1838.                         if type(v) ~= "Instance" and type(v) ~= "userdata" then
  1839.                             gen = gen .. "object"
  1840.                         elseif type(v) == "string" then
  1841.                             gen = gen .. '"' .. v .. '"'
  1842.                         elseif type(v) == "userdata" and typeof(v) ~= "Instance" then
  1843.                             gen = gen .. string.format("nil --[[%s]]", typeof(v))
  1844.                         elseif type(v) == "userdata" then
  1845.                             gen = gen .. "game." .. v:GetFullName()
  1846.                         end
  1847.                     end
  1848.                     gen = gen .. "\n}\n\n"
  1849.                 end)
  1850.             then
  1851.                 gen = gen .. "}\n-- Legacy tableToString failure! Unable to decompile."
  1852.             end
  1853.         end
  1854.         if not remote:IsDescendantOf(game) and not getnilrequired then
  1855.             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"
  1856.                 .. gen
  1857.         end
  1858.         if remote:IsA("RemoteEvent") then
  1859.             gen = gen .. v2s(remote) .. ":FireServer(unpack(args))"
  1860.         elseif remote:IsA("RemoteFunction") then
  1861.             gen = gen .. v2s(remote) .. ":InvokeServer(unpack(args))"
  1862.         end
  1863.     else
  1864.         if remote:IsA("RemoteEvent") then
  1865.             gen = gen .. v2s(remote) .. ":FireServer()"
  1866.         elseif remote:IsA("RemoteFunction") then
  1867.             gen = gen .. v2s(remote) .. ":InvokeServer()"
  1868.         end
  1869.     end
  1870.     gen = "-- Script generated by SimpleSpy - credits to exx#9394\n\n" .. gen
  1871.     prevTables = {}
  1872.     return gen
  1873. end
  1874.  
  1875. --- value-to-string: value, string (out), level (indentation), parent table, var name, is from tovar
  1876. function v2s(v, l, p, n, vtv, i, pt, path, tables, tI)
  1877.     if not tI then
  1878.         tI = { 0 }
  1879.     else
  1880.         -- tI[1] += 1
  1881.         tI[1] = tI[1] + 1
  1882.     end
  1883.     if typeof(v) == "number" then
  1884.         if v == math.huge then
  1885.             return "math.huge"
  1886.         elseif tostring(v):match("nan") then
  1887.             return "0/0 --[[NaN]]"
  1888.         end
  1889.         return tostring(v)
  1890.     elseif typeof(v) == "boolean" then
  1891.         return tostring(v)
  1892.     elseif typeof(v) == "string" then
  1893.         return formatstr(v, l)
  1894.     elseif typeof(v) == "function" then
  1895.         return f2s(v)
  1896.     elseif typeof(v) == "table" then
  1897.         return t2s(v, l, p, n, vtv, i, pt, path, tables, tI)
  1898.     elseif typeof(v) == "Instance" then
  1899.         return i2p(v)
  1900.     elseif typeof(v) == "userdata" then
  1901.         return "newproxy(true)"
  1902.     elseif type(v) == "userdata" then
  1903.         return u2s(v)
  1904.     elseif type(v) == "vector" then
  1905.         return string.format("Vector3.new(%s, %s, %s)", v2s(v.X), v2s(v.Y), v2s(v.Z))
  1906.     else
  1907.         return "nil --[[" .. typeof(v) .. "]]"
  1908.     end
  1909. end
  1910.  
  1911. --- value-to-variable
  1912. --- @param t any
  1913. function v2v(t)
  1914.     topstr = ""
  1915.     bottomstr = ""
  1916.     getnilrequired = false
  1917.     local ret = ""
  1918.     local count = 1
  1919.     for i, v in pairs(t) do
  1920.         if type(i) == "string" and i:match("^[%a_]+[%w_]*$") then
  1921.             ret = ret .. "local " .. i .. " = " .. v2s(v, nil, nil, i, true) .. "\n"
  1922.         elseif tostring(i):match("^[%a_]+[%w_]*$") then
  1923.             ret = ret
  1924.                 .. "local "
  1925.                 .. tostring(i):lower()
  1926.                 .. "_"
  1927.                 .. tostring(count)
  1928.                 .. " = "
  1929.                 .. v2s(v, nil, nil, tostring(i):lower() .. "_" .. tostring(count), true)
  1930.                 .. "\n"
  1931.         else
  1932.             ret = ret
  1933.                 .. "local "
  1934.                 .. type(v)
  1935.                 .. "_"
  1936.                 .. tostring(count)
  1937.                 .. " = "
  1938.                 .. v2s(v, nil, nil, type(v) .. "_" .. tostring(count), true)
  1939.                 .. "\n"
  1940.         end
  1941.         count = count + 1
  1942.     end
  1943.     if getnilrequired then
  1944.         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"
  1945.             .. topstr
  1946.     end
  1947.     if #topstr > 0 then
  1948.         ret = topstr .. "\n" .. ret
  1949.     end
  1950.     if #bottomstr > 0 then
  1951.         ret = ret .. bottomstr
  1952.     end
  1953.     return ret
  1954. end
  1955.  
  1956. --- table-to-string
  1957. --- @param t table
  1958. --- @param l number
  1959. --- @param p table
  1960. --- @param n string
  1961. --- @param vtv boolean
  1962. --- @param i any
  1963. --- @param pt table
  1964. --- @param path string
  1965. --- @param tables table
  1966. --- @param tI table
  1967. function t2s(t, l, p, n, vtv, i, pt, path, tables, tI)
  1968.     local globalIndex = table.find(getgenv(), t) -- checks if table is a global
  1969.     if type(globalIndex) == "string" then
  1970.         return globalIndex
  1971.     end
  1972.     if not tI then
  1973.         tI = { 0 }
  1974.     end
  1975.     if not path then -- sets path to empty string (so it doesn't have to manually provided every time)
  1976.         path = ""
  1977.     end
  1978.     if not l then -- sets the level to 0 (for indentation) and tables for logging tables it already serialized
  1979.         l = 0
  1980.         tables = {}
  1981.     end
  1982.     if not p then -- p is the previous table but doesn't really matter if it's the first
  1983.         p = t
  1984.     end
  1985.     for _, v in pairs(tables) do -- checks if the current table has been serialized before
  1986.         if n and rawequal(v, t) then
  1987.             bottomstr = bottomstr
  1988.                 .. "\n"
  1989.                 .. tostring(n)
  1990.                 .. tostring(path)
  1991.                 .. " = "
  1992.                 .. tostring(n)
  1993.                 .. tostring(({ v2p(v, p) })[2])
  1994.             return "{} --[[DUPLICATE]]"
  1995.         end
  1996.     end
  1997.     table.insert(tables, t) -- logs table to past tables
  1998.     local s = "{" -- start of serialization
  1999.     local size = 0
  2000.     l = l + indent -- set indentation level
  2001.     for k, v in pairs(t) do
  2002.         size = size + 1
  2003.         if size > (_G.SimpleSpyMaxTableSize or 1000) then
  2004.             s = s
  2005.                 .. "\n"
  2006.                 .. string.rep(" ", l)
  2007.                 .. "-- MAXIMUM TABLE SIZE REACHED, CHANGE '_G.SimpleSpyMaxTableSize' TO ADJUST MAXIMUM SIZE "
  2008.             break
  2009.         end
  2010.         if not rawequal(k, t) then -- Inverted condition to avoid using continue
  2011.             local currentPath = ""
  2012.             if type(k) == "string" and k:match("^[%a_]+[%w_]*$") then
  2013.                 currentPath = "." .. k
  2014.             else
  2015.                 currentPath = "[" .. k2s(k, l, p, n, vtv, k, t, path .. currentPath, tables, tI) .. "]"
  2016.             end
  2017.             if size % 100 == 0 then
  2018.                 scheduleWait()
  2019.             end
  2020.             s = s
  2021.                 .. "\n"
  2022.                 .. string.rep(" ", l)
  2023.                 .. "["
  2024.                 .. k2s(k, l, p, n, vtv, k, t, path .. currentPath, tables, tI)
  2025.                 .. "] = "
  2026.                 .. v2s(v, l, p, n, vtv, k, t, path .. currentPath, tables, tI)
  2027.                 .. ","
  2028.         else
  2029.             bottomstr = bottomstr
  2030.                 .. "\n"
  2031.                 .. tostring(n)
  2032.                 .. tostring(path)
  2033.                 .. "["
  2034.                 .. tostring(n)
  2035.                 .. tostring(path)
  2036.                 .. "]"
  2037.                 .. " = "
  2038.                 .. (
  2039.                     rawequal(v, k) and tostring(n) .. tostring(path)
  2040.                     or v2s(v, l, p, n, vtv, k, t, path .. "[" .. tostring(n) .. tostring(path) .. "]", tables)
  2041.                 )
  2042.             size = size - 1
  2043.         end
  2044.     end
  2045.     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...)
  2046.         s = s:sub(1, #s - 1)
  2047.     end
  2048.     if size > 0 then -- cleanly indents the last curly bracket
  2049.         s = s .. "\n" .. string.rep(" ", l - indent)
  2050.     end
  2051.     return s .. "}"
  2052. end
  2053.  
  2054. --- key-to-string
  2055. function k2s(v, ...)
  2056.     if keyToString then
  2057.         if typeof(v) == "userdata" and getrawmetatable(v) then
  2058.             return string.format(
  2059.                 '"<void> (%s)" --[[Potentially hidden data (tostring in SimpleSpy:HookRemote/GetRemoteFiredSignal at your own risk)]]',
  2060.                 safetostring(v)
  2061.             )
  2062.         elseif typeof(v) == "userdata" then
  2063.             return string.format('"<void> (%s)"', safetostring(v))
  2064.         elseif type(v) == "userdata" and typeof(v) ~= "Instance" then
  2065.             return string.format('"<%s> (%s)"', typeof(v), tostring(v))
  2066.         elseif type(v) == "function" then
  2067.             return string.format('"<Function> (%s)"', tostring(v))
  2068.         end
  2069.     end
  2070.     return v2s(v, ...)
  2071. end
  2072.  
  2073. --- function-to-string
  2074. function f2s(f)
  2075.     for k, x in pairs(getgenv()) do
  2076.         local isgucci, gpath
  2077.         if rawequal(x, f) then
  2078.             isgucci, gpath = true, ""
  2079.         elseif type(x) == "table" then
  2080.             isgucci, gpath = v2p(f, x)
  2081.         end
  2082.         if isgucci and type(k) ~= "function" then
  2083.             if type(k) == "string" and k:match("^[%a_]+[%w_]*$") then
  2084.                 return k .. gpath
  2085.             else
  2086.                 return "getgenv()[" .. v2s(k) .. "]" .. gpath
  2087.             end
  2088.         end
  2089.     end
  2090.     if funcEnabled and debug.getinfo(f).name:match("^[%a_]+[%w_]*$") then
  2091.         return "function()end --[[" .. debug.getinfo(f).name .. "]]"
  2092.     end
  2093.     return "function()end --[[" .. tostring(f) .. "]]"
  2094. end
  2095.  
  2096. --- instance-to-path
  2097. --- @param i userdata
  2098. function i2p(i)
  2099.     local player = getplayer(i)
  2100.     local parent = i
  2101.     local out = ""
  2102.     if parent == nil then
  2103.         return "nil"
  2104.     elseif player then
  2105.         while true do
  2106.             if parent and parent == player.Character then
  2107.                 if player == Players.LocalPlayer then
  2108.                     return 'game:GetService("Players").LocalPlayer.Character' .. out
  2109.                 else
  2110.                     return i2p(player) .. ".Character" .. out
  2111.                 end
  2112.             else
  2113.                 if parent.Name:match("[%a_]+[%w+]*") ~= parent.Name then
  2114.                     out = ":FindFirstChild(" .. formatstr(parent.Name) .. ")" .. out
  2115.                 else
  2116.                     out = "." .. parent.Name .. out
  2117.                 end
  2118.             end
  2119.             parent = parent.Parent
  2120.         end
  2121.     elseif parent ~= game then
  2122.         while true do
  2123.             if parent and parent.Parent == game then
  2124.                 local service = game:FindService(parent.ClassName)
  2125.                 if service then
  2126.                     if parent.ClassName == "Workspace" then
  2127.                         return "workspace" .. out
  2128.                     else
  2129.                         return 'game:GetService("' .. service.ClassName .. '")' .. out
  2130.                     end
  2131.                 else
  2132.                     if parent.Name:match("[%a_]+[%w_]*") then
  2133.                         return "game." .. parent.Name .. out
  2134.                     else
  2135.                         return "game:FindFirstChild(" .. formatstr(parent.Name) .. ")" .. out
  2136.                     end
  2137.                 end
  2138.             elseif parent.Parent == nil then
  2139.                 getnilrequired = true
  2140.                 return "getNil(" .. formatstr(parent.Name) .. ', "' .. parent.ClassName .. '")' .. out
  2141.             elseif parent == Players.LocalPlayer then
  2142.                 out = ".LocalPlayer" .. out
  2143.             else
  2144.                 if parent.Name:match("[%a_]+[%w_]*") ~= parent.Name then
  2145.                     out = ":FindFirstChild(" .. formatstr(parent.Name) .. ")" .. out
  2146.                 else
  2147.                     out = "." .. parent.Name .. out
  2148.                 end
  2149.             end
  2150.             parent = parent.Parent
  2151.         end
  2152.     else
  2153.         return "game"
  2154.     end
  2155. end
  2156.  
  2157. --- userdata-to-string: userdata
  2158. --- @param u userdata
  2159. function u2s(u)
  2160.     if typeof(u) == "TweenInfo" then
  2161.         -- TweenInfo
  2162.         return "TweenInfo.new("
  2163.             .. tostring(u.Time)
  2164.             .. ", Enum.EasingStyle."
  2165.             .. tostring(u.EasingStyle)
  2166.             .. ", Enum.EasingDirection."
  2167.             .. tostring(u.EasingDirection)
  2168.             .. ", "
  2169.             .. tostring(u.RepeatCount)
  2170.             .. ", "
  2171.             .. tostring(u.Reverses)
  2172.             .. ", "
  2173.             .. tostring(u.DelayTime)
  2174.             .. ")"
  2175.     elseif typeof(u) == "Ray" then
  2176.         -- Ray
  2177.         return "Ray.new(" .. u2s(u.Origin) .. ", " .. u2s(u.Direction) .. ")"
  2178.     elseif typeof(u) == "NumberSequence" then
  2179.         -- NumberSequence
  2180.         local ret = "NumberSequence.new("
  2181.         for i, v in pairs(u.KeyPoints) do
  2182.             ret = ret .. tostring(v)
  2183.             if i < #u.Keypoints then
  2184.                 ret = ret .. ", "
  2185.             end
  2186.         end
  2187.         return ret .. ")"
  2188.     elseif typeof(u) == "DockWidgetPluginGuiInfo" then
  2189.         -- DockWidgetPluginGuiInfo
  2190.         return "DockWidgetPluginGuiInfo.new(Enum.InitialDockState" .. tostring(u) .. ")"
  2191.     elseif typeof(u) == "ColorSequence" then
  2192.         -- ColorSequence
  2193.         local ret = "ColorSequence.new("
  2194.         for i, v in pairs(u.KeyPoints) do
  2195.             ret = ret .. "Color3.new(" .. tostring(v) .. ")"
  2196.             if i < #u.Keypoints then
  2197.                 ret = ret .. ", "
  2198.             end
  2199.         end
  2200.         return ret .. ")"
  2201.     elseif typeof(u) == "BrickColor" then
  2202.         -- BrickColor
  2203.         return "BrickColor.new(" .. tostring(u.Number) .. ")"
  2204.     elseif typeof(u) == "NumberRange" then
  2205.         -- NumberRange
  2206.         return "NumberRange.new(" .. tostring(u.Min) .. ", " .. tostring(u.Max) .. ")"
  2207.     elseif typeof(u) == "Region3" then
  2208.         -- Region3
  2209.         local center = u.CFrame.Position
  2210.         local size = u.CFrame.Size
  2211.         local vector1 = center - size / 2
  2212.         local vector2 = center + size / 2
  2213.         return "Region3.new(" .. u2s(vector1) .. ", " .. u2s(vector2) .. ")"
  2214.     elseif typeof(u) == "Faces" then
  2215.         -- Faces
  2216.         local faces = {}
  2217.         if u.Top then
  2218.             table.insert(faces, "Enum.NormalId.Top")
  2219.         end
  2220.         if u.Bottom then
  2221.             table.insert(faces, "Enum.NormalId.Bottom")
  2222.         end
  2223.         if u.Left then
  2224.             table.insert(faces, "Enum.NormalId.Left")
  2225.         end
  2226.         if u.Right then
  2227.             table.insert(faces, "Enum.NormalId.Right")
  2228.         end
  2229.         if u.Back then
  2230.             table.insert(faces, "Enum.NormalId.Back")
  2231.         end
  2232.         if u.Front then
  2233.             table.insert(faces, "Enum.NormalId.Front")
  2234.         end
  2235.         return "Faces.new(" .. table.concat(faces, ", ") .. ")"
  2236.     elseif typeof(u) == "EnumItem" then
  2237.         return tostring(u)
  2238.     elseif typeof(u) == "Enums" then
  2239.         return "Enum"
  2240.     elseif typeof(u) == "Enum" then
  2241.         return "Enum." .. tostring(u)
  2242.     elseif typeof(u) == "RBXScriptSignal" then
  2243.         return "nil --[[RBXScriptSignal]]"
  2244.     elseif typeof(u) == "Vector3" then
  2245.         return string.format("Vector3.new(%s, %s, %s)", v2s(u.X), v2s(u.Y), v2s(u.Z))
  2246.     elseif typeof(u) == "CFrame" then
  2247.         local xAngle, yAngle, zAngle = u:ToEulerAnglesXYZ()
  2248.         return string.format(
  2249.             "CFrame.new(%s, %s, %s) * CFrame.Angles(%s, %s, %s)",
  2250.             v2s(u.X),
  2251.             v2s(u.Y),
  2252.             v2s(u.Z),
  2253.             v2s(xAngle),
  2254.             v2s(yAngle),
  2255.             v2s(zAngle)
  2256.         )
  2257.     elseif typeof(u) == "DockWidgetPluginGuiInfo" then
  2258.         return string.format(
  2259.             "DockWidgetPluginGuiInfo(%s, %s, %s, %s, %s, %s, %s)",
  2260.             "Enum.InitialDockState.Right",
  2261.             v2s(u.InitialEnabled),
  2262.             v2s(u.InitialEnabledShouldOverrideRestore),
  2263.             v2s(u.FloatingXSize),
  2264.             v2s(u.FloatingYSize),
  2265.             v2s(u.MinWidth),
  2266.             v2s(u.MinHeight)
  2267.         )
  2268.     elseif typeof(u) == "PathWaypoint" then
  2269.         return string.format("PathWaypoint.new(%s, %s)", v2s(u.Position), v2s(u.Action))
  2270.     elseif typeof(u) == "UDim" then
  2271.         return string.format("UDim.new(%s, %s)", v2s(u.Scale), v2s(u.Offset))
  2272.     elseif typeof(u) == "UDim2" then
  2273.         return string.format(
  2274.             "UDim2.new(%s, %s, %s, %s)",
  2275.             v2s(u.X.Scale),
  2276.             v2s(u.X.Offset),
  2277.             v2s(u.Y.Scale),
  2278.             v2s(u.Y.Offset)
  2279.         )
  2280.     elseif typeof(u) == "Rect" then
  2281.         return string.format("Rect.new(%s, %s)", v2s(u.Min), v2s(u.Max))
  2282.     else
  2283.         return string.format("nil --[[%s]]", typeof(u))
  2284.     end
  2285. end
  2286.  
  2287. --- Gets the player an instance is descended from
  2288. function getplayer(instance)
  2289.     for _, v in pairs(Players:GetPlayers()) do
  2290.         if v.Character and (instance:IsDescendantOf(v.Character) or instance == v.Character) then
  2291.             return v
  2292.         end
  2293.     end
  2294. end
  2295.  
  2296. --- value-to-path (in table)
  2297. function v2p(x, t, path, prev)
  2298.     if not path then
  2299.         path = ""
  2300.     end
  2301.     if not prev then
  2302.         prev = {}
  2303.     end
  2304.     if rawequal(x, t) then
  2305.         return true, ""
  2306.     end
  2307.     for i, v in pairs(t) do
  2308.         if rawequal(v, x) then
  2309.             if type(i) == "string" and i:match("^[%a_]+[%w_]*$") then
  2310.                 return true, (path .. "." .. i)
  2311.             else
  2312.                 return true, (path .. "[" .. v2s(i) .. "]")
  2313.             end
  2314.         end
  2315.         if type(v) == "table" then
  2316.             local duplicate = false
  2317.             for _, y in pairs(prev) do
  2318.                 if rawequal(y, v) then
  2319.                     duplicate = true
  2320.                 end
  2321.             end
  2322.             if not duplicate then
  2323.                 table.insert(prev, t)
  2324.                 local found
  2325.                 found, p = v2p(x, v, path, prev)
  2326.                 if found then
  2327.                     if type(i) == "string" and i:match("^[%a_]+[%w_]*$") then
  2328.                         return true, "." .. i .. p
  2329.                     else
  2330.                         return true, "[" .. v2s(i) .. "]" .. p
  2331.                     end
  2332.                 end
  2333.             end
  2334.         end
  2335.     end
  2336.     return false, ""
  2337. end
  2338.  
  2339. --- format s: string, byte encrypt (for weird symbols)
  2340. function formatstr(s, indentation)
  2341.     if not indentation then
  2342.         indentation = 0
  2343.     end
  2344.     local handled, reachedMax = handlespecials(s, indentation)
  2345.     return '"'
  2346.         .. handled
  2347.         .. '"'
  2348.         .. (
  2349.             reachedMax
  2350.                 and " --[[ MAXIMUM STRING SIZE REACHED, CHANGE '_G.SimpleSpyMaxStringSize' TO ADJUST MAXIMUM SIZE ]]"
  2351.             or ""
  2352.         )
  2353. end
  2354.  
  2355. --- Adds \'s to the text as a replacement to whitespace chars and other things because string.format can't yayeet
  2356. function handlespecials(value, indentation)
  2357.     local buildStr = {}
  2358.     local i = 1
  2359.     local char = string.sub(value, i, i)
  2360.     local indentStr
  2361.     while char ~= "" do
  2362.         if char == '"' then
  2363.             buildStr[i] = '\\"'
  2364.         elseif char == "\\" then
  2365.             buildStr[i] = "\\\\"
  2366.         elseif char == "\n" then
  2367.             buildStr[i] = "\\n"
  2368.         elseif char == "\t" then
  2369.             buildStr[i] = "\\t"
  2370.         elseif string.byte(char) > 126 or string.byte(char) < 32 then
  2371.             buildStr[i] = string.format("\\%d", string.byte(char))
  2372.         else
  2373.             buildStr[i] = char
  2374.         end
  2375.         i = i + 1
  2376.         char = string.sub(value, i, i)
  2377.         if i % 200 == 0 then
  2378.             indentStr = indentStr or string.rep(" ", indentation + indent)
  2379.             table.move({ '"\n', indentStr, '... "' }, 1, 3, i, buildStr)
  2380.             -- i += 3
  2381.             i = i + 3
  2382.         end
  2383.     end
  2384.     return table.concat(buildStr)
  2385. end
  2386.  
  2387. -- safe (ish) tostring
  2388. function safetostring(v)
  2389.     if typeof(v) == "userdata" or type(v) == "table" then
  2390.         local mt = getrawmetatable(v)
  2391.         local badtostring = mt and rawget(mt, "__tostring")
  2392.         if mt and badtostring then
  2393.             rawset(mt, "__tostring", nil)
  2394.             local out = tostring(v)
  2395.             rawset(mt, "__tostring", badtostring)
  2396.             return out
  2397.         end
  2398.     end
  2399.     return tostring(v)
  2400. end
  2401.  
  2402. --- finds script from 'src' from getinfo, returns nil if not found
  2403. --- @param src string
  2404. function getScriptFromSrc(src)
  2405.     local realPath
  2406.     local runningTest
  2407.     --- @type number
  2408.     local s, e
  2409.     local match = false
  2410.     if src:sub(1, 1) == "=" then
  2411.         realPath = game
  2412.         s = 2
  2413.     else
  2414.         runningTest = src:sub(2, e and e - 1 or -1)
  2415.         for _, v in pairs(getnilinstances()) do
  2416.             if v.Name == runningTest then
  2417.                 realPath = v
  2418.                 break
  2419.             end
  2420.         end
  2421.         s = #runningTest + 1
  2422.     end
  2423.     if realPath then
  2424.         e = src:sub(s, -1):find("%.")
  2425.         local i = 0
  2426.         repeat
  2427.             -- i += 1
  2428.             i = i + 1
  2429.             if not e then
  2430.                 runningTest = src:sub(s, -1)
  2431.                 local test = realPath.FindFirstChild(realPath, runningTest)
  2432.                 if test then
  2433.                     realPath = test
  2434.                 end
  2435.                 match = true
  2436.             else
  2437.                 runningTest = src:sub(s, e)
  2438.                 local test = realPath.FindFirstChild(realPath, runningTest)
  2439.                 local yeOld = e
  2440.                 if test then
  2441.                     realPath = test
  2442.                     s = e + 2
  2443.                     e = src:sub(e + 2, -1):find("%.")
  2444.                     e = e and e + yeOld or e
  2445.                 else
  2446.                     e = src:sub(e + 2, -1):find("%.")
  2447.                     e = e and e + yeOld or e
  2448.                 end
  2449.             end
  2450.         until match or i >= 50
  2451.     end
  2452.     return realPath
  2453. end
  2454.  
  2455. --- schedules the provided function (and calls it with any args after)
  2456. function schedule(f, ...)
  2457.     table.insert(scheduled, { f, ... })
  2458. end
  2459.  
  2460. --- yields the current thread until the scheduler gives the ok
  2461. function scheduleWait()
  2462.     local thread = coroutine.running()
  2463.     schedule(function()
  2464.         coroutine.resume(thread)
  2465.     end)
  2466.     coroutine.yield()
  2467. end
  2468.  
  2469. --- the big (well tbh small now) boi task scheduler himself, handles p much anything as quicc as possible
  2470. function taskscheduler()
  2471.     if not toggle then
  2472.         scheduled = {}
  2473.         return
  2474.     end
  2475.     if #scheduled > 1000 then
  2476.         table.remove(scheduled, #scheduled)
  2477.     end
  2478.     if #scheduled > 0 then
  2479.         local currentf = scheduled[1]
  2480.         table.remove(scheduled, 1)
  2481.         if type(currentf) == "table" and type(currentf[1]) == "function" then
  2482.             pcall(unpack(currentf))
  2483.         end
  2484.     end
  2485. end
  2486.  
  2487. --- Handles remote logs
  2488. function remoteHandler(hookfunction, methodName, remote, args, funcInfo, calling, returnValue)
  2489.     local validInstance, validClass = pcall(function()
  2490.         return remote:IsA("RemoteEvent") or remote:IsA("RemoteFunction")
  2491.     end)
  2492.     if validInstance and validClass then
  2493.         local func = funcInfo.func
  2494.         if not calling then
  2495.             _, calling = pcall(getScriptFromSrc, funcInfo.source)
  2496.         end
  2497.         coroutine.wrap(function()
  2498.             if remoteSignals[remote] then
  2499.                 remoteSignals[remote]:Fire(args)
  2500.             end
  2501.         end)()
  2502.         if autoblock then
  2503.             if excluding[remote] then
  2504.                 return
  2505.             end
  2506.             if not history[remote] then
  2507.                 history[remote] = { badOccurances = 0, lastCall = tick() }
  2508.             end
  2509.             if tick() - history[remote].lastCall < 1 then
  2510.                 -- history[remote].badOccurances += 1
  2511.                 history[remote].badOccurances = history[remote].badOccurances + 1
  2512.                 return
  2513.             else
  2514.                 history[remote].badOccurances = 0
  2515.             end
  2516.             if history[remote].badOccurances > 3 then
  2517.                 excluding[remote] = true
  2518.                 return
  2519.             end
  2520.             history[remote].lastCall = tick()
  2521.         end
  2522.         local functionInfoStr
  2523.         local src
  2524.         if func and islclosure(func) then
  2525.             local functionInfo = {}
  2526.             functionInfo.info = funcInfo
  2527.             pcall(function()
  2528.                 functionInfo.constants = debug.getconstants(func)
  2529.             end)
  2530.             pcall(function()
  2531.                 functionInfoStr = v2v({ functionInfo = functionInfo })
  2532.             end)
  2533.             pcall(function()
  2534.                 if type(calling) == "userdata" then
  2535.                     src = calling
  2536.                 end
  2537.             end)
  2538.         end
  2539.         if methodName:lower() == "fireserver" then
  2540.             newRemote(
  2541.                 "event",
  2542.                 remote.Name,
  2543.                 args,
  2544.                 remote,
  2545.                 functionInfoStr,
  2546.                 (blocklist[remote] or blocklist[remote.Name]),
  2547.                 src
  2548.             )
  2549.         elseif methodName:lower() == "invokeserver" then
  2550.             newRemote(
  2551.                 "function",
  2552.                 remote.Name,
  2553.                 args,
  2554.                 remote,
  2555.                 functionInfoStr,
  2556.                 (blocklist[remote] or blocklist[remote.Name]),
  2557.                 src,
  2558.                 returnValue
  2559.             )
  2560.         end
  2561.     end
  2562. end
  2563.  
  2564. --- Used for hookfunction
  2565. function hookRemote(remoteType, remote, ...)
  2566.     if typeof(remote) == "Instance" then
  2567.         local args = { ... }
  2568.         local validInstance, remoteName = pcall(function()
  2569.             return remote.Name
  2570.         end)
  2571.         if validInstance and not (blacklist[remote] or blacklist[remoteName]) then
  2572.             local funcInfo = {}
  2573.             local calling
  2574.             if funcEnabled then
  2575.                 funcInfo = debug.getinfo(4) or funcInfo
  2576.                 calling = useGetCallingScript and getcallingscript() or nil
  2577.             end
  2578.             if recordReturnValues and remoteType == "RemoteFunction" then
  2579.                 local thread = coroutine.running()
  2580.                 local args = { ... }
  2581.                 task.defer(function()
  2582.                     local returnValue
  2583.                     if remoteHooks[remote] then
  2584.                         args = { remoteHooks[remote](unpack(args)) }
  2585.                         returnValue = originalFunction(remote, unpack(args))
  2586.                     else
  2587.                         returnValue = originalFunction(remote, unpack(args))
  2588.                     end
  2589.                     schedule(
  2590.                         remoteHandler,
  2591.                         true,
  2592.                         remoteType == "RemoteEvent" and "fireserver" or "invokeserver",
  2593.                         remote,
  2594.                         args,
  2595.                         funcInfo,
  2596.                         calling,
  2597.                         returnValue
  2598.                     )
  2599.                     if blocklist[remote] or blocklist[remoteName] then
  2600.                         coroutine.resume(thread)
  2601.                     else
  2602.                         coroutine.resume(thread, unpack(returnValue))
  2603.                     end
  2604.                 end)
  2605.             else
  2606.                 schedule(
  2607.                     remoteHandler,
  2608.                     true,
  2609.                     remoteType == "RemoteEvent" and "fireserver" or "invokeserver",
  2610.                     remote,
  2611.                     args,
  2612.                     funcInfo,
  2613.                     calling
  2614.                 )
  2615.                 if blocklist[remote] or blocklist[remoteName] then
  2616.                     return
  2617.                 end
  2618.             end
  2619.         end
  2620.     end
  2621.     if recordReturnValues and remoteType == "RemoteFunction" then
  2622.         return coroutine.yield()
  2623.     elseif remoteType == "RemoteEvent" then
  2624.         if remoteHooks[remote] then
  2625.             return originalEvent(remote, remoteHooks[remote](...))
  2626.         end
  2627.         return originalEvent(remote, ...)
  2628.     else
  2629.         if remoteHooks[remote] then
  2630.             return originalFunction(remote, remoteHooks[remote](...))
  2631.         end
  2632.         return originalFunction(remote, ...)
  2633.     end
  2634. end
  2635.  
  2636. local newnamecall = newcclosure(function(remote, ...)
  2637.     if typeof(remote) == "Instance" then
  2638.         local args = { ... }
  2639.         local methodName = getnamecallmethod()
  2640.         local validInstance, remoteName = pcall(function()
  2641.             return remote.Name
  2642.         end)
  2643.         if
  2644.             validInstance
  2645.             and (methodName == "FireServer" or methodName == "fireServer" or methodName == "InvokeServer" or methodName == "invokeServer")
  2646.             and not (blacklist[remote] or blacklist[remoteName])
  2647.         then
  2648.             local funcInfo = {}
  2649.             local calling
  2650.             if funcEnabled then
  2651.                 funcInfo = debug.getinfo(3) or funcInfo
  2652.                 calling = useGetCallingScript and getcallingscript() or nil
  2653.             end
  2654.             if recordReturnValues and (methodName == "InvokeServer" or methodName == "invokeServer") then
  2655.                 local namecallThread = coroutine.running()
  2656.                 local args = { ... }
  2657.                 task.defer(function()
  2658.                     local returnValue
  2659.                     setnamecallmethod(methodName)
  2660.                     if remoteHooks[remote] then
  2661.                         args = { remoteHooks[remote](unpack(args)) }
  2662.                         returnValue = { original(remote, unpack(args)) }
  2663.                     else
  2664.                         returnValue = { original(remote, unpack(args)) }
  2665.                     end
  2666.                     coroutine.resume(namecallThread, unpack(returnValue))
  2667.                     coroutine.wrap(function()
  2668.                         schedule(remoteHandler, false, methodName, remote, args, funcInfo, calling, returnValue)
  2669.                     end)()
  2670.                 end)
  2671.             else
  2672.                 coroutine.wrap(function()
  2673.                     schedule(remoteHandler, false, methodName, remote, args, funcInfo, calling)
  2674.                 end)()
  2675.             end
  2676.         end
  2677.         if recordReturnValues and (methodName == "InvokeServer" or methodName == "invokeServer") then
  2678.             return coroutine.yield()
  2679.         elseif
  2680.             validInstance
  2681.             and (methodName == "FireServer" or methodName == "fireServer" or methodName == "InvokeServer" or methodName == "invokeServer")
  2682.             and (blocklist[remote] or blocklist[remoteName])
  2683.         then
  2684.             return nil
  2685.         elseif
  2686.             (not recordReturnValues or methodName ~= "InvokeServer" or methodName ~= "invokeServer")
  2687.             and validInstance
  2688.             and (methodName == "FireServer" or methodName == "fireServer" or methodName == "InvokeServer" or methodName == "invokeServer")
  2689.             and remoteHooks[remote]
  2690.         then
  2691.             return original(remote, remoteHooks[remote](...))
  2692.         else
  2693.             return original(remote, ...)
  2694.         end
  2695.     end
  2696.     return original(remote, ...)
  2697. end, original)
  2698.  
  2699. local newFireServer = newcclosure(function(...)
  2700.     return hookRemote("RemoteEvent", ...)
  2701. end, originalEvent)
  2702.  
  2703. local newInvokeServer = newcclosure(function(...)
  2704.     return hookRemote("RemoteFunction", ...)
  2705. end, originalFunction)
  2706.  
  2707. --- Toggles on and off the remote spy
  2708. function toggleSpy()
  2709.     if not toggle then
  2710.         if hookmetamethod then
  2711.             local oldNamecall = hookmetamethod(game, "__namecall", newnamecall)
  2712.             original = original or function(...)
  2713.                 return oldNamecall(...)
  2714.             end
  2715.             _G.OriginalNamecall = original
  2716.         else
  2717.             gm = gm or getrawmetatable(game)
  2718.             original = original or function(...)
  2719.                 return gm.__namecall(...)
  2720.             end
  2721.             setreadonly(gm, false)
  2722.             if not original then
  2723.                 warn("SimpleSpy: namecall method not found!")
  2724.                 onToggleButtonClick()
  2725.                 return
  2726.             end
  2727.             gm.__namecall = newnamecall
  2728.             setreadonly(gm, true)
  2729.         end
  2730.         originalEvent = hookfunction(remoteEvent.FireServer, newFireServer)
  2731.         originalFunction = hookfunction(remoteFunction.InvokeServer, newInvokeServer)
  2732.     else
  2733.         if hookmetamethod then
  2734.             if original then
  2735.                 hookmetamethod(game, "__namecall", original)
  2736.             end
  2737.         else
  2738.             gm = gm or getrawmetatable(game)
  2739.             setreadonly(gm, false)
  2740.             gm.__namecall = original
  2741.             setreadonly(gm, true)
  2742.         end
  2743.         hookfunction(remoteEvent.FireServer, originalEvent)
  2744.         hookfunction(remoteFunction.InvokeServer, originalFunction)
  2745.     end
  2746. end
  2747.  
  2748. --- Toggles between the two remotespy methods (hookfunction currently = disabled)
  2749. function toggleSpyMethod()
  2750.     toggleSpy()
  2751.     toggle = not toggle
  2752. end
  2753.  
  2754. --- Shuts down the remote spy
  2755. function shutdown()
  2756.     if schedulerconnect then
  2757.         schedulerconnect:Disconnect()
  2758.     end
  2759.     for _, connection in pairs(connections) do
  2760.         coroutine.wrap(function()
  2761.             connection:Disconnect()
  2762.         end)()
  2763.     end
  2764.     SimpleSpy2:Destroy()
  2765.     hookfunction(remoteEvent.FireServer, originalEvent)
  2766.     hookfunction(remoteFunction.InvokeServer, originalFunction)
  2767.     if hookmetamethod then
  2768.         if original then
  2769.             hookmetamethod(game, "__namecall", original)
  2770.         end
  2771.     else
  2772.         gm = gm or getrawmetatable(game)
  2773.         setreadonly(gm, false)
  2774.         gm.__namecall = original
  2775.         setreadonly(gm, true)
  2776.     end
  2777.     _G.SimpleSpyExecuted = false
  2778. end
  2779.  
  2780. -- main
  2781. if not _G.SimpleSpyExecuted then
  2782.     local succeeded, err = pcall(function()
  2783.         if not RunService:IsClient() then
  2784.             error("SimpleSpy cannot run on the server!")
  2785.         end
  2786.         if
  2787.             not hookfunction
  2788.             or not getrawmetatable
  2789.             or getrawmetatable and not getrawmetatable(game).__namecall
  2790.             or not setreadonly
  2791.         then
  2792.             local missing = {}
  2793.             if not hookfunction then
  2794.                 table.insert(missing, "hookfunction")
  2795.             end
  2796.             if not getrawmetatable then
  2797.                 table.insert(missing, "getrawmetatable")
  2798.             end
  2799.             if getrawmetatable and not getrawmetatable(game).__namecall then
  2800.                 table.insert(missing, "getrawmetatable(game).__namecall")
  2801.             end
  2802.             if not setreadonly then
  2803.                 table.insert(missing, "setreadonly")
  2804.             end
  2805.             shutdown()
  2806.             error(
  2807.                 "This environment does not support method hooks!\n(Your exploit is not capable of running SimpleSpy)\nMissing: "
  2808.                     .. table.concat(missing, ", ")
  2809.             )
  2810.         end
  2811.         _G.SimpleSpyShutdown = shutdown
  2812.         ContentProvider:PreloadAsync({
  2813.             "rbxassetid://6065821980",
  2814.             "rbxassetid://6065774948",
  2815.             "rbxassetid://6065821086",
  2816.             "rbxassetid://6065821596",
  2817.             ImageLabel,
  2818.             ImageLabel_2,
  2819.             ImageLabel_3,
  2820.         })
  2821.         -- if gethui then funcEnabled = false end
  2822.         onToggleButtonClick()
  2823.         RemoteTemplate.Parent = nil
  2824.         FunctionTemplate.Parent = nil
  2825.         codebox = Highlight.new(CodeBox)
  2826.         codebox:setRaw("")
  2827.         getgenv().SimpleSpy = SimpleSpy
  2828.         getgenv().getNil = function(name, class)
  2829.             for _, v in pairs(getnilinstances()) do
  2830.                 if v.ClassName == class and v.Name == name then
  2831.                     return v
  2832.                 end
  2833.             end
  2834.         end
  2835.         TextLabel:GetPropertyChangedSignal("Text"):Connect(scaleToolTip)
  2836.         -- TopBar.InputBegan:Connect(onBarInput)
  2837.         MinimizeButton.MouseButton1Click:Connect(toggleMinimize)
  2838.         MaximizeButton.MouseButton1Click:Connect(toggleSideTray)
  2839.         Simple.MouseButton1Click:Connect(onToggleButtonClick)
  2840.         CloseButton.MouseEnter:Connect(onXButtonHover)
  2841.         CloseButton.MouseLeave:Connect(onXButtonUnhover)
  2842.         Simple.MouseEnter:Connect(onToggleButtonHover)
  2843.         Simple.MouseLeave:Connect(onToggleButtonUnhover)
  2844.         CloseButton.MouseButton1Click:Connect(shutdown)
  2845.         table.insert(connections, UserInputService.InputBegan:Connect(backgroundUserInput))
  2846.         connectResize()
  2847.         SimpleSpy2.Enabled = true
  2848.         coroutine.wrap(function()
  2849.             wait(1)
  2850.             onToggleButtonUnhover()
  2851.         end)()
  2852.         schedulerconnect = RunService.Heartbeat:Connect(taskscheduler)
  2853.         if syn and syn.protect_gui then
  2854.             pcall(syn.protect_gui, SimpleSpy2)
  2855.         end
  2856.         bringBackOnResize()
  2857.         SimpleSpy2.Parent = --[[gethui and gethui() or]]
  2858.             CoreGui
  2859.         _G.SimpleSpyExecuted = true
  2860.         if not Players.LocalPlayer then
  2861.             Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
  2862.         end
  2863.         Mouse = Players.LocalPlayer:GetMouse()
  2864.         oldIcon = Mouse.Icon
  2865.         table.insert(connections, Mouse.Move:Connect(mouseMoved))
  2866.     end)
  2867.     if not succeeded then
  2868.         warn(
  2869.             "A fatal error has occured, SimpleSpy was unable to launch properly.\nPlease DM this error message to @exx#9394:\n\n"
  2870.                 .. tostring(err)
  2871.         )
  2872.         SimpleSpy2:Destroy()
  2873.         hookfunction(remoteEvent.FireServer, originalEvent)
  2874.         hookfunction(remoteFunction.InvokeServer, originalFunction)
  2875.         if hookmetamethod then
  2876.             if original then
  2877.                 hookmetamethod(game, "__namecall", original)
  2878.             end
  2879.         else
  2880.             setreadonly(gm, false)
  2881.             gm.__namecall = original
  2882.             setreadonly(gm, true)
  2883.         end
  2884.         return
  2885.     end
  2886. else
  2887.     SimpleSpy2:Destroy()
  2888.     return
  2889. end
  2890.  
  2891. ----- ADD ONS ----- (easily add or remove additonal functionality to the RemoteSpy!)
  2892. --[[
  2893.     Some helpful things:
  2894.         - add your function in here, and create buttons for them through the 'newButton' function
  2895.         - the first argument provided is the TextButton the player clicks to run the function
  2896.         - generated scripts are generated when the namecall is initially fired and saved in remoteFrame objects
  2897.         - blacklisted remotes will be ignored directly in namecall (less lag)
  2898.         - the properties of a 'remoteFrame' object:
  2899.             {
  2900.                 Name: (string) The name of the Remote
  2901.                 GenScript: (string) The generated script that appears in the codebox (generated when namecall fired)
  2902.                 Source: (Instance (LocalScript)) The script that fired/invoked the remote
  2903.                 Remote: (Instance (RemoteEvent) | Instance (RemoteFunction)) The remote that was fired/invoked
  2904.                 Log: (Instance (TextButton)) The button being used for the remote (same as 'selected.Log')
  2905.             }
  2906.         - globals list: (contact @exx#9394 for more information or if you have suggestions for more to be added)
  2907.             - closed: (boolean) whether or not the GUI is currently minimized
  2908.             - logs: (table[remoteFrame]) full of remoteFrame objects (properties listed above)
  2909.             - selected: (remoteFrame) the currently selected remoteFrame (properties listed above)
  2910.             - blacklist: (string[] | Instance[] (RemoteEvent) | Instance[] (RemoteFunction)) an array of blacklisted names and remotes
  2911.             - codebox: (Instance (TextBox)) the textbox that holds all the code- cleared often
  2912. ]]
  2913. -- Copies the contents of the codebox
  2914. newButton("Copy Code", function()
  2915.     return "Click to copy code"
  2916. end, function()
  2917.     setclipboard(codebox:getString())
  2918.     TextLabel.Text = "Copied successfully!"
  2919. end)
  2920.  
  2921. --- Copies the source script (that fired the remote)
  2922. newButton("Copy Remote", function()
  2923.     return "Click to copy the path of the remote"
  2924. end, function()
  2925.     if selected then
  2926.         setclipboard(v2s(selected.Remote.remote))
  2927.         TextLabel.Text = "Copied!"
  2928.     end
  2929. end)
  2930.  
  2931. -- Executes the contents of the codebox through loadstring
  2932. newButton("Run Code", function()
  2933.     return "Click to execute code"
  2934. end, function()
  2935.     local orText = "Click to execute code"
  2936.     TextLabel.Text = "Executing..."
  2937.     local succeeded = pcall(function()
  2938.         return loadstring(codebox:getString())()
  2939.     end)
  2940.     if succeeded then
  2941.         TextLabel.Text = "Executed successfully!"
  2942.     else
  2943.         TextLabel.Text = "Execution error!"
  2944.     end
  2945. end)
  2946.  
  2947. --- Gets the calling script (not super reliable but w/e)
  2948. newButton("Get Script", function()
  2949.     return "Click to copy calling script to clipboard\nWARNING: Not super reliable, nil == could not find"
  2950. end, function()
  2951.     if selected then
  2952.         setclipboard(SimpleSpy:ValueToString(selected.Source))
  2953.         TextLabel.Text = "Done!"
  2954.     end
  2955. end)
  2956.  
  2957. --- Decompiles the script that fired the remote and puts it in the code box
  2958. newButton("Function Info", function()
  2959.     return "Click to view calling function information"
  2960. end, function()
  2961.     if selected then
  2962.         if selected.Function then
  2963.             codebox:setRaw(
  2964.                 "-- Calling function info\n-- Generated by the SimpleSpy serializer\n\n" .. tostring(selected.Function)
  2965.             )
  2966.         end
  2967.         TextLabel.Text = "Done! Function info generated by the SimpleSpy Serializer."
  2968.     end
  2969. end)
  2970.  
  2971. --- Clears the Remote logs
  2972. newButton("Clr Logs", function()
  2973.     return "Click to clear logs"
  2974. end, function()
  2975.     TextLabel.Text = "Clearing..."
  2976.     logs = {}
  2977.     for _, v in pairs(LogList:GetChildren()) do
  2978.         if not v:IsA("UIListLayout") then
  2979.             v:Destroy()
  2980.         end
  2981.     end
  2982.     codebox:setRaw("")
  2983.     selected = nil
  2984.     TextLabel.Text = "Logs cleared!"
  2985. end)
  2986.  
  2987. --- Excludes the selected.Log Remote from the RemoteSpy
  2988. newButton("Exclude (i)", function()
  2989.     return "Click to exclude this Remote.\nExcluding a remote makes SimpleSpy ignore it, but it will continue to be usable."
  2990. end, function()
  2991.     if selected then
  2992.         blacklist[selected.Remote.remote] = true
  2993.         TextLabel.Text = "Excluded!"
  2994.     end
  2995. end)
  2996.  
  2997. --- Excludes all Remotes that share the same name as the selected.Log remote from the RemoteSpy
  2998. newButton("Exclude (n)", function()
  2999.     return "Click to exclude all remotes with this name.\nExcluding a remote makes SimpleSpy ignore it, but it will continue to be usable."
  3000. end, function()
  3001.     if selected then
  3002.         blacklist[selected.Name] = true
  3003.         TextLabel.Text = "Excluded!"
  3004.     end
  3005. end)
  3006.  
  3007. --- clears blacklist
  3008. newButton("Clr Blacklist", function()
  3009.     return "Click to clear the blacklist.\nExcluding a remote makes SimpleSpy ignore it, but it will continue to be usable."
  3010. end, function()
  3011.     blacklist = {}
  3012.     TextLabel.Text = "Blacklist cleared!"
  3013. end)
  3014.  
  3015. --- Prevents the selected.Log Remote from firing the server (still logged)
  3016. newButton("Block (i)", function()
  3017.     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."
  3018. end, function()
  3019.     if selected then
  3020.         if selected.Remote.remote then
  3021.             blocklist[selected.Remote.remote] = true
  3022.             TextLabel.Text = "Excluded!"
  3023.         else
  3024.             TextLabel.Text = "Error! Instance may no longer exist, try using Block (n)."
  3025.         end
  3026.     end
  3027. end)
  3028.  
  3029. --- Prevents all remotes from firing that share the same name as the selected.Log remote from the RemoteSpy (still logged)
  3030. newButton("Block (n)", function()
  3031.     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."
  3032. end, function()
  3033.     if selected then
  3034.         blocklist[selected.Name] = true
  3035.         TextLabel.Text = "Excluded!"
  3036.     end
  3037. end)
  3038.  
  3039. --- clears blacklist
  3040. newButton("Clr Blocklist", function()
  3041.     return "Click to stop blocking remotes.\nBlocking a remote won't remove it from SimpleSpy logs, but it will not continue to fire the server."
  3042. end, function()
  3043.     blocklist = {}
  3044.     TextLabel.Text = "Blocklist cleared!"
  3045. end)
  3046.  
  3047. --- Attempts to decompile the source script
  3048. newButton("Decompile", function()
  3049.     return "Attempts to decompile source script\nWARNING: Not super reliable, nil == could not find"
  3050. end, function()
  3051.     if selected then
  3052.         if selected.Source then
  3053.             codebox:setRaw(decompile(selected.Source))
  3054.             TextLabel.Text = "Done!"
  3055.         else
  3056.             TextLabel.Text = "Source not found!"
  3057.         end
  3058.     end
  3059. end)
  3060.  
  3061. newButton("Disable Info", function()
  3062.     return string.format(
  3063.         "[%s] Toggle function info (because it can cause lag in some games)",
  3064.         funcEnabled and "ENABLED" or "DISABLED"
  3065.     )
  3066. end, function()
  3067.     funcEnabled = not funcEnabled
  3068.     TextLabel.Text = string.format(
  3069.         "[%s] Toggle function info (because it can cause lag in some games)",
  3070.         funcEnabled and "ENABLED" or "DISABLED"
  3071.     )
  3072. end)
  3073.  
  3074. newButton("Autoblock", function()
  3075.     return string.format(
  3076.         "[%s] [BETA] Intelligently detects and excludes spammy remote calls from logs",
  3077.         autoblock and "ENABLED" or "DISABLED"
  3078.     )
  3079. end, function()
  3080.     autoblock = not autoblock
  3081.     TextLabel.Text = string.format(
  3082.         "[%s] [BETA] Intelligently detects and excludes spammy remote calls from logs",
  3083.         autoblock and "ENABLED" or "DISABLED"
  3084.     )
  3085.     history = {}
  3086.     excluding = {}
  3087. end)
  3088.  
  3089. newButton("CallingScript", function()
  3090.     return string.format(
  3091.         "[%s] [UNSAFE] Uses 'getcallingscript' to get calling script for Decompile and GetScript. Much more reliable, but opens up SimpleSpy to detection and/or instability.",
  3092.         useGetCallingScript and "ENABLED" or "DISABLED"
  3093.     )
  3094. end, function()
  3095.     useGetCallingScript = not useGetCallingScript
  3096.     TextLabel.Text = string.format(
  3097.         "[%s] [UNSAFE] Uses 'getcallingscript' to get calling script for Decompile and GetScript. Much more reliable, but opens up SimpleSpy to detection and/or instability.",
  3098.         useGetCallingScript and "ENABLED" or "DISABLED"
  3099.     )
  3100. end)
  3101.  
  3102. newButton("KeyToString", function()
  3103.     return string.format(
  3104.         "[%s] [BETA] Uses an experimental new function to replicate Roblox's behavior when a non-primitive type is used as a key in a table. Still in development and may not properly reflect tostringed (empty) userdata.",
  3105.         keyToString and "ENABLED" or "DISABLED"
  3106.     )
  3107. end, function()
  3108.     keyToString = not keyToString
  3109.     TextLabel.Text = string.format(
  3110.         "[%s] [BETA] Uses an experimental new function to replicate Roblox's behavior when a non-primitive type is used as a key in a table. Still in development and may not properly reflect tostringed (empty) userdata.",
  3111.         keyToString and "ENABLED" or "DISABLED"
  3112.     )
  3113. end)
  3114.  
  3115. newButton("ToggleReturnValues", function()
  3116.     return string.format(
  3117.         "[%s] [EXPERIMENTAL] Enables recording of return values for 'GetReturnValue'\n\nUse this method at your own risk, as it could be detectable.",
  3118.         recordReturnValues and "ENABLED" or "DISABLED"
  3119.     )
  3120. end, function()
  3121.     recordReturnValues = not recordReturnValues
  3122.     TextLabel.Text = string.format(
  3123.         "[%s] [EXPERIMENTAL] Enables recording of return values for 'GetReturnValue'\n\nUse this method at your own risk, as it could be detectable.",
  3124.         recordReturnValues and "ENABLED" or "DISABLED"
  3125.     )
  3126. end)
  3127.  
  3128. newButton("GetReturnValue", function()
  3129.     return "[Experimental] If 'ReturnValues' is enabled, this will show the recorded return value for the RemoteFunction (if available)."
  3130. end, function()
  3131.     if selected then
  3132.         codebox:setRaw(SimpleSpy:ValueToVar(selected.ReturnValue, "returnValue"))
  3133.     end
  3134. end)
Advertisement
Add Comment
Please, Sign In to add comment