I've been trying out QuickHealth as a Grid-using healer, but I have to admit it's not working as well for me as reported by some here. Rather than just showing health changes on raid members quicker, it seems that it causes the health shown on Grid to become very "jumpy".
I can't be exactly sure of what's happening as it all happens very fast, but it appears as though after a health change on a raid member, Grid shows me the new health amount instantly (as it should, from QH), then suddenly jumps back to the old percentage for a fraction of a second, and then finally settles again on the new health amount. During this sequence of events I'm rather lost as to whether the tank needs a heal or not.
But as I said, I can't be exactly sure of what's happening, just that the health of players seems to jump more than the in game situation could explain. I can think of another possible explanation: Suppose a tank is at 100%, and gets hit for 25% of its health. QH processes this correctly and shows the tank at 75%. The server prepares to send the client a proper health update for the player's new 75% health status as well - redundant information since the player runs QH, but the server doesn't know that. Suddenly, the tank gets hit for another 50% of its health, reducing the total to 25%. QH processes this combat log entry immediately as well, updating the displayed percentage as 25%. Right after this, the health update the server was preparing arrives and is processed by the client's code, telling Grid that the tank is at 75% health. And finally, the server sends another health update after the 2nd hit, now telling the client the tank is at 25% health.
In short, the actual tank's health progresses as follows: 100% - 75% - 25%
But, if the above is anywhere near the truth, it would appear as: 100% - 75% - 25% - 75% - 25%
I realise this may sound a little far fetched, but I'm just trying to come up for explanations of what I'm witnessing. Anyone have any idea whether this could be anywhere near the truth, or if not, what could better explain this?
Ironhand, I believe your explanation is correct. The problem is that there are two health values, one from QH, and one from UnitHealth(), but it's not obvious which one is "better". Just because UNIT_HEALTH fired doesn't mean UnitHealth() is exact.
One suggestion I have is to use not only the current health from QH, but to also store the previous value. Then you could use the value from UnitHealth() only if it differs from both the current and the previous value.
There's one more problem though: Not every health change can be explained by CLEU events. I'm talking about the HP5 ticks from buffs like Elixir of Major Fortitude or Demon Armor. Those HP5 ticks are not processed by QH, right? So in this case you'd just have to trust UnitHealth().
Ok I wrote a small addon for experiments. Not an elegant solution, possibly CPU intensive, but I it should be working.
To understand what I'm doing take a look at the attached screen shot. The blue lines correspond to the HealthUpdated('player') events from LibQuickHealth-1.0. The green lines are UNIT_HEALTH('player') events. The addon listens to both, and tries to reconcile them (white lines). Every time HealthUpdated fires, the health difference is entered into a queue (in braces).
At 0.455, UnitHealth returned 7417. However, this value doesn't contain the last health update (-165). At this time, LQH just uses the "wrong" value of 7417, until UNIT_HEALTH is fired the next time at 0.705. This results in one of those "jumps" Ironhand described earlier.
The addon, however, looks at the queue, realizes that UnitHealth ignored the last value, and predicts a health value of 7252.
Another interesting line is at 3.297. At this time, the values in the queue sum up to +272, but according to UnitHealth the health changed by +273. This is because I have a 4 HP5 enchant. In these cases, when there are health changes that cannot be explained from the combat log, the addon still uses the health updates from the queue, but tries to minimize the error. In other words it assumes that HP5 ticks are minimal.
If you want to try it out, create a Chatframe called "QuickHealthTest" to see the debug messages. The addon requires Ace3 and LibQuickHealth-1.0 as stand-alone libs.
Edit:
Apparently I can insert an attached image. Is there some option do just display attached images automatically?
Very interesting. I haven't actually tried the test addon as the values in the screenshot say it all for me. Pretty certain that this is indeed what I'm witnessing on my Grid with QH enabled. Thank you for doing the work to better bring the problem to light.
As far as I can see, your code is able to give a value for a player's health that keeps the fast updates provided by QH, but at the same time removes the delayed "echo" like values caused by the slowness of UNIT_HEALTH updates.
If this could somehow be implemented in LibQuickHealth itself, that would be fantastic. When I'm playing a healer I love the idea of faster health updates (and the test log you show points out how scarily slow the UNIT_HEALTH updates can be), but the jumps that the current QH causes end up affecting my healing negatively as they cause me to cancel heals at the last instant that later turn out to be needed after all, or waste mana by finishing the cast on heals that end up landing on a full health tank.
I did some more tests, and enhanced my addon to gather UNIT_HEALTH and CLEU data (in raids), that I could use later for testing. I updated the addon accordingly, and I find it works very well now. Sometimes it still fails to predict the health correctly, but these cases are both very rare and not really important. Most of these special cases have to do with overheal, in other words the health is almost full anyways. You probably won't notice it.
So here's the new version of the addon. It contains a mockup LibQuickHealth-2.0 implementation, and a small example addon using the new lib. Only the player's health/healthMax are tracked. The example addon displays two health bars, one for LQH2 values, one for UnitHealth. I'm pretty sure bumping the major version is appropriate, because listening to UNIT_HEALTH and UNIT_HEALTHMAX is discouraged now.
Edit: Removed attachment, see below for the latest version. =)
Ok this is just too awesome: I implemented a real-time replay feature for recorded logs. You'll be able to see both the value from Blizz's code (UnitHealth), and the predicted value (QuickHealth). If two events from the log are more than 2s apart, the gap is shortened to 2s, so it won't get too boring.
Here's what you need to do to watch my health while tanking Azgalor from yesterday:
1. Extract the file "YOURACCOUNTNAME\SavedVariables\QuickHealthTest.lua" to "WTF\Account\YOURACCOUNTNAME\SavedVariables\QuickHealthTest.lua".
2. Extract the folders "QuickHealthTest" and "Ace3" to your AddOns directory. (Sorry, no embeds)
3. When you start WoW, make sure you enable the addon. (disabled by default)
3. In-game enter "/script qhtreplay(19,2644)". This tells the addon to load log #19, and start replaying at event #2644.
4. Enjoy the demo of UnitHealth lags. Notice how well the addon predicts the health.
5. Optional: Create a chat frame "QuickHealthTest" to see messages of what happens.
6. To stop the replay enter "/script qhtstop()".
Here's a little script I'm using in WoWLua, maybe you find it useful.
The implementation is done so far, it's on http://code.google.com/p/wow-tifi. It works perfect now on my testing data for player (MT in a raid). But it still needs testing (and profiling) in a party or raid.
I hope you're ok with a major version bump? LibQuickHealth-2.0 or LibQuickHealth-1.1?
There's one thing I'm not sure about. When a unit frame addon uses the library, this addon needs to keep track of the GUID->unitIDs mapping. How about incorporating this into the library? The library could have two events, HealthUpdated(guid, ...) and UnitHealthUpdated(unitID, ...). Maintaining the GUID->unitIDs map would only be necessary if UnitHealthUpdated is registered.
I added the new library to the SVN as LibQuickHealth-2.0. I'm probably going to add another event to the library which provides health updates for unitIDs instead of GUIDs. This should make it trivially easy to use the library in a unitframe addon.
Features:
LibQuickHealth-2.0 is a library that provides more up to date health data than the default Blizz events and functions.
In addition to the standard UNIT_HEALTH event, the library listens to combat log events (CLEU) to update health values. CLEU events fire more often than UNIT_HEALTH, and allow the library to get around the ~300ms throttling of UNIT_HEALTH updates.
The interesting CLEU events are those containing health differences (damage and heals). Normally, when UNIT_HEALTH fires, those differences add up to the value reported by Blizz's UnitHealth. But sometimes, when a unit takes damage according to the CLEU, that damage is processed *after* the next UNIT_HEALTH event. This is called "pending damage". One of the features of LibQuickHealth-2.0 (and an improvement over 1.0) is to detect pending damage, and incorporate it into the health values that are presented to players.
Limitations:
1.) Sometimes there are combat log entries like this: -6000 (damage), +5000 (heal). But what really happened on the server was that the heal occured first. If at least part of this heal was overheal, the prediction (-6000+5000 = -1000) is way off, because what happened is for example +50 heal first(4950 overheal), and then 6000 damage, for a total of +50-6000 = -5950. No AddOn could possibly prevent or predict this, but luckily these cases are very rare.
However, if you see something like this happening (tank at ~100%, then takes damage instantly followed by a heal), you should know that this heal might have been overheal. So don't cancel your heal. ;)
2.) If the maximum health changes, pending damage is completely ignored. Example: The tank is at 11001/18000, takes 9001 damage and uses Last Stand at the same time. Your client might tell you now that the tank health is 16401/23400 (+30% health and max health), because that 9k hit was not yet processed (pending damage). What the library does is: Show the tank at 11001/18000, then at 11001-9001 = 2000/18000, then at 16401/23400, because pending damage is ignored. But on the server, the tank has only 16401-9001 = 7400/23400 health!
This looks very similar to the case before: The tank takes damage, but shortly afterwards that damage is (magically) undone. Again, if you see this, don't cancel your heal!
3.) Bloodrush. There's no combat log entry for the health lost. I believe the health cost depends on the caster level and race, but I don't know the exact formula.
4.) Exceptionally large HP5 values. There are no combat log entries for HP5 ticks, which occur every 2 seconds. The library assumes that the health regen in combat is between 0 and 50 HP5, i.e. HP5 ticks between 0 and 20.
Common HP5 values: Demon skin/armor <=18 (24 talented?), Elixir of Major Fortitude 10, Enchant Boots - Vitality 4. Less common: Dreaming Glory 30 (buff from herbalism), and a few level 60 items.
So the library might get confused by two things: Small amounts of pending damage (20 or less), and Warlocks with herbalism...
Usage:
To start listening to the event, do QuickHealth.RegisterCallback(YourAddonTableHere, "HealthUpdated", HandlerMethodHere);
The callback triggers with 5 arguments: self, event, guid, health, healthMax (first two passed by the callbackhandler)
Additionaly, QuickHealth:UnitHealth(unitID) can be called to get what QuickHealth thinks is the current health.
Example:
local QuickHealth = LibStub("LibQuickHealth-2.0")
local MyAddon = {}
function MyAddon:Initialize()
-- Note: It's QuickHealth.RegisterCallback(), not QuickHealth:RegisterCallback().
QuickHealth.RegisterCallback(MyAddon, "HealthUpdated", "HealthUpdated")
-- Or:
-- QuickHealth.RegisterCallback(MyAddon, "HealthUpdated", MyAddon.HealthUpdated)
end
function MyAddon:HealthUpdated(event, guid, health, healthMax)
if event == "HealthUpdated" then
ChatFrame1:AddMessage(format("%s HealthUpdated %i/%i", guid, health, healthMax))
if guid==UnitGUID("player") then
assert(health==LibQuickHealth:UnitHealth("player")
end
end
end
As much as everyone loves CBH, it is a really slow Stub of a Lib. I would recommend implementing your own CBH methods that are more slim'ed down and don't have as much logistical overhead. I only recommend this because we're dealing with time constrain'ed information and CBH, CBF to be fast when it matters.
Well I can profile it tomorrow. If I bump the major version of CBH locally, LQH should get its own private CBH. Are you saying that CBH:Fire() is too slow?
Well I can profile it tomorrow. If I bump the major version of CBH locally, LQH should get its own private CBH. Are you saying that CBH:Fire() is too slow?
CBH is designed to be very small and dosn't use shared code really at all. But it's dispatch method is slow and painful. I love it's implementation and all, would recommend it for just about anything. However the time sensitive nature of this addon would be messed up something bad if you used it because it's dispatching is slow, it's a wraped, loadstring'ed, meta-table xpcall. I don't know much about internals but the huristic idea says that the fewer objects used in a proccess the faster it generally will be. A simple , pairs(callback) do pcall(func, ...) end
would work just fine, and maybe even faster.
In my honest, unintelligent, ignorant opinion, a custom solution / dispatcher would be faster due to the time sensitive nature of what this lib is doing. Otherwise what's the point in being .3 seconds behind in updates when you can just use the normal UNIT_HEALTH events?
Come on, wasn't the point of this lib to get around that annoyance in the event throttling from blizzard?
LQH might be somewhat time sensitive, but it's not a spacecraft. :> I'll profile it in ZA today, but I don't expect CBH to be a problem. And as far as I can tell from the code, one function is loadstring'ed, but only the first time it is used (the loadstring result is cached).
Btw UNIT_HEALTH throttling is ~300ms, but that doesn't mean UH fires every 300ms. It just means UH doesn't fire more often than every 300ms. Also, damage is laggier than heals (don't ask me why). I see UH lag of 600-1000ms quite often. So even if CBH:Fire() would take 2ms per call (which it doesn't), it wouldn't matter.
ups, yea, will fix it when I'm not instanced anymore
I can't be exactly sure of what's happening as it all happens very fast, but it appears as though after a health change on a raid member, Grid shows me the new health amount instantly (as it should, from QH), then suddenly jumps back to the old percentage for a fraction of a second, and then finally settles again on the new health amount. During this sequence of events I'm rather lost as to whether the tank needs a heal or not.
But as I said, I can't be exactly sure of what's happening, just that the health of players seems to jump more than the in game situation could explain. I can think of another possible explanation: Suppose a tank is at 100%, and gets hit for 25% of its health. QH processes this correctly and shows the tank at 75%. The server prepares to send the client a proper health update for the player's new 75% health status as well - redundant information since the player runs QH, but the server doesn't know that. Suddenly, the tank gets hit for another 50% of its health, reducing the total to 25%. QH processes this combat log entry immediately as well, updating the displayed percentage as 25%. Right after this, the health update the server was preparing arrives and is processed by the client's code, telling Grid that the tank is at 75% health. And finally, the server sends another health update after the 2nd hit, now telling the client the tank is at 25% health.
In short, the actual tank's health progresses as follows: 100% - 75% - 25%
But, if the above is anywhere near the truth, it would appear as: 100% - 75% - 25% - 75% - 25%
I realise this may sound a little far fetched, but I'm just trying to come up for explanations of what I'm witnessing. Anyone have any idea whether this could be anywhere near the truth, or if not, what could better explain this?
it seems that the Focusframe from Pitbull don`t work with Quickhealth. The Frames in sRaidFrames
are much faster than the Pitbull Focus.
Regards
Rorwin
One suggestion I have is to use not only the current health from QH, but to also store the previous value. Then you could use the value from UnitHealth() only if it differs from both the current and the previous value.
There's one more problem though: Not every health change can be explained by CLEU events. I'm talking about the HP5 ticks from buffs like Elixir of Major Fortitude or Demon Armor. Those HP5 ticks are not processed by QH, right? So in this case you'd just have to trust UnitHealth().
To understand what I'm doing take a look at the attached screen shot. The blue lines correspond to the HealthUpdated('player') events from LibQuickHealth-1.0. The green lines are UNIT_HEALTH('player') events. The addon listens to both, and tries to reconcile them (white lines). Every time HealthUpdated fires, the health difference is entered into a queue (in braces).
At 0.455, UnitHealth returned 7417. However, this value doesn't contain the last health update (-165). At this time, LQH just uses the "wrong" value of 7417, until UNIT_HEALTH is fired the next time at 0.705. This results in one of those "jumps" Ironhand described earlier.
The addon, however, looks at the queue, realizes that UnitHealth ignored the last value, and predicts a health value of 7252.
Another interesting line is at 3.297. At this time, the values in the queue sum up to +272, but according to UnitHealth the health changed by +273. This is because I have a 4 HP5 enchant. In these cases, when there are health changes that cannot be explained from the combat log, the addon still uses the health updates from the queue, but tries to minimize the error. In other words it assumes that HP5 ticks are minimal.
If you want to try it out, create a Chatframe called "QuickHealthTest" to see the debug messages. The addon requires Ace3 and LibQuickHealth-1.0 as stand-alone libs.
Edit:
Apparently I can insert an attached image. Is there some option do just display attached images automatically?
As far as I can see, your code is able to give a value for a player's health that keeps the fast updates provided by QH, but at the same time removes the delayed "echo" like values caused by the slowness of UNIT_HEALTH updates.
If this could somehow be implemented in LibQuickHealth itself, that would be fantastic. When I'm playing a healer I love the idea of faster health updates (and the test log you show points out how scarily slow the UNIT_HEALTH updates can be), but the jumps that the current QH causes end up affecting my healing negatively as they cause me to cancel heals at the last instant that later turn out to be needed after all, or waste mana by finishing the cast on heals that end up landing on a full health tank.
So here's the new version of the addon. It contains a mockup LibQuickHealth-2.0 implementation, and a small example addon using the new lib. Only the player's health/healthMax are tracked. The example addon displays two health bars, one for LQH2 values, one for UnitHealth. I'm pretty sure bumping the major version is appropriate, because listening to UNIT_HEALTH and UNIT_HEALTHMAX is discouraged now.
Edit: Removed attachment, see below for the latest version. =)
Here's what you need to do to watch my health while tanking Azgalor from yesterday:
1. Extract the file "YOURACCOUNTNAME\SavedVariables\QuickHealthTest.lua" to "WTF\Account\YOURACCOUNTNAME\SavedVariables\QuickHealthTest.lua".
2. Extract the folders "QuickHealthTest" and "Ace3" to your AddOns directory. (Sorry, no embeds)
3. When you start WoW, make sure you enable the addon. (disabled by default)
3. In-game enter "/script qhtreplay(19,2644)". This tells the addon to load log #19, and start replaying at event #2644.
4. Enjoy the demo of UnitHealth lags. Notice how well the addon predicts the health.
5. Optional: Create a chat frame "QuickHealthTest" to see messages of what happens.
6. To stop the replay enter "/script qhtstop()".
Here's a little script I'm using in WoWLua, maybe you find it useful.
What does that mean?
I hope you're ok with a major version bump? LibQuickHealth-2.0 or LibQuickHealth-1.1?
There's one thing I'm not sure about. When a unit frame addon uses the library, this addon needs to keep track of the GUID->unitIDs mapping. How about incorporating this into the library? The library could have two events, HealthUpdated(guid, ...) and UnitHealthUpdated(unitID, ...). Maintaining the GUID->unitIDs map would only be necessary if UnitHealthUpdated is registered.
Features:
LibQuickHealth-2.0 is a library that provides more up to date health data than the default Blizz events and functions.
In addition to the standard UNIT_HEALTH event, the library listens to combat log events (CLEU) to update health values. CLEU events fire more often than UNIT_HEALTH, and allow the library to get around the ~300ms throttling of UNIT_HEALTH updates.
The interesting CLEU events are those containing health differences (damage and heals). Normally, when UNIT_HEALTH fires, those differences add up to the value reported by Blizz's UnitHealth. But sometimes, when a unit takes damage according to the CLEU, that damage is processed *after* the next UNIT_HEALTH event. This is called "pending damage". One of the features of LibQuickHealth-2.0 (and an improvement over 1.0) is to detect pending damage, and incorporate it into the health values that are presented to players.
Limitations:
1.) Sometimes there are combat log entries like this: -6000 (damage), +5000 (heal). But what really happened on the server was that the heal occured first. If at least part of this heal was overheal, the prediction (-6000+5000 = -1000) is way off, because what happened is for example +50 heal first(4950 overheal), and then 6000 damage, for a total of +50-6000 = -5950. No AddOn could possibly prevent or predict this, but luckily these cases are very rare.
However, if you see something like this happening (tank at ~100%, then takes damage instantly followed by a heal), you should know that this heal might have been overheal. So don't cancel your heal. ;)
2.) If the maximum health changes, pending damage is completely ignored. Example: The tank is at 11001/18000, takes 9001 damage and uses Last Stand at the same time. Your client might tell you now that the tank health is 16401/23400 (+30% health and max health), because that 9k hit was not yet processed (pending damage). What the library does is: Show the tank at 11001/18000, then at 11001-9001 = 2000/18000, then at 16401/23400, because pending damage is ignored. But on the server, the tank has only 16401-9001 = 7400/23400 health!
This looks very similar to the case before: The tank takes damage, but shortly afterwards that damage is (magically) undone. Again, if you see this, don't cancel your heal!
3.) Bloodrush. There's no combat log entry for the health lost. I believe the health cost depends on the caster level and race, but I don't know the exact formula.
4.) Exceptionally large HP5 values. There are no combat log entries for HP5 ticks, which occur every 2 seconds. The library assumes that the health regen in combat is between 0 and 50 HP5, i.e. HP5 ticks between 0 and 20.
Common HP5 values: Demon skin/armor <=18 (24 talented?), Elixir of Major Fortitude 10, Enchant Boots - Vitality 4. Less common: Dreaming Glory 30 (buff from herbalism), and a few level 60 items.
So the library might get confused by two things: Small amounts of pending damage (20 or less), and Warlocks with herbalism...
Usage:
To start listening to the event, do QuickHealth.RegisterCallback(YourAddonTableHere, "HealthUpdated", HandlerMethodHere);
The callback triggers with 5 arguments: self, event, guid, health, healthMax (first two passed by the callbackhandler)
Additionaly, QuickHealth:UnitHealth(unitID) can be called to get what QuickHealth thinks is the current health.
Example:
CBH is designed to be very small and dosn't use shared code really at all. But it's dispatch method is slow and painful. I love it's implementation and all, would recommend it for just about anything. However the time sensitive nature of this addon would be messed up something bad if you used it because it's dispatching is slow, it's a wraped, loadstring'ed, meta-table xpcall. I don't know much about internals but the huristic idea says that the fewer objects used in a proccess the faster it generally will be. A simple , pairs(callback) do pcall(func, ...) end
would work just fine, and maybe even faster.
it is, but it's slow :P
In my honest, unintelligent, ignorant opinion, a custom solution / dispatcher would be faster due to the time sensitive nature of what this lib is doing. Otherwise what's the point in being .3 seconds behind in updates when you can just use the normal UNIT_HEALTH events?
Come on, wasn't the point of this lib to get around that annoyance in the event throttling from blizzard?
Btw UNIT_HEALTH throttling is ~300ms, but that doesn't mean UH fires every 300ms. It just means UH doesn't fire more often than every 300ms. Also, damage is laggier than heals (don't ask me why). I see UH lag of 600-1000ms quite often. So even if CBH:Fire() would take 2ms per call (which it doesn't), it wouldn't matter.