Advertisement
DarkHorse89

Simple Spy

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