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