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