From cc9e0f5b720cd09f9a25e6ca1e109f69bd5d671c Mon Sep 17 00:00:00 2001 From: Mikey-Mikey Date: Fri, 12 Jun 2026 20:18:18 -0400 Subject: [PATCH 1/5] Add a global limit for e2s --- lua/entities/gmod_wire_expression2/init.lua | 30 +++++++++++++++++++ lua/entities/gmod_wire_expression2/shared.lua | 1 + 2 files changed, 31 insertions(+) diff --git a/lua/entities/gmod_wire_expression2/init.lua b/lua/entities/gmod_wire_expression2/init.lua index 08fd5fb976..a725d6ff5c 100644 --- a/lua/entities/gmod_wire_expression2/init.lua +++ b/lua/entities/gmod_wire_expression2/init.lua @@ -9,6 +9,7 @@ e2_hardquota = nil e2_tickquota = nil e2_timequota = nil e2_timeaverage = nil +e2_globalmax = nil do local wire_expression2_unlimited = GetConVar("wire_expression2_unlimited") @@ -17,6 +18,7 @@ do local wire_expression2_quotatick = GetConVar("wire_expression2_quotatick") local wire_expression2_quotatime = GetConVar("wire_expression2_quotatime") local wire_expression2_quota_average = GetConVar("wire_expression2_quota_average") + local wire_expression2_quota_globalmax = GetConVar("wire_expression2_quota_globalmax") local function updateQuotas() if wire_expression2_unlimited:GetBool() then @@ -32,6 +34,7 @@ do end e2_timeaverage = 1 / wire_expression2_quota_average:GetFloat() + e2_globalmax = wire_expression2_quota_globalmax:GetFloat() end cvars.AddChangeCallback("wire_expression2_unlimited", updateQuotas) cvars.AddChangeCallback("wire_expression2_quotasoft", updateQuotas) @@ -39,6 +42,7 @@ do cvars.AddChangeCallback("wire_expression2_quotatick", updateQuotas) cvars.AddChangeCallback("wire_expression2_quotatime", updateQuotas) cvars.AddChangeCallback("wire_expression2_quota_average", updateQuotas) + cvars.AddChangeCallback("wire_expression2_quota_globalmax", updateQuotas) updateQuotas() end @@ -852,6 +856,32 @@ hook.Add("PlayerAuthed", "Wire_Expression2_Player_Authed", function(ply, sid, ui end end) +-- Terminates the highest usage e2 if the global limit is hit defined by the cvar wire_expression2_quota_globalmax +hook.Add( "Tick", "Wire_Expression2_Global_Limit", function() + local totalChipTime = 0 + local highestChipTime = 0 + local highestChip = nil + for _, ply in player.Iterator() do + local chips = E2Lib.PlayerChips[ply] + if not chips then continue end + local playerChipTime = chips:getTotalTime() + local playerHighChip, playerHighChipTime = chips:findMaxTimeChip() + + if playerHighChipTime > highestChipTime then + highestChipTime = playerHighChipTime + highestChip = playerHighChip + end + + totalChipTime = totalChipTime + playerChipTime * 1000 + end + + -- Terminate highest usage e2 + if highestChip and e2_globalmax > -1 and totalChipTime > e2_globalmax * 0.001 then + highestChip:Error("Expression 2 (" .. highestChip.name .. "): global time quota exceeded", "global time quota exceeded") + highestChip:Destruct() + end +end ) + function MakeWireExpression2(player, Pos, Ang, model, buffer, name, inputs, outputs, vars, inc_files, filepath, codeAuthor) if not player then player = game.GetWorld() end -- For Garry's Map Saver if IsValid(player) and not player:CheckLimit("wire_expressions") then return false end diff --git a/lua/entities/gmod_wire_expression2/shared.lua b/lua/entities/gmod_wire_expression2/shared.lua index c79499979b..e0a2077dad 100644 --- a/lua/entities/gmod_wire_expression2/shared.lua +++ b/lua/entities/gmod_wire_expression2/shared.lua @@ -13,6 +13,7 @@ function ENT:SetupDataTables() end CreateConVar("wire_expression2_unlimited", "0", {FCVAR_REPLICATED}) +CreateConVar("wire_expression2_quota_globalmax", "-1", {FCVAR_REPLICATED}, "The maximum amount of time all E2s can consume before killing the highest one (-1 is infinite)") CreateConVar("wire_expression2_quotasoft", "10000", {FCVAR_REPLICATED}) CreateConVar("wire_expression2_quotahard", "100000", {FCVAR_REPLICATED}) CreateConVar("wire_expression2_quotatick", "25000", {FCVAR_REPLICATED}) From 40b58b4060c49dfe10b0c2a7af9fcf364f35e182 Mon Sep 17 00:00:00 2001 From: Mikey-Mikey Date: Sun, 14 Jun 2026 12:09:52 -0400 Subject: [PATCH 2/5] Moved the global checking code --- lua/entities/gmod_wire_expression2/init.lua | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lua/entities/gmod_wire_expression2/init.lua b/lua/entities/gmod_wire_expression2/init.lua index a725d6ff5c..6ddeee9d01 100644 --- a/lua/entities/gmod_wire_expression2/init.lua +++ b/lua/entities/gmod_wire_expression2/init.lua @@ -396,6 +396,32 @@ function GlobalChips:remove(remove_chip) end end +--- Checks if total cpu usage is hitting the global limit and then terminates the highest usage e2 +function GlobalChips:checkGlobalTime() + local totalChipTime = 0 + local highestChipTime = 0 + local highestChip = nil + for _, ply in player.Iterator() do + local chips = self[ply] + if not chips then continue end + local playerChipTime = chips:getTotalTime() + local playerHighChip, playerHighChipTime = chips:findMaxTimeChip() + + if playerHighChipTime > highestChipTime then + highestChipTime = playerHighChipTime + highestChip = playerHighChip + end + + totalChipTime = totalChipTime + playerChipTime * 1000 + end + + -- Terminate highest usage e2 + if highestChip and e2_globalmax > -1 and totalChipTime > e2_globalmax * 0.001 then + highestChip:Error("Expression 2 (" .. highestChip.name .. "): global time quota exceeded", "global time quota exceeded") + highestChip:Destruct() + end +end + E2Lib.PlayerChips = E2Lib.PlayerChips or setmetatable({}, GlobalChips) hook.Add("Think", "E2_Think", function() @@ -403,6 +429,7 @@ hook.Add("Think", "E2_Think", function() for ply, chips in pairs(E2Lib.PlayerChips) do chips:checkCpuTime() end + E2Lib.PlayerChips:checkGlobalTime() end end) From 82c10b50bb2cf03b9c235571bc8a5c12ce5eb2ee Mon Sep 17 00:00:00 2001 From: Mikey-Mikey Date: Sun, 14 Jun 2026 12:11:39 -0400 Subject: [PATCH 3/5] Removed the old hook --- lua/entities/gmod_wire_expression2/init.lua | 26 --------------------- 1 file changed, 26 deletions(-) diff --git a/lua/entities/gmod_wire_expression2/init.lua b/lua/entities/gmod_wire_expression2/init.lua index 6ddeee9d01..73cb61bafd 100644 --- a/lua/entities/gmod_wire_expression2/init.lua +++ b/lua/entities/gmod_wire_expression2/init.lua @@ -883,32 +883,6 @@ hook.Add("PlayerAuthed", "Wire_Expression2_Player_Authed", function(ply, sid, ui end end) --- Terminates the highest usage e2 if the global limit is hit defined by the cvar wire_expression2_quota_globalmax -hook.Add( "Tick", "Wire_Expression2_Global_Limit", function() - local totalChipTime = 0 - local highestChipTime = 0 - local highestChip = nil - for _, ply in player.Iterator() do - local chips = E2Lib.PlayerChips[ply] - if not chips then continue end - local playerChipTime = chips:getTotalTime() - local playerHighChip, playerHighChipTime = chips:findMaxTimeChip() - - if playerHighChipTime > highestChipTime then - highestChipTime = playerHighChipTime - highestChip = playerHighChip - end - - totalChipTime = totalChipTime + playerChipTime * 1000 - end - - -- Terminate highest usage e2 - if highestChip and e2_globalmax > -1 and totalChipTime > e2_globalmax * 0.001 then - highestChip:Error("Expression 2 (" .. highestChip.name .. "): global time quota exceeded", "global time quota exceeded") - highestChip:Destruct() - end -end ) - function MakeWireExpression2(player, Pos, Ang, model, buffer, name, inputs, outputs, vars, inc_files, filepath, codeAuthor) if not player then player = game.GetWorld() end -- For Garry's Map Saver if IsValid(player) and not player:CheckLimit("wire_expressions") then return false end From c8c0428f54219c4eb62b53b6101930558ba1e340 Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Mon, 15 Jun 2026 21:15:01 +0300 Subject: [PATCH 4/5] Code cleanups/optimizations --- lua/entities/gmod_wire_expression2/init.lua | 69 ++++++++++++------- lua/entities/gmod_wire_expression2/shared.lua | 2 +- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/lua/entities/gmod_wire_expression2/init.lua b/lua/entities/gmod_wire_expression2/init.lua index 73cb61bafd..56f514a763 100644 --- a/lua/entities/gmod_wire_expression2/init.lua +++ b/lua/entities/gmod_wire_expression2/init.lua @@ -18,7 +18,7 @@ do local wire_expression2_quotatick = GetConVar("wire_expression2_quotatick") local wire_expression2_quotatime = GetConVar("wire_expression2_quotatime") local wire_expression2_quota_average = GetConVar("wire_expression2_quota_average") - local wire_expression2_quota_globalmax = GetConVar("wire_expression2_quota_globalmax") + local wire_expression2_quota_global = GetConVar("wire_expression2_quota_global") local function updateQuotas() if wire_expression2_unlimited:GetBool() then @@ -26,15 +26,16 @@ do e2_hardquota = 1000000 e2_tickquota = 100000 e2_timequota = -1 + e2_globalmax = -1 else e2_softquota = wire_expression2_quotasoft:GetFloat() e2_hardquota = wire_expression2_quotahard:GetFloat() e2_tickquota = wire_expression2_quotatick:GetFloat() e2_timequota = wire_expression2_quotatime:GetFloat() * 0.001 + e2_globalmax = wire_expression2_quota_global:GetFloat() * 0.001 end e2_timeaverage = 1 / wire_expression2_quota_average:GetFloat() - e2_globalmax = wire_expression2_quota_globalmax:GetFloat() end cvars.AddChangeCallback("wire_expression2_unlimited", updateQuotas) cvars.AddChangeCallback("wire_expression2_quotasoft", updateQuotas) @@ -42,7 +43,7 @@ do cvars.AddChangeCallback("wire_expression2_quotatick", updateQuotas) cvars.AddChangeCallback("wire_expression2_quotatime", updateQuotas) cvars.AddChangeCallback("wire_expression2_quota_average", updateQuotas) - cvars.AddChangeCallback("wire_expression2_quota_globalmax", updateQuotas) + cvars.AddChangeCallback("wire_expression2_quota_global", updateQuotas) updateQuotas() end @@ -363,6 +364,8 @@ function PlayerChips:checkCpuTime() break end end + + return total_time end local GlobalChips = {} @@ -396,40 +399,56 @@ function GlobalChips:remove(remove_chip) end end ---- Checks if total cpu usage is hitting the global limit and then terminates the highest usage e2 -function GlobalChips:checkGlobalTime() - local totalChipTime = 0 - local highestChipTime = 0 - local highestChip = nil - for _, ply in player.Iterator() do - local chips = self[ply] - if not chips then continue end - local playerChipTime = chips:getTotalTime() - local playerHighChip, playerHighChipTime = chips:findMaxTimeChip() +function GlobalChips:findMaxTimeChip(chips) + local max_chip, max_time = nil, 0 - if playerHighChipTime > highestChipTime then - highestChipTime = playerHighChipTime - highestChip = playerHighChip - end + for _, chip in ipairs(chips) do + local tab = chip:GetTable() + if tab.error then continue end - totalChipTime = totalChipTime + playerChipTime * 1000 - end + local context = tab.context + if not context then continue end - -- Terminate highest usage e2 - if highestChip and e2_globalmax > -1 and totalChipTime > e2_globalmax * 0.001 then - highestChip:Error("Expression 2 (" .. highestChip.name .. "): global time quota exceeded", "global time quota exceeded") - highestChip:Destruct() + if context.timebench > max_time then + max_time = context.timebench + max_chip = chip + end end + + return max_chip, max_time end E2Lib.PlayerChips = E2Lib.PlayerChips or setmetatable({}, GlobalChips) hook.Add("Think", "E2_Think", function() + local global_time = 0 + if e2_timequota > 0 then for ply, chips in pairs(E2Lib.PlayerChips) do - chips:checkCpuTime() + global_time = global_time + chips:checkCpuTime() + end + else + for ply, chips in pairs(E2Lib.PlayerChips) do + global_time = global_time + chips:getTotalTime() + end + end + + if e2_globalmax > 0 and global_time > e2_globalmax then + -- It will be faster to just iterate over all chips from now on + local chips = ents.FindByClass("gmod_wire_expression2") + + while global_time > e2_globalmax do + local max_chip, max_time = E2Lib.PlayerChips:findMaxTimeChip(chips) + + if max_chip then + global_time = global_time - max_time + max_chip:Error("Expression 2 (" .. max_chip.name .. "): Global time quota exceeded", "global time quota exceeded") + max_chip:Destruct() + else + -- It shouldn't happen, but if something breaks, it will prevent an infinity loop + break + end end - E2Lib.PlayerChips:checkGlobalTime() end end) diff --git a/lua/entities/gmod_wire_expression2/shared.lua b/lua/entities/gmod_wire_expression2/shared.lua index e0a2077dad..bbd844e088 100644 --- a/lua/entities/gmod_wire_expression2/shared.lua +++ b/lua/entities/gmod_wire_expression2/shared.lua @@ -13,11 +13,11 @@ function ENT:SetupDataTables() end CreateConVar("wire_expression2_unlimited", "0", {FCVAR_REPLICATED}) -CreateConVar("wire_expression2_quota_globalmax", "-1", {FCVAR_REPLICATED}, "The maximum amount of time all E2s can consume before killing the highest one (-1 is infinite)") CreateConVar("wire_expression2_quotasoft", "10000", {FCVAR_REPLICATED}) CreateConVar("wire_expression2_quotahard", "100000", {FCVAR_REPLICATED}) CreateConVar("wire_expression2_quotatick", "25000", {FCVAR_REPLICATED}) CreateConVar("wire_expression2_quotatime", "-1", {FCVAR_REPLICATED}, "Time in (ms) that all E2s of one player can consume before killing (-1 is infinite)") +CreateConVar("wire_expression2_quota_global", "-1", {FCVAR_REPLICATED}, "The maximum amount of time all E2s on server can consume before killing the highest one (-1 is infinite)") CreateConVar("wire_expression2_quota_average", "100", {FCVAR_REPLICATED}, "The chip load window width in ticks (smaller is roughly, larger is smooth)") include("core/e2lib.lua") From c01f313ba251266edf14df04e185f340e3945fcd Mon Sep 17 00:00:00 2001 From: Astralcircle <142503363+Astralcircle@users.noreply.github.com> Date: Mon, 15 Jun 2026 21:17:10 +0300 Subject: [PATCH 5/5] Move it tooper --- lua/entities/gmod_wire_expression2/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/entities/gmod_wire_expression2/init.lua b/lua/entities/gmod_wire_expression2/init.lua index 56f514a763..1362b8b14f 100644 --- a/lua/entities/gmod_wire_expression2/init.lua +++ b/lua/entities/gmod_wire_expression2/init.lua @@ -17,8 +17,8 @@ do local wire_expression2_quotahard = GetConVar("wire_expression2_quotahard") local wire_expression2_quotatick = GetConVar("wire_expression2_quotatick") local wire_expression2_quotatime = GetConVar("wire_expression2_quotatime") - local wire_expression2_quota_average = GetConVar("wire_expression2_quota_average") local wire_expression2_quota_global = GetConVar("wire_expression2_quota_global") + local wire_expression2_quota_average = GetConVar("wire_expression2_quota_average") local function updateQuotas() if wire_expression2_unlimited:GetBool() then