Thread Tools Display Modes
08-19-14, 08:19 AM   #1
MaLarsson
A Deviate Faerie Dragon
AddOn Author - Click to view addons
Join Date: Aug 2014
Posts: 13
Check if debuff exists in game

As the title says, I need help with error control.
The player types in the name of a debuff and if this debuff exists in the game then store it in the array debuffList. Code example follows:
Lua Code:
  1. local debuffList = {}
  2.  
  3. SLASH_CMD1 = '/cmd'
  4. function SlashCmdList.CMD(msg, editbox)
  5.     local command, rest = msg:match("^(%S*)%s*(.-)$")
  6.     if command == "add" then
  7.         if GetSpellInfo(rest) then -- GetSpellInfo() returns nil (false) if the spell does not exist.
  8.             table.insert(debuffList, rest)
  9.         else
  10.             print("Unknown spell")
  11.         end
  12.     end
  13. end
This code works fine in most cases but when the debuff does not share the same name as the spell this code does not work. For example; there is a debuff called weakened armor but it is applied using the spell sunder armor. If I want to add the debuff weakened armor it tells me that weakened armor is an unknow spell. Is there any way to check if a debuff exists in the game instead of checking if the spell exists in the game?
  Reply With Quote
08-19-14, 10:43 AM   #2
Lombra
A Molten Giant
 
Lombra's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 554
There is no distinction between spells and "debuffs". What's happening here is just that Sunder Armor triggers a different spell. In this case, supposedly due to the Weakened Armor effect being "normalised" these days and triggered by several abilities.
http://www.wowhead.com/spell=7386
http://www.wowhead.com/spell=113746
Also, GetSpellInfo only works with names if they are in your spellbook. If you use spell ID it will always work.
__________________
Grab your sword and fight the Horde!
  Reply With Quote
08-19-14, 11:12 AM   #3
MaLarsson
A Deviate Faerie Dragon
AddOn Author - Click to view addons
Join Date: Aug 2014
Posts: 13
ah, thanks for clarifying.
Is there any easy way to get spellID from the spellName if you do not have the spell in your spellbook?
  Reply With Quote
08-19-14, 11:18 AM   #4
zork
A Pyroguard Emberseer
 
zork's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 1,740
Well. You could do sth else. You could register UNIT_AURA to a specific event frame.

Each time UNIT_AURA triggers you can check
Lua Code:
  1. --get db via VARIABLES LOADED and saved variables
  2. local debuffList = DB.debuffList or {}
  3.  
  4. local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId = nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil
  5.  
  6. local function SearchAuras(self,event,unit)
  7.   if not debuffList then return end
  8.   for i=1,40 do
  9.     name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId = UnitDebuff(unit,i)
  10.     if not name then break end
  11.     if not debuffList[spellId] then
  12.       debuffList[spellid] = {name = name, rank = rank, icon = icon, debuffType = debuffType, spellId = spellId}
  13.     end
  14.   end
  15. end
  16.  
  17. local auraCheck = CreateFrame("Frame")
  18. auraCheck:SetScript("OnEvent",SearchAuras)
  19. auraCheck:RegisterEvent("UNIT_AURA")

All spells are spells. Some spells are auras. Some less spells are debuffs.

UnitDebuff (aka. UnitAura, HARMFUL) may deliver even more values. You can check that by doing print(UnitDebuff(unit,i)).
http://wowprogramming.com/docs/api/UnitAura
__________________
| Simple is beautiful.
| WoWI AddOns | GitHub | Zork (WoW)

"I wonder what the non-pathetic people are doing tonight?" - Rajesh Koothrappali (The Big Bang Theory)

Last edited by zork : 08-19-14 at 11:28 AM.
  Reply With Quote
08-19-14, 02:52 PM   #5
Sharparam
A Flamescale Wyrmkin
 
Sharparam's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2011
Posts: 102
I'm not sure if there's a performance benefit to declaring those local variables outside of the for loop, as they are reassigned each loop iteration anyway (although I could be wrong). There's also no need to explicitly initialize them to nil as this is the "default value" of a non-initialized variable anyway.
  Reply With Quote
08-19-14, 07:38 PM   #6
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
There's no benefit. As a matter of good practice, you should limit variables to the lowest scope in which they are needed. If they're only used inside a loop, they should only exist inside that loop.
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.
  Reply With Quote
08-20-14, 02:13 AM   #7
zork
A Pyroguard Emberseer
 
zork's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 1,740
Did not know that. Thought there may be some sort of benefit of declaring the variables only once.
__________________
| Simple is beautiful.
| WoWI AddOns | GitHub | Zork (WoW)

"I wonder what the non-pathetic people are doing tonight?" - Rajesh Koothrappali (The Big Bang Theory)
  Reply With Quote
08-20-14, 08:39 AM   #8
Resike
A Pyroguard Emberseer
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,290
Originally Posted by Phanx View Post
There's no benefit. As a matter of good practice, you should limit variables to the lowest scope in which they are needed. If they're only used inside a loop, they should only exist inside that loop.
It's not always true, keeping a local var inside the for loop:

- Create the variable
- Assign memory address to it.
- Allocate the memory for it.

- Then upvalue it with something.

However if you keep it outside the for loop, then the first 3 step is gonna run when you load the addon and only then, not recreating them at each loop, make the for loop run faster, since it only gonna overwrite whatever it can find the assigned memory address.
(Quick note with the current memory modules (DDR2-DDR3) overwriting a value is 30% faster then completely clear a memory block clean.)
However accessing a variable outside the loop also take some extra time.
You don't really make the whole code run faster, however you spread the workload into more modules, making the CPU/Memory peak usage lower.

More info in this thread:

http://www.wowinterface.com/forums/s...t=47694&page=3

Last edited by Resike : 08-20-14 at 09:19 AM.
  Reply With Quote
08-20-14, 09:23 AM   #9
Lag123
A Murloc Raider
AddOn Author - Click to view addons
Join Date: Nov 2008
Posts: 4
Best place for optimising here is

Remove the
Lua Code:
  1. if not debuffList then return end
because you know that it exist
If you really want to check if the debuffList exists do that before register the event.

And localize the UnitDebuff function outside the SearchAuras function
Lua Code:
  1. local UnitDebuff = UnitDebuff

Not huge things but a small performance boost

Lua Code:
  1. local UnitDebuff = UnitDebuff
  2.  
  3. --get db via VARIABLES LOADED and saved variables
  4. local debuffList = DB.debuffList or {}
  5.  
  6. local function SearchAuras(self,event,unit)
  7.     local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId
  8.     for i=1,40 do
  9.         name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId = UnitDebuff(unit,i)
  10.         if not name then break end
  11.         if not debuffList[spellId] then
  12.           debuffList[spellid] = {name = name, rank = rank, icon = icon, debuffType = debuffType, spellId = spellId}
  13.         end
  14.     end
  15. end
  16.  
  17. local auraCheck = CreateFrame("Frame")
  18. auraCheck:SetScript("OnEvent",SearchAuras)
  19. auraCheck:RegisterEvent("UNIT_AURA")
  Reply With Quote
08-20-14, 11:28 AM   #10
Lombra
A Molten Giant
 
Lombra's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 554
Originally Posted by Resike View Post
It's not always true, keeping a local var inside the for loop:

- Create the variable
- Assign memory address to it.
- Allocate the memory for it.

- Then upvalue it with something.

However if you keep it outside the for loop, then the first 3 step is gonna run when you load the addon and only then, not recreating them at each loop, make the for loop run faster, since it only gonna overwrite whatever it can find the assigned memory address.
(Quick note with the current memory modules (DDR2-DDR3) overwriting a value is 30% faster then completely clear a memory block clean.)
However accessing a variable outside the loop also take some extra time.
You don't really make the whole code run faster, however you spread the workload into more modules, making the CPU/Memory peak usage lower.

More info in this thread:

http://www.wowinterface.com/forums/s...t=47694&page=3
Well I don't know how this stuff works, but that doesn't seem to be true. I'm guessing it's not to simple as "replacing" the value of the memory address if the new value is much bigger, for example?

Anyway, I got the same results as in your post there, but you forgot the format that people actually use. That is, assigning the values directly instead of declaring the variables separately. Unless I did something wrong, that was a lot faster. (302 vs 546 ms at 100 000 iterations)

Edit: Yup. Upvalues are actually significantly slower.
Code:
local function func()
	return 0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
end

do
	local time = debugprofilestop()

	local q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l
	for i = 1, 1e6 do
		q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l = func()
	end

	print(debugprofilestop() - time)
end

do
	local time = debugprofilestop()

	for i = 1, 1e6 do
		local q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l = func()
	end

	print(debugprofilestop() - time)
end
235 vs. 161 ms.
__________________
Grab your sword and fight the Horde!

Last edited by Lombra : 08-20-14 at 11:38 AM.
  Reply With Quote
08-20-14, 12:30 PM   #11
zork
A Pyroguard Emberseer
 
zork's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 1,740
Lua Code:
  1. local function UnitDebug(unit,index)
  2.   return "name", "rank", "icon", 1, "debuffType", 1, 1, "unitCaster", true, true, 1
  3. end
  4.  
  5. local d = debugprofilestop
  6. local function OnEvent()
  7.   do
  8.     local time = d()
  9.     local debuffList = {}
  10.     local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId
  11.     for index=1,1e5 do
  12.       for i=1,40 do
  13.         name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId = UnitDebug("player",i)
  14.         debuffList[spellId] = {name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId}
  15.       end
  16.     end
  17.     print(d() - time)
  18.   end
  19.   print("~~~~~~~~~~")
  20.   do
  21.     local time = d()
  22.     local debuffList = {}
  23.       for index=1,1e5 do
  24.       for i=1,40 do
  25.         local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId = UnitDebug("player",i)
  26.         debuffList[spellId] = {name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId}
  27.       end
  28.     end
  29.     print(d() - time)
  30.   end
  31.  
  32. end
  33.  
  34. local f = CreateFrame("Frame")
  35. f:SetScript("OnEvent",OnEvent)
  36. f:RegisterEvent("PLAYER_LOGIN")

The lower one is faster by ~4%.
__________________
| Simple is beautiful.
| WoWI AddOns | GitHub | Zork (WoW)

"I wonder what the non-pathetic people are doing tonight?" - Rajesh Koothrappali (The Big Bang Theory)

Last edited by zork : 08-20-14 at 12:34 PM.
  Reply With Quote
08-20-14, 12:46 PM   #12
Lombra
A Molten Giant
 
Lombra's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 554
Yeah, but note that most of the execution time goes into creating the tables there. Seems to account for roughly 90% of the time, making the difference relatively smaller.
__________________
Grab your sword and fight the Horde!
  Reply With Quote
08-20-14, 01:42 PM   #13
Resike
A Pyroguard Emberseer
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,290
Originally Posted by Lombra View Post
Well I don't know how this stuff works, but that doesn't seem to be true. I'm guessing it's not to simple as "replacing" the value of the memory address if the new value is much bigger, for example?

Anyway, I got the same results as in your post there, but you forgot the format that people actually use. That is, assigning the values directly instead of declaring the variables separately. Unless I did something wrong, that was a lot faster. (302 vs 546 ms at 100 000 iterations)

Edit: Yup. Upvalues are actually significantly slower.
Code:
local function func()
	return 0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
end

do
	local time = debugprofilestop()

	local q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l
	for i = 1, 1e6 do
		q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l = func()
	end

	print(debugprofilestop() - time)
end

do
	local time = debugprofilestop()

	for i = 1, 1e6 do
		local q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l = func()
	end

	print(debugprofilestop() - time)
end
235 vs. 161 ms.
Intresting, i'm pretty sure i save some improvementss on frequently called cycles with upvaules, at least if you can belive in the actual cpu usage for lua/wow api.
Because if this is true, then whats the point of upvaluing global functions in the main chunk?
  Reply With Quote
08-20-14, 01:56 PM   #14
Lombra
A Molten Giant
 
Lombra's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 554
Well, in that case you're accessing variables rather than setting them. Upvalues saves you having to do a global lookup. When you're setting a variable there's "nothing" to lookup, I guess. Not sure what a more technical explanation would be.
__________________
Grab your sword and fight the Horde!
  Reply With Quote
08-20-14, 08:02 PM   #15
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Upvaluing a function indeed only saves you a global lookup. This is why I generally recommend against blindly upvaluing every global function you call in your addon, and only upvaluing the ones you're calling with extreme frequency, such as inside an OnUpdate script or CLEU handler. For more infrequent calls, the difference is hardly more than theoretical, so it's not worth the extra line of code to upvalue it.

Also, what's true in other Lua environments is not always true in the WoW Lua environment, as Blizzard has done a bit of customization. When in doubt, go benchmark it.
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.
  Reply With Quote
08-21-14, 02:12 AM   #16
Rilgamon
Premium Member
 
Rilgamon's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 822
235 vs. 161 ms.
With a difference so small I'd guess the time garbage collect needs will tip toward the first
__________________
The cataclysm broke the world ... and the pandas could not fix it!
  Reply With Quote
08-21-14, 02:15 AM   #17
zork
A Pyroguard Emberseer
 
zork's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 1,740
Well can't you test that aswell by calling collectgarbage() or collectgarbage("count") and checking for timestamps?
__________________
| Simple is beautiful.
| WoWI AddOns | GitHub | Zork (WoW)

"I wonder what the non-pathetic people are doing tonight?" - Rajesh Koothrappali (The Big Bang Theory)
  Reply With Quote
08-21-14, 02:22 AM   #18
Rilgamon
Premium Member
 
Rilgamon's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 822
I just tried to be funny
__________________
The cataclysm broke the world ... and the pandas could not fix it!
  Reply With Quote
08-21-14, 02:02 PM   #19
Sharparam
A Flamescale Wyrmkin
 
Sharparam's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2011
Posts: 102
With garbage collection disabled:

lua Code:
  1. collectgarbage("stop")
  2.  
  3. local clock = os.clock
  4.  
  5. local function func()
  6.     return 0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9
  7. end
  8.  
  9. do
  10.     local time = clock()
  11.  
  12.     local q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l
  13.     for i = 1, 1e6 do
  14.         q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l = func()
  15.     end
  16.  
  17.     print(clock() - time, collectgarbage("count"))
  18. end
  19.  
  20. do
  21.     local time = clock()
  22.  
  23.     for i = 1, 1e6 do
  24.         local q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l = func()
  25.     end
  26.  
  27.     print(clock() - time, collectgarbage("count"))
  28. end
  29.  
  30. collectgarbage("restart")

Code:
(Run #1)
0.277   24.7861328125
0.157   24.8369140625

(Run #2)
0.285   24.7861328125
0.155   24.8369140625

(Run #3)
0.281   24.7861328125
0.16    24.8369140625

(Average)
0.281
0.15733333333333
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Check if debuff exists in game

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