R

fuxk invoke server

public
rrixh Apr 14, 2024 Never 106
Clone
Plaintext fuxkInvokeServer_lulaslollipop.deb 3802 lines (3371 loc) | 153.48 KB
1
-- CREDIT TO https://github.com/Upbolt/Hydroxide/ FOR INSPIRATION AND A FEW FORKED PSEUDOCODE FUNCTIONS
2
3
--[[ TO DO:
4
* Add tuple support for return value in recieving pseudocode
5
* Make main window remote list use popups (depends on OnRightClick)
6
* Make arg list use right click (depends on defcon)
7
* Currently, deepClone sets unclonable objects to be userdatas with custom __tostring metamethods, but if the call is repeated, the userdatas get thrown away (for being in the args of the new call), making them not appear in the second call. One solution is to somehow pass a key to the userdata that will verify it is from Synapse, another is to check the caller whenever allowing userdatas, but neither of these solutions seem perfectly clean to me. Once a good solution is found, it will be implemented. Best solution is likely to set one of the metamethods to a function stored in the remotespy, so that it can be compared later. It'd be impossible for the game to get the value because of how the arg gets filtered out by roblox.
8
9
* Need to rewrite remotespy to break it down into multiple files
10
- One file for frontend, one for backend, one for pseudocode generation, one for initiation.
11
- Backend stores the data safely and calls the frontend's functions to tell it when to render.
12
- Frontend renders the calls
13
- Frontend renders buttons that call pseudcode generation functions with data from the backend.
14
]]
15
print("by lulas")
16
local mt = getrawmetatable(game)
17
if islclosure(mt.__namecall) or islclosure(mt.__index) or islclosure(mt.__newindex) then
18
error("script incompatibility detected, one of your scripts has set the game's metamethods to a luaclosure, please run the remotespy prior to that script")
19
end
20
21
if not RenderWindow then
22
error("EXPLOIT NOT SUPPORTED - GET SYNAPSE V3")
23
end
24
25
local function cleanUpSpy()
26
for _,v in _G.remoteSpyCallbackHooks do
27
restorefunction(v)
28
end
29
30
for _,v in _G.remoteSpySignalHooks do
31
if issignalhooked(v) then
32
restoresignal(v)
33
end
34
end
35
36
_G.remoteSpyMainWindow:Clear()
37
_G.remoteSpySettingsWindow:Clear()
38
_G.remoteSpyMainWindow = nil
39
_G.remoteSpySettingsWindow = nil
40
41
local oldHooks = _G.remoteSpyHooks
42
43
local unHook = syn.oth.unhook
44
unHook(Instance.new("RemoteEvent").FireServer, oldHooks.FireServer)
45
unHook(Instance.new("RemoteFunction").InvokeServer, oldHooks.InvokeServer)
46
unHook(Instance.new("BindableEvent").Fire, oldHooks.Fire)
47
unHook(Instance.new("BindableFunction").Invoke, oldHooks.Invoke)
48
49
unHook(mt.__namecall, oldHooks.Namecall)
50
unHook(mt.__index, oldHooks.Index)
51
unHook(mt.__newindex, oldHooks.NewIndex)
52
end
53
54
if _G.remoteSpyMainWindow or _G.remoteSpySettingsWindow then
55
cleanUpSpy()
56
end
57
58
local HttpService = cloneref(game:GetService("HttpService"))
59
local Players = cloneref(game:GetService("Players"))
60
61
local client, clientid
62
if not client then -- autoexec moment
63
task.spawn(function()
64
if not game:IsLoaded() then game.Loaded:Wait() end
65
client = cloneref(Players.LocalPlayer)
66
clientid = client:GetDebugId()
67
end)
68
end
69
70
local Settings = {
71
FireServer = true,
72
InvokeServer = true,
73
Fire = false,
74
Invoke = false,
75
76
OnClientEvent = false,
77
OnClientInvoke = false,
78
OnEvent = false,
79
OnInvoke = false,
80
81
Paused = false,
82
AlwaysOnTop = true,
83
84
CallbackButtons = false,
85
LogHiddenRemotesCalls = false,
86
MoreRepeatCallOptions = false,
87
CacheLimit = true,
88
MaxCallAmount = 1000,
89
ArgLimit = 25,
90
StoreCallStack = false,
91
GetCallingScriptV2 = false,
92
93
DecompilerOutput = 1,
94
ConnectionsOutput = 1,
95
PseudocodeOutput = 1,
96
CallStackOutput = 1,
97
98
PseudocodeLuaUTypes = false,
99
PseudocodeWatermark = true,
100
PseudocodeInliningMode = 2,
101
PseudocodeInlineRemote = true,
102
PseudocodeInlineHiddenNils = true,
103
PseudocodeFormatTables = true,
104
InstanceTrackerMode = 1,
105
OptimizedInstanceTracker = false
106
}
107
108
local function saveConfig()
109
if not isfolder("Remote Spy Settings") then
110
makefolder("Remote Spy Settings")
111
end
112
writefileasync("Remote Spy Settings/Settings.json", HttpService:JSONEncode(Settings))
113
end
114
115
if not isfile("Remote Spy Settings/Settings.json") then
116
saveConfig()
117
else
118
local tempSettings = HttpService:JSONDecode(readfile("Remote Spy Settings/Settings.json"))
119
for i,v in tempSettings do -- this is in case I add new settings
120
if Settings[i] ~= nil and type(Settings[i]) == type(v) then
121
Settings[i] = v
122
end
123
end
124
end
125
126
if isfile("Remote Spy Settings/Icons.ttf") then
127
delfile("Remote Spy Settings/Icons.ttf") -- no longer caching stuff, i'll do that later once i have a good system to cache the script and the icons
128
end
129
130
local fontData = syn.request({ Url = "https://raw.githubusercontent.com/GameGuyThrowaway/RemoteSpy/main/Icons.ttf" }).Body
131
132
Drawing:WaitForRenderer()
133
134
local RemoteIconFont = DrawFont.Register(fontData, {
135
Scale = false,
136
Bold = false,
137
UseStb = false,
138
PixelSize = 18,
139
Glyphs = {
140
{0xE000, 0xE007}
141
}
142
})
143
144
local CallerIconFont = DrawFont.Register(fontData, {
145
Scale = false,
146
Bold = false,
147
UseStb = false,
148
PixelSize = 27,
149
Glyphs = {
150
{0xE008, 0xE009}
151
}
152
})
153
154
local fontSize = 18
155
local DefaultTextFont = DrawFont.RegisterDefault("NotoSans_Regular", {
156
Scale = false,
157
Bold = false,
158
UseStb = true,
159
PixelSize = fontSize
160
})
161
162
local isHookThread, getCallStack, getOriginalThread, getDebugId, getThreadIdentity, setThreadIdentity, getn, ceil, floor, colorHSV, colorRGB, tableInsert, tableClear, tableRemove, deferFunc, spawnFunc, gsub, rep, sub, split, strformat, lower, match = syn.oth.is_hook_thread, debug.getcallstack, syn.oth.get_original_thread, game.GetDebugId, syn.get_thread_identity, syn.set_thread_identity, table.getn, math.ceil, math.floor, Color3.fromHSV, Color3.fromRGB, table.insert, table.clear, table.remove, task.defer, task.spawn, string.gsub, string.rep, string.sub, string.split, string.format, string.lower, string.match
163
164
local IsDescendantOf = game.IsDescendantOf
165
166
local oldIndex; -- this is for signal indexing
167
local oldNewIndex; -- this is for OnClientInvoke hooks
168
169
local watermarkString = "--Pseudocode Generated by GameGuy's Remote Spy\n"
170
171
local inf, neginf = (1/0), (-1/0)
172
173
local othHook = syn.oth.hook
174
175
local optimizedInstanceTrackerFunctionString = [[local function GetInstancesFromDebugIds(...)
176
local ids = {...}
177
local instances = {}
178
local idCount = #ids -- micro optimizations
179
local find = table.find
180
181
for _,v in getnilinstances() do
182
local discovery = find(ids, v:GetDebugId())
183
if discovery then
184
instances[discovery] = v
185
if #instances == idCount then
186
return unpack(instances)
187
end
188
end
189
end
190
191
for _,v in getweakdescendants(game) do
192
local discovery = find(ids, v:GetDebugId())
193
if discovery then
194
instances[discovery] = v
195
if #instances == idCount then
196
return unpack(instances)
197
end
198
end
199
end
200
201
return unpack(instances)
202
end]]
203
204
local instanceTrackerFunctionString = [[local function GetInstanceFromDebugId(id: string): Instance
205
for _,v in getnilinstances() do
206
if v:GetDebugId() == id then
207
return v
208
end
209
end
210
211
for _,v in getweakdescendants(game) do
212
if v:GetDebugId() == id then
213
return v
214
end
215
end
216
end]]
217
218
local red = colorRGB(255, 0, 0)
219
local green = colorRGB(0, 255, 0)
220
local white = colorRGB(255, 255, 255)
221
local grey = colorRGB(144, 144, 144)
222
local black = colorRGB()
223
224
local styleOptions = {
225
WindowRounding = 5,
226
WindowTitleAlign = Vector2.new(0.5, 0.5),
227
WindowBorderSize = 1,
228
FrameRounding = 3,
229
ButtonTextAlign = Vector2.new(0, 0.5),
230
PopupRounding = 5,
231
PopupBorderSize = 1
232
}
233
234
local colorOptions = {
235
Border = {black, 1},
236
TitleBgActive = {colorRGB(35, 35, 38), 1},
237
TitleBg = {colorRGB(35, 35, 38), 1},
238
TitleBgCollapsed = {colorRGB(35, 35, 38), 1},
239
WindowBg = {colorRGB(50, 50, 53), 1},
240
PopupBg = {colorRGB(20, 20, 23), 0.95},
241
Button = {colorRGB(75, 75, 78), 1},
242
ButtonHovered = {colorRGB(85, 85, 88), 1},
243
ButtonActive = {colorRGB(115, 115, 118), 1},
244
Text = {white, 1},
245
ResizeGrip = {colorRGB(65, 65, 68), 1},
246
ResizeGripActive = {colorRGB(115, 115, 118), 1},
247
ResizeGripHovered = {colorRGB(85, 85, 88), 1},
248
CheckMark = {white, 1},
249
FrameBg = {colorRGB(20, 20, 23), 1},
250
FrameBgHovered = {colorRGB(22, 22, 25), 1},
251
FrameBgActive = {colorRGB(30, 30, 35), 1},
252
Tab = {colorRGB(33, 36, 38), 1},
253
TabActive = {colorRGB(20, 20, 23), 1},
254
TabHovered = {colorRGB(119, 119, 119), 1},
255
TabUnfocused = {colorRGB(60, 60, 60), 1},
256
TabUnfocusedActive = {colorRGB(20, 20, 23), 1},
257
HeaderHovered = {colorRGB(55, 55, 55), 1},
258
HeaderActive = {colorRGB(75, 75, 75), 1},
259
}
260
261
local function resizeText(original: string, newWidth: number, proceedingChars: string, font: RenderFont) -- my fix using this and purifyString is pretty garbage as it brute forces the string size, but before that it makes a really rough guess on the maximum length of the string, allowing for general optimization for speed, but it isn't perfect. The current system is **enough**, taking approx 370 microseconds, but could be improved greatly. Fundamentally, this fix is also flawed because of how hard coded it is, and how unnecessarily it passes args through getArgString.
262
if type(original) ~= "string" then warn("non string text passed to resizeText"); return original end
263
264
local charSize = font:GetTextBounds(fontSize, original).X
265
if charSize < newWidth then return original end
266
267
local newCharCount = floor(newWidth/(charSize/#original))
268
local bestText = sub(original, 1, newCharCount)
269
270
if proceedingChars == "... " and fontSize == 18 then
271
newWidth -= 18
272
else
273
newWidth -= font:GetTextBounds(fontSize, proceedingChars).X
274
end
275
276
local steps = 0
277
278
local newSize = font:GetTextBounds(fontSize, bestText).X
279
if newSize <= newWidth then
280
local res = ceil((newWidth - newSize)/fontSize)
281
local oldText = ""
282
while true do
283
steps += 1
284
local newText = sub(original, 1, newCharCount+res)
285
local newerSize = font:GetTextBounds(fontSize, newText).X
286
287
if newerSize > newWidth then
288
if res == 1 then
289
return oldText .. proceedingChars
290
else
291
res = ceil(res/2)
292
end
293
else
294
newCharCount = #newText
295
oldText = newText
296
end
297
end
298
else
299
local res = ceil((newSize - newWidth)/fontSize)
300
while true do
301
steps += 1
302
local newText = sub(original, 1, newCharCount-res)
303
local newerSize = font:GetTextBounds(fontSize, newText).X
304
305
if newerSize < newWidth then
306
if res == 1 then
307
return newText .. proceedingChars
308
else
309
res = floor(res/2)
310
end
311
else
312
newCharCount = #newText
313
end
314
end
315
end
316
end
317
318
local function newHookMetamethod(toHook, mtmethod: string, hookFunction, filter: FilterBase)
319
local oldFunction
320
321
local func = getfilter(filter, function(...)
322
return oldFunction(...)
323
end, hookFunction)
324
325
restorefunction(getrawmetatable(toHook)[mtmethod]) -- restores any old hooks
326
oldFunction = othHook(getrawmetatable(toHook)[mtmethod], func) -- hookmetamethod(toHook, mtmethod, func)
327
return oldFunction
328
end
329
330
local function filteredOth(toHook, hookFunction, filter: FilterBase)
331
local oldFunction
332
333
local func = getfilter(filter, function(...)
334
return oldFunction(...)
335
end, hookFunction)
336
337
restorefunction(toHook)
338
oldFunction = othHook(toHook, func)
339
return oldFunction
340
end
341
342
local function pushError(title: string, message: string)
343
syn.toast_notification({
344
Type = ToastType.Error,
345
Duration = 5,
346
Title = message and title or "RemoteSpy",
347
Content = message or title
348
})
349
end
350
351
local function pushSuccess(message: string)
352
syn.toast_notification({
353
Type = ToastType.Success,
354
Duration = 5,
355
Title = "Remote Spy",
356
Content = message
357
})
358
end
359
360
local function outputData(source: string, destination: number, destinationTitle: string, successMessage: string)
361
if destination == 1 then
362
setclipboard(source)
363
pushSuccess(successMessage .. " to Clipboard")
364
elseif destination == 2 then
365
createuitab(destinationTitle, source)
366
pushSuccess(successMessage .. " to External UI")
367
elseif destination == 3 then
368
pushError("Internal UI Output Not Yet Supported")
369
end
370
end
371
372
local specialTypes = {
373
RBXScriptConnection = "Connection",
374
RBXScriptSignal = "EventInstance",
375
BrickColor = "int",
376
Rect = "Rect2D",
377
EnumItem = "token",
378
Enums = "void",
379
Enum = "void",
380
OverlapParams = "void",
381
userdata = "void",
382
RotationCurveKey = "void",
383
["function"] = "Function",
384
table = "Table"
385
}
386
387
local axis = Enum.Axis
388
local normalId = Enum.NormalId
389
390
local bindableUserdataClone = {
391
Axes = function(original: Axes): Axes
392
local args = {}
393
if original.X and not original.Left and not original.Right then
394
tableInsert(args, axis.X)
395
elseif original.Left then
396
tableInsert(args, normalId.Left)
397
end
398
if original.Right then
399
tableInsert(args, normalId.Right)
400
end
401
402
if original.Y and not original.Top and not original.Bottom then
403
tableInsert(args, axis.Y)
404
elseif original.Top then
405
tableInsert(args, normalId.Top)
406
end
407
if original.Bottom then
408
tableInsert(args, normalId.Bottom)
409
end
410
411
if original.Z and not original.Front and not original.Back then
412
tableInsert(args, axis.Z)
413
elseif original.Front then
414
tableInsert(args, normalId.Front)
415
end
416
if original.Back then
417
tableInsert(args, normalId.Back)
418
end
419
420
return Axes.new(unpack(args))
421
end,
422
BrickColor = function(original: BrickColor): BrickColor
423
return BrickColor.new(original.Number)
424
end,
425
CatalogSearchParams = function(original: CatalogSearchParams): CatalogSearchParams
426
local clone: CatalogSearchParams = CatalogSearchParams.new()
427
clone.AssetTypes = original.AssetTypes
428
clone.BundleTypes = original.BundleTypes
429
clone.CategoryFilter = original.CategoryFilter
430
clone.MaxPrice = original.MaxPrice
431
clone.MinPrice = original.MinPrice
432
clone.SearchKeyword = original.SearchKeyword
433
clone.SortType = original.SortType
434
435
return clone
436
end,
437
CFrame = function(original: CFrame): CFrame
438
return CFrame.fromMatrix(original.Position, original.XVector, original.YVector, original.ZVector)
439
end,
440
Color3 = function(original: Color3): Color3
441
return colorRGB(original.R, original.G, original.B)
442
end,
443
ColorSequence = function(original: ColorSequence): ColorSequence
444
return ColorSequence.new(original.Keypoints)
445
end,
446
ColorSequenceKeypoint = function(original: ColorSequenceKeypoint): ColorSequenceKeypoint
447
return ColorSequenceKeypoint.new(original.Time, original.Value)
448
end,
449
DateTime = function(original: DateTime): DateTime
450
return DateTime.fromUnixTimestamp(original.UnixTimestamp)
451
end,
452
DockWidgetPluginGuiInfo = function(original: DockWidgetPluginGuiInfo): DockWidgetPluginGuiInfo
453
local arguments = split(tostring(original), " ")
454
local dockState: string = sub(arguments[1], 18, -1)
455
local initialEnabled: boolean = tonumber(sub(arguments[2], 16, -1)) ~= 0
456
local initialShouldOverride: boolean = tonumber(sub(arguments[3], 38, -1)) ~= 0
457
local floatX: number = tonumber(sub(arguments[4], 15, -1))
458
local floatY: number = tonumber(sub(arguments[5], 15, -1))
459
local minWidth: number = tonumber(sub(arguments[6], 10, -1))
460
local minHeight: number = tonumber(sub(arguments[7], 11, -1))
461
-- can't read the properties so i have to tostring first :(
462
463
return DockWidgetPluginGuiInfo.new(Enum.InitialDockState[dockState], initialEnabled, initialShouldOverride, floatX, floatY, minWidth, minHeight)
464
end,
465
Enum = function(original: Enum): Enum
466
--return original -- enums don't gc
467
return false -- doesn't get sent
468
end,
469
EnumItem = function(original: EnumItem): EnumItem
470
return original -- enums don't gc
471
end,
472
Enums = function(original: Enums): Enums
473
--return original -- enums don't gc
474
return false -- doesn't get sent
475
end,
476
Faces = function(original: Faces): Faces
477
local args = {}
478
if original.Top then
479
tableInsert(args, normalId.Top)
480
end
481
if original.Bottom then
482
tableInsert(args, normalId.Bottom)
483
end
484
if original.Left then
485
tableInsert(args, normalId.Left)
486
end
487
if original.Right then
488
tableInsert(args, normalId.Right)
489
end
490
if original.Back then
491
tableInsert(args, normalId.Back)
492
end
493
if original.Front then
494
tableInsert(args, normalId.Front)
495
end
496
497
return Faces.new(unpack(args))
498
end,
499
FloatCurveKey = function(original: FloatCurveKey): FloatCurveKey
500
return FloatCurveKey.new(original.Time, original.Value, original.Interpolation)
501
end,
502
Font = function(original: Font): Font
503
local clone: Font = Font.new(original.Family, original.Weight, original.Style)
504
clone.Bold = original.Bold
505
506
return clone
507
end,
508
Instance = function(original: Instance): Instance
509
return cloneref(original)
510
end,
511
NumberRange = function(original: NumberRange): NumberRange
512
return NumberRange.new(original.Min, original.Max)
513
end,
514
NumberSequence = function(original: NumberSequence): NumberSequence
515
return NumberSequence.new(original.Keypoints)
516
end,
517
NumberSequenceKeypoint = function(original: NumberSequenceKeypoint): NumberSequenceKeypoint
518
return NumberSequenceKeypoint.new(original.Time, original.Value, original.Envelope)
519
end,
520
OverlapParams = function(original: OverlapParams): OverlapParams
521
--[[local clone: OverlapParams = OverlapParams.new()
522
clone.CollisionGroup = original.CollisionGroup
523
clone.FilterDescendantsInstances = original.FilterDescendantsInstances
524
clone.FilterType = original.FilterType
525
clone.MaxParts = original.MaxParts
526
527
return clone]]
528
return false -- doesn't get sent
529
end,
530
PathWaypoint = function(original: PathWaypoint): PathWaypoint
531
return PathWaypoint.new(original.Position, original.Action)
532
end,
533
PhysicalProperties = function(original: PhysicalProperties): PhysicalProperties
534
return PhysicalProperties.new(original.Density, original.Friction, original.Elasticity, original.FrictionWeight, original.ElasticityWeight)
535
end,
536
Random = function(original: Random): Random
537
--return original:Clone()
538
return false -- doesn't get sent
539
end,
540
Ray = function(original: Ray): Ray
541
return Ray.new(original.Origin, original.Direction)
542
end,
543
RaycastParams = function(original: RaycastParams): RaycastParams
544
local clone: RaycastParams = RaycastParams.new()
545
clone.CollisionGroup = original.CollisionGroup
546
clone.FilterDescendantsInstances = original.FilterDescendantsInstances
547
clone.FilterType = original.FilterType
548
clone.IgnoreWater = original.IgnoreWater
549
550
return clone
551
end,
552
RaycastResult = function(original: RaycastResult): RaycastResult
553
local params: RaycastParams = RaycastParams.new()
554
params.IgnoreWater = original.Material.Name ~= "Water"
555
params.FilterType = Enum.RaycastFilterType.Whitelist
556
params.FilterDescendantsInstances = { original.Instance }
557
558
local startPos: Vector3 = original.Position+(original.Distance*original.Normal)
559
560
return workspace:Raycast(startPos, CFrame.lookAt(startPos, original.Position).LookVector*math.ceil(original.Distance), params)
561
end,
562
RBXScriptConnection = function(original: RBXScriptConnection): RBXScriptConnection
563
return nil -- can't be sent, unsupported, another option is to send the original, but that's detectable
564
end,
565
RBXScriptSignal = function(original: RBXScriptSignal): RBXScriptSignal
566
return nil -- can't be sent, unsupported
567
end,
568
Rect = function(original: Rect): Rect
569
return Rect.new(original.Min, original.Max)
570
end,
571
Region3 = function(original: Region3): Region3
572
local center = original.CFrame.Position
573
574
return Region3.new(center-original.Size/2, center+original.Size/2)
575
end,
576
Region3int16 = function(original: Region3int16): Region3int16
577
return Region3int16.new(original.Min, original.Max)
578
end,
579
RotationCurveKey = function(original: RotationCurveKey): RotationCurveKey
580
--return RotationCurveKey.new(original.Time, original.Value, original.Interpolation)
581
return false -- doesn't get sent
582
end,
583
TweenInfo = function(original: TweenInfo): TweenInfo
584
return TweenInfo.new(original.Time, original.EasingStyle, original.EasingDirection, original.RepeatCount, original.Reverses, original.DelayTime)
585
end,
586
UDim = function(original: UDim): UDim
587
return UDim.new(original.Scale, original.Offset)
588
end,
589
UDim2 = function(original: UDim2): UDim2
590
-- I've tested it and confirmed that even though they share identical X and Y userdata properties, they do not reference the same thing, and so gcing will not detect this.
591
return UDim2.new(original.X, original.Y)
592
end,
593
userdata = function(original) -- no typechecking for userdatas like this (newproxy)
594
return false -- doesn't get sent
595
end,
596
Vector2 = function(original: Vector2): Vector2
597
return Vector2.new(original.X, original.Y)
598
end,
599
Vector2int16 = function(original: Vector2int16): Vector2int16
600
return Vector2int16.new(original.X, original.Y)
601
end,
602
Vector3 = function(original: Vector3): Vector3
603
return Vector3.new(original.X, original.Y, original.Z)
604
end,
605
Vector3int16 = function(original: Vector3int16): Vector3int16
606
return Vector3int16.new(original.X, original.Y, original.Z)
607
end
608
}
609
610
local remoteUserdataClone = {
611
Axes = function(original: Axes): Axes
612
local args = {}
613
if original.X and not original.Left and not original.Right then
614
tableInsert(args, axis.X)
615
elseif original.Left then
616
tableInsert(args, normalId.Left)
617
end
618
if original.Right then
619
tableInsert(args, normalId.Right)
620
end
621
622
if original.Y and not original.Top and not original.Bottom then
623
tableInsert(args, axis.Y)
624
elseif original.Top then
625
tableInsert(args, normalId.Top)
626
end
627
if original.Bottom then
628
tableInsert(args, normalId.Bottom)
629
end
630
631
if original.Z and not original.Front and not original.Back then
632
tableInsert(args, axis.Z)
633
elseif original.Front then
634
tableInsert(args, normalId.Front)
635
end
636
if original.Back then
637
tableInsert(args, normalId.Back)
638
end
639
640
return Axes.new(unpack(args))
641
end,
642
BrickColor = function(original: BrickColor): BrickColor
643
return BrickColor.new(original.Number)
644
end,
645
CatalogSearchParams = function(original: CatalogSearchParams): CatalogSearchParams
646
--[[local clone: CatalogSearchParams = CatalogSearchParams.new()
647
clone.AssetTypes = original.AssetTypes
648
clone.BundleTypes = original.BundleTypes
649
clone.CategoryFilter = original.CategoryFilter
650
clone.MaxPrice = original.MaxPrice
651
clone.MinPrice = original.MinPrice
652
clone.SearchKeyword = original.SearchKeyword
653
clone.SortType = original.SortType
654
655
return clone]]
656
return false -- doesn't get sent
657
end,
658
CFrame = function(original: CFrame): CFrame
659
return CFrame.fromMatrix(original.Position, original.XVector, original.YVector, original.ZVector)
660
end,
661
Color3 = function(original: Color3): Color3
662
return colorRGB(original.R, original.G, original.B)
663
end,
664
ColorSequence = function(original: ColorSequence): ColorSequence
665
return ColorSequence.new(original.Keypoints)
666
end,
667
ColorSequenceKeypoint = function(original: ColorSequenceKeypoint): ColorSequenceKeypoint
668
return ColorSequenceKeypoint.new(original.Time, original.Value)
669
end,
670
DateTime = function(original: DateTime): DateTime
671
return DateTime.fromUnixTimestamp(original.UnixTimestamp)
672
end,
673
DockWidgetPluginGuiInfo = function(original: DockWidgetPluginGuiInfo): DockWidgetPluginGuiInfo
674
--[[local arguments = split(tostring(original), " ")
675
local dockState: string = sub(arguments[1], 18, -1)
676
local initialEnabled: boolean = tonumber(sub(arguments[2], 16, -1)) ~= 0
677
local initialShouldOverride: boolean = tonumber(sub(arguments[3], 38, -1)) ~= 0
678
local floatX: number = tonumber(sub(arguments[4], 15, -1))
679
local floatY: number = tonumber(sub(arguments[5], 15, -1))
680
local minWidth: number = tonumber(sub(arguments[6], 10, -1))
681
local minHeight: number = tonumber(sub(arguments[7], 11, -1))
682
-- can't read the properties so i have to tostring first :(
683
684
return DockWidgetPluginGuiInfo.new(Enum.InitialDockState[dockState], initialEnabled, initialShouldOverride, floatX, floatY, minWidth, minHeight)]]
685
return false -- doesn't get sent
686
end,
687
Enum = function(original: Enum): Enum
688
--return original -- enums don't gc
689
return false -- doesn't get sent
690
end,
691
EnumItem = function(original: EnumItem): EnumItem
692
return original -- enums don't gc
693
end,
694
Enums = function(original: Enums): Enums
695
--return original -- enums don't gc
696
return false -- doesn't get sent
697
end,
698
Faces = function(original: Faces): Faces
699
local args = {}
700
if original.Top then
701
tableInsert(args, normalId.Top)
702
end
703
if original.Bottom then
704
tableInsert(args, normalId.Bottom)
705
end
706
if original.Left then
707
tableInsert(args, normalId.Left)
708
end
709
if original.Right then
710
tableInsert(args, normalId.Right)
711
end
712
if original.Back then
713
tableInsert(args, normalId.Back)
714
end
715
if original.Front then
716
tableInsert(args, normalId.Front)
717
end
718
719
return Faces.new(unpack(args))
720
end,
721
FloatCurveKey = function(original: FloatCurveKey): FloatCurveKey
722
--return FloatCurveKey.new(original.Time, original.Value, original.Interpolation)
723
return false -- doesn't get sent
724
end,
725
Font = function(original: Font): Font
726
local clone: Font = Font.new(original.Family, original.Weight, original.Style)
727
clone.Bold = original.Bold
728
729
return clone
730
end,
731
Instance = function(original: Instance): Instance
732
return cloneref(original)
733
end,
734
NumberRange = function(original: NumberRange): NumberRange
735
return NumberRange.new(original.Min, original.Max)
736
end,
737
NumberSequence = function(original: NumberSequence): NumberSequence
738
return NumberSequence.new(original.Keypoints)
739
end,
740
NumberSequenceKeypoint = function(original: NumberSequenceKeypoint): NumberSequenceKeypoint
741
return NumberSequenceKeypoint.new(original.Time, original.Value, original.Envelope)
742
end,
743
OverlapParams = function(original: OverlapParams): OverlapParams
744
--[[local clone: OverlapParams = OverlapParams.new()
745
clone.CollisionGroup = original.CollisionGroup
746
clone.FilterDescendantsInstances = original.FilterDescendantsInstances
747
clone.FilterType = original.FilterType
748
clone.MaxParts = original.MaxParts
749
750
return clone]]
751
return false -- doesn't get sent
752
end,
753
PathWaypoint = function(original: PathWaypoint): PathWaypoint
754
return PathWaypoint.new(original.Position, original.Action)
755
end,
756
PhysicalProperties = function(original: PhysicalProperties): PhysicalProperties
757
return PhysicalProperties.new(original.Density, original.Friction, original.Elasticity, original.FrictionWeight, original.ElasticityWeight)
758
end,
759
Random = function(original: Random): Random
760
--return original:Clone()
761
return false -- doesn't get sent
762
end,
763
Ray = function(original: Ray): Ray
764
return Ray.new(original.Origin, original.Direction)
765
end,
766
RaycastParams = function(original: RaycastParams): RaycastParams
767
--[[local clone: RaycastParams = RaycastParams.new()
768
clone.CollisionGroup = original.CollisionGroup
769
clone.FilterDescendantsInstances = original.FilterDescendantsInstances
770
clone.FilterType = original.FilterType
771
clone.IgnoreWater = original.IgnoreWater
772
773
return clone]]
774
return false -- doesn't get sent
775
end,
776
RaycastResult = function(original: RaycastResult): RaycastResult
777
--[[local params: RaycastParams = RaycastParams.new()
778
params.IgnoreWater = original.Material.Name ~= "Water"
779
params.FilterType = Enum.RaycastFilterType.Whitelist
780
params.FilterDescendantsInstances = { original.Instance }
781
782
local startPos: Vector3 = original.Position+(original.Distance*original.Normal)
783
784
return workspace:Raycast(startPos, CFrame.lookAt(startPos, original.Position).LookVector*math.ceil(original.Distance), params)]]
785
return false -- doesn't get sent
786
end,
787
RBXScriptConnection = function(original: RBXScriptConnection): RBXScriptConnection
788
return false -- doesn't get sent
789
end,
790
RBXScriptSignal = function(original: RBXScriptSignal): RBXScriptSignal
791
return false -- doesn't get sent
792
end,
793
Rect = function(original: Rect): Rect
794
return Rect.new(original.Min, original.Max)
795
end,
796
Region3 = function(original: Region3): Region3
797
local center = original.CFrame.Position
798
799
return Region3.new(center-original.Size/2, center+original.Size/2)
800
end,
801
Region3int16 = function(original: Region3int16): Region3int16
802
return Region3int16.new(original.Min, original.Max)
803
end,
804
RotationCurveKey = function(original: RotationCurveKey): RotationCurveKey
805
--return RotationCurveKey.new(original.Time, original.Value, original.Interpolation)
806
return false -- doesn't get sent
807
end,
808
TweenInfo = function(original: TweenInfo): TweenInfo
809
--return TweenInfo.new(original.Time, original.EasingStyle, original.EasingDirection, original.RepeatCount, original.Reverses, original.DelayTime)
810
return false -- doesn't get sent
811
end,
812
UDim = function(original: UDim): UDim
813
return UDim.new(original.Scale, original.Offset)
814
end,
815
UDim2 = function(original: UDim2): UDim2
816
-- I've tested it and confirmed that even though they share identical X and Y userdata properties, they do not reference the same thing, and so gcing will not detect this.
817
return UDim2.new(original.X, original.Y)
818
end,
819
userdata = function(original) -- no typechecking for userdatas like this (newproxy)
820
return false -- doesn't get sent
821
end,
822
Vector2 = function(original: Vector2): Vector2
823
return Vector2.new(original.X, original.Y)
824
end,
825
Vector2int16 = function(original: Vector2int16): Vector2int16
826
return Vector2int16.new(original.X, original.Y)
827
end,
828
Vector3 = function(original: Vector3): Vector3
829
return Vector3.new(original.X, original.Y, original.Z)
830
end,
831
Vector3int16 = function(original: Vector3int16): Vector3int16
832
return Vector3int16.new(original.X, original.Y, original.Z)
833
end
834
}
835
836
local function cloneUserdata(userdata: any, remoteType: string): any
837
local cloneTable = (remoteType == "BindableEvent" or remoteType == "BindableFunction") and bindableUserdataClone or remoteUserdataClone
838
local userdataType = typeof(userdata)
839
local func = cloneTable[userdataType]
840
if not func then -- func was false
841
pushError("Unknown Userdata: \"" .. userdataType .. ",\" please report to GameGuy#5286")
842
local clone = newproxy(true)
843
getmetatable(clone).__tostring = function()
844
return userdataType
845
end
846
return clone -- userdata that I have never seen before
847
else
848
local clone = func(userdata)
849
if clone == nil then
850
clone = newproxy(true)
851
getmetatable(clone).__tostring = function()
852
return userdataType -- be careful here, if I were to put typeof(userdata) in, it would pass userdata as an upvalue, which would cause it to never gc, leading to a detection.
853
end
854
return clone -- userdatas are reserved for unclonable types because they can never be sent to the server
855
elseif not clone then
856
return -- userdata isn't sent to the server
857
else
858
return clone -- good userdata
859
end
860
end
861
end
862
863
local function getSpecialKey(index: any): string
864
local prefix = specialTypes[typeof(index)] or typeof(index)
865
866
local oldMt = getrawmetatable(index)
867
local returnStr = ""
868
if oldMt then
869
local wasReadOnly = isreadonly(oldMt)
870
if wasReadOnly then setreadonly(oldMt, false) end
871
local oldToString = rawget(oldMt, "__tostring")
872
873
rawset(oldMt, "__tostring", nil)
874
returnStr = "<" .. prefix .. ">" .. " (" .. tostring(index) .. ")"
875
rawset(oldMt, "__tostring", oldToString)
876
if wasReadOnly then setreadonly(oldMt, true) end
877
else
878
returnStr = "<" .. prefix .. ">" .. " (" .. tostring(index) .. ")"
879
end
880
881
return returnStr
882
end
883
884
local function cloneData(data: any, callType: string)
885
local primType: string = type(data)
886
if primType == "userdata" or primType == "vector" then
887
if typeof(data) == "Instance" and not IsDescendantOf(data, game) then
888
return cloneUserdata(data, callType), true
889
end
890
891
return cloneUserdata(data, callType)
892
elseif primType == "thread" then
893
return -- can't be sent
894
elseif primType == "function" and (callType == "RemoteEvent" or callType == "RemoteFunction") then
895
return -- can't be sent
896
else
897
return data -- any non cloneables (numbers, strings, etc)
898
end
899
end
900
901
local function createIndex(index: any) -- from my testing, calltype doesn't affect indexes
902
local primType = type(index)
903
if primType == "userdata" or primType == "vector" or primType == "function" or primType == "table" then
904
return getSpecialKey(index)
905
else
906
return index -- threads and nils are unhandled in this function because neither can be indexed by
907
end
908
end
909
910
-- this function should only be used on deepClone({...}), and only on the first table, where we can be sure that it should be all number indices, this is unsafe to use on any other tables. The first table from deepClone may not be in order (due to nils being weird), so we need a comparison of indices.
911
local function getLastIndex(tbl): number
912
local final: number = 0
913
for i in tbl do
914
if i > final then
915
final = i
916
end
917
end
918
919
return final
920
end
921
922
local function deepClone(myTable, callType: string, stack: number?) -- cyclic check built in
923
stack = stack or 0 -- you can offset stack by setting the starting parameter to a number
924
local newTable = {}
925
local hasTable = false
926
local hasNilParentedInstance = false
927
local started = false
928
local originalDepth = stack
929
local consecutiveIndices = getn(myTable)
930
local isConsecutive = (consecutiveIndices ~= 0) and (stack ~= -1) -- consecutives don't count when it's the original data (nils break stuff)
931
932
if stack == 300 then -- this stack overflow check doesn't really matter as a stack overflow check, it's just here to make sure there are no cyclic tables. While I could just check for cyclics directly, this is faster.
933
return false, stack
934
end
935
for i,v in next, myTable do
936
if not isConsecutive or (type(i) == "number" and i <= consecutiveIndices) then
937
if not started then started = true; stack += 1 end
938
local index = createIndex(i)
939
if index then
940
local value = nil
941
if type(v) == "table" then
942
hasTable = true
943
local newTab, maxStack, _, subHasNilParentedInstance = deepClone(v, callType, originalDepth+1)
944
hasNilParentedInstance = hasNilParentedInstance or subHasNilParentedInstance
945
if maxStack > stack then
946
stack = maxStack
947
end
948
949
if newTab then
950
value = newTab
951
else
952
return false, stack -- stack overflow
953
end
954
else
955
local nilParented
956
value, nilParented = cloneData(v, callType)
957
hasNilParentedInstance = hasNilParentedInstance or nilParented
958
end
959
newTable[index] = value
960
end
961
end
962
end
963
964
return newTable, stack, hasTable, hasNilParentedInstance
965
end
966
967
local function pushTheme(window: RenderChildBase)
968
for i,v in styleOptions do
969
window:SetStyle(RenderStyleOption[i], v)
970
end
971
972
for i,v in colorOptions do
973
window:SetColor(RenderColorOption[i], v[1], v[2])
974
end
975
end
976
977
local function addSpacer(window, amt: number)
978
local bufferMain = window:Dummy()
979
bufferMain:SetColor(RenderColorOption.Button, black, 0)
980
bufferMain:SetColor(RenderColorOption.ButtonActive, black, 0)
981
bufferMain:SetColor(RenderColorOption.ButtonHovered, black, 0)
982
local buffer = bufferMain:Button()
983
buffer.Size = Vector2.new(10, amt)
984
return bufferMain
985
end
986
987
local asciiFilteredCharacters = {
988
["\""] = "\\\"",
989
["\\"] = "\\\\",
990
["\a"] = "\\a",
991
["\b"] = "\\b",
992
["\t"] = "\\t",
993
["\n"] = "\\n",
994
["\v"] = "\\v",
995
["\f"] = "\\f",
996
["\r"] = "\\r"
997
}
998
999
for Index = 0, 255 do
1000
if (Index < 32 or Index > 126) then -- only non printable ascii characters
1001
local character = string.char(Index)
1002
if not asciiFilteredCharacters[character] then
1003
asciiFilteredCharacters[character] = "\\" .. Index
1004
end
1005
end
1006
end
1007
1008
local function purifyString(str: string, quotes: boolean, maxLength: number)
1009
if type(maxLength) == "number" then
1010
str = sub(str, 1, maxLength)
1011
end
1012
str = gsub(str, "[\"\\\0-\31\127-\255]", asciiFilteredCharacters)
1013
if type(maxLength) == "number" then
1014
str = sub(str, 1, maxLength)
1015
end
1016
--[[
1017
This gsub can be broken down into multiple steps.
1018
It filters quotations (\") and backslashes "\\" to be replaced,
1019
Then it filters characters 0-31, and 127-255, replacing them all with their escape sequences
1020
]]
1021
if quotes then
1022
return "\"" .. str .. "\""
1023
else
1024
return str
1025
end
1026
end
1027
1028
local gameId, workspaceId = getDebugId(game), getDebugId(workspace)
1029
1030
local function instanceParentedToNil(instance: Instance) -- too cursed to use (insanely slow in certain games)
1031
local instanceId = getDebugId(instance)
1032
for _,v in getnilinstances() do
1033
if getDebugId(v) == instanceId then
1034
return true
1035
end
1036
end
1037
end
1038
1039
local function getInstancePath(instance: Instance) -- FORKED FROM HYDROXIDE
1040
if not instance then return "NIL INSTANCE" end -- probably is impossible, cant be bothered to confirm
1041
local s = tick()
1042
setThreadIdentity(8)
1043
local id = getDebugId(instance)
1044
1045
local name = instance.Name
1046
local head = (#name > 0 and '.' .. name) or "['']"
1047
1048
if not instance.Parent and id ~= gameId then
1049
return "(nil)" .. head .. " --[[ INSTANCE DELETED/PARENTED TO NIL ]]", false
1050
--if not instanceParentedToNil(instance) then
1051
--return "(nil)" .. head .. " --[[ INSTANCE DELETED FROM GAME ]]", false
1052
--else
1053
--return "(nil)" .. head .. " --[[ PARENTED TO NIL ]]", false
1054
--end
1055
end
1056
1057
if id == gameId then
1058
return "game", true, true
1059
elseif id == workspaceId then
1060
return "workspace", true, true
1061
else
1062
local plr = Players:GetPlayerFromCharacter(instance)
1063
if plr then
1064
if getDebugId(plr) == clientid then
1065
return 'game:GetService("Players").LocalPlayer.Character', true, true
1066
else
1067
if tonumber(sub(plr.Name, 1, 1)) then
1068
return 'game:GetService("Players")["'..plr.Name..'"]".Character', true, true
1069
else
1070
return 'game:GetService("Players").'..plr.Name..'.Character', true, true
1071
end
1072
end
1073
end
1074
local _success, result = pcall(game.GetService, game, instance.ClassName)
1075
1076
if _success and result then
1077
return 'game:GetService("' .. instance.ClassName .. '")', true, true
1078
elseif id == clientid then -- cloneref moment
1079
return 'game:GetService("Players").LocalPlayer', true, true
1080
else
1081
local nonAlphaNum = gsub(name, '[%w_]', '')
1082
local noPunct = gsub(nonAlphaNum, '[%s%p]', '')
1083
1084
if tonumber(sub(name, 1, 1)) or (#nonAlphaNum ~= 0 and #noPunct == 0) then
1085
head = '[' .. purifyString(name, true) .. ']'
1086
elseif #nonAlphaNum ~= 0 and #noPunct > 0 then
1087
head = '[' .. purifyString(name, true) .. ']'
1088
end
1089
end
1090
end
1091
1092
return (getInstancePath(instance.Parent) .. head), true
1093
end
1094
1095
local tableToString;
1096
local makeUserdataConstructor = {
1097
Axes = function(original: Axes): Axes
1098
local constructor: string = "Axes.new("
1099
if original.X and not original.Left and not original.Right then
1100
constructor ..= "Enum.Axis.X, "
1101
elseif original.Left then
1102
constructor ..= "Enum.NormalId.Left, "
1103
end
1104
if original.Right then
1105
constructor ..= "Enum.NormalId.Right, "
1106
end
1107
1108
if original.Y and not original.Top and not original.Bottom then
1109
constructor ..= "Enum.Axis.Y, "
1110
elseif original.Top then
1111
constructor ..= "Enum.NormalId.Top, "
1112
end
1113
if original.Bottom then
1114
constructor ..= "Enum.NormalId.Bottom, "
1115
end
1116
1117
if original.Z and not original.Front and not original.Back then
1118
constructor ..= "Enum.Axis.Z, "
1119
elseif original.Front then
1120
constructor ..= "Enum.NormalId.Front, "
1121
end
1122
if original.Back then
1123
constructor ..= "Enum.NormalId.Back, "
1124
end
1125
1126
return (constructor ~= "Axes.new(" and sub(constructor, 0, -3) or constructor) .. ")"
1127
end,
1128
BrickColor = function(original: BrickColor): string
1129
return "BrickColor.new(\"" .. original.Name .. "\")"
1130
end,
1131
CatalogSearchParams = function(original: CatalogSearchParams): string
1132
return strformat("(function() local clone: CatalogSearchParams = CatalogSearchParams.new(); clone.AssetTypes = %s; clone.BundleTimes = %s; clone.CategoryFilter = %s; clone.MaxPrice = %s; clone.MinPrice = %s; clone.SearchKeyword = %s; clone.SortType = %s; return clone end)()", tostring(original.AssetTypes), tostring(original.BundleTypes), tostring(original.CategoryFilter), original.MaxPrice, original.MinPrice, original.SearchKeyword, tostring(original.SortType))
1133
end,
1134
CFrame = function(original: CFrame): string
1135
return "CFrame.new(" .. tostring(original) .. ")"
1136
end,
1137
Color3 = function(original: Color3): string
1138
return "Color3.new(" .. tostring(original) .. ")"
1139
end,
1140
ColorSequence = function(original: ColorSequence): string
1141
return "ColorSequence.new(" .. tableToString(original.Keypoints, false) ..")"
1142
end,
1143
ColorSequenceKeypoint = function(original: ColorSequenceKeypoint): string
1144
return "ColorSequenceKeypoint.new(" .. original.Time .. ", Color3.new(" .. tostring(original.Value) .. "))"
1145
end,
1146
DateTime = function(original: DateTime): string
1147
return "DateTime.fromUnixTimestamp(" .. tostring(original.UnixTimestamp) .. ")"
1148
end,
1149
DockWidgetPluginGuiInfo = function(original: DockWidgetPluginGuiInfo): string
1150
local arguments = split(tostring(original), " ")
1151
local dockState: string = sub(arguments[1], 18, -1)
1152
local initialEnabled: boolean = tonumber(sub(arguments[2], 16, -1)) ~= 0
1153
local initialShouldOverride: boolean = tonumber(sub(arguments[3], 38, -1)) ~= 0
1154
local floatX: number = tonumber(sub(arguments[4], 15, -1))
1155
local floatY: number = tonumber(sub(arguments[5], 15, -1))
1156
local minWidth: number = tonumber(sub(arguments[6], 10, -1))
1157
local minHeight: number = tonumber(sub(arguments[7], 11, -1))
1158
-- can't read the properties so i have to tostring first :(
1159
1160
return strformat("DockWidgetPluginGuiInfo.new(%s, %s, %s, %s, %s, %s, %s)", "Enum.InitialDockState." .. dockState, tostring(initialEnabled), tostring(initialShouldOverride), tostring(floatX), tostring(floatY), tostring(minWidth), tostring(minHeight))
1161
end,
1162
Enum = function(original: Enum): string
1163
return "Enum." .. tostring(original)
1164
end,
1165
EnumItem = function(original: EnumItem): string
1166
return tostring(original)
1167
end,
1168
Enums = function(original: Enums): string
1169
return "Enum"
1170
end,
1171
Faces = function(original: Faces): string
1172
local constructor = "Faces.new("
1173
if original.Top then
1174
constructor ..= "Enum.NormalId.Top"
1175
end
1176
if original.Bottom then
1177
constructor ..= "Enum.NormalId.Bottom"
1178
end
1179
if original.Left then
1180
constructor ..= "Enum.NormalId.Left"
1181
end
1182
if original.Right then
1183
constructor ..= "Enum.NormalId.Right"
1184
end
1185
if original.Back then
1186
constructor ..= "Enum.NormalId.Back"
1187
end
1188
if original.Front then
1189
constructor ..= "Enum.NormalId.Front"
1190
end
1191
1192
return (constructor ~= "Faces.new(" and sub(constructor, 0, -3) or constructor) .. ")"
1193
end,
1194
FloatCurveKey = function(original: FloatCurveKey): string
1195
return "FloatCurveKey.new(" .. tostring(original.Time) .. ", " .. tostring(original.Value) .. ", " .. tostring(original.Interpolation) .. ")"
1196
end,
1197
Font = function(original: Font): string
1198
return strformat("(function() local clone: Font = Font.new(%s, %s, %s); clone.Bold = %s; return clone end)()", '"' .. original.Family .. '"', tostring(original.Weight), tostring(original.Style), tostring(original.Bold))
1199
end,
1200
Instance = function(original: Instance): string
1201
return getInstancePath(original)
1202
end,
1203
NumberRange = function(original: NumberRange): string
1204
return "NumberRange.new(" .. tostring(original.Min) .. ", " .. tostring(original.Max) .. ")"
1205
end,
1206
NumberSequence = function(original: NumberSequence): string
1207
return "NumberSequence.new(" .. tableToString(original.Keypoints, false) .. ")"
1208
end,
1209
NumberSequenceKeypoint = function(original: NumberSequenceKeypoint): string
1210
return "NumberSequenceKeypoint.new(" .. tostring(original.Time) .. ", " .. tostring(original.Value) .. ", " .. tostring(original.Envelope) .. ")"
1211
end,
1212
OverlapParams = function(original: OverlapParams): OverlapParams
1213
return strformat("(function(): OverlapParams local clone: OverlapParams = OverlapParams.new(); clone.CollisionGroup = %s; clone.FilterDescendantInstances = %s; clone.FilterType = %s; clone.MaxParts = %s; return clone end)()", original.CollisionGroup, tableToString(original.FilterDescendantsInstances, false, Settings.InstanceTrackerMode), tostring(original.FilterType), tostring(original.MaxParts))
1214
end,
1215
PathWaypoint = function(original: PathWaypoint): string
1216
return "PathWaypoint.new(Vector3.new(" .. tostring(original.Position) .. "), " .. tostring(original.Action) .. ")"
1217
end,
1218
PhysicalProperties = function(original: PhysicalProperties): string
1219
return "PhysicalProperties.new(" .. tostring(original) .. ")"
1220
end,
1221
Random = function(original: Random): string
1222
return "Random.new()" -- detectable cause of seed change
1223
end,
1224
Ray = function(original: Ray): string
1225
return "Ray.new(Vector3.new(" .. tostring(original.Origin) .. "), Vector3.new(" .. tostring(original.Direction) .. "))"
1226
end,
1227
RaycastParams = function(original: RaycastParams): string
1228
return strformat("(function(): RaycastParams local clone: RaycastParams = RaycastParams.new(); clone.CollisionGroup = %s; clone.FilterDescendantsInstances = %s; clone.FilterType = %s; clone.FilterWater = %s; return clone end)()", original.CollisionGroup, tableToString(original.FilterDescendantsInstances, false, Settings.InstanceTrackerMode), tostring(original.FilterType), tostring(original.IgnoreWater))
1229
end,
1230
RaycastResult = function(original: RaycastResult): string
1231
return strformat("(function(): RaycastParams local params: RaycastParams = RaycastParams.new(); params.IgnoreWater = %s; params.FilterType = %s; params.FilterDescendantsInstances = %s; local startPos: Vector3 = %s; return workspace:Raycast(startPos, CFrame.lookAt(startPos, %s).LookVector*math.ceil(%s), params) end)()", tostring(original.Material.Name ~= "Water"), tostring(Enum.RaycastFilterType.Whitelist), tableToString(original.Instance, false, Settings.InstanceTrackerMode), "Vector3.new(" .. original.Position+(original.Distance*original.Normal) .. ")", "Vector3.new(" .. original.Position .. ")", "Vector3.new(" .. original.Distance .. ")")
1232
end,
1233
RBXScriptConnection = function(original: RBXScriptConnection): string
1234
return "nil --[[ RBXScriptConnection is Unsupported ]]"
1235
end,
1236
RBXScriptSignal = function(original: RBXScriptSignal): string
1237
return "nil --[[ RBXScriptSignal is Unsupported ]]"
1238
end,
1239
Rect = function(original: Rect): string
1240
return "Rect.new(Vector2.new(" .. tostring(original.Min) .. "), Vector2.new(" .. tostring(original.Max) .. "))"
1241
end,
1242
Region3 = function(original: Region3): string
1243
local center = original.CFrame.Position
1244
1245
return "Region3.new(Vector3.new(" .. tostring(center-original.Size/2) .. "), Vector3.new(" .. tostring(center+original.Size/2) .. "))"
1246
end,
1247
Region3int16 = function(original: Region3int16): string
1248
return "Region3int16.new(Vector3int16.new(" .. tostring(original.Min) .. "), Vector3int16.new(" .. tostring(original.Max) .. "))"
1249
end,
1250
RotationCurveKey = function(original: RotationCurveKey): RotationCurveKey
1251
return "RotationCurveKey.new(" .. tostring(original.Time) .. ", CFrame.new(" .. tostring(original.Value) .. "), " .. tostring(original.Interpolation) .. ")"
1252
end,
1253
TweenInfo = function(original: TweenInfo): string
1254
return "TweenInfo.new(" .. tostring(original.Time) .. ", " .. tostring(original.EasingStyle) .. ", " .. tostring(original.EasingDirection) .. ", " .. tostring(original.RepeatCount) .. ", " .. tostring(original.Reverses) .. ", " .. tostring(original.DelayTime) .. ")"
1255
end,
1256
UDim = function(original: UDim): string
1257
return "UDim.new(" .. tostring(original) .. ")"
1258
end,
1259
UDim2 = function(original: UDim2): string
1260
return "UDim2.new(" .. tostring(original) .. ")"
1261
end,
1262
userdata = function(original): string -- no typechecking for userdatas like this (newproxy)
1263
return "nil --[[ " .. tostring(original) .. " is Unsupported ]]" -- newproxies can never be sent, and as such are reserved by the remotespy to be used when a type that could not be deepCloned was sent. The tostring metamethod should've been modified to refelct the original type.
1264
end,
1265
Vector2 = function(original: Vector2): string
1266
return "Vector2.new(" .. tostring(original) .. ")"
1267
end,
1268
Vector2int16 = function(original: Vector2int16): string
1269
return "Vector2int16.new(" .. tostring(original) .. ")"
1270
end,
1271
Vector3 = function(original: Vector3): string
1272
return "Vector3.new(" .. tostring(original) .. ")"
1273
end,
1274
Vector3int16 = function(original: Vector3int16): string
1275
return "Vector3int16.new(" .. tostring(original) .. ")"
1276
end
1277
}
1278
1279
local function getUserdataConstructor(userdata: any): string
1280
local userdataType = typeof(userdata)
1281
local constructorCreator = makeUserdataConstructor[userdataType]
1282
1283
if constructorCreator then
1284
return constructorCreator(userdata)
1285
else
1286
return "nil --[[ " .. userdataType .. " is Unsupported ]]"
1287
end
1288
end
1289
1290
-- localized elsewhere
1291
1292
tableToString = function(data: any, format: boolean, debugMode: boolean, root: any, indents: number) -- FORKED FROM HYDROXIDE
1293
local dataType = type(data)
1294
1295
format = format == nil and true or format
1296
1297
if dataType == "userdata" or dataType == "vector" then
1298
if typeof(data) == "Instance" then
1299
local str, parented, bypasses = getInstancePath(data)
1300
if (debugMode == 3 or (debugMode == 2 and parented)) and not bypasses then
1301
return ("GetInstanceFromDebugId(\"" .. getDebugId(data) .."\")") .. (" --[[ Original Path: " .. str..(parented and " ]]" or ""))
1302
else
1303
return str
1304
end
1305
else
1306
return getUserdataConstructor(data)
1307
end
1308
elseif dataType == "string" then
1309
local success, result = pcall(purifyString, data, true)
1310
return (success and result) or tostring(data)
1311
elseif dataType == "table" then
1312
indents = indents or 1
1313
root = root or data
1314
1315
local head = format and '{\n' or '{ '
1316
local indent = rep('\t', indents)
1317
local consecutiveIndices = (#data ~= 0)
1318
local elementCount = 0
1319
-- moved checkCyclic check to hook
1320
if format then
1321
if consecutiveIndices then
1322
for i,v in data do
1323
elementCount += 1
1324
if type(i) ~= "number" then continue end
1325
1326
if i ~= elementCount then
1327
for _ = 1, (i-elementCount) do
1328
head ..= (indent .. "nil,\n")
1329
end
1330
elementCount = i
1331
end
1332
head ..= strformat("%s%s,\n", indent, tableToString(v, true, debugMode, root, indents + 1))
1333
end
1334
else
1335
for i,v in data do
1336
head ..= strformat("%s[%s] = %s,\n", indent, tableToString(i, true, debugMode, root, indents + 1), tableToString(v, true, debugMode, root, indents + 1))
1337
end
1338
end
1339
else
1340
if consecutiveIndices then
1341
for i,v in data do
1342
elementCount += 1
1343
if type(i) ~= "number" then continue end
1344
1345
if i ~= elementCount then
1346
for _ = 1, (i-elementCount) do
1347
head ..= "nil, "
1348
end
1349
elementCount = i
1350
end
1351
head ..= (tableToString(v, false, debugMode, root, indents + 1) .. ", ")
1352
end
1353
else
1354
for i,v in data do
1355
head ..= strformat("[%s] = %s, ", tableToString(i, false, debugMode, root, indents + 1), tableToString(v, false, debugMode, root, indents + 1))
1356
end
1357
end
1358
end
1359
1360
if format then
1361
return #head > 2 and strformat("%s\n%s", sub(head, 1, -3), rep('\t', indents - 1) .. '}') or "{}"
1362
else
1363
return #head > 2 and (sub(head, 1, -3) .. ' }') or "{}"
1364
end
1365
elseif dataType == "function" then -- functions are only receivable through bindables, not remotes
1366
return 'nil --[[ ' .. tostring(data) .. " ]]" -- just in case
1367
elseif dataType == "number" then
1368
local dataStr = tostring(data)
1369
if not match(dataStr, "%d") then
1370
if data == inf then
1371
return "(1/0)"
1372
elseif data == neginf then
1373
return "(-1/0)"
1374
elseif dataStr == "nan" then
1375
return "(0/0)"
1376
else
1377
return ("tonumber(\"" .. dataStr .. "\")")
1378
end
1379
else
1380
return dataStr
1381
end
1382
else
1383
return tostring(data)
1384
end
1385
end
1386
1387
local types = {
1388
["string"] = { colorHSV(29/360, 0.8, 1), function(obj, maxLength)
1389
return purifyString(obj, true, maxLength)
1390
end },
1391
["number"] = { colorHSV(120/360, 0.8, 1), function(obj)
1392
return tostring(obj)
1393
end },
1394
["boolean"] = { colorHSV(211/360, 0.8, 1), function(obj)
1395
return tostring(obj)
1396
end },
1397
["table"] = { white, function(obj)
1398
return tostring(obj)
1399
end },
1400
1401
--[[["userdata"] = { colorHSV(258/360, 0.8, 1), function(obj)
1402
return "Unprocessed Userdata: " .. typeof(obj) .. ": " .. tostring(obj)
1403
end },
1404
["Instance"] = { colorHSV(57/360, 0.8, 1), function(obj)
1405
return tostring(obj)
1406
end },]]
1407
1408
["function"] = { white, function(obj)
1409
-- functions can't be received by Remotes, but can be received by Bindables
1410
return tostring(obj)
1411
end },
1412
["nil"] = { colorHSV(360/360, 0.8, 1), function(obj)
1413
return "nil"
1414
end }
1415
}
1416
1417
local function getArgString(arg: any, maxLength: number)
1418
local t = type(arg)
1419
1420
if types[t] and t ~= "userdata" then
1421
local st = types[t]
1422
return st[2](arg, maxLength), st[1]
1423
elseif t == "userdata" or t == "vector" then
1424
local st = getUserdataConstructor(arg)
1425
return st, (typeof(arg) == "Instance" and colorHSV(57/360, 0.8, 1)) or colorHSV(314/360, 0.8, 1)
1426
else
1427
return ("Unprocessed Lua Type: " .. tostring(t)), colorRGB(1, 1, 1)
1428
end
1429
end
1430
1431
local spaces = " "
1432
local spaces2 = " " -- 8 spaces
1433
1434
local idxs = {
1435
FireServer = 1,
1436
InvokeServer = 2,
1437
Fire = 3,
1438
Invoke = 4,
1439
1440
fireServer = 1,
1441
invokeServer = 2,
1442
fire = 3,
1443
invoke = 4,
1444
1445
OnClientEvent = 5,
1446
OnClientInvoke = 6,
1447
Event = 7,
1448
OnInvoke = 8,
1449
}
1450
1451
local spyFunctions = {
1452
{
1453
Name = "FireServer",
1454
Object = "RemoteEvent",
1455
Type = "Call",
1456
Method = "FireServer",
1457
DeprecatedMethod = "fireServer",
1458
Enabled = Settings.FireServer,
1459
Icon = "\xee\x80\x80 ",
1460
Indent = 0
1461
},
1462
{
1463
Name = "InvokeServer",
1464
Object = "RemoteFunction",
1465
Type = "Call",
1466
ReturnsValue = true,
1467
Method = "InvokeServer",
1468
DeprecatedMethod = "invokeServer",
1469
Enabled = Settings.InvokeServer,
1470
Icon = "\xee\x80\x81 ",
1471
Indent = 153
1472
},
1473
{
1474
Name = "Fire",
1475
Object = "BindableEvent",
1476
Type = "Call",
1477
FiresLocally = true,
1478
Method = "Fire",
1479
DeprecatedMethod = "fire",
1480
Enabled = Settings.Fire,
1481
Icon = "\xee\x80\x82 ",
1482
Indent = 319
1483
},
1484
{
1485
Name = "Invoke",
1486
Object = "BindableFunction",
1487
Type = "Call",
1488
ReturnsValue = true,
1489
FiresLocally = true,
1490
Method = "Invoke",
1491
DeprecatedMethod = "invoke",
1492
Enabled = Settings.Invoke,
1493
Icon = "\xee\x80\x83 ",
1494
Indent = 434
1495
},
1496
1497
{
1498
Name = "OnClientEvent",
1499
Object = "RemoteEvent",
1500
HasNoCaller = true,
1501
Type = "Connection",
1502
Connection = "OnClientEvent",
1503
DeprecatedConnection = "onClientEvent",
1504
Enabled = Settings.OnClientEvent,
1505
Icon = "\xee\x80\x84 ",
1506
Indent = 0
1507
},
1508
{
1509
Name = "OnClientInvoke",
1510
Object = "RemoteFunction",
1511
ReturnsValue = true,
1512
HasNoCaller = true,
1513
Type = "Callback",
1514
Callback = "OnClientInvoke",
1515
DeprecatedCallback = "onClientInvoke",
1516
Enabled = Settings.OnClientInvoke,
1517
Icon = "\xee\x80\x85 ",
1518
Indent = 153
1519
},
1520
{
1521
Name = "OnEvent",
1522
Object = "BindableEvent",
1523
Type = "Connection",
1524
Connection = "Event", -- not OnEvent cause roblox naming is wacky
1525
DeprecatedConnection = "event",
1526
Enabled = Settings.OnEvent,
1527
Icon = "\xee\x80\x86 ",
1528
Indent = 319
1529
},
1530
{
1531
Name = "OnInvoke",
1532
Object = "BindableFunction",
1533
ReturnsValue = true,
1534
Type = "Callback",
1535
Callback = "OnInvoke",
1536
DeprecatedCallback = "onInvoke",
1537
Enabled = Settings.OnInvoke,
1538
Icon = "\xee\x80\x87 ",
1539
Indent = 434
1540
}
1541
}
1542
1543
local repeatCallSteps = {
1544
1,
1545
10,
1546
100,
1547
1000
1548
}
1549
1550
local function repeatStringWithIndex(prefix: string, suffix: string, count: number)
1551
local retVal = ""
1552
for i = 1, count do
1553
retVal ..= (prefix .. tostring(i) .. suffix)
1554
end
1555
1556
return retVal
1557
end
1558
1559
local function genSendPseudo(rem: Instance, call, spyFunc)
1560
local watermark = Settings.PseudocodeWatermark and watermarkString or ""
1561
1562
local debugMode = Settings.InstanceTrackerMode
1563
1564
if debugMode == 3 or (debugMode == 2 and call.HasInstance) then
1565
watermark ..= (--[[Settings.OptimizedInstanceTracker and optimizedInstanceTrackerFunctionString or]] instanceTrackerFunctionString) .. "\n\n"
1566
else
1567
watermark ..= "\n"
1568
end
1569
1570
local pathStr, parented = getInstancePath(rem)
1571
local remPath = ((debugMode == 3 or ((debugMode == 2) and not parented)) and ("GetInstanceFromDebugId(\"" .. getDebugId(rem) .."\")" .. " -- Original Path: " .. pathStr)) or pathStr
1572
1573
if call.NonNilArgCount == 0 and call.NilCount == 0 then
1574
if spyFunc.Type == "Call" then
1575
return watermark .. (Settings.PseudocodeInlineRemote and ("local remote" .. (Settings.PseudocodeLuaUTypes and (": " .. spyFunc.Object) or "").." = " .. remPath .. "\n\n" .. (spyFunc.ReturnsValue and ("local returnValue = ") or "") .. "remote:") or (remPath .. ":")) .. spyFunc.Method .."()"
1576
elseif spyFunc.Type == "Connection" then
1577
return watermark .. (Settings.PseudocodeInlineRemote and ("local remote" .. (Settings.PseudocodeLuaUTypes and (": " .. spyFunc.Object) or "").." = " .. remPath .. "\n\nfiresignal(remote.") or ("firesignal(" .. remPath ".")) .. spyFunc.Connection ..")"
1578
elseif spyFunc.Type == "Callback" then
1579
return watermark .. (Settings.PseudocodeInlineRemote and ("local remote" .. (Settings.PseudocodeLuaUTypes and (": " .. spyFunc.Object) or "").." = " .. remPath .. "\n\ngetcallbackmember(remote, \"") or ("getcallbackmember(" .. remPath .. ", \"")) .. spyFunc.Callback .."\")()"
1580
end
1581
else
1582
local argCalls = {}
1583
local argCallCount = {}
1584
1585
local pseudocode = ""
1586
local addedArg = false
1587
1588
for i = 1, call.NonNilArgCount do
1589
local arg = call.Args[i]
1590
local primTyp = type(arg)
1591
local tempTyp = typeof(arg)
1592
local typ = (gsub(tempTyp, "^%u", lower))
1593
1594
argCallCount[typ] = argCallCount[typ] and argCallCount[typ] + 1 or 1
1595
1596
local varName = typ .. tostring(argCallCount[typ])
1597
1598
if primTyp == "nil" then
1599
tableInsert(argCalls, { typ, primTyp, "", "" })
1600
continue
1601
end
1602
1603
local varPrefix = ""
1604
if primTyp ~= "function" and primTyp ~= "table" and Settings.PseudocodeLuaUTypes then
1605
varPrefix = "local " .. varName .. ": ".. tempTyp .." = "
1606
else
1607
varPrefix = "local " .. varName .." = "
1608
end
1609
local varConstructor = ""
1610
1611
if primTyp == "userdata" or primTyp == "vector" then -- roblox should just get rid of vector already
1612
if typeof(arg) == "Instance" then
1613
local str, parented, bypasses = getInstancePath(arg)
1614
if (debugMode == 3 or (debugMode == 2 and not parented)) and not bypasses then
1615
varConstructor = ("GetInstanceFromDebugId(\"" .. getDebugId(arg) .."\")") .. (" -- Original Path: " .. str)
1616
else
1617
varConstructor = str
1618
end
1619
else
1620
varConstructor = getUserdataConstructor(arg)
1621
end
1622
elseif primTyp == "table" then
1623
varConstructor = tableToString(arg, Settings.PseudocodeFormatTables, debugMode)
1624
elseif primTyp == "string" then
1625
varConstructor = purifyString(arg, true)
1626
elseif primTyp == "function" then
1627
varConstructor = 'nil -- [[ ' .. tostring(arg) .. ' ]]' -- functions can be sent by bindables, but I can't exactly generate pseudocode for them
1628
elseif primTyp == "number" then
1629
local dataStr = tostring(arg)
1630
if not match(dataStr, "%d") then
1631
if arg == inf then
1632
varConstructor = "(1/0)"
1633
elseif arg == neginf then
1634
varConstructor = "(-1/0)"
1635
elseif dataStr == "nan" then
1636
varConstructor = "(0/0)"
1637
else
1638
varConstructor = ("tonumber(\"" .. dataStr .. "\")")
1639
end
1640
else
1641
varConstructor = dataStr
1642
end
1643
else
1644
varConstructor = tostring(arg)
1645
end
1646
1647
tableInsert(argCalls, { typ, primTyp, varConstructor, varName })
1648
1649
if Settings.PseudocodeInliningMode == 3 and primTyp == "table" then
1650
pseudocode ..= (varPrefix .. (varConstructor .. "\n"))
1651
addedArg = true
1652
elseif Settings.PseudocodeInliningMode == 2 and (primTyp == "table" or primTyp == "userdata") then
1653
pseudocode ..= (varPrefix .. (varConstructor .. "\n"))
1654
addedArg = true
1655
elseif Settings.PseudocodeInliningMode == 1 then
1656
pseudocode ..= (varPrefix .. (varConstructor .. "\n"))
1657
addedArg = true
1658
end
1659
end
1660
1661
if Settings.PseudocodeInlineHiddenNils then
1662
for i = 1, call.NilCount do
1663
pseudocode ..= "local hiddenNil" .. tostring(i) .. " = nil -- games can detect if this is missing, but likely won't.\n"
1664
addedArg = true
1665
end
1666
end
1667
if spyFunc.Type == "Call" then
1668
pseudocode ..= Settings.PseudocodeInlineRemote and ((addedArg and "\n" or "") .. "local remote" .. (Settings.PseudocodeLuaUTypes and (": " .. spyFunc.Object) or "").." = " .. remPath .. "\n" .. (spyFunc.ReturnsValue and "local returnValue = " or "") .. "remote:" .. spyFunc.Method .. "(") or (remPath .. ":" .. spyFunc.Method .. "(")
1669
elseif spyFunc.Type == "Connection" then
1670
pseudocode ..= Settings.PseudocodeInlineRemote and ((addedArg and "\n" or "") .. "local remote" .. (Settings.PseudocodeLuaUTypes and (": " .. spyFunc.Object) or "").." = " .. remPath .. "\n" .. (spyFunc.ReturnsValue--[[yes i know this is redundant]] and "local returnValue = " or "") .. "firesignal(remote." .. spyFunc.Connection .. ", ") or ("firesignal(" .. remPath .. "." .. spyFunc.Connection .. ", ")
1671
elseif spyFunc.Type == "Callback" then
1672
pseudocode ..= Settings.PseudocodeInlineRemote and ((addedArg and "\n" or "") .. "local remote" .. (Settings.PseudocodeLuaUTypes and (": " .. spyFunc.Object) or "").." = " .. remPath .. "\n" .. (spyFunc.ReturnsValue and "local returnValue = " or "") .. "getcallbackmember(remote, \"" .. spyFunc.Callback .. "\")(") or ("getcallbackmember(" .. remPath .. ", \"" .. spyFunc.Callback .. "\")(")
1673
end
1674
1675
if Settings.PseudocodeInliningMode == 4 then
1676
for _,v in argCalls do
1677
if v[1] == "nil" then
1678
pseudocode ..= "nil, "
1679
else
1680
pseudocode ..= (v[3] .. ", ")
1681
end
1682
end
1683
elseif Settings.PseudocodeInliningMode == 3 then
1684
for _,v in argCalls do
1685
if v[1] == "nil" then
1686
pseudocode ..= "nil, "
1687
elseif (v[2] == "table") then
1688
pseudocode ..= ( v[4] .. ", " )
1689
else
1690
pseudocode ..= ( v[3] .. ", " )
1691
end
1692
end
1693
elseif Settings.PseudocodeInliningMode == 2 then
1694
for _,v in argCalls do
1695
if v[1] == "nil" then
1696
pseudocode ..= "nil, "
1697
elseif (v[2] == "table" or v[2] == "userdata") then
1698
pseudocode ..= ( v[4] .. ", " )
1699
else
1700
pseudocode ..= ( v[3] .. ", " )
1701
end
1702
end
1703
else
1704
for _,v in argCalls do
1705
if v[1] == "nil" then
1706
pseudocode ..= "nil, "
1707
else
1708
pseudocode ..= ( v[4] .. ", " )
1709
end
1710
end
1711
end
1712
1713
if Settings.PseudocodeInlineHiddenNils then
1714
for i = 1, call.NilCount do
1715
pseudocode ..= ("hiddenNil" .. tostring(i) .. ", ")
1716
end
1717
else
1718
for _ = 1, call.NilCount do
1719
pseudocode ..= "nil, "
1720
end
1721
end
1722
1723
return watermark .. (sub(pseudocode, -2, -2) == "," and sub(pseudocode, 1, -3) or pseudocode) .. ")" -- sub gets rid of the last ", "
1724
end
1725
end
1726
1727
local function genReturnValuePseudo(returnTable, spyFunc)
1728
local watermark = Settings.PseudocodeWatermark and watermarkString .. "\n" or ""
1729
1730
if #returnTable.Args == 0 and returnTable.NilCount == 0 then
1731
return watermark .. "return"
1732
else
1733
local argCalls = {}
1734
local argCallCount = {}
1735
1736
local pseudocode = ""
1737
local addedArg = false
1738
1739
for i = 1, #returnTable.Args do
1740
local arg = returnTable.Args[i]
1741
local primTyp = type(arg)
1742
local tempTyp = typeof(arg)
1743
local typ = (gsub(tempTyp, "^%u", lower))
1744
1745
argCallCount[typ] = argCallCount[typ] and argCallCount[typ] + 1 or 1
1746
1747
local varName = typ .. tostring(argCallCount[typ])
1748
1749
if primTyp == "nil" then
1750
continue
1751
end
1752
1753
local varPrefix = ""
1754
if primTyp ~= "function" and Settings.PseudocodeLuaUTypes then
1755
varPrefix = "local " .. varName .. ": ".. tempTyp .." = "
1756
else
1757
varPrefix = "local " .. varName .." = "
1758
end
1759
local varConstructor = ""
1760
1761
if primTyp == "userdata" or primTyp == "vector" then -- roblox should just get rid of vector already
1762
varConstructor = getUserdataConstructor(arg)
1763
elseif primTyp == "table" then
1764
varConstructor = tableToString(arg, Settings.PseudocodeFormatTables, 1)
1765
elseif primTyp == "string" then
1766
varConstructor = purifyString(arg, true)
1767
elseif primTyp == "function" then
1768
varConstructor = 'nil --[[ ' .. tostring(arg) .. ' ]]'
1769
elseif primTyp == "number" then
1770
local dataStr = tostring(arg)
1771
if not match(tostring(arg), "%d") then
1772
if arg == inf then
1773
varConstructor = "(1/0)"
1774
elseif arg == neginf then
1775
varConstructor = "(-1/0)"
1776
elseif dataStr == "nan" then
1777
varConstructor = "(0/0)"
1778
else
1779
varConstructor = ("tonumber(\"" .. dataStr .. "\")")
1780
end
1781
else
1782
varConstructor = dataStr
1783
end
1784
else
1785
varConstructor = tostring(arg)
1786
end
1787
1788
tableInsert(argCalls, { typ, primTyp, varConstructor, varName })
1789
1790
if Settings.PseudocodeInliningMode == 3 and primTyp == "table" then
1791
pseudocode ..= (varPrefix .. (varConstructor .. "\n"))
1792
addedArg = true
1793
elseif Settings.PseudocodeInliningMode == 2 and (primTyp == "table" or primTyp == "userdata") then
1794
pseudocode ..= (varPrefix .. (varConstructor .. "\n"))
1795
addedArg = true
1796
elseif Settings.PseudocodeInliningMode == 1 then
1797
pseudocode ..= (varPrefix .. (varConstructor .. "\n"))
1798
addedArg = true
1799
end
1800
end
1801
1802
if Settings.PseudocodeInlineHiddenNils then
1803
for i = 1, returnTable.NilCount do
1804
pseudocode ..= "local hiddenNil" .. tostring(i) .. " = nil -- games can detect if this is missing, but likely won't.\n"
1805
addedArg = true
1806
end
1807
end
1808
pseudocode ..= (addedArg and "\n" or "") .. "return "
1809
1810
if Settings.PseudocodeInliningMode == 4 then
1811
for _,v in argCalls do
1812
if v[1] == "nil" then
1813
pseudocode ..= "nil, "
1814
else
1815
pseudocode ..= (v[3] .. ", ")
1816
end
1817
end
1818
elseif Settings.PseudocodeInliningMode == 3 then
1819
for _,v in argCalls do
1820
if v[1] == "nil" then
1821
pseudocode ..= "nil, "
1822
elseif (v[2] == "table") then
1823
pseudocode ..= ( v[4] .. ", " )
1824
else
1825
pseudocode ..= ( v[3] .. ", " )
1826
end
1827
end
1828
elseif Settings.PseudocodeInliningMode == 2 then
1829
for _,v in argCalls do
1830
if v[1] == "nil" then
1831
pseudocode ..= "nil, "
1832
elseif (v[2] == "table" or v[2] == "userdata") then
1833
pseudocode ..= ( v[4] .. ", " )
1834
else
1835
pseudocode ..= ( v[3] .. ", " )
1836
end
1837
end
1838
else
1839
for _,v in argCalls do
1840
if v[1] == "nil" then
1841
pseudocode ..= "nil, "
1842
else
1843
pseudocode ..= ( v[4] .. ", " )
1844
end
1845
end
1846
end
1847
1848
if Settings.PseudocodeInlineHiddenNils then
1849
for i = 1, returnTable.NilCount do
1850
pseudocode ..= ("hiddenNil" .. tostring(i) .. ", ")
1851
end
1852
else
1853
for _ = 1, returnTable.NilCount do
1854
pseudocode ..= "nil, "
1855
end
1856
end
1857
1858
return watermark .. sub(pseudocode, 1, -3) -- gets rid of last ", "
1859
end
1860
end
1861
1862
local function genRecvPseudo(rem: Instance, call, spyFunc, watermark: string)
1863
local watermark = watermark and watermarkString or ""
1864
1865
local debugMode = Settings.InstanceTrackerMode
1866
1867
if debugMode == 3 or (debugMode == 2 and call.HasInstance) then
1868
watermark ..= (--[[Settings.OptimizedInstanceTracker and optimizedInstanceTrackerFunctionString or]] instanceTrackerFunctionString) .. "\n\n"
1869
else
1870
watermark ..= "\n"
1871
end
1872
1873
local pathStr = getInstancePath(rem)
1874
local remPath = ((debugMode == 3 or ((debugMode == 2) and (sub(pathStr, -2, -1) == "]]"))) and ("GetInstanceFromDebugId(\"" .. getDebugId(rem) .."\")" .. " -- Original Path: " .. pathStr)) or pathStr
1875
1876
if spyFunc.Type == "Connection" then
1877
local pseudocode = ""
1878
1879
pseudocode ..= Settings.PseudocodeInlineRemote and ("local remote" .. (Settings.PseudocodeLuaUTypes and (": " .. spyFunc.Object) or "") .." = " .. remPath .. "\nremote." .. spyFunc.Connection .. ":Connect(function(") or (remPath .. "." .. spyFunc.Connection .. ":Connect(function(")
1880
for i = 1,call.NonNilArgCount do
1881
pseudocode ..= "p" .. tostring(i) .. ", "
1882
end
1883
pseudocode = (call.NonNilArgCount > 0 and sub(pseudocode, 1, -3) or pseudocode) .. ")"
1884
1885
pseudocode ..= "\n\tprint(\"" .. spyFunc.Connection .. " Connection Fired:\", "
1886
for i = 1,call.NonNilArgCount do
1887
pseudocode ..= "p"..tostring(i) .. ", "
1888
end
1889
pseudocode = (sub(pseudocode, 1, -3) .. ")")
1890
1891
pseudocode ..= "\nend)"
1892
return watermark .. pseudocode
1893
elseif spyFunc.Type == "Callback" then
1894
local pseudocode = ""
1895
1896
pseudocode ..= Settings.PseudocodeInlineRemote and ("local remote" .. (Settings.PseudocodeLuaUTypes and (": " .. spyFunc.Object) or "").." = " .. remPath .. "\nremote." .. spyFunc.Callback .. " = function(") or (remPath .. "." .. spyFunc.Callback .. " = function(")
1897
for i = 1,call.NonNilArgCount do
1898
pseudocode ..= "p"..tostring(i) .. ", "
1899
end
1900
pseudocode = (call.NonNilArgCount > 0 and sub(pseudocode, 1, -3) or pseudocode) .. ")"
1901
1902
pseudocode ..= "\n\tprint(\"" .. spyFunc.Callback .. " Callback Called:\", "
1903
for i = 1,call.NonNilArgCount do
1904
pseudocode ..= "p"..tostring(i) .. ", "
1905
end
1906
pseudocode = (sub(pseudocode, 1, -3) .. ")")
1907
1908
pseudocode ..= "\nend"
1909
return watermark .. pseudocode
1910
end
1911
end
1912
1913
local otherLines = {}
1914
local otherLogs = {}
1915
local otherFuncs = {}
1916
local callLines = {}
1917
local callLogs = {}
1918
local callFuncs = {}
1919
1920
local originalCallerCache = {}
1921
local remoteNameCache = {}
1922
local remoteBlacklistCache = setmetatable({}, {__mode="kv"}) -- used to store remotes that aren't replicated to the server
1923
-- remoteBlacklistCache[remote] = nil or 1 or 2, 1 = allowed, 2 = blacklisted, nil = not initialized
1924
1925
local argLines = {}
1926
local callbackButtonline
1927
1928
local searchBar -- declared later
1929
local function clearFilter()
1930
for i,v in callLines do
1931
for _,x in spyFunctions do
1932
if v[1] == x.Name and (v[3].Label ~= "0" or callLogs[i].Ignored) and x.Enabled then
1933
v[2].Visible = true
1934
v[4].Visible = true
1935
break
1936
end
1937
end
1938
end
1939
for i,v in otherLines do
1940
for _,x in spyFunctions do
1941
if v[1] == x.Name and (v[3].Label ~= "0" or otherLogs[i].Ignored) and x.Enabled then
1942
v[2].Visible = true
1943
v[4].Visible = true
1944
break
1945
end
1946
end
1947
end
1948
end
1949
1950
local function filterLines(name: string)
1951
if name == "" then
1952
return clearFilter()
1953
end
1954
1955
for i,v in callLines do
1956
if not match(lower(tostring(i)), lower(name)) then -- check for if the remote actually had a log made
1957
v[2].Visible = false
1958
v[4].Visible = false
1959
elseif spyFunctions[idxs[v[1]]].Enabled then
1960
v[2].Visible = true
1961
v[4].Visible = true
1962
end
1963
end
1964
for i,v in otherLines do
1965
if not match(lower(tostring(i)), lower(name)) then -- check for if the remote actually had a log made
1966
v[2].Visible = false
1967
v[4].Visible = false
1968
elseif spyFunctions[idxs[v[1]]].Enabled then
1969
v[2].Visible = true
1970
v[4].Visible = true
1971
end
1972
end
1973
end
1974
1975
local function updateLines(name: string, enabled: boolean)
1976
for i,v in callLines do
1977
if v[1] == name then
1978
if v[2].Visible ~= enabled then
1979
if (enabled and (v[3].Label ~= "0" or callLogs[i].Ignored)) or not enabled then
1980
v[2].Visible = enabled
1981
v[4].Visible = enabled
1982
end
1983
end
1984
end
1985
end
1986
for i,v in otherLines do
1987
if v[1] == name then
1988
if v[2].Visible ~= enabled then
1989
if (enabled and (v[3].Label ~= "0" or otherLogs[i].Ignored)) or not enabled then
1990
v[2].Visible = enabled
1991
v[4].Visible = enabled
1992
end
1993
end
1994
end
1995
end
1996
filterLines(searchBar.Value)
1997
end
1998
1999
repeat task.wait() until pcall(function()
2000
_G.remoteSpyMainWindow = RenderWindow.new("Remote Spy")
2001
end)
2002
_G.remoteSpySettingsWindow = RenderWindow.new("Remote Spy Settings")
2003
_G.remoteSpyCallbackHooks = {}
2004
_G.remoteSpySignalHooks = {}
2005
_G.remoteSpyHooks = {}
2006
2007
local mainWindow = _G.remoteSpyMainWindow
2008
local mainWindowWeakReference = setmetatable({mainWindow}, {__mode="v"})
2009
local settingsWindow = _G.remoteSpySettingsWindow
2010
local settingsWindowWeakReference = setmetatable({settingsWindow}, {__mode="v"})
2011
pushTheme(mainWindow)
2012
pushTheme(settingsWindow)
2013
2014
-- settings page init
2015
local settingsWidth = 310
2016
local settingsHeight = 312
2017
settingsWindow.DefaultSize = Vector2.new(settingsWidth, settingsHeight)
2018
settingsWindow.CanResize = false
2019
settingsWindow.VisibilityOverride = Settings.AlwaysOnTop
2020
settingsWindow.Visible = false
2021
settingsWindow:SetColor(RenderColorOption.ResizeGrip, black, 0)
2022
settingsWindow:SetColor(RenderColorOption.ResizeGripActive, black, 0)
2023
settingsWindow:SetColor(RenderColorOption.ResizeGripHovered, black, 0)
2024
settingsWindow:SetStyle(RenderStyleOption.WindowPadding, Vector2.new(8, 4))
2025
settingsWindow:SetStyle(RenderStyleOption.ButtonTextAlign, Vector2.new(0.5, 0.5))
2026
2027
-- main page init
2028
local width = 562
2029
mainWindow.DefaultSize = Vector2.new(width, 350)
2030
mainWindow.MinSize = Vector2.new(width, 356)
2031
mainWindow.MaxSize = Vector2.new(width, 5000)
2032
mainWindow.VisibilityOverride = Settings.AlwaysOnTop
2033
2034
local frontPage = mainWindow:Dummy()
2035
local remotePage = mainWindow:Dummy()
2036
2037
-- Below this is rendering Settings page
2038
2039
local topBar = settingsWindow:SameLine()
2040
local exitButtonFrame = topBar:SameLine()
2041
exitButtonFrame:SetColor(RenderColorOption.Button, black, 0)
2042
--exitButtonFrame:SetColor(RenderColorOption.ButtonHovered, black, 0)
2043
--exitButtonFrame:SetColor(RenderColorOption.ButtonActive, black, 0)
2044
2045
local exitButton = exitButtonFrame:Indent(settingsWidth-40):Button()
2046
exitButton.Label = "\xef\x80\x8d"
2047
exitButton.Size = Vector2.new(24, 24)
2048
exitButton.OnUpdated:Connect(function()
2049
settingsWindowWeakReference[1].Visible = false
2050
end)
2051
2052
local tabFrame = topBar:SameLine()
2053
local settingsTabs = tabFrame:Indent(-1):Indent(1):TabMenu()
2054
local generalTab = settingsTabs:Add("General")
2055
local pseudocodeTab = settingsTabs:Add("Pseudocode")
2056
local outputTab = settingsTabs:Add("Output")
2057
local creditsTab = settingsTabs:Add("Credits")
2058
do -- general Settings
2059
local checkBox = generalTab:CheckBox()
2060
checkBox.Label = "Display Callbacks And Connections"
2061
checkBox.Value = Settings.CallbackButtons
2062
checkBox.OnUpdated:Connect(function(value)
2063
Settings.CallbackButtons = value
2064
callbackButtonline.Visible = value
2065
if not value then
2066
for i = 5,8 do
2067
local spyFunc = spyFunctions[i]
2068
spyFunc.Button.Value = false
2069
spyFunc.Enabled = false
2070
Settings[spyFunc.Name] = false
2071
updateLines(spyFunc.Name, false)
2072
end
2073
end
2074
saveConfig()
2075
end)
2076
2077
local checkBox4 = generalTab:CheckBox()
2078
checkBox4.Label = "Cache Unselected Types' Calls"
2079
checkBox4.Value = Settings.LogHiddenRemotesCalls
2080
checkBox4.OnUpdated:Connect(function(value)
2081
Settings.LogHiddenRemotesCalls = value
2082
saveConfig()
2083
end)
2084
2085
local checkBox6 = generalTab:CheckBox()
2086
checkBox6.Label = "Call Cache Amount Limiter (Per Remote)"
2087
checkBox6.Value = Settings.CacheLimit
2088
checkBox6.OnUpdated:Connect(function(value)
2089
Settings.CacheLimit = value
2090
saveConfig()
2091
end)
2092
2093
local slider1 = generalTab:IntSlider()
2094
slider1.Label = "Max Calls"
2095
slider1.Min = 100
2096
slider1.Max = 10000 -- if you need to cache more than 10k calls, just disable caching
2097
slider1.Value = Settings.MaxCallAmount
2098
slider1.Clamped = true
2099
slider1.OnUpdated:Connect(function(value)
2100
if value >= 100 and value <= 10000 then -- incase they're mid way through typing it
2101
Settings.MaxCallAmount = value
2102
saveConfig()
2103
end
2104
end)
2105
2106
local slider2 = generalTab:IntSlider()
2107
slider2.Label = "Max Args"
2108
slider2.Min = 10
2109
slider2.Max = 100
2110
slider2.Value = Settings.ArgLimit
2111
slider2.Clamped = true
2112
slider2.OnUpdated:Connect(function(value)
2113
if value >= 10 and value <= 100 then -- incase they're mid way through typing it
2114
Settings.ArgLimit = value
2115
saveConfig()
2116
end
2117
end)
2118
2119
local checkBox7 = generalTab:CheckBox()
2120
checkBox7.Label = "Enable Get Calling Script V2"
2121
checkBox7.Value = Settings.GetCallingScriptV2
2122
checkBox7.OnUpdated:Connect(function(value)
2123
Settings.GetCallingScriptV2 = value
2124
saveConfig()
2125
end)
2126
2127
local checkBox8 = generalTab:CheckBox()
2128
checkBox8.Label = "Enable Get Call Stack"
2129
checkBox8.Value = Settings.StoreCallStack
2130
checkBox8.OnUpdated:Connect(function(value)
2131
Settings.StoreCallStack = value
2132
saveConfig()
2133
end)
2134
2135
local checkBox5 = generalTab:CheckBox()
2136
checkBox5.Label = "Extra Repeat Call Amounts"
2137
checkBox5.Value = Settings.MoreRepeatCallOptions
2138
checkBox5.OnUpdated:Connect(function(value)
2139
Settings.MoreRepeatCallOptions = value
2140
saveConfig()
2141
end)
2142
2143
local checkBox6 = generalTab:CheckBox()
2144
checkBox6.Label = "Always On Top"
2145
checkBox6.Value = Settings.AlwaysOnTop
2146
checkBox6.OnUpdated:Connect(function(value)
2147
Settings.AlwaysOnTop = value
2148
mainWindowWeakReference[1].VisibilityOverride = value
2149
settingsWindowWeakReference[1].VisibilityOverride = value
2150
saveConfig()
2151
end)
2152
end -- general settings
2153
2154
do -- pseudocode settings
2155
2156
local checkBox1 = pseudocodeTab:CheckBox()
2157
checkBox1.Label = "Pseudocode Watermark"
2158
checkBox1.Value = Settings.PseudocodeWatermark
2159
checkBox1.OnUpdated:Connect(function(value)
2160
Settings.PseudocodeWatermark = value
2161
saveConfig()
2162
end)
2163
2164
local checkBox2 = pseudocodeTab:CheckBox()
2165
checkBox2.Label = "Use LuaU Type Declarations"
2166
checkBox2.Value = Settings.PseudocodeLuaUTypes
2167
checkBox2.OnUpdated:Connect(function(value)
2168
Settings.PseudocodeLuaUTypes = value
2169
saveConfig()
2170
end)
2171
2172
local checkBox5 = pseudocodeTab:CheckBox()
2173
checkBox5.Label = "Format Tables"
2174
checkBox5.Value = Settings.PseudocodeFormatTables
2175
checkBox5.OnUpdated:Connect(function(value)
2176
Settings.PseudocodeFormatTables = value
2177
saveConfig()
2178
end)
2179
2180
local checkBox3 = pseudocodeTab:CheckBox()
2181
checkBox3.Label = "Inline Remote"
2182
checkBox3.Value = Settings.PseudocodeInlineRemote
2183
checkBox3.OnUpdated:Connect(function(value)
2184
Settings.PseudocodeInlineRemote = value
2185
saveConfig()
2186
end)
2187
2188
local checkBox4 = pseudocodeTab:CheckBox()
2189
checkBox4.Label = "Inline Hidden Nils"
2190
checkBox4.Value = Settings.PseudocodeInlineHiddenNils
2191
checkBox4.OnUpdated:Connect(function(value)
2192
Settings.PseudocodeInlineHiddenNils = value
2193
saveConfig()
2194
end)
2195
2196
pseudocodeTab:Label("Pseudocode Inlining Mode")
2197
local combo2 = pseudocodeTab:Combo()
2198
combo2.Items = { "Everything", "Tables And Userdatas", "Tables Only", "Nothing" }
2199
combo2.SelectedItem = Settings.PseudocodeInliningMode
2200
combo2.OnUpdated:Connect(function(selection)
2201
Settings.PseudocodeInliningMode = selection
2202
saveConfig()
2203
end)
2204
2205
pseudocodeTab:Label("Instance Tracker")
2206
local combo3 = pseudocodeTab:Combo()
2207
combo3.Items = { "Off", "Nil Parented Only", "All Instances" }
2208
combo3.SelectedItem = Settings.InstanceTrackerMode
2209
combo3.OnUpdated:Connect(function(selection)
2210
Settings.InstanceTrackerMode = selection
2211
saveConfig()
2212
end)
2213
2214
--[[local checkBox5 = pseudocodeTab:CheckBox()
2215
checkBox5.Label = "Optimized Instance Tracker"
2216
checkBox5.Value = Settings.OptimizedInstanceTracker
2217
checkBox5.OnUpdated:Connect(function(value)
2218
Settings.OptimizedInstanceTracker = value
2219
saveConfig()
2220
end)]]
2221
end -- pseudocode settings
2222
2223
do -- output settings
2224
outputTab:Label("Pseudocode Output")
2225
local combo1 = outputTab:Combo()
2226
combo1.Items = { "Clipboard", "External UI", "Internal UI (Not Implemented)" }
2227
combo1.SelectedItem = Settings.PseudocodeOutput
2228
combo1.OnUpdated:Connect(function(selection)
2229
Settings.PseudocodeOutput = selection
2230
saveConfig()
2231
end)
2232
2233
outputTab:Label("Decompiled Script Output")
2234
local combo2 = outputTab:Combo()
2235
combo2.Items = { "Clipboard", "External UI", "Internal UI (Not Implemented)" }
2236
combo2.SelectedItem = Settings.DecompilerOutput
2237
combo2.OnUpdated:Connect(function(selection)
2238
Settings.DecompilerOutput = selection
2239
saveConfig()
2240
end)
2241
2242
outputTab:Label("Connections List Output")
2243
local combo3 = outputTab:Combo()
2244
combo3.Items = { "Clipboard", "External UI", "Internal UI (Not Implemented)" }
2245
combo3.SelectedItem = Settings.ConnectionsOutput
2246
combo3.OnUpdated:Connect(function(selection)
2247
Settings.ConnectionsOutput = selection
2248
saveConfig()
2249
end)
2250
2251
outputTab:Label("Call Stack Output")
2252
local combo4 = outputTab:Combo()
2253
combo4.Items = { "Clipboard", "External UI", "Internal UI (Not Implemented)" }
2254
combo4.SelectedItem = Settings.CallStackOutput
2255
combo4.OnUpdated:Connect(function(selection)
2256
Settings.CallStackOutput = selection
2257
saveConfig()
2258
end)
2259
end -- theme settings
2260
2261
do -- credits
2262
creditsTab:Label("Written by GameGuy")
2263
creditsTab:Label("With Inspriation from Hydroxide")
2264
creditsTab:Separator()
2265
2266
creditsTab:Label("Discord: GameGuy#5286 | 515708480661749770")
2267
local setDiscordToClipboard = creditsTab:Button()
2268
setDiscordToClipboard.Label = "Set Discord ID To Clipboard"
2269
setDiscordToClipboard.OnUpdated:Connect(function()
2270
setclipboard("515708480661749770")
2271
end)
2272
2273
creditsTab:Separator()
2274
creditsTab:Label("Thank you to all of the Contributors on Github")
2275
end -- credits
2276
2277
-- Below this is rendering Remote Page
2278
2279
remotePage.Visible = false
2280
2281
local currentSelectedRemote, currentSelectedRemoteInstance, currentSelectedType
2282
2283
local remotePageObjects = {
2284
Name = nil,
2285
Icon = nil,
2286
IconFrame = nil,
2287
IgnoreButton = nil,
2288
IgnoreButtonFrame = nil,
2289
BlockButton = nil,
2290
BlockButtonFrame = nil
2291
}
2292
2293
local remoteViewerMainWindow = nil
2294
2295
local function unloadRemote()
2296
frontPage.Visible = true
2297
remotePage.Visible = false
2298
currentSelectedRemote = nil
2299
currentSelectedRemoteInstance = nil
2300
currentSelectedType = ""
2301
for _,v in argLines do
2302
v[2]:Clear()
2303
end
2304
tableClear(argLines)
2305
remoteViewerMainWindow:Clear()
2306
end
2307
2308
local topBar = remotePage:SameLine()
2309
2310
local pauseSpyButton -- declared later, referenced here
2311
local pauseSpyButton2
2312
2313
do -- topbar code
2314
2315
local buttonsFrame = topBar:Dummy():SameLine()
2316
buttonsFrame:SetColor(RenderColorOption.Button, black, 0)
2317
buttonsFrame:SetStyle(RenderStyleOption.ButtonTextAlign, Vector2.new(0.5, 0.5))
2318
2319
pauseSpyButton2 = buttonsFrame:Indent(width-68):Button()
2320
pauseSpyButton2.Size = Vector2.new(24, 24)
2321
pauseSpyButton2.Label = Settings.Paused and "\xef\x80\x9d" or "\xef\x8a\x8c"
2322
pauseSpyButton2.OnUpdated:Connect(function()
2323
if Settings.Paused then
2324
Settings.Paused = false
2325
pauseSpyButton.Label = "\xef\x8a\x8c"
2326
pauseSpyButton2.Label = "\xef\x8a\x8c"
2327
else
2328
Settings.Paused = true
2329
pauseSpyButton.Label = "\xef\x80\x9d"
2330
pauseSpyButton2.Label = "\xef\x80\x9d"
2331
end
2332
end)
2333
2334
local exitButton = buttonsFrame:Indent(width-40):Button()
2335
exitButton.Size = Vector2.new(24, 24)
2336
exitButton.Label = "\xef\x80\x8d"
2337
exitButton.OnUpdated:Connect(unloadRemote)
2338
2339
local remoteNameFrame = topBar:Dummy()
2340
remoteNameFrame:SetStyle(RenderStyleOption.ButtonTextAlign, Vector2.new(0, 0.5))
2341
remoteNameFrame:SetColor(RenderColorOption.Button, black, 0)
2342
remoteNameFrame:SetColor(RenderColorOption.ButtonActive, black, 0)
2343
remoteNameFrame:SetColor(RenderColorOption.ButtonHovered, black, 0)
2344
local remoteName = remoteNameFrame:Indent(26):Button()
2345
remoteName.Size = Vector2.new(300, 24)
2346
remoteName.Label = "RemoteEvent"
2347
2348
local remoteIconFrame = topBar:Dummy():WithFont(RemoteIconFont)
2349
remoteIconFrame:SetStyle(RenderStyleOption.ButtonTextAlign, Vector2.new(1, 0.5))
2350
remoteIconFrame:SetColor(RenderColorOption.Button, black, 0)
2351
remoteIconFrame:SetColor(RenderColorOption.ButtonActive, black, 0)
2352
remoteIconFrame:SetColor(RenderColorOption.ButtonHovered, black, 0)
2353
remoteIconFrame:SetColor(RenderColorOption.Text, black, 1) -- temporarily black, gets set later
2354
local remoteIcon = remoteIconFrame:Indent(4):Button()
2355
remoteIcon.Size = Vector2.new(20, 24)
2356
remoteIcon.Label = "\xee\x80\x80" -- default
2357
2358
remotePageObjects.Name = remoteName
2359
remotePageObjects.Icon = remoteIcon
2360
remotePageObjects.IconFrame = remoteIconFrame
2361
end
2362
2363
local buttonBarFrame = remotePage:SameLine()
2364
2365
do -- button bar code
2366
local buttonBar = buttonBarFrame:Indent(125):SameLine()
2367
2368
local ignoreButtonFrame = buttonBar:Dummy()
2369
ignoreButtonFrame:SetColor(RenderColorOption.Text, red, 1)
2370
local ignoreButton = ignoreButtonFrame:Button()
2371
ignoreButton.Label = "Ignore"
2372
2373
ignoreButton.OnUpdated:Connect(function()
2374
if currentSelectedRemote then
2375
local funcList = (currentSelectedType == "Call") and callFuncs or otherFuncs
2376
local logs = (currentSelectedType == "Call") and callLogs or otherLogs
2377
2378
if logs[currentSelectedRemote].Ignored then
2379
logs[currentSelectedRemote].Ignored = false
2380
ignoreButtonFrame:SetColor(RenderColorOption.Text, red, 1)
2381
ignoreButton.Label = "Ignore"
2382
else
2383
logs[currentSelectedRemote].Ignored = true
2384
ignoreButtonFrame:SetColor(RenderColorOption.Text, green, 1)
2385
ignoreButton.Label = "Unignore"
2386
end
2387
funcList[currentSelectedRemote].UpdateIgnores()
2388
end
2389
end)
2390
--[[buttonBar = buttonBar:SameLine()
2391
buttonBar:SetColor(RenderColorOption.Button, colorRGB(25, 25, 28), 1)
2392
buttonBar:SetColor(RenderColorOption.ButtonHovered, colorRGB(55, 55, 58), 1)
2393
buttonBar:SetColor(RenderColorOption.ButtonActive, colorRGB(75, 75, 78), 1)]]
2394
2395
local blockButtonFrame = buttonBar:Dummy()
2396
blockButtonFrame:SetColor(RenderColorOption.Text, red, 1)
2397
local blockButton = blockButtonFrame:Button()
2398
blockButton.Label = "Block"
2399
blockButton.OnUpdated:Connect(function()
2400
local logs = (currentSelectedType == "Call") and callLogs or otherLogs
2401
local funcList = (currentSelectedType == "Call") and callFuncs or otherFuncs
2402
2403
if currentSelectedRemote then
2404
if logs[currentSelectedRemote].Blocked then
2405
logs[currentSelectedRemote].Blocked = false
2406
blockButtonFrame:SetColor(RenderColorOption.Text, red, 1)
2407
blockButton.Label = "Block"
2408
else
2409
logs[currentSelectedRemote].Blocked = true
2410
blockButtonFrame:SetColor(RenderColorOption.Text, green, 1)
2411
blockButton.Label = "Unblock"
2412
end
2413
funcList[currentSelectedRemote].UpdateBlocks()
2414
end
2415
end)
2416
local clearLogsButton = buttonBar:Button()
2417
clearLogsButton.Label = "Clear Logs"
2418
clearLogsButton.OnUpdated:Connect(function()
2419
local logs = (currentSelectedType == "Call") and callLogs or otherLogs
2420
local lines = (currentSelectedType == "Call") and callLines or otherLines
2421
if currentSelectedRemote then
2422
do -- updates front menu
2423
tableClear(logs[currentSelectedRemote].Calls)
2424
lines[currentSelectedRemote][3].Label = "0"
2425
if not logs[currentSelectedRemote].Ignored then
2426
lines[currentSelectedRemote][2].Visible = false
2427
lines[currentSelectedRemote][4].Visible = false
2428
end
2429
end
2430
2431
do -- updates remote menu
2432
for _,v in argLines do
2433
v[2]:Clear()
2434
end
2435
tableClear(argLines)
2436
remoteViewerMainWindow:Clear()
2437
addSpacer(remoteViewerMainWindow, 8)
2438
end
2439
end
2440
end)
2441
2442
local copyPathButton = buttonBar:Button()
2443
copyPathButton.Label = "Copy Path"
2444
copyPathButton.OnUpdated:Connect(function()
2445
if currentSelectedRemote then
2446
local str = getInstancePath(currentSelectedRemoteInstance)
2447
if type(str) == "string" then
2448
outputData(str, 1, "", "Copied Path")
2449
else
2450
pushError("Failed to Copy Path")
2451
end
2452
end
2453
end)
2454
2455
remotePageObjects.IgnoreButton = ignoreButton
2456
remotePageObjects.IgnoreButtonFrame = ignoreButtonFrame
2457
remotePageObjects.BlockButton = blockButton
2458
remotePageObjects.BlockButtonFrame = blockButtonFrame
2459
end
2460
2461
local remoteArgFrame = remotePage:SameLine()
2462
2463
do -- arg frame code
2464
remoteArgFrame:SetColor(RenderColorOption.ChildBg, colorOptions.TitleBg[1], 1)
2465
remoteArgFrame:SetStyle(RenderStyleOption.ChildRounding, 5)
2466
remoteViewerMainWindow = remoteArgFrame:Child()
2467
remoteViewerMainWindow:SetStyle(RenderStyleOption.ItemSpacing, Vector2.new(4, 0))
2468
remoteViewerMainWindow:SetStyle(RenderStyleOption.ItemSpacing, Vector2.new(0, 0))
2469
end
2470
2471
local function createCSButton(window: RenderWindow, call, spyFunc)
2472
local frame = window:Dummy()
2473
if spyFunc.HasNoCaller or call.FromSynapse then
2474
frame:SetColor(RenderColorOption.Text, grey, 1)
2475
frame:SetColor(RenderColorOption.HeaderHovered, black, 0)
2476
frame:SetColor(RenderColorOption.HeaderActive, black, 0)
2477
end
2478
local button = frame:Selectable()
2479
button.Label = "Get Calling Script"
2480
if not (spyFunc.HasNoCaller or call.FromSynapse) then
2481
button.OnUpdated:Connect(function()
2482
local scr = Settings.GetCallingScriptV2 and call.ScriptV2 or call.Script
2483
local str = scr and getInstancePath(scr) -- not sure if getcallingscript can return a ModuleScript, I assume it can't, but adding this just in case
2484
if type(str) == "string" then
2485
outputData(str, 1, "", "Copied Calling Script")
2486
else
2487
pushError("Failed to get Calling Script")
2488
end
2489
end)
2490
end
2491
end
2492
2493
local function createCSDecompileButton(window: RenderWindow, call, spyFunc)
2494
local frame = window:Dummy()
2495
if spyFunc.HasNoCaller or call.FromSynapse then
2496
frame:SetColor(RenderColorOption.Text, grey, 1)
2497
frame:SetColor(RenderColorOption.HeaderHovered, black, 0)
2498
frame:SetColor(RenderColorOption.HeaderActive, black, 0)
2499
end
2500
local button = frame:Selectable()
2501
button.Label = "Decompile Calling Script"
2502
if not (spyFunc.HasNoCaller or call.FromSynapse) then
2503
button.OnUpdated:Connect(function()
2504
local suc, res = pcall(function()
2505
local scr = Settings.GetCallingScriptV2 and call.ScriptV2 or call.Script
2506
local str = decompile(scr)
2507
local scriptName = scr and getInstancePath(scr)
2508
if type(str) == "string" then
2509
outputData(str, Settings.DecompilerOutput, scriptName, "Decompiled Calling Script")
2510
else
2511
pushError("Failed to Decompile Calling Script2")
2512
end
2513
end)
2514
if not suc then
2515
pushError("Failed to Decompile Calling Script", res)
2516
end
2517
end)
2518
end
2519
end
2520
2521
local function repeatCall(call, remote: Instance, remoteId: string, spyFunc, repeatCount: number)
2522
if spyFunc.Type == "Call" then
2523
local func = spyFunc.Function
2524
2525
local success, result = pcall(function()
2526
if spyFunc.ReturnsValue then
2527
for _ = 1,repeatCount do
2528
originalCallerCache[remoteId] = {nil, true}
2529
spawnFunc(func, remote, unpack(call.Args, 1, call.NonNilArgCount + call.NilCount))
2530
end
2531
else
2532
for _ = 1,repeatCount do
2533
spawnFunc(func, remote, unpack(call.Args, 1, call.NonNilArgCount + call.NilCount)) -- shouldn't be task.spawned but needs to be because of oth.hook being weird
2534
end
2535
end
2536
end)
2537
if not success then
2538
pushError("Failed to Repeat Call", result)
2539
end
2540
elseif spyFunc.Type == "Callback" then
2541
local success, result = pcall(function()
2542
for _ = 1,repeatCount do
2543
spawnFunc(call.CallbackLog.CurrentFunction, unpack(call.Args, 1, call.NonNilArgCount + call.NilCount)) -- always spawned to make callstack look legit
2544
end
2545
end)
2546
if not success then
2547
pushError("Failed to Repeat Callback Call", result)
2548
end
2549
elseif spyFunc.Type == "Connection" then
2550
local success, result = pcall(function()
2551
for _ = 1,repeatCount do
2552
originalCallerCache[remoteId] = {nil, true}
2553
cfiresignal(call.Signal, unpack(call.Args, 1, call.NonNilArgCount + call.NilCount))
2554
end
2555
end)
2556
if not success then
2557
pushError("Failed to Repeat Connection", result)
2558
end
2559
end
2560
end
2561
2562
local function createRepeatCallButton(window: RenderWindow, call, remote: Instance, remoteId, spyFunc, amt) -- NEEDS TO BE REDONE FOR CONS AND CALLBACKS
2563
local button = window:Selectable()
2564
button.Label = amt and ("Repeat Call x" .. tostring(amt)) or "Repeat Call"
2565
button.Visible = true
2566
2567
amt = amt or 1
2568
2569
button.OnUpdated:Connect(function() repeatCall(call, remote, remoteId, spyFunc, amt) end)
2570
end
2571
2572
local function createGenSendPCButton(window: RenderWindow, call, remote: Instance, spyFunc)
2573
local button = window:Selectable()
2574
button.Label = "Generate Calling Pseudocode"
2575
button.OnUpdated:Connect(function()
2576
local suc, ret = pcall(function()
2577
outputData(genSendPseudo(remote, call, spyFunc), Settings.PseudocodeOutput, "RS Pseudocode", "Generated Pseudocode")
2578
end)
2579
if not suc then
2580
pushError("Failed to Generate Pseudocode", ret)
2581
end
2582
end)
2583
end
2584
2585
local function createGenRecvPCButton(window: RenderWindow, call, remote: Instance, spyFunc)
2586
local frame = window:Dummy()
2587
if spyFunc.Type == "Call" then
2588
frame:SetColor(RenderColorOption.Text, grey, 1)
2589
frame:SetColor(RenderColorOption.HeaderHovered, black, 0)
2590
frame:SetColor(RenderColorOption.HeaderActive, black, 0)
2591
end
2592
local button = frame:Selectable()
2593
button.Label = "Generate Receiving Pseudocode"
2594
if spyFunc.Type ~= "Call" then
2595
button.OnUpdated:Connect(function()
2596
local suc, res = pcall(function()
2597
outputData(genRecvPseudo(remote, call, spyFunc, Settings.PseudocodeWatermark), Settings.PseudocodeOutput, "RS Pseudocode", "Generated Pseudocode")
2598
end)
2599
if not suc then
2600
pushError("Failed to Generate Pseudocode", res)
2601
end
2602
end)
2603
end
2604
end
2605
2606
local function genCallStackString(callStack)
2607
local callStackString = ""
2608
if Settings.PseudocodeWatermark then
2609
callStackString ..= watermarkString
2610
end
2611
2612
callStackString ..= "\nlocal CallStack = {"
2613
2614
for i,v in callStack do
2615
callStackString ..= strformat("\n\t[%s] = {\n\t\tScript = %s,\n\t\tLine = %s,\n\t\tType = %s\n\t},", tostring(i), v.Script and getInstancePath(v.Script) or "\"nil\"", tostring(v.LineNumber), "\"" .. v.Type.. "\"")
2616
end
2617
2618
return (sub(callStackString, 1, -2) .. "\n}")
2619
end
2620
2621
local function createGetCallStackButton(window: RenderWindow, call, spyFunc)
2622
local frame = window:Dummy()
2623
if spyFunc.Type ~= "Call" or call.FromSynapse then
2624
frame:SetColor(RenderColorOption.Text, grey, 1)
2625
frame:SetColor(RenderColorOption.HeaderHovered, black, 0)
2626
frame:SetColor(RenderColorOption.HeaderActive, black, 0)
2627
end
2628
local button = frame:Selectable()
2629
button.Label = "Get Call Stack"
2630
if spyFunc.Type == "Call" and not call.FromSynapse then
2631
button.OnUpdated:Connect(function()
2632
local suc, res = pcall(function()
2633
outputData(genCallStackString(call.CallStack), Settings.CallStackOutput, "Call Stack", "Output Call Stack")
2634
end)
2635
if not suc then
2636
pushError("Failed to Output Call Stack", res)
2637
end
2638
end)
2639
end
2640
end
2641
2642
local function createGetConnectionScriptsButton(window: RenderWindow, call, spyFunc)
2643
local frame = window:Dummy()
2644
if spyFunc.Type ~= "Connection" then
2645
frame:SetColor(RenderColorOption.Text, grey, 1)
2646
frame:SetColor(RenderColorOption.HeaderHovered, black, 0)
2647
frame:SetColor(RenderColorOption.HeaderActive, black, 0)
2648
end
2649
local button = frame:Selectable()
2650
button.Label = "Get Connections' Creator-Scripts"
2651
if spyFunc.Type == "Connection" then
2652
button.OnUpdated:Connect(function()
2653
local suc, res = pcall(function()
2654
local str = Settings.PseudocodeWatermark and watermarkString or ""
2655
str ..= "\nlocal Connections = {"
2656
local count = 0
2657
for i,v in call.Scripts do
2658
count += 1
2659
str ..= strformat("\n\t[%s] = {\n\t\tInstance = %s,\n\t\tAmount = %s\n\t},", tostring(count), typeof(i) == "Instance" and getInstancePath(i) or "nil, -- Created by "..tostring(i), tostring(v))
2660
end
2661
str = sub(str, 1, -2)
2662
str ..= "\n}"
2663
outputData(str, Settings.ConnectionsOutput, "Connection Scripts", "Output Connections' Creator-Scripts List")
2664
end)
2665
if not suc then
2666
pushError("Failed to Get Connection Scripts", res)
2667
end
2668
end)
2669
end
2670
end
2671
2672
local function createGetRetValButton(window: RenderWindow, call, spyFunc)
2673
local frame = window:Dummy()
2674
if not spyFunc.ReturnsValue then
2675
frame:SetColor(RenderColorOption.Text, grey, 1)
2676
frame:SetColor(RenderColorOption.HeaderHovered, black, 0)
2677
frame:SetColor(RenderColorOption.HeaderActive, black, 0)
2678
end
2679
local button = frame:Selectable()
2680
button.Label = "Get Return Value"
2681
if spyFunc.ReturnsValue then
2682
button.OnUpdated:Connect(function()
2683
local suc, res = pcall(function()
2684
local ret = call.ReturnValue
2685
if ret.Args then
2686
outputData(genReturnValuePseudo(ret, spyFunc), Settings.PseudocodeOutput, "RS Return Value", "Generated Return Value")
2687
else
2688
pushError("Failed to Get Return Value (may have never returned)")
2689
end
2690
end)
2691
if not suc then
2692
pushError("Failed to Get Return Value", res)
2693
end
2694
end)
2695
end
2696
end
2697
2698
local function createCBButton(window: RenderWindow, call, spyFunc)
2699
local frame = window:Dummy()
2700
if spyFunc.Type ~= "Callback" or call.FromSynapse then
2701
frame:SetColor(RenderColorOption.Text, grey, 1)
2702
frame:SetColor(RenderColorOption.HeaderHovered, black, 0)
2703
frame:SetColor(RenderColorOption.HeaderActive, black, 0)
2704
end
2705
local button = frame:Selectable()
2706
button.Label = "Get Callback Creator-Script"
2707
if spyFunc.Type == "Callback" and not call.FromSynapse then
2708
button.OnUpdated:Connect(function()
2709
local str = call.CallbackScript and getInstancePath(call.CallbackScript) -- not sure if getcallingscript can return a ModuleScript, I assume it can't, but adding this just in case
2710
if type(str) == "string" then
2711
outputData(str, 1, "", "Set Callback Script")
2712
else
2713
pushError("Failed to get Callback Script")
2714
end
2715
end)
2716
end
2717
end
2718
2719
local function createCBDecompileButton(window: RenderWindow, call, spyFunc)
2720
local frame = window:Dummy()
2721
if spyFunc.Type ~= "Callback" or call.FromSynapse then
2722
frame:SetColor(RenderColorOption.Text, grey, 1)
2723
frame:SetColor(RenderColorOption.HeaderHovered, black, 0)
2724
frame:SetColor(RenderColorOption.HeaderActive, black, 0)
2725
end
2726
local button = frame:Selectable()
2727
button.Label = "Decompile Callback Creator-Script"
2728
if spyFunc.Type == "Callback" and not call.FromSynapse then
2729
button.OnUpdated:Connect(function()
2730
local suc, res = pcall(function()
2731
local str = decompile(call.CallbackScript)
2732
local scriptName = call.CallbackScript and getInstancePath(call.CallbackScript)
2733
if type(str) == "string" then
2734
outputData(str, Settings.DecompileScriptsToExternal, scriptName, "Set Callback Script")
2735
else
2736
pushError("Failed to Decompile Callback Script2")
2737
end
2738
end)
2739
if not suc then
2740
pushError("Failed to Decompile Callback Script", res)
2741
end
2742
end)
2743
end
2744
end
2745
2746
local function makeRemoteViewerLog(call: Instance, remote: Instance, remoteId: string)
2747
local totalArgCount = call.NonNilArgCount + call.NilCount
2748
local spyFunc = spyFunctions[call.TypeIndex]
2749
local tempMainDummy = remoteViewerMainWindow:Dummy()
2750
local tempMain = tempMainDummy:SameLine()
2751
tempMain:SetColor(RenderColorOption.ChildBg, colorRGB(25, 25, 28), 1)
2752
2753
local childWindow = tempMain:Indent(8):Child()
2754
2755
if totalArgCount < 2 then
2756
childWindow.Size = Vector2.new(width-46, 24 + 16) -- 2 lines (top line = 24) + 2x (8px) spacers | -46 because 16 padding on each side, plus 14 wide scrollbar
2757
elseif totalArgCount <= 10 then
2758
childWindow.Size = Vector2.new(width-46, (totalArgCount * 28) - 4 + 16) -- 24px per line, 4px spacer, 16px header and footer | -46 because 16 padding on each side, plus 14 wide scrollbar
2759
else -- 28 pixels per line (24 for arg, 4 for spacer), but -4 because no spacer at end, then +24 because button line, and +24 for top, bottom, and middle spacer
2760
childWindow.Size = Vector2.new(width-46, (10 * 28) - 4 + 16)
2761
end
2762
2763
local pop = mainWindowWeakReference[1]:Popup()
2764
2765
createGetRetValButton(pop, call, spyFunc)
2766
if Settings.CallbackButtons then
2767
createGetConnectionScriptsButton(pop, call, spyFunc)
2768
createCBButton(pop, call, spyFunc)
2769
createCBDecompileButton(pop, call, spyFunc)
2770
end
2771
2772
pop:Separator()
2773
createCSButton(pop, call, spyFunc)
2774
createCSDecompileButton(pop, call, spyFunc)
2775
if Settings.StoreCallStack then
2776
createGetCallStackButton(pop, call, spyFunc)
2777
end
2778
if Settings.CallbackButtons then
2779
createGenRecvPCButton(pop, call, remote, spyFunc)
2780
end
2781
createGenSendPCButton(pop, call, remote, spyFunc)
2782
if Settings.MoreRepeatCallOptions then
2783
pop:Separator()
2784
for _,v in repeatCallSteps do
2785
createRepeatCallButton(pop, call, remote, remoteId, spyFunc, v)
2786
end
2787
else
2788
createRepeatCallButton(pop, call, remote, remoteId, spyFunc)
2789
end
2790
2791
local textFrame = childWindow:Dummy()
2792
2793
addSpacer(textFrame, 6)
2794
2795
local indentFrame = textFrame:Indent(4):SameLine()
2796
2797
local temp = indentFrame:Indent(-2):WithFont(CallerIconFont) -- center it
2798
temp:SetStyle(RenderStyleOption.FramePadding, Vector2.new(2, 0))
2799
temp:SetColor(RenderColorOption.Button, black, 0)
2800
temp:SetColor(RenderColorOption.ButtonActive, black, 0)
2801
temp:SetColor(RenderColorOption.ButtonHovered, black, 0)
2802
local btn = temp:Button()
2803
2804
if call.FromSynapse then
2805
btn.Label = "\xee\x80\x89"
2806
else
2807
btn.Label = "\xee\x80\x88"
2808
end
2809
btn.Size = Vector2.new(24, 30)
2810
2811
local firstArgFrame = indentFrame:Indent(28):Child() -- 1 extra cause -1 later, and using child so I can make the icon line up
2812
firstArgFrame.Size = Vector2.new(width-24-38-23, 30)
2813
addSpacer(firstArgFrame, 2)
2814
firstArgFrame = firstArgFrame:SameLine()
2815
2816
if totalArgCount == 0 or totalArgCount == 1 then
2817
local argFrame = firstArgFrame:SameLine()
2818
2819
local topLine = argFrame:SameLine():Indent(8)
2820
topLine:SetColor(RenderColorOption.Button, black, 0)
2821
topLine:SetColor(RenderColorOption.ButtonActive, white, 0)
2822
topLine:SetColor(RenderColorOption.ButtonHovered, white, 0)
2823
local mainButton = topLine:Button()
2824
mainButton.OnUpdated:Connect(function()
2825
pop:Show()
2826
end)
2827
2828
local temp2 = argFrame:SameLine()
2829
temp2:SetColor(RenderColorOption.ButtonActive, colorOptions.FrameBg[1], 1)
2830
temp2:SetColor(RenderColorOption.ButtonHovered, colorOptions.FrameBg[1], 1)
2831
temp2:SetColor(RenderColorOption.Button, colorOptions.FrameBg[1], 1)
2832
temp2:SetStyle(RenderStyleOption.ButtonTextAlign, Vector2.new(0, 0.5))
2833
2834
local lineContents = temp2:Indent(-1):Indent(1):Button()
2835
lineContents.Size = Vector2.new(width-24-38-23, 24) -- 24 = left padding, 38 = right padding, and no scrollbar
2836
if totalArgCount == 0 then
2837
argFrame:SetColor(RenderColorOption.Text, colorRGB(156, 0, 0), 1)
2838
lineContents.Label = spaces2 .. "nil"
2839
elseif call.NonNilArgCount == 1 then
2840
local text, color = getArgString(call.Args[1], lineContents.Size.X)
2841
local str = resizeText(spaces2 .. text, lineContents.Size.X, "... ", DefaultTextFont)
2842
lineContents.Label = str
2843
argFrame:SetColor(RenderColorOption.Text, color, 1)
2844
else
2845
lineContents.Label = spaces2 .. "HIDDEN NIL"
2846
argFrame:SetColor(RenderColorOption.Text, colorHSV(258/360, 0.8, 1), 1)
2847
end
2848
mainButton.Size = Vector2.new(lineContents.Size.X, lineContents.Size.Y+4)
2849
2850
local temp = argFrame:SameLine()
2851
argFrame:SetColor(RenderColorOption.ButtonActive, black, 0)
2852
argFrame:SetColor(RenderColorOption.ButtonHovered, black, 0)
2853
argFrame:SetColor(RenderColorOption.Button, black, 0)
2854
temp:SetStyle(RenderStyleOption.ButtonTextAlign, Vector2.new(1, 0.5))
2855
temp:SetColor(RenderColorOption.Text, colorHSV(179/360, 0.8, 1), 1)
2856
2857
local lineNum = temp:Indent(-7):Button()
2858
lineNum.Label = "1"
2859
lineNum.Size = Vector2.new(32, 24)
2860
else
2861
local normalSize = Vector2.new(width-24-38, 24)-- 24 = left padding + indent, 38 = right padding (no scrollbar)
2862
local normalFirstSize = Vector2.new(width-24-38-23, 24)
2863
local scrollSize = Vector2.new(width-24-38-14, 24) -- 14 = scrollbar width, plus read above
2864
local scrollFirstSize = Vector2.new(width-24-38-14-23, 24)
2865
for i = 1, call.NonNilArgCount do
2866
if i > Settings.ArgLimit then break end
2867
2868
local x = call.Args[i]
2869
2870
local firstLine = (i == 1)
2871
local argFrame = ((firstLine and firstArgFrame) or childWindow):SameLine()
2872
2873
local topLine = firstLine and argFrame:SameLine():Indent(-1):Indent(1) or argFrame:SameLine():Indent(8)
2874
topLine:SetColor(RenderColorOption.Button, black, 0)
2875
topLine:SetColor(RenderColorOption.ButtonActive, white, 0)
2876
topLine:SetColor(RenderColorOption.ButtonHovered, white, 0)
2877
local mainButton = topLine:Button()
2878
mainButton.OnUpdated:Connect(function()
2879
pop:Show()
2880
end)
2881
2882
local temp2 = argFrame:SameLine():Indent(-1):Indent(1)
2883
temp2:SetColor(RenderColorOption.ButtonActive, colorOptions.FrameBg[1], 1)
2884
temp2:SetColor(RenderColorOption.ButtonHovered, colorOptions.FrameBg[1], 1)
2885
temp2:SetColor(RenderColorOption.Button, colorOptions.FrameBg[1], 1)
2886
temp2:SetStyle(RenderStyleOption.ButtonTextAlign, Vector2.new(0, 0.5))
2887
2888
local lineContents = firstLine and temp2:Indent(-1):Indent(1):Button() or temp2:Indent(8):Button()
2889
if totalArgCount < 10 then
2890
lineContents.Size = firstLine and normalFirstSize or normalSize
2891
else
2892
lineContents.Size = firstLine and scrollFirstSize or scrollSize
2893
end
2894
if i ~= Settings.ArgLimit then
2895
local text, color
2896
text, color = getArgString(x, lineContents.Size.X)
2897
lineContents.Label = resizeText(spaces2 .. text, lineContents.Size.X, "... ", DefaultTextFont)
2898
argFrame:SetColor(RenderColorOption.Text, color, 1)
2899
else
2900
lineContents.Label = spaces2 .. "ARG LIMIT REACHED"
2901
argFrame:SetColor(RenderColorOption.Text, Color3.new(1, 0, 0), 1)
2902
end
2903
mainButton.Size = Vector2.new(lineContents.Size.X, lineContents.Size.Y+4) -- +4 to add spacer
2904
2905
local temp = argFrame:SameLine()
2906
argFrame:SetColor(RenderColorOption.ButtonActive, black, 0)
2907
argFrame:SetColor(RenderColorOption.ButtonHovered, black, 0)
2908
argFrame:SetColor(RenderColorOption.Button, black, 0)
2909
temp:SetStyle(RenderStyleOption.ButtonTextAlign, Vector2.new(1, 0.5))
2910
temp:SetColor(RenderColorOption.Text, colorHSV(179/360, 0.8, 1), 1)
2911
2912
local lineNum = firstLine and temp:Indent(-7):Button() or temp:Indent(1):Button()
2913
lineNum.Label = tostring(i)
2914
lineNum.Size = Vector2.new(32, 24)
2915
--addSpacer(childWindow, 4)
2916
end
2917
local argAmt = call.NonNilArgCount
2918
for i = 1, call.NilCount do
2919
if (i + argAmt) > Settings.ArgLimit then break end
2920
2921
local firstLine = (i == 1 and argAmt == 0)
2922
local argFrame = (firstLine and firstArgFrame:SameLine()) or childWindow:SameLine()
2923
2924
local topLine = argFrame:Dummy():Indent(8)
2925
topLine:SetColor(RenderColorOption.Button, black, 0)
2926
topLine:SetColor(RenderColorOption.ButtonActive, white, 0)
2927
topLine:SetColor(RenderColorOption.ButtonHovered, white, 0)
2928
local mainButton = topLine:Button()
2929
mainButton.OnUpdated:Connect(function()
2930
pop:Show()
2931
end)
2932
2933
local temp2 = argFrame:SameLine()
2934
temp2:SetColor(RenderColorOption.ButtonActive, colorOptions.FrameBg[1], 1)
2935
temp2:SetColor(RenderColorOption.ButtonHovered, colorOptions.FrameBg[1], 1)
2936
temp2:SetColor(RenderColorOption.Button, colorOptions.FrameBg[1], 1)
2937
temp2:SetStyle(RenderStyleOption.ButtonTextAlign, Vector2.new(0, 0.5))
2938
2939
local lineContents = firstLine and temp2:Indent(-1):Indent(1):Button() or temp2:Indent(8):Button()
2940
if (i + argAmt) == Settings.ArgLimit then
2941
lineContents.Label = spaces2 .. "ARG LIMIT REACHED"
2942
argFrame:SetColor(RenderColorOption.Text, Color3.new(1, 0, 0), 1)
2943
else
2944
lineContents.Label = spaces2 .. "HIDDEN NIL"
2945
argFrame:SetColor(RenderColorOption.Text, colorHSV(258/360, 0.8, 1), 1)
2946
end
2947
if totalArgCount < 10 then
2948
lineContents.Size = firstLine and normalFirstSize or normalSize
2949
else
2950
lineContents.Size = firstLine and scrollFirstSize or scrollSize
2951
end
2952
if firstLine then
2953
mainButton.Size = Vector2.new(lineContents.Size.X, lineContents.Size.Y) -- +2 cause type icon adds 2 for some reason
2954
else
2955
mainButton.Size = Vector2.new(lineContents.Size.X, lineContents.Size.Y+4) -- +4 to add spacer
2956
end
2957
2958
local temp = argFrame:SameLine()
2959
argFrame:SetColor(RenderColorOption.ButtonActive, black, 0)
2960
argFrame:SetColor(RenderColorOption.ButtonHovered, black, 0)
2961
argFrame:SetColor(RenderColorOption.Button, black, 0)
2962
temp:SetStyle(RenderStyleOption.ButtonTextAlign, Vector2.new(1, 0.5))
2963
temp:SetColor(RenderColorOption.Text, colorHSV(179/360, 0.8, 1), 1)
2964
2965
local lineNum = firstLine and temp:Indent(-7):Button() or temp:Indent(1):Button()
2966
lineNum.Label = tostring(i + argAmt)
2967
lineNum.Size = Vector2.new(32, 24)
2968
--addSpacer(childWindow, 4)
2969
end
2970
addSpacer(childWindow, 4) -- account for the space at the end of the arg list so when you scroll all the way down the padding looks good
2971
end
2972
2973
addSpacer(tempMainDummy, 4)
2974
tableInsert(argLines, { tempMainDummy, pop })
2975
end
2976
2977
local function loadRemote(remote: Instance, remoteId: string, data)
2978
local funcInfo = spyFunctions[data.TypeIndex]
2979
local logs = funcInfo.Type == "Call" and callLogs or otherLogs
2980
frontPage.Visible = false
2981
remotePage.Visible = true
2982
currentSelectedRemote = remoteId
2983
currentSelectedRemoteInstance = remote
2984
currentSelectedType = funcInfo.Type
2985
remotePageObjects.Name.Label = remote and resizeText(purifyString(remoteNameCache[remoteId] or remote.Name, false, remotePageObjects.Name.Size.X), remotePageObjects.Name.Size.X, "... ", DefaultTextFont) or "NIL REMOTE"
2986
remotePageObjects.Icon.Label = funcInfo.Icon .. " "
2987
remotePageObjects.IgnoreButton.Label = (logs[remoteId].Ignored and "Unignore") or "Ignore"
2988
remotePageObjects.IgnoreButtonFrame:SetColor(RenderColorOption.Text, (logs[remoteId].Ignored and green) or red, 1)
2989
remotePageObjects.BlockButton.Label = (logs[remoteId].Blocked and "Unblock") or "Block"
2990
remotePageObjects.BlockButtonFrame:SetColor(RenderColorOption.Text, (logs[remoteId].Blocked and green) or red, 1)
2991
2992
addSpacer(remoteViewerMainWindow, 8)
2993
2994
for _,v in logs[remoteId].Calls do
2995
makeRemoteViewerLog(v, remote, remoteId)
2996
end
2997
end
2998
2999
-- Below this is rendering Front Page
3000
local topBar = frontPage:SameLine()
3001
local frameWidth = width-150
3002
local searchBarFrame = topBar:Indent(-0.35*frameWidth):Child()
3003
searchBarFrame.Size = Vector2.new(frameWidth, 24)
3004
searchBarFrame:SetColor(RenderColorOption.ChildBg, black, 0)
3005
searchBar = searchBarFrame:Indent(0.35*frameWidth):TextBox() -- localized earlier
3006
searchBar.OnUpdated:Connect(filterLines)
3007
3008
local searchButton = topBar:Button()
3009
searchButton.Label = "Search"
3010
searchButton.OnUpdated:Connect(function()
3011
filterLines(searchBar.Value) -- redundant because i did it above but /shrug
3012
end)
3013
3014
local childWindow
3015
3016
local clearAllLogsButton = topBar:Button()
3017
clearAllLogsButton.Label = "Clear All Logs"
3018
clearAllLogsButton.OnUpdated:Connect(function()
3019
tableClear(callLines)
3020
tableClear(otherLines)
3021
for _,v in callLogs do
3022
tableClear(v.Calls)
3023
end
3024
3025
for _,v in otherLogs do
3026
tableClear(v.Calls)
3027
end
3028
childWindow:Clear()
3029
addSpacer(childWindow, 8)
3030
end)
3031
3032
local topRightBar = topBar:Indent(width-96):SameLine() -- -8 for right padding, -8 for previous left indent, -28 per button +4 for left side padding
3033
topRightBar:SetColor(RenderColorOption.Button, black, 0)
3034
topRightBar:SetStyle(RenderStyleOption.ButtonTextAlign, Vector2.new(0.5, 0.5))
3035
topRightBar:SetStyle(RenderStyleOption.ItemSpacing, Vector2.new(0, 0))
3036
3037
3038
local settingsButton = topRightBar:Button()
3039
settingsButton.Label = "\xef\x80\x93"
3040
settingsButton.Size = Vector2.new(24, 24)
3041
settingsButton.OnUpdated:Connect(function()
3042
settingsWindowWeakReference[1].Visible = not settingsWindowWeakReference[1].Visible
3043
end)
3044
3045
pauseSpyButton = topRightBar:Indent(28):Button()
3046
pauseSpyButton.Label = Settings.Paused and "\xef\x80\x9d" or "\xef\x8a\x8c"
3047
pauseSpyButton.Size = Vector2.new(24, 24)
3048
pauseSpyButton.OnUpdated:Connect(function()
3049
if Settings.Paused then
3050
Settings.Paused = false
3051
pauseSpyButton.Label = "\xef\x8a\x8c"
3052
pauseSpyButton2.Label = "\xef\x8a\x8c"
3053
else
3054
Settings.Paused = true
3055
pauseSpyButton.Label = "\xef\x80\x9d"
3056
pauseSpyButton2.Label = "\xef\x80\x9d"
3057
end
3058
end)
3059
3060
local exitButton = topRightBar:Indent(56):Button()
3061
exitButton.Label = "\xef\x80\x91"
3062
exitButton.Size = Vector2.new(24, 24)
3063
exitButton.OnUpdated:Connect(function()
3064
if messagebox("Are you sure you want to Close/Disconnect the RemoteSpy? You can reexecute later.", "Warning", 4) == 6 then
3065
cleanUpSpy()
3066
end
3067
end)
3068
3069
addSpacer(frontPage, 4)
3070
3071
local sameLine = frontPage:SameLine()
3072
3073
local splitAmt = (floor(#spyFunctions/2)+1)
3074
for i,v in spyFunctions do
3075
3076
if i == splitAmt then
3077
sameLine = frontPage:SameLine()
3078
sameLine.Visible = Settings.CallbackButtons
3079
callbackButtonline = sameLine
3080
end
3081
3082
local tempLine = v.Indent == 0 and sameLine:Dummy() or sameLine:Indent(v.Indent):Dummy()
3083
3084
local btn = tempLine:WithFont(RemoteIconFont):CheckBox()
3085
btn.Label = v.Icon
3086
btn.Value = v.Enabled
3087
v.Button = btn
3088
btn.OnUpdated:Connect(function(enabled)
3089
v.Enabled = enabled
3090
Settings[v.Name] = enabled
3091
updateLines(v.Name, enabled)
3092
3093
saveConfig()
3094
end)
3095
3096
sameLine:Label(v.Name)
3097
end
3098
3099
frontPage:SetColor(RenderColorOption.ChildBg, colorOptions.TitleBg[1], 1)
3100
frontPage:SetStyle(RenderStyleOption.ChildRounding, 5)
3101
3102
childWindow = frontPage:Child()
3103
childWindow:SetStyle(RenderStyleOption.ItemSpacing, Vector2.new(4, 0))
3104
childWindow:SetStyle(RenderStyleOption.FrameRounding, 3)
3105
addSpacer(childWindow, 8)
3106
3107
local function makeCopyPathButton(sameLine: RenderSameLine, remote: Instance)
3108
local copyPathButton = sameLine:Button()
3109
copyPathButton.Label = "Copy Path"
3110
3111
copyPathButton.OnUpdated:Connect(function()
3112
local str = getInstancePath(remote)
3113
if type(str) == "string" then
3114
outputData(str, 1, "", "Copied Path")
3115
else
3116
pushError("Failed to Copy Path")
3117
end
3118
end)
3119
end
3120
3121
local function makeClearLogsButton(sameLine: RenderSameLine, remoteId: string, method)
3122
local clearLogsButton = sameLine:Button()
3123
clearLogsButton.Label = "Clear Logs"
3124
3125
local lines = (method == "Call") and callLines or otherLines
3126
local logs = (method == "Call") and callLogs or otherLogs
3127
3128
clearLogsButton.OnUpdated:Connect(function()
3129
tableClear(logs[remoteId].Calls)
3130
lines[remoteId][3].Label = "0"
3131
if not logs[remoteId].Ignored then
3132
lines[remoteId][2].Visible = false
3133
lines[remoteId][4].Visible = false
3134
end
3135
end)
3136
end
3137
3138
local function makeIgnoreButton(sameLine: RenderSameLine, remoteId: string, method)
3139
local spoofLine = sameLine:SameLine()
3140
spoofLine:SetColor(RenderColorOption.Text, red, 1)
3141
local ignoreButton = spoofLine:Button()
3142
ignoreButton.Label = "Ignore"
3143
3144
local logs = (method == "Call") and callLogs or otherLogs
3145
local funcList = (method == "Call") and callFuncs or otherFuncs
3146
3147
funcList[remoteId].UpdateIgnores = function()
3148
if logs[remoteId].Ignored then
3149
ignoreButton.Label = "Unignore"
3150
spoofLine:SetColor(RenderColorOption.Text, green, 1)
3151
else
3152
ignoreButton.Label = "Ignore"
3153
spoofLine:SetColor(RenderColorOption.Text, red, 1)
3154
end
3155
end
3156
3157
ignoreButton.OnUpdated:Connect(function()
3158
if logs[remoteId].Ignored then
3159
logs[remoteId].Ignored = false
3160
ignoreButton.Label = "Ignore"
3161
spoofLine:SetColor(RenderColorOption.Text, red, 1)
3162
else
3163
logs[remoteId].Ignored = true
3164
ignoreButton.Label = "Unignore"
3165
spoofLine:SetColor(RenderColorOption.Text, green, 1)
3166
end
3167
end)
3168
end
3169
3170
local function makeBlockButton(sameLine: RenderSameLine, remoteId: string, method)
3171
local spoofLine = sameLine:SameLine()
3172
spoofLine:SetColor(RenderColorOption.Text, red, 1)
3173
local blockButton = spoofLine:Button()
3174
blockButton.Label = "Block"
3175
3176
local logs = (method == "Call") and callLogs or otherLogs
3177
local funcList = (method == "Call") and callFuncs or otherFuncs
3178
3179
funcList[remoteId].UpdateBlocks = function()
3180
if logs[remoteId].Blocked then
3181
spoofLine:SetColor(RenderColorOption.Text, green, 1)
3182
blockButton.Label = "Unblock"
3183
else
3184
spoofLine:SetColor(RenderColorOption.Text, red, 1)
3185
blockButton.Label = "Block"
3186
end
3187
end
3188
3189
blockButton.OnUpdated:Connect(function()
3190
if logs[remoteId].Blocked then
3191
logs[remoteId].Blocked = false
3192
spoofLine:SetColor(RenderColorOption.Text, red, 1)
3193
blockButton.Label = "Block"
3194
else
3195
logs[remoteId].Blocked = true
3196
spoofLine:SetColor(RenderColorOption.Text, green, 1)
3197
blockButton.Label = "Unblock"
3198
end
3199
end)
3200
end
3201
3202
local function renderNewLog(remote: Instance, remoteId: string, data)
3203
local spyFunc = spyFunctions[data.TypeIndex]
3204
local method = spyFunc.Type
3205
local lines, log, funcList
3206
if method == "Call" then
3207
lines = callLines
3208
log = callLogs[remoteId]
3209
funcList = callFuncs
3210
else
3211
lines = otherLines
3212
log = otherLogs[remoteId]
3213
funcList = otherFuncs
3214
end
3215
funcList[remoteId] = {}
3216
3217
local temp = childWindow:Dummy():Indent(8)
3218
temp:SetStyle(RenderStyleOption.ItemSpacing, Vector2.new(4, 0))
3219
temp:SetColor(RenderColorOption.ChildBg, colorRGB(25, 25, 28), 1)
3220
temp:SetStyle(RenderStyleOption.SelectableTextAlign, Vector2.new(0, 0.5))
3221
3222
local line = {}
3223
line[1] = spyFunc.Name
3224
line[2] = temp:Child()
3225
sameButtonLine = line[2]
3226
sameButtonLine.Visible = spyFunc.Enabled
3227
sameButtonLine.Size = Vector2.new(width-32-14, 32) -- minus 32 because 4x 8px spacers, minus 14 because scrollbar
3228
addSpacer(sameButtonLine, 4)
3229
sameButtonLine = sameButtonLine:SameLine()
3230
3231
local remoteButton = sameButtonLine:Indent(6):Selectable()
3232
remoteButton.Size = Vector2.new(width-327-4-14, 24)
3233
remoteButton.Label = spaces .. (remote and resizeText(purifyString(remoteNameCache[remoteId] or remote.Name, false, remoteButton.Size.X), remoteButton.Size.X, "... ", DefaultTextFont) or "NIL REMOTE")
3234
remoteButton.OnUpdated:Connect(function()
3235
loadRemote(remote, remoteId, data)
3236
end)
3237
3238
addSpacer(sameButtonLine, 3)
3239
3240
local cloneLine = sameButtonLine:WithFont(RemoteIconFont):Indent(6)
3241
3242
cloneLine:Label(spyFunc.Icon .. " ")
3243
3244
local cloneLine2 = sameButtonLine:SameLine()
3245
cloneLine2:SetColor(RenderColorOption.Text, colorHSV(179/360, 0.8, 1), 1)
3246
3247
local callAmt = #log.Calls
3248
local callStr = (callAmt < 1000 and tostring(callAmt)) or "999+"
3249
line[3] = cloneLine2:Indent(27):Label(callStr)
3250
3251
local ind = sameButtonLine:Indent(width-333)
3252
3253
makeCopyPathButton(ind, remote)
3254
makeClearLogsButton(sameButtonLine, remoteId, method)
3255
makeIgnoreButton(sameButtonLine, remoteId, method)
3256
makeBlockButton(sameButtonLine, remoteId, method)
3257
3258
line[4] = addSpacer(childWindow, 4)
3259
line[4].Visible = spyFunc.Enabled
3260
line[5] = remoteButton
3261
3262
lines[remoteId] = line
3263
filterLines(searchBar.Value)
3264
end
3265
3266
_G.ChangeRemoteSpyRemoteDisplayName = function(remote: Instance, newName: string)
3267
local remoteId = remote:GetDebugId()
3268
remoteNameCache[remoteId] = newName
3269
3270
local line = callLines[remoteId]
3271
local line2 = otherLines[remoteId]
3272
if line then
3273
line[5].Label = spaces .. resizeText(purifyString(newName, false, line[5].Size.X), line[5].Size.X, "... ", DefaultTextFont)
3274
end
3275
if line2 then
3276
line2[5].Label = spaces .. resizeText(purifyString(newName, false, line2[5].Size.X), line2[5].Size.X, "... ", DefaultTextFont)
3277
end
3278
end
3279
3280
local function sendLog(remote: Instance, remoteId: string, data)
3281
local spyFunc = spyFunctions[data.TypeIndex]
3282
local method = spyFunc.Type
3283
local check = (currentSelectedRemote == remoteId and currentSelectedType == method) and true
3284
3285
local line, log
3286
if method == "Call" then
3287
line = callLines[remoteId]
3288
log = callLogs[remoteId]
3289
else
3290
line = otherLines[remoteId]
3291
log = otherLogs[remoteId]
3292
end
3293
3294
tableInsert(log.Calls, data)
3295
3296
if Settings.CacheLimit then
3297
local callCount = (#log.Calls-Settings.MaxCallAmount)
3298
if callCount > 0 then
3299
for _ = 1,callCount do
3300
if check then
3301
argLines[1][2]:Clear()
3302
argLines[1][1]:Clear()
3303
argLines[1][1].Visible = false
3304
tableRemove(argLines, 1)
3305
end
3306
tableRemove(log.Calls, 1)
3307
end
3308
end
3309
end
3310
3311
if line then
3312
local callAmt = #log.Calls
3313
if callAmt > 0 and spyFunc.Enabled then
3314
line[2].Visible = true
3315
line[4].Visible = true
3316
end
3317
local callStr = (callAmt < 1000 and tostring(callAmt)) or "999+"
3318
line[3].Label = callStr
3319
else
3320
renderNewLog(remote, remoteId, data)
3321
end
3322
3323
if check then
3324
makeRemoteViewerLog(data, remote, remoteId)
3325
end
3326
end
3327
3328
local function processReturnValue(callType: string, refTable, ...)
3329
spawnFunc(function(...)
3330
local args = deepClone({...}, callType, -1)
3331
if args then
3332
local lastIdx = getLastIndex(args)
3333
refTable.Args = args
3334
refTable.NonNilArgCount = lastIdx
3335
refTable.NilCount = (select("#", ...) - lastIdx)
3336
else
3337
refTable.Args = false
3338
pushError("Impossible error 1 has occurred, please report to GameGuy#5920")
3339
end
3340
end, ...)
3341
3342
return ...
3343
end
3344
3345
local function createCallStack(callStack)
3346
local newCallStack = {}
3347
local callStackLength = #callStack
3348
3349
for i,v in callStack do -- last index in call stack is the remotespy hook
3350
if i ~= callStackLength then
3351
local tempScript = rawget(getfenv(v.func), "script")
3352
local funcInfo = getinfo(v.func)
3353
newCallStack[i] = {
3354
Script = typeof(tempScript) == "Instance" and cloneref(tempScript),
3355
LineNumber = funcInfo.currentline,
3356
Type = funcInfo.what
3357
}
3358
end
3359
end
3360
3361
return newCallStack
3362
end
3363
3364
local function addCall(remote: Instance, remoteId: string, returnValue, spyFunc, caller: boolean, cs: Instance, callStack, ...)
3365
if not remoteBlacklistCache[remote] and spyFunc.Object == "RemoteEvent" then
3366
if cansignalreplicate(remote.OnServerEvent) then
3367
remoteBlacklistCache[remote] = 1
3368
else
3369
remoteBlacklistCache[remote] = 2
3370
return
3371
end
3372
end
3373
3374
if not callLogs[remoteId] then
3375
callLogs[remoteId] = {
3376
Blocked = false,
3377
Ignored = false,
3378
Calls = {}
3379
}
3380
end
3381
if not callLogs[remoteId].Ignored and (Settings.LogHiddenRemotesCalls or spyFunc.Enabled) then
3382
local args, tableDepth, _, hasInstance = deepClone({...}, remote.ClassName, -1) -- 1 deeper total
3383
local argCount = select("#", ...)
3384
3385
if not args or argCount > 7995 or (tableDepth > 0 and ((argCount + tableDepth) > 298)) then
3386
return
3387
end
3388
3389
local V2Script = callStack[#callStack-1] and rawget(getfenv(callStack[#callStack-1].func), "script")
3390
if typeof(V2Script) ~= "Instance" then V2Script = nil end
3391
3392
local lastIdx = getLastIndex(args)
3393
3394
local data = {
3395
HasInstance = hasInstance or (not remote:IsAncestorOf(game)),
3396
TypeIndex = idxs[spyFunc.Name],
3397
Script = cs,
3398
Args = args, -- 2 deeper total
3399
NonNilArgCount = lastIdx,
3400
ReturnValue = returnValue,
3401
NilCount = (argCount - lastIdx),
3402
FromSynapse = caller,
3403
ScriptV2 = V2Script,
3404
CallStack = Settings.StoreCallStack and createCallStack(callStack)
3405
}
3406
sendLog(remote, remoteId, data)
3407
end
3408
end
3409
3410
local function addCallback(remote: Instance, method: string, func)
3411
local oldIdentity = getThreadIdentity()
3412
setThreadIdentity(8)
3413
if remoteBlacklistCache[remote] ~= 2 then
3414
local remoteId = getDebugId(remote)
3415
local remoteType = remote.ClassName--isHookThread() and oldIndex(remote, "ClassName") or remote.ClassName
3416
3417
if not otherLogs[remoteId] then
3418
otherLogs[remoteId] = {
3419
Type = "Callback",
3420
CurrentFunction = func,
3421
Ignored = false,
3422
Blocked = false,
3423
Calls = {}
3424
}
3425
elseif otherLogs[remoteId].CurrentFunction then
3426
local curFunc = otherLogs[remoteId].CurrentFunction
3427
for i,v in _G.remoteSpyCallbackHooks do
3428
if v == curFunc then
3429
tableRemove(_G.remoteSpyCallbackHooks, i)
3430
break
3431
end
3432
end
3433
restorefunction(curFunc)
3434
otherLogs[remoteId].CurrentFunction = func
3435
end
3436
3437
if func then
3438
local oldfunc
3439
oldfunc = hookfunction(func, function(...) -- lclosure, so oth.hook not applicable
3440
if #getCallStack() == 2 then -- check that the function is actually being called by a cclosure
3441
local oldLevel = getThreadIdentity()
3442
setThreadIdentity(8) -- fix for people passing coregui as an arg, also it's here because I'm too lazy to implement at the start of every hook. Shouldn't be too dangerous because I restore it afterwards
3443
3444
if not Settings.Paused then
3445
local spyFunc = spyFunctions[idxs[method]]
3446
local args, _, _, hasInstance = deepClone({...}, remoteType, -1)
3447
if not args then
3448
pushError("Impossible error 2 has occurred, please report to GameGuy#5920")
3449
return oldfunc(...)
3450
end
3451
local argCount = select("#", ...)
3452
3453
local callingScript = originalCallerCache[remoteId] or {nil, checkcaller()}
3454
3455
originalCallerCache[remoteId] = nil
3456
3457
local scr = getcallingscript()
3458
if scr then scr = cloneref(scr) end
3459
3460
local lastIdx = getLastIndex(args)
3461
3462
local data = {
3463
HasInstance = hasInstance or (not remote:IsAncestorOf(game)),
3464
TypeIndex = idxs[method],
3465
CallbackScript = scr,
3466
Script = callingScript[1],
3467
Args = args, -- 2 deeper total
3468
NonNilArgCount = lastIdx,
3469
CallbackLog = otherLogs[remoteId],
3470
NilCount = (argCount - lastIdx),
3471
FromSynapse = callingScript[2]
3472
}
3473
3474
if spyFunc.ReturnsValue and not otherLogs[remoteId].Blocked then
3475
local returnValue = {}
3476
spawnFunc(function()
3477
if not otherLogs[remoteId].Ignored and (Settings.LogHiddenRemotesCalls or spyFunc.Enabled) then
3478
data.ReturnValue = returnValue
3479
sendLog(remote, remoteId, data)
3480
end
3481
end)
3482
3483
setThreadIdentity(oldLevel)
3484
return processReturnValue(remoteType, returnValue, oldfunc(...))
3485
end
3486
3487
3488
if not otherLogs[remoteId].Ignored and (Settings.LogHiddenRemotesCalls or spyFunc.Enabled) then
3489
sendLog(remote, remoteId, data)
3490
end
3491
end
3492
3493
setThreadIdentity(oldLevel)
3494
if otherLogs[remoteId] and otherLogs[remoteId].Blocked then
3495
return
3496
end
3497
end
3498
3499
return oldfunc(...)
3500
end)
3501
tableInsert(_G.remoteSpyCallbackHooks, func)
3502
end
3503
end
3504
setThreadIdentity(oldIdentity)
3505
end
3506
3507
local function addConnection(remote: Instance, signalType: string, signal: RBXScriptSignal)
3508
local oldIdentity = getThreadIdentity()
3509
setThreadIdentity(8)
3510
if remoteBlacklistCache[remote] ~= 2 then
3511
local remoteId = getDebugId(remote)
3512
local remoteType = remote.ClassName--isHookThread() and oldIndex(remote, "ClassName") or remote.ClassName
3513
3514
if not otherLogs[remoteId] then
3515
otherLogs[remoteId] = {
3516
Type = "Connection",
3517
Ignored = false,
3518
Blocked = false,
3519
Calls = {}
3520
}
3521
3522
local scriptCache = setmetatable({}, {__mode = "k"})
3523
local connectionCache = {} -- unused (for now)
3524
hooksignal(signal, function(info, ...)
3525
if not Settings.Paused then
3526
spawnFunc(function(...)
3527
local original = getThreadIdentity()
3528
setThreadIdentity(8) -- not sure why hooksignal threads aren't level 8, but I restore this later anyways, just to be safe
3529
local spyFunc = spyFunctions[idxs[signalType]]
3530
if not otherLogs[remoteId].Ignored and (Settings.LogHiddenRemotesCalls or spyFunc.Enabled) then
3531
if info.Index == 0 then
3532
tableClear(connectionCache)
3533
tableClear(scriptCache)
3534
end
3535
3536
tableInsert(connectionCache, info.Connection)
3537
local CS = issynapsethread(coroutine.running()) and "Synapse" or getcallingscript()
3538
if CS then
3539
if scriptCache[CS] then
3540
scriptCache[CS] += 1
3541
else
3542
scriptCache[CS] = 1
3543
end
3544
end
3545
3546
local callingScript = originalCallerCache[remoteId] or {nil, false}
3547
3548
originalCallerCache[remoteId] = nil
3549
3550
if info.Index == (#getconnections(signal)-1) then -- -1 because base 0 for info.Index
3551
local args, _, _, hasInstance = deepClone({...}, remoteType, -1)
3552
if not args then
3553
pushError("Impossible error 3 has occurred, please report to GameGuy#5920")
3554
return true, ...
3555
end
3556
local argCount = select("#", ...)
3557
local lastIdx = getLastIndex(args)
3558
local data = {
3559
HasInstance = hasInstance or (not remote:IsAncestorOf(game)),
3560
TypeIndex = idxs[signalType],
3561
Script = callingScript[1],
3562
Scripts = scriptCache,
3563
Connections = connectionCache,
3564
Signal = signal,
3565
Args = args, -- 2 deeper total
3566
NonNilArgCount = lastIdx,
3567
NilCount = (argCount - lastIdx),
3568
FromSynapse = callingScript[2]
3569
}
3570
3571
sendLog(remote, remoteId, data)
3572
end
3573
end
3574
setThreadIdentity(original)
3575
end, ...)
3576
end
3577
3578
if otherLogs[remoteId].Blocked then
3579
return false
3580
end
3581
return true, ...
3582
end)
3583
tableInsert(_G.remoteSpySignalHooks, signal)
3584
end
3585
end
3586
setThreadIdentity(oldIdentity)
3587
end
3588
3589
local namecallFilters = {}
3590
local newIndexFilters = {}
3591
local indexFilters = {}
3592
3593
do -- filter setup
3594
for _,v in spyFunctions do
3595
if v.Type == "Callback" then
3596
tableInsert(newIndexFilters, AllFilter.new({
3597
InstanceTypeFilter.new(1, v.Object),
3598
AnyFilter.new({
3599
ArgumentFilter.new(2, v.Callback),
3600
ArgumentFilter.new(2, v.DeprecatedCallback)
3601
}),
3602
TypeFilter.new(3, "function")
3603
}))
3604
elseif v.Type == "Call" then
3605
tableInsert(namecallFilters, AllFilter.new({
3606
InstanceTypeFilter.new(1, v.Object),
3607
AnyFilter.new({
3608
NamecallFilter.new(v.Method),
3609
NamecallFilter.new(v.DeprecatedMethod)
3610
})
3611
}))
3612
elseif v.Type == "Connection" then
3613
tableInsert(indexFilters, AllFilter.new({
3614
InstanceTypeFilter.new(1, v.Object),
3615
AnyFilter.new({
3616
ArgumentFilter.new(2, v.Connection),
3617
ArgumentFilter.new(2, v.DeprecatedConnection)
3618
})
3619
}))
3620
end
3621
end
3622
end
3623
-- need to pass an arg telling addCallback/addConnection that the call came from a hook thread, which will use oldIndex, as opposed to being called from the getweakdescendants iteration, where oldIndex will throw an error
3624
oldNewIndex = newHookMetamethod(game, "__newindex", function(remote, idx, newidx)
3625
spawnFunc(addCallback, cloneref(remote), idx, newidx)
3626
3627
return oldNewIndex(remote, idx, newidx)
3628
end, AnyFilter.new(newIndexFilters))
3629
_G.remoteSpyHooks.NewIndex = oldNewIndex
3630
3631
oldIndex = newHookMetamethod(game, "__index", function(remote, idx)
3632
local newSignal = oldIndex(remote, idx)
3633
spawnFunc(addConnection, cloneref(remote), idx, newSignal)
3634
3635
return newSignal
3636
end, AnyFilter.new(indexFilters))
3637
_G.remoteSpyHooks.Index = oldIndex
3638
3639
local oldNewInstance
3640
oldNewInstance = filteredOth(Instance.new, function(instanceType: string, ...)
3641
local newInstance = oldNewInstance(instanceType, ...)
3642
remoteBlacklistCache[newInstance] = 2
3643
3644
return newInstance
3645
end, AllFilter.new({
3646
AnyFilter.new({
3647
ArgumentFilter.new(1, "RemoteEvent"),
3648
ArgumentFilter.new(1, "RemoteFunction")
3649
}),
3650
AnyFilter.new({
3651
TypeFilter.new(2, "Instance"),
3652
ArgCountFilter.new(1)
3653
})
3654
}))
3655
3656
local oldClone
3657
oldClone = filteredOth(Instance.new, function(original: Instance, ...)
3658
local newInstance = oldClone(original, ...)
3659
remoteBlacklistCache[newInstance] = 2
3660
3661
return newInstance
3662
end, AllFilter.new({
3663
AnyFilter.new({
3664
InstanceTypeFilter.new(1, "RemoteEvent"),
3665
InstanceTypeFilter.new(1, "RemoteFunction")
3666
})
3667
}))
3668
3669
table.insert(namecallFilters, AllFilter.new({ -- setup :Clone filter
3670
AnyFilter.new({
3671
NamecallFilter.new("Clone"),
3672
NamecallFilter.new("clone")
3673
}),
3674
AnyFilter.new({
3675
InstanceTypeFilter.new(1, "RemoteEvent"),
3676
InstanceTypeFilter.new(1, "RemoteFunction")
3677
})
3678
}))
3679
3680
local initInfo = {
3681
RemoteFunction = { "Callback", "OnClientInvoke" },
3682
BindableFunction = { "Callback", "OnInvoke" },
3683
RemoteEvent = { "Connection", "OnClientEvent" },
3684
BindableEvent = { "Connection", "Event" }
3685
}
3686
3687
do -- init OnClientInvoke and signal index
3688
for _,v in getweakdescendants(game) do
3689
local data = initInfo[v.ClassName]
3690
if data then
3691
if data[1] == "Connection" then
3692
local _ = v[data[2]] -- calls the OTH of __index which adds the connection
3693
elseif data[1] == "Callback" then
3694
local func = getcallbackmember(v, data[2])
3695
if func then
3696
addCallback(cloneref(v), data[2], func)
3697
end
3698
end
3699
end
3700
end
3701
3702
for _,v in getnilinstances() do
3703
local data = initInfo[v.ClassName]
3704
if data then
3705
if data[1] == "Connection" then
3706
local _ = v[data[2]] -- calls the OTH of __index which adds the connection
3707
elseif data[1] == "Callback" then
3708
local func = getcallbackmember(v, data[2])
3709
if func then
3710
addCallback(cloneref(v), data[2], func)
3711
end
3712
end
3713
end
3714
end
3715
end
3716
3717
do -- namecall and function hooks
3718
local oldNamecall
3719
oldNamecall = newHookMetamethod(game, "__namecall", newcclosure(function(remote, ...)
3720
setThreadIdentity(8) -- oth isn't stock at 8 for some reason
3721
3722
local nmcMethod = getnamecallmethod()
3723
if nmcMethod == "Clone" or nmcMethod == "clone" then -- faster than string.lower
3724
local newInstance: Instance = oldNamecall(remote, ...)
3725
if newInstance then
3726
remoteBlacklistCache[newInstance] = 2
3727
end
3728
3729
return newInstance
3730
end
3731
3732
if remoteBlacklistCache[remote] ~= 2 then
3733
local remoteId = getDebugId(remote)
3734
if not Settings.Paused and select("#", ...) < 7996 then
3735
local scr = getcallingscript()
3736
if scr then scr = cloneref(scr) end
3737
3738
local spyFunc = spyFunctions[idxs[nmcMethod]]
3739
if spyFunc.Type == "Call" and spyFunc.FiresLocally then
3740
local caller = checkcaller()
3741
originalCallerCache[remoteId] = originalCallerCache[remoteId] or {(not caller and scr), caller}
3742
end
3743
-- it will either return true at checkcaller because called from synapse (non remspy), or have already been set by remspy
3744
3745
if spyFunc.ReturnsValue and (not callLogs[remoteId] or not callLogs[remoteId].Blocked) then
3746
local returnValue = {}
3747
spawnFunc(addCall, cloneref(remote), remoteId, returnValue, spyFunc, checkcaller(), scr, getCallStack(getOriginalThread()), ...)
3748
3749
return processReturnValue(spyFunc.Object, returnValue, oldNamecall(remote, ...)) -- getproperties(remote).ClassName is not performant at all, but using oldIndex breaks stuff
3750
end
3751
spawnFunc(addCall, cloneref(remote), remoteId, nil, spyFunc, checkcaller(), scr, getCallStack(getOriginalThread()), ...)
3752
end
3753
3754
if callLogs[remoteId] and callLogs[remoteId].Blocked then return end
3755
end
3756
3757
return oldNamecall(remote, ...)
3758
end), AnyFilter.new(namecallFilters))
3759
_G.remoteSpyHooks.Namecall = oldNamecall
3760
3761
for _,v in spyFunctions do
3762
if v.Type == "Call" then
3763
local oldFunc
3764
local newfunction = function(remote, ...)
3765
setThreadIdentity(8) -- oth isn't stock at 8 for some reason
3766
if remoteBlacklistCache[remote] ~= 2 then
3767
local remoteId = getDebugId(remote)
3768
3769
if not Settings.Paused and select("#", ...) < 7996 then
3770
local scr = getcallingscript()
3771
if scr then scr = cloneref(scr) end
3772
3773
if v.Type == "Call" and v.FiresLocally then
3774
local caller = checkcaller()
3775
originalCallerCache[remoteId] = originalCallerCache[remoteId] or {(not caller and scr), caller}
3776
end
3777
3778
if v.ReturnsValue and (not callLogs[remoteId] or not callLogs[remoteId].Blocked) then
3779
local returnValue = {}
3780
spawnFunc(addCall, cloneref(remote), remoteId, returnValue, v, checkcaller(), scr, getCallStack(getOriginalThread()), ...)
3781
3782
return processReturnValue(v.Object, returnValue, oldFunc(remote, ...))
3783
end
3784
spawnFunc(addCall, cloneref(remote), remoteId, nil, v, checkcaller(), scr, getCallStack(getOriginalThread()), ...)
3785
end
3786
3787
if callLogs[remoteId] and callLogs[remoteId].Blocked then return end
3788
end
3789
3790
return oldFunc(remote, ...)
3791
end
3792
3793
local originalFunc = Instance.new(v.Object)[v.Method]
3794
oldFunc = filteredOth(originalFunc, newcclosure(newfunction), InstanceTypeFilter.new(1, v.Object))
3795
3796
v.Function = originalFunc
3797
_G.remoteSpyHooks[v.Method] = oldFunc
3798
end
3799
end
3800
end
3801
3802
-- CREDIT TO https://github.com/Upbolt/Hydroxide/ FOR INSPIRATION AND A FEW FORKED TOSTRING FUNCTIONS