├── .gitignore
├── LICENSE
├── README.md
└── lua
├── autorun
└── blobsprofiler_autorun.lua
└── blobsprofiler
├── client
├── cl_blobsprofiler.lua
├── js
│ ├── ace1.js.lua
│ ├── ace2.js.lua
│ ├── ace3.js.lua
│ ├── ace4.js.lua
│ ├── ace5.js.lua
│ ├── ace6.js.lua
│ ├── ace7.js.lua
│ ├── ace8.js.lua
│ ├── mode-glua1.js.lua
│ ├── mode-glua2.js.lua
│ ├── mode-glua3.js.lua
│ ├── mode-glua4.js.lua
│ └── mode-sql.js.lua
└── vgui
│ ├── vgui_bpdtree.lua
│ ├── vgui_bpdtree_node.lua
│ └── vgui_bpdtree_node_button.lua
├── server
└── sv_blobsprofiler.lua
└── shared
├── modules
├── bp_concommands.lua
├── bp_errors.lua
├── bp_files.lua
├── bp_hooks.lua
├── bp_lua.lua
├── bp_network.lua
├── bp_profiling.lua
├── bp_sqlite.lua
└── bp_timers.lua
├── sh_blobsprofiler.lua
├── sh_modules.lua
├── sh_netstream.lua
└── sh_pon.lua
/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blobles-dev/blobsProfiler/60903e4caf307a752cbd02a4677e7629091ca265/.gitignore
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 blobles-dev
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 | # blobsProfiler
2 | A powerful task-manager style & profiling addon for garrysmod
3 |
4 | 
5 |
6 | More screenshots can be found [here](https://github.com/blobles-dev/blobsProfiler/wiki/Media "here")
7 |
8 | ## Very early development (lacking a lot of features)
9 | Found an issue/bug or have an idea/suggestion? Use the [Issues](https://github.com/blobles-dev/blobsProfiler/issues "Issues") tab
10 | ### Overview
11 | This addon will be the one-stop shop for your development & test server needs.
12 | Early development access, feel free to contribute.
13 | I suck at repo management, so bare with me.
14 | Open the menu with `blobsprofiler`
15 |
16 | ### :exclamation: WARNING :exclamation:
17 | I would highly recommend against using this on a production/live server, especially in its current state.
18 | For now, the all modules are currently locked away behind only a usergroup == superadmin check (CL for client side area of modules, and SV checks for server side area of modules)
19 |
20 |
21 | ### Features & To-Do
22 | ##### Modules
23 | | Module | Client | Server |
24 | | ------------ | ------------ | ------------ |
25 | | [Globals](https://github.com/blobles-dev/blobsProfiler/wiki/Media#profiling-module-wip) | :white_check_mark: | :white_check_mark: |
26 | | [Lua execute](https://github.com/blobles-dev/blobsProfiler/wiki/Media#execute-lua-submodule) | :white_check_mark: | :white_check_mark: |
27 | | [Hooks](https://github.com/blobles-dev/blobsProfiler/wiki/Media#hooks-module) | :white_check_mark: | :white_check_mark: |
28 | | [ConCommands](https://github.com/blobles-dev/blobsProfiler/wiki/Media#concommands-module) | :white_check_mark: | :white_check_mark: |
29 | | Convar | :x: | :x: |
30 | | [Files](https://github.com/blobles-dev/blobsProfiler/wiki/Media#files-module) | :white_check_mark: | :white_check_mark: |
31 | | [Network (Receivers)](https://github.com/blobles-dev/blobsProfiler/wiki/Media#network-module) | :white_check_mark: | :white_check_mark: |
32 | | [Timers](https://github.com/blobles-dev/blobsProfiler/wiki/Media#timers-module) | :white_check_mark: | :white_check_mark: |
33 | | [Profiling](https://github.com/blobles-dev/blobsProfiler/wiki/Media#profiling-module-wip) | :x: | :x: |
34 | | [SQLite Schema](https://github.com/blobles-dev/blobsProfiler/wiki/Media#schema-sqlite-submodule) | :white_check_mark: | :white_check_mark: |
35 | | [SQLite Data](https://github.com/blobles-dev/blobsProfiler/wiki/Media#data-sqlite-submodule) | :white_check_mark: | :white_check_mark: |
36 | | [SQLite Execute](https://github.com/blobles-dev/blobsProfiler/wiki/Media#execute-sqlite-submodule) | :white_check_mark: | :white_check_mark: |
37 | | [Errors](https://github.com/blobles-dev/blobsProfiler/wiki/Media#errors-module) | :white_check_mark: | :white_check_mark: |
38 | | Remote SQL Schema | :x: | :x: |
39 | | Remote SQL Data | :x: | :x: |
40 | | Remote SQL Execute | :x: | :x: |
41 |
42 | ##### Other
43 | - Settings
44 | - :x: Theme
45 | - :x: Module enable/disable
46 | - :x: Module usergroup permission
47 | - Cleanup
48 | - :wavy_dash: Module-system (Code refactor)
49 |
50 | ### Known issues
51 | - Other addons detouring timer.Create can cause issues with obtaining correct source
52 |
53 | ### Credits
54 | - [Ace Editor](https://ace.c9.io/ "Ace Editor")
55 | - Ingame Lua (and soon SQL) editors
56 | - [Yogpod](https://github.com/Yogpod "Yogpod")
57 | - Posted a DTree script which sparked the whole idea behind this journey
58 | - [Meta Construct](https://github.com/Metastruct "Meta Construct")
59 | - GLua mode for Ace Editor
60 | - [Phoenixf](https://github.com/phoen1xf/ "Phoenixf")
61 | - Being an awesome friend <3
62 |
--------------------------------------------------------------------------------
/lua/autorun/blobsprofiler_autorun.lua:
--------------------------------------------------------------------------------
1 | blobsProfiler = blobsProfiler or {}
2 | blobsProfiler.Modules = blobsProfiler.Modules or {}
3 |
4 | blobsProfiler.Client = blobsProfiler.Client or {}
5 | blobsProfiler.Server = blobsProfiler.Server or {}
6 |
7 | blobsProfiler.Client.Profile = blobsProfiler.Client.Profile or {}
8 | blobsProfiler.Server.Profile = blobsProfiler.Server.Profile or {}
9 |
10 | blobsProfiler.svDataChunkSize = 15000
11 |
12 | local realmDataTable = {}
13 |
14 | if SERVER then
15 | realmDataTable = blobsProfiler.Server
16 | else
17 | realmDataTable = blobsProfiler.Client
18 | end
19 |
20 |
21 | MsgN("- blobsProfiler initializing files -")
22 |
23 | local function incFile(fileData)
24 | local CL = fileData.CL or false
25 | local SV = fileData.SV or false
26 | local filePath = fileData.File or error("blobsProfiler: Failed to load file no .File provided!")
27 |
28 | if SV && SERVER && !CL then
29 | print("[blobsProfiler] SV Load: " .. filePath)
30 | include("blobsprofiler/" .. filePath)
31 | else
32 | if SERVER then
33 | print("[blobsProfiler] " .. (SV && "SH" || "CL") .. " DL: " .. filePath)
34 | AddCSLuaFile("blobsprofiler/" .. filePath)
35 | end
36 |
37 | if (SV && CL) || (CL && CLIENT) then
38 | print("[blobsProfiler] " .. (SV && "SH" || "CL") .. " Load: " .. filePath)
39 | if CLIENT and fileData.CL_NoInclude then print("[blobsProfiler] " .. (SV && "SH" || "CL") .. " CL_NoInclude: " .. filePath) return end
40 | include("blobsprofiler/" .. filePath)
41 | end
42 | end
43 | end
44 |
45 | blobsProfiler.FileList = {
46 | {
47 | File = "shared/sh_pon.lua",
48 | CL = true,
49 | SV = true,
50 | },
51 | {
52 | File = "shared/sh_netstream.lua",
53 | CL = true,
54 | SV = true,
55 | },
56 | {
57 | File = "shared/sh_blobsprofiler.lua",
58 | CL = true,
59 | SV = true,
60 | },
61 | {
62 | File = "shared/sh_modules.lua",
63 | CL = true,
64 | SV = true,
65 | },
66 | {
67 | File = "client/cl_blobsprofiler.lua",
68 | CL = true
69 | },
70 | {
71 | File = "client/vgui/vgui_bpdtree.lua",
72 | CL = true
73 | },
74 | {
75 | File = "client/vgui/vgui_bpdtree_node.lua",
76 | CL = true
77 | },
78 | {
79 | File = "client/vgui/vgui_bpdtree_node_button.lua",
80 | CL = true
81 | },
82 | {
83 | File = "server/sv_blobsprofiler.lua",
84 | SV = true,
85 | }
86 | }
87 |
88 | blobsProfiler.LoadFiles = function()
89 | for _, fileData in ipairs(blobsProfiler.FileList) do
90 | incFile(fileData)
91 | end
92 | end
93 |
94 | blobsProfiler.LoadFiles()
95 |
96 | blobsProfiler.LoadModules = function()
97 | local foundModuleFiles = file.Find("blobsprofiler/shared/modules/bp_*.lua", "LUA")
98 |
99 | for _, moduleLuaFile in ipairs(foundModuleFiles) do
100 | if SERVER then
101 | AddCSLuaFile("blobsprofiler/shared/modules/"..moduleLuaFile)
102 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "Module AddCSLuaFile: ".. moduleLuaFile)
103 | end
104 | include("blobsprofiler/shared/modules/"..moduleLuaFile)
105 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "Module include(): ".. moduleLuaFile)
106 | end
107 | end
108 |
109 | blobsProfiler.LoadModules()
110 |
111 | local JSFiles = {
112 | { FileName = "ace", Parts = 8 },
113 | { FileName = "mode-sql" },
114 | { FileName = "mode-glua", Parts = 4}
115 | }
116 |
117 | blobsProfiler.JSFileData = blobsProfiler.JSFileData or {}
118 |
119 | blobsProfiler.LoadJSFiles = function()
120 | for _, JSFile in ipairs(JSFiles) do
121 | local basePath = "blobsProfiler/client/js/" .. JSFile.FileName
122 | local partCount = JSFile.Parts or 1
123 | local parts = {}
124 |
125 | for i = 1, partCount do
126 | local filePath = basePath .. (partCount > 1 and i or "") .. ".js.lua"
127 | if SERVER then
128 | AddCSLuaFile(filePath)
129 | else
130 | parts[i] = include(filePath)
131 | end
132 | end
133 |
134 | if not SERVER then
135 | blobsProfiler.JSFileData[JSFile.FileName] = table.concat(parts)
136 | end
137 | end
138 | end
139 |
140 | blobsProfiler.LoadJSFiles()
--------------------------------------------------------------------------------
/lua/blobsprofiler/client/cl_blobsprofiler.lua:
--------------------------------------------------------------------------------
1 | blobsProfiler = blobsProfiler or {}
2 | -- <3 yogpod for the initial file tree viewer base && sparking the idea
3 | -- https://gist.github.com/Yogpod/f3be860207bc71607f14225cd7a0c948
4 | blobsProfiler.Menu = blobsProfiler.Menu or {}
5 |
6 | --[[
7 | TODO:
8 | Double click for first RC option
9 | Hooks: Suspend/Resume
10 | More RC stuff
11 | profiling
12 | profiler results
13 | SV data
14 | Theming / dark theme (settings)
15 | security
16 | errors - sv/cl (player focus?)
17 | convars
18 | settings - disable individual modules
19 | Refactoring - proper modularisation
20 | sqlite - data pagination
21 | sqlite - execute (ace editor, sql mode)
22 | ]]
23 |
24 |
25 | --[[
26 | string, number, boolean,
27 | function, table, Angle,
28 | Vector, Player, Entity,
29 | Panel, IMaterial, ITexture,
30 | IMesh, CUserCmd, ConVar,
31 | nil, userdata, Vehicle
32 | Weapon, NPC, NextBot
33 | PhysObj, SaveRestore, EffectData
34 | Sound, Texture, NavArea
35 | Path, PhysicsCollide, Trace
36 | WeaponProficiency, ScriptedVehicle
37 | ]]
38 |
39 | blobsProfiler.TypesToIcon = {
40 | ["table"] = "folder",
41 | ["function"] = "page_white_code",
42 | ["IMaterial"] = "page_white_picture",
43 | ["Panel"] = "page_white_paint",
44 | ["Player"] = "page_white_world",
45 | ["Entity"] = "page_white_world"
46 | }
47 |
48 | blobsProfiler.Menu.GlobalTypesToCondense = {
49 | {
50 | type = "string",
51 | prettyPlural = "Strings"
52 | },
53 | {
54 | type = "number",
55 | prettyPlural = "Numbers"
56 | },
57 | {
58 | type = "boolean",
59 | prettyPlural = "Booleans"
60 | },
61 | {
62 | type = "Panel",
63 | prettyPlural = "Panels"
64 | },
65 | {
66 | type = "function",
67 | prettyPlural = "Functions"
68 | },
69 | {
70 | type = "Entity",
71 | prettyPlural = "Entities"
72 | },
73 | {
74 | type = "Vector",
75 | prettyPlural = "Vectors"
76 | },
77 | {
78 | type = "Angle",
79 | prettyPlural = "Angles"
80 | },
81 | {
82 | type = "table",
83 | prettyPlural = "Tables"
84 | }
85 | }
86 |
87 | blobsProfiler.viewPropertiesPopup = function(title, data, width, height)
88 | local propertiesFrame = vgui.Create("DFrame")
89 | width = width or 500
90 | height = height or 500
91 |
92 | propertiesFrame:SetSize(width, height)
93 | propertiesFrame:SetTitle(title)
94 | propertiesFrame:Center()
95 | propertiesFrame:MakePopup()
96 |
97 | local propertiesList = vgui.Create("DProperties", propertiesFrame)
98 | propertiesList:Dock(FILL)
99 |
100 | for propertiesGroup, propertiesData in pairs(data) do
101 | for propertyKey, propertyValue in pairs(propertiesData) do
102 | local propertyRow = propertiesList:CreateRow(propertiesGroup, propertyKey)
103 | propertyRow:Setup("Generic")
104 | propertyRow:SetValue(tostring(propertyValue))
105 | end
106 | end
107 |
108 | return propertiesFrame
109 | end
110 |
111 | blobsProfiler.generateAceEditorPanel = function(parentPanel, content, editorMode, readOnly, startLine, highlightLine)
112 | local dhtmlPanel = vgui.Create("DHTML", parentPanel)
113 | content = content or [[print("Hello world!")]]
114 | editorMode = editorMode or "Lua"
115 | local useMode = "ace/mode/glua"
116 | local useModeFile = "mode-glua"
117 |
118 | if editorMode == "SQL" then
119 | useMode = "ace/mode/sql"
120 | useModeFile = "mode-sql"
121 | end
122 |
123 | local highlightJS = ""
124 | if highlightLine and highlightLine ~= 0 then
125 | highlightJS = [[
126 | var lineNumber = ]].. highlightLine - 1 ..[[;
127 |
128 | var Range = ace.require("ace/range").Range;
129 | editor.session.addMarker(new Range(lineNumber, 0, lineNumber, 1), "errorHighlight", "fullLine");
130 |
131 | editor.session.setAnnotations([{
132 | row: lineNumber,
133 | column: 0,
134 | //text: "Error: ", // TODO: Pass through error message?
135 | type: "error"
136 | }]);
137 |
138 | setTimeout(function() {
139 | editor.scrollToLine(lineNumber, true, true, function() {});
140 | editor.gotoLine(lineNumber);
141 | }, 100); // murder me in my sleep
142 | ]]
143 | end
144 |
145 | dhtmlPanel:SetHTML([[
146 |
147 |
148 |
149 |
150 | blobsProfiler: Lua Execution
151 |
162 |
163 |
164 | ]].. content ..[[
165 |
166 |
167 |
192 |
193 |
194 | ]])
195 |
196 | return dhtmlPanel
197 | end
198 |
199 | blobsProfiler.sourceFrames = {}
200 |
201 | local function popupSourceView(sourceContent, frameTitle, highlightLine)
202 | print("highlightLine", highlightLine)
203 | local sourceFrame = vgui.Create("DFrame")
204 | sourceFrame:SetSize(500,500)
205 | sourceFrame:SetTitle(frameTitle or "View source")
206 | sourceFrame:Center()
207 | sourceFrame:MakePopup()
208 |
209 | local startLine, endLine = frameTitle:match("Lines%: (%d+)%-(%d+)")
210 | startLine = tonumber(startLine)
211 | endLine = tonumber(endLine)
212 |
213 | local sourcePanel = blobsProfiler.generateAceEditorPanel(sourceFrame, sourceContent, "Lua", true, startLine, highlightLine)
214 | sourcePanel:Dock(FILL)
215 |
216 | sourcePanel.OnRemove = function()
217 | blobsProfiler.sourceFrames[frameTitle] = nil
218 | end
219 |
220 | blobsProfiler.sourceFrames[frameTitle] = sourceFrame
221 | end
222 |
223 | local function killAllSourcePopups()
224 | for k,v in pairs(blobsProfiler.sourceFrames) do
225 | if IsValid(v) then
226 | v:Remove()
227 | end
228 |
229 | blobsProfiler.sourceFrames = {}
230 | end
231 | end
232 | killAllSourcePopups()
233 |
234 | local receivedSource = {}
235 |
236 | net.Receive("blobsProfiler:sendSourceChunk", function()
237 | local requestId = net.ReadString()
238 | local startPos = net.ReadUInt(32)
239 | local chunk = net.ReadString()
240 | local highlightLine = net.ReadUInt(16)
241 |
242 | if not receivedSource[requestId] then
243 | receivedSource[requestId] = {
244 | receivedSource = {},
245 | chunksReceived = 0
246 | }
247 | end
248 |
249 | local request = receivedSource[requestId]
250 | request.receivedSource[startPos] = chunk
251 | request.chunksReceived = request.chunksReceived + 1
252 |
253 | local combinedSource = ""
254 | local allChunksReceived = true
255 | local chunkSize = 30000
256 |
257 | for i = 1, request.chunksReceived * chunkSize, chunkSize do
258 | if request.receivedSource[i] then
259 | combinedSource = combinedSource .. request.receivedSource[i]
260 | else
261 | allChunksReceived = false
262 | break
263 | end
264 | end
265 |
266 | if allChunksReceived then
267 | --local splitRequest = string.Explode(":", requestId)
268 | popupSourceView(combinedSource, requestId, highlightLine)
269 |
270 | receivedSource[requestId] = nil -- Clean up the request data
271 | end
272 | end)
273 |
274 | blobsProfiler.Menu.RCFunctions = {}
275 | blobsProfiler.Menu.RCFunctions_DEFAULT = {
276 | ["string"] = {
277 | {
278 | name = "Print",
279 | func = function(ref, node)
280 | print(ref.value)
281 | print(node.GlobalPath)
282 | end,
283 | icon = "icon16/application_osx_terminal.png"
284 | }
285 | },
286 | ["number"] = {
287 | {
288 | name = "Print",
289 | func = function(ref, node)
290 | print(ref.value)
291 | print(node.GlobalPath)
292 | end,
293 | icon = "icon16/application_osx_terminal.png"
294 | }
295 | },
296 | ["boolean"] = {
297 | {
298 | name = "Print",
299 | func = function(ref, node)
300 | print(ref.value)
301 | print(node.GlobalPath)
302 | end,
303 | icon = "icon16/application_osx_terminal.png"
304 | }
305 | },
306 | ["table"] = {
307 | {
308 | name = "Expand/Collapse",
309 | func = function(ref, node)
310 | local curState
311 | if node and IsValid(node) and node.SetExpanded then
312 | local curState = node:GetExpanded()
313 | node:SetExpanded(not curState)
314 |
315 | if ref.special then -- Don't go deeper for Lua.Globals 'type' root nodes
316 | return
317 | end
318 |
319 | for _, childNode in ipairs(node:GetChildNodes()) do
320 | if childNode and IsValid(childNode) and childNode.SetExpanded then
321 | childNode:SetExpanded(not curState)
322 | end
323 | end
324 | end
325 | end,
326 | icon = "icon16/folder_explore.png"
327 | },
328 | {
329 | name = "Print",
330 | func = function(ref, node)
331 | PrintTable(ref.value)
332 | print("Global Path:", node.GlobalPath)
333 | end,
334 | icon = "icon16/application_osx_terminal.png"
335 | }
336 | },
337 | ["function"] = {
338 | {
339 | name = "Toggle Profiling",
340 | func = function(ref, node)
341 | if node.Expander and IsValid(node.Expander) and node.Expander.SetChecked then
342 | local curChecked = node.Expander:GetChecked()
343 | node.Expander:SetChecked(not curChecked)
344 | node.Expander:OnChange(not curChecked)
345 | end
346 | end,
347 | condition = function(ref, node)
348 | if not node.Expander or not IsValid(node.Expander) or not node.Expander.SetChecked or not node.Expander:IsVisible() then
349 | return false
350 | end
351 |
352 | return true
353 | end,
354 | icon = "icon16/chart_bar.png"
355 | },
356 | {
357 | name = "Stop Profiling", -- this is literally only for the profiling module. i could use the modular function, but then i'd lose all the defaults. TODO: default AND custom combined RC options
358 | func = function(ref, node)
359 | if istable(ref.value) and ref.value.node and IsValid(ref.value.node) then
360 | if ref.value.node.Expander and IsValid(ref.value.node.Expander) and ref.value.node.Expander.SetChecked then
361 | ref.value.node.Expander:SetChecked(false)
362 | ref.value.node.Expander:OnChange(false)
363 |
364 | if node:GetParentNode() and node:GetParentNode():GetChildNodeCount() == 1 then
365 | node:GetParentNode():Remove()
366 | else
367 | node:Remove()
368 | end
369 | end
370 | end
371 | end,
372 | condition = function(ref, node, luaState)
373 | if node.Expander and not node.Expander:IsVisible() then
374 | return true
375 | end
376 |
377 | return false
378 | end,
379 | icon = "icon16/chart_bar.png"
380 | },
381 | {
382 | name = "View source",
383 | func = function(ref, node, luaState)
384 | local useValue = isfunction(ref.value) and ref.value or ref.value.func
385 | if luaState == "Client" then
386 | local debugInfo = debug.getinfo(useValue, "S")
387 | if not string.EndsWith(debugInfo.short_src, ".lua") then
388 | Derma_Message("Invalid function source: ".. debugInfo.short_src.."\nOnly functions defined in Lua can be read!", "Function view source", "OK")
389 | return
390 | end
391 |
392 | net.Start("blobsProfiler:requestSource")
393 | net.WriteString(debugInfo.short_src)
394 | net.WriteUInt(debugInfo.linedefined, 16)
395 | net.WriteUInt(debugInfo.lastlinedefined, 16)
396 | net.SendToServer()
397 | elseif luaState == "Server" then
398 | if not string.EndsWith(ref.value.short_src, ".lua") then
399 | Derma_Message("Invalid function source: ".. ref.value.short_src.."\nOnly functions defined in Lua can be read!", "Function view source", "OK")
400 | return
401 | end
402 |
403 | net.Start("blobsProfiler:requestSource")
404 | net.WriteString(ref.value.short_src)
405 | net.WriteUInt(ref.value.linedefined, 16)
406 | net.WriteUInt(ref.value.lastlinedefined, 16)
407 | net.SendToServer()
408 | end
409 | end,
410 | icon = "icon16/magnifier.png"
411 | },
412 | {
413 | name = "View properties",
414 | func = function(ref, node, luaState)
415 | local propertiesData = {}
416 |
417 | if luaState == "Client" then
418 | local debugInfo = debug.getinfo(isfunction(ref.value) and ref.value or ref.value.func)
419 | propertiesData["debug.getinfo()"] = debugInfo
420 | elseif luaState == "Server" then
421 | local propertiesTbl = table.Copy(ref.value)
422 | propertiesTbl.fakeVarType = nil
423 | propertiesData["debug.getinfo()"] = propertiesTbl
424 | end
425 |
426 | local popupView = blobsProfiler.viewPropertiesPopup("View Function: " .. ref.key, propertiesData)
427 | end,
428 | icon = "icon16/magnifier.png"
429 | }
430 | },
431 | ["file"] = {
432 | {
433 | name = "View source",
434 | func = function(ref, node, luaState)
435 | if not string.EndsWith(ref.value.Source, ".lua") then
436 | Derma_Message("Invalid file source: ".. ref.value.Source .."\nOnly Lua files can be read!", "Function view source", "OK")
437 | return
438 | end
439 |
440 | net.Start("blobsProfiler:requestSource")
441 | net.WriteString(ref.value.Source)
442 | net.WriteUInt(ref.value.Line, 16)
443 | net.WriteUInt(0, 16)
444 | net.SendToServer()
445 | end,
446 | icon = "icon16/magnifier.png"
447 | }
448 | }
449 | }
450 |
451 | blobsProfiler.Menu.TypeFolders = {}
452 | blobsProfiler.Menu.TypeFolders.Client = {}
453 | blobsProfiler.Menu.TypeFolders.Server = {}
454 |
455 | for k,v in ipairs(blobsProfiler.Menu.GlobalTypesToCondense) do
456 | blobsProfiler.Menu.TypeFolders.Client[v.type] = true
457 | blobsProfiler.Menu.TypeFolders.Server[v.type] = true
458 | end
459 |
460 | local function nodeEntriesTableKeySort(a, b)
461 | local aIsTable = type(a.value) == 'table'
462 | local bIsTable = type(b.value) == 'table'
463 | if aIsTable && !bIsTable then
464 | return true
465 | elseif !aIsTable && bIsTable then
466 | return false
467 | else
468 | return tostring(a.key) < tostring(b.key) -- Just in case.. (It's here for a reason :))
469 | end
470 | end
471 |
472 | local function rootNodeEntriesTableKeySort(a, b)
473 | local aIsTable = type(a.value) == 'table'
474 | local bIsTable = type(b.value) == 'table'
475 | if !aIsTable && bIsTable then
476 | return true
477 | elseif aIsTable && !bIsTable then
478 | return false
479 | else
480 | return a.key < b.key
481 | end
482 | end
483 |
484 | local selectedNode = nil
485 | local function addDTreeNode(parentNode, nodeData, specialType, isRoot, varType, luaState)
486 | local nodeKey = tostring(nodeData.key)
487 | local nodeValue = nodeData.value
488 | local dataType = type(nodeValue)
489 | local visualDataType = dataType
490 | local iconOverride = nil
491 |
492 | local childNode
493 |
494 | local useParent = parentNode
495 |
496 | if istable(nodeValue) and nodeValue.fakeVarType then
497 | visualDataType = nodeValue.fakeVarType
498 | end
499 |
500 | if isRoot && varType == "Lua.Globals" then
501 | local dataType = type(nodeData.value)
502 | local specialFolderPanel = blobsProfiler.Menu.TypeFolders[luaState][visualDataType]
503 | if specialFolderPanel && type(specialFolderPanel) == "Panel" then
504 | useParent = specialFolderPanel
505 | end
506 | end
507 | if visualDataType == "table" then
508 | local useNodeName = nodeKey
509 | local getModule = blobsProfiler.GetModule(varType)
510 | if not specialType and getModule.FormatNodeName and getModule.FormatNodeName(luaState, nodeKey, nodeValue) then
511 | useNodeName = getModule.FormatNodeName(luaState, nodeKey, nodeValue)
512 | end
513 |
514 | childNode = useParent:AddNode(useNodeName)
515 |
516 | childNode.Icon:SetImage("icon16/folder.png")
517 |
518 | childNode.oldExpand = childNode.SetExpanded
519 |
520 | --childNode.NeedsLazyLoad = true -- TODO: add check to make sure there even is children?
521 |
522 | if istable(nodeValue) and table.Count(nodeValue) > 0 then
523 | childNode.NeedsLazyLoad = true
524 | end
525 |
526 | childNode.SetExpanded = function(...)
527 | if !childNode.LazyLoaded then
528 | -- Lazy loading!
529 |
530 | local grandchildNode = {}
531 |
532 | for key, value in pairs(nodeValue) do
533 | table.insert(grandchildNode, {
534 | key = key,
535 | value = value
536 | })
537 | end
538 |
539 | table.sort(grandchildNode, nodeEntriesTableKeySort)
540 |
541 | for index, gcNodeData in ipairs(grandchildNode) do
542 | addDTreeNode(childNode, gcNodeData, false, false, varType, luaState)
543 | end
544 |
545 |
546 | childNode.LazyLoaded = true
547 | end
548 |
549 | local RCTable = blobsProfiler.GetRCFunctionsTable(varType)
550 | if RCTable and RCTable[dataType] then
551 | for k,v in ipairs(RCTable[dataType]) do
552 | if v.condition and not v.condition(nodeData, childNode, luaState) then
553 | continue
554 | end
555 |
556 | if v.onLoad then v.onLoad(nodeData, childNode, luaState) end
557 | end
558 | end
559 |
560 | childNode.oldExpand(...)
561 | end
562 |
563 | local getModule = blobsProfiler.GetModule(varType)
564 | if getModule.FormatNodeIcon and getModule.FormatNodeIcon(luaState, nodeKey, nodeValue) then
565 | childNode.Icon:SetImage(getModule.FormatNodeIcon(luaState, nodeKey, nodeValue))
566 | end
567 | else
568 | local nodeText = nodeKey
569 |
570 | local getModule = blobsProfiler.GetModule(varType)
571 | if getModule.FormatNodeName then
572 | nodeText = getModule.FormatNodeName(luaState, nodeKey, nodeValue)
573 | end
574 |
575 | childNode = useParent:AddNode(nodeText)
576 | childNode.Icon:SetImage("icon16/".. (blobsProfiler.TypesToIcon[visualDataType] || "page_white_text") ..".png")
577 |
578 | if getModule.FormatNodeIcon and getModule.FormatNodeIcon(luaState, nodeKey, nodeValue) then
579 | childNode.Icon:SetImage(getModule.FormatNodeIcon(luaState, nodeKey, nodeValue))
580 | end
581 |
582 | childNode.DoClick = function()
583 | if isRoot && useParent == parentNode && varType == "Globals" then
584 | print("blobsProfiler: Non-foldered root for type: ".. type(nodeValue))
585 | end
586 |
587 | return true
588 | end
589 |
590 | end
591 |
592 | childNode.GlobalPath = childNode.GlobalPath || ""
593 |
594 | if not nodeData.special then
595 | if isRoot then
596 | childNode.GlobalPath = nodeKey
597 | else
598 | childNode.GlobalPath = parentNode.GlobalPath .. "." .. nodeKey
599 | end
600 | end
601 |
602 | varType = varType or "Lua.Globals"
603 |
604 | childNode.DoRightClick = function()
605 | childNode:InternalDoClick()
606 |
607 | local RCTable = blobsProfiler.GetRCFunctionsTable(varType)
608 | if RCTable and RCTable[visualDataType] then
609 | blobsProfiler.Menu.RCMenu = DermaMenu()
610 | local RCMenu = blobsProfiler.Menu.RCMenu
611 |
612 | for _, rcM in ipairs(RCTable[visualDataType]) do
613 | if rcM.condition and not rcM.condition(nodeData, childNode, luaState) then
614 | continue
615 | end
616 |
617 | local useName = rcM.name
618 | if type(rcM.name) == "function" then
619 | useName = rcM.name(nodeData, childNode, luaState)
620 | end
621 | if useName then
622 | if rcM.submenu then
623 | local rcChild, rcParent = RCMenu:AddSubMenu(useName)
624 |
625 | local useIcon = rcM.icon
626 | if type(rcM.icon) == "function" then
627 | useIcon = rcM.icon(nodeData, childNode, luaState)
628 | end
629 | if useIcon then rcParent:SetIcon(useIcon) end
630 |
631 | for _, rcMS in ipairs(rcM.submenu) do
632 | if rcMS.condition and not rcMS.condition(nodeData, childNode, luaState) then
633 | continue
634 | end
635 |
636 | local useNameSM = rcMS.name
637 | if type(rcMS.name) == "function" then
638 | useNameSM = rcMS.name(nodeData, childNode, luaState)
639 | end
640 | if useNameSM then
641 | local rcChildP = rcChild:AddOption(useNameSM, function()
642 | rcMS.func(nodeData, childNode, luaState)
643 | if rcMS.onLoad then
644 | rcMS.onLoad(nodeData, childNode, luaState)
645 | end
646 | end)
647 |
648 | local useIconSM = rcMS.icon
649 | if type(rcMS.icon) == "function" then
650 | useIconSM = rcMS.icon(nodeData, childNode, luaState)
651 | end
652 | if useIconSM then rcChildP:SetIcon(useIconSM) end
653 | end
654 | end
655 | else
656 | local rcOption = RCMenu:AddOption(useName, function()
657 | rcM.func(nodeData, childNode, luaState)
658 | if rcM.onLoad then
659 | rcM.onLoad(nodeData, childNode, luaState)
660 | end
661 | end)
662 |
663 | local useIcon = rcM.icon
664 | if type(rcM.icon) == "function" then
665 | useIcon = rcM.icon(nodeData, childNode, luaState)
666 | end
667 | if useIcon then rcOption:SetIcon(useIcon) end
668 | end
669 | end
670 | end
671 |
672 | RCMenu:Open()
673 | end
674 | end
675 |
676 | local RCTable = blobsProfiler.GetRCFunctionsTable(varType)
677 | if RCTable and RCTable[visualDataType] then
678 | for k,v in ipairs(RCTable[visualDataType]) do
679 | if v.condition and not v.condition(nodeData, childNode, luaState) then
680 | continue
681 | end
682 |
683 | if v.onLoad then v.onLoad(nodeData, childNode, luaState) end
684 | end
685 | end
686 |
687 | if not isRoot then
688 | childNode.parentNode = parentNode
689 | end
690 |
691 | if visualDataType and visualDataType == "function" then
692 | childNode.FunctionRef = {name=nodeKey, func=nodeValue, path = childNode.GlobalPath, fakeVarType = "function", node=childNode}
693 | childNode:SetForceShowExpander(true)
694 |
695 | if varType ~= "Profiling.Targets" then
696 | childNode:IsFunc() -- This is what swaps the expander for a dcheckbox if it's a function
697 | else
698 | childNode:SetForceShowExpander(false) -- No need to select already selected functions for profiling..
699 | end
700 |
701 | blobsProfiler[luaState].Profile = blobsProfiler[luaState].Profile or {}
702 |
703 | blobsProfiler[luaState].Profile.Raw = blobsProfiler[luaState].Profile.Raw or {}
704 | blobsProfiler[luaState].Profile.Called = blobsProfiler[luaState].Profile.Called or {}
705 | blobsProfiler[luaState].Profile.Results = blobsProfiler[luaState].Profile.Results or {}
706 | childNode.Expander.OnChange = function(s, isChecked)
707 | blobsProfiler[luaState].Profile[varType] = blobsProfiler[luaState].Profile[varType] or {}
708 |
709 | if isChecked then
710 | blobsProfiler[luaState].Profile[varType][tostring(nodeValue)] = childNode.FunctionRef
711 | blobsProfiler[luaState].Profile.Raw[tostring(nodeValue)] = true
712 | blobsProfiler[luaState].Profile.Called[tostring(nodeValue)] = blobsProfiler[luaState].Profile.Called[tostring(nodeValue)] or {}
713 | blobsProfiler[luaState].Profile.Results[tostring(nodeValue)] = blobsProfiler[luaState].Profile.Results[tostring(nodeValue)] or {}
714 | else
715 | blobsProfiler[luaState].Profile[varType][tostring(nodeValue)] = nil
716 | blobsProfiler[luaState].Profile.Raw[tostring(nodeValue)] = falses
717 | blobsProfiler[luaState].Profile.Called[tostring(nodeValue)] = blobsProfiler[luaState].Profile.Called[tostring(nodeValue)] or {}
718 | blobsProfiler[luaState].Profile.Results[tostring(nodeValue)] = blobsProfiler[luaState].Profile.Results[tostring(nodeValue)] or {}
719 | end
720 | end
721 | end
722 |
723 | childNode.Label.DoDoubleClick = function()
724 | local dataType = type(nodeValue)
725 | local visualDataType = dataType
726 |
727 | if istable(nodeValue) and nodeValue.fakeVarType then
728 | visualDataType = nodeValue.fakeVarType -- every day we stray further away from god
729 | end
730 |
731 | local RCTable = blobsProfiler.GetRCFunctionsTable(varType)
732 |
733 | if RCTable and RCTable[visualDataType] then
734 | for _, rcM in ipairs(RCTable[visualDataType]) do
735 | if rcM.condition and not rcM.condition(nodeData, childNode, luaState) then
736 | continue
737 | end
738 |
739 | rcM.func(nodeData, childNode, luaState) -- this should be first one they have access to
740 | break
741 | end
742 | end
743 | end
744 |
745 | childNode.varType = varType
746 |
747 |
748 | return childNode
749 | end
750 |
751 | local function buildDTree(luaState, parentPanel, rvarType, dataTableOverride)
752 | local dTree = vgui.Create("BP_DTree", parentPanel)
753 | dTree:Dock(FILL)
754 | --dTree:SetVisible(false)
755 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "buildDTree " .. luaState .. " " .. rvarType)
756 | local rootNodes = {}
757 |
758 | local subModuleSplit = string.Explode(".", rvarType)
759 | local varType = subModuleSplit[1]
760 |
761 | if #subModuleSplit > 1 then
762 | varType = subModuleSplit[2] -- ew
763 | end
764 |
765 | local dataTable
766 |
767 | if dataTableOverride then
768 | dataTable = dataTableOverride
769 | else
770 | dataTable = blobsProfiler.GetDataTableForRealm(luaState, rvarType) or {}
771 | end
772 |
773 | if rvarType == "Lua.Globals" then -- TODO: make this shit modular
774 | for key, value in pairs(dataTable) do
775 | table.insert(rootNodes, {
776 | key = key,
777 | value = value
778 | })
779 | end
780 |
781 | local specialNodes = {}
782 |
783 | for k,v in ipairs(blobsProfiler.Menu.GlobalTypesToCondense) do
784 | table.insert(specialNodes, {
785 | key = v.prettyPlural,
786 | value = {},
787 | special = v.type
788 | })
789 | end
790 |
791 | for index, nodeData in ipairs(specialNodes) do
792 | if blobsProfiler.Menu.TypeFolders[luaState][nodeData.special] == true then
793 | blobsProfiler.Menu.TypeFolders[luaState][nodeData.special] = addDTreeNode(dTree, nodeData, true, true, rvarType, luaState)
794 | blobsProfiler.Menu.TypeFolders[luaState][nodeData.special].nodeData = nodeData
795 | end
796 | end
797 | elseif rvarType == "SQLite.Schema" then
798 | if dataTable.Tables and dataTable.Indices then
799 | table.insert(rootNodes, {
800 | key = "Tables",
801 | value = blobsProfiler.TableSort.SQLTableColSort(dataTable.Tables)
802 | })
803 |
804 | table.insert(rootNodes, {
805 | key = "Indices",
806 | value = blobsProfiler.TableSort.KeyAlphabetical(dataTable.Indices)
807 | })
808 | end
809 | else
810 | for k, v in pairs(dataTable) do
811 | table.insert(rootNodes, {
812 | key = k,
813 | value = v
814 | })
815 | end
816 | end
817 |
818 | if rvarType ~= "SQLite.Schema" then -- ewww
819 | table.sort(rootNodes, nodeEntriesTableKeySort)
820 | end
821 |
822 | local rootNodesLen = #rootNodes
823 |
824 | for index, nodeData in ipairs(rootNodes) do
825 | addDTreeNode(dTree, nodeData, false, true, rvarType, luaState)
826 |
827 | if index == rootNodesLen then
828 | dTree:SetVisible(true)
829 | end
830 | end
831 | end
832 |
833 | if blobsProfiler.Menu.MenuFrame && IsValid(blobsProfiler.Menu.MenuFrame) then
834 | blobsProfiler.Menu.MenuFrame:Remove() -- kill on lua refresh
835 | end
836 |
837 | blobsProfiler.Tabs = {}
838 | blobsProfiler.Tabs.Client = {}
839 | blobsProfiler.Tabs.Server = {}
840 |
841 | concommand.Add("blobsprofiler", function(ply, cmd, args, argStr)
842 | if not blobsProfiler.CanAccess(LocalPlayer(), "OpenMenu") then return end -- TODO: better more modular permissions via settings
843 |
844 | if argStr == "reloadclient" then
845 | if blobsProfiler.Menu.MenuFrame or IsValid(blobsProfiler.Menu.MenuFrame) then
846 | blobsProfiler.Menu.MenuFrame:Remove()
847 | blobsProfiler.Menu.MenuFrame = nil
848 | end
849 |
850 | blobsProfiler.Client = {}
851 | blobsProfiler.DataTablesSetup = false
852 |
853 | blobsProfiler.LoadFiles()
854 |
855 | print("blobsProfiler: Reloaded client data & files")
856 | return
857 | end
858 |
859 | if not blobsProfiler.DataTablesSetup or argStr == "refresh" then
860 | blobsProfiler.Client = {}
861 | blobsProfiler.Server = {}
862 |
863 | if blobsProfiler.Menu.MenuFrame or IsValid(blobsProfiler.Menu.MenuFrame) then
864 | blobsProfiler.Menu.MenuFrame:Remove()
865 | blobsProfiler.Menu.MenuFrame = nil
866 | end
867 | end
868 |
869 | if blobsProfiler.Menu.MenuFrame && IsValid(blobsProfiler.Menu.MenuFrame) then
870 | if blobsProfiler.Menu.MenuFrame:IsVisible() then
871 | blobsProfiler.Menu.MenuFrame:Hide()
872 | else
873 | blobsProfiler.Menu.MenuFrame:Show()
874 | end
875 |
876 | return
877 | end
878 |
879 | blobsProfiler.Menu.TypeFolders = {}
880 | blobsProfiler.Menu.TypeFolders.Client = {}
881 | blobsProfiler.Menu.TypeFolders.Server = {}
882 | for k,v in ipairs(blobsProfiler.Menu.GlobalTypesToCondense) do
883 | blobsProfiler.Menu.TypeFolders.Client[v.type] = true
884 | blobsProfiler.Menu.TypeFolders.Server[v.type] = true
885 | end
886 |
887 | blobsProfiler.Menu.selectedRealm = "Client"
888 | blobsProfiler.Menu.MenuFrame = vgui.Create("DFrame")
889 | blobsProfiler.Menu.MenuFrame:SetSize(900, 550)
890 | blobsProfiler.Menu.MenuFrame:Center()
891 | blobsProfiler.Menu.MenuFrame:SetTitle("blobsProfiler - " .. blobsProfiler.Menu.selectedRealm)
892 | blobsProfiler.Menu.MenuFrame:MakePopup()
893 | blobsProfiler.Menu.MenuFrame:SetSizable(true)
894 | blobsProfiler.Menu.MenuFrame:SetMinWidth( blobsProfiler.Menu.MenuFrame:GetWide() )
895 | blobsProfiler.Menu.MenuFrame:SetMinHeight( blobsProfiler.Menu.MenuFrame:GetTall() )
896 |
897 | blobsProfiler.Menu.MenuFrame.OnRemove = function()
898 | killAllSourcePopups()
899 | end
900 |
901 | local tabMenu = vgui.Create( "DPropertySheet", blobsProfiler.Menu.MenuFrame)
902 | tabMenu:Dock( FILL )
903 |
904 | local tabClient = vgui.Create("DPropertySheet", tabMenu)
905 | tabMenu:AddSheet("Client", tabClient, "icon16/application.png")
906 |
907 | local tabServer = vgui.Create("DPropertySheet", tabMenu)
908 | tabMenu:AddSheet("Server", tabServer, "icon16/application_xp_terminal.png")
909 |
910 | local tabSettings = vgui.Create("DPropertySheet", tabMenu)
911 | tabMenu:AddSheet("Settings", tabSettings, "icon16/cog.png")
912 |
913 | local luaStates = {
914 | Client = tabClient,
915 | Server = tabServer
916 | }
917 |
918 | local orderedModules = {}
919 | for moduleName, moduleData in pairs(blobsProfiler.Modules) do
920 | table.insert(orderedModules, {name=moduleName, data=moduleData})
921 | end
922 |
923 | local function sortByLoadPriority(a, b)
924 | return (a.data.OrderPriority or 9999) < (b.data.OrderPriority or 9999)
925 | end
926 |
927 | table.sort(orderedModules, sortByLoadPriority)
928 | local firstSubModule = {}
929 | for luaState, statePanel in pairs(luaStates) do
930 | for loadOrderID, loadData in ipairs(orderedModules) do
931 | local moduleName = loadData.name
932 | local moduleData = loadData.data
933 | local usePanelType = moduleData.SubModules and "DPropertySheet" or "DPanel"
934 |
935 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "Module panel setup for module: " .. moduleName .. " (".. luaState ..")")
936 |
937 | local moduleTab = vgui.Create(usePanelType, statePanel)
938 | local moduleSheet = statePanel:AddSheet( moduleName, moduleTab, moduleData.Icon )
939 |
940 | if luaState == "Client" and moduleData.UpdateRealmData and moduleData.PreloadClient then
941 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "Preloading client data for module: ".. moduleName)
942 | moduleData.UpdateRealmData("Client")
943 | end
944 | if luaState == "Server" and moduleData.UpdateRealmData and moduleData.PreloadServer then
945 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "Preloading server data for module: ".. moduleName)
946 | moduleData.UpdateRealmData("Server")
947 | moduleData.retrievingData = true
948 | end
949 |
950 | if moduleData.BuildPanel then
951 | moduleData.BuildPanel(luaState, moduleTab)
952 | end
953 |
954 | if moduleData.SubModules then
955 | local orderedSubModules = {}
956 | for subModuleName, subModuleData in pairs(moduleData.SubModules) do
957 | table.insert(orderedSubModules, {name=subModuleName, data=subModuleData})
958 | end
959 |
960 | table.sort(orderedSubModules, sortByLoadPriority)
961 |
962 |
963 | for subLoadOrderID, subLoadData in pairs(orderedSubModules) do
964 | local subModuleName = subLoadData.name
965 | local subModuleData = subLoadData.data
966 |
967 | if not firstSubModule[moduleName] then firstSubModule[moduleName] = {name=subModuleName, data=subModuleData} end
968 |
969 | local subModuleTab = vgui.Create("DPanel", moduleTab)
970 | local subModuleSheet = moduleTab:AddSheet( subModuleName, subModuleTab, subModuleData.Icon )
971 |
972 | if luaState == "Client" and subModuleData.PreloadClient and subModuleData.UpdateRealmData then
973 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "Preloading client data for ".. moduleName .." submodule: ".. subModuleName)
974 | subModuleData.UpdateRealmData("Client")
975 | end
976 | if luaState == "Server" and subModuleData.PreloadServer and subModuleData.UpdateRealmData then
977 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "Preloading server data for ".. moduleName .." submodule: ".. subModuleName)
978 | subModuleData.UpdateRealmData("Server")
979 | subModuleData.retrievingData = true
980 | end
981 |
982 | if subModuleData.CustomPanel then
983 | subModuleData.CustomPanel(luaState, subModuleTab)
984 | end
985 |
986 | if subModuleData.BuildPanel then
987 | subModuleData.BuildPanel(luaState, subModuleTab)
988 | end
989 | if subModuleData.RefreshButton then
990 | local refreshButton = vgui.Create("DButton", subModuleTab)
991 | refreshButton:Dock(BOTTOM)
992 | refreshButton:SetText(subModuleData.RefreshButton)
993 | refreshButton.DoClick = function()
994 | subModuleData.UpdateRealmData(luaState)
995 | if luaState == "Server" then subModuleData.retrievingData = true end
996 | if luaState == "Client" and subModuleData.BuildPanel then
997 | subModuleData.BuildPanel(luaState, subModuleTab)
998 | end
999 | end
1000 | end
1001 | subModuleTab.PaintOver = function(s,w,h)
1002 | if luaState == "Server" and subModuleData.retrievingData then
1003 | surface.SetDrawColor(50,50,50,100)
1004 | surface.DrawRect(0,0,w,h)
1005 | draw.SimpleTextOutlined("Retrieving data..", "HudDefault", w/2, h/2, Color(255,255,255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, 1, Color(0,0,0))
1006 |
1007 | local subModuleFullName = moduleName .. "." .. subModuleName
1008 | if blobsProfiler.chunkModuleData[subModuleFullName] and blobsProfiler.chunkModuleData[subModuleFullName].receivedChunks then
1009 | local recvChunks = #blobsProfiler.chunkModuleData[subModuleFullName].receivedChunks
1010 | local totChunks = blobsProfiler.chunkModuleData[subModuleFullName].totalChunks
1011 |
1012 | if recChunks ~= totChunks then
1013 | draw.SimpleTextOutlined(recvChunks.. "/" .. totChunks, "HudDefault", w/2, h/2+15, Color(255,255,255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, 1, Color(0,0,0))
1014 | end
1015 | end
1016 | end
1017 | end
1018 |
1019 | if luaState == "Client" then
1020 | subModuleData.ClientTab = subModuleTab
1021 | elseif luaState == "Server" then
1022 | subModuleData.ServerTab = subModuleTab
1023 | local rawModuleName = moduleName .. "." .. subModuleName
1024 | local subModuleTable, parentModuleTable = blobsProfiler.GetModule(rawModuleName)
1025 |
1026 | subModuleSheet.Tab.PaintOver = function(s,w,h)
1027 | if parentModuleTable.childrenReceiving and parentModuleTable.childrenReceiving[rawModuleName] then
1028 | local recvTbl = parentModuleTable.childrenReceiving[rawModuleName]
1029 | local recvChunks = recvTbl.receivedChunks and #recvTbl.receivedChunks or 0
1030 | local totChunks = recvTbl.totalChunks or 1
1031 | local perc = recvChunks / totChunks
1032 |
1033 | local dynamicH = (moduleTab.GetActiveTab and moduleTab:GetActiveTab() == s) and h-7 or h -- this is SO dumb
1034 | local startY = (moduleTab.GetActiveTab and moduleTab:GetActiveTab() == s) and 0 or 1
1035 |
1036 | draw.RoundedBoxEx(4, 0, startY, perc * w, dynamicH, Color(255,255,0,50), true, true)
1037 | elseif subModuleTable.flashyUpdate then
1038 | if (moduleTab.GetActiveTab and moduleTab:GetActiveTab() == s) then -- TODO: DPanel subModuleTabs will never stop flashing
1039 | subModuleTable.flashyUpdate = nil
1040 | return
1041 | end
1042 |
1043 | local flashState = math.floor(CurTime() / 0.35) % 2 == 0
1044 |
1045 | if flashState then
1046 | local dynamicH = (moduleTab.GetActiveTab and moduleTab:GetActiveTab() == s) and h-7 or h -- this is SO dumb
1047 | local startY = (moduleTab.GetActiveTab and moduleTab:GetActiveTab() == s) and 0 or 1
1048 |
1049 | draw.RoundedBoxEx(4, 0, startY, w, dynamicH, Color(0,255,0,50), true, true)
1050 | end
1051 | elseif subModuleTable.retrievingData then
1052 | local marqueeSpeed = 50 -- Speed of the marquee movement in pixels per second
1053 | local marqueeWidth = 50 -- Width of the marquee rectangle
1054 | local currentTime = CurTime()
1055 |
1056 | local marqueeX = (currentTime * marqueeSpeed) % (w + marqueeWidth) - marqueeWidth
1057 |
1058 | local dynamicH = (moduleTab.GetActiveTab and moduleTab:GetActiveTab() == s) and h-7 or h -- this is SO dumb
1059 | local startY = (moduleTab.GetActiveTab and moduleTab:GetActiveTab() == s) and 0 or 1
1060 |
1061 | local visibleWidth = marqueeWidth
1062 | local drawLeftRound = false
1063 | local drawRightRound = false
1064 |
1065 | if marqueeX < 0 then -- off screen - left side
1066 | visibleWidth = marqueeWidth + marqueeX
1067 | marqueeX = 0
1068 | drawLeftRound = true
1069 | elseif marqueeX + marqueeWidth > w then -- off screen - right side
1070 | visibleWidth = w - marqueeX
1071 | drawRightRound = true
1072 | end
1073 |
1074 | draw.RoundedBoxEx(
1075 | 4,
1076 | marqueeX,
1077 | startY,
1078 | visibleWidth,
1079 | dynamicH,
1080 | Color(255, 255, 0, 50),
1081 | drawLeftRound, -- Top-left rounding if the left edge is offscreen
1082 | drawRightRound -- Top-right rounding if the right edge is offscreen
1083 | )
1084 | end
1085 | end
1086 | end
1087 | moduleTab:OnActiveTabChanged(nil, moduleTab:GetActiveTab())
1088 | end
1089 |
1090 | moduleTab.OnActiveTabChanged = function(s, pnlOld, pnlNew)
1091 | local rawModuleName = moduleName .. "." .. pnlNew:GetText()
1092 | local subModuleTable, parentModuleTable = blobsProfiler.GetModule(rawModuleName)
1093 |
1094 | if not blobsProfiler[luaState][moduleName] or not blobsProfiler[luaState][moduleName][pnlNew:GetText()] then
1095 | if subModuleTable.UpdateRealmData then
1096 | subModuleTable.UpdateRealmData(luaState)
1097 | if luaState == "Server" then
1098 | subModuleTable.retrievingData = true
1099 | end
1100 | end
1101 | end
1102 |
1103 | if subModuleTable.OnOpen then
1104 | local prntPanel
1105 | if luaState == "Client" then
1106 | prntPanel = subModuleTable.ClientTab
1107 | elseif luaState == "Server" then
1108 | prntPanel = subModuleTable.ServerTab
1109 | end
1110 | subModuleTable.OnOpen(luaState, prntPanel)
1111 | end
1112 | end
1113 | end
1114 |
1115 | moduleTab.PaintOver = function(s,w,h)
1116 | if luaState == "Server" and moduleData.retrievingData then
1117 | surface.SetDrawColor(50,50,50,100)
1118 | surface.DrawRect(0,0,w,h)
1119 | draw.SimpleTextOutlined("Retrieving data..", "HudDefault", w/2, h/2-15, Color(255,255,255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, 1, Color(0,0,0))
1120 |
1121 | if blobsProfiler.chunkModuleData[moduleName] and blobsProfiler.chunkModuleData[moduleName].receivedChunks then
1122 | local recvChunks = #blobsProfiler.chunkModuleData[moduleName].receivedChunks
1123 | local totChunks = blobsProfiler.chunkModuleData[moduleName].totalChunks
1124 |
1125 | if recChunks ~= totChunks then
1126 | draw.SimpleTextOutlined(recvChunks.. "/" .. totChunks, "HudDefault", w/2, h/2+15, Color(255,255,255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, 1, Color(0,0,0))
1127 | end
1128 | end
1129 | end
1130 | end
1131 |
1132 | if moduleData.RefreshButton then
1133 | local refreshButton = vgui.Create("DButton", moduleTab)
1134 | refreshButton:Dock(BOTTOM)
1135 | refreshButton:SetText(moduleData.RefreshButton)
1136 | refreshButton.DoClick = function()
1137 | moduleData.UpdateRealmData(luaState)
1138 | if luaState == "Server" then moduleData.retrievingData = true end
1139 | if luaState == "Client" and moduleData.BuildPanel then
1140 | moduleData.BuildPanel(luaState, moduleTab)
1141 | end
1142 | end
1143 | end
1144 |
1145 | if luaState == "Client" then
1146 | moduleData.ClientTab = moduleTab
1147 | elseif luaState == "Server" then
1148 | moduleData.ServerTab = moduleTab
1149 |
1150 | local moduleTable = blobsProfiler.GetModule(moduleName)
1151 | moduleSheet.Tab.PaintOver = function(s,w,h)
1152 | -- 'Total' indicator progress bar of all submodules progress
1153 | if moduleTable.childrenReceiving then
1154 | local totRecv = 0
1155 | local totChunks = 1
1156 |
1157 | for moduleN, moduleRD in pairs(moduleTable.childrenReceiving) do
1158 | totRecv = totRecv + (moduleRD.receivedChunks and #moduleRD.receivedChunks or 0)
1159 | totChunks = totChunks + (moduleRD.totalChunks or 1)
1160 | end
1161 |
1162 | local totalPerc = totRecv / totChunks
1163 |
1164 | local dynamicH = (statePanel.GetActiveTab and statePanel:GetActiveTab() == s) and h-7 or h -- this is SO dumb
1165 | local startY = (statePanel.GetActiveTab and statePanel:GetActiveTab() == s) and 0 or 1
1166 | draw.RoundedBoxEx(4, 0, startY, totalPerc * w, dynamicH, Color(255,255,0,50), true, true)
1167 | elseif moduleTable.flashyUpdate then
1168 | if (statePanel.GetActiveTab and statePanel:GetActiveTab() == s) then -- TODO: DPanel subModuleTabs will never stop flashing
1169 | moduleTable.flashyUpdate = nil
1170 | return
1171 | end
1172 |
1173 | local flashState = math.floor(CurTime() / 0.35) % 2 == 0
1174 |
1175 | if flashState then
1176 | local dynamicH = (statePanel.GetActiveTab and statePanel:GetActiveTab() == s) and h-7 or h -- this is SO dumb
1177 | local startY = (statePanel.GetActiveTab and statePanel:GetActiveTab() == s) and 0 or 1
1178 | draw.RoundedBoxEx(4, 0, startY, w, dynamicH, Color(0,255,0,50), true, true)
1179 | end
1180 | elseif moduleTable.retrievingData then
1181 | local marqueeSpeed = 50 -- Speed of the marquee movement in pixels per second
1182 | local marqueeWidth = 50 -- Width of the marquee rectangle
1183 | local currentTime = CurTime()
1184 |
1185 | local marqueeX = (currentTime * marqueeSpeed) % (w + marqueeWidth) - marqueeWidth
1186 |
1187 | local dynamicH = (statePanel.GetActiveTab and statePanel:GetActiveTab() == s) and h-7 or h -- this is SO dumb
1188 | local startY = (statePanel.GetActiveTab and statePanel:GetActiveTab() == s) and 0 or 1
1189 |
1190 | local visibleWidth = marqueeWidth
1191 | local drawLeftRound = false
1192 | local drawRightRound = false
1193 |
1194 | if marqueeX < 0 then
1195 | visibleWidth = marqueeWidth + marqueeX
1196 | marqueeX = 0
1197 | drawLeftRound = true
1198 | elseif marqueeX + marqueeWidth > w then
1199 | visibleWidth = w - marqueeX
1200 | drawRightRound = true
1201 | end
1202 |
1203 | draw.RoundedBoxEx(
1204 | 4,
1205 | marqueeX,
1206 | startY,
1207 | visibleWidth,
1208 | dynamicH,
1209 | Color(255, 255, 0, 50),
1210 | drawLeftRound, -- Top-left rounding if the left edge is offscreen
1211 | drawRightRound -- Top-right rounding if the right edge is offscreen
1212 | )
1213 | end
1214 | end
1215 | end
1216 | end
1217 |
1218 | --[[
1219 | local tabSQL = vgui.Create( "DPropertySheet", statePanel )
1220 | statePanel:AddSheet( "SQLite", tabSQL, "icon16/database.png" )
1221 | buildSQLTab(luaState, tabSQL)
1222 | blobsProfiler.Tabs[luaState].SQLite = tabSQL]]
1223 | end
1224 |
1225 | tabMenu.OnActiveTabChanged = function(s, pnlOld, pnlNew)
1226 | blobsProfiler.Menu.selectedRealm = pnlNew:GetText()
1227 | blobsProfiler.Menu.MenuFrame:SetTitle("blobsProfiler - " .. blobsProfiler.Menu.selectedRealm)
1228 |
1229 | local subActiveTab = pnlNew:GetPanel():GetActiveTab()
1230 | local subPropertySheetText = ""
1231 | if subActiveTab and subActiveTab:GetText() then
1232 | subPropertySheetText = " - " .. subActiveTab:GetText()
1233 | end
1234 | blobsProfiler.Menu.MenuFrame:SetTitle("blobsProfiler - " .. blobsProfiler.Menu.selectedRealm .. subPropertySheetText)
1235 | if not subActiveTab then return end
1236 |
1237 | if blobsProfiler.Modules[subActiveTab:GetText()] and firstSubModule[subActiveTab:GetText()] then
1238 | if pnlNew:GetText() == "Server" and firstSubModule[subActiveTab:GetText()].data.UpdateRealmData then
1239 | if blobsProfiler.Server[subActiveTab:GetText()][firstSubModule[subActiveTab:GetText()].name] then return end
1240 | firstSubModule[subActiveTab:GetText()].data.retrievingData = true
1241 | firstSubModule[subActiveTab:GetText()].data.UpdateRealmData(pnlNew:GetText())
1242 | end
1243 | end
1244 |
1245 | -- get selected module tab, call on ActiveTabChanged
1246 | if pnlNew:GetText() == "Client" then
1247 | tabClient:OnActiveTabChanged(nil, tabClient:GetActiveTab())
1248 | else
1249 | tabServer:OnActiveTabChanged(nil, tabServer:GetActiveTab())
1250 | end
1251 | end
1252 |
1253 | tabClient.OnActiveTabChanged = function(s, pnlOld, pnlNew)
1254 | blobsProfiler.Menu.MenuFrame:SetTitle("blobsProfiler - " .. blobsProfiler.Menu.selectedRealm .. " - " .. pnlNew:GetText())
1255 | local moduleTable = blobsProfiler.GetModule(pnlNew:GetText())
1256 |
1257 | if not blobsProfiler.Client[pnlNew:GetText()] then
1258 | if moduleTable.UpdateRealmData then
1259 | moduleTable.UpdateRealmData("Client")
1260 | end
1261 | end
1262 |
1263 | local getSheet = pnlNew:GetPanel()
1264 | if getSheet.OnActiveTabChanged then
1265 | getSheet:OnActiveTabChanged(nil, getSheet:GetActiveTab())
1266 | end
1267 |
1268 | if moduleTable.OnOpen then
1269 | moduleTable.OnOpen("Client", moduleTable.ClientTab)
1270 | end
1271 | end
1272 |
1273 | tabServer.OnActiveTabChanged = function(s, pnlOld, pnlNew)
1274 | blobsProfiler.Menu.MenuFrame:SetTitle("blobsProfiler - " .. blobsProfiler.Menu.selectedRealm .. " - " .. pnlNew:GetText())
1275 | local moduleTable = blobsProfiler.GetModule(pnlNew:GetText())
1276 |
1277 | if not blobsProfiler.Server[pnlNew:GetText()] then
1278 | if moduleTable.UpdateRealmData then
1279 | moduleTable.retrievingData = true
1280 | moduleTable.UpdateRealmData("Server")
1281 | end
1282 | end
1283 |
1284 | if firstSubModule[pnlNew:GetText()] then
1285 | if firstSubModule[pnlNew:GetText()].data.UpdateRealmData then
1286 | if blobsProfiler.Server[pnlNew:GetText()][firstSubModule[pnlNew:GetText()].name] then return end
1287 | firstSubModule[pnlNew:GetText()].data.retrievingData = true
1288 | firstSubModule[pnlNew:GetText()].data.UpdateRealmData("Server")
1289 | end
1290 | end
1291 |
1292 | local getSheet = pnlNew:GetPanel()
1293 | if getSheet.OnActiveTabChanged then
1294 | getSheet:OnActiveTabChanged(nil, getSheet:GetActiveTab())
1295 | end
1296 |
1297 | if moduleTable.OnOpen then
1298 | moduleTable.OnOpen("Server", moduleTable.ServerTab)
1299 | end
1300 | end
1301 |
1302 | tabMenu:OnActiveTabChanged(nil, tabMenu:GetActiveTab()) -- lol
1303 | end)
1304 |
1305 | local function handleSVDataUpdate(rawModuleName, dataTable)
1306 | local moduleTable, parentModule = blobsProfiler.GetModule(rawModuleName)
1307 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "requestData module: ".. rawModuleName)
1308 |
1309 | if parentModule then
1310 | if parentModule.childrenReceiving[rawModuleName] then
1311 | parentModule.childrenReceiving[rawModuleName] = nil
1312 | end
1313 |
1314 | parentModule.flashyUpdate = true
1315 | else
1316 | if moduleTable.childrenReceiving[rawModuleName] then
1317 | moduleTable.childrenReceiving[rawModuleName] = nil
1318 | end
1319 | end
1320 |
1321 | blobsProfiler.SetRealmData("Server", rawModuleName, dataTable)
1322 | moduleTable.retrievingData = false
1323 | moduleTable.flashyUpdate = true
1324 |
1325 | if moduleTable.BuildPanel then
1326 | moduleTable.BuildPanel("Server", moduleTable.ServerTab)
1327 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "Module.BuildPanel completed for ".. rawModuleName .. " (Server)")
1328 | end
1329 | end
1330 |
1331 | blobsProfiler.chunkModuleData = {}
1332 |
1333 | net.Receive("blobsProfiler:requestData", function()
1334 | local moduleName = net.ReadString()
1335 | local totalChunks = net.ReadUInt(16)
1336 | local currentChunk = net.ReadUInt(16)
1337 | local chunkData = net.ReadData(blobsProfiler.svDataChunkSize)
1338 |
1339 | local moduleSplit = string.Explode(".", moduleName)
1340 | local moduleParent = moduleSplit[1]
1341 |
1342 | if not blobsProfiler.chunkModuleData[moduleName] then
1343 | blobsProfiler.chunkModuleData[moduleName] = {
1344 | totalChunks = totalChunks,
1345 | receivedChunks = {},
1346 | }
1347 | end
1348 |
1349 | if not blobsProfiler.chunkModuleData[moduleName].receivedChunks then
1350 | blobsProfiler.chunkModuleData[moduleName].receivedChunks = {}
1351 | end
1352 |
1353 | table.insert(blobsProfiler.chunkModuleData[moduleName].receivedChunks, chunkData)
1354 |
1355 | -- using blobsProfiler.Modules is acceptable here because it would look shit using .GetModule (i tried)
1356 | blobsProfiler.Modules[moduleParent].childrenReceiving = blobsProfiler.Modules[moduleParent].childrenReceiving or {}
1357 | blobsProfiler.Modules[moduleParent].childrenReceiving[moduleName] = blobsProfiler.chunkModuleData[moduleName]
1358 |
1359 | if #blobsProfiler.chunkModuleData[moduleName].receivedChunks == totalChunks then
1360 | local fullData = table.concat(blobsProfiler.chunkModuleData[moduleName].receivedChunks)
1361 | blobsProfiler.chunkModuleData[moduleName] = util.JSONToTable(util.Decompress(fullData), false, true)
1362 |
1363 | handleSVDataUpdate(moduleName, blobsProfiler.chunkModuleData[moduleName])
1364 |
1365 | blobsProfiler.chunkModuleData[moduleName] = nil
1366 | blobsProfiler.Modules[moduleParent].childrenReceiving[moduleName] = nil
1367 |
1368 | if table.Count(blobsProfiler.Modules[moduleParent].childrenReceiving) == 0 then
1369 | blobsProfiler.Modules[moduleParent].childrenReceiving = nil
1370 | end
1371 | end
1372 | end)
1373 |
1374 | blobsProfiler.buildDTree = buildDTree
--------------------------------------------------------------------------------
/lua/blobsprofiler/client/js/ace8.js.lua:
--------------------------------------------------------------------------------
1 | return [==[tion:"Add cursor below",exec:function(e){e.selectMoreLines(1)},bindKey:{win:"Ctrl-Alt-Down",mac:"Ctrl-Alt-Down"},scrollIntoView:"cursor",readOnly:!0},{name:"addCursorAboveSkipCurrent",description:"Add cursor above (skip current)",exec:function(e){e.selectMoreLines(-1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Up",mac:"Ctrl-Alt-Shift-Up"},scrollIntoView:"cursor",readOnly:!0},{name:"addCursorBelowSkipCurrent",description:"Add cursor below (skip current)",exec:function(e){e.selectMoreLines(1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Down",mac:"Ctrl-Alt-Shift-Down"},scrollIntoView:"cursor",readOnly:!0},{name:"selectMoreBefore",description:"Select more before",exec:function(e){e.selectMore(-1)},bindKey:{win:"Ctrl-Alt-Left",mac:"Ctrl-Alt-Left"},scrollIntoView:"cursor",readOnly:!0},{name:"selectMoreAfter",description:"Select more after",exec:function(e){e.selectMore(1)},bindKey:{win:"Ctrl-Alt-Right",mac:"Ctrl-Alt-Right"},scrollIntoView:"cursor",readOnly:!0},{name:"selectNextBefore",description:"Select next before",exec:function(e){e.selectMore(-1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Left",mac:"Ctrl-Alt-Shift-Left"},scrollIntoView:"cursor",readOnly:!0},{name:"selectNextAfter",description:"Select next after",exec:function(e){e.selectMore(1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Right",mac:"Ctrl-Alt-Shift-Right"},scrollIntoView:"cursor",readOnly:!0},{name:"toggleSplitSelectionIntoLines",description:"Split selection into lines",exec:function(e){e.multiSelect.rangeCount>1?e.multiSelect.joinSelections():e.multiSelect.splitIntoLines()},bindKey:{win:"Ctrl-Alt-L",mac:"Ctrl-Alt-L"},readOnly:!0},{name:"splitSelectionIntoLines",description:"Split into lines",exec:function(e){e.multiSelect.splitIntoLines()},readOnly:!0},{name:"alignCursors",description:"Align cursors",exec:function(e){e.alignCursors()},bindKey:{win:"Ctrl-Alt-A",mac:"Ctrl-Alt-A"},scrollIntoView:"cursor"},{name:"findAll",description:"Find all",exec:function(e){e.findAll()},bindKey:{win:"Ctrl-Alt-K",mac:"Ctrl-Alt-G"},scrollIntoView:"cursor",readOnly:!0}],t.multiSelectCommands=[{name:"singleSelection",description:"Single selection",bindKey:"esc",exec:function(e){e.exitMultiSelectMode()},scrollIntoView:"cursor",readOnly:!0,isAvailable:function(e){return e&&e.inMultiSelectMode}}];var i=e("../keyboard/hash_handler").HashHandler;t.keyboardHandler=new i(t.multiSelectCommands)}),define("ace/multi_select",["require","exports","module","ace/range_list","ace/range","ace/selection","ace/mouse/multi_select_handler","ace/lib/event","ace/lib/lang","ace/commands/multi_select_commands","ace/search","ace/edit_session","ace/editor","ace/config"],function(e,t,n){var i=e("./range_list").RangeList,o=e("./range").Range,r=e("./selection").Selection,s=e("./mouse/multi_select_handler").onMouseDown,a=e("./lib/event"),l=e("./lib/lang"),c=e("./commands/multi_select_commands");t.commands=c.defaultCommands.concat(c.multiSelectCommands);var h=new(e("./search")).Search;(function(){this.getSelectionMarkers=function(){return this.$selectionMarkers}}).call(e("./edit_session").EditSession.prototype),(function(){this.ranges=null,this.rangeList=null,this.addRange=function(e,t){if(e){if(!this.inMultiSelectMode&&0===this.rangeCount){var n=this.toOrientedRange();if(this.rangeList.add(n),this.rangeList.add(e),2!=this.rangeList.ranges.length)return this.rangeList.removeAll(),t||this.fromOrientedRange(e);this.rangeList.removeAll(),this.rangeList.add(n),this.$onAddRange(n)}e.cursor||(e.cursor=e.end);var i=this.rangeList.add(e);return this.$onAddRange(e),i.length&&this.$onRemoveRange(i),this.rangeCount>1&&!this.inMultiSelectMode&&(this._signal("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)}},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length&&this.$onRemoveRange(e)},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal("addRange",{range:e})},this.$onRemoveRange=function(e){if(this.rangeCount=this.rangeList.ranges.length,1==this.rangeCount&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var i=this.ranges.indexOf(e[n]);this.ranges.splice(i,1)}this._signal("removeRange",{ranges:e}),0===this.rangeCount&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),(t=t||this.ranges[0])&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){this.rangeList||(this.rangeList=new i,this.ranges=[],this.rangeCount=0)},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){for(var e=this.ranges.length?this.ranges:[this.getRange()],t=[],n=0;n1){var e=this.rangeList.ranges,t=e[e.length-1],n=o.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var i=this.session.documentToScreenPosition(this.cursor),r=this.session.documentToScreenPosition(this.anchor);this.rectangularRangeBlock(i,r).forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var i,r=[],s=e.column0;)v--;if(v>0)for(var y=0;r[y].isEmpty();)y++;for(var w=v;w>=y;w--)r[w].isEmpty()&&r.splice(w,1)}return r}}).call(r.prototype);var u=e("./editor").Editor;function d(e,t){return e.row==t.row&&e.column==t.column}function p(e){e.$multiselectOnSessionChange||(e.$onAddRange=e.$onAddRange.bind(e),e.$onRemoveRange=e.$onRemoveRange.bind(e),e.$onMultiSelect=e.$onMultiSelect.bind(e),e.$onSingleSelect=e.$onSingleSelect.bind(e),e.$multiselectOnSessionChange=t.onSessionChange.bind(e),e.$checkMultiselectChange=e.$checkMultiselectChange.bind(e),e.$multiselectOnSessionChange(e),e.on("changeSession",e.$multiselectOnSessionChange),e.on("mousedown",s),e.commands.addCommands(c.defaultCommands),function e(t){if(t.textInput){var n=t.textInput.getElement(),i=!1;a.addListener(n,"keydown",function(e){var n=18==e.keyCode&&!(e.ctrlKey||e.shiftKey||e.metaKey);t.$blockSelectEnabled&&n?i||(t.renderer.setMouseCursor("crosshair"),i=!0):i&&o()},t),a.addListener(n,"keyup",o,t),a.addListener(n,"blur",o,t)}function o(e){i&&(t.renderer.setMouseCursor(""),i=!1)}}(e))}(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(e.marker){this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);-1!=t&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length}},this.removeSelectionMarkers=function(e){for(var t=this.session.$selectionMarkers,n=e.length;n--;){var i=e[n];if(i.marker){this.session.removeMarker(i.marker);var o=t.indexOf(i);-1!=o&&t.splice(o,1)}}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){this.inMultiSelectMode||(this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(c.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers())},this.$onSingleSelect=function(e){this.session.multiSelect.inVirtualMode||(this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(c.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit("changeSelection"))},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(n.multiSelect){if(t.multiSelectAction)"forEach"==t.multiSelectAction?i=n.forEachSelection(t,e.args):"forEachLine"==t.multiSelectAction?i=n.forEachSelection(t,e.args,!0):"single"==t.multiSelectAction?(n.exitMultiSelectMode(),i=t.exec(n,e.args||{})):i=t.multiSelectAction(n,e.args||{});else{var i=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}return i}},this.forEachSelection=function(e,t,n){if(!this.inVirtualSelectionMode){var i,o=n&&n.keepOrder,s=!0==n||n&&n.$byLines,a=this.session,l=this.selection,c=l.rangeList,h=(o?l:c).ranges;if(!h.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var u=l._eventRegistry;l._eventRegistry={};var d=new r(a);this.inVirtualSelectionMode=!0;for(var p=h.length;p--;){if(s)for(;p>0&&h[p].start.row==h[p-1].end.row;)p--;d.fromOrientedRange(h[p]),d.index=p,this.selection=a.selection=d;var g=e.exec?e.exec(this,t||{}):e(this,t||{});i||void 0===g||(i=g),d.toOrientedRange(h[p])}d.detach(),this.selection=a.selection=l,this.inVirtualSelectionMode=!1,l._eventRegistry=u,l.mergeOverlappingRanges(),l.ranges[0]&&l.fromOrientedRange(l.ranges[0]);var f=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),f&&f.from==f.to&&this.renderer.animateScrolling(f.from),i}},this.exitMultiSelectMode=function(){this.inMultiSelectMode&&!this.inVirtualSelectionMode&&this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){for(var t=this.multiSelect.rangeList.ranges,n=[],i=0;is&&(s=n.column),ih?e.insert(i,l.stringRepeat(" ",r-h)):e.remove(new o(i.row,i.column,i.row,i.column-r+h)),t.start.column=t.end.column=s,t.start.row=t.end.row=i.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}else{var h=this.selection.getRange(),u=h.start.row,d=h.end.row,p=u==d;if(p){var g,f=this.session.getLength();do g=this.session.getLine(d);while(/[=:]/.test(g)&&++d0);u<0&&(u=0),d>=f&&(d=f-1)}var m=this.session.removeFullLines(u,d);m=this.$reAlignText(m,p),this.session.insert({row:u,column:0},m.join("\n")+"\n"),p||(h.start.column=0,h.end.column=m[m.length-1].length),this.selection.setRange(h)}},this.$reAlignText=function(e,t){var n,i,o,r=!0,s=!0;return e.map(function(e){var t=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return t?null==n?(n=t[1].length,i=t[2].length,o=t[3].length,t):(n+i+o!=t[1].length+t[2].length+t[3].length&&(s=!1),n!=t[1].length&&(r=!1),n>t[1].length&&(n=t[1].length),it[3].length&&(o=t[3].length),t):[e]}).map(t?c:r?s?function e(t){return t[2]?a(n+i-t[2].length)+t[2]+a(o)+t[4].replace(/^([=:])\s+/,"$1 "):t[0]}:c:function e(t){return t[2]?a(n)+t[2]+a(o)+t[4].replace(/^([=:])\s+/,"$1 "):t[0]});function a(e){return l.stringRepeat(" ",e)}function c(e){return e[2]?a(n)+e[2]+a(i-e[2].length+o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}}}).call(u.prototype),t.onSessionChange=function(e){var t=e.session;t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect;var n=e.oldSession;n&&(n.multiSelect.off("addRange",this.$onAddRange),n.multiSelect.off("removeRange",this.$onRemoveRange),n.multiSelect.off("multiSelect",this.$onMultiSelect),n.multiSelect.off("singleSelect",this.$onSingleSelect),n.multiSelect.lead.off("change",this.$checkMultiselectChange),n.multiSelect.anchor.off("change",this.$checkMultiselectChange)),t&&(t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),t.multiSelect.lead.on("change",this.$checkMultiselectChange),t.multiSelect.anchor.on("change",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=p,e("./config").defineOptions(u.prototype,"editor",{enableMultiselect:{set:function(e){p(this),e?this.on("mousedown",s):this.off("mousedown",s)},value:!0},enableBlockSelect:{set:function(e){this.$blockSelectEnabled=e},value:!0}})}),define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,n){"use strict";var i=e("../../range").Range;(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){var i=e.getLine(n);return this.foldingStartMarker.test(i)?"start":"markbeginend"==t&&this.foldingStopMarker&&this.foldingStopMarker.test(i)?"end":""},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var o=/\S/,r=e.getLine(t),s=r.search(o);if(-1!=s){for(var a=n||r.length,l=e.getLength(),c=t,h=t;++tc){var p=e.getLine(h).length;return new i(c,a,h,p)}}},this.openingBracketBlock=function(e,t,n,o,r){var s={row:n,column:o+1},a=e.$findClosingBracket(t,s,r);if(a){var l=e.foldWidgets[a.row];return null==l&&(l=e.getFoldWidget(a.row)),"start"==l&&a.row>s.row&&(a.row--,a.column=e.getLine(a.row).length),i.fromPoints(s,a)}},this.closingBracketBlock=function(e,t,n,o,r){var s={row:n,column:o},a=e.$findOpeningBracket(t,s);if(a)return a.column++,s.column--,i.fromPoints(a,s)}}).call((t.FoldMode=function(){}).prototype)}),define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range","ace/config"],function(e,t,n){"use strict";var i=e("../line_widgets").LineWidgets,o=e("../lib/dom"),r=e("../range").Range,s=e("../config").nls;t.showErrorMarker=function(e,t){var n,a=e.session;a.widgetManager||(a.widgetManager=new i(a),a.widgetManager.attach(e));var l=e.getCursorPosition(),c=l.row,h=a.widgetManager.getWidgetsAtRow(c).filter(function(e){return"errorMarker"==e.type})[0];h?h.destroy():c-=t;var u=function e(t,n,i){var o=t.getAnnotations().sort(r.comparePoints);if(o.length){var s=function e(t,n,i){for(var o=0,r=t.length-1;o<=r;){var s=o+r>>1,a=i(n,t[s]);if(a>0)o=s+1;else{if(!(a<0))return s;r=s-1}}return-(o+1)}(o,{row:n,column:-1},r.comparePoints);s<0&&(s=-s-1),s>=o.length?s=i>0?0:o.length-1:0===s&&i<0&&(s=o.length-1);var a=o[s];if(a&&i){if(a.row===n){do a=o[s+=i];while(a&&a.row===n);if(!a)return o.slice()}var l=[];n=a.row;do l[i<0?"unshift":"push"](a),a=o[s+=i];while(a&&a.row==n);return l.length&&l}}}(a,c,t);if(u){var d=u[0];l.column=(d.pos&&"number"!=typeof d.column?d.pos.sc:d.column)||0,l.row=d.row,n=e.renderer.$gutterLayer.$annotations[l.row]}else{if(h)return;n={displayText:[s("error-marker.good-state","Looks good!")],className:"ace_ok"}}e.session.unfold(l.row),e.selection.moveToPosition(l);var p={row:l.row,fixedWidth:!0,coverGutter:!0,el:o.createElement("div"),type:"errorMarker"},g=p.el.appendChild(o.createElement("div")),f=p.el.appendChild(o.createElement("div"));f.className="error_widget_arrow "+n.className;var m=e.renderer.$cursorLayer.getPixelPosition(l).left;f.style.left=m+e.renderer.gutterWidth-5+"px",p.el.className="error_widget_wrapper",g.className="error_widget "+n.className,n.displayText.forEach(function(e,t){g.appendChild(o.createTextNode(e)),t|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|="
52 | }, {
53 | token: "paren.lparen",
54 | regex: "[\\(]"
55 | }, {
56 | token: "paren.rparen",
57 | regex: "[\\)]"
58 | }, {
59 | token: "text",
60 | regex: "\\s+"
61 | }]
62 | };
63 | this.normalizeRules();
64 | };
65 | oop.inherits(SqlHighlightRules, TextHighlightRules);
66 | exports.SqlHighlightRules = SqlHighlightRules;
67 |
68 | });
69 |
70 | define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"], function(require, exports, module){"use strict";
71 | var oop = require("../../lib/oop");
72 | var Range = require("../../range").Range;
73 | var BaseFoldMode = require("./fold_mode").FoldMode;
74 | var FoldMode = exports.FoldMode = function (commentRegex) {
75 | if (commentRegex) {
76 | this.foldingStartMarker = new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.start));
77 | this.foldingStopMarker = new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.end));
78 | }
79 | };
80 | oop.inherits(FoldMode, BaseFoldMode);
81 | (function () {
82 | this.foldingStartMarker = /([\{\[\(])[^\}\]\)]*$|^\s*(\/\*)/;
83 | this.foldingStopMarker = /^[^\[\{\(]*([\}\]\)])|^[\s\*]*(\*\/)/;
84 | this.singleLineBlockCommentRe = /^\s*(\/\*).*\*\/\s*$/;
85 | this.tripleStarBlockCommentRe = /^\s*(\/\*\*\*).*\*\/\s*$/;
86 | this.startRegionRe = /^\s*(\/\*|\/\/)#?region\b/;
87 | this._getFoldWidgetBase = this.getFoldWidget;
88 | this.getFoldWidget = function (session, foldStyle, row) {
89 | var line = session.getLine(row);
90 | if (this.singleLineBlockCommentRe.test(line)) {
91 | if (!this.startRegionRe.test(line) && !this.tripleStarBlockCommentRe.test(line))
92 | return "";
93 | }
94 | var fw = this._getFoldWidgetBase(session, foldStyle, row);
95 | if (!fw && this.startRegionRe.test(line))
96 | return "start"; // lineCommentRegionStart
97 | return fw;
98 | };
99 | this.getFoldWidgetRange = function (session, foldStyle, row, forceMultiline) {
100 | var line = session.getLine(row);
101 | if (this.startRegionRe.test(line))
102 | return this.getCommentRegionBlock(session, line, row);
103 | var match = line.match(this.foldingStartMarker);
104 | if (match) {
105 | var i = match.index;
106 | if (match[1])
107 | return this.openingBracketBlock(session, match[1], row, i);
108 | var range = session.getCommentFoldRange(row, i + match[0].length, 1);
109 | if (range && !range.isMultiLine()) {
110 | if (forceMultiline) {
111 | range = this.getSectionRange(session, row);
112 | }
113 | else if (foldStyle != "all")
114 | range = null;
115 | }
116 | return range;
117 | }
118 | if (foldStyle === "markbegin")
119 | return;
120 | var match = line.match(this.foldingStopMarker);
121 | if (match) {
122 | var i = match.index + match[0].length;
123 | if (match[1])
124 | return this.closingBracketBlock(session, match[1], row, i);
125 | return session.getCommentFoldRange(row, i, -1);
126 | }
127 | };
128 | this.getSectionRange = function (session, row) {
129 | var line = session.getLine(row);
130 | var startIndent = line.search(/\S/);
131 | var startRow = row;
132 | var startColumn = line.length;
133 | row = row + 1;
134 | var endRow = row;
135 | var maxRow = session.getLength();
136 | while (++row < maxRow) {
137 | line = session.getLine(row);
138 | var indent = line.search(/\S/);
139 | if (indent === -1)
140 | continue;
141 | if (startIndent > indent)
142 | break;
143 | var subRange = this.getFoldWidgetRange(session, "all", row);
144 | if (subRange) {
145 | if (subRange.start.row <= startRow) {
146 | break;
147 | }
148 | else if (subRange.isMultiLine()) {
149 | row = subRange.end.row;
150 | }
151 | else if (startIndent == indent) {
152 | break;
153 | }
154 | }
155 | endRow = row;
156 | }
157 | return new Range(startRow, startColumn, endRow, session.getLine(endRow).length);
158 | };
159 | this.getCommentRegionBlock = function (session, line, row) {
160 | var startColumn = line.search(/\s*$/);
161 | var maxRow = session.getLength();
162 | var startRow = row;
163 | var re = /^\s*(?:\/\*|\/\/|--)#?(end)?region\b/;
164 | var depth = 1;
165 | while (++row < maxRow) {
166 | line = session.getLine(row);
167 | var m = re.exec(line);
168 | if (!m)
169 | continue;
170 | if (m[1])
171 | depth--;
172 | else
173 | depth++;
174 | if (!depth)
175 | break;
176 | }
177 | var endRow = row;
178 | if (endRow > startRow) {
179 | return new Range(startRow, startColumn, endRow, line.length);
180 | }
181 | };
182 | }).call(FoldMode.prototype);
183 |
184 | });
185 |
186 | define("ace/mode/folding/sql",["require","exports","module","ace/lib/oop","ace/mode/folding/cstyle"], function(require, exports, module){"use strict";
187 | var oop = require("../../lib/oop");
188 | var BaseFoldMode = require("./cstyle").FoldMode;
189 | var FoldMode = exports.FoldMode = function () { };
190 | oop.inherits(FoldMode, BaseFoldMode);
191 | (function () {
192 | }).call(FoldMode.prototype);
193 |
194 | });
195 |
196 | define("ace/mode/sql",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/sql_highlight_rules","ace/mode/folding/sql"], function(require, exports, module){"use strict";
197 | var oop = require("../lib/oop");
198 | var TextMode = require("./text").Mode;
199 | var SqlHighlightRules = require("./sql_highlight_rules").SqlHighlightRules;
200 | var SqlFoldMode = require("./folding/sql").FoldMode;
201 | var Mode = function () {
202 | this.HighlightRules = SqlHighlightRules;
203 | this.foldingRules = new SqlFoldMode();
204 | this.$behaviour = this.$defaultBehaviour;
205 | };
206 | oop.inherits(Mode, TextMode);
207 | (function () {
208 | this.lineCommentStart = "--";
209 | this.blockComment = { start: "/*", end: "*/" };
210 | this.$id = "ace/mode/sql";
211 | this.snippetFileId = "ace/snippets/sql";
212 | }).call(Mode.prototype);
213 | exports.Mode = Mode;
214 |
215 | }); (function() {
216 | window.require(["ace/mode/sql"], function(m) {
217 | if (typeof module == "object" && typeof exports == "object" && module) {
218 | module.exports = m;
219 | }
220 | });
221 | })();
222 |
223 | ]==]
--------------------------------------------------------------------------------
/lua/blobsprofiler/client/vgui/vgui_bpdtree.lua:
--------------------------------------------------------------------------------
1 |
2 | local PANEL = {}
3 |
4 | AccessorFunc( PANEL, "m_bShowIcons", "ShowIcons" )
5 | AccessorFunc( PANEL, "m_iIndentSize", "IndentSize" )
6 | AccessorFunc( PANEL, "m_iLineHeight", "LineHeight" )
7 | AccessorFunc( PANEL, "m_pSelectedItem", "SelectedItem" )
8 | AccessorFunc( PANEL, "m_bClickOnDragHover", "ClickOnDragHover" )
9 |
10 | function PANEL:Init()
11 |
12 | --self:SetMouseInputEnabled( true )
13 | --self:SetClickOnDragHover( false )
14 |
15 | self:SetShowIcons( true )
16 | self:SetIndentSize( 14 )
17 | self:SetLineHeight( 17 )
18 | --self:SetPadding( 2 )
19 |
20 | self.RootNode = self:GetCanvas():Add( "BP_DTree_Node" )
21 | self.RootNode:SetRoot( self )
22 | self.RootNode:SetParentNode( self )
23 | self.RootNode:Dock( TOP )
24 | self.RootNode:SetText( "" )
25 | self.RootNode:SetExpanded( true, true )
26 | self.RootNode:DockMargin( 0, 4, 0, 0 )
27 |
28 | self:SetPaintBackground( true )
29 |
30 | end
31 |
32 | --
33 | -- Get the root node
34 | --
35 | function PANEL:Root()
36 | return self.RootNode
37 | end
38 |
39 | function PANEL:AddNode( strName, strIcon )
40 |
41 | return self.RootNode:AddNode( strName, strIcon )
42 |
43 | end
44 |
45 | function PANEL:ChildExpanded( bExpand )
46 |
47 | self:InvalidateLayout()
48 |
49 | end
50 |
51 | function PANEL:ShowIcons()
52 |
53 | return self.m_bShowIcons
54 |
55 | end
56 |
57 | function PANEL:ExpandTo( bExpand )
58 | end
59 |
60 | function PANEL:SetExpanded( bExpand )
61 |
62 | -- The top most node shouldn't react to this.
63 |
64 | end
65 |
66 | function PANEL:Clear()
67 | self:Root():Clear()
68 | end
69 |
70 | function PANEL:Paint( w, h )
71 |
72 | derma.SkinHook( "Paint", "Tree", self, w, h )
73 | return true
74 |
75 | end
76 |
77 | function PANEL:DoClick( node )
78 | return false
79 | end
80 |
81 | function PANEL:DoRightClick( node )
82 | return false
83 | end
84 |
85 | function PANEL:SetSelectedItem( node )
86 |
87 | if ( IsValid( self.m_pSelectedItem ) ) then
88 | self.m_pSelectedItem:SetSelected( false )
89 | end
90 |
91 | self.m_pSelectedItem = node
92 |
93 | if ( node ) then
94 | node:SetSelected( true )
95 | node:OnNodeSelected( node )
96 | end
97 |
98 | end
99 |
100 | function PANEL:OnNodeSelected( node )
101 | end
102 |
103 | function PANEL:MoveChildTo( child, pos )
104 |
105 | self:InsertAtTop( child )
106 |
107 | end
108 |
109 | function PANEL:LayoutTree()
110 |
111 | self:InvalidateChildren( true )
112 |
113 | end
114 |
115 | function PANEL:GenerateExample( ClassName, PropertySheet, Width, Height )
116 |
117 | local ctrl = vgui.Create( ClassName )
118 | --ctrl:SetPadding( 5 )
119 | ctrl:SetSize( 300, 300 )
120 |
121 | local node = ctrl:AddNode( "Node One" )
122 | local node = ctrl:AddNode( "Node Two" )
123 |
124 | local cnode = node:AddNode( "Node 2.1" )
125 | local cnode = node:AddNode( "Node 2.2" )
126 | local cnode = node:AddNode( "Node 2.3" )
127 | local cnode = node:AddNode( "Node 2.4" )
128 | local cnode = node:AddNode( "Node 2.5" )
129 | for i = 1, 64 do
130 | local gcnode = cnode:AddNode( "Node 2.5." .. i )
131 | end
132 | local cnode = node:AddNode( "Node 2.6" )
133 |
134 | local node = ctrl:AddNode( "Node Three ( Maps Folder )" )
135 | node:MakeFolder( "maps", "GAME" )
136 |
137 | local node = ctrl:AddNode( "Node Four" )
138 |
139 | PropertySheet:AddSheet( ClassName, ctrl, nil, true, true )
140 |
141 | end
142 |
143 | derma.DefineControl( "BP_DTree", "Tree View", PANEL, "DScrollPanel" )
--------------------------------------------------------------------------------
/lua/blobsprofiler/client/vgui/vgui_bpdtree_node.lua:
--------------------------------------------------------------------------------
1 |
2 | local PANEL = {}
3 |
4 | AccessorFunc( PANEL, "m_pRoot", "Root" )
5 |
6 | AccessorFunc( PANEL, "m_pParentNode", "ParentNode" )
7 |
8 | AccessorFunc( PANEL, "m_strFolder", "Folder" )
9 | AccessorFunc( PANEL, "m_strFileName", "FileName" )
10 | AccessorFunc( PANEL, "m_strPathID", "PathID" )
11 | AccessorFunc( PANEL, "m_strWildCard", "WildCard" )
12 | AccessorFunc( PANEL, "m_bNeedsPopulating", "NeedsPopulating" )
13 | AccessorFunc( PANEL, "m_bShowFiles", "ShowFiles", FORCE_BOOL )
14 |
15 | AccessorFunc( PANEL, "m_bDirty", "Dirty", FORCE_BOOL )
16 | AccessorFunc( PANEL, "m_bHideExpander", "HideExpander", FORCE_BOOL )
17 | AccessorFunc( PANEL, "m_bNeedsChildSearch", "NeedsChildSearch", FORCE_BOOL )
18 |
19 | AccessorFunc( PANEL, "m_bForceShowExpander", "ForceShowExpander", FORCE_BOOL )
20 | AccessorFunc( PANEL, "m_bDoubleClickToOpen", "DoubleClickToOpen", FORCE_BOOL )
21 |
22 | AccessorFunc( PANEL, "m_bLastChild", "LastChild", FORCE_BOOL )
23 | AccessorFunc( PANEL, "m_bDrawLines", "DrawLines", FORCE_BOOL )
24 | AccessorFunc( PANEL, "m_bExpanded", "Expanded", FORCE_BOOL )
25 | AccessorFunc( PANEL, "m_strDraggableName", "DraggableName" )
26 |
27 | function PANEL:Init()
28 |
29 | self:SetDoubleClickToOpen( true )
30 |
31 | self.Label = vgui.Create( "BP_DTree_Node_Button", self )
32 | self.Label:SetDragParent( self )
33 | self.Label.DoClick = function() self:InternalDoClick() end
34 | self.Label.DoDoubleClick = function() self:InternalDoClick() end
35 | self.Label.DoRightClick = function() self:InternalDoRightClick() end
36 | self.Label.DragHover = function( s, t ) self:DragHover( t ) end
37 |
38 | self.Expander = vgui.Create( "DExpandButton", self )
39 | self.Expander.DoClick = function() self:SetExpanded( !self:GetExpanded() ) end
40 | self.Expander:SetVisible( false )
41 |
42 | self.Icon = vgui.Create( "DImage", self )
43 | self.Icon:SetImage( "icon16/folder.png" )
44 | self.Icon:SizeToContents()
45 |
46 | self.animSlide = Derma_Anim( "Anim", self, self.AnimSlide )
47 |
48 | self.fLastClick = SysTime()
49 |
50 | self:SetDrawLines( false )
51 | self:SetLastChild( false )
52 |
53 | end
54 |
55 | function PANEL:IsFunc()
56 | self.Expander = vgui.Create("DCheckBox", self)
57 | self.Expander:SetTooltip("Toggle Profiling")
58 | end
59 |
60 | function PANEL:IsRootNode()
61 | return self.m_pRoot == self.m_pParentNode
62 | end
63 |
64 | function PANEL:InternalDoClick()
65 |
66 | self:GetRoot():SetSelectedItem( self )
67 |
68 | if ( self:DoClick() ) then return end
69 | if ( self:GetRoot():DoClick( self ) ) then return end
70 |
71 | if ( !self.m_bDoubleClickToOpen || ( SysTime() - self.fLastClick < 0.3 ) ) then
72 | self:SetExpanded( !self:GetExpanded() )
73 | end
74 |
75 | self.fLastClick = SysTime()
76 |
77 | end
78 |
79 | function PANEL:OnNodeSelected( node )
80 |
81 | local parent = self:GetParentNode()
82 | if ( IsValid( parent ) && parent.OnNodeSelected ) then
83 | parent:OnNodeSelected( node )
84 | end
85 |
86 | end
87 |
88 | function PANEL:OnNodeAdded( node )
89 | -- Called when Panel.AddNode is called on this node
90 | end
91 |
92 | function PANEL:InternalDoRightClick()
93 |
94 | if ( self:DoRightClick() ) then return end
95 | if ( self:GetRoot():DoRightClick( self ) ) then return end
96 |
97 | end
98 |
99 | function PANEL:DoClick()
100 | return false
101 | end
102 |
103 | function PANEL:DoRightClick()
104 | return false
105 | end
106 |
107 | function PANEL:Clear()
108 | if ( IsValid( self.ChildNodes ) ) then self.ChildNodes:Clear() end
109 | end
110 |
111 | function PANEL:AnimSlide( anim, delta, data )
112 |
113 | if ( !IsValid( self.ChildNodes ) ) then anim:Stop() return end
114 |
115 | if ( anim.Started ) then
116 | data.To = self:GetTall()
117 | data.Visible = self.ChildNodes:IsVisible()
118 | self.canExpand = false
119 | end
120 |
121 | if ( anim.Finished ) then
122 | self:InvalidateLayout()
123 | self.ChildNodes:SetVisible( data.Visible )
124 | self:SetTall( data.To )
125 | self:GetParentNode():ChildExpanded()
126 | self.canExpand = true
127 | return
128 | end
129 |
130 | self.ChildNodes:SetVisible( true )
131 |
132 | self:SetTall( Lerp( delta, data.From, data.To ) )
133 |
134 | self:GetParentNode():ChildExpanded()
135 |
136 | end
137 |
138 | function PANEL:SetIcon( str )
139 | if ( !str || str == "" ) then return end
140 |
141 | self.Icon:SetImage( str )
142 | end
143 |
144 | function PANEL:ShowIcons()
145 | return self:GetParentNode():ShowIcons()
146 | end
147 |
148 | function PANEL:GetLineHeight()
149 | return self:GetParentNode():GetLineHeight()
150 | end
151 |
152 | function PANEL:GetIndentSize()
153 | return self:GetParentNode():GetIndentSize()
154 | end
155 |
156 | function PANEL:SetText( strName )
157 |
158 | self.Label:SetText( strName )
159 |
160 | end
161 |
162 | function PANEL:ExpandRecurse( bExpand )
163 |
164 | self:SetExpanded( bExpand, true )
165 |
166 | if ( !IsValid( self.ChildNodes ) ) then return end
167 |
168 | for k, Child in pairs( self.ChildNodes:GetChildren() ) do
169 | if ( Child.ExpandRecurse ) then
170 | Child:ExpandRecurse( bExpand )
171 | end
172 | end
173 |
174 | end
175 |
176 | function PANEL:ExpandTo( bExpand )
177 |
178 | self:SetExpanded( bExpand, true )
179 | self:GetParentNode():ExpandTo( bExpand )
180 |
181 | end
182 |
183 | function PANEL:SetExpanded( bExpand, bSurpressAnimation )
184 |
185 | self:GetParentNode():ChildExpanded( bExpand )
186 | if self.Expander.SetExpanded then self.Expander:SetExpanded( bExpand ) end -- we use checkboxes for function profiling selection, this will error hehe
187 | self.m_bExpanded = bExpand
188 | self:InvalidateLayout( true )
189 |
190 | if ( !IsValid( self.ChildNodes ) ) then return end
191 |
192 | local StartTall = self:GetTall()
193 | self.animSlide:Stop()
194 |
195 | -- Populate the child folders..
196 | if ( bExpand && self:PopulateChildrenAndSelf( true ) ) then
197 | -- Could really do with a 'loading' thing here
198 | return
199 | end
200 |
201 | if ( IsValid( self.ChildNodes ) ) then
202 | self.ChildNodes:SetVisible( bExpand )
203 | if ( bExpand ) then
204 | self.ChildNodes:InvalidateLayout( true )
205 | end
206 | end
207 |
208 | self:InvalidateLayout( true )
209 |
210 | -- Do animation..
211 | if ( !bSurpressAnimation ) then
212 | self.animSlide:Start( 0.3, { From = StartTall } )
213 | self.animSlide:Run()
214 | end
215 |
216 | end
217 |
218 | function PANEL:ChildExpanded( bExpand )
219 |
220 | self.ChildNodes:InvalidateLayout( true )
221 | self:InvalidateLayout( true )
222 | self:GetParentNode():ChildExpanded( bExpand )
223 |
224 | end
225 |
226 | function PANEL:Paint()
227 | end
228 |
229 | function PANEL:HasChildren()
230 |
231 | if ( !IsValid( self.ChildNodes ) ) then return false end
232 | return self.ChildNodes:HasChildren()
233 |
234 | end
235 |
236 | function PANEL:DoChildrenOrder()
237 |
238 | if ( !IsValid( self.ChildNodes ) ) then return end
239 |
240 | local children = self.ChildNodes:GetChildren()
241 | local last = #children
242 |
243 | for i = 1, (last - 1) do
244 | children[i]:SetLastChild( false )
245 | end
246 | children[last]:SetLastChild( true )
247 |
248 | end
249 |
250 | function PANEL:PerformRootNodeLayout()
251 |
252 | self.Expander:SetVisible( false )
253 | self.Label:SetVisible( false )
254 | self.Icon:SetVisible( false )
255 |
256 | if ( IsValid( self.ChildNodes ) ) then
257 |
258 | self.ChildNodes:Dock( TOP )
259 | self:SetTall( self.ChildNodes:GetTall() )
260 |
261 | end
262 |
263 | end
264 |
265 | function PANEL:PerformLayout()
266 |
267 | if ( self:IsRootNode() ) then
268 | return self:PerformRootNodeLayout()
269 | end
270 |
271 | if ( self.animSlide:Active() ) then return end
272 |
273 | local LineHeight = self:GetLineHeight()
274 |
275 | if ( self.m_bHideExpander ) then
276 |
277 | self.Expander:SetPos( -11, 0 )
278 | self.Expander:SetSize( 15, 15 )
279 | self.Expander:SetVisible( false )
280 |
281 | else
282 |
283 | self.Expander:SetPos( 2, 0 )
284 | self.Expander:SetSize( 15, 15 )
285 | self.Expander:SetVisible( self.NeedsLazyLoad || self:HasChildren() || self:GetForceShowExpander() )
286 | self.Expander:SetZPos( 10 )
287 |
288 | end
289 |
290 | self.Label:StretchToParent( 0, nil, 0, nil )
291 | self.Label:SetTall( LineHeight )
292 |
293 | if ( self:ShowIcons() ) then
294 | self.Icon:SetVisible( true )
295 | self.Icon:SetPos( self.Expander.x + self.Expander:GetWide() + 4, ( LineHeight - self.Icon:GetTall() ) * 0.5 )
296 | self.Label:SetTextInset( self.Icon.x + self.Icon:GetWide() + 4, 0 )
297 | else
298 | self.Icon:SetVisible( false )
299 | self.Label:SetTextInset( self.Expander.x + self.Expander:GetWide() + 4, 0 )
300 | end
301 |
302 | if ( !IsValid( self.ChildNodes ) || !self.ChildNodes:IsVisible() ) then
303 | self:SetTall( LineHeight )
304 | return
305 | end
306 |
307 | self.ChildNodes:SizeToContents()
308 | self:SetTall( LineHeight + self.ChildNodes:GetTall() )
309 |
310 | self.ChildNodes:StretchToParent( LineHeight, LineHeight, 0, 0 )
311 |
312 | self:DoChildrenOrder()
313 |
314 | end
315 |
316 | function PANEL:CreateChildNodes()
317 |
318 | if ( IsValid( self.ChildNodes ) ) then return end
319 |
320 | self.ChildNodes = vgui.Create( "DListLayout", self )
321 | self.ChildNodes:SetDropPos( "852" )
322 | self.ChildNodes:SetVisible( self:GetExpanded() )
323 | self.ChildNodes.OnChildRemoved = function()
324 |
325 | self.ChildNodes:InvalidateLayout()
326 |
327 | -- Root node should never be closed
328 | if ( !self.ChildNodes:HasChildren() && !self:IsRootNode() ) then
329 | self:SetExpanded( false )
330 | end
331 |
332 | end
333 |
334 | self.ChildNodes.OnModified = function()
335 |
336 | self:OnModified()
337 |
338 | end
339 |
340 | self:InvalidateLayout()
341 |
342 | end
343 |
344 | function PANEL:AddPanel( pPanel )
345 |
346 | self:CreateChildNodes()
347 |
348 | self.ChildNodes:Add( pPanel )
349 | self:InvalidateLayout()
350 |
351 | end
352 |
353 | function PANEL:AddNode( strName, strIcon )
354 |
355 | self:CreateChildNodes()
356 |
357 | local pNode = vgui.Create( "BP_DTree_Node", self )
358 | pNode:SetText( strName )
359 | pNode:SetParentNode( self )
360 | pNode:SetTall( self:GetLineHeight() )
361 | pNode:SetRoot( self:GetRoot() )
362 | pNode:SetIcon( strIcon )
363 | pNode:SetDrawLines( !self:IsRootNode() )
364 |
365 | self:InstallDraggable( pNode )
366 |
367 | self.ChildNodes:Add( pNode )
368 | self:InvalidateLayout()
369 |
370 | -- Let addons do whatever they need
371 | self:OnNodeAdded( pNode )
372 |
373 | return pNode
374 |
375 | end
376 |
377 | function PANEL:InsertNode( pNode )
378 |
379 | self:CreateChildNodes()
380 |
381 | pNode:SetParentNode( self )
382 | pNode:SetRoot( self:GetRoot() )
383 | self:InstallDraggable( pNode )
384 |
385 | self.ChildNodes:Add( pNode )
386 | self:InvalidateLayout()
387 |
388 | return pNode
389 |
390 | end
391 |
392 | function PANEL:InstallDraggable( pNode )
393 |
394 | local DragName = self:GetDraggableName()
395 | if ( !DragName ) then return end
396 |
397 | -- Make this node draggable
398 | pNode:SetDraggableName( DragName )
399 | pNode:Droppable( DragName )
400 |
401 | -- Allow item dropping onto us
402 | self.ChildNodes:MakeDroppable( DragName, true )
403 |
404 | end
405 |
406 | function PANEL:DroppedOn( pnl )
407 |
408 | self:InsertNode( pnl )
409 | self:SetExpanded( true )
410 |
411 | end
412 |
413 | function PANEL:AddFolder( strName, strFolder, strPath, bShowFiles, strWildCard, bDontForceExpandable )
414 |
415 | local node = self:AddNode( strName )
416 | node:MakeFolder( strFolder, strPath, bShowFiles, strWildCard, bDontForceExpandable )
417 | return node
418 |
419 | end
420 |
421 | function PANEL:MakeFolder( strFolder, strPath, bShowFiles, strWildCard, bDontForceExpandable )
422 |
423 | -- Store the data
424 | self:SetNeedsPopulating( true )
425 | self:SetWildCard( strWildCard || "*" )
426 | self:SetFolder( strFolder )
427 | self:SetPathID( strPath )
428 | self:SetShowFiles( bShowFiles || false )
429 |
430 | self:CreateChildNodes()
431 | self:SetNeedsChildSearch( true )
432 |
433 | if ( !bDontForceExpandable ) then
434 | self:SetForceShowExpander( true )
435 | end
436 |
437 | -- If the parent is already open, populate myself. Do not require the user to collapse and expand for this to happen
438 | if ( self:GetParentNode():GetExpanded() ) then
439 | -- Yuck! This is basically a hack for gameprops.lua
440 | timer.Simple( 0, function()
441 | if ( !IsValid( self ) ) then return end
442 | self:PopulateChildrenAndSelf()
443 | end )
444 | end
445 |
446 | end
447 |
448 | function PANEL:FilePopulateCallback( files, folders, foldername, path, bAndChildren, wildcard )
449 |
450 | local showfiles = self:GetShowFiles()
451 |
452 | self.ChildNodes:InvalidateLayout( true )
453 |
454 | local FileCount = 0
455 |
456 | if ( folders ) then
457 |
458 | for k, File in SortedPairsByValue( folders ) do
459 |
460 | local Node = self:AddNode( File )
461 | Node:MakeFolder( string.Trim( foldername .. "/" .. File, "/" ), path, showfiles, wildcard, true )
462 | FileCount = FileCount + 1
463 |
464 | end
465 |
466 | end
467 |
468 | if ( showfiles ) then
469 |
470 | for k, File in SortedPairs( files ) do
471 |
472 | local icon = "icon16/page_white.png"
473 |
474 | local Node = self:AddNode( File, icon )
475 | Node:SetFileName( string.Trim( foldername .. "/" .. File, "/" ) )
476 | FileCount = FileCount + 1
477 |
478 | end
479 |
480 | end
481 |
482 | if ( FileCount == 0 ) then
483 | self.ChildNodes:Remove()
484 | self.ChildNodes = nil
485 |
486 | self:SetNeedsPopulating( false )
487 | self:SetShowFiles( nil )
488 | self:SetWildCard( nil )
489 |
490 | self:InvalidateLayout()
491 |
492 | self.Expander:SetExpanded( true )
493 |
494 | return
495 | end
496 |
497 | self:InvalidateLayout()
498 |
499 | end
500 |
501 | function PANEL:FilePopulate( bAndChildren, bExpand )
502 |
503 | if ( !self:GetNeedsPopulating() ) then return end
504 |
505 | local folder = self:GetFolder()
506 | local path = self:GetPathID()
507 | local wildcard = self:GetWildCard()
508 |
509 | if ( !folder || !wildcard || !path ) then return false end
510 |
511 | local files, folders = file.Find( string.Trim( folder .. "/" .. wildcard, "/" ), path )
512 | if ( folders && folders[ 1 ] == "/" ) then table.remove( folders, 1 ) end
513 |
514 | self:SetNeedsPopulating( false )
515 | self:SetNeedsChildSearch( false )
516 |
517 | self:FilePopulateCallback( files, folders, folder, path, bAndChildren, wildcard )
518 |
519 | if ( bExpand ) then
520 | self:SetExpanded( true )
521 | end
522 |
523 | return true
524 |
525 | end
526 |
527 | function PANEL:PopulateChildren()
528 |
529 | if ( !IsValid( self.ChildNodes ) ) then return end
530 |
531 | for k, v in ipairs( self.ChildNodes:GetChildren() ) do
532 | timer.Simple( k * 0.1, function()
533 |
534 | if ( IsValid( v ) ) then
535 | v:FilePopulate( false )
536 | end
537 |
538 | end )
539 |
540 | end
541 |
542 | end
543 |
544 | function PANEL:PopulateChildrenAndSelf( bExpand )
545 |
546 | -- Make sure we're populated
547 | if ( self:FilePopulate( true, bExpand ) ) then return true end
548 |
549 | self:PopulateChildren()
550 |
551 | end
552 |
553 | function PANEL:SetSelected( b )
554 |
555 | self.Label:SetSelected( b )
556 | self.Label:InvalidateLayout()
557 |
558 | end
559 |
560 | function PANEL:Think()
561 |
562 | self.animSlide:Run()
563 |
564 | end
565 |
566 | --
567 | -- DragHoverClick
568 | --
569 | function PANEL:DragHoverClick( HoverTime )
570 |
571 | if ( !self:GetExpanded() ) then
572 | self:SetExpanded( true )
573 | end
574 |
575 | if ( self:GetRoot():GetClickOnDragHover() ) then
576 |
577 | self:InternalDoClick()
578 |
579 | end
580 |
581 | end
582 |
583 | function PANEL:MoveToTop()
584 |
585 | local parent = self:GetParentNode()
586 | if ( !IsValid( parent ) ) then return end
587 |
588 | self:GetParentNode():MoveChildTo( self, 1 )
589 |
590 | end
591 |
592 | function PANEL:MoveChildTo( child )
593 |
594 | self.ChildNodes:InsertAtTop( child )
595 |
596 | end
597 |
598 | function PANEL:GetText()
599 | return self.Label:GetText()
600 | end
601 |
602 | function PANEL:GetIcon()
603 | return self.Icon:GetImage()
604 | end
605 |
606 | function PANEL:CleanList()
607 |
608 | for k, panel in pairs( self.Items ) do
609 |
610 | if ( !IsValid( panel ) || panel:GetParent() != self.pnlCanvas ) then
611 | self.Items[k] = nil
612 | end
613 |
614 | end
615 |
616 | end
617 |
618 | function PANEL:Insert( pNode, pNodeNextTo, bBefore )
619 |
620 | pNode:SetParentNode( self )
621 | pNode:SetRoot( self:GetRoot() )
622 |
623 | self:CreateChildNodes()
624 |
625 | if ( bBefore ) then
626 | self.ChildNodes:InsertBefore( pNodeNextTo, pNode )
627 | else
628 | self.ChildNodes:InsertAfter( pNodeNextTo, pNode )
629 | end
630 |
631 | self:InvalidateLayout()
632 |
633 | end
634 |
635 | function PANEL:LeaveTree( pnl )
636 |
637 | self.ChildNodes:RemoveItem( pnl, true )
638 | self:InvalidateLayout()
639 |
640 | end
641 |
642 | function PANEL:OnModified()
643 |
644 | -- Override Me
645 |
646 | end
647 |
648 | function PANEL:GetChildNode( iNum )
649 |
650 | if ( !IsValid( self.ChildNodes ) ) then return end
651 | return self.ChildNodes:GetChild( iNum )
652 |
653 | end
654 |
655 | function PANEL:GetChildNodes()
656 |
657 | if ( !IsValid( self.ChildNodes ) ) then return {} end
658 | return self.ChildNodes:GetChildren()
659 |
660 | end
661 |
662 | function PANEL:GetChildNodeCount()
663 |
664 | if ( !IsValid( self.ChildNodes ) ) then return 0 end
665 | return self.ChildNodes:ChildCount()
666 |
667 | end
668 |
669 | function PANEL:Paint( w, h )
670 |
671 | derma.SkinHook( "Paint", "TreeNode", self, w, h )
672 |
673 | end
674 |
675 | function PANEL:Copy()
676 |
677 | local copy = vgui.Create( "BP_DTree_Node", self:GetParent() )
678 | copy:SetText( self:GetText() )
679 | copy:SetIcon( self:GetIcon() )
680 | copy:SetRoot( self:GetRoot() )
681 | copy:SetParentNode( self:GetParentNode() )
682 |
683 | if ( self.ChildNodes ) then
684 |
685 | for k, v in ipairs( self.ChildNodes:GetChildren() ) do
686 |
687 | local childcopy = v:Copy()
688 | copy:InsertNode( childcopy )
689 |
690 | end
691 |
692 | end
693 |
694 | self:SetupCopy( copy )
695 |
696 | return copy
697 |
698 | end
699 |
700 | function PANEL:SetupCopy( copy )
701 |
702 | -- TODO.
703 |
704 | end
705 |
706 | derma.DefineControl( "BP_DTree_Node", "Tree Node", PANEL, "DPanel" )
--------------------------------------------------------------------------------
/lua/blobsprofiler/client/vgui/vgui_bpdtree_node_button.lua:
--------------------------------------------------------------------------------
1 |
2 | local PANEL = {}
3 |
4 | function PANEL:Init()
5 |
6 | self:SetTextInset( 32, 0 )
7 | self:SetContentAlignment( 4 )
8 |
9 | end
10 |
11 | function PANEL:Paint( w, h )
12 |
13 | derma.SkinHook( "Paint", "TreeNodeButton", self, w, h )
14 |
15 | --
16 | -- Draw the button text
17 | --
18 | return false
19 |
20 | end
21 |
22 | function PANEL:UpdateColours( skin )
23 |
24 | if ( self:IsSelected() ) then return self:SetTextStyleColor( skin.Colours.Tree.Selected ) end
25 | if ( self.Hovered ) then return self:SetTextStyleColor( skin.Colours.Tree.Hover ) end
26 |
27 | return self:SetTextStyleColor( skin.Colours.Tree.Normal )
28 |
29 | end
30 |
31 | function PANEL:GenerateExample()
32 |
33 | -- Do nothing!
34 |
35 | end
36 |
37 | derma.DefineControl( "BP_DTree_Node_Button", "Tree Node Button", PANEL, "DButton" )
--------------------------------------------------------------------------------
/lua/blobsprofiler/server/sv_blobsprofiler.lua:
--------------------------------------------------------------------------------
1 | print("blobsProfiler - SV INIT")
2 |
3 | util.AddNetworkString("blobsProfiler:requestSource")
4 | util.AddNetworkString("blobsProfiler:sendSourceChunk")
5 |
6 | util.AddNetworkString("blobsProfiler:requestData")
7 |
8 | local function SendChunkedString(ply, requestID, largeString, highlightLine)
9 | local chunkSize = 30000 -- If changed, don't forget to change on the client!
10 | local totalLength = #largeString
11 |
12 | for i = 1, totalLength, chunkSize do
13 | local chunk = largeString:sub(i, i + chunkSize - 1)
14 | net.Start("blobsProfiler:sendSourceChunk")
15 | net.WriteString(requestID)
16 | net.WriteUInt(i, 32)
17 | net.WriteString(chunk)
18 | net.WriteUInt(highlightLine or 0, 16) -- ugh
19 | net.Send(ply)
20 | end
21 | end
22 |
23 | net.Receive("blobsProfiler:requestSource", function(l, ply)
24 | if not blobsProfiler.CanAccess(ply, "requestSource") then return end
25 |
26 | local filePath = net.ReadString()
27 | local startLine = net.ReadUInt(16)
28 | local endLine = net.ReadUInt(16)
29 |
30 | local highlightLine
31 | if startLine ~= 0 and endLine == 0 then
32 | highlightLine = startLine
33 | startLine = 0
34 | endLine = 0
35 | end
36 |
37 | if startLine < 0 or endLine < 0 or startLine > endLine then return end
38 |
39 | local findFile = file.Open(filePath, "r", "GAME")
40 | if not findFile then
41 | blobsProfiler.Log(blobsProfiler.L_NH_ERROR, "Cannot find file: " .. filePath)
42 | return
43 | end
44 |
45 | local fileContent = {}
46 | local currentLine = 1
47 | local readWholeFile = (startLine == 0 and endLine == 0)
48 |
49 | if startLine == 0 then
50 | startLine = 1
51 | end
52 |
53 | while not findFile:EndOfFile() do
54 | local line = findFile:ReadLine()
55 |
56 | if readWholeFile or (currentLine >= startLine and currentLine <= endLine) then
57 | table.insert(fileContent, line)
58 | end
59 |
60 | if not readWholeFile and currentLine > endLine then
61 | break
62 | end
63 |
64 | currentLine = currentLine + 1
65 | end
66 |
67 | findFile:Close()
68 |
69 | local combinedSource = table.concat(fileContent)
70 | local requestID = string.format("View source: %s (Lines: %i-%i)", filePath, startLine, (endLine == 0 and currentLine) or endLine) -- Generate request ID
71 | SendChunkedString(ply, requestID, combinedSource, highlightLine)
72 | end)
73 |
74 | local transmissionStates = {}
75 |
76 | local function sendDataToClient(ply, moduleName, dataTbl)
77 | if transmissionStates[ply] and transmissionStates[ply][moduleName] then
78 | return
79 | end
80 |
81 | local data = util.Compress(util.TableToJSON(dataTbl))
82 | local totalChunks = math.ceil(#data / blobsProfiler.svDataChunkSize)
83 |
84 | transmissionStates[ply] = transmissionStates[ply] or {}
85 | transmissionStates[ply][moduleName] = {
86 | data = data,
87 | totalChunks = totalChunks,
88 | currentChunk = 1
89 | }
90 |
91 | local function sendNextChunk()
92 | if not IsValid(ply) then return end
93 | local state = transmissionStates[ply][moduleName]
94 | if not state then return end
95 |
96 | local startIdx = (state.currentChunk - 1) * blobsProfiler.svDataChunkSize + 1
97 | local endIdx = math.min(startIdx + blobsProfiler.svDataChunkSize - 1, #state.data)
98 | local chunk = string.sub(state.data, startIdx, endIdx)
99 |
100 | net.Start("blobsProfiler:requestData")
101 | net.WriteString(moduleName)
102 | net.WriteUInt(state.totalChunks, 16)
103 | net.WriteUInt(state.currentChunk, 16)
104 | net.WriteData(chunk, #chunk)
105 | net.Send(ply)
106 |
107 | state.currentChunk = state.currentChunk + 1
108 | if state.currentChunk > state.totalChunks then
109 | transmissionStates[ply][moduleName] = nil
110 | else
111 | timer.Simple(0.1, sendNextChunk)
112 | end
113 | end
114 |
115 | sendNextChunk()
116 | end
117 |
118 | local function transformModuleNameForPermissionsCheck(moduleName)
119 | -- Example: Takes 'Lua.Globals' and turns it into {'Lua', 'Lua_Globals'}
120 | local part1, part2 = string.match(moduleName, "([^%.]+)%.(.+)")
121 |
122 | if part1 and part2 then
123 | return { part1, part1 .. "_" .. part2 }
124 | else
125 | return { moduleName }
126 | end
127 | end
128 |
129 | blobsProfiler.SendModuleData = function(rawModuleName, ply)
130 | if not blobsProfiler.CanAccess(ply, "serverData") then return end
131 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "requestData SV: ".. rawModuleName)
132 |
133 | local permStrs = transformModuleNameForPermissionsCheck(rawModuleName)
134 | for k, v in ipairs(permStrs) do
135 | if not blobsProfiler.CanAccess(ply, "serverData"..v) then return end -- Passing 'Lua.Globals' will check 'serverData_Lua' & 'serverData_Lua_Globals'
136 | end
137 |
138 | local moduleTable, parentModuleTable = blobsProfiler.GetModule(rawModuleName)
139 |
140 | local dataTbl
141 |
142 | if moduleTable and moduleTable.PrepServerData then
143 | dataTbl = moduleTable:PrepServerData()
144 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "Module: ".. rawModuleName .. " PrepServerData() called!")
145 | end
146 |
147 | if not dataTbl then
148 | blobsProfiler.Log(blobsProfiler.L_NH_ERROR, "Module: ".. rawModuleName .." did not return data in PrepServerData()!")
149 | dataTbl = {}
150 | end
151 |
152 | sendDataToClient(ply, rawModuleName, dataTbl)
153 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "Module: ".. rawModuleName .." data sent to client!")
154 | end
155 |
156 | net.Receive("blobsProfiler:requestData", function(l, ply)
157 | local rawModuleName = net.ReadString()
158 |
159 | blobsProfiler.SendModuleData(rawModuleName, ply)
160 | end)
--------------------------------------------------------------------------------
/lua/blobsprofiler/shared/modules/bp_concommands.lua:
--------------------------------------------------------------------------------
1 | blobsProfiler.RegisterModule("ConCommands", {
2 | Icon = "icon16/application_xp_terminal.png",
3 | OrderPriority = 4,
4 | UpdateRealmData = function(luaState)
5 | if luaState == "Client" then
6 | local concmdTbl = concommand.GetTable()
7 | local concommandsData = {}
8 |
9 | for ccName, ccFunc in pairs(concmdTbl) do
10 | ccName = tostring(ccName)
11 |
12 | local debugInfo = debug.getinfo(ccFunc)
13 | concommandsData[ccName] = debugInfo
14 | concommandsData[ccName].func = ccFunc
15 |
16 | concommandsData[ccName].fakeVarType = "function"
17 | end
18 |
19 | blobsProfiler.Client.ConCommands = concommandsData
20 | else
21 | net.Start("blobsProfiler:requestData")
22 | net.WriteString("ConCommands")
23 | net.SendToServer()
24 | end
25 | end,
26 | PrepServerData = function()
27 | local concmdTbl = concommand.GetTable()
28 | local concommandsData = {}
29 |
30 | for ccName, ccFunc in pairs(concmdTbl) do
31 | ccName = tostring(ccName)
32 |
33 | local debugInfo = debug.getinfo(ccFunc)
34 | concommandsData[ccName] = debugInfo
35 | concommandsData[ccName].func = tostring(ccFunc)
36 |
37 | concommandsData[ccName].fakeVarType = "function"
38 | end
39 |
40 | return concommandsData
41 | end,
42 | PreloadClient = true,
43 | PreloadServer = false,
44 | BuildPanel = function(luaState, parentPanel)
45 | blobsProfiler.buildDTree(luaState, parentPanel, "ConCommands")
46 | end,
47 | RefreshButton = "Re-scan"
48 | })
--------------------------------------------------------------------------------
/lua/blobsprofiler/shared/modules/bp_errors.lua:
--------------------------------------------------------------------------------
1 | blobsProfiler.RegisterModule("Errors", {
2 | Icon = "icon16/bug.png",
3 | OrderPriority = 9,
4 | UpdateRealmData = function(luaState)
5 | if luaState == "Client" then
6 | -- we dont need to set, it's automatically set by the OnLuaError hook
7 | else
8 | net.Start("blobsProfiler:requestData")
9 | net.WriteString("Errors")
10 | net.SendToServer()
11 | end
12 | end,
13 | PrepServerData = function()
14 | return blobsProfiler.Server and blobsProfiler.Server.Errors or {}
15 | end,
16 | PreloadClient = true,
17 | PreloadServer = false,
18 | BuildPanel = function(luaState, parentPanel)
19 | blobsProfiler.buildDTree(luaState, parentPanel, "Errors")
20 | end,
21 | OnOpen = function(luaState, parentPanel)
22 | if luaState == "Client" then
23 | blobsProfiler.buildDTree(luaState, parentPanel, "Errors")
24 | end
25 | end,
26 | RefreshButton = "Reload",
27 | FormatNodeName = function(luaState, nodeKey, nodeValue)
28 | if nodeKey == "Last Errored" or nodeKey == "First Errored" then
29 | return nodeKey .. ": ".. os.date("%c", nodeValue)
30 | elseif not istable(nodeValue) then
31 | return nodeKey .. ": ".. tostring(nodeValue)
32 | elseif istable(nodeValue) and nodeValue.Error then
33 | return nodeValue.Error
34 | elseif istable(nodeValue) and nodeValue.fakeVarType == "file" then
35 | return "[" .. nodeValue.Level .. "] " .. nodeValue.Source .. ":" ..nodeValue.Line
36 | end
37 |
38 | return nodeKey
39 | end,
40 | FormatNodeIcon = function(luaState, nodeKey, nodeValue)
41 | if nodeKey == "Last Errored" or nodeKey == "First Errored" then
42 | return "icon16/clock.png"
43 | elseif nodeKey == "Count" then
44 | return "icon16/chart_bar.png"
45 | elseif nodeKey == "Stacktrace" then
46 | return "icon16/script_error.png"
47 | elseif nodeKey == "Addon ID" then
48 | return "icon16/plugin_link.png"
49 | elseif istable(nodeValue) and nodeValue.Stacktrace and nodeValue.Count then
50 | return "icon16/bug_error.png"
51 | elseif istable(nodeValue) and nodeValue.fakeVarType == "file" then
52 | return "icon16/page_white_code.png"
53 | end
54 | end
55 | })
56 |
57 | local function capitalizeFirstLetter(str)
58 | return (str and str:sub(1, 1):upper() .. str:sub(2):lower()) or str -- are you shitting me
59 | end
60 |
61 | hook.Add("OnLuaError", "test", function( err, realm, stack, name, addon_id )
62 | local luaState = capitalizeFirstLetter(realm)
63 |
64 | name = name or "Unknown Origin"
65 | err = err or "Unknown Error" -- should this ever happen?
66 |
67 | local errorKey = util.CRC(err .. "\n" .. (stack and util.TableToJSON(stack) or "No Stack Trace"))
68 |
69 | blobsProfiler[luaState] = blobsProfiler[luaState] or {}
70 | blobsProfiler[luaState].Errors = blobsProfiler[luaState].Errors or {}
71 | blobsProfiler[luaState].Errors[name] = blobsProfiler[luaState].Errors[name] or {}
72 |
73 | if not blobsProfiler[luaState].Errors[name][errorKey] then
74 | local genStack
75 | for k, stackDetails in ipairs(stack) do
76 | genStack = genStack or {}
77 |
78 | local stackData = {}
79 | stackData.fakeVarType = "file"
80 | stackData.Level = k
81 | stackData.Func = tostring(stackDetails.Function)
82 | stackData.Source = stackDetails.File
83 | stackData.Line = stackDetails.Line
84 |
85 | table.insert(genStack, stackData)
86 | end
87 |
88 | blobsProfiler[luaState].Errors[name][errorKey] = {
89 | Error = err,
90 | Count = 1,
91 | Stacktrace = genStack,
92 | ["Addon ID"] = (tonumber(addon_id) ~= 0) and addon_id or nil,
93 | ["First Errored"] = os.time()
94 | }
95 | else
96 | blobsProfiler[luaState].Errors[name][errorKey].Count = blobsProfiler[luaState].Errors[name][errorKey].Count + 1
97 | blobsProfiler[luaState].Errors[name][errorKey]["Last Errored"] = os.time()
98 | end
99 | end)
--------------------------------------------------------------------------------
/lua/blobsprofiler/shared/modules/bp_files.lua:
--------------------------------------------------------------------------------
1 | local localIncludedFiles = {}
2 |
3 | local function normalizePath(filePath)
4 | return string.gsub(filePath, '\\', '/')
5 | end
6 |
7 | local scanNewFiles = {}
8 | -- Function to recursively build nested tables for directories
9 | local function addFileToTable(directoryTable, filePath)
10 | local parts = string.Explode("/", filePath)
11 | local currentTable = directoryTable
12 |
13 | for i = 1, #parts - 1 do
14 | local part = parts[i]
15 | currentTable[part] = currentTable[part] or {}
16 | currentTable = currentTable[part]
17 | end
18 |
19 | if not table.HasValue(currentTable, parts[#parts]) then
20 | table.insert(currentTable, parts[#parts])
21 | table.insert(scanNewFiles, filePath)
22 | end
23 | end
24 |
25 | local function scanTable(tbl, visited)
26 | for key, value in pairs(tbl) do
27 | if type(value) == "function" then
28 | local info = debug.getinfo(value, "S")
29 | if info and info.source and info.source:sub(1, 1) == "@" then
30 | local fullPath = info.source:sub(2)
31 | if string.EndsWith(fullPath, ".lua") then
32 | addFileToTable(localIncludedFiles, fullPath)
33 | end
34 | end
35 | elseif type(value) == "table" and not visited[value] then
36 | visited[value] = true
37 | scanTable(value, visited)
38 | end
39 | end
40 | end
41 |
42 | blobsProfiler.ScanGLoadedFiles = function()
43 | scanNewFiles = {}
44 |
45 | local visited = {}
46 | visited[_G] = true
47 | scanTable(_G, visited)
48 |
49 | return localIncludedFiles, scanNewFiles
50 | end
51 |
52 | blobsProfiler.RegisterModule("Files", {
53 | Icon = "icon16/folder_page.png",
54 | OrderPriority = 5,
55 | UpdateRealmData = function(luaState)
56 | if luaState == "Client" then
57 | local allFiles, newFiles = blobsProfiler.ScanGLoadedFiles()
58 |
59 | blobsProfiler.Client.Files = allFiles
60 | else
61 | net.Start("blobsProfiler:requestData")
62 | net.WriteString("Files")
63 | net.SendToServer()
64 | end
65 | end,
66 | PrepServerData = function()
67 | local allFiles, newFiles = blobsProfiler.ScanGLoadedFiles()
68 |
69 | return allFiles
70 | end,
71 | PreloadClient = true,
72 | PreloadServer = false,
73 | BuildPanel = function(luaState, parentPanel)
74 | blobsProfiler.buildDTree(luaState, parentPanel, "Files")
75 | end,
76 | RefreshButton = "Re-scan",
77 | RCFunctions = {
78 | ["table"] = {
79 | {
80 | name = "Expand/Collapse",
81 | func = function(ref, node)
82 | node:ExpandRecurse(not node:GetExpanded())
83 | -- it's ok to use the ExpandRecurse here, not as bad as global tables.
84 | end,
85 | icon = "icon16/folder_explore.png"
86 | },
87 | {
88 | name = "Print path",
89 | func = function(ref, node)
90 | local path = ""
91 | local function apparentParentDir(child)
92 | if child.parentNode then
93 | path = child.Label:GetText() .. (path ~= "" and "/" or "") .. path
94 | apparentParentDir(child.parentNode)
95 | else
96 | path = child:GetText() .. "/" .. path
97 | end
98 | end
99 |
100 | apparentParentDir(node)
101 |
102 | print(path)
103 | end,
104 | icon = "icon16/application_osx_terminal.png"
105 | },
106 | {
107 | name = "Copy path",
108 | func = function(ref, node)
109 | local path = ""
110 | local function apparentParentDir(child)
111 | if child.parentNode then
112 | path = child.Label:GetText() .. (path ~= "" and "/" or "") .. path
113 | apparentParentDir(child.parentNode)
114 | else
115 | path = child:GetText() .. "/" .. path
116 | end
117 | end
118 |
119 | apparentParentDir(node)
120 |
121 | SetClipboardText(path)
122 | end,
123 | icon = "icon16/page_copy.png"
124 | }
125 | },
126 | ["string"] = {
127 | {
128 | name = "View source",
129 | func = function(ref, node)
130 | local path = ""
131 | local function apparentParentDir(child)
132 | if child.parentNode then
133 | path = child.Label:GetText() .. (path ~= "" and "/" or "") .. path
134 | apparentParentDir(child.parentNode)
135 | else
136 | path = child:GetText() .. "/" .. path
137 | end
138 | end
139 |
140 | apparentParentDir(node)
141 |
142 | net.Start("blobsProfiler:requestSource")
143 | net.WriteString(path)
144 | net.SendToServer()
145 | end,
146 | icon = "icon16/script_code.png"
147 | },
148 | {
149 | name = "Print path",
150 | func = function(ref, node)
151 | local path = ""
152 | local function apparentParentDir(child)
153 | if child.parentNode then
154 | path = child.Label:GetText() .. (path ~= "" and "/" or "") .. path
155 | apparentParentDir(child.parentNode)
156 | else
157 | path = child:GetText() .. "/" .. path
158 | end
159 | end
160 |
161 | apparentParentDir(node)
162 |
163 | print(path)
164 | end,
165 | icon = "icon16/application_osx_terminal.png"
166 | },
167 | {
168 | name = "Copy path",
169 | func = function(ref, node)
170 | local path = ""
171 | local function apparentParentDir(child)
172 | if child.parentNode then
173 | path = child.Label:GetText() .. (path ~= "" and "/" or "") .. path
174 | apparentParentDir(child.parentNode)
175 | else
176 | path = child:GetText() .. "/" .. path
177 | end
178 | end
179 |
180 | apparentParentDir(node)
181 |
182 | SetClipboardText(path)
183 | end,
184 | icon = "icon16/page_copy.png"
185 | }
186 | }
187 | },
188 | FormatNodeName = function(luaState, nodeKey, nodeValue)
189 | return istable(nodeValue) and nodeKey or nodeValue
190 | end
191 | })
--------------------------------------------------------------------------------
/lua/blobsprofiler/shared/modules/bp_hooks.lua:
--------------------------------------------------------------------------------
1 | blobsProfiler.RegisterModule("Hooks", {
2 | Icon = "icon16/brick_add.png",
3 | OrderPriority = 3,
4 | UpdateRealmData = function(luaState)
5 | if luaState == "Client" then
6 | local hooksTable = {}
7 | for hookName, hookEvents in pairs(hook.GetTable()) do
8 | hookName = tostring(hookName)
9 |
10 | hooksTable[hookName] = hooksTable[hookName] or {}
11 | for eventName, eventFunc in pairs(hookEvents) do
12 | eventName = tostring(eventName)
13 |
14 | local debugInfo = debug.getinfo(eventFunc)
15 | hooksTable[hookName][eventName] = debugInfo
16 | hooksTable[hookName][eventName].func = eventFunc
17 |
18 | hooksTable[hookName][eventName].fakeVarType = "function"
19 | end
20 | end
21 |
22 | blobsProfiler.Client.Hooks = hooksTable
23 | else
24 | net.Start("blobsProfiler:requestData")
25 | net.WriteString("Hooks")
26 | net.SendToServer()
27 | end
28 | end,
29 | PrepServerData = function()
30 | local hooksTable = {}
31 | for hookName, hookEvents in pairs(hook.GetTable()) do
32 | hookName = tostring(hookName)
33 |
34 | hooksTable[hookName] = hooksTable[hookName] or {}
35 | for eventName, eventFunc in pairs(hookEvents) do
36 | eventName = tostring(eventName)
37 |
38 | local debugInfo = debug.getinfo(eventFunc)
39 | hooksTable[hookName][eventName] = debugInfo
40 | hooksTable[hookName][eventName].func = tostring(eventFunc)
41 |
42 | hooksTable[hookName][eventName].fakeVarType = "function"
43 | end
44 | end
45 |
46 | return hooksTable
47 | end,
48 | PreloadClient = true,
49 | PreloadServer = false,
50 | BuildPanel = function(luaState, parentPanel)
51 | blobsProfiler.buildDTree(luaState, parentPanel, "Hooks")
52 | end,
53 | RefreshButton = "Re-scan",
54 | FormatNodeName = function(luaState, nodeKey, nodeValue)
55 | if nodeValue and istable(nodeValue) and not nodeValue.fakeVarType then
56 | return nodeKey .. " (".. table.Count(nodeValue) ..")"
57 | end
58 |
59 | return nodeKey
60 | end
61 | })
--------------------------------------------------------------------------------
/lua/blobsprofiler/shared/modules/bp_lua.lua:
--------------------------------------------------------------------------------
1 | if SERVER then
2 | util.AddNetworkString("blobsProfiler:sendLua")
3 | util.AddNetworkString("blobsProfiler:sendLua_versus")
4 | end
5 |
6 | blobsProfiler.RegisterModule("Lua", {
7 | Icon = "icon16/world.png",
8 | OrderPriority = 2,
9 | })
10 |
11 | --[[local validTypes = {
12 | ["string"] = true,
13 | ["number"] = true,
14 | ["boolean"] = true,
15 | --["Panel"] = true, -- the function is only used for serverside, which this will never exist on anyway
16 | ["function"] = true,
17 | ["Entity"] = true,
18 | ["Vector"] = true,
19 | ["Angle"] = true,
20 | ["table"] = true,
21 | ["Weapon"] = true,
22 | ["Player"] = true
23 | }]]
24 | -- https://gist.github.com/Yogpod/94b8ffa6ed9222e29961d0288f2b969c
25 |
26 | blobsProfiler.serialiseGlobals = function()
27 | local function copyTable(orig, seen)
28 | seen = seen or {}
29 | if seen[orig] then
30 | return nil
31 | --return seen[orig]
32 | end
33 |
34 | local copy = {}
35 | seen[orig] = copy
36 |
37 | for k, v in pairs(orig) do
38 | --local keyIsValid = type(k) == "string" or type(k) == "number" or type(k) == "boolean"
39 | local value
40 |
41 | --if validTypes[type(v)] and keyIsValid then
42 | if type(v) == 'table' then
43 | value = copyTable(v, seen)
44 | elseif type(v) == 'function' then
45 | local getDebugInfo = debug.getinfo(v, "S")
46 | value = {
47 | func = tostring(v),
48 | fakeVarType = "function",
49 | lastlinedefined = getDebugInfo.lastlinedefined,
50 | linedefined = getDebugInfo.linedefined,
51 | short_src = getDebugInfo.short_src
52 | --debugInfo = debugInfo -- this breaks pON??
53 | }
54 | else
55 | value = v
56 | end
57 | copy[k] = value
58 | --else
59 | -- This line can get spammy
60 | -- pOn can only encode certain types, so we need to whitelist (for now)
61 | -- print("blobsProfiler.serialiseGlobals: Invalid type for key:", k, "value:", v, "type:", type(v))
62 | --end
63 | end
64 |
65 | return copy
66 | end
67 |
68 | return copyTable(_G)
69 | end
70 |
71 | blobsProfiler.RegisterSubModule("Lua", "Globals", {
72 | Icon = "icon16/page_white_world.png",
73 | OrderPriority = 1,
74 | UpdateRealmData = function(luaState, t)
75 | if luaState == "Client" then
76 | blobsProfiler.Client.Lua = blobsProfiler.Client.Lua or {}
77 | local gimmyG = table.Copy(_G)
78 | blobsProfiler.Client.Lua.Globals = gimmyG
79 | else
80 | net.Start("blobsProfiler:requestData")
81 | net.WriteString("Lua.Globals")
82 | net.SendToServer()
83 | end
84 | end,
85 | PrepServerData = function()
86 | local _GSerialized = blobsProfiler.serialiseGlobals()
87 | return _GSerialized
88 | end,
89 | PreloadClient = true,
90 | PreloadServer = false,
91 | BuildPanel = function(luaState, parentPanel)
92 | blobsProfiler.Menu.TypeFolders = {}
93 | blobsProfiler.Menu.TypeFolders.Client = {}
94 | blobsProfiler.Menu.TypeFolders.Server = {}
95 | for k,v in ipairs(blobsProfiler.Menu.GlobalTypesToCondense) do
96 | blobsProfiler.Menu.TypeFolders.Client[v.type] = true
97 | blobsProfiler.Menu.TypeFolders.Server[v.type] = true
98 | end
99 |
100 | blobsProfiler.buildDTree(luaState, parentPanel, "Lua.Globals")
101 | end,
102 | RefreshButton = "Re-scan",
103 | FormatNodeName = function(luaState, nodeKey, nodeValue)
104 | if nodeValue and istable(nodeValue) and not nodeValue.fakeVarType then
105 | return nodeKey .. " (".. table.Count(nodeValue) ..")"
106 | end
107 |
108 | return nodeKey
109 | end,
110 | FormatNodeIcon = function(luaState, nodeKey, nodeValue)
111 | for _, varTypes in ipairs(blobsProfiler.Menu.GlobalTypesToCondense) do
112 | if varTypes.prettyPlural == nodeKey then
113 | return "icon16/folder_database.png"
114 | end
115 | end
116 | end
117 | })
118 |
119 | local function luaExecuteFilesInit()
120 | if not file.Exists("blobsProfiler", "DATA") then
121 | file.CreateDir("blobsProfiler")
122 | end
123 |
124 | if not file.Exists("blobsProfiler/Client_LuaExecute.txt", "DATA") then
125 | file.Write("blobsProfiler/Client_LuaExecute.txt", [[print("Hello client!")]])
126 | end
127 |
128 | if not file.Exists("blobsProfiler/Server_LuaExecute.txt", "DATA") then
129 | file.Write("blobsProfiler/Server_LuaExecute.txt", [[print("Hello server!")]])
130 | end
131 | end
132 |
133 | if CLIENT then
134 | luaExecuteFilesInit()
135 | end
136 |
137 | blobsProfiler.RegisterSubModule("Lua", "Execute", {
138 | Icon = "icon16/script_code.png",
139 | OrderPriority = 2,
140 | CustomPanel = function(luaState, parentPanel)
141 | luaExecuteFilesInit()
142 |
143 | local preLoadContent = file.Read("blobsProfiler/"..luaState.."_LuaExecute.txt")
144 | local dhtmlPanel = blobsProfiler.generateAceEditorPanel(parentPanel, preLoadContent or [[print("Hello world!")]])
145 |
146 | dhtmlPanel:Dock(FILL)
147 | dhtmlPanel.lastCode = preLoadContent
148 |
149 | dhtmlPanel:AddFunction("gmod", "saveContentsToFile", function(value)
150 | if value ~= dhtmlPanel.lastCode then -- prevent unnecessary writes
151 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "Writing Lua Execute editor content to: 'blobsProfiler/"..luaState.."_LuaExecute.txt'")
152 | file.Write("blobsProfiler/"..luaState.."_LuaExecute.txt", value)
153 | dhtmlPanel.lastCode = value
154 | end
155 | end)
156 |
157 | dhtmlPanel:AddFunction("gmod", "receiveEditorContent", function(value)
158 | if value ~= dhtmlPanel.lastCode then -- prevent unnecessary writes
159 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "Writing Lua Execute editor content to: 'blobsProfiler/"..luaState.."_LuaExecute.txt'")
160 | file.Write("blobsProfiler/"..luaState.."_LuaExecute.txt", value)
161 | dhtmlPanel.lastCode = value
162 | end
163 |
164 | if luaState == "Client" then
165 | RunString(value, "blobsprofiler:lua.execute")
166 | elseif luaState == "Server" then
167 | net.Start("blobsProfiler:sendLua")
168 | net.WriteString(value) -- TODO: allow bigger strings? no biggy for now, needs to be done securely!
169 | net.SendToServer()
170 | end
171 | end)
172 |
173 |
174 | local executeButton = vgui.Create("DButton", parentPanel)
175 | executeButton:Dock(BOTTOM)
176 | executeButton:SetText("Execute Lua Code")
177 | executeButton:DockMargin(0, 5, 0, 0)
178 |
179 | executeButton.DoClick = function()
180 | dhtmlPanel:RunJavascript([[
181 | var value = getEditorValue();
182 | gmod.receiveEditorContent(value);
183 | ]])
184 | end
185 |
186 | dhtmlPanel.attemptSaveContentsToFile = function()
187 | dhtmlPanel:RunJavascript([[
188 | var value = getEditorValue();
189 | gmod.saveContentsToFile(value);
190 | ]])
191 | end
192 |
193 | parentPanel.codePanel = dhtmlPanel
194 | end
195 | })
196 |
197 | timer.Create("blobsProfiler:LuaExecute_SaveToFile", 15, 0, function() -- TODO: Make this configurable once I do settings - and module settings
198 | local moduleTbl = blobsProfiler.GetModule("Lua.Execute")
199 | if not moduleTbl then return end
200 |
201 | if moduleTbl.ClientTab and moduleTbl.ClientTab.codePanel and moduleTbl.ClientTab.codePanel.attemptSaveContentsToFile then
202 | moduleTbl.ClientTab.codePanel.attemptSaveContentsToFile()
203 | end
204 | if moduleTbl.ServerTab and moduleTbl.ServerTab.codePanel and moduleTbl.ServerTab.codePanel.attemptSaveContentsToFile then
205 | moduleTbl.ServerTab.codePanel.attemptSaveContentsToFile()
206 | end
207 | end)
208 |
209 | local function prettyProfilerTimeFormat(seconds)
210 | if seconds >= 0.5 then
211 | return string.format("%.2fs", seconds)
212 | else
213 | return string.format("%.6fms", seconds * 1000)
214 | end
215 | end
216 |
217 | local function SetupResultsPanel(parentPanel, numIterations, lcTotalTime, lcMin, lcMax, rcTotalTime, rcMin, rcMax)
218 | parentPanel.stopCompare = false
219 |
220 | if parentPanel.resultsPanel and IsValid(parentPanel.resultsPanel) then
221 | parentPanel.resultsPanel:Remove()
222 | parentPanel.resultsPanel = nil
223 | end
224 |
225 | parentPanel.resultsPanel = vgui.Create("DPanel", parentPanel)
226 | parentPanel.resultsPanel:Dock(BOTTOM)
227 | parentPanel.resultsPanel:SetTall(70)
228 |
229 | local colorGreen = Color(0,136,0)
230 | local colorRed = Color(255,0,0)
231 |
232 | parentPanel.resultsPanel.Paint = function(s,w,h)
233 | draw.SimpleText("Total execution time: ".. prettyProfilerTimeFormat(lcTotalTime), "DermaDefault", 5, 5, lcTotalTimercTotalTime and colorGreen or colorRed, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
240 | draw.SimpleText("Slowest execution time: ".. prettyProfilerTimeFormat(rcMax), "DermaDefault", 5 + dividerPos, 20, lcMax>rcMax and colorGreen or colorRed, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
241 | draw.SimpleText("Fastest execution time: ".. prettyProfilerTimeFormat(rcMin), "DermaDefault", 5 + dividerPos, 35, lcMin>rcMin and colorGreen or colorRed, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
242 | draw.SimpleText("Average execution time: ".. prettyProfilerTimeFormat((rcTotalTime/numIterations)), "DermaDefault", 5 + dividerPos, 50, ((lcTotalTime/numIterations)>(rcTotalTime/numIterations)) and colorGreen or colorRed, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
243 | end
244 | end
245 |
246 | blobsProfiler.RegisterSubModule("Lua", "Versus", {
247 | Icon = "icon16/application_cascade.png",
248 | CustomPanel = function(luaState, parentPanel)
249 | local leftCode = blobsProfiler.generateAceEditorPanel(parentPanel,[[local function addNumbers(a, b)
250 | return a + b
251 | end
252 |
253 | local sum = addNumbers(5, 5)]])
254 | local rightCode = blobsProfiler.generateAceEditorPanel(parentPanel,[[local sum = 5 + 5]])
255 |
256 | local codeContainer = vgui.Create("DHorizontalDivider", parentPanel)
257 | codeContainer:Dock(FILL)
258 |
259 | codeContainer:SetLeft(leftCode)
260 | codeContainer:SetRight(rightCode)
261 |
262 | codeContainer:SetLeftMin(0)
263 | codeContainer:SetRightMin(0)
264 |
265 | -- Custom paint function for the drag bar
266 | codeContainer.m_DragBar.Paint = function(s, w, h)
267 | surface.SetDrawColor(0, 0, 0)
268 | surface.DrawRect(0, 0, w, h)
269 | end
270 |
271 | local optionsContainer = vgui.Create("DPanel", parentPanel)
272 | optionsContainer:Dock(BOTTOM)
273 | optionsContainer:SetTall(50)
274 |
275 | local iterationNumSlider = vgui.Create("DNumSlider", optionsContainer)
276 | iterationNumSlider:SetText("## of iterations")
277 | iterationNumSlider.Label:SetTextColor(Color(0,0,0))
278 | iterationNumSlider:SetMin(0)
279 | iterationNumSlider:SetMax(50000)
280 | iterationNumSlider:SetDecimals(0)
281 | iterationNumSlider:SetValue(1000)
282 | iterationNumSlider:Dock(TOP)
283 | iterationNumSlider:DockMargin(15,0,0,0)
284 | iterationNumSlider:DockPadding(0,0,0,5)
285 |
286 | local runComparison = vgui.Create("DButton", optionsContainer)
287 | runComparison:Dock(BOTTOM)
288 | runComparison:SetText("Compare code")
289 |
290 | runComparison.DoClick = function()
291 | if not parentPanel.stopCompare or parentPanel.stopCompare == false then -- Is this redundant? probably. I forget. my brain is mush.
292 | if parentPanel.resultsPanel and IsValid(parentPanel.resultsPanel) then
293 | parentPanel.resultsPanel:Remove()
294 | parentPanel.resultsPanel = nil
295 | end
296 |
297 | leftCode:RunJavascript([[
298 | var value = getEditorValue();
299 | gmod.receiveEditorContent(value);
300 | ]])
301 |
302 | rightCode:RunJavascript([[
303 | var value = getEditorValue();
304 | gmod.receiveEditorContent(value);
305 | ]])
306 |
307 | parentPanel.stopCompare = true -- This will be reset when results are displayed
308 | else
309 | if runComparison.Warning and IsValid(runComparison.Warning) then runComparison.Warning:Remove() runComparison.Warning = nil end
310 | runComparison.Warning = Derma_Message("Please wait for the previous comparison to finish", "blobsProfiler - Slow down!", "OK")
311 | end
312 | end
313 |
314 | local leftCodeContent
315 | local rightCodeContent
316 |
317 | leftCode:AddFunction("gmod", "receiveEditorContent", function(value)
318 | leftCodeContent = value
319 | end)
320 |
321 | rightCode:AddFunction("gmod", "receiveEditorContent", function(value)
322 | rightCodeContent = value
323 |
324 | if luaState == "Client" then
325 | local numIterations = math.Round(iterationNumSlider:GetValue())
326 | if numIterations > 0 then
327 | local lcMin, lcMax
328 | local rcMin, rcMax -- Why aren't all these on a single line? because, fuck you. Thats why.
329 |
330 | -- TODO: Maybe add up the individual times, so the c checks aren't adding to performace (which is likely VERY minor)
331 | local lcStart = SysTime()
332 | for i=1, numIterations do
333 | local ciStart = SysTime()
334 | RunString(leftCodeContent, "blobsprofiler:lua.versus_LEFT")
335 | local ciTotal = SysTime() - ciStart
336 |
337 | if not lcMax or (ciTotal > lcMax) then lcMax = ciTotal end
338 | if not lcMin or (ciTotal < lcMin) then lcMin = ciTotal end
339 | end
340 | local lcTotalTime = SysTime() - lcStart
341 |
342 | local rcStart = SysTime()
343 | for i=1, numIterations do
344 | local ciStart = SysTime()
345 | RunString(rightCodeContent, "blobsprofiler:lua.versus_RIGHT")
346 | local ciTotal = SysTime() - ciStart
347 |
348 | if not rcMax or (ciTotal > rcMax) then rcMax = ciTotal end
349 | if not rcMin or (ciTotal < rcMin) then rcMin = ciTotal end
350 | end
351 | local rcTotalTime = SysTime() - rcStart
352 |
353 | SetupResultsPanel(parentPanel, numIterations, lcTotalTime, lcMin, lcMax, rcTotalTime, rcMin, rcMax)
354 | end
355 | elseif luaState == "Server" then
356 | blobsProfiler.Modules["Lua"].SubModules["Versus"].retrievingData = true
357 | net.Start("blobsProfiler:sendLua_versus")
358 | net.WriteUInt(math.Round(iterationNumSlider:GetValue()) ,20)
359 | net.WriteString(leftCodeContent)
360 | net.WriteString(rightCodeContent)
361 | net.SendToServer()
362 | end
363 | end)
364 |
365 | parentPanel.codeContainer = codeContainer
366 | end,
367 | OnOpen = function(luaState, parentPanel)
368 | timer.Simple(0, function()
369 | local width = parentPanel.codeContainer:GetWide()
370 | local dividerWidth = parentPanel.codeContainer:GetDividerWidth() or 8
371 | local halfWidth = (width - dividerWidth) * 0.5
372 | parentPanel.codeContainer:SetLeftWidth(halfWidth)
373 | end)
374 | end
375 | })
376 |
377 | net.Receive("blobsProfiler:sendLua", function(l, ply)
378 | if not SERVER or not blobsProfiler.CanAccess(ply, "sendLua", "Server") then return end
379 | local luaRun = net.ReadString()
380 |
381 | blobsProfiler.Log(blobsProfiler.L_LOG, ply:Name() .. " (".. ply:SteamID64() ..") sent Lua to the server:")
382 | blobsProfiler.Log(blobsProfiler.L_LOG, luaRun)
383 |
384 | RunString(luaRun, "blobsprofiler:lua.execute")
385 | end)
386 |
387 |
388 | net.Receive("blobsProfiler:sendLua_versus", function(l, ply)
389 | if not blobsProfiler.CanAccess(ply or LocalPlayer(), "sendLua_versus") then return end
390 | if SERVER then
391 | local numIterations = net.ReadUInt(20)
392 | local luaRunL = net.ReadString()
393 | local luaRunR = net.ReadString()
394 |
395 | if numIterations > 0 then
396 | local lcMin, lcMax
397 | local rcMin, rcMax -- Why aren't all these on a single line? because, fuck you. Thats why.
398 |
399 | -- TODO: Maybe add up the individual times, so the c checks aren't adding to performace (which is likely VERY minor)
400 | local lcStart = SysTime()
401 | for i=1, numIterations do
402 | local ciStart = SysTime()
403 | RunString(luaRunL, "blobsprofiler:lua.versus_LEFT")
404 | local ciTotal = SysTime() - ciStart
405 |
406 | if not lcMax or (ciTotal > lcMax) then lcMax = ciTotal end
407 | if not lcMin or (ciTotal < lcMin) then lcMin = ciTotal end
408 | end
409 | local lcTotalTime = SysTime() - lcStart
410 |
411 | local rcStart = SysTime()
412 | for i=1, numIterations do
413 | local ciStart = SysTime()
414 | RunString(luaRunR, "blobsprofiler:lua.versus_RIGHT")
415 | local ciTotal = SysTime() - ciStart
416 |
417 | if not rcMax or (ciTotal > rcMax) then rcMax = ciTotal end
418 | if not rcMin or (ciTotal < rcMin) then rcMin = ciTotal end
419 | end
420 | local rcTotalTime = SysTime() - rcStart
421 |
422 | net.Start("blobsProfiler:sendLua_versus")
423 | net.WriteUInt(numIterations, 20)
424 |
425 | net.WriteDouble(lcTotalTime)
426 | net.WriteDouble(lcMin)
427 | net.WriteDouble(lcMax)
428 |
429 | net.WriteDouble(rcTotalTime)
430 | net.WriteDouble(rcMin)
431 | net.WriteDouble(rcMax)
432 | net.Send(ply)
433 | end
434 | else
435 | blobsProfiler.Modules["Lua"].SubModules["Versus"].retrievingData = false
436 |
437 | local numIterations = net.ReadUInt(20)
438 |
439 | local lcTotalTime = net.ReadDouble()
440 | local lcMin = net.ReadDouble()
441 | local lcMax = net.ReadDouble()
442 |
443 | local rcTotalTime = net.ReadDouble()
444 | local rcMin = net.ReadDouble()
445 | local rcMax = net.ReadDouble()
446 |
447 | local parentPanel = blobsProfiler.Modules["Lua"].SubModules["Versus"].ServerTab
448 | SetupResultsPanel(parentPanel, numIterations, lcTotalTime, lcMin, lcMax, rcTotalTime, rcMin, rcMax)
449 | end
450 | end)
--------------------------------------------------------------------------------
/lua/blobsprofiler/shared/modules/bp_network.lua:
--------------------------------------------------------------------------------
1 | blobsProfiler.RegisterModule("Network", {
2 | Icon = "icon16/drive_network.png",
3 | OrderPriority = 6,
4 | UpdateRealmData = function(luaState)
5 | if luaState == "Client" then
6 | local netRecieversData = {}
7 | for recvName, recvFunc in pairs(net.Receivers) do
8 | recvName = tostring(recvName)
9 |
10 | local debugInfo = debug.getinfo(recvFunc)
11 | netRecieversData[recvName] = debugInfo
12 | netRecieversData[recvName].func = recvFunc
13 |
14 | netRecieversData[recvName].fakeVarType = "function"
15 | end
16 |
17 | blobsProfiler.Client.Network = netRecieversData
18 | else
19 | net.Start("blobsProfiler:requestData")
20 | net.WriteString("Network")
21 | net.SendToServer()
22 | end
23 | end,
24 | PrepServerData = function()
25 | local netRecieversData = {}
26 | for recvName, recvFunc in pairs(net.Receivers) do
27 | recvName = tostring(recvName)
28 |
29 | local debugInfo = debug.getinfo(recvFunc)
30 | netRecieversData[recvName] = debugInfo
31 | netRecieversData[recvName].func = tostring(recvFunc)
32 |
33 | netRecieversData[recvName].fakeVarType = "function"
34 | end
35 |
36 | return netRecieversData
37 | end,
38 | PreloadClient = true,
39 | PreloadServer = false,
40 | BuildPanel = function(luaState, parentPanel)
41 | blobsProfiler.buildDTree(luaState, parentPanel, "Network")
42 | end,
43 | RefreshButton = "Re-scan"
44 | })
--------------------------------------------------------------------------------
/lua/blobsprofiler/shared/modules/bp_profiling.lua:
--------------------------------------------------------------------------------
1 | blobsProfiler.RegisterModule("Profiling", {
2 | Icon = "icon16/hourglass.png",
3 | OrderPriority = 1
4 | })
5 |
6 | local function profileLog(luaState, event)
7 | local info = debug.getinfo(3, "f")
8 | if not info then return end -- ???
9 |
10 | local funcId = tostring(info.func)
11 | if blobsProfiler[luaState].Profile.Raw[funcId] then
12 | if event == "call" then
13 | blobsProfiler[luaState].Profile.Called[funcId] = SysTime()
14 | elseif event == "return" then
15 | if blobsProfiler[luaState].Profile.Called[funcId] then
16 | local timeToRun = SysTime() - blobsProfiler[luaState].Profile.Called[funcId]
17 | blobsProfiler[luaState].Profile.Called[funcId] = nil
18 | table.insert(blobsProfiler[luaState].Profile.Results[funcId], timeToRun)
19 | end
20 | end
21 | end
22 | end
23 |
24 | if not blobsProfiler.Modules["Profiling"].ProfilingStatus then
25 | blobsProfiler.Modules["Profiling"].ProfilingStatus = {}
26 | blobsProfiler.Modules["Profiling"].ProfilingStatus["Client"] = false
27 | blobsProfiler.Modules["Profiling"].ProfilingStatus["Server"] = false
28 | end
29 |
30 |
31 | blobsProfiler.RegisterSubModule("Profiling", "Targets", {
32 | Icon = "icon16/book_addresses.png",
33 | OrderPriority = 1,
34 | OnOpen = function(luaState, parentPanel)
35 | local profilerTable = blobsProfiler[luaState].Profile or {}
36 | local profilerData = table.Copy(profilerTable)
37 | profilerData.Raw = nil -- we dont need to display these
38 | profilerData.Called = nil
39 | profilerData.Results = nil
40 |
41 | local getKeys = table.GetKeys(profilerData)
42 | for _, tblKey in ipairs(getKeys) do
43 | if profilerData[tblKey] and table.Count(profilerData[tblKey]) == 0 then
44 | profilerData[tblKey] = nil
45 | end
46 | end
47 |
48 | blobsProfiler.buildDTree(luaState, parentPanel, "Profiling.Targets", profilerData)
49 |
50 | if parentPanel.startProfilingButton and IsValid(parentPanel.startProfilingButton) then
51 | parentPanel.startProfilingButton:Remove()
52 | parentPanel.startProfilingButton = nil
53 | end
54 | if parentPanel.stopProfilingButton and IsValid(parentPanel.stopProfilingButton) then
55 | parentPanel.stopProfilingButton:Remove()
56 | parentPanel.stopProfilingButton = nil
57 | end
58 |
59 | parentPanel.startProfilingButton = vgui.Create("DButton", parentPanel)
60 | parentPanel.stopProfilingButton = vgui.Create("DButton", parentPanel)
61 |
62 | parentPanel.startProfilingButton:SetText("Start profiling")
63 | parentPanel.stopProfilingButton:SetText("Stop profiling")
64 |
65 | parentPanel.stopProfilingButton:Dock(BOTTOM)
66 | parentPanel.startProfilingButton:Dock(BOTTOM)
67 |
68 | parentPanel.startProfilingButton:SetEnabled(not blobsProfiler.Modules["Profiling"].ProfilingStatus[luaState])
69 | parentPanel.stopProfilingButton:SetEnabled(blobsProfiler.Modules["Profiling"].ProfilingStatus[luaState])
70 |
71 | parentPanel.startProfilingButton.DoClick = function()
72 | if not blobsProfiler.CanAccess(LocalPlayer(), "Profiling_"..luaState) then return end
73 | if luaState == "Client" then
74 | blobsProfiler.Modules["Profiling"].ProfilingStatus[luaState] = true
75 | debug.sethook(function(e) profileLog("Client", e) end, "cr")
76 |
77 | parentPanel.startProfilingButton:SetEnabled(not blobsProfiler.Modules["Profiling"].ProfilingStatus[luaState])
78 | parentPanel.stopProfilingButton:SetEnabled(blobsProfiler.Modules["Profiling"].ProfilingStatus[luaState])
79 | elseif luaState == "Server" then
80 |
81 | end
82 | end
83 |
84 | parentPanel.stopProfilingButton.DoClick = function()
85 | if not blobsProfiler.CanAccess(LocalPlayer(), "Profiling_"..luaState) then return end
86 | if luaState == "Client" then
87 | blobsProfiler.Modules["Profiling"].ProfilingStatus[luaState] = false
88 | debug.sethook()
89 |
90 | parentPanel.startProfilingButton:SetEnabled(not blobsProfiler.Modules["Profiling"].ProfilingStatus[luaState])
91 | parentPanel.stopProfilingButton:SetEnabled(blobsProfiler.Modules["Profiling"].ProfilingStatus[luaState])
92 | elseif luaState == "Server" then
93 |
94 | end
95 | end
96 | end,
97 | FormatNodeName = function(luaState, nodeKey, nodeValue)
98 | return nodeValue.name or nodeKey
99 | end,
100 | FormatNodeIcon = function(luaState, nodeKey, nodeValue)
101 | local fullModuleName = nodeKey
102 |
103 | local getModule = blobsProfiler.GetModule(fullModuleName)
104 | if getModule and getModule.Icon then
105 | return getModule.Icon
106 | end
107 | end
108 | })
109 |
110 | blobsProfiler.RegisterSubModule("Profiling", "Results", {
111 | Icon = "icon16/chart_bar.png",
112 | OrderPriority = 2
113 | })
--------------------------------------------------------------------------------
/lua/blobsprofiler/shared/modules/bp_sqlite.lua:
--------------------------------------------------------------------------------
1 | blobsProfiler.RegisterModule("SQLite", {
2 | Icon = "icon16/database.png",
3 | OrderPriority = 8
4 | })
5 |
6 | local function buildSQLiteSchemaTable()
7 | local SQLiteSchema = {}
8 | SQLiteSchema.Tables = {}
9 | SQLiteSchema.Indices = {}
10 |
11 | local grabTableIndexData = sql.Query("SELECT * FROM sqlite_master")
12 |
13 | if grabTableIndexData then
14 | for _, tblData in ipairs(grabTableIndexData) do
15 | if tblData.type == "table" then
16 | SQLiteSchema.Tables[tblData.name] = {}
17 |
18 | local grabTableColumnData = sql.Query("PRAGMA table_info(".. sql.SQLStr(tblData.name) ..");")
19 | if grabTableColumnData then
20 | for _, tblCol in ipairs(grabTableColumnData) do
21 | SQLiteSchema.Tables[tblData.name][tblCol.cid] = {}
22 | SQLiteSchema.Tables[tblData.name][tblCol.cid]["ID"] = tblCol.cid
23 | SQLiteSchema.Tables[tblData.name][tblCol.cid]["Name"] = tblCol.name
24 | SQLiteSchema.Tables[tblData.name][tblCol.cid]["Primary Key"] = tobool(tblCol.pk) or nil
25 | SQLiteSchema.Tables[tblData.name][tblCol.cid]["Type"] = tblCol.type or nil
26 | SQLiteSchema.Tables[tblData.name][tblCol.cid]["Not NULL"] = tblCol.notnull or nil
27 | SQLiteSchema.Tables[tblData.name][tblCol.cid]["Default"] = tblCol.dflt_value or nil
28 | end
29 | end
30 | elseif tblData.type == "index" then
31 | SQLiteSchema.Indices[tblData.name] = {}
32 | --blobsProfiler.SQLite.SchemaIndices[v.name].CreateSQL = v.sql
33 | end
34 | end
35 | end
36 |
37 | return SQLiteSchema
38 | end
39 |
40 | if SERVER then
41 | util.AddNetworkString("blobsProfiler:SQLite_Schema_CreateSQL")
42 |
43 | net.Receive("blobsProfiler:SQLite_Schema_CreateSQL", function(len, ply)
44 | if not blobsProfiler.CanAccess(ply, "SQLite") then return end
45 | if not blobsProfiler.CanAccess(ply, "SQLite_Schema") then return end
46 | if not blobsProfiler.CanAccess(ply, "SQLite_Schema_CreateSQL") then return end
47 |
48 | local tableName = net.ReadString()
49 | local rcAction = net.ReadUInt(2)
50 | local grabData = sql.QueryValue("SELECT sql FROM sqlite_master WHERE name = ".. sql.SQLStr(tableName) .." LIMIT 1;")
51 |
52 | if grabData then
53 | net.Start("blobsProfiler:SQLite_Schema_CreateSQL")
54 | net.WriteString(grabData)
55 | net.WriteUInt(rcAction, 2)
56 | net.Send(ply)
57 | end
58 | end)
59 | else
60 | net.Receive("blobsProfiler:SQLite_Schema_CreateSQL", function()
61 | local createSQL = net.ReadString()
62 | local rcAction = net.ReadUInt(2)
63 |
64 | if rcAction == 1 then
65 | print(createSQL)
66 |
67 | Derma_Message("SQLite creation statement printed to console", "blobsProfiler: SQLite Schema - Create SQL", "OK")
68 | elseif rcAction == 2 then
69 | SetClipboardText(createSQL)
70 |
71 | Derma_Message("SQLite creation statement copied to clipboard", "blobsProfiler: SQLite Schema - Create SQL", "OK")
72 | end
73 | end)
74 | end
75 |
76 | blobsProfiler.RegisterSubModule("SQLite", "Schema", {
77 | Icon = "icon16/database_gear.png",
78 | OrderPriority = 1,
79 | UpdateRealmData = function(luaState)
80 | if luaState == "Client" then
81 | blobsProfiler.Client.SQLite = blobsProfiler.Client.SQLite or {}
82 | blobsProfiler.Client.SQLite.Schema = buildSQLiteSchemaTable()
83 | else
84 | net.Start("blobsProfiler:requestData")
85 | net.WriteString("SQLite.Schema")
86 | net.SendToServer()
87 | end
88 | end,
89 | PrepServerData = function()
90 | return buildSQLiteSchemaTable()
91 | end,
92 | PreloadClient = true,
93 | PreloadServer = false,
94 | BuildPanel = function(luaState, parentPanel)
95 | blobsProfiler.buildDTree(luaState, parentPanel, "SQLite.Schema")
96 | end,
97 | RefreshButton = "Refresh",
98 | RCFunctions = {
99 | ["table"] = {
100 | {
101 | name = "SQL Create statement",
102 | submenu = {
103 | {
104 | name = "Print",
105 | func = function(ref, node, luaState)
106 | if luaState == "Client" then
107 | local grabSQLCreate = sql.QueryValue("SELECT sql FROM sqlite_master WHERE name = ".. sql.SQLStr(ref.key) .." LIMIT 1;")
108 | print(grabSQLCreate)
109 |
110 | Derma_Message("SQLite creation statement printed to console", "blobsProfiler: SQLite Schema - Create SQL", "OK")
111 | else
112 | net.Start("blobsProfiler:SQLite_Schema_CreateSQL")
113 | net.WriteString(ref.key)
114 | net.WriteUInt(1, 2)
115 | net.SendToServer()
116 | end
117 | end,
118 | icon = "icon16/application_osx_terminal.png"
119 | },
120 | {
121 | name = "Copy to clipboard",
122 | func = function(ref, node, luaState)
123 | if luaState == "Client" then
124 | local grabSQLCreate = sql.QueryValue("SELECT sql FROM sqlite_master WHERE name = ".. sql.SQLStr(ref.key) .." LIMIT 1;")
125 | SetClipboardText(grabSQLCreate)
126 |
127 | Derma_Message("SQLite creation statement copied to clipboard", "blobsProfiler: SQLite Schema - Create SQL", "OK")
128 | else
129 | net.Start("blobsProfiler:SQLite_Schema_CreateSQL")
130 | net.WriteString(ref.key)
131 | net.WriteUInt(2, 2)
132 | net.SendToServer()
133 | end
134 | end,
135 | icon = "icon16/page_copy.png"
136 | }
137 | },
138 | icon = "icon16/table_lightning.png",
139 | condition = function(ref, node, realm)
140 | if not blobsProfiler[realm].SQLite.Schema.Tables then return false end
141 |
142 | return blobsProfiler[realm].SQLite.Schema.Tables[ref.key]
143 | end
144 | }
145 | }
146 | },
147 | FormatNodeName = function(luaState, nodeKey, nodeValue)
148 | if istable(nodeValue) then
149 | if nodeValue["ID"] and nodeValue["Name"] then
150 | return nodeValue["Name"]
151 | else
152 | return nodeKey
153 | end
154 | else
155 | return nodeKey .. ": " .. tostring(nodeValue)
156 | end
157 | end,
158 | FormatNodeIcon = function(luaState, nodeKey, nodeValue)
159 | if istable(nodeValue) and nodeValue["Primary Key"] then
160 | return "icon16/table_key.png"
161 | elseif not istable(nodeValue) then
162 | return "icon16/page.png"
163 | else
164 | return "icon16/table.png"
165 | end
166 | end
167 | })
168 |
169 | local function splitAndProcessQueries(query) -- why the fuck did I bother
170 | local results = {}
171 | local queries = string.Explode(";", query)
172 |
173 | for _, singleQuery in ipairs(queries) do
174 | singleQuery = string.Trim(singleQuery)
175 |
176 | if singleQuery ~= "" then
177 | local queryType = string.match(singleQuery:upper(), "^(%w+)")
178 | local result
179 | local affectedRowsStr
180 | local affectedRows
181 |
182 | if queryType == "SELECT" or queryType == "PRAGMA" or queryType == "EXPLAIN" then
183 | result = sql.Query(singleQuery)
184 | if result == false then
185 | table.insert(results, {type = "ERROR", message=sql.LastError(), query = singleQuery})
186 | else
187 | table.insert(results, {type = queryType, data = result, query = singleQuery})
188 | end
189 | elseif queryType == "INSERT" or queryType == "UPDATE" or queryType == "DELETE" then
190 | result = sql.Query(singleQuery)
191 | affectedRowsStr = sql.QueryValue("SELECT changes()")
192 | affectedRows = tonumber(affectedRowsStr) or 0
193 | if result == false then
194 | table.insert(results, {type = "ERROR", message = sql.LastError(), query = singleQuery})
195 | else
196 | table.insert(results, {type = queryType, message = "Rows affected: " .. affectedRows, rowsAffected = affectedRows, query = singleQuery})
197 | end
198 | elseif queryType == "COMMIT" or queryType == "ROLLBACK" or queryType == "CREATE" or queryType == "ALTER" or queryType == "DROP" then
199 | local success = sql.Query(singleQuery)
200 | if success == false then
201 | table.insert(results, {type = "ERROR", message = sql.LastError(), query = singleQuery})
202 | else
203 | table.insert(results, {type = queryType, message = queryType .. " operation successful", query = singleQuery})
204 | end
205 | elseif queryType == "SAVEPOINT" then
206 | local savepointName = string.match(singleQuery, "SAVEPOINT%s+([%w_]+)")
207 | if savepointName then
208 | local success = sql.Query("SAVEPOINT " .. savepointName)
209 | if success == false then
210 | table.insert(results, {type = "ERROR", message = sql.LastError(), query = singleQuery})
211 | else
212 | table.insert(results, {type = "SAVEPOINT", message = "Savepoint created: " .. savepointName, query = singleQuery})
213 | end
214 | else
215 | table.insert(results, {type = "ERROR", message = "Savepoint name not specified", query = singleQuery})
216 | end
217 | else
218 | local tryInvalid = sql.Query(singleQuery)
219 |
220 | if tryInvalid == false then
221 | table.insert(results, {type = "ERROR", message = sql.LastError(), query = singleQuery})
222 | else
223 | table.insert(results, {type = "ERROR", message = "UNHANDLED SQL QUERY TYPE: ".. queryType, query = singleQuery})
224 | end
225 | end
226 | end
227 | end
228 |
229 | return results
230 | end
231 |
232 | if SERVER then
233 | util.AddNetworkString("blobsProfiler:requestSQLiteData")
234 |
235 | net.Receive("blobsProfiler:requestSQLiteData", function(len, ply)
236 | if not blobsProfiler.CanAccess(ply, "serverData") then return end
237 | if not blobsProfiler.CanAccess(ply, "serverData_SQLite") then return end
238 | if not blobsProfiler.CanAccess(ply, "serverData_SQLite_Data") then return end
239 |
240 | local tableName = net.ReadString()
241 | local pageNum = net.ReadUInt(12)
242 | if pageNum < 1 then pageNum = 1 end
243 |
244 | local getSQLData = sql.Query("SELECT * FROM ".. sql.SQLStr(tableName) .. " LIMIT 25")
245 | if getSQLData == false then
246 | -- error
247 | elseif getSQLData == nil then
248 | -- no data
249 | else
250 | net.Start("blobsProfiler:requestSQLiteData")
251 | net.WriteString(tableName)
252 | net.WriteTable(getSQLData)
253 | net.Send(ply)
254 | end
255 | end)
256 |
257 | util.AddNetworkString("blobsProfiler:runSQLite")
258 |
259 | net.Receive("blobsProfiler:runSQLite", function(len, ply)
260 | if not blobsProfiler.CanAccess(ply, "serverData") then return end
261 | if not blobsProfiler.CanAccess(ply, "serverData_SQLite") then return end
262 | if not blobsProfiler.CanAccess(ply, "serverData_SQLite_Execute") then return end
263 |
264 | local sqlQuery = net.ReadString()
265 |
266 | local proccessQuery = splitAndProcessQueries(sqlQuery)
267 |
268 | net.Start("blobsProfiler:runSQLite")
269 | net.WriteTable(proccessQuery or {}) -- todo: use chunked sending
270 | net.Send(ply)
271 | end)
272 | else
273 | net.Receive("blobsProfiler:requestSQLiteData", function()
274 | local tableName = net.ReadString()
275 | local getSQLData = net.ReadTable()
276 |
277 | blobsProfiler.Modules.SQLite.SubModules.Data.retrievingData = false
278 | local schemaDataTable = blobsProfiler.Server.SQLite.Schema
279 | local tableSelectorList = blobsProfiler.Server.SQLite.Data.tableSelectorList
280 | local tableDataListView = blobsProfiler.Server.SQLite.Data.tableDataListView
281 |
282 | for k, line in ipairs( tableDataListView:GetLines() ) do
283 | tableDataListView:RemoveLine(k)
284 | end
285 |
286 | for k,v in ipairs(tableDataListView.Columns) do
287 | if v and IsValid(v) then v:Remove() end
288 | end
289 |
290 | tableDataListView.Columns = {}
291 |
292 | local colList = schemaDataTable.Tables[tableName]
293 | if colList then
294 | local colAmnt = table.Count(colList)
295 | for i=0, colAmnt-1 do
296 | local colData = schemaDataTable.Tables[tableName][i] -- why the fuck is it a number now
297 | tableDataListView:AddColumn(colData.Name)
298 | end
299 | end
300 | tableDataListView:SetDirty( true )
301 |
302 | tableDataListView:FixColumnsLayout()
303 |
304 | local tblOrder = {}
305 | local colList = schemaDataTable.Tables[tableName]
306 | if colList then
307 | local colAmnt = table.Count(colList)
308 | for i=0, colAmnt-1 do
309 | local colData = schemaDataTable.Tables[tableName][i]
310 | table.insert(tblOrder, colData.Name)
311 | end
312 | end
313 |
314 | for _, record in ipairs(getSQLData) do
315 | local dataBuild = {}
316 | for __, key in ipairs(tblOrder) do
317 | table.insert(dataBuild, record[key])
318 | end
319 | tableDataListView:AddLine(unpack(dataBuild))
320 | end
321 | end)
322 |
323 | net.Receive("blobsProfiler:runSQLite", function()
324 | local queryResults = net.ReadTable()
325 |
326 | local subModuleRef = blobsProfiler.Modules.SQLite.SubModules.Execute
327 | subModuleRef.ServerTab.handleQueries("Server", queryResults)
328 | end)
329 | end
330 |
331 | local function requestSQLiteData(luaState, tableName, pageNum)
332 | pageNum = pageNum or 1
333 | local schemaDataTable = blobsProfiler[luaState].SQLite.Schema
334 |
335 | local tableSelectorList = blobsProfiler[luaState].SQLite.Data.tableSelectorList
336 | local tableDataListView = blobsProfiler[luaState].SQLite.Data.tableDataListView
337 |
338 | if luaState == "Client" then
339 | for k, line in ipairs( tableDataListView:GetLines() ) do
340 | tableDataListView:RemoveLine(k)
341 | end
342 |
343 | for k,v in ipairs(tableDataListView.Columns) do
344 | if v and IsValid(v) then v:Remove() end
345 | end
346 |
347 | tableDataListView.Columns = {}
348 |
349 | local colList = schemaDataTable.Tables[tableName]
350 | if colList then
351 | local colAmnt = table.Count(colList)
352 | for i=0, colAmnt-1 do
353 | local colData = schemaDataTable.Tables[tableName][tostring(i)] -- TODO: make this actual number ffs
354 | tableDataListView:AddColumn(colData.Name)
355 | end
356 | end
357 | tableDataListView:SetDirty( true )
358 |
359 | tableDataListView:FixColumnsLayout()
360 |
361 | local getSQLData = sql.Query("SELECT * FROM ".. sql.SQLStr(tableName) .. " LIMIT 25")
362 | if getSQLData == false then
363 | -- error
364 | elseif getSQLData == nil then
365 | -- no data
366 | else
367 |
368 | local tblOrder = {}
369 | local colList = schemaDataTable.Tables[tableName]
370 | if colList then
371 | local colAmnt = table.Count(colList)
372 | for i=0, colAmnt-1 do
373 | local colData = schemaDataTable.Tables[tableName][tostring(i)] -- TODO: make this actual number ffs
374 | table.insert(tblOrder, colData.Name)
375 | end
376 | end
377 |
378 | for _, record in ipairs(getSQLData) do
379 | local dataBuild = {}
380 | for __, key in ipairs(tblOrder) do
381 | table.insert(dataBuild, record[key])
382 | end
383 | tableDataListView:AddLine(unpack(dataBuild))
384 | end
385 |
386 | end
387 | else
388 | blobsProfiler.Modules.SQLite.SubModules.Data.retrievingData = true
389 | net.Start("blobsProfiler:requestSQLiteData")
390 | net.WriteString(tableName)
391 | net.WriteUInt(pageNum, 12)
392 | net.SendToServer()
393 | end
394 | end
395 |
396 | blobsProfiler.RegisterSubModule("SQLite", "Data", {
397 | Icon = "icon16/page_white_database.png",
398 | OrderPriority = 2,
399 | CustomPanel = function(luaState, parentPanel)
400 | blobsProfiler[luaState].SQLite = blobsProfiler[luaState].SQLite or {}
401 |
402 | blobsProfiler[luaState].SQLite.Data = blobsProfiler[luaState].SQLite.Data or {}
403 |
404 | local schemaDataTable = blobsProfiler[luaState].SQLite.Schema or {}
405 |
406 | blobsProfiler[luaState].SQLite.Data.tableSelectorList = vgui.Create("DComboBox", parentPanel)
407 | blobsProfiler[luaState].SQLite.Data.tableDataListView = vgui.Create("DListView", parentPanel)
408 |
409 | local tableSelectorList = blobsProfiler[luaState].SQLite.Data.tableSelectorList
410 | local tableDataListView = blobsProfiler[luaState].SQLite.Data.tableDataListView
411 |
412 | tableDataListView:Dock(FILL)
413 |
414 | tableSelectorList:Dock(TOP)
415 | tableSelectorList:SetSortItems(false)
416 |
417 | tableSelectorList.OnSelect = function(s, index, value)
418 | requestSQLiteData(luaState, value)
419 | end
420 |
421 | for k,v in pairs(schemaDataTable.Tables or {}) do
422 | tableSelectorList:AddChoice(k)
423 |
424 | if not tableSelectorList:GetSelected() then
425 | tableSelectorList:ChooseOption(k, 1)
426 | end
427 | end
428 | end,
429 | OnOpen = function(luaState)
430 | if blobsProfiler[luaState].SQLite.Data.tableSelectorList then
431 |
432 | blobsProfiler[luaState].SQLite.Data.tableSelectorList.Data = {}
433 | blobsProfiler[luaState].SQLite.Data.tableSelectorList.Choices = {}
434 |
435 | local schemaDataTable = blobsProfiler[luaState].SQLite.Schema or {}
436 |
437 | for k, v in pairs(schemaDataTable.Tables or {}) do
438 | blobsProfiler[luaState].SQLite.Data.tableSelectorList:AddChoice(k)
439 |
440 | if not blobsProfiler[luaState].SQLite.Data.tableSelectorList:GetSelected() then
441 | blobsProfiler[luaState].SQLite.Data.tableSelectorList:ChooseOption(k, 1)
442 | end
443 | end
444 | end
445 | end
446 | })
447 |
448 | blobsProfiler.RegisterSubModule("SQLite", "Execute", {
449 | Icon = "icon16/database_go.png",
450 | OrderPriority = 3,
451 | CustomPanel = function(luaState, parentPanel)
452 | local dhtmlPanel = blobsProfiler.generateAceEditorPanel(parentPanel, "", "SQL")
453 | dhtmlPanel:Dock(FILL)
454 |
455 | local resultContainer = vgui.Create("DPropertySheet", parentPanel)
456 | resultContainer:SetVisible(false)
457 |
458 | local executeButton = vgui.Create("DButton", parentPanel)
459 |
460 | parentPanel.handleQueries = function(luaStateHQ, dataTable)
461 | local realmString = string.lower(luaStateHQ)
462 | if not blobsProfiler.CanAccess(LocalPlayer(), realmString .. "Data") then return end
463 | if not blobsProfiler.CanAccess(LocalPlayer(), realmString .. "Data_SQLite") then return end
464 | if not blobsProfiler.CanAccess(LocalPlayer(), realmString .. "Data_SQLite_Execute") then return end
465 |
466 | if resultContainer and IsValid(resultContainer) then
467 | resultContainer:Remove()
468 | end
469 |
470 | resultContainer = vgui.Create("DPropertySheet", parentPanel)
471 | resultContainer:SetVisible(false)
472 |
473 | for queryID, queryTable in ipairs(dataTable) do
474 | local queryType = queryTable.type or "ERROR"
475 | local isError = queryType == "ERROR" or false
476 |
477 | local panel1 = vgui.Create( "DPanel", resultContainer )
478 |
479 | if queryType == "ERROR" then -- TODO: some of this can be condensed
480 | local queryResult = vgui.Create("DPanel", panel1)
481 | queryResult:Dock(FILL)
482 | queryResult.Paint = function(s,w,h)
483 | draw.SimpleTextOutlined(queryTable.query, "DermaDefault", 5, 2, Color(255,80,80), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP, 1, color_black)
484 | draw.SimpleText(queryTable.message or "Unknown Error", "DermaDefault", 5, 20, color_black, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
485 | end
486 | elseif queryType == "SELECT" or queryType == "PRAGMA" or queryType == "EXPLAIN" then
487 | local queryResult = vgui.Create("DListView", panel1)
488 | queryResult:Dock(FILL)
489 |
490 | if queryTable.data and #queryTable.data >= 1 then
491 | local keys = table.GetKeys(queryTable.data[1]) -- idk if this is good enough
492 | for _, keyName in ipairs(keys) do
493 | queryResult:AddColumn(keyName)
494 | end
495 |
496 | for i, tblRecord in ipairs(queryTable.data) do
497 | local lineData = {}
498 | for _, key in ipairs(keys) do
499 | table.insert(lineData, tblRecord[key])
500 | end
501 | queryResult:AddLine(unpack(lineData))
502 | end
503 | end
504 |
505 | local querySummary = vgui.Create("DPanel", panel1)
506 | querySummary:SetTall(20)
507 | if queryType == "SELECT" and queryTable.data then
508 | querySummary:SetTall(32)
509 | end
510 | querySummary:Dock(TOP)
511 | querySummary.Paint = function(s,w,h)
512 | draw.SimpleTextOutlined(queryTable.query, "DermaDefault", 5, 2, Color(0,255,0), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP, 1, color_black)
513 | if queryType == "SELECT" and queryTable.data then
514 | draw.SimpleText("Rows: " .. (queryTable.data and #queryTable.data or "0"), "DermaDefault", 5, 16, color_black, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
515 | end
516 | end
517 | elseif queryType == "INSERT" or queryType == "UPDATE" or queryType == "DELETE"
518 | or queryType == "COMMIT" or queryType == "ROLLBACK" or queryType == "CREATE" or queryType == "ALTER" or queryType == "DROP"
519 | or queryType == "SAVEPOINT" then
520 | local queryResult = vgui.Create("DPanel", panel1)
521 | queryResult:Dock(FILL)
522 | queryResult.Paint = function(s,w,h)
523 | draw.SimpleTextOutlined(queryTable.query, "DermaDefault", 5, 2, Color(0,255,0), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP, 1, color_black)
524 | draw.SimpleText(queryTable.message, "DermaDefault", 5, 16, color_black, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
525 | end
526 | else
527 | --- ???
528 | blobsProfiler.Log(blobsProfiler.L_NH_ERROR, "Unhandled SQL query type: ".. (queryTable.type or "UNKNOWN"))
529 | PrintTable(queryTable)
530 | end
531 |
532 | local qTab
533 | if isError then
534 | qTab = resultContainer:AddSheet( "Query "..queryID, panel1, "icon16/database_error.png")
535 | qTab.Tab.PaintOver = function(self, w, h)
536 | surface.SetDrawColor(255, 0, 0, 50)
537 | surface.DrawRect(0, 0, w, h)
538 | end
539 | else
540 | qTab = resultContainer:AddSheet( "Query "..queryID, panel1, "icon16/database.png")
541 | end
542 |
543 | qTab.Tab:SetTooltip(queryTable.query)
544 | end
545 |
546 | resultContainer:SetVisible(true)
547 | resultContainer:Dock(BOTTOM)
548 | resultContainer:SetTall(200)
549 | executeButton:Dock(BOTTOM)
550 | end
551 |
552 | dhtmlPanel:AddFunction("gmod", "receiveEditorContent", function(value)
553 | if luaState == "Client" then -- 'data' indicates a response from SV, ASSUME SV IF data IS PASSED!
554 | local results = splitAndProcessQueries(value)
555 | parentPanel.handleQueries("Client", results)
556 | elseif luaState == "Server" then
557 | net.Start("blobsProfiler:runSQLite")
558 | net.WriteString(value)
559 | net.SendToServer()
560 | end
561 | end)
562 |
563 | executeButton:Dock(BOTTOM)
564 | executeButton:SetText("Execute SQL")
565 |
566 | executeButton.DoClick = function()
567 | dhtmlPanel:RunJavascript([[
568 | var value = getEditorValue();
569 | gmod.receiveEditorContent(value);
570 | ]])
571 | end
572 | end
573 | })
--------------------------------------------------------------------------------
/lua/blobsprofiler/shared/modules/bp_timers.lua:
--------------------------------------------------------------------------------
1 | blobsProfiler.original_timerCreate = blobsProfiler.original_timerCreate or timer.Create
2 |
3 | blobsProfiler.createdTimers = blobsProfiler.createdTimers or {}
4 |
5 | function timer.Create(identifier, delay, reps, func)
6 | local debugInfo = debug.getinfo(func)
7 |
8 | blobsProfiler.createdTimers[tostring(identifier)] = {
9 | ["Identifier: " .. tostring(identifier) ] = tostring(identifier),
10 | ["Delay: "..delay] = delay,
11 | ["Repititions: "..reps] = reps or 0,
12 | [tostring(func)] = {
13 | fakeVarType = "function",
14 | func = CLIENT and func or tostring(func),
15 | lastlinedefined = debugInfo.lastlinedefined,
16 | linedefined = debugInfo.linedefined,
17 | short_src = debugInfo.short_src
18 | }
19 | }
20 |
21 | return blobsProfiler.original_timerCreate(identifier, delay, reps, func)
22 | end
23 |
24 | if SERVER then
25 | util.AddNetworkString("blobsProfiler:Timers_Control")
26 | net.Receive("blobsProfiler:Timers_Control", function(len,ply)
27 | if not blobsProfiler.CanAccess(ply, "Timers") then return end
28 | if not blobsProfiler.CanAccess(ply, "Timers_Control") then return end
29 |
30 | local timerName = net.ReadString()
31 | local Control = net.ReadUInt(2)
32 |
33 | if Control == 1 then
34 | timer.Pause(timerName)
35 | elseif Control == 2 then
36 | timer.UnPause(timerName)
37 | elseif Control == 3 then
38 | timer.Remove(timerName)
39 | elseif Control == 0 then
40 | blobsProfiler.createdTimers[timerName] = nil
41 | end
42 |
43 | blobsProfiler.SendModuleData("Timers", ply)
44 | end)
45 | end
46 |
47 | blobsProfiler.RegisterModule("Timers", {
48 | Icon = "icon16/clock.png",
49 | OrderPriority = 7,
50 | UpdateRealmData = function(luaState)
51 | if luaState == "Client" then
52 | -- why dont we just set it straight away ffs
53 | blobsProfiler.Client.Timers = blobsProfiler.createdTimers
54 | else
55 | net.Start("blobsProfiler:requestData")
56 | net.WriteString("Timers")
57 | net.SendToServer()
58 | end
59 | end,
60 | PrepServerData = function()
61 | for k,v in pairs(blobsProfiler.createdTimers) do
62 | local timerAlive = timer.Exists(k)
63 | v.timeLeft = timerAlive and timer.TimeLeft(k)
64 | v.isAlive = timerAlive
65 | end
66 | return blobsProfiler.createdTimers
67 | end,
68 | PreloadClient = true,
69 | PreloadServer = false,
70 | BuildPanel = function(luaState, parentPanel)
71 | blobsProfiler.buildDTree(luaState, parentPanel, "Timers")
72 | end,
73 | RefreshButton = "Refresh",
74 | RCFunctions = {
75 | ["table"] = {
76 | {
77 | name = "Expand/Collapse",
78 | func = function(ref, node)
79 | if node and IsValid(node) and node.SetExpanded then
80 | node:SetExpanded(not node:GetExpanded())
81 | end
82 | end,
83 | icon = "icon16/folder_explore.png"
84 | },
85 | {
86 | name = "Print",
87 | func = function(ref, node)
88 | print(ref.value)
89 | print(node.GlobalPath)
90 | end,
91 | icon = "icon16/application_osx_terminal.png"
92 | },
93 | { -- Pause/Resume timer
94 | name = function(ref, node, luaState)
95 | if luaState == "Client" and not timer.Exists(node.GlobalPath) then return end
96 | if luaState == "Server" and not ref.value.isAlive then return end
97 | local timeLeft = luaState == "Client" and timer.TimeLeft(node.GlobalPath) or ref.value.timeLeft
98 | if timeLeft < 0 then
99 | return "Resume"
100 | else
101 | return "Pause"
102 | end
103 | end,
104 | func = function(ref, node, luaState)
105 | if luaState == "Client" and not timer.Exists(node.GlobalPath) then return end
106 | if luaState == "Server" and not ref.value.isAlive then return end
107 | local timeLeft = luaState == "Client" and timer.TimeLeft(node.GlobalPath) or ref.value.timeLeft
108 | if timeLeft < 0 then
109 | if luaState == "Client" then
110 | timer.UnPause(node.GlobalPath)
111 | else
112 | net.Start("blobsProfiler:Timers_Control")
113 | net.WriteString(node.GlobalPath)
114 | net.WriteUInt(2, 2)
115 | net.SendToServer()
116 | end
117 |
118 | node.Icon:SetImage("icon16/clock_stop.png")
119 | else
120 | if luaState == "Client" then
121 | timer.Pause(node.GlobalPath)
122 | else
123 | net.Start("blobsProfiler:Timers_Control")
124 | net.WriteString(node.GlobalPath)
125 | net.WriteUInt(1, 2)
126 | net.SendToServer()
127 | end
128 | node.Icon:SetImage("icon16/clock_play.png")
129 | end
130 | end,
131 | onLoad = function(ref, node, luaState)
132 | if luaState == "Client" and not timer.Exists(node.GlobalPath) then return end
133 | if luaState == "Server" and not ref.value.isAlive then return end
134 | local timeLeft = luaState == "Client" and timer.TimeLeft(node.GlobalPath) or ref.value.timeLeft
135 | if timeLeft < 0 then
136 | node.Icon:SetImage("icon16/clock_stop.png")
137 | else
138 | node.Icon:SetImage("icon16/clock_play.png")
139 | end
140 | end,
141 | icon = function(ref, node, luaState)
142 | if luaState == "Client" and not timer.Exists(node.GlobalPath) then return end
143 | if luaState == "Server" and not ref.value.isAlive then return end
144 | local timeLeft = luaState == "Client" and timer.TimeLeft(node.GlobalPath) or ref.value.timeLeft
145 | if timeLeft < 0 then
146 | return "icon16/clock_play.png"
147 | else
148 | return "icon16/clock_stop.png"
149 | end
150 | end
151 | },
152 | { -- Delete timer
153 | name = function(ref, node, luaState)
154 | if (luaState == "Client" and not timer.Exists(node.GlobalPath)) or (luaState == "Server" and not ref.value.isAlive) then
155 | node.Label:SetTextColor(Color(255,0,0))
156 | return
157 | end
158 | return "Delete"
159 | end,
160 | func = function(ref, node, luaState)
161 | if luaState == "Client" then
162 | timer.Remove(node.GlobalPath)
163 | else
164 | net.Start("blobsProfiler:Timers_Control")
165 | net.WriteString(node.GlobalPath)
166 | net.WriteUInt(3, 2)
167 | net.SendToServer()
168 | end
169 | node.Label:SetTextColor(Color(255,0,0))
170 | node.Icon:SetImage("icon16/clock_delete.png")
171 | end,
172 | onLoad = function(ref, node, luaState)
173 | if (luaState == "Client" and not timer.Exists(node.GlobalPath)) or (luaState == "Server" and not ref.value.isAlive) then
174 | node.Label:SetTextColor(Color(255,0,0))
175 | node.Icon:SetImage("icon16/clock_delete.png")
176 | end
177 | end,
178 | icon = "icon16/clock_delete.png"
179 | },
180 | { -- Remove timer reference
181 | name = function(ref, node, luaState)
182 | if (luaState == "Client" and not timer.Exists(node.GlobalPath)) or (luaState == "Server" and not ref.value.isAlive) then
183 | return "Remove reference"
184 | end
185 | end,
186 | func = function(ref, node, luaState)
187 | blobsProfiler.createdTimers[node.GlobalPath] = nil
188 |
189 | if luaState == "Server" then
190 | net.Start("blobsProfiler:Timers_Control")
191 | net.WriteString(node.GlobalPath)
192 | net.WriteUInt(0, 2)
193 | net.SendToServer()
194 | end
195 |
196 | node:Remove()
197 | end,
198 | icon = "icon16/clock_red.png"
199 | }
200 | },
201 | ["function"] = {
202 | {
203 | name = "Toggle Profiling",
204 | func = function(ref, node)
205 | if node.Expander and IsValid(node.Expander) and node.Expander.SetChecked then
206 | local curChecked = node.Expander:GetChecked()
207 | node.Expander:SetChecked(not curChecked)
208 | node.Expander:OnChange(not curChecked)
209 | end
210 | end,
211 | condition = function(ref, node)
212 | if not node.Expander or not IsValid(node.Expander) or not node.Expander.SetChecked or not node.Expander:IsVisible() then
213 | return false
214 | end
215 |
216 | return true
217 | end,
218 | icon = "icon16/chart_bar.png"
219 | },
220 | {
221 | name = "View source",
222 | func = function(ref, node, luaState)
223 | if not string.EndsWith(ref.value.short_src, ".lua") then
224 | Derma_Message("Invalid function source: ".. ref.value.short_src.."\nOnly functions defined in Lua can be read!", "Function view source", "OK")
225 | return
226 | end
227 |
228 | net.Start("blobsProfiler:requestSource")
229 | net.WriteString(ref.value.short_src)
230 | net.WriteUInt(ref.value.linedefined, 16)
231 | net.WriteUInt(ref.value.lastlinedefined, 16)
232 | net.SendToServer()
233 | end,
234 | icon = "icon16/magnifier.png"
235 | },
236 | {
237 | name = "View properties",
238 | func = function(ref, node, luaState)
239 | local propertiesData = {}
240 | local propertiesTbl = table.Copy(ref.value)
241 | propertiesTbl.fakeVarType = nil
242 | propertiesData["debug.getinfo()"] = propertiesTbl
243 |
244 | local popupView = blobsProfiler.viewPropertiesPopup("View Function: " .. ref.key, propertiesData)
245 | end,
246 | icon = "icon16/magnifier.png"
247 | }
248 | }
249 | }
250 | })
--------------------------------------------------------------------------------
/lua/blobsprofiler/shared/sh_blobsprofiler.lua:
--------------------------------------------------------------------------------
1 | blobsProfiler = blobsProfiler or {}
2 |
3 | blobsProfiler.Client = blobsProfiler.Client or {}
4 | blobsProfiler.Server = blobsProfiler.Server or {}
5 |
6 | blobsProfiler.DebugMode = true -- This is currently only used by the logger
7 |
8 | blobsProfiler.L_NH_ERROR = -2 -- Throw error, no halt
9 | blobsProfiler.L_ERROR = -1 -- Throws error
10 | blobsProfiler.L_DEBUG = 0 -- For debug messages (can be hidden with DebugMode = false)
11 | blobsProfiler.L_LOG = 1 -- Prints, but also stores
12 | blobsProfiler.L_INFO = 2 -- Prints
13 |
14 |
15 | --[[
16 | blobsProfiler.Log( number lLevel, string lMessage, string lSendToServer, bool lErrNHStack)
17 | - Logs a lMessage with the lLevel type on lRealm(s)
18 |
19 | number lLevel: The log level, available values: blobsProfiler.L_* (DEFAULT: blobsProfiler.L_INFO)
20 | string lMessage: The message to actually log
21 | bool lSendToServer: Send log to server (DEFAULT: false) has no effect when used on server realm.
22 | bool lErrStack: Set to true to provide the error stack, only works with error log types (DEFAULT: false)
23 |
24 | Examples: -- I think I covered every case?
25 | blobsProfiler.Log("Test message")
26 | blobsProfiler.Log("Test message", true)
27 | blobsProfiler.Log("Test message", false, true)
28 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "Test message with debug")
29 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "Test message with debug to server", true)
30 | ]]
31 | blobsProfiler.Log = function(lLevel, lMessage, lSendToServer, lErrStack) -- TODO: send to server
32 | -- The shit we're about to do for variable arguments..
33 | if type(lLevel) == "string" and type(lMessage) == "boolean" then -- blobsProfiler.Log(lMessage, lSendToServer) / blobsProfiler.Log(lMessage, lSendToServer, lErrStack)
34 | lSendToServer = lMessage
35 | lMessage = lLevel
36 | lLevel = blobsProfiler.L_INFO
37 | lErrStack = lSendToServer or false
38 | elseif type(lLevel) == "string" then -- blobsProfiler.Log(lMessage)
39 | lMessage = lLevel
40 | lLevel = blobsProfiler.L_INFO
41 | lSendToServer = false
42 | lErrStack = false
43 | elseif type(lLevel) == "number" then -- Standard parameters
44 | if type(lMessage) ~= "string" then
45 | error("\nblobsProfiler.Log: Invalid parameters")
46 | end
47 | lSendToServer = lSendToServer or false
48 | lErrStack = lErrStack or false
49 | else
50 | error("\nblobsProfiler.Log: Invalid parameters")
51 | end
52 |
53 | local curTime = os.date("%H:%M:%S") -- tHaNK gOd lUa is caSe sEnSitiVE
54 | if lLevel == blobsProfiler.L_NH_ERROR then
55 | if lErrStack then
56 | ErrorNoHaltWithStack(string.format("[%s] [ERROR_S] blobsProfiler: %s", curTime, lMessage), 2)
57 | else
58 | ErrorNoHalt(string.format("[%s] [ERROR] blobsProfiler: %s\n", curTime, lMessage))
59 | end
60 | elseif lLevel == blobsProfiler.L_ERROR then
61 | if lErrStack then
62 | error(string.format("\n[%s] [CRITICAL_S] blobsProfiler: %s", curTime, lMessage), 2)
63 | else
64 | Error(string.format("[%s] [CRITICAL] blobsProfiler: %s\n", curTime, lMessage))
65 | end
66 | elseif lLevel == blobsProfiler.L_DEBUG and blobsProfiler.DebugMode then
67 | print(string.format("[%s] [DEBUG] blobsProfiler: %s", curTime, lMessage))
68 | elseif lLevel == blobsProfiler.L_LOG then
69 | print(string.format("[%s] [LOG] blobsProfiler: %s", curTime, lMessage))
70 | -- TODO: Store it
71 | else -- Default / Generic
72 | print(string.format("[%s] [INFO] blobsProfiler: %s", curTime, lMessage))
73 | end
74 | end
75 |
76 | blobsProfiler.CanAccess = function(cPly, cArea, cRealm)
77 | return cPly:IsUserGroup("superadmin") -- TODO
78 | end
79 |
80 | blobsProfiler.SetRealmData = function(luaState, moduleName, dataTable)
81 | blobsProfiler.Log(blobsProfiler.L_DEBUG, "Setting " .. moduleName .. " ".. luaState .. " data")
82 | blobsProfiler[luaState] = blobsProfiler[luaState] or {}
83 |
84 | local moduleSplit = string.Explode(".", moduleName) -- [1] is parent, [2] is submodule
85 |
86 | if #moduleSplit == 1 then -- ew
87 | blobsProfiler[luaState][moduleSplit[1]] = dataTable
88 | else
89 | blobsProfiler[luaState][moduleSplit[1]] = blobsProfiler[luaState][moduleSplit[1]] or {}
90 | blobsProfiler[luaState][moduleSplit[1]][moduleSplit[2]] = dataTable
91 | end
92 | end
93 |
94 | blobsProfiler.GetDataTableForRealm = function(luaState, rvarType)
95 | blobsProfiler[luaState] = blobsProfiler[luaState] or {}
96 |
97 | local moduleSplit = string.Explode(".", rvarType)
98 |
99 | if #moduleSplit == 1 then -- ew
100 | return blobsProfiler[luaState][moduleSplit[1]] or {} -- brother ewwww
101 | else
102 | blobsProfiler[luaState][moduleSplit[1]] = blobsProfiler[luaState][moduleSplit[1]] or {}
103 | return blobsProfiler[luaState][moduleSplit[1]][moduleSplit[2]] or {} --- what is that brother
104 | end
105 |
106 | return blobsProfiler[luaState][rvarType] or {}
107 | end
108 |
109 | blobsProfiler.TableSort = {}
110 | blobsProfiler.TableSort.KeyAlphabetical = function(t)
111 | local keys = {}
112 |
113 | for key in pairs(t) do
114 | table.insert(keys, key)
115 | end
116 |
117 | table.sort(keys, function(a, b)
118 | return a < b
119 | end)
120 |
121 | local sortedTable = {}
122 |
123 | for _, key in ipairs(keys) do
124 | sortedTable[key] = t[key]
125 | end
126 |
127 | return sortedTable
128 | end
129 | blobsProfiler.TableSort.ByIndex = function(t, i)
130 | return table.sort(t, function(a, b)
131 | return a[i] < b[i]
132 | end)
133 | end
134 | blobsProfiler.TableSort.SQLTableColSort = function(parentTable)
135 | local parentKeys = {}
136 | for parentKey in pairs(parentTable) do
137 | table.insert(parentKeys, parentKey)
138 | end
139 |
140 | table.sort(parentKeys)
141 |
142 | local sortedParentTable = {}
143 | for _, parentKey in ipairs(parentKeys) do
144 | local childTable = parentTable[parentKey]
145 |
146 | local childKeys = {}
147 | for childKey in pairs(childTable) do
148 | table.insert(childKeys, childKey)
149 | end
150 |
151 | table.sort(childKeys, function(a, b)
152 | return childTable[a].ID < childTable[b].ID
153 | end)
154 |
155 | local sortedChildTable = {}
156 | for _, childKey in ipairs(childKeys) do
157 | sortedChildTable[childKey] = childTable[childKey]
158 | end
159 |
160 | sortedParentTable[parentKey] = sortedChildTable
161 | end
162 |
163 | return sortedParentTable
164 | end
--------------------------------------------------------------------------------
/lua/blobsprofiler/shared/sh_modules.lua:
--------------------------------------------------------------------------------
1 | blobsProfiler = blobsProfiler or {}
2 | blobsProfiler.Modules = blobsProfiler.Modules or {}
3 |
4 | blobsProfiler.RegisterModule = function(Name, ModuleConfig)
5 | blobsProfiler.Modules[Name] = ModuleConfig
6 |
7 | print("[blobsProfiler] Module: ".. Name .." - Loaded!")
8 | end
9 |
10 | blobsProfiler.RegisterSubModule = function(ParentModule, Name, ModuleConfig)
11 | blobsProfiler.Modules[ParentModule].SubModules = blobsProfiler.Modules[ParentModule].SubModules or {}
12 | blobsProfiler.Modules[ParentModule].SubModules[Name] = ModuleConfig
13 |
14 | print("[blobsProfiler] ".. ParentModule .." SubModule: ".. Name .." - Loaded!")
15 | end
16 |
17 | blobsProfiler.GetModule = function(fullModuleName)
18 | local splitModuleName = string.Explode(".", fullModuleName)
19 | if not blobsProfiler.Modules[splitModuleName[1]] then return end
20 |
21 | if #splitModuleName == 1 then
22 | return blobsProfiler.Modules[splitModuleName[1]]
23 | else
24 | return blobsProfiler.Modules[splitModuleName[1]].SubModules[splitModuleName[2]], blobsProfiler.Modules[splitModuleName[1]]
25 | end
26 | end
27 |
28 | blobsProfiler.GetRCFunctionsTable = function(fullModuleName)
29 | local moduleTable = blobsProfiler.GetModule(fullModuleName)
30 | return moduleTable.RCFunctions or blobsProfiler.Menu.RCFunctions_DEFAULT
31 | end
--------------------------------------------------------------------------------
/lua/blobsprofiler/shared/sh_netstream.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | NetStream - 2.0.0
3 |
4 | Alexander Grist-Hucker
5 | http://www.revotech.org
6 |
7 | Credits to:
8 | thelastpenguin for pON.
9 | https://github.com/thelastpenguin/gLUA-Library/tree/master/pON
10 | --]]
11 |
12 |
13 | local type, error, pcall, pairs, _player = type, error, pcall, pairs, player;
14 |
15 | if (!pon) then
16 | error("NetStream: Unable to find pON!");
17 | end;
18 |
19 | AddCSLuaFile();
20 |
21 | netstream = netstream or {};
22 |
23 | local stored = netstream.stored or {};
24 | netstream.stored = stored;
25 |
26 | local cache = netstream.cache or {};
27 | netstream.cache = cache;
28 |
29 | -- A function to split data for a data stream.
30 | function netstream.Split(data)
31 | local index = 1;
32 | local result = {};
33 | local buffer = {};
34 |
35 | for i = 0, string.len(data) do
36 | buffer[#buffer + 1] = string.sub(data, i, i);
37 |
38 | if (#buffer == 32768) then
39 | result[#result + 1] = table.concat(buffer);
40 | index = index + 1;
41 | buffer = {};
42 | end;
43 | end;
44 |
45 | result[#result + 1] = table.concat(buffer);
46 |
47 | return result;
48 | end;
49 |
50 | -- A function to hook a net stream.
51 | function netstream.Hook(name, Callback)
52 | stored[name] = Callback;
53 | end;
54 |
55 | if (SERVER) then
56 | util.AddNetworkString("NetStreamDS");
57 | util.AddNetworkString("NetStreamHeavy");
58 |
59 | -- A function to start a net stream.
60 | function netstream.Start(player, name, ...)
61 | local recipients = {};
62 | local bShouldSend = false;
63 |
64 | if (!istable(player)) then
65 | if (!IsValid(player)) then
66 | player = _player.GetAll();
67 | else
68 | player = {player};
69 | end;
70 | end;
71 |
72 | for k, v in ipairs(player) do
73 | if (type(v) == "Player") then
74 | recipients[#recipients + 1] = v;
75 |
76 | bShouldSend = true;
77 | end;
78 | end;
79 |
80 | local encodedData = pon.encode({...});
81 |
82 | if (encodedData and #encodedData > 0 and bShouldSend) then
83 | net.Start("NetStreamDS");
84 | net.WriteString(name);
85 | net.WriteUInt(#encodedData, 32);
86 | net.WriteData(encodedData, #encodedData);
87 | net.Send(recipients);
88 | end;
89 | end;
90 |
91 | -- A function to start a > 64KB net stream.
92 | function netstream.Heavy(player, name, ...)
93 | local recipients = {};
94 | local bShouldSend = false;
95 |
96 | if (!istable(player)) then
97 | if (!IsValid(player)) then
98 | player = _player.GetAll();
99 | else
100 | player = {player};
101 | end;
102 | end;
103 |
104 | for k, v in ipairs(player) do
105 | if (type(v) == "Player") then
106 | recipients[#recipients + 1] = v;
107 |
108 | bShouldSend = true;
109 | end;
110 | end;
111 |
112 | local encodedData = pon.encode({...});
113 | local split = netstream.Split(encodedData);
114 |
115 | if (encodedData and #encodedData > 0 and bShouldSend) then
116 | for k, v in ipairs(split) do
117 | if #split < 3 then
118 | net.Start("NetStreamHeavy");
119 | net.WriteString(name);
120 | net.WriteUInt(#v, 32);
121 | net.WriteData(v, #v);
122 | net.WriteUInt(k, 8);
123 | net.WriteUInt(#split, 8);
124 | net.Send(recipients);
125 | else
126 | timer.Simple(0.5 * k, function()
127 | net.Start("NetStreamHeavy");
128 | net.WriteString(name);
129 | net.WriteUInt(#v, 32);
130 | net.WriteData(v, #v);
131 | net.WriteUInt(k, 8);
132 | net.WriteUInt(#split, 8);
133 | net.Send(recipients);
134 | end)
135 | end
136 | end;
137 | end;
138 | end;
139 |
140 | net.Receive("NetStreamDS", function(length, player)
141 | local NS_DS_NAME = net.ReadString();
142 | local NS_DS_LENGTH = net.ReadUInt(32);
143 | local NS_DS_DATA = net.ReadData(NS_DS_LENGTH);
144 |
145 | if (NS_DS_NAME and NS_DS_DATA and NS_DS_LENGTH) then
146 | player.nsDataStreamName = NS_DS_NAME;
147 | player.nsDataStreamData = "";
148 |
149 | if (player.nsDataStreamName and player.nsDataStreamData) then
150 | player.nsDataStreamData = NS_DS_DATA;
151 |
152 | if (stored[player.nsDataStreamName]) then
153 | local bStatus, value = pcall(pon.decode, player.nsDataStreamData);
154 |
155 | if (bStatus) then
156 | stored[player.nsDataStreamName](player, unpack(value));
157 | else
158 | ErrorNoHalt("NetStream: '"..NS_DS_NAME.."'\n"..value.."\n");
159 | end;
160 | end;
161 |
162 | player.nsDataStreamName = nil;
163 | player.nsDataStreamData = nil;
164 | end;
165 | end;
166 |
167 | NS_DS_NAME, NS_DS_DATA, NS_DS_LENGTH = nil, nil, nil;
168 | end);
169 | else
170 | -- A function to start a net stream.
171 | function netstream.Start(name, ...)
172 | local encodedData = pon.encode({...});
173 |
174 | if (encodedData and #encodedData > 0) then
175 | net.Start("NetStreamDS");
176 | net.WriteString(name);
177 | net.WriteUInt(#encodedData, 32);
178 | net.WriteData(encodedData, #encodedData);
179 | net.SendToServer();
180 | end;
181 | end;
182 |
183 | net.Receive("NetStreamDS", function(length)
184 | local NS_DS_NAME = net.ReadString();
185 | local NS_DS_LENGTH = net.ReadUInt(32);
186 | local NS_DS_DATA = net.ReadData(NS_DS_LENGTH);
187 |
188 | if (NS_DS_NAME and NS_DS_DATA and NS_DS_LENGTH) then
189 | if (stored[NS_DS_NAME]) then
190 | local bStatus, value = pcall(pon.decode, NS_DS_DATA);
191 |
192 | if (bStatus) then
193 | stored[NS_DS_NAME](unpack(value));
194 | else
195 | ErrorNoHalt("NetStream: '"..NS_DS_NAME.."'\n"..value.."\n");
196 | end;
197 | end;
198 | end;
199 |
200 | NS_DS_NAME, NS_DS_DATA, NS_DS_LENGTH = nil, nil, nil;
201 | end);
202 |
203 | net.Receive("NetStreamHeavy", function(length)
204 | local NS_DS_NAME = net.ReadString();
205 | local NS_DS_LENGTH = net.ReadUInt(32);
206 | local NS_DS_DATA = net.ReadData(NS_DS_LENGTH);
207 | local NS_DS_PIECE = net.ReadUInt(8);
208 | local NS_DS_TOTAL = net.ReadUInt(8);
209 |
210 | if (!cache[NS_DS_NAME]) then
211 | cache[NS_DS_NAME] = "";
212 | end;
213 |
214 | if (NS_DS_NAME and NS_DS_DATA and NS_DS_LENGTH) then
215 | if (NS_DS_PIECE < NS_DS_TOTAL) then
216 | if (NS_DS_PIECE == 1) then
217 | cache[NS_DS_NAME] = "";
218 | end;
219 |
220 | cache[NS_DS_NAME] = cache[NS_DS_NAME]..NS_DS_DATA;
221 | else
222 | cache[NS_DS_NAME] = cache[NS_DS_NAME]..NS_DS_DATA;
223 |
224 | if (stored[NS_DS_NAME]) then
225 | local bStatus, value = pcall(pon.decode, cache[NS_DS_NAME]);
226 |
227 | if (bStatus) then
228 | stored[NS_DS_NAME](unpack(value));
229 | else
230 | ErrorNoHalt("NetStream Heavy: '"..NS_DS_NAME.."'\n"..value.."\n");
231 | end;
232 |
233 | cache[NS_DS_NAME] = nil;
234 | end;
235 | end;
236 | end;
237 |
238 | NS_DS_NAME, NS_DS_DATA, NS_DS_LENGTH, NS_DS_PIECE, NS_DS_TOTAL = nil, nil, nil, nil, nil;
239 | end);
240 | end;
--------------------------------------------------------------------------------
/lua/blobsprofiler/shared/sh_pon.lua:
--------------------------------------------------------------------------------
1 | --[[
2 |
3 | DEVELOPMENTAL VERSION;
4 |
5 | VERSION 1.2.2
6 | Copyright thelastpenguin™
7 |
8 | You may use this for any purpose as long as:
9 | - You don't remove this copyright notice.
10 | - You don't claim this to be your own.
11 | - You properly credit the author, thelastpenguin™, if you publish your work based on (and/or using) this.
12 |
13 | If you modify the code for any purpose, the above still applies to the modified code.
14 |
15 | The author is not held responsible for any damages incured from the use of pon, you use it at your own risk.
16 |
17 | DATA TYPES SUPPORTED:
18 | - tables - k,v - pointers
19 | - strings - k,v - pointers
20 | - numbers - k,v
21 | - booleans- k,v
22 | - Vectors - k,v
23 | - Angles - k,v
24 | - Entities- k,v
25 | - Players - k,v
26 |
27 | CHANGE LOG
28 | V 1.1.0
29 | - Added Vehicle, NPC, NextBot, Player, Weapon
30 | V 1.2.0
31 | - Added custom handling for k,v tables without any array component.
32 | V 1.2.1
33 | - fixed deserialization bug.
34 |
35 | THANKS TO...
36 | - VERCAS for the inspiration.
37 | ]]
38 |
39 |
40 | local pon = {};
41 | _G.pon = pon;
42 |
43 | local type, count = type, table.Count ;
44 | local tonumber = tonumber ;
45 | local format = string.format;
46 | do
47 | local type, count = type, table.Count ;
48 | local tonumber = tonumber ;
49 | local format = string.format;
50 |
51 | local encode = {};
52 |
53 | local tryCache ;
54 |
55 | local cacheSize = 0;
56 |
57 | encode['table'] = function( self, tbl, output, cache )
58 |
59 | if( cache[ tbl ] )then
60 | output[ #output + 1 ] = format('(%x)', cache[tbl] );
61 | return ;
62 | else
63 | cacheSize = cacheSize + 1;
64 | cache[ tbl ] = cacheSize;
65 | end
66 |
67 |
68 | local first = next(tbl, nil)
69 | local predictedNumeric = 1
70 | local lastKey = nil
71 | -- starts with a numeric dealio
72 | if first == 1 then
73 | output[#output + 1] = '{'
74 |
75 | for k,v in next, tbl do
76 | if k == predictedNumeric then
77 | predictedNumeric = predictedNumeric + 1
78 |
79 | local tv = type(v)
80 | if tv == 'string' then
81 | local pid = cache[v]
82 | if pid then
83 | output[#output + 1] = format('(%x)', pid)
84 | else
85 | cacheSize = cacheSize + 1
86 | cache[v] = cacheSize
87 | self.string(self, v, output, cache)
88 | end
89 | else
90 | self[tv](self, v, output, cache)
91 | end
92 |
93 | else
94 | break
95 | end
96 | end
97 |
98 | predictedNumeric = predictedNumeric - 1
99 | else
100 | predictedNumeric = nil
101 | end
102 |
103 | if predictedNumeric == nil then
104 | output[#output + 1] = '[' -- no array component
105 | else
106 | output[#output + 1] = '~' -- array component came first so shit needs to happen
107 | end
108 |
109 | for k, v in next, tbl, predictedNumeric do
110 | local tk, tv = type(k), type(v)
111 |
112 | -- WRITE KEY
113 | if tk == 'string' then
114 | local pid = cache[ k ];
115 | if( pid )then
116 | output[ #output + 1 ] = format('(%x)', pid );
117 | else
118 | cacheSize = cacheSize + 1;
119 | cache[ k ] = cacheSize;
120 |
121 | self.string( self, k, output, cache );
122 | end
123 | else
124 | self[tk](self, k, output, cache)
125 | end
126 |
127 | -- WRITE VALUE
128 | if( tv == 'string' )then
129 | local pid = cache[ v ];
130 | if( pid )then
131 | output[ #output + 1 ] = format('(%x)', pid );
132 | else
133 | cacheSize = cacheSize + 1;
134 | cache[ v ] = cacheSize;
135 |
136 | self.string( self, v, output, cache );
137 | end
138 | else
139 | self[ tv ]( self, v, output, cache );
140 | end
141 | end
142 |
143 | output[#output + 1] = '}'
144 | end
145 | -- ENCODE STRING
146 | local gsub = string.gsub ;
147 | encode['string'] = function( self, str, output )
148 | --if tryCache( str, output ) then return end
149 | local estr, count = gsub( str, ";", "\\;");
150 | if( count == 0 )then
151 | output[ #output + 1 ] = '\''..str..';';
152 | else
153 | output[ #output + 1 ] = '"'..estr..'";';
154 | end
155 | end
156 | -- ENCODE NUMBER
157 | encode['number'] = function( self, num, output )
158 | if num%1 == 0 then
159 | if num < 0 then
160 | output[ #output + 1 ] = format( 'x%x;', -num );
161 | else
162 | output[ #output + 1 ] = format('X%x;', num );
163 | end
164 | else
165 | output[ #output + 1 ] = tonumber( num )..';';
166 | end
167 | end
168 | -- ENCODE BOOLEAN
169 | encode['boolean'] = function( self, val, output )
170 | output[ #output + 1 ] = val and 't' or 'f'
171 | end
172 | -- ENCODE VECTOR
173 | encode['Vector'] = function( self, val, output )
174 | output[ #output + 1 ] = ('v'..val.x..','..val.y)..(','..val.z..';');
175 | end
176 | -- ENCODE ANGLE
177 | encode['Angle'] = function( self, val, output )
178 | output[ #output + 1 ] = ('a'..val.p..','..val.y)..(','..val.r..';');
179 | end
180 | encode['Entity'] = function( self, val, output )
181 | output[ #output + 1] = 'E'..(IsValid( val ) and (val:EntIndex( )..';') or '#');
182 | end
183 | encode['Player'] = encode['Entity'];
184 | encode['Vehicle'] = encode['Entity'];
185 | encode['Weapon'] = encode['Entity'];
186 | encode['NPC'] = encode['Entity'];
187 | encode['NextBot'] = encode['Entity'];
188 | encode['PhysObj'] = encode['Entity'];
189 |
190 | encode['nil'] = function()
191 | output[ #output + 1 ] = '?';
192 | end
193 | encode.__index = function( key )
194 | ErrorNoHalt('Type: '..key..' can not be encoded. Encoded as as pass-over value.');
195 | return encode['nil'];
196 | end
197 |
198 | do
199 | local empty, concat = table.Empty, table.concat ;
200 | function pon.encode( tbl )
201 | local output = {};
202 | cacheSize = 0;
203 | encode[ 'table' ]( encode, tbl, output, {} );
204 | local res = concat( output );
205 |
206 | return res;
207 | end
208 | end
209 | end
210 |
211 | do
212 | local tonumber = tonumber ;
213 | local find, sub, gsub, Explode = string.find, string.sub, string.gsub, string.Explode ;
214 | local Vector, Angle, Entity = Vector, Angle, Entity ;
215 |
216 | local decode = {};
217 | decode['{'] = function( self, index, str, cache )
218 |
219 | local cur = {};
220 | cache[ #cache + 1 ] = cur;
221 |
222 | local k, v, tk, tv = 1, nil, nil, nil;
223 | while( true )do
224 | tv = sub( str, index, index );
225 | if( not tv or tv == '~' )then
226 | index = index + 1;
227 | break ;
228 | end
229 | if( tv == '}' )then
230 | return index + 1, cur;
231 | end
232 |
233 | -- READ THE VALUE
234 | index = index + 1;
235 | index, v = self[ tv ]( self, index, str, cache );
236 | cur[ k ] = v;
237 |
238 | k = k + 1;
239 | end
240 |
241 | while( true )do
242 | tk = sub( str, index, index );
243 | if( not tk or tk == '}' )then
244 | index = index + 1;
245 | break ;
246 | end
247 |
248 | -- READ THE KEY
249 |
250 | index = index + 1;
251 | index, k = self[ tk ]( self, index, str, cache );
252 |
253 | -- READ THE VALUE
254 | tv = sub( str, index, index );
255 | index = index + 1;
256 | index, v = self[ tv ]( self, index, str, cache );
257 |
258 | cur[ k ] = v;
259 | end
260 |
261 | return index, cur;
262 | end
263 | decode['['] = function( self, index, str, cache )
264 |
265 | local cur = {};
266 | cache[ #cache + 1 ] = cur;
267 |
268 | local k, v, tk, tv = 1, nil, nil, nil;
269 | while( true )do
270 | tk = sub( str, index, index );
271 | if( not tk or tk == '}' )then
272 | index = index + 1;
273 | break ;
274 | end
275 |
276 | -- READ THE KEY
277 | index = index + 1;
278 | index, k = self[ tk ]( self, index, str, cache );
279 | if not k then continue end
280 |
281 | -- READ THE VALUE
282 | tv = sub( str, index, index );
283 | index = index + 1;
284 | if not self[tv] then
285 | print('did not find type: '..tv)
286 | end
287 | index, v = self[ tv ]( self, index, str, cache );
288 |
289 | cur[ k ] = v;
290 | end
291 |
292 | return index, cur;
293 | end
294 |
295 | -- STRING
296 | decode['"'] = function( self, index, str, cache )
297 | local finish = find( str, '";', index, true );
298 | local res = gsub( sub( str, index, finish - 1 ), '\\;', ';' );
299 | index = finish + 2;
300 |
301 | cache[ #cache + 1 ] = res;
302 | return index, res;
303 | end
304 | -- STRING NO ESCAPING NEEDED
305 | decode['\''] = function( self, index, str, cache )
306 | local finish = find( str, ';', index, true );
307 | local res = sub( str, index, finish - 1 )
308 | index = finish + 1;
309 |
310 | cache[ #cache + 1 ] = res;
311 | return index, res;
312 | end
313 |
314 | -- NUMBER
315 | decode['n'] = function( self, index, str, cache )
316 | index = index - 1;
317 | local finish = find( str, ';', index, true );
318 | local num = tonumber( sub( str, index, finish - 1 ) );
319 | index = finish + 1;
320 | return index, num;
321 | end
322 | decode['0'] = decode['n'];
323 | decode['1'] = decode['n'];
324 | decode['2'] = decode['n'];
325 | decode['3'] = decode['n'];
326 | decode['4'] = decode['n'];
327 | decode['5'] = decode['n'];
328 | decode['6'] = decode['n'];
329 | decode['7'] = decode['n'];
330 | decode['8'] = decode['n'];
331 | decode['9'] = decode['n'];
332 | decode['-'] = decode['n'];
333 | -- positive hex
334 | decode['X'] = function( self, index, str, cache )
335 | local finish = find( str, ';', index, true );
336 | local num = tonumber( sub( str, index, finish - 1), 16 );
337 | index = finish + 1;
338 | return index, num;
339 | end
340 | -- negative hex
341 | decode['x'] = function( self, index, str, cache )
342 | local finish = find( str, ';', index, true );
343 | local num = -tonumber( sub( str, index, finish - 1), 16 );
344 | index = finish + 1;
345 | return index, num;
346 | end
347 |
348 | -- POINTER
349 | decode['('] = function( self, index, str, cache )
350 | local finish = find( str, ')', index, true );
351 | local num = tonumber( sub( str, index, finish - 1), 16 );
352 | index = finish + 1;
353 | return index, cache[ num ];
354 | end
355 |
356 | -- BOOLEAN. ONE DATA TYPE FOR YES, ANOTHER FOR NO.
357 | decode[ 't' ] = function( self, index )
358 | return index, true;
359 | end
360 | decode[ 'f' ] = function( self, index )
361 | return index, false;
362 | end
363 |
364 | -- VECTOR
365 | decode[ 'v' ] = function( self, index, str, cache )
366 | local finish = find( str, ';', index, true );
367 | local vecStr = sub( str, index, finish - 1 );
368 | index = finish + 1; -- update the index.
369 | local segs = Explode( ',', vecStr, false );
370 | return index, Vector( tonumber( segs[1] ), tonumber( segs[2] ), tonumber( segs[3] ) );
371 | end
372 | -- ANGLE
373 | decode[ 'a' ] = function( self, index, str, cache )
374 | local finish = find( str, ';', index, true );
375 | local angStr = sub( str, index, finish - 1 );
376 | index = finish + 1; -- update the index.
377 | local segs = Explode( ',', angStr, false );
378 | return index, Angle( tonumber( segs[1] ), tonumber( segs[2] ), tonumber( segs[3] ) );
379 | end
380 | -- ENTITY
381 | decode[ 'E' ] = function( self, index, str, cache )
382 | if( str[index] == '#' )then
383 | index = index + 1;
384 | return index, NULL ;
385 | else
386 | local finish = find( str, ';', index, true );
387 | local num = tonumber( sub( str, index, finish - 1 ) );
388 | index = finish + 1;
389 | return index, Entity( num );
390 | end
391 | end
392 | -- PLAYER
393 | decode[ 'P' ] = function( self, index, str, cache )
394 | local finish = find( str, ';', index, true );
395 | local num = tonumber( sub( str, index, finish - 1 ) );
396 | index = finish + 1;
397 | return index, Entity( num ) or NULL;
398 | end
399 | -- NIL
400 | decode['?'] = function( self, index, str, cache )
401 | return index + 1, nil;
402 | end
403 |
404 |
405 | function pon.decode( data )
406 | local _, res = decode[sub(data,1,1)]( decode, 2, data, {});
407 | return res;
408 | end
409 | end
--------------------------------------------------------------------------------