TradeSkillMaster/Core/Service/MyAuctions/Core.lua

264 lines
8.5 KiB
Lua

-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster --
-- https://tradeskillmaster.com --
-- All Rights Reserved - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
local _, TSM = ...
local MyAuctions = TSM:NewPackage("MyAuctions")
local L = TSM.Include("Locale").GetTable()
local Database = TSM.Include("Util.Database")
local Event = TSM.Include("Util.Event")
local TempTable = TSM.Include("Util.TempTable")
local Log = TSM.Include("Util.Log")
local AuctionTracking = TSM.Include("Service.AuctionTracking")
local ItemInfo = TSM.Include("Service.ItemInfo")
local AuctionHouseWrapper = TSM.Include("Service.AuctionHouseWrapper")
local private = {
pendingDB = nil,
ahOpen = false,
pendingHashes = {},
expectedCounts = {},
auctionInfo = { numPosted = 0, numSold = 0, postedGold = 0, soldGold = 0 },
dbHashFields = {},
pendingFuture = nil,
}
-- ============================================================================
-- Module Functions
-- ============================================================================
function MyAuctions.OnInitialize()
private.pendingDB = Database.NewSchema("MY_AUCTIONS_PENDING")
:AddUniqueNumberField("index")
:AddNumberField("hash")
:AddBooleanField("isPending")
:AddNumberField("pendingAuctionId")
:AddIndex("index")
:Commit()
for field in AuctionTracking.DatabaseFieldIterator() do
if field ~= "index" and field ~= "auctionId" then
tinsert(private.dbHashFields, field)
end
end
Event.Register("AUCTION_HOUSE_SHOW", private.AuctionHouseShowEventHandler)
Event.Register("AUCTION_HOUSE_CLOSED", private.AuctionHouseHideEventHandler)
Event.Register("CHAT_MSG_SYSTEM", private.ChatMsgSystemEventHandler)
Event.Register("UI_ERROR_MESSAGE", private.UIErrorMessageEventHandler)
AuctionTracking.RegisterCallback(private.OnAuctionsUpdated)
end
function MyAuctions.CreateQuery()
local query = AuctionTracking.CreateQuery()
:LeftJoin(private.pendingDB, "index")
:InnerJoin(ItemInfo.GetDBForJoin(), "itemString")
:VirtualField("group", "string", private.AuctionsGetGroupText, "itemString")
if TSM.IsWowClassic() then
query:OrderBy("index", false)
else
query:OrderBy("saleStatus", false)
query:OrderBy("name", true)
query:OrderBy("auctionId", true)
end
return query
end
function MyAuctions.CancelAuction(auctionId)
local row = private.pendingDB:NewQuery()
:Equal("pendingAuctionId", auctionId)
:GetFirstResultAndRelease()
local hash = row:GetField("hash")
assert(hash)
Log.Info("Canceling (auctionId=%d, hash=%d)", auctionId, hash)
if TSM.IsWowClassic() then
CancelAuction(auctionId)
else
private.pendingFuture = AuctionHouseWrapper.CancelAuction(auctionId)
if not private.pendingFuture then
Log.PrintUser(L["Failed to cancel auction due to the auction house being busy. Ensure no other addons are scanning the AH and try again."])
return
end
private.pendingFuture:SetScript("OnDone", private.PendingFutureOnDone)
end
if private.expectedCounts[hash] and private.expectedCounts[hash] > 0 then
private.expectedCounts[hash] = private.expectedCounts[hash] - 1
else
private.expectedCounts[hash] = private.GetNumRowsByHash(hash) - 1
end
assert(private.expectedCounts[hash] >= 0)
assert(not row:GetField("isPending"))
row:SetField("isPending", true)
:Update()
row:Release()
tinsert(private.pendingHashes, hash)
end
function MyAuctions.CanCancel(index)
if TSM.IsWowClassic() then
local numPending = private.pendingDB:NewQuery()
:Equal("isPending", true)
:LessThanOrEqual("index", index)
:CountAndRelease()
return numPending == 0
else
return not private.pendingFuture
end
end
function MyAuctions.GetNumPending()
if TSM.IsWowClassic() then
return private.pendingDB:NewQuery()
:Equal("isPending", true)
:CountAndRelease()
else
return private.pendingFuture and 1 or 0
end
end
function MyAuctions.GetAuctionInfo()
if not private.ahOpen then
return
end
return private.auctionInfo.numPosted, private.auctionInfo.numSold, private.auctionInfo.postedGold, private.auctionInfo.soldGold
end
-- ============================================================================
-- Private Helper Functions
-- ============================================================================
function private.AuctionHouseShowEventHandler()
private.ahOpen = true
end
function private.AuctionHouseHideEventHandler()
private.ahOpen = false
if private.pendingFuture then
private.pendingFuture:Cancel()
private.pendingFuture = nil
end
end
function private.ChatMsgSystemEventHandler(_, msg)
if msg == ERR_AUCTION_REMOVED and #private.pendingHashes > 0 and TSM.IsWowClassic() then
local hash = tremove(private.pendingHashes, 1)
assert(hash)
Log.Info("Confirmed (hash=%d)", hash)
end
end
function private.UIErrorMessageEventHandler(_, _, msg)
if (msg == ERR_ITEM_NOT_FOUND or msg == ERR_NOT_ENOUGH_MONEY) and #private.pendingHashes > 0 and TSM.IsWowClassic() then
local hash = tremove(private.pendingHashes, 1)
assert(hash)
Log.Info("Failed to cancel (hash=%d)", hash)
if private.expectedCounts[hash] then
private.expectedCounts[hash] = private.expectedCounts[hash] + 1
end
end
end
function private.PendingFutureOnDone()
local result = private.pendingFuture:GetValue()
private.pendingFuture = nil
local hash = tremove(private.pendingHashes, 1)
assert(hash)
if result then
Log.Info("Confirmed (hash=%d)", hash)
else
Log.Info("Failed to cancel (hash=%d)", hash)
if private.expectedCounts[hash] then
private.expectedCounts[hash] = private.expectedCounts[hash] + 1
end
private.OnAuctionsUpdated()
AuctionTracking.QueryOwnedAuctions()
end
end
function private.GetNumRowsByHash(hash)
return private.pendingDB:NewQuery()
:Equal("hash", hash)
:CountAndRelease()
end
function private.AuctionsGetGroupText(itemString)
local groupPath = TSM.Groups.GetPathByItem(itemString)
if not groupPath then
return ""
end
return groupPath
end
function private.OnAuctionsUpdated()
local minPendingIndexByHash = TempTable.Acquire()
local numByHash = TempTable.Acquire()
local query = AuctionTracking.CreateQuery()
:OrderBy("index", true)
for _, row in query:Iterator() do
local index = row:GetField("index")
local hash = row:CalculateHash(private.dbHashFields)
numByHash[hash] = (numByHash[hash] or 0) + 1
if not minPendingIndexByHash[hash] and private.pendingDB:GetUniqueRowField("index", index, "isPending") then
minPendingIndexByHash[hash] = index
end
end
local numUsed = TempTable.Acquire()
private.pendingDB:TruncateAndBulkInsertStart()
for _, row in query:Iterator() do
local hash = row:CalculateHash(private.dbHashFields)
assert(numByHash[hash] > 0)
local expectedCount = private.expectedCounts[hash]
local isPending = nil
if not expectedCount then
-- this was never pending
isPending = false
elseif numByHash[hash] <= expectedCount then
-- this is no longer pending
isPending = false
private.expectedCounts[hash] = nil
elseif row:GetField("index") >= (minPendingIndexByHash[hash] or math.huge) then
local numPending = numByHash[hash] - expectedCount
assert(numPending > 0)
numUsed[hash] = (numUsed[hash] or 0) + 1
isPending = numUsed[hash] <= numPending
else
-- it's a later auction which is pending
isPending = false
end
private.pendingDB:BulkInsertNewRow(row:GetField("index"), hash, isPending, row:GetField("auctionId"))
end
private.pendingDB:BulkInsertEnd()
TempTable.Release(numByHash)
TempTable.Release(numUsed)
TempTable.Release(minPendingIndexByHash)
-- update the player's auction status
private.auctionInfo.numPosted = 0
private.auctionInfo.numSold = 0
private.auctionInfo.postedGold = 0
private.auctionInfo.soldGold = 0
for _, row in query:Iterator() do
local itemString, saleStatus, buyout, currentBid, stackSize = row:GetFields("itemString", "saleStatus", "buyout", "currentBid", "stackSize")
if saleStatus == 1 then
private.auctionInfo.numSold = private.auctionInfo.numSold + 1
-- if somebody did a buyout, then bid will be equal to buyout, otherwise it'll be the winning bid
private.auctionInfo.soldGold = private.auctionInfo.soldGold + currentBid
else
private.auctionInfo.numPosted = private.auctionInfo.numPosted + 1
if ItemInfo.IsCommodity(itemString) then
private.auctionInfo.postedGold = private.auctionInfo.postedGold + (buyout * stackSize)
else
private.auctionInfo.postedGold = private.auctionInfo.postedGold + buyout
end
end
end
query:Release()
end