Ralgathor / LibClassicSwingTimerAPI

GNU General Public License v3.0
4 stars 5 forks source link

Lua Errors with Example Implementation #36

Closed Snoogens101 closed 1 year ago

Snoogens101 commented 1 year ago

Am I doing something wrong? Playing on Official Classic, WotLK Pre-Patch

    local SwingTimerLib = LibStub("LibClassicSwingTimerAPI", true)
    if not SwingTimerLib then return end

    local f = CreateFrame("Frame", nil)

    local SwingTimerInfo = function(hand)
        return SwingTimerLib:SwingTimerInfo(hand)
    end

    local SwingTimerEventHandler = function(event, ...)
        return f[event](f, event, ...)
    end

    SwingTimerLib.RegisterCallback(f, "SWING_TIMER_START", SwingTimerEventHandler)
    SwingTimerLib.RegisterCallback(f, "SWING_TIMER_UPDATE", SwingTimerEventHandler)
    SwingTimerLib.RegisterCallback(f, "SWING_TIMER_CLIPPED", SwingTimerEventHandler)
    SwingTimerLib.RegisterCallback(f, "SWING_TIMER_PAUSED", SwingTimerEventHandler)
    SwingTimerLib.RegisterCallback(f, "SWING_TIMER_STOP", SwingTimerEventHandler)
    SwingTimerLib.RegisterCallback(f, "SWING_TIMER_DELTA", SwingTimerEventHandler)

If I fix the Lua errors below with guards, I only get nil, nil, nil as a return to SwingTimerInfo("mainhand"). However if I mount, funnily enough, I get a proper value for all three returns - but it also results in a Lua error for the line return f[event](f, event, ...) (attempt to call field '?' (a nil value)).

Lua Errors before manual guards added:

attempt to perform arithmetic on field 'offSpeed' (a nil value)

function lib:PLAYER_ENTER_COMBAT()
    local now = GetTime()
    self.isAttacking = true
    if now > (self.offExpirationTime - (self.offSpeed / 2)) then -- THIS LINE (self.offSpeed)
        if self.offTimer then
            self.offTimer:Cancel()
        end
        self:SwingStart("offhand", now, true)
    end
end

attempt to compare number with nil

function lib:UNIT_ATTACK_SPEED()
    if isClassic and self.class == "PALADIN" then return end -- Ignore UNIT_ATTACK_SPEED on Classic for Paladin. Seal of the Crusader snapshot. No other dynamic speed change.
    local now = GetTime()
    if
        self.skipNextAttackSpeedUpdate
        and tonumber(self.skipNextAttackSpeedUpdate)
        and (now - self.skipNextAttackSpeedUpdate) < 0.04
        and tonumber(self.skipNextAttackSpeedUpdateCount)
    then
        self.skipNextAttackSpeedUpdateCount = self.skipNextAttackSpeedUpdateCount - 1
        return
    end
    if self.mainTimer then
        self.mainTimer:Cancel()
    end
    if self.offTimer then
        self.offTimer:Cancel()
    end
    local mainSpeedNew, offSpeedNew = UnitAttackSpeed("player")
    offSpeedNew = offSpeedNew or 0
    if mainSpeedNew > 0 and self.mainSpeed > 0 and mainSpeedNew ~= self.mainSpeed then -- THIS LINE (self.mainSpeed)
        local multiplier = mainSpeedNew / self.mainSpeed
        local timeLeft = (self.lastMainSwing + self.mainSpeed - now) * multiplier
        self.mainSpeed = mainSpeedNew
        self.mainExpirationTime = now + timeLeft
        self:Fire("SWING_TIMER_UPDATE", self.mainSpeed, self.mainExpirationTime, "mainhand")
        if self.mainSpeed > 0 and self.mainExpirationTime - GetTime() > 0 then
            self.mainTimer = C_Timer.NewTimer(self.mainExpirationTime - GetTime(), function()
                self:SwingEnd("mainhand")
            end)
        end
    end
    if offSpeedNew > 0 and self.offSpeed > 0 and offSpeedNew ~= self.offSpeed then -- THIS LINE (self.offSpeed)
        local multiplier = offSpeedNew / self.offSpeed
        local timeLeft = (self.lastOffSwing + self.offSpeed - now) * multiplier
        self.offSpeed = offSpeedNew
        self.offExpirationTime = now + timeLeft
        if self.calculaDeltaTimer ~= nil then
            self.calculaDeltaTimer:Cancel()
        end
        self:Fire("SWING_TIMER_UPDATE", self.offSpeed, self.offExpirationTime, "offhand")
        if self.offSpeed > 0 and self.offExpirationTime - GetTime() > 0 then
            self.offTimer = C_Timer.NewTimer(self.offExpirationTime - GetTime(), function()
                self:SwingEnd("offhand")
            end)
        end
    end
end
Ralgathor commented 1 year ago

I think @hypernormalisation had a similar issue during it's addon implementation. On the next release we are adding a new event to the lib SWING_TIMER_INFO_INITIALIZED. This event give the possibility for addon implementation to know when the lib is initialized and all method/EVENT can be used.

Snoogens101 commented 1 year ago

@Ralgathor , I see the new event is now added, but I get the exact same issues with the latest version. Is there any special thing that needs to be done for it to work properly? It passively fails, I'm not even calling any function, I just have the lib initialized.

Snoogens101 commented 1 year ago

Well after a lot of messing around I got it to work. First problem was initialising. You only seem to do it on PLAYER_ENTER_WORLD, but so I had to add that it also inits on load. Then the next problem is this function, no idea what it does but whatever it tries to process is always nil. I had to make it just return true to get it work.

local SwingTimerEventHandler = function(event, ...)
    return f[event](f, event, ...)
end
Ralgathor commented 1 year ago

@Snoogens101 What your exact use of the lib? If you can provide some code example maybe It can hehp to better understand your issue.

Snoogens101 commented 1 year ago

It's OK, I've managed to get it to work, I'm not sure what the purpose is of the function above but it doesn't work with it - works well without, as the function will always return nil. Then I recommend you add some nil checks for your self.nTimer:Cancel(), was missing in one part for all of them so had to add that to avoid Lua errors when zoning. Works great tho with some tweaks!

Ralgathor commented 1 year ago

It's OK, I've managed to get it to work, I'm not sure what the purpose is of the function above but it doesn't work with it - works well without, as the function will always return nil. Then I recommend you add some nil checks for your self.nTimer:Cancel(), was missing in one part for all of them so had to add that to avoid Lua errors when zoning. Works great tho with some tweaks!

@Snoogens101

local SwingTimerEventHandler = function(event, ...)
    return f[event](f, event, ...)
end

That an exampe of an event handler you need to adapt it to your addon implementation. f is a frame you need to declare the event handled on the frame to avoid the nil like:

function f:SWING_TIMER_START()
// Whatever your addon need to do with this event
end