├── .github └── FUNDING.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── lua ├── autorun │ └── sh_badcoderz.lua └── badcoderz │ ├── client │ ├── cl_fonts.lua │ ├── cl_network.lua │ ├── cl_proceduralthingcredits.lua │ ├── cl_ui.lua │ ├── cl_utils.lua │ └── lapin_tdlib.lua │ ├── server │ └── sv_network.lua │ ├── sh_code_smells.lua │ ├── sh_compiled_functions.lua │ ├── sh_data.lua │ ├── sh_gmaparser.lua │ └── sh_luajit_decompiler.lua └── sound └── badcoderz └── oof.mp3 /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ["https://www.paypal.me/lpn64"] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Lua sources 2 | luac.out 3 | 4 | # luarocks build files 5 | *.src.rock 6 | *.zip 7 | *.tar.gz 8 | 9 | # Object files 10 | *.o 11 | *.os 12 | *.ko 13 | *.obj 14 | *.elf 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | *.def 26 | *.exp 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | 42 | #Specific 43 | *do_not_remove.lua 44 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Don't bother merging code that is uncorrectly indented (aka using anything else than tabs (size=4space)) 2 | 3 | You can use [GluaFixer](https://github.com/FPtje/GLuaFixer) 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Pierre FICHEPOIL 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 | 2 | ![BadCoderz Blue](https://github.com/user-attachments/assets/9f2d930c-8fa1-4992-bf73-8282f590a05b) 3 | 4 | 5 | Anyone telling you this addon reports false-positifs is stupid or lying, CANNOT report false positives, just remember to check the number of calls of the reported functions 6 | 7 | # BadCoderz 8 | ### Find unoptimized addons and KILL the devs who made them 9 | 10 | 11 | Roasting darkrp """devs""" since 2019 12 | 13 | 14 | [Workshop page](https://steamcommunity.com/sharedfiles/filedetails/?id=1955436281) 15 | 16 | [English video](https://www.youtube.com/watch?v=TVT5k3CHPQQ) 17 | 18 | [Video in french for my fellows baguettes](https://youtu.be/nNvq3CKccic) (Oudated since the video doesn't show GMA path support) 19 | 20 | 21 | ## **_How is it different from a profiler ?_** 22 | 23 | A profiler doesn't tell you if code is shit so you don't know if there is a good reason for a code to be "slow", here BadCoderz highlights you the shit code of your server and explains you why it's shit. 24 | 25 | ![](https://i.imgur.com/gyO07pu.png) Finds un-optimized code and bottlenecks 26 | 27 | ![](https://i.imgur.com/gyO07pu.png) Supports code ran in hooks, GM functions, Entities and Panels 28 | 29 | ![](https://i.imgur.com/gyO07pu.png) Exports reports in TXT format to work with your team 30 | 31 | ![](https://i.imgur.com/gyO07pu.png) Detailed call stack history 32 | 33 | ![](https://i.imgur.com/gyO07pu.png) **Even shows the absolute path with GMAs** (Gmod doesn't natively support it) 34 | 35 | ![](https://i.imgur.com/gyO07pu.png) Code preview 36 | 37 | ![](https://i.imgur.com/gyO07pu.png) Ingame advices and hook/function documentation (Right click on it opens the wiki page) 38 | 39 | ![](https://i.imgur.com/gyO07pu.png) Compatibility with [GLib](https://github.com/notcake/glib) allowing you to decompile functions generated with CompileString or RunString (you need to download GLib if you want to use it) 40 | 41 | ![](https://i.imgur.com/gyO07pu.png) [RunString explorer](https://www.youtube.com/watch?v=Plwa65CNePw) (no defensive/offensive mechanism) 42 | 43 | 1. Download BadCoderz 44 | 2. Identify the addons coded like shit 45 | 3. Do something about it : 46 | * Remove the addon 47 | * Fix it 48 | * Send the report to the dev so he can fix it 49 | 7. (Optional) Smash the dev's head on the ground 50 | 8. Keep your players because your server now has much better performances and less shitty code 51 | 52 | BadCoderz isn't exactly a profiler, a profiler is only measuring how much time the CPU spend on each function, without telling you if the CPU has a good reason to do so. 53 | 54 | A profiler can flag a function as "heavy/slow" without knowing if it's actually optimized. 55 | 56 | BadCoderz uses a database of known mistakes (function and contexts) to find the unoptimized code in your addons/gamemode. 57 | 58 | ## _So, how does it work exactly ?_ 59 | 60 | Simply open BadCoderz using the `badcoderz` command. 61 | 62 | Then you can start scanning for unoptimized code on the Client or on the Server (or even both at the same time) 63 | 64 | https://www.youtube.com/embed/CxLTw-b6ObY 65 | 66 | In this video i downloaded 7 hud from the workshop, and as expected the code was total shit. 67 | 68 | The BF4 hud, for example re-reads the textures from the disk on each frame. 69 | 70 | If you put your mouse over the Function name it tells you what the function does (fetched from the wiki on the fly) and explains you why it's wrong to use it in this context. 71 | 72 | Right clicking on the function name opens the wiki page. (Same with the hook). 73 | 74 | ![](https://i.imgur.com/iK4wZCE.png) 75 | 76 | Bellow we can see the places (in the code) where the function was called. 77 | 78 | It also shows you the call stack. 79 | 80 | Hover the one of the call stack line and it will show you a preview of the code 81 | 82 | ![](https://i.imgur.com/VcTb2ZW.png) 83 | 84 | ## _Wait, do i need to understand Lua to use it ?_ 85 | 86 | Not at all, but it's obviously much better to know how Lua and gmod works, but with common sense you can easily find which addon is coded like shit. 87 | 88 | [You can also export the Clientside/Serverside report to .txt and share it with your team/the dev of the addon.](https://pastebin.com/TGd1HNJ8) 89 | 90 | ![](https://i.imgur.com/OvCscUW.png) 91 | 92 | Obviously, there might be some "false positive" detections, but with common sense and with the stack call history, you should be fine. 93 | 94 | ## _I got a short memory, can you explain me quickly the difference between a profiler and this ?_ 95 | 96 | A profiler finds CPU intensive functions without knowing if they are actually coded like shit, this addon finds all the functions coded like shit. 97 | 98 | Then you can actually blame the dev who coded it for making shit code. 99 | 100 | Most of the people who use fprofiler actually don't know what they're doing. 101 | 102 | Optimized doesn't mean being fast, optimized means being efficient. 103 | 104 | ## _I still got a short memory, who can use this addon ?_ 105 | 106 | Here is a non-exhaustive list : 107 | 108 | * Server owner : His server is lagging, his players are leaving or complaining, he want to fire his server dev, but since he is his brother-in-law he need evidence. 109 | 110 | * Server owner 2 : His server is lagging and he wants to find which addon is coded like ass. 111 | 112 | * Junior Lua dev releasing his first addon : He wants to be sure he didn't do anything that would destroy the server performance. 113 | 114 | ## _Anything else i should know ?_ 115 | 116 | Keep in mind this is a technical tool, it's meant to be used with a brain and at least one eye. 117 | 118 | 119 | If you want your staff to help you, export a report and send it to them. 120 | 121 | The addon is using debug functions, so it's not compatible with SecureGmod 122 | 123 | Thanks : 124 | 125 | [RE Lexi](https://steamcommunity.com/profiles/76561198090218596) for the banner 126 | 127 | Threebow for [TDLib](https://github.com/Threebow/tdlib) 128 | 129 | [Metastruct](https://github.com/Metastruct) for the [html lua editor](https://github.com/Metastruct/lua_editor) 130 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Only the very last version of the addon supports security updates 6 | 7 | ## Reporting a Vulnerability 8 | 9 | To report a vulnerability describe the exploit (step by step) or point out to the part of the code that leads to exploit. 10 | -------------------------------------------------------------------------------- /lua/autorun/sh_badcoderz.lua: -------------------------------------------------------------------------------- 1 | BadCoderz = BadCoderz or {} 2 | 3 | local metaplayer = FindMetaTable("Player") 4 | 5 | function metaplayer:CanUseBadCoderz() 6 | return game.SinglePlayer() or self:IsSuperAdmin() 7 | end 8 | 9 | 10 | if SERVER then 11 | AddCSLuaFile() 12 | resource.AddWorkshop("1804554591") -- OOF sound, literally 13 | AddCSLuaFile("badcoderz/sh_data.lua") 14 | AddCSLuaFile("badcoderz/sh_code_smells.lua") 15 | AddCSLuaFile("badcoderz/sh_compiled_functions.lua") 16 | AddCSLuaFile("badcoderz/client/cl_utils.lua") 17 | AddCSLuaFile("badcoderz/client/cl_fonts.lua") 18 | AddCSLuaFile("badcoderz/client/cl_ui.lua") 19 | AddCSLuaFile("badcoderz/client/lapin_tdlib.lua") 20 | AddCSLuaFile("badcoderz/client/cl_network.lua") 21 | AddCSLuaFile("badcoderz/client/cl_proceduralthingcredits.lua") 22 | AddCSLuaFile("badcoderz/sh_gmaparser.lua") 23 | AddCSLuaFile("badcoderz/sh_luajit_decompiler.lua") 24 | end 25 | 26 | include("badcoderz/sh_data.lua") 27 | include("badcoderz/sh_code_smells.lua") 28 | include("badcoderz/sh_compiled_functions.lua") 29 | include("badcoderz/sh_luajit_decompiler.lua") 30 | 31 | if CLIENT then 32 | include("badcoderz/client/cl_utils.lua") 33 | include("badcoderz/client/cl_fonts.lua") 34 | include("badcoderz/client/cl_ui.lua") 35 | include("badcoderz/client/cl_network.lua") 36 | include("badcoderz/client/cl_proceduralthingcredits.lua") 37 | end 38 | 39 | if SERVER then 40 | include("badcoderz/server/sv_network.lua") 41 | end 42 | 43 | include("badcoderz/sh_gmaparser.lua") 44 | 45 | -------------------------------------------------------------------------------- /lua/badcoderz/client/cl_fonts.lua: -------------------------------------------------------------------------------- 1 | surface.CreateFont("BadCoderzFontSmall", { 2 | font = "Roboto Lt", 3 | size = 22, 4 | weight = 200 5 | }) 6 | 7 | surface.CreateFont("BadCoderzFont1", { 8 | font = "Roboto Lt", 9 | size = 25, 10 | weight = 200 11 | }) 12 | 13 | 14 | surface.CreateFont("BadCoderzFont1.2", { 15 | font = "Roboto Lt", 16 | size = 27, 17 | weight = 200 18 | }) 19 | 20 | surface.CreateFont("BadCoderzFont1.5", { 21 | font = "Roboto Lt", 22 | size = 31, 23 | weight = 200 24 | }) 25 | 26 | 27 | 28 | 29 | surface.CreateFont("BadCoderzFont2", { 30 | font = "Roboto Lt", 31 | size = 35, 32 | weight = 100 33 | }) 34 | 35 | surface.CreateFont("BadCoderzFont3", { 36 | font = "Roboto Lt", 37 | size = 40, 38 | weight = 100 39 | }) 40 | 41 | if not DarkRP then 42 | -- if any fuckers tries to "emulate" darkrp by creating the DarkRP global var, he's an idiot 43 | surface.CreateFont("Trebuchet48", { 44 | size = 48, 45 | weight = 500, 46 | antialias = true, 47 | shadow = false, 48 | font = "Trebuchet MS" 49 | }) 50 | end -------------------------------------------------------------------------------- /lua/badcoderz/client/cl_network.lua: -------------------------------------------------------------------------------- 1 | function BadCoderz.ReadReport() 2 | local len = net.ReadUInt(16) 3 | local data = net.ReadData(len) 4 | local json = util.Decompress(data) 5 | local table = util.JSONToTable(json) 6 | 7 | return table 8 | end 9 | 10 | net.Receive("BadCoderz_status_request", function() 11 | local codeSmellsScan = net.ReadBool() 12 | local compiledFuncsScan = net.ReadBool() 13 | local scanOnReboot = net.ReadBool() 14 | BadCoderz.ShowUI(codeSmellsScan, compiledFuncsScan, scanOnReboot) 15 | end) 16 | 17 | concommand.Add("BadCoderz", function() 18 | if not LocalPlayer():CanUseBadCoderz() then 19 | LocalPlayer():ChatPrint("You cannot use badcoderz") 20 | 21 | return 22 | end 23 | 24 | net.Start("BadCoderz_status_request") 25 | net.SendToServer() 26 | end) 27 | 28 | net.Receive("BadCoderz_report_request", function() 29 | local active = net.ReadBool() 30 | local report = BadCoderz.ReadReport() 31 | 32 | if (BadCoderz.Derma and IsValid(BadCoderz.Derma)) then 33 | BadCoderz.Derma.buttonServerside:UpdateAsync(active, report) 34 | end 35 | end) 36 | 37 | net.Receive("BadCoderz_serverside_file_open", function(len) 38 | local realPath = net.ReadString() 39 | local datalen = net.ReadUInt(16) 40 | local data = net.ReadData(datalen) 41 | local line 42 | 43 | if (net.ReadBool()) then 44 | line = net.ReadUInt(13) 45 | end 46 | 47 | data = util.Decompress(data) 48 | 49 | if not data then 50 | error("Could not read file data from server : " .. realPath) 51 | 52 | return 53 | end 54 | 55 | BadCoderz.openLuaPad(data, realPath, line) 56 | end) 57 | 58 | net.Receive("BadCoderz_serverside_bytecode_reader", function(len) 59 | assert(GLib, "GLib is missing") 60 | local bytecode = util.Decompress(net.ReadData(net.ReadUInt(16))) 61 | local pointer = net.ReadString() 62 | local stack = net.ReadString() 63 | local location = net.ReadString() 64 | local code = string.format("--[[\n\n%s\n\n]]\n\n", stack) .. bytecode 65 | BadCoderz.openLuaPad(code, "DECOMPILED VIRTUAL FUNCTION : " .. pointer .. " " .. location) 66 | end) -------------------------------------------------------------------------------- /lua/badcoderz/client/cl_proceduralthingcredits.lua: -------------------------------------------------------------------------------- 1 | 2 | local color_green = Color(65, 200, 0, 255) 3 | surface.CreateFont("BadCoderzHACK", { 4 | font = "Courier New", 5 | extended = false, 6 | size = ScrH()*23/1440, 7 | weight = 0, 8 | blursize = 0, 9 | scanlines = 0, 10 | antialias = false, 11 | underline = false, 12 | italic = false, 13 | strikeout = false, 14 | symbol = false, 15 | rotary = false, 16 | shadow = false, 17 | additive = false, 18 | outline = false 19 | }) 20 | 21 | surface.CreateFont("BadCoderzHACK2", { 22 | font = "Courier New", 23 | extended = false, 24 | size = ScrH()*50/1440, 25 | weight = 0, 26 | blursize = 0, 27 | scanlines = 0, 28 | antialias = false, 29 | underline = false, 30 | italic = false, 31 | strikeout = false, 32 | symbol = false, 33 | rotary = false, 34 | shadow = false, 35 | additive = false, 36 | outline = false 37 | }) 38 | 39 | local ASCII = [[ 40 | 41 | ▀█████████▄ ▄████████ ████████▄ ▄████████ ▄██████▄ ████████▄ ▄████████ ▄████████ ▄███████▄ 42 | ███ ███ ███ ███ ███ ▀███ ███ ███ ███ ███ ███ ▀███ ███ ███ ███ ███ ██▀ ▄██ 43 | ███ ███ ███ ███ ███ ███ ███ █▀ ███ ███ ███ ███ ███ █▀ ███ ███ ▄███▀ 44 | ▄███▄▄▄██▀ ███ ███ ███ ███ ███ ███ ███ ███ ███ ▄███▄▄▄ ▄███▄▄▄▄██▀ ▀█▀▄███▀▄▄ 45 | ▀▀███▀▀▀██▄ ▀███████████ ███ ███ ███ ███ ███ ███ ███ ▀▀███▀▀▀ ▀▀███▀▀▀▀▀ ▄███▀ ▀ 46 | ███ ██▄ ███ ███ ███ ███ ███ █▄ ███ ███ ███ ███ ███ █▄ ▀███████████ ▄███▀ 47 | ███ ███ ███ ███ ███ ▄███ ███ ███ ███ ███ ███ ▄███ ███ ███ ███ ███ ███▄ ▄█ 48 | ▄█████████▀ ███ █▀ ████████▀ ████████▀ ▀██████▀ ████████▀ ██████████ ███ ███ ▀████████▀ 49 | ███ ███ 50 | 51 | ]] 52 | 53 | 54 | local credits = { 55 | "Nux [76561197962571798]", 56 | "Riddick [76561197963098122]", 57 | "Alexander Ivanov [76561197971459535]", 58 | "[CC] Danny [76561197974070406]", 59 | "Vernoctus [76561197976207928]", 60 | "pigeon [76561197978789107]", 61 | "⬜HP⬜F.M. SMITH [76561197979056981]", 62 | "alexgrist [76561197979205163]", 63 | "Mika32 [76561197984232597]", 64 | "DegenerateManiac [76561197985131383]", 65 | "Marc [76561197987428962]", 66 | "INJ3 [76561197988568430]", 67 | "St11l [76561197989007643]", 68 | "freemmaann [76561197989323181]", 69 | "#Attache [76561197989693629]", 70 | "Sgt.Val [76561197989881498]", 71 | "Jckuk [76561197990859059]", 72 | "gameout [76561197991275937]", 73 | "[MG] Mark [76561197991892879]", 74 | "Nurdism [76561197991928585]", 75 | "-ICE- [76561197993955387]", 76 | "Mrkrabz [76561197994206223]", 77 | "MrGrim [76561197994240526]", 78 | "Twitch.tv/m0rtos [76561197994560611]", 79 | "ZerTeK [76561197995326047]", 80 | "|DC| Drewbie [76561197995916061]", 81 | "Mka0207 [76561197996267438]", 82 | "Nosjo [76561197996463808]", 83 | "[LRS] Aranir [76561197997606141]", 84 | "[WPC] Forrest Mark X [76561197997881512]", 85 | "$zero++; [76561197999997389]", 86 | "Tontsa [76561198000189501]", 87 | "chelog [76561198001309034]", 88 | "Watchdog [76561198006360138]", 89 | "GodOfNothing. [76561198007111303]", 90 | "Jouaram [76561198010069529]", 91 | "Kommandant Jut [76561198010077714]", 92 | "Tyzu [76561198010806404]", 93 | "mo3g666 [76561198011777394]", 94 | "Nykez [76561198011844757]", 95 | "Zerochain [76561198013322242]", 96 | "☣»√ιяυƨ«☣ [76561198013802225]", 97 | "flamboo [76561198014940064]", 98 | "Nguyen [76561198015157261]", 99 | "FrOnTeZ [76561198015224919]", 100 | "Dallas [76561198016349436]", 101 | "Erazor White [76561198017884700]", 102 | "Mipastu [76561198018477778]", 103 | "Jam Session [76561198019048041]", 104 | "Pure [76561198019210482]", 105 | "Loaskyial [Varus] SUS [76561198019442318]", 106 | "{CCz} Jibinwar [76561198021824216]", 107 | "Mr.ROFLMAN™ [76561198022493588]", 108 | "GhostComander72 [76561198023723483]", 109 | "Killer™ [76561198024821053]", 110 | "Captain Black [76561198025881657]", 111 | "Jorim [76561198026900589]", 112 | "Tomas [76561198027094023]", 113 | "TheFaiint [76561198028087472]", 114 | "Ventz - Wang Dong Wingo [76561198029085201]", 115 | "Instinkt [76561198030713180]", 116 | "GrandpaTroll [76561198031700108]", 117 | "=[HN]= Havoc [76561198033383770]", 118 | "4udo [76561198033468770]", 119 | "Horatio™ [76561198033756338]", 120 | "[TG-of-TN]walkka [76561198034333249]", 121 | "Ptilly [76561198035559333]", 122 | "Rizqo [76561198035668047]", 123 | "Vodka [76561198036772852]", 124 | "Havanna ElLama [76561198037211601]", 125 | "Major Death [76561198038710503]", 126 | "azuspl [76561198038749037]", 127 | "Clorox [76561198039226224]", 128 | "Jesse [76561198040571472]", 129 | "#Black4President [76561198040919114]", 130 | "Hasan [76561198041569183]", 131 | "Jomsy [76561198041934099]", 132 | "Brassx [76561198041959113]", 133 | "[Gaminglight.com] Zeeptin [76561198042112161]", 134 | "Kezhar [76561198043293182]", 135 | "Questionable [76561198044346824]", 136 | "Alpha [76561198046362677]", 137 | "我是淡定当然 [76561198046405253]", 138 | "Gaboo [76561198046907785]", 139 | "DrEnder [76561198047920491]", 140 | "『 𝓕𝓵𝓾𝓰𝓪𝓵 』 [76561198047926641]", 141 | "Alaxandar [76561198049186088]", 142 | "Komi-sar [76561198049442792]", 143 | "-JakerZ [76561198050338246]", 144 | "[BLN] Resh [76561198050652544]", 145 | "SunlessPine [76561198051517023]", 146 | "Skyrox [76561198051535832]", 147 | "sɦɨиγɑ ќღиɢɑʍɨ [76561198054648669]", 148 | "Nidolai [76561198055180234]", 149 | "Markus [76561198055581096]", 150 | "Adam James [76561198056469034]", 151 | "Dinkleberg >:( [76561198057690973]", 152 | "whisperrybowl228 [76561198057974840]", 153 | "Owain [76561198058562944]", 154 | "Flol [76561198059279463]", 155 | "101 Palpatyńczyków [76561198059635872]", 156 | "Gmanc2 [76561198060166859]", 157 | "Right_Twix [76561198063610282]", 158 | "Sensei Hiraku [76561198063946583]", 159 | "DogerTheDoge [76561198065274788]", 160 | "Wasuma [76561198066766008]", 161 | "Heaven [76561198068199263]", 162 | "mcNuggets [76561198068523613]", 163 | "Fruity [76561198068753895]", 164 | "GrandDaddySmoke [76561198068857227]", 165 | "Jellyton69 [76561198069393930]", 166 | "MasterX [76561198069840599]", 167 | "sacul.h [76561198071443989]", 168 | "Shadow [76561198071598838]", 169 | "TheLuMaster [76561198071604863]", 170 | "YouTube - Wunder [76561198072208440]", 171 | "Warden Potato [76561198072825924]", 172 | "useless [76561198073125273]", 173 | "Mayze6 [76561198074906310]", 174 | "VALERY [76561198074911795]", 175 | "[₯₰۞]μṬṓṙṙḙṉṯ® [76561198075148568]", 176 | "Romarin [76561198075390609]", 177 | "{SBF}Sauronox [76561198077277128]", 178 | "Bhoon [76561198078444928]", 179 | "Soul [76561198078481716]", 180 | "JockeRex [76561198078608537]", 181 | "[*P:R*]Diemon_EX [76561198080439685]", 182 | "twtich.tv/highfivebadger [76561198083570332]", 183 | "Walker [76561198083670208]", 184 | "Golgi Apparatus [76561198084042269]", 185 | "Alex T. Foxe [76561198084204021]", 186 | "MultiGK [76561198085267384]", 187 | "nerzlakai96 [76561198085282329]", 188 | "Amanite2012 [76561198085690199]", 189 | "Stan [76561198086036321]", 190 | "Hades [76561198086193663]", 191 | "Adsman100 [76561198087621128]", 192 | "mark_xero_04 [76561198088133019]", 193 | "Sam Maxis [76561198088824319]", 194 | "Verdict [76561198089465570]", 195 | "Yogpod [76561198092381685]", 196 | "Flash [76561198092742034]", 197 | "Kantil [76561198092983444]", 198 | "LucaReno [76561198093244860]", 199 | "Moonlight | AJ [76561198093521540]", 200 | "slaVAC [76561198095033845]", 201 | "Keitho [76561198096900690]", 202 | "TorrentofShame [76561198097015658]", 203 | "Chris [76561198097562734]", 204 | "Camper™ [76561198098449576]", 205 | "Shocks [76561198098764577]", 206 | "ЯМАТО ???????? [76561198100296417]", 207 | "Lanos [76561198100383665]", 208 | "Dawnables [76561198100789604]", 209 | "Triangle [76561198100793182]", 210 | "Juaket [76561198100925473]", 211 | "3pixel [76561198102241597]", 212 | "RockerOfWorlds [76561198102292519]", 213 | "Noobly [76561198102403039]", 214 | "Nikku Miru [76561198104065271]", 215 | "Lufou [76561198104759836]", 216 | "taka_qiao [76561198104948727]", 217 | "wtfisarabesk [76561198105957680]", 218 | "Niff [76561198106316621]", 219 | "Soks [76561198107255211]", 220 | "Pierre Johnson [76561198109871279]", 221 | "clear [76561198109963148]", 222 | "NacBoi [76561198110022447]", 223 | "Stezzers - Naxal-Networks.co.uk [76561198111790342]", 224 | "Shadowsun™ [76561198112042939]", 225 | "Tanker [76561198113025297]", 226 | "[Toxidation] [76561198114369348]", 227 | "Gasp ★ [76561198115550675]", 228 | "Habel [76561198116515492]", 229 | "Aws0me [76561198116888982]", 230 | "Trillium [76561198117369504]", 231 | "Bucket [76561198117917496]", 232 | "odesza [76561198118169513]", 233 | "BlueSn00w [76561198120125520]", 234 | "BOT Benny [76561198120695978]", 235 | "Aevoa [76561198120882898]", 236 | "Livaco [76561198121018313]", 237 | "Omni [76561198121441395]", 238 | "Miskie [76561198122638261]", 239 | "Conwell [76561198123068127]", 240 | "Marki [76561198124382817]", 241 | "☜☆☞ -GPX- Demo ☜☆☞ [76561198124708739]", 242 | "Isaac131 [76561198125561657]", 243 | "AvoxPaine [76561198126083590]", 244 | "Marcin46405 [76561198127143504]", 245 | "Nystoo/Neal [76561198127456980]", 246 | "Kydesn1k [76561198128047514]", 247 | "MrDiaboloz [76561198128166689]", 248 | "Skeptic [76561198128507071]", 249 | "Syzco [76561198128725929]", 250 | "|$m0k!n|~IKILLYOU~ [76561198129536045]", 251 | "waythink [76561198129697732]", 252 | "Jacob [76561198130115660]", 253 | "Sven [76561198130141257]", 254 | "RyanTheTechMan [76561198131304286]", 255 | "Jean Marie LePen [76561198132219148]", 256 | "Sleety [76561198133686949]", 257 | ".#NIKOLAY!3370 ☾⋆ [76561198134029133]", 258 | "xXReAL OtVeRTKAXx [76561198134148886]", 259 | "Offshorp [76561198134595040]", 260 | "Pandaa [76561198135638255]", 261 | "Dopler [76561198135698052]", 262 | "Greenwood [76561198135727623]", 263 | "Nitch [76561198136394134]", 264 | "MasterBeef [76561198136775271]", 265 | "Gamma [76561198137025938]", 266 | "Sombra Carmesí [76561198137854113]", 267 | "[FL] AweSUCCBullet [76561198138802562]", 268 | "KyrBaXa (≧∇≦) [76561198139315626]", 269 | "KyleJames0408 [76561198139359719]", 270 | "a [76561198139637545]", 271 | "yam [76561198139733880]", 272 | "Военком [76561198140183136]", 273 | "JYST3R [76561198140995609]", 274 | "Batmanhey [76561198141043787]", 275 | "Blaster Alpha [76561198141142594]", 276 | "TEH 1337 |-|VHS7 [76561198141666619]", 277 | "[RU]LOyoujoLI [76561198142346653]", 278 | "STRELOKMAX [76561198142760541]", 279 | "[HG] x Hyper Gaming x [76561198142971462]", 280 | "Erik [76561198143535846]", 281 | "DrumFire [76561198143682256]", 282 | "Mr. Tapout [76561198143907681]", 283 | "Enzio [76561198144440215]", 284 | "Zebra [76561198144651117]", 285 | "The_Cosmic [76561198144807674]", 286 | "Kotyarishka [76561198144964099]", 287 | "JackGamesFTW [76561198145669259]", 288 | "SeeTwo [76561198145700098]", 289 | "RoZeL [76561198146913082]", 290 | "Marshall [76561198147082669]", 291 | "XxsnipercatxX81 [76561198148179653]", 292 | "Ben [76561198150268365]", 293 | "TheGeek47 [76561198151644706]", 294 | "Brozzor [76561198156635728]", 295 | "_RedWolf [76561198157339064]", 296 | "HeyJack [76561198158042782]", 297 | "Log [76561198159652224]", 298 | "Aiko Suzuki [76561198160570208]", 299 | "Micah [76561198162087822]", 300 | "Ethan [76561198163953541]", 301 | "Walrusking [76561198167304516]", 302 | "CandyApple [76561198168652477]", 303 | "Shadow [76561198170651942]", 304 | "Nicolas [76561198173245925]", 305 | "SugarTheCat 2nd[CaT] [76561198174394338]", 306 | "AvarianKnight [76561198174460202]", 307 | "Ghost36000 [76561198175653470]", 308 | "{Belda} [76561198177069851]", 309 | "Rayek [76561198178393710]", 310 | "Seth [76561198180318085]", 311 | "Kotus [76561198181970707]", 312 | "Ceaser [76561198183691630]", 313 | "Focus [76561198183857985]", 314 | "LAST OF US [76561198184300797]", 315 | "KinGoHD [76561198184662818]", 316 | "Ballerd™ [76561198187121533]", 317 | "No-Name Legend2 [76561198192711263]", 318 | "1328 | SOG | Cohen™ #Abrakadab [76561198193206907]", 319 | "Peteplays42 [76561198194722521]", 320 | "versa [76561198194961926]", 321 | "Flame [76561198195497869]", 322 | "minethan60 [76561198198093633]", 323 | "Wesley ❉ [76561198198105490]", 324 | "[GG] Jeremy [76561198202011800]", 325 | "Sinyx [76561198204327588]", 326 | "Doublés [76561198208154057]", 327 | "tasid [76561198210577821]", 328 | "Vulgo [76561198212875496]", 329 | "Tom.bat [76561198215456356]", 330 | "[♥D.SS♥]Phoenixmeister [76561198216476691]", 331 | "Mekphen [76561198224385656]", 332 | "[Wrups2.0] Benoit Freeman [76561198226277844]", 333 | "Elisium Mesrine [76561198237595634]", 334 | "dontworry [76561198242113260]", 335 | "[RN] Zeo [76561198249648050]", 336 | "SlownLS [76561198251737334]", 337 | "Zeo [76561198251742058]", 338 | "Twacker [76561198253168662]", 339 | "[Corvezeo] [76561198253220777]", 340 | "MultiXeon [76561198256490200]", 341 | "Zepioz [76561198262193882]", 342 | "Swank Weezy [76561198262538001]", 343 | "[罪] Hunzerg [76561198271953473]", 344 | "ichickenwafflei - twitch [76561198284023854]", 345 | "joeleroi1990 [76561198296934706]", 346 | "JAKE1234 [76561198297340417]", 347 | "Feeps [76561198297871252]", 348 | "[IG] Eclipse [76561198299938652]", 349 | "Foks [76561198300670656]", 350 | "Wrath [76561198305817302]", 351 | "Johnson [76561198307660201]", 352 | "Wryer [76561198316998248]", 353 | "[76561198321157245]", 354 | "_Dubrovski_ [76561198327871184]", 355 | "-MG- Pie [76561198328716062]", 356 | "[PS] Kairos [76561198328995481]", 357 | "Kaname [76561198329543401]", 358 | "Sebiann [76561198337460582]", 359 | "Flase | LegacyGaming.co [76561198339936290]", 360 | "Emaister : P [76561198340258665]", 361 | "Килиан | UnityServers.FR [76561198343872798]", 362 | "Nafik Ma Wyjebane [76561198347648056]", 363 | "LindaBigTits [76561198352098873]", 364 | "Jame [76561198353159846]", 365 | "Dog are cute. hellcase.com [76561198360062712]", 366 | "Adapting [76561198368262297]", 367 | "Urgo [76561198370489073]", 368 | "poorMrbumblepuss [76561198378141280]", 369 | "_KaMYu_ [76561198378359741]", 370 | "Garry [76561198378570387]", 371 | "Binbin [76561198387524220]", 372 | "D34THC47 [76561198391272884]", 373 | "Wicca Phase [76561198398798258]", 374 | "Zaphkiel [76561198412850298]", 375 | "Alex Loven [76561198420959775]", 376 | "Sir. Skeleton [76561198423673722]", 377 | "Deadpool [76561198427344474]", 378 | "Haley | GMOD Central [76561198444401856]", 379 | "SacredAI [76561198806609844]", 380 | "[WOLF] The Omega(Second) [76561198815111433]", 381 | "Miau-Hase [76561198815186070]", 382 | "bilalunal252 [76561198835587375]", 383 | "komatoz.# [76561198843430939]", 384 | "Donovan[El Rodriguez][Capitaine] [76561198864405374]", 385 | "notsaying [76561198872122075]", 386 | "NoSharp [76561198879542401]", 387 | "intellectpvp [76561198977521251]", 388 | "High Octane Gaming [76561198979042539]", 389 | "DummKopf [76561198979866465]", 390 | "BigBoiBorris [76561198982162713]", 391 | "nshatov21 [76561198987046616]", 392 | "RedFox-Community [76561199001230296]", 393 | "Tom Hagen [76561199008488819]", 394 | "BillyW [76561198346972028]", 395 | "Lapin/LPN64 [76561198001451981]" 396 | } 397 | 398 | local posTbl 399 | local proGamer 400 | 401 | local bdctbl = {utf8.codepoint(ASCII, 1, -1)} 402 | local bdcchartbl = {} 403 | 404 | for k, v in ipairs(bdctbl) do 405 | bdcchartbl[k] = utf8.char(v) 406 | end 407 | 408 | local timedown = 5 409 | local offsetbase = -ScrH()/2-300 410 | local function flagdraw(startTime) 411 | surface.SetFont("BadCoderzHACK") 412 | surface.SetTextColor(color_green) 413 | local charsizew, charsizeh = surface.GetTextSize('▀') 414 | local i = 1 415 | local x = ScrW() / 2 - (108 * charsizew) / 2 + math.cos(CurTime() * 1.5 + 2) * 15 416 | local yoffset = 0 417 | 418 | if (CurTime() < startTime + timedown) then 419 | yoffset = math.Remap(CurTime(), startTime, startTime + timedown, offsetbase, 0) 420 | end 421 | 422 | local y = ScrH() / 2.4 + math.sin(CurTime() * 0.75 - 1) * 25 + yoffset 423 | local cursize = 0 424 | local count = #bdcchartbl 425 | local y2 = 0 426 | 427 | while (i <= count) do 428 | if (bdcchartbl[i] == '\n') then 429 | cursize = 0 430 | y2 = y2 + charsizeh 431 | i = i + 1 432 | if (i > count) then break end 433 | end 434 | 435 | cursize = cursize + charsizew 436 | surface.SetTextPos(x + cursize, y + y2 + math.cos(cursize + SysTime() * 10) * 4) 437 | surface.DrawText(bdcchartbl[i]) 438 | i = i + 1 439 | end 440 | 441 | surface.SetTextColor(Color(65, 200, 0, math.Remap(CurTime(), startTime, startTime + timedown, 0, 255))) 442 | 443 | for k, v in ipairs(credits) do 444 | if k == proGamer then continue end 445 | local timeChange1 = math.cos(CurTime() + k * k / 2) * 20 446 | local timeChange2 = math.sin(CurTime() + k * k / 5) * 80 447 | surface.SetTextPos(posTbl[k][1] + timeChange1, posTbl[k][2] - timeChange2) 448 | surface.DrawText(v) 449 | end 450 | 451 | if proGamer then 452 | surface.SetFont("BadCoderzHACK2") 453 | surface.SetTextColor( 454 | Color(math.abs(math.sin(CurTime() * 5)) * 255, 455 | math.cos(math.sin(CurTime() * 9)) * 255, 456 | math.abs(math.sin(CurTime() * 7)) * 255, 457 | math.Remap(CurTime(), startTime, startTime + timedown, 0, 255) 458 | ) 459 | ) 460 | local timeChange1 = math.cos(CurTime() + proGamer * proGamer / 2) * 20 461 | local timeChange2 = math.sin(CurTime() + proGamer * proGamer / 5) * 80 462 | surface.SetTextPos(posTbl[proGamer][1] + timeChange1, posTbl[proGamer][2] - timeChange2) 463 | surface.DrawText(credits[proGamer]) 464 | end 465 | end 466 | 467 | function BadCoderz.Credits() 468 | if BadCoderz.DermaCredits and IsValid(BadCoderz.DermaCredits) then 469 | BadCoderz.DermaCredits:Close() 470 | end 471 | posTbl = {} 472 | 473 | proGamer = nil 474 | -- no optimization for you 475 | for k, v in ipairs(credits) do 476 | if not proGamer and string.find(v, LocalPlayer():SteamID64()) then proGamer = k end 477 | surface.SetFont("BadCoderzHACK") 478 | local w, h = surface.GetTextSize(v) 479 | posTbl[k] = {math.random(0, ScrW()-w), math.random(h, ScrH()-h)} 480 | end 481 | 482 | 483 | 484 | DermaBadCoderz = TDLib("DFrame") 485 | BadCoderz.DermaCredits = DermaBadCoderz 486 | DermaBadCoderz:SetPos(0, 0) 487 | DermaBadCoderz:SetSize(ScrW(), ScrH()) 488 | DermaBadCoderz:SetTitle("") 489 | DermaBadCoderz:SetDraggable(false) 490 | DermaBadCoderz:MakePopup() 491 | DermaBadCoderz:Center() 492 | DermaBadCoderz:ShowCloseButton(false) 493 | DermaBadCoderz.btnMaxim.Paint = function(panel, w, h) end 494 | DermaBadCoderz.btnMinim.Paint = function(panel, w, h) end 495 | 496 | local buttonClose = TDLib("DButton", DermaBadCoderz) 497 | buttonClose:SetText("") 498 | buttonClose:SetSize(100, 40) 499 | buttonClose:SetPos(DermaBadCoderz:GetWide() - buttonClose:GetWide() - 1, 1) 500 | buttonClose:ClearPaint():Background(color_black):Text("×", "Trebuchet48", color_green) 501 | 502 | buttonClose.DoClick = function(pnl) 503 | pnl:GetParent():Close() 504 | end 505 | 506 | local startTime = CurTime() 507 | 508 | DermaBadCoderz.Paint = function(pnl, w, h) 509 | surface.SetDrawColor(color_black) 510 | surface.DrawRect(0, 0, w, h) 511 | flagdraw(startTime) 512 | end 513 | 514 | local music 515 | local url = "https://extrem-team.com/keygen3.mp3" 516 | 517 | sound.PlayURL(url, "", function(station, errorid, errorstr) 518 | music = station 519 | end) 520 | 521 | DermaBadCoderz.OnClose = function(pnl) 522 | if (music == nil or music:IsValid() == false) then 523 | return 524 | end 525 | 526 | music:Stop() 527 | end 528 | end 529 | 530 | --BadCoderz.Credits() -------------------------------------------------------------------------------- /lua/badcoderz/client/cl_ui.lua: -------------------------------------------------------------------------------- 1 | local DermaBadCoderz; 2 | 3 | local TDLib = include("badcoderz/client/lapin_tdlib.lua") 4 | 5 | local LastCSReport = {} 6 | local LastSSReport = {} 7 | 8 | 9 | local color_light_grey = Color(0, 0, 0, 20) 10 | local color_button_fade = Color(0, 0, 0, 100) 11 | local color_black = Color(0, 0, 0) 12 | local color_light_red = Color(255, 100, 100) 13 | local color_dark_green = Color(0,200,0) 14 | local color_dark_red = Color(200,0,0) 15 | local color_CS = Color(255,136,0) 16 | local color_SS = Color(0,136,255) 17 | local color_green = Color(65, 200, 0, 255) 18 | local color_red = Color(225, 0, 0) 19 | 20 | 21 | function BadCoderz.openLuaPad(luaCode, path, line) 22 | local EditorFrame = TDLib( "DFrame" ) 23 | EditorFrame:SetSize( ScrW() * 0.75, ScrH() * 0.75 ) 24 | EditorFrame:SetTitle( "" ) 25 | EditorFrame:Center() 26 | EditorFrame:MakePopup() 27 | 28 | 29 | EditorFrame:ClearPaint():Background(color_white):Outline(color_black):Text("MetaStruct's WEB Lua editor @" .. path, "BadCoderzFont1", color_black, TEXT_ALIGN_LEFT, 10, -EditorFrame:GetTall() / 2 + 15, true) 30 | 31 | 32 | EditorFrame:ShowCloseButton(false) 33 | EditorFrame.btnMaxim.Paint = function() end 34 | EditorFrame.btnMinim.Paint = function() end 35 | 36 | local buttonClose = TDLib("DButton", EditorFrame) 37 | buttonClose:SetText("") 38 | buttonClose:SetSize(60, 20) 39 | buttonClose:SetPos(EditorFrame:GetWide() - buttonClose:GetWide() - 1, 1) 40 | buttonClose:ClearPaint():Background(color_white):FadeHover(color_light_red, 15):Text("×", "BadCoderzFont1", color_black) 41 | 42 | buttonClose.DoClick = function(pnl) 43 | pnl:GetParent():Close() 44 | end 45 | 46 | local html = vgui.Create( "DHTML", EditorFrame ) 47 | html:Dock( FILL ) 48 | html:OpenURL( "http://metastruct.github.io/lua_editor/" ) 49 | html:Call("SetContent(\"" .. string.JavascriptSafe(luaCode) .. "\");") 50 | if (line) then 51 | html:Call(" GotoLine(\"" .. tostring(line) .. "\");") 52 | end 53 | 54 | end 55 | 56 | 57 | 58 | --ghetto ui m8 59 | 60 | -- the arguments here are provided by the server, those are the different states of the possible scans/settings 61 | function BadCoderz.ShowUI(serverCodeSmellScan, serverCompiledFuncsScan, scanonreboot) 62 | if BadCoderz.Derma and IsValid(BadCoderz.Derma) then 63 | BadCoderz.Derma:Close() 64 | end 65 | 66 | -- resync to server 67 | if scanonreboot ~= BadCoderz.scanCompiledFuncsOnNextBoot then 68 | BadCoderz.prepareCompiledFuncsScanNextReboot(scanonreboot) 69 | print("[BadCoderz] Resynced [SCAN ON NEXT REBOOT] to prevent issues") 70 | end 71 | 72 | DermaBadCoderz = TDLib("DFrame") 73 | BadCoderz.Derma = DermaBadCoderz 74 | DermaBadCoderz:SetPos(100, 100) 75 | DermaBadCoderz:SetSize(1366, 768) 76 | DermaBadCoderz:SetTitle("") 77 | DermaBadCoderz:SetDraggable(true) 78 | DermaBadCoderz:MakePopup() 79 | DermaBadCoderz:Center() 80 | 81 | DermaBadCoderz:ShowCloseButton(false) 82 | DermaBadCoderz.btnMaxim.Paint = function() end 83 | DermaBadCoderz.btnMinim.Paint = function() end 84 | DermaBadCoderz:ClearPaint():Background(color_white):Outline(color_black):Text("BadCoderz control Panel", "BadCoderzFont1", color_black, TEXT_ALIGN_LEFT, 10, -DermaBadCoderz:GetTall() / 2 + 20, true) 85 | 86 | DermaBadCoderz.Wizz = function(pnl) 87 | notification.AddLegacy("No report to export!", NOTIFY_ERROR, 3) 88 | surface.PlaySound("badcoderz/oof.mp3") 89 | local start = CurTime() 90 | local x, y = BadCoderz.Derma:GetPos() 91 | 92 | hook.Add("Think", "badcoderzWizz", function() 93 | if start + 0.1 < CurTime() or not IsValid(BadCoderz.Derma) then 94 | hook.Remove("Think", "badcoderzWizz") 95 | 96 | return 97 | end 98 | 99 | BadCoderz.Derma:SetPos(x + math.random(-5, 5), y + math.random(-5, 5)) 100 | end) 101 | end 102 | 103 | 104 | 105 | local buttonClose = TDLib("DButton", DermaBadCoderz) 106 | buttonClose:SetText("") 107 | buttonClose:SetSize(100, 40) 108 | buttonClose:SetPos(DermaBadCoderz:GetWide() - buttonClose:GetWide() - 1, 1) 109 | buttonClose:ClearPaint():Background(color_white):FadeHover(color_light_red, 15):Text("×", "Trebuchet48", color_black) 110 | 111 | 112 | local mainHolder = TDLib("DPanel", DermaBadCoderz) 113 | DermaBadCoderz.mainHolder = mainHolder 114 | local holderx, holdery = 200, 100 115 | local holderbordel = 20 116 | mainHolder:SetPos( holderx, holdery ) 117 | mainHolder:SetSize( DermaBadCoderz:GetWide() - holderx-holderbordel, DermaBadCoderz:GetTall() - holdery-1 ) 118 | 119 | 120 | --:CircleHover() 121 | buttonClose.DoClick = function(pnl) 122 | pnl:GetParent():Close() 123 | end 124 | 125 | 126 | local buttonClientside = TDLib("DButton", DermaBadCoderz) 127 | local buttonRunStrings = TDLib("DButton", DermaBadCoderz) 128 | local buttonServerside = TDLib("DButton", DermaBadCoderz) 129 | 130 | 131 | 132 | --[[ 133 | ______ __ _____ ____ 134 | / ____/___ ____/ /__ / ___/____ ___ ___ / / /____ 135 | / / / __ \/ __ / _ \\__ \/ __ `__ \/ _ \/ / / ___/ 136 | / /___/ /_/ / /_/ / __/__/ / / / / / / __/ / (__ ) 137 | \____/\____/\__,_/\___/____/_/ /_/ /_/\___/_/_/____/ 138 | 139 | ]] 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | local nbutton = 0 148 | local buttonh = 50 149 | 150 | 151 | local textCodeSmells = TDLib("DPanel", DermaBadCoderz) 152 | 153 | textCodeSmells:SetSize(DermaBadCoderz:GetWide() - mainHolder:GetWide() - holderbordel-1, buttonh) 154 | textCodeSmells:SetPos(1, holdery + nbutton * buttonh) 155 | textCodeSmells:ClearPaint() 156 | :Text("Code smells", "BadCoderzFont2", color_black, TEXT_ALIGN_CENTER, 5) 157 | 158 | nbutton = nbutton + 1 159 | 160 | 161 | local curcolor = color_CS 162 | 163 | 164 | DermaBadCoderz.buttonClientside = buttonClientside 165 | buttonClientside:SetPos(1, holdery + nbutton * buttonh) 166 | buttonClientside:SetSize(DermaBadCoderz:GetWide() - mainHolder:GetWide() - holderbordel-1, buttonh) 167 | buttonClientside:SetText("") 168 | buttonClientside:ClearPaint() 169 | :Background(color_light_grey) 170 | :SideBlock(curcolor, 4, LEFT) 171 | :FillHover(curcolor) 172 | :Text("Client Realm", "BadCoderzFont2", color_black, TEXT_ALIGN_CENTER, 5):CircleClick(color_black) 173 | 174 | buttonClientside.DoClick = function(pnl) 175 | if BadCoderz.scanningCompiledFuncs then return end 176 | pnl.ClickX, pnl.ClickY = pnl:CursorPos() 177 | pnl.Rad = 0 178 | pnl.Alpha = color_black.a 179 | mainHolder:Clear() 180 | mainHolder:ClearPaint():Background(color_light_grey):SideBlock(curcolor, 6, LEFT) 181 | 182 | local labelStatus = vgui.Create( "DLabel", mainHolder ) 183 | labelStatus:SetPos( 40, 20 ) 184 | labelStatus:SetSize( 300, 30 ) 185 | labelStatus:SetText( "Scan status : " .. (BadCoderz.scanningCodeSmells and "Active" or "Inactive")) 186 | labelStatus:SetColor(BadCoderz.scanningCodeSmells and color_dark_green or color_dark_red) 187 | labelStatus:SetFont("BadCoderzFont2") 188 | 189 | local labelProtip = vgui.Create( "DLabel", mainHolder ) 190 | labelProtip:SetPos( 720, 10 ) 191 | labelProtip:SetSize( 400, 300 ) 192 | labelProtip:SetText( "After starting the scan, if you want\nto catch as much 'code smells' as possible\nyou should close this menu and play the game.\nThe more you interract with other addons\nthe more 'code smells' the scanner will catch.") 193 | labelProtip:SetColor(color_black) 194 | labelProtip:SetFont("BadCoderzFontSmall") 195 | labelProtip:SetContentAlignment(8) 196 | 197 | local buttonStartPause = TDLib("DButton", mainHolder) 198 | buttonStartPause:SetPos(40, 70) 199 | buttonStartPause:SetSize(150,50) 200 | buttonStartPause:SetText("") 201 | 202 | 203 | if BadCoderz.scanningCodeSmells then 204 | buttonStartPause:ClearPaint():Background(color_red):FadeHover(color_button_fade, 15):Text("Pause", "BadCoderzFont2", color_white) 205 | else 206 | buttonStartPause:ClearPaint():Background(color_green):FadeHover(color_button_fade, 15):Text("Start", "BadCoderzFont2", color_white) 207 | end 208 | 209 | buttonStartPause.DoClick = function() 210 | if (BadCoderz.scanningCodeSmells) then 211 | LastCSReport = BadCoderz.getReport() 212 | else 213 | LastCSReport = {} 214 | end 215 | BadCoderz.toggleCodeSmellsScan() 216 | local shouldclose = BadCoderz.settings.auto_close_menu:GetBool() 217 | local shouldreopen = BadCoderz.settings.auto_reopen_menu:GetBool() 218 | if (shouldclose and BadCoderz.scanningCodeSmells) then -- == true because it was called right before 219 | DermaBadCoderz:Close() 220 | if shouldreopen then 221 | local timer_reopen = BadCoderz.settings.auto_reopen_menu_time:GetInt() 222 | timer.Simple(timer_reopen, function() 223 | if BadCoderz.Derma and IsValid(BadCoderz.Derma) then 224 | return 225 | end 226 | net.Start("BadCoderz_status_request") 227 | net.SendToServer() 228 | end) 229 | 230 | end 231 | return 232 | end 233 | 234 | 235 | pnl:DoClick(pnl) 236 | end 237 | 238 | local treeReport = vgui.Create( "DTree", mainHolder ) 239 | treeReport:SetPos(20,150) 240 | treeReport:SetSize(mainHolder:GetWide() - 40,475) 241 | 242 | BadCoderz.populateTreeWithData(treeReport, LastCSReport, true) 243 | 244 | end 245 | 246 | 247 | 248 | local curcolor = color_SS 249 | 250 | local nbutton = nbutton + 1 251 | 252 | DermaBadCoderz.buttonServerside = buttonServerside 253 | buttonServerside:SetPos(1, holdery + nbutton * buttonh) 254 | buttonServerside:SetSize(DermaBadCoderz:GetWide() - mainHolder:GetWide() - holderbordel-1, buttonh) 255 | buttonServerside:SetText("") 256 | buttonServerside:ClearPaint() 257 | :Background(color_light_grey) 258 | :SideBlock(curcolor, 4, LEFT) 259 | :FillHover(curcolor) 260 | :Text("Server Realm", "BadCoderzFont2", color_black, TEXT_ALIGN_CENTER, 5):CircleClick(color_black) 261 | 262 | 263 | buttonServerside.DoClick = function(pnl) 264 | if serverCompiledFuncsScan then return end 265 | pnl.ClickX, pnl.ClickY = pnl:CursorPos() 266 | pnl.Rad = 0 267 | pnl.Alpha = color_black.a 268 | 269 | net.Start("BadCoderz_report_request") 270 | net.SendToServer() 271 | 272 | mainHolder:Clear() 273 | end 274 | 275 | buttonServerside.UpdateAsync = function(pnl, active, data) 276 | 277 | mainHolder:ClearPaint():Background(color_light_grey):SideBlock(curcolor, 6, LEFT) 278 | --:FillHover(curcolor) 279 | LastSSReport = data 280 | local labelStatus = vgui.Create( "DLabel", mainHolder ) 281 | labelStatus:SetPos( 40, 20 ) 282 | labelStatus:SetSize( 300, 30 ) 283 | labelStatus:SetText( "Scan status : " .. (active and "Active" or "Inactive")) 284 | labelStatus:SetColor(active and color_dark_green or color_dark_red) 285 | labelStatus:SetFont("BadCoderzFont2") 286 | 287 | 288 | local labelProtip = vgui.Create( "DLabel", mainHolder ) 289 | labelProtip:SetPos( 760, 20 ) 290 | labelProtip:SetSize( 400, 300 ) 291 | labelProtip:SetText( "The more player you have on the server,\nthe more 'code smells' will be detected.\nThe idea is to do as much different\nthings as possible to cover an area\nas wide as possible.") 292 | labelProtip:SetColor(color_black) 293 | labelProtip:SetFont("BadCoderzFontSmall") 294 | labelProtip:SetContentAlignment(8) 295 | 296 | 297 | 298 | local buttonStartPause = TDLib("DButton", mainHolder) 299 | buttonStartPause:SetPos(40, 70) 300 | buttonStartPause:SetSize(150,50) 301 | buttonStartPause:SetText("") 302 | 303 | 304 | if active then 305 | buttonStartPause:ClearPaint():Background(color_red):FadeHover(color_button_fade, 15):Text("Pause", "BadCoderzFont2", color_white) 306 | else 307 | buttonStartPause:ClearPaint():Background(color_green):FadeHover(color_button_fade, 15):Text("Start", "BadCoderzFont2", color_white) 308 | end 309 | 310 | buttonStartPause.DoClick = function() 311 | net.Start("BadCoderz_scan_request") 312 | net.SendToServer() 313 | 314 | local shouldclose = BadCoderz.settings.auto_close_menu:GetBool() 315 | local shouldreopen = BadCoderz.settings.auto_reopen_menu:GetBool() 316 | if (shouldclose and not active) then -- test running == true because it was called right before 317 | 318 | DermaBadCoderz:Close() 319 | if shouldreopen then 320 | local timer_reopen = BadCoderz.settings.auto_reopen_menu_time:GetInt() 321 | timer.Simple(timer_reopen, function() 322 | if BadCoderz.Derma and IsValid(BadCoderz.Derma) then 323 | return 324 | end 325 | net.Start("BadCoderz_status_request") 326 | net.SendToServer() 327 | end) 328 | end 329 | return 330 | end 331 | 332 | pnl:DoClick(pnl) 333 | end 334 | 335 | local treeReport = vgui.Create( "DTree", mainHolder ) 336 | treeReport:SetPos(20,150) 337 | treeReport:SetSize(mainHolder:GetWide() - 40,475) 338 | 339 | BadCoderz.populateTreeWithData(treeReport, data, false) 340 | 341 | 342 | end 343 | 344 | 345 | nbutton = nbutton + 2 346 | 347 | 348 | 349 | 350 | 351 | local textCompiledFuncs = TDLib("DPanel", DermaBadCoderz) 352 | textCompiledFuncs:SetSize(DermaBadCoderz:GetWide() - mainHolder:GetWide() - holderbordel-1, buttonh) 353 | textCompiledFuncs:SetPos(1, holdery + nbutton * buttonh) 354 | textCompiledFuncs:ClearPaint() 355 | :Text("Misc", "BadCoderzFont2", color_black, TEXT_ALIGN_CENTER, 5) 356 | 357 | --[[ 358 | ____ __ _ 359 | / __ \____ / /_(_)___ ____ _____ 360 | / / / / __ \/ __/ / __ \/ __ \/ ___/ 361 | / /_/ / /_/ / /_/ / /_/ / / / (__ ) 362 | \____/ .___/\__/_/\____/_/ /_/____/ 363 | /_/ 364 | ]] 365 | 366 | 367 | 368 | 369 | local curcolor = color_light_red 370 | nbutton = nbutton + 1 371 | local buttonOptions = TDLib("DButton", DermaBadCoderz) 372 | DermaBadCoderz.buttonOptions = buttonOptions 373 | buttonOptions:SetPos(1, holdery + nbutton * buttonh) 374 | buttonOptions:SetSize(DermaBadCoderz:GetWide() - mainHolder:GetWide() - holderbordel-1, buttonh) 375 | buttonOptions:SetText("") 376 | buttonOptions:ClearPaint() 377 | 378 | :Background(color_light_grey) 379 | :SideBlock(curcolor, 4, LEFT) 380 | :FillHover(curcolor) 381 | :Text("Options & Exports", "BadCoderzFont1.2", color_black, TEXT_ALIGN_CENTER, 5):CircleClick(color_black) 382 | 383 | 384 | buttonOptions.DoClick = function(pnl) 385 | pnl.ClickX, pnl.ClickY = pnl:CursorPos() 386 | pnl.Rad = 0 387 | pnl.Alpha = color_black.a 388 | 389 | mainHolder:Clear() 390 | mainHolder:ClearPaint():Background(color_light_grey):SideBlock(curcolor, 6, LEFT) 391 | 392 | local CheckBoxAutoClose = TDLib( "DCheckBoxLabel", mainHolder ) 393 | CheckBoxAutoClose:SetPos( 35, 50 ) 394 | CheckBoxAutoClose:SetText( "") 395 | CheckBoxAutoClose:SetConVar( BadCoderz.settings.auto_close_menu:GetName()) 396 | CheckBoxAutoClose:SetChecked( BadCoderz.settings.auto_close_menu:GetBool() ) 397 | CheckBoxAutoClose:SizeToContents() 398 | CheckBoxAutoClose:SetDark(true) 399 | CheckBoxAutoClose:ClearPaint():Text(BadCoderz.settings.auto_close_menu:GetHelpText(), "BadCoderzFont1", color_black) 400 | 401 | 402 | local CheckBoxAutoReOpen = TDLib( "DCheckBoxLabel", mainHolder ) 403 | CheckBoxAutoReOpen:SetPos( 35, 90 ) 404 | CheckBoxAutoReOpen:SetText( "") 405 | CheckBoxAutoReOpen:SetConVar( BadCoderz.settings.auto_reopen_menu:GetName()) 406 | CheckBoxAutoReOpen:SetChecked( BadCoderz.settings.auto_reopen_menu:GetBool() ) 407 | CheckBoxAutoReOpen:SizeToContents() 408 | CheckBoxAutoReOpen:SetDark(true) 409 | CheckBoxAutoReOpen:ClearPaint():Text(BadCoderz.settings.auto_reopen_menu:GetHelpText(), "BadCoderzFont1", color_black) 410 | 411 | 412 | local SliderReopentimer = TDLib( "DNumSlider", mainHolder ) 413 | --SliderReopentimer.TextArea:Hide() 414 | SliderReopentimer.Label:Hide() 415 | SliderReopentimer.Scratch:Hide() 416 | SliderReopentimer:SetPos( 30, 110 ) 417 | SliderReopentimer:SetSize( 175, 50 ) 418 | --SliderReopentimer:SetText(false) 419 | SliderReopentimer:SetMin( 1 ) 420 | SliderReopentimer:SetMax( 60 ) 421 | SliderReopentimer:SetDecimals( 0 ) 422 | SliderReopentimer:SetConVar( BadCoderz.settings.auto_reopen_menu_time:GetName() ) 423 | SliderReopentimer:SetDark(true) 424 | SliderReopentimer:SizeToContents() 425 | SliderReopentimer:SetValue(BadCoderz.settings.auto_reopen_menu_time:GetInt()) 426 | 427 | local labelSliderReopen = vgui.Create( "DLabel", mainHolder ) 428 | labelSliderReopen:SetPos( 190, 123 ) 429 | labelSliderReopen:SetSize( 500, 300 ) 430 | labelSliderReopen:SetText( BadCoderz.settings.auto_reopen_menu_time:GetHelpText()) 431 | labelSliderReopen:SetColor(color_black) 432 | labelSliderReopen:SetFont("BadCoderzFont1") 433 | labelSliderReopen:SetContentAlignment(8) 434 | 435 | 436 | SliderReopentimer.Slider.Paint = function(self, w, h) 437 | surface.SetDrawColor(75, 75, 75, 100) 438 | surface.DrawRect(8, h / 2 - 2, w - 12, 3) 439 | end 440 | 441 | SliderReopentimer.Slider.Knob.Paint = function(self, w, h) 442 | surface.SetDrawColor(150, 150, 255, 255) 443 | surface.DrawRect(8, 0, 3, h) 444 | end 445 | 446 | local buttonExportCS = TDLib("DButton", mainHolder) 447 | buttonExportCS:SetText("") 448 | buttonExportCS:SetSize(500, 60) 449 | buttonExportCS:SetPos(40,165) 450 | buttonExportCS:ClearPaint():Background(color_CS):Text("Export last Clientside report to Clipboard", "BadCoderzFont1.5", color_black):CircleClick(color_black) 451 | 452 | buttonExportCS.DoClick = function(pnl) 453 | pnl.ClickX, pnl.ClickY = pnl:CursorPos() 454 | pnl.Rad = 0 455 | pnl.Alpha = color_black.a 456 | if (table.IsEmpty(LastCSReport)) then 457 | DermaBadCoderz.Wizz() 458 | end 459 | SetClipboardText(BadCoderz.reportToText(LastCSReport)) 460 | 461 | end 462 | 463 | 464 | local buttonExportSS = TDLib("DButton", mainHolder) 465 | buttonExportSS:SetText("") 466 | buttonExportSS:SetSize(500, 60) 467 | buttonExportSS:SetPos(40,245) 468 | buttonExportSS:ClearPaint():Background(color_SS):Text("Export last Serverside report to Clipboard", "BadCoderzFont1.5", color_black):CircleClick(color_black) 469 | buttonExportSS.DoClick = function(pnl) 470 | pnl.ClickX, pnl.ClickY = pnl:CursorPos() 471 | pnl.Rad = 0 472 | pnl.Alpha = color_black.a 473 | if (table.IsEmpty(LastSSReport)) then 474 | DermaBadCoderz.Wizz() 475 | end 476 | SetClipboardText(BadCoderz.reportToText(LastSSReport)) 477 | end 478 | 479 | end 480 | 481 | 482 | nbutton = nbutton + 1 483 | 484 | local curcolor = color_SS 485 | 486 | 487 | 488 | 489 | DermaBadCoderz.buttonRunStrings = buttonRunStrings 490 | buttonRunStrings:SetPos(1, holdery + nbutton * buttonh) 491 | buttonRunStrings:SetSize(DermaBadCoderz:GetWide() - mainHolder:GetWide() - holderbordel-1, buttonh) 492 | buttonRunStrings:SetText("") 493 | buttonRunStrings:ClearPaint() 494 | 495 | :Background(color_light_grey) 496 | :SideBlock(curcolor, 4, LEFT) 497 | :FillHover(curcolor) 498 | :Text("RunStrings", "BadCoderzFont2", color_black, TEXT_ALIGN_CENTER, 5):CircleClick(color_black) 499 | 500 | buttonRunStrings.DoClick = function(pnl) 501 | if serverCodeSmellScan or BadCoderz.scanningCodeSmells then return end 502 | pnl.ClickX, pnl.ClickY = pnl:CursorPos() 503 | pnl.Rad = 0 504 | pnl.Alpha = color_black.a 505 | 506 | 507 | mainHolder:Clear() 508 | mainHolder:ClearPaint():Background(color_light_grey):SideBlock(curcolor, 6, LEFT) 509 | 510 | 511 | local labelProtip = vgui.Create( "DLabel", mainHolder ) 512 | labelProtip:SetPos( 25, 20 ) 513 | labelProtip:SetSize( mainHolder:GetWide()-40, 300 ) 514 | labelProtip:SetText( "You might want to find any suspicious hiden code ran on the fly using RunString and compilestring. As most of this code is ran at the\nstart of the server, you need to enable it for the next reboot as running it right now would be useless.") 515 | labelProtip:SetColor(color_black) 516 | labelProtip:SetFont("BadCoderzFontSmall") 517 | labelProtip:SetContentAlignment(7) 518 | 519 | 520 | local ClientSideText = TDLib("DPanel", mainHolder) 521 | ClientSideText:SetSize(200 , 40) 522 | ClientSideText:SetPos(167 , 100) 523 | ClientSideText:ClearPaint() 524 | :Text("Client Realm", "BadCoderzFont2", color_black, TEXT_ALIGN_CENTER, 5) 525 | 526 | 527 | local ServerSideText = TDLib("DPanel", mainHolder) 528 | ServerSideText:SetSize(200 , 40) 529 | ServerSideText:SetPos(mainHolder:GetWide()- 400 , 100) 530 | ServerSideText:ClearPaint() 531 | :Text("Server Realm", "BadCoderzFont2", color_black, TEXT_ALIGN_CENTER, 5) 532 | 533 | 534 | local x = 10 535 | local w = mainHolder:GetWide()/2-x*2 536 | 537 | 538 | BadCoderz.generateDListView(mainHolder, x, 150, w, mainHolder:GetTall()-250, BadCoderz.compiledFuncs, true) 539 | BadCoderz.generateDListView(mainHolder, mainHolder:GetWide()-w-x, 150, w, mainHolder:GetTall()-250, nil, false) 540 | 541 | 542 | local DisclamerText = TDLib("DPanel", mainHolder) 543 | DisclamerText:SetSize(800 , 40) 544 | DisclamerText:SetPos(300 , 572) 545 | DisclamerText:ClearPaint() 546 | 547 | if GLib then 548 | DisclamerText:Text("Double click on a line to view the decompiled bytecode.", "BadCoderzFont1.2", color_black, TEXT_ALIGN_LEFT, 5) 549 | else 550 | DisclamerText:Text("Install GLib to inspect decompiled bytecode (link in console)", "BadCoderzFont1.2", color_black, TEXT_ALIGN_LEFT, 5) 551 | print("[BadCoderz] link to download GLib : https://github.com/notcake/glib") 552 | end 553 | 554 | if BadCoderz.scanningCompiledFuncs then 555 | local buttonStopScanCS = TDLib("DButton", mainHolder) 556 | buttonStopScanCS:SetText("") 557 | buttonStopScanCS:SetSize(110, 60) 558 | buttonStopScanCS:SetPos(135, 592) 559 | buttonStopScanCS:ClearPaint():Background(color_CS):Text("STOP", "BadCoderzFont1.5", color_black):CircleClick(color_black) 560 | 561 | buttonStopScanCS.DoClick = function(pnl) 562 | pnl.ClickX, pnl.ClickY = pnl:CursorPos() 563 | pnl.Rad = 0 564 | pnl.Alpha = color_black.a 565 | debug.sethook() 566 | BadCoderz.scanningCompiledFuncs = false 567 | buttonRunStrings:DoClick() 568 | end 569 | end 570 | 571 | if serverCompiledFuncsScan then 572 | local buttonStopScanSS = TDLib("DButton", mainHolder) 573 | buttonStopScanSS:SetText("") 574 | buttonStopScanSS:SetSize(110, 60) 575 | buttonStopScanSS:SetPos(900,592) 576 | buttonStopScanSS:ClearPaint():Background(color_SS):Text("STOP", "BadCoderzFont1.5", color_black):CircleClick(color_black) 577 | buttonStopScanSS.DoClick = function(pnl) 578 | pnl.ClickX, pnl.ClickY = pnl:CursorPos() 579 | pnl.Rad = 0 580 | pnl.Alpha = color_black.a 581 | 582 | net.Start("BadCoderz_stop_boot_scan") 583 | net.SendToServer() 584 | serverCompiledFuncsScan = false 585 | buttonRunStrings:DoClick() 586 | end 587 | end 588 | 589 | local CheckBoxScanNextBoot = TDLib( "DCheckBoxLabel", mainHolder ) 590 | CheckBoxScanNextBoot:SetPos( 880, 60 ) 591 | CheckBoxScanNextBoot:SetText("") 592 | CheckBoxScanNextBoot:SetChecked( scanonreboot ) 593 | CheckBoxScanNextBoot:SizeToContents() 594 | CheckBoxScanNextBoot:SetDark(true) 595 | CheckBoxScanNextBoot:ClearPaint():Text("Scan on next boot", "BadCoderzFont1", color_black) 596 | CheckBoxScanNextBoot.OnChange = function(_, bool) 597 | BadCoderz.prepareCompiledFuncsScanNextReboot(bool) 598 | net.Start("BadCoderz_next_reboot_scan_change_status") 599 | net.WriteBool(bool) 600 | net.SendToServer() 601 | end 602 | 603 | 604 | end 605 | 606 | 607 | local secretbutton = TDLib("DButton", DermaBadCoderz) 608 | secretbutton:SetSize(DermaBadCoderz:GetWide() - mainHolder:GetWide() - holderbordel-1, buttonh) 609 | secretbutton:SetPos(1,DermaBadCoderz:GetTall()-buttonh-1) 610 | 611 | secretbutton:SetText("") 612 | secretbutton:ClearPaint() 613 | 614 | :Background(color_white) 615 | :CircleHover(color_black) 616 | :Text("The Twilight Zone", "BadCoderzFont1", color_white, TEXT_ALIGN_CENTER, 5) 617 | 618 | 619 | secretbutton.DoClick = function(pnl) 620 | pnl:GetParent():Close() 621 | BadCoderz.Credits() 622 | end 623 | 624 | -- select which menu to autoopen 625 | if BadCoderz.scanningCodeSmells then 626 | buttonClientside:DoClick() 627 | elseif serverCodeSmellScan then 628 | buttonServerside:DoClick() 629 | elseif serverCompiledFuncsScan or BadCoderz.scanningCompiledFuncs then 630 | buttonRunStrings:DoClick() 631 | else 632 | buttonClientside:DoClick() -- I could just remove first condition but i'll keep readability here 633 | end 634 | 635 | end 636 | 637 | -------------------------------------------------------------------------------- /lua/badcoderz/client/cl_utils.lua: -------------------------------------------------------------------------------- 1 | local function openWikipage(func_name) 2 | 3 | gui.OpenURL("https://wiki.facepunch.com/gmod/" .. func_name) 4 | --input.SetCursorPos(ScrW()/2+239, ScrH()/2+34) -- hackerman 5 | end 6 | 7 | 8 | local disclamer = [[ 9 | This report is meant to be understood by devs. 10 | Check the call stack, because the real location of the function is in it. 11 | Of course, if you know a bit about lua, you should be able to spot the addons with A LOT of code smell/bad things. 12 | ]] 13 | local preloaded_data = file.Read("garrysmod/lua/send.txt", "BASE_PATH") 14 | 15 | 16 | local wiki_base = "https://wiki.facepunch.com/gmod/" 17 | function BadCoderz.reportToText(dataTbl) 18 | 19 | if table.IsEmpty(dataTbl) then 20 | return "report was empty :(" 21 | end 22 | 23 | local str = disclamer 24 | for functionsNames, hookTable in pairs(dataTbl) do 25 | local function_internet_name = functionsNames--string.Replace(functionsNames,".","/") 26 | if not string.find(function_internet_name, '.',1, true) then 27 | function_internet_name = "Global." .. function_internet_name 28 | end 29 | str = str .. "\nFunction : " .. functionsNames .. " (Wiki page : " .. wiki_base .. function_internet_name .. " )\n" 30 | for hookName, callsLocations in pairs(hookTable) do 31 | local hook_internet_name = hookName--string.Replace(hookName,":","/") 32 | str = str .. " Hook : " .. hookName .. " (Wiki page : " .. wiki_base .. hook_internet_name .. " )\n" 33 | for filename, LinesTable in pairs(callsLocations) do 34 | local warning; 35 | if string.find(preloaded_data, string.Replace(filename, "/", "\\"), 1, false) then 36 | warning = " /!\\(Probably wrong, check the stack call history bellow)/!\\" 37 | else 38 | warning = "" 39 | end 40 | 41 | str = str .. " File : " .. filename .. warning .. "\n" 42 | for callLines, calls in pairs(LinesTable) do 43 | local info; 44 | if calls.calls == 1 then 45 | info = " /!\\(Only one call, probably used for caching)/!\\" 46 | else 47 | info = "" 48 | end 49 | str = str .. " └►Line #" .. callLines .. "-> " .. calls.calls .. " calls" .. info .. "\n" 50 | str = str .. " Call History\n" 51 | for k, location in pairs(calls.lines) do 52 | str = str .. " #" .. k .. " File:" .. location.location .. ", Line #" .. location.line .. "\n" 53 | end 54 | end 55 | end 56 | end 57 | end 58 | return str 59 | end 60 | 61 | 62 | local function getDescFromWikiBody(body) 63 | body = util.JSONToTable(body).html 64 | 65 | 66 | local tofind = "function_description section" 67 | 68 | if not string.find(body, tofind) then 69 | return "" 70 | end 71 | local found = string.find(body, tofind) + string.len(tofind) + 5 72 | local enddesc = string.find(body, ".", found, true) 73 | local tip = string.sub(body, found, enddesc) 74 | tip = string.gsub(tip, "<.->", "") 75 | 76 | 77 | return tip 78 | end 79 | 80 | 81 | 82 | local function absolutePathToLuaPath(path) 83 | if string.StartWith(path, "lua/") then 84 | return path:sub(5, path:len()) 85 | end 86 | if string.StartWith(path, "gamemodes/") then 87 | return path:sub(11, path:len()) 88 | end 89 | 90 | local luaPos = string.find(path, "/lua/", 1, false) 91 | if not luaPos then return path end 92 | return string.sub(path, luaPos + 5, path:len()) 93 | end 94 | 95 | 96 | function BadCoderz.populateTreeWithData(treePanel, dataTbl, client) 97 | 98 | for functionsNames, hookTable in pairs(dataTbl) do 99 | local nodeFunctionName = treePanel:AddNode( functionsNames , "icon16/sitemap.png") 100 | local function_internet_name = functionsNames--string.Replace(functionsNames,".","/") 101 | if not string.find(function_internet_name, '.',1, true) then 102 | function_internet_name = "Global." .. function_internet_name 103 | end 104 | 105 | nodeFunctionName:SetExpanded(true, true) 106 | local fName = nodeFunctionName:GetText() 107 | nodeFunctionName.DoRightClick = function(pnl) 108 | openWikipage(fName) 109 | end 110 | if BadCoderz.toolTips[fName] then 111 | nodeFunctionName:SetTooltip(BadCoderz.toolTips[fName]) 112 | end 113 | 114 | http.Fetch("https://wiki.facepunch.com/gmod/" .. function_internet_name.. "?format=json",function(body) 115 | if nodeFunctionName and IsValid(nodeFunctionName) then 116 | local tip = getDescFromWikiBody(body) 117 | if BadCoderz.toolTips[fName] then 118 | tip = tip .. "\n-------------------\n" .. BadCoderz.toolTips[fName] 119 | end 120 | 121 | nodeFunctionName:SetTooltip(tip) 122 | end 123 | end) 124 | 125 | for hookName, callsLocations in pairs(hookTable) do 126 | local nodeHookName = nodeFunctionName:AddNode( hookName , "icon16/wrench.png") 127 | local hook_internet_name = hookName--string.Replace(hookName,":","/") 128 | nodeHookName:SetTooltip("Loading ...") 129 | http.Fetch("https://wiki.facepunch.com/gmod/" .. hook_internet_name.. "?format=json",function(body) 130 | if nodeHookName and IsValid(nodeHookName) then 131 | nodeHookName:SetTooltip(getDescFromWikiBody(body)) 132 | end 133 | end) 134 | 135 | nodeHookName:SetExpanded(true, true) 136 | nodeHookName:SetDoubleClickToOpen(false) 137 | nodeHookName.DoRightClick = function(pnl) 138 | local fName = pnl:GetText() 139 | openWikipage(fName) 140 | end 141 | 142 | for filename, LinesTable in pairs(callsLocations) do 143 | local fileIcon; 144 | if string.find(preloaded_data, string.Replace(filename, "/", "\\"), 1, false) then 145 | fileIcon = "icon16/application_error.png" 146 | else 147 | fileIcon = "icon16/script_code.png" 148 | end 149 | 150 | local fixedName = filename 151 | local findGMA = string.find(filename, ".gma", 1 , true) 152 | if findGMA then 153 | fixedName = filename:sub(findGMA + 5, filename:len()) 154 | end 155 | 156 | fixedName = absolutePathToLuaPath(fixedName) 157 | 158 | local nodeFileName = nodeHookName:AddNode( filename, fileIcon ) 159 | 160 | nodeFileName:SetTooltip("Right click to open in Lua editor") 161 | nodeFileName.DoRightClick = function(pnl) 162 | net.Start("BadCoderz_serverside_file_open") 163 | net.WriteString(fixedName) 164 | net.WriteString(filename) 165 | net.WriteBool(false) 166 | net.SendToServer() 167 | end 168 | 169 | for callLines, calls in pairs(LinesTable) do 170 | local icon; 171 | if calls.calls > 1 then 172 | icon = "icon16/stop.png" 173 | else 174 | icon = "icon16/lightning_go.png" 175 | end 176 | 177 | local callLineNode = nodeFileName:AddNode( "└►Line #" .. callLines .. "-> " .. calls.calls .. " calls | Expand to see call history", icon) 178 | callLineNode:SetTooltip("Click to expand the call history") 179 | for k, location in pairs(calls.lines) do 180 | 181 | --[[ 182 | Intentional variable shadowing, 183 | don't remove `local`, 184 | it would override next loop's value 185 | ]] 186 | local fixedName = location.location 187 | local findGMA = string.find(location.location, ".gma", 1 , true) 188 | if findGMA then 189 | fixedName = location.location:sub(findGMA+5, location.location:len()) 190 | end 191 | fixedName = absolutePathToLuaPath(fixedName) 192 | 193 | local iconCallHistory; 194 | if k == #calls.lines then 195 | iconCallHistory = "icon16/stop.png" 196 | else 197 | iconCallHistory = "icon16/clock.png" 198 | end 199 | 200 | local _endNode = callLineNode:AddNode( "#".. k .. " File:" .. location.location .. ", Line #" .. location.line, iconCallHistory) 201 | 202 | _endNode.DoRightClick = function(pnl) 203 | if client and not file.Exists(fixedName, "LUA") then 204 | if not GLib then 205 | LocalPlayer():ChatPrint("You need GLib to decompile this function, get it here : https://github.com/notcake/glib") 206 | return 207 | end 208 | local byteCode = GLib.Lua.BytecodeReader(calls.funcs[k]):ToString() 209 | BadCoderz.openLuaPad(byteCode, "DECOMPILED VIRTUAL FUNCTION : " .. location.location) 210 | return 211 | end 212 | net.Start("BadCoderz_serverside_file_open") 213 | net.WriteString(fixedName) 214 | net.WriteString(location.location) 215 | net.WriteBool(true) 216 | net.WriteUInt(location.line,13) 217 | net.WriteBool(client) 218 | if not client then 219 | net.WriteString(functionsNames) 220 | net.WriteString(hookName) 221 | net.WriteString(filename) 222 | net.WriteUInt(callLines, 13) 223 | net.WriteUInt(k, 8) 224 | end 225 | net.SendToServer() 226 | end 227 | _endNode:SetTooltip("Right click to open in Lua editor") 228 | end 229 | end 230 | end 231 | end 232 | end 233 | end 234 | 235 | function BadCoderz.generateDListView(pnl, x, y, w, h, dataTbl, client) 236 | local CompiledFuncsList = vgui.Create("DListView", pnl) 237 | CompiledFuncsList:SetPos(x, y) 238 | CompiledFuncsList:SetSize(w, h) 239 | CompiledFuncsList:SetMultiSelect(false) 240 | CompiledFuncsList:AddColumn("Signature") 241 | CompiledFuncsList:AddColumn("Address") 242 | CompiledFuncsList:AddColumn("Bytecode Size") 243 | CompiledFuncsList:AddColumn("Suspiciosity Level") 244 | CompiledFuncsList:AddColumn("Compilation timestamp") 245 | 246 | if client then 247 | for index, compiledTable in pairs(dataTbl) do 248 | CompiledFuncsList:AddLine(compiledTable.location, compiledTable.pointer, string.dump(compiledTable.func):len(), compiledTable.level, compiledTable.time, index, compiledTable.stack, compiledTable.func) 249 | end 250 | CompiledFuncsList:SortByColumn(4, true) 251 | else 252 | net.Start("BadCoderz_compiled_report_request") -- ghetto async 253 | net.SendToServer() 254 | net.Receive("BadCoderz_compiled_report_request", function() 255 | local len = net.ReadUInt(16) 256 | local i = 1 257 | 258 | while (i <= len) do 259 | local funcLen = net.ReadUInt(16) 260 | local location = net.ReadString() 261 | local pointer = net.ReadString() 262 | local level = net.ReadUInt(4) 263 | local time = net.ReadFloat() 264 | if CompiledFuncsList and CompiledFuncsList:IsValid() then -- we need to empty the network buffer anyway 265 | CompiledFuncsList:AddLine(location, pointer, funcLen, level, time, i) 266 | end 267 | i = i + 1 268 | end 269 | if CompiledFuncsList and CompiledFuncsList:IsValid() then 270 | CompiledFuncsList:SortByColumn(4, true) 271 | end 272 | end) 273 | end 274 | 275 | 276 | if not GLib then return end 277 | 278 | CompiledFuncsList.DoDoubleClick = function(lst, lineID, pnl) 279 | local tblIndex = pnl:GetColumnText(6) 280 | 281 | if client then 282 | local code = string.format("--[[\n\n%s\n\n]]\n\n", pnl:GetColumnText(7)) .. GLib.Lua.BytecodeReader(pnl:GetColumnText(8)):ToString() 283 | BadCoderz.openLuaPad(code, "DECOMPILED VIRTUAL FUNCTION : " .. pnl:GetColumnText(2) .. " " .. pnl:GetColumnText(1)) 284 | else 285 | net.Start("BadCoderz_serverside_bytecode_reader") 286 | net.WriteUInt(tblIndex, 16) 287 | net.SendToServer() 288 | end 289 | end 290 | 291 | return CompiledFuncsList 292 | end 293 | -------------------------------------------------------------------------------- /lua/badcoderz/client/lapin_tdlib.lua: -------------------------------------------------------------------------------- 1 | --[[--------------------------------------------------------------------------- 2 | Three's Derma Lib 3 | Made by Threebow, moddified by Lapin to fix render order and to not "overwrite" other libs 4 | ---------------------------------------------------------------------------]] 5 | --[[--------------------------------------------------------------------------- 6 | Constants 7 | ---------------------------------------------------------------------------]] 8 | local blur = Material("pp/blurscreen") 9 | local gradLeft = Material("vgui/gradient-l") 10 | local gradUp = Material("vgui/gradient-u") 11 | local gradRight = Material("vgui/gradient-r") 12 | local gradDown = Material("vgui/gradient-d") 13 | --[[--------------------------------------------------------------------------- 14 | Collection of various utilities 15 | ---------------------------------------------------------------------------]] 16 | local TDLibUtil = {} 17 | 18 | --Beast's circle drawing function v2 19 | TDLibUtil.DrawCircle = function(x, y, r, col) 20 | local circle = {} 21 | 22 | for i = 1, 360 do 23 | circle[i] = {} 24 | circle[i].x = x + math.cos(math.rad(i * 360) / 360) * r 25 | circle[i].y = y + math.sin(math.rad(i * 360) / 360) * r 26 | end 27 | 28 | surface.SetDrawColor(col) 29 | draw.NoTexture() 30 | surface.DrawPoly(circle) 31 | end 32 | 33 | TDLibUtil.DrawArc = function(x, y, ang, p, rad, color, seg) 34 | seg = seg or 80 35 | ang = (-ang) + 180 36 | local circle = {} 37 | 38 | table.insert(circle, { 39 | x = x, 40 | y = y 41 | }) 42 | 43 | for i = 0, seg do 44 | local a = math.rad((i / seg) * -p + ang) 45 | 46 | table.insert(circle, { 47 | x = x + math.sin(a) * rad, 48 | y = y + math.cos(a) * rad 49 | }) 50 | end 51 | 52 | surface.SetDrawColor(color) 53 | draw.NoTexture() 54 | surface.DrawPoly(circle) 55 | end 56 | 57 | TDLibUtil.LerpColor = function(frac, from, to) 58 | return Color(Lerp(frac, from.r, to.r), Lerp(frac, from.g, to.g), Lerp(frac, from.b, to.b), Lerp(frac, from.a, to.a)) 59 | end 60 | 61 | --Various handy premade transition functions 62 | TDLibUtil.HoverFunc = function(s) 63 | return s:IsHovered() 64 | end 65 | 66 | TDLibUtil.HoverFuncChild = function(s) 67 | return s:IsHovered() or s:IsChildHovered() 68 | end 69 | 70 | --[[--------------------------------------------------------------------------- 71 | Circle function - credit to Beast 72 | ---------------------------------------------------------------------------]] 73 | local function drawCircle(x, y, r) 74 | local circle = {} 75 | 76 | for i = 1, 360 do 77 | circle[i] = {} 78 | circle[i].x = x + math.cos(math.rad(i * 360) / 360) * r 79 | circle[i].y = y + math.sin(math.rad(i * 360) / 360) * r 80 | end 81 | 82 | surface.DrawPoly(circle) 83 | end 84 | 85 | --[[--------------------------------------------------------------------------- 86 | Basic helper classes 87 | ---------------------------------------------------------------------------]] 88 | local classes = {} 89 | 90 | classes.On = function(pnl, name, fn) 91 | name = pnl.AppendOverwrite or name 92 | local old = pnl[name] 93 | 94 | pnl[name] = function(s, ...) 95 | if (old) then 96 | old(s, ...) 97 | end 98 | 99 | fn(s, ...) 100 | end 101 | end 102 | 103 | classes.SetupTransition = function(pnl, name, speed, fn) 104 | fn = pnl.TransitionFunc or fn 105 | pnl[name] = 0 106 | 107 | pnl:On("Think", function(s) 108 | s[name] = Lerp(FrameTime() * speed, s[name], fn(s) and 1 or 0) 109 | end) 110 | end 111 | 112 | --[[--------------------------------------------------------------------------- 113 | Classes 114 | ---------------------------------------------------------------------------]] 115 | classes.FadeHover = function(pnl, col, speed, rad) 116 | col = col or Color(255, 255, 255, 30) 117 | speed = speed or 6 118 | pnl:SetupTransition("FadeHover", speed, TDLibUtil.HoverFunc) 119 | 120 | pnl:On("Paint", function(s, w, h) 121 | local col = ColorAlpha(col, col.a * s.FadeHover) 122 | 123 | if (rad and rad > 0) then 124 | draw.RoundedBox(rad, 0, 0, w, h, col) 125 | else 126 | surface.SetDrawColor(col) 127 | surface.DrawRect(0, 0, w, h) 128 | end 129 | end) 130 | end 131 | 132 | classes.BarHover = function(pnl, col, height, speed) 133 | col = col or Color(255, 255, 255, 255) 134 | height = height or 2 135 | speed = speed or 6 136 | pnl:SetupTransition("BarHover", speed, TDLibUtil.HoverFunc) 137 | 138 | pnl:On("Paint", function(s, w, h) 139 | local bar = math.Round(w * s.BarHover) 140 | surface.SetDrawColor(col) 141 | surface.DrawRect(w / 2 - bar / 2, h - height, bar, height) 142 | end) 143 | end 144 | 145 | classes.FillHover = function(pnl, col, dir, speed, mat) 146 | col = col or Color(255, 255, 255, 30) 147 | dir = dir or LEFT 148 | speed = speed or 8 149 | pnl:SetupTransition("FillHover", speed, TDLibUtil.HoverFunc) 150 | 151 | pnl:On("Paint", function(s, w, h) 152 | surface.SetDrawColor(col) 153 | local x, y, fw, fh 154 | 155 | if (dir == LEFT) then 156 | x, y, fw, fh = 0, 0, math.Round(w * s.FillHover), h 157 | elseif (dir == TOP) then 158 | x, y, fw, fh = 0, 0, w, math.Round(h * s.FillHover) 159 | elseif (dir == RIGHT) then 160 | local prog = math.Round(w * s.FillHover) 161 | x, y, fw, fh = w - prog, 0, prog, h 162 | elseif (dir == BOTTOM) then 163 | local prog = math.Round(h * s.FillHover) 164 | x, y, fw, fh = 0, h - prog, w, prog 165 | end 166 | 167 | if (mat) then 168 | surface.SetMaterial(mat) 169 | surface.DrawTexturedRect(x, y, fw, fh) 170 | else 171 | surface.DrawRect(x, y, fw, fh) 172 | end 173 | end) 174 | end 175 | 176 | classes.Background = function(pnl, col, rad, rtl, rtr, rbl, rbr) 177 | pnl:On("Paint", function(s, w, h) 178 | if (rad and rad > 0) then 179 | if (rtl ~= nil) then 180 | draw.RoundedBoxEx(rad, 0, 0, w, h, col, rtl, rtr, rbl, rbr) 181 | else 182 | draw.RoundedBox(rad, 0, 0, w, h, col) 183 | end 184 | else 185 | surface.SetDrawColor(col) 186 | surface.DrawRect(0, 0, w, h) 187 | end 188 | end) 189 | end 190 | 191 | classes.Material = function(pnl, mat, col) 192 | col = col or Color(255, 255, 255) 193 | 194 | pnl:On("Paint", function(s, w, h) 195 | surface.SetDrawColor(col) 196 | surface.SetMaterial(mat) 197 | surface.DrawTexturedRect(0, 0, w, h) 198 | end) 199 | end 200 | 201 | classes.TiledMaterial = function(pnl, mat, tw, th, col) 202 | col = col or Color(255, 255, 255, 255) 203 | 204 | pnl:On("Paint", function(s, w, h) 205 | surface.SetMaterial(mat) 206 | surface.SetDrawColor(col) 207 | surface.DrawTexturedRectUV(0, 0, w, h, 0, 0, w / tw, h / th) 208 | end) 209 | end 210 | 211 | classes.Outline = function(pnl, col, width) 212 | col = col or Color(255, 255, 255, 255) 213 | width = width or 1 214 | 215 | pnl:On("Paint", function(s, w, h) 216 | surface.SetDrawColor(col) 217 | 218 | for i = 0, width - 1 do 219 | surface.DrawOutlinedRect(0 + i, 0 + i, w - i * 2, h - i * 2) 220 | end 221 | end) 222 | end 223 | 224 | classes.LinedCorners = function(pnl, col, len) 225 | col = col or Color(255, 255, 255, 255) 226 | len = len or 15 227 | 228 | pnl:On("Paint", function(s, w, h) 229 | surface.SetDrawColor(col) 230 | surface.DrawRect(0, 0, len, 1) 231 | surface.DrawRect(0, 1, 1, len - 1) 232 | surface.DrawRect(w - len, h - 1, len, 1) 233 | surface.DrawRect(w - 1, h - len, 1, len - 1) 234 | end) 235 | end 236 | 237 | classes.SideBlock = function(pnl, col, size, side) 238 | col = col or Color(255, 255, 255, 255) 239 | size = size or 3 240 | side = side or LEFT 241 | 242 | pnl:On("Paint", function(s, w, h) 243 | surface.SetDrawColor(col) 244 | 245 | if (side == LEFT) then 246 | surface.DrawRect(0, 0, size, h) 247 | elseif (side == TOP) then 248 | surface.DrawRect(0, 0, w, size) 249 | elseif (side == RIGHT) then 250 | surface.DrawRect(w - size, 0, size, h) 251 | elseif (side == BOTTOM) then 252 | surface.DrawRect(0, h - size, w, size) 253 | end 254 | end) 255 | end 256 | 257 | classes.Text = function(pnl, text, font, col, alignment, ox, oy, paint) 258 | font = font or "Trebuchet24" 259 | col = col or Color(255, 255, 255, 255) 260 | alignment = alignment or TEXT_ALIGN_CENTER 261 | ox = ox or 0 262 | oy = oy or 0 263 | 264 | if (not paint and pnl.SetText and pnl.SetFont and pnl.SetTextColor) then 265 | pnl:SetText(text) 266 | pnl:SetFont(font) 267 | pnl:SetTextColor(col) 268 | else 269 | pnl:On("PaintOver", function(s, w, h) 270 | local x = 0 271 | 272 | if (alignment == TEXT_ALIGN_CENTER) then 273 | x = w / 2 274 | elseif (alignment == TEXT_ALIGN_RIGHT) then 275 | x = w 276 | end 277 | 278 | draw.SimpleText(text, font, x + ox, h / 2 + oy, col, alignment, TEXT_ALIGN_CENTER) 279 | end) 280 | end 281 | end 282 | 283 | classes.DualText = function(pnl, toptext, topfont, topcol, bottomtext, bottomfont, bottomcol, alignment, centerSpacing) 284 | topfont = topfont or "Trebuchet24" 285 | topcol = topcol or Color(0, 127, 255, 255) 286 | bottomfont = bottomfont or "Trebuchet18" 287 | bottomcol = bottomcol or Color(255, 255, 255, 255) 288 | alignment = alignment or TEXT_ALIGN_CENTER 289 | centerSpacing = centerSpacing or 0 290 | 291 | pnl:On("Paint", function(s, w, h) 292 | surface.SetFont(topfont) 293 | local tw, th = surface.GetTextSize(toptext) 294 | surface.SetFont(bottomfont) 295 | local bw, bh = surface.GetTextSize(bottomtext) 296 | local y1, y2 = h / 2 - bh / 2, h / 2 + th / 2 297 | local x 298 | 299 | if (alignment == TEXT_ALIGN_LEFT) then 300 | x = 0 301 | elseif (alignment == TEXT_ALIGN_CENTER) then 302 | x = w / 2 303 | elseif (alignment == TEXT_ALIGN_RIGHT) then 304 | x = w 305 | end 306 | 307 | draw.SimpleText(toptext, topfont, x, y1 + centerSpacing, topcol, alignment, TEXT_ALIGN_CENTER) 308 | draw.SimpleText(bottomtext, bottomfont, x, y2 - centerSpacing, bottomcol, alignment, TEXT_ALIGN_CENTER) 309 | end) 310 | end 311 | 312 | classes.Blur = function(pnl, amount) 313 | pnl:On("Paint", function(s, w, h) 314 | local x, y = s:LocalToScreen(0, 0) 315 | local scrW, scrH = ScrW(), ScrH() 316 | surface.SetDrawColor(255, 255, 255) 317 | surface.SetMaterial(blur) 318 | 319 | for i = 1, 3 do 320 | blur:SetFloat("$blur", (i / 3) * (amount or 8)) 321 | blur:Recompute() 322 | render.UpdateScreenEffectTexture() 323 | surface.DrawTexturedRect(x * -1, y * -1, scrW, scrH) 324 | end 325 | end) 326 | end 327 | 328 | classes.CircleClick = function(pnl, col, speed, trad) 329 | col = col or Color(255, 255, 255, 50) 330 | speed = speed or 5 331 | pnl.Rad, pnl.Alpha, pnl.ClickX, pnl.ClickY = 0, 0, 0, 0 332 | 333 | pnl:On("Paint", function(s, w, h) 334 | if (s.Alpha >= 1) then 335 | surface.SetDrawColor(ColorAlpha(col, s.Alpha)) 336 | draw.NoTexture() 337 | drawCircle(s.ClickX, s.ClickY, s.Rad) 338 | s.Rad = Lerp(FrameTime() * speed, s.Rad, trad or w) 339 | s.Alpha = Lerp(FrameTime() * speed, s.Alpha, 0) 340 | end 341 | end) 342 | 343 | pnl:On("DoClick", function(s) 344 | s.ClickX, s.ClickY = s:CursorPos() 345 | s.Rad = 0 346 | s.Alpha = col.a 347 | end) 348 | end 349 | 350 | classes.CircleHover = function(pnl, col, speed, trad) 351 | col = col or Color(255, 255, 255, 30) 352 | speed = speed or 6 353 | pnl.LastX, pnl.LastY = 0, 0 354 | pnl:SetupTransition("CircleHover", speed, TDLibUtil.HoverFunc) 355 | 356 | pnl:On("Think", function(s) 357 | if (s:IsHovered()) then 358 | s.LastX, s.LastY = s:CursorPos() 359 | end 360 | end) 361 | 362 | pnl:On("Paint", function(s, w, h) 363 | draw.NoTexture() 364 | surface.SetDrawColor(ColorAlpha(col, col.a * s.CircleHover)) 365 | drawCircle(s.LastX, s.LastY, s.CircleHover * (trad or w)) 366 | end) 367 | end 368 | 369 | classes.SquareCheckbox = function(pnl, inner, outer, speed) 370 | inner = inner or Color(0, 255, 0, 255) 371 | outer = outer or Color(255, 255, 255, 255) 372 | speed = speed or 14 373 | 374 | pnl:SetupTransition("SquareCheckbox", speed, function(s) 375 | return s:GetChecked() 376 | end) 377 | 378 | pnl:On("Paint", function(s, w, h) 379 | surface.SetDrawColor(outer) 380 | surface.DrawRect(0, 0, w, h) 381 | surface.SetDrawColor(inner) 382 | surface.DrawOutlinedRect(0, 0, w, h) 383 | local bw, bh = (w - 4) * s.SquareCheckbox, (h - 4) * s.SquareCheckbox 384 | bw, bh = math.Round(bw), math.Round(bh) 385 | surface.DrawRect(w / 2 - bw / 2, h / 2 - bh / 2, bw, bh) 386 | end) 387 | end 388 | 389 | classes.CircleCheckbox = function(pnl, inner, outer, speed) 390 | inner = inner or Color(0, 255, 0, 255) 391 | outer = outer or Color(255, 255, 255, 255) 392 | speed = speed or 14 393 | 394 | pnl:SetupTransition("CircleCheckbox", speed, function(s) 395 | return s:GetChecked() 396 | end) 397 | 398 | pnl:On("Paint", function(s, w, h) 399 | draw.NoTexture() 400 | surface.SetDrawColor(outer) 401 | drawCircle(w / 2, h / 2, w / 2 - 1) 402 | surface.SetDrawColor(inner) 403 | drawCircle(w / 2, h / 2, w * s.CircleCheckbox / 2) 404 | end) 405 | end 406 | 407 | classes.AvatarMask = function(pnl, mask) 408 | pnl.Avatar = vgui.Create("AvatarImage", pnl) 409 | pnl.Avatar:SetPaintedManually(true) 410 | 411 | pnl.Paint = function(s, w, h) 412 | render.ClearStencil() 413 | render.SetStencilEnable(true) 414 | render.SetStencilWriteMask(1) 415 | render.SetStencilTestMask(1) 416 | render.SetStencilFailOperation(STENCILOPERATION_REPLACE) 417 | render.SetStencilPassOperation(STENCILOPERATION_ZERO) 418 | render.SetStencilZFailOperation(STENCILOPERATION_ZERO) 419 | render.SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_NEVER) 420 | render.SetStencilReferenceValue(1) 421 | draw.NoTexture() 422 | surface.SetDrawColor(255, 255, 255, 255) 423 | mask(s, w, h) 424 | render.SetStencilFailOperation(STENCILOPERATION_ZERO) 425 | render.SetStencilPassOperation(STENCILOPERATION_REPLACE) 426 | render.SetStencilZFailOperation(STENCILOPERATION_ZERO) 427 | render.SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_EQUAL) 428 | render.SetStencilReferenceValue(1) 429 | s.Avatar:SetPaintedManually(false) 430 | s.Avatar:PaintManual() 431 | s.Avatar:SetPaintedManually(true) 432 | render.SetStencilEnable(false) 433 | render.ClearStencil() 434 | end 435 | 436 | pnl.PerformLayout = function(s) 437 | s.Avatar:SetSize(s:GetWide(), s:GetTall()) 438 | end 439 | 440 | pnl.SetPlayer = function(s, ply, size) 441 | s.Avatar:SetPlayer(ply, size) 442 | end 443 | 444 | pnl.SetSteamID = function(s, id, size) 445 | s.Avatar:SetSteamID(id, size) 446 | end 447 | end 448 | 449 | classes.CircleAvatar = function(pnl) 450 | pnl:Class("AvatarMask", function(s, w, h) 451 | drawCircle(w / 2, h / 2, w / 2) 452 | end) 453 | end 454 | 455 | classes.Circle = function(pnl, col) 456 | col = col or Color(255, 255, 255, 255) 457 | 458 | pnl:On("Paint", function(s, w, h) 459 | draw.NoTexture() 460 | surface.SetDrawColor(col) 461 | drawCircle(w / 2, h / 2, math.min(w, h) / 2) 462 | end) 463 | end 464 | 465 | classes.CircleFadeHover = function(pnl, col, speed) 466 | col = col or Color(255, 255, 255, 30) 467 | speed = speed or 6 468 | pnl:SetupTransition("CircleFadeHover", speed, TDLibUtil.HoverFunc) 469 | 470 | pnl:On("Paint", function(s, w, h) 471 | draw.NoTexture() 472 | surface.SetDrawColor(ColorAlpha(col, col.a * s.CircleFadeHover)) 473 | drawCircle(w / 2, h / 2, math.min(w, h) / 2) 474 | end) 475 | end 476 | 477 | classes.CircleExpandHover = function(pnl, col, speed) 478 | col = col or Color(255, 255, 255, 30) 479 | speed = speed or 6 480 | pnl:SetupTransition("CircleExpandHover", speed, TDLibUtil.HoverFunc) 481 | 482 | pnl:On("Paint", function(s, w, h) 483 | local rad = math.Round(w / 2 * s.CircleExpandHover) 484 | draw.NoTexture() 485 | surface.SetDrawColor(ColorAlpha(col, col.a * s.CircleExpandHover)) 486 | drawCircle(w / 2, h / 2, rad) 487 | end) 488 | end 489 | 490 | classes.Gradient = function(pnl, col, dir, frac, op) 491 | dir = dir or BOTTOM 492 | frac = frac or 1 493 | 494 | pnl:On("Paint", function(s, w, h) 495 | surface.SetDrawColor(col) 496 | local x, y, gw, gh 497 | 498 | if (dir == LEFT) then 499 | local prog = math.Round(w * frac) 500 | x, y, gw, gh = 0, 0, prog, h 501 | surface.SetMaterial(op and gradRight or gradLeft) 502 | elseif (dir == TOP) then 503 | local prog = math.Round(h * frac) 504 | x, y, gw, gh = 0, 0, w, prog 505 | surface.SetMaterial(op and gradDown or gradUp) 506 | elseif (dir == RIGHT) then 507 | local prog = math.Round(w * frac) 508 | x, y, gw, gh = w - prog, 0, prog, h 509 | surface.SetMaterial(op and gradLeft or gradRight) 510 | elseif (dir == BOTTOM) then 511 | local prog = math.Round(h * frac) 512 | x, y, gw, gh = 0, h - prog, w, prog 513 | surface.SetMaterial(op and gradUp or gradDown) 514 | end 515 | 516 | surface.DrawTexturedRect(x, y, gw, gh) 517 | end) 518 | end 519 | 520 | classes.SetOpenURL = function(pnl, url) 521 | pnl:On("DoClick", function() 522 | gui.OpenURL(url) 523 | end) 524 | end 525 | 526 | classes.NetMessage = function(pnl, name, data) 527 | data = data or function() end 528 | 529 | pnl:On("DoClick", function() 530 | net.Start(name) 531 | data(pnl) 532 | net.SendToServer() 533 | end) 534 | end 535 | 536 | classes.Stick = function(pnl, dock, margin, dontInvalidate) 537 | dock = dock or FILL 538 | margin = margin or 0 539 | pnl:Dock(dock) 540 | 541 | if (margin > 0) then 542 | pnl:DockMargin(margin, margin, margin, margin) 543 | end 544 | 545 | if (not dontInvalidate) then 546 | pnl:InvalidateParent(true) 547 | end 548 | end 549 | 550 | classes.DivTall = function(pnl, frac, target) 551 | frac = frac or 2 552 | target = target or pnl:GetParent() 553 | pnl:SetTall(target:GetTall() / frac) 554 | end 555 | 556 | classes.DivWide = function(pnl, frac, target) 557 | target = target or pnl:GetParent() 558 | frac = frac or 2 559 | pnl:SetWide(target:GetWide() / frac) 560 | end 561 | 562 | classes.SquareFromHeight = function(pnl) 563 | pnl:SetWide(pnl:GetTall()) 564 | end 565 | 566 | classes.SquareFromWidth = function(pnl) 567 | pnl:SetTall(pnl:GetWide()) 568 | end 569 | 570 | classes.SetRemove = function(pnl, target) 571 | target = target or pnl 572 | 573 | pnl:On("DoClick", function() 574 | if (IsValid(target)) then 575 | target:Remove() 576 | end 577 | end) 578 | end 579 | 580 | classes.FadeIn = function(pnl, time, alpha) 581 | time = time or 0.2 582 | alpha = alpha or 255 583 | pnl:SetAlpha(0) 584 | pnl:AlphaTo(alpha, time) 585 | end 586 | 587 | classes.HideVBar = function(pnl) 588 | local vbar = pnl:GetVBar() 589 | vbar:SetWide(0) 590 | vbar:Hide() 591 | end 592 | 593 | classes.SetTransitionFunc = function(pnl, fn) 594 | pnl.TransitionFunc = fn 595 | end 596 | 597 | classes.ClearTransitionFunc = function(pnl) 598 | pnl.TransitionFunc = nil 599 | end 600 | 601 | classes.SetAppendOverwrite = function(pnl, fn) 602 | pnl.AppendOverwrite = fn 603 | end 604 | 605 | classes.ClearAppendOverwrite = function(pnl) 606 | pnl.AppendOverwrite = nil 607 | end 608 | 609 | classes.ClearPaint = function(pnl) 610 | pnl.Paint = nil 611 | end 612 | 613 | classes.ReadyTextbox = function(pnl) 614 | pnl:SetPaintBackground(false) 615 | 616 | pnl:SetAppendOverwrite("PaintOver"):SetTransitionFunc(function(s) 617 | return s:IsEditing() 618 | end) 619 | end 620 | 621 | --[[--------------------------------------------------------------------------- 622 | TDLib function which adds all the classes to your panel 623 | ---------------------------------------------------------------------------]] 624 | local meta = FindMetaTable("Panel") 625 | 626 | function meta:TDLib() 627 | self.Class = function(pnl, name, ...) 628 | local class = classes[name] 629 | assert(class, "[TDLib]: Class " .. name .. " does not exist.") 630 | class(pnl, ...) 631 | 632 | return pnl 633 | end 634 | 635 | for k, v in pairs(classes) do 636 | self[k] = function(s, ...) 637 | return s:Class(k, ...) 638 | end 639 | end 640 | 641 | return self 642 | end 643 | 644 | function TDLib(c, p, n) 645 | local pnl = vgui.Create(c, p, n) 646 | 647 | return pnl:TDLib() 648 | end 649 | 650 | return (TDLib) -------------------------------------------------------------------------------- /lua/badcoderz/server/sv_network.lua: -------------------------------------------------------------------------------- 1 | util.AddNetworkString("BadCoderz_status_request") 2 | util.AddNetworkString("BadCoderz_report_request") 3 | util.AddNetworkString("BadCoderz_scan_request") 4 | util.AddNetworkString("BadCoderz_compiled_report_request") 5 | util.AddNetworkString("BadCoderz_serverside_file_open") 6 | util.AddNetworkString("BadCoderz_serverside_bytecode_reader") 7 | util.AddNetworkString("BadCoderz_next_reboot_scan_change_status") 8 | util.AddNetworkString("BadCoderz_stop_boot_scan") 9 | 10 | net.Receive("BadCoderz_status_request",function (len, context) 11 | if not context:CanUseBadCoderz() then return end 12 | net.Start("BadCoderz_status_request") 13 | net.WriteBool(BadCoderz.scanningCodeSmells) 14 | net.WriteBool(BadCoderz.scanningCompiledFuncs) 15 | net.WriteBool(BadCoderz.scanCompiledFuncsOnNextBoot) 16 | net.Send(context) 17 | end) 18 | 19 | 20 | function BadCoderz.WriteReport(ply) 21 | if not ply:CanUseBadCoderz() then return end 22 | local json = util.TableToJSON(BadCoderz.getReport()) -- big boi 23 | local data = util.Compress(json) 24 | 25 | if string.len(data) > 64000 then 26 | error("Report is too big :(") 27 | end 28 | 29 | net.Start("BadCoderz_report_request") 30 | net.WriteBool(BadCoderz.scanningCodeSmells) 31 | net.WriteUInt(string.len(data), 16) 32 | net.WriteData(data, string.len(data)) 33 | net.Send(ply) 34 | end 35 | 36 | 37 | net.Receive("BadCoderz_report_request",function (_, ply) 38 | if not ply:CanUseBadCoderz() then return end 39 | BadCoderz.WriteReport(ply) 40 | end) 41 | 42 | net.Receive("BadCoderz_scan_request",function (_, ply) 43 | if not ply:CanUseBadCoderz() then return end 44 | BadCoderz.toggleCodeSmellsScan() 45 | end) 46 | 47 | 48 | net.Receive("BadCoderz_serverside_file_open",function (_, ply) 49 | if not ply:CanUseBadCoderz() then return end 50 | 51 | local gamePath = net.ReadString() 52 | local fakePath = net.ReadString() 53 | local line; 54 | local text 55 | if (net.ReadBool() == true) then 56 | line = net.ReadUInt(13) 57 | local client = net.ReadBool() 58 | local _func 59 | if client == false then 60 | local func = net.ReadString() 61 | local hookname = net.ReadString() 62 | local location = net.ReadString() 63 | local callLineID = net.ReadUInt(13) 64 | local k = net.ReadUInt(8) 65 | _func = BadCoderz.getReport()[func][hookname][location][callLineID].funcs[k] 66 | end 67 | if file.Exists(gamePath, "LUA") then 68 | text = file.Read(gamePath,"LUA") 69 | elseif GLib then 70 | text = GLib.Lua.BytecodeReader(_func):ToString() 71 | else 72 | ply:ChatPrint("You need GLib to decompile this function, get it here : https://github.com/notcake/glib") 73 | return 74 | end 75 | else 76 | if not file.Exists(gamePath, "LUA") then 77 | ply:ChatPrint("Can't open the file, it was created with compilestring, please try to open a function of it, not the file itself.") 78 | return 79 | end 80 | 81 | text = file.Read(gamePath,"LUA") 82 | end 83 | 84 | 85 | local data = util.Compress(text) 86 | local len = data:len() 87 | net.Start("BadCoderz_serverside_file_open") 88 | net.WriteString(fakePath) 89 | net.WriteUInt(len,16) 90 | net.WriteData(data, len) 91 | local sendLine = line != nil 92 | net.WriteBool(sendLine) 93 | if sendLine == true then 94 | net.WriteUInt(line, 13) 95 | end 96 | net.Send(ply) 97 | 98 | end) 99 | 100 | 101 | 102 | net.Receive("BadCoderz_serverside_bytecode_reader",function (_, ply) 103 | if not ply:CanUseBadCoderz() then return end 104 | local index = net.ReadUInt(16) 105 | local funcTable = BadCoderz.compiledFuncs[index] 106 | assert(funcTable, "Index for decompiled function " .. index .. " doesn't exist") 107 | 108 | net.Start("BadCoderz_serverside_bytecode_reader") 109 | local byteCode = util.Compress(GLib.Lua.BytecodeReader(funcTable.func):ToString()) 110 | local byteCodeLen = byteCode:len() 111 | net.WriteUInt(byteCodeLen, 16) 112 | net.WriteData(byteCode, byteCodeLen) 113 | net.WriteString(funcTable.pointer) 114 | net.WriteString(funcTable.stack) 115 | net.WriteString(funcTable.location) 116 | net.Send(ply) 117 | end) 118 | 119 | net.Receive("BadCoderz_compiled_report_request", function(_, ply) 120 | if not ply:CanUseBadCoderz() then return end 121 | local len = #BadCoderz.compiledFuncs 122 | net.Start("BadCoderz_compiled_report_request") 123 | net.WriteUInt(len, 16) 124 | local i = 1 125 | while (i <= len) do 126 | local funcTable = BadCoderz.compiledFuncs[i] 127 | local byteCode = string.dump(funcTable.func) 128 | local byteCodeLen = byteCode:len() 129 | net.WriteUInt(byteCodeLen, 16) 130 | net.WriteString(funcTable.location) 131 | net.WriteString(funcTable.pointer) 132 | net.WriteUInt(funcTable.level, 4) 133 | net.WriteFloat(funcTable.time) 134 | i = i + 1 135 | end 136 | net.Send(ply) 137 | end) 138 | 139 | 140 | net.Receive("BadCoderz_next_reboot_scan_change_status", function(len, ply) 141 | if not ply:CanUseBadCoderz() then return end 142 | BadCoderz.prepareCompiledFuncsScanNextReboot(net.ReadBool()) 143 | end) 144 | 145 | net.Receive("BadCoderz_stop_boot_scan", function(len, ply) 146 | if not ply:CanUseBadCoderz() or not BadCoderz.scanningCompiledFuncs then return end 147 | BadCoderz.scanningCompiledFuncs = false 148 | debug.sethook() 149 | end) -------------------------------------------------------------------------------- /lua/badcoderz/sh_code_smells.lua: -------------------------------------------------------------------------------- 1 | local reports = {} 2 | 3 | local function do_report(func, hookname, lines, parentFuncs) 4 | reports[func] = reports[func] or {} 5 | reports[func][hookname] = reports[func][hookname] or {} 6 | reports[func][hookname][lines[#lines].location] = reports[func][hookname][lines[#lines].location] or {} 7 | 8 | if not reports[func][hookname][lines[#lines].location][lines[#lines].line] then 9 | reports[func][hookname][lines[#lines].location][lines[#lines].line] = { 10 | calls = 1, 11 | lines = lines, 12 | funcs = parentFuncs 13 | } 14 | else 15 | reports[func][hookname][lines[#lines].location][lines[#lines].line].calls = reports[func][hookname][lines[#lines].location][lines[#lines].line].calls + 1 16 | end 17 | end 18 | 19 | function BadCoderz.getReport() 20 | return reports 21 | end 22 | 23 | local color_red = Color(255, 50, 50) 24 | 25 | local function fixGMAPath(path) 26 | if file.Exists("garrysmod/" .. path, "BASE_PATH") then return path end 27 | 28 | for k, v in pairs(BadCoderz.GMA_DB) do 29 | if table.HasValue(v, path) then 30 | path = k .. "/" .. path 31 | 32 | return path 33 | end 34 | end 35 | 36 | return path 37 | end 38 | 39 | --[[ 40 | Some libs like ULib overwrite the hooks functions, so to detect it by locations we need to manually run a hook 41 | then detect the top function in the call stack and decide it's the file where the call comes from 42 | ]] 43 | local function initScan() 44 | local debugTable 45 | local level = 1 46 | 47 | while true do 48 | local _debugTable = debug.getinfo(level, "S") 49 | if not _debugTable then break end 50 | debugTable = _debugTable 51 | level = level + 1 52 | end 53 | 54 | assert(debugTable, "What the fuck did you do to the hook system ?") 55 | BadCoderz.potentialsHooksFiles[debugTable.source:sub(2)] = true 56 | hook.Remove("Think", "badCoderzTrapHook") 57 | end 58 | 59 | local hookNames = { 60 | name = true, 61 | event_name = true 62 | } 63 | 64 | local function _hook() 65 | local curStackLevel = 2 66 | local calledFunc = debug.getinfo(curStackLevel, "f").func 67 | if not BadCoderz.heavy_funcs[calledFunc] then return end 68 | --threats functions like Color/Angle/Vector in a different way since the way it's called matters 69 | local heavyObject = BadCoderz.heavy_funcs_objects[calledFunc] 70 | 71 | if heavyObject then 72 | local callingContext = debug.getinfo(curStackLevel + 1, "fSl") 73 | if callingContext.what == "C" then return end -- C could be calling it for some reason 74 | local callingContextFunc = callingContext.func 75 | local found = BadCoderz.find_call_static_args(callingContextFunc, heavyObject, callingContext.currentline) 76 | if not found then return end 77 | end 78 | 79 | local level = 0 80 | traceTable = {} 81 | 82 | while true do 83 | local debugTable = debug.getinfo(level, "Sl") 84 | if not debugTable then break end 85 | level = level + 1 86 | 87 | -- already got level++'ed, don't do it again here 88 | traceTable[level] = { 89 | location = debugTable.source:sub(2), 90 | line = debugTable.currentline 91 | } 92 | end 93 | 94 | --------- getting the hook call 95 | local foundhookLevel = -1 96 | local foundHookContext 97 | local topStack = #traceTable 98 | local stackData = traceTable[topStack] 99 | local name, value = debug.getlocal(topStack - 1, 1) 100 | local foundName 101 | 102 | if name == "self" then 103 | local func = debug.getinfo(topStack - 1, "f").func 104 | 105 | if value == gmod.GetGamemode() then 106 | for k, v in pairs(value) do 107 | if not isfunction(v) then continue end 108 | 109 | if v == func then 110 | foundName = k 111 | break 112 | end 113 | end 114 | 115 | if foundName and BadCoderz.dangerous_hooks[foundName] then 116 | foundHookContext = "GM:" .. foundName 117 | foundhookLevel = topStack - 1 118 | else 119 | return 120 | end 121 | elseif IsEntity(value) then 122 | for k, v in pairs(value:GetTable()) do 123 | if not isfunction(v) then continue end 124 | 125 | if func == v then 126 | foundName = k 127 | break 128 | end 129 | end 130 | 131 | if foundName and BadCoderz.dangerous_hooks[foundName] then 132 | foundHookContext = "ENT:" .. foundName 133 | foundhookLevel = topStack - 1 134 | else 135 | return 136 | end 137 | elseif ispanel(value) then 138 | for k, v in pairs(value:GetTable()) do 139 | if not isfunction(v) then continue end 140 | 141 | if func == v then 142 | foundName = k 143 | break 144 | end 145 | end 146 | 147 | if foundName and BadCoderz.dangerous_hooks[foundName] then 148 | foundHookContext = "PANEL:" .. foundName 149 | foundhookLevel = topStack - 1 150 | else 151 | return 152 | end 153 | else 154 | print("Var type " .. type(value) .. " is not implemented in BadCoderz, pls tell the dev") 155 | 156 | return 157 | end 158 | elseif BadCoderz.potentialsHooksFiles[stackData.location] then 159 | local hookStackLevel = topStack - 1 160 | local i = 1 161 | 162 | while (true) do 163 | local _name, _value = debug.getlocal(hookStackLevel, i) 164 | if (_name == nil) then break end 165 | 166 | if hookNames[_name] == true then 167 | foundName = _value 168 | break 169 | end 170 | 171 | i = i + 1 172 | end 173 | 174 | if foundName and BadCoderz.dangerous_hooks[foundName] then 175 | foundHookContext = "GM:" .. foundName 176 | foundhookLevel = topStack - 2 -- ignore the very stop stack cuz it's the hook call from Lua 177 | else 178 | return 179 | end 180 | else 181 | return 182 | end 183 | 184 | local lines = {} 185 | local functions = {} 186 | local targetStackLevel = curStackLevel 187 | 188 | if debug.getinfo(calledFunc).what == "Lua" then 189 | targetStackLevel = targetStackLevel + 1 190 | end 191 | 192 | while (foundhookLevel >= targetStackLevel) do 193 | local data = debug.getinfo(foundhookLevel, "lSf") 194 | 195 | if data.currentline == -1 then 196 | foundhookLevel = foundhookLevel - 1 197 | continue 198 | end 199 | 200 | local infoline = { 201 | location = fixGMAPath(data.source:gsub("^@", "")), 202 | line = data.currentline 203 | } 204 | 205 | table.insert(lines, infoline) 206 | table.insert(functions, data.func) 207 | foundhookLevel = foundhookLevel - 1 208 | end 209 | 210 | do_report(BadCoderz.heavy_funcs[calledFunc], foundHookContext, lines, functions) 211 | end 212 | 213 | local function start_scan() 214 | print("started scan") 215 | jit.off() 216 | -- you also need to flush the jit cache because it may miss something if one day LuaJIT stitching gets jitted with black magic 217 | jit.flush() 218 | hook.Add("Think", "badCoderzTrapHook", initScan) 219 | BadCoderz.scanningCodeSmells = true 220 | reports = {} 221 | 222 | if CLIENT and gui.IsConsoleVisible() then 223 | MsgC(color_red, "PLEASE CLOSE THE CONSOLE TO RUN ALL CLIENTS CHECKS\n") 224 | end 225 | 226 | debug.sethook(_hook, "c") -- hook functions calls 227 | end 228 | 229 | local function stop_scan() 230 | jit.on() 231 | BadCoderz.scanningCodeSmells = false 232 | debug.sethook(_hook, "") 233 | end 234 | 235 | function BadCoderz.toggleCodeSmellsScan() 236 | if not BadCoderz.scanningCodeSmells then 237 | start_scan() 238 | else 239 | stop_scan() 240 | end 241 | end 242 | -------------------------------------------------------------------------------- /lua/badcoderz/sh_compiled_functions.lua: -------------------------------------------------------------------------------- 1 | BadCoderz.compiledFuncs = BadCoderz.compiledFuncs or {} 2 | BadCoderz.scanningCompiledFuncs = false 3 | BadCoderz.scanCompiledFuncsOnNextBoot = false 4 | 5 | 6 | local filename; 7 | 8 | if CLIENT then 9 | filename = "badcoderz_scan_on_boot_cl.txt" 10 | else 11 | filename = "badcoderz_scan_on_boot_sv.txt" 12 | end 13 | 14 | function BadCoderz.prepareCompiledFuncsScanNextReboot(bEnable) 15 | if bEnable then 16 | file.Write(filename, "") 17 | BadCoderz.scanCompiledFuncsOnNextBoot = true 18 | else 19 | file.Delete(filename) 20 | BadCoderz.scanCompiledFuncsOnNextBoot = false 21 | end 22 | end 23 | 24 | 25 | local ignored_funcs = {} -- rip garbage collection 26 | 27 | local settings = { 28 | detectFakePath = true -- detecting fuckers doing RunString("bluh blug", "lua/legit_lua_file.lua") 29 | } 30 | 31 | 32 | local function call_wrapper(_) 33 | local debugInfos = debug.getinfo(2, "flnSu") 34 | local curFunction = debugInfos.func 35 | if ignored_funcs[curFunction] then return end 36 | local source = debugInfos.source:sub(2) -- source might need to be reformated to work with file.Read(source, "LUA") 37 | 38 | if source == "[C]" then 39 | ignored_funcs[curFunction] = true 40 | 41 | return 42 | end 43 | 44 | local lineDefined = debugInfos.linedefined 45 | 46 | if lineDefined ~= 0 then 47 | ignored_funcs[curFunction] = true 48 | 49 | return 50 | end 51 | 52 | local level = 0 53 | 54 | -- at this point if you're not even trying to hide it ... 55 | if source == "RunString" then 56 | level = 1 57 | elseif settings.detectFakePath and file.Exists(source, "GAME") then 58 | local code = file.Read(source, "GAME") 59 | local bytecode = CompileString(code, source) 60 | 61 | -- ghetto 62 | if string.dump(curFunction) == string.dump(bytecode) then 63 | ignored_funcs[curFunction] = true 64 | 65 | return 66 | end 67 | 68 | level = 2 69 | end 70 | 71 | 72 | table.insert(BadCoderz.compiledFuncs, { 73 | pointer = tostring(curFunction):sub(11), 74 | func = curFunction, 75 | location = source, 76 | level = level, 77 | time = SysTime(), 78 | stack = debug.traceback("", 2) 79 | }) 80 | end 81 | 82 | if file.Exists(filename, "DATA") then 83 | 84 | debug.sethook(call_wrapper, "c") 85 | BadCoderz.scanningCompiledFuncs = true 86 | file.Delete(filename) 87 | end 88 | 89 | 90 | -------------------------------------------------------------------------------- /lua/badcoderz/sh_data.lua: -------------------------------------------------------------------------------- 1 | BadCoderz.scanningCodeSmells = BadCoderz.scanningCodeSmells or false 2 | 3 | BadCoderz.heavy_funcs = { 4 | [player.GetAll] = "player.GetAll", 5 | [ents.GetAll] = "ents.GetAll", 6 | [file.Append] = "file.Append", 7 | [file.CreateDir] = "file.CreateDir", 8 | [file.Delete] = "file.Delete", 9 | [file.Exists] = "file.Exists", 10 | [file.Find] = "file.Find", 11 | [file.IsDir] = "file.IsDir", 12 | [file.Open] = "file.Open", 13 | [file.Read] = "file.Read", 14 | [file.Rename] = "file.Rename", 15 | [file.Size] = "file.Size", 16 | [file.Time] = "file.Time", 17 | [file.Write] = "file.Write", 18 | [Color] = "Color", 19 | [Vector] = "Vector", 20 | [Angle] = "Angle", 21 | [CompileString] = "CompileString", 22 | [RunString] = "RunString", 23 | [RunStringEx] = "RunStringEx", 24 | [table.HasValue] = "table.HasValue", 25 | 26 | } 27 | 28 | -- bool is represending a required(true) knum/kshort or an optional one (false), it's used when inspecting the bytecode 29 | BadCoderz.heavy_funcs_objects = { 30 | [Color] = { 31 | { 32 | ["Color"] = true, 33 | ["SetDrawColor"] = true 34 | }, 35 | {true, true, true, false} 36 | }, 37 | [Vector] = { 38 | { 39 | ["Vector"] = true 40 | }, 41 | {true, true, true} 42 | }, 43 | [Angle] = { 44 | { 45 | ["Angle"] = true 46 | }, 47 | {true, true, true} 48 | } 49 | } 50 | 51 | 52 | BadCoderz.toolTips = { 53 | ["player.GetAll"] = "This function is used to find all players, it depends of the implementation and what the dev is doing with it but there is good chances he's doing CPU Intensive things in this loop", 54 | ["ents.GetAll"] = "This function is used to find all entities (A LOT), it depends of the implementation and what the dev is doing with it but there is good chances he's doing CPU Intensive things in this loop", 55 | ["file.Append"] = "Working with files is always slow, doing it a lot is a TERRIBLE IDEA", 56 | ["Color"] = "You NEVER need to create a color on each frame with static arguments, cache it out of your rendering context.", 57 | ["Vector"] = "You NEVER need to create a vector with static arguments on each tick, cache it outside of the hook and if you need to, use the Vectors metamethods.\nEx :\n\tpos:Add(posOffset)\ninstead of :\n\tpos1+Vector(4,0,9)\n", 58 | ["Angle"] = "You NEVER need to create an angle with static arguments on each tick, cache it outside of the hook and if you need to, use the Angles metamethods.\nEx :\n\tang1:Add(ang2)\ninstead of :\n\tang1+Vector(4,0,9)\n" 59 | 60 | } 61 | 62 | BadCoderz.toolTips["file.CreateDir"] = BadCoderz.toolTips["file.Append"] 63 | BadCoderz.toolTips["file.Delete"] = BadCoderz.toolTips["file.Append"] 64 | BadCoderz.toolTips["file.Exists"] = BadCoderz.toolTips["file.Append"] 65 | BadCoderz.toolTips["file.Find"] = BadCoderz.toolTips["file.Append"] 66 | BadCoderz.toolTips["file.IsDir"] = BadCoderz.toolTips["file.Append"] 67 | BadCoderz.toolTips["file.Open"] = BadCoderz.toolTips["file.Append"] 68 | BadCoderz.toolTips["file.Read"] = BadCoderz.toolTips["file.Append"] 69 | BadCoderz.toolTips["file.Rename"] = BadCoderz.toolTips["file.Append"] 70 | BadCoderz.toolTips["file.Size"] = BadCoderz.toolTips["file.Append"] 71 | BadCoderz.toolTips["file.Time"] = BadCoderz.toolTips["file.Append"] 72 | BadCoderz.toolTips["file.Write"] = BadCoderz.toolTips["file.Append"] 73 | 74 | 75 | --pretty much the heavy hooks where you should not use IO/heavy funcs 76 | BadCoderz.dangerous_hooks = { 77 | ["Tick"] = true, 78 | ["Think"] = true, 79 | ["PlayerDeathThink"] = true, 80 | ["PlayerPostThink"] = true, 81 | ["PlayerTick"] = true, 82 | ["HUDAmmoPickedUp"] = true, 83 | ["HUDItemPickedUp"] = true, 84 | ["HUDPaint"] = true, 85 | ["HUDPaintBackground"] = true, 86 | ["Paint"] = true, 87 | ["HUDWeaponPickedUp"] = true, 88 | ["DrawDeathNotice"] = true, 89 | ["DrawMonitors"] = true, 90 | ["DrawOverlay"] = true, 91 | ["DrawPhysgunBeam"] = true, 92 | ["HUDDrawPickupHistory"] = true, 93 | ["HUDDrawScoreBoard"] = true, 94 | ["HUDDrawTargetID"] = true, 95 | ["PostDraw2DSkyBox"] = true, 96 | ["PostDrawEffects"] = true, 97 | ["PostDrawHUD"] = true, 98 | ["PostDrawOpaqueRenderables"] = true, 99 | ["PostDrawPlayerHands"] = true, 100 | ["PostDrawSkyBox"] = true, 101 | ["PostDrawTranslucentRenderables"] = true, 102 | ["PostPlayerDraw"] = true, 103 | ["PreDrawEffects"] = true, 104 | ["PreDrawHalos"] = true, 105 | ["PreDrawHUD"] = true, 106 | ["PreDrawOpaqueRenderables"] = true, 107 | ["PreDrawPlayerHands"] = true, 108 | ["PreDrawSkyBox"] = true, 109 | ["PreDrawTranslucentRenderables"] = true, 110 | ["PreDrawViewModel"] = true, 111 | ["PrePlayerDraw"] = true, 112 | ["PostDraw2DSkyBox"] = true, 113 | ["PostDrawEffects"] = true, 114 | ["PostDrawHUD"] = true, 115 | ["PostDrawOpaqueRenderables"] = true, 116 | ["PostDrawPlayerHands"] = true, 117 | ["PostDrawSkyBox"] = true, 118 | ["PostDrawTranslucentRenderables"] = true, 119 | ["PreRender"] = true, 120 | ["PreDrawEffects"] = true, 121 | ["PreDrawHUD"] = true, 122 | ["PreDrawHalos"] = true, 123 | ["PreDrawOpaqueRenderables"] = true, 124 | ["PreDrawPlayerHands"] = true, 125 | ["PreDrawSkyBox"] = true, 126 | ["PreDrawTranslucentRenderables"] = true, 127 | ["PostPlayerDraw"] = true, 128 | ["Move"] = true, 129 | ["VehicleMove"] = true, 130 | ["Draw"] = true, 131 | ["DrawTranslucent"] = true, 132 | ["CalcAbsolutePosition"] = true, 133 | ["CalcMainActivity"] = true, 134 | ["CalcVehicleView"] = true, 135 | ["CalcView"] = true, 136 | ["CalcViewModelView"] = true, 137 | ["DrawWeaponSelection"] = true, 138 | ["DrawWorldModel"] = true, 139 | ["DrawWorldModelTranslucent"] = true, 140 | ["ViewModelDrawn"] = true 141 | } 142 | if CLIENT then 143 | BadCoderz.toolTips["player.GetAll"] = "This function is used to find all players, it depends of the implementation and what the dev is doing with it but there is good chances he's doing CPU Intensive things in this loop" 144 | BadCoderz.heavy_funcs[surface.CreateFont] = "surface.CreateFont" 145 | BadCoderz.heavy_funcs[surface.GetTextureID] = "surface.GetTextureID" 146 | BadCoderz.heavy_funcs[surface.CreateFont] = "surface.CreateFont" 147 | BadCoderz.heavy_funcs[Material] = "Material" 148 | BadCoderz.heavy_funcs[vgui.Create] = "vgui.Create" 149 | BadCoderz.toolTips["surface.CreateFont"] = "Creating a font on each frame is destroying performance, there is only one specific case where it's needed, when you want to smooth for size change in real time, but it's still eating too much CPU Time" 150 | BadCoderz.toolTips["surface.GetTextureID"] = "Common mistake, reading textures from the disk on each frame instead of caching it outside of the rendering function." 151 | BadCoderz.toolTips["Material"] = BadCoderz.toolTips["surface.GetTextureID"] 152 | BadCoderz.toolTips["vgui.Create"] = "Common mistake (especially for avatars), created Derma on user interaction, and only on user interaction." 153 | 154 | BadCoderz.settings = { 155 | auto_close_menu = CreateClientConVar("badcoderz_autoclosemenu", "0", true, false, "Auto close BadCoderz menu after starting a scan"), 156 | auto_reopen_menu = CreateClientConVar("badcoderz_autoreopenmenu", "1", true, false, "Auto re-open BadCoderz menu after it was auto-closed"), 157 | auto_reopen_menu_time = CreateClientConVar("badcoderz_autoreopenmenu_time", "15", true, false, "After how many seconds the menu should reopen") 158 | } 159 | end 160 | 161 | -- clientside functions 162 | BadCoderz.potentialsHooksFiles = {} 163 | BadCoderz.potentialsHooksFiles["lua/includes/modules/hook.lua"] = true 164 | -------------------------------------------------------------------------------- /lua/badcoderz/sh_gmaparser.lua: -------------------------------------------------------------------------------- 1 | BadCoderz.GMA_DB = {} 2 | 3 | --[[ 4 | Documentation here : 5 | https://github.com/Facepunch/garrysmod-requests/issues/1394#issuecomment-524551787 6 | ]] 7 | 8 | 9 | -- go forward till you "meet" a \0 10 | local function nextzero(file, _return) 11 | local byte = file:ReadByte() 12 | local str = "" 13 | 14 | while (byte ~= 0) do 15 | if (_return) then 16 | str = str .. string.char(byte) 17 | end 18 | 19 | byte = file:ReadByte() 20 | end 21 | 22 | if _return then return str end 23 | end 24 | 25 | local function parseFileList(gma_path) 26 | local gma = file.Open(gma_path, "rb", "GAME") 27 | if not gma then return nil end 28 | local filelist = {} 29 | gma:Skip(22) 30 | nextzero(gma) 31 | nextzero(gma) 32 | nextzero(gma) 33 | gma:Skip(4) 34 | 35 | while (true) do 36 | local finenum = gma:ReadLong() 37 | if (finenum == 0) then break end -- 2019 hack 38 | local fileName = nextzero(gma, true) 39 | 40 | if string.EndsWith(fileName, ".lua") then 41 | table.insert(filelist, fileName) 42 | end 43 | 44 | gma:Skip(12) 45 | end 46 | 47 | gma:Close() 48 | 49 | return filelist 50 | end 51 | 52 | local function buildDataBase() 53 | for _, v in ipairs(engine.GetAddons()) do 54 | if v.mounted == false then continue end 55 | local fileList = parseFileList(v.file) 56 | BadCoderz.GMA_DB[v.file] = fileList 57 | end 58 | end 59 | 60 | hook.Add("Initialize", "BadCoderzBuildDB", buildDataBase) -------------------------------------------------------------------------------- /lua/badcoderz/sh_luajit_decompiler.lua: -------------------------------------------------------------------------------- 1 | 2 | --[[ 3 | If you're interrested in this kind of stuff you can go here 4 | https://github.com/ExtReMLapin/luajit_func_bytecode_toolbox 5 | 6 | https://gist.github.com/meepen/807dd81a572ffb0f28a8c44c04922fdd 7 | 8 | ]] 9 | 10 | local instructions_db; 11 | 12 | if jit.version_num < 20100 then 13 | instructions_db = { 14 | CALL = 62, 15 | KSHORT = 39, 16 | GGET = 52, 17 | UGET = 43, 18 | FUNCF = 85, 19 | KNUM = 40 20 | } 21 | else -- bytecode changes 22 | instructions_db = { 23 | CALL = 66, 24 | KSHORT = 41, 25 | GGET = 54, 26 | UGET = 45, 27 | FUNCF = 89, 28 | KNUM = 42 29 | } 30 | end 31 | 32 | local function disassemble_function(fn) 33 | local upvalues = {} 34 | n = 0 35 | local upvalue = jit.util.funcuvname(fn, n) 36 | while (upvalue ~= nil) do 37 | upvalues[n] = upvalue 38 | n = n + 1 39 | upvalue = jit.util.funcuvname(fn, n) 40 | end 41 | 42 | 43 | -- consts are BELLOW zero, 64bits nums (KNUMS) are ABOVE zero but we don't need them here, we're just looking for the functions call names 44 | local consts = {} 45 | n = -1 46 | local value = jit.util.funck(fn, n) 47 | while (value ~= nil) do 48 | consts[-1 * n - 1] = value 49 | n = n - 1 50 | value = jit.util.funck(fn, n) 51 | end 52 | n = 1 53 | 54 | local countBC = jit.util.funcinfo(fn).bytecodes 55 | local instructions = {} 56 | 57 | if bit.band(select(1, jit.util.funcbc(fn, 0)), 0xFF) < instructions_db.FUNCF then 58 | print("BadCoderz decompiler : this should not happen") 59 | end 60 | 61 | while (n < countBC) do 62 | local ins = jit.util.funcbc(fn, n) 63 | local instruction = { 64 | C = bit.rshift(bit.band(ins, 0x00ff0000), 16), 65 | B = bit.rshift(ins, 24), 66 | A = bit.rshift(bit.band(ins, 0x0000ff00), 8), 67 | D = bit.rshift(ins, 16), 68 | OP_CODE = bit.band(ins, 0xFF) 69 | } 70 | instructions[n] = instruction 71 | n = n + 1 72 | end 73 | 74 | local ret = { 75 | consts = consts, 76 | instructions = instructions, 77 | upvalues = upvalues 78 | } 79 | 80 | return ret 81 | end 82 | 83 | function BadCoderz.find_call_static_args(fn, definition, expectedLine) 84 | -- to fix : it just find any Color() with static short int call, doesn't return the line 85 | local minNums = 0 86 | local maxNums = 0 87 | for k, v in ipairs(definition[2]) do 88 | if v == true then 89 | minNums = minNums + 1 90 | end 91 | maxNums = maxNums + 1 92 | end 93 | 94 | 95 | 96 | local disassembled_code = disassemble_function(fn) 97 | local targeted_consts = {} 98 | local targeted_upvalues = {} 99 | 100 | for k, v in pairs(disassembled_code.consts) do 101 | if definition[1][v] then 102 | targeted_consts[k] = true 103 | end 104 | end 105 | 106 | for k, v in pairs(disassembled_code.upvalues) do 107 | if definition[1][v] then 108 | targeted_upvalues[k] = true 109 | end 110 | end 111 | 112 | if (table.Count(targeted_consts) == 0) and (table.Count(targeted_upvalues) == 0) then 113 | return false, "Not found in const" 114 | end 115 | 116 | local count = #disassembled_code.instructions 117 | local i = 1 118 | 119 | while (i <= count) do 120 | local instruction = disassembled_code.instructions[i] 121 | 122 | if (((instruction.OP_CODE == instructions_db.GGET) and (targeted_consts[instruction.D] == true) or 123 | (instruction.OP_CODE == instructions_db.UGET) and (targeted_upvalues[instruction.D] == true)) 124 | and ((i + minNums) < count) and jit.util.funcinfo(fn, i).currentline == expectedLine) then 125 | local NUMBER_count = 0 126 | local i2 = i + 1 127 | local cur_instruction = disassembled_code.instructions[i2] 128 | 129 | while ((i2 <= count) and ((cur_instruction.OP_CODE == instructions_db.KSHORT) or (cur_instruction.OP_CODE == instructions_db.KNUM))) do 130 | NUMBER_count = NUMBER_count + 1 131 | i2 = i2 + 1 132 | cur_instruction = disassembled_code.instructions[i2] 133 | end 134 | 135 | if (NUMBER_count < minNums or NUMBER_count > maxNums) then 136 | i = i + 1 137 | continue 138 | end 139 | 140 | if (cur_instruction.OP_CODE ~= instructions_db.CALL) then 141 | i = i + 1 142 | continue 143 | end 144 | 145 | return true 146 | end 147 | 148 | i = i + 1 149 | end 150 | 151 | return false, "didnt find shit" 152 | end -------------------------------------------------------------------------------- /sound/badcoderz/oof.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtReMLapin/BadCoderz/8e8f26f1c25239a151d70bec7b1b13c19c88590c/sound/badcoderz/oof.mp3 --------------------------------------------------------------------------------