Advertisement
CriShoux

Old Console

Mar 17th, 2020
961
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 71.09 KB | None | 0 0
  1. local Instance_new = Instance.new
  2. local UDim2_new = UDim2.new
  3. local Color3_new = Color3.new
  4. local math_max = math.max
  5. local tick = tick
  6. local pairs = pairs
  7. local require = getrenv().require;
  8. local os_time = os.time
  9. local DEBUG = false
  10.  
  11. local AnalyticsCategory_Game = "Game"
  12. local AnalyticsAction_InitialOpenTab = "DeveloperConsole_InitialOpenTab"
  13. local AnalyticsAction_ClickToOpenOpenTab = "DeveloperConsole_ClickToOpenOpenTab"
  14.  
  15. local CoreGui = game:GetService("CoreGui")
  16. local RobloxGui = CoreGui:FindFirstChild("RobloxGui")
  17. local Modules = RobloxGui:FindFirstChild("Modules")
  18.  
  19. local ContextActionService = game:GetService("ContextActionService")
  20. local TextService = game:GetService("TextService")
  21. local GuiService = game:GetService("GuiService")
  22. local VRService = game:GetService("VRService")
  23. local isTenFootInterface = GuiService:IsTenFootInterface()
  24.  
  25. local ClientMemoryAnalyzerClass = require(CoreGui:WaitForChild("RobloxGui"):WaitForChild("Modules"):WaitForChild("Stats"):WaitForChild("ClientMemoryAnalyzer"))
  26. local ServerMemoryAnalyzerClass = require(CoreGui:WaitForChild("RobloxGui"):WaitForChild("Modules"):WaitForChild("Stats"):WaitForChild("ServerMemoryAnalyzer"))
  27. local StatsUtils = require(CoreGui:WaitForChild("RobloxGui"):WaitForChild("Modules"):WaitForChild("Stats"):WaitForChild("StatsUtils"))
  28.  
  29. local Style; do
  30. -- local function c3(r, g, b)
  31. -- return Color3.new(r / 255, g / 255, b / 255)
  32. -- end
  33. local c3 = Color3.fromRGB;
  34. local frameColor = Color3.new(0.1, 0.1, 0.1)
  35. local textColor = Color3.new(1, 1, 1)
  36. local optionsFrameColor = Color3.new(1, 1, 1)
  37.  
  38. pcall(function() -- Fun window colors for cool people
  39. local Players = game:GetService("Players")
  40. if not Players or not Players.LocalPlayer then
  41. return
  42. end
  43. local FunColors = {
  44. [56449] = {c3(255, 63, 127)}; -- ReeseMcBlox
  45. [6949935] = {c3(255, 63, 127)}; -- NobleDragon
  46. }
  47. local funColor = FunColors[Players.LocalPlayer.UserId]
  48. if funColor then
  49. frameColor = funColor[1] or frameColor
  50. textColor = funColor[2] or textColor
  51. end
  52. end)
  53.  
  54. Style = {
  55. ZINDEX = 6;
  56. Font = Enum.Font.SourceSans;
  57. FontBold = Enum.Font.SourceSansBold;
  58.  
  59. HandleHeight = 24; -- How tall the top window handle is, as well as the width of the scroll bar
  60. TabHeight = 28;
  61. GearSize = 24;
  62. BorderSize = 2;
  63. CommandLineHeight = 22;
  64.  
  65. OptionAreaHeight = 56;
  66.  
  67. FrameColor = frameColor; -- Applies to pretty much everything, including buttons
  68. FrameTransparency = 0.5;
  69. OptionsFrameColor = optionsFrameColor;
  70.  
  71. TextColor = textColor;
  72.  
  73. MessageColors = {
  74. [0] = Color3.new(1, 1, 1); -- Enum.MessageType.MessageOutput
  75. [1] = Color3.new(0.4, 0.5, 1); -- Enum.MessageType.MessageInfo
  76. [2] = Color3.new(1, 0.6, 0.4); -- Enum.MessageType.MessageWarning
  77. [3] = Color3.new(1, 0, 0); -- Enum.MessageType.MessageError
  78. };
  79.  
  80. ScrollbarFrameColor = frameColor;
  81. ScrollbarBarColor = frameColor;
  82.  
  83. ScriptButtonHeight = 32;
  84. ScriptButtonColor = Color3.new(0, 1/3, 2/3);
  85. ScriptButtonTransparency = 0.5;
  86.  
  87. CheckboxSize = 24;
  88.  
  89. ChartTitleHeight = 20;
  90. ChartGraphHeight = 64;
  91. ChartDataHeight = 24;
  92. ChartHeight = 0; -- This gets added up at end and set at end of block
  93. ChartWidth = 620;
  94.  
  95. -- (-1) means right to left
  96. -- (1) means left to right
  97. ChartGraphDirection = 1; -- the direction the bars move
  98.  
  99.  
  100. GetButtonDownColor = function(normalColor)
  101. local r, g, b = normalColor.r, normalColor.g, normalColor.b
  102. return Color3.new(1 - 0.75 * (1 - r), 1 - 0.75 * (1 - g), 1 - 0.75 * (1 - b))
  103. end;
  104. GetButtonHoverColor = function(normalColor)
  105. local r, g, b = normalColor.r, normalColor.g, normalColor.b
  106. return Color3.new(1 - 0.875 * (1 - r), 1 - 0.875 * (1 - g), 1 - 0.875 * (1 - b))
  107. end;
  108.  
  109. }
  110.  
  111. Style.ChartHeight = Style.ChartTitleHeight + Style.ChartGraphHeight + Style.ChartDataHeight + Style.BorderSize
  112. end
  113.  
  114. local Primitives = {}; do
  115. local function new(className, parent, name)
  116. local n = Instance.new(className, parent)
  117. n.ZIndex = 0
  118. if name then
  119. n.Name = name
  120. end
  121. return n
  122. end
  123. local unitSize = UDim2.new(1, 0, 1, 0)
  124.  
  125. local function setupFrame(n)
  126. n.BackgroundColor3 = Style.FrameColor
  127. n.BackgroundTransparency = Style.FrameTransparency
  128. n.BorderSizePixel = 0
  129. end
  130. local function setupText(n, text)
  131. n.Font = Style.Font
  132. n.TextColor3 = Style.TextColor
  133. n.Text = text or n.Text
  134. end
  135.  
  136. function Primitives.Frame(parent, name)
  137. local n = new('Frame', parent, name)
  138. setupFrame(n)
  139. return n
  140. end
  141. function Primitives.TextLabel(parent, name, text)
  142. local n = new('TextLabel', parent, name)
  143. setupFrame(n)
  144. setupText(n, text)
  145. return n
  146. end
  147. function Primitives.TextBox(parent, name, text)
  148. local n = new('TextBox', parent, name)
  149. setupFrame(n)
  150. setupText(n, text)
  151. return n
  152. end
  153. function Primitives.TextButton(parent, name, text)
  154. local n = new('TextButton', parent, name)
  155. setupFrame(n)
  156. setupText(n, text)
  157. return n
  158. end
  159. function Primitives.Button(parent, name)
  160. local n = new('TextButton', parent, name)
  161. setupFrame(n)
  162. n.Text = ""
  163. return n
  164. end
  165. function Primitives.ImageButton(parent, name, image)
  166. local n = new('ImageButton', parent, name)
  167. setupFrame(n)
  168. n.Image = image or ""
  169. n.Size = unitSize
  170. return n
  171. end
  172.  
  173. -- An invisible frame of size (1, 0, 1, 0)
  174. function Primitives.FolderFrame(parent, name) -- Should this be called InvisibleFrame? lol
  175. local n = new('Frame', parent, name)
  176. n.BackgroundTransparency = 1
  177. n.Size = unitSize
  178. return n
  179. end
  180. function Primitives.InvisibleTextLabel(parent, name, text)
  181. local n = new('TextLabel', parent, name)
  182. setupText(n, text)
  183. n.BackgroundTransparency = 1
  184. return n
  185. end
  186. function Primitives.InvisibleButton(parent, name, text)
  187. local n = new('TextButton', parent, name)
  188. n.BackgroundTransparency = 1
  189. n.Text = ""
  190. return n
  191. end
  192. function Primitives.InvisibleImageLabel(parent, name, image)
  193. local n = new('ImageLabel', parent, name)
  194. n.BackgroundTransparency = 1
  195. n.Image = image or ""
  196. n.Size = unitSize
  197. return n
  198. end
  199. end
  200.  
  201. --[[ Flags ]]--
  202. local function checkFFlag(flagName)
  203. local flagSuccess, flagValue = pcall(function()
  204. return settings():GetFFlag(flagName)
  205. end)
  206. return (flagSuccess and flagValue)
  207. end
  208.  
  209.  
  210. -- Eye candy uses RenderStepped
  211. local EYECANDY_ENABLED = true
  212.  
  213. local AUTO_TAB_WIDTH = -1
  214. local TAB_TEXT_SIZE = 14
  215. local TAB_TEXT_PADDING = 8
  216.  
  217.  
  218. local function CreateSignal()
  219. local this = {}
  220.  
  221. local mBindableEvent = Instance.new('BindableEvent')
  222. local mAllCns = {} --all connection objects returned by mBindableEvent::connect
  223.  
  224. --main functions
  225. function this:connect(func)
  226. if self ~= this then error("connect must be called with `:`, not `.`", 2) end
  227. if type(func) ~= 'function' then
  228. error("Argument #1 of connect must be a function, got a "..type(func), 2)
  229. end
  230. local cn = mBindableEvent.Event:Connect(func)
  231. mAllCns[cn] = true
  232. local pubCn = {}
  233. function pubCn:disconnect()
  234. cn:Disconnect()
  235. mAllCns[cn] = nil
  236. end
  237. pubCn.Disconnect = pubCn.disconnect
  238.  
  239. return pubCn
  240. end
  241.  
  242. function this:disconnect()
  243. if self ~= this then error("disconnect must be called with `:`, not `.`", 2) end
  244. for cn, _ in pairs(mAllCns) do
  245. cn:Disconnect()
  246. mAllCns[cn] = nil
  247. end
  248. end
  249.  
  250. function this:wait()
  251. if self ~= this then error("wait must be called with `:`, not `.`", 2) end
  252. return mBindableEvent.Event:Wait()
  253. end
  254.  
  255. function this:fire(...)
  256. if self ~= this then error("fire must be called with `:`, not `.`", 2) end
  257. mBindableEvent:Fire(...)
  258. end
  259.  
  260. this.Connect = this.connect
  261. this.Disconnect = this.disconnect
  262. this.Wait = this.wait
  263. this.Fire = this.fire
  264.  
  265. return this
  266. end
  267.  
  268. -- This is a Signal that only calls once, then forgets about the function. It also accepts event listeners as functions
  269. local CreateDisconnectSignal; do
  270. local Methods = {}
  271. local Metatable = {__index = Methods}
  272. function Methods.fire(this, ...)
  273. return this.Signal:fire(...)
  274. end
  275. function Methods.wait(this, ...)
  276. return this.Signal:wait(...)
  277. end
  278. function Methods.connect(this, func)
  279. local t = type(func)
  280. if t == 'table' or t == 'userdata' then
  281. -- Got event listener
  282. local listener = func
  283. function func()
  284. listener:disconnect()
  285. end
  286. elseif t ~= 'function' then
  287. error('Invalid disconnect method type: ' .. t, 2)
  288. end
  289.  
  290. local listener;
  291. listener = this.Signal:connect(function(...)
  292. if listener then
  293. listener:disconnect()
  294. listener = nil
  295. func(...)
  296. end
  297. end)
  298. return listener
  299. end
  300. function CreateDisconnectSignal()
  301. return setmetatable({
  302. Signal = CreateSignal();
  303. }, Metatable)
  304. end
  305. end
  306.  
  307. -- Services
  308. local UserInputService = game:GetService('UserInputService')
  309. local RunService = game:GetService('RunService')
  310. local TouchEnabled = UserInputService.TouchEnabled
  311.  
  312. local DeveloperConsole = {}
  313.  
  314. local Methods = {}
  315. local Metatable = {__index = Methods}
  316.  
  317. -------------------------
  318. -- Listener management --
  319. -------------------------
  320. function Methods.ConnectSetVisible(devConsole, func)
  321. -- This is used mainly for pausing rendering and stuff when the console isn't visible
  322. func(devConsole.Visible)
  323. return devConsole.VisibleChanged:connect(function(visible)
  324. func(visible)
  325. end)
  326. end
  327. function Methods.ConnectObjectSetVisible(devConsole, object, func)
  328. -- Same as above, but used for calling methods like object:SetVisible()
  329. func(object, devConsole.Visible)
  330. return devConsole.VisibleChanged:connect(function(visible)
  331. func(object, visible)
  332. end)
  333. end
  334.  
  335. -----------------------------
  336. -- Frame/Window Dimensions --
  337. -----------------------------
  338.  
  339. local function connectPropertyChanged(object, property, callback)
  340. return object.Changed:connect(function(propertyChanged)
  341. if propertyChanged == property then
  342. callback(object[property])
  343. end
  344. end)
  345. end
  346.  
  347. function Methods.ResetFrameDimensions(devConsole)
  348. devConsole.Frame.Size = UDim2_new(0.5, 20, 0.5, 20);
  349.  
  350. local abSize = devConsole.Frame.AbsoluteSize
  351. devConsole:SetFrameSize(abSize.x, abSize.y)
  352. local newSize = devConsole.Frame.Size
  353. devConsole.Frame.Position = UDim2_new(0.5, -newSize.X.Offset/2, 0.5, -newSize.Y.Offset/2)
  354. end
  355. function Methods.BoundFrameSize(devConsole, x, y)
  356. -- Minimum frame size
  357. return math_max(x, 400), math_max(y, 200)
  358. end
  359. function Methods.SetFrameSize(devConsole, x, y)
  360. x, y = devConsole:BoundFrameSize(x, y)
  361. devConsole.Frame.Size = UDim2_new(0, x, 0, y)
  362. end
  363. function Methods.BoundFramePosition(devConsole, x, y)
  364. -- Make sure the frame doesn't go somewhere where the bar can't be clicked
  365. return x, math_max(y, 0)
  366. end
  367. function Methods.SetFramePosition(devConsole, x, y)
  368. x, y = devConsole:BoundFramePosition(x, y)
  369. devConsole.Frame.Position = UDim2_new(0, x, 0, y)
  370. end
  371.  
  372. -- Open/Close the console
  373. function Methods.SetVisible(devConsole, visible, animate)
  374. if devConsole.Visible == visible then
  375. return
  376. end
  377. devConsole.Visible = visible
  378. devConsole.VisibleChanged:fire(visible)
  379. if devConsole.Frame then
  380. devConsole.Frame.Visible = visible
  381. end
  382. if visible then -- Open the console
  383. devConsole:ResetFrameDimensions()
  384.  
  385. local tab = devConsole:GetCurrentOpenTab()
  386. if (tab ~= nil) then
  387. tab:RecordInitialOpen()
  388. end
  389. end
  390. if VRService.VREnabled then
  391. if visible then
  392. UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.ForceShow
  393. else
  394. UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.ForceHide
  395. end
  396. end
  397. end
  398.  
  399. -----------------
  400. -- Constructor --
  401. -----------------
  402. function DeveloperConsole.new(screenGui, permissions, messagesAndStats)
  403.  
  404. local visibleChanged = CreateSignal()
  405.  
  406. local devConsole = {
  407. ScreenGui = screenGui;
  408. Permissions = permissions;
  409. MessagesAndStats = messagesAndStats;
  410. Initialized = false;
  411. Visible = false;
  412. Tabs = {};
  413. CurrentOpenedTab = nil; -- save last tab opened to set SelectedCoreObject for TenFootInterfaces
  414. VisibleChanged = visibleChanged; -- Created by :Initialize(); It's used to stop and disconnect things when the window is hidden
  415. }
  416.  
  417. setmetatable(devConsole, Metatable)
  418.  
  419. devConsole:EnableGUIMouse()
  420.  
  421. -- It's a button so it catches mouse events
  422. local frame = Primitives.Button(screenGui, 'DeveloperConsole')
  423. frame.AutoButtonColor = false
  424. --frame.ClipsDescendants = true
  425. frame.Visible = devConsole.Visible
  426. frame.Selectable = not isTenFootInterface
  427.  
  428. local function onVREnabled()
  429. frame.Modal = VRService.VREnabled
  430. end
  431. onVREnabled()
  432. VRService:GetPropertyChangedSignal("VREnabled"):connect(onVREnabled)
  433.  
  434. devConsole.Frame = frame
  435. devConsole:ResetFrameDimensions()
  436.  
  437. -- The bar at the top that you can drag around
  438. local handle = Primitives.Button(frame, 'Handle')
  439. handle.Size = UDim2_new(1, -(Style.HandleHeight + Style.BorderSize), 0, Style.HandleHeight)
  440. handle.Selectable = not isTenFootInterface
  441. handle.Modal = true -- Unlocks mouse
  442. handle.AutoButtonColor = false
  443.  
  444.  
  445. do -- Title
  446. local title = Primitives.InvisibleTextLabel(handle, 'Title', "Developer Console")
  447. title.Size = UDim2_new(1, -5, 1, 0)
  448. title.Position = UDim2_new(0, 5, 0, 0)
  449. title.FontSize = Enum.FontSize.Size18
  450. title.TextXAlignment = Enum.TextXAlignment.Left
  451. end
  452.  
  453. local function setCornerButtonImageSize(buttonImage, buttonImageSize)
  454. buttonImage.Size = UDim2_new(buttonImageSize, 0, buttonImageSize, 0)
  455. buttonImage.Position = UDim2_new((1 - buttonImageSize) / 2, 0, (1 - buttonImageSize) / 2, 0)
  456. end
  457.  
  458. -- This is used for creating the square exit button and the square window resize button
  459. local function createCornerButton(name, x, y, image, buttonImageSize)
  460. -- Corners (x, y):
  461. -- (0, 0) (1, 0)
  462. -- (0, 1) (1, 1)
  463.  
  464. local button = Primitives.Button(frame, name)
  465. button.Size = UDim2_new(0, Style.HandleHeight, 0, Style.HandleHeight)
  466. button.Position = UDim2_new(x, -x * Style.HandleHeight, y, -y * Style.HandleHeight)
  467.  
  468. local buttonImage = Primitives.InvisibleImageLabel(button, 'Image', image)
  469. setCornerButtonImageSize(buttonImage, buttonImageSize)
  470.  
  471. return button, buttonImage
  472. end
  473.  
  474. do -- Create top right exit button
  475. local exitButton, exitButtonImage = createCornerButton('Exit', 1, 0, 'https://www.roblox.com/asset/?id=261878266', 2/3)
  476. exitButton.AutoButtonColor = false
  477. exitButton.Visible = not isTenFootInterface
  478. exitButton.Selectable = not isTenFootInterface
  479.  
  480. local buttonEffectFunction = devConsole:CreateButtonEffectFunction(exitButton)
  481.  
  482. devConsole:ConnectButtonHover(exitButton, function(clicking, hovering)
  483. if hovering and not clicking then
  484. setCornerButtonImageSize(exitButtonImage, 3/4)
  485. else
  486. setCornerButtonImageSize(exitButtonImage, 2/3)
  487. end
  488. buttonEffectFunction(clicking, hovering)
  489. end)
  490.  
  491. exitButton.MouseButton1Click:connect(function()
  492. devConsole:SetVisible(false, true)
  493. end)
  494. end
  495.  
  496. do -- Repositioning and Resizing
  497.  
  498. do -- Create bottom right window resize button and activate resize dragging
  499. local resizeButton, resizeButtonImage = createCornerButton('Resize', 1, 1, 'https://www.roblox.com/asset/?id=261880743', 1)
  500. resizeButtonImage.Position = UDim2_new(0, 0, 0, 0)
  501. resizeButtonImage.Size = UDim2_new(1, 0, 1, 0)
  502. resizeButton.Selectable = not isTenFootInterface
  503.  
  504. local dragging = false
  505.  
  506. local buttonEffectFunction = devConsole:CreateButtonEffectFunction(resizeButton)
  507.  
  508. devConsole:ConnectButtonDragging(resizeButton, function()
  509. local x0, y0 = frame.AbsoluteSize.X, frame.AbsoluteSize.Y
  510. return function(dx, dy)
  511. devConsole:SetFrameSize(x0 + dx, y0 + dy)
  512. end
  513. end, function(clicking, hovering)
  514. dragging = clicking
  515. buttonEffectFunction(clicking, hovering)
  516. end)
  517.  
  518. end
  519.  
  520. do -- Activate top handle dragging
  521. local frame = devConsole.Frame
  522. local handle = frame.Handle
  523.  
  524. local buttonEffectFunction = devConsole:CreateButtonEffectFunction(handle)
  525.  
  526. devConsole:ConnectButtonDragging(handle, function()
  527. local x, y = frame.AbsolutePosition.X, frame.AbsolutePosition.Y
  528. return function(dx, dy)
  529. devConsole:SetFramePosition(x + dx, y + dy)
  530. end
  531. --deltaCallback_Resize(-dx, -dy) -- Used if they are grabbing both at the same time
  532. end, buttonEffectFunction)
  533. end
  534. end
  535.  
  536. -- interiorFrame contains tabContainer and window
  537. local interiorFrame = Primitives.FolderFrame(frame, 'Interior')
  538. interiorFrame.Position = UDim2_new(0, 0, 0, Style.HandleHeight)
  539. interiorFrame.Size = UDim2_new(1, -(Style.HandleHeight + Style.BorderSize * 2), 1, -(Style.HandleHeight + Style.BorderSize))
  540.  
  541. local windowContainer = Primitives.FolderFrame(interiorFrame, 'WindowContainer')
  542. windowContainer.Size = UDim2_new(1, 0, 1, -(Style.TabHeight))
  543. windowContainer.Position = UDim2_new(0, Style.BorderSize, 0, Style.TabHeight)
  544.  
  545. -- This is what applies ClipsDescendants to tab contents
  546. local window = Primitives.Frame(windowContainer, 'Window')
  547. window.Size = UDim2_new(1, 0, 1, 0) -- The tab open/close methods, and the consoles also set this
  548. window.Position = UDim2_new(0, 0, 0, 0)
  549. window.ClipsDescendants = true
  550.  
  551. -- This is the frame that moves around with the scroll bar
  552. local body = Primitives.FolderFrame(window, 'Body')
  553.  
  554. do -- Scrollbars
  555. local scrollbar = devConsole:CreateScrollbar()
  556. devConsole.WindowScrollbar = scrollbar
  557. local scrollbarFrame = scrollbar.Frame
  558. scrollbarFrame.Parent = frame
  559. scrollbarFrame.Size = UDim2_new(0, Style.HandleHeight, 1, -(Style.HandleHeight + Style.BorderSize) * 2)
  560. scrollbarFrame.Position = UDim2_new(1, -Style.HandleHeight, 0, Style.HandleHeight + Style.BorderSize)
  561.  
  562. devConsole:ApplyScrollbarToFrame(scrollbar, window, body, frame)
  563. end
  564.  
  565. local tabContainer = Primitives.FolderFrame(interiorFrame, 'Tabs') -- Shouldn't this be named 'tabFrame'?
  566. tabContainer.Size = UDim2_new(1, -(Style.GearSize + Style.BorderSize), 0, Style.TabHeight)
  567. tabContainer.Position = UDim2_new(0, 0, 0, 0)
  568. tabContainer.ClipsDescendants = true
  569.  
  570. -- Options button
  571. local optionsButton = Primitives.InvisibleButton(frame, 'OptionsButton')
  572.  
  573. local optionsClippingFrame = Primitives.FolderFrame(interiorFrame, 'OptionsClippingFrame')
  574. optionsClippingFrame.ClipsDescendants = true
  575. optionsClippingFrame.Position = UDim2_new(0, 0, 0, 0)
  576. optionsClippingFrame.Size = UDim2_new(1, 0, 0, 0)
  577. local optionsFrame = Primitives.FolderFrame(optionsClippingFrame, 'OptionsFrame')
  578. optionsFrame.Size = UDim2_new(1, 0, 0, Style.OptionAreaHeight)
  579. optionsFrame.Position = UDim2_new(0, 0, 0, Style.OptionAreaHeight)
  580. --optionsFrame.BackgroundColor3 = Style.OptionsFrameColor
  581. do -- Options animation
  582.  
  583. local gearSize = Style.GearSize
  584. local tabHeight = Style.TabHeight
  585. local offset = (tabHeight - gearSize) / 2
  586. optionsButton.Size = UDim2_new(0, Style.GearSize, 0, Style.GearSize)
  587. optionsButton.Position = UDim2_new(1, -(Style.GearSize + offset + Style.HandleHeight), 0, Style.HandleHeight + offset)
  588. local gear = Primitives.InvisibleImageLabel(optionsButton, 'Image', 'https://www.roblox.com/asset/?id=261882463')
  589. --gear.ZIndex = ZINDEX + 1
  590. local animationToggle = devConsole:GenerateOptionButtonAnimationToggle(interiorFrame, optionsButton, gear, tabContainer, optionsClippingFrame, optionsFrame)
  591. local open = false
  592. optionsButton.MouseButton1Click:connect(function()
  593. open = not open
  594. animationToggle(open)
  595. end)
  596.  
  597. end
  598.  
  599. -- Console/Log and Stats options
  600. local setShownOptionTypes; -- Toggles what options to show: setOptionType({Log = true})
  601.  
  602. local textFilter, scriptStatFilter;
  603. local textFilterChanged, scriptStatFilterChanged;
  604.  
  605. local messageFilter;
  606. local messageFilterChanged, messageTextWrappedChanged;
  607. do -- Options contents/filters
  608.  
  609. local function createCheckbox(color, callback)
  610. local this = {
  611. Value = true;
  612. }
  613.  
  614. local frame = Primitives.FolderFrame(nil, 'Checkbox')
  615. this.Frame = frame
  616. frame.Size = UDim2_new(0, Style.CheckboxSize, 0, Style.CheckboxSize)
  617. frame.BackgroundColor3 = color
  618.  
  619. local padding = 2
  620.  
  621. local function f(xs, xp, yp) -- quick way to get an opaque border around a transparent center
  622. local ys = 1 - xs
  623. local f = Primitives.Frame(frame, 'Border')
  624. f.BackgroundColor3 = color
  625. f.BackgroundTransparency = 0
  626. f.Size = UDim2_new(xs, ys * padding, ys, xs * padding)
  627. f.Position = UDim2_new(xp, -xp * padding, yp, -yp * padding)
  628. end
  629. f(1, 0, 0)
  630. f(1, 0, 1)
  631. f(0, 0, 0)
  632. f(0, 1, 0)
  633.  
  634. local button = Primitives.Button(frame, 'Button')
  635. button.Size = UDim2_new(1, -padding * 2, 1, -padding * 2)
  636. button.Position = UDim2_new(0, padding, 0, padding)
  637.  
  638. local buttonEffectFunction = devConsole:CreateButtonEffectFunction(button)
  639.  
  640. local check = Primitives.Frame(button, 'Check')
  641.  
  642. local padding = 4
  643. check.Size = UDim2_new(1, -padding * 2, 1, -padding * 2)
  644. check.Position = UDim2_new(0, padding, 0, padding)
  645. check.BackgroundColor3 = color
  646. check.BackgroundTransparency = 0
  647.  
  648. devConsole:ConnectButtonHover(button, buttonEffectFunction)
  649.  
  650. function this.SetValue(this, value)
  651. if value == this.Value then
  652. return
  653. end
  654. this.Value = value
  655. check.Visible = value
  656. this.Value = value
  657. callback(value)
  658. end
  659.  
  660. button.MouseButton1Click:connect(function()
  661. this:SetValue(not this.Value)
  662. end)
  663.  
  664. return this
  665. end
  666.  
  667. local string_find = string.find
  668. local containsString; -- the text typed into the search textBox, nil if equal to ""
  669.  
  670. function textFilter(text)
  671. return not containsString or string_find(text:lower(), containsString)
  672. end
  673.  
  674. local filterLookup = {} -- filterLookup[Enum.MessageType.x.Value] = true or false
  675. function messageFilter(message)
  676. return filterLookup[message.Type] and (not containsString or string_find(message.Message:lower(), containsString))
  677. end
  678.  
  679. -- Events
  680. textFilterChanged = CreateSignal()
  681. scriptStatFilterChanged = CreateSignal()
  682.  
  683. messageFilterChanged = CreateSignal()
  684. messageTextWrappedChanged = CreateSignal()
  685.  
  686. local optionTypeContainers = {
  687. --[OptionType] = Frame
  688. --Log = Frame;
  689. --Scripts = Frame;
  690. }
  691. function setShownOptionTypes(shownOptionTypes)
  692. -- Example showOptionTypes:
  693. -- {Log = true}
  694. for optionType, container in pairs(optionTypeContainers) do
  695. container.Visible = shownOptionTypes[optionType] or false
  696. end
  697. end
  698.  
  699. do -- Log options
  700. local container = Primitives.FolderFrame(optionsFrame, 'Log')
  701. container.Visible = false
  702. optionTypeContainers.Log = container
  703.  
  704. local label = Primitives.InvisibleTextLabel(container, 'FilterLabel', "Filters")
  705. label.FontSize = 'Size18'
  706. label.TextXAlignment = 'Left'
  707. label.Size = UDim2_new(0, 54, 0, Style.CheckboxSize)
  708. label.Position = UDim2_new(0, 4, 0, 2)
  709.  
  710. do
  711. local x = label.Size.X.Offset
  712. local messageColors = Style.MessageColors
  713. for i = 0, #messageColors do -- 0, 3 initially
  714. local checkbox = createCheckbox(messageColors[i], function(value)
  715. filterLookup[i] = value
  716. messageFilterChanged:fire()
  717. end)
  718. filterLookup[i] = checkbox.Value
  719. checkbox.Frame.Parent = container
  720. checkbox.Frame.Position = UDim2_new(0, x, 0, 4)
  721. x = x + Style.CheckboxSize + 4
  722. end
  723.  
  724. do -- Word wrap
  725. x = x + 8
  726.  
  727. local label = Primitives.InvisibleTextLabel(container, 'WrapLabel', "Word Wrap")
  728. label.FontSize = 'Size18'
  729. label.TextXAlignment = 'Left'
  730. label.Size = UDim2_new(0, 54 + Style.CheckboxSize, 0, Style.CheckboxSize)
  731. label.Position = UDim2_new(0, x + 4, 0, 2)
  732.  
  733. local checkbox = createCheckbox(Color3.new(0.65, 0.65, 0.65), function(value)
  734. messageTextWrappedChanged:fire(value) -- an event isn't ideal here
  735. end)
  736. checkbox:SetValue(false)
  737. checkbox.Frame.Parent = container
  738. checkbox.Frame.Position = UDim2_new(0, x + label.Size.X.Offset, 0, 4)
  739. end
  740. end
  741. end
  742.  
  743. do -- Scripts options
  744. local container = Primitives.FolderFrame(optionsFrame, 'Stats')
  745. container.Visible = false
  746. optionTypeContainers.Scripts = container
  747.  
  748. do
  749. local x = 0
  750.  
  751. do -- Show inactive
  752. x = x + 4
  753. local label = Primitives.InvisibleTextLabel(container, 'FilterLabel', "Show inactive")
  754. label.FontSize = 'Size18'
  755. label.TextXAlignment = 'Left'
  756. label.Size = UDim2_new(0, label.TextBounds.X + 6, 0, Style.CheckboxSize)
  757. label.Position = UDim2_new(0, x, 0, 2)
  758. x = x + label.Size.X.Offset
  759.  
  760. local showInactive;
  761. local function getScriptCurrentlyActive(chartStat)
  762. local stats = chartStat.Stats
  763. if stats then
  764. local stat = stats[#stats]
  765. if stat then
  766. return stat[1] > 0.000001 or stat[2] > 0.000001
  767. end
  768. end
  769. return false
  770. end
  771. function scriptStatFilter(chartStat)
  772. return (showInactive or getScriptCurrentlyActive(chartStat))
  773. and (not containsString or string_find(chartStat.Name:lower(), containsString))
  774. end
  775.  
  776. local checkbox = createCheckbox(Color3_new(1, 1, 1), function(value)
  777. showInactive = value
  778. scriptStatFilterChanged:fire()
  779. end)
  780. showInactive = checkbox.Value
  781. checkbox.Frame.Parent = container
  782. checkbox.Frame.Position = UDim2_new(0, x, 0, 4)
  783. x = x + Style.CheckboxSize + 4
  784.  
  785. end
  786.  
  787. x = x + 8
  788. end
  789. end
  790.  
  791. do -- Search/filter/contains textbox
  792.  
  793. local container = Primitives.FolderFrame(optionsFrame, 'Search')
  794. container.Visible = false
  795. optionTypeContainers.Search = container
  796.  
  797. local label = Primitives.InvisibleTextLabel(container, 'FilterLabel', "Contains:")
  798. label.FontSize = 'Size18'
  799. label.TextXAlignment = 'Left'
  800. label.Size = UDim2_new(0, 60, 0, Style.CheckboxSize)
  801. label.Position = UDim2_new(0, 4, 0, 4 + Style.CheckboxSize + 4)
  802.  
  803. local textBox = Primitives.TextBox(container, 'ContainsFilter')
  804. textBox.ClearTextOnFocus = true
  805. textBox.FontSize = 'Size18'
  806. textBox.TextXAlignment = 'Left'
  807. textBox.Size = UDim2_new(0, 150, 0, Style.CheckboxSize)
  808. textBox.Position = UDim2_new(0, label.Position.X.Offset + label.Size.X.Offset + 4, 0, 4 + Style.CheckboxSize + 4)
  809. textBox.Text = ""
  810.  
  811. local runningColor = Color3.new(0, 0.5, 0)
  812. local normalColor = textBox.BackgroundColor3
  813.  
  814. connectPropertyChanged(textBox, 'Text', function(text)
  815. text = text:lower()
  816. if text == "" then
  817. text = nil
  818. end
  819. if text == containsString then
  820. return
  821. end
  822. textBox.BackgroundColor3 = text and runningColor or normalColor
  823. containsString = text
  824. messageFilterChanged:fire()
  825. textFilterChanged:fire()
  826. end)
  827.  
  828. connectPropertyChanged(textBox, 'TextBounds', function(textBounds)
  829. textBox.Size = UDim2_new(0, math.max(textBounds.X, 150), 0, Style.CheckboxSize)
  830. end)
  831. end
  832. end
  833.  
  834. ----------
  835. -- Tabs --
  836. ----------
  837. do -- Console/Log tabs
  838.  
  839. -- Wrapper for :AddTab
  840. local function createConsoleTab(name,
  841. text,
  842. outputMessageSync,
  843. commandLineVisible,
  844. commandInputtedCallback,
  845. openCallback)
  846. local tabBody = Primitives.FolderFrame(body, name)
  847. local output, commandLine;
  848. local disconnector = CreateDisconnectSignal()
  849.  
  850. local tab = devConsole:AddTab(text, tabBody, function(open)
  851. if commandLine then
  852. commandLine.Frame.Visible = open
  853. end
  854.  
  855. if open then
  856. setShownOptionTypes({
  857. Log = true;
  858. Search = true;
  859. })
  860.  
  861. if not output then
  862. output = devConsole:CreateOutput(outputMessageSync:GetMessages(), messageFilter)
  863. output.Frame.Parent = tabBody
  864. end
  865.  
  866. output:SetVisible(true)
  867.  
  868. if commandLineVisible then
  869. if open and not commandLine then
  870. commandLine = devConsole:CreateCommandLine()
  871. commandLine.Frame.Parent = frame
  872. commandLine.Frame.Size = UDim2_new(1,
  873. -(Style.HandleHeight + Style.BorderSize * 2),
  874. 0,
  875. Style.CommandLineHeight)
  876. commandLine.Frame.Position = UDim2_new(0,
  877. Style.BorderSize,
  878. 1,
  879. -(Style.CommandLineHeight + Style.BorderSize))
  880. commandLine.CommandInputted:connect(commandInputtedCallback)
  881. end
  882. end
  883.  
  884. window.Size = commandLineVisible
  885. and UDim2_new(1, 0, 1, -(Style.HandleHeight))
  886. or UDim2_new(1, 0, 1, 0)
  887.  
  888.  
  889. local messages = outputMessageSync:GetMessages()
  890.  
  891. local height = output:RefreshMessages()
  892. body.Size = UDim2_new(1, 0, 0, height)
  893.  
  894. disconnector:connect(output.HeightChanged:connect(function(height)
  895. body.Size = UDim2_new(1, 0, 0, height)
  896. end))
  897. body.Size = UDim2_new(1, 0, 0, output.Height)
  898.  
  899. disconnector:connect(outputMessageSync.MessageAdded:connect(function(message)
  900. output:RefreshMessages(#messages)
  901. end))
  902.  
  903. disconnector:connect(messageFilterChanged:connect(function()
  904. output:RefreshMessages()
  905. end))
  906. disconnector:connect(messageTextWrappedChanged:connect(function(enabled)
  907. output:SetTextWrappedEnabled(enabled)
  908. end))
  909. else
  910. if output then
  911. output:SetVisible(false)
  912. end
  913. window.Size = UDim2_new(1, 0, 1, 0)
  914. disconnector:fire()
  915. end
  916. if openCallback then
  917. openCallback(open)
  918. end
  919. end)
  920.  
  921. return tab
  922. end
  923.  
  924. -- Client Log tab --
  925. if permissions.MayViewClientLog then
  926. local tab = createConsoleTab(
  927. 'ClientLog', "Client Log",
  928. devConsole.MessagesAndStats.OutputMessageSyncLocal,
  929. true,
  930. function(text)
  931. if #text <= 1 then
  932. return
  933. end
  934. local f, err = loadstring(text)
  935. if err then
  936. warn(err)
  937. return
  938. end
  939. f();
  940. end
  941. )
  942. tab:SetVisible(true)
  943. tab:SetOpen(true)
  944. end
  945. end
  946.  
  947.  
  948. return devConsole
  949. end
  950.  
  951. ----------------------
  952. -- Backup GUI Mouse --
  953. ----------------------
  954. do -- This doesn't support multiple windows very well
  955. function Methods.EnableGUIMouse(devConsole)
  956. local label = Instance.new("ImageLabel")
  957. label.BackgroundTransparency = 1
  958. label.BorderSizePixel = 0
  959. label.Size = UDim2.new(0, 64, 0, 64)
  960. label.Image = "rbxasset://Textures/ArrowFarCursor.png"
  961. label.Name = "BackupMouse"
  962. label.ZIndex = Style.ZINDEX + 2
  963.  
  964. local disconnector = CreateDisconnectSignal()
  965.  
  966. local enabled = false
  967.  
  968. local mouse = game:GetService("Players").LocalPlayer:GetMouse()
  969.  
  970. local function Refresh()
  971. local enabledNew = devConsole.Visible and not UserInputService.MouseIconEnabled
  972. if enabledNew == enabled then
  973. return
  974. end
  975. enabled = enabledNew
  976. label.Visible = enabled
  977. label.Parent = enabled and devConsole.ScreenGui or nil
  978. disconnector:fire()
  979. if enabled then
  980. label.Position = UDim2.new(0, mouse.X - 32, 0, mouse.Y - 32)
  981. disconnector:connect(UserInputService.InputChanged:connect(function(input)
  982. if input.UserInputType == Enum.UserInputType.MouseMovement then
  983. --local p = input.Position
  984. --if p then
  985. label.Position = UDim2.new(0, mouse.X - 32, 0, mouse.Y - 32)
  986. --end
  987. end
  988. end))
  989. end
  990. end
  991.  
  992. Refresh()
  993. local userInputServiceListener;
  994. devConsole.VisibleChanged:connect(function(visible)
  995. if userInputServiceListener then
  996. userInputServiceListener:disconnect()
  997. userInputServiceListener = nil
  998. end
  999.  
  1000. userInputServiceListener = UserInputService.Changed:connect(Refresh)
  1001.  
  1002. Refresh()
  1003. end)
  1004.  
  1005. end
  1006. end
  1007.  
  1008. --------------------
  1009. -- Output console --
  1010. --------------------
  1011. do
  1012. function Methods.CreateCommandLine(devConsole)
  1013. local this = {
  1014. CommandInputted = CreateSignal();
  1015. }
  1016.  
  1017. local frame = Primitives.FolderFrame(nil, 'CommandLine')
  1018. this.Frame = frame
  1019. frame.Size = UDim2_new(1, 0, 0, Style.CommandLineHeight)
  1020.  
  1021. local textBoxFrame = Primitives.Frame(frame, 'TextBoxFrame')
  1022. textBoxFrame.Size = UDim2_new(1, 0, 0, Style.CommandLineHeight)
  1023. textBoxFrame.Position = UDim2_new(0, 0, 0, 0)
  1024. textBoxFrame.ClipsDescendants = true
  1025.  
  1026. local label = Primitives.InvisibleTextLabel(textBoxFrame, 'Label', ">")
  1027. label.Position = UDim2_new(0, 4, 0, 0)
  1028. label.Size = UDim2_new(0, 12, 1, -1)
  1029. label.FontSize = 'Size14'
  1030.  
  1031. local DEFAULT_COMMAND_BAR_TEXT = "Type command here"
  1032.  
  1033. local textBox = Primitives.TextBox(textBoxFrame, 'TextBox')
  1034. --textBox.TextWrapped = true -- This needs to auto-resize
  1035. textBox.BackgroundTransparency = 1
  1036. textBox.Text = DEFAULT_COMMAND_BAR_TEXT
  1037. textBox.ClearTextOnFocus = false
  1038. local padding = 2
  1039. textBox.Size = UDim2_new(1, -(padding * 2) - 4 - 12, 0, 500)
  1040. textBox.Position = UDim2_new(0, 4 + 12 + padding, 0, 0)
  1041. textBox.TextXAlignment = 'Left'
  1042. textBox.TextYAlignment = 'Top'
  1043. textBox.FontSize = 'Size18'
  1044. textBox.TextWrapped = true
  1045.  
  1046. -- override SelectionImageObject to better fit
  1047. if isTenFootInterface then
  1048. local selectionImage = Instance.new('ImageLabel')
  1049. selectionImage.Name = "SelectionImage"
  1050. selectionImage.Size = UDim2.new(1, textBoxFrame.AbsoluteSize.x + 36, 0, Style.CommandLineHeight + 24)
  1051. selectionImage.Position = UDim2.new(0, -18, 0, -12)
  1052. selectionImage.Image = 'rbxasset://textures/ui/SelectionBox.png'
  1053. selectionImage.ScaleType = Enum.ScaleType.Slice
  1054. selectionImage.SliceCenter = Rect.new(21,21,41,41)
  1055. selectionImage.BackgroundTransparency = 1
  1056.  
  1057. textBox.SelectionImageObject = selectionImage
  1058. end
  1059.  
  1060. do
  1061. local defaultSize = UDim2_new(1, 0, 0, Style.CommandLineHeight)
  1062. local first = true
  1063.  
  1064. textBox.Changed:connect(function(property)
  1065. if property == 'TextBounds' or property == 'AbsoluteSize' then
  1066. if first then -- There's a glitch that only occurs on the first change
  1067. first = false
  1068. return
  1069. end
  1070. local textBounds = textBox.TextBounds
  1071. if textBounds.Y > Style.CommandLineHeight then
  1072. textBoxFrame.Size = UDim2_new(1, 0, 0, textBounds.Y + 2)
  1073. else
  1074. textBoxFrame.Size = defaultSize
  1075. end
  1076. end
  1077. end)
  1078. end
  1079.  
  1080. local disconnector = CreateDisconnectSignal()
  1081.  
  1082. local backtrackPosition = 0
  1083. local inputtedText = {}
  1084. local isLastWeak = false
  1085. local function addInputtedText(text, weak)
  1086. -- weak means it gets overwritten by the next text that's inputted
  1087. if isLastWeak then
  1088. table.remove(inputtedText, 1)
  1089. end
  1090. if inputtedText[1] == text then
  1091. isLastWeak = isLastWeak and weak
  1092. return
  1093. end
  1094. isLastWeak = weak
  1095. if not weak then
  1096. for i = #inputtedText, 1, -1 do
  1097. if inputtedText[i] == text then
  1098. table.remove(inputtedText, i)
  1099. end
  1100. end
  1101. end
  1102. table.insert(inputtedText, 1, text)
  1103. end
  1104. local function backtrack(direction)
  1105. backtrackPosition = backtrackPosition + direction
  1106. if backtrackPosition < 1 then
  1107. backtrackPosition = 1
  1108. elseif backtrackPosition > #inputtedText then
  1109. backtrackPosition = #inputtedText
  1110. end
  1111. if inputtedText[backtrackPosition] then
  1112. -- Setting the text doesn't always work, especially after losing focus without pressing enter, then clicking back
  1113. textBox.Text = inputtedText[backtrackPosition]
  1114. end
  1115. end
  1116.  
  1117. local focusLostWithoutEnter = false
  1118.  
  1119. textBox.Focused:connect(function()
  1120. if textBox.Text == DEFAULT_COMMAND_BAR_TEXT then
  1121. textBox.Text = ""
  1122. end
  1123. disconnector:fire()
  1124. backtrackPosition = 0
  1125. disconnector:connect(UserInputService.InputBegan:connect(function(input)
  1126. if input.KeyCode == Enum.KeyCode.Up then
  1127. if backtrackPosition == 0 and not focusLostWithoutEnter then
  1128. -- They typed something, then pressed up. They might want what they typed back, so we store it
  1129. -- after they input the next thing, we know they meant to discard this, which is why it's "weak" (second arg is true)
  1130. addInputtedText(textBox.Text, true)
  1131. backtrackPosition = 1
  1132. end
  1133. backtrack(1)
  1134. elseif input.KeyCode == Enum.KeyCode.Down then
  1135. backtrack(-1)
  1136. end
  1137. end))
  1138. end)
  1139.  
  1140. textBox.FocusLost:connect(function(enterPressed)
  1141. disconnector:fire()
  1142. if enterPressed then
  1143. focusLostWithoutEnter = false
  1144.  
  1145. local text = textBox.Text
  1146. addInputtedText(text, false)
  1147. this.CommandInputted:fire(text)
  1148. textBox.Text = ""
  1149.  
  1150. -- let's not spam the popup keyboard after text is entered
  1151. if not isTenFootInterface then
  1152. textBox:CaptureFocus()
  1153. end
  1154. else
  1155. backtrackPosition = 0
  1156. focusLostWithoutEnter = true
  1157. addInputtedText(textBox.Text, true)
  1158. if textBox.Text == "" then
  1159. textBox.Text = DEFAULT_COMMAND_BAR_TEXT
  1160. end
  1161. end
  1162. end)
  1163.  
  1164. return this
  1165. end
  1166. end
  1167.  
  1168. do
  1169. local padding = 5
  1170. local LabelSize = UDim2_new(1, -padding, 0, 2048)
  1171.  
  1172. local TextColors = Style.MessageColors
  1173. local TextColorUnknown = Color3_new(0.5, 0, 1)
  1174.  
  1175. local function isHidden(message)
  1176. return false
  1177. end
  1178.  
  1179. function Methods.CreateOutput(devConsole, messages, messageFilter)
  1180.  
  1181. -- AKA 'Log'
  1182. local heightChanged = CreateSignal()
  1183. local output = {
  1184. Visible = false;
  1185. Height = 0;
  1186. HeightChanged = heightChanged;
  1187. MessagesDirty = false;
  1188. MessagesDirtyPosition = 1;
  1189. }
  1190.  
  1191. local function setHeight(height)
  1192. height = height + 4
  1193. output.Height = height
  1194. heightChanged:fire(height)
  1195. end
  1196.  
  1197. -- The label container
  1198. local frame = Primitives.FolderFrame(nil, 'Output')
  1199. frame.ClipsDescendants = true
  1200. output.Frame = frame
  1201.  
  1202. local textWrappedEnabled = false
  1203.  
  1204. do
  1205. local lastX = 0
  1206. connectPropertyChanged(frame, 'AbsoluteSize', function(size)
  1207. local currentX = size.X
  1208. --currentY = currentY - currentY
  1209. if currentX ~= lastX then
  1210. lastX = currentX
  1211. output:RefreshMessages()
  1212. end
  1213. end)
  1214. end
  1215.  
  1216. local labels = {}
  1217. local labelPositions = {}
  1218.  
  1219. local function RefreshTextWrapped()
  1220. if not output.Visible then
  1221. return
  1222. end
  1223. local y = 1
  1224. for i = 1, #labels do
  1225. local label = labels[i]
  1226. label.TextWrapped = textWrappedEnabled
  1227. local height = label.TextBounds.Y
  1228. label.Size = LabelSize -- UDim2_new(1, 0, 0, height)
  1229. label.Position = UDim2_new(0, padding, 0, y)
  1230. y = y + height
  1231. if height > 16 then
  1232. y = y + 4
  1233. end
  1234. end
  1235. setHeight(y)
  1236. end
  1237. local MAX_LINES = 2048
  1238.  
  1239. local function RefreshMessagesForReal(messageStartPosition)
  1240. if not output.Visible then
  1241. return
  1242. end
  1243.  
  1244. local y = 1
  1245. local labelPosition = 0 -- position of last used label
  1246.  
  1247. -- Failed optimization:
  1248. messageStartPosition = nil
  1249. if messageStartPosition then
  1250. local labelPositionLast;
  1251. for i = messageStartPosition, math_max(1, #messages - MAX_LINES), -1 do
  1252. if labelPositions[i] then
  1253. labelPositionLast = labelPositions[i]
  1254. break
  1255. end
  1256. end
  1257. if labels[labelPositionLast] then
  1258. labelPosition = labelPositionLast
  1259. local label = labels[labelPositionLast]
  1260. y = label.Position.Y.Offset + label.Size.Y.Offset
  1261. else
  1262. messageStartPosition = nil
  1263. end
  1264. end
  1265.  
  1266. for i = messageStartPosition or math_max(1, #messages - MAX_LINES), #messages do
  1267. local message = messages[i]
  1268. if messageFilter(message) then
  1269. labelPosition = labelPosition + 1
  1270. labelPositions[i] = labelPosition
  1271. local label = labels[labelPosition]
  1272. if not label then
  1273. label = Instance_new('TextLabel', frame)
  1274. label.ZIndex = Style.ZINDEX
  1275. label.BackgroundTransparency = 1
  1276. --label.Font = Style.Font
  1277. label.FontSize = 'Size10'
  1278. label.TextXAlignment = 'Left'
  1279. label.TextYAlignment = 'Top'
  1280. labels[labelPosition] = label
  1281. end
  1282. label.TextWrapped = textWrappedEnabled
  1283. label.Size = LabelSize
  1284. label.TextColor3 = TextColors[message.Type] or TextColorUnknown
  1285. label.Text = message.Time .. " -- " .. message.Message
  1286.  
  1287. local height = label.TextBounds.Y
  1288. label.Size = LabelSize -- UDim2_new(1, -padding, 0, height)
  1289. label.Position = UDim2_new(0, padding, 0, y)
  1290.  
  1291. y = y + height
  1292.  
  1293. if height > 16 then
  1294. y = y + 4
  1295. end
  1296. else
  1297. labelPositions[i] = false
  1298. end
  1299. end
  1300.  
  1301. -- Destroy extra labels
  1302. for i = #labels, labelPosition + 1, -1 do
  1303. labels[i]:Destroy()
  1304. labels[i] = nil
  1305. end
  1306.  
  1307. setHeight(y)
  1308. end
  1309.  
  1310. function output.SetMessagesDirty(output, messageStartPosition)
  1311. if output.MessagesDirty then
  1312. return
  1313. end
  1314. output.MessagesDirty = true
  1315. output.MessagesDirtyPosition = messageStartPosition
  1316. end
  1317.  
  1318. local refreshHandle;
  1319. function output.RefreshMessages(output, messageStartPosition)
  1320. if not output.Visible then
  1321. return
  1322. end
  1323. if not refreshHandle then
  1324. refreshHandle = true
  1325. coroutine.wrap(function() -- Not ideal
  1326. wait()
  1327. refreshHandle = false
  1328. RefreshMessagesForReal()
  1329. end)()
  1330. end
  1331. end
  1332.  
  1333. function output.SetTextWrappedEnabled(output, textWrappedEnabledNew)
  1334. if textWrappedEnabledNew == textWrappedEnabled then
  1335. return
  1336. end
  1337. textWrappedEnabled = textWrappedEnabledNew
  1338. RefreshTextWrapped()
  1339. end
  1340.  
  1341. function output.SetVisible(output, visible)
  1342. if visible == output.Visible then
  1343. return
  1344. end
  1345. output.Visible = visible
  1346. if visible then
  1347. RefreshMessagesForReal()
  1348. else
  1349. for i = #labels, 1, -1 do
  1350. labels[i]:Destroy()
  1351. labels[i] = nil
  1352. end
  1353. end
  1354. end
  1355.  
  1356. return output
  1357. end
  1358. end
  1359.  
  1360. ----------
  1361. -- Tabs --
  1362. ----------
  1363. function Methods.GetCurrentOpenTab(devConsole)
  1364. local tabs = devConsole.Tabs
  1365. if tabs == nil then
  1366. return nil
  1367. end
  1368.  
  1369. for i = 1, #tabs do
  1370. local tab = tabs[i]
  1371. if tab.Open then
  1372. return tab
  1373. end
  1374. end
  1375. return nil
  1376. end
  1377.  
  1378.  
  1379. function Methods.RefreshTabs(devConsole)
  1380. -- Go through and reposition them
  1381. local x = Style.BorderSize
  1382. local tabs = devConsole.Tabs
  1383. for i = 1, #tabs do
  1384. local tab = tabs[i]
  1385. if tab.ButtonFrame.Visible then
  1386. x = x + 3
  1387. tab.ButtonFrame.Position = UDim2_new(0, x, 0, 0)
  1388. x = x + tab.ButtonFrame.AbsoluteSize.X + 3
  1389. end
  1390. end
  1391. end
  1392.  
  1393. function Methods.AddTab(devConsole, text, body, openCallback, visibleCallback)
  1394. -- Body is a frame that contains the tab contents
  1395. body.Visible = false
  1396.  
  1397. local tab = {
  1398. Open = false; -- If the tab is open
  1399. Visible = false; -- If the tab is shown
  1400. OpenCallback = openCallback;
  1401. VisibleCallback = visibleCallback;
  1402. Body = body;
  1403. }
  1404.  
  1405. local nominalSize = TextService:GetTextSize(text, TAB_TEXT_SIZE, Enum.Font.SourceSans, Vector2.new(1e3, 1e3))
  1406. local width = nominalSize.x + (TAB_TEXT_PADDING * 2)
  1407.  
  1408. local buttonFrame = Primitives.InvisibleButton(devConsole.Frame.Interior.Tabs, 'Tab_' .. text)
  1409. tab.ButtonFrame = buttonFrame
  1410. buttonFrame.Size = UDim2_new(0, width, 0, Style.TabHeight)
  1411. buttonFrame.Visible = false
  1412.  
  1413. local textLabel = Primitives.TextLabel(buttonFrame, 'Label', text)
  1414. textLabel.TextSize = TAB_TEXT_SIZE
  1415. --textLabel.TextYAlignment = Enum.TextYAlignment.Top
  1416.  
  1417. devConsole:ConnectButtonHover(buttonFrame, devConsole:CreateButtonEffectFunction(textLabel))
  1418.  
  1419. -- These are the dimensions when the tab is closed
  1420. local size0 = UDim2_new(1, 0, 1, -7)
  1421. local position0 = UDim2_new(0, 0, 0, 4)
  1422. -- There are the dimensions when the tab is open
  1423. local size1 = UDim2_new(1, 0, 1, -4)
  1424. local position1 = UDim2_new(0, 0, 0, 4)
  1425. -- It starts closed
  1426. textLabel.Size = size0
  1427. textLabel.Position = position0
  1428.  
  1429. function tab.SetVisible(tab, visible)
  1430. if visible == tab.Visible then
  1431. return
  1432. end
  1433. tab.Visible = visible
  1434. tab:SetOpen(false)
  1435. if tab.VisibleCallback then
  1436. tab.VisibleCallback(visible)
  1437. end
  1438. buttonFrame.Visible = visible
  1439. devConsole:RefreshTabs()
  1440. if not visible then
  1441. tab.SetOpen(false)
  1442. end
  1443. end
  1444.  
  1445. function tab.RecordInitialOpen(tab)
  1446. end
  1447.  
  1448. function tab.RecordClickToOpen(tab)
  1449. end
  1450.  
  1451. function tab.SetOpen(tab, open)
  1452. if open == tab.Open then
  1453. return
  1454. end
  1455. tab.Open = open
  1456.  
  1457. if open then
  1458. if tab.SavedScrollbarValue then
  1459. devConsole.WindowScrollbar:SetValue(tab.SavedScrollbarValue) -- This doesn't load correctly?
  1460. end
  1461. local tabs = devConsole.Tabs
  1462. for i = 1, #tabs do
  1463. if tabs[i] ~= tab then
  1464. tabs[i]:SetOpen(false)
  1465. end
  1466. end
  1467. if body then
  1468. body.Visible = true
  1469. end
  1470. devConsole:RefreshTabs()
  1471. -- Set dimensions for folder effect
  1472. textLabel.Size = size1
  1473. textLabel.Position = position1
  1474. devConsole.CurrentOpenedTab = buttonFrame
  1475. else
  1476. tab.SavedScrollbarValue = devConsole.WindowScrollbar:GetValue() -- This doesn't save correctly
  1477.  
  1478. if body then
  1479. body.Visible = false
  1480. -- todo: (not essential) these 2 lines should instead exist during open (above block) after going through tabs
  1481. devConsole.Frame.Interior.WindowContainer.Window.Body.Size = UDim2_new(1, 0, 1, 0)
  1482. devConsole.Frame.Interior.WindowContainer.Window.Body.Position = UDim2_new(0, 0, 0, 0)
  1483. end
  1484.  
  1485. -- Set dimensions for folder effect
  1486. textLabel.Size = size0
  1487. textLabel.Position = position0
  1488. end
  1489.  
  1490. if tab.OpenCallback then
  1491. tab.OpenCallback(open)
  1492. end
  1493.  
  1494. end
  1495.  
  1496. buttonFrame.MouseButton1Click:connect(function()
  1497. if tab.Visible then
  1498. tab:RecordClickToOpen()
  1499. tab:SetOpen(true)
  1500. end
  1501. end)
  1502.  
  1503. table.insert(devConsole.Tabs, tab)
  1504.  
  1505. return tab
  1506.  
  1507. end
  1508.  
  1509. ----------------
  1510. -- Scroll bar --
  1511. ----------------
  1512. function Methods.ApplyScrollbarToFrame(devConsole,
  1513. scrollbar,
  1514. window,
  1515. body,
  1516. frame)
  1517. local windowHeight, bodyHeight
  1518. local height = scrollbar:GetHeight()
  1519. local value = scrollbar:GetValue()
  1520. local function getHeights()
  1521. return window.AbsoluteSize.Y, body.AbsoluteSize.Y
  1522. end
  1523. local function refreshDimension()
  1524. local windowHeightNew, bodyHeightNew = getHeights()
  1525.  
  1526. if bodyHeight ~= bodyHeightNew or windowHeight ~= windowHeightNew then
  1527. bodyHeight, windowHeight = bodyHeightNew, windowHeightNew
  1528. height = windowHeight / bodyHeight
  1529. scrollbar:SetHeight(height)
  1530.  
  1531. local yOffset = (bodyHeight - windowHeight) * value
  1532. -- Never let yOffset go negative.
  1533. -- Without this line, things that are smaller than the containing scroll
  1534. -- window start at the bottom and grow up.
  1535. -- It's a better UX to have things start at top and grow down.
  1536. if (yOffset < 0) then
  1537. yOffset = 0
  1538. end
  1539.  
  1540. local x = body.Position.X
  1541. local y = body.Position.Y
  1542.  
  1543. body.Position = UDim2_new(x.Scale, x.Offset, y.Scale, -math.floor(yOffset))
  1544. end
  1545.  
  1546. end
  1547.  
  1548. local function setValue(valueNew)
  1549. value = valueNew
  1550. refreshDimension()
  1551. local yOffset = (bodyHeight - windowHeight) * value
  1552. local x = body.Position.X
  1553. local y = body.Position.Y
  1554. body.Position = UDim2_new(x.Scale, x.Offset, y.Scale, -math.floor(yOffset))
  1555. end
  1556. scrollbar.ValueChanged:connect(setValue)
  1557. setValue(scrollbar:GetValue())
  1558.  
  1559. local scrollDistance = 120
  1560.  
  1561. window.Active = true
  1562.  
  1563. scrollbar.ButtonUp.MouseButton1Click:connect(function()
  1564. scrollbar:Scroll(-scrollDistance, getHeights())
  1565. end)
  1566. scrollbar.ButtonDown.MouseButton1Click:connect(function()
  1567. scrollbar:Scroll(scrollDistance, getHeights())
  1568. end)
  1569.  
  1570. connectPropertyChanged(window, 'AbsoluteSize', refreshDimension)
  1571. connectPropertyChanged(body, 'AbsoluteSize', function()
  1572. local windowHeight, bodyHeight = getHeights()
  1573. local value = scrollbar:GetValue()
  1574. if value ~= 1 and value ~= 0 then
  1575. local value = -body.Position.Y.Offset / (bodyHeight - windowHeight)
  1576. scrollbar:SetValue(value)
  1577. end
  1578. refreshDimension()
  1579. end)
  1580.  
  1581. window.MouseWheelForward:connect(function()
  1582. scrollbar:Scroll(-scrollDistance, getHeights())
  1583. end)
  1584. window.MouseWheelBackward:connect(function()
  1585. scrollbar:Scroll(scrollDistance, getHeights())
  1586. end)
  1587. window.TouchPan:connect(function(positions, delta, velocity, userInputState)
  1588. scrollbar:Scroll(-delta.y, getHeights())
  1589. end)
  1590. end
  1591.  
  1592. function Methods.CreateScrollbar(devConsole, rotation)
  1593. local scrollbar = {}
  1594.  
  1595. local main = nil
  1596. main = Primitives.FolderFrame(main, 'Scrollbar')
  1597. scrollbar.Frame = main
  1598.  
  1599. local frame = Primitives.Button(main, 'Frame')
  1600. frame.AutoButtonColor = false
  1601. frame.Size = UDim2_new(1, 0, 1, -(Style.HandleHeight) * 2 - 2)
  1602. frame.Position = UDim2_new(0, 0, 0, Style.HandleHeight + 1)
  1603. -- frame.BackgroundTransparency = 0.75
  1604.  
  1605. -- This replaces the scrollbar when it's not being used
  1606. local frame2 = Primitives.Frame(main, 'Frame')
  1607. frame2.Size = UDim2_new(1, 0, 1, 0)
  1608. frame2.Position = UDim2_new(0, 0, 0, 0)
  1609.  
  1610. function scrollbar.SetVisible(scrollbar, visible)
  1611. frame.Visible = visible
  1612. frame2.Visible = not visible
  1613. end
  1614.  
  1615. local buttonUp = Primitives.ImageButton(frame, 'Up', 'https://www.roblox.com/asset/?id=261880783')
  1616. scrollbar.ButtonUp = buttonUp
  1617. buttonUp.Size = UDim2_new(1, 0, 0, Style.HandleHeight)
  1618. buttonUp.Position = UDim2_new(0, 0, 0, -Style.HandleHeight - 1)
  1619. buttonUp.AutoButtonColor = false
  1620. devConsole:ConnectButtonHover(buttonUp, devConsole:CreateButtonEffectFunction(buttonUp))
  1621.  
  1622. local buttonDown = Primitives.ImageButton(frame, 'Down', 'https://www.roblox.com/asset/?id=261880783')
  1623. scrollbar.ButtonDown = buttonDown
  1624. buttonDown.Size = UDim2_new(1, 0, 0, Style.HandleHeight)
  1625. buttonDown.Position = UDim2_new(0, 0, 1, 1)
  1626. buttonDown.Rotation = 180
  1627. buttonDown.AutoButtonColor = false
  1628. devConsole:ConnectButtonHover(buttonDown, devConsole:CreateButtonEffectFunction(buttonDown))
  1629.  
  1630. local bar = Primitives.Button(frame, 'Bar')
  1631. bar.Size = UDim2_new(1, 0, 0.5, 0)
  1632. bar.Position = UDim2_new(0, 0, 0.25, 0)
  1633.  
  1634. bar.AutoButtonColor = false
  1635.  
  1636. local grip = Primitives.InvisibleImageLabel(bar, 'Image', 'https://www.roblox.com/asset/?id=261904959')
  1637. grip.Size = UDim2_new(0, 16, 0, 16)
  1638. grip.Position = UDim2_new(0.5, -8, 0.5, -8)
  1639.  
  1640. local buttonEffectFunction = devConsole:CreateButtonEffectFunction(bar, nil, bar.BackgroundColor3, bar.BackgroundColor3)
  1641.  
  1642. -- Inertial scrolling would be added around here
  1643.  
  1644. local value = 1
  1645. local valueChanged = CreateSignal()
  1646. scrollbar.ValueChanged = valueChanged
  1647. -- value = 0: at very top
  1648. -- value = 1: at very bottom
  1649.  
  1650. local height = 0.25
  1651. local heightChanged = CreateSignal()
  1652. scrollbar.HeightChanged = heightChanged
  1653. -- height = 0: infinite page size
  1654. -- height = 1: bar fills frame completely, no need to scroll
  1655.  
  1656. local function getValueAtPosition(pos)
  1657. return ((pos - main.AbsolutePosition.Y) / main.AbsoluteSize.Y) / (1 - height)
  1658. end
  1659.  
  1660. -- Refreshes the position and size of the scrollbar
  1661. local function refresh()
  1662. local y = height
  1663. bar.Size = UDim2_new(1, 0, y, 0)
  1664. bar.Position = UDim2_new(0, 0, value * (1 - y), 0)
  1665. end
  1666. refresh()
  1667.  
  1668. function scrollbar.SetValue(scrollbar, valueNew)
  1669. if valueNew < 0 then
  1670. valueNew = 0
  1671. elseif valueNew > 1 then
  1672. valueNew = 1
  1673. end
  1674. if valueNew ~= value then
  1675. value = valueNew
  1676. refresh()
  1677. valueChanged:fire(valueNew)
  1678. end
  1679. end
  1680. function scrollbar.GetValue(scrollbar)
  1681. return value
  1682. end
  1683.  
  1684. function scrollbar.Scroll(scrollbar, direction, windowHeight, bodyHeight)
  1685. scrollbar:SetValue(value + direction / bodyHeight) -- needs to be adjusted
  1686. end
  1687.  
  1688. function scrollbar.SetHeight(scrollbar, heightNew)
  1689. if heightNew < 0 then
  1690. heightNew = 0 -- this is still an awkward case of divide-by-zero that shouldn't happen
  1691. elseif heightNew > 1 then
  1692. heightNew = 1
  1693. end
  1694. heightNew = math.max(heightNew, 0.1) -- Minimum scroll bar size, from that point on it is not the actual ratio
  1695. if heightNew ~= height then
  1696. height = heightNew
  1697. scrollbar:SetVisible(heightNew < 1)
  1698. refresh()
  1699. heightChanged:fire(heightNew)
  1700. end
  1701. end
  1702. function scrollbar.GetHeight(scrollbar)
  1703. return height
  1704. end
  1705.  
  1706. devConsole:ConnectButtonDragging(bar, function()
  1707. local value0 = value -- starting value
  1708. return function(dx, dy)
  1709. local dposition = dy -- net position change relative to the bar's axis (could support rotated scroll bars)
  1710. local dvalue = (dposition / frame.AbsoluteSize.Y) / (1 - height) -- net value change
  1711. scrollbar:SetValue(value0 + dvalue)
  1712. end
  1713. end, buttonEffectFunction)
  1714.  
  1715. return scrollbar
  1716. end
  1717.  
  1718. ----------------------
  1719. -- Fancy color lerp --
  1720. ----------------------
  1721. local RenderLerpAnimation; do
  1722. local math_cos = math.cos
  1723. local math_pi = math.pi
  1724. function RenderLerpAnimation(disconnectSignal, length, callback)
  1725. disconnectSignal:fire()
  1726. local timeStamp = tick()
  1727. local listener = RunService.RenderStepped:connect(function()
  1728. local t = (tick() - timeStamp) / length
  1729. if t >= 1 then
  1730. t = 1
  1731. disconnectSignal:fire()
  1732. else
  1733. t = (1 - math_cos(t * math_pi)) / 2 -- cosine interpolation aka 'Sine' in :TweenSizeAndPosition
  1734. end
  1735. callback(t)
  1736. end)
  1737. disconnectSignal:connect(listener)
  1738. return listener
  1739. end
  1740. end
  1741.  
  1742. if EYECANDY_ENABLED then
  1743. -- This is the pretty version
  1744. function Methods.CreateButtonEffectFunction(devConsole, button, normalColor, clickingColor, hoveringColor)
  1745. normalColor = normalColor or button.BackgroundColor3
  1746. clickingColor = clickingColor or Style.GetButtonDownColor(normalColor)
  1747. hoveringColor = hoveringColor or Style.GetButtonHoverColor(normalColor)
  1748. local disconnectSignal = CreateDisconnectSignal()
  1749. return function(clicking, hovering)
  1750. local color0 = button.BackgroundColor3
  1751. local color1 = clicking and clickingColor or (hovering and hoveringColor or normalColor)
  1752. local r0, g0, b0 = color0.r, color0.g, color0.b
  1753. local r1, g1, b1 = color1.r, color1.g, color1.b
  1754. local r2, g2, b2 = r1 - r0, g1 - g0, b1 - b0
  1755. RenderLerpAnimation(disconnectSignal, clicking and 0.125 or 0.25, function(t)
  1756. button.BackgroundColor3 = Color3_new(r0 + r2 * t, g0 + g2 * t, b0 + b2 * t)
  1757. end)
  1758. end
  1759. end
  1760. else
  1761. -- This is the simple version
  1762. function Methods.CreateButtonEffectFunction(devConsole, button, normalColor, clickingColor, hoveringColor)
  1763. normalColor = normalColor or button.BackgroundColor3
  1764. clickingColor = clickingColor or Style.GetButtonDownColor(normalColor)
  1765. hoveringColor = hoveringColor or Style.GetButtonHoverColor(normalColor)
  1766. return function(clicking, hovering)
  1767. button.BackgroundColor3 = clicking and clickingColor or (hovering and hoveringColor or normalColor)
  1768. end
  1769. end
  1770. end
  1771.  
  1772. function Methods.GenerateOptionButtonAnimationToggle(devConsole, interior, button, gear, tabContainer, optionsClippingFrame, optionsFrame)
  1773.  
  1774. local tabContainerSize0 = tabContainer.Size
  1775. local tabContainerSize1 = UDim2_new(
  1776. tabContainerSize0.X.Scale, tabContainerSize0.X.Offset + (Style.GearSize + 2) + Style.BorderSize,
  1777. tabContainerSize0.Y.Scale, tabContainerSize0.Y.Offset)
  1778.  
  1779. local gearRotation0 = gear.Rotation
  1780. local gearRotation1 = gear.Rotation - 90
  1781. local interiorSize0 = interior.Size
  1782. local interiorSize1 = UDim2_new(interiorSize0.X.Scale, interiorSize0.X.Offset, interiorSize0.Y.Scale, interiorSize0.Y.Offset - Style.OptionAreaHeight)
  1783. local interiorPosition0 = interior.Position
  1784. local interiorPosition1 = UDim2_new(interiorPosition0.X.Scale, interiorPosition0.X.Offset, interiorPosition0.Y.Scale, interiorPosition0.Y.Offset + Style.OptionAreaHeight)
  1785.  
  1786. local length = 0.5
  1787. local disconnector = CreateDisconnectSignal()
  1788. return function(open)
  1789. if open then
  1790. interior:TweenSizeAndPosition(interiorSize1, interiorPosition1, 'Out', 'Sine', length, true)
  1791. tabContainer:TweenSize(tabContainerSize1, 'Out', 'Sine', length, true)
  1792. optionsClippingFrame:TweenSizeAndPosition(
  1793. UDim2_new(1, 0, 0, Style.OptionAreaHeight),
  1794. UDim2_new(0, 0, 0, -Style.OptionAreaHeight),
  1795. 'Out', 'Sine', length, true
  1796. )
  1797. optionsFrame:TweenPosition(
  1798. UDim2_new(0, 0, 0, 0),-- -Style.OptionAreaHeight),
  1799. 'Out', 'Sine', length, true
  1800. )
  1801. local gearRotation = gear.Rotation
  1802. RenderLerpAnimation(disconnector, length, function(t)
  1803. gear.Rotation = gearRotation1 * t + gearRotation * (1 - t)
  1804. end)
  1805. else
  1806. interior:TweenSizeAndPosition(interiorSize0, interiorPosition0, 'Out', 'Sine', length, true)
  1807. tabContainer:TweenSize(tabContainerSize0, 'Out', 'Sine', length, true)
  1808. optionsClippingFrame:TweenSizeAndPosition(
  1809. UDim2_new(1, 0, 0, 0),
  1810. UDim2_new(0, 0, 0, 0),
  1811. 'Out', 'Sine', length, true
  1812. )
  1813. optionsFrame:TweenPosition(
  1814. UDim2_new(0, 0, 0, Style.OptionAreaHeight),
  1815. 'Out', 'Sine', length, true
  1816. )
  1817. local gearRotation = gear.Rotation
  1818. RenderLerpAnimation(disconnector, length, function(t)
  1819. gear.Rotation = gearRotation0 * t + gearRotation * (1 - t)
  1820. end)
  1821. end
  1822. end
  1823. end
  1824.  
  1825. ------------------------------
  1826. -- Events for color effects --
  1827. ------------------------------
  1828. do
  1829. local globalInteractEvent = CreateSignal()
  1830. function Methods.ConnectButtonHover(devConsole, button, mouseInteractCallback)
  1831. -- void mouseInteractCallback(bool clicking, bool hovering)
  1832.  
  1833. local this = {}
  1834.  
  1835. local clicking = false
  1836. local hovering = false
  1837. local function set(clickingNew, hoveringNew)
  1838. if hoveringNew and TouchEnabled then
  1839. hoveringNew = false -- Touch screens don't hover
  1840. end
  1841. if clickingNew ~= clicking or hoveringNew ~= hovering then
  1842. clicking, hovering = clickingNew, hoveringNew
  1843. mouseInteractCallback(clicking, hovering)
  1844. end
  1845. end
  1846.  
  1847. button.MouseButton1Down:connect(function()
  1848. set(true, true)
  1849. end)
  1850. button.MouseButton1Up:connect(function()
  1851. set(false, true)
  1852. end)
  1853. button.MouseEnter:connect(function()
  1854. set(clicking, true)
  1855. end)
  1856. button.MouseLeave:connect(function()
  1857. set(false, false)
  1858. end)
  1859. --[[ these might cause memory leakes (when creating temporary buttons)
  1860. -- This solves the case in which the user presses F9 while hovering over a button
  1861. devConsole.VisibleChanged:connect(function()
  1862. set(false, false)
  1863. end)
  1864.  
  1865. globalInteractEvent:connect(function()
  1866. set(false, false)
  1867. end)
  1868. --]]
  1869. end
  1870. end
  1871.  
  1872. -------------------------
  1873. -- Events for draggers -- (for the window's top handle, the resize button, and scrollbars)
  1874. -------------------------
  1875. function Methods.ConnectButtonDragging(devConsole, button, dragCallback, mouseInteractCallback)
  1876.  
  1877. -- How dragCallback is called: local deltaCallback = dragCallback(xPositionAtMouseDown, yPositionAtMouseDown)
  1878. -- How deltaCallback is called: deltaCallback(netChangeInAbsoluteXPositionSinceMouseDown, netChangeInAbsoluteYPositionSinceMouseDown)
  1879.  
  1880. local dragging = false -- AKA 'clicking'
  1881. local hovering = false
  1882.  
  1883. local listeners = {}
  1884.  
  1885. local disconnectCallback;
  1886.  
  1887. local function stopDragging()
  1888. if not dragging then
  1889. return
  1890. end
  1891. dragging = false
  1892. mouseInteractCallback(dragging, hovering)
  1893. for i = #listeners, 1, -1 do
  1894. listeners[i]:disconnect()
  1895. listeners[i] = nil
  1896. end
  1897. end
  1898.  
  1899. local ButtonUserInputTypes = {
  1900. [Enum.UserInputType.MouseButton1] = true;
  1901. [Enum.UserInputType.Touch] = true; -- I'm not sure if touch actually works here
  1902. }
  1903.  
  1904. local mouse = game:GetService("Players").LocalPlayer:GetMouse()
  1905.  
  1906. local function startDragging(startP)
  1907. if dragging then
  1908. return
  1909. end
  1910. dragging = true
  1911.  
  1912. mouseInteractCallback(dragging, hovering)
  1913. local deltaCallback;
  1914.  
  1915. local x0, y0 = startP.X, startP.Y
  1916. --[[
  1917. listeners[#listeners + 1] = UserInputService.InputBegan:connect(function(input)
  1918. if ButtonUserInputTypes[input.UserInputType] then
  1919. local position = input.Position
  1920. if position and not x0 then
  1921. x0, y0 = position.X, position.Y -- The same click
  1922. end
  1923. end
  1924. end)
  1925. --]]
  1926. listeners[#listeners + 1] = UserInputService.InputEnded:connect(function(input)
  1927. if ButtonUserInputTypes[input.UserInputType] then
  1928. stopDragging()
  1929. end
  1930. end)
  1931. listeners[#listeners + 1] = UserInputService.InputChanged:connect(function(input)
  1932.  
  1933. if not (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch)then -- added in a touch check
  1934. return
  1935. end
  1936.  
  1937. local p1 = input.Position
  1938.  
  1939. if not p1 then
  1940. return
  1941. end
  1942. local x1, y1 = p1.X, p1.Y
  1943. if not deltaCallback then
  1944. deltaCallback, disconnectCallback = dragCallback(x0 or x1, y0 or y1)
  1945. end
  1946. if x0 then
  1947. deltaCallback(x1 - x0, y1 - y0)
  1948. end
  1949. end)
  1950. end
  1951.  
  1952. --button.MouseButton1Down:connect(startDragging)
  1953. --button.MouseButton1Up:connect(stopDragging)
  1954.  
  1955. button.InputBegan:connect(function(iobj)
  1956. if iobj.UserInputType == Enum.UserInputType.Touch or iobj.UserInputType == Enum.UserInputType.MouseButton1 then
  1957. startDragging(iobj.Position)
  1958. end
  1959. end)
  1960.  
  1961. button.InputEnded:connect(function(iobj)
  1962. if iobj.UserInputType == Enum.UserInputType.Touch or iobj.UserInputType == Enum.UserInputType.MouseButton1 then
  1963. stopDragging()
  1964. end
  1965. end)
  1966.  
  1967. button.MouseEnter:connect(function()
  1968. if not hovering then
  1969. hovering = true
  1970. mouseInteractCallback(dragging, hovering)
  1971. end
  1972. end)
  1973. button.MouseLeave:connect(function()
  1974. if hovering then
  1975. hovering = false
  1976. mouseInteractCallback(dragging, hovering)
  1977. end
  1978. end)
  1979.  
  1980. devConsole.VisibleChanged:connect(stopDragging)
  1981. end
  1982.  
  1983. -----------------
  1984. -- Permissions --
  1985. -----------------
  1986. do
  1987. local permissionsLoading, permissions = false;
  1988. function DeveloperConsole.GetPermissions()
  1989. while permissionsLoading do wait() end
  1990.  
  1991. if permissions then
  1992. return permissions
  1993. end
  1994.  
  1995. permissions = {}
  1996. permissionsLoading = true
  1997. permissions.IsCreator = false
  1998.  
  1999. local success, result = pcall(function()
  2000. local url = string.format("/users/%d/canmanage/%d", game:GetService("Players").LocalPlayer.UserId, game.PlaceId)
  2001. return game:GetService('HttpRbxApiService'):GetAsync(url, Enum.ThrottlingPriority.Default, Enum.HttpRequestType.Default, true)
  2002. end)
  2003. if success and type(result) == "string" then
  2004. -- API returns: {"Success":BOOLEAN,"CanManage":BOOLEAN}
  2005. -- Convert from JSON to a table
  2006. -- pcall in case of invalid JSON
  2007. success, result = pcall(function()
  2008. return game:GetService('HttpService'):JSONDecode(result)
  2009. end)
  2010. if success and result.CanManage == true then
  2011. permissions.IsCreator = result.CanManage
  2012. end
  2013. end
  2014.  
  2015. permissions.ClientCodeExecutionEnabled = false
  2016. pcall(function()
  2017. permissions.ServerCodeExecutionEnabled = permissions.IsCreator and (not settings():GetFFlag("DebugDisableLogServiceExecuteScript"))
  2018. end)
  2019.  
  2020. if DEBUG or (RunService:IsStudio()) then
  2021. permissions.IsCreator = true
  2022. permissions.ServerCodeExecutionEnabled = true
  2023. end
  2024.  
  2025. permissions.MayViewServerLog = permissions.IsCreator
  2026. permissions.MayViewClientLog = true
  2027.  
  2028. permissions.MayViewServerStats = permissions.IsCreator
  2029. permissions.MayViewServerMemory = permissions.IsCreator
  2030. permissions.MayViewServerScripts = permissions.IsCreator
  2031. permissions.MayViewServerJobs = permissions.IsCreator
  2032.  
  2033. permissions.MayViewDataStoreBudget = false
  2034. pcall(function()
  2035. permissions.MayViewDataStoreBudget = permissions.IsCreator
  2036. end)
  2037. permissions.MayViewHttpResultClient = false
  2038. permissions.MayViewHttpResultClient = permissions.IsCreator
  2039. permissions.MayViewHttpResultServer = false
  2040. permissions.MayViewHttpResultServer = permissions.IsCreator
  2041.  
  2042. permissions.MayViewContextActionBindings = permissions.IsCreator
  2043.  
  2044. permissionsLoading = false
  2045.  
  2046. return permissions
  2047. end
  2048. end
  2049.  
  2050. ----------------------
  2051. -- Output interface --
  2052. ----------------------
  2053. do
  2054. local messagesAndStats;
  2055. function DeveloperConsole.GetMessagesAndStats(permissions)
  2056.  
  2057. if messagesAndStats then
  2058. return messagesAndStats
  2059. end
  2060.  
  2061. local function NewOutputMessageSync(getMessages)
  2062. local this;
  2063. this = {
  2064. Messages = nil; -- Private member, DeveloperConsole should use :GetMessages()
  2065. MessageAdded = CreateSignal();
  2066. GetMessages = function()
  2067. local messages = this.Messages
  2068. if not messages then
  2069. -- If it errors while getting messages, it skip it next time
  2070. if this.Attempted then
  2071. messages = {}
  2072. else
  2073. this.Attempted = true
  2074. messages = getMessages(this)
  2075. this.Messages = messages
  2076. end
  2077.  
  2078. end
  2079. return messages
  2080. end;
  2081. }
  2082. return this
  2083. end
  2084.  
  2085. local ConvertTimeStamp; do
  2086. -- Easy, fast, and working nicely
  2087. local function numberWithZero(num)
  2088. return (num < 10 and "0" or "") .. num
  2089. end
  2090. local string_format = string.format -- optimization
  2091. function ConvertTimeStamp(timeStamp)
  2092. local localTime = timeStamp - os_time() + math.floor(tick())
  2093. local dayTime = localTime % 86400
  2094.  
  2095. local hour = math.floor(dayTime/3600)
  2096.  
  2097. dayTime = dayTime - (hour * 3600)
  2098. local minute = math.floor(dayTime/60)
  2099.  
  2100. dayTime = dayTime - (minute * 60)
  2101. local second = dayTime
  2102.  
  2103. local h = numberWithZero(hour)
  2104. local m = numberWithZero(minute)
  2105. local s = numberWithZero(dayTime)
  2106.  
  2107. return string_format("%s:%s:%s", h, m, s)
  2108. end
  2109. end
  2110.  
  2111. local warningsToFilter = {"ClassDescriptor failed to learn", "EventDescriptor failed to learn", "Type failed to learn"}
  2112.  
  2113. -- Filter "ClassDescriptor failed to learn" errors
  2114. local function filterMessageOnAdd(message)
  2115. if message.Type ~= Enum.MessageType.MessageWarning.Value then
  2116. return false
  2117. end
  2118. local found = false
  2119. for _, filterString in ipairs(warningsToFilter) do
  2120. if string.find(message.Message, filterString) ~= nil then
  2121. found = true
  2122. break
  2123. end
  2124. end
  2125. return found
  2126. end
  2127.  
  2128. local outputMessageSyncLocal;
  2129. if permissions.MayViewClientLog then
  2130. outputMessageSyncLocal = NewOutputMessageSync(function(this)
  2131. local messages = {}
  2132.  
  2133. local LogService = game:GetService("LogService")
  2134. do -- This do block keeps history from sticking around in memory
  2135. local history = LogService:GetLogHistory()
  2136. for i = 1, #history do
  2137. local msg = history[i]
  2138. local message = {
  2139. Message = msg.message or "[DevConsole Error 1]";
  2140. Time = ConvertTimeStamp(msg.timestamp);
  2141. Type = msg.messageType.Value;
  2142. }
  2143. if not filterMessageOnAdd(message) then
  2144. messages[#messages + 1] = message
  2145. end
  2146. end
  2147. end
  2148.  
  2149. LogService.MessageOut:connect(function(text, messageType)
  2150. local message = {
  2151. Message = text or "[DevConsole Error 2]";
  2152. Time = ConvertTimeStamp(os_time());
  2153. Type = messageType.Value;
  2154. }
  2155. if not filterMessageOnAdd(message) then
  2156. messages[#messages + 1] = message
  2157. this.MessageAdded:fire(message)
  2158. end
  2159. end)
  2160.  
  2161. return messages
  2162. end)
  2163. end
  2164.  
  2165. local outputMessageSyncServer;
  2166. if permissions.MayViewServerLog then
  2167. outputMessageSyncServer = NewOutputMessageSync(function(this)
  2168. local messages = {}
  2169.  
  2170. local LogService = game:GetService("LogService")
  2171.  
  2172. LogService.ServerMessageOut:connect(function(text, messageType, timestamp)
  2173. local message = {
  2174. Message = text or "[DevConsole Error 3]";
  2175. Time = ConvertTimeStamp(timestamp);
  2176. Type = messageType.Value;
  2177. }
  2178. if not filterMessageOnAdd(message) then
  2179. messages[#messages + 1] = message
  2180. this.MessageAdded:fire(message)
  2181. end
  2182. end)
  2183. LogService:RequestServerOutput()
  2184.  
  2185. return messages
  2186. end)
  2187. end
  2188.  
  2189. local statsSyncServer;
  2190. if (permissions.MayViewServerStats or
  2191. permissions.MayViewServerScripts or
  2192. permissions.MayViewServerMemory) then
  2193.  
  2194. statsSyncServer = {
  2195. Stats = nil; -- Private member, use GetStats instead
  2196. StatsReceived = CreateSignal();
  2197. }
  2198. local statsListenerConnection;
  2199. function statsSyncServer.GetStats(statsSyncServer)
  2200. local stats = statsSyncServer.Stats
  2201. if not stats then
  2202. stats = {}
  2203. pcall(function()
  2204. local clientReplicator = game:FindService("NetworkClient"):GetChildren()[1]
  2205. if clientReplicator then
  2206. statsListenerConnection = clientReplicator.StatsReceived:connect(function(stat)
  2207. statsSyncServer.StatsReceived:fire(stat)
  2208. end)
  2209. clientReplicator:RequestServerStats(true)
  2210. end
  2211. end)
  2212. statsSyncServer.Stats = stats
  2213. end
  2214. return stats
  2215. end
  2216.  
  2217. end
  2218. --]]
  2219.  
  2220. messagesAndStats = {
  2221. OutputMessageSyncLocal = outputMessageSyncLocal;
  2222. OutputMessageSyncServer = outputMessageSyncServer;
  2223. StatsSyncServer = statsSyncServer;
  2224. }
  2225.  
  2226. return messagesAndStats
  2227. end
  2228. end
  2229.  
  2230. --[[ Module Table ]]--
  2231. -- We only create the dev console if we need it; user toggles visibility.
  2232.  
  2233. local DevConsoleModuleTable = {}
  2234. local myDeveloperConsole = nil
  2235.  
  2236. -- Tenfoot Interface set up
  2237. local function onDevConsoleVisibilityChanged(isVisible)
  2238. local blockMenuActionName = "blockMenuAction"
  2239. local closeDevConsoleActionName = "closeDevConsoleAction"
  2240. local selectionParentName = "devConsoleSelectionGroup"
  2241.  
  2242. local function closeDevConsole(actionName, inputState, inputObject)
  2243. if inputState == Enum.UserInputState.End then
  2244. myDeveloperConsole:SetVisible(false)
  2245. end
  2246. end
  2247.  
  2248. if isVisible then
  2249. -- block menu open input while dev console is open
  2250. ContextActionService:BindCoreAction(blockMenuActionName, function() end, false, Enum.KeyCode.ButtonStart)
  2251.  
  2252. local menuModule = require(Modules.Settings.SettingsHub)
  2253. menuModule:SetVisibility(false, true)
  2254. ContextActionService:BindCoreAction(closeDevConsoleActionName, closeDevConsole, false, Enum.KeyCode.ButtonB)
  2255.  
  2256. GuiService:AddSelectionParent(selectionParentName, myDeveloperConsole.Frame)
  2257. GuiService.SelectedCoreObject = myDeveloperConsole.CurrentOpenedTab
  2258. else
  2259. ContextActionService:UnbindCoreAction(closeDevConsoleActionName)
  2260. ContextActionService:UnbindCoreAction(blockMenuActionName)
  2261.  
  2262. GuiService:RemoveSelectionGroup(selectionParentName)
  2263. GuiService.SelectedCoreObject = nil
  2264. end
  2265. end
  2266.  
  2267. local devConsoleCreating = false
  2268. local function getDeveloperConsole()
  2269. if (not myDeveloperConsole and not devConsoleCreating) then
  2270. devConsoleCreating = true
  2271. local permissions = DeveloperConsole.GetPermissions()
  2272. local messagesAndStats = DeveloperConsole.GetMessagesAndStats(permissions)
  2273.  
  2274. myDeveloperConsole = DeveloperConsole.new(RobloxGui, permissions, messagesAndStats)
  2275.  
  2276. if isTenFootInterface then
  2277. myDeveloperConsole.VisibleChanged:connect(onDevConsoleVisibilityChanged)
  2278. end
  2279. devConsoleCreating = false
  2280. end
  2281. return myDeveloperConsole
  2282. end
  2283.  
  2284. function DevConsoleModuleTable:GetVisibility()
  2285. local devConsole = getDeveloperConsole()
  2286. if devConsole then
  2287. return devConsole.Visible
  2288. else
  2289. return false
  2290. end
  2291. end
  2292.  
  2293. function DevConsoleModuleTable:SetVisibility(value)
  2294. local devConsole = getDeveloperConsole()
  2295. if devConsole then
  2296. devConsole:SetVisible(value)
  2297. end
  2298. end
  2299.  
  2300. local creatingLock = false
  2301. local creatingVisibleValueToSet = false
  2302.  
  2303. local function SetCoreConsoleCreation()
  2304. if (creatingLock) then return end
  2305. creatingLock = true
  2306.  
  2307. spawn(function()
  2308. --// Keep GetVisibility call before SetVisibility because the first call will yield for some time and
  2309. --// there is the possibility that during the yield time the value of 'creatingVisibleValueToSet' may
  2310. --// change.
  2311. DevConsoleModuleTable:GetVisibility()
  2312. DevConsoleModuleTable:SetVisibility(creatingVisibleValueToSet)
  2313.  
  2314. creatingLock = false
  2315. end)
  2316. end
  2317.  
  2318. local StarterGui = game:GetService("StarterGui")
  2319. local function GetDeveloperConsoleVisible()
  2320. if (not myDeveloperConsole) then
  2321. SetCoreConsoleCreation()
  2322. return creatingVisibleValueToSet;
  2323. else
  2324. return DevConsoleModuleTable:GetVisibility()
  2325. end
  2326. end
  2327.  
  2328. local function DeveloperConsoleVisible(visible)
  2329. if (type(visible) ~= "boolean") then
  2330. error("DeveloperConsoleVisible must be given a boolean value.")
  2331. end
  2332.  
  2333. if (not myDeveloperConsole) then
  2334. creatingVisibleValueToSet = visible
  2335. SetCoreConsoleCreation()
  2336. else
  2337. DevConsoleModuleTable:SetVisibility(visible)
  2338. end
  2339. end
  2340.  
  2341. -- BetterConsole.lua by Josh#0903
  2342. local InputService = game:GetService('UserInputService')
  2343. local StarterGui = game:GetService('StarterGui')
  2344.  
  2345. InputService.InputBegan:connect(function(a)
  2346. if a.UserInputType == Enum.UserInputType.Keyboard and a.KeyCode == Enum.KeyCode.F9 then
  2347. local b = GetDeveloperConsoleVisible();
  2348. StarterGui:SetCore('DevConsoleVisible', false)
  2349. DeveloperConsoleVisible(not b)
  2350. end
  2351. end)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement