• 0

    posted a message on LibNameplateRegistry-1.0
    Well, I have noticed some frames got hung in the middle of the screen, which was pretty strange. It seems to only happen in the middle of a raid encounter with a good number of adds. I cannot reproduce it. In the latest changes, the unit frames themselves register with LNR so they always check if everything is ok, whatever my common unitFrames table contains.
    Posted in: Libraries
  • 0

    posted a message on LibNameplateRegistry-1.0
    I just updated to the latest alpha. I'll do some tests.

    My "testing" addon (AdiBuffPlate) creates frame for enemy to show useful (de)buffs. These frames are identified by the unit GUID. They are attached (e.g. reparented and reanchored) to a nameplate as soon as the nameplate GUID is found. And they should be detached and hidden as soon as the nameplate is recycled.

    However, for some reason, that was not happening properly with only the LNB callbacks. I had to double-check if the nameplate GUID still matches my frame GUID (see there) and an OnHide handler (since my frames are parented to the nameplate, their OnHide handler is called when the nameplate is hidden). Please note this was happening with the old LibNameplate-1.0 too, so maybe I'm doing something wrong.
    Posted in: Libraries
  • 0

    posted a message on Fixing the Interface Options Cancel button taint
    Well, BlizzBugsSuck fixes the InterfaceOptionsFrame_OpenToCategory bug, where this function only shows InterfaceOptionsFrame but doesn't select the panel passed as an argument.

    All my addons add their configuration panel into the InterfaceOptionsFrame. All of them add a slash command to open it directly and most of them add a (optional) map icon that does the same. Without BlizzBugsSuck, this doesn't work as intended. I've got bug reports of people about this.

    My point is: right now, BlizzBugsSuck doesn't add any new feature that people may found annoying, it just has a couple of things working as they should.

    I could list it as a required dependency of my addons so the Curse client would install it along them (I'm sure it works like this actually, as I'm not using the Curse client for ages) but it wouldn't work for people that manually download the addons.
    Posted in: Lua Code Discussion
  • 0

    posted a message on Fixing the Interface Options Cancel button taint
    Quote from Phanx
    Wouldn't BlizzBugsSuck be a better place for it? While I see the merit of using an embeddable library


    Why not both ?

    Make an embeddable LibBlizzBugsSuck so authors could embed it in their addon and release BlizzBugsSuck as a facade addon that just embeds LibBlizzBugSuck.

    Or just modify BlizzBugsSuck to make it an embeddable library that pretends to be an addon.
    Posted in: Lua Code Discussion
  • 0

    posted a message on options tables
    The issue is not the AceDB value but the variables Player and TokenName that you use to index it.

    If you create functions in a loop, like get and set, that use a variable that changes which every iteration, be sure this variable is local to the loop.

    For more background, your get and set functions are closures, of which TokenName and Players are upvalues. There's a bit of literature about closures in programming language on the interwebs.
    Posted in: Lua Code Discussion
  • 0

    posted a message on options tables
    This fails because TokenName is not local to the inner block of the loop. So all your get and set functions will references the *same* TokenName , which will probably have the value of the last item of the loop.

    TokenName is actually a global, which is quite bad. It can cause conflicts with another addon or taint issues (if Blizzard used a global by that name).

    Try something like this :

    function BuildTokensDisplay()
         local Player = UnitName( "player" );
         local Realm = GetRealmName();      
         local title = ( "%s's Tokens" ):format( Player );
         local tCount = GetCurrencyListSize();
         local order = 1;
         Opts.args.cat6.args.AllowedTokens.name = ( "%s" ):format( ColorUtils( "GetCClass", Characters[ Realm ][ Player ].Class, title ) );
         for i = 1, tCount do
              local TokenName, isHeader, _, _, isWatched, count, icon = GetCurrencyListInfo( i );
    
    Posted in: Lua Code Discussion
  • 0

    posted a message on Just a performance question.
    @rowaasr13: the Ace3 standalone is always loaded. Other libraries might be loaded on demand if their author(s) chose to configure the standalone version to do so.
    Posted in: Ace3
  • 0

    posted a message on [Fanupdate] GridIndicatorIconBar
    I do not maintain GridIndicatorIconBar anymore because I've written my own unit frame addon. However, I can release the latest version of GridIndicatorIconBar with an open-source license in case someone wanted to take it over.
    Posted in: Addon Ideas
  • 0

    posted a message on Manually inflate SVN revision numbers?
    Quote from Galvin
    Yeah I dont know all the stuff on subversion. Its really complexed.


    This complexity is an answer to the needs of people working together on big projects for years. It's not just "complexed" for the sake of it. It is actually quite powerful once you master it, even when you are the only developer.
    Posted in: General Chat
  • 0

    posted a message on Manually inflate SVN revision numbers?
    Again : each time you modify your Subversion repository, the tool assigns it a new version number, actually called a revision number, which is linear : 1, 2, 3, ..., 144, and so on. You have no control over that. It is the way it works. (Other VCS use other convention.)

    If you want to "name" a version, you have to create a tag. This is exactly what tags are for: associating an human-readable name to a version of your code. With Subversion, this is done by svn-copying your "trunk/" in a subfolder of "tags/", e.g. to create a "FOO" tag, you would do something like this:

    svn copy svn://url-to-myrepository-root/trunk svn://url-to-myrepository-root/tags/FOO -m "Tagging as FOO."


    (By the way, as you do this, you modify the repository, so this will create a new revision, with a incremented revision number.)

    Hopefully, the wowace site provides a more convenient wait to do so: open the "Project management" dropdown and click on the "Tag repository" link. You can input "FOO" and "Tagging as FOO." there and the site will create the tag for you.

    Until then, I was only speaking of Subversion. Now, the wowace packager comes into play:

    The wowace packager automatically creates packages from commits in Subversion. By default, the settings is "alpha, beta and release" for new projects.

    The packager has to pick a version name for the new packages. If it packages an "alpha", e.g. a simple commit to your "trunk/", it simply prefixes the revision number with "r", hence "r144".

    However, if it detects a new tag, it will use the tag name instead: if you create a new tag "BAR", the version name will be "BAR", not "r145". There are some subtle rules to detect if a version is a beta or a release, but this can be changed afterwards on the site.

    BTW, Torhal: there is no such thing as a local tag in Subversion, every is done on the online repository.
    Posted in: General Chat
  • 0

    posted a message on LibNameplateRegistry-1.0
    Quote from Archarodim
    I'd also like to point out that using the same handler for both is not recommended as those two callbacks behave very differently:

    One fires each time a nameplate's frame appears on screen and the other one only fires when new information (the GUID) is available about a nameplate which is already on screen and thus for which LNR_ON_NEW_PLATE fired.


    The addon doesn't care about nameplates without GUID. Moreover, if the "unitFrame" is already attached to the same nameplate, :AttachToNameplate does nothing.

    Quote from Archarodim

    You're right it's not that complex, thanks for the code snippet above (that removes me the laziness excuse^^). Anyway I'll regret the animation timer neatness which only executes code when they tick at the scheduled rate. Knowing that "timer = timer - elapsed; if timer > 0 then return end;" will be done 120 times (more or less) per second hurts my perfectionism side :(


    You can do that too :

    if not LNR_Private.Anim  then
      LNR_Private.Anim = LNR_Private.EventFrame:CreateAnimationGroup()
    end
    if not LNR_Private.Timer then
      LNR_Private.Timer = LNR_Private.Anim:CreateAnimation()
    end
    
    LNR_Private.Anim:SetLooping("REPEAT")
    LNR_Private.Timer:SetDuration(0.1)
    
    LNR_Private.Timer:SetScript('OnFinished', function()
      -- Copy previous handler there, without the throttle code
    end)
    


    Then call LNR_Private.Anim:Play() in :Enable and LNR_Private.Anim:Stop() in :Disable.

    Quote from Archarodim

    That's what I tried to do at first but it became messy pretty quickly so I decided to do it using a known paradigm, it makes it easier to understand. (I'm very much into OO programming nowadays)
    I also wanted to use a shutdown mechanism instead of passing everything to a newer version which would have to know and handle every other previous version particularities to upgrade cleanly. This private/public design simplify things in my opinion, each library version only has to know about itself...


    Actually, this is the upgrading process of the defunct AceLibrary. It has been abandoned because it was considered too complex and heavy.
    Posted in: Libraries
  • 0

    posted a message on Can't compare profile tables correctly
    Yes, Phanx, but you belong to the "educated" kind of users. Sometimes users have to be taken by the hand, especially when adding new features.

    Gavin, maybe this could be an optional behavior (with a "do not show this again" checkbox).
    Posted in: Ace3
  • 0

    posted a message on LibNameplateRegistry-1.0
    Quote from Archarodim
    nice :) what is your add-on by the way?


    The first is DiminishingReturns, that tracks PvP diminishing returns and displays feedback as icons on unit frames and nameplates.

    The second is a "private" (i.e. not published on Curse), experimental one: AdiBuffPlate, that displays player's debuffs on enemy nameplates.

    One I found quite annoying though is the difference between the second argument of the LNR_ON_NEW_PLATE and LNR_ON_GUID_FOUND callbacks: the former passes nameplate data, the latter only the GUID. I was doing something like this :

    function addon:OnEnable()
    	--[[ ... ]]
    	self:LNR_RegisterCallback('LNR_ON_NEW_PLATE', 'NewPlate')
    	self:LNR_RegisterCallback('LNR_ON_GUID_FOUND', 'NewPlate')
    	--[[ ... ]]
    end
    
    --[[ ... ]]
    
    function addon:NewPlate(event, nameplate, data)
    	local unitFrame = data.GUID and unitFrames[data.GUID]
    	if unitFrame then
    		unitFrame:AttachToNameplate(nameplate)
    	end
    end
    


    So I had to add another handler.

    Quote from Archarodim
    I know, I hesitated seeing that no other library was doing this but I was too lazy to reinvent the wheel once more and make the library more complex and bug prone.
    AceTimer being a very common and simple library, it shouldn't be a problem anyway.


    Yep, but it requires the developer to include it in the addon. For example, DiminishingReturns doesn't use AceTimer-3.0. So instead of shipping LibNameplateRegistry-1.0 with it, it is optional and I tell the user had to install the standalone version if he wanted the nameplate icons.

    What you do with LNR isn't that complex. It doesn't seems there is more than one timer for each task so you could just have a big OnUpdate handler with a front rate limiter and several tasks based on boolean tests. AceTimer-3.0 spawns (or recycles) an animation each time you call :Schedule*Timer.

    local timer = 0
    local sanityDivisor = 0
    LNR_Private.EventFrame:SetScript('OnUpdate', function(_, elapsed)
      -- Main throttle
      timer = timer - elapsed
      if timer > 0 then return end
      timer = 0.1
    
      -- Check sanity every 100th tick
      sanityDivisor = sanityDivisor  + 1
      if sanityDivisor == 100 then
        sanityDivisor = 0
        LNR_Private:CheckHookSanity()
      end
    
      -- Look for new plates
      LNR_Private:LookForNewPlates()
    
      -- Look for target plate
      if HasTarget then
        LNR_Private:CheckPlatesForTarget()
      end
    
      --[===[@debug@
      self:DebugTests()
      --@end-debug@]===]
    end)
    


    Hide EventFrame at creation, show it in :Enable and hide it in :Disable, et voilà !

    And for the out-of-combat enabling, just register PLAYER_REGEN_ENABLED and call Enable when it fires.

    Quote from Archarodim
    I think it's cleaner that way and it also limits the amount of strange bugs that could be caused by a bugged add-on that would find its way into the library and damage it. (I've seen this happen a few times and lost hours to find the cause)
    It's not that complicated once you get used to it, it makes the design clearer. At least for me.


    A bunch of upvalues would have worked too and the library tables were designed intended to pass important data from one instance of the library to another one.
    Posted in: Libraries
  • 0

    posted a message on LibNameplateRegistry-1.0
    Ok, so, the API does what LibNamePlate-1.0 did, I just had to replace some names there and there. It seems to work a first glance, I have to test it further.

    One question though about the internals: why bother with all that private stuff ? It just seems to make things more complicated than they should.

    By the way, isn't there any way to get rid of the dependency to AceTimer-3.0 ? Most of the timers could be handled by a simple OnUpdate handler (eventually of a animation to control the execution rate) and one of them could just be done using an event handler.
    Posted in: Libraries
  • 0

    posted a message on Can't compare profile tables correctly
    According to AceDB-3.0 source, the events are fired as this :

    OnNewProfile(db, profileKey): fired when a new profile is spawned out of the nether. It does not need upgrading.

    OnProfileChanged(db, profileKey): fired when the current profile has been changed. It may need upgrading. This happens when the user selects a new profile (db:SetProfile) and when the database is reset (db:ResetDb).

    OnProfileCopied(db, profileKey): fired when the content of the current profile has been overwritten by another one. It may need upgrading. This happens when the user copy another profile into the current one (db:CopyProfile).

    OnProfileReset(db, profileKey): fired when the current profile has been reset to defaults (db:ResetProfile). It does not need upgrading. This happens when the user resets its profile.

    So basically, OnNewProfile and OnProfileReset should only do so minimal initialization, like settings the version field to the latest value and nothing more. You should check if the profile needs to be upgraded in OnProfileChanged and OnProfileCopied callbacks. Please also note that no callbacks is fired when the addon starts with an existing profile, so you have to check the profile at that time too.

    By the way, you should never put the version field in the defaults, because AceDB will remove it from the saved variable at unloading. The next time the profile is loaded, eventually by a new version of the addon, AceDB will initialize with the (new) default.

    Your code might look like this :

    local UnitBars
    local CURRENT_PROFILE_VERSION = 2 -- increase this each time changes in the defaults requires to upgrade the existing profiles
    
    function GUB:OnInitialize()
      -- Add blizzards powerbar colors and class colors to defaults.
      InitializeColors()
    
      _, PlayerClass = UnitClass('player')
      PlayerPowerType = UnitPowerType('player')
    
      -- Get the globally unique identifier for the player.
      PlayerGUID = UnitGUID('player')
    
      -- Load the unitbars database
      GUB.MainDB = LibStub('AceDB-3.0'):New('GalvinUnitBarsDB', GUB.DefaultUB.Default, true)
      GUB.MainDB.RegisterCallback(GUB, 'OnNewProfile', 'ProfileNew')
      GUB.MainDB.RegisterCallback(GUB, 'OnProfileReset', 'ProfileNew')
      GUB.MainDB.RegisterCallback(GUB, 'OnProfileChanged', 'ProfileChanged')
      GUB.MainDB.RegisterCallback(GUB, 'OnProfileCopied', 'ProfileChanged')
    
      -- Initialize the options panel.
      Options:OnInitialize()
    
      -- I feel this should be called after UpgradeProfile, so probably in OnEnable or ApplyProfile
      ShareData()
    end
    
    function GUB:OnEnable()
      self:UpgradeProfile()
      self:ApplyProfile()
    
      -- Initialize the events.
      RegisterEvents('register', 'main')
    end
    
    function GUB:ProfileNew()
      GUB.MainDB.profile.version = CURRENT_PROFILE_VERSION 
    end
    
    function GUB:ProfileChanged()
      self:UpgradeProfile()
      self:ApplyProfile()
    end
    
    function GUB:UpgradeProfile()
      if GUB.MainDB.profile.version == CURRENT_PROFILE_VERSION then
        -- Nothing to do
        return
      end
    
      -- Upgrade there
    
      -- Then:
      GUB.MainDB.profile.version = CURRENT_PROFILE_VERSION 
    end
    
    function GUB:ApplyProfile()
      -- Setup the upvalue
      UnitBars = GUB.MainDB.profile
    
      -- Apply other settings there
    end


    Alternatively, you could use this, since UpgradeProfile and ApplyProfile are in OnEnable :

    function GUB:ProfileChanged()
      -- Properly shutdown the addon and all its modules
      self:Disable()
      -- Re-enable the addon and all its modules to apply the new settings
      self:Enable()
    end


    I found this pretty useful when :OnEnable registers some events or shows some frames depending on the settings.
    Posted in: Ace3
  • To post a comment, please or register a new account.