WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   MoP Beta archived threads (https://www.wowinterface.com/forums/forumdisplay.php?f=162)
-   -   Nameplates (https://www.wowinterface.com/forums/showthread.php?t=43789)

Dridzt 07-26-12 02:31 AM

Nameplates
 
Quote:

Originally Posted by Maul (Post 258736)
Not entirely, I turned the taint log off and still had issues until I narrowed down my crash issue to modifying a custom gametooltip (like used for tooltip scanning). I eliminated the modifications as well as the :GetRegion() calls on the custom tooltip and only then did the crashes stop.

And I still crash on a /reload occasionally with the taint log off.

You can add WorldFrame:GetChildren() to the list of things that hard crash the beta client (build 15882) :(

(typical for nameplate addons looking for nameplates)

Note: Pulled these posts from MoP beta topic as they're no longer MoP specific. - Haleth

ballagarba 07-26-12 07:21 AM

Quote:

Originally Posted by Dridzt (Post 258790)
You can add WorldFrame:GetChildren() to the list of things that hard crash the beta client (build 15882) :(

(typical for nameplate addons looking for nameplates)

In case you didn't know: http://www.wowinterface.com/forums/s...9&postcount=30

Haleth 07-26-12 07:49 AM

You still need to use WorldFrame:GetChildren() to actually iterate through potential frames. Then you can check them by name.

It would be much better if we could hook into the function that creates nameplates, but we don't have access to it.

p3lim 07-26-12 01:28 PM

Quote:

Originally Posted by Haleth (Post 258797)
You still need to use WorldFrame:GetChildren() to actually iterate through potential frames. Then you can check them by name.

Actually...
Lua Code:
  1. local index = 1
  2.  
  3. local addon = CreateFrame('Frame')
  4. addon:SetScript('OnUpdate', function()
  5.     while _G['NamePlate' .. index] do
  6.         local frame = _G['NamePlate' .. index]
  7.  
  8.         -- do whatever
  9.  
  10.         index = index + 1
  11.     end
  12. end)
  13. addon:Show()

Dridzt 07-26-12 01:42 PM

Quote:

Originally Posted by p3lim (Post 258810)
Actually...

That's a simple and clever workaround for the bug actually,
might have to poach it for a couple addons that I've had to keep disabled in beta. :p

Edit: Scratch that, a while loop won't work sadly, nameplate frames are reused for one so you don't always have a nice collection of sequential frames from 1 to n.
Even if we did have that, it would still need to wrap around to index 1 when it exits the while loop.
It's food for thought definitely...

And another function that hard crashes the client (no addons present) GetDestinationReforgeStats() (build 15882)

Haleth 07-26-12 01:46 PM

Okay, p3lim, that's clever. Should be quite a bit more efficient :p

p3lim 07-26-12 02:25 PM

Quote:

Originally Posted by Dridzt (Post 258812)
That's a simple and clever workaround for the bug actually,
might have to poach it for a couple addons that I've had to keep disabled in beta. :p

Edit: Scratch that, a while loop won't work sadly, nameplate frames are reused for one so you don't always have a nice collection of sequential frames from 1 to n.
Even if we did have that, it would still need to wrap around to index 1 when it exits the while loop.
It's food for thought definitely...


Lua Code:
  1. local function Update(self)
  2.     -- do whatever
  3. end
  4.  
  5. local index = 1
  6.  
  7. local addon = CreateFrame('Frame')
  8. addon:SetScript('OnUpdate', function()
  9.     while _G['NamePlate' .. index] do
  10.         local frame = _G['NamePlate' .. index]
  11.         frame:HookScript('OnShow', Update)
  12.  
  13.         Update(frame)
  14.  
  15.         index = index + 1
  16.     end
  17. end)
  18. addon:Show()

endx7 07-26-12 04:32 PM

Quote:

Originally Posted by zork (Post 258820)
You are sure that you want to update every single name plate on every single frame in the game?
With 40 plates thats 40 updates per frame. At 100fps thats 4000 calls per second.

since index is never reset, it is only nameplates that have never been seen before. Each OnUpdate starts where it left off. If there are no new frames at all, the while loop fails immediately.

The OnShow handler ensures that the next time the frame is reused, we can handle updating the frame again.

Dridzt 07-26-12 04:35 PM

Quote:

Originally Posted by zork (Post 258820)
You are sure that you want to update every single name plate on every single frame in the game?
With 40 plates thats 40 updates per frame. At 100fps thats 4000 calls per second.

Not sure why you even want to do that. All nameplate stuff can be done with one style function and some hooks. Once a nameplate is styled it can be ignored from that time on.

The discussion originated from the fact that in current MoP beta one of the things that hard crashes the client is calls to WorldFrame:GetChildren() which most nameplate addons use in their nameplate identification functions.

zork 07-26-12 04:47 PM

Forget what I wrote.

Btw...Does adding a throttle based on elapsed have any positive results performance-wise?

*edit* Tested...does not work

NamePlate1 is not found. Script stops.

Not sure why you say that checking the WorldFrame crashs the client...I'm doing just that and the client does not crash. Or is that only in combination with the taintlog enabled?



NamePlateIds start at 2xx in my case. Not sure why...it's just what the GetName() spits out.

p3lim 07-26-12 05:14 PM

Quote:

Originally Posted by zork (Post 258823)
NamePlate1 is not found. Script stops.
NamePlateIds start at 2xx in my case. Not sure why...it's just what the GetName() spits out.

The reason is because you did /reloads, and the game keeps continuing from where it left off before you did.
Unlike most frames, the nameplates are written (or wrapped) in C, and will not reload like the rest of the UI does.

Here is a way to counter this:
Lua Code:
  1. local function Update(self)
  2.     -- do whatever
  3. end
  4.  
  5. local index = 1
  6. local fresh = true
  7.  
  8. local addon = CreateFrame('Frame')
  9. addon:SetScript('OnUpdate', function()
  10.     while _G['NamePlate' .. index] or fresh do
  11.         local frame = _G['NamePlate' .. index]
  12.         if(frame) then
  13.             fresh = false
  14.  
  15.             frame:HookScript('OnShow', Update)
  16.  
  17.             Update(frame)
  18.         end
  19.  
  20.         index = index + 1
  21.     end
  22. end)
  23. addon:Show()

Dridzt 07-26-12 05:49 PM

Wouldn't that loop run 'forever' and keep bumping index if you happen to reload in an area with no nameplates?

Haleth 07-26-12 06:09 PM

I'd still prefer to limit the while loop to run only, say, 10 times per second, rather than every new frame. Comparing two numbers (to check if enough time has elapsed) is likely far more efficient CPU-wise than looking up a frame in _G.

Btw, I pulled these posts from the MoP topic.

p3lim 07-26-12 06:12 PM

Quote:

Originally Posted by Dridzt (Post 258825)
Wouldn't that loop run 'forever' and keep bumping index if you happen to reload in an area with no nameplates?

It would indeed.

A possible solution instead of the previous would be using a combination of an event and function that only works on a login (not reload) to reset a SV containing a variable that is set on PLAYER_LOGOUT to keep counting properly.

It would lead into a monstrosity though.

p3lim 07-26-12 06:13 PM

Quote:

Originally Posted by Haleth (Post 258827)
I'd still prefer to limit the while loop to run only, say, 10 times per second, rather than every new frame. Comparing two numbers (to check if enough time has elapsed) is likely far more efficient CPU-wise than looking up a frame in _G.

That is not desireable, as it would leave a (possibly) big gap where a nameplate would look like default.

Haleth 07-26-12 06:22 PM

Perhaps we should just request on the official forums that they add a better way for addons to do this. Since it's handled outside of lua, perhaps there could be an event like NAMEPLATE_CREATED, passing the frame as a parameter.

They listened to the request for new macro conditionals. Worth a shot, or?

p3lim 07-26-12 06:23 PM

Quote:

Originally Posted by Haleth (Post 258830)
Perhaps we should just request on the official forums that they add a better way for addons to do this. Since it's handled outside of lua, perhaps there could be an event like NAMEPLATE_CREATED, passing the frame as a parameter.

They listened to the request for new macro conditionals. Worth a shot, or?

Go for it, I don't have access to the US forums.
Also, frame name or id would be enough, don't think they'd go for passing the whole frame table.

Remember, I am not trying to replace the current method we are using, this is just a temporary thing to work around the issues on the beta client, and perhaps find something nice while we're at it.

I agree that the nameplates should be easier to do anything with, but blizzard is avoiding showing the code, most likely to counter potential bots.

p3lim 07-26-12 08:11 PM

Here we go, PLAYER_LOGOUT event saving the last index to a savedvariable, two OnUpdate scripts iterating either a new index or the savedvariable, and either of them shuts down the savedvariable one.

Lua Code:
  1. local index = 1
  2.  
  3. local backup = CreateFrame'Frame'
  4. backup:SetScript('OnUpdate', function(self)
  5.     if(not SomeCleverVariableName) then return end
  6.  
  7.     if(_G['NamePlate' .. SomeCleverVariableName]) then
  8.         self:Hide()
  9.  
  10.         index = SomeCleverVariableName
  11.         SomeCleverVariableName = nil
  12.     end
  13. end)
  14.  
  15. local iterate = CreateFrame'Frame'
  16. iterate:SetScript('OnUpdate', function()
  17.     while(_G['NamePlate' .. index]) do
  18.         local frame = _G['NamePlate' .. index]
  19.         frame:HookScript('OnShow', Update)
  20.         backup:Hide()
  21.  
  22.         Update(frame)
  23.  
  24.         index = index + 1
  25.     end
  26. end)
  27.  
  28. iterate:RegisterEvent'PLAYER_LOGOUT'
  29. iterate:SetScript('OnEvent', function()
  30.     SomeCleverVariableName = index + 1
  31. end)

p3lim 07-27-12 09:26 AM

Also, move this to the proper MoP discussion sub-forum, as it is not related to live clients.

Lombra 07-27-12 06:14 PM

Quote:

Originally Posted by p3lim (Post 258832)
Go for it, I don't have access to the US forums.
Also, frame name or id would be enough, don't think they'd go for passing the whole frame table.

Some spontaneous ideas, should anyone put together a formal proposal to Blizzard; NAMEPLATE_SHOW(index), GetNumNameplates(), GetNameplate(index). Possibly additional events for _CREATE and _UPDATED, and/or an argument indicating a new frame was created.

zork 07-30-12 02:51 PM

In case anyone is interested. I took p3lims advise and changed my script. I'm now checking for nameplates like this:

Lua Code:
  1. --init
  2.   local _DB
  3.   local a = CreateFrame("Frame")
  4.   local index = 1
  5.   --timer  
  6.   local ag = a:CreateAnimationGroup()
  7.   ag.anim = ag:CreateAnimation()
  8.   ag.anim:SetDuration(0.33)
  9.   ag:SetLooping("REPEAT")
  10.   ag:SetScript("OnLoop", function(self, event, ...)
  11.     if _DB.namePlateIndex and _G["NamePlate".._DB.namePlateIndex] then
  12.       index = _DB.namePlateIndex
  13.       _DB.namePlateIndex = nil
  14.     end
  15.     while(_G["NamePlate" .. index]) do
  16.       local frame = _G['NamePlate' .. index]
  17.       if frame and not frame.rDB_styled then
  18.         styleNameplate(frame)
  19.       end
  20.       index = index + 1
  21.     end
  22.   end)
  23.   ag:Play()
  24.   --load some variable stuff
  25.   a:RegisterEvent("PLAYER_LOGIN")
  26.   a:RegisterEvent("PLAYER_LOGOUT")
  27.   a:SetScript("OnEvent", function(self,event,...)
  28.     if event == "PLAYER_LOGIN" then
  29.       _DB = _G["rDP_DB"] or {}
  30.       _G["rDP_DB"] = _DB
  31.     else
  32.       _DB.namePlateIndex = index + 0
  33.     end
  34.   end)

Sidenote...the index must not be increased by 1 on logout or one nameplate will not get styled. (Because the index is already increased by 1 once an index is found.

Running perfectly for now:

ballagarba 07-30-12 02:55 PM

Did you do any CPU profiling comparing the two methods?

Dridzt 07-30-12 02:58 PM

I'm interested :)

I tested build 15913 and it seems they've fixed the :GetChildren() crash, can't reproduce it in any of the situations I used for previous build (it wasn't 100% reproducible, seemed to happen reliably when many nameplates spawned - ex. training all the mobs in Stockade).

Anyway, it seems the client no longer crashes with WorldFrame:GetChildren() so I reverted back to the 'traditional' way of finding plates as simpler.

Will keep an eye on this thread though for sure, an alternative is good to have.

zork 07-30-12 03:13 PM

@ballagarba
The two versions don't differ that much. Both use OnUpdate. (Even the animationgroup does).

The only big difference is that in the traditional way you iterate over all WorldFrame:GetChildren() all the time.

In the new version you check just one index that is either available or not.

ballagarba 07-30-12 03:43 PM

Quote:

Originally Posted by zork (Post 259031)
@ballagarba
The two versions don't differ that much. Both use OnUpdate. (Even the animationgroup does).

The only big difference is that in the traditional way you iterate over all WorldFrame:GetChildren() all the time.

In the new version you check just one index that is either available or not.

I'm curious about how expensive WorldFrame:GetChildren() is. Assume: "local frames = select('#', WorldFrame:GetChildren())". Now you only have to do something if "frames" has changed from previous iteration, and you'd only have to iterate from the previous value of "frames" to its new one. So the cost of the function pretty much equals select('#', WorldFrame:GetChildren()).

p3lim 07-30-12 03:45 PM

Why are you using an animationgroup for this?

Torhal 07-30-12 04:40 PM

Because for the past few years now it's been the cool kids' way of writing a timer. The theory is since it's done in C, it's less overhead. Since we can't measure, I don't actually buy it.

acapela 07-30-12 06:26 PM

Quote:

Originally Posted by Lombra (Post 258882)
Some spontaneous ideas, should anyone put together a formal proposal to Blizzard; NAMEPLATE_SHOW(index), GetNumNameplates(), GetNameplate(index). Possibly additional events for _CREATE and _UPDATED, and/or an argument indicating a new frame was created.

yeah, show/hide events, at least, would be great. given something like that for initial discovery, one could maintain a small cache of frame references, and just iterate it. separate events for the whole lifecycle (create/show/hide/update/destroy/whatever) would be OK, too, assuming there was any value in delivery timing (and assuming "destroy" actually ever meant something).

one of the most requested features in my experience is the ability to control the in-game "range" at which nameplates become visible (currently "hardcoded" at 40 yards, in game terms; used to be much shorter, several major revisions ago). i requested this in the Blizzard forums, long ago and far away, but never heard a peep in response. some folks want to shorten this range, so they update fewer frames.

its funny, too... i never experienced this crash with :GetChildren(). i was not among the first round(s) of Beta access granted, however. seems to have been fixed by the time i received my Blizzard email.

zork 07-31-12 01:20 AM

Quote:

Originally Posted by p3lim (Post 259033)
Why are you using an animationgroup for this?

I just like the timer handling more. But using OnUpdate wouldn't be that much different.

p3lim 07-31-12 10:18 AM

Alright, haven't really used animationgroups before so I just found it weird to be used like this.

One thing tho, to avoid spamming the loading process, run :Play() on PLAYER_LOGIN.

Lua Code:
  1. local function Update(frame)
  2.     -- do whatever
  3. end
  4.  
  5. local index = 1
  6.  
  7. local handler = CreateFrame('Frame')
  8. handler:RegisterEvent('PLAYER_LOGOUT')
  9. handler:RegisterEvent('PLAYER_LOGIN')
  10.  
  11. local animation = handler:CreateAnimationGroup()
  12. animation:CreateAnimation():SetDuration(1/3)
  13. animation:SetLooping('REPEAT')
  14. animation:SetScript('OnLoop', function()
  15.     if(t9e5e5rb and _G['NamePlate' .. t9e5e5rb]) then
  16.         index = t9e5e5rb
  17.         t9e5e5rb = nil
  18.     end
  19.  
  20.     while(_G['NamePlate' .. index]) do
  21.         local frame = _G['NamePlate' .. index]
  22.         frame:HookScript('OnShow', Update)
  23.         Update(frame)
  24.  
  25.         index = index + 1
  26.     end
  27. end)
  28.  
  29. handler:SetScript('OnEvent', function(self, event)
  30.     if(event == 'PLAYER_LOGIN') then
  31.         animation:Play()
  32.     else
  33.         t9e5e5rb = index
  34.     end
  35. end)

p3lim 08-09-12 10:44 PM

Tried both methods today, though as hard as it is to try and stress-test this, I ended up with some results.

The "new" method uses 0.01 CPU/ms less than the previous method (on average), and 0.1 CPU/ms less during stress testing (20+ nameplates).
This was tested with the addon Addon Profiler, and both versions were polling 5 times per second.

These values are so miniscule, you can just use what ever you prefer tbh.
Personally, I will use the stuff I came up with, just because :p.

villiv 08-14-12 01:53 AM

really helpful thread. thank you for sharing this. :)

since all we like efficiency, do CVars-checking will help as well? or useless thoughts?

Lua Code:
  1. local f = CreateFrame('Frame')
  2. local a = f:CreateAnimationGroup()
  3. a:CreateAnimation():SetDuration(1/3)
  4. a:SetLooping('REPEAT')
  5. a:SetScript('OnLoop', function ()
  6.   -- LF Nameplates
  7. end)
  8.  
  9. local cvars = {
  10.     nameplateShowFriends = true, nameplateShowFriendlyPets = true, nameplateShowFriendlyGuardians = true, nameplateShowFriendlyTotems = true,
  11.     nameplateShowEnemies = true, nameplateShowEnemyPets = true, nameplateShowEnemyGuardians = true, nameplateShowEnemyTotems = true,
  12. }
  13.  
  14. local function check ()
  15.     for cvar in next, cvars do if ( GetCVarBool(cvar) ) then return a:Play() end end
  16.     return a:Stop()
  17. end
  18.  
  19. hooksecurefunc('SetCVar', function (cvar) if ( cvars[cvar] ) then return check() end end)


All times are GMT -6. The time now is 05:40 AM.

vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI