Last active
May 4, 2021 00:18
-
-
Save TankNut/3b21ca9437f726a6ae9a37365dbeab64 to your computer and use it in GitHub Desktop.
A script for Garry's Mod that monitors and logs outgoing net library messages
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
Commands: | |
net_getinfo: Prints out stats about the X biggest and most common net messages, where X is the value of net_maxlines | |
net_getinfo <net message>: Prints out stats about a specific net message | |
net_reset: Resets the interval and counts used in net_getinfo | |
net_addfilter <net message>: Prevents a net message from being logged or shown in net_getinfo | |
net_removefilter <net message>: Removes a net message from the filter list | |
net_checkfilter: Prints out all net messages that are on the filter list | |
net_clearfilter: Clears out all net messages from the filter | |
Convars: | |
net_logoutgoing: Outputs stats about every outgoing net message to the server console (Except where filtered) | |
net_maxlines: How many lines net_getinfo should show | |
]] | |
local doLog = CreateConVar("net_logoutgoing", 0, FCVAR_NONE) | |
local maxLines = CreateConVar("net_maxlines", 5, FCVAR_ARCHIVE) | |
net.StartTime = net.StartTime or CurTime() | |
net.NetworkData = net.NetworkData or {} | |
net.NetworkFilter = net.NetworkFilter or {} | |
net.MessageCount = net.MessageCount or 0 | |
net.BytesCount = net.BytesCount or 0 | |
local function AccessCheck(ply) | |
if game.SinglePlayer() or not IsValid(ply) then | |
return true | |
end | |
return false | |
end | |
local function printf(str, ...) | |
print(string.format(str, ...)) | |
end | |
local function perc(val, max) | |
return math.Round((val / max) * 100, 2) | |
end | |
function net.LogOutgoing(filter) | |
local recipients | |
local typeID = TypeID(filter or nil) | |
local playerCount = 0 | |
local broadcast = false | |
if typeID == TYPE_ENTITY then | |
recipients = string.format("client: %s (%s)", filter:Nick(), filter:SteamID()) | |
playerCount = 1 | |
elseif typeID == TYPE_TABLE then | |
recipients = string.format("%s clients", #filter) | |
playerCount = #filter | |
elseif typeID == TYPE_RECIPIENTFILTER then | |
recipients = string.format("%s clients", filter:GetCount()) | |
playerCount = filter:GetCount() | |
else | |
recipients = "everyone" | |
playerCount = player.GetCount() | |
broadcast = true | |
end | |
local name = net.CurrentMessageName | |
local bytes = (net.BytesWritten() * playerCount) | |
net.MessageCount = net.MessageCount + 1 | |
net.BytesCount = net.BytesCount + bytes | |
net.NetworkData[name] = net.NetworkData[name] or { | |
Count = 0, | |
Bytes = 0, | |
PlayerCount = 0, | |
Broadcast = 0 | |
} | |
net.NetworkData[name].Count = net.NetworkData[name].Count + 1 | |
net.NetworkData[name].Bytes = net.NetworkData[name].Bytes + bytes | |
net.NetworkData[name].PlayerCount = net.NetworkData[name].PlayerCount + playerCount | |
if broadcast then | |
net.NetworkData[name].Broadcast = net.NetworkData[name].Broadcast + 1 | |
end | |
if doLog:GetBool() and not net.NetworkFilter[name] then | |
printf("Outgoing %s message %s (%s) to %s", net.IsUnreliable and "unreliable" or "reliable", name, string.NiceSize(bytes), recipients) | |
end | |
end | |
concommand.Add("net_addfilter", function(ply, cmd, args) | |
if not AccessCheck(ply) or not args[1] then | |
return | |
end | |
net.NetworkFilter[args[1]] = true | |
end) | |
concommand.Add("net_removefilter", function(ply, cmd, args) | |
if not AccessCheck(ply) or not args[1] then | |
return | |
end | |
net.NetworkFilter[args[1]] = nil | |
end) | |
concommand.Add("net_checkfilter", function(ply) | |
if not AccessCheck(ply) then | |
return | |
end | |
if table.Count(net.NetworkFilter) == 0 then | |
print("-------") | |
print("No network messages are being filtered") | |
print("-------") | |
return | |
end | |
print("-------") | |
print("Filtered network messages:") | |
print("-------") | |
for k in SortedPairs(net.NetworkFilter) do | |
print(k) | |
end | |
print("-------") | |
end) | |
concommand.Add("net_clearfilter", function(ply) | |
if not AccessCheck(ply) then | |
return | |
end | |
net.NetworkFilter = {} | |
end) | |
concommand.Add("net_getinfo", function(ply, cmd, args) | |
if not AccessCheck(ply) then | |
return | |
end | |
print("-------") | |
local time = CurTime() - net.StartTime | |
local messageName = args[1] | |
if messageName then | |
if net.NetworkData[messageName] then | |
print("Network data for message: " .. messageName) | |
print("Interval: " .. string.NiceTime(time)) | |
print("-------") | |
local data = net.NetworkData[messageName] | |
printf("Times sent: %s (%s messages/sec, %s%%)", data.Count, math.Round(data.Count / time, 2), perc(data.Count, net.MessageCount)) | |
printf("Sent to %s players on average per message", math.Round(data.PlayerCount / data.Count)) | |
printf("Broadcast to everyone %s%% of the time (%s/%s messages)", perc(data.Broadcast, data.Count), data.Broadcast, data.Count) | |
printf("Data sent: %s (%s/sec, %s%%)", string.NiceSize(data.Bytes), string.NiceSize(math.Round(data.Bytes / time)), perc(data.Bytes, net.BytesCount)) | |
else | |
print("No network data found for this message") | |
end | |
else | |
print("Biggest contributors to net library usage:") | |
printf("Interval: %s", string.NiceTime(time)) | |
printf("Messages sent: %s", net.MessageCount) | |
printf("Data sent: %s", string.NiceSize(net.BytesCount)) | |
print("-------") | |
local keys = {} | |
for k in pairs(net.NetworkData) do | |
if net.NetworkFilter[k] then | |
continue | |
end | |
keys[#keys + 1] = k | |
end | |
local function output() | |
local max = { | |
name = 0, | |
count = 0, | |
data = 0 | |
} | |
local lines = {} | |
for i = 1, math.min(#keys, maxLines:GetInt()) do | |
local name = keys[i] | |
local data = net.NetworkData[name] | |
lines[name] = { | |
string.format("Count: %s (%s messages/sec, %s%%)", data.Count, math.Round(data.Count / time, 2), perc(data.Count, net.MessageCount)), | |
string.format("Data: %s (%s/sec, %s%%)", string.NiceSize(data.Bytes), string.NiceSize(math.Round(data.Bytes / time)), perc(data.Bytes, net.BytesCount)) | |
} | |
max.name = math.max(max.name, #name + 2) | |
max.count = math.max(max.count, #lines[name][1] + 1) | |
max.data = math.max(max.data, #lines[name][2]) | |
end | |
for i = 1, math.min(#keys, maxLines:GetInt()) do | |
local name = keys[i] | |
local line = lines[name] | |
printf("%-" .. max.name .. "s %-" .. max.count .. "s %-" .. max.data .. "s", name .. ": ", line[1], line[2]) | |
end | |
end | |
print("Sorted by message size") | |
print("-------") | |
table.sort(keys, function(a, b) | |
return net.NetworkData[a].Bytes > net.NetworkData[b].Bytes | |
end) | |
output() | |
print("-------") | |
print("Sorted by message count") | |
print("-------") | |
table.sort(keys, function(a, b) | |
return net.NetworkData[a].Count > net.NetworkData[b].Count | |
end) | |
output() | |
end | |
print("-------") | |
end) | |
concommand.Add("net_reset", function(ply) | |
if not AccessCheck(ply) then | |
return | |
end | |
net.StartTime = CurTime() | |
net.NetworkData = {} | |
net.MessageCount = 0 | |
net.BytesCount = 0 | |
end) | |
net.OldStart = net.OldStart or net.Start | |
net.OldSend = net.OldSend or net.Send | |
net.OldSendOmit = net.OldSendOmit or net.SendOmit | |
net.OldSendPAS = net.OldSendPAS or net.SendPAS | |
net.OldSendPVS = net.OldSendPVS or net.SendPVS | |
net.OldBroadcast = net.OldBroadcast or net.Broadcast | |
function net.Start(messageName, unreliable) | |
net.CurrentMessageName = messageName | |
net.IsUnreliable = unreliable or false | |
net.OldStart(messageName, unreliable) | |
end | |
function net.Send(ply) | |
net.LogOutgoing(ply) | |
net.OldSend(ply) | |
end | |
function net.SendOmit(ply) | |
local filter = RecipientFilter() | |
filter:AddAllPlayers() | |
if istable(ply) then | |
for _, v in pairs(ply) do | |
if v:IsPlayer() then | |
filter:RemovePlayer(v) | |
end | |
end | |
else | |
filter:RemovePlayer(ply) | |
end | |
net.LogOutgoing(filter) | |
net.OldSendOmit(ply) | |
end | |
function net.SendPAS(vec) | |
local filter = RecipientFilter() | |
filter:AddPAS(vec) | |
net.LogOutgoing(filter) | |
net.OldSendPAS(vec) | |
end | |
function net.SendPVS(vec) | |
local filter = RecipientFilter() | |
filter:AddPVS(vec) | |
net.LogOutgoing(filter) | |
net.OldSendPVS(vec) | |
end | |
function net.Broadcast() | |
net.LogOutgoing() | |
net.OldBroadcast() | |
end |
My only guess is that it's to do with how you are using the net library in general as that shouldn't be able to happen unless someone calls one of the net.Send/Broadcast functions without having called net.Start first
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi thanks for writing this up. This is awesome, helps a lot with debugging.
I'm seeing an error for some messages:
lua:67: attempt to perform arithmetic on a nil value
Any pointers? Thanks.