vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2 Name: RosterLib-2.0
3 Revision: $Revision: 8205 $
4 X-ReleaseDate: "$Date: 2006-08-10 08:55:29 +0200 (Thu, 10 Aug 2006) $
5 Author: Maia (maia.proudmoore@gmail.com)
6 Website: http://wiki.wowace.com/index.php/RosterLib-2.0
7 Documentation: http://wiki.wowace.com/index.php/RosterLib-2.0_API_Documentation
8 SVN: http://svn.wowace.com/root/trunk/RosterLib-2.0/
9 Description: party/raid roster management
10 Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0
11 ]]
12  
13 local MAJOR_VERSION = "RosterLib-2.0"
14 local MINOR_VERSION = "$Revision: 8205 $"
15  
16 if not AceLibrary then error(vmajor .. " requires AceLibrary.") end
17 if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
18 if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
19 if not AceLibrary:HasInstance("AceEvent-2.0") then error(MAJOR_VERSION .. " requires AceEvent-2.0") end
20  
21 local Compost = AceLibrary:HasInstance("Compost-2.0") and AceLibrary("Compost-2.0")
22 local rosterCount, addonCount, standby
23 local updatedUnits = Compost and Compost:Acquire() or {}
24 local RosterLib = {}
25  
26 AceLibrary("AceEvent-2.0"):embed(RosterLib)
27  
28 ------------------------------------------------
29 -- activate, enable, disable
30 ------------------------------------------------
31  
32 local function print(text)
33 DEFAULT_CHAT_FRAME:AddMessage(text)
34 end
35  
36 local function activate(self, oldLib, oldDeactivate)
37 RosterLib = self
38 if oldLib then
39 self.registry = oldLib.registry
40 end
41 if not self.registry then
42 self.registry = {}
43 end
44 if oldDeactivate then
45 oldDeactivate(oldLib)
46 end
47 addonCount = 0
48 end
49  
50  
51 function RosterLib:Enable()
52 addonCount = addonCount + 1
53 if addonCount > 1 then return end
54 if AceLibrary("AceEvent-2.0"):IsFullyInitialized() or standby then
55 RosterLib:AceEvent_FullyInitialized()
56 else
57 self:RegisterEvent("AceEvent_FullyInitialized")
58 end
59 standby = false
60 self.roster = Compost and Compost:Acquire() or {}
61 rosterCount = 0
62 end
63  
64  
65 function RosterLib:Disable()
66 addonCount = addonCount - 1
67 if addonCount > 0 then return end
68 self:TriggerEvent("RosterLib_Disabled")
69 self:UnregisterAllEvents()
70 standby = true
71 rosterAvailable = false
72 for name in pairs(self.roster) do
73 if Compost then Compost:Reclaim(self.roster[name]) end
74 self.roster[name] = nil
75 end
76 rosterCount = 0
77 end
78  
79 ------------------------------------------------
80 -- Internal functions
81 ------------------------------------------------
82  
83 function RosterLib:AceEvent_FullyInitialized()
84 self:TriggerEvent("RosterLib_Enabled")
85 self:RegisterEvent("RAID_ROSTER_UPDATE","UpdateRoster")
86 self:RegisterEvent("PARTY_MEMBERS_CHANGED","UpdateRoster")
87 self:RegisterEvent("UNIT_PET","UpdateRoster")
88 self:CheckRoster()
89 end
90  
91 --[[
92  
93 roster checks:
94  
95 1. AceEvent_FullyInitialized() is triggered when data *should* be available. It will
96 register party/raid/pet events as we don't want then to fire before.
97 Also, it will trigger CheckRoster().
98  
99 2. CheckRoster() will compare the roster table with the currently available data. If
100 there's a difference, it will schedule UpdateRoster() with a delay of 0.5sec.
101  
102 3. UpdateRoster() will create/update the roster. To see if everything worked fine,
103 it will call CheckRoster() when it's done, this time with a silent flag.
104  
105 4. Now the saved data should be valid. If it is, it'll trigger the event
106 RosterLib_RosterChanged your addon can listen to. It'll pass a table with the unit
107 names that have changed as first argument. If not (server lag?) it'll send a
108 RosterLib_PendingRefresh event in case the roster was valid before (to not spam
109 this event when we're waiting for the server)
110  
111 5. WOW events that are related to roster updates directly trigger UpdateRoster().
112  
113 ]]
114  
115 local function GetName(unit)
116 if not UnitExists(unit) then return nil end
117 local n = UnitName(unit)
118 if n and n ~= UNKNOWNOBJECT and n ~= UKNOWNBEING then return n end
119 end
120  
121  
122 local function UnitIterator()
123 local pmem, rmem = GetNumPartyMembers(), GetNumRaidMembers()
124 local playersent = false
125 local petsent = false
126 local unitcount = 1
127 local petcount = 1
128 local unit, name
129 return function()
130 -- STEP 1: pet
131 if not petsent then
132 petsent = true
133 if rmem == 0 then
134 unit = "pet"
135 name = GetName(unit)
136 if name and not UnitIsCharmed(unit) then
137 return unit, name
138 end
139 end
140 end
141 -- STEP 2: player
142 if not playersent then
143 playersent = true
144 if rmem == 0 then
145 unit = "player"
146 name = GetName(unit)
147 if name then
148 return unit, name
149 end
150 end
151 end
152 -- STEP 3: raid units
153 if rmem > 0 then
154 -- STEP 3a: pet units
155 for i = petcount, rmem do
156 unit = string.format("raidpet%d", i)
157 petcount = petcount + 1
158 name = GetName(unit)
159 if name and not UnitIsCharmed(unit) then
160 return unit, name
161 end
162 end
163 -- STEP 3b: player units
164 for i = unitcount, rmem do
165 unit = string.format("raid%d", i)
166 unitcount = unitcount + 1
167 name = GetName(unit)
168 if name then
169 return unit, name
170 end
171 end
172 -- STEP 4: party units
173 elseif pmem > 0 then
174 -- STEP 3a: pet units
175 for i = petcount, pmem do
176 unit = string.format("partypet%d", i)
177 petcount = petcount + 1
178 name = GetName(unit)
179 if name and not UnitIsCharmed(unit) then
180 return unit, name
181 end
182 end
183 -- STEP 3b: player units
184 for i = unitcount, pmem do
185 unit = string.format("party%d", i)
186 unitcount = unitcount + 1
187 name = GetName(unit)
188 if name then
189 return unit, name
190 end
191 end
192 end
193 end
194 end
195  
196  
197  
198 local function RosterValid(silent)
199 local mem = 0
200 local n, u
201 -- STEP 1: count how many units the roster should have
202 -- note: if the player has joined a raid but doesnt have a raid id yet
203 -- (can take a second or two), the count will be one less.
204 local testRoster = Compost and Compost:Acquire() or {}
205 for u,n in UnitIterator(true) do
206 testRoster[n] = true
207 end
208 for i in testRoster do
209 mem = mem + 1
210 end
211 if Compost then Compost:Reclaim(testRoster) end
212 if mem ~= rosterCount then
213 if rosterAvailable then
214 print(string.format("RosterLib: count mismatch: roster:%d members:%d (TELL MAIA)", rosterCount, mem))
215 end
216 return false
217 end
218 -- STEP 2: compare unit names
219 for n in pairs(RosterLib.roster) do
220 u = RosterLib.roster[n]
221 if n ~= UnitName(u.unitid) then
222 if rosterAvailable then
223 print(string.format("name mismatch: name:%s, UnitName(%s):%q (TELL MAIA)", n or "nil", u.unitid or "nil", UnitName(u.unitid) or "nil"))
224 end
225 return false
226 end
227 end
228 return true
229 end
230  
231  
232 function RosterLib:CheckRoster(silent)
233 local wasAvailable = rosterAvailable
234 if RosterValid(silent) then
235 rosterAvailable = true
236 if silent then
237 local cnt = 0
238 for name in pairs(updatedUnits) do cnt = cnt + 1 end
239 if cnt > 0 then
240 self:TriggerEvent("RosterLib_RosterChanged", updatedUnits)
241 for name in pairs(updatedUnits) do
242 local u = updatedUnits[name]
243 self:TriggerEvent("RosterLib_UnitChanged", u.unitid, u.name, u.class, u.subgroup, u.rank, u.oldname, u.oldunitid, u.oldclass, u.oldsubgroup, u.oldrank)
244 if Compost then Compost:Reclaim(updatedUnits[name]) end
245 updatedUnits[name] = nil
246 end
247 end
248 end
249 else
250 rosterAvailable = false
251 if wasAvailable then
252 print("RosterLib_PendingRefresh")
253 self:TriggerEvent("RosterLib_PendingRefresh")
254 end
255 self:ScheduleEvent(self.UpdateRoster, 0.5, self) --what do we need to change here to convert it to a local schedule?
256 end
257 end
258  
259  
260 function RosterLib:UpdateRoster()
261 local unitid, name
262 -- print("RosterLib:UpdateRoster")
263 -- STEP 1: copy the roster to be able to compare it later.
264 local oldRoster = Compost and Compost:Acquire() or {}
265 for name in self.roster do
266 oldRoster[name] = Compost and Compost:Acquire() or {}
267 oldRoster[name].name = self.roster[name].name
268 oldRoster[name].unitid = self.roster[name].unitid
269 oldRoster[name].class = self.roster[name].class
270 oldRoster[name].rank = self.roster[name].rank
271 oldRoster[name].subgroup = self.roster[name].subgroup
272 end
273 -- STEP 2: clear the .unitids, as we're replacing them and removing old units later
274 for name in pairs(self.roster) do self.roster[name].unitid = nil end
275 -- STEP 3: add all units and see if something has changed.
276 for unitid, name in UnitIterator(true) do
277 -- object
278 if not self.roster[name] then
279 self.roster[name] = Compost and Compost:Acquire() or {}
280 end
281 -- name
282 self.roster[name].name = name
283 -- unitid
284 self.roster[name].unitid = unitid
285 -- class
286 if string.find(unitid,"pet") then
287 self.roster[name].class = "PET"
288 else
289 _,self.roster[name].class = UnitClass(unitid)
290 end
291 -- subgroup and rank
292 if GetNumRaidMembers() > 0 then
293 local _,_,num = string.find(unitid, "(%d+)")
294 _,self.roster[name].rank,self.roster[name].subgroup = GetRaidRosterInfo(num)
295 else
296 self.roster[name].subgroup = 1
297 self.roster[name].rank = 0
298 end
299 -- compare data
300 if not oldRoster[name]
301 or self.roster[name].name ~= oldRoster[name].name
302 or self.roster[name].unitid ~= oldRoster[name].unitid
303 or self.roster[name].class ~= oldRoster[name].class
304 or self.roster[name].subgroup ~= oldRoster[name].subgroup
305 or self.roster[name].rank ~= oldRoster[name].rank
306 then
307 updatedUnits[name] = Compost and Compost:Acquire() or {}
308 updatedUnits[name].oldname = (oldRoster[name] and oldRoster[name].name) or nil
309 updatedUnits[name].oldunitid = (oldRoster[name] and oldRoster[name].unitid) or nil
310 updatedUnits[name].oldclass = (oldRoster[name] and oldRoster[name].class) or nil
311 updatedUnits[name].oldsubgroup = (oldRoster[name] and oldRoster[name].subgroup) or nil
312 updatedUnits[name].oldrank = (oldRoster[name] and oldRoster[name].rank) or nil
313 updatedUnits[name].name = self.roster[name].name
314 updatedUnits[name].unitid = self.roster[name].unitid
315 updatedUnits[name].class = self.roster[name].class
316 updatedUnits[name].subgroup = self.roster[name].subgroup
317 updatedUnits[name].rank = self.roster[name].rank
318 end
319 end
320 -- STEP 4: now delete the old units in roster that have left your group
321 for name in pairs(self.roster) do
322 if not self.roster[name].unitid then
323 if Compost then Compost:Reclaim(self.roster[name]) end
324 self.roster[name] = nil
325 updatedUnits[name] = Compost and Compost:Acquire() or {}
326 updatedUnits[name].oldname = oldRoster[name].name
327 updatedUnits[name].oldunitid = oldRoster[name].unitid
328 updatedUnits[name].oldclass = oldRoster[name].class
329 updatedUnits[name].oldsubgroup = oldRoster[name].subgroup
330 updatedUnits[name].oldrank = oldRoster[name].rank
331 end
332 end
333 -- STEP 5: save the number of units, we need that in RosterValid()
334 rosterCount = 0
335 for name in pairs(self.roster) do rosterCount = rosterCount + 1 end
336 -- STEP 6: clear oldRoster
337 if Compost then Compost:Reclaim(oldRoster, 1) end
338 -- STEP 6: we're done. Hopefully.
339 self:CheckRoster(true)
340 end
341  
342  
343 ------------------------------------------------
344 -- API
345 ------------------------------------------------
346  
347 function RosterLib:GetUnitIDFromName(name)
348 if rosterAvailable and self.roster[name] then
349 return self.roster[name].unitid
350 else
351 return nil
352 end
353 end
354  
355  
356 function RosterLib:GetUnitIDFromUnit(unit)
357 local name = UnitName(unit)
358 if rosterAvailable and name and self.roster[name] then
359 return self.roster[name].unitid
360 else
361 return nil
362 end
363 end
364  
365  
366 function RosterLib:GetUnitObjectFromName(name)
367 if rosterAvailable and self.roster[name] then
368 return self.roster[name]
369 else
370 return nil
371 end
372 end
373  
374  
375 function RosterLib:GetUnitObjectFromUnit(unit)
376 local name = UnitName(unit)
377 if rosterAvailable and self.roster[name] then
378 return self.roster[name]
379 else
380 return nil
381 end
382 end
383  
384  
385 function RosterLib:IterateRoster(pets)
386 local key
387 return function()
388 key = next(self.roster, key)
389 if key and (pets or self.roster[key].class ~= "PET") then
390 return self.roster[key]
391 end
392 end
393 end
394  
395  
396 --[[
397 TODO:
398 - use events AceEvent_EventRegistered, AceEvent_EventUnregistered plus :IsEventRegistered("event") to see if we can disable the library
399 - optimize GC in IterateRoster()
400 ]]
401  
402  
403 AceLibrary:Register(RosterLib, MAJOR_VERSION, MINOR_VERSION, activate)
404 RosterLib = AceLibrary(MAJOR_VERSION)