AuctioneerSuite/Auc-Advanced/CoreAPI.lua

928 lines
32 KiB
Lua
Raw Permalink Normal View History

2026-04-13 17:48:13 -04:00
--[[
Auctioneer Advanced
Version: 5.9.4961 (WhackyWallaby)
Revision: $Id: CoreAPI.lua 4933 2010-10-13 17:16:14Z Nechckn $
URL: http://auctioneeraddon.com/
This is an addon for World of Warcraft that adds statistical history to the auction data that is collected
when the auction is scanned, so that you can easily determine what price
you will be able to sell an item for at auction or at a vendor whenever you
mouse-over an item in the game
License:
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program(see GPL.txt); if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Note:
This AddOn's source code is specifically designed to work with
World of Warcraft's interpreted AddOn system.
You have an implicit license to use this AddOn with these facilities
since that is its designated purpose as per:
http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat
]]
if not AucAdvanced then return end
local AucAdvanced = AucAdvanced
local coremodule = AucAdvanced.GetCoreModule("CoreAPI")
if not coremodule then return end -- Someone has explicitely broken us
AucAdvanced.API = {}
local lib = AucAdvanced.API
local private = {}
lib.Print = AucAdvanced.Print
local Const = AucAdvanced.Const
local GetFaction = AucAdvanced.GetFaction
local GetSetting = AucAdvanced.Settings.GetSetting
local DecodeLink = AucAdvanced.DecodeLink
local SanitizeLink = AucAdvanced.SanitizeLink
local tinsert = table.insert
local tremove = table.remove
local next,pairs,ipairs,type = next,pairs,ipairs,type
local wipe = wipe
local ceil,floor,max,abs = ceil,floor,max,abs
local tostring,tonumber,strjoin,strsplit,format = tostring,tonumber,strjoin,strsplit,format
local GetItemInfo = GetItemInfo
local time = time
-- GLOBALS: nLog, N_NOTICE, N_WARNING, N_ERROR
coremodule.Processors = {}
function coremodule.Processors.scanstats()
lib.ClearMarketCache()
end
function coremodule.Processors.configchanged()
lib.ClearMarketCache()
end
function coremodule.Processors.newmodule()
private.ClearEngineCache()
lib.ClearMarketCache()
end
do
local EPSILON = 0.000001;
local IMPROVEMENT_FACTOR = 0.8;
local CORRECTION_FACTOR = 1000; -- 10 silver per gold, integration steps at tail
local FALLBACK_ERROR = 1; -- 1 silver per gold fallback error max
-- cache[serverKey][itemsig]={value, seen, #stats}
local cache = setmetatable({}, { __index = function(tbl,key)
tbl[key] = {}
return tbl[key]
end
})
local pdfList = {};
local engines = {};
local ERROR = 0.05;
-- local LOWER_INT_LIMIT, HIGHER_INT_LIMIT = -100000, 10000000;
--[[
This function acquires the current market value of the mentioned item using
a configurable algorithm to process the data used by the other installed
algorithms.
The returned value is the most probable value that the item is worth
using the algorithms in each of the STAT modules as specified
by the GetItemPDF() function.
AucAdvanced.API.GetMarketValue(itemLink, serverKey)
]]
function lib.GetMarketValue(itemLink, serverKey)
local _;
if type(itemLink) == 'number' then _, itemLink = GetItemInfo(itemLink) end
if not itemLink then return end
local cacheSig = lib.GetSigFromLink(itemLink)
if not cacheSig then return end -- not a valid item link
serverKey = serverKey or GetFaction() -- call GetFaction once here, instead of in every Stat module
local cacheEntry = cache[serverKey][cacheSig]
if cacheEntry then
return cacheEntry[1], cacheEntry[2], cacheEntry[3] -- explicit indexing faster than 'unpack' for 3 values
end
ERROR = GetSetting("marketvalue.accuracy");
local saneLink = SanitizeLink(itemLink)
local upperLimit, lowerLimit, seen = 0, 1e11, 0;
if #engines == 0 then
-- Rebuild the engine cache
local modules = AucAdvanced.GetAllModules(nil, "Stat")
for pos, engineLib in ipairs(modules) do
local fn = engineLib.GetItemPDF;
if fn then
tinsert(engines, {pdf = fn, array = engineLib.GetPriceArray});
elseif nLog then
nLog.AddMessage("Auctioneer", "Market Pricing", N_WARNING, "Missing PDF", "Auctioneer engine '"..engineLib.GetName().."' does not have a GetItemPDF() function. This check will be removed in the near future in favor of faster calls. Implement this function.");
end
end
end
-- Run through all of the stat modules and get the PDFs
local c, oldPdfMax, total = 0, #pdfList, 0;
local convergedFallback = nil;
for _, engine in ipairs(engines) do
local i, min, max, area = engine.pdf(saneLink, serverKey);
if type(i) == 'number' then
-- This is a fallback
if convergedFallback == nil or (type(convergedFallback) == 'number' and abs(convergedFallback - i) < FALLBACK_ERROR * convergedFallback / 10000) then
convergedFallback = i;
else
convergedFallback = false; -- Cannot converge on fallback pricing
end
end
local priceArray = engine.array(saneLink, serverKey);
if priceArray and (priceArray.seen or 0) > seen then
seen = priceArray.seen;
end
if i and type(i) ~= 'number' then -- pdfList[++c] = i;
total = total + (area or 1); -- Add total area, assume 1 if not supplied
c = c + 1;
pdfList[c] = i;
if min < lowerLimit then lowerLimit = min; end
if max > upperLimit then upperLimit = max; end
end
end
-- Clean out extras if needed
for i = c+1, oldPdfMax do
pdfList[i] = nil;
end
if #pdfList == 0 and convergedFallback then
if nLog then nLog.AddMessage("Auctioneer", "Market Pricing", N_WARNING, "Fallback Pricing Used", "Fallback pricing used due to no available PDFs on item "..itemLink); end
return convergedFallback, 1, 1;
end
if not (lowerLimit > -1/0 and upperLimit < 1/0) then
error("Invalid bounds detected while pricing "..(GetItemInfo(itemLink) or itemLink)..": "..tostring(lowerLimit).." to "..tostring(upperLimit))
end
-- Determine the totals from the PDFs
local delta = (upperLimit - lowerLimit) * .01;
if #pdfList == 0 or delta < EPSILON or total < EPSILON then
return; -- No PDFs available for this item
end
local limit = total/2;
local midpoint, lastMidpoint = 0, 0;
-- Now find the 50% point
repeat
lastMidpoint = midpoint;
total = 0;
if not(delta > 0) then
error("Infinite loop detected during market pricing for "..(GetItemInfo(itemLink) or itemLink))
end
for x = lowerLimit, upperLimit, delta do
for i = 1, #pdfList do
local val = pdfList[i](x);
total = total + val * delta;
end
if total > limit then
midpoint = x;
break;
end
end
delta = delta * IMPROVEMENT_FACTOR;
if midpoint ~= midpoint or midpoint == 0 then
if nLog and midpoint ~= midpoint then
nLog.AddMessage("Auctioneer", "Market Pricing", N_WARNING, "Unable To Calculate", "A NaN value was detected while processing the midpoint for PDF of "..(GetItemInfo(itemLink) or itemLink).."... Giving up.");
elseif nLog then
nLog.AddMessage("Auctioneer", "Market Pricing", N_NOTICE, "Unable To Calculate", "A zero total was detected while processing the midpoint for PDF of "..(GetItemInfo(itemLink) or itemLink).."... Giving up.");
end
if convergedFallback then
if nLog then
nLog.AddMessage("Auctioneer", "Market Pricing", N_WARNING, "Fallback Pricing Used", "Fallback pricing used due to NaN/Zero total for item "..itemLink);
end
return convergedFallback, 1, 1;
end
return; -- Cannot calculate: NaN
end
until abs(midpoint - lastMidpoint)/midpoint < ERROR;
if midpoint and midpoint > 0 then
midpoint = floor(midpoint + 0.5); -- Round to nearest copper
-- Cache before finishing up
cache[serverKey][cacheSig] = {midpoint, seen, #pdfList}
return midpoint, seen, #pdfList;
else
if nLog then
nLog.AddMessage("Auctioneer", "Market Pricing", N_WARNING, "Unable To Calculate", "No midpoint was detected for item "..(GetItemInfo(itemLink) or itemLink).."... Giving up.");
end
return;
end
end
-- Clear the cache of Stats engines (called if a new module is registered)
function private.ClearEngineCache()
wipe(engines)
end
-- Clears the results cache for AucAdvanced.API.GetMarketValue()
function lib.ClearMarketCache()
wipe(cache)
end
end
function lib.ClearItem(itemLink, serverKey)
local saneLink = SanitizeLink(itemLink)
local modules = AucAdvanced.GetAllModules("ClearItem")
for pos, engineLib in ipairs(modules) do
engineLib.ClearItem(saneLink, serverKey)
end
lib.ClearMarketCache()
end
--[[ AucAdvanced.API.IsKeyword(testword [, keyword])
Determine whether testword is equal to or an alias of keyword
Returns the keyword if it matches, nil otherwise
For case-insensitive keywords, tries both unmodified and lowercase
Note: default cases must be handled separately
--]]
do
-- allowable keywords (so far): ALL, faction, server
local keywords = { -- entry: alias = keyword,
ALL = "ALL",
faction = "faction",
server = "server",
realm = "server",
}
-- todo: functions to add new keywords, and to add new aliases for keywords
function lib.IsKeyword(testword, keyword)
if type(testword) ~= "string" then return end
local key = keywords[testword] or keywords[testword:lower()] -- try unmodified and lowercased
if key then
if not keyword or keyword == key then
return key
end
end
end
end
function lib.ClearData(command)
local serverKey1, serverKey2, serverKey3
-- split command into keyword and extra parts
local keyword, extra = "faction", "" -- default
if type(command) == "string" then
local _, ind, key = strfind(command, "(%S+)")
if key then
key = lib.IsKeyword(key)
if key then
keyword = key -- recognised keyword
extra = strtrim(strsub(command, ind+1))
else
extra = strtrim(command) -- try to resolve whole command (as a "faction")
end
end
elseif command then -- only valid types are string or nil
error("Unrecognised parameter type to ClearData: "..type(command)..":"..tostring(command))
end
-- At this point keyword should be one of the strings in the following if-block
-- extra should be a string, where 'no extra information' is denoted by ""
if keyword == "ALL" then
if extra == "" then serverKey1 = "ALL" end
elseif keyword == "server" then
if extra == "" then extra = Const.PlayerRealm end
-- otherwise assume the user typed the server name correctly
-- modules should silently ignore unrecognised serverKeys
serverKey1 = extra.."-Alliance"
serverKey2 = extra.."-Horde"
serverKey3 = extra.."-Neutral"
elseif keyword == "faction" then
if extra == "" then
serverKey1 = GetFaction()
elseif AucAdvanced.SplitServerKey(extra) then -- it's a valid serverKey
serverKey1 = extra
else
local fac = AucAdvanced.IsFaction(extra) -- it's a valid faction group
if fac then
serverKey1 = Const.PlayerRealm.."-"..fac
end
end
end
if serverKey1 then
local modules = AucAdvanced.GetAllModules("ClearData")
for pos, lib in ipairs(modules) do
lib.ClearData(serverKey1)
if serverKey2 then
lib.ClearData(serverKey2)
lib.ClearData(serverKey3)
end
end
lib.ClearMarketCache()
else
lib.Print("Auctioneer: Unrecognized keyword or faction for ClearData {{"..command.."}}")
end
end
function lib.GetAlgorithms(itemLink)
local saneLink = SanitizeLink(itemLink)
local engines = {}
local modules = AucAdvanced.GetAllModules()
for pos, engineLib in ipairs(modules) do
if engineLib.GetPrice or engineLib.GetPriceArray then
if not engineLib.IsValidAlgorithm
or engineLib.IsValidAlgorithm(saneLink) then
local engine = engineLib.GetName()
tinsert(engines, engine)
end
end
end
return engines
end
function lib.IsValidAlgorithm(algorithm, itemLink)
local saneLink = SanitizeLink(itemLink)
local modules = AucAdvanced.GetAllModules()
for pos, engineLib in ipairs(modules) do
if engineLib.GetName() == algorithm and (engineLib.GetPrice or engineLib.GetPriceArray) then
if engineLib.IsValidAlgorithm then
return engineLib.IsValidAlgorithm(saneLink)
end
return true
end
end
return false
end
--store the last data request and just return a cache value for the next 5 secs (5 secs is just arbitrary)
local LastAlgorithmSig, LastAlgorithmTime, LastAlgorithmPrice, LastAlgorithmSeen, LastAlgorithmArray
function lib.GetAlgorithmValue(algorithm, itemLink, serverKey, reserved)
if (not algorithm) then
if nLog then nLog.AddMessage("Auctioneer", "API", N_ERROR, "Incorrect Usage", "No pricing algorithm supplied to GetAlgorithmValue") end
return
end
if type(itemLink) == "number" then
local _
_, itemLink = GetItemInfo(itemLink)
end
if (not itemLink) then
if nLog then nLog.AddMessage("Auctioneer", "API", N_ERROR, "Incorrect Usage", "No itemLink supplied to GetAlgorithmValue") end
return
end
if reserved then
lib.ShowDeprecationAlert("AucAdvanced.API.GetAlgorithmValue(algorithm, itemLink, serverKey)",
"The 'faction' and 'realm' parameters are deprecated in favor of the new 'serverKey' parameter. Use this instead."
);
serverKey = reserved.."-"..serverKey;
end
serverKey = serverKey or GetFaction()
local saneLink = SanitizeLink(itemLink)
--check if this was just retrieved and return that value
local algosig = strjoin(":", algorithm, saneLink, serverKey)
if algosig == LastAlgorithmSig and LastAlgorithmTime + 5 > time() then
return LastAlgorithmPrice, LastAlgorithmSeen, LastAlgorithmArray
end
local modules = AucAdvanced.GetAllModules()
for pos, engineLib in ipairs(modules) do
if engineLib.GetName() == algorithm and (engineLib.GetPrice or engineLib.GetPriceArray) then
if engineLib.IsValidAlgorithm
and not engineLib.IsValidAlgorithm(saneLink) then
return
end
local price, seen, array
if (engineLib.GetPriceArray) then
array = engineLib.GetPriceArray(saneLink, serverKey)
if (array) then
price = array.price
seen = array.seen
end
else
price = engineLib.GetPrice(saneLink, serverKey)
end
LastAlgorithmSig = algosig
LastAlgorithmTime = time()
LastAlgorithmPrice, LastAlgorithmSeen, LastAlgorithmArray = price, seen, array
return price, seen, array
end
end
--error(("Cannot find pricing algorithm: %s"):format(algorithm))
return
end
--[[ resultsTable = AucAdvanced.API.QueryImage(queryTable, serverKey, reserved, ...)
'queryTable' specifies the query to perform
'serverKey' defaults to the current faction
'reserved' must always be nil
The working code can be viewed in CoreScan.lua for more details.
--]]
lib.QueryImage = AucAdvanced.Scan.QueryImage
-- unpackedTable = AucAdvanced.API.UnpackImageItem(imageItem)
-- imageItem is one of the values (subtables) in the table returned by QueryImage or GetImageCopy
lib.UnpackImageItem = AucAdvanced.Scan.UnpackImageItem
-- scanStatsTable = AucAdvanced.API.GetScanStats(serverKey)
-- Timestamps: scanstats.LastScan, scanstats.LastFullScan, scanstats.ImageUpdated
-- Scan statistics subtables: scanstats[0] (last scan), scanstats[1], scanstats[2] (two scans prior to last scan)
lib.GetScanStats = AucAdvanced.Scan.GetScanStats
-- imageTable = AucAdvanced.API.GetImageCopy(serverKey)
-- Generates an independent copy of the current scan data image for the specified serverKey
lib.GetImageCopy = AucAdvanced.Scan.GetImageCopy
function lib.ListUpdate()
if lib.IsBlocked() then return end
AucAdvanced.SendProcessorMessage("listupdate")
end
function lib.BlockUpdate(block, propagate)
local blocked
if block == true then
blocked = true
private.isBlocked = true
AuctionFrameBrowse:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
else
blocked = false
private.isBlocked = nil
AuctionFrameBrowse:RegisterEvent("AUCTION_ITEM_LIST_UPDATE")
end
if (propagate) then
AucAdvanced.SendProcessorMessage("blockupdate", blocked)
end
end
function lib.IsBlocked()
return private.isBlocked == true
end
--[[Progress bars that are usable by any addon.
name = string - unique bar name
value = 0-100 the % the bar should be filled
show = boolean true will keep bar displayed, false will hide the bar and free it for use by another addon
text = string - the text to display on the bar
options = table containing formatting commands.
options.barColor = { R,G,B, A} red, green, blue, alpha values.
options.textColor = { R,G,B, A} red, green, blue, alpha values.
value, text, color, and options are all optional variables
]]
local availableBars = {}
local NumGenericBars = 0
--generate new bars as needed
local function newBar()
local bar = CreateFrame("STATUSBAR", nil, UIParent, "TextStatusBar")
bar:SetWidth(300)
bar:SetHeight(18)
bar:SetBackdrop({
bgFile="Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile="Interface\\Tooltips\\UI-Tooltip-Border",
tile=1, tileSize=10, edgeSize=10,
insets={left=1, right=1, top=1, bottom=1}
})
bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
bar:SetStatusBarColor(0.6,0,0,0.6)
bar:SetMinMaxValues(0,100)
bar:SetValue(50)
bar:Hide()
bar.text = bar:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
bar.text:SetPoint("CENTER", bar, "CENTER")
bar.text:SetJustifyH("CENTER")
bar.text:SetJustifyV("CENTER")
bar.text:SetTextColor(1,1,1)
if NumGenericBars < 1 then
bar:SetPoint("CENTER", UIParent, "CENTER", -5,5)
else--attach to previous bar
bar:SetPoint("BOTTOM", lib["GenericProgressBar"..NumGenericBars], "TOP", 0, 0)
end
NumGenericBars = NumGenericBars + 1
lib["GenericProgressBar"..(NumGenericBars)] = bar
return NumGenericBars
end
--create 1 bar to start for anchoring
newBar()
-- handles the rendering
local function renderBars(ID, name, value, text, options)
local self = lib["GenericProgressBar"..ID]
if not self then assert("No bar found available for ID", ID, name, text) end
--reset all generated bars that are not inuse to defaults
if self and not name then
self:Hide()
self.text:SetText("")
self:SetStatusBarColor(0.6, 0, 0, 0.6) --light red color
self.text:SetTextColor(1, 1, 1, 1)
return
end
self:Show()
--update progress
if value then
self:SetValue(value)
end
--change bars text if desired
if text then
self.text:SetText(text)
end
--[[options is a table that contains, "tweaks" ie text or bar color changes
Nothing below this line will be processed unless an options table is passed]]
if not options or type(options) ~= "table" then return end
--change bars color
local barColor = options.barColor
if barColor then
local r, g, b, a = barColor[1],barColor[2], barColor[3], barColor[4]
if r and g and b then
a = a or 0.6
self:SetStatusBarColor(r, g, b, a)
end
end
--change text color
local textColor = options.textColor
if textColor then
local r, g, b, a = textColor[1],textColor[2], textColor[3], textColor[4]
if r and g and b then
a = a or 1
self.text:SetTextColor(r, g, b, a)
end
end
end
--main entry point. Handles which bar will be assigned and recycling bars
function lib.ProgressBars(name, value, show, text, options)
--setup parent so we can display even if AH is closed
if AuctionFrame and AuctionFrame:IsShown() then
lib.GenericProgressBar1:SetParent(AuctionFrame)
lib.GenericProgressBar1:SetPoint("TOPRIGHT", AuctionFrame, "TOPRIGHT", -5, 5)
else
lib.GenericProgressBar1:SetParent(UIParent)
end
if not name then return end
--find a generic bar available for use
local ID = availableBars[name]
if show and not ID then --find a bar
for i = 1, NumGenericBars do
if not availableBars[i] then
availableBars[i] = {name, value, text, options}
availableBars[name] = i
ID = i
break
end
end
--no bar available make a new one
if not ID then
ID = newBar()
availableBars[ID] = {name, value, text, options}
availableBars[name] = ID
end
end
--Render Bars
if show then
renderBars(ID, name, value, text, options)
else
table.remove(availableBars, ID)
availableBars[name] = nil
--ReRender bars
for ID = 1, NumGenericBars do
if availableBars[ID] then
barData = availableBars[ID]
renderBars(ID, barData[1], barData[2], barData[3], barData[4])
else--blank bars
renderBars(ID)
end
end
end
end
--[[ Market matcher APIs ]]--
function lib.GetBestMatch(itemLink, algorithm, serverKey, reserved)
local saneLink = SanitizeLink(itemLink)
if reserved then
lib.ShowDeprecationAlert("AucAdvanced.API.GetBestMatch(itemLink, algorithm, serverKey)",
"The 'realm' and 'faction' parameters have been removed in favor of a single "..
"variable 'serverKey' which should be used in the future."
);
serverKey = reserved.."-"..serverKey;
end
-- TODO: Make a configurable algorithm.
-- This algorithm is currently less than adequate.
local price
if algorithm == "market" then
price = lib.GetMarketValue(saneLink, serverKey)
elseif type(algorithm) == "string" then
price = lib.GetAlgorithmValue(algorithm, saneLink, serverKey)
else
price = algorithm
end
if not price then return end
local matchers = lib.GetMatchers(saneLink)
local total, count, diff = 0, 0, 0
local infoString = ""
for index, matcher in ipairs(matchers) do
if lib.IsValidMatcher(matcher, saneLink) then -- todo: shoudn't this already be valid from calling lib.GetMatchers(saneLink) ?
local value, MatchpriceArray = lib.GetMatcherValue(matcher, saneLink, price, serverKey)
price = value
count = count + 1
diff = diff + MatchpriceArray.diff
if MatchpriceArray.returnstring then
infoString = infoString.."\n"..MatchpriceArray.returnstring -- using two .. is faster than calling strjoin
end
end
end
if count > 1 then
diff = diff / count
end
if price > 0 then
return price, total, count, diff, infoString
end
end
function lib.GetMatcherDropdownList()
private.matcherlist = GetSetting("matcherlist")
if not private.matcherlist or #private.matcherlist == 0 then
lib.GetMatchers()
end
if not private.matcherlist or #private.matcherlist == 0 then
return
end
local dropdownlist = {}
for index, value in ipairs(private.matcherlist) do
dropdownlist[index] = tostring(index)..": "..tostring(private.matcherlist[index])
end
return dropdownlist
end
function lib.GetMatchers(itemLink)
local saneLink = SanitizeLink(itemLink)
private.matcherlist = GetSetting("matcherlist")
local engines = {}
local modules = AucAdvanced.GetAllModules()
for pos, engineLib in ipairs(modules) do
if engineLib.GetMatchArray then
if not engineLib.IsValidMatcher
or engineLib.IsValidMatcher(saneLink) then
local engine = engineLib.GetName()
tinsert(engines, engine)
end
end
end
local insetting = false
local stillactive = false
--check to see if there are any new matchers. If so, add them to the end of the running order.
--There is no check to see if matchers are missing, as this would destroy the saved order. Instead, invalid matchers can be called without errors.
if private.matcherlist then
for index, matcher in ipairs(engines) do
for i, j in ipairs(private.matcherlist) do
if matcher == j then insetting = true
end
end
if not insetting then
AucAdvanced.Print("AucAdvanced: New matcher found: "..tostring(matcher))
tinsert(private.matcherlist, matcher)
end
insetting = false
end
else
private.matcherlist = engines
end
AucAdvanced.Settings.SetSetting("matcherlist", private.matcherlist)
return private.matcherlist
end
function lib.IsValidMatcher(matcher, itemLink)
local saneLink = SanitizeLink(itemLink)
local engines = {}
local modules = AucAdvanced.GetAllModules()
for pos, engineLib in ipairs(modules) do
local engine = engineLib.GetName()
if engine == matcher and engineLib.GetMatchArray then
if engineLib.IsValidMatcher then
return engineLib.IsValidMatcher(saneLink)
end
return engineLib
end
end
return false
end
function lib.GetMatcherValue(matcher, itemLink, price, serverKey)
local saneLink = SanitizeLink(itemLink)
if (type(matcher) == "string") then
matcher = lib.IsValidMatcher(matcher, saneLink)
end
if not matcher then return end
--If matcher is not a table at this point, the following code will throw an "attempt to index a <something> value" type error
local matchArray = matcher.GetMatchArray(saneLink, price, serverKey)
if not matchArray then
matchArray = {}
matchArray.value = price
matchArray.diff = 0
end
return matchArray.value, matchArray
end
-- Signature conversion functions
-- Creates an AucAdvanced signature from an item link
function lib.GetSigFromLink(link)
local sig
local itype, id, suffix, factor, enchant = DecodeLink(link)
if itype == "item" then
if enchant ~= 0 then
sig = ("%d:%d:%d:%d"):format(id, suffix, factor, enchant)
elseif factor ~= 0 then
sig = ("%d:%d:%d"):format(id, suffix, factor)
elseif suffix ~= 0 then
sig = ("%d:%d"):format(id, suffix)
else
sig = tostring(id)
end
end
return sig
end
-- Creates an item link from an AucAdvanced signature
function lib.GetLinkFromSig(sig)
local id, suffix, factor, enchant = strsplit(":", sig)
local itemstring = format("item:%d:%d:0:0:0:0:%d:%d:0", id, enchant or 0, suffix or 0, factor or 0)
local name, link = GetItemInfo(itemstring)
link = SanitizeLink(link)
return link, name -- name is ignored by most calls
end
-- Decodes an AucAdvanced signature into numerical values
-- Can be compared to the return values from DecodeLink
function lib.DecodeSig(sig)
if type(sig) ~= "string" then return end
local id, suffix, factor, enchant = strsplit(":", sig)
id = tonumber(id)
if not id or id == 0 then return end
suffix = tonumber(suffix) or 0
factor = tonumber(factor) or 0
enchant = tonumber(enchant) or 0
return id, suffix, factor, enchant
end
-------------------------------------------------------------------------------
-- Statistical devices created by Matthew 'Shirik' Del Buono
-- For Auctioneer
-------------------------------------------------------------------------------
local sqrtpi = math.sqrt(math.pi);
local sqrtpiinv = 1/sqrtpi;
local sq2pi = math.sqrt(2*math.pi);
local pi = math.pi;
local exp = math.exp;
local bellCurveMeta = {
__index = {
SetParameters = function(self, mean, stddev)
if (stddev == 0) then
error("Standard deviation cannot be zero");
elseif (stddev ~= stddev) then
error("Standard deviation must be a real number");
end
if stddev < .1 then --need to prevent obsurdly small stddevs like 1e-11, as they cause freeze-ups
stddev = .1
end
self.mean = mean;
self.stddev = stddev;
self.param1 = 1/(stddev*sq2pi); -- Make __call a little faster where we can
self.param2 = 2*stddev^2;
end
},
-- Simple bell curve call
__call = function(self, x)
local n = self.param1*exp(-(x-self.mean)^2/self.param2);
-- if n ~= n then
-- DEFAULT_CHAT_FRAME:AddMessage("-----------------");
-- DevTools_Dump{param1 = self.param1, param2 = self.param2, x = x, mean = self.mean, stddev = self.stddev, exp = exp(-(x-self.mean)^2/self.param2)};
-- error(x.." produced NAN ("..tostring(n)..")");
-- end
return n;
end
}
-------------------------------------------------------------------------------
-- Creates a bell curve object that can then be manipulated to pass
-- as a PDF function. This is a recyclable object -- the mean and
-- standard deviation can be updated as necessary so that it does not have
-- to be regenerated
--
-- Note: This creates a bell curve with a standard deviation of 1 and
-- mean of 0. You will probably want to update it to your own desired
-- values by calling return:SetParameters(mean, stddev)
-------------------------------------------------------------------------------
function lib.GenerateBellCurve()
return setmetatable({mean=0, stddev=1, param1=sqrtpiinv, param2=2}, bellCurveMeta);
end
-- Dumps out market pricing information for debugging. Only handles bell curves for now.
function lib.DumpMarketPrice(itemLink, serverKey)
local modules = AucAdvanced.GetAllModules(nil, "Stat");
for pos, engineLib in ipairs(modules) do
local success, result = pcall(engineLib.GetItemPDF, itemLink, serverKey);
if success then
if getmetatable(result) == bellCurveMeta then
print(engineLib.GetName() .. ": Mean = " .. result.mean .. ", Standard Deviation = " .. result.stddev);
else
print(engineLib.GetName() .. ": Non-Standard PDF: " .. tostring(result));
end
else
print(engineLib.GetName() .. ": Reported error: " .. tostring(result));
end
end
end
--[[===========================================================================
--|| Deprecation Alert Functions
--||=========================================================================]]
do
local SOURCE_PATTERN = "([^\\/:]+:%d+): in function `([^\"']+)[\"']";
local seenCalls = {};
local uid = 0;
-------------------------------------------------------------------------------
-- Shows a deprecation alert. Indicates that a deprecated function has
-- been called and provides a stack trace that can be used to help
-- find the culprit.
-- @param replacementName (Optional) The displayable name of the replacement function
-- @param comments (Optional) Any extra text to display
-------------------------------------------------------------------------------
function lib.ShowDeprecationAlert(replacementName, comments)
local caller, source, functionName =
debugstack(3):match(SOURCE_PATTERN), -- Keep in mind this will be truncated to only the first in the tuple
debugstack(2):match(SOURCE_PATTERN); -- This will give us both the source and the function name
functionName = functionName .. "()";
-- Check for this source & caller combination
seenCalls[source] = seenCalls[source] or {};
if not seenCalls[source][caller] then
-- Not warned yet, so warn them!
seenCalls[source][caller]=true
-- Display it
AucAdvanced.Print(
"Auctioneer: "..
functionName .. " has been deprecated and was called by |cFF9999FF"..caller:match("^(.+)%.[lLxX][uUmM][aAlL]:").."|r. "..
(replacementName and ("Please use "..replacementName.." instead. ") or "")..
(comments or "")
);
geterrorhandler()(
"Deprecated function call occurred in Auctioneer API:\n {{{Deprecated Function:}}} "..functionName..
"\n {{{Source Module:}}} "..source:match("^(.+)%.[lLxX][uUmM][aAlL]:")..
"\n {{{Calling Module:}}} "..caller:match("^(.+)%.[lLxX][uUmM][aAlL]:")..
"\n {{{Available Replacement:}}} "..(replacementName or "None")..
(comments and "\n\n"..comments or "")
)
end
end
end
AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Advanced/CoreAPI.lua $", "$Rev: 4933 $")