Skip to content

CFC-Servers/gm_gangbox

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 

Repository files navigation

Gangbox

A collection of helpful tools for any GMod developer.

Gangbox (also gang box) is a colloquial term utilized in the construction industry, referring to a toolbox or workbox that can be accessed by multiple workers.

Features

Gangbox offers a lot of useful, time-saving modules. Here are a few of the major ones:

  • ⚠️ Alerts for no-op or silent failures in the GMod API
  • ↔️ Git-Like Table Diffs
  • 🗑️ Helpful Garbage Collector functions
  • 🔎 Asynchronous file finding
  • 🔢 Bitflag pretty-printing
  • 🎲 Functions to return random datasets for testing
  • 📝 Table helper functions (like Map/Filter)
  • 🧵 Super simple Threading functions to run large amounts of work asynchronously
  • 🎨 ANSI Color output for server terminal coloring

Modules

Alerts

Alert modules simply give you printed feedback if you mess something up and it's not immediately clear what you did.

file.Write with an invalid (or no) suffix

In GMod, if you do:

file.Write( "test.blah", "this fails because .blah is not a valid suffix" )

-- or

file.Write( "test", "this fails because it has no suffix" )

It will simply do nothing, because the filename does not have a valid suffix.

With Gangbox, a message is printed (and a non-halting error thrown) in your terminal/output if you write to an invalid suffix: hl2_owARE2QK0x

Entity:AddCallback() with nil return

If you add a Callback function to an entity with an invalid function, or an invalid hook, AddCallback will return nil

Developers typically don't check for this, because realistically it's just a development mistake.

As a result, there's really no way to know that this failed. Gangbox prints an alert and raises a (non-halting) error if this happens, giving you visibility to an otherwise frustrating situation:

hl2_FleE3umtSL


Table Diffing

Sometimes you need to compare two similar, but different tables. Usually this means you print both tables and manually compare them.

But that's tedious. Gangbox offers a function that prints a git-like Diff between two tables.

hl2_LRckBdwxF2

gb.Diff( table t1, table t2 )

Arguments

  1. table t1
    • The table to compare t2 against
  2. table t2
    • The table to compare against t1

Example

Diff two similar entities save tables

gb.Diff( Entity( 106 ):GetSaveTable(), Entity( 109 ):GetSaveTable() )

File Finding

If you ever need to find certain files by name, the asynchronous gb.Find function is the tool for you.

hALBm85MGt.mp4

gb.Find( string search )

Description

gb.Find will asynchronously search through all of the Game files and print a list of any files that match your string search.

You may also use the gbfind concommand.

Arguments

  1. string search
    • The substring to search for in all qualifying file names

Example

Searches all files for the string "wiremonitorbig"

gb.Find( "wiremonitorbig" )

Or, in your console:

gbfind wiremonitorbig

Bitflag Parsing / Pretty-printing

Bitflags are common in Garry's Mod. Unfortunately, they're difficult to understand on their own.

Gangbox's ParseFlags function will parse any Bitflag in the game and print each flag that makes up the full bitflag.

hl2_cRPTVn4658

gb.ParseFlags( any subject, table? flagSet )

Description

Print all of the flags within a given Bitflag.

Arguments

  1. any subject
    • The bitflag, or subject containing the Bitflag.
    • If subject is a number, the second parameter (flagSet) is required to tell the function what kind of Bitflag it is.
    • Can be one of:
      • number: The bitflag number itself (flagSet must be provided)
      • ConVar: Prints the ConVar's FCVAR_ flags
      • IMaterial: Prints the Material's $flags and $flags2 details
      • Entity: Prints the Entity's Engine (EFL_), Spawn (SF_), and Solid (FSOLID_) flags
      • CTakeDamageInfo: Prints the DamageInfo's Damage Type (DMG_) flags
      • TraceResult struct: Prints the Surface (SURF_) or Displacement Surface (DISPSURF_) flags
  2. table? flagSet (optional)
    • A bitflag table to parse the given subject with
    • (You'll likely want to use one of the flagsets stored in gb.Bitflags)

Example

Parse a convar's Bitflags

gb.ParseFlags( GetConVar( "example_convar" ) )

Generating Random Data

Sometimes while developing, you just need some data. It doesn't matter specifically what data you have, you just need some.

Gangbox offers a collection of Random functions that return whatever data type(s) you need.

gb.RandomBool( number trueWeight )

Description

Flips a coin, but with an optional weight.

Arguments

  1. number trueWeight
    • Adjusts the likelihood of returning 'true'
    • (e.g. 1 means equal chance, 2 means double chance of true, 0.5 means half chance of true)

Example

"Flip a coin" - 50/50 chance of true/false:

-- These are the same
gb.RandomBool()
	true
gb.RandomBool( 1 )
	false

80% chance (4x) of returning true:

gb.RandomBool( 4 )
	true
gb.RandomBool( 4 )
	true
gb.RandomBool( 4 )
	true
gb.RandomBool( 4 )
	true
gb.RandomBool( 4 )
	false

gb.RandomFloat( number scale )

Description

Returns a random float with the given scale

Arguments

  1. number scale
    • The scale of the float (from -scale to scale)

Example

Return a random float between -20 and 20:

gb.RandomFloat( 20 )
	-7.9041241188604
gb.RandomFloat( 20 )
	-5.7258202419517
gb.RandomFloat( 20 )
	-15.256068461918
gb.RandomFloat( 20 )
	12.474272548027
gb.RandomFloat( 20 )
	-0.72508905413891

gb.RandomInt( number scale )

Description

Returns a random whole number with the given scale

Arguments

  1. number scale
    • The scale of the int (from -scale to scale)

Example

Return a random int between -5 and 5:

gb.RandomInt( 5 )
	3
gb.RandomInt( 5 )
	1
gb.RandomInt( 5 )
	-1
gb.RandomInt( 5 )
	2
gb.RandomInt( 5 )
	1

gb.RandomNumber( number scale )

Description

Returns a random number (either a float or an integer, equal chance).

Arguments

  1. number scale
    • The scale of the number (from -scale to scale)

Example

Return a random number between -40 and 40:

gb.RandomNumber( 40 )
	-10.120163536926
gb.RandomNumber( 40 )
	-36.65331504445
gb.RandomNumber( 40 )
	-4
gb.RandomNumber( 40 )
	-12.704684187564
gb.RandomNumber( 40 )
	38.994572482706

gb.RandomString( number len, number minChar, number maxChar )

Description

Returns a random string with the given length, and optionally, a utf8 character range.

Arguments

  1. number len
    • The end-length of the string
  2. number minChar (optional)
    • The minimum utf8 character to use
    • (defaults to 97)
  3. numer maxChar (optional)
    • The maximum utf8 character to use
    • (defaults to 122)

Example

Return a random string with 30 character

gb.RandomString( 30 )
	-- 21 B
	"eqrzfblsawmvdbqoougbf"

gb.RandomEnt()

Description

Returns a random Entity on the map. This is Shared-safe, meaning it will not return clientside entities on the Client.

Example

Return a random Entity

gb.RandomEnt()
	-- weapon_crossbow
	-- models/weapons/w_crossbow.mdl
	Entity (94) {}
	-- 1 total entry.

gb.RandomVec( number scale )

Description

Returns a random Vector containing random values within the given scale

Arguments

  1. number scale (optional)
    • The scale of the numbers within the Vector
    • (defaults to 1)

Example

Return a random Vector with values between -10 and 10

gb.RandomVec( 10 )
	Vector (8.5804996490479, -7.8739347457886, -8.8563461303711)

gb.RandomAngle()

Description

Returns a random Angle with values between -360 and 360

Example

Return a random Angle

gb.RandomAngle()
	Angle (313, -12, 174)
gb.RandomAngle()
	Angle (3, 333, -307)
gb.RandomAngle()
	Angle (329, 157, -249)
gb.RandomAngle()
	Angle (-332, -295, -175)
gb.RandomAngle()
	Angle (-167, -315, 310)

gb.RandomColor()

Description

Returns a random Color

Example

Return a random Color

hl2_d5ozt9FXqU

gb.RandomFillTable( table tbl, number count )

Description

Fills the given table with random data

Arguments

  1. table tbl
    • The numerically-indexed table to fill with random data
  2. number count (optional)
    • The total number of elements to insert
    • (defaults to 1000)

Example

Fill a global table with 10 elements

mytbl = {}
gb.RandomFillTable( mytbl, 10 )
mytbl
	-- 0xe18c4c32
	{
		[ 1] = Color ( 32, 203,  61, 255),
		[ 2] = Angle ( 331               ,    9               , -293               ),
		[ 3] = Entity ( 84)               --[[ weapon_crowbar, models/weapons/w_crowbar.mdl ]],
		[ 4] = Angle ( 295               , - 36               ,  326               ),
		[ 5] = Color (195, 169, 179, 255),
		[ 6] = Angle (- 42               , -173               ,   78               ),
		[ 7] = Angle ( 341               ,  218               ,  327               ),
		[ 8] = -  0.84973641542287,
		[ 9] = "frquqhozabmzeycclhzxsbyasnglyhlshrznnxkgsiqsskret",
		[10] = Entity (105)               --[[ viewmodel      ]]
	}               

gb.RandomTable( number count )

Description

Returns a table filled with random elements.

Arguments

  1. number count
    • The total number of elements present in the return table

Example

Create a table with 10 random elements

gb.RandomTable( 10 )
	{
		[ 1] = Vector ( 0.75274169445038  , -0.60511749982834  , -0.17379862070084  ),
		[ 2] = Vector (-0.66277325153351  , -0.6087372303009   , -0.71377575397491  ),
		[ 3] = Vector ( 0.99587422609329  ,  0.72514963150024  ,  0.67650163173676  ),
		[ 4] = Vector ( 0.49982884526253  ,  0.34409433603287  ,  0.82090830802917  ),
		[ 5] = false,
		[ 6] = Vector (-0.10748841613531  , -0.0051460382528603,  0.76348125934601  ),
		[ 7] = -0.31730214164516,
		[ 8] =  0.59660673127015,
		[ 9] = Color (154,   7, 252, 255),
		[10] = Color (228,  65, 213, 255)
	}                       

Table Helpers

Gangbox ships with a number of table helpers that make sorting through / processing large tables a breeze.

gb.Filter( table tbl, function comp )

Description

Filters the given table using the given function.

Returns a table containing elements that pass the filter.

Arguments

  1. table tbl
    • The table to filter
  2. function comp
    • The filtering function, takes a single parameter:
      • any element: An element in the table.
        • Return true to keep the element in the output
        • Return false/nil to exclude the element from the output

Example

Filter all Entities, returning only weapons

gb.Filter( ents.GetAll(), function( e ) return e.IsWeapon and e:IsWeapon() end )
	{
		[15] = Entity ( 84) --[[ weapon_crowbar   , models/weapons/w_crowbar.mdl         ]],
		[21] = Entity ( 90) --[[ weapon_pistol    , models/weapons/w_pistol.mdl          ]],
		[22] = Entity ( 91) --[[ weapon_smg1      , models/weapons/w_smg1.mdl            ]],
		[23] = Entity ( 92) --[[ weapon_frag      , models/weapons/w_grenade.mdl         ]],
		[24] = Entity ( 93) --[[ weapon_physcannon, models/weapons/w_Physics.mdl         ]],
		[25] = Entity ( 94) --[[ weapon_crossbow  , models/weapons/w_crossbow.mdl        ]],
		[26] = Entity ( 95) --[[ weapon_shotgun   , models/weapons/w_shotgun.mdl         ]],
		[27] = Entity ( 96) --[[ weapon_357       , models/weapons/w_357.mdl             ]],
		[28] = Entity ( 97) --[[ weapon_rpg       , models/weapons/w_rocket_launcher.mdl ]],
		[29] = Entity ( 98) --[[ weapon_ar2       , models/weapons/w_irifle.mdl          ]],
		[30] = Entity ( 99) --[[ gmod_tool        , models/weapons/w_toolgun.mdl         ]],
		[31] = Entity (100) --[[ gmod_camera      , models/MaxOfS2D/camera.mdl           ]],
		[32] = Entity (101) --[[ weapon_physgun   , models/weapons/w_Physics.mdl         ]]
	}                

gb.Map( table tbl, function func )

Description

Runs the given function on all elements in the given table.

Returns a table containing the results of running the map function on each element in the table.

Arguments

  1. table tbl
    • The table to map over
  2. function comp
    • The mapping function, takes a single parameter:
      • any element: An element in the table.

Example

Returns the type of all elements in the given table

mytbl = gb.RandomTable( 5 )
	{
		[1] = Entity ( 94)               --[[ weapon_crossbow, models/weapons/w_crossbow.mdl ]],
		[2] = Entity (101)               --[[ weapon_physgun , models/weapons/w_Physics.mdl  ]],
		[3] = true,
		[4] = Entity (105)               --[[ viewmodel       ]],
		[5] = Color ( 18, 185, 172, 255)
	}
	-- 5 total entries.

gb.Map( mytbl, function( e ) return type( e ) end )
	{
		[1] = "Weapon",
		[2] = "Weapon",
		[3] = "boolean",
		[4] = "Entity",
		[5] = "table"
	}
	-- 5 total entries.                    

gb.CountRecursive( table tbl )

Description

Recursively counts the total number of elements in the given table. Searches all sub tables.

Arguments

  1. table tbl
    • The table to count

Example

Returns the total element count in a recursive table

mytbl = gb.RandomTable( 10 )
	{
		[1] = Vector (-0.82562392950058,  0.5458727478981 ,  0.39729726314545),
		[2] = Vector ( 0.79435861110687, -0.79544585943222, -0.37145271897316),
		[3] = "hqqmcspyaicboubkwqdesfaxjlscuvlpsgezuml",
		[4] = Entity (105)               --[[ viewmodel ]],
		[5] = Color (  3,  51,  69, 255) --[[]],
		[6] = "hrnmlwcjfozjkjvcurxjmcrunddoidlgqzcdtoekeu",
		[7] = "ypdcqddrskj",
		[8] = { --[[ table: 0xee9a3b2a ]] },
		[9] =  0
	}
	-- 9 total entries.

mytbl[8]
	{
		[1] = true,
		[2] = Angle (-230,  286,  128)
	}
	-- 2 total entries.

gb.CountRecursive( mytbl )
	10                                     

Threaded Work Helpers

Gangbox offers a couple of functions that let you split large chunks of work up over multiple ticks, keeping the game from freezing while you're working.

gb.ThreadWork( function func, table tbl )

Description

Run the given function on every element in the table asynchronously.

Arguments

  1. function func
    • The work function to run on each element. Takes two parameters:
      • any index: The index of an element in the table.
      • any element: The value of an element in the table.
  2. table tbl
    • The table with data to run the worker function on

Example

Generates a cache of functions in _R mapped to the file and line they're defined in. Would normally freeze the game, but threading the work means it can operate over a longer timeframe, only using a small amount of the available tick interval.

nameCache = {}

local worker = function( k, item )
    if not isfunction( item ) then return end
    
    local info = debug.getinfo( item )
    local sourceFile = info.short_src
    
    if sourceFile == "[C]" then return end
    
    local line = info.linedefined
    nameCache[item] = sourceFile .. ":" .. line
end

gb.ThreadWork( worker, _R )

Output:

Thread progress: 19%
Thread progress: 39%
Thread progress: 59%
Thread progress: 79%
Thread progress: 99%
Thread progress: 100%


PrintTable( nameCache )
	function: 0xb5ba2b6a	=	addons/glib/lua/glib/resources/resources.lua:196
	function: 0xb5bb9ff2	=	addons/glib/lua/glib/transfers/transfers.lua:200
	function: 0xbce91832	=	gamemodes/base/gamemode/cl_voice.lua:107
	function: 0xbcec3caa	=	lua/autorun/client/toolsearch.lua:129
	function: 0xbdac5d6a	=	lua/vgui/dcheckbox.lua:56
	function: 0xbe9e6f7a	=	lua/vgui/dmenu.lua:136
	function: 0xbe9f9f7a	=	addons/niknaks/lua/niknaks/modules/sh_datetime.lua:56
	function: 0xbea8edf2	=	lua/cw/shared/cw_cmodel_management.lua:30
	function: 0xbead5c6a	=	addons/mat_faker/lua/missing_no_more/client/progress.lua:52
	function: 0xbeaf0e7a	=	lua/mediaplayer/sh_mediaplayer.lua:231
	function: 0xbf9a58a2	=	lua/autorun/client/toolsearch.lua:129
	function: 0xbf9afa2a	=	lua/autorun/client/toolsearch.lua:129
	function: 0xbf9b982a	=	lua/vgui/dcategorylist.lua:30
	function: 0xbf9b9972	=	lua/autorun/client/toolsearch.lua:129
	function: 0xbf9ed9aa	=	lua/derma/init.lua:56
	function: 0xbfa83b72	=	lua/derma/init.lua:56
	function: 0xbfa918f2	=	lua/tfa/modules/cl_tfa_models.lua:3
	function: 0xbfa9bc7a	=	lua/derma/init.lua:56
	function: 0xbfaa6d62	=	lua/vgui/dimage.lua:149
	function: 0xbfacadfa	=	addons/ulib/lua/ulib/shared/hook.lua:97
	function: 0xbfbabe32	=	lua/autorun/client/toolsearch.lua:129
	function: 0xbfbbaf2a	=	lua/autorun/client/toolsearch.lua:129
	function: 0xbfceba72	=	lua/autorun/client/toolsearch.lua:129
	function: 0xbfcebd2a	=	lua/derma/init.lua:56
	function: 0xc4898a22	=	lua/autorun/client/toolsearch.lua:129
	function: 0xc48d09aa	=	lua/vgui/dcheckbox.lua:162
	function: 0xc48d2dfa	=	addons/cfc_wire/lua/entities/gmod_wire_egp/lib/egplib/queuesystem.lua:130
	function: 0xc48ed962	=	lua/vgui/dcategorycollapse.lua:166
	function: 0xc48fbd22	=	lua/autorun/client/toolsearch.lua:129
	function: 0xc49cffaa	=	lua/vgui/dtree_node.lua:553
    ...

gb.Thread( function func )

Description

This one is cool. Give gb.Thread any function that runs work in a for loop (pairs or ipairs), and the work will be run asynchronously instead, with no code changes required to the base function.

This is great if you need to run huge functions from another addon where you can't change the source, but you don't want it to freeze the game.

Arguments

  1. function func
    • The worker function. Takes no parameters.

Example

Same example as before, generating a source cache for all functions in _R, but this time, the function is defined by itself, and we Thread it after it's instantiated.

nameCache = {}

local worker = function()
    for _, item in ipairs( _R ) do
        if not isfunction( item ) then return end
        
        local info = debug.getinfo( item )
        local sourceFile = info.short_src
        
        if sourceFile == "[C]" then return end
        
        local line = info.linedefined
        nameCache[item] = sourceFile .. ":" .. line
    end
end

gb.Thread( worker )

Output:

Thread progress: 19%
Thread progress: 39%
Thread progress: 59%
Thread progress: 79%
Thread progress: 99%
Thread progress: 100%


PrintTable( nameCache )
	function: 0xb5ba2b6a	=	addons/glib/lua/glib/resources/resources.lua:196
	function: 0xb5bb9ff2	=	addons/glib/lua/glib/transfers/transfers.lua:200
	function: 0xbce91832	=	gamemodes/base/gamemode/cl_voice.lua:107
	function: 0xbcec3caa	=	lua/autorun/client/toolsearch.lua:129
	function: 0xbdac5d6a	=	lua/vgui/dcheckbox.lua:56
	function: 0xbe9e6f7a	=	lua/vgui/dmenu.lua:136
	function: 0xbe9f9f7a	=	addons/niknaks/lua/niknaks/modules/sh_datetime.lua:56
	function: 0xbea8edf2	=	lua/cw/shared/cw_cmodel_management.lua:30
	function: 0xbead5c6a	=	addons/mat_faker/lua/missing_no_more/client/progress.lua:52
	function: 0xbeaf0e7a	=	lua/mediaplayer/sh_mediaplayer.lua:231
	function: 0xbf9a58a2	=	lua/autorun/client/toolsearch.lua:129
	function: 0xbf9afa2a	=	lua/autorun/client/toolsearch.lua:129
	function: 0xbf9b982a	=	lua/vgui/dcategorylist.lua:30
	function: 0xbf9b9972	=	lua/autorun/client/toolsearch.lua:129
	function: 0xbf9ed9aa	=	lua/derma/init.lua:56
	function: 0xbfa83b72	=	lua/derma/init.lua:56
	function: 0xbfa918f2	=	lua/tfa/modules/cl_tfa_models.lua:3
	function: 0xbfa9bc7a	=	lua/derma/init.lua:56
	function: 0xbfaa6d62	=	lua/vgui/dimage.lua:149
	function: 0xbfacadfa	=	addons/ulib/lua/ulib/shared/hook.lua:97
	function: 0xbfbabe32	=	lua/autorun/client/toolsearch.lua:129
	function: 0xbfbbaf2a	=	lua/autorun/client/toolsearch.lua:129
	function: 0xbfceba72	=	lua/autorun/client/toolsearch.lua:129
	function: 0xbfcebd2a	=	lua/derma/init.lua:56
	function: 0xc4898a22	=	lua/autorun/client/toolsearch.lua:129
	function: 0xc48d09aa	=	lua/vgui/dcheckbox.lua:162
	function: 0xc48d2dfa	=	addons/cfc_wire/lua/entities/gmod_wire_egp/lib/egplib/queuesystem.lua:130
	function: 0xc48ed962	=	lua/vgui/dcategorycollapse.lua:166
	function: 0xc48fbd22	=	lua/autorun/client/toolsearch.lua:129
	function: 0xc49cffaa	=	lua/vgui/dtree_node.lua:553
    ...

Releases

No releases published

Sponsor this project

 

Packages

No packages published

Languages