Facepunch / garrysmod-issues

Garry's Mod issue tracker
145 stars 56 forks source link

PANEL:OnChildAdded called by engine before Lua metatable set #2759

Open mandrewpowers opened 8 years ago

mandrewpowers commented 8 years ago

GMod Version: 2016.07.06

Details

I wanted to override how the DButtons are setup in the DMenuBarand thought I could add a PANEL:OnChildAdded(panel) to the menubar and modify it there. I was getting odd errors about methods not existing when I knew for a fact they should have and realized the engine was calling PANEL:OnChildAdded(panel) before Lua applied the metatable and called PANEL:Prepare() which is done in lua\includes\extensions\client\panel\scriptedpanels.lua. I discovered that the panel reference I had was the one I indeed wanted by calling timer.Simple(...) and printing it a second time which revealed it was the DButton I thought it was from the start.

I assume that PANEL:OnChildAdded(panel) is being called from the engine to make sure PANEL:SetParent(parent) calls work since that seems to not be overwritten in Lua but the engine sets the parent early when creating the C base class Label since vgui.Create(...) recursively creates each base until it hits the engine VGUI class.

Steps to reproduce

tl;dr (List of steps at bottom), Execute this script and let the game run the timers. You'll see the label change to red:

-- OnChildAdded issue

-- Frame to hold hook
local frame = vgui.Create('DFrame')

-- Cleanup
timer.Simple(10, function()
    if IsValid(frame) then frame:Remove() end
end)

-- Frame setup
frame:SetSize(400, 300)
frame:Center()

function frame:OnChildAdded(panel)
    -- Timer for proof later
    timer.Simple(5, function()
        if IsValid(panel) then
            print('timer.Simple - DLabel added to DFrame: ', panel)
            panel:SetTextColor(Color(255, 0, 0, 255))
        end
    end)

    -- Print VGUI Label
    print('OnChildAdded - DLabel added to DFrame: ', panel)

    -- Call DLabel method
    -- Will explode since the metatable isn't setup yet
    panel:SetTextColor(Color(255, 0, 0, 255))
end

-- Victim DLabel
local label = vgui.Create('DLabel', frame)
label:Dock(FILL)
label:SetContentAlignment(5)

-- Lets go
frame:MakePopup()

Actual steps if needed:

  1. Grab script from above
  2. Execute in console Hook error should have already triggered
  3. Close console and let the timers run After 5 seconds the same method is called on the upvalue reference from the hook and the DLabel changes to red (you will also see the references printed in the console) After 10 seconds the frame is removed
Be1zebub commented 4 months ago

if anyone else comes across this issue too, here's how i workaround it:

local Prepare = FindMetaTable("Panel").Prepare
function Child:Prepare()
    Prepare(self)
    self:GetParent():OnChildReady(self)
end