From 8a39d4eb1e6e14ecfdf8bda26f6836d5d56dbdb1 Mon Sep 17 00:00:00 2001 From: Brandon Sturgeon Date: Sun, 30 Jan 2022 22:17:38 -0800 Subject: [PATCH 1/5] WIP buildcheck command --- lua/ulx/modules/sh/cfc_buildcheck.lua | 213 ++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 lua/ulx/modules/sh/cfc_buildcheck.lua diff --git a/lua/ulx/modules/sh/cfc_buildcheck.lua b/lua/ulx/modules/sh/cfc_buildcheck.lua new file mode 100644 index 00000000..6b1930d6 --- /dev/null +++ b/lua/ulx/modules/sh/cfc_buildcheck.lua @@ -0,0 +1,213 @@ +CFCUlxCommands.buildCheck = CFCUlxCommands.buildCheck or {} +local cmd = CFCUlxCommands.buildCheck +local CATEGORY_NAME = "Utility" +local IsValid = IsValid +local rawget = rawget +local rawset = rawset +local IsValid = IsValid + +if SERVER then + util.AddNetworkString( "CFC_ULX_BuildCheckResults" ) +end + +if CLIENT then + local MsgC = MsgC + -- TODO: Choose the colors + local INTRO_COLOR = Color( 175, 175, 235 ) + local HEADER_COLOR = Color( 175, 235, 175 ) + local CLASS_COLOR = Color( 235, 235, 175 ) + + -- These are vectors so we can lerp them + local MOST_COUNT_COLOR = Vector( 255, 0, 0 ) -- If a single count makes up 100% of the total count + local LEAST_COUNT_COLOR = Vector( 0, 255, 0 ) -- If a single count make sup 0% of the total count + + -- Returns a dynamic color between MOST_COUNT and + -- LEAST_COUNT colors based on how much of the total is + -- made up by the given count + local function getCountColor( total, count ) + return LerpVector( count / total, LEAST_COUNT_COLOR, MOST_COUNT_COLOR ):ToColor() + end + + -- MsgC with prefix and auto-newlining + local consolePrint = function( prefix, ... ) + MsgC( prefix, ... , "\n" ) + end + + -- consolePrint with no prefix + local msg = function( ... ) + consolePrint( "", ... ) + end + + -- consolePrint with indentation + local subPrefix = " " + local subMsg = function( ... ) + consolePrint( subPrefix, ... ) + end + + -- Writes a table of = + local function writeCountData( header, data ) + msg( HEADER_COLOR, header, ":") + + local total = data.total + + for identifier, count in pairs( SortedPairsByValue( data.items, true ) ) do + subMsg( CLASS_COLOR, identifier, ": ", COUNT_COLOR, count ) + totalCount = totalCount + count + end + + subMsg( TOTAL_COLOR, "Total: ", COUNT_COLOR, totalCount, "\n" ) + end + + local function writeAlertData( alerts ) + msg( HEADER_COLOR, "Alerts:" ) + + for _, alert in ipairs( alerts ) do + subMsg( ALERT_COLOR, " - ", alert ) + end + end + + local function writePlayerData( plyName, data ) + -- Padded with newlines for surround spacing + msg( "\n\n", INTRO_COLOR, "Building summary for: '" .. plyName .. "'", "\n" ) + + writeCountData( "Props by model", data.props ) + writeCountData( "Constraints by class", data.constraints ) + writeCountData( "Ents by class", data.ents ) + + writeAlertData( data.alerts ) + end + + net.Receive( "CFC_ULX_BuildCheckResults", function() + local json = util.Decompress( net.ReadData() ) + local data = util.JSONToTable( json ) + + for plyName, data in pairs( data.players ) do + writePlayerData( plyName, data ) + end + + -- Write the unknown data last + writePlayerData( "Unowned", data.unknown ) + end ) +end + +-- Insert alerts into the alerts table +local function addEntAlerts( ent, alerts ) +end + +-- Insert alerts into the plyData.alerts table +local function plyAlerts( plyData ) +end + +-- Returns , +-- i.e. a prop returns "props", +-- a constraint returns "constraints", +-- TODO: Rename this to indicate it returns the identifier too +local function entCategory( ent ) + local class = ent:GetClass() + + if class == "prop_physics" then + return "props", ent:GetModel() + end + + if ent:IsConstraint() then + return "constraints", class + end + + return "ents", class +end + +local function tallyEnt( ent, trackedPlayers, playerData, unknownData ) + local ent = rawget( allEnts, i ) + local owner = ent.CPPIGetOwner and ent:CPPIGetOwner() + local validOwner = IsValid( owner ) + + if not validOwner and trackedPlayers[owner] then return end + + local plyData = validOwner and rawget( playerData, owner ) or unknownData + local category, identifier = entCategory( ent ) + + local categoryData = rawget( plyData, category ) + local categoryTotal = rawget( categoryData, "total" ) + local itemData = rawget( categoryData, "items" ) + + local count = rawget( itemData, identifier ) or 0 + rawset( itemData, identifier, count + 1 ) + rawset( categoryData, "total", categoryTotal + 1 ) + + local alerts = rawget( plyData, "alerts" ) + addEntAlerts( ent, alerts ) +end + +local function countTable() + return { + total = 0, + items = {} + } +end + +local function categoryTable() + return { + constraints = countTable(), + props = countTable(), + ents = countTable(), + alerts = {} + } +end + +function cmd.buildCheck( caller, targets ) + if not caller:IsAdmin() then + local now = CurTime() + local lastcall = caller.lastBuildCheck or 0 + if lastCall > ( now - 5 ) then + return -- TODO: Hey nerd that's too soon, chill will ya' + end + + caller.lastBuildCheck = now + end + + local targetsCount = #targets + local buildData = { + unknown = categoryTable(), + players = {} + } + + for i = 1, targets do + local ply = rawget( targets, i ) + buildData.players[ply] = categoryTable() + end + + local allEnts = ents.GetAll() + local entsCount = #allEnts + + local unknownData = buildData.unknown + local playerData = buildData.players + + for i = 1, entsCount do + local ent = rawget( allEnts, i ) + tallyEnt( ent, targets, playerData, unknownData ) + end + + for ply, data in pairs( playerData ) do + -- Check for player alerts + addPlyAlerts( data ) + + -- Convert keys to the player's name and steamid + local plyString = ply:Nick() .. "<" .. ply:SteamID() .. ">" + playerData[plyString] = data + playerData[ply] = nil + end + + local json = util.TableToJSON( buildData ) + local compress = util.Compress( json ) + + net.Start( "CFC_ULX_BuildCheckResults" ) + net.WriteData( compress ) + net.Send( caller ) + + -- TODO: Fancylogadmin some highlights from the data here +end + +local command = ulx.command( CATEGORY_NAME, "ulx buildcheck", cmd.buildCheck, "!buildcheck" ) +command:addParam{ type = ULib.cmds.PlayersArg } +command:defaultAccess( ULib.ACCESS_ADMIN ) +command:help( "Returns building information about the target player(s)" ) From a4d2c4d137129008da7367f13887e32aee6e7c03 Mon Sep 17 00:00:00 2001 From: Brandon Sturgeon Date: Sun, 30 Jan 2022 22:21:13 -0800 Subject: [PATCH 2/5] Make alerts a simple counttable --- lua/ulx/modules/sh/cfc_buildcheck.lua | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/lua/ulx/modules/sh/cfc_buildcheck.lua b/lua/ulx/modules/sh/cfc_buildcheck.lua index 6b1930d6..cb376ac8 100644 --- a/lua/ulx/modules/sh/cfc_buildcheck.lua +++ b/lua/ulx/modules/sh/cfc_buildcheck.lua @@ -58,14 +58,6 @@ if CLIENT then subMsg( TOTAL_COLOR, "Total: ", COUNT_COLOR, totalCount, "\n" ) end - local function writeAlertData( alerts ) - msg( HEADER_COLOR, "Alerts:" ) - - for _, alert in ipairs( alerts ) do - subMsg( ALERT_COLOR, " - ", alert ) - end - end - local function writePlayerData( plyName, data ) -- Padded with newlines for surround spacing msg( "\n\n", INTRO_COLOR, "Building summary for: '" .. plyName .. "'", "\n" ) @@ -73,8 +65,7 @@ if CLIENT then writeCountData( "Props by model", data.props ) writeCountData( "Constraints by class", data.constraints ) writeCountData( "Ents by class", data.ents ) - - writeAlertData( data.alerts ) + writeCountData( "Alerts", data.alerts ) end net.Receive( "CFC_ULX_BuildCheckResults", function() @@ -90,11 +81,11 @@ if CLIENT then end ) end --- Insert alerts into the alerts table +-- TODO: alerts is a countTable ( { total = 0, items = { = } ) local function addEntAlerts( ent, alerts ) end --- Insert alerts into the plyData.alerts table +-- TODO: plyData.alerts is a countTable ( { total = 0, items = { = } ) local function plyAlerts( plyData ) end @@ -150,7 +141,7 @@ local function categoryTable() constraints = countTable(), props = countTable(), ents = countTable(), - alerts = {} + alerts = countTable() } end From 798c24fabf40d2b30c3813dc15babb4349759730 Mon Sep 17 00:00:00 2001 From: Brandon Sturgeon Date: Sun, 30 Jan 2022 22:23:45 -0800 Subject: [PATCH 3/5] Use dynamic colors --- lua/ulx/modules/sh/cfc_buildcheck.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/ulx/modules/sh/cfc_buildcheck.lua b/lua/ulx/modules/sh/cfc_buildcheck.lua index cb376ac8..6d78f35e 100644 --- a/lua/ulx/modules/sh/cfc_buildcheck.lua +++ b/lua/ulx/modules/sh/cfc_buildcheck.lua @@ -51,11 +51,11 @@ if CLIENT then local total = data.total for identifier, count in pairs( SortedPairsByValue( data.items, true ) ) do + local color = getCountColor( total, count ) subMsg( CLASS_COLOR, identifier, ": ", COUNT_COLOR, count ) - totalCount = totalCount + count end - subMsg( TOTAL_COLOR, "Total: ", COUNT_COLOR, totalCount, "\n" ) + subMsg( TOTAL_COLOR, "Total: ", COUNT_COLOR, total, "\n" ) end local function writePlayerData( plyName, data ) From 4e37eb765d0d195057b32cfb1dc2a41ec9131ee0 Mon Sep 17 00:00:00 2001 From: Brandon Sturgeon Date: Sun, 30 Jan 2022 22:27:53 -0800 Subject: [PATCH 4/5] Add a note about getting owners --- lua/ulx/modules/sh/cfc_buildcheck.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/ulx/modules/sh/cfc_buildcheck.lua b/lua/ulx/modules/sh/cfc_buildcheck.lua index 6d78f35e..ea68a2fd 100644 --- a/lua/ulx/modules/sh/cfc_buildcheck.lua +++ b/lua/ulx/modules/sh/cfc_buildcheck.lua @@ -109,6 +109,7 @@ end local function tallyEnt( ent, trackedPlayers, playerData, unknownData ) local ent = rawget( allEnts, i ) + -- TODO: Make a more robust method to get owners (i.e. for wire holograms, npcs, grenades, etc.) local owner = ent.CPPIGetOwner and ent:CPPIGetOwner() local validOwner = IsValid( owner ) From 702fdfc6d8af762a44a3cec4f5d8b951ab191a19 Mon Sep 17 00:00:00 2001 From: Brandon Sturgeon Date: Mon, 31 Jan 2022 01:49:45 -0800 Subject: [PATCH 5/5] Fix command, improve printing --- lua/ulx/modules/sh/cfc_buildcheck.lua | 80 ++++++++++++++++++--------- 1 file changed, 54 insertions(+), 26 deletions(-) diff --git a/lua/ulx/modules/sh/cfc_buildcheck.lua b/lua/ulx/modules/sh/cfc_buildcheck.lua index ea68a2fd..2a9da761 100644 --- a/lua/ulx/modules/sh/cfc_buildcheck.lua +++ b/lua/ulx/modules/sh/cfc_buildcheck.lua @@ -11,26 +11,45 @@ if SERVER then end if CLIENT then - local MsgC = MsgC -- TODO: Choose the colors - local INTRO_COLOR = Color( 175, 175, 235 ) - local HEADER_COLOR = Color( 175, 235, 175 ) - local CLASS_COLOR = Color( 235, 235, 175 ) + local INTRO_COLOR = Color( 145, 145, 245 ) + local HEADER_COLOR = Color( 145, 245, 145 ) + local CLASS_COLOR = Color( 245, 245, 145 ) + local DEFAULT_COLOR = Color( 245, 245, 245 ) -- These are vectors so we can lerp them - local MOST_COUNT_COLOR = Vector( 255, 0, 0 ) -- If a single count makes up 100% of the total count - local LEAST_COUNT_COLOR = Vector( 0, 255, 0 ) -- If a single count make sup 0% of the total count + local MOST_COUNT_COLOR = Vector( 1, 0, 0 ) -- If a single count makes up 100% of the total count + local MID_COUNT_COLOR = Vector( 1, 1, 0 ) -- If a single count makes up 50% of the total count + local LEAST_COUNT_COLOR = Vector( 0, 1, 0 ) -- If a single count make sup 0% of the total count + + -- TODO: Create some way of scaling the total counts + local function getTotalColor( total, category ) + end -- Returns a dynamic color between MOST_COUNT and -- LEAST_COUNT colors based on how much of the total is -- made up by the given count local function getCountColor( total, count ) - return LerpVector( count / total, LEAST_COUNT_COLOR, MOST_COUNT_COLOR ):ToColor() + local fraction = count / total + + local min, max + if fraction <= 0.5 then + fraction = math.Remap( fraction, 0, 0.5, 0, 1 ) + min = LEAST_COUNT_COLOR + max = MID_COUNT_COLOR + else + min = MID_COUNT_COLOR + max = MOST_COUNT_COLOR + end + + return LerpVector( fraction, min, max ):ToColor() end -- MsgC with prefix and auto-newlining - local consolePrint = function( prefix, ... ) - MsgC( prefix, ... , "\n" ) + consolePrint = function( prefix, ... ) + MsgC( prefix ) + MsgC( ... ) + MsgC( "\n" ) end -- consolePrint with no prefix @@ -46,16 +65,16 @@ if CLIENT then -- Writes a table of = local function writeCountData( header, data ) - msg( HEADER_COLOR, header, ":") + msg( HEADER_COLOR, header, ":" ) local total = data.total - for identifier, count in pairs( SortedPairsByValue( data.items, true ) ) do - local color = getCountColor( total, count ) - subMsg( CLASS_COLOR, identifier, ": ", COUNT_COLOR, count ) + for identifier, count in SortedPairsByValue( data.items, true ) do + local col = getCountColor( total, count ) + subMsg( CLASS_COLOR, identifier, ": ", col, count ) end - subMsg( TOTAL_COLOR, "Total: ", COUNT_COLOR, total, "\n" ) + subMsg( CLASS_COLOR, "Total: ", INTRO_COLOR, total, "\n" ) end local function writePlayerData( plyName, data ) @@ -69,9 +88,12 @@ if CLIENT then end net.Receive( "CFC_ULX_BuildCheckResults", function() - local json = util.Decompress( net.ReadData() ) + local dataLen = net.ReadUInt( 32 ) + local json = util.Decompress( net.ReadData( dataLen ) ) local data = util.JSONToTable( json ) + PrintTable( data ) + for plyName, data in pairs( data.players ) do writePlayerData( plyName, data ) end @@ -86,7 +108,7 @@ local function addEntAlerts( ent, alerts ) end -- TODO: plyData.alerts is a countTable ( { total = 0, items = { = } ) -local function plyAlerts( plyData ) +local function addPlyAlerts( plyData ) end -- Returns , @@ -108,7 +130,6 @@ local function entCategory( ent ) end local function tallyEnt( ent, trackedPlayers, playerData, unknownData ) - local ent = rawget( allEnts, i ) -- TODO: Make a more robust method to get owners (i.e. for wire holograms, npcs, grenades, etc.) local owner = ent.CPPIGetOwner and ent:CPPIGetOwner() local validOwner = IsValid( owner ) @@ -163,7 +184,7 @@ function cmd.buildCheck( caller, targets ) players = {} } - for i = 1, targets do + for i = 1, targetsCount do local ply = rawget( targets, i ) buildData.players[ply] = categoryTable() end @@ -173,6 +194,7 @@ function cmd.buildCheck( caller, targets ) local unknownData = buildData.unknown local playerData = buildData.players + PrintTable( playerData ) for i = 1, entsCount do local ent = rawget( allEnts, i ) @@ -180,20 +202,26 @@ function cmd.buildCheck( caller, targets ) end for ply, data in pairs( playerData ) do - -- Check for player alerts - addPlyAlerts( data ) - - -- Convert keys to the player's name and steamid - local plyString = ply:Nick() .. "<" .. ply:SteamID() .. ">" - playerData[plyString] = data - playerData[ply] = nil + if not isstring( ply ) then + -- Check for player alerts + addPlyAlerts( data ) + + -- Convert keys to the player's name and steamid + local plyString = ply:Nick() .. "<" .. ply:SteamID() .. ">" + playerData[plyString] = data + playerData[ply] = nil + end end + PrintTable( buildData ) + local json = util.TableToJSON( buildData ) local compress = util.Compress( json ) + -- TODO: Figure out a more reasonable number for this UInt net.Start( "CFC_ULX_BuildCheckResults" ) - net.WriteData( compress ) + net.WriteUInt( #compress, 32 ) + net.WriteData( compress, #compress ) net.Send( caller ) -- TODO: Fancylogadmin some highlights from the data here