sylvanaar

LibWho Error Fix Attempt

Sep 6th, 2012
90
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 23.86 KB | None | 0 0
  1. ---
  2. --- check for an already loaded old WhoLib
  3. ---
  4.  
  5. if WhoLibByALeX or WhoLib then
  6.     -- the WhoLib-1.0 (WhoLibByALeX) or WhoLib (by Malex) is loaded -> fail!
  7.     error("an other WhoLib is already running - disable them first!\n")
  8.     return
  9. end -- if
  10.  
  11. ---
  12. --- check version
  13. ---
  14.  
  15. assert(LibStub, "LibWho-2.0 requires LibStub")
  16.  
  17.  
  18. local major_version = 'LibWho-2.0'
  19. local minor_version = tonumber("@project-revision@") or 99999
  20.  
  21. local lib = LibStub:NewLibrary(major_version, minor_version)
  22.  
  23.  
  24. if not lib then
  25.     return  -- already loaded and no upgrade necessary
  26. end
  27.  
  28. lib.callbacks = lib.callbacks or LibStub("CallbackHandler-1.0"):New(lib)
  29. local callbacks = lib.callbacks
  30.  
  31. local am = {}
  32. local om = getmetatable(lib)
  33. if om then
  34.     for k, v in pairs(om) do am[k] = v end
  35. end
  36. am.__tostring = function() return major_version end
  37. setmetatable(lib, am)
  38.  
  39. local function dbgfunc(...) if lib.Debug then print(...) end end
  40. local function NOP() return end
  41. local dbg = NOP
  42.  
  43. ---
  44. --- initalize base
  45. ---
  46.  
  47. if type(lib['hooked']) ~= 'table' then
  48.     lib['hooked'] = {}
  49. end -- if
  50.  
  51. if type(lib['hook']) ~= 'table' then
  52.     lib['hook'] = {}
  53. end -- if
  54.  
  55. if type(lib['events']) ~= 'table' then
  56.     lib['events'] = {}
  57. end -- if
  58.  
  59. if type(lib['embeds']) ~= 'table' then
  60.     lib['embeds'] = {}
  61. end -- if
  62.  
  63. if type(lib['frame']) ~= 'table' then
  64.     lib['frame'] = CreateFrame('Frame', major_version);
  65. end -- if
  66. lib['frame']:Hide()
  67.  
  68. lib.Queue = {[1]={}, [2]={}, [3]={}}
  69. lib.WhoInProgress = false
  70. lib.Result = nil
  71. lib.Args = nil
  72. lib.Total = nil
  73. lib.Quiet = nil
  74. lib.Debug = false
  75. lib.Cache = {}
  76. lib.CacheQueue = {}
  77. lib.SetWhoToUIState = 0
  78.  
  79.  
  80. lib.MinInterval = 2.5
  81. lib.MaxInterval = 10
  82.  
  83. ---
  84. --- locale
  85. ---
  86.  
  87. if (GetLocale() == "ruRU") then
  88.     lib.L = {
  89.         ['console_queued'] = 'Добавлено в очередь "/who %s"',
  90.         ['console_query'] = 'Результат "/who %s"',
  91.         ['gui_wait'] = '- Пожалуйста подождите -',
  92.     }
  93. else
  94.     -- enUS is the default
  95.     lib.L = {
  96.         ['console_queued'] = 'Added "/who %s" to queue',
  97.         ['console_query'] = 'Result of "/who %s"',
  98.         ['gui_wait'] = '- Please Wait -',
  99.     }
  100. end -- if
  101.  
  102.  
  103. ---
  104. --- external functions/constants
  105. ---
  106.  
  107. lib['external'] = {
  108.     'WHOLIB_QUEUE_USER',
  109.     'WHOLIB_QUEUE_QUIET',
  110.     'WHOLIB_QUEUE_SCANNING',
  111.     'WHOLIB_FLAG_ALWAYS_CALLBACK',
  112.     'Who',
  113.     'UserInfo',
  114.     'CachedUserInfo',
  115.     'GetWhoLibDebug',
  116.     'SetWhoLibDebug',
  117. --  'RegisterWhoLibEvent',
  118. }
  119.  
  120. -- queues
  121. lib['WHOLIB_QUEUE_USER'] = 1
  122. lib['WHOLIB_QUEUE_QUIET'] = 2
  123. lib['WHOLIB_QUEUE_SCANNING'] = 3
  124.  
  125.  
  126.  
  127. local queue_all = {
  128.     [1] = 'WHOLIB_QUEUE_USER',
  129.     [2] = 'WHOLIB_QUEUE_QUIET',
  130.     [3] = 'WHOLIB_QUEUE_SCANNING',
  131. }
  132.  
  133. local queue_quiet = {
  134.     [2] = 'WHOLIB_QUEUE_QUIET',
  135.     [3] = 'WHOLIB_QUEUE_SCANNING',
  136. }
  137.  
  138. -- bit masks!
  139. lib['WHOLIB_FLAG_ALWAYS_CALLBACK'] = 1
  140.  
  141. function lib:Reset()
  142.     self.Queue = {[1]={}, [2]={}, [3]={}}
  143.     self.Cache = {}
  144.     self.CacheQueue = {}
  145. end    
  146.  
  147. function lib.Who(defhandler, query, opts)
  148.     local self, args, usage = lib, {}, 'Who(query, [opts])'
  149.  
  150.     args.query = self:CheckArgument(usage, 'query', 'string', query)
  151.     opts = self:CheckArgument(usage, 'opts', 'table', opts, {})
  152.     args.queue = self:CheckPreset(usage, 'opts.queue', queue_all, opts.queue, self.WHOLIB_QUEUE_SCANNING)
  153.     args.flags = self:CheckArgument(usage, 'opts.flags', 'number', flags, 0)
  154.     args.callback, args.handler = self:CheckCallback(usage, 'opts.', opts.callback, opts.handler, defhandler)  
  155.     -- now args - copied and verified from opts
  156.    
  157.     if args.queue == self.WHOLIB_QUEUE_USER then
  158.         if WhoFrame:IsShown() then
  159.             self:GuiWho(args.query)
  160.         else
  161.             self:ConsoleWho(args.query)
  162.         end
  163.     else
  164.         self:AskWho(args)
  165.     end
  166. end
  167.  
  168. function lib.UserInfo(defhandler, name, opts)
  169.     local self, args, usage = lib, {}, 'UserInfo(name, [opts])'
  170.     local now = time()
  171.    
  172.     name = self:CheckArgument(usage, 'name', 'string', name)
  173.     if name:len() == 0 then return end
  174.  
  175.     if name:find("%-") then --[[dbg("ignoring xrealm: "..name)]] return end
  176.  
  177.     args.name = self:CapitalizeInitial(name)
  178.     opts = self:CheckArgument(usage, 'opts', 'table', opts, {})
  179.     args.queue = self:CheckPreset(usage, 'opts.queue', queue_quiet, opts.queue, self.WHOLIB_QUEUE_SCANNING)
  180.     args.flags = self:CheckArgument(usage, 'opts.flags', 'number', opts.flags, 0)
  181.     args.timeout = self:CheckArgument(usage, 'opts.timeout', 'number', opts.timeout, 5)
  182.     args.callback, args.handler = self:CheckCallback(usage, 'opts.', opts.callback,  opts.handler, defhandler)
  183.    
  184.     -- now args - copied and verified from opts
  185.     local cachedName = self.Cache[args.name]
  186.    
  187.     if(cachedName ~= nil)then
  188.         -- user is in cache
  189.         if(cachedName.valid == true and (args.timeout < 0 or cachedName.last + args.timeout*60 > now))then
  190.             -- cache is valid and timeout is in range
  191.             --dbg('Info(' .. args.name ..') returned immedeatly')
  192.             if(bit.band(args.flags, self.WHOLIB_FLAG_ALWAYS_CALLBACK) ~= 0)then
  193.                 self:RaiseCallback(args, cachedName.data)
  194.                 return false
  195.             else
  196.                 return self:DupAll(self:ReturnUserInfo(args.name))
  197.             end
  198.         elseif(cachedName.valid == false)then
  199.             -- query is already running (first try)
  200.             if(args.callback ~= nil)then
  201.                 tinsert(cachedName.callback, args)
  202.             end
  203.             --dbg('Info(' .. args.name ..') returned cause it\'s already searching')
  204.             return nil
  205.         end
  206.     else
  207.         self.Cache[args.name] = {valid=false, inqueue=false, callback={}, data={Name = args.name}, last=now }
  208.     end
  209.    
  210.     local cachedName = self.Cache[args.name]
  211.    
  212.     if(cachedName.inqueue)then
  213.         -- query is running!
  214.         if(args.callback ~= nil)then
  215.             tinsert(cachedName.callback, args)
  216.         end
  217.         dbg('Info(' .. args.name ..') returned cause it\'s already searching')
  218.         return nil
  219.     end
  220.     if (GetLocale() == "ruRU") then -- in ruRU with n- not show information about player in WIM addon
  221.     if args.name and args.name:len() > 0 then
  222.         local query = 'и-"' .. args.name .. '"'
  223.         cachedName.inqueue = true
  224.         if(args.callback ~= nil)then
  225.             tinsert(cachedName.callback, args)
  226.         end
  227.         self.CacheQueue[query] = args.name
  228.         dbg('Info(' .. args.name ..') added to queue')
  229.         self:AskWho( { query = query, queue = args.queue, flags = 0, info = args.name } )
  230.     end
  231.     else
  232.         if args.name and args.name:len() > 0 then
  233.         local query = 'n-"' .. args.name .. '"'
  234.         cachedName.inqueue = true
  235.         if(args.callback ~= nil)then
  236.             tinsert(cachedName.callback, args)
  237.         end
  238.         self.CacheQueue[query] = args.name
  239.         dbg('Info(' .. args.name ..') added to queue')
  240.         self:AskWho( { query = query, queue = args.queue, flags = 0, info = args.name } )
  241.     end
  242.     end
  243.     return nil
  244. end
  245.  
  246. function lib.CachedUserInfo(_, name)
  247.     local self, usage = lib, 'CachedUserInfo(name)'
  248.    
  249.     name = self:CapitalizeInitial(self:CheckArgument(usage, 'name', 'string', name))
  250.  
  251.     if self.Cache[name] == nil then
  252.         return nil
  253.     else
  254.         return self:DupAll(self:ReturnUserInfo(name))
  255.     end
  256. end
  257.  
  258. function lib.GetWhoLibDebug(_, mode)
  259.     return lib.Debug
  260. end
  261.  
  262. function lib.SetWhoLibDebug(_, mode)
  263.     lib.Debug = mode
  264.     dbg = mode and dbgfunc or NOP
  265. end
  266.  
  267. --function lib.RegisterWhoLibEvent(defhandler, event, callback, handler)
  268. --  local self, usage = lib, 'RegisterWhoLibEvent(event, callback, [handler])'
  269. -- 
  270. --  self:CheckPreset(usage, 'event', self.events, event)
  271. --  local callback, handler = self:CheckCallback(usage, '', callback, handler, defhandler, true)
  272. --  table.insert(self.events[event], {callback=callback, handler=handler})
  273. --end
  274.  
  275. -- non-embedded externals
  276.  
  277. function lib.Embed(_, handler)
  278.     local self, usage = lib, 'Embed(handler)'
  279.  
  280.     self:CheckArgument(usage, 'handler', 'table', handler)
  281.  
  282.     for _,name in pairs(self.external) do
  283.         handler[name] = self[name]
  284.     end -- do
  285.      self['embeds'][handler] = true
  286.  
  287.     return handler
  288. end
  289.  
  290. function lib.Library(_)
  291.     local self = lib
  292.  
  293.     return self:Embed({})
  294. end
  295.  
  296. ---
  297. --- internal functions
  298. ---
  299.  
  300. function lib:AllQueuesEmpty()
  301.     local queueCount = #self.Queue[1] + #self.Queue[2] + #self.Queue[3] + #self.CacheQueue
  302.  
  303.     -- Be sure that we have cleared the in-progress status
  304.     if self.WhoInProgress then
  305.         queueCount = queueCount + 1
  306.     end
  307.    
  308.     return queueCount == 0
  309. end
  310.  
  311. local queryInterval = 5
  312.  
  313. function lib:GetQueryInterval() return queryInterval end
  314.  
  315. function lib:AskWhoNextIn5sec()
  316.     if self.frame:IsShown() then return end
  317.  
  318.     dbg("Waiting to send next who")
  319.     self.Timeout_time = queryInterval
  320.     self['frame']:Show()
  321. end
  322.  
  323. function lib:CancelPendingWhoNext()
  324.     lib['frame']:Hide()
  325. end
  326.  
  327. lib['frame']:SetScript("OnUpdate", function(frame, elapsed)
  328.     lib.Timeout_time = lib.Timeout_time - elapsed
  329.     if lib.Timeout_time <= 0 then
  330.         lib['frame']:Hide()
  331.         lib:AskWhoNext()
  332.     end -- if
  333. end);
  334.  
  335.  
  336. -- queue scheduler
  337. local queue_weights = { [1] = 0.6, [2] = 0.2, [3] = 0.2 }
  338. local queue_bounds = { [1] = 0.6, [2] = 0.2, [3] = 0.2 }
  339.  
  340. -- allow for single queries from the user to get processed faster
  341. local lastInstantQuery = time()
  342. local INSTANT_QUERY_MIN_INTERVAL = 60 -- only once every 1 min
  343.  
  344. function lib:UpdateWeights()
  345.    local weightsum, sum, count = 0, 0, 0
  346.    for k,v in pairs(queue_weights) do
  347.         sum = sum + v
  348.         weightsum = weightsum + v * #self.Queue[k]
  349.    end
  350.  
  351.    if weightsum == 0 then
  352.         for k,v in pairs(queue_weights) do queue_bounds[k] = v end
  353.         return
  354.    end
  355.  
  356.    local adjust = sum / weightsum
  357.  
  358.    for k,v in pairs(queue_bounds) do
  359.         queue_bounds[k] = queue_weights[k] * adjust * #self.Queue[k]
  360.    end
  361. end
  362.  
  363. function lib:GetNextFromScheduler()
  364.    self:UpdateWeights()
  365.  
  366.    -- Since an addon could just fill up the user q for instant processing
  367.    -- we have to limit instants to 1 per INSTANT_QUERY_MIN_INTERVAL
  368.    -- and only try instant fulfilment if it will empty the user queue
  369.    if #self.Queue[1] == 1 then
  370.         if time() - lastInstantQuery > INSTANT_QUERY_MIN_INTERVAL then
  371.             dbg("INSTANT")
  372.             lastInstantQuery = time()
  373.             return 1, self.Queue[1]
  374.         end
  375.    end
  376.  
  377.    local n,i = math.random(),0
  378.    repeat
  379.         i=i+1
  380.         n = n - queue_bounds[i]
  381.    until i>=#self.Queue or n <= 0
  382.  
  383.    dbg(("Q=%d, bound=%d"):format(i, queue_bounds[i]))
  384.  
  385.    if #self.Queue[i] > 0 then
  386.         dbg(("Q=%d, bound=%d"):format(i, queue_bounds[i]))
  387.        return i, self.Queue[i]
  388.    else
  389.         dbg("Queues empty, waiting")
  390.    end
  391. end
  392.  
  393. lib.queue_bounds = queue_bounds
  394.  
  395. function lib:AskWhoNext()
  396.     if lib.frame:IsShown() then
  397.         dbg("Already waiting")
  398.         return
  399.     end
  400.  
  401.     self:CancelPendingWhoNext()
  402.  
  403.     if self.WhoInProgress then
  404.         -- if we had a who going, it didnt complete
  405.         dbg("TIMEOUT: "..self.Args.query)
  406.         local args = self.Args
  407.         self.Args = nil
  408. --      if args.info and self.CacheQueue[args.query] ~= nil then
  409.             dbg("Requeing "..args.query)
  410.             tinsert(self.Queue[args.queue], args)
  411.             if args.console_show ~= nil then
  412.                 DEFAULT_CHAT_FRAME:AddMessage(("Timeout on result of '%s' - retrying..."):format(args.query),1,1,0)
  413.                 args.console_show = true
  414.             end
  415. --      end
  416.  
  417.         if queryInterval < lib.MaxInterval then
  418.             queryInterval = queryInterval + 0.5
  419.             dbg("--Throttling down to 1 who per " .. queryInterval .. "s")
  420.         end
  421.     end
  422.  
  423.  
  424.     self.WhoInProgress = false
  425.  
  426.     local v,k,args = nil
  427.     local kludge = 10
  428.     repeat
  429.         k,v = self:GetNextFromScheduler()
  430.         if not k then break end
  431.         if(WhoFrame:IsShown() and k > self.WHOLIB_QUEUE_QUIET)then
  432.             break
  433.         end
  434.         if(#v > 0)then
  435.             args = tremove(v, 1)
  436.             break
  437.         end
  438.         kludge = kludge - 1
  439.     until kludge <= 0
  440.    
  441.     if args then
  442.         self.WhoInProgress = true
  443.         self.Result = {}
  444.         self.Args = args
  445.         self.Total = -1
  446.         if(args.console_show == true)then
  447.             DEFAULT_CHAT_FRAME:AddMessage(string.format(self.L['console_query'], args.query), 1, 1, 0)
  448.            
  449.         end
  450.        
  451.         if args.queue == self.WHOLIB_QUEUE_USER then
  452.             WhoFrameEditBox:SetText(args.query)
  453.             self.Quiet = false
  454.    
  455.             if args.whotoui then
  456.                 self.hooked.SetWhoToUI(args.whotoui)
  457.             else
  458.                 self.hooked.SetWhoToUI(args.gui and 1 or 0)
  459.             end
  460.         else
  461.             self.hooked.SetWhoToUI(1)
  462.             self.Quiet = true      
  463.         end
  464.  
  465.         dbg("QUERY: "..args.query)
  466.         self.hooked.SendWho(args.query)
  467.     else
  468.         self.Args = nil
  469.         self.WhoInProgress = false
  470.     end
  471.  
  472.     -- Keep processing the who queue if there is more work
  473.     if not self:AllQueuesEmpty() then
  474.         self:AskWhoNextIn5sec()
  475.     else
  476.         dbg("*** Done processing requests ***")
  477.     end
  478. end
  479.  
  480. function lib:AskWho(args)
  481.     tinsert(self.Queue[args.queue], args)
  482.     dbg('[' .. args.queue .. '] added "' .. args.query .. '", queues=' .. #self.Queue[1] .. '/'.. #self.Queue[2] .. '/'.. #self.Queue[3])
  483.     self:TriggerEvent('WHOLIB_QUERY_ADDED')
  484.    
  485.     self:AskWhoNext()
  486. end
  487.  
  488. function lib:ReturnWho()
  489.     if not self.Args then
  490.         self.Quiet = nil
  491.         return
  492.     end
  493.  
  494.     if(self.Args.queue == self.WHOLIB_QUEUE_QUIET or self.Args.queue == self.WHOLIB_QUEUE_SCANNING)then
  495.         self.Quiet = nil
  496.     end
  497.  
  498.     if queryInterval > self.MinInterval then
  499.         queryInterval = queryInterval - 0.5
  500.         dbg("--Throttling up to 1 who per " .. queryInterval .. "s")
  501.     end
  502.  
  503.     self.WhoInProgress = false
  504.     dbg("RESULT: "..self.Args.query)
  505.     dbg('[' .. self.Args.queue .. '] returned "' .. self.Args.query .. '", total=' .. self.Total ..' , queues=' .. #self.Queue[1] .. '/'.. #self.Queue[2] .. '/'.. #self.Queue[3])
  506.     local now = time()
  507.     local complete = self.Total == #self.Result
  508.     for _,v in pairs(self.Result)do
  509.         if(self.Cache[v.Name] == nil)then
  510.             self.Cache[v.Name] = { inqueue = false, callback = {} }
  511.         end
  512.        
  513.         local cachedName = self.Cache[v.Name]
  514.        
  515.         cachedName.valid = true -- is now valid
  516.         cachedName.data = v -- update data
  517.         cachedName.data.Online = true -- player is online
  518.         cachedName.last = now -- update timestamp
  519.         if(cachedName.inqueue)then
  520.             if(self.Args.info and self.CacheQueue[self.Args.query] == v.Name)then
  521.                 -- found by the query which was created to -> remove us from query
  522.                 self.CacheQueue[self.Args.query] = nil
  523.             else
  524.                 -- found by another query
  525.                 for k2,v2 in pairs(self.CacheQueue) do
  526.                     if(v2 == v.Name)then
  527.                         for i=self.WHOLIB_QUEUE_QUIET, self.WHOLIB_QUEUE_SCANNING do
  528.                             for k3,v3 in pairs(self.Queue[i]) do
  529.                                 if(v3.query == k2 and v3.info)then
  530.                                     -- remove the query which was generated for this user, cause another query was faster...
  531.                                     dbg("Found '"..v.Name.."' early via query '"..self.Args.query.."'")
  532.                                     table.remove(self.Queue[i], k3)
  533.                                     self.CacheQueue[k2] = nil
  534.                                 end
  535.                             end
  536.                         end
  537.                     end
  538.                 end
  539.             end
  540.             dbg('Info(' .. v.Name ..') returned: on')
  541.             for _,v2 in pairs(cachedName.callback) do
  542.                 self:RaiseCallback(v2, self:ReturnUserInfo(v.Name))
  543.             end
  544.             cachedName.callback = {}
  545.         end
  546.         cachedName.inqueue = false -- query is done
  547.     end
  548.     if(self.Args.info and self.CacheQueue[self.Args.query])then
  549.         -- the query did not deliver the result => not online!
  550.         local name = self.CacheQueue[self.Args.query]
  551.         local cachedName = self.Cache[name]
  552.         if (cachedName.inqueue)then
  553.             -- nothing found (yet)
  554.             cachedName.valid = true -- is now valid
  555.             cachedName.inqueue = false -- query is done?
  556.             cachedName.last = now -- update timestamp
  557.             if(complete)then
  558.                 cachedName.data.Online = false -- player is offline
  559.             else
  560.                 cachedName.data.Online = nil -- player is unknown (more results from who than can be displayed)
  561.             end
  562.         end
  563.         dbg('Info(' .. name ..') returned: ' .. (cachedName.data.Online == false and 'off' or 'unkn'))
  564.         for _,v in pairs(cachedName.callback) do
  565.             self:RaiseCallback(v, self:ReturnUserInfo(v.Name))
  566.         end
  567.         cachedName.callback = {}
  568.         self.CacheQueue[self.Args.query] = nil
  569.     end
  570.     self:RaiseCallback(self.Args, self.Args.query, self.Result, complete, self.Args.info)
  571.     self:TriggerEvent('WHOLIB_QUERY_RESULT', self.Args.query, self.Result, complete, self.Args.info)
  572.    
  573.     if not self:AllQueuesEmpty() then
  574.         self:AskWhoNextIn5sec()
  575.     end
  576. end
  577.  
  578. function lib:GuiWho(msg)
  579.     if(msg == self.L['gui_wait'])then
  580.         return
  581.     end
  582.  
  583.     for _,v in pairs(self.Queue[self.WHOLIB_QUEUE_USER]) do
  584.         if(v.gui == true)then
  585.             return
  586.         end
  587.     end
  588.     if(self.WhoInProgress)then
  589.         WhoFrameEditBox:SetText(self.L['gui_wait'])
  590.     end
  591.     self.savedText = msg
  592.     self:AskWho({query = msg, queue = self.WHOLIB_QUEUE_USER, flags = 0, gui = true})
  593.     WhoFrameEditBox:ClearFocus();
  594. end
  595.  
  596. function lib:ConsoleWho(msg)
  597.     --WhoFrameEditBox:SetText(msg)
  598.     local console_show = false
  599.     local q1 = self.Queue[self.WHOLIB_QUEUE_USER]
  600.     local q1count = #q1
  601.    
  602.     if(q1count > 0 and q1[q1count].query == msg)then -- last query is itdenical: drop
  603.         return
  604.     end
  605.    
  606.     if(q1count > 0 and q1[q1count].console_show == false)then -- display 'queued' if console and not yet shown
  607.         DEFAULT_CHAT_FRAME:AddMessage(string.format(self.L['console_queued'], q1[q1count].query), 1, 1, 0)
  608.         q1[q1count].console_show = true
  609.     end
  610.     if(q1count > 0 or self.WhoInProgress)then
  611.         DEFAULT_CHAT_FRAME:AddMessage(string.format(self.L['console_queued'], msg), 1, 1, 0)
  612.         console_show = true
  613.     end
  614.     self:AskWho({query = msg, queue = self.WHOLIB_QUEUE_USER, flags = 0, console_show = console_show})
  615. end
  616.  
  617. function lib:ReturnUserInfo(name)
  618.     if(name ~= nil and self ~= nil and self.Cache ~= nil and self.Cache[name] ~= nil) then
  619.         return self.Cache[name].data, (time() - self.Cache[name].last) / 60
  620.     end
  621. end
  622.  
  623. function lib:RaiseCallback(args, ...)
  624.     if type(args.callback) == 'function' then
  625.         args.callback(self:DupAll(...))
  626.     elseif args.callback then -- must be a string
  627.         args.handler[args.callback](args.handler, self:DupAll(...))
  628.     end -- if
  629. end
  630.  
  631. -- Argument checking
  632.  
  633. function lib:CheckArgument(func, name, argtype, arg, defarg)
  634.     if arg == nil and defarg ~= nil then
  635.         return defarg
  636.     elseif type(arg) == argtype then
  637.         return arg
  638.     else
  639.         error(string.format("%s: '%s' - %s%s expected got %s", func, name, (defarg ~= nil) and 'nil or ' or '', argtype, type(arg)), 3)
  640.     end -- if
  641. end
  642.  
  643. function lib:CheckPreset(func, name, preset, arg, defarg)
  644.     if arg == nil and defarg ~= nil then
  645.         return defarg
  646.     elseif arg ~= nil and preset[arg] ~= nil then
  647.         return arg
  648.     else
  649.         local p = {}
  650.         for k,v in pairs(preset) do
  651.             if type(v) ~= 'string' then
  652.                 table.insert(p, k)
  653.             else
  654.                 table.insert(p, v)
  655.             end -- if
  656.         end -- for
  657.         error(string.format("%s: '%s' - one of %s%s expected got %s", func, name, (defarg ~= nil) and 'nil, ' or '', table.concat(p, ', '), self:simple_dump(arg)), 3)
  658.     end -- if
  659. end
  660.  
  661. function lib:CheckCallback(func, prefix, callback, handler, defhandler, nonil)
  662.     if not nonil and callback == nil then
  663.         -- no callback: ignore handler
  664.         return nil, nil
  665.     elseif type(callback) == 'function' then
  666.         -- simple function
  667.         if handler ~= nil then
  668.             error(string.format("%s: '%shandler' - nil expected got %s", func, prefix, type(arg)), 3)
  669.         end -- if
  670.     elseif type(callback) == 'string' then
  671.         -- method
  672.         if handler == nil then
  673.             handler = defhandler
  674.         end -- if
  675.         if type(handler) ~= 'table' or type(handler[callback]) ~= 'function' or handler == self then
  676.             error(string.format("%s: '%shandler' - nil or function expected got %s", func, prefix, type(arg)), 3)
  677.         end -- if
  678.     else
  679.         error(string.format("%s: '%scallback' - %sfunction or string expected got %s", func, prefix, nonil and 'nil or ' or '', type(arg)), 3)
  680.     end -- if
  681.  
  682.     return callback, handler
  683. end
  684.  
  685. -- helpers
  686.  
  687. function lib:simple_dump(x)
  688.     if type(x) == 'string' then
  689.         return 'string \''..x..'\''
  690.     elseif type(x) == 'number' then
  691.         return 'number '..x
  692.     else
  693.         return type(x)
  694.     end
  695. end
  696.  
  697. function lib:Dup(from)
  698.     local to = {}
  699.  
  700.     for k,v in pairs(from) do
  701.         if type(v) == 'table' then
  702.             to[k] = self:Dup(v)
  703.         else
  704.             to[k] = v
  705.         end -- if
  706.     end -- for
  707.  
  708.     return to
  709. end
  710.  
  711. function lib:DupAll(x, ...)
  712.     if type(x) == 'table' then
  713.         return self:Dup(x), self:DupAll(...)
  714.     elseif x ~= nil then
  715.         return x, self:DupAll(...)
  716.     else
  717.         return nil
  718.     end -- if
  719. end
  720.  
  721. local MULTIBYTE_FIRST_CHAR = "^([\192-\255]?%a?[\128-\191]*)"
  722.  
  723. function lib:CapitalizeInitial(name)
  724.     return name:gsub(MULTIBYTE_FIRST_CHAR, string.upper, 1)
  725. end
  726.  
  727. ---
  728. --- user events (Using CallbackHandler)
  729. ---
  730.  
  731. lib.PossibleEvents = {
  732.     'WHOLIB_QUERY_RESULT',
  733.     'WHOLIB_QUERY_ADDED',
  734. }
  735.  
  736. function lib:TriggerEvent(event, ...)
  737.     callbacks:Fire(event, ...)
  738. end
  739.  
  740. ---
  741. --- slash commands
  742. ---
  743.  
  744. SlashCmdList['WHO'] = function(msg)
  745.     dbg("console /who: "..msg)
  746.     -- new /who function
  747.     --local self = lib
  748.    
  749.     if(msg == '')then
  750.         lib:GuiWho(WhoFrame_GetDefaultWhoCommand())
  751.     elseif(WhoFrame:IsVisible())then
  752.         lib:GuiWho(msg)
  753.     else
  754.         lib:ConsoleWho(msg)
  755.     end
  756. end
  757.    
  758. SlashCmdList['WHOLIB_DEBUG'] = function()
  759.     -- /wholibdebug: toggle debug on/off
  760.     local self = lib
  761.    
  762.     self:SetWhoLibDebug(not self.Debug)
  763. end
  764.  
  765. SLASH_WHOLIB_DEBUG1 = '/wholibdebug'
  766.  
  767.  
  768. ---
  769. --- hook activation
  770. ---
  771.  
  772. -- functions to hook
  773. local hooks = {
  774.     'SendWho',
  775.     'WhoFrameEditBox_OnEnterPressed',
  776. --  'FriendsFrame_OnEvent',
  777.     'SetWhoToUI',
  778. }
  779.  
  780. -- hook all functions (which are not yet hooked)
  781. for _, name in pairs(hooks) do
  782.     if not lib['hooked'][name] then
  783.         lib['hooked'][name] = _G[name]
  784.         _G[name] = function(...)
  785.             lib.hook[name](lib, ...)
  786.         end -- function
  787.     end -- if
  788. end -- for
  789.  
  790. -- fake 'WhoFrame:Hide' as hooked
  791. table.insert(hooks, 'WhoFrame_Hide')
  792.  
  793. -- check for unused hooks -> remove function
  794. for name, _ in pairs(lib['hook']) do
  795.     if not hooks[name] then
  796.         lib['hook'][name] = function() end
  797.     end -- if
  798. end -- for
  799.  
  800. -- secure hook 'WhoFrame:Hide'
  801. if not lib['hooked']['WhoFrame_Hide'] then
  802.     lib['hooked']['WhoFrame_Hide'] = true
  803.     hooksecurefunc(WhoFrame, 'Hide', function(...)
  804.             lib['hook']['WhoFrame_Hide'](lib, ...)
  805.         end -- function
  806.     )
  807. end -- if
  808.  
  809.  
  810.  
  811. ----- Coroutine based implementation (future)
  812. --function lib:sendWhoResult(val)
  813. --    coroutine.yield(val)
  814. --end
  815. --
  816. --function lib:sendWaitState(val)
  817. --    coroutine.yield(val)
  818. --end
  819. --
  820. --function lib:producer()
  821. --    return coroutine.create(
  822. --    function()
  823. --        lib:AskWhoNext()
  824. --        lib:sendWaitState(true)
  825. --        
  826. --        -- Resumed look for data
  827. --
  828. --    end)
  829. --end  
  830.  
  831.  
  832.  
  833.  
  834.  
  835. ---
  836. --- hook replacements
  837. ---
  838.  
  839. function lib.hook.SendWho(self, msg)
  840.     dbg("SendWho: "..msg)
  841.     lib.AskWho(self, {query = msg, queue = lib.WHOLIB_QUEUE_USER, whotoui = lib.SetWhoToUIState, flags = 0})
  842. end
  843.  
  844. function lib.hook.WhoFrameEditBox_OnEnterPressed(self)
  845.     lib:GuiWho(WhoFrameEditBox:GetText())
  846. end
  847.  
  848. --[[
  849. function lib.hook.FriendsFrame_OnEvent(self, ...)
  850.     if event ~= 'WHO_LIST_UPDATE' or not lib.Quiet then
  851.         lib.hooked.FriendsFrame_OnEvent(...)
  852.     end
  853. end
  854. ]]
  855.  
  856. hooksecurefunc(FriendsFrame, 'RegisterEvent', function(self, event)
  857.         if(event == "WHO_LIST_UPDATE") then
  858.             self:UnregisterEvent("WHO_LIST_UPDATE");
  859.         end
  860.     end);
  861.  
  862.  
  863. function lib.hook.SetWhoToUI(self, state)
  864.     lib.SetWhoToUIState = state
  865. end
  866.  
  867. function lib.hook.WhoFrame_Hide(self)
  868.     if(not lib.WhoInProgress)then
  869.         lib:AskWhoNextIn5sec()
  870.     end
  871. end
  872.  
  873.  
  874. ---
  875. --- WoW events
  876. ---
  877.  
  878. local who_pattern = string.gsub(WHO_NUM_RESULTS, '%%d', '%%d%+')
  879.  
  880.  
  881. function lib:CHAT_MSG_SYSTEM(arg1)
  882.     if arg1 and arg1 == ERR_FRIEND_NOT_FOUND then return end
  883.     if arg1 and arg1:find(who_pattern) then
  884.         lib:ProcessWhoResults()
  885.     end
  886. end
  887.  
  888. FriendsFrame:UnregisterEvent("WHO_LIST_UPDATE")
  889.  
  890. function lib:WHO_LIST_UPDATE()
  891.     if not lib.Quiet then
  892.         WhoList_Update()       
  893.         FriendsFrame_Update()
  894.     end
  895.  
  896.     lib:ProcessWhoResults()
  897. end
  898.  
  899. function lib:ProcessWhoResults()
  900.     local num
  901.     self.Total, num = GetNumWhoResults()
  902.     for i=1, num do
  903.         local charname, guildname, level, race, class, zone, nonlocalclass, sex = GetWhoInfo(i)
  904.         self.Result[i] = {Name=charname, Guild=guildname, Level=level, Race=race, Class=class, Zone=zone, NoLocaleClass=nonlocalclass, Sex=sex }
  905.     end
  906.    
  907.     self:ReturnWho()
  908. end
  909.  
  910. ---
  911. --- event activation
  912. ---
  913.  
  914. lib['frame']:UnregisterAllEvents();
  915.  
  916. lib['frame']:SetScript("OnEvent", function(frame, event, ...)
  917.     lib[event](lib, ...)
  918. end);
  919.  
  920. for _,name in pairs({
  921.     'CHAT_MSG_SYSTEM',
  922.     'WHO_LIST_UPDATE',
  923. }) do
  924.     lib['frame']:RegisterEvent(name);
  925. end -- for
  926.  
  927.  
  928. ---
  929. --- re-embed
  930. ---
  931.  
  932. for target,_ in pairs(lib['embeds']) do
  933.     if type(target) == 'table' then
  934.         lib:Embed(target)
  935.     end -- if
  936. end -- for
Advertisement
Add Comment
Please, Sign In to add comment