├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── Source ├── Client │ ├── Interface │ │ ├── Components │ │ │ ├── Button.luau │ │ │ ├── Codeblock.luau │ │ │ ├── ImageButton.luau │ │ │ ├── ImageLabel.luau │ │ │ ├── RadioButton.luau │ │ │ ├── TextBox.luau │ │ │ ├── TextButton.luau │ │ │ ├── TextLabel.luau │ │ │ ├── UICorner.luau │ │ │ ├── UIDrag.luau │ │ │ ├── UIPadding.luau │ │ │ └── UIStroke.luau │ │ ├── Fusion │ │ │ ├── ContentPanel │ │ │ │ ├── Content.luau │ │ │ │ ├── Topbar.luau │ │ │ │ └── init.luau │ │ │ ├── Indicator.luau │ │ │ ├── Indicator.story.luau │ │ │ ├── Pages │ │ │ │ ├── Actions │ │ │ │ │ ├── ActionExecution.luau │ │ │ │ │ ├── ActionList.luau │ │ │ │ │ └── init.luau │ │ │ │ ├── Admin.luau │ │ │ │ ├── Dashboard.luau │ │ │ │ ├── Datastores.luau │ │ │ │ ├── Debugger │ │ │ │ │ ├── ListFrame.luau │ │ │ │ │ ├── ThreadFrame.luau │ │ │ │ │ └── init.luau │ │ │ │ ├── Embedded.luau │ │ │ │ ├── Explorer │ │ │ │ │ ├── Explorer.luau │ │ │ │ │ ├── Properties.luau │ │ │ │ │ └── init.luau │ │ │ │ ├── Inspector.luau │ │ │ │ ├── Interceptor │ │ │ │ │ ├── EventInformation.luau │ │ │ │ │ ├── EventList.luau │ │ │ │ │ └── init.luau │ │ │ │ ├── LiveEvents.luau │ │ │ │ ├── Logging.luau │ │ │ │ ├── Performance.luau │ │ │ │ ├── Sandbox.luau │ │ │ │ ├── ScriptHub.luau │ │ │ │ └── Statistics │ │ │ │ │ ├── Graph.luau │ │ │ │ │ ├── Views.luau │ │ │ │ │ └── init.luau │ │ │ ├── SidebarPanel │ │ │ │ └── init.luau │ │ │ ├── Window.luau │ │ │ └── Window.story.luau │ │ └── Theme.luau │ ├── Singletons │ │ ├── Actions.luau │ │ ├── Admin.luau │ │ ├── Authentication.luau │ │ ├── Debugger.luau │ │ ├── Explorer.luau │ │ ├── Focus.luau │ │ ├── Hotkeys.luau │ │ ├── Interface.luau │ │ ├── LiveEvents.luau │ │ ├── Logging.luau │ │ ├── RemoteEvents.luau │ │ ├── Sandbox.luau │ │ ├── ScriptHub.luau │ │ ├── Settings.luau │ │ ├── Statistics.luau │ │ └── VM │ │ │ ├── Bindings.luau │ │ │ ├── Constants.luau │ │ │ ├── Context.luau │ │ │ ├── Environment.luau │ │ │ ├── Exploit │ │ │ ├── EnvironmentTable.luau │ │ │ ├── Globals.luau │ │ │ └── Hooks │ │ │ │ ├── CoreGui.luau │ │ │ │ └── HttpGetAsync.luau │ │ │ ├── Macros.luau │ │ │ ├── Roblox │ │ │ ├── EnvironmentTable.luau │ │ │ ├── GlobalTable.luau │ │ │ ├── Globals.luau │ │ │ ├── Hooks │ │ │ │ ├── Connection.luau │ │ │ │ └── Signal.luau │ │ │ ├── Libraries │ │ │ │ └── Task.luau │ │ │ └── SharedTable.luau │ │ │ ├── Scheduler.luau │ │ │ ├── Types.luau │ │ │ └── init.luau │ └── init.luau ├── Containers │ ├── Runtime.client.luau │ └── Runtime.server.luau ├── Server │ ├── Singletons │ │ ├── Actions.luau │ │ ├── Admin.luau │ │ ├── Authentication.luau │ │ ├── Hooks.luau │ │ ├── Logging.luau │ │ ├── RemoteEvents.luau │ │ ├── Sandbox.luau │ │ └── Settings.luau │ └── init.luau ├── Types │ ├── Fusion.luau │ ├── Page.luau │ └── Signal.luau ├── Utilities │ ├── Luau │ │ ├── deserialise.luau │ │ └── disassemble.luau │ └── Remotes │ │ ├── decodeFromJSON.luau │ │ ├── encodeToJSON.luau │ │ └── prettifyVaradics.luau ├── Vendor │ ├── LuauCeption.luau │ └── Repr.luau └── init.luau ├── aftman.toml ├── default.project.json ├── development.project.json ├── network.zap ├── selene.toml ├── sourcemap.json ├── testez.yml ├── wally.lock └── wally.toml /.gitignore: -------------------------------------------------------------------------------- 1 | Network.luau 2 | 3 | *.rbxl 4 | *.rbxm 5 | 6 | Packages -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "luau-lsp.sourcemap.rojoProjectFile": "development.project.json", 3 | "filewatcher.commands": [ 4 | { 5 | "match": "\\.lua*", 6 | "isAsync": true, 7 | "cmd": "cd ${currentWorkspace} && ~/.aftman/bin/rojo sourcemap development.project.json -o sourcemap.json", 8 | "shell": "bash", 9 | "event": "onFileChange" 10 | }, 11 | ], 12 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 AsynchronousMatrix 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dev Suite - 👋 2 | 3 | Dev Suite is a collection of tools and resources that enables developers to pentest and validate the security of their Roblox experiences. 4 | 5 | Dev Suite is yet to be feature complete - but you're still able to use this tool in your projects by installing it via wally. 6 | 7 | ![image](https://github.com/user-attachments/assets/4cb42ea4-da13-4d66-b9e2-1b76055d37f8) 8 | 9 | ### Motivation 10 | I've always found that exploiters generally have the advantage over developers when it comes to testing, they have a client that they can use to execute *any* code in the Roblox environment.. 11 | 12 | Right, so this is why I created Dev Suite, it's designed to not only emulate the exploiter environment - but provide tools wrapping around the ecosystem itself. 13 | 14 | ### API Documentation 15 | Dev Suite provides a minimal API that enables you to interact with some of the modules, as well as authentication for this package. 16 | 17 | #### Actions 18 | > DevSuite.CreateAction 19 | 20 | The `CreateAction` function enables developers to add custom functionality to the Dev Suite, when adding a function - that function will be represented in the `Actions` tab. 21 | 22 | The purpose is to allow developers to create debug commands, or functionality for QA teams to use, without needing to use an alternative such as a commands framework. 23 | 24 | ```lua 25 | DevSuite:CreateAction(actionCallback: (...any) -> (), actionSettings: { 26 | name: string, 27 | description: string?, 28 | arguments: { 29 | type: "Number" | "String" | "Boolean" | "Player", 30 | name: string, 31 | default: any? 32 | }?, 33 | }) 34 | ``` 35 | 36 | #### Authentication 37 | > DevSuite.SetAuthenticationCallback 38 | --- 39 | 40 | The `SetAuthenticationCallback` function allows developers to set a custom authentication callback that determines which players can access Dev Suite. 41 | 42 | This callback is only available on the server and will be called whenever a player first loads in. 43 | 44 | ```lua 45 | DevSuite:SetAuthenticationCallback(callback: (Player) -> boolean) 46 | ``` 47 | 48 | *Example:* 49 | ```lua 50 | -- Only allow players in group 12345 with rank 100 or higher 51 | DevSuite:SetAuthenticationCallback(function(player) 52 | return player:GetRankInGroup(12345) >= 100 53 | end) 54 | ``` 55 | 56 | #### VM 57 | *As developers - Dev Suite enables you to interop with the Client-side VM, this functionality is limited - but can be used in special cases to do some unique things.* 58 | 59 | > DevSuite.AddLibraryToVM 60 | 61 | Allows developers to add custom libraries to the client-side VM environment. This enables extending the VM's functionality with custom functions that can interact with the LuauCeption VM and underlying Lua state. 62 | 63 | ```lua 64 | DevSuite:AddLibraryToVM(self: DevSuite, name: string, library: { 65 | [string]: (luauCeption: VM.LuauCeption, luaState: number) -> number 66 | }) 67 | ``` 68 | 69 | > DevSuite.AddGlobalToVM 70 | 71 | Enables adding global variables/functions to the VM's global environment. This allows injecting individual global values that can be accessed from code executed in the VM. 72 | 73 | ```lua 74 | DevSuite:AddGlobalToVM(self: DevSuite, name: string, callback: (luauCeption: VM.LuauCeption, luaState: number) -> number) 75 | ``` 76 | 77 | > DevSuite.HookVMMetamethod 78 | 79 | Provides the ability to hook into Lua metamethods within the VM, allowing developers to intercept and modify behavior of core operations like indexing, newindex, etc. 80 | 81 | ```lua 82 | DevSuite:HookVMMetamethod(self: DevSuite, name: string, callback: (luauCeption: VM.LuauCeption, luaState: number) -> number) 83 | ``` 84 | 85 | *Example:* 86 | 87 | ```lua 88 | -- keep track of the original __namecall function 89 | local __namecall 90 | 91 | __namecall = DevSuite:HookVMMetamethod("__namecall", function(luauCeption, luaState: VMTypes.LuaState) 92 | -- do cool things 93 | local methodName = cstring(lua_namecallatom(luaState, 0)) 94 | local object = toLuau(luaState, 1) 95 | 96 | print(`VM called '{methodName}' on '{object}'`) 97 | 98 | -- return what the default implementation would return 99 | return __namecall(luaState) 100 | end) 101 | -------------------------------------------------------------------------------- /Source/Client/Interface/Components/Button.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | 5 | local Sift = require(Package.Parent.Sift) 6 | 7 | return function(scope: FusionTypes.Scope, props: { 8 | [any]: any 9 | }) 10 | return scope:New("ImageButton")(Sift.Dictionary.merge({ 11 | Size = UDim2.fromScale(1, 1), 12 | BackgroundTransparency = 1, 13 | 14 | ScaleType = Enum.ScaleType.Fit, 15 | }, props)) 16 | end 17 | -------------------------------------------------------------------------------- /Source/Client/Interface/Components/Codeblock.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | 5 | local TextLabel = require(Package.Client.Interface.Components.TextLabel) 6 | 7 | local UICorner = require(Package.Client.Interface.Components.UICorner) 8 | 9 | local Highlighter = require(Package.Parent.Highlighter) 10 | 11 | local Theme = require(Package.Client.Interface.Theme) 12 | 13 | local Fusion = require(Package.Parent.Fusion) 14 | 15 | local DEFAULT_EDITOR_TEXT_CONTENT = "-- Press 'Run' to execute this code!\n\nprint(\"Hello, World!\")" 16 | 17 | local function createTextBox(scope: FusionTypes.Scope, props: { 18 | lineCount: FusionTypes.Value 19 | }) 20 | return scope:New("TextBox")({ 21 | Size = UDim2.fromScale(1, 1), 22 | FontFace = Theme.CodeblockFont, 23 | BackgroundTransparency = 1, 24 | ClearTextOnFocus = false, 25 | RichText = true, 26 | TextColor3 = Theme.CodeblockColor3, 27 | TextSize = 17, 28 | Text = DEFAULT_EDITOR_TEXT_CONTENT, 29 | MultiLine = true, 30 | TextXAlignment = Enum.TextXAlignment.Left, 31 | TextYAlignment = Enum.TextYAlignment.Top, 32 | 33 | AutomaticSize = Enum.AutomaticSize.Y, 34 | 35 | [Fusion.OnChange("Text")] = function(content) 36 | props.lineCount:set(#string.split(content, "\n")) 37 | end, 38 | 39 | [Fusion.Children] = { 40 | UIPadding = scope:New("UIPadding")({ 41 | PaddingTop = UDim.new(0, 10), 42 | PaddingBottom = UDim.new(0, 10), 43 | PaddingLeft = UDim.new(0, 0), 44 | PaddingRight = UDim.new(0, 10), 45 | }) 46 | } 47 | }) 48 | end 49 | 50 | local function createScrollingFrame(scope: FusionTypes.Scope, props: { 51 | textBox: FusionTypes.Value, 52 | scrollingFrame: FusionTypes.Value?, 53 | }) 54 | local hue, saturation, value = Theme.CodeblockBackgroundColor3:ToHSV() 55 | 56 | local lineCountFrameColor3 = Color3.fromHSV(hue, saturation, value * 0.7) 57 | local lineCountNumberFrameColor3 = Color3.fromHSV(hue, saturation, value * 0.8) 58 | 59 | local lineCount = scope:Value(#string.split(DEFAULT_EDITOR_TEXT_CONTENT, "\n")) 60 | 61 | local textBox = createTextBox(scope, { 62 | lineCount = lineCount 63 | }) 64 | 65 | local removeHighlighting 66 | 67 | props.textBox:set(textBox) 68 | 69 | local scrollingFrame = scope:New("ScrollingFrame")({ 70 | Size = UDim2.fromScale(1, 1), 71 | AutomaticCanvasSize = Enum.AutomaticSize.Y, 72 | CanvasSize = UDim2.fromScale(0, 0), 73 | ScrollBarThickness = 0, 74 | BackgroundTransparency = 1, 75 | ClipsDescendants = true, 76 | 77 | [Fusion.Children] = { 78 | LineCountFrame = scope:New("Frame")({ 79 | Size = UDim2.new(0, 40, 1, 0), 80 | Position = UDim2.fromOffset(0, 0), 81 | 82 | BackgroundColor3 = lineCountFrameColor3, 83 | AutomaticSize = Enum.AutomaticSize.Y, 84 | 85 | [Fusion.Children] = { 86 | UICorner = UICorner(scope, {}), 87 | 88 | RightPanel = scope:New("Frame")({ 89 | Size = UDim2.fromScale(0.3, 1), 90 | BorderSizePixel = 0, 91 | BackgroundColor3 = lineCountNumberFrameColor3, 92 | Position = UDim2.fromScale(0.7, 0), 93 | AutomaticSize = Enum.AutomaticSize.Y, 94 | }), 95 | 96 | LineCount = TextLabel(scope, { 97 | Size = UDim2.fromScale(0.7, 999), 98 | Position = UDim2.fromOffset(0, 3.5), 99 | AnchorPoint = Vector2.new(0, 0), 100 | TextSize = 17, 101 | FontFace = Theme.CodeblockFont, 102 | TextScaled = false, 103 | Text = scope:Computed(function(use) 104 | local lineCountText = "" 105 | use(lineCount) 106 | local count = Fusion.peek(lineCount) 107 | 108 | if count > 1000 and removeHighlighting then 109 | removeHighlighting() 110 | removeHighlighting = nil 111 | else 112 | if count < 1000 then 113 | if not removeHighlighting then 114 | task.defer(function() 115 | removeHighlighting = Highlighter.highlight({ 116 | textObject = textBox 117 | }) 118 | end) 119 | end 120 | end 121 | end 122 | 123 | for index = 1, count do 124 | lineCountText ..= `{index}\n` 125 | end 126 | 127 | return lineCountText 128 | end), 129 | TextXAlignment = Enum.TextXAlignment.Center, 130 | TextYAlignment = Enum.TextYAlignment.Top, 131 | AutomaticSize = Enum.AutomaticSize.Y, 132 | 133 | 134 | [Fusion.Children] = { 135 | UIPadding = scope:New("UIPadding")({ 136 | PaddingTop = UDim.new(0, 10), 137 | PaddingBottom = UDim.new(0, 10), 138 | }) 139 | } 140 | }) 141 | } 142 | }), 143 | 144 | CodeEditor = scope:New("Frame")({ 145 | Size = UDim2.new(1, -45, 1, 0), 146 | BackgroundTransparency = 1, 147 | Position = UDim2.fromOffset(45, 0), 148 | 149 | AutomaticSize = Enum.AutomaticSize.Y, 150 | 151 | [Fusion.Children] = { 152 | TextBox = textBox 153 | } 154 | }) 155 | } 156 | }) 157 | 158 | if props.scrollingFrame then 159 | props.scrollingFrame:set(scrollingFrame) 160 | end 161 | 162 | return scrollingFrame 163 | end 164 | 165 | return function(scope: FusionTypes.Scope, props: { 166 | Size: UDim2, 167 | 168 | textBox: FusionTypes.Value, 169 | scrollingFrame: FusionTypes.Value?, 170 | }) 171 | local scrollingFrame = createScrollingFrame(scope, { 172 | textBox = props.textBox, 173 | scrollingFrame = props.scrollingFrame, 174 | }) 175 | 176 | return scope:New("CanvasGroup")({ 177 | BackgroundColor3 = Theme.CodeblockBackgroundColor3, 178 | Size = props.Size, 179 | 180 | [Fusion.Children] = { 181 | UICorner = UICorner(scope, { 182 | level = 2, 183 | }), 184 | 185 | UIGradient = scope:New("UIGradient")({ 186 | Rotation = 90, 187 | Color = ColorSequence.new({ 188 | ColorSequenceKeypoint.new(0, Color3.new(1, 1, 1)), 189 | ColorSequenceKeypoint.new(1, Color3.new(0.8, 0.8, 0.8)) 190 | }) 191 | }), 192 | 193 | ScrollingFrame = scrollingFrame 194 | } 195 | }) 196 | end 197 | -------------------------------------------------------------------------------- /Source/Client/Interface/Components/ImageButton.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | 5 | local Sift = require(Package.Parent.Sift) 6 | 7 | local Theme = require(Package.Client.Interface.Theme) 8 | 9 | return function(scope: FusionTypes.Scope, props: { 10 | [any]: any 11 | }) 12 | return scope:New("ImageButton")(Sift.Dictionary.merge({ 13 | Size = UDim2.fromScale(1, 1), 14 | BackgroundTransparency = 1, 15 | ImageColor3 = Theme.BaseIconColor3, 16 | 17 | ScaleType = Enum.ScaleType.Fit, 18 | }, props)) 19 | end 20 | -------------------------------------------------------------------------------- /Source/Client/Interface/Components/ImageLabel.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | 5 | local Sift = require(Package.Parent.Sift) 6 | 7 | local Theme = require(Package.Client.Interface.Theme) 8 | 9 | return function(scope: FusionTypes.Scope, props: { 10 | [any]: any 11 | }) 12 | return scope:New("ImageLabel")(Sift.Dictionary.merge({ 13 | Size = UDim2.fromScale(1, 1), 14 | BackgroundTransparency = 1, 15 | ImageColor3 = Theme.BaseIconColor3, 16 | 17 | ScaleType = Enum.ScaleType.Fit, 18 | }, props)) 19 | end 20 | -------------------------------------------------------------------------------- /Source/Client/Interface/Components/RadioButton.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local Theme = require(Package.Client.Interface.Theme) 4 | local FusionTypes = require(Package.Types.Fusion) 5 | 6 | local Sift = require(Package.Parent.Sift) 7 | local Fusion = require(Package.Parent.Fusion) 8 | 9 | return function(scope: FusionTypes.Scope, props: { 10 | toggled: FusionTypes.Value, 11 | 12 | [any]: any 13 | }) 14 | local propsClone = table.clone(props) 15 | 16 | local children = propsClone[Fusion.Children] 17 | local isToggled = propsClone.toggled 18 | 19 | propsClone.toggled = nil 20 | propsClone[Fusion.Children] = nil 21 | 22 | return scope:New("ImageButton")(Sift.Dictionary.merge({ 23 | Size = UDim2.fromScale(1, 1), 24 | BackgroundColor3 = Theme.BaseBackgroundColor3, 25 | 26 | ScaleType = Enum.ScaleType.Fit, 27 | 28 | [Fusion.Children] = { 29 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}), 30 | 31 | UICorner = scope:New("UICorner")({ 32 | CornerRadius = UDim.new(1, 0) 33 | }), 34 | 35 | Fill = scope:New("Frame")({ 36 | Size = UDim2.fromScale(0.4, 0.4), 37 | AnchorPoint = Vector2.new(0.5, 0.5), 38 | Position = UDim2.fromScale(0.5, 0.5), 39 | BackgroundColor3 = Theme.AccentColor3, 40 | Visible = scope:Computed(function(use) 41 | return use(isToggled) 42 | end), 43 | 44 | [Fusion.Children] = Sift.Dictionary.merge(children, { 45 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}), 46 | 47 | UICorner = scope:New("UICorner")({ 48 | CornerRadius = UDim.new(1, 0) 49 | }), 50 | }) 51 | }) 52 | } 53 | }, propsClone)) 54 | end 55 | -------------------------------------------------------------------------------- /Source/Client/Interface/Components/TextBox.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | 5 | local Sift = require(Package.Parent.Sift) 6 | 7 | local Theme = require(Package.Client.Interface.Theme) 8 | 9 | return function(scope: FusionTypes.Scope, props: { 10 | [any]: any 11 | }) 12 | return scope:New("TextBox")(Sift.Dictionary.merge({ 13 | FontFace = Theme.BaseLabelFont, 14 | TextColor3 = Theme.BaseLabelColor3, 15 | 16 | RichText = true, 17 | 18 | Size = UDim2.fromScale(1, 1), 19 | BackgroundTransparency = 1, 20 | 21 | TextScaled = true, 22 | }, props)) 23 | end 24 | -------------------------------------------------------------------------------- /Source/Client/Interface/Components/TextButton.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | 5 | local Sift = require(Package.Parent.Sift) 6 | 7 | local Theme = require(Package.Client.Interface.Theme) 8 | 9 | return function(scope: FusionTypes.Scope, props: { 10 | [any]: any 11 | }) 12 | return scope:New("TextButton")(Sift.Dictionary.merge({ 13 | FontFace = Theme.BaseLabelFont, 14 | TextColor3 = Theme.BaseLabelColor3, 15 | 16 | RichText = true, 17 | 18 | Size = UDim2.fromScale(1, 1), 19 | BackgroundTransparency = 1, 20 | 21 | TextScaled = true, 22 | }, props)) 23 | end 24 | -------------------------------------------------------------------------------- /Source/Client/Interface/Components/TextLabel.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | 5 | local Sift = require(Package.Parent.Sift) 6 | 7 | local Theme = require(Package.Client.Interface.Theme) 8 | 9 | return function(scope: FusionTypes.Scope, props: { 10 | [any]: any 11 | }) 12 | return scope:New("TextLabel")(Sift.Dictionary.merge({ 13 | FontFace = Theme.BaseLabelFont, 14 | TextColor3 = Theme.BaseLabelColor3, 15 | 16 | RichText = true, 17 | 18 | Size = UDim2.fromScale(1, 1), 19 | BackgroundTransparency = 1, 20 | 21 | TextScaled = true, 22 | }, props)) 23 | end 24 | -------------------------------------------------------------------------------- /Source/Client/Interface/Components/UICorner.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | 5 | return function(scope: FusionTypes.Scope, props: { 6 | level: number?, 7 | }) 8 | return scope:New("UICorner")({ 9 | CornerRadius = UDim.new(0, 8 - ((props.level or 1 - 1) * 2)), 10 | }) 11 | end 12 | -------------------------------------------------------------------------------- /Source/Client/Interface/Components/UIDrag.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | 5 | return function(scope: FusionTypes.Scope) 6 | return scope:New("UIDragDetector")({ 7 | BoundingBehavior = Enum.UIDragDetectorBoundingBehavior.EntireObject, 8 | }) 9 | end 10 | -------------------------------------------------------------------------------- /Source/Client/Interface/Components/UIPadding.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | 5 | return function(scope: FusionTypes.Scope, props: { 6 | paddingWidth: UDim, 7 | paddingHeight: UDim, 8 | }) 9 | return scope:New("UIPadding")({ 10 | PaddingBottom = UDim.new(props.paddingHeight.Scale / 2, props.paddingHeight.Offset / 2), 11 | PaddingTop = UDim.new(props.paddingHeight.Scale / 2, props.paddingHeight.Offset / 2), 12 | 13 | PaddingLeft = UDim.new(props.paddingWidth.Scale / 2, props.paddingWidth.Offset / 2), 14 | PaddingRight = UDim.new(props.paddingWidth.Scale / 2, props.paddingWidth.Offset / 2), 15 | }) 16 | end 17 | -------------------------------------------------------------------------------- /Source/Client/Interface/Components/UIStroke.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | 5 | local Theme = require(Package.Client.Interface.Theme) 6 | 7 | return function(scope: FusionTypes.Scope, props: { 8 | transparency: number? 9 | }) 10 | return scope:New("UIStroke")({ 11 | Thickness = Theme.UIStrokeThickness, 12 | Color = Theme.AccentColor3, 13 | Transparency = props.transparency or 0 14 | }) 15 | end 16 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/ContentPanel/Content.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | 5 | local Theme = require(Package.Client.Interface.Theme) 6 | 7 | local Fusion = require(Package.Parent.Fusion) 8 | 9 | local lastScope 10 | local lastFrame 11 | local lastRender 12 | 13 | return function(scope: FusionTypes.Scope, props: { 14 | [any]: any 15 | }): Frame 16 | table.insert(scope, function() 17 | if lastScope then 18 | Fusion.doCleanup(lastScope) 19 | end 20 | end) 21 | 22 | return scope:New("Frame")({ 23 | Size = UDim2.fromScale(1, 0.9), 24 | Position = UDim2.fromScale(0, 0.1), 25 | 26 | BorderSizePixel = 0, 27 | 28 | BackgroundColor3 = Theme.BaseBackgroundColor3, 29 | 30 | [Fusion.Children] = { 31 | scope:Computed(function(use) 32 | local frame = use(props.selectedPage) 33 | 34 | if lastFrame == frame then 35 | return lastRender 36 | end 37 | 38 | lastFrame = frame 39 | 40 | local contentScope = Fusion.scoped(Fusion) 41 | 42 | if lastScope then 43 | Fusion.doCleanup(lastScope) 44 | end 45 | 46 | lastScope = contentScope 47 | 48 | if frame.CustomPage then 49 | table.insert(lastScope, frame.CleanupCallback) 50 | 51 | return frame.RenderCallback(contentScope, props) 52 | else 53 | lastRender = frame and frame.Render(contentScope, props) or nil 54 | 55 | return lastRender 56 | end 57 | end), 58 | } 59 | }) 60 | end 61 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/ContentPanel/Topbar.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | 5 | local Theme = require(Package.Client.Interface.Theme) 6 | 7 | local UIPadding = require(Package.Client.Interface.Components.UIPadding) 8 | 9 | local TextLabel = require(Package.Client.Interface.Components.TextLabel) 10 | local ImageButton = require(Package.Client.Interface.Components.ImageButton) 11 | 12 | local Fusion = require(Package.Parent.Fusion) 13 | 14 | return function(scope: FusionTypes.Scope, props: { 15 | selectedPage: FusionTypes.Value, 16 | 17 | minimiseWindowRequested: () -> (), 18 | }): Frame 19 | return scope:New("Frame")({ 20 | Size = UDim2.fromScale(1, 0.1), 21 | Position = UDim2.fromScale(0, 0), 22 | 23 | BackgroundColor3 = Theme.BaseTopbarColor3, 24 | 25 | [Fusion.Children] = { 26 | UIPadding = UIPadding(scope, { 27 | paddingHeight = UDim.new(0, 0), 28 | paddingWidth = UDim.new(0.05, 0) 29 | }), 30 | 31 | HeaderLabel = TextLabel(scope, { 32 | TextXAlignment = Enum.TextXAlignment.Left, 33 | Text = scope:Computed(function(use) 34 | local frame = scope.peek(props.selectedPage) 35 | 36 | use(props.selectedPage) 37 | 38 | return `● Status: {frame and frame.Properties.DisplayName or `No Page Selected`}` 39 | end), 40 | 41 | FontFace = Theme.TopbarLabelFont, 42 | TextColor3 = Theme.TopbarLabelColor3, 43 | 44 | [Fusion.Children] = { 45 | UIPadding = UIPadding(scope, { 46 | paddingHeight = UDim.new(0.4, 0), 47 | paddingWidth = UDim.new(0, 0) 48 | }) 49 | } 50 | }), 51 | 52 | ExitButton = ImageButton(scope, { 53 | Image = Theme.MinimiseIcon, 54 | 55 | Size = UDim2.fromScale(1, 0.7), 56 | 57 | Position = UDim2.fromScale(1, 0.5), 58 | AnchorPoint = Vector2.new(1, 0.5), 59 | 60 | [Fusion.OnEvent("Activated")] = function() 61 | props.minimiseWindowRequested() 62 | end, 63 | 64 | [Fusion.Children] = { 65 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}) 66 | } 67 | }) 68 | } 69 | }) 70 | end 71 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/ContentPanel/init.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | 5 | local Theme = require(Package.Client.Interface.Theme) 6 | 7 | local Content = require(Package.Client.Interface.Fusion.ContentPanel.Content) 8 | local Topbar = require(Package.Client.Interface.Fusion.ContentPanel.Topbar) 9 | 10 | local Fusion = require(Package.Parent.Fusion) 11 | 12 | return function(scope: FusionTypes.Scope, props: { 13 | [any]: any 14 | }): Frame 15 | return scope:New("Frame")({ 16 | Size = UDim2.fromScale(0.8, 1), 17 | Position = UDim2.fromScale(0.2, 0), 18 | 19 | BorderSizePixel = 0, 20 | 21 | BackgroundColor3 = Theme.BaseBackgroundColor3, 22 | 23 | [Fusion.Children] = { 24 | Content = Content(scope, props), 25 | Topbar = Topbar(scope, props) 26 | } 27 | }) 28 | end 29 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Indicator.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local Fusion = require(Package.Parent.Fusion) 4 | 5 | local FusionTypes = require(Package.Types.Fusion) 6 | local SignalTypes = require(Package.Types.Signal) 7 | 8 | local UICorner = require(Package.Client.Interface.Components.UICorner) 9 | local UIStroke = require(Package.Client.Interface.Components.UIStroke) 10 | local UIPadding = require(Package.Client.Interface.Components.UIPadding) 11 | 12 | local ImageButton = require(Package.Client.Interface.Components.ImageButton) 13 | 14 | local Theme = require(Package.Client.Interface.Theme) 15 | 16 | return function(scope: FusionTypes.Scope, props: { 17 | callbacks: { 18 | showIndicator: FusionTypes.Value<() -> ()>, 19 | hideIndicator: FusionTypes.Value<() -> ()>, 20 | }, 21 | 22 | signals: { 23 | openWindow: SignalTypes.Signal<()>, 24 | } 25 | }): ScreenGui 26 | local goalPosition: FusionTypes.Value = scope:Value(0) 27 | local tween: FusionTypes.Tween = scope:Tween(goalPosition, TweenInfo.new(0.15, Enum.EasingStyle.Quad)) 28 | 29 | props.callbacks.showIndicator:set(function() 30 | goalPosition:set(1) 31 | end) 32 | 33 | props.callbacks.hideIndicator:set(function() 34 | goalPosition:set(0) 35 | end) 36 | 37 | return scope:New("ScreenGui")({ 38 | IgnoreGuiInset = true, 39 | ClipToDeviceSafeArea = false, 40 | SafeAreaCompatibility = Enum.SafeAreaCompatibility.None, 41 | ScreenInsets = Enum.ScreenInsets.None, 42 | 43 | DisplayOrder = math.huge, 44 | ResetOnSpawn = false, 45 | ZIndexBehavior = Enum.ZIndexBehavior.Sibling, 46 | 47 | [Fusion.Children] = { 48 | ContentFrame = scope:New("CanvasGroup")({ 49 | AnchorPoint = Vector2.new(0.5, 0.5), 50 | 51 | Size = scope:Computed(function(use) 52 | local value = use(tween) 53 | 54 | return UDim2.fromOffset(24, 24):Lerp(UDim2.fromOffset(32, 32), value) 55 | end), 56 | 57 | Position = UDim2.fromScale(0.5, 0.1), 58 | 59 | BackgroundColor3 = Theme.BaseBackgroundColor3, 60 | 61 | [Fusion.Children] = { 62 | UICorner = UICorner(scope, {}), 63 | 64 | UIStroke = UIStroke(scope, { 65 | transparency = scope:Computed(function(use) 66 | local value = use(tween) 67 | 68 | return 1 - value 69 | end) 70 | }), 71 | 72 | UIPadding = UIPadding(scope, { 73 | paddingWidth = UDim.new(0.025, 0), 74 | paddingHeight = UDim.new(0.025, 0) 75 | }), 76 | 77 | UIGradient = scope:New("UIGradient")({ 78 | Transparency = scope:Computed(function(use) 79 | local value = use(tween) 80 | 81 | return NumberSequence.new({ 82 | NumberSequenceKeypoint.new(0, 1 - value), 83 | NumberSequenceKeypoint.new(1, 1 - value) 84 | }) 85 | end), 86 | }), 87 | 88 | OpenButton = ImageButton(scope, { 89 | Image = Theme.DevSuiteIcon, 90 | ImageColor3 = Theme.BaseIconColor3, 91 | ImageTransparency = 0.5, 92 | 93 | Size = UDim2.fromScale(0.75, 0.75), 94 | 95 | AnchorPoint = Vector2.new(0.5, 0.5), 96 | Position = UDim2.fromScale(0.5, 0.5), 97 | 98 | [Fusion.OnEvent("Activated")] = function() 99 | props.signals.openWindow:Fire() 100 | end, 101 | 102 | [Fusion.Children] = { 103 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}) 104 | } 105 | }) 106 | } 107 | }) 108 | } 109 | }) 110 | end 111 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Indicator.story.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local Fusion = require(Package.Parent.Fusion) 4 | local Signal = require(Package.Parent.Signal) 5 | 6 | local Indicator = require(Package.Client.Interface.Fusion.Indicator) 7 | 8 | return function(parent) 9 | local scope = Fusion.scoped(Fusion) 10 | 11 | local showIndicatorValue = scope:Value(nil) 12 | 13 | local windowObject = Indicator(scope, { 14 | callbacks = { 15 | showIndicator = showIndicatorValue, 16 | hideIndicator = scope:Value(nil), 17 | }, 18 | 19 | signals = { 20 | openWindow = Signal.new() 21 | } 22 | }) 23 | 24 | task.delay(0.1, function() 25 | Fusion.peek(showIndicatorValue)() 26 | end) 27 | 28 | windowObject.Parent = parent 29 | 30 | return function() 31 | windowObject:Destroy() 32 | 33 | Fusion.doCleanup(scope) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/Actions/init.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local SignalTypes = require(Package.Types.Signal) 4 | local FusionTypes = require(Package.Types.Fusion) 5 | local PageTypes = require(Package.Types.Page) 6 | 7 | local UICorner = require(Package.Client.Interface.Components.UICorner) 8 | local UIPadding = require(Package.Client.Interface.Components.UIPadding) 9 | local ActionList = require(Package.Client.Interface.Fusion.Pages.Actions.ActionList) 10 | local ActionExecution = require(Package.Client.Interface.Fusion.Pages.Actions.ActionExecution) 11 | 12 | local Theme = require(Package.Client.Interface.Theme) 13 | 14 | local Fusion = require(Package.Parent.Fusion) 15 | 16 | local Actions = {} 17 | 18 | Actions.DisplayName = `Actions` 19 | Actions.DisplayOrder = 6 20 | Actions.DisplayIcon = Theme.ActionsIcon 21 | Actions.Enabled = true 22 | 23 | function Actions.Render(scope: Scope, props: Props) 24 | local focusedAction = scope:Value(nil) 25 | 26 | return scope:New("Frame")({ 27 | Size = UDim2.fromScale(1, 1), 28 | 29 | BackgroundTransparency = 1, 30 | 31 | [Fusion.Children] = { 32 | UIPadding = UIPadding(scope, { 33 | paddingHeight = UDim.new(0.05, 0), 34 | paddingWidth = UDim.new(0.03, 0) 35 | }), 36 | 37 | ActionsPanel = scope:New("Frame")({ 38 | Size = UDim2.fromScale(0.45, 1), 39 | BackgroundColor3 = Theme.BaseTopbarColor3, 40 | 41 | [Fusion.Children] = { 42 | UICorner = UICorner(scope, {}), 43 | 44 | Background = scope:New("Frame")({ 45 | Size = UDim2.fromScale(0.5, 1), 46 | BackgroundColor3 = Theme.BaseTopbarColor3, 47 | }), 48 | 49 | [Fusion.Children] = { 50 | Content = scope:New("Frame")({ 51 | Size = UDim2.fromScale(1, 1), 52 | BackgroundTransparency = 1, 53 | ClipsDescendants = true, 54 | 55 | [Fusion.Children] = scope:Computed(function(use) 56 | local actions = use(props.actions.actions) 57 | 58 | use(props.actions.closedFolders) 59 | 60 | return ActionList.Render(scope, actions, { 61 | selectedAction = focusedAction, 62 | closedFolders = props.actions.closedFolders, 63 | }) 64 | end) 65 | }) 66 | } 67 | } 68 | }), 69 | 70 | ActionExecutionPanel = scope:New("Frame")({ 71 | Size = UDim2.fromScale(0.535, 1), 72 | Position = UDim2.fromScale(0.465, 0), 73 | BackgroundColor3 = Theme.BaseTopbarColor3, 74 | 75 | [Fusion.Children] = { 76 | UICorner = UICorner(scope, {}), 77 | 78 | Content = scope:New("Frame")({ 79 | Size = UDim2.fromScale(1, 1), 80 | BackgroundTransparency = 1, 81 | ClipsDescendants = true, 82 | 83 | [Fusion.Children] = scope:Computed(function(use) 84 | use(focusedAction) 85 | 86 | return ActionExecution.Render(scope, { 87 | selectedAction = focusedAction, 88 | closedFolders = props.actions.closedFolders 89 | }, props.signals.actions.executeAction) 90 | end) 91 | }) 92 | }, 93 | }), 94 | } 95 | }) 96 | end 97 | 98 | type Scope = FusionTypes.Scope 99 | type Props = { 100 | actions: { 101 | closedFolders: FusionTypes.Value<{ string }>, 102 | 103 | actions: { 104 | { 105 | path: { string }, 106 | name: string, 107 | description: string, 108 | 109 | uuid: string, 110 | context: "Server" | "Client", 111 | 112 | arguments: { 113 | { 114 | type: string, 115 | name: string, 116 | default: string, 117 | } 118 | } 119 | } 120 | } 121 | }, 122 | 123 | signals: { 124 | actions: { 125 | executeAction: SignalTypes.Signal 126 | }, 127 | } 128 | } 129 | 130 | return { 131 | Render = Actions.Render, 132 | Properties = { 133 | DisplayOrder = Actions.DisplayOrder, 134 | DisplayName = Actions.DisplayName, 135 | DisplayIcon = Actions.DisplayIcon, 136 | 137 | Enabled = Actions.Enabled, 138 | } 139 | } :: PageTypes.PageElement 140 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/Dashboard.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | local PageTypes = require(Package.Types.Page) 5 | 6 | local Theme = require(Package.Client.Interface.Theme) 7 | 8 | local ImageLabel = require(Package.Client.Interface.Components.ImageLabel) 9 | local TextLabel = require(Package.Client.Interface.Components.TextLabel) 10 | 11 | local Fusion = require(Package.Parent.Fusion) 12 | 13 | local Dashboard = {} 14 | 15 | Dashboard.DisplayName = `Dashboard` 16 | Dashboard.DisplayOrder = 1 17 | Dashboard.DisplayIcon = Theme.DashboardIcon 18 | Dashboard.Enabled = true 19 | Dashboard.DefaultPage = true 20 | 21 | function Dashboard.Render(scope: Scope, _props: Props) 22 | return scope:New("Frame")({ 23 | Size = UDim2.fromScale(1, 1), 24 | 25 | BackgroundTransparency = 1, 26 | 27 | [Fusion.Children] = { 28 | ImageLabel = ImageLabel(scope, { 29 | Image = Theme.DashboardIcon, 30 | Size = UDim2.fromScale(0.3, 0.3), 31 | Position = UDim2.fromScale(0.5, 0.3), 32 | AnchorPoint = Vector2.new(0.5, 0.5), 33 | }), 34 | 35 | Label1 = TextLabel(scope, { 36 | Size = UDim2.fromScale(0.4, 0.2), 37 | Position = UDim2.fromScale(0.5, 0.6), 38 | AnchorPoint = Vector2.new(0.5, 0.5), 39 | 40 | Text = `Welcome to the Dev-Suite.` 41 | }), 42 | 43 | Label2 = TextLabel(scope, { 44 | Size = UDim2.fromScale(0.7, 0.1), 45 | Position = UDim2.fromScale(0.5, 0.9), 46 | AnchorPoint = Vector2.new(0.5, 0.5), 47 | 48 | TextTransparency = 0.5, 49 | 50 | Text = `Click on any of the side-bar buttons to get started.` 51 | }) 52 | } 53 | }) 54 | end 55 | 56 | type Scope = FusionTypes.Scope 57 | type Props = {} 58 | 59 | return { 60 | Render = Dashboard.Render, 61 | Properties = { 62 | DisplayOrder = Dashboard.DisplayOrder, 63 | DisplayName = Dashboard.DisplayName, 64 | DisplayIcon = Dashboard.DisplayIcon, 65 | 66 | Enabled = Dashboard.Enabled, 67 | DefaultPage = Dashboard.DefaultPage 68 | } 69 | } :: PageTypes.PageElement 70 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/Datastores.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | local PageTypes = require(Package.Types.Page) 5 | 6 | local Theme = require(Package.Client.Interface.Theme) 7 | 8 | local Fusion = require(Package.Parent.Fusion) 9 | 10 | local Datastores = {} 11 | 12 | Datastores.DisplayName = `Datastores` 13 | Datastores.DisplayOrder = 11 14 | Datastores.DisplayIcon = Theme.DatastoresIcon 15 | Datastores.Enabled = false 16 | 17 | function Datastores.Render(scope: Scope, _props: Props) 18 | return scope:New("Frame")({ 19 | Size = UDim2.fromScale(1, 1), 20 | 21 | BackgroundTransparency = 1, 22 | 23 | [Fusion.Children] = { 24 | 25 | } 26 | }) 27 | end 28 | 29 | type Scope = FusionTypes.Scope 30 | type Props = {} 31 | 32 | return { 33 | Render = Datastores.Render, 34 | Properties = { 35 | DisplayOrder = Datastores.DisplayOrder, 36 | DisplayName = Datastores.DisplayName, 37 | DisplayIcon = Datastores.DisplayIcon, 38 | 39 | Enabled = Datastores.Enabled, 40 | } 41 | } :: PageTypes.PageElement 42 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/Debugger/init.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local Sift = require(Package.Parent.Sift) 4 | 5 | local FusionTypes = require(Package.Types.Fusion) 6 | local PageTypes = require(Package.Types.Page) 7 | 8 | local Theme = require(Package.Client.Interface.Theme) 9 | 10 | local ListFrame = require(Package.Client.Interface.Fusion.Pages.Debugger.ListFrame) 11 | local ThreadFrame = require(Package.Client.Interface.Fusion.Pages.Debugger.ThreadFrame) 12 | 13 | local Fusion = require(Package.Parent.Fusion) 14 | 15 | local Embedded = {} 16 | 17 | Embedded.DisplayName = `Debugger` 18 | Embedded.DisplayOrder = 3 19 | Embedded.DisplayIcon = Theme.DebuggerIcon 20 | Embedded.Enabled = true 21 | 22 | function Embedded.Render(scope: Scope, props: Props) 23 | local selectedThread = scope:Value() 24 | 25 | return scope:New("Frame")({ 26 | Size = UDim2.fromScale(1, 1), 27 | 28 | BackgroundTransparency = 1, 29 | 30 | [Fusion.Children] = scope:Computed(function(use) 31 | local threadValue = use(selectedThread) 32 | 33 | if threadValue then 34 | return ThreadFrame.Render(scope, Sift.Dictionary.merge({ 35 | selectedThread = selectedThread 36 | }, props)) 37 | else 38 | return ListFrame.Render(scope, Sift.Dictionary.merge({ 39 | selectedThread = selectedThread 40 | }, props)) 41 | end 42 | end) 43 | }) 44 | end 45 | 46 | type Scope = FusionTypes.Scope 47 | type Props = {} 48 | 49 | return { 50 | Render = Embedded.Render, 51 | Properties = { 52 | DisplayOrder = Embedded.DisplayOrder, 53 | DisplayName = Embedded.DisplayName, 54 | DisplayIcon = Embedded.DisplayIcon, 55 | 56 | Enabled = Embedded.Enabled, 57 | } 58 | } :: PageTypes.PageElement 59 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/Embedded.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | local PageTypes = require(Package.Types.Page) 5 | 6 | local Theme = require(Package.Client.Interface.Theme) 7 | 8 | local Fusion = require(Package.Parent.Fusion) 9 | 10 | local Embedded = {} 11 | 12 | Embedded.DisplayName = `Embedded` 13 | Embedded.DisplayOrder = 12 14 | Embedded.DisplayIcon = Theme.EmbeddedIcon 15 | Embedded.Enabled = false 16 | 17 | function Embedded.Render(scope: Scope, _props: Props) 18 | return scope:New("Frame")({ 19 | Size = UDim2.fromScale(1, 1), 20 | 21 | BackgroundTransparency = 1, 22 | 23 | [Fusion.Children] = { 24 | 25 | } 26 | }) 27 | end 28 | 29 | type Scope = FusionTypes.Scope 30 | type Props = {} 31 | 32 | return { 33 | Render = Embedded.Render, 34 | Properties = { 35 | DisplayOrder = Embedded.DisplayOrder, 36 | DisplayName = Embedded.DisplayName, 37 | DisplayIcon = Embedded.DisplayIcon, 38 | 39 | Enabled = Embedded.Enabled, 40 | } 41 | } :: PageTypes.PageElement 42 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/Explorer/Explorer.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | 5 | local ImageLabel = require(Package.Client.Interface.Components.ImageLabel) 6 | local ImageButton = require(Package.Client.Interface.Components.ImageButton) 7 | local TextLabel = require(Package.Client.Interface.Components.TextLabel) 8 | 9 | local Theme = require(Package.Client.Interface.Theme) 10 | 11 | local Fusion = require(Package.Parent.Fusion) 12 | local ClassIndex = require(Package.Parent.ClassIndex) 13 | 14 | local expandedInstances = { [workspace] = true } 15 | local instanceLabels = {} 16 | local selectedInstance 17 | 18 | local Explorer = {} 19 | 20 | local function processInstanceLabel(scope: FusionTypes.Scope, object: Instance, depth: number) 21 | local imageData = ClassIndex.GetClassIcon(object.ClassName) 22 | local childrenCount = #object:GetChildren() 23 | 24 | local tabButton 25 | local frame 26 | 27 | local isReadyForEvent 28 | 29 | tabButton = ImageButton(scope, { 30 | Size = UDim2.fromScale(1, 1), 31 | Image = Theme.TabIcon, 32 | ImageTransparency = childrenCount <= 0 and 1 or 0, 33 | 34 | Rotation = expandedInstances[object] and 90 or 0, 35 | 36 | [Fusion.OnEvent("Activated")] = function() 37 | expandedInstances[object] = not expandedInstances[object] 38 | 39 | tabButton.Rotation = expandedInstances[object] and 90 or 0 40 | end, 41 | 42 | [Fusion.Children] = { 43 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}) 44 | } 45 | }) 46 | 47 | frame = scope:New("Frame")({ 48 | Size = UDim2.new(1, 0, 0, 20), 49 | AutomaticSize = Enum.AutomaticSize.X, 50 | BackgroundTransparency = 0, 51 | BackgroundColor3 = Theme.BaseTopbarColor3, 52 | 53 | [Fusion.OnEvent("InputBegan")] = function(inputObject: InputObject) 54 | if inputObject.UserInputType == Enum.UserInputType.MouseButton1 or inputObject.UserInputType == Enum.UserInputType.Touch then 55 | isReadyForEvent = true 56 | end 57 | end, 58 | 59 | [Fusion.OnEvent("InputEnded")] = function(inputObject: InputObject) 60 | if inputObject.UserInputType == Enum.UserInputType.MouseButton1 or inputObject.UserInputType == Enum.UserInputType.Touch then 61 | if not isReadyForEvent then 62 | return 63 | end 64 | 65 | selectedInstance:set(object) 66 | end 67 | end, 68 | 69 | [Fusion.Children] = { 70 | UIListLayout = scope:New("UIListLayout")({ 71 | SortOrder = Enum.SortOrder.LayoutOrder, 72 | FillDirection = Enum.FillDirection.Horizontal, 73 | VerticalAlignment = Enum.VerticalAlignment.Center, 74 | 75 | Padding = UDim.new(0.025, 0), 76 | }), 77 | 78 | PaddingLeft = scope:New("Frame")({ 79 | Size = UDim2.fromScale(0.1 * depth, 1), 80 | BackgroundTransparency = 1, 81 | LayoutOrder = 1 82 | }), 83 | 84 | Tab = scope:New("Frame")({ 85 | Size = UDim2.fromScale(0.5, 0.5), 86 | BackgroundTransparency = 1, 87 | LayoutOrder = 2, 88 | 89 | [Fusion.Children] = { 90 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}), 91 | 92 | Tab = tabButton 93 | } 94 | }), 95 | 96 | Icon = ImageLabel(scope, { 97 | Size = UDim2.fromScale(0.75, 0.75), 98 | Image = imageData.Image, 99 | ImageRectOffset = imageData.ImageRectOffset, 100 | ImageRectSize = imageData.ImageRectSize, 101 | LayoutOrder = 3, 102 | 103 | [Fusion.Children] = { 104 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}) 105 | } 106 | }), 107 | 108 | Label = TextLabel(scope, { 109 | Size = UDim2.fromScale(1, 0.75), 110 | Text = object.Name, 111 | TextXAlignment = Enum.TextXAlignment.Left, 112 | LayoutOrder = 4, 113 | }) 114 | } 115 | }) 116 | 117 | return frame 118 | end 119 | 120 | function Explorer.ProcessInstance(scope: FusionTypes.Scope, object: Instance, scrollingFrame: ScrollingFrame, depth: number) 121 | local children = object:GetChildren() 122 | local order = #scrollingFrame:GetChildren() 123 | local isSelected = Fusion.peek(selectedInstance) == object 124 | 125 | if object:HasTag("_DEBUG_IGNORE") then 126 | return 127 | end 128 | 129 | local label = instanceLabels[object] or processInstanceLabel(scope, object, depth) 130 | 131 | label.LayoutOrder = order 132 | 133 | if isSelected then 134 | label.BackgroundColor3 = Theme.AccentColor3 135 | else 136 | label.BackgroundColor3 = Theme.BaseTopbarColor3 137 | end 138 | 139 | if not instanceLabels[object] then 140 | instanceLabels[object] = label 141 | end 142 | 143 | label.Parent = scrollingFrame 144 | 145 | if expandedInstances[object] then 146 | for _, child in children do 147 | Explorer.ProcessInstance(scope, child, scrollingFrame, depth + 1) 148 | end 149 | end 150 | end 151 | 152 | function Explorer.Reset() 153 | instanceLabels = {} 154 | end 155 | 156 | function Explorer.Setup(scope: FusionTypes.Scope, value: FusionTypes.Value) 157 | selectedInstance = value 158 | 159 | scope:Observer(value):onChange(function() 160 | local instance = Fusion.peek(value) 161 | 162 | if instance then 163 | local parent = instance.Parent 164 | 165 | while parent ~= game do 166 | expandedInstances[parent] = true 167 | 168 | parent = parent.Parent 169 | end 170 | end 171 | end) 172 | end 173 | 174 | function Explorer.Render(scope: FusionTypes.Scope, instances: { Instance }, scrollingFrame: ScrollingFrame) 175 | for _, scrollingFrameObject in scrollingFrame:GetChildren() do 176 | if scrollingFrameObject:IsA("UIListLayout") then 177 | continue 178 | end 179 | 180 | scrollingFrameObject.Parent = nil 181 | end 182 | 183 | for _, object in instances do 184 | Explorer.ProcessInstance(scope, object, scrollingFrame, 0) 185 | end 186 | end 187 | 188 | return Explorer 189 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/Explorer/init.luau: -------------------------------------------------------------------------------- 1 | local RunService = game:GetService("RunService") 2 | 3 | local Package = script.Parent.Parent.Parent.Parent.Parent 4 | 5 | local FusionTypes = require(Package.Types.Fusion) 6 | local PageTypes = require(Package.Types.Page) 7 | 8 | local UIPadding = require(Package.Client.Interface.Components.UIPadding) 9 | local UICorner = require(Package.Client.Interface.Components.UICorner) 10 | local Button = require(Package.Client.Interface.Components.Button) 11 | local ImageLabel = require(Package.Client.Interface.Components.ImageLabel) 12 | 13 | local UIExplorer = require(Package.Client.Interface.Fusion.Pages.Explorer.Explorer) 14 | local UIProperties = require(Package.Client.Interface.Fusion.Pages.Explorer.Properties) 15 | 16 | local Theme = require(Package.Client.Interface.Theme) 17 | 18 | local Fusion = require(Package.Parent.Fusion) 19 | 20 | local Explorer = {} 21 | 22 | Explorer.DisplayName = `Explorer` 23 | Explorer.DisplayOrder = 6 24 | Explorer.DisplayIcon = Theme.ExplorerIcon 25 | Explorer.Enabled = true 26 | 27 | function Explorer.Render(scope: Scope, props: Props) 28 | local frameCount = 0 29 | 30 | local explorerScrollingFrame = scope:New("ScrollingFrame")({ 31 | Size = UDim2.fromScale(1, 1), 32 | CanvasSize = UDim2.fromScale(0, 0), 33 | AutomaticCanvasSize = Enum.AutomaticSize.XY, 34 | ScrollBarThickness = 3, 35 | MidImage = Theme.MiddleScrollingFrameIcon, 36 | TopImage = Theme.TopScrollingFrameIcon, 37 | BottomImage = Theme.BottomScrollingFrameIcon, 38 | ScrollBarImageColor3 = Theme.AccentColor3, 39 | ZIndex = 2, 40 | ClipsDescendants = true, 41 | 42 | BackgroundTransparency = 1, 43 | 44 | [Fusion.Children] = { 45 | scope:New("UIListLayout")({ 46 | SortOrder = Enum.SortOrder.LayoutOrder 47 | }) 48 | } 49 | }) 50 | 51 | local propertiesScrollingFrame = scope:New("ScrollingFrame")({ 52 | Size = UDim2.fromScale(1, 1), 53 | CanvasSize = UDim2.fromScale(0, 0), 54 | AutomaticCanvasSize = Enum.AutomaticSize.XY, 55 | ScrollBarThickness = 3, 56 | MidImage = Theme.MiddleScrollingFrameIcon, 57 | TopImage = Theme.TopScrollingFrameIcon, 58 | BottomImage = Theme.BottomScrollingFrameIcon, 59 | ScrollBarImageColor3 = Theme.AccentColor3, 60 | ZIndex = 2, 61 | ClipsDescendants = true, 62 | 63 | BackgroundTransparency = 1, 64 | 65 | [Fusion.Children] = { 66 | scope:New("UIListLayout")({ 67 | SortOrder = Enum.SortOrder.LayoutOrder 68 | }) 69 | } 70 | }) 71 | 72 | UIExplorer.Setup(scope, props.explorer.selectedInstance) 73 | UIProperties.Setup(scope, props.explorer.selectedInstance) 74 | 75 | table.insert(scope, RunService.Heartbeat:Connect(function() 76 | frameCount += 1 77 | 78 | if frameCount % 5 ~= 0 then 79 | return 80 | end 81 | 82 | frameCount = 0 83 | 84 | UIExplorer.Render(scope, props.explorer.instances, explorerScrollingFrame) 85 | UIProperties.Render(scope, propertiesScrollingFrame) 86 | end)) 87 | 88 | table.insert(scope, function() 89 | UIExplorer.Reset() 90 | UIProperties.Stop(scope) 91 | end) 92 | 93 | return scope:New("Frame")({ 94 | Size = UDim2.fromScale(1, 1), 95 | 96 | BackgroundTransparency = 1, 97 | 98 | [Fusion.Children] = { 99 | UIPadding = UIPadding(scope, { 100 | paddingHeight = UDim.new(0.045, 0), 101 | paddingWidth = UDim.new(0.025, 0) 102 | }), 103 | 104 | ExplorerPanel = scope:New("Frame")({ 105 | Size = UDim2.fromScale(0.425, 1), 106 | 107 | BackgroundColor3 = Theme.BaseTopbarColor3, 108 | 109 | [Fusion.Children] = { 110 | UICorner = UICorner(scope, { 111 | level = 0.5 112 | }), 113 | 114 | BackgroundFrame = scope:New("Frame")({ 115 | Size = UDim2.fromScale(0.5, 1), 116 | 117 | BackgroundColor3 = Theme.BaseTopbarColor3, 118 | }), 119 | 120 | ContentFrame = explorerScrollingFrame 121 | } 122 | }), 123 | 124 | PropertiesPanel = scope:New("Frame")({ 125 | Size = UDim2.fromScale(0.425, 1), 126 | Position = UDim2.fromScale(0.44, 0), 127 | 128 | BackgroundColor3 = Theme.BaseTopbarColor3, 129 | 130 | [Fusion.Children] = { 131 | UICorner = UICorner(scope, { 132 | level = 0.5 133 | }), 134 | 135 | BackgroundFrame = scope:New("Frame")({ 136 | Size = UDim2.fromScale(0.5, 1), 137 | 138 | BackgroundColor3 = Theme.BaseTopbarColor3, 139 | }), 140 | 141 | ContentFrame = propertiesScrollingFrame 142 | } 143 | }), 144 | 145 | SettingPanel = scope:New("Frame")({ 146 | Size = UDim2.fromScale(0.12, 1), 147 | Position = UDim2.fromScale(0.88, 0), 148 | 149 | BackgroundColor3 = Theme.BaseTopbarColor3, 150 | 151 | [Fusion.Children] = { 152 | UICorner = UICorner(scope, { 153 | level = 0.5 154 | }), 155 | 156 | UIListLayout = scope:New("UIListLayout")({ 157 | SortOrder = Enum.SortOrder.LayoutOrder 158 | }), 159 | 160 | UIPadding = UIPadding(scope, { 161 | paddingHeight = UDim.new(0.025, 0), 162 | paddingWidth = UDim.new(0.2, 0) 163 | }), 164 | 165 | ClickToSelectButton = Button(scope, { 166 | Size = UDim2.fromScale(1, 1), 167 | BackgroundTransparency = 0, 168 | BackgroundColor3 = Theme.BaseSidebarColor3, 169 | 170 | [Fusion.OnEvent("Activated")] = function() 171 | props.explorer.clickToSelect:set(not Fusion.peek(props.explorer.clickToSelect)) 172 | end, 173 | 174 | [Fusion.Children] = { 175 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}), 176 | 177 | UICorner = UICorner(scope, { 178 | level = 0.5 179 | }), 180 | 181 | UIPadding = UIPadding(scope, { 182 | paddingHeight = UDim.new(0.5, 0), 183 | paddingWidth = UDim.new(0.5, 0) 184 | }), 185 | 186 | ImageLabe = ImageLabel(scope, { 187 | Image = Theme.PointerIcon, 188 | ImageTransparency = scope:Computed(function(use) 189 | local isEnabled = use(props.explorer.clickToSelect) 190 | 191 | return isEnabled and 0 or 0.5 192 | end), 193 | }) 194 | } 195 | }), 196 | 197 | ShowSelectionButton = Button(scope, { 198 | Size = UDim2.fromScale(1, 1), 199 | BackgroundTransparency = 0, 200 | BackgroundColor3 = Theme.BaseSidebarColor3, 201 | 202 | [Fusion.OnEvent("Activated")] = function() 203 | props.explorer.showSelection:set(not Fusion.peek(props.explorer.showSelection)) 204 | end, 205 | 206 | [Fusion.Children] = { 207 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}), 208 | 209 | UICorner = UICorner(scope, { 210 | level = 0.5 211 | }), 212 | 213 | UIPadding = UIPadding(scope, { 214 | paddingHeight = UDim.new(0.5, 0), 215 | paddingWidth = UDim.new(0.5, 0) 216 | }), 217 | 218 | ImageLabe = ImageLabel(scope, { 219 | Image = Theme.SelectionIcon, 220 | ImageTransparency = scope:Computed(function(use) 221 | local isEnabled = use(props.explorer.showSelection) 222 | 223 | return isEnabled and 0 or 0.5 224 | end), 225 | }) 226 | } 227 | }) 228 | } 229 | }) 230 | } 231 | }) 232 | end 233 | 234 | type Scope = FusionTypes.Scope 235 | type Props = { 236 | explorer: { 237 | instances: { Instance }, 238 | 239 | clickToSelect: FusionTypes.Value, 240 | showSelection: FusionTypes.Value, 241 | 242 | selectedInstance: FusionTypes.Value, 243 | }, 244 | } 245 | 246 | return { 247 | Render = Explorer.Render, 248 | Properties = { 249 | DisplayOrder = Explorer.DisplayOrder, 250 | DisplayName = Explorer.DisplayName, 251 | DisplayIcon = Explorer.DisplayIcon, 252 | 253 | Enabled = Explorer.Enabled, 254 | } 255 | } :: PageTypes.PageElement 256 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/Inspector.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | local PageTypes = require(Package.Types.Page) 5 | local Repr = require(Package.Vendor.Repr) 6 | 7 | local Highlighter = require(Package.Parent.Highlighter) 8 | local Sift = require(Package.Parent.Sift) 9 | 10 | local TextLabel = require(Package.Client.Interface.Components.TextLabel) 11 | local UICorner = require(Package.Client.Interface.Components.UICorner) 12 | local UIPadding = require(Package.Client.Interface.Components.UIPadding) 13 | 14 | local Theme = require(Package.Client.Interface.Theme) 15 | 16 | local Fusion = require(Package.Parent.Fusion) 17 | 18 | local Inspector = {} 19 | 20 | Inspector.DisplayName = `Inspector` 21 | Inspector.DisplayOrder = 7 22 | Inspector.DisplayIcon = Theme.InspectorIcon 23 | Inspector.Enabled = true 24 | 25 | function Inspector.Render(scope: Scope, props: Props) 26 | return scope:New("Frame")({ 27 | Size = UDim2.fromScale(1, 1), 28 | 29 | BackgroundTransparency = 1, 30 | 31 | [Fusion.Children] = { 32 | UICorner = UICorner(scope, {}), 33 | 34 | UIPadding = UIPadding(scope, { 35 | paddingHeight = UDim.new(0, 10), 36 | paddingWidth = UDim.new(0, 10) 37 | }), 38 | 39 | InstanceLabel = scope:New("Frame")({ 40 | Size = UDim2.fromScale(1, 0.09), 41 | BackgroundColor3 = Theme.BaseTopbarColor3, 42 | BackgroundTransparency = 0, 43 | 44 | [Fusion.Children] = { 45 | UIListLayout = scope:New("UIListLayout")({ 46 | SortOrder = Enum.SortOrder.LayoutOrder, 47 | FillDirection = Enum.FillDirection.Horizontal, 48 | VerticalAlignment = Enum.VerticalAlignment.Center, 49 | 50 | Padding = UDim.new(0.025, 0), 51 | }), 52 | 53 | UIPadding = UIPadding(scope, { 54 | paddingHeight = UDim.new(0, 0), 55 | paddingWidth = UDim.new(0, 10) 56 | }), 57 | 58 | Label = scope:Computed(function(use) 59 | local selectedInstance = use(props.explorer.selectedInstance) 60 | 61 | if not selectedInstance or not selectedInstance:IsA("ModuleScript") then 62 | return nil 63 | end 64 | 65 | local name = selectedInstance:GetFullName() 66 | 67 | if #name > 50 then 68 | name = `...{string.sub(name, #name - 50, #name)}` 69 | end 70 | 71 | return TextLabel(scope, { 72 | Size = UDim2.fromScale(1, 0.5), 73 | Text = `{name}`, 74 | TextTransparency = 0.5, 75 | TextXAlignment = Enum.TextXAlignment.Left, 76 | }) 77 | end) 78 | } 79 | }), 80 | 81 | ScrollingFrame = scope:Computed(function(use) 82 | local selectedInstance = use(props.explorer.selectedInstance) 83 | local isValidInstance = selectedInstance and selectedInstance:IsA("ModuleScript") 84 | 85 | local messageLabels = {} 86 | 87 | if not selectedInstance or not selectedInstance:IsA("ModuleScript") then 88 | table.insert(messageLabels, scope:New("Frame")({ 89 | Size = UDim2.new(1, 0, 0, 40), 90 | BackgroundColor3 = Theme.BaseTopbarColor3, 91 | 92 | [Fusion.Children] = { 93 | UIPadding = UIPadding(scope, { 94 | paddingWidth = UDim.new(0.05, 0), 95 | paddingHeight = UDim.new(0.5, 0) 96 | }), 97 | 98 | TextLabel = TextLabel(scope, { 99 | TextTransparency = 0.5, 100 | Text = `Please select a ModuleScript in the Explorer to inspect.`, 101 | }) 102 | } 103 | })) 104 | else 105 | local success, response = pcall(require, selectedInstance) 106 | 107 | if not success then 108 | response = string.gsub(response, "<", "<") 109 | response = string.gsub(response, ">", ">") 110 | 111 | table.insert(messageLabels, scope:New("Frame")({ 112 | Size = UDim2.new(1, 0, 0, 40), 113 | BackgroundColor3 = Theme.BaseTopbarColor3, 114 | 115 | [Fusion.Children] = { 116 | UIPadding = UIPadding(scope, { 117 | paddingWidth = UDim.new(0.05, 0), 118 | paddingHeight = UDim.new(0.5, 0) 119 | }), 120 | 121 | TextLabel = TextLabel(scope, { 122 | TextTransparency = 0.5, 123 | Text = `Module {selectedInstance.Name} could not be loaded.`, 124 | }), 125 | } 126 | })) 127 | 128 | table.insert(messageLabels, scope:New("Frame")({ 129 | Size = UDim2.new(1, 0, 0, 40), 130 | BackgroundColor3 = Theme.BaseTopbarColor3, 131 | 132 | [Fusion.Children] = { 133 | UIPadding = UIPadding(scope, { 134 | paddingWidth = UDim.new(0.05, 0), 135 | paddingHeight = UDim.new(0.5, 0) 136 | }), 137 | 138 | TextLabel = TextLabel(scope, { 139 | TextTransparency = 0.5, 140 | TextColor3 = Theme.ErrorColor3, 141 | Text = response, 142 | }), 143 | } 144 | })) 145 | else 146 | local label = TextLabel(scope, { 147 | TextTransparency = 0.5, 148 | TextXAlignment = Enum.TextXAlignment.Left, 149 | AutomaticSize = Enum.AutomaticSize.XY, 150 | Text = Repr(response, { 151 | pretty = true, 152 | -- tabs = true, 153 | spaces = 8, 154 | }), 155 | TextScaled = false, 156 | TextWrapped = false, 157 | }) 158 | 159 | table.insert(messageLabels, scope:New("Frame")({ 160 | Size = UDim2.new(1, 0, 0, 0), 161 | AutomaticSize = Enum.AutomaticSize.XY, 162 | BackgroundTransparency = 1, 163 | 164 | [Fusion.Children] = { 165 | UIPadding = UIPadding(scope, { 166 | paddingWidth = UDim.new(0, 10), 167 | paddingHeight = UDim.new(0, 10) 168 | }), 169 | 170 | TextLabel = label, 171 | } 172 | })) 173 | 174 | task.defer(function() 175 | Highlighter.highlight({ 176 | textObject = label 177 | }) 178 | end) 179 | end 180 | end 181 | 182 | return scope:New("ScrollingFrame")({ 183 | Size = UDim2.fromScale(1, isValidInstance and 0.89 or 1), 184 | Position = UDim2.fromScale(0, isValidInstance and 0.11 or 0), 185 | BackgroundColor3 = Theme.BaseTopbarColor3, 186 | AutomaticCanvasSize = Enum.AutomaticSize.Y, 187 | CanvasSize = UDim2.fromScale(0, 0), 188 | ClipsDescendants = true, 189 | MidImage = Theme.MiddleScrollingFrameIcon, 190 | TopImage = Theme.TopScrollingFrameIcon, 191 | BottomImage = Theme.BottomScrollingFrameIcon, 192 | ScrollBarImageColor3 = Theme.AccentColor3, 193 | ScrollBarThickness = 3, 194 | 195 | [Fusion.Children] = Sift.Dictionary.merge(messageLabels, { 196 | UIListLayout = scope:New("UIListLayout")({ 197 | SortOrder = Enum.SortOrder.LayoutOrder, 198 | FillDirection = Enum.FillDirection.Vertical, 199 | VerticalAlignment = Enum.VerticalAlignment.Top, 200 | 201 | Padding = UDim.new(0, 10), 202 | }), 203 | }) 204 | }) 205 | end) 206 | } 207 | }) 208 | end 209 | 210 | type Scope = FusionTypes.Scope 211 | type Props = { 212 | explorer: { 213 | instances: { Instance }, 214 | 215 | clickToSelect: FusionTypes.Value, 216 | showSelection: FusionTypes.Value, 217 | 218 | selectedInstance: FusionTypes.Value, 219 | }, 220 | } 221 | 222 | return { 223 | Render = Inspector.Render, 224 | Properties = { 225 | DisplayOrder = Inspector.DisplayOrder, 226 | DisplayName = Inspector.DisplayName, 227 | DisplayIcon = Inspector.DisplayIcon, 228 | 229 | Enabled = Inspector.Enabled, 230 | } 231 | } :: PageTypes.PageElement 232 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/Interceptor/EventList.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent.Parent 2 | 3 | local Fusion = require(Package.Parent.Fusion) 4 | local Sift = require(Package.Parent.Sift) 5 | local ClassIndex = require(Package.Parent.ClassIndex) 6 | 7 | local Button = require(Package.Client.Interface.Components.Button) 8 | local ImageLabel = require(Package.Client.Interface.Components.ImageLabel) 9 | local TextLabel = require(Package.Client.Interface.Components.TextLabel) 10 | local UICorner = require(Package.Client.Interface.Components.UICorner) 11 | local UIPadding = require(Package.Client.Interface.Components.UIPadding) 12 | 13 | local Theme = require(Package.Client.Interface.Theme) 14 | 15 | local FusionTypes = require(Package.Types.Fusion) 16 | 17 | local EventList = {} 18 | 19 | function EventList.Render(scope: FusionTypes.Scope, props: any, focusedEvent: FusionTypes.Value) 20 | return scope:New("ScrollingFrame")({ 21 | Size = UDim2.fromScale(1, 1), 22 | Position = UDim2.fromScale(0, 0), 23 | AutomaticCanvasSize = Enum.AutomaticSize.Y, 24 | CanvasSize = UDim2.fromScale(0, 0), 25 | ScrollBarThickness = 0, 26 | BackgroundTransparency = 1, 27 | ClipsDescendants = true, 28 | 29 | [Fusion.Children] = scope:Computed(function(use) 30 | local interceptedEvents = use(props.interceptor.intercepted) 31 | local interceptedEventLabels = {} 32 | 33 | if #interceptedEvents == 0 then 34 | table.insert(interceptedEventLabels, scope:New("Frame")({ 35 | Size = UDim2.fromScale(1, 0.2), 36 | BackgroundColor3 = Theme.BaseTopbarColor3, 37 | 38 | [Fusion.Children] = { 39 | UIPadding = UIPadding(scope, { 40 | paddingWidth = UDim.new(0.05, 0), 41 | paddingHeight = UDim.new(0.5, 0) 42 | }), 43 | 44 | TextLabel = TextLabel(scope, { 45 | TextTransparency = 0.5, 46 | Text = `No remotes have been intercepted yet, i'd suggest we do something!` 47 | }) 48 | } 49 | })) 50 | end 51 | 52 | for index, event in interceptedEvents do 53 | local iconInformation = ClassIndex.GetClassIcon(event.eventType) 54 | 55 | table.insert(interceptedEventLabels, 1, scope:New("Frame")({ 56 | Size = UDim2.new(1, 0, 0, 35), 57 | BackgroundColor3 = index % 2 == 0 and Theme.BaseTopbarColor3 or Theme.BaseSidebarColor3, 58 | LayoutOrder = index, 59 | 60 | [Fusion.Children] = { 61 | UICorner = UICorner(scope, { }), 62 | 63 | UIPadding = UIPadding(scope, { 64 | paddingWidth = UDim.new(0.05, 0), 65 | paddingHeight = UDim.new(0.5, 0) 66 | }), 67 | 68 | Icon = ImageLabel(scope, { 69 | Size = UDim2.fromScale(1, 1), 70 | Image = iconInformation.Image, 71 | ImageRectOffset = iconInformation.ImageRectOffset, 72 | ImageRectSize = iconInformation.ImageRectSize, 73 | 74 | [Fusion.Children] = { 75 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}) 76 | } 77 | }), 78 | 79 | ArrowIcon0 = ImageLabel(scope, { 80 | Size = UDim2.fromScale(1, 1), 81 | Position = UDim2.fromScale(0.1, 0), 82 | Image = Theme.StraightArrowIcon, 83 | 84 | [Fusion.Children] = { 85 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}) 86 | } 87 | }), 88 | 89 | ContextIcon = ImageLabel(scope, { 90 | Size = UDim2.fromScale(1, 1), 91 | Position = UDim2.fromScale(0.165, 0), 92 | Image = event.context == "Server" and Theme.ServerIcon or Theme.ClientIcon, 93 | ImageColor3 = event.context == "Server" and Theme.ServerColor3 or Theme.ClientColor3, 94 | 95 | [Fusion.Children] = { 96 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}) 97 | } 98 | }), 99 | 100 | ArrowIcon1 = ImageLabel(scope, { 101 | Size = UDim2.fromScale(1, 1), 102 | Position = UDim2.fromScale(0.23, 0), 103 | Image = Theme.StraightArrowIcon, 104 | 105 | [Fusion.Children] = { 106 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}) 107 | } 108 | }), 109 | 110 | RemoteInformation = TextLabel(scope, { 111 | Size = UDim2.fromScale(0.4, 1), 112 | Position = UDim2.fromScale(0.33, 0), 113 | TextTransparency = 0.5, 114 | TextXAlignment = Enum.TextXAlignment.Left, 115 | TextScaled = false, 116 | TextTruncate = Enum.TextTruncate.AtEnd, 117 | 118 | Text = `{event.eventName} - {event.eventSize}`, 119 | 120 | [Fusion.Children] = { 121 | UIPadding = UIPadding(scope, { 122 | paddingWidth = UDim.new(0, 0), 123 | paddingHeight = UDim.new(0.1, 0) 124 | }), 125 | } 126 | }), 127 | 128 | MoreInformationButton = Button(scope, { 129 | Size = UDim2.fromScale(1.4, 1.4), 130 | AnchorPoint = Vector2.new(1, 0.5), 131 | Position = UDim2.fromScale(1.01, 0.5), 132 | BackgroundTransparency = 0, 133 | BackgroundColor3 = index % 2 == 0 and Theme.BaseSidebarColor3 or Theme.BaseTopbarColor3, 134 | 135 | [Fusion.OnEvent("Activated")] = function() 136 | focusedEvent:set(event) 137 | end, 138 | 139 | [Fusion.Children] = { 140 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}), 141 | 142 | UICorner = UICorner(scope, { 143 | level = 0.85, 144 | }), 145 | 146 | UIPadding = UIPadding(scope, { 147 | paddingWidth = UDim.new(0.2, 0), 148 | paddingHeight = UDim.new(0.2, 0) 149 | }), 150 | 151 | QuestionIcon = ImageLabel(scope, { 152 | Size = UDim2.fromScale(1, 1), 153 | Position = UDim2.fromScale(0.165, 0), 154 | Image = Theme.QuestionIcon, 155 | 156 | [Fusion.Children] = { 157 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}) 158 | } 159 | }), 160 | } 161 | }), 162 | 163 | RepeatEventButton = Button(scope, { 164 | Size = UDim2.fromScale(1.4, 1.4), 165 | AnchorPoint = Vector2.new(1, 0.5), 166 | Position = UDim2.fromScale(0.95, 0.5), 167 | BackgroundTransparency = 0, 168 | BackgroundColor3 = index % 2 == 0 and Theme.BaseSidebarColor3 or Theme.BaseTopbarColor3, 169 | Visible = event.context == "Server", 170 | 171 | [Fusion.OnEvent("Activated")] = function() 172 | props.signals.interceptor.repeatEvent:Fire(event.eventUUID) 173 | end, 174 | 175 | [Fusion.Children] = { 176 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}), 177 | 178 | UICorner = UICorner(scope, { 179 | level = 0.85, 180 | }), 181 | 182 | UIPadding = UIPadding(scope, { 183 | paddingWidth = UDim.new(0.3, 0), 184 | paddingHeight = UDim.new(0.3, 0) 185 | }), 186 | 187 | RepeatIcon = ImageLabel(scope, { 188 | Size = UDim2.fromScale(1, 1), 189 | Position = UDim2.fromScale(0.165, 0), 190 | Image = Theme.RepeatIcon, 191 | 192 | [Fusion.Children] = { 193 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}) 194 | } 195 | }), 196 | } 197 | }) 198 | } 199 | })) 200 | end 201 | 202 | return Sift.Dictionary.merge(interceptedEventLabels, { 203 | UIListLayout = scope:New("UIListLayout")({ 204 | SortOrder = Enum.SortOrder.LayoutOrder, 205 | FillDirection = Enum.FillDirection.Vertical, 206 | VerticalAlignment = Enum.VerticalAlignment.Top, 207 | 208 | Padding = UDim.new(0.025, 0), 209 | }), 210 | 211 | UIPadding = UIPadding(scope, { 212 | paddingWidth = UDim.new(0, 10), 213 | paddingHeight = UDim.new(0, 10) 214 | }), 215 | }) 216 | end) 217 | }) 218 | end 219 | 220 | return EventList -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/Interceptor/init.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | local PageTypes = require(Package.Types.Page) 5 | local SignalTypes = require(Package.Types.Signal) 6 | 7 | local Theme = require(Package.Client.Interface.Theme) 8 | 9 | local EventList = require(Package.Client.Interface.Fusion.Pages.Interceptor.EventList) 10 | local EventInformation = require(Package.Client.Interface.Fusion.Pages.Interceptor.EventInformation) 11 | 12 | local Fusion = require(Package.Parent.Fusion) 13 | 14 | local Interceptor = {} 15 | 16 | Interceptor.DisplayName = `Interceptor` 17 | Interceptor.DisplayOrder = 8 18 | Interceptor.DisplayIcon = Theme.InterceptorIcon 19 | Interceptor.Enabled = true 20 | 21 | function Interceptor.Render(scope: Scope, props: Props) 22 | local focusedEvent = scope:Value(nil) 23 | 24 | return scope:New("Frame")({ 25 | Size = UDim2.fromScale(1, 1), 26 | 27 | BackgroundTransparency = 1, 28 | 29 | [Fusion.Children] = scope:Computed(function(use) 30 | local event = use(focusedEvent) 31 | 32 | if event then 33 | return EventInformation.Render(scope, props, focusedEvent) 34 | else 35 | return EventList.Render(scope, props, focusedEvent) 36 | end 37 | end) 38 | }) 39 | end 40 | 41 | type Scope = FusionTypes.Scope 42 | type Props = { 43 | interceptor: { 44 | intercepted: FusionTypes.Value<{ 45 | eventType: "RemoteEvent" | "BindableEvent" | "RemoteFunction" | "BindableFunction", 46 | eventUUID: string?, 47 | 48 | eventName: string, 49 | eventData: string, 50 | eventSize: string, 51 | 52 | ancestors: { 53 | { 54 | class: string, 55 | name: string 56 | } 57 | }, 58 | 59 | context: "Server" | "Client", 60 | }> 61 | }, 62 | 63 | signals: { 64 | interceptor: { 65 | repeatEvent: SignalTypes.Signal, 66 | }, 67 | } 68 | } 69 | 70 | return { 71 | Render = Interceptor.Render, 72 | Properties = { 73 | DisplayOrder = Interceptor.DisplayOrder, 74 | DisplayName = Interceptor.DisplayName, 75 | DisplayIcon = Interceptor.DisplayIcon, 76 | 77 | Enabled = Interceptor.Enabled, 78 | } 79 | } :: PageTypes.PageElement 80 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/LiveEvents.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local ClassIndex = require(Package.Parent.ClassIndex) 4 | local Sift = require(Package.Parent.Sift) 5 | 6 | local ImageLabel = require(Package.Client.Interface.Components.ImageLabel) 7 | local TextLabel = require(Package.Client.Interface.Components.TextLabel) 8 | local UICorner = require(Package.Client.Interface.Components.UICorner) 9 | local UIPadding = require(Package.Client.Interface.Components.UIPadding) 10 | 11 | local FusionTypes = require(Package.Types.Fusion) 12 | local PageTypes = require(Package.Types.Page) 13 | 14 | local Theme = require(Package.Client.Interface.Theme) 15 | 16 | local Fusion = require(Package.Parent.Fusion) 17 | 18 | local VALUE_TYPE_TEXT_COLORS = table.freeze({ 19 | ["boolean"] = Theme.BooleanColor, 20 | ["number"] = Theme.NumberColor, 21 | ["Vector3"] = Theme.NumberColor, 22 | ["CFrame"] = Theme.NumberColor, 23 | ["nil"] = Theme.NilColor, 24 | ["string"] = Theme.StringColor, 25 | ["EnumItem"] = Theme.EnumColor, 26 | ["Instance"] = Theme.InstanceColor 27 | }) 28 | 29 | local LiveEvents = {} 30 | 31 | LiveEvents.DisplayName = `LiveEvents` 32 | LiveEvents.DisplayOrder = 10 33 | LiveEvents.DisplayIcon = Theme.LiveEventsIcon 34 | LiveEvents.Enabled = true 35 | 36 | function LiveEvents.Render(scope: Scope, props: Props) 37 | return scope:New("Frame")({ 38 | Size = UDim2.fromScale(1, 1), 39 | 40 | BackgroundTransparency = 1, 41 | 42 | [Fusion.Children] = { 43 | UICorner = UICorner(scope, {}), 44 | 45 | UIPadding = UIPadding(scope, { 46 | paddingHeight = UDim.new(0, 10), 47 | paddingWidth = UDim.new(0, 10) 48 | }), 49 | 50 | ScrollingFrame = scope:New("ScrollingFrame")({ 51 | Size = UDim2.fromScale(1, 1), 52 | BackgroundTransparency = 1, 53 | AutomaticCanvasSize = Enum.AutomaticSize.Y, 54 | CanvasSize = UDim2.fromScale(0, 0), 55 | ClipsDescendants = true, 56 | MidImage = Theme.MiddleScrollingFrameIcon, 57 | TopImage = Theme.TopScrollingFrameIcon, 58 | BottomImage = Theme.BottomScrollingFrameIcon, 59 | ScrollBarImageColor3 = Theme.AccentColor3, 60 | ScrollBarThickness = 3, 61 | 62 | [Fusion.Children] = scope:Computed(function(use) 63 | local events = use(props.liveEvents.events) 64 | local messageLabels = {} 65 | 66 | if #events == 0 then 67 | table.insert(messageLabels, scope:New("Frame")({ 68 | Size = UDim2.fromScale(1, 0.2), 69 | BackgroundColor3 = Theme.BaseTopbarColor3, 70 | 71 | [Fusion.Children] = { 72 | UIPadding = UIPadding(scope, { 73 | paddingWidth = UDim.new(0.05, 0), 74 | paddingHeight = UDim.new(0.5, 0) 75 | }), 76 | 77 | TextLabel = TextLabel(scope, { 78 | TextTransparency = 0.5, 79 | Text = `No events have been snooped on yet, go do something!` 80 | }) 81 | } 82 | })) 83 | else 84 | for index, event in events do 85 | local imageData = ClassIndex.GetClassIcon(event.className) 86 | local valueColor = VALUE_TYPE_TEXT_COLORS[typeof(event.propertyValue)] 87 | 88 | if not valueColor then 89 | valueColor = Color3.new(1, 1, 1) 90 | end 91 | 92 | table.insert(messageLabels, 1, scope:New("Frame")({ 93 | Size = UDim2.new(1, 0, 0, 35), 94 | BackgroundColor3 = index % 2 == 0 and Theme.BaseTopbarColor3 or Theme.BaseSidebarColor3, 95 | LayoutOrder = index, 96 | 97 | [Fusion.Children] = { 98 | UICorner = UICorner(scope, { }), 99 | 100 | UIPadding = UIPadding(scope, { 101 | paddingWidth = UDim.new(0.05, 0), 102 | paddingHeight = UDim.new(0.5, 0) 103 | }), 104 | 105 | Icon = ImageLabel(scope, { 106 | Size = UDim2.fromScale(1, 1), 107 | Image = imageData.Image, 108 | ImageRectOffset = imageData.ImageRectOffset, 109 | ImageRectSize = imageData.ImageRectSize, 110 | 111 | [Fusion.Children] = { 112 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}) 113 | } 114 | }), 115 | 116 | InstanceInformation = TextLabel(scope, { 117 | Size = UDim2.fromScale(0.4, 1), 118 | Position = UDim2.fromScale(0.075, 0), 119 | TextTransparency = 0.25, 120 | TextXAlignment = Enum.TextXAlignment.Left, 121 | TextScaled = false, 122 | TextTruncate = Enum.TextTruncate.AtEnd, 123 | 124 | Text = `{event.instanceName}.{event.propertyName}`, 125 | 126 | [Fusion.Children] = { 127 | UIPadding = UIPadding(scope, { 128 | paddingWidth = UDim.new(0, 0), 129 | paddingHeight = UDim.new(0.1, 0) 130 | }), 131 | } 132 | }), 133 | 134 | ArrowIcon = ImageLabel(scope, { 135 | Size = UDim2.fromScale(1, 1), 136 | Position = UDim2.fromScale(0.49, 0), 137 | Image = Theme.StraightArrowIcon, 138 | 139 | [Fusion.Children] = { 140 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}) 141 | } 142 | }), 143 | 144 | ValueInformation = TextLabel(scope, { 145 | Size = UDim2.fromScale(0.5, 1), 146 | Position = UDim2.fromScale(0.55, 0), 147 | TextTransparency = 0.25, 148 | TextXAlignment = Enum.TextXAlignment.Left, 149 | TextScaled = false, 150 | TextTruncate = Enum.TextTruncate.AtEnd, 151 | 152 | Text = `{event.propertyValue}`, 153 | TextColor3 = valueColor, 154 | 155 | [Fusion.Children] = { 156 | UIPadding = UIPadding(scope, { 157 | paddingWidth = UDim.new(0, 0), 158 | paddingHeight = UDim.new(0.1, 0) 159 | }), 160 | } 161 | }), 162 | } 163 | })) 164 | end 165 | end 166 | 167 | return Sift.Dictionary.merge(messageLabels, { 168 | UIListLayout = scope:New("UIListLayout")({ 169 | SortOrder = Enum.SortOrder.LayoutOrder, 170 | FillDirection = Enum.FillDirection.Vertical, 171 | VerticalAlignment = Enum.VerticalAlignment.Top, 172 | 173 | Padding = UDim.new(0.025, 0), 174 | }), 175 | }) 176 | end) 177 | }), 178 | } 179 | }) 180 | end 181 | 182 | type Scope = FusionTypes.Scope 183 | type Props = { 184 | liveEvents: { 185 | events: FusionTypes.Value<{ 186 | { 187 | instanceName: string, 188 | className: string, 189 | propertyName: string, 190 | propertyValue: any, 191 | } 192 | }> 193 | } 194 | } 195 | 196 | return { 197 | Render = LiveEvents.Render, 198 | Properties = { 199 | DisplayOrder = LiveEvents.DisplayOrder, 200 | DisplayName = LiveEvents.DisplayName, 201 | DisplayIcon = LiveEvents.DisplayIcon, 202 | 203 | Enabled = LiveEvents.Enabled, 204 | } 205 | } :: PageTypes.PageElement 206 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/Performance.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | local PageTypes = require(Package.Types.Page) 5 | 6 | local Theme = require(Package.Client.Interface.Theme) 7 | 8 | local Fusion = require(Package.Parent.Fusion) 9 | 10 | local Performance = {} 11 | 12 | Performance.DisplayName = `Performance` 13 | Performance.DisplayOrder = 10 14 | Performance.DisplayIcon = Theme.PerformanceIcon 15 | Performance.Enabled = false 16 | 17 | function Performance.Render(scope: Scope, _props: Props) 18 | return scope:New("Frame")({ 19 | Size = UDim2.fromScale(1, 1), 20 | 21 | BackgroundTransparency = 1, 22 | 23 | [Fusion.Children] = { 24 | 25 | } 26 | }) 27 | end 28 | 29 | type Scope = FusionTypes.Scope 30 | type Props = {} 31 | 32 | return { 33 | Render = Performance.Render, 34 | Properties = { 35 | DisplayOrder = Performance.DisplayOrder, 36 | DisplayName = Performance.DisplayName, 37 | DisplayIcon = Performance.DisplayIcon, 38 | 39 | Enabled = Performance.Enabled, 40 | } 41 | } :: PageTypes.PageElement 42 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/Sandbox.luau: -------------------------------------------------------------------------------- 1 | local UserInputService = game:GetService("UserInputService") 2 | 3 | local Package = script.Parent.Parent.Parent.Parent.Parent 4 | 5 | local FusionTypes = require(Package.Types.Fusion) 6 | local PageTypes = require(Package.Types.Page) 7 | local SignalTypes = require(Package.Types.Signal) 8 | 9 | local Theme = require(Package.Client.Interface.Theme) 10 | 11 | local Button = require(Package.Client.Interface.Components.Button) 12 | local ImageLabel = require(Package.Client.Interface.Components.ImageLabel) 13 | 14 | local UIPadding = require(Package.Client.Interface.Components.UIPadding) 15 | local UICorner = require(Package.Client.Interface.Components.UICorner) 16 | 17 | local Codeblock = require(Package.Client.Interface.Components.Codeblock) 18 | 19 | local disassemble = require(Package.Utilities.Luau.disassemble) 20 | 21 | local LuauCeption = require(Package.Vendor.LuauCeption) 22 | 23 | local Fusion = require(Package.Parent.Fusion) 24 | 25 | local Sandbox = {} 26 | 27 | Sandbox.DisplayName = `Sandbox` 28 | Sandbox.DisplayOrder = 2 29 | Sandbox.DisplayIcon = Theme.SandboxIcon 30 | Sandbox.Enabled = true 31 | 32 | function Sandbox.Render(scope: Scope, props: Props) 33 | local textBoxValue = scope:Value() 34 | local scrollingFrameValue = scope:Value() 35 | local dropdownOpenValue = scope:Value() 36 | 37 | local ignoreInputOnce = false 38 | 39 | table.insert(scope, UserInputService.InputEnded:Connect(function(inputObject: InputObject) 40 | if inputObject.UserInputType == Enum.UserInputType.MouseButton1 or inputObject.UserInputType == Enum.UserInputType.Touch then 41 | if not Fusion.peek(dropdownOpenValue) then 42 | return 43 | end 44 | 45 | if ignoreInputOnce then 46 | ignoreInputOnce = false 47 | 48 | return 49 | end 50 | 51 | dropdownOpenValue:set(false) 52 | end 53 | end)) 54 | 55 | return scope:New("Frame")({ 56 | Size = UDim2.fromScale(1, 1), 57 | 58 | BackgroundTransparency = 1, 59 | 60 | [Fusion.Children] = { 61 | UIPadding = UIPadding(scope, { 62 | paddingHeight = UDim.new(0.045, 0), 63 | paddingWidth = UDim.new(0.025, 0) 64 | }), 65 | 66 | Codeblock = Codeblock(scope, { 67 | Size = UDim2.fromScale(1, 0.85), 68 | 69 | textBox = textBoxValue, 70 | scrollingFrame = scrollingFrameValue 71 | }), 72 | 73 | runOnServerButton = Button(scope, { 74 | Size = UDim2.fromScale(0.13, 0.13), 75 | Position = UDim2.fromScale(0.92, 0.87), 76 | BackgroundTransparency = 0, 77 | BackgroundColor3 = Theme.ServerColor3, 78 | 79 | [Fusion.OnEvent("Activated")] = function() 80 | local textBox = Fusion.peek(textBoxValue) 81 | 82 | if textBox then 83 | props.signals.sandbox.evalServerCode:Fire(textBox.Text) 84 | end 85 | end, 86 | 87 | [Fusion.Children] = { 88 | UICorner = UICorner(scope, {}), 89 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}), 90 | 91 | ImageLabel = ImageLabel(scope, { 92 | Image = Theme.PlayIcon, 93 | 94 | Size = UDim2.fromScale(0.5, 0.5), 95 | Position = UDim2.fromScale(0.5, 0.5), 96 | 97 | AnchorPoint = Vector2.new(0.5, 0.5) 98 | }) 99 | } 100 | }), 101 | 102 | runOnClientButton = Button(scope, { 103 | Size = UDim2.fromScale(0.13, 0.13), 104 | Position = UDim2.fromScale(0.83, 0.87), 105 | BackgroundTransparency = 0, 106 | BackgroundColor3 = Theme.ClientColor3, 107 | 108 | [Fusion.OnEvent("Activated")] = function() 109 | local textBox = Fusion.peek(textBoxValue) 110 | 111 | if textBox then 112 | props.signals.sandbox.evalClientCode:Fire(textBox.Text) 113 | end 114 | end, 115 | 116 | [Fusion.Children] = { 117 | UICorner = UICorner(scope, {}), 118 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}), 119 | 120 | ImageLabel = ImageLabel(scope, { 121 | Image = Theme.PlayIcon, 122 | 123 | Size = UDim2.fromScale(0.5, 0.5), 124 | Position = UDim2.fromScale(0.5, 0.5), 125 | 126 | AnchorPoint = Vector2.new(0.5, 0.5) 127 | }) 128 | } 129 | }), 130 | 131 | clearButton = Button(scope, { 132 | Size = UDim2.fromScale(0.13, 0.13), 133 | Position = UDim2.fromScale(0, 0.87), 134 | BackgroundTransparency = 0, 135 | BackgroundColor3 = Theme.BaseSidebarColor3, 136 | 137 | [Fusion.OnEvent("Activated")] = function() 138 | local textBox = Fusion.peek(textBoxValue) 139 | local scrollingFrame = Fusion.peek(scrollingFrameValue) 140 | 141 | if textBox then 142 | textBox.Text = `` 143 | end 144 | 145 | if scrollingFrame then 146 | scrollingFrame.CanvasPosition = Vector2.new() 147 | end 148 | end, 149 | 150 | [Fusion.Children] = { 151 | UICorner = UICorner(scope, {}), 152 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}), 153 | 154 | ImageLabel = ImageLabel(scope, { 155 | Image = Theme.RestartIcon, 156 | 157 | Size = UDim2.fromScale(0.5, 0.5), 158 | Position = UDim2.fromScale(0.5, 0.5), 159 | 160 | AnchorPoint = Vector2.new(0.5, 0.5) 161 | }) 162 | } 163 | }), 164 | 165 | disassembleButton = Button(scope, { 166 | Size = UDim2.fromScale(0.13, 0.13), 167 | Position = UDim2.fromScale(0.085, 0.87), 168 | BackgroundTransparency = 0, 169 | BackgroundColor3 = Theme.BaseSidebarColor3, 170 | 171 | [Fusion.OnEvent("Activated")] = function() 172 | local textBox = Fusion.peek(textBoxValue) 173 | 174 | if textBox then 175 | local bytecode = LuauCeption.luau_compile(textBox.Text) 176 | 177 | textBox.Text = disassemble(bytecode) 178 | end 179 | end, 180 | 181 | [Fusion.Children] = { 182 | UICorner = UICorner(scope, {}), 183 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}), 184 | 185 | ImageLabel = ImageLabel(scope, { 186 | Image = Theme.DisassembleIcon, 187 | 188 | Size = UDim2.fromScale(0.5, 0.5), 189 | Position = UDim2.fromScale(0.5, 0.5), 190 | 191 | AnchorPoint = Vector2.new(0.5, 0.5) 192 | }) 193 | } 194 | }) 195 | } 196 | }) 197 | end 198 | 199 | type Scope = FusionTypes.Scope 200 | type Props = { 201 | sandbox: { 202 | runtime: FusionTypes.Value, 203 | availableRuntimes: FusionTypes.Value<{ string }>, 204 | }, 205 | 206 | signals: { 207 | sandbox: { 208 | evalServerCode: SignalTypes.Signal, 209 | evalClientCode: SignalTypes.Signal, 210 | }, 211 | } 212 | } 213 | 214 | return { 215 | Render = Sandbox.Render, 216 | Properties = { 217 | DisplayOrder = Sandbox.DisplayOrder, 218 | DisplayName = Sandbox.DisplayName, 219 | DisplayIcon = Sandbox.DisplayIcon, 220 | 221 | Enabled = Sandbox.Enabled, 222 | } 223 | } :: PageTypes.PageElement 224 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/ScriptHub.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | local PageTypes = require(Package.Types.Page) 5 | local SignalTypes = require(Package.Types.Signal) 6 | 7 | local Button = require(Package.Client.Interface.Components.Button) 8 | local ImageLabel = require(Package.Client.Interface.Components.ImageLabel) 9 | local TextLabel = require(Package.Client.Interface.Components.TextLabel) 10 | local UIPadding = require(Package.Client.Interface.Components.UIPadding) 11 | 12 | local Sift = require(Package.Parent.Sift) 13 | 14 | local Theme = require(Package.Client.Interface.Theme) 15 | 16 | local Fusion = require(Package.Parent.Fusion) 17 | 18 | local ScriptHub = {} 19 | 20 | ScriptHub.DisplayName = `Script Hub` 21 | ScriptHub.DisplayOrder = 4 22 | ScriptHub.DisplayIcon = Theme.ScriptHubIcon 23 | ScriptHub.Enabled = true 24 | 25 | function ScriptHub.Render(scope: Scope, props: Props) 26 | return scope:New("ScrollingFrame")({ 27 | Size = UDim2.fromScale(1, 1), 28 | Position = UDim2.fromScale(0, 0), 29 | AutomaticCanvasSize = Enum.AutomaticSize.Y, 30 | CanvasSize = UDim2.fromScale(0, 0), 31 | ScrollBarThickness = 0, 32 | BackgroundTransparency = 1, 33 | ClipsDescendants = true, 34 | 35 | [Fusion.Children] = scope:Computed(function(use) 36 | local scripts = use(props.scriptHub.scripts) 37 | local scriptFrames = {} 38 | 39 | if #scripts == 0 then 40 | table.insert(scriptFrames, scope:New("Frame")({ 41 | Size = UDim2.fromScale(1, 0.2), 42 | BackgroundColor3 = Theme.BaseTopbarColor3, 43 | 44 | [Fusion.Children] = { 45 | UIPadding = UIPadding(scope, { 46 | paddingWidth = UDim.new(0.05, 0), 47 | paddingHeight = UDim.new(0.5, 0) 48 | }), 49 | 50 | TextLabel = TextLabel(scope, { 51 | TextTransparency = 0.5, 52 | Text = `No scripts found.. somethings gone wrong! Please contact a developer!`, 53 | Size = UDim2.fromScale(1, 0.6), 54 | Position = UDim2.fromScale(0, 0.2), 55 | }) 56 | } 57 | })) 58 | end 59 | 60 | for _, script in scripts do 61 | table.insert(scriptFrames, scope:New("Frame")({ 62 | Size = UDim2.fromScale(1, 0.2), 63 | BackgroundColor3 = Theme.BaseTopbarColor3, 64 | 65 | [Fusion.Children] = { 66 | UIPadding = UIPadding(scope, { 67 | paddingWidth = UDim.new(0.05, 0), 68 | paddingHeight = UDim.new(0.5, 0) 69 | }), 70 | 71 | Icon = ImageLabel(scope, { 72 | Size = UDim2.fromScale(0.1, 1), 73 | Position = UDim2.fromScale(0, 0), 74 | Image = Theme.ScriptHubIcon, 75 | 76 | [Fusion.Children] = { 77 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}), 78 | } 79 | }), 80 | 81 | TitleLabel = TextLabel(scope, { 82 | TextTransparency = 0, 83 | Text = `{script.name}`, 84 | 85 | TextXAlignment = Enum.TextXAlignment.Left, 86 | Size = UDim2.fromScale(1, 0.5), 87 | Position = UDim2.fromScale(0.1, 0), 88 | }), 89 | 90 | DescriptionLabel = TextLabel(scope, { 91 | TextTransparency = 0.5, 92 | Text = script.description, 93 | 94 | TextXAlignment = Enum.TextXAlignment.Left, 95 | Size = UDim2.fromScale(1, 0.5), 96 | Position = UDim2.fromScale(0.1, 0.5), 97 | }), 98 | 99 | ExecuteButton = Button(scope, { 100 | Image = Theme.PlayIcon, 101 | Size = UDim2.fromScale(0.1, 1), 102 | Position = UDim2.fromScale(0.925, 0), 103 | 104 | [Fusion.OnEvent("Activated")] = function() 105 | props.signals.scriptHub.execute:Fire(script.source) 106 | end 107 | }) 108 | } 109 | })) 110 | end 111 | 112 | return Sift.Dictionary.merge(scriptFrames, { 113 | UIListLayout = scope:New("UIListLayout")({ 114 | SortOrder = Enum.SortOrder.LayoutOrder, 115 | FillDirection = Enum.FillDirection.Vertical, 116 | VerticalAlignment = Enum.VerticalAlignment.Top, 117 | 118 | Padding = UDim.new(0.025, 0), 119 | }), 120 | 121 | UIPadding = UIPadding(scope, { 122 | paddingWidth = UDim.new(0, 10), 123 | paddingHeight = UDim.new(0, 10) 124 | }), 125 | }) 126 | end) 127 | }) 128 | end 129 | 130 | type Scope = FusionTypes.Scope 131 | type Props = { 132 | scriptHub: { 133 | scripts: FusionTypes.Value<{ 134 | { 135 | name: string, 136 | description: string, 137 | source: string, 138 | } 139 | }> 140 | }, 141 | 142 | signals: { 143 | scriptHub: { 144 | execute: SignalTypes.Signal, 145 | }, 146 | } 147 | } 148 | 149 | return { 150 | Render = ScriptHub.Render, 151 | Properties = { 152 | DisplayOrder = ScriptHub.DisplayOrder, 153 | DisplayName = ScriptHub.DisplayName, 154 | DisplayIcon = ScriptHub.DisplayIcon, 155 | 156 | Enabled = ScriptHub.Enabled, 157 | } 158 | } :: PageTypes.PageElement 159 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/Statistics/Graph.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | local SignalTypes = require(Package.Types.Signal) 5 | 6 | local Graph = {} 7 | 8 | function Graph.render(_scope: Scope, _props: Props) 9 | -- to-do, later date. 10 | end 11 | 12 | type StatStructure = { 13 | prefix: string, 14 | name: string, 15 | stats: { 16 | [string]: { 17 | [string]: number 18 | } | string, 19 | }, 20 | graph: { 21 | [string]: number 22 | } 23 | } 24 | 25 | type Scope = FusionTypes.Scope 26 | type Props = { 27 | statistics: { 28 | selectedCategory: FusionTypes.Value, 29 | 30 | memoryStats: FusionTypes.Value, 31 | networkStats: FusionTypes.Value, 32 | instanceStats: FusionTypes.Value, 33 | metadataStats: FusionTypes.Value, 34 | drawcallStats: FusionTypes.Value, 35 | frameStats: FusionTypes.Value 36 | }, 37 | 38 | signals: { 39 | statistics: { 40 | refresh: SignalTypes.Signal<()>, 41 | 42 | enableAutoRefresh: SignalTypes.Signal<()>, 43 | disableAutoRefresh: SignalTypes.Signal<()> 44 | }, 45 | } 46 | } 47 | 48 | return Graph -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/Pages/Statistics/init.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | local PageTypes = require(Package.Types.Page) 5 | local SignalTypes = require(Package.Types.Signal) 6 | 7 | local Graph = require(Package.Client.Interface.Fusion.Pages.Statistics.Graph) 8 | local Views = require(Package.Client.Interface.Fusion.Pages.Statistics.Views) 9 | 10 | local Theme = require(Package.Client.Interface.Theme) 11 | 12 | local Fusion = require(Package.Parent.Fusion) 13 | 14 | local Statistics = {} 15 | 16 | Statistics.DisplayName = `Statistics` 17 | Statistics.DisplayOrder = 11 18 | Statistics.DisplayIcon = Theme.StatisticsIcon 19 | Statistics.Enabled = true 20 | 21 | function Statistics.Render(scope: Scope, props: Props) 22 | return scope:New("Frame")({ 23 | Size = UDim2.fromScale(1, 1), 24 | 25 | BackgroundTransparency = 1, 26 | 27 | [Fusion.Children] = scope:Computed(function(use) 28 | local selectedCategory = use(props.statistics.selectedCategory) 29 | 30 | if selectedCategory then 31 | return Graph.render(scope, props) 32 | else 33 | return Views.render(scope, props) 34 | end 35 | end) 36 | }) 37 | end 38 | 39 | type StatStructure = { 40 | prefix: string, 41 | name: string, 42 | stats: { 43 | [string]: { 44 | [string]: number 45 | } | string, 46 | }, 47 | graph: { 48 | [string]: number 49 | } 50 | } 51 | 52 | type Scope = FusionTypes.Scope 53 | type Props = { 54 | statistics: { 55 | selectedCategory: FusionTypes.Value, 56 | 57 | memoryStats: FusionTypes.Value, 58 | networkStats: FusionTypes.Value, 59 | instanceStats: FusionTypes.Value, 60 | metadataStats: FusionTypes.Value, 61 | drawcallStats: FusionTypes.Value, 62 | frameStats: FusionTypes.Value 63 | }, 64 | 65 | signals: { 66 | statistics: { 67 | refresh: SignalTypes.Signal<()>, 68 | 69 | enableAutoRefresh: SignalTypes.Signal<()>, 70 | disableAutoRefresh: SignalTypes.Signal<()> 71 | }, 72 | } 73 | } 74 | 75 | return { 76 | Render = Statistics.Render, 77 | Properties = { 78 | DisplayOrder = Statistics.DisplayOrder, 79 | DisplayName = Statistics.DisplayName, 80 | DisplayIcon = Statistics.DisplayIcon, 81 | 82 | Enabled = Statistics.Enabled, 83 | } 84 | } :: PageTypes.PageElement 85 | -------------------------------------------------------------------------------- /Source/Client/Interface/Fusion/SidebarPanel/init.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent 2 | 3 | local FusionTypes = require(Package.Types.Fusion) 4 | local PageTypes = require(Package.Types.Page) 5 | 6 | local Theme = require(Package.Client.Interface.Theme) 7 | 8 | local UIPadding = require(Package.Client.Interface.Components.UIPadding) 9 | 10 | local ImageLabel = require(Package.Client.Interface.Components.ImageLabel) 11 | local TextLabel = require(Package.Client.Interface.Components.TextLabel) 12 | local Button = require(Package.Client.Interface.Components.Button) 13 | 14 | local Sift = require(Package.Parent.Sift) 15 | local Fusion = require(Package.Parent.Fusion) 16 | 17 | local DEBUG_DEFAULT_MENU = nil -- `Admin` 18 | 19 | return function(scope: FusionTypes.Scope, props: { 20 | selectedPage: FusionTypes.Value>, 21 | customPages: FusionTypes.Value<{ 22 | { 23 | name: string, 24 | icon: string?, 25 | renderCallback: () -> () -> (), 26 | cleanupCallback: () -> () 27 | } 28 | }>, 29 | }): CanvasGroup 30 | return scope:Computed(function(use) 31 | local pageProperties = use(props.customPages) 32 | local customPages = {} 33 | local modulePages = {} 34 | 35 | local sidebarElements = { } 36 | local firstPage 37 | 38 | for _, page in Package.Client.Interface.Fusion.Pages:GetChildren() do 39 | local pageData = require(page) 40 | 41 | if not pageData.Properties.Enabled then 42 | pageData.Properties.DisplayOrder += 100 43 | end 44 | 45 | table.insert(modulePages, pageData) 46 | end 47 | 48 | for _, pageProperty in pageProperties do 49 | table.insert(customPages, { 50 | Properties = { 51 | DisplayOrder = #modulePages + 1, 52 | DisplayName = pageProperty.name, 53 | DisplayIcon = pageProperty.icon, 54 | 55 | Enabled = true, 56 | }, 57 | 58 | RenderCallback = pageProperty.renderCallback, 59 | CleanupCallback = pageProperty.cleanupCallback, 60 | 61 | CustomPage = true, 62 | }) 63 | end 64 | 65 | for _, pageData in Sift.Array.concat(modulePages, Fusion.peek(customPages)) do 66 | if DEBUG_DEFAULT_MENU then 67 | if pageData.Properties.DisplayName == DEBUG_DEFAULT_MENU then 68 | firstPage = pageData 69 | end 70 | else 71 | if pageData.Properties.DisplayOrder == 1 then 72 | firstPage = pageData 73 | end 74 | end 75 | 76 | table.insert(sidebarElements, scope:New("Frame")({ 77 | Size = UDim2.new(1, 0, 0, 32), 78 | 79 | BackgroundTransparency = 1, 80 | 81 | LayoutOrder = pageData.Properties.DisplayOrder, 82 | 83 | [Fusion.Children] = { 84 | UIPadding = UIPadding(scope, { 85 | paddingHeight = UDim.new(0.5, 0), 86 | paddingWidth = UDim.new(0.2, 0) 87 | }), 88 | 89 | ImageLabel = ImageLabel(scope, { 90 | Position = UDim2.fromScale(0, 0.5), 91 | AnchorPoint = Vector2.new(0, 0.5), 92 | Size = UDim2.fromScale(0.175, 1), 93 | 94 | ImageTransparency = scope:Computed(function(use_) 95 | local isSelectedPage = scope.peek(props.selectedPage) == pageData 96 | 97 | use_(props.selectedPage) 98 | 99 | if not pageData.Properties.Enabled then 100 | return 0.8 101 | end 102 | 103 | return isSelectedPage and 0 or 0.5 104 | end), 105 | 106 | Image = pageData.Properties.DisplayIcon, 107 | 108 | [Fusion.Children] = { 109 | UIAspectRatioConstraint = scope:New("UIAspectRatioConstraint")({}) 110 | } 111 | }), 112 | 113 | TextLabel = TextLabel(scope, { 114 | Position = UDim2.fromScale(0.2, 0), 115 | Size = UDim2.fromScale(0.8, 1), 116 | 117 | TextTransparency = scope:Computed(function(use_) 118 | local isSelectedPage = scope.peek(props.selectedPage) == pageData 119 | 120 | use_(props.selectedPage) 121 | 122 | if not pageData.Properties.Enabled then 123 | return 0.8 124 | end 125 | 126 | return isSelectedPage and 0 or 0.5 127 | end), 128 | 129 | TextXAlignment = Enum.TextXAlignment.Left, 130 | 131 | Text = pageData.Properties.DisplayName, 132 | 133 | [Fusion.Children] = { 134 | UIPadding = UIPadding(scope, { 135 | paddingHeight = UDim.new(0.2, 0), 136 | paddingWidth = UDim.new(0.1, 0) 137 | }), 138 | } 139 | }), 140 | 141 | Button = Button(scope, { 142 | Active = pageData.Properties.Enabled, 143 | 144 | [Fusion.OnEvent("Activated")] = function() 145 | props.selectedPage:set(pageData) 146 | end 147 | }) 148 | } 149 | })) 150 | end 151 | 152 | if not scope.peek(props.selectedPage) then 153 | props.selectedPage:set(firstPage) 154 | end 155 | 156 | return scope:New("Frame")({ 157 | Size = UDim2.fromScale(0.2, 1), 158 | 159 | BackgroundColor3 = Theme.BaseSidebarColor3, 160 | 161 | [Fusion.Children] = { 162 | Content = scope:New("CanvasGroup")({ 163 | Size = UDim2.fromScale(1, 1), 164 | 165 | BackgroundTransparency = 1, 166 | 167 | [Fusion.Children] = { 168 | UIGradient = scope:New("UIGradient")({ 169 | Rotation = 90, 170 | Transparency = NumberSequence.new({ 171 | NumberSequenceKeypoint.new(0, 1), 172 | NumberSequenceKeypoint.new(0.05, 0), 173 | NumberSequenceKeypoint.new(0.9, 0), 174 | NumberSequenceKeypoint.new(1, 1) 175 | }) 176 | }), 177 | 178 | ScrollingFrame = scope:New("ScrollingFrame")({ 179 | Size = UDim2.fromScale(1, 1), 180 | CanvasSize = UDim2.fromScale(0, 0), 181 | 182 | ScrollBarThickness = 0, 183 | 184 | AutomaticCanvasSize = Enum.AutomaticSize.Y, 185 | 186 | BackgroundTransparency = 1, 187 | 188 | [Fusion.Children] = Sift.Dictionary.merge({ 189 | UIListLayout = scope:New("UIListLayout")({ 190 | FillDirection = Enum.FillDirection.Vertical, 191 | SortOrder = Enum.SortOrder.LayoutOrder 192 | }) 193 | }, sidebarElements) 194 | }) 195 | } 196 | }) 197 | } 198 | }) 199 | end) 200 | end 201 | -------------------------------------------------------------------------------- /Source/Client/Singletons/Actions.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | ]] 4 | 5 | local HttpService = game:GetService("HttpService") 6 | 7 | local Package = script.Parent.Parent.Parent 8 | 9 | local Sift = require(Package.Parent.Sift) 10 | 11 | local Interface = require(Package.Client.Singletons.Interface) 12 | 13 | local Network = require(Package.Client.Network) 14 | 15 | local clientSideActions = {} 16 | local serverSideActions = {} 17 | 18 | local updateActionsThread = nil 19 | 20 | local Actions = {} 21 | 22 | --[[ 23 | 24 | ]] 25 | function Actions.UpdateInterface(_: Actions) 26 | if updateActionsThread then 27 | task.cancel(updateActionsThread) 28 | end 29 | 30 | updateActionsThread = task.defer(function() 31 | Interface:SetActions(Sift.Dictionary.merge(clientSideActions, serverSideActions)) 32 | end) 33 | end 34 | 35 | --[[ 36 | 37 | ]] 38 | function Actions.ExecuteClientAction(_: Actions, actionUuid: string, actionArguments: { any }) 39 | clientSideActions[actionUuid].callback(table.unpack(actionArguments)) 40 | end 41 | 42 | --[[ 43 | 44 | ]] 45 | function Actions.ExecuteServerAction(_: Actions, actionUuid: string, actionArguments: { any }) 46 | local arguments = {} 47 | local serverSideAction = nil 48 | 49 | for _, serverSideActionObject in serverSideActions do 50 | if serverSideActionObject.uuid == actionUuid then 51 | serverSideAction = serverSideActionObject 52 | 53 | break 54 | end 55 | end 56 | 57 | if not serverSideAction then 58 | return 59 | end 60 | 61 | for index, argument in actionArguments do 62 | local argumentData = serverSideAction.arguments[index] 63 | 64 | table.insert(arguments, { 65 | type = argumentData.type, 66 | value = tostring(argument), 67 | }) 68 | end 69 | 70 | Network.ExecuteServerActionRequested.Fire({ 71 | actionUuid = actionUuid, 72 | arguments = arguments, 73 | }) 74 | end 75 | 76 | --[[ 77 | 78 | ]] 79 | function Actions.RegisterClientAction(self: Actions, actionSettings: { 80 | name: string, 81 | description: string, 82 | arguments: { 83 | type: "Number" | "String" | "Boolean" | "Player", 84 | name: string, 85 | default: any 86 | }, 87 | callback: (...any) -> () 88 | }) 89 | local actionUuid = HttpService:GenerateGUID(false) 90 | local path = string.split(actionSettings.name, ".") 91 | local name = table.remove(path, #path) 92 | 93 | clientSideActions[actionUuid] = { 94 | path = path, 95 | name = name, 96 | uuid = actionUuid, 97 | context = "Client", 98 | arguments = actionSettings.arguments, 99 | description = actionSettings.description, 100 | callback = actionSettings.callback, 101 | } 102 | 103 | self:UpdateInterface() 104 | end 105 | 106 | function Actions.OnStart(self: Actions) 107 | Interface.ExecuteActionRequested:Connect(function(actionUuid: string, actionArguments: { any }) 108 | if clientSideActions[actionUuid] then 109 | self:ExecuteClientAction(actionUuid, actionArguments) 110 | else 111 | self:ExecuteServerAction(actionUuid, actionArguments) 112 | end 113 | end) 114 | 115 | Network.ServerReportingActions.On(function(actions) 116 | for _, object in actions do 117 | object.context = "Server" 118 | 119 | serverSideActions[object.uuid] = object 120 | end 121 | 122 | self:UpdateInterface() 123 | end) 124 | 125 | Network.FetchActionsRequested.Fire() 126 | end 127 | 128 | export type Actions = typeof(Actions) 129 | 130 | return Actions -------------------------------------------------------------------------------- /Source/Client/Singletons/Admin.luau: -------------------------------------------------------------------------------- 1 | local Players = game:GetService("Players") 2 | --[[ 3 | 4 | ]] 5 | 6 | local Package = script.Parent.Parent.Parent 7 | 8 | local Interface = require(Package.Client.Singletons.Interface) 9 | 10 | local Network = require(Package.Client.Network) 11 | 12 | local Admin = {} 13 | 14 | function Admin.UpdateAdminPlayers(_: Admin) 15 | local playerList = {} 16 | 17 | for _, player in Players:GetPlayers() do 18 | table.insert(playerList, { 19 | name = player.DisplayName, 20 | id = tostring(player.UserId), 21 | isAdmin = player:GetAttribute("DevSuite_Auth") 22 | }) 23 | end 24 | 25 | Interface:SetAdminPlayers(playerList) 26 | end 27 | 28 | function Admin.OnPlayerAdded(self: Admin, player: Player, avoidUpdate: boolean?) 29 | player:GetAttributeChangedSignal("DevSuite_Auth"):Connect(function() 30 | self:UpdateAdminPlayers() 31 | end) 32 | 33 | if not avoidUpdate then 34 | self:UpdateAdminPlayers() 35 | end 36 | end 37 | 38 | function Admin.OnStart(self: Admin) 39 | Interface.KickUserRequested:Connect(function(userId: string) 40 | Network.KickUserRequested.Fire(Players:GetPlayerByUserId(userId)) 41 | end) 42 | 43 | Interface.BanUserRequested:Connect(function(userId: string) 44 | Network.BanUserRequested.Fire(Players:GetPlayerByUserId(userId)) 45 | end) 46 | 47 | Interface.AdminUserRequested:Connect(function(userId: string) 48 | Network.AdminUserRequested.Fire(Players:GetPlayerByUserId(userId)) 49 | end) 50 | 51 | Interface.RejoinServerRequested:Connect(function() 52 | Network.RejoinRequested.Fire() 53 | end) 54 | 55 | Interface.ShutdownServerRequested:Connect(function() 56 | Network.ShutdownServerRequested.Fire() 57 | end) 58 | 59 | Players.PlayerAdded:Connect(function(player) 60 | self:OnPlayerAdded(player) 61 | end) 62 | 63 | Players.PlayerRemoving:Connect(function() 64 | self:UpdateAdminPlayers() 65 | end) 66 | 67 | for _, player in Players:GetPlayers() do 68 | self:OnPlayerAdded(player, true) 69 | end 70 | 71 | self:UpdateAdminPlayers() 72 | end 73 | 74 | export type Admin = typeof(Admin) 75 | 76 | return Admin -------------------------------------------------------------------------------- /Source/Client/Singletons/Authentication.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | High level explanation of this Singleton is that it's job is to provide bindings to other Services to figure out if 3 | the current player (local player) is able to access the Dev Suite. 4 | ]] 5 | 6 | local Package = script.Parent.Parent.Parent 7 | 8 | local Network = require(Package.Client.Network) 9 | 10 | local authenticationState 11 | 12 | local Authentication = {} 13 | 14 | Authentication.AuthenticationCallback = nil :: ((Player) -> boolean)? 15 | 16 | --[[ 17 | Returns a boolean, defines if the user is able to interact with the dev suite or not. 18 | ]] 19 | function Authentication.IsAuthenticated(_: Authentication): boolean 20 | if authenticationState == nil then 21 | authenticationState = Network.AuthenticateRequested.Call() 22 | end 23 | 24 | return authenticationState == "Accepted" 25 | end 26 | 27 | export type Authentication = typeof(Authentication) 28 | 29 | return Authentication -------------------------------------------------------------------------------- /Source/Client/Singletons/Debugger.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | Debugger 3 | ]] 4 | 5 | local Package = script.Parent.Parent.Parent 6 | 7 | local LuauCeption = require(Package.Vendor.LuauCeption) 8 | 9 | local Sift = require(Package.Parent.Sift) 10 | 11 | local Interface = require(Package.Client.Singletons.Interface) 12 | 13 | local VM = require(Package.Client.Singletons.VM) 14 | 15 | local VMContext = require(Package.Client.Singletons.VM.Context) 16 | local VMBindings = require(Package.Client.Singletons.VM.Bindings) 17 | local VMEnvironment = require(Package.Client.Singletons.VM.Environment) 18 | local VMTypes = require(Package.Client.Singletons.VM.Types) 19 | local VMScheduler = require(Package.Client.Singletons.VM.Scheduler) 20 | local VMMacros = require(Package.Client.Singletons.VM.Macros) 21 | 22 | local WASM_PAGE_SIZE = 65536 23 | local MAXIMUM_THREADS_TO_TRACK = 50 24 | 25 | local cFunctions = LuauCeption.wasm.cfns 26 | 27 | local threadList = {} 28 | local threadHistory = {} 29 | local threadStats = {} 30 | 31 | local luauRegistry = {} 32 | local vmStats = { 33 | memory = 0, 34 | threads = 0, 35 | instances = 0 36 | } 37 | 38 | local Debugger = {} 39 | 40 | function Debugger.HookNamecall(_: Debugger) 41 | local callback 42 | 43 | callback = VMEnvironment:Hook("__namecall", function(luaState: VMTypes.LuaState) 44 | local methodName = VMBindings:ReadCString(cFunctions.lua_namecallatom(luaState, 0)) 45 | local object = VMBindings:ToLuau(luaState, 1) 46 | 47 | local thread = VMBindings:GetCurrentThread(luaState) 48 | 49 | local scriptThread = VMScheduler:GetScriptThread(thread) 50 | local scriptThreadUuid = `Thread: {scriptThread}` 51 | 52 | local arguments = {} 53 | local response = {} 54 | 55 | for i = 2, cFunctions.lua_gettop(luaState) - 1 do 56 | table.insert(arguments, VMBindings:ToLuau(luaState, i)) 57 | end 58 | 59 | local count = callback(luaState) 60 | local top = cFunctions.lua_gettop(luaState) 61 | 62 | for i = (top - count) + 1, top do 63 | table.insert(response, VMBindings:ReadCString(cFunctions.luaL_tolstring(luaState, i, 0))) 64 | VMMacros.lua_pop(luaState, 1) 65 | end 66 | 67 | if threadStats[scriptThreadUuid] and threadHistory[scriptThreadUuid] then 68 | threadStats[scriptThreadUuid].methodCallCount += 1 69 | table.insert(threadHistory[scriptThreadUuid], { 70 | type = "Function", 71 | message = `{object}:{methodName}({table.unpack(arguments)}) -> {table.unpack(response)}`, 72 | }) 73 | end 74 | 75 | return count 76 | end) 77 | end 78 | 79 | function Debugger.HookIndex(_: Debugger) 80 | local callback 81 | 82 | callback = VMEnvironment:Hook("__index", function(luaState: VMTypes.LuaState) 83 | local object = VMBindings:ToLuau(luaState, 1) 84 | local index = VMBindings:ToLuau(luaState, 2) 85 | 86 | local thread = VMBindings:GetCurrentThread(luaState) 87 | 88 | local scriptThread = VMScheduler:GetScriptThread(thread) 89 | local scriptThreadUuid = `Thread: {scriptThread}` 90 | 91 | local response = {} 92 | 93 | local count = callback(luaState) 94 | local top = cFunctions.lua_gettop(luaState) 95 | 96 | for i = (top - count) + 1, top do 97 | table.insert(response, VMBindings:ReadCString(cFunctions.luaL_tolstring(luaState, i, 0))) 98 | VMMacros.lua_pop(luaState, 1) 99 | end 100 | 101 | if threadStats[scriptThreadUuid] and threadHistory[scriptThreadUuid] then 102 | threadStats[scriptThreadUuid].indexCount += 1 103 | table.insert(threadHistory[scriptThreadUuid], { 104 | type = "Index", 105 | message = `{object}.{index} -> {table.unpack(response)}`, 106 | }) 107 | end 108 | 109 | return count 110 | end) 111 | end 112 | 113 | function Debugger.HookNewIndex(_: Debugger) 114 | local callback 115 | 116 | callback = VMEnvironment:Hook("__newindex", function(luaState: VMTypes.LuaState) 117 | local object = VMBindings:ToLuau(luaState, 1) 118 | local index = VMBindings:ToLuau(luaState, 2) 119 | local value = VMBindings:ToLuau(luaState, 3) 120 | 121 | local thread = VMBindings:GetCurrentThread(luaState) 122 | 123 | local scriptThread = VMScheduler:GetScriptThread(thread) 124 | local scriptThreadUuid = `Thread: {scriptThread}` 125 | 126 | if threadHistory[scriptThreadUuid] then 127 | table.insert(threadHistory[scriptThreadUuid], { 128 | type = "Index", 129 | message = `{object}.{index} = {value}`, 130 | }) 131 | end 132 | 133 | return callback(luaState) 134 | end) 135 | end 136 | 137 | function Debugger.PollVMStats(_: Debugger) 138 | local bufferData = buffer.tostring(LuauCeption.wasm.memory.data) 139 | local printableCount = 0 140 | 141 | for i = 1, #bufferData do 142 | local char = string.byte(bufferData, i) 143 | 144 | if i % 100000 == 0 then 145 | task.wait() 146 | end 147 | 148 | if char >= 32 and char <= 126 then 149 | printableCount = printableCount + 1 150 | end 151 | end 152 | 153 | local memoryUsage = math.round((printableCount / (LuauCeption.wasm.memory.max * WASM_PAGE_SIZE)) * 1000) 154 | 155 | vmStats.memory = memoryUsage / 1000 156 | vmStats.threads = VMContext:GetValue(`Scheduler.threadCount`) 157 | vmStats.instances = #Sift.Dictionary.keys(VMContext:GetValue("userdata-uuids", {})) 158 | end 159 | 160 | function Debugger.PollLuauRegistry(_: Debugger) 161 | local registryEval = VM:Eval([[ 162 | local registry = {} 163 | 164 | for i, v in getreg() do 165 | registry[tostring(i)] = tostring(v) 166 | end 167 | 168 | return registry 169 | ]], true) 170 | 171 | local newRegistry = {} 172 | 173 | for key, value in registryEval do 174 | local type = "unknown" 175 | 176 | if string.match(value, "function:") then 177 | type = "Function" 178 | elseif string.match(value, "table:") then 179 | type = "Table" 180 | end 181 | 182 | table.insert(newRegistry, { 183 | type = type, 184 | name = `{key}={value}` 185 | }) 186 | end 187 | 188 | table.sort(luauRegistry, function(a, b) 189 | return a.name < b.name 190 | end) 191 | 192 | luauRegistry = newRegistry 193 | end 194 | 195 | function Debugger.UpdateInterface(_: Debugger) 196 | Interface:SetThreadHistory(threadHistory) 197 | Interface:SetThreadStats(threadStats) 198 | Interface:SetThreadList(threadList) 199 | 200 | Interface:SetVMStats(vmStats) 201 | Interface:SetLuauRegistry(luauRegistry) 202 | end 203 | 204 | function Debugger.OnStart(self: Debugger) 205 | self:HookNamecall() 206 | self:HookIndex() 207 | self:HookNewIndex() 208 | 209 | VMScheduler.ScriptThreadSpawned:Connect(function(thread: VMTypes.LuaThread) 210 | local threadUuid = `Thread: {thread}` 211 | 212 | table.insert(threadList, threadUuid) 213 | 214 | threadHistory[threadUuid] = {} 215 | threadStats[threadUuid] = { 216 | childThreadCount = 0, 217 | indexCount = 0, 218 | methodCallCount = 0, 219 | instanceRefCount = 0, 220 | } 221 | 222 | local removedThread = table.remove(threadList, MAXIMUM_THREADS_TO_TRACK) 223 | 224 | if removedThread then 225 | threadHistory[removedThread] = nil 226 | threadStats[removedThread] = nil 227 | end 228 | end) 229 | 230 | while true do 231 | task.wait(1) 232 | 233 | if Interface:GetSelectedPage() ~= "Debugger" then 234 | continue 235 | end 236 | 237 | self:PollVMStats() 238 | self:PollLuauRegistry() 239 | 240 | self:UpdateInterface() 241 | end 242 | end 243 | 244 | export type Debugger = typeof(Debugger) 245 | 246 | return Debugger -------------------------------------------------------------------------------- /Source/Client/Singletons/Explorer.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | Explorer handles the logic for the Explorer tab on the Interface 3 | ]] 4 | 5 | local UserInputService = game:GetService("UserInputService") 6 | 7 | local Package = script.Parent.Parent.Parent 8 | 9 | local Interface = require(Package.Client.Singletons.Interface) 10 | local Theme = require(Package.Client.Interface.Theme) 11 | 12 | local highlightInstance = Instance.new("Highlight") 13 | 14 | local clickToHighlightEnabled = false 15 | local clickToSelectEnabled = false 16 | 17 | local Explorer = {} 18 | 19 | function Explorer.OnStart(_: Explorer) 20 | highlightInstance.FillColor = Theme.AccentColor3 21 | highlightInstance:AddTag(`_DEBUG_IGNORE`) 22 | 23 | -- click to select logic 24 | UserInputService.InputBegan:Connect(function(inputObject: InputObject, gameProcessed: boolean) 25 | if gameProcessed then 26 | return 27 | end 28 | 29 | if not clickToSelectEnabled then 30 | return 31 | end 32 | 33 | if inputObject.UserInputType == Enum.UserInputType.MouseButton1 or inputObject.UserInputType == Enum.UserInputType.Touch then 34 | -- translating the position of our 2d mouse into a 3d object(?) 35 | local mouse = game.UserInputService:GetMouseLocation() 36 | local ray = workspace.Camera:ViewportPointToRay(mouse.X, mouse.Y) 37 | local result = workspace:Raycast(ray.Origin, ray.Direction * 9999999999) 38 | 39 | if result then 40 | Interface:SetSelectedInstance(result.Instance) 41 | end 42 | end 43 | end) 44 | 45 | Interface.ClickToSelectDisabled:Connect(function() 46 | clickToSelectEnabled = false 47 | end) 48 | 49 | Interface.ClickToSelectEnabled:Connect(function() 50 | clickToSelectEnabled = true 51 | end) 52 | 53 | Interface.HighlightSelectedInstanceDisabled:Connect(function() 54 | clickToHighlightEnabled = false 55 | 56 | highlightInstance.Parent = nil 57 | end) 58 | 59 | Interface.HighlightSelectedInstanceEnabled:Connect(function() 60 | clickToHighlightEnabled = true 61 | 62 | highlightInstance.Parent = Interface:GetSelectedInstance() 63 | end) 64 | 65 | Interface.SelectedInstanceChanged:Connect(function() 66 | if not clickToHighlightEnabled then 67 | return 68 | end 69 | 70 | highlightInstance.Parent = Interface:GetSelectedInstance() 71 | end) 72 | end 73 | 74 | export type Explorer = typeof(Explorer) 75 | 76 | return Explorer -------------------------------------------------------------------------------- /Source/Client/Singletons/Focus.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | Focus provides a way to query if the user is focused on the Roblox application or not. 3 | ]] 4 | 5 | local UserInputService = game:GetService("UserInputService") 6 | 7 | local Package = script.Parent.Parent.Parent 8 | 9 | local Signal = require(Package.Parent.Signal) 10 | 11 | local isFocused = true 12 | 13 | local Focus = {} 14 | 15 | Focus.OnFocused = Signal.new() 16 | Focus.OnUnfocused = Signal.new() 17 | 18 | --[[ 19 | Returns a boolean dependant on if the window is active or not. 20 | ]] 21 | function Focus.IsActive(_: Focus) 22 | return isFocused 23 | end 24 | 25 | function Focus.OnStart(self: Focus) 26 | UserInputService.WindowFocusReleased:Connect(function() 27 | isFocused = false 28 | 29 | self.OnUnfocused:Fire() 30 | end) 31 | 32 | UserInputService.WindowFocused:Connect(function() 33 | isFocused = true 34 | 35 | self.OnFocused:Fire() 36 | end) 37 | end 38 | 39 | export type Focus = typeof(Focus) 40 | 41 | return Focus -------------------------------------------------------------------------------- /Source/Client/Singletons/Hotkeys.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | High level explanation of this Singleton is that it's job to provide a singleton for tracking inputs, specific 3 | inputs will allow specific actions to occur, for example; 4 | 5 | - upon F10 being activated, show the Dev-Suite interface. 6 | ]] 7 | 8 | local UserInputService = game:GetService("UserInputService") 9 | 10 | local Package = script.Parent.Parent.Parent 11 | 12 | local Signal = require(Package.Parent.Signal) 13 | 14 | local Settings = require(Package.Client.Singletons.Settings) 15 | 16 | local Hotkeys = {} 17 | 18 | Hotkeys.OnHotkeyPressed = Signal.new() 19 | 20 | function Hotkeys.OnStart(self: Hotkeys) 21 | UserInputService.InputBegan:Connect(function(inputObject: InputObject) 22 | local hotkey = Settings.GetSetting(Settings, "DefaultHotkey") 23 | 24 | if inputObject.KeyCode ~= hotkey then 25 | return 26 | end 27 | 28 | self.OnHotkeyPressed:Fire() 29 | end) 30 | end 31 | 32 | export type Hotkeys = typeof(Hotkeys) 33 | 34 | return Hotkeys -------------------------------------------------------------------------------- /Source/Client/Singletons/LiveEvents.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | LiveEvents handles the logic for the LiveEvents tab on the Interface 3 | ]] 4 | 5 | local Players = game:GetService("Players") 6 | 7 | local Package = script.Parent.Parent.Parent 8 | 9 | local Interface = require(Package.Client.Singletons.Interface) 10 | 11 | local ClassIndex = require(Package.Parent.ClassIndex) 12 | 13 | local MAXIMUM_EVENTS_COUNT = 100 14 | 15 | local instanceIgnoreList = {} 16 | local propertyIgnoreList = {} 17 | 18 | local updateInterfaceThread 19 | 20 | local liveEventsArray = {} 21 | 22 | local LiveEvents = {} 23 | 24 | --[[ 25 | Will block an Instance, or descendants of that instance from appearing on the 'LiveEvents' tab. 26 | ]] 27 | function LiveEvents.AddIgnoredInstance(_: LiveEvents, instance: Instance) 28 | table.insert(instanceIgnoreList, instance) 29 | end 30 | 31 | --[[ 32 | Will block a property from appearing on the 'LiveEvents' tab. 33 | ]] 34 | function LiveEvents.AddIgnoredProperty(_: LiveEvents, property: string) 35 | table.insert(propertyIgnoreList, property) 36 | end 37 | 38 | --[[ 39 | Responsible for updating the interface, has a delay of 0.05 to reduce large changes having an impact on performance. 40 | ]] 41 | function LiveEvents.UpdateInterface(_: LiveEvents, noDelay: boolean?) 42 | if updateInterfaceThread then 43 | task.cancel(updateInterfaceThread) 44 | end 45 | 46 | updateInterfaceThread = task.delay(noDelay and 0 or 0.05, function() 47 | updateInterfaceThread = nil 48 | 49 | Interface:SetLiveEvents(liveEventsArray) 50 | end) 51 | end 52 | 53 | --[[ 54 | Responsible for removing any excess events in the case we hit the maximum events we can log. 55 | ]] 56 | function LiveEvents.ReserveMemory(_: LiveEvents) 57 | local totalCount = #liveEventsArray 58 | 59 | if totalCount > MAXIMUM_EVENTS_COUNT then 60 | table.remove(liveEventsArray, #liveEventsArray) 61 | end 62 | end 63 | 64 | --[[ 65 | Callback responsible for handling what happens when an instance's property, changes. 66 | ]] 67 | function LiveEvents.OnInstanceChanged(self: LiveEvents, object: Instance, property: string) 68 | local propertySecurity = ClassIndex.GetClassMemberSecurity(object.ClassName, property) 69 | 70 | if not ClassIndex.IsKnownClassMember(object.ClassName, property) then 71 | return 72 | end 73 | 74 | table.insert(liveEventsArray, 1, { 75 | instanceName = object.Name, 76 | className = object.ClassName, 77 | propertyName = property, 78 | propertyValue = propertySecurity.Read == "None" and object[property] 79 | or "Unknown (ScriptSecurity)" 80 | }) 81 | 82 | self:ReserveMemory() 83 | self:UpdateInterface() 84 | end 85 | 86 | function LiveEvents.OnStart(self: LiveEvents) 87 | -- events that just annoyed me, we don't need and update when the camera changes. We know. 88 | self:AddIgnoredInstance(workspace.CurrentCamera) 89 | self:AddIgnoredInstance(Players.LocalPlayer) 90 | 91 | self:AddIgnoredProperty(`Parent`) 92 | self:AddIgnoredProperty(`TopbarInset`) 93 | self:AddIgnoredProperty(`NetworkIsSleeping`) 94 | 95 | Players.LocalPlayer.CharacterAdded:Connect(function() 96 | self:AddIgnoredInstance(Players.LocalPlayer.Character) 97 | end) 98 | 99 | if Players.LocalPlayer.Character then 100 | self:AddIgnoredInstance(Players.LocalPlayer.Character) 101 | end 102 | 103 | -- selene: allow(deprecated) 104 | game.ItemChanged:Connect(function(object: Instance, descriptor: string) 105 | if table.find(instanceIgnoreList, object) then 106 | return 107 | end 108 | 109 | if table.find(propertyIgnoreList, descriptor) then 110 | return 111 | end 112 | 113 | for _, ignored in instanceIgnoreList do 114 | if ignored:IsAncestorOf(object) then 115 | return 116 | end 117 | end 118 | 119 | self:OnInstanceChanged(object, descriptor) 120 | end) 121 | end 122 | 123 | export type LiveEvents = typeof(LiveEvents) 124 | 125 | return LiveEvents -------------------------------------------------------------------------------- /Source/Client/Singletons/Logging.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | Logging handles the logic for the Logging tab on the Interface 3 | ]] 4 | 5 | local LogService = game:GetService("LogService") 6 | local RunService = game:GetService("RunService") 7 | 8 | local Package = script.Parent.Parent.Parent 9 | 10 | local Sift = require(Package.Parent.Sift) 11 | 12 | local Interface = require(script.Parent.Interface) 13 | 14 | local Network = require(Package.Client.Network) 15 | 16 | local MAXIMUM_LOG_COUNT = 100 17 | 18 | local logObjectsToSendToServer = {} 19 | 20 | local serverLogs = {} 21 | local clientLogs = {} 22 | 23 | local updateInterfaceThread 24 | 25 | local Logging = {} 26 | 27 | --[[ 28 | Creates a breakpoint in the console, useful for figuring out when something begins/ends. 29 | ]] 30 | function Logging.Breakpoint(_: Logging) 31 | warn(`--[[ {string.rep(`-`, 65)} ]]--`) 32 | end 33 | 34 | --[[ 35 | Responsible for updating the interface, has a delay of 0.05 to reduce large changes having an impact on performance. 36 | ]] 37 | function Logging.UpdateInterface(_: Logging, noDelay: boolean?) 38 | if updateInterfaceThread then 39 | task.cancel(updateInterfaceThread) 40 | end 41 | 42 | updateInterfaceThread = task.delay(noDelay and 0 or 0.05, function() 43 | updateInterfaceThread = nil 44 | 45 | Interface:SetLogs(Sift.Dictionary.merge(serverLogs, clientLogs)) 46 | end) 47 | end 48 | 49 | --[[ 50 | Responsible for removing any excess events in the case we hit the maximum events we can log. 51 | ]] 52 | function Logging.ReserveMemory(_: Logging) 53 | local totalCount = #serverLogs + #clientLogs 54 | 55 | if totalCount > MAXIMUM_LOG_COUNT then 56 | if #serverLogs == #clientLogs then 57 | local logTable = math.random() > 0.5 and clientLogs or serverLogs 58 | 59 | table.remove(logTable, #logTable) 60 | elseif #serverLogs > #clientLogs then 61 | table.remove(serverLogs, #serverLogs) 62 | else 63 | table.remove(clientLogs, #serverLogs) 64 | end 65 | end 66 | end 67 | 68 | --[[ 69 | Invoked when the server logs are given to the player, will occur every second if there's server logs to be 70 | handed over. 71 | 72 | This method requires a time as we're offsetting when the genuine log occurs on the server so that we can 73 | bunch together a list of logs, for performance reasons - we don't want to spam the client with server 74 | logs. 75 | ]] 76 | function Logging.OnServerLogReceived(self: Logging, message: string, type: string, time: number) 77 | local messageType = Enum.MessageType 78 | 79 | local lastLog = serverLogs[1] 80 | 81 | if lastLog and lastLog.message == message then 82 | lastLog.count += 1 83 | else 84 | table.insert(serverLogs, 1, { 85 | message = message, 86 | type = messageType[type], 87 | context = "Server", 88 | time = time, 89 | count = 1 90 | }) 91 | end 92 | 93 | self:ReserveMemory() 94 | self:UpdateInterface() 95 | end 96 | 97 | --[[ 98 | Called when the client logs are created. 99 | ]] 100 | function Logging.OnClientLogReceived(self: Logging, message: string, type: Enum.MessageType) 101 | local logTime = DateTime.now().UnixTimestampMillis 102 | 103 | table.insert(logObjectsToSendToServer, { 104 | message = message, 105 | type = type.Name, 106 | time = logTime 107 | }) 108 | 109 | local lastLog = clientLogs[1] 110 | 111 | if lastLog and lastLog.message == message then 112 | lastLog.count += 1 113 | else 114 | table.insert(clientLogs, 1, { 115 | message = message, 116 | type = type, 117 | context = "Client", 118 | time = logTime, 119 | count = 1 120 | }) 121 | end 122 | 123 | self:ReserveMemory() 124 | self:UpdateInterface() 125 | end 126 | 127 | function Logging.OnStart(self: Logging) 128 | if not RunService:IsStudio() then 129 | LogService.MessageOut:Connect(function(message: string, type: Enum.MessageType) 130 | self:OnClientLogReceived(message, type) 131 | end) 132 | end 133 | 134 | Network.ServerReportingLogs.On(function(logs) 135 | for _, log in logs do 136 | self:OnServerLogReceived(log.message, log.type, log.time) 137 | end 138 | end) 139 | 140 | Interface.ClearLogsRequested:Connect(function() 141 | LogService:ClearOutput() 142 | 143 | clientLogs = {} 144 | serverLogs = {} 145 | 146 | self:UpdateInterface(true) 147 | end) 148 | 149 | Interface.ExportLogsRequested:Connect(function() 150 | 151 | end) 152 | 153 | Interface.BreakpointLogsRequested:Connect(function() 154 | self:Breakpoint() 155 | end) 156 | 157 | --[[ 158 | Routinely send client logs to the server, we want the server to know all client logs so that if we want to 159 | export our logs, we can do so without sending the server the entire log history of both the server and 160 | client. 161 | 162 | That would be a bad idea, so instead we just incremental update the server. 163 | ]] 164 | while true do 165 | task.wait(1) 166 | 167 | if #logObjectsToSendToServer == 0 then 168 | continue 169 | end 170 | 171 | Network.ClientReportingLogs.Fire(logObjectsToSendToServer) 172 | logObjectsToSendToServer = {} 173 | end 174 | end 175 | 176 | export type ServerLogObject = { 177 | message: string, 178 | type: Enum.MessageType, 179 | time: number 180 | } 181 | 182 | export type Logging = typeof(Logging) 183 | 184 | return Logging -------------------------------------------------------------------------------- /Source/Client/Singletons/RemoteEvents.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | RemoteEvents handles the logic for the RemoteEvents tab on the Interface 3 | ]] 4 | 5 | local HttpService = game:GetService("HttpService") 6 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 7 | 8 | local Package = script.Parent.Parent.Parent 9 | 10 | local decodeFromJSON = require(Package.Utilities.Remotes.decodeFromJSON) 11 | local Network = require(Package.Client.Network) 12 | 13 | local Interface = require(Package.Client.Singletons.Interface) 14 | 15 | local MAXIMUM_REMOTE_COUNT = 10000 16 | 17 | local updateInterfaceThread 18 | 19 | local remoteEventArray = {} 20 | local remoteEventCallbacks = {} 21 | 22 | local RemoteEvents = {} 23 | 24 | --[[ 25 | Responsible for updating the interface, has a delay of 0.05 to reduce large changes having an impact on performance. 26 | ]] 27 | function RemoteEvents.UpdateInterface(_: RemoteEvents, noDelay: boolean?) 28 | if updateInterfaceThread then 29 | task.cancel(updateInterfaceThread) 30 | end 31 | 32 | updateInterfaceThread = task.delay(noDelay and 0 or 0.05, function() 33 | updateInterfaceThread = nil 34 | 35 | Interface:SetInterceptedRemotes(remoteEventArray) 36 | end) 37 | end 38 | 39 | --[[ 40 | Responsible for removing any excess events in the case we hit the maximum events we can log. 41 | ]] 42 | function RemoteEvents.ReserveMemory(_: RemoteEvents) 43 | local totalCount = #remoteEventArray 44 | 45 | if totalCount > MAXIMUM_REMOTE_COUNT then 46 | local object = table.remove(totalCount, #totalCount) 47 | 48 | if object.eventUUID and remoteEventCallbacks[object.eventUUID] then 49 | remoteEventCallbacks[object.eventUUID] = nil 50 | end 51 | end 52 | end 53 | 54 | --[[ 55 | Hooks the remote event on the client, so this listens to when the client receives information from the server. 56 | ]] 57 | function RemoteEvents.HookRemoteEvent(self: RemoteEvents, event: RemoteEvent) 58 | if RemoteEvents[event] then 59 | return 60 | end 61 | 62 | RemoteEvents[event] = true 63 | 64 | local connection = event.OnClientEvent:Connect(function(...) 65 | self:OnClientEventReceived({ 66 | event = event, 67 | arguments = { ... } 68 | }) 69 | end) 70 | 71 | event.Destroying:Once(function() 72 | connection:Disconnect() 73 | end) 74 | end 75 | 76 | --[[ 77 | Queries, and then hooks any remote events found in the scope of where we search for remote events. 78 | ]] 79 | function RemoteEvents.QueryRemoteEvents(self: RemoteEvents) 80 | for _, object in ReplicatedStorage:GetDescendants() do 81 | if object:IsA("RemoteEvent") or object:IsA("UnreliableRemoteEvent") then 82 | if object.Name == "DEBUG_REMOTES_RELIABLE" or object.Name == "DEBUG_REMOTES_UNRELIABLE" then 83 | continue 84 | end 85 | 86 | self:HookRemoteEvent(object) 87 | end 88 | end 89 | end 90 | 91 | --[[ 92 | Remote event received from the server, so this is actually the client invoking the server, then the server sends us 93 | that request back so we can display it here. 94 | 95 | We can't hook 96 | 97 | Client -> Server 98 | 99 | But what we can do is just relay that back: 100 | 101 | Client -> Server -> Client 102 | ]] 103 | function RemoteEvents.OnServerEventReceived(self: RemoteEvents, event: ServerEventObject) 104 | local arguments = decodeFromJSON(event.arguments, event.instances) 105 | local eventUuid = HttpService:GenerateGUID(false) 106 | local ancestorTree = {} 107 | 108 | local parent = event.event.Parent 109 | 110 | while parent ~= game do 111 | table.insert(ancestorTree, { 112 | name = parent.Name, 113 | class = parent.ClassName 114 | }) 115 | 116 | parent = parent.Parent 117 | end 118 | 119 | remoteEventCallbacks[eventUuid] = function() 120 | local object = event.event 121 | 122 | object:FireServer(table.unpack(arguments)) 123 | end 124 | 125 | table.insert(remoteEventArray, { 126 | eventType = event.event.ClassName, 127 | eventUUID = eventUuid, 128 | eventName = event.event.Name, 129 | eventData = event.arguments, 130 | eventSize = `{string.len(event.arguments) / 1000}kb`, 131 | ancestors = ancestorTree, 132 | context = "Server", 133 | }) 134 | 135 | self:ReserveMemory() 136 | self:UpdateInterface() 137 | end 138 | 139 | --[[ 140 | This is invoked when the client receives data from an hooked remote event. 141 | ]] 142 | function RemoteEvents.OnClientEventReceived(self: RemoteEvents, event: ClientEventObject) 143 | local ancestorTree = {} 144 | 145 | local parent = event.event.Parent 146 | 147 | while parent ~= game do 148 | table.insert(ancestorTree, { 149 | name = parent.Name, 150 | class = parent.ClassName 151 | }) 152 | 153 | parent = parent.Parent 154 | end 155 | 156 | local encodedEventData = HttpService:JSONEncode(event.arguments) 157 | 158 | table.insert(remoteEventArray, { 159 | eventType = event.event.ClassName, 160 | eventUUID = HttpService:GenerateGUID(), 161 | eventName = event.event.Name, 162 | eventData = encodedEventData, 163 | eventSize = `{string.len(encodedEventData) / 1000}kb`, 164 | ancestors = ancestorTree, 165 | context = "Server", 166 | }) 167 | 168 | self:ReserveMemory() 169 | self:UpdateInterface() 170 | end 171 | 172 | function RemoteEvents.OnStart(self: RemoteEvents) 173 | self:QueryRemoteEvents() 174 | 175 | ReplicatedStorage.DescendantAdded:Connect(function() 176 | self:QueryRemoteEvents() 177 | end) 178 | 179 | Interface.RepeatRemoteEventCallRequested:Connect(function(remoteUuid) 180 | if remoteEventCallbacks[remoteUuid] then 181 | remoteEventCallbacks[remoteUuid]() 182 | else 183 | warn(`Unable to find remote UUID for: '{remoteUuid}'`) 184 | end 185 | end) 186 | 187 | Network.ServerRemotesFired.On(function(object) 188 | self:OnServerEventReceived(object) 189 | end) 190 | end 191 | 192 | export type ServerEventObject = { 193 | event: RemoteEvent | UnreliableRemoteEvent, 194 | arguments: string, 195 | instances: { Instance } 196 | } 197 | 198 | export type ClientEventObject = { 199 | event: RemoteEvent | UnreliableRemoteEvent, 200 | arguments: { any } 201 | } 202 | 203 | export type RemoteEvents = typeof(RemoteEvents) 204 | 205 | return RemoteEvents -------------------------------------------------------------------------------- /Source/Client/Singletons/Sandbox.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | ]] 4 | 5 | local Package = script.Parent.Parent.Parent 6 | 7 | local Network = require(Package.Client.Network) 8 | 9 | local VM = require(Package.Client.Singletons.VM) 10 | 11 | local Interface = require(Package.Client.Singletons.Interface) 12 | 13 | local Sandbox = {} 14 | 15 | function Sandbox.OnStart(_: Sandbox) 16 | Interface.EvalClientCodeRequested:Connect(function(source: string) 17 | VM:Eval(source) 18 | end) 19 | 20 | Interface.EvalServerCodeRequested:Connect(function(source: string) 21 | Network.ExecuteServerScriptRequested.Fire(source) 22 | end) 23 | end 24 | 25 | export type Sandbox = typeof(Sandbox) 26 | 27 | return Sandbox -------------------------------------------------------------------------------- /Source/Client/Singletons/ScriptHub.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | ScriptHub handles the logic for the ScriptHub tab on the Interface 3 | ]] 4 | 5 | local Package = script.Parent.Parent.Parent 6 | 7 | local Interface = require(Package.Client.Singletons.Interface) 8 | local VM = require(Package.Client.Singletons.VM) 9 | 10 | local INTERNALLY_REGISTERED_SCRIPTS = table.freeze({ 11 | { 12 | name = "Infinite Yield", 13 | description = "Loads the Admin 'Infinite Yield'", 14 | source = [[loadstring(game:HttpGetAsync("https://raw.githubusercontent.com/EdgeIY/infiniteyield/master/source"))()]] 15 | }, 16 | { 17 | name = "Hello, World", 18 | description = "Development script used to test the Script Hub", 19 | source = [[loadstring(game:HttpGetAsync("https://pastebin.com/raw/uXvzZpcM"))()]] 20 | } 21 | }) 22 | 23 | local ScriptHub = {} 24 | 25 | function ScriptHub.OnStart(_: ScriptHub) 26 | Interface:SetScripts(table.clone(INTERNALLY_REGISTERED_SCRIPTS)) 27 | 28 | Interface.ExecuteScriptRequested:Connect(function(code: string) 29 | VM:Eval(code, true) 30 | end) 31 | end 32 | 33 | export type ScriptHub = typeof(ScriptHub) 34 | 35 | return ScriptHub -------------------------------------------------------------------------------- /Source/Client/Singletons/Settings.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | Settings is responsible for not only fetching settings from the server, when, and once they change - but 3 | allowing other singletons to query these settings. 4 | ]] 5 | 6 | local Package = script.Parent.Parent.Parent 7 | 8 | local Signal = require(Package.Parent.Signal) 9 | 10 | local Network = require(Package.Client.Network) 11 | 12 | local clientSettings = {} 13 | 14 | local Settings = {} 15 | 16 | Settings.OnUpdated = Signal.new() 17 | 18 | --[[ 19 | Query the setting of a value. 20 | ]] 21 | function Settings.GetSetting(_: Settings, setting: string) 22 | return clientSettings[setting] 23 | end 24 | 25 | function Settings.OnStart(_: Settings) 26 | local function updateClientSettings(serverSettings) 27 | clientSettings = { 28 | DefaultHotkey = Enum.KeyCode[serverSettings.DefaultHotkey] 29 | } 30 | end 31 | 32 | Network.ServerSettingsUpdated.SetCallback(function(serverSettings) 33 | updateClientSettings(serverSettings) 34 | end) 35 | 36 | updateClientSettings(Network.FetchSettingsRequested.Call()) 37 | end 38 | 39 | export type Settings = typeof(Settings) 40 | 41 | return Settings -------------------------------------------------------------------------------- /Source/Client/Singletons/Statistics.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | ]] 4 | 5 | local Stats = game:GetService("Stats") 6 | 7 | local Package = script.Parent.Parent.Parent 8 | 9 | local Interface = require(Package.Client.Singletons.Interface) 10 | 11 | local AUTO_UPDATE_ENABLED = false 12 | local INTERNAL_MEMORY_TAGS_MAP = table.freeze({ 13 | Roblox = { 14 | Enum.DeveloperMemoryTag.LuaHeap, 15 | Enum.DeveloperMemoryTag.Script, 16 | Enum.DeveloperMemoryTag.Signals, 17 | Enum.DeveloperMemoryTag.Internal, 18 | Enum.DeveloperMemoryTag.HttpCache, 19 | }, 20 | World = { 21 | Enum.DeveloperMemoryTag.Sounds, 22 | Enum.DeveloperMemoryTag.GeometryCSG, 23 | Enum.DeveloperMemoryTag.Navigation, 24 | Enum.DeveloperMemoryTag.TerrainVoxels, 25 | Enum.DeveloperMemoryTag.Animation, 26 | Enum.DeveloperMemoryTag.Gui, 27 | }, 28 | Physics = { 29 | Enum.DeveloperMemoryTag.PhysicsCollision, 30 | Enum.DeveloperMemoryTag.PhysicsParts, 31 | }, 32 | Graphics = { 33 | Enum.DeveloperMemoryTag.GraphicsSolidModels, 34 | Enum.DeveloperMemoryTag.GraphicsSpatialHash, 35 | Enum.DeveloperMemoryTag.GraphicsTerrain, 36 | Enum.DeveloperMemoryTag.GraphicsParticles, 37 | Enum.DeveloperMemoryTag.GraphicsTexture, 38 | } 39 | }) 40 | 41 | local Statistics = {} 42 | 43 | function Statistics.UpdateMemoryStats(_: Statistics) 44 | local memoryStats = {} 45 | 46 | for category, objects in INTERNAL_MEMORY_TAGS_MAP do 47 | memoryStats[category] = {} 48 | 49 | for _, memoryTag in objects do 50 | local name = memoryTag.Name 51 | local stat = Stats:GetMemoryUsageMbForTag(memoryTag) 52 | 53 | memoryStats[category][name] = math.round(stat * 100) / 100 54 | end 55 | end 56 | 57 | Interface:SetMemoryStats(memoryStats) 58 | end 59 | 60 | function Statistics.UpdateNetworkStats(_: Statistics) 61 | Interface:SetNetworkStats({ 62 | Data = { 63 | Send = math.round(Stats.DataSendKbps * 100) / 100, 64 | Recv = math.round(Stats.DataReceiveKbps * 100) / 100, 65 | }, 66 | Physics = { 67 | Send = math.round(Stats.PhysicsSendKbps * 100) / 100, 68 | Recv = math.round(Stats.PhysicsReceiveKbps * 100) / 100, 69 | } 70 | }) 71 | end 72 | 73 | function Statistics.UpdateInstanceStats(_: Statistics) 74 | Interface:SetInstanceStats({ 75 | Moving = math.round(Stats.MovingPrimitivesCount * 100) / 100, 76 | Static = math.round(Stats.PrimitivesCount * 100) / 100, 77 | 78 | Total = math.round(Stats.InstanceCount * 100) / 100, 79 | Collisions = math.round(Stats.ContactsCount * 100) / 100 80 | }) 81 | end 82 | 83 | function Statistics.UpdateFrameStats(_: Statistics) 84 | Interface:SetFrameStats({ 85 | Frame = -1, 86 | Heartbeat = math.round(Stats.HeartbeatTime * 100) / 100, 87 | Physics = math.round(Stats.PhysicsStepTime * 100) / 100, 88 | 89 | ["GPU Time"] = math.round(Stats.RenderGPUFrameTime * 100) / 100, 90 | ["CPU Time"] = math.round(Stats.RenderCPUFrameTime * 100) / 100, 91 | }) 92 | end 93 | 94 | function Statistics.UpdateDrawcallStats(_: Statistics) 95 | Interface:SetDrawcallStats({ 96 | Scene = { 97 | Triangles = math.round(Stats.SceneTriangleCount * 100) / 100, 98 | Drawcalls = math.round(Stats.SceneDrawcallCount * 100) / 100, 99 | }, 100 | Shadows = { 101 | Triangles = math.round(Stats.ShadowsTriangleCount * 100) / 100, 102 | Drawcalls = math.round(Stats.ShadowsDrawcallCount * 100) / 100, 103 | } 104 | }) 105 | end 106 | 107 | function Statistics.UpdateStatsBulk(self: Statistics) 108 | self:UpdateDrawcallStats() 109 | task.wait() 110 | self:UpdateFrameStats() 111 | task.wait() 112 | self:UpdateInstanceStats() 113 | task.wait() 114 | self:UpdateMemoryStats() 115 | task.wait() 116 | self:UpdateNetworkStats() 117 | end 118 | 119 | function Statistics.OnStart(self: Statistics) 120 | Interface:SetMetadataStats({ 121 | -- selene: allow(undefined_variable) 122 | ["Rbxl Version"] = string.split(version(), ".")[2], 123 | ["Place Version"] = game.PlaceVersion, 124 | }) 125 | 126 | Interface.RefreshStatsRequested:Connect(function() 127 | self:UpdateStatsBulk() 128 | end) 129 | 130 | while AUTO_UPDATE_ENABLED do 131 | task.wait(1) 132 | 133 | self:UpdateStatsBulk() 134 | end 135 | end 136 | 137 | export type Statistics = typeof(Statistics) 138 | 139 | return Statistics -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Constants.luau: -------------------------------------------------------------------------------- 1 | local LUAI_MAXCSTACK = 8000 2 | 3 | return { 4 | LUAI_MAXCSTACK = LUAI_MAXCSTACK, 5 | LUAU_GLOBALSINDEX = -LUAI_MAXCSTACK - 2002, 6 | LUA_REGISTRYINDEX = -LUAI_MAXCSTACK - 2000, 7 | 8 | lua_Status = { 9 | LUA_OK = 0, 10 | LUA_YIELD = 1, 11 | LUA_ERRRUN = 2, 12 | LUA_ERRSYNTAX = 3, 13 | LUA_ERRMEM = 4, 14 | LUA_ERRERR = 5, 15 | LUA_BREAK = 6, 16 | }; 17 | } -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Context.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | ]] 4 | local contextMap = {} 5 | 6 | local Context = {} 7 | 8 | --[[ 9 | Set a value for this specific LuaState. 10 | ]] 11 | function Context.SetValue(_: Context, key: any, value: any) 12 | contextMap[key] = value 13 | end 14 | 15 | --[[ 16 | Update a value for this specific LuaState. 17 | ]] 18 | function Context.UpdateValue(_: Context, key: any, compute: (value: any) -> any) 19 | contextMap[key] = compute(contextMap[key]) 20 | end 21 | 22 | --[[ 23 | Get a value for this specific LuaState. 24 | ]] 25 | function Context.GetValue(_: Context, key: any, default: any?) 26 | return contextMap[key] ~= nil and contextMap[key] or default 27 | end 28 | 29 | --[[ 30 | Get a value for this specific LuaState. 31 | ]] 32 | function Context.GetMap(_: Context) 33 | return contextMap 34 | end 35 | 36 | export type ContextMap = { } 37 | 38 | export type Context = typeof(Context) 39 | 40 | return Context -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Environment.luau: -------------------------------------------------------------------------------- 1 | --# selene: allow(shadowing) 2 | 3 | --[[ 4 | 5 | ]] 6 | 7 | local Package = script.Parent.Parent.Parent.Parent 8 | 9 | local LuauCeption = require(Package.Vendor.LuauCeption) 10 | 11 | local VMBindings = require(Package.Client.Singletons.VM.Bindings) 12 | local VMTypes = require(Package.Client.Singletons.VM.Types) 13 | local VMMacros = require(Package.Client.Singletons.VM.Macros) 14 | local VMContext = require(Package.Client.Singletons.VM.Context) 15 | local VMConstants = require(Package.Client.Singletons.VM.Constants) 16 | 17 | local RobloxGlobals = require(Package.Client.Singletons.VM.Roblox.Globals) 18 | local RobloxGlobalTable = require(Package.Client.Singletons.VM.Roblox.GlobalTable) 19 | local RobloxSharedTable = require(Package.Client.Singletons.VM.Roblox.SharedTable) 20 | local RobloxEnvironmentTable = require(Package.Client.Singletons.VM.Roblox.EnvironmentTable) 21 | 22 | local ExploitGlobals = require(Package.Client.Singletons.VM.Exploit.Globals) 23 | local ExploitEnvironmentTable = require(Package.Client.Singletons.VM.Exploit.EnvironmentTable) 24 | 25 | local TaskLibrary = require(Package.Client.Singletons.VM.Roblox.Libraries.Task) 26 | 27 | local cFunctions = LuauCeption.wasm.cfns 28 | 29 | local Environment = {} 30 | 31 | --[[ 32 | 33 | ]] 34 | function Environment.Hook(_: Environment, metamethod: string, caller: (luaState: VMTypes.LuaState) -> number) 35 | local metamethods = VMContext:GetValue(`metatable.methods.source`) 36 | local pointer = metamethods[metamethod] 37 | 38 | assert(pointer, `failed to hook metamethod '{metamethod}' doesn't exist!`) 39 | 40 | return VMBindings:OverwriteCFunction(pointer, function(...) 41 | local _, response = xpcall(caller, function(response) 42 | warn(`failed to hook metamethod '{metamethod}' with error: {response}`) 43 | print(debug.traceback()) 44 | end, ...) 45 | 46 | return response 47 | end) 48 | end 49 | 50 | --[[ 51 | 52 | ]] 53 | function Environment.Implement(_: Environment, luaState: VMTypes.LuaState, name: string, source: any) 54 | VMMacros.lua_newtable(luaState) 55 | 56 | for objName, callback in source do 57 | VMMacros.lua_pushcfunction(luaState, VMBindings:LoadCFunction(luaState, function(luaState: VMTypes.LuaState) 58 | local arguments = {} 59 | 60 | for index = 1, cFunctions.lua_gettop(luaState) do 61 | table.insert(arguments, VMBindings:ToLuau(luaState, index)) 62 | end 63 | 64 | local results = table.pack(callback(table.unpack(arguments))) 65 | 66 | for index = 1, results.n do 67 | VMBindings:FromLuau(luaState, results[index]) 68 | end 69 | 70 | return results.n 71 | end), VMBindings:LoadCString(objName)) 72 | 73 | cFunctions.lua_setfield(luaState, -2, VMBindings:LoadCString(objName)) 74 | end 75 | 76 | VMMacros.lua_setglobal(luaState, VMBindings:LoadCString(name)) 77 | end 78 | 79 | --[[ 80 | 81 | ]] 82 | function Environment.WriteEnvironment(self: Environment, luaState: VMTypes.LuaState) 83 | RobloxGlobalTable(luaState) 84 | RobloxSharedTable(luaState) 85 | RobloxEnvironmentTable(luaState) 86 | 87 | RobloxGlobals(luaState) 88 | TaskLibrary(luaState) 89 | 90 | ExploitEnvironmentTable(luaState) 91 | ExploitGlobals(luaState) 92 | 93 | self:Implement(luaState, "Axes", Axes) 94 | self:Implement(luaState, "BrickColor", BrickColor) 95 | self:Implement(luaState, "CatalogSearchParams", CatalogSearchParams) 96 | self:Implement(luaState, "CFrame", CFrame) 97 | self:Implement(luaState, "Color3", Color3) 98 | self:Implement(luaState, "ColorSequence", ColorSequence) 99 | self:Implement(luaState, "ColorSequenceKeypoint", ColorSequenceKeypoint) 100 | self:Implement(luaState, "DateTime", DateTime) 101 | self:Implement(luaState, "Faces", Faces) 102 | self:Implement(luaState, "FloatCurveKey", FloatCurveKey) 103 | self:Implement(luaState, "Font", Font) 104 | self:Implement(luaState, "Instance", Instance) 105 | self:Implement(luaState, "NumberRange", NumberRange) 106 | self:Implement(luaState, "NumberSequence", NumberSequence) 107 | self:Implement(luaState, "NumberSequenceKeypoint", NumberSequenceKeypoint) 108 | self:Implement(luaState, "OverlapParams", OverlapParams) 109 | self:Implement(luaState, "PathWaypoint", PathWaypoint) 110 | self:Implement(luaState, "PhysicalProperties", PhysicalProperties) 111 | self:Implement(luaState, "Random", Random) 112 | self:Implement(luaState, "Ray", Ray) 113 | self:Implement(luaState, "RaycastParams", RaycastParams) 114 | self:Implement(luaState, "Rect", Rect) 115 | self:Implement(luaState, "Region3", Region3) 116 | self:Implement(luaState, "Region3int16", Region3int16) 117 | self:Implement(luaState, "RotationCurveKey", RotationCurveKey) 118 | self:Implement(luaState, "SharedTable", SharedTable) 119 | self:Implement(luaState, "TweenInfo", TweenInfo) 120 | self:Implement(luaState, "UDim", UDim) 121 | self:Implement(luaState, "UDim2", UDim2) 122 | self:Implement(luaState, "Vector2", Vector2) 123 | self:Implement(luaState, "Vector2int16", Vector2int16) 124 | self:Implement(luaState, "Vector3", Vector3) 125 | self:Implement(luaState, "Vector3int16", Vector3int16) 126 | end 127 | 128 | --[[ 129 | 130 | ]] 131 | function Environment.WriteFunctionEnvironment(_: Environment, luaState: VMTypes.LuaState) 132 | cFunctions.lua_getfield(luaState, VMConstants.LUA_REGISTRYINDEX, VMBindings:LoadCString("script.exploit.environment")) 133 | 134 | cFunctions.lua_setfenv(luaState, -2) 135 | end 136 | 137 | export type Environment = typeof(Environment) 138 | 139 | return Environment -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Exploit/EnvironmentTable.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local LuauCeption = require(Package.Vendor.LuauCeption) 4 | 5 | local VMBindings = require(Package.Client.Singletons.VM.Bindings) 6 | local VMTypes = require(Package.Client.Singletons.VM.Types) 7 | local VMConstants = require(Package.Client.Singletons.VM.Constants) 8 | local VMMacros = require(Package.Client.Singletons.VM.Macros) 9 | 10 | local cFunctions = LuauCeption.wasm.cfns 11 | 12 | return function(luaState: VMTypes.LuaState) 13 | VMMacros.lua_newtable(luaState) 14 | VMMacros.lua_newtable(luaState) 15 | 16 | cFunctions.lua_pushstring(luaState, VMBindings:LoadCString("The metatable is locked")) 17 | cFunctions.lua_setfield(luaState, -2, VMBindings:LoadCString("__metatable")) 18 | 19 | cFunctions.lua_pushvalue(luaState, VMConstants.LUAU_GLOBALSINDEX) 20 | cFunctions.lua_setfield(luaState, -2, VMBindings:LoadCString("__index")) 21 | 22 | cFunctions.lua_setmetatable(luaState, -2) 23 | 24 | cFunctions.lua_getfield(luaState, VMConstants.LUA_REGISTRYINDEX, VMBindings:LoadCString("global")) 25 | cFunctions.lua_setfield(luaState, -2, VMBindings:LoadCString("_G")) 26 | 27 | cFunctions.lua_getfield(luaState, VMConstants.LUA_REGISTRYINDEX, VMBindings:LoadCString("shared")) 28 | cFunctions.lua_setfield(luaState, -2, VMBindings:LoadCString("shared")) 29 | 30 | cFunctions.lua_setfield(luaState, VMConstants.LUA_REGISTRYINDEX, VMBindings:LoadCString("script.exploit.environment")) 31 | end 32 | -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Exploit/Hooks/CoreGui.luau: -------------------------------------------------------------------------------- 1 | local Players = game:GetService("Players") 2 | local Package = script.Parent.Parent.Parent.Parent.Parent.Parent 3 | 4 | local LuauCeption = require(Package.Vendor.LuauCeption) 5 | 6 | local VMBindings = require(Package.Client.Singletons.VM.Bindings) 7 | local VMEnvironment = require(Package.Client.Singletons.VM.Environment) 8 | local VMTypes = require(Package.Client.Singletons.VM.Types) 9 | local VMMacros = require(Package.Client.Singletons.VM.Macros) 10 | 11 | local cFunctions = LuauCeption.wasm.cfns 12 | 13 | local function getService(luaState: VMTypes.LuaState) 14 | local serviceToRequire = VMBindings:ToLuau(luaState, 2) 15 | 16 | if serviceToRequire == "CoreGui" then 17 | VMBindings:FromLuau(luaState, Players.LocalPlayer.PlayerGui) 18 | else 19 | VMBindings:FromLuau(luaState, game:GetService(serviceToRequire)) 20 | end 21 | 22 | return 1 23 | end 24 | 25 | local function __index() 26 | local __index 27 | 28 | __index = VMEnvironment:Hook("__index", function(luaState: VMTypes.LuaState) 29 | local instance = VMBindings:ToLuau(luaState, 1) 30 | 31 | local indexPointer = VMMacros.lua_tostring(luaState, 2) 32 | local index = VMBindings:ReadCString(indexPointer) 33 | 34 | if instance == game and index == "CoreGui" then 35 | VMBindings:FromLuau(luaState, Players.LocalPlayer.PlayerGui) 36 | 37 | return 1 38 | end 39 | 40 | return __index(luaState) 41 | end) 42 | end 43 | 44 | local function __namecall() 45 | local __namecall 46 | 47 | __namecall = VMEnvironment:Hook("__namecall", function(luaState: VMTypes.LuaState) 48 | local methodNamePointer = cFunctions.lua_namecallatom(luaState, 0) 49 | local methodName = VMBindings:ReadCString(methodNamePointer) 50 | 51 | if methodName == "GetService" then 52 | return getService(luaState) 53 | end 54 | 55 | return __namecall(luaState) 56 | end) 57 | end 58 | 59 | return function() 60 | __index() 61 | __namecall() 62 | end 63 | -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Exploit/Hooks/HttpGetAsync.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent.Parent 2 | 3 | local LuauCeption = require(Package.Vendor.LuauCeption) 4 | 5 | local VMBindings = require(Package.Client.Singletons.VM.Bindings) 6 | local VMEnvironment = require(Package.Client.Singletons.VM.Environment) 7 | local VMTypes = require(Package.Client.Singletons.VM.Types) 8 | 9 | local Network = require(Package.Client.Network) 10 | 11 | local cFunctions = LuauCeption.wasm.cfns 12 | 13 | local function getAsync(luaState: VMTypes.LuaState) 14 | local url = VMBindings:ToLuau(luaState, 2) 15 | local response = Network.HttpGetRequest.Call(url) 16 | 17 | VMBindings:FromLuau(luaState, response) 18 | 19 | return 1 20 | end 21 | 22 | return function() 23 | local __namecall 24 | 25 | __namecall = VMEnvironment:Hook("__namecall", function(luaState: VMTypes.LuaState) 26 | local methodName = VMBindings:ReadCString(cFunctions.lua_namecallatom(luaState, 0)) 27 | local object = VMBindings:ToLuau(luaState, 1) 28 | 29 | if object == game and (methodName == "HttpGetAsync" or methodName == "HttpGet") then 30 | return getAsync(luaState) 31 | end 32 | 33 | return __namecall(luaState) 34 | end) 35 | end 36 | -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Macros.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | Macros is more embedded into the Luau VM, and some understanding of the Luau repository, it essentially 3 | just implements the C++ macros that make life a little bit easier. 4 | ]] 5 | 6 | local Package = script.Parent.Parent.Parent.Parent 7 | 8 | local LuauCeption = require(Package.Vendor.LuauCeption) 9 | 10 | local VMTypes = require(Package.Client.Singletons.VM.Types) 11 | local VMConstants = require(Package.Client.Singletons.VM.Constants) 12 | 13 | local cFunctions = LuauCeption.wasm.cfns 14 | 15 | local Macros = {} 16 | 17 | -- #define lua_tonumber(luaState, i) lua_tonumberx(luaState, i, NULL) 18 | function Macros.lua_tonumber(luaState: VMTypes.LuaState, i: number) 19 | return cFunctions.lua_tonumberx(luaState, i, 0) 20 | end 21 | 22 | -- #define lua_upvalueindex(i) (LUA_GLOBALSINDEX - (i)) 23 | function Macros.lua_upvalueindex(i: number) 24 | return VMConstants.LUAU_GLOBALSINDEX - i 25 | end 26 | 27 | -- #define lua_tointeger(luaState, i) lua_tointegerx(luaState, i, NULL) 28 | function Macros.lua_tointeger(luaState: VMTypes.LuaState, i: number) 29 | return cFunctions.lua_tointegerx(luaState, i) 30 | end 31 | 32 | -- #define lua_tounsigned(luaState, i) lua_tounsignedx(luaState, i, NULL) 33 | function Macros.lua_tounsigned(_: VMTypes.LuaState) 34 | -- idk 35 | end 36 | 37 | -- #define lua_pop(luaState, n) lua_settop(luaState, -(n)-1) 38 | function Macros.lua_pop(luaState: VMTypes.LuaState, n: number) 39 | return cFunctions.lua_settop(luaState, -n - 1) 40 | end 41 | 42 | -- #define lua_newtable(luaState) lua_createtable(luaState, 0, 0) 43 | function Macros.lua_newtable(luaState: VMTypes.LuaState) 44 | return cFunctions.lua_createtable(luaState, 0, 0) 45 | end 46 | 47 | -- #define lua_newuserdata(luaState, s) lua_newuserdatatagged(luaState, s, 0) 48 | function Macros.lua_newuserdata(luaState: VMTypes.LuaState, s: number) 49 | return cFunctions.lua_newuserdatatagged(luaState, s, 0) 50 | end 51 | 52 | -- #define lua_strlen(luaState, i) lua_objlen(luaState, (i)) 53 | function Macros.lua_strlen(luaState: VMTypes.LuaState, i: number) 54 | return cFunctions.lua_objlen(luaState, i) 55 | end 56 | 57 | -- #define lua_isfunction(L, n) (lua_type(L, (n)) == LUA_TFUNCTION) 58 | function Macros.lua_isfunction(_: VMTypes.LuaState) 59 | -- idk 60 | end 61 | 62 | -- #define lua_istable(L, n) (lua_type(L, (n)) == LUA_TTABLE) 63 | function Macros.lua_istable(_: VMTypes.LuaState) 64 | -- idk 65 | end 66 | 67 | -- #define lua_islightuserdata(L, n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) 68 | function Macros.lua_islightuserdata(_: VMTypes.LuaState) 69 | -- idk 70 | end 71 | 72 | -- #define lua_isnil(L, n) (lua_type(L, (n)) == LUA_TNIL) 73 | function Macros.lua_isnil(_: VMTypes.LuaState) 74 | -- idk 75 | end 76 | 77 | -- #define lua_isboolean(L, n) (lua_type(L, (n)) == LUA_TBOOLEAN) 78 | function Macros.lua_isboolean(_: VMTypes.LuaState) 79 | -- idk 80 | end 81 | 82 | -- #define lua_isvector(L, n) (lua_type(L, (n)) == LUA_TVECTOR) 83 | function Macros.lua_isvector(_: VMTypes.LuaState) 84 | -- idk 85 | end 86 | 87 | -- #define lua_isthread(L, n) (lua_type(L, (n)) == LUA_TTHREAD) 88 | function Macros.lua_isthread(_: VMTypes.LuaState) 89 | -- idk 90 | end 91 | 92 | -- #define lua_isbuffer(L, n) (lua_type(L, (n)) == LUA_TBUFFER) 93 | function Macros.lua_isbuffer(_: VMTypes.LuaState) 94 | -- idk 95 | end 96 | 97 | -- #define lua_isnone(L, n) (lua_type(L, (n)) == LUA_TNONE) 98 | function Macros.lua_isnone(_: VMTypes.LuaState) 99 | -- idk 100 | end 101 | 102 | -- #define lua_isnoneornil(L, n) (lua_type(L, (n)) <= LUA_TNIL) 103 | function Macros.lua_isnoneornil(_: VMTypes.LuaState) 104 | -- idk 105 | end 106 | 107 | -- #define lua_pushliteral(L, s) lua_pushlstring(L, "" s, (sizeof(s) / sizeof(char)) - 1) 108 | function Macros.lua_pushliteral(_: VMTypes.LuaState) 109 | -- idk 110 | end 111 | 112 | -- #define lua_pushcfunction(L, fn, debugname) lua_pushcclosurek(L, fn, debugname, 0, NULL) 113 | function Macros.lua_pushcfunction(luaState: VMTypes.LuaState, fn: number, debugname: number?) 114 | return cFunctions.lua_pushcclosurek(luaState, fn, debugname or 0, 0, 0) 115 | end 116 | 117 | -- #define lua_pushcclosure(L, fn, debugname, nup) lua_pushcclosurek(L, fn, debugname, nup, NULL) 118 | function Macros.lua_pushcclosure(luaState: VMTypes.LuaState, fn: number, nup: number) 119 | cFunctions.lua_pushcclosurek(luaState, fn, 0, nup, 0) 120 | end 121 | 122 | -- #define lua_pushlightuserdata(L, p) lua_pushlightuserdatatagged(L, p, 0) 123 | function Macros.lua_pushlightuserdata(_: VMTypes.LuaState) 124 | -- idk 125 | end 126 | 127 | -- #define lua_setglobal(L, s) lua_setfield(L, LUA_GLOBALSINDEX, (s)) 128 | function Macros.lua_setglobal(luaState: VMTypes.LuaState, s: number) 129 | return cFunctions.lua_setfield(luaState, VMConstants.LUAU_GLOBALSINDEX, s) 130 | end 131 | 132 | -- #define lua_getglobal(L, s) lua_getfield(L, LUA_GLOBALSINDEX, (s)) 133 | function Macros.lua_getglobal(luaState: VMTypes.LuaState, s: number) 134 | return cFunctions.lua_getfield(luaState, VMConstants.LUAU_GLOBALSINDEX, s) 135 | end 136 | 137 | -- #define lua_tostring(L, i) lua_tolstring(L, (i), NULL) 138 | function Macros.lua_tostring(luaState: VMTypes.LuaState, i: number) 139 | return cFunctions.lua_tolstring(luaState, i, 0) 140 | end 141 | 142 | -- #define lua_pushfstring(L, fmt, ...) lua_pushfstringL(L, fmt, ##__VA_ARGS__) 143 | function Macros.lua_pushfstring(_: VMTypes.LuaState) 144 | -- idk 145 | end 146 | 147 | -- #define luaL_checkstring(L, n) (luaL_checklstring(L, (n), NULL)) 148 | function Macros.luaL_checkstring(luaState: VMTypes.LuaState, n: number) 149 | return cFunctions.luaL_checklstring(luaState, n, 0) 150 | end 151 | 152 | -- #define luaL_optstring(L, n, d) (luaL_optlstring(L, (n), (d), NULL)) 153 | function Macros.luaL_optstring(luaState: VMTypes.LuaState, n: number, d: number) 154 | return cFunctions.luaL_optlstring(luaState, n, d, 0) 155 | end 156 | 157 | -- #define luaL_getmetatable(L, n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) 158 | function Macros.luaL_getmetatable(luaState: VMTypes.LuaState, n: number) 159 | return cFunctions.lua_getfield(luaState, VMConstants.LUA_REGISTRYINDEX, n) 160 | end 161 | 162 | -- LUA_API void lua_getfenv(lua_State* L, int idx); 163 | function Macros.lua_getfenv(luaState: VMTypes.LuaState, i: number) 164 | return LuauCeption.wasm.indirect_function_table.data[480](luaState, i) 165 | end 166 | 167 | return Macros -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Roblox/EnvironmentTable.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local LuauCeption = require(Package.Vendor.LuauCeption) 4 | 5 | local VMBindings = require(Package.Client.Singletons.VM.Bindings) 6 | local VMTypes = require(Package.Client.Singletons.VM.Types) 7 | local VMConstants = require(Package.Client.Singletons.VM.Constants) 8 | local VMMacros = require(Package.Client.Singletons.VM.Macros) 9 | 10 | local cFunctions = LuauCeption.wasm.cfns 11 | 12 | return function(luaState: VMTypes.LuaState) 13 | VMMacros.lua_newtable(luaState) 14 | VMMacros.lua_newtable(luaState) 15 | 16 | cFunctions.lua_pushstring(luaState, VMBindings:LoadCString("The metatable is locked")) 17 | cFunctions.lua_setfield(luaState, -2, VMBindings:LoadCString("__metatable")) 18 | 19 | cFunctions.lua_pushvalue(luaState, VMConstants.LUAU_GLOBALSINDEX) 20 | cFunctions.lua_setfield(luaState, -2, VMBindings:LoadCString("__index")) 21 | 22 | cFunctions.lua_setmetatable(luaState, -2) 23 | 24 | cFunctions.lua_getfield(luaState, VMConstants.LUA_REGISTRYINDEX, VMBindings:LoadCString("global")) 25 | cFunctions.lua_setfield(luaState, -2, VMBindings:LoadCString("_G")) 26 | 27 | cFunctions.lua_getfield(luaState, VMConstants.LUA_REGISTRYINDEX, VMBindings:LoadCString("shared")) 28 | cFunctions.lua_setfield(luaState, -2, VMBindings:LoadCString("shared")) 29 | 30 | cFunctions.lua_setfield(luaState, VMConstants.LUA_REGISTRYINDEX, VMBindings:LoadCString("script.roblox.environment")) 31 | end 32 | -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Roblox/GlobalTable.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local LuauCeption = require(Package.Vendor.LuauCeption) 4 | 5 | local VMBindings = require(Package.Client.Singletons.VM.Bindings) 6 | local VMTypes = require(Package.Client.Singletons.VM.Types) 7 | local VMConstants = require(Package.Client.Singletons.VM.Constants) 8 | local VMMacros = require(Package.Client.Singletons.VM.Macros) 9 | 10 | local cFunctions = LuauCeption.wasm.cfns 11 | 12 | return function(luaState: VMTypes.LuaState) 13 | VMMacros.lua_newtable(luaState) 14 | 15 | cFunctions.lua_setfield(luaState, VMConstants.LUA_REGISTRYINDEX, VMBindings:LoadCString("global")) 16 | end 17 | -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Roblox/Globals.luau: -------------------------------------------------------------------------------- 1 | --# selene: allow(shadowing) 2 | 3 | local Package = script.Parent.Parent.Parent.Parent.Parent 4 | 5 | local LuauCeption = require(Package.Vendor.LuauCeption) 6 | 7 | local VMBindings = require(Package.Client.Singletons.VM.Bindings) 8 | local VMTypes = require(Package.Client.Singletons.VM.Types) 9 | local VMConstants = require(Package.Client.Singletons.VM.Constants) 10 | local VMMacros = require(Package.Client.Singletons.VM.Macros) 11 | local VMScheduler = require(Package.Client.Singletons.VM.Scheduler) 12 | 13 | local cFunctions = LuauCeption.wasm.cfns 14 | 15 | local function delayImpl(luaState: VMTypes.LuaState) 16 | local timeToWait = cFunctions.luaL_checknumber(luaState, 1) 17 | local thread = VMScheduler:Spawn(luaState) 18 | 19 | VMMacros.lua_pop(luaState, 1) 20 | cFunctions.lua_xmove(luaState, thread, 1) 21 | 22 | task.delay(timeToWait, function() 23 | VMScheduler:Resume(thread) 24 | end) 25 | 26 | return 0 27 | end 28 | 29 | local function spawnImpl(luaState: VMTypes.LuaState) 30 | local thread = VMScheduler:Spawn(luaState) 31 | 32 | VMMacros.lua_pop(luaState, 1) 33 | cFunctions.lua_xmove(luaState, thread, 1) 34 | 35 | VMScheduler:Resume(thread) 36 | 37 | return 0 38 | end 39 | 40 | local function waitImpl(luaState: VMTypes.LuaState) 41 | local timeToWait = VMMacros.lua_tonumber(luaState, 1) 42 | local clockStart = os.clock() 43 | 44 | task.delay(timeToWait, function() 45 | VMScheduler:Resume(luaState, os.clock() - clockStart) 46 | end) 47 | 48 | return VMScheduler:Yield(luaState) 49 | end 50 | 51 | local function elapsedTimeImpl(luaState: VMTypes.LuaState) 52 | cFunctions.lua_pushnumber(luaState, elapsedTime()) 53 | 54 | return 1 55 | end 56 | 57 | local function pluginManagerImpl(luaState: VMTypes.LuaState) 58 | VMBindings:Error(luaState, VMBindings:LoadCString(`The current thread cannot call 'PluginManager' (lacking capability Plugin)`)) 59 | 60 | return 0 61 | end 62 | 63 | local function debuggerManagerImpl(luaState: VMTypes.LuaState) 64 | VMBindings:Error(luaState, VMBindings:LoadCString(`The current thread cannot call 'settings' (lacking capability Plugin)`)) 65 | 66 | return 0 67 | end 68 | 69 | local function printidentityImpl(_: VMTypes.LuaState) 70 | print("Current identity is 2") 71 | 72 | return 0 73 | end 74 | 75 | local function settingsImpl(luaState: VMTypes.LuaState) 76 | VMBindings:Error(luaState, VMBindings:LoadCString(`The current thread cannot call 'settings' (lacking capability Plugin)`)) 77 | 78 | return 0 79 | end 80 | 81 | local function tickImpl(luaState: VMTypes.LuaState) 82 | cFunctions.lua_pushnumber(luaState, tick()) 83 | 84 | return 1 85 | end 86 | 87 | local function timeImpl(luaState: VMTypes.LuaState) 88 | cFunctions.lua_pushnumber(luaState, time()) 89 | 90 | return 1 91 | end 92 | 93 | local function userSettingsImpl(luaState: VMTypes.LuaState) 94 | VMBindings:FromLuau(luaState, UserSettings()) 95 | 96 | return 1 97 | end 98 | 99 | local function statsImpl(luaState: VMTypes.LuaState) 100 | VMBindings:FromLuau(luaState, game:GetService("Stats")) 101 | 102 | return 1 103 | end 104 | 105 | local function versionImpl(luaState: VMTypes.LuaState) 106 | -- selene: allow(undefined_variable) 107 | local versionPointer = VMBindings:LoadCString(version()) 108 | 109 | cFunctions.lua_pushstring(luaState, versionPointer) 110 | 111 | return 1 112 | end 113 | 114 | local function typeofImpl(luaState: VMTypes.LuaState) 115 | cFunctions.lua_pushstring(luaState, cFunctions.luaL_typename(luaState, 1)) 116 | 117 | return 1 118 | end 119 | 120 | local function warnImpl(luaState: VMTypes.LuaState) 121 | local numberOfArguments = cFunctions.lua_gettop(luaState) 122 | local source = "" 123 | 124 | for index = 1, numberOfArguments do 125 | local stringPointer = cFunctions.luaL_tolstring(luaState, index, 0) 126 | 127 | if index > 1 then 128 | source ..= `\t` 129 | end 130 | 131 | source ..= VMBindings:ReadCString(stringPointer) 132 | VMMacros.lua_pop(luaState, 1) 133 | end 134 | 135 | warn(source) 136 | 137 | return 0 138 | end 139 | 140 | local globals = table.freeze({ 141 | delay = delayImpl, 142 | wait = waitImpl, 143 | elapsedTime = elapsedTimeImpl, 144 | ElapsedTime = elapsedTimeImpl, 145 | PluginManager = pluginManagerImpl, 146 | DebuggerManager = debuggerManagerImpl, 147 | settings = settingsImpl, 148 | UserSettings = userSettingsImpl, 149 | version = versionImpl, 150 | warn = warnImpl, 151 | tick = tickImpl, 152 | time = timeImpl, 153 | spawn = spawnImpl, 154 | printidentity = printidentityImpl, 155 | stats = statsImpl, 156 | typeof = typeofImpl, 157 | }) 158 | 159 | return function(luaState: VMTypes.LuaState) 160 | for globalName, global in globals do 161 | VMMacros.lua_pushcfunction(luaState, VMBindings:LoadCFunction(luaState, global), VMBindings:LoadCString(globalName)) 162 | VMMacros.lua_setglobal(luaState, VMBindings:LoadCString(globalName)) 163 | end 164 | 165 | -- weird globals 166 | VMMacros.lua_getglobal(luaState, VMBindings:LoadCString("pcall")) 167 | VMMacros.lua_setglobal(luaState, VMBindings:LoadCString("ypcall")) 168 | 169 | cFunctions.lua_getfield(luaState, VMConstants.LUA_REGISTRYINDEX, VMBindings:LoadCString("global")) 170 | VMMacros.lua_setglobal(luaState, VMBindings:LoadCString("_G")) 171 | 172 | cFunctions.lua_getfield(luaState, VMConstants.LUA_REGISTRYINDEX, VMBindings:LoadCString("global")) 173 | VMMacros.lua_setglobal(luaState, VMBindings:LoadCString("shared")) 174 | 175 | -- instances 176 | VMBindings:LoadInstance(luaState, workspace, true) 177 | VMMacros.lua_setglobal(luaState, VMBindings:LoadCString("workspace")) 178 | 179 | VMBindings:LoadInstance(luaState, game, true) 180 | VMMacros.lua_setglobal(luaState, VMBindings:LoadCString("game")) 181 | 182 | VMBindings:LoadInstance(luaState, Instance.new("Script"), true) 183 | VMMacros.lua_setglobal(luaState, VMBindings:LoadCString("script")) 184 | 185 | VMBindings:LoadInstance(luaState, Enum, true) 186 | VMMacros.lua_setglobal(luaState, VMBindings:LoadCString("Enum")) 187 | end 188 | -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Roblox/Hooks/Connection.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent.Parent 2 | 3 | local LuauCeption = require(Package.Vendor.LuauCeption) 4 | 5 | local VMBindings = require(Package.Client.Singletons.VM.Bindings) 6 | local VMEnvironment = require(Package.Client.Singletons.VM.Environment) 7 | local VMTypes = require(Package.Client.Singletons.VM.Types) 8 | local VMContext = require(Package.Client.Singletons.VM.Context) 9 | 10 | local cFunctions = LuauCeption.wasm.cfns 11 | 12 | local function disconnect(luaState: VMTypes.LuaState, connection: RBXScriptConnection) 13 | VMContext:UpdateValue("RBXScriptSignal.functionRefs", function(refs) 14 | local refsTable = refs or {} 15 | local functionRef = refsTable[connection] 16 | 17 | if not functionRef then 18 | return refsTable 19 | end 20 | 21 | cFunctions.lua_unref(luaState, functionRef) 22 | refsTable[connection] = nil 23 | 24 | return refsTable 25 | end) 26 | 27 | VMContext:SetValue(connection, nil) 28 | 29 | return 0 30 | end 31 | 32 | return function() 33 | local __namecall 34 | 35 | __namecall = VMEnvironment:Hook("__namecall", function(luaState: VMTypes.LuaState) 36 | local type = VMBindings:ReadCString(cFunctions.luaL_typename(luaState, 1)) 37 | local methodNamePointer = cFunctions.lua_namecallatom(luaState, 0) 38 | 39 | local methodName = VMBindings:ReadCString(methodNamePointer) 40 | local connection = VMBindings:ToLuau(luaState, 1) 41 | 42 | if type == "RBXScriptConnection" then 43 | if methodName == "Disconnect" or methodName == "disconnect" then 44 | disconnect(luaState, connection) 45 | end 46 | end 47 | 48 | return __namecall(luaState) 49 | end) 50 | end 51 | -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Roblox/Hooks/Signal.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent.Parent 2 | 3 | local LuauCeption = require(Package.Vendor.LuauCeption) 4 | 5 | local VMBindings = require(Package.Client.Singletons.VM.Bindings) 6 | local VMEnvironment = require(Package.Client.Singletons.VM.Environment) 7 | local VMTypes = require(Package.Client.Singletons.VM.Types) 8 | local VMConstants = require(Package.Client.Singletons.VM.Constants) 9 | local VMContext = require(Package.Client.Singletons.VM.Context) 10 | local VMMacros = require(Package.Client.Singletons.VM.Macros) 11 | local VMScheduler = require(Package.Client.Singletons.VM.Scheduler) 12 | 13 | local cFunctions = LuauCeption.wasm.cfns 14 | 15 | local function addFunctionRef(signal: RBXScriptSignal, connection: RBXScriptConnection, functionRef: number) 16 | VMContext:UpdateValue("RBXScriptSignal.functionRefs", function(refs) 17 | local refsTable = refs or {} 18 | 19 | refsTable[connection] = functionRef 20 | 21 | return refsTable 22 | end) 23 | 24 | VMContext:SetValue(connection, signal) 25 | end 26 | 27 | local function removeFunctionRef(luaState: VMTypes.LuaState, connection: RBXScriptConnection) 28 | VMContext:UpdateValue("RBXScriptSignal.functionRefs", function(refs) 29 | local refsTable = refs or {} 30 | local functionRef = refsTable[connection] 31 | 32 | if not functionRef then 33 | return refsTable 34 | end 35 | 36 | cFunctions.lua_unref(luaState, functionRef) 37 | refsTable[connection] = nil 38 | 39 | return refsTable 40 | end) 41 | 42 | VMContext:SetValue(connection, nil) 43 | end 44 | 45 | local function connect(luaState: VMTypes.LuaState, signal: RBXScriptSignal) 46 | local functionRef = cFunctions.lua_ref(luaState, 2) 47 | local connection = signal:Connect(function(...) 48 | local thread = VMScheduler:Spawn(luaState) 49 | 50 | VMMacros.lua_pop(luaState, 1) 51 | cFunctions.lua_rawgeti(luaState, VMConstants.LUA_REGISTRYINDEX, functionRef) 52 | cFunctions.lua_xmove(luaState, thread, 1) 53 | 54 | VMScheduler:Resume(thread, ...) 55 | 56 | while VMScheduler:Active(thread) do 57 | task.wait() 58 | end 59 | 60 | VMScheduler:Close(thread) 61 | end) 62 | 63 | VMBindings:FromLuau(luaState, connection) 64 | 65 | addFunctionRef(signal, connection, functionRef) 66 | 67 | return 1 68 | end 69 | 70 | local function once(luaState: VMTypes.LuaState, signal: RBXScriptSignal) 71 | local functionRef = cFunctions.lua_ref(luaState, 2) 72 | local connection 73 | 74 | connection = signal:Connect(function(...) 75 | local thread = VMScheduler:Spawn(luaState) 76 | 77 | VMMacros.lua_pop(luaState, 1) 78 | cFunctions.lua_rawgeti(luaState, VMConstants.LUA_REGISTRYINDEX, functionRef) 79 | cFunctions.lua_xmove(luaState, thread, 1) 80 | 81 | removeFunctionRef(luaState, connection) 82 | 83 | VMScheduler:Resume(luaState, ...) 84 | end) 85 | 86 | VMBindings:FromLuau(luaState, connection) 87 | addFunctionRef(signal, connection, functionRef) 88 | 89 | return 1 90 | end 91 | 92 | local function wait(luaState: VMTypes.LuaState, signal: RBXScriptSignal) 93 | signal:Once(function(...) 94 | VMScheduler:Resume(luaState, nil, ...) 95 | end) 96 | 97 | return VMScheduler:Yield(luaState) 98 | end 99 | 100 | return function() 101 | local __namecall 102 | 103 | __namecall = VMEnvironment:Hook("__namecall", function(luaState: VMTypes.LuaState) 104 | local type = VMBindings:ReadCString(cFunctions.luaL_typename(luaState, 1)) 105 | local methodNamePointer = cFunctions.lua_namecallatom(luaState, 0) 106 | 107 | local methodName = VMBindings:ReadCString(methodNamePointer) 108 | local signal = VMBindings:ToLuau(luaState, 1) 109 | 110 | if type == "RBXScriptSignal" then 111 | if methodName == "connect" or methodName == "Connect" then 112 | return connect(luaState, signal) 113 | end 114 | 115 | if methodName == "Once" then 116 | return once(luaState, signal) 117 | end 118 | 119 | if methodName == "wait" or methodName == "Wait" then 120 | return wait(luaState, signal) 121 | end 122 | end 123 | 124 | return __namecall(luaState) 125 | end) 126 | end 127 | -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Roblox/Libraries/Task.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent.Parent 2 | 3 | local LuauCeption = require(Package.Vendor.LuauCeption) 4 | 5 | local VMBindings = require(Package.Client.Singletons.VM.Bindings) 6 | local VMTypes = require(Package.Client.Singletons.VM.Types) 7 | local VMMacros = require(Package.Client.Singletons.VM.Macros) 8 | local VMScheduler = require(Package.Client.Singletons.VM.Scheduler) 9 | 10 | local cFunctions = LuauCeption.wasm.cfns 11 | 12 | local function spawn(luaState: VMTypes.LuaState) 13 | local numberOfArguments = cFunctions.lua_gettop(luaState) - 1 14 | local arguments = { n = numberOfArguments } 15 | 16 | -- + 1, because we want to skip the first argument, which is the function to call 17 | for index = 1, numberOfArguments do 18 | arguments[index] = VMBindings:ToLuau(luaState, index + 1) 19 | end 20 | 21 | local thread = VMScheduler:Spawn(luaState) 22 | 23 | cFunctions.lua_pushvalue(luaState, 1) 24 | cFunctions.lua_xmove(luaState, thread, 1) 25 | 26 | VMScheduler:Resume(thread, table.unpack(arguments)) 27 | 28 | return 1 29 | end 30 | 31 | local function defer(luaState: VMTypes.LuaState) 32 | local numberOfArguments = cFunctions.lua_gettop(luaState) - 1 33 | local arguments = { n = numberOfArguments } 34 | 35 | -- + 1, because we want to skip the first argument, which is the function to call 36 | for index = 1, numberOfArguments do 37 | arguments[index] = VMBindings:ToLuau(luaState, index + 1) 38 | end 39 | 40 | local thread = VMScheduler:Spawn(luaState) 41 | 42 | cFunctions.lua_pushvalue(luaState, 1) 43 | cFunctions.lua_xmove(luaState, thread, 1) 44 | 45 | VMScheduler:Resume(thread, table.unpack(arguments)) 46 | 47 | return 1 48 | end 49 | 50 | local function delay(luaState: VMTypes.LuaState) 51 | local timeToWait = cFunctions.luaL_checknumber(luaState, 1) 52 | local numberOfArguments = cFunctions.lua_gettop(luaState) - 2 53 | local arguments = { n = numberOfArguments } 54 | 55 | -- + 1, because we want to skip the first argument, which is the function to call 56 | for index = 1, numberOfArguments do 57 | arguments[index] = VMBindings:ToLuau(luaState, index + 2) 58 | end 59 | 60 | local thread = VMScheduler:Spawn(luaState) 61 | 62 | cFunctions.lua_pushvalue(luaState, 2) 63 | cFunctions.lua_xmove(luaState, thread, 1) 64 | 65 | task.delay(timeToWait, function() 66 | VMScheduler:Resume(thread, table.unpack(arguments)) 67 | end) 68 | 69 | return 1 70 | end 71 | 72 | local function desynchronize(_: VMTypes.LuaState) 73 | return 0 74 | end 75 | 76 | local function synchronize(_: VMTypes.LuaState) 77 | return 0 78 | end 79 | 80 | local function wait(luaState: VMTypes.LuaState) 81 | local timeToWait = cFunctions.luaL_checknumber(luaState, 1) 82 | local clockStart = os.clock() 83 | 84 | task.delay(timeToWait, function() 85 | VMScheduler:Resume(luaState, os.clock() - clockStart) 86 | end) 87 | 88 | return VMScheduler:Yield(luaState) 89 | end 90 | 91 | local function cancel(luaState: VMTypes.LuaState) 92 | local thread = cFunctions.lua_tothread(luaState, 1) 93 | 94 | cFunctions.lua_resetthread(thread) 95 | 96 | return 0 97 | end 98 | 99 | local callbacks = table.freeze({ 100 | spawn = spawn, 101 | defer = defer, 102 | delay = delay, 103 | desynchronize = desynchronize, 104 | synchronize = synchronize, 105 | wait = wait, 106 | cancel = cancel, 107 | }) 108 | 109 | return function(luaState: VMTypes.LuaState) 110 | VMMacros.lua_newtable(luaState) 111 | 112 | for name, callback in callbacks do 113 | VMMacros.lua_pushcfunction(luaState, VMBindings:LoadCFunction(luaState, callback), VMBindings:LoadCString(name)) 114 | cFunctions.lua_setfield(luaState, -2, VMBindings:LoadCString(name)) 115 | end 116 | 117 | VMMacros.lua_setglobal(luaState, VMBindings:LoadCString("task")) 118 | end 119 | -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Roblox/SharedTable.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent.Parent.Parent 2 | 3 | local LuauCeption = require(Package.Vendor.LuauCeption) 4 | 5 | local VMBindings = require(Package.Client.Singletons.VM.Bindings) 6 | local VMTypes = require(Package.Client.Singletons.VM.Types) 7 | local VMConstants = require(Package.Client.Singletons.VM.Constants) 8 | local VMMacros = require(Package.Client.Singletons.VM.Macros) 9 | 10 | local cFunctions = LuauCeption.wasm.cfns 11 | 12 | return function(luaState: VMTypes.LuaState) 13 | VMMacros.lua_newtable(luaState) 14 | 15 | cFunctions.lua_setfield(luaState, VMConstants.LUA_REGISTRYINDEX, VMBindings:LoadCString("shared")) 16 | end 17 | -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Scheduler.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | A scheduler for managing Lua threads in the VM. 3 | ]] 4 | 5 | local Package = script.Parent.Parent.Parent.Parent 6 | 7 | local Signal = require(Package.Parent.Signal) 8 | 9 | local LuauCeption = require(Package.Vendor.LuauCeption) 10 | 11 | local VMBindings = require(Package.Client.Singletons.VM.Bindings) 12 | local VMTypes = require(Package.Client.Singletons.VM.Types) 13 | local VMConstants = require(Package.Client.Singletons.VM.Constants) 14 | local VMContext = require(Package.Client.Singletons.VM.Context) 15 | 16 | local LUA_STATUS_MAP = table.freeze({ 17 | [VMConstants.lua_Status.LUA_OK] = "OK", 18 | [VMConstants.lua_Status.LUA_YIELD] = "YIELD", 19 | [VMConstants.lua_Status.LUA_ERRRUN] = "ERRRUN", 20 | [VMConstants.lua_Status.LUA_ERRSYNTAX] = "ERRSYNTAX", 21 | [VMConstants.lua_Status.LUA_ERRMEM] = "ERRMEM", 22 | [VMConstants.lua_Status.LUA_ERRERR] = "ERRERR", 23 | [VMConstants.lua_Status.LUA_BREAK] = "BREAK" 24 | }) 25 | 26 | local cFunctions = LuauCeption.wasm.cfns 27 | 28 | local Scheduler = {} 29 | 30 | Scheduler.ScriptThreadSpawned = Signal.new() 31 | Scheduler.ThreadSpawned = Signal.new() 32 | 33 | --[[ 34 | Yields the execution of a thread. 35 | ]] 36 | function Scheduler.Yield(_: Scheduler, thread: VMTypes.LuaThread) 37 | return cFunctions.lua_yield(thread, -1) 38 | end 39 | 40 | --[[ 41 | Resumes a suspended thread with optional arguments. 42 | ]] 43 | function Scheduler.Resume(self: Scheduler, thread: VMTypes.LuaThread, ...) 44 | local args = table.pack(...) 45 | 46 | for index = 1, #args do 47 | VMBindings:FromLuau(thread, args[index]) 48 | end 49 | 50 | local parentThread = self:GetParentThread(thread) 51 | local resumeState = cFunctions.lua_resume(thread, parentThread, #args) 52 | 53 | return LUA_STATUS_MAP[resumeState] 54 | end 55 | 56 | --[[ 57 | Gets the current status of a thread. 58 | ]] 59 | function Scheduler.Status(_: Scheduler, thread: VMTypes.LuaThread) 60 | local threadStatus = cFunctions.lua_status(thread) 61 | 62 | return LUA_STATUS_MAP[threadStatus] 63 | end 64 | 65 | --[[ 66 | Checks if a thread or any of its child threads are yielding. 67 | ]] 68 | function Scheduler.Active(self: Scheduler, thread: VMTypes.LuaThread) 69 | local threadStats = VMContext:GetValue(`Scheduler.threads.stats.{thread}`) 70 | local threadStatus = self:Status(thread) 71 | 72 | if threadStatus == "YIELD" then 73 | return true 74 | end 75 | 76 | if threadStats.isScriptThread then 77 | for _, childThread in threadStats.childThreads do 78 | local childThreadStatus = self:Status(childThread) 79 | 80 | if childThreadStatus == "YIELD" then 81 | return true 82 | end 83 | end 84 | end 85 | 86 | return false 87 | end 88 | 89 | --[[ 90 | Closes a thread and all its child threads, cleaning up resources. 91 | ]] 92 | function Scheduler.Close(self: Scheduler, thread: VMTypes.LuaThread) 93 | local stats = VMContext:GetValue(`Scheduler.threads.stats.{thread}`) 94 | 95 | if stats.isScriptThread then 96 | for _, childThread in stats.childThreads do 97 | self:Close(childThread) 98 | end 99 | end 100 | 101 | VMContext:UpdateValue(`Scheduler.threads.refs.{thread}`, function(ref) 102 | cFunctions.lua_unref(thread, ref) 103 | cFunctions.lua_resetthread(thread) 104 | 105 | VMContext:SetValue(`Scheduler.threads.stats.{thread}`, nil) 106 | end) 107 | 108 | VMContext:UpdateValue(`Scheduler.threadCount`, function(value) 109 | local count = value or 0 110 | 111 | return count - 1 112 | end) 113 | end 114 | 115 | --[[ 116 | Creates a new thread with an optional parent thread and script thread flag. 117 | ]] 118 | function Scheduler.Spawn(self: Scheduler, parentThread: VMTypes.LuaThread, isScriptThread: boolean?, quiet: boolean?) 119 | local thread = cFunctions.lua_newthread(parentThread) 120 | local ref = cFunctions.lua_ref(thread, 1) 121 | 122 | VMContext:UpdateValue(`Scheduler.threadCount`, function(value) 123 | local count = value or 0 124 | 125 | return count + 1 126 | end) 127 | 128 | VMContext:SetValue(`Scheduler.threads.refs.{thread}`, ref) 129 | VMContext:SetValue(`Scheduler.threads.stats.{thread}`, { 130 | childThreads = isScriptThread and {} or nil, 131 | refs = isScriptThread and {} or nil, 132 | isScriptThread = isScriptThread or false, 133 | 134 | parent = parentThread, 135 | }) 136 | 137 | if isScriptThread then 138 | if not quiet then 139 | self.ScriptThreadSpawned:Fire(thread) 140 | end 141 | else 142 | local scriptThread = self:GetScriptThread(thread) 143 | local stats = VMContext:GetValue(`Scheduler.threads.stats.{scriptThread}`) 144 | 145 | table.insert(stats.childThreads, thread) 146 | 147 | if not quiet then 148 | self.ThreadSpawned:Fire(thread, scriptThread) 149 | end 150 | end 151 | 152 | return thread 153 | end 154 | 155 | --[[ 156 | Gets the script thread associated with a given thread. 157 | ]] 158 | function Scheduler.GetScriptThread(_: Scheduler, thread: VMTypes.LuaThread) 159 | local targetThread = thread 160 | local threadStats = VMContext:GetValue(`Scheduler.threads.stats.{targetThread}`) 161 | 162 | while not threadStats.isScriptThread do 163 | targetThread = threadStats.parent 164 | threadStats = VMContext:GetValue(`Scheduler.threads.stats.{targetThread}`) 165 | end 166 | 167 | return targetThread 168 | end 169 | 170 | --[[ 171 | Gets the parent thread of a given thread. 172 | ]] 173 | function Scheduler.GetParentThread(_: Scheduler, thread: VMTypes.LuaThread) 174 | local threadStats = VMContext:GetValue(`Scheduler.threads.stats.{thread}`) 175 | 176 | return threadStats.parent 177 | end 178 | 179 | export type Scheduler = typeof(Scheduler) 180 | 181 | return Scheduler -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/Types.luau: -------------------------------------------------------------------------------- 1 | export type LuaState = number 2 | export type LuaThread = number 3 | export type CPointer = number 4 | 5 | return {} -------------------------------------------------------------------------------- /Source/Client/Singletons/VM/init.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | ]] 4 | 5 | local Package = script.Parent.Parent.Parent 6 | 7 | local LuauCeption = require(Package.Vendor.LuauCeption) 8 | 9 | local VMBindings = require(Package.Client.Singletons.VM.Bindings) 10 | local VMEnvironment = require(Package.Client.Singletons.VM.Environment) 11 | local VMMacros = require(Package.Client.Singletons.VM.Macros) 12 | local VMScheduler = require(Package.Client.Singletons.VM.Scheduler) 13 | 14 | local SignalHook = require(Package.Client.Singletons.VM.Roblox.Hooks.Signal) 15 | local ConnectionHook = require(Package.Client.Singletons.VM.Roblox.Hooks.Connection) 16 | 17 | local CoreGuiHook = require(Package.Client.Singletons.VM.Exploit.Hooks.CoreGui) 18 | local HttpGetAsyncHook = require(Package.Client.Singletons.VM.Exploit.Hooks.HttpGetAsync) 19 | 20 | local LUAU_OPTIMISATION_LEVEL = 1 21 | local LUAU_DEBUG_LEVEL = 1 22 | local LUAU_COVERAGE_LEVEL = 0 23 | local LUAU_TYPE_INFO_LEVEL = 0 24 | 25 | local cFunctions = LuauCeption.wasm.cfns 26 | 27 | local VM = {} 28 | 29 | VM.Priority = 1 30 | VM.LuaState = nil 31 | 32 | --[[ 33 | 34 | ]] 35 | function VM.AddLibrary(self: VM, name: string, lib: { [string]: (luauCeption: {}, luaState: number) -> number }) 36 | local library = {} 37 | 38 | for i, v in lib do 39 | library[i] = function(luaState: number) 40 | return v(LuauCeption, luaState) 41 | end 42 | end 43 | 44 | VMEnvironment:Implement(self.LuaState, name, library) 45 | end 46 | 47 | --[[ 48 | 49 | ]] 50 | function VM.AddGlobal(self: VM, name: string, value: (luauCeption: {}, luaState: number) -> number) 51 | VMMacros.lua_pushcfunction(self.LuaState, VMBindings:LoadCFunction(self.LuaState, function(luaState) 52 | return value(LuauCeption, luaState) 53 | end), VMBindings:LoadCString(name)) 54 | 55 | VMMacros.lua_setglobal(self.LuaState, VMBindings:LoadCString(name)) 56 | end 57 | 58 | --[[ 59 | 60 | ]] 61 | function VM.HookMetamethod(self: VM, metamethod: string, caller: (luauCeption: {}, luaState: number) -> number) 62 | return VMEnvironment:Hook(metamethod, function() 63 | return caller(LuauCeption, self.LuaState) 64 | end) 65 | end 66 | 67 | --[[ 68 | 69 | ]] 70 | function VM.Load(self: VM, source: string) 71 | local bytecode = LuauCeption.luau_compile(source, LUAU_OPTIMISATION_LEVEL, LUAU_DEBUG_LEVEL, LUAU_TYPE_INFO_LEVEL, LUAU_COVERAGE_LEVEL) 72 | 73 | local bytecodePointer = VMBindings:LoadCString(bytecode) 74 | local chunkNamePointer = VMBindings:LoadCString("SandboxChunk") 75 | 76 | local loadResult = cFunctions.luau_load(self.LuaState, chunkNamePointer, bytecodePointer, #bytecode, 0) 77 | 78 | cFunctions.free(bytecodePointer) 79 | cFunctions.free(chunkNamePointer) 80 | 81 | if loadResult ~= 0 then 82 | cFunctions.lua_close(self.LuaState) 83 | 84 | error(`Failed to load Luau bytecode`) 85 | end 86 | end 87 | 88 | --[[ 89 | 90 | ]] 91 | function VM.Run(self: VM, quiet: boolean?) 92 | local thread = VMScheduler:Spawn(self.LuaState, true, quiet) 93 | 94 | VMMacros.lua_pop(self.LuaState, 1) 95 | cFunctions.lua_xmove(self.LuaState, thread, 1) 96 | 97 | VMScheduler:Resume(thread) 98 | 99 | while VMScheduler:Active(thread) do 100 | task.wait(0.5) 101 | end 102 | 103 | local results = cFunctions.lua_gettop(thread) 104 | local response = {} 105 | 106 | for i = 1, results do 107 | table.insert(response, VMBindings:ToLuau(thread, i)) 108 | end 109 | 110 | VMScheduler:Close(thread) 111 | 112 | return table.unpack(response) 113 | end 114 | 115 | --[[ 116 | 117 | ]] 118 | function VM.Eval(self: VM, source: string, quiet: boolean?) 119 | self:Load(source) 120 | 121 | VMEnvironment:WriteFunctionEnvironment(self.LuaState) 122 | 123 | return self:Run(quiet) 124 | end 125 | 126 | function VM.OnStart(self: VM) 127 | self.LuaState = cFunctions.luaL_newstate() 128 | 129 | cFunctions.luaL_openlibs(self.LuaState) 130 | VMEnvironment:WriteEnvironment(self.LuaState) 131 | -- cFunctions.luaL_sandbox(luaState) 132 | 133 | SignalHook() 134 | ConnectionHook() 135 | CoreGuiHook() 136 | HttpGetAsyncHook() 137 | end 138 | 139 | export type VM = typeof(VM) 140 | export type LuauCeption = typeof(LuauCeption) 141 | 142 | return VM -------------------------------------------------------------------------------- /Source/Client/init.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | @class DevSuite.Client 3 | 4 | Summary 5 | ]] 6 | 7 | local Package = script.Parent 8 | 9 | local Console = require(Package.Parent.Console) 10 | local Runtime = require(Package.Parent.Runtime) 11 | 12 | local Authentication = require(Package.Client.Singletons.Authentication) 13 | 14 | local reporter = Console.new(script.Name) 15 | 16 | local DevSuiteClient = {} 17 | 18 | DevSuiteClient.Interface = {} 19 | DevSuiteClient.Prototype = {} 20 | 21 | function DevSuiteClient.Interface.new(): DevSuiteClient 22 | local self = setmetatable({} :: DevSuiteClient, { 23 | __index = DevSuiteClient.Prototype 24 | }) 25 | 26 | if not Authentication:IsAuthenticated() then 27 | reporter:Debug(`Local-Player not authenticated, not initializing Developer Suite!`) 28 | 29 | return 30 | end 31 | 32 | local singletons = Runtime:RequireChildren(script.Singletons) 33 | 34 | table.sort(singletons, function(singletonA: { Priority: number? }, singletonB: { Priority: number? }) 35 | local priorityA = singletonA.Priority or 0 36 | local priorityB = singletonB.Priority or 0 37 | 38 | return priorityA > priorityB 39 | end) 40 | 41 | Runtime:CallSpawnedMethodOn(singletons, "OnStart") 42 | 43 | return self 44 | end 45 | 46 | export type DevSuiteClient = typeof(DevSuiteClient.Prototype) 47 | 48 | return DevSuiteClient.Interface 49 | -------------------------------------------------------------------------------- /Source/Containers/Runtime.client.luau: -------------------------------------------------------------------------------- 1 | require(script.Parent.Parent) -------------------------------------------------------------------------------- /Source/Containers/Runtime.server.luau: -------------------------------------------------------------------------------- 1 | require(script.Parent.Parent) -------------------------------------------------------------------------------- /Source/Server/Singletons/Actions.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | ]] 4 | 5 | local HttpService = game:GetService("HttpService") 6 | local Players = game:GetService("Players") 7 | 8 | local Package = script.Parent.Parent.Parent 9 | 10 | local Network = require(Package.Server.Network) 11 | 12 | local Authentication = require(Package.Server.Singletons.Authentication) 13 | 14 | local serverSideActions = {} 15 | 16 | local Actions = {} 17 | 18 | --[[ 19 | 20 | ]] 21 | function Actions.UpdateClients(_: Actions) 22 | Network.ServerReportingActions.FireList(Authentication:GetListOfAuthenticatedPlayers(), serverSideActions) 23 | end 24 | 25 | --[[ 26 | 27 | ]] 28 | function Actions.ExecuteServerAction(_: Actions, player:Player, actionUuid: string, actionArguments: { any }) 29 | for _, action in serverSideActions do 30 | if action.uuid == actionUuid then 31 | action.callback(player, table.unpack(actionArguments)) 32 | end 33 | end 34 | end 35 | 36 | --[[ 37 | 38 | ]] 39 | function Actions.RegisterServerAction(self: Actions, actionSettings: { 40 | name: string, 41 | description: string, 42 | arguments: { 43 | type: "Number" | "String" | "Boolean" | "Player", 44 | name: string, 45 | default: any 46 | }, 47 | callback: (...any) -> () 48 | }) 49 | local actionUuid = HttpService:GenerateGUID(false) 50 | local path = string.split(actionSettings.name, ".") 51 | local name = table.remove(path, #path) 52 | 53 | table.insert(serverSideActions, { 54 | path = path, 55 | name = name, 56 | uuid = actionUuid, 57 | arguments = actionSettings.arguments, 58 | description = actionSettings.description, 59 | callback = actionSettings.callback, 60 | }) 61 | 62 | self:UpdateClients() 63 | end 64 | 65 | function Actions.OnStart(self: Actions) 66 | Network.FetchActionsRequested.On(function(player) 67 | Authentication:Validate(player) 68 | 69 | Network.ServerReportingActions.Fire(player, serverSideActions) 70 | end) 71 | 72 | Network.ExecuteServerActionRequested.On(function(player, object) 73 | Authentication:Validate(player) 74 | 75 | local arguments = {} 76 | 77 | for _, argument in object.arguments do 78 | if argument.type == "Player" then 79 | table.insert(arguments, Players:FindFirstChild(argument.value)) 80 | elseif argument.type == "Boolean" then 81 | table.insert(arguments, argument.value == "true") 82 | elseif argument.type == "Number" then 83 | table.insert(arguments, tonumber(argument.value)) 84 | else 85 | table.insert(arguments, argument.value) 86 | end 87 | end 88 | 89 | self:ExecuteServerAction(player, object.actionUuid, arguments) 90 | end) 91 | end 92 | 93 | export type Actions = typeof(Actions) 94 | 95 | return Actions -------------------------------------------------------------------------------- /Source/Server/Singletons/Admin.luau: -------------------------------------------------------------------------------- 1 | local Players = game:GetService("Players") 2 | local TeleportService = game:GetService("TeleportService") 3 | 4 | --[[ 5 | 6 | ]] 7 | 8 | local Package = script.Parent.Parent.Parent 9 | 10 | local Network = require(Package.Server.Network) 11 | 12 | local Authentication = require(Package.Server.Singletons.Authentication) 13 | 14 | local Admin = {} 15 | 16 | function Admin.OnStart(_: Admin) 17 | Network.KickUserRequested.On(function(player, target) 18 | Authentication:Validate(player) 19 | 20 | target:Kick("You have been kicked by an administrator.") 21 | end) 22 | 23 | Network.BanUserRequested.On(function(player, target) 24 | Authentication:Validate(player) 25 | 26 | Players:BanAsync({ 27 | UserIds = { target.UserId }, 28 | ApplyToUniverse = true, 29 | Duration = -1, 30 | DisplayReason = "You have been banned by an administrator.", 31 | PrivateReason = `Player '{target.Name}' has been banned by {player.Name}.`, 32 | ExcludeAltAccounts = false, 33 | }) 34 | end) 35 | 36 | Network.AdminUserRequested.On(function(player, target) 37 | Authentication:Validate(player) 38 | 39 | Authentication:SetAuthenticated(target, true) 40 | TeleportService:Teleport(game.PlaceId, target) 41 | end) 42 | 43 | Network.RejoinRequested.On(function(player) 44 | Authentication:Validate(player) 45 | 46 | TeleportService:Teleport(game.PlaceId, player) 47 | end) 48 | 49 | Network.ShutdownServerRequested.On(function(requestingPlayer) 50 | Authentication:Validate(requestingPlayer) 51 | 52 | local reservedServerId = TeleportService:ReserveServer(game.PlaceId) 53 | 54 | for _, player in Players:GetPlayers() do 55 | TeleportService:TeleportToPrivateServer(game.PlaceId, reservedServerId, { player }, nil, { DEV_SUITE_REJOIN_PUBLIC_SERVER = true }) 56 | end 57 | 58 | Players.PlayerAdded:Connect(function(player) 59 | TeleportService:TeleportToPrivateServer(game.PlaceId, reservedServerId, { player }, nil, { DEV_SUITE_REJOIN_PUBLIC_SERVER = true }) 60 | end) 61 | 62 | TeleportService.TeleportInitFailed:Connect(function(player) 63 | TeleportService:TeleportToPrivateServer(game.PlaceId, reservedServerId, { player }, nil, { DEV_SUITE_REJOIN_PUBLIC_SERVER = true }) 64 | end) 65 | end) 66 | 67 | Players.PlayerAdded:Connect(function(player) 68 | local joinData = player:GetJoinData() 69 | 70 | if joinData.TeleportData and joinData.TeleportData.DEV_SUITE_REJOIN_PUBLIC_SERVER then 71 | TeleportService:TeleportAsync(game.PlaceId, { player }) 72 | end 73 | end) 74 | end 75 | 76 | export type Admin = typeof(Admin) 77 | 78 | return Admin -------------------------------------------------------------------------------- /Source/Server/Singletons/Authentication.luau: -------------------------------------------------------------------------------- 1 | local Players = game:GetService("Players") 2 | 3 | local Package = script.Parent.Parent.Parent 4 | 5 | local Console = require(Package.Parent.Console) 6 | 7 | local Network = require(Package.Server.Network) 8 | 9 | local evaluationCache: { [number]: boolean } = { } 10 | local reporter = Console.new(script.Name) 11 | 12 | local hasWarned = false 13 | 14 | local Authentication = {} 15 | 16 | Authentication.AuthenticationCallback = nil :: ((Player) -> boolean)? 17 | 18 | function Authentication.IsAuthenticated(self: Authentication, player: Player): boolean 19 | if evaluationCache[player.UserId] ~= nil then 20 | return evaluationCache[player.UserId] 21 | end 22 | 23 | if self.AuthenticationCallback then 24 | evaluationCache[player.UserId] = self.AuthenticationCallback(player) 25 | 26 | if evaluationCache[player.UserId] then 27 | player:SetAttribute(`DevSuite_Auth`, true) 28 | end 29 | 30 | return evaluationCache[player.UserId] 31 | end 32 | 33 | if not hasWarned then 34 | reporter:Warn(`Authentication callback not set, authenticating player '{player.Name}' as authenticated.`) 35 | 36 | hasWarned = true 37 | end 38 | 39 | player:SetAttribute(`DevSuite_Auth`, true) 40 | 41 | return true 42 | end 43 | 44 | function Authentication.Validate(self: Authentication, player: Player, ...): boolean 45 | assert(self:IsAuthenticated(player), `Player '{player.Name}' is not authenticated.`) 46 | 47 | return ... 48 | end 49 | 50 | function Authentication.SetAuthenticated(_: Authentication, player: Player, authenticated: boolean) 51 | evaluationCache[player.UserId] = authenticated 52 | end 53 | 54 | function Authentication.GetListOfAuthenticatedPlayers(self: Authentication): { Player } 55 | local authenticated = {} 56 | 57 | for _, player in Players:GetPlayers() do 58 | if self:IsAuthenticated(player) then 59 | table.insert(authenticated, player) 60 | end 61 | end 62 | 63 | return authenticated 64 | end 65 | 66 | function Authentication.OnStart(self: Authentication) 67 | Network.AuthenticateRequested.SetCallback(function(player: Player) 68 | return self:IsAuthenticated(player) and "Accepted" or "Rejected" 69 | end) 70 | end 71 | 72 | export type Authentication = typeof(Authentication) 73 | 74 | return Authentication -------------------------------------------------------------------------------- /Source/Server/Singletons/Hooks.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | ]] 4 | 5 | local HttpService = game:GetService("HttpService") 6 | 7 | local Package = script.Parent.Parent.Parent 8 | 9 | local Network = require(Package.Server.Network) 10 | 11 | local Authentication = require(Package.Server.Singletons.Authentication) 12 | 13 | local Hooks = {} 14 | 15 | function Hooks.OnStart(_: Hooks) 16 | Network.HttpGetRequest.SetCallback(function(player, url) 17 | Authentication:Validate(player) 18 | 19 | local response = HttpService:GetAsync(url, true) 20 | 21 | return response 22 | end) 23 | end 24 | 25 | export type Hooks = typeof(Hooks) 26 | 27 | return Hooks -------------------------------------------------------------------------------- /Source/Server/Singletons/Logging.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | ]] 4 | 5 | local LogService = game:GetService("LogService") 6 | local Players = game:GetService("Players") 7 | 8 | local Package = script.Parent.Parent.Parent 9 | 10 | local Network = require(Package.Server.Network) 11 | 12 | local Authentication = require(Package.Server.Singletons.Authentication) 13 | 14 | local logObjectsToSendToClient = {} 15 | 16 | local _serverLogs = {} 17 | local playerLogs = {} 18 | 19 | local Logging = {} 20 | 21 | function Logging.OnServerLogReceived(_: Logging, message: string, type: Enum.MessageType) 22 | local logTime = DateTime.now().UnixTimestampMillis 23 | 24 | table.insert(logObjectsToSendToClient, { 25 | message = message, 26 | type = type.Name, 27 | time = logTime 28 | }) 29 | 30 | table.insert(_serverLogs, { 31 | message = message, 32 | type = type, 33 | time = logTime 34 | }) 35 | end 36 | 37 | function Logging.OnClientLogReceived(_: Logging, player: Player, message: string, type: Enum.MessageType, time: number) 38 | local playerLogArray = playerLogs[player] or {} 39 | 40 | table.insert(playerLogArray, { 41 | message = message, 42 | type = type, 43 | time = time, 44 | }) 45 | 46 | playerLogs[player] = playerLogArray 47 | end 48 | 49 | function Logging.OnStart(self: Logging) 50 | LogService.MessageOut:Connect(function(message: string, type: Enum.MessageType) 51 | self:OnServerLogReceived(message, type) 52 | end) 53 | 54 | Network.ClientReportingLogs.On(function(player, logs) 55 | Authentication:Validate(player) 56 | 57 | for _, log in logs do 58 | local messageType = Enum.MessageType 59 | 60 | self:OnClientLogReceived(player, log.message, messageType[log.type], log.time) 61 | end 62 | end) 63 | 64 | Players.PlayerRemoving:Connect(function(player) 65 | playerLogs[player] = nil 66 | end) 67 | 68 | while true do 69 | task.wait(0.1) 70 | 71 | if #logObjectsToSendToClient == 0 then 72 | continue 73 | end 74 | 75 | Network.ServerReportingLogs.FireList(Authentication:GetListOfAuthenticatedPlayers(), logObjectsToSendToClient) 76 | logObjectsToSendToClient = {} 77 | end 78 | end 79 | 80 | export type Logging = typeof(Logging) 81 | 82 | return Logging -------------------------------------------------------------------------------- /Source/Server/Singletons/RemoteEvents.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | ]] 4 | 5 | local ReplicatedStorage = game:GetService("ReplicatedStorage") 6 | 7 | local Package = script.Parent.Parent.Parent 8 | 9 | local Network = require(Package.Server.Network) 10 | 11 | local encodeToJSON = require(Package.Utilities.Remotes.encodeToJSON) 12 | 13 | local Authentication = require(Package.Server.Singletons.Authentication) 14 | 15 | local RemoteEvents = {} 16 | 17 | function RemoteEvents.HookRemoteEvent(_: RemoteEvents, event: RemoteEvent) 18 | if RemoteEvents[event] then 19 | return 20 | end 21 | 22 | RemoteEvents[event] = true 23 | 24 | local connection = event.OnServerEvent:Connect(function(player, ...) 25 | if not Authentication:IsAuthenticated(player) then 26 | return 27 | end 28 | 29 | local json, instances = encodeToJSON(...) 30 | 31 | Network.ServerRemotesFired.Fire(player, { 32 | event = event, 33 | arguments = json, 34 | instances = instances 35 | }) 36 | end) 37 | 38 | event.Destroying:Once(function() 39 | connection:Disconnect() 40 | end) 41 | end 42 | 43 | function RemoteEvents.QueryRemoteEvents(self: RemoteEvents) 44 | for _, object in ReplicatedStorage:GetDescendants() do 45 | if object:IsA("RemoteEvent") or object:IsA("UnreliableRemoteEvent") then 46 | if object.Name == "DEBUG_REMOTES_RELIABLE" or object.Name == "DEBUG_REMOTES_UNRELIABLE" then 47 | continue 48 | end 49 | 50 | self:HookRemoteEvent(object) 51 | end 52 | end 53 | end 54 | 55 | function RemoteEvents.OnStart(self: RemoteEvents) 56 | self:QueryRemoteEvents() 57 | 58 | ReplicatedStorage.DescendantAdded:Connect(function() 59 | self:QueryRemoteEvents() 60 | end) 61 | end 62 | 63 | export type RemoteEvents = typeof(RemoteEvents) 64 | 65 | return RemoteEvents -------------------------------------------------------------------------------- /Source/Server/Singletons/Sandbox.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | ]] 4 | 5 | local Package = script.Parent.Parent.Parent 6 | 7 | local Network = require(Package.Server.Network) 8 | 9 | local Authentication = require(Package.Server.Singletons.Authentication) 10 | 11 | local Console = require(Package.Parent.Console) 12 | 13 | local reporter = Console.new(script.Name) 14 | 15 | local Sandbox = {} 16 | 17 | function Sandbox.Execute(_: Sandbox, source: string) 18 | local response = { pcall(loadstring, source, "DevSuite-Eval") } 19 | 20 | if response[1] then 21 | response[2]() 22 | else 23 | reporter:Warn(`Enable ServerScriptService.LoadstringEnabled for Server execution!`) 24 | reporter:Debug(response[2]) 25 | end 26 | end 27 | 28 | function Sandbox.OnStart(self: Sandbox) 29 | Network.ExecuteServerScriptRequested.On(function(player: Player, source: string) 30 | Authentication:Validate(player) 31 | 32 | self:Execute(source) 33 | end) 34 | end 35 | 36 | export type Sandbox = typeof(Sandbox) 37 | 38 | return Sandbox -------------------------------------------------------------------------------- /Source/Server/Singletons/Settings.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent 2 | 3 | local Signal = require(Package.Parent.Signal) 4 | 5 | local Authentication = require(Package.Server.Singletons.Authentication) 6 | 7 | local Network = require(Package.Server.Network) 8 | 9 | local serverSettings: SettingOptions = { 10 | DefaultHotkey = Enum.KeyCode.F10 11 | } 12 | 13 | local Settings = {} 14 | 15 | Settings.OnUpdated = Signal.new() 16 | 17 | function Settings.GetSetting(_: Settings, setting: string) 18 | return serverSettings[setting] 19 | end 20 | 21 | function Settings.UpdateServerSettings(self: Settings, settings: SettingOptions) 22 | serverSettings = settings 23 | 24 | self.OnUpdated:Fire() 25 | end 26 | 27 | function Settings.OnStart(self: Settings) 28 | local function generateSettingsTableForClient() 29 | return { 30 | DefaultHotkey = serverSettings.DefaultHotkey.Name 31 | } 32 | end 33 | 34 | self.OnUpdated:Connect(function() 35 | Network.ServerSettingsUpdated.FireList(Authentication:GetListOfAuthenticatedPlayers(), generateSettingsTableForClient()) 36 | end) 37 | 38 | Network.FetchSettingsRequested.SetCallback(function() 39 | return generateSettingsTableForClient() 40 | end) 41 | end 42 | 43 | export type Settings = typeof(Settings) 44 | 45 | export type SettingOptions = { 46 | DefaultHotkey: Enum.KeyCode, 47 | } 48 | 49 | return Settings -------------------------------------------------------------------------------- /Source/Server/init.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | @class DevSuite.Server 3 | 4 | Summary 5 | ]] 6 | 7 | local Package = script.Parent 8 | 9 | local Runtime = require(Package.Parent.Runtime) 10 | 11 | local DevSuiteServer = {} 12 | 13 | DevSuiteServer.Interface = {} 14 | DevSuiteServer.Prototype = {} 15 | 16 | function DevSuiteServer.Interface.new(): DevSuiteServer 17 | local self = setmetatable({} :: DevSuiteServer, { 18 | __index = DevSuiteServer.Prototype 19 | }) 20 | 21 | local singletons = Runtime:RequireChildren(script.Singletons) 22 | 23 | table.sort(singletons, function(singletonA: { Priority: number? }, singletonB: { Priority: number? }) 24 | local priorityA = singletonA.Priority or 0 25 | local priorityB = singletonB.Priority or 0 26 | 27 | return priorityA > priorityB 28 | end) 29 | 30 | Runtime:CallSpawnedMethodOn(singletons, "OnStart") 31 | 32 | return self 33 | end 34 | 35 | 36 | export type DevSuiteServer = typeof(DevSuiteServer.Prototype) 37 | 38 | return DevSuiteServer.Interface 39 | -------------------------------------------------------------------------------- /Source/Types/Page.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | --!nolint LocalUnused 3 | --!nolint LocalShadow 4 | 5 | export type PageElement = { 6 | Render: (scope, props) -> Instance, 7 | Properties: { 8 | DisplayOrder: number, 9 | DisplayName: string, 10 | DisplayIcon: string, 11 | 12 | Enabled: boolean, 13 | DefaultPage: boolean? 14 | } 15 | } 16 | 17 | return nil 18 | -------------------------------------------------------------------------------- /Source/Types/Signal.luau: -------------------------------------------------------------------------------- 1 | --!strict 2 | --!nolint LocalUnused 3 | --!nolint LocalShadow 4 | 5 | export type Connection = { 6 | Disconnect: (self: Connection) -> (), 7 | Destroy: (self: Connection) -> (), 8 | Connected: boolean, 9 | } 10 | 11 | export type Signal = { 12 | Fire: (self: Signal, T...) -> (), 13 | FireDeferred: (self: Signal, T...) -> (), 14 | Connect: (self: Signal, fn: (T...) -> ()) -> Connection, 15 | Once: (self: Signal, fn: (T...) -> ()) -> Connection, 16 | DisconnectAll: (self: Signal) -> (), 17 | GetConnections: (self: Signal) -> { Connection }, 18 | Destroy: (self: Signal) -> (), 19 | Wait: (self: Signal) -> T..., 20 | } 21 | 22 | return nil 23 | -------------------------------------------------------------------------------- /Source/Utilities/Luau/disassemble.luau: -------------------------------------------------------------------------------- 1 | local Package = script.Parent.Parent.Parent 2 | 3 | local deserialise = require(Package.Utilities.Luau.deserialise) 4 | 5 | return function(bytecode: string) 6 | local luauObject = deserialise(bytecode) 7 | local luauDecompiledOutput = "" 8 | 9 | local function processProto(protoName, proto) 10 | luauDecompiledOutput ..= `.{protoName} {proto.debugname and `({proto.debugname or ""})`}:` 11 | 12 | for _, instruction in proto.code do 13 | if not instruction.opcode then 14 | continue 15 | end 16 | 17 | local opmode = instruction.opmode 18 | local kmode = instruction.kmode 19 | 20 | local line = "" 21 | 22 | if opmode == 1 then 23 | line ..= `{ instruction.opname } { instruction.A }` 24 | elseif opmode == 2 then 25 | line ..= `{ instruction.opname } { instruction.A } { instruction.B }` 26 | elseif opmode == 3 then 27 | line ..= `{ instruction.opname } { instruction.A } { instruction.B } { instruction.C }` 28 | elseif opmode == 4 then 29 | line ..= `{ instruction.opname } { instruction.A } { instruction.D }` 30 | elseif opmode == 5 then 31 | line ..= `{ instruction.opname } { instruction.E }` 32 | end 33 | 34 | if kmode == 4 then 35 | line ..= ` ["{instruction.K0}"]` 36 | elseif kmode ~= 0 then 37 | line ..= ` ["{instruction.K}"]` 38 | end 39 | 40 | luauDecompiledOutput ..= `\n\t{line}` 41 | end 42 | 43 | luauDecompiledOutput ..= `\n\n` 44 | end 45 | 46 | local function processProtoMetadata(proto) 47 | luauDecompiledOutput ..= `maxstacksize: {proto.maxstacksize}\n` 48 | luauDecompiledOutput ..= `sizecode: {proto.sizecode}\n` 49 | luauDecompiledOutput ..= `sizek: {proto.sizek}\n` 50 | luauDecompiledOutput ..= `sizep: {proto.sizek}\n` 51 | luauDecompiledOutput ..= `lineinfoenabled: {proto.lineinfoenabled}\n` 52 | luauDecompiledOutput ..= `instructionlineinfo: {table.concat(proto.instructionlineinfo, ", ")}\n` 53 | luauDecompiledOutput ..= `bytecodeid: {proto.bytecodeid}\n` 54 | 55 | luauDecompiledOutput ..= `\n--------\n\n` 56 | end 57 | 58 | processProtoMetadata(luauObject.mainProto) 59 | 60 | for protoIndex, protoData in luauObject.protoList do 61 | if protoIndex == #luauObject.protoList then 62 | continue 63 | end 64 | 65 | processProto(`PROTO_{protoIndex}`, protoData) 66 | end 67 | 68 | processProto(`MAIN`, luauObject.mainProto) 69 | 70 | return luauDecompiledOutput 71 | end -------------------------------------------------------------------------------- /Source/Utilities/Remotes/decodeFromJSON.luau: -------------------------------------------------------------------------------- 1 | local HttpService = game:GetService("HttpService") 2 | 3 | return function(jsonArguments: string, instanceMap: { Instance }) 4 | local arguments = HttpService:JSONDecode(jsonArguments) 5 | 6 | for index, value in arguments do 7 | local typeofValue = typeof(value) 8 | 9 | if typeofValue == "string" and string.sub(value, 1, 1) == "I" then 10 | local instanceId = string.match(value, "I(%d+)") 11 | 12 | if not instanceId then 13 | continue 14 | end 15 | 16 | instanceId = tonumber(instanceId) 17 | 18 | if not instanceId then 19 | continue 20 | end 21 | 22 | if not instanceMap[instanceId] then 23 | continue 24 | end 25 | 26 | arguments[index] = instanceMap[instanceId] 27 | end 28 | end 29 | 30 | return arguments 31 | end 32 | -------------------------------------------------------------------------------- /Source/Utilities/Remotes/encodeToJSON.luau: -------------------------------------------------------------------------------- 1 | local HttpService = game:GetService("HttpService") 2 | 3 | local function encode(object: any, instanceMap: { Instance }) 4 | local objectType = typeof(object) 5 | 6 | if objectType == "Instance" then 7 | local instanceId = #instanceMap + 1 8 | 9 | table.insert(instanceMap, object) 10 | 11 | return `I{instanceId}` 12 | end 13 | 14 | return object 15 | end 16 | 17 | return function(...) 18 | local arguments = { ... } 19 | local instanceMap = {} 20 | 21 | for index, value in arguments do 22 | arguments[index] = encode(value, instanceMap) 23 | end 24 | 25 | return HttpService:JSONEncode(arguments) or "", instanceMap 26 | end 27 | -------------------------------------------------------------------------------- /Source/Utilities/Remotes/prettifyVaradics.luau: -------------------------------------------------------------------------------- 1 | local HttpService = game:GetService("HttpService") 2 | 3 | local function tostring(object) 4 | local objectType = typeof(object) 5 | 6 | if objectType == "Instance" then 7 | return `Instance<'{object:GetFullName()}'>` 8 | elseif objectType == "string" then 9 | return `"{object}"` 10 | elseif objectType == "table" then 11 | if select(1, object) == nil then 12 | return "{ }" 13 | end 14 | 15 | local objectMap = "{ " 16 | 17 | for key, value in object do 18 | objectMap ..= `[{tostring(key)}] = {tostring(value)},` 19 | end 20 | 21 | return `{objectMap} }` 22 | end 23 | 24 | return object 25 | end 26 | 27 | return function(...) 28 | local arguments = { ... } 29 | 30 | for index, value in arguments do 31 | arguments[index] = tostring(value) 32 | end 33 | 34 | return HttpService:JSONEncode(arguments) or "" 35 | end 36 | -------------------------------------------------------------------------------- /Source/init.luau: -------------------------------------------------------------------------------- 1 | --[[ 2 | @class DevSuite 3 | 4 | Summary 5 | ]] 6 | 7 | local RunService = game:GetService("RunService") 8 | 9 | local Package = script 10 | 11 | local Sift = require(Package.Parent.Sift) 12 | local Runtime = require(Package.Parent.Runtime) 13 | 14 | --selene: allow(unused_variable) 15 | local LuauCeption = require(Package.Vendor.LuauCeption) 16 | 17 | local Client = RunService:IsClient() and require(Package.Client) 18 | local Server = RunService:IsServer() and require(Package.Server) 19 | 20 | local Authentication = Server and require(Package.Server.Singletons.Authentication) 21 | local Settings = Server and require(Package.Server.Singletons.Settings) 22 | local Interface = not Server and require(Package.Client.Singletons.Interface) 23 | 24 | local ServerActions = Server and require(Package.Server.Singletons.Actions) 25 | local ClientActions = not Server and require(Package.Client.Singletons.Actions) 26 | 27 | local VM = not Server and require(Package.Client.Singletons.VM) 28 | 29 | local DevSuite = {} 30 | 31 | DevSuite.Interface = {} 32 | DevSuite.Prototype = {} 33 | 34 | function DevSuite.Prototype.SetSettings(self: DevSuite, settings: { [string]: any }) 35 | assert(not self.isClient, `Cannot set settings on client.`) 36 | 37 | Settings:UpdateServerSettings(settings) 38 | end 39 | 40 | function DevSuite.Prototype.SetAuthenticationCallback(self: DevSuite, callback: (Player) -> boolean) 41 | assert(not self.isClient, `Cannot set authentication callback on client.`) 42 | 43 | Authentication.AuthenticationCallback = callback 44 | end 45 | 46 | function DevSuite.Prototype.CreateAction(self: DevSuite, actionCallback: (...any) -> (), actionSettings: { 47 | name: string, 48 | description: string?, 49 | arguments: { 50 | type: "Number" | "String" | "Boolean" | "Player", 51 | name: string, 52 | default: any? 53 | }?, 54 | }) 55 | local object = Sift.Dictionary.copyDeep(actionSettings) 56 | 57 | object.description = object.description or "" 58 | object.arguments = object.arguments or {} 59 | object.callback = actionCallback 60 | 61 | for index, argument in object.arguments do 62 | argument.type = argument.type or "String" 63 | argument.name = argument.name or "Argument " .. index 64 | 65 | if argument.default then 66 | argument.default = tostring(argument.default) 67 | end 68 | end 69 | 70 | if self.isClient then 71 | ClientActions:RegisterClientAction(object) 72 | else 73 | ServerActions:RegisterServerAction(object) 74 | end 75 | end 76 | 77 | function DevSuite.Prototype.AddLibraryToVM(self: DevSuite, name: string, library: { [string]: (luauCeption: LuauCeption, luaState: number) -> number }) 78 | assert(self.isClient, `Mutable VM is only available on client.`) 79 | 80 | VM:AddLibrary(name, library) 81 | end 82 | 83 | function DevSuite.Prototype.AddGlobalToVM(self: DevSuite, name: string, callback: (luauCeption: LuauCeption, luaState: number) -> number) 84 | assert(self.isClient, `Mutable VM is only available on client.`) 85 | 86 | VM:AddGlobal(name, callback) 87 | end 88 | 89 | function DevSuite.Prototype.HookVMMetamethod(self: DevSuite, name: string, callback: (luauCeption: LuauCeption, luaState: number) -> number) 90 | assert(self.isClient, `Mutable VM is only available on client.`) 91 | 92 | VM:HookMetamethod(name, callback) 93 | end 94 | 95 | function DevSuite.Prototype.AddPage(self: DevSuite, pageName: string, pageIcon: string?, renderCallback: () -> (), cleanupCallback: () -> ()) 96 | assert(self.isClient, `AddPage is only available on client.`) 97 | 98 | Interface:AddCustomPage(pageName, pageIcon, renderCallback, cleanupCallback) 99 | end 100 | 101 | function DevSuite.Interface.new(): DevSuite 102 | local self = setmetatable({}, { 103 | __index = DevSuite.Prototype 104 | }) 105 | 106 | if self.isClient then 107 | repeat 108 | task.wait() 109 | until script:GetAttribute("ServerActive") 110 | end 111 | 112 | self.isClient = RunService:IsClient() 113 | if self.isClient then 114 | self.context = Client.new() 115 | else 116 | self.context = Server.new() 117 | end 118 | 119 | if not self.isClient then 120 | script:SetAttribute("ServerActive", true) 121 | end 122 | 123 | return self 124 | end 125 | 126 | Runtime:SetFFValue("Version", "0.0.6") 127 | Runtime:SetFFValue("Branch", "Development") 128 | 129 | export type LuauCeption = typeof(LuauCeption) 130 | export type DevSuite = typeof(DevSuite.Prototype) & { 131 | isClient: boolean, 132 | context: any, 133 | } 134 | 135 | return DevSuite.Interface.new() :: DevSuite 136 | -------------------------------------------------------------------------------- /aftman.toml: -------------------------------------------------------------------------------- 1 | # This file lists tools managed by Aftman, a cross-platform toolchain manager. 2 | # For more information, see https://github.com/LPGhatguy/aftman 3 | 4 | # To add a new tool, add an entry to this table. 5 | 6 | [tools] 7 | rojo = "rojo-rbx/rojo@7.4.4" 8 | selene = "Kampfkarren/selene@0.25.0" 9 | wally = "UpliftGames/wally@0.3.2" 10 | zap = "red-blox/zap@0.6.8" -------------------------------------------------------------------------------- /default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dev-suite", 3 | "emitLegacyScripts": false, 4 | "tree": { 5 | "$path": "Source" 6 | } 7 | } -------------------------------------------------------------------------------- /development.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dev-suite", 3 | "emitLegacyScripts": false, 4 | "tree": { 5 | "$className": "DataModel", 6 | 7 | "ReplicatedStorage": { 8 | "$className": "ReplicatedStorage", 9 | "$ignoreUnknownInstances": true, 10 | 11 | "Packages": { 12 | "$className": "Folder", 13 | "$path": "Packages", 14 | 15 | "DevSuite": { 16 | "$path": "Source" 17 | } 18 | }, 19 | 20 | "DevPackages": { 21 | "$className": "Folder", 22 | "$path": "DevPackages" 23 | } 24 | }, 25 | 26 | "ServerScriptService": { 27 | "$className": "ServerScriptService", 28 | "$ignoreUnknownInstances": true, 29 | "$path": "Tests" 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /network.zap: -------------------------------------------------------------------------------- 1 | opt server_output = "Source/Server/Network.luau" 2 | opt client_output = "Source/Client/Network.luau" 3 | opt remote_scope = "DEBUG_REMOTES" 4 | opt casing = "PascalCase" 5 | 6 | type SettingOptions = struct { 7 | DefaultHotkey: string 8 | } 9 | 10 | type RemoteEvenRelay = struct { 11 | arguments: string, 12 | instances: Instance[], 13 | event: Instance, 14 | } 15 | 16 | type Log = struct { 17 | message: string, 18 | type: string, 19 | time: u32 20 | } 21 | 22 | type EncodedActionArgument = struct { 23 | type: string, 24 | value: string, 25 | } 26 | 27 | type ActionArgument = struct { 28 | type: string, 29 | name: string, 30 | default: string? 31 | } 32 | 33 | type Action = struct { 34 | path: string[], 35 | name: string, 36 | description: string, 37 | uuid: string, 38 | 39 | arguments: ActionArgument[] 40 | } 41 | 42 | funct AuthenticateRequested = { 43 | call: Async, 44 | rets: enum { Accepted, Rejected }, 45 | } 46 | 47 | funct FetchSettingsRequested = { 48 | call: Async, 49 | rets: SettingOptions, 50 | } 51 | 52 | event ServerSettingsUpdated = { 53 | from: Server, 54 | type: Reliable, 55 | call: SingleAsync, 56 | data: SettingOptions 57 | } 58 | 59 | event ServerRemotesFired = { 60 | from: Server, 61 | type: Reliable, 62 | call: ManyAsync, 63 | data: RemoteEvenRelay 64 | } 65 | 66 | event ServerReportingLogs = { 67 | from: Server, 68 | type: Reliable, 69 | call: ManyAsync, 70 | data: Log[] 71 | } 72 | 73 | event ClientReportingLogs = { 74 | from: Client, 75 | type: Reliable, 76 | call: ManyAsync, 77 | data: Log[] 78 | } 79 | 80 | event FetchActionsRequested = { 81 | from: Client, 82 | type: Reliable, 83 | call: ManyAsync 84 | } 85 | 86 | event ServerReportingActions = { 87 | from: Server, 88 | type: Reliable, 89 | call: ManyAsync, 90 | data: Action[] 91 | } 92 | 93 | event ExecuteServerActionRequested = { 94 | from: Client, 95 | type: Reliable, 96 | call: ManyAsync, 97 | data: struct { 98 | actionUuid: string, 99 | arguments: EncodedActionArgument[] 100 | } 101 | } 102 | 103 | event ExecuteServerScriptRequested = { 104 | from: Client, 105 | type: Reliable, 106 | call: ManyAsync, 107 | data: string 108 | } 109 | 110 | event KickUserRequested = { 111 | from: Client, 112 | type: Reliable, 113 | call: ManyAsync, 114 | data: Instance 115 | } 116 | 117 | event BanUserRequested = { 118 | from: Client, 119 | type: Reliable, 120 | call: ManyAsync, 121 | data: Instance 122 | } 123 | 124 | event AdminUserRequested = { 125 | from: Client, 126 | type: Reliable, 127 | call: ManyAsync, 128 | data: Instance 129 | } 130 | 131 | event RejoinRequested = { 132 | from: Client, 133 | type: Reliable, 134 | call: ManyAsync, 135 | } 136 | 137 | event ShutdownServerRequested = { 138 | from: Client, 139 | type: Reliable, 140 | call: ManyAsync, 141 | } 142 | 143 | funct HttpGetRequest = { 144 | call: Sync, 145 | args: string, 146 | rets: string 147 | } -------------------------------------------------------------------------------- /selene.toml: -------------------------------------------------------------------------------- 1 | std = "roblox+testez" -------------------------------------------------------------------------------- /testez.yml: -------------------------------------------------------------------------------- 1 | --- 2 | globals: 3 | FIXME: 4 | args: 5 | - required: false 6 | type: string 7 | FOCUS: 8 | args: [] 9 | SKIP: 10 | args: [] 11 | afterAll: 12 | args: 13 | - type: function 14 | afterEach: 15 | args: 16 | - type: function 17 | beforeAll: 18 | args: 19 | - type: function 20 | beforeEach: 21 | args: 22 | - type: function 23 | describe: 24 | args: 25 | - type: string 26 | - type: function 27 | describeFOCUS: 28 | args: 29 | - type: string 30 | - type: function 31 | describeSKIP: 32 | args: 33 | - type: string 34 | - type: function 35 | expect: 36 | args: 37 | - type: any 38 | it: 39 | args: 40 | - type: string 41 | - type: function 42 | itFIXME: 43 | args: 44 | - type: string 45 | - type: function 46 | itFOCUS: 47 | args: 48 | - type: string 49 | - type: function 50 | itSKIP: 51 | args: 52 | - type: string 53 | - type: function -------------------------------------------------------------------------------- /wally.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Wally. 2 | # It is not intended for manual editing. 3 | registry = "test" 4 | 5 | [[package]] 6 | name = "4x8matrix/class-index" 7 | version = "3.0.0" 8 | dependencies = [] 9 | 10 | [[package]] 11 | name = "4x8matrix/console" 12 | version = "2.1.2" 13 | dependencies = [["Signal", "sleitnick/signal@1.5.0"]] 14 | 15 | [[package]] 16 | name = "4x8matrix/dev-suite" 17 | version = "0.1.0" 18 | dependencies = [["ClassIndex", "4x8matrix/class-index@3.0.0"], ["Console", "4x8matrix/console@2.1.2"], ["Fusion", "elttob/fusion@0.3.0"], ["Highlighter", "boatbomber/highlighter@0.8.3"], ["Runtime", "4x8matrix/runtime@1.0.0"], ["Sift", "csqrl/sift@0.0.9"], ["Signal", "sleitnick/signal@2.0.1"]] 19 | 20 | [[package]] 21 | name = "4x8matrix/runtime" 22 | version = "1.0.0" 23 | dependencies = [] 24 | 25 | [[package]] 26 | name = "boatbomber/highlighter" 27 | version = "0.8.3" 28 | dependencies = [] 29 | 30 | [[package]] 31 | name = "csqrl/sift" 32 | version = "0.0.9" 33 | dependencies = [] 34 | 35 | [[package]] 36 | name = "elttob/fusion" 37 | version = "0.3.0" 38 | dependencies = [] 39 | 40 | [[package]] 41 | name = "sleitnick/signal" 42 | version = "1.5.0" 43 | dependencies = [] 44 | 45 | [[package]] 46 | name = "sleitnick/signal" 47 | version = "2.0.1" 48 | dependencies = [] 49 | -------------------------------------------------------------------------------- /wally.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "4x8matrix/dev-suite" 3 | version = "0.1.1" 4 | realm = "shared" 5 | registry = "https://github.com/UpliftGames/wally-index" 6 | licence = "MIT" 7 | authors = ["AsynchronousMatrix"] 8 | 9 | description = "A suite of tools for developing experiences with Roblox." 10 | 11 | exclude = ["**"] 12 | include = ["Source", "Source/*", "default.project.json", "wally.toml"] 13 | 14 | [dependencies] 15 | Signal = "sleitnick/signal@2.0.1" 16 | Runtime = "4x8matrix/runtime@1.0.0" 17 | Console = "4x8matrix/console@2.1.2" 18 | Fusion = "elttob/fusion@0.3.0" 19 | Sift = "csqrl/sift@0.0.9" 20 | Highlighter = "boatbomber/highlighter@0.8.3" 21 | ClassIndex = "4x8matrix/class-index@3.0.0" --------------------------------------------------------------------------------