Thread Tools Display Modes
03-25-18, 03:47 PM   #1
nKweo
A Deviate Faerie Dragon
 
nKweo's Avatar
Join Date: Oct 2012
Posts: 16
Adding %HP to default nameplates; getting an error occasionally

I've been looking into ways to add a simple %HP text to the default enemy nameplates. So far the best thing I've found is Gello's old addon Nameplate Percents.

It actually still works fine most of the times, but it sometimes throws an error (and doesn't display) in PvE instances. I'm still trying to nail down the exact situation; so far it seems that it only happens in boss fights (but not every time).

The error isn't very clear, but it does say something about taint. I suspect the problem is related to this function:
Code:
-- look for new nameplates that don't have a percent overlay and add one
function frame:ScanNameplates(...)
  for i=1,select("#",...) do
    local plate = select(i,...)
		local name = plate:GetName()
		if name and name:match("^NamePlate") then
			-- the statusBar is the first child of the first child of the nameplate
			local statusBar = plate:GetChildren():GetChildren()
			if not statusBar.percentOverlay then
				statusBar.percentOverlay = statusBar:CreateFontString(nil,"OVERLAY")
				statusBar.percentOverlay:SetFont(STANDARD_TEXT_FONT,12,"OUTLINE")
				local percent = statusBar.percentOverlay
				percent:SetPoint("LEFT",3,0)
				statusBar:HookScript("OnShow",ShowPercent)
				statusBar:HookScript("OnHide",HidePercent)
				overlays[statusBar.percentOverlay] = 1 -- add new child to next update batch
			end
    end
  end
end
As I said, it's pretty old code and Blizzard obviously has made big changes to the way nameplates are handled (e.g. C_NamePlate.GetNamePlates). Moreover, they protected some stuff in 7.2(?).

I've been messing around with it a little bit, but my knowledge is insufficient. I'm hoping for some ideas to get this working perfectly. I'm guessing that this shouldn't be too difficult for some of you code wizards. Any ideas are appreciated though!
  Reply With Quote
03-25-18, 05:37 PM   #2
Ammako
A Frostmaul Preserver
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 256
You would only get those errors if friendly nameplates are enabled, but then again you skipped providing the actual error message, so there's no way to say for certain whether that is the issue or not.
  Reply With Quote
03-25-18, 10:48 PM   #3
briskman3000
A Flamescale Wyrmkin
 
briskman3000's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2009
Posts: 108
Blizzard restricted all friendly nameplate customizations inside PvE instances. You can do literally nothing to them anymore in dungeons, Raids, etc.

http://www.wowhead.com/news=260801/a...s-in-patch-7-2
__________________
My Addons: Convert Ratings Honor Track
  Reply With Quote
03-26-18, 02:53 AM   #4
nKweo
A Deviate Faerie Dragon
 
nKweo's Avatar
Join Date: Oct 2012
Posts: 16
Thanks for the reply guys. The thing is that I'm not using friendly nameplates. Do you think this could nevertheless still lead to an error? I'll try to copy-past the error next time I get it. Like I said, it's been pretty elusive so far (only happening in PvE instances every now and then, so not the best time to troubleshoot).

If the error is caused by the code somehow trying to alter friendly nameplates, how could I make it so that it only looks for enemy plates?
  Reply With Quote
03-26-18, 06:42 AM   #5
Ammako
A Frostmaul Preserver
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 256
If friendly nameplates aren't used, then there is no way for them to trigger taint.
Plates are only protected when assigned to friendly units, and only in PvE instances. Everything else is free game.
If they aren't enabled then they can't cause errors and break things.

The only thing I could think of would be neutral units that get yellow nameplates while "enemy nameplates" are enabled, but who would be somehow considered friendly by nameplate code... but I don't think that actually exists, and I've never had a problem in nearly a year of insecurely altering nameplates, as long as I kept friendly nameplates disabled (and I've been in a lot of raids/dungeon instances, but admittedly I could have missed some.)

(For the record, as far as I'm aware, Personal Resource Display is not considered a friendly nameplate, so it can't be that.)

But then again, error logs are king. I'd imagine it's not even friendly nameplate-related (or at least I'd be surprised if it were.)

Last edited by Ammako : 03-26-18 at 06:44 AM.
  Reply With Quote
03-29-18, 07:16 PM   #6
thomasjohnshannon
A Theradrim Guardian
 
thomasjohnshannon's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 68
You can use "framenamehere:IsForbidden()" to check if the frame is protected. I use this method in my nameplate addon and I haven't had any problems with it.
__________________
Thomas aka Urnn
  Reply With Quote
03-30-18, 01:05 PM   #7
nKweo
A Deviate Faerie Dragon
 
nKweo's Avatar
Join Date: Oct 2012
Posts: 16
Brilliant, thanks! Seems to work smooth now. I 'borrowed' heavily from your code. Happy to hear if there are flaws here.
Code:
-- Add health percent text to (unprotected) nameplates (credit: nPlates by Grimsbain)
hooksecurefunc("CompactUnitFrame_UpdateStatusText", function(frame)
	if frame:IsForbidden() or ( UnitIsFriend("player",frame.displayedUnit) and not UnitIsUnit(frame.displayedUnit,"player") ) then return end
	if not frame.healthBar.percent then
		frame.healthBar.percent = frame.healthBar:CreateFontString(nil,"OVERLAY")
		frame.healthBar.percent:SetPoint("LEFT",frame.healthBar)
		frame.healthBar.percent:SetFont(STANDARD_TEXT_FONT,12,"OUTLINE")
	end
		local percentcalc = ceil((UnitHealth(frame.displayedUnit) / UnitHealthMax(frame.displayedUnit)) * 100)
		frame.healthBar.percent:SetFormattedText("%d%%",percentcalc)
--		frame.healthBar.percent:Show()
end)
EDIT: I'm trying to hide the display from friendly plates at all times (except the personal resource display). Doesn't seem to work like this. Suggestions appreciated!

Last edited by nKweo : 03-30-18 at 01:21 PM.
  Reply With Quote
03-30-18, 03:02 PM   #8
Ammako
A Frostmaul Preserver
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 256
What happens if you try frame.unit rather than frame.displayedUnit?
  Reply With Quote
03-30-18, 05:48 PM   #9
semlar
A Pyroguard Emberseer
 
semlar's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2007
Posts: 1,060
The issue is that you're scanning for nameplates by looping through the children of WorldFrame, then you're trying to call a function on the frame (plate:GetName()), and if that frame happens to be forbidden it will throw an error.

You could add a check like if not plate:IsForbidden(), and put the rest of your function inside of that, or you can use the actual nameplate events that they added a couple years ago with the launch of Legion (NAME_PLATE_CREATED, NAME_PLATE_UNIT_ADDED, NAME_PLATE_UNIT_REMOVED).

The protected friendly nameplates in instances are implemented in a way that addons don't need to do anything to account for their existence; they don't trigger the same nameplate events and are completely separate from the standard nameplate frames, so unless you're doing something like actively scanning through every child of WorldFrame, you won't accidentally encounter them.
  Reply With Quote
03-31-18, 01:59 PM   #10
nKweo
A Deviate Faerie Dragon
 
nKweo's Avatar
Join Date: Oct 2012
Posts: 16
@Ammako: didn't seem to make a difference.
@Semlar: thanks for clarifying. I'm trying a completely different approach now (see previous post), which works better. The problem now though is that the percent display gets drawn on friendly plates even though I'm trying to prevent it (just for consistency).

What's worse is that the percentage overlay gets drawn onto the raid frames as well now... Initially friendly plates stay clean, but as soon as a percentage overlay is created for an enemy plate, overlays get added everywhere... Is there an easy way to circumvent this with this method?
  Reply With Quote
03-31-18, 03:57 PM   #11
Ammako
A Frostmaul Preserver
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 256
OH wait no, I'm dumb.

See, nameplates get reused, once you draw %HP onto a plate from an enemy it will continue showing once the plate gets reused for another unit, friendly or not.

When unit is friend you want to specifically :Hide() frame.healthBar.percent

Unless this has changed lately, but last I was messing with this stuff, this has been the case.

The thing is, though, doing it via this method taints the friendly nameplates regardless, hiding the text doesn't untaint them (you can't untaint something once it's tainted.)
I wouldn't know of a way to do it without tainting friendly nameplates and tbh I never cared. Default plates are useless within PvE instances so there is very little reason to even have them enabled.


btw you still haven't shown us what the error you were getting from the first code actually was. If it's really just a friendly-nameplates-in-instances thing maybe you can just keep using that code but with extra checks like semlar said. That may be the better way to do it. Or maybe it's not even related to friendly nameplates, because we don't know.

Last edited by Ammako : 03-31-18 at 04:24 PM.
  Reply With Quote
03-31-18, 05:18 PM   #12
nKweo
A Deviate Faerie Dragon
 
nKweo's Avatar
Join Date: Oct 2012
Posts: 16
That makes a lot of sense. At least I've managed to keep them from the raid frames. Using this function did the trick (thanks again Grimsbain!):
Code:
local IsNameplate = function(unit)
	if type(unit) ~= "string" then return false end
	if ( string.match(unit,"nameplate") ~= "nameplate" and string.match(unit,"NamePlate") ~= "NamePlate" ) then
		return false
	else
		return true
	end
end
Taint has never actually affected any functionality for me while playing, so I guess it's more of a 'puritan' thing. Pretty happy with this at the moment.

Last edited by nKweo : 04-01-18 at 03:00 PM.
  Reply With Quote
03-31-18, 06:02 PM   #13
Ammako
A Frostmaul Preserver
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 256
Nameplate taint doesn't really affect anything beyond throwing errors if a nameplate used by a friendly unit has been tainted, even if you aren't currently touching them. Anything you touch becomes tainted, but this is only a problem if said code is expected to run through secure Blizzard functions. Code tainted = the code can no longer be verified as secure = errors if secure code ever processes it.

Maybe I messed up when trying to avoid protected nameplates taint, though; if semlar says they're completely separate maybe I should mess with it again some more.


Edit: Sounds like my implementation of the check was bogus. I had no idea frame:IsForbidden() was a thing, so I was using a bunch of different conditionals together that were meant to prevent modifications from being done to friendly nameplates while you were in an instance, but I guess that wasn't really failproof and some of it got through anyway.

Using frame:IsForbidden() and not even changing any of the rest of my code appears to prevent taint errors when within instances and friendly nameplates are enabled. Or at least, disabling the checks triggers an error whenever a friendly nameplate comes on screen, and when I re-enable them those errors no longer occur.

Well that's pretty cool, that was barely any work, and basically, ignore most of the other things I said earlier about nameplate taint, because it's wrong.

Last edited by Ammako : 03-31-18 at 06:47 PM.
  Reply With Quote
04-01-18, 01:10 AM   #14
nKweo
A Deviate Faerie Dragon
 
nKweo's Avatar
Join Date: Oct 2012
Posts: 16
That's super cool. Guess we all learned something then. Funny how newbie questions such as mine can sometimes even (indirectly) help seasoned coders. And thanks again for your time and thoughts.
  Reply With Quote
04-01-18, 07:30 AM   #15
Ammako
A Frostmaul Preserver
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 256
Psh I'm not a seasoned coder. I just do whatever gives me the results I want. To sum it up: I don't code, I hack.
I hang around and sometimes people show up with better and usually easier/more straightforward ways to do things, and that's how I learn to do better I guess.

I blame the lack of proper API documentation from Blizzard... fansites often just never get updated with new API documentation (For ex: nameplates are still undocumented on wowpedia, and the 7.2.0 API changes don't even mention forbidden nameplates.) So you have to just mess with it and feel your way around until you figure out how things work, or find the right people to ask for info, who happen to have done the research already.
Though I hear something is in the works with 8.0, as far as official documentation from Blizzard goes...
  Reply With Quote
04-01-18, 03:02 PM   #16
nKweo
A Deviate Faerie Dragon
 
nKweo's Avatar
Join Date: Oct 2012
Posts: 16
Correction: more seasoned coders . I made a small correction to the aforementioned function by the way. I tried to be a smart ass by fiddling with the original...
  Reply With Quote
10-24-20, 11:55 PM   #17
Chekoz9
A Kobold Labourer
Join Date: Oct 2020
Posts: 1
Pls Help

Ok so Idk anything about programming just been modifying the script using some of your suggestions and the error is still a thing plus 2 percentages are shown in the nameplates if you guys could help me solve it or pass the right script so I can copy and paste it I would appreciatte it.

This is the script that I got so far:

-- 06/22/2015 1.0.5 toc update for 6.2
-- 04/19/2015 1.0.4 fix for lua error when chat bubbles enabled
-- 02/24/2015 1.0.3 toc update for 6.1
-- 10/14/2014 1.0.2 6.0 patch
-- 05/20/2014 1.0.1 initial release

local frequency = 0.2 -- how frequently to look for new nameplates and update visible percents
local numChildren = 0 -- number of WorldFrame's children
local overlays = {} -- indexed by overlay frame added to each nameplate's statusBar

local frame = CreateFrame("Frame",nil,UIParent)
frame.timer = 0
frame.knownChildren = 0 -- number of WorldFrame's children that we know about

-- updates the percentOverlay text on the nameplate's statusbar
local function UpdatePercent(self)
local parent = self:GetParent()
local value = parent:GetValue()
local _,maxValue = parent:GetMinMaxValues()
if maxValue and value<maxValue then
self:SetText(format("%d%%",100*value/maxValue))
else
self:SetText("") -- blank if no relevant values or value is maxValue (100% life)
end
end

-- when a nameplate shows, add it to frame.statusBars
local function ShowPercent(self)
overlays[self.percentOverlay] = 1
end

-- when a nameplate hides, remove it from frame.statusBars
local function HidePercent(self)
overlays[self.percentOverlay] = nil
self.percentOverlay:SetText("") -- blank for when nameplate reused
end

local IsNameplate = function(unit)
if type(unit) ~= "string" then return false end
if ( string.match(unit,"nameplate") ~= "nameplate" and string.match(unit,"NamePlate") ~= "NamePlate" ) then
return false
else
return true
end
end


-- look for new nameplates that don't have a percent overlay and add one
function frame:ScanNameplates(...)
for i=1,select("#",...) do
local plate = select(i,...)
local name = plate:GetName()
if name and name:match("^NamePlate") then
-- the statusBar is the first child of the first child of the nameplate
local statusBar = plate:GetChildren():GetChildren()
if not statusBar.percentOverlay then
statusBar.percentOverlay = statusBar:CreateFontString(nil,"OVERLAY","GameFontHighlightSmall")
local percent = statusBar.percentOverlay
percent:SetPoint("RIGHT")
statusBar:HookScript("OnShow",HidePercent)
statusBar:HookScript("OnHide",HidePercent)
overlays[statusBar.percentOverlay] = 1 -- add new child to next update batch
end
end
end
end

function frame:OnUpdate(elapsed)
self.timer = self.timer + elapsed
if self.timer > frequency then
self.timer = 0
-- first look for any new nameplates (if WorldFrame has a new kid, it's likely a nameplate)
numChildren = WorldFrame:GetNumChildren()
if numChildren > self.knownChildren then
self.knownChildren = numChildren
self:ScanNameplates(WorldFrame:GetChildren())
end
-- next update percents for all visible nameplate statusBars
for overlay in pairs(overlays) do
UpdatePercent(overlay)
end
end
end
frame:SetScript("OnUpdate",frame.OnUpdate)

-- Add health percent text to (unprotected) nameplates (credit: nPlates by Grimsbain)
hooksecurefunc("CompactUnitFrame_UpdateStatusText", function(frame)
if frame:IsForbidden() or ( UnitIsFriend("player",frame.displayedUnit) and not UnitIsUnit(frame.displayedUnit,"player") ) then return end
if not frame.healthBar.percent then
frame.healthBar.percent = frame.healthBar:CreateFontString(nil,"OVERLAY")
frame.healthBar.percent:SetPoint("RIGHT",frame.healthBar)
frame.healthBar.percent:SetFont(STANDARD_TEXT_FONT,12,"OUTLINE")
end
local percentcalc = ceil((UnitHealth(frame.displayedUnit) / UnitHealthMax(frame.displayedUnit)) * 100)
frame.healthBar.percent:SetFormattedText("%d%%",percentcalc)
-- frame.healthBar.percent:Show()
end)
  Reply With Quote
10-27-20, 07:04 AM   #18
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
This is similar to what I did with my personal nameplate addon. I modified it to duplicate the layout of your existing code. If you're running this on Classic, change the first hook from AcquireUnitFrame to OnNamePlateCreated.
Lua Code:
  1. local TextObjects={};
  2.  
  3. hooksecurefunc(NamePlateDriverFrame,"AcquireUnitFrame",function(self,base)
  4.     if base:IsForbidden() then return; end
  5.  
  6.     local uf=base.UnitFrame; if TextObjects[uf] then return; end
  7.     local healthtext=uf.healthBar:CreateFontString(nil,"OVERLAY",FontName);--"NumberFontNormalSmall");
  8.     healthtext:SetPoint("RIGHT",0,0);
  9.     healthtext:SetFont(STANDARD_TEXT_FONT,12,"OUTLINE");
  10.  
  11.     TextObjects[uf]=healthtext;
  12. end);
  13.  
  14. hooksecurefunc("CompactUnitFrame_UpdateHealth",function(self)
  15.     local healthtext=TextObjects[self]; if not healthtext then return; end
  16.     if UnitIsUnit(frame.displayedUnit,"player") or not UnitIsFriend("player",frame.displayedUnit) then
  17.         healthtext:SetFormattedText("%.0f%%",100*UnitHealth(self.displayedUnit)/UnitHealthMax(self.displayedUnit));
  18.     else healthtext:SetText(nil); end
  19. end);
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote

WoWInterface » AddOns, Compilations, Macros » AddOn Help/Support » Adding %HP to default nameplates; getting an error occasionally

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off