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