- Curse Premium
Member for 11 years, 2 months, and 19 days
Last active Thu, Sep, 12 2019 17:20:59
- 1 Follower
- 2,404 Total Posts
- 0 Thanks
Dec 9, 2013Well, 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
Dec 9, 2013I just updated to the latest alpha. I'll do some tests.Posted in: Libraries
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.
Dec 7, 2013Well, BlizzBugsSuck fixes the InterfaceOptionsFrame_OpenToCategory bug, where this function only shows InterfaceOptionsFrame but doesn't select the panel passed as an argument.Posted in: Lua Code Discussion
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.
Dec 6, 2013Posted in: Lua Code DiscussionQuote from PhanxWouldn'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.
Dec 2, 2013The issue is not the AceDB value but the variables Player and TokenName that you use to index it.Posted in: Lua Code Discussion
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.
Dec 2, 2013This 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.Posted in: Lua Code Discussion
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 );
Nov 26, 2013I 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
Nov 23, 2013Posted in: General ChatQuote from GalvinYeah 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.
Nov 21, 2013Again : 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.)Posted in: General Chat
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.
Nov 14, 2013Posted in: Libraries
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.
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.
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.
Nov 14, 2013Yes, Phanx, but you belong to the "educated" kind of users. Sometimes users have to be taken by the hand, especially when adding new features.Posted in: Ace3
Gavin, maybe this could be an optional behavior (with a "do not show this again" checkbox).
Nov 13, 2013Posted in: Libraries
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.
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.
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.
Nov 10, 2013Ok, 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.Posted in: Libraries
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.
Nov 7, 2013According to AceDB-3.0 source, the events are fired as this :Posted in: Ace3
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.
- To post a comment, please login or register a new account.