· 4 years ago · Aug 06, 2021, 10:30 AM
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
327local original
328--- used to prevent recursives
329local prevTables = {}
330--- holds logs (for deletion)
331local remoteLogs = {}
332--- used for hookfunction
333local remoteEvent = Instance.new("RemoteEvent")
334--- used for hookfunction
335local remoteFunction = Instance.new("RemoteFunction")
336local originalEvent = remoteEvent.FireServer
337local originalFunction = remoteFunction.InvokeServer
338--- the maximum amount of remotes allowed in logs
339_G.SIMPLESPYCONFIG_MaxRemotes = 500
340--- how many spaces to indent
341local indent = 4
342--- used for task scheduler
343local scheduled = {}
344--- RBXScriptConnect of the task scheduler
345local schedulerconnect
346local SimpleSpy = {}
347local topstr = ""
348local bottomstr = ""
349local remotesFadeIn
350local rightFadeIn
351local codebox
352local p
353local getnilrequired = false
354
355-- autoblock variables
356local autoblock = false
357local history = {}
358local excluding = {}
359
360-- function info variables
361local funcEnabled = true
362
363-- remote hooking/connecting api variables
364local remoteSignals = {}
365local remoteHooks = {}
366
367-- original mouse icon
368local oldIcon
369
370-- if mouse inside gui
371local mouseInGui = false
372
373-- handy array of RBXScriptConnections to disconnect on shutdown
374local connections = {}
375
376-- whether or not SimpleSpy uses 'getcallingscript()' to get the script (default is false because detection)
377local useGetCallingScript = false
378
379--- used to enable/disable SimpleSpy's keyToString for remotes
380local keyToString = false
381
382-- functions
383
384--- 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`)
385--- @param method string
386--- @param args any[]
387--- @return string
388function SimpleSpy:ArgsToString(method, args)
389 assert(typeof(method) == "string", "string expected, got " .. typeof(method))
390 assert(typeof(args) == "table", "table expected, got " .. typeof(args))
391 return v2v({args = args}) .. "\n\n" .. method .. "(unpack(args))"
392end
393
394--- Converts a value to variables with the specified index as the variable name (if nil/invalid then the name will be assigned automatically)
395--- @param t any[]
396--- @return string
397function SimpleSpy:TableToVars(t)
398 assert(typeof(t) == "table", "table expected, got " .. typeof(t))
399 return v2v(t)
400end
401
402--- Converts a value to a variable with the specified `variablename` (if nil/invalid then the name will be assigned automatically)
403--- @param value any
404--- @return string
405function SimpleSpy:ValueToVar(value, variablename)
406 assert(variablename == nil or typeof(variablename) == "string", "string expected, got " .. typeof(variablename))
407 if not variablename then
408 variablename = 1
409 end
410 return v2v({[variablename] = value})
411end
412
413--- Converts any value to a string, cannot preserve function contents
414--- @param value any
415--- @return string
416function SimpleSpy:ValueToString(value)
417 return v2s(value)
418end
419
420--- Generates the simplespy function info
421--- @param func function
422--- @return string
423function SimpleSpy:GetFunctionInfo(func)
424 assert(typeof(func) == "function", "Instance expected, got " .. typeof(func))
425 warn("Function info currently unavailable due to crashing in Synapse X")
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 maximized = false
792 wait(0.5)
793 disable:Destroy()
794 CodeBox.Size = UDim2.new(1, 0, 0.5, 0)
795 CodeBox.Position = UDim2.new(0, 0, 0, 0)
796 CodeBox.ZIndex = 0
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(value, indentation)
1561 local buildStr = {}
1562 local i = 1
1563 local char = string.sub(value, i, i)
1564 local indentStr
1565 while char ~= "" do
1566 if char == '"' then
1567 buildStr[i] = '\\"'
1568 elseif char == "\\" then
1569 buildStr[i] = "\\\\"
1570 elseif char == "\n" then
1571 buildStr[i] = "\\n"
1572 elseif char == "\t" then
1573 buildStr[i] = "\\t"
1574 elseif string.byte(char) > 126 or string.byte(char) < 32 then
1575 buildStr[i] = string.format("\\%d", string.byte(char))
1576 else
1577 buildStr[i] = char
1578 end
1579 i = i + 1
1580 char = string.sub(value, i, i)
1581 if i % 200 == 0 then
1582 indentStr = indentStr or string.rep(" ", indentation + indent)
1583 table.move({'"\n', indentStr, '... "'}, 1, 3, i, buildStr)
1584 i += 3
1585 end
1586 end
1587 return table.concat(buildStr)
1588end
1589
1590-- safe (ish) tostring
1591function safetostring(v: any)
1592 if typeof(v) == "userdata" or type(v) == "table" then
1593 local mt = getrawmetatable(v)
1594 local badtostring = mt and rawget(mt, "__tostring")
1595 if mt and badtostring then
1596 rawset(mt, "__tostring", nil)
1597 local out = tostring(v)
1598 rawset(mt, "__tostring", badtostring)
1599 return out
1600 end
1601 end
1602 return tostring(v)
1603end
1604
1605--- finds script from 'src' from getinfo, returns nil if not found
1606--- @param src string
1607function getScriptFromSrc(src)
1608 local realPath
1609 local runningTest
1610 --- @type number
1611 local s, e
1612 local match = false
1613 if src:sub(1, 1) == "=" then
1614 realPath = game
1615 s = 2
1616 else
1617 runningTest = src:sub(2, e and e - 1 or -1)
1618 for _, v in pairs(getnilinstances()) do
1619 if v.Name == runningTest then
1620 realPath = v
1621 break
1622 end
1623 end
1624 s = #runningTest + 1
1625 end
1626 if realPath then
1627 e = src:sub(s, -1):find("%.")
1628 local i = 0
1629 repeat
1630 i += 1
1631 if not e then
1632 runningTest = src:sub(s, -1)
1633 local test = realPath.FindFirstChild(realPath, runningTest)
1634 if test then
1635 realPath = test
1636 end
1637 match = true
1638 else
1639 runningTest = src:sub(s, e)
1640 local test = realPath.FindFirstChild(realPath, runningTest)
1641 local yeOld = e
1642 if test then
1643 realPath = test
1644 s = e + 2
1645 e = src:sub(e + 2, -1):find("%.")
1646 e = e and e + yeOld or e
1647 else
1648 e = src:sub(e + 2, -1):find("%.")
1649 e = e and e + yeOld or e
1650 end
1651 end
1652 until match or i >= 50
1653 end
1654 return realPath
1655end
1656
1657--- schedules the provided function (and calls it with any args after)
1658function schedule(f, ...)
1659 table.insert(scheduled, {f, ...})
1660end
1661
1662--- yields the current thread until the scheduler gives the ok
1663function scheduleWait()
1664 local thread = coroutine.running()
1665 schedule(function()
1666 coroutine.resume(thread)
1667 end)
1668 coroutine.yield()
1669end
1670
1671--- the big (well tbh small now) boi task scheduler himself, handles p much anything as quicc as possible
1672function taskscheduler()
1673 if not toggle then
1674 scheduled = {}
1675 return
1676 end
1677 if #scheduled > 1000 then
1678 table.remove(scheduled, #scheduled)
1679 end
1680 if #scheduled > 0 then
1681 local currentf = scheduled[1]
1682 table.remove(scheduled, 1)
1683 if type(currentf) == "table" and type(currentf[1]) == "function" then
1684 pcall(unpack(currentf))
1685 end
1686 end
1687end
1688
1689--- Handles remote logs
1690function remoteHandler(hookfunction, methodName, remote, args, funcInfo, calling)
1691 local validInstance, validClass = pcall(function()
1692 return remote:IsA("RemoteEvent") or remote:IsA("RemoteFunction")
1693 end)
1694 if validInstance and validClass then
1695 local func = funcInfo.func
1696 if not calling then
1697 _, calling = pcall(getScriptFromSrc, funcInfo.source)
1698 end
1699 coroutine.wrap(function()
1700 if remoteSignals[remote] then
1701 remoteSignals[remote]:Fire(args)
1702 end
1703 end)()
1704 if autoblock then
1705 if excluding[remote] then
1706 return
1707 end
1708 if not history[remote] then
1709 history[remote] = {badOccurances = 0, lastCall = tick()}
1710 end
1711 if tick() - history[remote].lastCall < 1 then
1712 history[remote].badOccurances += 1
1713 return
1714 else
1715 history[remote].badOccurances = 0
1716 end
1717 if history[remote].badOccurances > 3 then
1718 excluding[remote] = true
1719 return
1720 end
1721 history[remote].lastCall = tick()
1722 end
1723 local functionInfoStr
1724 local src
1725 if func and islclosure(func) then
1726 local functionInfo = {}
1727 functionInfo.info = funcInfo
1728 pcall(function() functionInfo.constants = debug.getconstants(func) end)
1729 pcall(function() functionInfoStr = v2v{functionInfo = functionInfo} end)
1730 pcall(function() if type(calling) == "userdata" then src = calling end end)
1731 end
1732 if methodName:lower() == "fireserver" then
1733 newRemote("event", remote.Name, args, remote, functionInfoStr, (blocklist[remote] or blocklist[remote.Name]), src)
1734 elseif methodName:lower() == "invokeserver" then
1735 newRemote("function", remote.Name, args, remote, functionInfoStr, (blocklist[remote] or blocklist[remote.Name]), src)
1736 end
1737 end
1738end
1739
1740--- Used for hookfunction
1741function hookRemote(remoteType, remote, ...)
1742 local args = {...}
1743 local validInstance, remoteName = pcall(function()
1744 return remote.Name
1745 end)
1746 if validInstance and typeof(remote) == "Instance" and not (blacklist[remote] or blacklist[remoteName]) then
1747 local funcInfo = {}
1748 local calling
1749 if funcEnabled then
1750 funcInfo = debug.getinfo(4) or funcInfo
1751 calling = useGetCallingScript and getcallingscript() or nil
1752 end
1753 schedule(remoteHandler, true, remoteType == "RemoteEvent" and "fireserver" or "invokeserver", remote, args, funcInfo, calling)
1754 if (blocklist[remote] or blocklist[remoteName]) then
1755 return
1756 end
1757 end
1758 if remoteType == "RemoteEvent" then
1759 if remoteHooks[remote] then
1760 return originalEvent(remote, remoteHooks[remote](...))
1761 end
1762 return originalEvent(remote, ...)
1763 else
1764 if remoteHooks[remote] then
1765 return originalFunction(remote, remoteHooks[remote](...))
1766 end
1767 return originalFunction(remote, ...)
1768 end
1769end
1770
1771local newnamecall = newcclosure(function(remote, ...)
1772 local args = {...}
1773 local methodName = getnamecallmethod()
1774 local validInstance, remoteName = pcall(function()
1775 return remote.Name
1776 end)
1777 if validInstance and (methodName == "FireServer" or methodName == "fireServer" or methodName == "InvokeServer" or methodName == "invokeServer") and not (blacklist[remote] or blacklist[remoteName]) then
1778 local funcInfo = {}
1779 local calling
1780 if funcEnabled then
1781 funcInfo = debug.getinfo(3) or funcInfo
1782 calling = useGetCallingScript and getcallingscript() or nil
1783 end
1784 coroutine.wrap(function()
1785 schedule(remoteHandler, false, methodName, remote, args, funcInfo, calling)
1786 end)()
1787 end
1788 if validInstance and typeof(remote) == "Instance" and (methodName == "FireServer" or methodName == "fireServer" or methodName == "InvokeServer" or methodName == "invokeServer") and (blocklist[remote] or blocklist[remoteName]) then
1789 return nil
1790 elseif validInstance and (methodName == "FireServer" or methodName == "fireServer" or methodName == "InvokeServer" or methodName == "invokeServer") and remoteHooks[remote] then
1791 return original(remote, remoteHooks[remote](...))
1792 else
1793 return original(remote, ...)
1794 end
1795end, original)
1796
1797local newFireServer = newcclosure(function(...) return hookRemote("RemoteEvent", ...) end, originalEvent)
1798
1799local newInvokeServer = newcclosure(function(...) return hookRemote("RemoteFunction", ...) end, originalFunction)
1800
1801--- Toggles on and off the remote spy
1802function toggleSpy()
1803 if not toggle then
1804 if hookmetamethod then
1805 local oldNamecall = hookmetamethod(game, "__namecall", newnamecall)
1806 original = original or oldNamecall
1807 else
1808 gm = gm or getrawmetatable(game)
1809 original = original or gm.__namecall
1810 setreadonly(gm, false)
1811 if not original then
1812 warn("SimpleSpy: namecall method not found!")
1813 onToggleButtonClick()
1814 return
1815 end
1816 gm.__namecall = newnamecall
1817 setreadonly(gm, true)
1818 end
1819 originalEvent = hookfunction(remoteEvent.FireServer, newFireServer)
1820 originalFunction = hookfunction(remoteFunction.InvokeServer, newInvokeServer)
1821 else
1822 if hookmetamethod then
1823 if original then
1824 hookmetamethod(game, "__namecall", original)
1825 end
1826 else
1827 setreadonly(gm, false)
1828 gm.__namecall = original
1829 setreadonly(gm, true)
1830 end
1831 hookfunction(remoteEvent.FireServer, originalEvent)
1832 hookfunction(remoteFunction.InvokeServer, originalFunction)
1833 end
1834end
1835
1836--- Toggles between the two remotespy methods (hookfunction currently = disabled)
1837function toggleSpyMethod()
1838 toggleSpy()
1839 toggle = not toggle
1840end
1841
1842--- Shuts down the remote spy
1843function shutdown()
1844 if schedulerconnect then
1845 schedulerconnect:Disconnect()
1846 end
1847 for _, connection in pairs(connections) do
1848 coroutine.wrap(function()
1849 connection:Disconnect()
1850 end)()
1851 end
1852 SimpleSpy2:Destroy()
1853 hookfunction(remoteEvent.FireServer, originalEvent)
1854 hookfunction(remoteFunction.InvokeServer, originalFunction)
1855 if hookmetamethod then
1856 if original then
1857 hookmetamethod(game, "__namecall", original)
1858 end
1859 else
1860 setreadonly(gm, false)
1861 gm.__namecall = original
1862 setreadonly(gm, true)
1863 end
1864 _G.SimpleSpyExecuted = false
1865end
1866
1867-- main
1868if not _G.SimpleSpyExecuted then
1869 local succeeded, err = pcall(function()
1870 if not RunService:IsClient() then
1871 error("SimpleSpy cannot run on the server!")
1872 end
1873 _G.SimpleSpyShutdown = shutdown
1874 ContentProvider:PreloadAsync({"rbxassetid://6065821980", "rbxassetid://6065774948", "rbxassetid://6065821086", "rbxassetid://6065821596", ImageLabel, ImageLabel_2, ImageLabel_3})
1875 -- if gethui then funcEnabled = false end
1876 onToggleButtonClick()
1877 RemoteTemplate.Parent = nil
1878 FunctionTemplate.Parent = nil
1879 codebox = Highlight.new(CodeBox)
1880 codebox:setRaw("")
1881 getgenv().SimpleSpy = SimpleSpy
1882 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
1883 TextLabel:GetPropertyChangedSignal("Text"):Connect(scaleToolTip)
1884 -- TopBar.InputBegan:Connect(onBarInput)
1885 MinimizeButton.MouseButton1Click:Connect(toggleMinimize)
1886 MaximizeButton.MouseButton1Click:Connect(toggleSideTray)
1887 Simple.MouseButton1Click:Connect(onToggleButtonClick)
1888 CloseButton.MouseEnter:Connect(onXButtonHover)
1889 CloseButton.MouseLeave:Connect(onXButtonUnhover)
1890 Simple.MouseEnter:Connect(onToggleButtonHover)
1891 Simple.MouseLeave:Connect(onToggleButtonUnhover)
1892 CloseButton.MouseButton1Click:Connect(shutdown)
1893 table.insert(connections, UserInputService.InputBegan:Connect(backgroundUserInput))
1894 connectResize()
1895 SimpleSpy2.Enabled = true
1896 coroutine.wrap(function()
1897 wait(1)
1898 onToggleButtonUnhover()
1899 end)()
1900 schedulerconnect = RunService.Heartbeat:Connect(taskscheduler)
1901 if syn and syn.protect_gui then pcall(syn.protect_gui, SimpleSpy2) end
1902 bringBackOnResize()
1903 SimpleSpy2.Parent = --[[gethui and gethui() or]] CoreGui
1904 if not Players.LocalPlayer then
1905 Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
1906 end
1907 Mouse = Players.LocalPlayer:GetMouse()
1908 oldIcon = Mouse.Icon
1909 table.insert(connections, Mouse.Move:Connect(mouseMoved))
1910 end)
1911 if succeeded then
1912 _G.SimpleSpyExecuted = true
1913 else
1914 warn("A fatal error has occured, SimpleSpy was unable to launch properly.\nPlease DM this error message to @exx#9394:\n\n" .. tostring(err))
1915 SimpleSpy2:Destroy()
1916 hookfunction(remoteEvent.FireServer, originalEvent)
1917 hookfunction(remoteFunction.InvokeServer, originalFunction)
1918 if hookmetamethod then
1919 if original then
1920 hookmetamethod(game, "__namecall", original)
1921 end
1922 else
1923 setreadonly(gm, false)
1924 gm.__namecall = original
1925 setreadonly(gm, true)
1926 end
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)