From 46afcd25d6700deffdba8bb97a6f80201e8844c1 Mon Sep 17 00:00:00 2001 From: n1lordduck <223183259+n1lordduck@users.noreply.github.com> Date: Tue, 30 Dec 2025 12:35:38 -0300 Subject: [PATCH 1/5] feat(cpulib): add DetachDebugger method --- lua/wire/cpulib.lua | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lua/wire/cpulib.lua b/lua/wire/cpulib.lua index 345ad97..501c081 100644 --- a/lua/wire/cpulib.lua +++ b/lua/wire/cpulib.lua @@ -565,6 +565,34 @@ if SERVER then -- Players and corresponding entities (for the debugger) CPULib.DebuggerData = {} + + ------------------------------------------------------------------------------ + -- Detach debugger + function CPULib.DetachDebugger(player) + if not IsValid(player) then return end + + local data = CPULib.DebuggerData[player:UserID()] + if not data then return end + + local ent = data.Entity + if IsValid(ent) and ent.VM then + ent.BreakpointInstructions = nil + + if ent.VM.BaseJump then + ent.VM.Jump = ent.VM.BaseJump + ent.VM.BaseJump = nil + end + + if ent.VM.BaseInterrupt then + ent.VM.Interrupt = ent.VM.BaseInterrupt + ent.VM.BaseInterrupt = nil + end + end + + CPULib.DebuggerData[player:UserID()] = nil + end + + ------------------------------------------------------------------------------ -- Attach a debugger function CPULib.AttachDebugger(entity,player) From f700fcec0871ef7c65f2c51e049fc99e676c780e Mon Sep 17 00:00:00 2001 From: n1lordduck <223183259+n1lordduck@users.noreply.github.com> Date: Tue, 30 Dec 2025 12:44:42 -0300 Subject: [PATCH 2/5] fix(cpulib): send InvalidDebugger net to deatach debugger properly --- lua/wire/cpulib.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lua/wire/cpulib.lua b/lua/wire/cpulib.lua index 501c081..9188d01 100644 --- a/lua/wire/cpulib.lua +++ b/lua/wire/cpulib.lua @@ -589,6 +589,10 @@ if SERVER then end end + net.Start("CPULib.InvalidateDebugger") + net.WriteUInt(1, 2) -- 1 = detach + net.Send(player) + CPULib.DebuggerData[player:UserID()] = nil end From 735f90ac4ca4d28ab6887beda3b8648cf31f046a Mon Sep 17 00:00:00 2001 From: n1lordduck <223183259+n1lordduck@users.noreply.github.com> Date: Tue, 30 Dec 2025 12:47:37 -0300 Subject: [PATCH 3/5] feat(cpulib): add safe debugger detach on entity removal --- lua/wire/cpulib.lua | 109 +++++++++++++++++++++++++++----------------- 1 file changed, 67 insertions(+), 42 deletions(-) diff --git a/lua/wire/cpulib.lua b/lua/wire/cpulib.lua index 9188d01..7455313 100644 --- a/lua/wire/cpulib.lua +++ b/lua/wire/cpulib.lua @@ -590,7 +590,7 @@ if SERVER then end net.Start("CPULib.InvalidateDebugger") - net.WriteUInt(1, 2) -- 1 = detach + net.WriteUInt(1, 2) --// 1 = detach net.Send(player) CPULib.DebuggerData[player:UserID()] = nil @@ -599,50 +599,74 @@ if SERVER then ------------------------------------------------------------------------------ -- Attach a debugger - function CPULib.AttachDebugger(entity,player) - if entity then - entity.BreakpointInstructions = {} - entity.OnBreakpointInstruction = function(IP) - CPULib.SendDebugData(entity.VM,CPULib.DebuggerData[player:UserID()].MemPointers,player) - end - entity.OnVMStep = function() - if CurTime() - CPULib.DebuggerData[player:UserID()].PreviousUpdateTime > 0.2 then - CPULib.DebuggerData[player:UserID()].PreviousUpdateTime = CurTime() - - -- Send a fake update that messes up line pointer, updates registers - local tempIP = entity.VM.IP - entity.VM.IP = INVALID_BREAKPOINT_IP - CPULib.SendDebugData(entity.VM,nil,player) - entity.VM.IP = tempIP - end - end - if not entity.VM.BaseJump then - entity.VM.BaseJump = entity.VM.Jump - entity.VM.Jump = function(VM,IP,CS) - VM:BaseJump(IP,CS) - entity.ForceLastInstruction = true + function CPULib.AttachDebugger(entity, player) + if not IsValid(player) then return end + + -- DETACH PATH + if not entity then + CPULib.DetachDebugger(player) + return + end + + if not IsValid(entity) or not entity.VM then return end + + -- Detach any existing debugger first + CPULib.DetachDebugger(player) + + -- Hook entity removal so debugger disables itself + if not entity._CPULibDebuggerHooked then + entity._CPULibDebuggerHooked = true + + local oldOnRemove = entity.OnRemove + entity.OnRemove = function(ent) + if oldOnRemove then + oldOnRemove(ent) end - entity.VM.BaseInterrupt = entity.VM.Interrupt - entity.VM.Interrupt = function(VM,interruptNo,interruptParameter,isExternal,cascadeInterrupt) - VM:BaseInterrupt(interruptNo,interruptParameter,isExternal,cascadeInterrupt) - if interruptNo < 27 then - CPULib.DebugLogInterrupt(player,interruptNo,interruptParameter,isExternal,cascadeInterrupt) - CPULib.SendDebugData(entity.VM,CPULib.DebuggerData[player:UserID()].MemPointers,player) - end + + if IsValid(player) then + CPULib.DetachDebugger(player) end end - else - if CPULib.DebuggerData[player:UserID()] then - if CPULib.DebuggerData[player:UserID()].Entity and - CPULib.DebuggerData[player:UserID()].Entity.VM and - CPULib.DebuggerData[player:UserID()].Entity.VM.BaseInterrupt then - - CPULib.DebuggerData[player:UserID()].Entity.BreakpointInstructions = nil - if CPULib.DebuggerData[player:UserID()].Entity.VM.BaseJump then - CPULib.DebuggerData[player:UserID()].Entity.VM.Jump = CPULib.DebuggerData[player:UserID()].Entity.VM.BaseJump - CPULib.DebuggerData[player:UserID()].Entity.VM.Interrupt = CPULib.DebuggerData[player:UserID()].Entity.VM.BaseInterrupt - CPULib.DebuggerData[player:UserID()].Entity.VM.BaseJump = nil - CPULib.DebuggerData[player:UserID()].Entity.VM.BaseInterrupt = nil + end + + entity.BreakpointInstructions = {} + entity.OnBreakpointInstruction = function(IP) + local data = CPULib.DebuggerData[player:UserID()] + if not data then return end + CPULib.SendDebugData(entity.VM, data.MemPointers, player) + end + + entity.OnVMStep = function() + local data = CPULib.DebuggerData[player:UserID()] + if not data then return end + + if CurTime() - data.PreviousUpdateTime > 0.2 then + data.PreviousUpdateTime = CurTime() + + -- Send a fake update that messes up line pointer, updates registers + local tempIP = entity.VM.IP + entity.VM.IP = INVALID_BREAKPOINT_IP + CPULib.SendDebugData(entity.VM, nil, player) + entity.VM.IP = tempIP + end + end + + if not entity.VM.BaseJump then + entity.VM.BaseJump = entity.VM.Jump + entity.VM.Jump = function(VM, IP, CS) + VM:BaseJump(IP, CS) + entity.ForceLastInstruction = true + end + + entity.VM.BaseInterrupt = entity.VM.Interrupt + entity.VM.Interrupt = function(VM, interruptNo, interruptParameter, isExternal, cascadeInterrupt) + VM:BaseInterrupt(interruptNo, interruptParameter, isExternal, cascadeInterrupt) + if interruptNo < 27 then + CPULib.DebugLogInterrupt(player, interruptNo, interruptParameter, isExternal, cascadeInterrupt) + + local data = CPULib.DebuggerData[player:UserID()] + if data then + CPULib.SendDebugData(entity.VM, data.MemPointers, player) end end end @@ -656,6 +680,7 @@ if SERVER then } end + -- Log debug interrupt function CPULib.DebugLogInterrupt(player,interruptNo,interruptParameter,isExternal,cascadeInterrupt) local umsgrp = RecipientFilter() From 21d878f3389203fe1d119b21d276dcb35793fb13 Mon Sep 17 00:00:00 2001 From: n1lordduck <223183259+n1lordduck@users.noreply.github.com> Date: Wed, 31 Dec 2025 10:31:35 -0300 Subject: [PATCH 4/5] fix(cpulib): avoid duplicate detach notifications --- lua/wire/cpulib.lua | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lua/wire/cpulib.lua b/lua/wire/cpulib.lua index 7455313..70c82fe 100644 --- a/lua/wire/cpulib.lua +++ b/lua/wire/cpulib.lua @@ -589,9 +589,7 @@ if SERVER then end end - net.Start("CPULib.InvalidateDebugger") - net.WriteUInt(1, 2) --// 1 = detach - net.Send(player) + CPULib.DebuggerData[player:UserID()] = nil end @@ -610,10 +608,11 @@ if SERVER then if not IsValid(entity) or not entity.VM then return end - -- Detach any existing debugger first + + --// detach any existing debugger first CPULib.DetachDebugger(player) - -- Hook entity removal so debugger disables itself + if not entity._CPULibDebuggerHooked then entity._CPULibDebuggerHooked = true @@ -625,14 +624,20 @@ if SERVER then if IsValid(player) then CPULib.DetachDebugger(player) + + net.Start("CPULib.InvalidateDebugger") + net.WriteUInt(1, 2) --// 1 = detach + net.Send(player) end end end entity.BreakpointInstructions = {} + entity.OnBreakpointInstruction = function(IP) local data = CPULib.DebuggerData[player:UserID()] if not data then return end + CPULib.SendDebugData(entity.VM, data.MemPointers, player) end @@ -661,6 +666,7 @@ if SERVER then entity.VM.BaseInterrupt = entity.VM.Interrupt entity.VM.Interrupt = function(VM, interruptNo, interruptParameter, isExternal, cascadeInterrupt) VM:BaseInterrupt(interruptNo, interruptParameter, isExternal, cascadeInterrupt) + if interruptNo < 27 then CPULib.DebugLogInterrupt(player, interruptNo, interruptParameter, isExternal, cascadeInterrupt) From 87c1409fcafbfbea34fbb501072ddb3242a842a9 Mon Sep 17 00:00:00 2001 From: n1lordduck <223183259+n1lordduck@users.noreply.github.com> Date: Wed, 31 Dec 2025 17:29:50 -0300 Subject: [PATCH 5/5] fix(cpulib): properly cleanup debugger hooks on detach --- lua/wire/cpulib.lua | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/lua/wire/cpulib.lua b/lua/wire/cpulib.lua index 70c82fe..e5e3c85 100644 --- a/lua/wire/cpulib.lua +++ b/lua/wire/cpulib.lua @@ -566,7 +566,7 @@ if SERVER then CPULib.DebuggerData = {} - ------------------------------------------------------------------------------ + ---------------------------------------------------------------------- -- Detach debugger function CPULib.DetachDebugger(player) if not IsValid(player) then return end @@ -577,6 +577,9 @@ if SERVER then local ent = data.Entity if IsValid(ent) and ent.VM then ent.BreakpointInstructions = nil + ent.OnBreakpointInstruction = nil + ent.OnVMStep = nil + if ent.VM.BaseJump then ent.VM.Jump = ent.VM.BaseJump @@ -587,15 +590,24 @@ if SERVER then ent.VM.Interrupt = ent.VM.BaseInterrupt ent.VM.BaseInterrupt = nil end + + if ent._CPULibDebuggerHooked then + ent._CPULibDebuggerHooked = nil + + if ent._CPULibOriginalOnRemove then + ent.OnRemove = ent._CPULibOriginalOnRemove + ent._CPULibOriginalOnRemove = nil + else + ent.OnRemove = nil + end + end end - - CPULib.DebuggerData[player:UserID()] = nil end - ------------------------------------------------------------------------------ + ---------------------------------------------------------------------- -- Attach a debugger function CPULib.AttachDebugger(entity, player) if not IsValid(player) then return end @@ -612,17 +624,20 @@ if SERVER then --// detach any existing debugger first CPULib.DetachDebugger(player) - + if not entity._CPULibDebuggerHooked then entity._CPULibDebuggerHooked = true - local oldOnRemove = entity.OnRemove + entity._CPULibOriginalOnRemove = entity.OnRemove + entity.OnRemove = function(ent) - if oldOnRemove then - oldOnRemove(ent) + if entity._CPULibOriginalOnRemove then + entity._CPULibOriginalOnRemove(ent) end if IsValid(player) then + if not CPULib.DebuggerData[player:UserID()] then return end + CPULib.DetachDebugger(player) net.Start("CPULib.InvalidateDebugger")