List of commits:
Subject Hash Author Date (UTC)
fix(clearcasting): Reduce update and redraw frequency 0b3d789be1673ef4e7a6212e4131ff4b13f571bd Vladyslav Bondarenko 2021-12-25 20:44:24
fix(clearcasting): Reduce update frequency 7bf094779eef383805e43a711ac5339a7677c860 Vladyslav Bondarenko 2021-12-19 10:30:17
fix(clearcasting)!: Reduce redundant updates e93ca1628e362276ef20d10bbc0b4314d8bb0ae9 Vladyslav Bondarenko 2021-11-09 07:18:49
fix(clearcasting): Lower priority of debuff type 5e74f3bb6adcf5ee9644a08f839ec6fdf7f59027 Vladyslav Bondarenko 2021-11-07 14:27:37
feat(clearcasting): Remove individual class profiles c7b4d8cf1ad3885e8f0bd84f33082a29330f1f18 Vladyslav Bondarenko 2021-11-05 18:41:10
fix(clearcasting): Correct subset button order when first allocated f3b52da4b40e436e7dc5fc5bda8c4cdbf2c3f4a7 Vladyslav Bondarenko 2021-11-05 13:15:09
fix(clearcasting): Subset button clickable and updates slower ec4768e7b925e52126140788fe866070ee3796c3 Vladyslav Bondarenko 2021-11-05 13:05:13
fix(clearcasting): Improve subset buttons readabilitiy a86de677ad80a16372b76c67d1f8baa457f72faa Vladyslav Bondarenko 2021-11-04 22:23:53
fix(clearcasting)!: Export createSubset correctly e4a0fdb48ac3e3e63d7e763460fb6ca2d95a1f9f Vladyslav Bondarenko 2021-11-04 22:02:35
feat(choir): Render debuff type color for subset cb13282dcb4c42732eccf46821d9ff49e6f8bdff Vladyslav Bondarenko 2021-11-04 20:12:49
fix(choir)!: Subset auras update in combat 081493e96ee7c72371f8770d8bbdc9b108db53ac Vladyslav Bondarenko 2021-11-04 19:02:16
feat(choir)!: Add alternative section creation 1aac7826a961509021679db26b95071edba77ad8 Vladyslav Bondarenko 2021-11-04 17:36:05
feat(clearcasting): Add death knight indicators 5c42814210b355e6de601faaf8e173222995cfa6 Vladyslav Bondarenko 2021-10-30 12:23:43
feat!: Show spell tooltip on indicator mouseover dd69f46f1522f49ce942d8e0b3756c609471be7a Vladyslav Bondarenko 2021-10-13 01:12:08
feat!: Indicator grouping feature df7e0579a51388dba73bc5d92b3d04369bb0e426 Vladyslav Bondarenko 2021-10-13 00:17:28
feat!: Redo the core functionality 085af1bc403b3a9d2d4b99ec460d67889239fabd Vladyslav Bondarenko 2021-07-09 11:15:31
feat: Track additional auras 958e52f9808d64ebb816663d2aa22a4eb39a2068 Vladyslav Bondarenko 2021-01-22 20:28:13
fix!: No longer crash on unknown spell 65061085f0f55b75910c9ed4d8249b1e8e30f9ee Vladyslav Bondarenko 2020-12-11 17:31:16
fix: Render indicators properly on first login bf102cc749cc23d3769a82f10fe93a05afc5277f Vladyslav Bondarenko 2020-12-11 14:25:11
feat: Add Serendipity and Surge of Light indicators ba1d0f32a4b8f4f5d4e2730f0a4e409e23b52974 Vladyslav Bondarenko 2020-12-10 16:19:55
Commit 0b3d789be1673ef4e7a6212e4131ff4b13f571bd - fix(clearcasting): Reduce update and redraw frequency
Reduce the quantity of clearcasting button update and redraw per second
to improve performance. Specifically remove redundant updates and more
importantly significantly simplify sorting algorithm. The sorting
algorithm is required to arrange the indicators (buttons) in a
predictable order at runtime.

In player groups of size thirty and larger the performance issues
persist.
Author: Vladyslav Bondarenko
Author date (UTC): 2021-12-25 20:44
Committer name: Vladyslav Bondarenko
Committer date (UTC): 2021-12-25 20:44
Parent(s): 7bf094779eef383805e43a711ac5339a7677c860
Signing key:
Tree: 91276be35d1ec8b8907b77d41035f3005572ba56
File Lines added Lines deleted
clearcasting.lua 49 149
File clearcasting.lua changed (mode: 100644) (index 5edec42..d9d287f)
... ... FIXME Cancel aura on right click.
6 6 @script clearcasting @script clearcasting
7 7 ]] ]]
8 8
9 local strtrim = _G['strtrim']
10 local GameTooltip = _G['GameTooltip']
11 local UnitAura = _G['UnitAura']
12 local GetTime = _G['GetTime']
13 local UnitExists = _G['UnitExists']
14 local date = _G['date']
15 local DebuffTypeColor = _G['DebuffTypeColor']
16 local UIParent = _G['UIParent']
17 local CreateFrame = _G['CreateFrame']
18 local BuffFrame = _G['BuffFrame']
19 local GetLocale = _G['GetLocale']
20
9 21 local function debug(...) local function debug(...)
10 22 if true == ClearcastingDebugFlag then if true == ClearcastingDebugFlag then
11 23 print(date('%X'), '[Clearcasting]: ', ...) print(date('%X'), '[Clearcasting]: ', ...)
 
... ... local function applySubsetButtonDuration(f, duration, expirationInstance, stackQ
58 70 f.stackQuantity = stackQuantity f.stackQuantity = stackQuantity
59 71 end end
60 72
61 local function unpackAuraTable(auraTable)
62 assert(auraTable ~= nil)
63 assert("table" == type(auraTable))
64
65 local spellName = auraTable[1]
66 local spellRank = auraTable[2]
67 local pictureFile = auraTable[3]
68 local stackQuantity = auraTable[4]
69 local category = auraTable[5]
70 local durationSec = auraTable[6]
71 local expirationInstance = auraTable[7]
72 local casterUnitDesignation = auraTable[8]
73 local stealableFlag = auraTable[9]
74 local consolidationFlag = auraTable[10]
75 local spellId = auraTable[11]
76 --[[ FIXME There is no 12th return value in UnitAura function.
77 -- This value is added by the script in requestUnitAuraTable function.
78 -- The purpose of the value is to enable game tooltip.
79 -- This is a source of potential compatibility issues,
80 -- especially when porting this add-on to newer client versions.
81 -- For now it works.]]--
82 local unitAuraIndex = auraTable[12]
83
84 return spellName, spellRank, pictureFile, stackQuantity, category, durationSec, expirationInstance,
85 casterUnitDesignation, stealableFlag, consolidationFlag, spellId, unitAuraIndex
86 end
73 local function getAuraWeightSimple(now, ...)
74 local expirationInstance = select(7, ...)
75 local casterUnitDesignation = select(8, ...)
87 76
88 local function getAuraWeight(
89 eitherAuraTableOrSpellName,
90 spellRank,
91 pictureFile,
92 stackQuantity,
93 category,
94 durationSec,
95 expirationInstance,
96 casterUnitDesignation,
97 stealableFlag,
98 consolidationFlag,
99 spellId)
100
101 local spellName
102 if 'table' == type(eitherAuraTableOrSpellName) then
103 local auraTable = eitherAuraTableOrSpellName
104
105 spellName,
106 spellRank,
107 pictureFile,
108 stackQuantity,
109 category,
110 durationSec,
111 expirationInstance,
112 casterUnitDesignation,
113 stealableFlag,
114 consolidationFlag,
115 spellId = unpackAuraTable(auraTable)
116 elseif 'string' == type(eitherAuraTableOrSpellName) then
117 spellName = eitherAuraTableOrSpellName
77 local w = 0
78 if expirationInstance > 0 then
79 w = w + (expirationInstance - now)
118 80 else else
119 debug('invalid argument', eitherAuraTableOrSpellName)
120 return
121 end
122
123 assert (spellName ~= nil)
124 assert (nil == spellRank or 'string' == type(spellRank))
125 assert (pictureFile ~= nil)
126 assert (stackQuantity ~= nil)
127 assert (nil == stealableFlag or 1 == stealableFlag or 0 == stealableFlag)
128 assert (nil == consolidationFlag or 1 == consolidationFlag or 0 == consolidationFlag)
129 assert (spellId ~= nil)
130
131 local casterWeight = 0
132 if 'player' == casterUnitDesignation then
133 casterWeight = 1
81 w = w + 90000
134 82 end end
135
136 local categoryMap = {['Magic'] = 4, ['Poison'] = 3, ['Disease'] = 2, ['Curse'] = 1}
137 local categoryWeight = 0
138 if category then
139 categoryWeight = categoryMap[category] or 0
83 if 'player' ~= casterUnitDesignation then
84 w = w + 100000
140 85 end end
141
142 local maxDurationWeight = 999
143 local durationWeight
144 if expirationInstance and expirationInstance > 0 and durationSec and durationSec > 0 then
145 durationWeight = maxDurationWeight / (expirationInstance - GetTime())
146 elseif durationSec and durationSec == 0 then
147 durationWeight = 0
148 else
149 durationWeight = maxDurationWeight
150 end
151 durationWeight = math.ceil(durationWeight)
152
153 local weight = 0
154 weight = weight + 10000 * casterWeight
155 weight = weight + 10 * durationWeight
156 weight = weight + 1 * categoryWeight
157
158 return weight
159 end
160
161
162 local function sortUnitAuraTable(a, b)
163 return getAuraWeight(a) > getAuraWeight(b)
86 return w
164 87 end end
165 88
166 89 local function requestUnitAuraIndexTable(unitDesignation, filterDescriptor) local function requestUnitAuraIndexTable(unitDesignation, filterDescriptor)
 
... ... local function requestUnitAuraIndexTable(unitDesignation, filterDescriptor)
189 112 e[i] = i e[i] = i
190 113 end end
191 114
115 local now = GetTime()
192 116 table.sort(e, function(a, b) table.sort(e, function(a, b)
193 local aw = getAuraWeight(UnitAura(unitDesignation, a, filterDescriptor))
194 local bw = getAuraWeight(UnitAura(unitDesignation, b, filterDescriptor))
195 return aw > bw
117 local aw = getAuraWeightSimple(now, UnitAura(unitDesignation, a, filterDescriptor))
118 local bw = getAuraWeightSimple(now, UnitAura(unitDesignation, b, filterDescriptor))
119 return aw < bw
196 120 end) end)
197 121
198 122 assert (e ~= nil) assert (e ~= nil)
199 assert ('table' == type(e))
200 return e
201 end
202
203 local function requestUnitAuraTable(unitDesignation, filterDescriptor)
204 assert (unitDesignation ~= nil)
205 assert ('string' == type(unitDesignation))
206 unitDesignation = strtrim(unitDesignation)
207 assert (string.len(unitDesignation) >= 4)
208 assert (string.len(unitDesignation) <= 64)
209
210 assert (filterDescriptor ~= nil)
211 assert ('string' == type(filterDescriptor))
212 filterDescriptor = strtrim(filterDescriptor)
213 assert (string.len(filterDescriptor) >= 4)
214 assert (string.len(filterDescriptor) <= 128)
215
216 local index = 0
217 local q = 0
218 local e = {}
219 local maxAuraQuantityPerUnit = 144
220 while (index < maxAuraQuantityPerUnit) do
221 index = index + 1
222 local name, rank, pictureFile, stackQuantity, category,
223 duration, expirationInstance,
224 caster, stealableFlag, consolidateFlag, id = UnitAura(unitDesignation, index, filterDescriptor)
225 if name then
226 local auraTable = {name, rank, pictureFile, stackQuantity, category,
227 duration, expirationInstance,
228 caster, stealableFlag, consolidateFlag, id, index}
229 q = q + 1
230 e[q] = auraTable
231 else
232 break
233 end
234 end
235 table.sort(e, sortUnitAuraTable)
236
237 123 return e return e
238 124 end end
239 125
 
... ... local function unapplySubsetButtonSpell(subsetButton, unitDesignation, filterDes
309 195 assert (unitDesignation ~= nil) assert (unitDesignation ~= nil)
310 196 assert (filterDescriptor ~= nil) assert (filterDescriptor ~= nil)
311 197
312 applySubsetButtonArtwork(subsetButton, "Interface\\Icons\\spell_nature_wispsplode")
198 --[[applySubsetButtonArtwork(subsetButton, "Interface\\Icons\\spell_nature_wispsplode")]]--
313 199
314 200 subsetButton.unit = unitDesignation subsetButton.unit = unitDesignation
315 201 subsetButton.filter = filterDescriptor subsetButton.filter = filterDescriptor
 
... ... local function applySubsetButtonSpell(subsetButton, unitDesignation, filterDescr
349 235 applySubsetButtonBackground(subsetButton, category) applySubsetButtonBackground(subsetButton, category)
350 236 applySubsetButtonTextColor(subsetButton, casterUnitDesignation) applySubsetButtonTextColor(subsetButton, casterUnitDesignation)
351 237
352 subsetButton.unit = unitDesignation
353 238 subsetButton.spell = spellName subsetButton.spell = spellName
354 239 subsetButton.index = unitAuraIndex subsetButton.index = unitAuraIndex
240
241 if subsetButton:IsShown() then
242 return
243 end
244
245 subsetButton.unit = unitDesignation
355 246 subsetButton.filter = filterDescriptor subsetButton.filter = filterDescriptor
356 247
357 248 subsetButton:SetScript('OnUpdate', subsetButtonUpdateProcessor) subsetButton:SetScript('OnUpdate', subsetButtonUpdateProcessor)
 
... ... local function applySubsetButtonSpell(subsetButton, unitDesignation, filterDescr
359 250 end end
360 251
361 252
362 local function subsetEventProcessorFactory(t)
363
364 return function(subsetFrame)
253 local function subsetEventProcessor(subsetFrame, eventCategory, targetUnit)
365 254 assert (subsetFrame ~= nil) assert (subsetFrame ~= nil)
366 255
367 256 local unitDesignation = subsetFrame.unit local unitDesignation = subsetFrame.unit
 
... ... return function(subsetFrame)
371 260 assert (string.len(unitDesignation) >= 4) assert (string.len(unitDesignation) >= 4)
372 261 assert (string.len(unitDesignation) <= 64) assert (string.len(unitDesignation) <= 64)
373 262
263 if targetUnit and unitDesignation ~= targetUnit then
264 return
265 end
266
374 267 if UnitExists(unitDesignation) then if UnitExists(unitDesignation) then
375 268 subsetFrame:Show() subsetFrame:Show()
376 269 else else
 
... ... return function(subsetFrame)
388 281 local e = requestUnitAuraIndexTable(unitDesignation, filterDescriptor) local e = requestUnitAuraIndexTable(unitDesignation, filterDescriptor)
389 282
390 283 local i = 0 local i = 0
391 --[[local t = {subsetFrame:GetChildren()}]]--
284 local t = subsetFrame.buttonSet or {subsetFrame:GetChildren()}
285 assert (t ~= nil)
286 assert ('table' == type(t))
392 287 while (i < math.min(#e, #t)) do while (i < math.min(#e, #t)) do
393 288 i = i + 1 i = i + 1
394 289
 
... ... return function(subsetFrame)
413 308 end end
414 309 end end
415 310
416 end
417
418 311 local function createSubsetButtonArtwork(subsetButton) local function createSubsetButtonArtwork(subsetButton)
419 312 assert (subsetButton ~= nil) assert (subsetButton ~= nil)
420 313
 
... ... local function createSubset(parentFrame, frameDesignation, unitDesignation, filt
648 541
649 542 subsetFrame.unit = unitDesignation subsetFrame.unit = unitDesignation
650 543 subsetFrame.filter = filterDescriptor subsetFrame.filter = filterDescriptor
544 subsetFrame.buttonSet = t
545
651 546 subsetFrame:RegisterEvent('PARTY_CONVERTED_TO_RAID') subsetFrame:RegisterEvent('PARTY_CONVERTED_TO_RAID')
652 547 subsetFrame:RegisterEvent('PARTY_MEMBERS_CHANGED') subsetFrame:RegisterEvent('PARTY_MEMBERS_CHANGED')
653 548 --subsetFrame:RegisterEvent('PLAYER_FOCUS_CHANGED') --subsetFrame:RegisterEvent('PLAYER_FOCUS_CHANGED')
 
... ... local function createSubset(parentFrame, frameDesignation, unitDesignation, filt
658 553 subsetFrame:RegisterEvent('UNIT_AURA') subsetFrame:RegisterEvent('UNIT_AURA')
659 554 --subsetFrame:RegisterEvent('UNIT_HEALTH') --subsetFrame:RegisterEvent('UNIT_HEALTH')
660 555 --subsetFrame:RegisterEvent('UPDATE_BATTLEFIELD_SCORE') --subsetFrame:RegisterEvent('UPDATE_BATTLEFIELD_SCORE')
661 local subsetEventProcessor = subsetEventProcessorFactory(t)
662 556 subsetFrame:SetScript('OnEvent', subsetEventProcessor) subsetFrame:SetScript('OnEvent', subsetEventProcessor)
663 557 subsetEventProcessor(subsetFrame) subsetEventProcessor(subsetFrame)
664 558
 
... ... local function init(rootFrame)
703 597 rootFrame.createSubset = function(...) rootFrame.createSubset = function(...)
704 598 return createSubset(...) return createSubset(...)
705 599 end end
600
601 rootFrame.getAuraWeight = function(...)
602 return getAuraWeightSimple(GetTime(), ...)
603 end
604
605 debug('init')
706 606 end end
707 607
708 608 local function main() local function main()
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/vrtc/clearcasting

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/vrtc/clearcasting

Clone this repository using git:
git clone git://git.rocketgit.com/user/vrtc/clearcasting

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main