Compare commits

...

46 Commits

Author SHA1 Message Date
Jørgen Lien Sellæg 9f29b4ab02 asmart avg 4 months ago
Jørgen Lien Sellæg 51a389d780 add ah restock command 4 months ago
Jørgen Lien Sellæg 7952573bab update crafting and warehousing 4 months ago
Jørgen Lien Sellæg 6d7a9e90b9 ignore local wtf and omanfred 4 months ago
Jørgen Lien Sellæg 4d760e9b79 update player gold tab 4 months ago
Jørgen Lien Sellæg 697be43d7a link and go 4 months ago
Jørgen Lien Sellæg c14a5f8ca7 add scan complete flash toggle 4 months ago
Jørgen Lien Sellæg e938497873 refresh crafting queue on buyout 4 months ago
Jørgen Lien Sellæg 01371c7c2d refine auctiondb buyout log 4 months ago
Jørgen Lien Sellæg 33e6d6718a log bag count on buyout 4 months ago
Jørgen Lien Sellæg 94590f30ed tweak auctiondb buyout log 4 months ago
Jørgen Lien Sellæg 96b210e65d log auctiondb info on buyout 4 months ago
Jørgen Lien Sellæg 800e7a17c3 add more tips 4 months ago
Jørgen Lien Sellæg 0e24db1153 expand custom price function docs 4 months ago
Jørgen Lien Sellæg 9f2205850e document custom price sources 4 months ago
Jørgen Lien Sellæg 472f8d2be6 update auctiondb sync and accounting graph 4 months ago
Jørgen Lien Sellæg 0fbfb60c9a fix channel name parsing 4 months ago
Jørgen Lien Sellæg 0f922cf180 fix channel name resolution 4 months ago
Jørgen Lien Sellæg 8b9106c975 shrink auctioning status bar 4 months ago
Jørgen Lien Sellæg 469e692eb5 remove getall and add inventory scan 4 months ago
Jørgen Lien Sellæg 1cfbdc4c4a fix channel name parsing 4 months ago
Jørgen Lien Sellæg f2e48bb0cd log channel sync prefix mismatch 4 months ago
Jørgen Lien Sellæg 1ff65947fe add channel sync debug logging 4 months ago
Jørgen Lien Sellæg 6157f813bf remove getall and add inventory buttons 4 months ago
Jørgen Lien Sellæg d6f0b2ee21 batch auctiondb channel sync 4 months ago
Jørgen Lien Sellæg 7483aa3ad3 pause auctiondb scan on tab change 4 months ago
Jørgen Lien Sellæg b181ef7bc6 add scan pause resume 4 months ago
Jørgen Lien Sellæg 4ae12602b0 add meta stuff 4 months ago
Jørgen Lien Sellæg caf152df3a update scan 4 months ago
Jørgen Lien Sellæg 4081ece2e9 add messages in chat 4 months ago
Jørgen Lien Sellæg c443d40d2f async send of messages to avoid fps drop 4 months ago
Jørgen Lien Sellæg 505f2304d4 fix encode 4 months ago
Jørgen Lien Sellæg 10047e31ab codec 4 months ago
Jørgen Lien Sellæg 95f5bec324 update to constraints 4 months ago
Jørgen Lien Sellæg 36e7bc0be5 change api 4 months ago
Jørgen Lien Sellæg c6820172a9 channel sync? 4 months ago
Jørgen Lien Sellæg 8ec1d3fd08 add more custom price options 4 months ago
Jørgen Lien Sellæg e9177f4a98 bag scan and bank scan seperate 4 months ago
Jørgen Lien Sellæg 4d78f056ff Merge branch 'no-attributes-dev' into dev 4 months ago
Jørgen Sverre Lien Sellæg d26abcc6ae add group scan 4 months ago
Jørgen Lien Sellæg a5fb892a13 Revert "add attributes to keep windows endings in files" 4 months ago
Jørgen Sverre Lien Sellæg 8b77bede68 skip wipe of data when searching 4 months ago
Jørgen Sverre Lien Sellæg 1a80d3f148 attempt to update scan data on searches 4 months ago
Jørgen Lien Sellæg 2f5db70887 add attributes to keep windows endings in files 5 months ago
Jørgen Lien Sellæg e722482be5 stores auction data that has been scanned up to the point of interruption 5 months ago
Jørgen Lien Sellæg 8c661ab24f add stuff 5 months ago
  1. 129
      .gitignore
  2. 22
      AGENTS.md
  3. 133
      README.md
  4. 1
      TradeSkillMaster/Auction/AuctionControl.lua
  5. 49
      TradeSkillMaster/Auction/AuctionScanning.lua
  6. 71
      TradeSkillMaster/Core/Prices.lua
  7. 40
      TradeSkillMaster/GUI/BuildPage.lua
  8. 2
      TradeSkillMaster/Libs/LibAuctionScan-1.0/LibAuctionScan-1.0.lua
  9. 3
      TradeSkillMaster/TradeSkillMaster.lua
  10. 2
      TradeSkillMaster/TradeSkillMaster.toc
  11. 14
      TradeSkillMaster_Accounting/Locale/enUS.lua
  12. 7
      TradeSkillMaster_Accounting/Modules/data.lua
  13. 200
      TradeSkillMaster_Accounting/Modules/gui.lua
  14. 89
      TradeSkillMaster_Accounting/TradeSkillMaster_Accounting.lua
  15. 6
      TradeSkillMaster_AuctionDB/Locale/deDE.lua
  16. 26
      TradeSkillMaster_AuctionDB/Locale/enUS.lua
  17. 6
      TradeSkillMaster_AuctionDB/Locale/esES.lua
  18. 6
      TradeSkillMaster_AuctionDB/Locale/esMX.lua
  19. 6
      TradeSkillMaster_AuctionDB/Locale/frFR.lua
  20. 6
      TradeSkillMaster_AuctionDB/Locale/koKR.lua
  21. 6
      TradeSkillMaster_AuctionDB/Locale/ptBR.lua
  22. 6
      TradeSkillMaster_AuctionDB/Locale/ruRU.lua
  23. 6
      TradeSkillMaster_AuctionDB/Locale/zhCN.lua
  24. 6
      TradeSkillMaster_AuctionDB/Locale/zhTW.lua
  25. 436
      TradeSkillMaster_AuctionDB/Modules/ChannelSync.lua
  26. 293
      TradeSkillMaster_AuctionDB/Modules/GUI.lua
  27. 303
      TradeSkillMaster_AuctionDB/Modules/Scanning.lua
  28. 7
      TradeSkillMaster_AuctionDB/Modules/config.lua
  29. 63
      TradeSkillMaster_AuctionDB/Modules/data.lua
  30. 30
      TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.lua
  31. 5
      TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.toc
  32. 5
      TradeSkillMaster_Auctioning/locale/enUS.lua
  33. 30
      TradeSkillMaster_Auctioning/modules/GUI.lua
  34. 8
      TradeSkillMaster_Auctioning/modules/PostScan.lua
  35. 10
      TradeSkillMaster_Auctioning/modules/ResetScan.lua
  36. 20
      TradeSkillMaster_Auctioning/modules/manage.lua
  37. 3
      TradeSkillMaster_Crafting/Locale/enUS.lua
  38. 427
      TradeSkillMaster_Crafting/Modules/CraftingGUI.lua
  39. 4
      TradeSkillMaster_Crafting/TradeSkillMaster_Crafting.lua
  40. 4
      TradeSkillMaster_Shopping/modules/Search.lua
  41. 12
      TradeSkillMaster_Shopping/modules/Util.lua
  42. 5
      TradeSkillMaster_Warehousing/Locale/enUS.lua
  43. 13
      TradeSkillMaster_Warehousing/Modules/bankui.lua
  44. 75
      TradeSkillMaster_Warehousing/Modules/move.lua
  45. 5
      TradeSkillMaster_Warehousing/TradeSkillMaster_Warehousing.lua
  46. 40
      bin/link_addons.sh
  47. 32
      bin/sshfs-mounts.sh
  48. 78
      scripts/link_and_go.sh

129
.gitignore vendored

@ -1,7 +1,134 @@
*~
.env
.DS_Store
.release
.install
.lua/*
.vscode
.idea
.idea.
02a7e8db-fd8d-4213-8d9c-ab66009a487d.zip
408e9dd4-e03b-4a89-b1f1-a488136d6283.zip
4a37aaf8-9aa6-4246-8c0d-1cafc6b994a1.zip
4dfc20a9-9140-4893-b179-be02e48463be.zip
90bb9009-f7ef-46ff-8a2e-38192b1823a9.zip
9bc9a709-ad9d-4536-9841-f0c5f04b6822.zip
a1464a44-7ec6-4cec-896d-d24131e2ac4a.zip
AI_VoiceOver
AI_VoiceOverData_Vanilla
AllStats
AtlasLoot
AtlasLoot_BurningCrusade
AtlasLoot_Cache
AtlasLoot_Crafting_OriginalWoW
AtlasLoot_Crafting_TBC
AtlasLoot_Crafting_Wrath
AtlasLoot_OriginalWoW
AtlasLoot_Vanity
AtlasLoot_WorldEvents
AtlasLoot_WrathoftheLichKing
AutoQuest
b823d87f-b909-4698-9ac1-a6d9934e618e.zip
Bagnon
Bagnon_Config
Bagnon_Forever
Bagnon_GuildBank
Bagnon_Tooltips
Blizzard_AchievementUI
Blizzard_ArenaUI
Blizzard_AuctionUI
Blizzard_BarbershopUI
Blizzard_BattlefieldMinimap
Blizzard_BindingUI
Blizzard_Calendar
Blizzard_CombatLog
Blizzard_CombatText
Blizzard_DebugTools
Blizzard_GlyphUI
Blizzard_GMChatUI
Blizzard_GMSurveyUI
Blizzard_GuildBankUI
Blizzard_InspectUI
Blizzard_ItemSocketingUI
Blizzard_MacroUI
Blizzard_RaidUI
Blizzard_TalentUI
Blizzard_TimeManager
Blizzard_TokenUI
Blizzard_TradeSkillUI
Blizzard_TrainerUI.old
c81d84c4-62a1-4839-b245-b48123ba3973.zip
Chatter
d5d9dbc2-4e0f-4472-9301-ee99107f0039.zip
d613bf63-a1bb-4311-b82a-4cbe7c2fd272.zip
DBM-AQ20
DBM-AQ40
DBM-BlackTemple
DBM-BurningCrusade
DBM-BWL
DBM-Core
DBM-GruulsLair
DBM-GUI
DBM-Hyjal
DBM-Karazhan
DBM-MagtheridonsLair
DBM-MC
DBM-Naxx
DBM-Onyxia
DBM-Outlands
DBM-Party-BC
DBM-Party-Vanilla
DBM-PvP
DBM-Serpentshrine
DBM-SpellTimers
DBM-Sunwell
DBM-TheEye
DBM-WorldEvents
DBM-ZG
DBM-ZulAman
dcfbd3bc-59dd-4f48-b9c0-4b7db6beb0ec.zip
Decursive
Details
Details_Compare2
Details_DataStorage
Details_EncounterDetails
Details_RaidCheck
Details_Streamer
Details_TargetCaller
Details_TinyThreat
Details_Vanguard
df1a0fb8-4b69-4468-99ad-6fdb2ef72b78.zip
e0910ca2-c063-47de-a651-8705d6d70be8.zip
ElvUI
ElvUI_AddOnSkins
ElvUI_Enhanced
ElvUI_EnhancedFriendsList
ElvUI_ExtraActionBars
ElvUI_OptionsUI
ESN_Rare
f024ba61-e2a4-47a5-86c7-3d1d5542cc5d.zip
f33cd75c-e6a9-4330-ad26-f94483d0f8a7.zip
fa9e16f2-0fbb-4f77-b4b5-af33af8987e8.zip
GatherMate2
.git
.github
LootCollector
New Folder
Omen
OmniCC
OmniCC_Config
Outfitter
Passloot
pfQuest
pfQuest-ascension
README.md
SilverDragon
SilverDragon_Data
tmp
TomTom
TrinketMenu
WeakAuras
WeakAurasArchive
WeakAurasModelPaths
WeakAurasOptions
WTF/
omanfred/

22
AGENTS.md

@ -0,0 +1,22 @@
# Repository Guidelines
## Project Structure & Module Organization
TradeSkillMaster is a World of Warcraft addon composed of a core module and several feature modules. Each module lives in its own top-level folder (for example `TradeSkillMaster/`, `TradeSkillMaster_Auctioning/`) and contains a `.toc` manifest plus one or more `.lua` files. Most modules also include `ChangeLog.txt` and `LICENSE.txt`. The root contains repository metadata like `README.md` and `.github/` templates.
## Build, Test, and Development Commands
There is no build system in this repository. To run locally, copy the desired module folders into your WoW AddOns directory (for example `World of Warcraft/Interface/AddOns/TradeSkillMaster_Auctioning`) and use `/reload` in-game after changes. Manual in-game verification is the default test loop.
## Coding Style & Naming Conventions
Lua files use tab indentation in existing code; follow that style when editing. Keep module folder names and primary files aligned (`TradeSkillMaster_Mailing/TradeSkillMaster_Mailing.lua`, `TradeSkillMaster_Mailing.toc`). Use clear, descriptive function and local variable names, and avoid introducing new globals unless required by the addon API.
## Testing Guidelines
No automated test framework is present. Validate changes by enabling the addon in-game and exercising the affected feature paths. When changes are module-specific, test both module initialization (login/reload) and the primary UI workflows.
## Commit & Pull Request Guidelines
Recent commit messages are short and action-focused (for example “add group scan”, “skip wipe of data when searching”). Keep messages concise and imperative when possible. For pull requests, follow the template in `.github/PULL_REQUEST_TEPMLATE.md`: include a clear description, reference issues with `Fixes #<id>`, select a change type, describe test steps, and complete the self-review checklist.
## Agent Notes
When adding or renaming modules, update the `.toc` and matching `.lua` filenames together, and ensure any new files are listed in the `.toc` manifest. Keep changes scoped to the relevant module folder to avoid cross-module regressions. Increment numeric `Version`/`X-Curse-Packaged-Version` in `TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.toc` and `Version` in `TradeSkillMaster/TradeSkillMaster.toc` on each commit.
Local Ascension client paths used in this workspace:
- AddOns: `/home/zalox/Games/ascension-wow/drive_c/Program Files/Ascension Launcher/resources/client/Interface/AddOns`
- SavedVariables: `/home/zalox/Games/ascension-wow/drive_c/Program Files/Ascension Launcher/resources/client/WTF/Account/omanfred/SavedVariables`

133
README.md

@ -1,3 +1,132 @@
# TradeSkillMaster
# TradeSkillMaster (Ascension.gg)
This is the repository for Trade Skill Master (TSM). Modified for Ascension.gg.
TradeSkillMaster (TSM) is a World of Warcraft addon suite. This fork is tailored for Ascension.gg and includes the core addon plus feature modules.
## Modules
Each module lives in its own top-level folder and is loaded by its `.toc` manifest:
- `TradeSkillMaster` (core)
- `TradeSkillMaster_Accounting`
- `TradeSkillMaster_AuctionDB`
- `TradeSkillMaster_Auctioning`
- `TradeSkillMaster_Crafting`
- `TradeSkillMaster_Destroying`
- `TradeSkillMaster_ItemTracker`
- `TradeSkillMaster_Mailing`
- `TradeSkillMaster_Shopping`
- `TradeSkillMaster_Warehousing`
## Install (Local)
Copy the desired module folders into your AddOns directory and reload the game:
- AddOns: `/home/zalox/Games/ascension-wow/drive_c/Program Files/Ascension Launcher/resources/client/Interface/AddOns`
- SavedVariables: `/home/zalox/Games/ascension-wow/drive_c/Program Files/Ascension Launcher/resources/client/WTF/Account/omanfred/SavedVariables`
In-game, use `/reload` after changes.
## Development Notes
- Lua files use tab indentation.
- Keep `.toc` and primary `.lua` filenames aligned with the module name.
- If adding files, list them in the module `.toc`.
- Versions are tracked in `TradeSkillMaster/TradeSkillMaster.toc` and `TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.toc`.
## Custom Price Sources (Full Reference)
TSM price strings can reference built-in data sources and combine them with math. Source names are case-insensitive.
### Core sources
- `VendorBuy`: Buy from vendor.
- `VendorSell`: Sell to vendor.
- `Disenchant`: Disenchant value (if available).
### AuctionDB sources
- `DBMarket`: AuctionDB market value (scan/app historical average).
- `DBMinBuyout`: AuctionDB minimum buyout from the most recent scan data.
### Accounting sources
- `avgSell`: Average sell price.
- `avgBuy`: Average buy price.
- `maxSell`: Maximum sell price.
- `maxBuy`: Maximum buy price.
- `minSell`: Minimum sell price.
- `minBuy`: Minimum buy price.
### Crafting sources
- `Crafting`: Crafting cost.
- `matPrice`: Crafting material cost.
### Optional addon sources
Available only if the addon is installed and enabled.
- `AucAppraiser`: Auctioneer - Appraiser.
- `AucMinBuyout`: Auctioneer - Minimum Buyout.
- `AucMarket`: Auctioneer - Market Value.
- `AtrValue`: Auctionator - Auction Value.
### Functions and syntax
- Math operators: `+`, `-`, `*`, `/`, `^`
- Money literals: `100g`, `50s`, `25c`, or combined `12g 34s 56c`
- Convert another item: `convert(priceSource, itemLink)` or `convert(priceSource, item:12345)`
### Function: min(...)
Returns the smallest of the provided values. Ignores invalid values.
Example: `min(dbmarket, 250g)`
### Function: max(...)
Returns the largest of the provided values. Ignores invalid values.
Example: `max(dbminbuyout, 100g)`
### Function: avg(...)
Returns the average (rounded) of the provided values. Ignores invalid values.
Example: `avg(dbmarket, dbminbuyout)`
### Function: first(...)
Returns the first valid value from left to right.
Example: `first(dbminbuyout, dbmarket, 100g)`
### Function: check(checkValue, ifValue, elseValue)
Returns `ifValue` when `checkValue` is greater than 0, otherwise `elseValue`.
Example: `check(dbminbuyout, dbminbuyout, dbmarket)`
### Function: ifgt(a, b, ifValue, elseValue)
Returns `ifValue` when `a` is greater than `b`, otherwise `elseValue`.
Example: `ifgt(dbminbuyout, 200g, dbminbuyout, dbmarket)`
### Function: ifgte(a, b, ifValue, elseValue)
Returns `ifValue` when `a` is greater than or equal to `b`, otherwise `elseValue`.
Example: `ifgte(dbmarket, 500g, 500g, dbmarket)`
### Function: iflt(a, b, ifValue, elseValue)
Returns `ifValue` when `a` is less than `b`, otherwise `elseValue`.
Example: `iflt(dbminbuyout, 50g, 50g, dbminbuyout)`
### Function: iflte(a, b, ifValue, elseValue)
Returns `ifValue` when `a` is less than or equal to `b`, otherwise `elseValue`.
Example: `iflte(dbmarket, 20g, dbmarket, 20g)`
### Function: ifeq(a, b, ifValue, elseValue)
Returns `ifValue` when `a` equals `b`, otherwise `elseValue`.
Example: `ifeq(dbminbuyout, dbmarket, dbminbuyout, dbmarket)`
### Function: round(value[, step])
Rounds to the nearest `step` (default `1`).
Example: `round(dbmarket, 1g)`
### Function: roundup(value[, step])
Rounds up to the nearest `step` (default `1`).
Example: `roundup(dbmarket, 5g)`
### Function: rounddown(value[, step])
Rounds down to the nearest `step` (default `1`).
Example: `rounddown(dbmarket, 5g)`
### Examples
- `dbmarket * 0.9` (90% of market value)
- `max(dbminbuyout, 100g)` (at least 100g)
- `min(dbmarket, 250g)` (cap at 250g)
- `ifgt(dbminbuyout, 200g, dbminbuyout, dbmarket)` (prefer min buyout if above 200g)
Notes:
- Sources return values only when their module has data (for example, no AuctionDB scan means empty values).
- Use `DBMarket` for stable pricing and `DBMinBuyout` for recent lows.
## Testing
There is no automated test framework. Validate changes in-game by:
1. Logging in / reloading to verify module initialization.
2. Exercising the relevant UI workflows.

1
TradeSkillMaster/Auction/AuctionControl.lua

@ -277,6 +277,7 @@ function private:AUCTION_ITEM_LIST_UPDATE()
end
TSM:AuctionControlCallback("OnBuyout", prevAuction)
TSMAPI:FireEvent("TSM:AUCTIONCONTROL:ITEMBOUGHT", prevAuction)
end
end

49
TradeSkillMaster/Auction/AuctionScanning.lua

@ -14,7 +14,7 @@ TSMAPI.AuctionScan = {}
local RETRY_DELAY = 2
local MAX_RETRIES = 4
local BASE_DELAY = 0.10 -- time to delay for before trying to scan a page again when it isn't fully loaded
local private = { callbackHandler = nil, query = {}, options = {}, data = {}, isScanning = nil }
local private = { callbackHandler = nil, query = {}, options = {}, data = {}, isScanning = nil, isPaused = nil, pauseReason = nil }
TSMAPI:RegisterForTracing(private, "TradeSkillMaster.AuctionScanning_private")
local scanCache = {}
@ -34,7 +34,7 @@ local function eventHandler(event)
-- auction house was closed, make sure all scanning is stopped
AuctionScanning:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
private.auctionHouseShown = false
DoCallback("INTERRUPTED")
DoCallback("INTERRUPTED", private.data)
private:StopScanning()
elseif event == "AUCTION_ITEM_LIST_UPDATE" then
-- gets called whenever the AH window is updated (something is shown in the results section)
@ -147,6 +147,7 @@ function TSMAPI.AuctionScan:RunQuery(query, callbackHandler, resolveSellers, max
-- setup other stuff
wipe(private.data)
private.isScanning = true
private.isPaused = nil
private.callbackHandler = callbackHandler
private.resolveSellers = resolveSellers
private.scanType = "query"
@ -179,6 +180,7 @@ function TSMAPI.AuctionScan:ScanLastPage(callbackHandler)
-- setup other stuff
wipe(private.data)
private.isScanning = true
private.isPaused = nil
private.callbackHandler = callbackHandler
private.scanType = "lastPage"
@ -189,7 +191,7 @@ end
-- sends a query to the AH frame once it is ready to be queried (uses frame as a delay)
function private:SendQuery()
if not private.isScanning then return end
if not private.isScanning or private.isPaused then return end
if CanSendAuctionQuery() then
-- stop delay timer
@ -208,7 +210,7 @@ end
--scans the currently shown page of auctions and collects all the data
function private:ScanAuctions()
if not private.isScanning then return end
if not private.isScanning or private.isPaused then return end
local shown, total = GetNumAuctionItems("list")
local totalPages = ceil(total / NUM_AUCTION_ITEMS_PER_PAGE)
@ -303,6 +305,9 @@ function private:ScanAuctions()
end
-- query the next page and continue scanning
if private.isPaused then
return
end
private:SendQuery()
end
@ -363,14 +368,47 @@ function private:StopScanning()
TSMAPI:CancelFrame("updateDelay")
AuctionScanning:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
private.isScanning = nil
private.isPaused = nil
private.pauseReason = nil
private.pageTemp = nil
end
-- API for stopping the scan
-- returns true/false if we were/weren't actually scanning
function TSMAPI.AuctionScan:StopScan()
function TSMAPI.AuctionScan:StopScan(force)
if force == nil then
force = true
end
if private.isPaused and private.pauseReason == "AuctionDB" and not force then return false end
private:StopScanning()
TSM:StopGeneratingQueries()
return true
end
function TSMAPI.AuctionScan:PauseScan(reason)
if not private.isScanning or private.isPaused then return false end
private.isPaused = true
private.pauseReason = reason
TSMAPI:CancelFrame("queryDelay")
TSMAPI:CancelFrame("updateDelay")
AuctionScanning:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
return true
end
function TSMAPI.AuctionScan:ResumeScan()
if not private.isScanning or not private.isPaused then return false end
private.isPaused = nil
private.pauseReason = nil
private:SendQuery()
return true
end
function TSMAPI.AuctionScan:IsPaused()
return private.isPaused and true or false
end
function TSMAPI.AuctionScan:IsScanning()
return private.isScanning and true or false
end
@ -422,6 +460,7 @@ function TSMAPI.AuctionScan:GetNumPages(query, callbackHandler)
-- setup other stuff
wipe(private.data)
private.isScanning = true
private.isPaused = nil
private.callbackHandler = callbackHandler
private.scanType = "numPages"

71
TradeSkillMaster/Core/Prices.lua

@ -28,6 +28,14 @@ local MATH_FUNCTIONS = {
["max"] = "_max",
["first"] = "_first",
["check"] = "_check",
["ifgt"] = "_ifgt",
["ifgte"] = "_ifgte",
["iflt"] = "_iflt",
["iflte"] = "_iflte",
["ifeq"] = "_ifeq",
["round"] = "_round",
["roundup"] = "_roundup",
["rounddown"] = "_rounddown",
}
function TSMAPI:GetPriceSources()
@ -68,7 +76,7 @@ end
-- validates a price string that was passed into TSMAPI:ParseCustomPrice
local supportedOperators = { "+", "-", "*", "/" }
local supportedOperators = { "+", "-", "*", "/", "^" }
local function ParsePriceString(str, badPriceSource)
if tonumber(str) then
return function() return tonumber(str) end
@ -396,6 +404,67 @@ local function ParsePriceString(str, badPriceSource)
elseValue = elseValue or NAN
return check > 0 and ifValue or elseValue
end
local function _ifgt(...)
local a, b, c, d = ...
if isNAN(a) or isNAN(b) then return NAN end
if a > b then
return (c and not isNAN(c)) and c or NAN
end
return (d and not isNAN(d)) and d or NAN
end
local function _ifgte(...)
local a, b, c, d = ...
if isNAN(a) or isNAN(b) then return NAN end
if a >= b then
return (c and not isNAN(c)) and c or NAN
end
return (d and not isNAN(d)) and d or NAN
end
local function _iflt(...)
local a, b, c, d = ...
if isNAN(a) or isNAN(b) then return NAN end
if a < b then
return (c and not isNAN(c)) and c or NAN
end
return (d and not isNAN(d)) and d or NAN
end
local function _iflte(...)
local a, b, c, d = ...
if isNAN(a) or isNAN(b) then return NAN end
if a <= b then
return (c and not isNAN(c)) and c or NAN
end
return (d and not isNAN(d)) and d or NAN
end
local function _ifeq(...)
local a, b, c, d = ...
if isNAN(a) or isNAN(b) then return NAN end
if a == b then
return (c and not isNAN(c)) and c or NAN
end
return (d and not isNAN(d)) and d or NAN
end
local function _round(...)
local a, b = ...
if isNAN(a) then return NAN end
b = b or 1
if isNAN(b) or b == 0 then return NAN end
return floor(a / b + 0.5) * b
end
local function _roundup(...)
local a, b = ...
if isNAN(a) then return NAN end
b = b or 1
if isNAN(b) or b == 0 then return NAN end
return ceil(a / b) * b
end
local function _rounddown(...)
local a, b = ...
if isNAN(a) then return NAN end
b = b or 1
if isNAN(b) or b == 0 then return NAN end
return floor(a / b) * b
end
local values = {}
for i, params in ipairs({%s}) do
local itemString, key, extraParam = unpack(params)

40
TradeSkillMaster/GUI/BuildPage.lua

@ -74,11 +74,31 @@ local function CreateCustomPriceFrame()
text = TSMAPI.Design:GetInlineColor("category")..L["More Advanced Methods"].."|r",
relativeWidth = 1,
},
{
type = "Label",
text = "Combine sources, math, and functions to build rules. Sources are case-insensitive.",
relativeWidth = 1,
},
{
type = "Label",
text = format("See %s for more info.", TSMAPI.Design:GetInlineColor("link").."https://tinyurl.com/tsm2Ascension|r"),
relativeWidth = 1,
},
{
type = "Label",
text = "Functions: min, max, avg, first, check, ifgt, ifgte, iflt, iflte, ifeq, round, roundup, rounddown",
relativeWidth = 1,
},
{
type = "Label",
text = "Convert other items: convert(dbmarket, item:12345) or convert(dbmarket, |cffffffff|Hitem:12345|h[Item]|h|r)",
relativeWidth = 1,
},
{
type = "Label",
text = "Test strings: /tsm price <ItemLink> <Price String>",
relativeWidth = 1,
},
{
type = "HeadingLine",
relativeWidth = 1,
@ -113,6 +133,26 @@ local function CreateCustomPriceFrame()
text = "max(vendor, 120% crafting)",
relativeWidth = 1,
},
{
type = "Label",
text = "dbmarket * 0.9",
relativeWidth = 1,
},
{
type = "Label",
text = "ifgt(dbminbuyout, 200g, dbminbuyout, dbmarket)",
relativeWidth = 1,
},
{
type = "Label",
text = "round(dbmarket, 1g)",
relativeWidth = 1,
},
{
type = "Label",
text = "convert(dbmarket, item:36901) * 1.05",
relativeWidth = 1,
},
}
TSMAPI:BuildPage(container, page)

2
TradeSkillMaster/Libs/LibAuctionScan-1.0/LibAuctionScan-1.0.lua

@ -522,7 +522,7 @@ do
if interrupted then
-- fires if the scan was interrupted
DoCallback("SCAN_INTERRUPTED")
DoCallback("SCAN_INTERRUPTED", status.data)
else
-- fires if the scan completed sucessfully
DoCallback("SCAN_COMPLETE", status.data)

3
TradeSkillMaster/TradeSkillMaster.lua

@ -735,6 +735,9 @@ function TSMAPI:GetItemPrices(itemLink)
-- Use TSM:GetCustomPrice to get avgBuy price
prices.avgBuy = TSM:GetCustomPrice("avgBuy", itemString) or 0
-- Use TSM:GetCustomPrice to get smartAvgBuy price
prices.smartAvgBuy = TSM:GetCustomPrice("smartAvgBuy", itemString) or 0
-- Use TSM:GetCustomPrice to get avgSell price
prices.avgSell = TSM:GetCustomPrice("avgSell", itemString) or 0

2
TradeSkillMaster/TradeSkillMaster.toc

@ -2,7 +2,7 @@
## Title: |cff00fe00TradeSkillMaster: Revived|r
## Notes: Core addon for the TradeSkillMaster suite, revived for Wrath of the Lich King. Does nothing without modules installed.
## Author: Sapu94, Bart39, Gnomezilla [Warmane-Icecrown(A)], BlueAo [Warmane], andrew6180, Yoshiyuka, DimaSheiko, and other contributors...
## Version: Rev668
## Version: 2.3.24
## SavedVariables: TradeSkillMasterAppDB, AscensionTSMDB
## OptionalDeps: AccurateTime, Ace3, LibDataBroker-1.1, LibDBIcon-1.0, LibExtraTip, TipHelper, LibParse, LibCompress, LibGraph-2.0, SharedMedia, TheUndermineJournal, TheUndermineJournalGE
## X-Embeds: AccurateTime, Ace3, LibDataBroker-1.1, LibDBIcon-1.0, LibExtraTip, TipHelper, LibParse, LibCompress, LibGraph-2.0

14
TradeSkillMaster_Accounting/Locale/enUS.lua

@ -15,7 +15,7 @@ if not L then return end
L["|cffff0000IMPORTANT:|r When TSM_Accounting last saved data for this realm, it was too big for WoW to handle, so old data was automatically trimmed in order to avoid corruption of the saved variables. The last %s of %s data has been preserved."] = true
L["%s ago"] = true
L["Accounting has not yet collected enough information for this tab. This is likely due to not having recorded enough data points or not seeing any significant fluctuations (over 1k gold) in your gold on hand."] = true
L["Accounting has not yet collected enough information for this tab. This is likely due to not having recorded enough data points or not seeing any significant fluctuations in your gold on hand."] = true
L["Activity Type"] = true
L["All"] = true
L["Amount"] = true
@ -24,9 +24,11 @@ L["Average Prices:"] = true
L["Avg Buy Price"] = true
L["Avg Resale Profit"] = true
L["Avg Sell Price"] = true
L["Smart Avg Buy Price:"] = true
L["Smart Avg Buy Price"] = true
L["Back to Previous Page"] = true
L["Balance"] = true
L["Below is a graph of the your character's gold on hand over time.\n\nThe x-axis is time and goes from %s to %s\nThe y-axis is thousands of gold."] = true
L["Below is a graph of the your character's gold on hand over time.\n\nThe x-axis is time and goes from %s to %s\nThe y-axis is gold."] = true
L["Bought"] = true
L["Buyer/Seller"] = true
L["Cancelled Since Last Sale:"] = true
@ -81,6 +83,8 @@ L["Market Value Source"] = true
L["Market Value"] = true
L["Max Buy Price"] = true
L["Max Sell Price"] = true
L["Min Buy Price"] = true
L["Min Sell Price"] = true
L["None"] = true
L["Options"] = true
L["Other Income"] = true
@ -143,6 +147,12 @@ L["Total Sale Price"] = true
L["Total Spent:"] = true
L["Total Value"] = true
L["Total:"] = true
L["Player Wealth"] = true
L["Not Available"] = true
L["Bags:"] = true
L["Bank:"] = true
L["Auctions:"] = true
L["Coin on Hand"] = true
L["Track sales/purchases via trade"] = true
L["Type"] = true
L["Use smart average for purchase price"] = true

7
TradeSkillMaster_Accounting/Modules/data.lua

@ -1251,7 +1251,12 @@ function Data:LogGold()
TSM.db.realm.goldLog[player] = TSM.db.realm.goldLog[player] or {}
local goldLog = TSM.db.realm.goldLog[player]
local currentGold = TSM:Round(GetMoney(), COPPER_PER_GOLD * 1000)
local currentMoney = GetMoney()
local resolution = COPPER_PER_GOLD
if currentMoney >= (COPPER_PER_GOLD * 1000) then
resolution = COPPER_PER_GOLD * 1000
end
local currentGold = TSM:Round(currentMoney, resolution)
if #goldLog > 0 and currentGold == goldLog[#goldLog].copper then
goldLog[#goldLog].endMinute = currentMinute
else

200
TradeSkillMaster_Accounting/Modules/gui.lua

@ -809,7 +809,7 @@ function private:GetGoldGraphPoints(goldLog)
local data = {}
for _, info in ipairs(goldLog) do
local x1, x2 = info.startMinute, info.endMinute
local y = info.copper / COPPER_PER_GOLD / 1000
local y = info.copper / COPPER_PER_GOLD
minX = min(minX, x1)
maxX = max(maxX, x2)
minY = min(minY, floor(y))
@ -831,7 +831,7 @@ function private:GetGoldGraphSumData()
if i > 1 then
data[i].startMinute = data[i-1].endMinute+1
end
data[i].copper = TSM:Round(data[i].copper, COPPER_PER_GOLD*1000)
data[i].copper = TSM:Round(data[i].copper, COPPER_PER_GOLD)
end
tinsert(players, data)
tinsert(starts, data[1].startMinute)
@ -884,22 +884,100 @@ function private:GetGoldGraphSumData()
return private:GetGoldGraphPoints(temp)
end
function GUI:DrawGoldGraph(container)
TSM.db.realm.goldGraphCharacter = TSM.db.realm.goldGraphCharacter or UnitName("player")
local player = TSM.db.realm.goldGraphCharacter
local data, minX, maxX, minY, maxY
function private:GetWealthPrice(itemString)
local avgSell = TSM:GetAvgSellPrice(itemString)
local marketValue = TSMAPI:GetItemValue(itemString, "DBMarket")
if avgSell and marketValue then
return (avgSell + marketValue) / 2
end
return avgSell or marketValue
end
function private:GetWealthTotals(player)
local totals = {
bags = 0,
bank = 0,
auctions = 0,
}
local hasData = false
local function AddTotals(itemData, key)
if not itemData then return end
hasData = true
for itemString, quantity in pairs(itemData) do
if quantity > 0 then
local price = private:GetWealthPrice(itemString)
if price then
totals[key] = totals[key] + (price * quantity)
end
end
end
end
if player == "<ALL>" then
data, minX, maxX, minY, maxY = private:GetGoldGraphSumData()
local players = TSMAPI:ModuleAPI("ItemTracker", "playerlist") or {}
for _, playerName in ipairs(players) do
AddTotals(TSMAPI:ModuleAPI("ItemTracker", "playerbags", playerName), "bags")
AddTotals(TSMAPI:ModuleAPI("ItemTracker", "playerbank", playerName), "bank")
AddTotals(TSMAPI:ModuleAPI("ItemTracker", "playerauctions", playerName), "auctions")
end
else
data, minX, maxX, minY, maxY = private:GetGoldGraphPoints(TSM.db.realm.goldLog[player])
AddTotals(TSMAPI:ModuleAPI("ItemTracker", "playerbags", player), "bags")
AddTotals(TSMAPI:ModuleAPI("ItemTracker", "playerbank", player), "bank")
AddTotals(TSMAPI:ModuleAPI("ItemTracker", "playerauctions", player), "auctions")
end
if not hasData then return end
return totals
end
function private:GetLatestGold(player)
local function GetLogGold(log)
if not log or #log == 0 then return end
return log[#log].copper
end
if player == "<ALL>" then
local total = 0
local hasData = false
for name, log in pairs(TSM.db.realm.goldLog) do
local gold = GetLogGold(log)
if gold then
total = total + gold
hasData = true
end
end
if not hasData then return end
return total
end
return GetLogGold(TSM.db.realm.goldLog[player])
end
function GUI:DrawGoldGraph(container)
TSM.db.realm.goldGraphCharacter = TSM.db.realm.goldGraphCharacter or UnitName("player")
local player = TSM.db.realm.goldGraphCharacter
local dropdownList = {["<ALL>"]="Sum of All Characters"}
for player in pairs(TSM.db.realm.goldLog) do
dropdownList[player] = player
end
if not data then
local wealthTotals = private:GetWealthTotals(player)
local wealthTotalText = L["Not Available"]
local wealthBagsText = L["Not Available"]
local wealthBankText = L["Not Available"]
local wealthAuctionsText = L["Not Available"]
if wealthTotals then
wealthTotalText = TSMAPI:FormatTextMoney(wealthTotals.bags + wealthTotals.bank + wealthTotals.auctions) or "---"
wealthBagsText = TSMAPI:FormatTextMoney(wealthTotals.bags) or "---"
wealthBankText = TSMAPI:FormatTextMoney(wealthTotals.bank) or "---"
wealthAuctionsText = TSMAPI:FormatTextMoney(wealthTotals.auctions) or "---"
end
local coinTotal = private:GetLatestGold(player)
local coinTotalText = coinTotal and TSMAPI:FormatTextMoney(coinTotal) or L["Not Available"]
local page = {
{
type = "SimpleGroup",
@ -907,104 +985,66 @@ function GUI:DrawGoldGraph(container)
children = {
{
type = "Label",
text = L["Accounting has not yet collected enough information for this tab. This is likely due to not having recorded enough data points or not seeing any significant fluctuations (over 1k gold) in your gold on hand."],
text = L["Player Gold"],
relativeWidth = 1,
},
{
type = "Spacer",
},
-- {
-- type = "Spacer",
-- },
{
type = "Dropdown",
label = "Character to Graph",
label = L["Character to Graph"],
settingInfo = {TSM.db.realm, "goldGraphCharacter"},
relativeWidth = 0.5,
list = dropdownList,
callback = function() container:ReloadTab() end,
tooltip = "",
},
},
},
}
TSMAPI:BuildPage(container, page)
return
end
local startDate, endDate
if TSM.db.realm.timeFormat == "eudate" then
startDate = date("%d/%m/%y %H:%M", minX * 60)
endDate = date("%d/%m/%y %H:%M", maxX * 60)
elseif TSM.db.realm.timeFormat == "aidate" then
startDate = date("%y/%m/%d %H:%M", minX * 60)
endDate = date("%y/%m/%d %H:%M", maxX * 60)
else
startDate = date("%m/%d/%y %H:%M", minX * 60)
endDate = date("%m/%d/%y %H:%M", maxX * 60)
end
local page = {
{
type = "SimpleGroup",
type = "InlineGroup",
layout = "Flow",
title = L["Player Wealth"],
backdrop = true,
children = {
{
type = "Label",
--text = format(L["Below is a graph of the your character's gold on hand over time.\n\nThe x-axis is time and goes from %s to %s\nThe y-axis is thousands of gold."], startDate, endDate),
text = format("Below is a graph of the your character's gold on hand over time.\nThe x-axis is time and goes from %s to %s. The y-axis is thousands of gold.", startDate, endDate),
text = L["Total:"] .. " " .. wealthTotalText,
relativeWidth = 1,
},
-- {
-- type = "Spacer",
-- },
{
type = "Dropdown",
label = L["Character to Graph"],
settingInfo = {TSM.db.realm, "goldGraphCharacter"},
relativeWidth = 0.5,
list = dropdownList,
callback = function() container:ReloadTab() end,
type = "Label",
text = L["Bags:"] .. " " .. wealthBagsText,
relativeWidth = 0.33,
},
-- {
-- type = "HeadingLine"
-- },
{
type = "Spacer",
type = "Label",
text = L["Bank:"] .. " " .. wealthBankText,
relativeWidth = 0.33,
},
{
type = "ScrollFrame",
fullHeight = false, --true
layout = "flow"
type = "Label",
text = L["Auctions:"] .. " " .. wealthAuctionsText,
relativeWidth = 0.33,
},
},
},
{
type = "InlineGroup",
layout = "Flow",
title = L["Coin on Hand"],
backdrop = true,
children = {
{
type = "Label",
text = L["Total:"] .. " " .. coinTotalText,
relativeWidth = 1,
},
},
},
},
},
}
TSMAPI:BuildPage(container, page)
local parent = container.children[1].children[#container.children[1].children].frame
parent:SetWidth(container.frame:GetWidth() - 80)
parent:SetHeight(container.frame:GetHeight() - 140)
--if not GUI.lineGraph then
local graph = LibStub("LibGraph-2.0"):CreateGraphLine(nil, parent, "CENTER", nil, nil, nil, parent:GetWidth(), parent:GetHeight())
graph:SetGridColor({ 0.8, 0.8, 0.8, 0.6 })
graph:SetYLabels(true)
GUI.lineGraph = graph
--end
GUI.lineGraph:Show()
GUI.lineGraph:SetParent(parent)
GUI.lineGraph:ClearAllPoints()
GUI.lineGraph:SetAllPoints(parent)
GUI.lineGraph:ResetData()
local ySpacing = max(ceil((maxY - minY) / 20), 0.5)
GUI.lineGraph:SetGridSpacing(nil, ySpacing)
local xBuffer = (maxX-minX)*0.05
local yBuffer = (maxY-minY)*0.03
GUI.lineGraph:SetXAxis(minX-xBuffer, maxX)
GUI.lineGraph:SetYAxis(minY-yBuffer, maxY+yBuffer)
GUI.lineGraph:AddDataSeries(data, {1, 0.83, 0, 1})
GUI.lineGraph:RefreshGraph()
end
function GUI:DrawOptions(container)

89
TradeSkillMaster_Accounting/TradeSkillMaster_Accounting.lua

@ -90,8 +90,8 @@ function TSM:OnInitialize()
if data.startMinute == data.endMinute and data.copper == 0 then
tremove(playerData, i)
else
-- round to nearest 1k gold
data.copper = TSM:Round(data.copper, COPPER_PER_GOLD*1000)
-- round to nearest gold
data.copper = TSM:Round(data.copper, COPPER_PER_GOLD)
end
end
if #playerData >= 2 then
@ -125,8 +125,11 @@ function TSM:RegisterModule()
TSM.priceSources = {
{ key = "avgSell", label = L["Avg Sell Price"], callback = "GetAvgSellPrice" },
{ key = "avgBuy", label = L["Avg Buy Price"], callback = "GetAvgBuyPrice" },
{ key = "smartAvgBuy", label = L["Smart Avg Buy Price"], callback = "GetSmartAvgBuyPrice" },
{ key = "maxSell", label = L["Max Sell Price"], callback = "GetMaxSellPrice" },
{ key = "maxBuy", label = L["Max Buy Price"], callback = "GetMaxBuyPrice" },
{ key = "minSell", label = L["Min Sell Price"], callback = "GetMinSellPrice" },
{ key = "minBuy", label = L["Min Buy Price"], callback = "GetMinBuyPrice" },
}
TSM.tooltipOptions = { callback = "GUI:LoadTooltipOptions" }
@ -200,11 +203,19 @@ function TSM:GetTooltip(itemString)
else
local avgPrice = TSM:GetAvgBuyPrice(itemString)
local maxPrice = TSM:GetMaxBuyPrice(itemString)
local smartAvgPrice = TSM:GetSmartAvgBuyPrice(itemString)
if moneyCoinsTooltip then
tinsert(text, { left = " " .. L["Purchased (Avg/Max Price):"], right = format("%s (%s / %s)", "|cffffffff" .. totalNum .. "|r", (TSMAPI:FormatTextMoneyIcon(avgPrice, "|cffffffff", true) or ("|cffffffff" .. "?")), (TSMAPI:FormatTextMoneyIcon(maxPrice, "|cffffffff", true) or ("|cffffffff" .. "?"))) })
else
tinsert(text, { left = " " .. L["Purchased (Avg/Max Price):"], right = format("%s (%s / %s)", "|cffffffff" .. totalNum .. "|r", (TSMAPI:FormatTextMoney(avgPrice, "|cffffffff", true) or ("|cffffffff" .. "?")), (TSMAPI:FormatTextMoney(maxPrice, "|cffffffff", true) or ("|cffffffff" .. "?"))) })
end
if smartAvgPrice then
if moneyCoinsTooltip then
tinsert(text, { left = " " .. L["Smart Avg Buy Price:"], right = (TSMAPI:FormatTextMoneyIcon(smartAvgPrice, "|cffffffff", true) or ("|cffffffff" .. "?")) })
else
tinsert(text, { left = " " .. L["Smart Avg Buy Price:"], right = (TSMAPI:FormatTextMoney(smartAvgPrice, "|cffffffff", true) or ("|cffffffff" .. "?")) })
end
end
end
if lastPurchased then
local timeDiff = SecondsToTime(time() - lastPurchased)
@ -422,12 +433,24 @@ function TSM:GetAvgSellPrice(itemString)
return TSM.cache[itemString].avgSellPrice, TSM.cache[itemString].avgSellNum
end
local function GetAvgerageBuyPrice(itemString, noBaseItem)
local function GetSmartBuyItemCount(itemString)
local player, alts = TSMAPI:ModuleAPI("ItemTracker", "playertotal", itemString)
if not player then
alts = nil
end
player = player or 0
alts = alts or 0
local guild = TSMAPI:ModuleAPI("ItemTracker", "guildtotal", itemString) or 0
local auctions = TSMAPI:ModuleAPI("ItemTracker", "auctionstotal", itemString) or 0
return player + alts + guild + auctions
end
local function GetAvgerageBuyPrice(itemString, noBaseItem, useSmart)
if not noBaseItem and itemString and baseItemLookup[itemString] then
local totalPrice, totalNum = 0, 0
for _, item in ipairs(baseItemLookup[itemString]) do
if not baseItemLookup[item] then
local price, num = GetAvgerageBuyPrice(item, true)
local price, num = GetAvgerageBuyPrice(item, true, useSmart)
if price and num and num > 0 then
totalPrice = totalPrice + price
totalNum = totalNum + num
@ -441,16 +464,11 @@ local function GetAvgerageBuyPrice(itemString, noBaseItem)
if not (TSM.items[itemString] and #TSM.items[itemString].buys > 0) then return end
local itemCount = 0
if TSM.db.realm.smartBuyPrice then
local player, alts = TSMAPI:ModuleAPI("ItemTracker", "playertotal", itemString)
if not player then
alts = nil
if useSmart or TSM.db.realm.smartBuyPrice then
itemCount = GetSmartBuyItemCount(itemString)
if useSmart and itemCount <= 0 then
return
end
player = player or 0
alts = alts or 0
local guild = TSMAPI:ModuleAPI("ItemTracker", "guildtotal", itemString) or 0
local auctions = TSMAPI:ModuleAPI("ItemTracker", "auctionstotal", itemString) or 0
itemCount = player + alts + guild + auctions
end
local num, totalPrice = 0, 0
@ -480,6 +498,19 @@ function TSM:GetAvgBuyPrice(itemString)
return TSM.cache[itemString].avgBuyPrice, TSM.cache[itemString].avgBuyNum
end
function TSM:GetSmartAvgBuyPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(itemString)))
if not itemString then return end
TSM.cache[itemString] = TSM.cache[itemString] or {}
TSM:UpdateBaseItemLookup()
if not TSM.cache[itemString].smartAvgBuyPrice then
local price, num = GetAvgerageBuyPrice(itemString, nil, true)
TSM.cache[itemString].smartAvgBuyPrice = price
TSM.cache[itemString].smartAvgBuyNum = num
end
return TSM.cache[itemString].smartAvgBuyPrice, TSM.cache[itemString].smartAvgBuyNum
end
function TSM:GetMaxSellPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(itemString)))
if not (itemString and TSM.items[itemString] and #TSM.items[itemString].sales > 0) then return end
@ -496,6 +527,22 @@ function TSM:GetMaxSellPrice(itemString)
return TSM.cache[itemString].maxSellPrice
end
function TSM:GetMinSellPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(itemString)))
if not (itemString and TSM.items[itemString] and #TSM.items[itemString].sales > 0) then return end
TSM.cache[itemString] = TSM.cache[itemString] or {}
if not TSM.cache[itemString].minSellPrice then
local minPrice = math.huge
for _, record in ipairs(TSM.items[itemString].sales) do
minPrice = min(minPrice, record.copper)
end
TSM.cache[itemString].minSellPrice = minPrice
end
return TSM.cache[itemString].minSellPrice
end
function TSM:GetMaxBuyPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(itemString)))
if not (itemString and TSM.items[itemString] and #TSM.items[itemString].buys > 0) then return end
@ -512,6 +559,22 @@ function TSM:GetMaxBuyPrice(itemString)
return TSM.cache[itemString].maxBuyPrice
end
function TSM:GetMinBuyPrice(itemString)
itemString = TSMAPI:GetItemString(select(2, TSMAPI:GetSafeItemInfo(itemString)))
if not (itemString and TSM.items[itemString] and #TSM.items[itemString].buys > 0) then return end
TSM.cache[itemString] = TSM.cache[itemString] or {}
if not TSM.cache[itemString].minBuyPrice then
local minPrice = math.huge
for _, record in ipairs(TSM.items[itemString].buys) do
minPrice = min(minPrice, record.copper)
end
TSM.cache[itemString].minBuyPrice = minPrice
end
return TSM.cache[itemString].minBuyPrice
end
function TSM:Round(value, sig)
sig = sig or 1
local gold = value / sig

6
TradeSkillMaster_AuctionDB/Locale/deDE.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "deDE")
if not L then return end
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "Ein voller Auktionshausscan wird jedes einzelne Item im Auktionshaus scannen, ist aber sehr viel langsamer als der GetAll-Scan. Erwarte, dass es mehrere Minuten dauert oder länger."
L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "Ein GetAll-Scan ist die schnellste Methode, um in-game alle Gegenstände im Auktionshaus zu scannen. Allerdings gibt es viele mögliche Bugs seitens Blizzard die auftreten können, inklusive der Möglichkeit, dass deine Verbindung zum Spiel getrennt wird. Außerdem gibt es einen 15-Minuten-Cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server." -- Needs review
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "Es werden alle Gegenstände in der \"AuctionDB\" Datenbank angezeigt, deren Namen mit der Sucheingabe übereinstimmen."
L["Are you sure you want to clear your AuctionDB data?"] = "Sind Sie sicher, dass Sie die \"AuctionDB\" Daten löschen wollen?"
L["Ascending"] = "Aufsteigend"
L["AuctionDB - Market Value"] = "AuctionDB - Marktwert"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - Mindestpreis"
L["Can't run a GetAll scan right now."] = "Kann im Moment keinen GetAll Scan durchführen."
L["Descending"] = "Absteigend"
L["Display lowest buyout value seen in the last scan in tooltip."] = "Zeige den niedrigsten Sofortkauf-Betrag aus dem letzten Scan im Tooltip."
L["Display market value in tooltip."] = "Zeige Marktwert im Tooltip."
@ -29,7 +26,6 @@ L["Display total number of items ever seen in tooltip."] = "Zeige Gesamtzahl der
L["Done Scanning"] = "Scannen beendet"
L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = "Lade die KOSTENLOSE TSM Desktopsoftware herunter die automatisch deine TSM_AuctionDB mit den Preisen aus Blizzard's online API aktualisiert (und noch VIEL mehr kann). Besuche %s für weitere Informationen und scanne das AH nie wieder! Dies ist die beste Art deine AuctionDB Preise aktuell zu halten."
L["Enable display of AuctionDB data in tooltip."] = "Aktiviere die Anzeige der AuctionDB-Daten im Tooltip."
L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = "GetAll scan nicht erfolgreich aufgrund von Problemen bei Blizzard. Nutzung der TSM Software für deinen Scan wird empfohlen."
L["Hide poor quality items"] = "Verstecke Gegenstände schlechter Qualität"
L["If checked, poor quality items won't be shown in the search results."] = "Wenn markiert, tauchen Gegenstände schlechter Qualität nicht in den Suchergebnissen auf."
L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = "Wenn ausgewählt, wird der niedrigste Sofortkaufpreis der im letzten Scan gefunden wurde angezeigt."
@ -44,7 +40,6 @@ L["Items per page"] = "Gegenstände pro Seite"
L["Items %s - %s (%s total)"] = "Gegenstände %s - %s (%s gesamt)"
L["Item SubType Filter"] = "Gegenstands-Unterkategorie-Filter"
L["Item Type Filter"] = "Gegenstands-Kategorie-Filter"
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = "Es wird sehr empfohlen, dass du die UI neu lädst (tippe \"/reload\"), nachdem du einen GetAll-Scan gemacht hast. Sonst werden andere Scans (Posten/Abbrechen/Suchen/etc) sehr viel langsamer als Normal laufen."
L["Last Scanned"] = "Zuletzt gescannt"
L["Last updated from in-game scan %s ago."] = "Zuletzt aktualisiert durch in-game Scan vor %s"
L["Last updated from the TSM Application %s ago."] = "Zuletzt aktualisiert durch TSM-Applikation vor %s"
@ -71,7 +66,6 @@ L["Reset Data"] = "Daten zurücksetzen"
L["Resets AuctionDB's scan data"] = "Setzt die Scandaten der \"AuctionDB\" zurück"
L["Result Order:"] = "Reihenfolge der Ergebnisse:"
L["Run Full Scan"] = "Starte einen vollen Scan"
L["Run GetAll Scan"] = "Starte Komplettscan"
L["Running query..."] = "Abfrage läuft..."
L["%s ago"] = "Vor %s"
L["Scanning page %s/%s"] = "Scanne Seite %s/%s"

26
TradeSkillMaster_AuctionDB/Locale/enUS.lua

@ -14,31 +14,26 @@ local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "enUS
if not L then return end
L["%s ago"] = true
L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = true
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = true
L["A full auction house scan will scan every item on the auction house. Expect this scan to take several minutes or longer."] = true
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = true
L["A newer version of AuctionDB (%s) was received from %s. You are running %s."] = true
L["Are you sure you want to clear your AuctionDB data?"] = true
L["Ascending"] = true
L["AuctionDB - Market Value"] = true
L["AuctionDB - Minimum Buyout"] = true
L["Can't run a GetAll scan right now."] = true
L["Descending"] = true
L["Disable \"GetAll\" Auction Scans"] = true
L["Display lowest buyout value seen in the last scan in tooltip."] = true
L["Display market value in tooltip."] = true
L["Done Scanning"] = true
L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = true
L["Enable display of AuctionDB data in tooltip."] = true
L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = true
L["Hide poor quality items"] = true
L["If checked, AuctionDB will add a tab to the AH to allow for in-game scans. If you are using the TSM app exclusively for your scans, you may want to hide it by unchecking this option. This option requires a reload to take effect."] = true
L["If checked, AuctionDB will not perform \"GetAll\" scans. This is useful if your server doesn't return all auctions in its \"GetAll\" results, which means that you'll get incorrect market value calculations for all items. If you're playing on such servers, it's best to disable the \"GetAll\" feature to avoid accidentally polluting your price database with incorrect data. This option takes effect immediately, but requires a reload to completely hide the \"Run GetAll Scan\" button."] = true
L["If checked, poor quality items won't be shown in the search results."] = true
L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = true
L["If checked, the market value of the item will be displayed"] = true
L["Imported %s scans worth of new auction data!"] = true
L["Invalid value entered. You must enter a number between 5 and 500 inclusive."] = true
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = true
L["Item Link"] = true
L["Item MinLevel"] = true
L["Item SubType Filter"] = true
@ -60,6 +55,7 @@ L["No scans found."] = true
L["Not Ready"] = true
L["Not Scanned"] = true
L["Options"] = true
L["Pause"] = true
L["Preparing Filter %d / %d"] = true
L["Preparing Filters..."] = true
L["Previous Page"] = true
@ -70,11 +66,17 @@ L["Refreshes the current search results."] = true
L["Removed %s from AuctionDB."] = true
L["Reset Data"] = true
L["Resets AuctionDB's scan data"] = true
L["Resume"] = true
L["Resuming Scan..."] = true
L["Result Order:"] = true
L["Run Full Scan"] = true
L["Run GetAll Scan"] = true
L["Running query..."] = true
L["Running query... Server not responding due to throttling? Try again later..."] = true
L["Scan Bags"] = true
L["Scan Bank"] = true
L["Cancel Scan"] = true
L["Scan Inventory"] = true
L["Scan Paused"] = true
L["Scan Selected Groups"] = true
L["Scanning %d / %d (Page 1 / ?)"] = true
L["Scanning %d / %d (Page %d / %d)"] = true
@ -87,10 +89,16 @@ L["Select whether to sort search results in ascending or descending order."] = t
L["Shift-Right-Click to clear all data for this item from AuctionDB."] = true
L["Show AuctionDB AH Tab (Requires Reload)"] = true
L["Sort items by"] = true
L["Scan bag items."] = true
L["Scan bank items."] = true
L["Scans bags and bank for all characters."] = true
L["This determines how many items are shown per page in results area of the \"Search\" tab of the AuctionDB page in the main TSM window. You may enter a number between 5 and 500 inclusive. If the page lags, you may want to decrease this number."] = true
L["This will do a slow auction house scan of every item in the selected groups and update their AuctionDB prices. This may take several minutes."] = true
L["Use the search box and category filters above to search the AuctionDB data."] = true
L["You can filter the results by item subtype by using this dropdown. For example, if you want to search for all herbs, you would select \"Trade Goods\" in the item type dropdown and \"Herbs\" in this dropdown."] = true
L["You can filter the results by item type by using this dropdown. For example, if you want to search for all herbs, you would select \"Trade Goods\" in this dropdown and \"Herbs\" as the subtype filter."] = true
L["You can use this page to lookup an item or group of items in the AuctionDB database. Note that this does not perform a live search of the AH."] = true
L["You have disabled GetAll scans via AuctionDB's options."] = true
L["No bag items found."] = true
L["No inventory items found."] = true
L["No bank items found."] = true
L["ItemTracker data unavailable."] = true

6
TradeSkillMaster_AuctionDB/Locale/esES.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "esES")
if not L then return end
-- L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = ""
-- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = ""
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "Todos los artículos en la base de datos de AuctionDB que contienen la frase de búsqueda en su nombre en la pantalla."
L["Are you sure you want to clear your AuctionDB data?"] = "¿Está seguro que desea borrar los datos AuctionDB?"
L["Ascending"] = "Ascendente"
-- L["AuctionDB - Market Value"] = ""
-- L["AuctionDB - Minimum Buyout"] = ""
-- L["Can't run a GetAll scan right now."] = ""
L["Descending"] = "Descendiendo"
-- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
-- L["Display market value in tooltip."] = ""
@ -29,7 +26,6 @@ L["Descending"] = "Descendiendo"
-- L["Done Scanning"] = ""
-- L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = ""
L["Enable display of AuctionDB data in tooltip."] = "Permitir la visualización de los datos AuctionDB en la descripción."
-- L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = ""
L["Hide poor quality items"] = "Esconder objetos de calidad pobre"
L["If checked, poor quality items won't be shown in the search results."] = "Si se marca, los artículos de calidad pobre no se mostrará en los resultados de búsqueda."
-- L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = ""
@ -44,7 +40,6 @@ L["Item MinLevel"] = "NivelMin Objeto"
-- L["Items %s - %s (%s total)"] = ""
-- L["Item SubType Filter"] = ""
-- L["Item Type Filter"] = ""
-- L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = ""
-- L["Last Scanned"] = ""
-- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = ""
@ -71,7 +66,6 @@ L["Ready in %s min and %s sec"] = "Listo en %s min y %s sec."
-- L["Resets AuctionDB's scan data"] = ""
-- L["Result Order:"] = ""
-- L["Run Full Scan"] = ""
L["Run GetAll Scan"] = "Hacer un GetAll Scan"
-- L["Running query..."] = ""
L["%s ago"] = "hace %s"
-- L["Scanning page %s/%s"] = ""

6
TradeSkillMaster_AuctionDB/Locale/esMX.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "esMX")
if not L then return end
-- L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = ""
-- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = ""
-- L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = ""
-- L["Are you sure you want to clear your AuctionDB data?"] = ""
-- L["Ascending"] = ""
-- L["AuctionDB - Market Value"] = ""
-- L["AuctionDB - Minimum Buyout"] = ""
-- L["Can't run a GetAll scan right now."] = ""
-- L["Descending"] = ""
-- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
-- L["Display market value in tooltip."] = ""
@ -29,7 +26,6 @@ if not L then return end
-- L["Done Scanning"] = ""
-- L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = ""
-- L["Enable display of AuctionDB data in tooltip."] = ""
-- L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = ""
-- L["Hide poor quality items"] = ""
-- L["If checked, poor quality items won't be shown in the search results."] = ""
-- L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = ""
@ -44,7 +40,6 @@ if not L then return end
-- L["Items %s - %s (%s total)"] = ""
-- L["Item SubType Filter"] = ""
-- L["Item Type Filter"] = ""
-- L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = ""
-- L["Last Scanned"] = ""
-- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = ""
@ -71,7 +66,6 @@ if not L then return end
-- L["Resets AuctionDB's scan data"] = ""
-- L["Result Order:"] = ""
-- L["Run Full Scan"] = ""
-- L["Run GetAll Scan"] = ""
-- L["Running query..."] = ""
-- L["%s ago"] = ""
-- L["Scanning page %s/%s"] = ""

6
TradeSkillMaster_AuctionDB/Locale/frFR.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "frFR")
if not L then return end
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "Une analyse complète de l'Hôtel des ventes examinera tous les objets de l'hôtel des ventes mais est beaucoup plus lente que la méthode du GetAll. Attendez-vous à ce que cette analyse dure de nombreuses minutes."
-- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = ""
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "Tout objets présent dans la base de donnée d'AuctionDB contenant la phrase recherchée dans son nom sera affiché."
L["Are you sure you want to clear your AuctionDB data?"] = "Êtes-vous sûr de vouloir vider les données d'AuctionDB?"
L["Ascending"] = "Croissant"
L["AuctionDB - Market Value"] = "AuctionDB - Valeur du Marché"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - Achat minimum"
-- L["Can't run a GetAll scan right now."] = ""
L["Descending"] = "Décroissant"
-- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
-- L["Display market value in tooltip."] = ""
@ -29,7 +26,6 @@ L["Descending"] = "Décroissant"
L["Done Scanning"] = "Analyse terminée"
-- L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = ""
L["Enable display of AuctionDB data in tooltip."] = "Activer l'affichage des données d'AuctionDB dans les info-bulles."
-- L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = ""
L["Hide poor quality items"] = "Masquer les objets gris"
L["If checked, poor quality items won't be shown in the search results."] = "Si coché, les objets gris ne seront pas affiché lors de la recherche."
-- L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = ""
@ -44,7 +40,6 @@ L["Items per page"] = "Objets par page"
L["Items %s - %s (%s total)"] = "Objets %s - %s (%s au total)"
L["Item SubType Filter"] = "Sous-catégorie de l'objet"
L["Item Type Filter"] = "Catégorie de l'objet"
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = "Il est fortement recommandé que vous rechargez votre interface (tapez '/reload') après une analyse GetAll. Sinon, toutes les autres analyses (Poster/Annuler/Rechercher/etc) vont être plus longue que d'habitude."
L["Last Scanned"] = "Dernière fois analysé"
-- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = ""
@ -71,7 +66,6 @@ L["Reset Data"] = "Réinitialiser les données"
L["Resets AuctionDB's scan data"] = "Réinitialise les données d'AuctionDB"
-- L["Result Order:"] = ""
L["Run Full Scan"] = "Analyse complète"
L["Run GetAll Scan"] = "Analyse GetAll"
-- L["Running query..."] = ""
L["%s ago"] = "Il y a %s"
L["Scanning page %s/%s"] = "Scan de la page %s/%s" -- Needs review

6
TradeSkillMaster_AuctionDB/Locale/koKR.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "koKR")
if not L then return end
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "전체 검색은 경매장 내의 모든 아이템을 검색하지만 GetAll 검색보다는 훨씬 느립니다. 이 검색은 몇 분 정도 또는 그 이상의 시간이 소요됩니다."
L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "GetAll 검색은 게임 내에서 경매장의 모든 아이템을 검색하기 위한 가장 빠른 검색 방법입니다. 하지만 블리자드 쪽에 많은 버그가 존재하며 게임의 접속이 끊길 가능성도 있습니다. 또한, 15분의 쿨다운이 존재합니다. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server." -- Needs review
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "이름에 검색 구문을 포함하는 AuctionDB 데이터베이스 내의 모든 아이템이 표시됩니다."
L["Are you sure you want to clear your AuctionDB data?"] = "모든 AuctionDB 데이터를 삭제 하시겠습니까?"
L["Ascending"] = "오름차순"
L["AuctionDB - Market Value"] = "AuctionDB - 시장 가격"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - 최소 구매가격"
L["Can't run a GetAll scan right now."] = "지금은 GetAll 검색을 실행할 수 없습니다." -- Needs review
L["Descending"] = "내림차순"
L["Display lowest buyout value seen in the last scan in tooltip."] = "최근 검색 시 본 최소 구매가를 툴팁에 표시합니다." -- Needs review
L["Display market value in tooltip."] = "시장 가격을 툴팁에 표시합니다." -- Needs review
@ -29,7 +26,6 @@ L["Display total number of items ever seen in tooltip."] = "아이템의 전체
L["Done Scanning"] = "검색 완료"
L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = "무료 TSM 데스크톱 애플리케이션을 다운로드하면 TSM_AuctionDB 가격을 블리자드의 온라인 API를 이용해 자동으로 업데이트합니다. %s을 방문하여 더 많은 정보를 얻고 더 이상은 경매장 검색을 하지 마세요! 이것은 AuctionDB 가격을 업데이트하는 최고의 방법입니다." -- Needs review
L["Enable display of AuctionDB data in tooltip."] = "AuctionDB 데이터를 툴팁에 표시" -- Needs review
L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = "블리자드의 문제로 GetAll 검색이 성공적으로 수행되지 못했습니다. TSM 애플리케이션의 사용한 검색을 추천합니다." -- Needs review
L["Hide poor quality items"] = "저급 품질 아이템 숨기기"
L["If checked, poor quality items won't be shown in the search results."] = "선택하면, 저급 품질 아이템은 검색 결과에 표시하지 않습니다."
L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = "선택하면, 최근 검색에서 본 아이템의 최소 구매 가격을 표시합니다." -- Needs review
@ -44,7 +40,6 @@ L["Items per page"] = "페이지당 아이템 개수"
L["Items %s - %s (%s total)"] = "아이템 %s - %s (전체 %s)"
L["Item SubType Filter"] = "아이템 하위 유형 필터" -- Needs review
L["Item Type Filter"] = "아이템 유형 필터"
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = "GetAll 검색을 실행한 후 UI를 다시 로드('/reload' 입력)해 주시길 바랍니다. 그렇게 하지 않으면, 다른 검색(등록/취소/검색/기타)은 정상보다 훨씬 느려지게 됩니다."
L["Last Scanned"] = "최근 검색"
L["Last updated from in-game scan %s ago."] = "게임 내 검색의 최근 업데이트 %s 전." -- Needs review
L["Last updated from the TSM Application %s ago."] = "TSM 애플리케이션의 최근 업데이트 %s 전." -- Needs review
@ -71,7 +66,6 @@ L["Reset Data"] = "데이터 리셋"
L["Resets AuctionDB's scan data"] = "AuctionDB의 검색 데이터 리셋"
L["Result Order:"] = "결과 정렬:" -- Needs review
L["Run Full Scan"] = "전체 검색"
L["Run GetAll Scan"] = "GetAll 검색"
L["Running query..."] = "쿼리 실행 중..." -- Needs review
L["%s ago"] = "%s 전"
L["Scanning page %s/%s"] = "페이지 검색 %s/%s" -- Needs review

6
TradeSkillMaster_AuctionDB/Locale/ptBR.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "ptBR")
if not L then return end
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "Um escaneamento completo da casa de leilões irá escanear todos os itens da casa de leilões, porém é bem mais lento que um escaneamento PegaTudo. Espere que este escaneamento demore vários minutos ou mais."
-- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = ""
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "Qualquer item no bando de dados do AuctionDB que contém a frase procurada em seus nomes serão exibidos."
L["Are you sure you want to clear your AuctionDB data?"] = "Você tem certeza de que quer limpar os dados do seu AuctionDB?"
L["Ascending"] = "Crescente"
L["AuctionDB - Market Value"] = "AuctionDB - Valor de Mercado"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - Arremate Mínimo"
-- L["Can't run a GetAll scan right now."] = ""
L["Descending"] = "Decrescente"
-- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
-- L["Display market value in tooltip."] = ""
@ -29,7 +26,6 @@ L["Descending"] = "Decrescente"
L["Done Scanning"] = "Escaneamento Completo"
-- L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = ""
L["Enable display of AuctionDB data in tooltip."] = "Habilita a exibição de dados do AuctionDB nas dicas de interface."
-- L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = ""
L["Hide poor quality items"] = "Esconder itens de qualidade inferior"
L["If checked, poor quality items won't be shown in the search results."] = "Se marcado, itens de qualidade inferior não serão exibidos nos resultados das buscas."
-- L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = ""
@ -44,7 +40,6 @@ L["Items per page"] = "Itens por página"
L["Items %s - %s (%s total)"] = "Itens %s - %s (%s no total)"
L["Item SubType Filter"] = "Filtro de SubTipo de Item"
L["Item Type Filter"] = "Filtro de Tipo de Item"
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = "É altamente recomendado que você recarregue sua IU (digite '/reload') após rodar um escaneamento PegaTudo. De outra forma, qualquer outro escaneamento (Postagem/Cancelamento/Busca/etc) será muito mais lento que o normal."
L["Last Scanned"] = "Escaneado pela última vez"
-- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = ""
@ -71,7 +66,6 @@ L["Reset Data"] = "Redefinir Dados"
L["Resets AuctionDB's scan data"] = "Redefine os dados de escaneamento do AuctionDB"
L["Result Order:"] = "Order de Resultado"
L["Run Full Scan"] = "Escaneamento Completo" -- Needs review
L["Run GetAll Scan"] = "PegaTudo" -- Needs review
-- L["Running query..."] = ""
L["%s ago"] = "%s atrás"
L["Scanning page %s/%s"] = "Escaneando página %s/%s" -- Needs review

6
TradeSkillMaster_AuctionDB/Locale/ruRU.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "ruRU")
if not L then return end
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "Полный скан Аукциона просканирует каждый товар, но намного дольше, чем GetAll скан. Ждите, это займёт несколько минут или более."
L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "GetAll скан - самый быстрый внутриигровой способ сканирования. Однако, из-за из-за ошибок со стороны Blizzard's, существует вероятность отключения от сервера. Кроме того, он имеет 15-минутный перерыв. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server." -- Needs review
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "Будут отображены все товары из базы данных AuctionDB, содержащие искомую фразу в названии."
L["Are you sure you want to clear your AuctionDB data?"] = "Вы действительно хотите очистить базу AuctionDB?"
L["Ascending"] = "Возрастание"
L["AuctionDB - Market Value"] = "AuctionDB - Рыночная стоимость"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - Минимальный выкуп"
L["Can't run a GetAll scan right now."] = "Сейчас невозможно запустить GetAll скан."
L["Descending"] = "Убывание"
-- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
L["Display market value in tooltip."] = "Показывать рыночную цену в подсказке."
@ -29,7 +26,6 @@ L["Display market value in tooltip."] = "Показывать рыночную
L["Done Scanning"] = "Сканирование завершено"
-- L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = ""
L["Enable display of AuctionDB data in tooltip."] = "Показывать данные AuctionDB в подсказке."
L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = "GetAll скан завершился неудачно из-за проблем со стороны Blizzard. Рекомендуется использовать для сканирования TSM."
L["Hide poor quality items"] = "Скрыть товары низкого качества"
L["If checked, poor quality items won't be shown in the search results."] = "Не показывать товары низкого качества в результатах поиска."
L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = "Если выбрано, будет показана минимальная цена выкупа товара с последнего скана"
@ -44,7 +40,6 @@ L["Items per page"] = "Товаров на страницу"
L["Items %s - %s (%s total)"] = "Товаров %s - %s (%s всего)"
L["Item SubType Filter"] = "Фильтр по подтипу товара"
L["Item Type Filter"] = "Фильтр по типу товара"
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = "Настоятельно рекомендуем перезагрузить ваш интерфейс (наберите в чате '/reload') после выполнения GetAll скана. Иначе, любые другие сканы (Выставить/Отменить/Поиск/т.д.) будут намного медленнее, чем обычно."
L["Last Scanned"] = "Последний скан"
-- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = ""
@ -71,7 +66,6 @@ L["Reset Data"] = "Сбросить данные"
L["Resets AuctionDB's scan data"] = "Сбрасывает данные сканирования модуля AuctionDB"
-- L["Result Order:"] = ""
L["Run Full Scan"] = "Полный скан"
L["Run GetAll Scan"] = "GetAll скан"
L["Running query..."] = "Осуществляется запрос..."
L["%s ago"] = "%s назад"
L["Scanning page %s/%s"] = "Сканирование страницы %s/%s"

6
TradeSkillMaster_AuctionDB/Locale/zhCN.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "zhCN")
if not L then return end
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "完整扫描拍卖行内的所有物品,此方式远慢于快速扫描,预计费时几分钟甚至更久。"
L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = "快速扫描时扫描拍卖行中每件物品最快的方式。然而,在服务器端有着可能的BUG会使您掉线,所以每15分钟才能执行一次。 You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server." -- Needs review
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "任何包含搜索短语的AuctionDB数据库中的物品都将显示。"
L["Are you sure you want to clear your AuctionDB data?"] = "您确定要清除AuctionDB数据吗?"
L["Ascending"] = "升序"
L["AuctionDB - Market Value"] = "AuctionDB - 市场价"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - 最低一口价"
L["Can't run a GetAll scan right now."] = "现在还不能执行快速扫描。"
L["Descending"] = "降序"
L["Display lowest buyout value seen in the last scan in tooltip."] = "在鼠标提示中显示上次扫描的最低一口价。"
L["Display market value in tooltip."] = "在鼠标提示中显示市场价。"
@ -29,7 +26,6 @@ L["Display total number of items ever seen in tooltip."] = "在鼠标提示中
L["Done Scanning"] = "完成扫描"
L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = "下载完全免费的 TSM APP (TSM应用程序) 来更新你的 AuctionDB数据库中的物品价格 (利用到暴雪提供的在线APIs)。访问 %s 来获取更多信息。以后将不用在游戏里扫描拍卖行物价了,这将是更新拍卖行物价好最好的方法。"
L["Enable display of AuctionDB data in tooltip."] = "在鼠标提示中显示AuctionDB数据"
L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = "快速扫描由于服务器端的争议而不能成功的运行,因此强烈推荐使用 TSM APP 进行物价扫描。"
L["Hide poor quality items"] = "隐藏灰色物品"
L["If checked, poor quality items won't be shown in the search results."] = "如果勾选,灰色物品将不会出现在扫描结果中。"
L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = "如果勾选,将显示上次扫描的物品最低一口价。"
@ -44,7 +40,6 @@ L["Items per page"] = "每页显示的物品"
L["Items %s - %s (%s total)"] = "物品 %s - %s (总数 %s) "
L["Item SubType Filter"] = "物品子类型筛选"
L["Item Type Filter"] = "物品类型筛选"
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = "强烈推荐您在运行快速扫描后重载界面(输入'/reload'),否则任何其他的扫描(上架/下架/搜索等)都较平时要慢。"
L["Last Scanned"] = "上一次扫描"
L["Last updated from in-game scan %s ago."] = "距离上次游戏内的数据扫描 %s 。"
L["Last updated from the TSM Application %s ago."] = "距离上次游戏外TSM APP的数据扫描 %s 。"
@ -71,7 +66,6 @@ L["Reset Data"] = "重置数据"
L["Resets AuctionDB's scan data"] = "重置AuctionDB扫描数据"
L["Result Order:"] = "结果顺序:"
L["Run Full Scan"] = "执行完整扫描"
L["Run GetAll Scan"] = "进行快速扫描"
L["Running query..."] = "运行查询…"
L["%s ago"] = "%s之前"
L["Scanning page %s/%s"] = "扫描页面 %s/%s"

6
TradeSkillMaster_AuctionDB/Locale/zhTW.lua

@ -13,14 +13,11 @@
local L = LibStub("AceLocale-3.0"):NewLocale("TradeSkillMaster_AuctionDB", "zhTW")
if not L then return end
L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."] = "完整的拍賣行掃描會掃描每件在拍賣行的物品但是比GetAll掃描還緩慢。預期掃描會花費幾分鐘或是更久。"
-- L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."] = ""
L["Any items in the AuctionDB database that contain the search phrase in their names will be displayed."] = "任何在AuctionDB資料庫中符合搜尋條件的物品,都會顯示出來。"
L["Are you sure you want to clear your AuctionDB data?"] = "你確定要清除AuctionDB資料?"
L["Ascending"] = "遞增"
L["AuctionDB - Market Value"] = "AuctionDB - 市場價格"
L["AuctionDB - Minimum Buyout"] = "AuctionDB - 最小直購價"
-- L["Can't run a GetAll scan right now."] = ""
L["Descending"] = "遞減"
-- L["Display lowest buyout value seen in the last scan in tooltip."] = ""
-- L["Display market value in tooltip."] = ""
@ -29,7 +26,6 @@ L["Descending"] = "遞減"
L["Done Scanning"] = "完成掃描"
-- L["Download the FREE TSM desktop application which will automatically update your TSM_AuctionDB prices using Blizzard's online APIs (and does MUCH more). Visit %s for more info and never scan the AH again! This is the best way to update your AuctionDB prices."] = ""
L["Enable display of AuctionDB data in tooltip."] = "啟用在提示顯示AuctionDB資料。"
-- L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."] = ""
L["Hide poor quality items"] = "隱藏低品質物品"
L["If checked, poor quality items won't be shown in the search results."] = "勾選,低品質物㗊將不會顯示在搜尋記錄。"
-- L["If checked, the lowest buyout value seen in the last scan of the item will be displayed."] = ""
@ -44,7 +40,6 @@ L["Items per page"] = "一頁幾項物品"
L["Items %s - %s (%s total)"] = "物品%s - %s (總共:%s)"
L["Item SubType Filter"] = "物品次要類型過濾"
L["Item Type Filter"] = "物品類型過濾"
L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."] = "強烈建議你在跑了GetAll掃描後重新載入你的UI(輸入\"/reload\")。否則,任何其它掃描(發佈/取消/搜尋/等等)都會比平常更慢。"
L["Last Scanned"] = "最後掃描"
-- L["Last updated from in-game scan %s ago."] = ""
-- L["Last updated from the TSM Application %s ago."] = ""
@ -71,7 +66,6 @@ L["Reset Data"] = "重置資料"
L["Resets AuctionDB's scan data"] = "重置AuctionDB的掃描資料"
L["Result Order:"] = "結果順序:"
L["Run Full Scan"] = "執行完整掃描"
L["Run GetAll Scan"] = "執行GetAll掃描"
-- L["Running query..."] = ""
L["%s ago"] = "%s以前"
-- L["Scanning page %s/%s"] = ""

436
TradeSkillMaster_AuctionDB/Modules/ChannelSync.lua

@ -0,0 +1,436 @@
-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster_AuctionDB --
-- http://www.curse.com/addons/wow/tradeskillmaster_auctiondb --
-- --
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
-- All Rights Reserved* - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
-- Channel sync for sharing AuctionDB scan data between players.
local TSM = select(2, ...)
local ChannelSync = TSM:NewModule("ChannelSync", "AceEvent-3.0")
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_AuctionDB")
local CHANNEL_NAME = "TSM_AuctionDB"
local COMM_PREFIX = "TSMADB1"
local CHUNK_SIZE = 220
local BUNDLE_TIMEOUT = 45
local SEND_INTERVAL = 0.08
local MAX_TOTAL_CHUNKS = 800
local MAX_ITEMS_PER_PAYLOAD = 250
local private = {
channelId = nil,
channelName = nil,
lastBroadcastHash = nil,
incoming = {},
sendQueue = {},
isSending = false,
notifiedNewer = {},
}
local strbyte = string.byte
local libS = LibStub:GetLibrary("AceSerializer-3.0")
local libD = LibStub("LibDeflate", true)
local warnedNoDeflate
local debugEnabled = true
local function GetAddonVersion()
local version = GetAddOnMetadata("TradeSkillMaster_AuctionDB", "X-Curse-Packaged-Version")
or GetAddOnMetadata("TradeSkillMaster_AuctionDB", "Version")
if not version or version == "" then
return "unknown"
end
return version
end
local function VersionKey(version)
local key = 0
for part in tostring(version or ""):gmatch("%d+") do
key = key * 1000 + tonumber(part)
end
return key
end
private.addonVersion = GetAddonVersion()
private.addonVersionKey = VersionKey(private.addonVersion)
local function DebugPrint(msg)
if debugEnabled then
TSM:Print("ChannelSync: " .. msg)
end
end
local function CanEncode()
if libD then return true end
if not warnedNoDeflate then
warnedNoDeflate = true
TSM:Print("AuctionDB channel sync requires LibDeflate. Install or enable an addon that provides it.")
end
return false
end
local function HashString(str)
local hash = 0
for i = 1, #str do
hash = (hash * 31 + strbyte(str, i)) % 4294967296
end
return hash
end
local function EnsureChannel()
local channelId = GetChannelName(CHANNEL_NAME)
if channelId == 0 then
JoinChannelByName(CHANNEL_NAME)
channelId = GetChannelName(CHANNEL_NAME)
end
private.channelId = channelId > 0 and channelId or nil
private.channelName = private.channelId and CHANNEL_NAME or nil
end
local function GetChannelDisplayName(channelName, channelString, channelNumber, channelBaseName)
if channelBaseName and channelBaseName ~= "" and not tonumber(channelBaseName) then
return channelBaseName
end
if channelName and channelName ~= "" and not tonumber(channelName) then
return channelName
end
if channelString and channelString ~= "" then
local parsed = channelString:match("^%d+%.%s*(.+)$")
if parsed then return parsed end
local numeric = tonumber(channelString)
if numeric then return select(2, GetChannelName(numeric)) end
return channelString
end
if channelNumber then
return select(2, GetChannelName(channelNumber))
end
end
local function ChatFilter(_, _, msg, _, channelString, _, channelNumber, channelName, channelBaseName)
local displayName = GetChannelDisplayName(channelName, channelString, channelNumber, channelBaseName)
if displayName ~= CHANNEL_NAME then return end
if strsub(msg, 1, #COMM_PREFIX) == COMM_PREFIX then
return true
end
end
function ChannelSync:OnEnable()
self:RegisterEvent("PLAYER_ENTERING_WORLD", "OnPlayerEnteringWorld")
self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", "OnChannelNotice")
self:RegisterEvent("CHAT_MSG_CHANNEL", "OnChannelMessage")
if ChatFrame_AddMessageEventFilter then
ChatFrame_AddMessageEventFilter("CHAT_MSG_CHANNEL", ChatFilter)
end
EnsureChannel()
end
function ChannelSync:OnPlayerEnteringWorld()
EnsureChannel()
end
function ChannelSync:OnChannelNotice()
EnsureChannel()
end
local function EncodePayload(payload)
if not CanEncode() then return end
local serialized = libS:Serialize(payload)
if not serialized then
DebugPrint("Serialize failed.")
return
end
local compressed = libD:CompressDeflate(serialized, { level = 9 })
if not compressed then
DebugPrint("CompressDeflate failed.")
return
end
return libD:EncodeForPrint(compressed)
end
local function DecodePayload(encoded)
if not CanEncode() then return end
local decoded = libD:DecodeForPrint(encoded)
if not decoded then
DebugPrint("DecodeForPrint failed.")
return
end
local decompressed = libD:DecompressDeflate(decoded)
if not decompressed then
DebugPrint("DecompressDeflate failed.")
return
end
local ok, payload = libS:Deserialize(decompressed)
if not ok then
DebugPrint("Deserialize failed.")
return
end
return payload
end
local function MaybeNotifyNewerVersion(remoteVersion, sender)
local remoteKey = VersionKey(remoteVersion)
local localKey = private.addonVersionKey
if remoteKey == 0 or localKey == 0 or remoteKey <= localKey then return end
local noticeKey = tostring(sender or "unknown") .. ":" .. tostring(remoteVersion)
if private.notifiedNewer[noticeKey] then return end
private.notifiedNewer[noticeKey] = true
TSM:Printf(L["A newer version of AuctionDB (%s) was received from %s. You are running %s."], remoteVersion, sender or "unknown", private.addonVersion)
end
function ChannelSync:BroadcastScanData(scanType, items)
if scanType ~= "Full" and scanType ~= "Group" and scanType ~= "Search" then
return
end
if TSM.processingData then
TSMAPI:CreateTimeDelay("auctionDBChannelSyncBroadcast", 0.5, function()
ChannelSync:BroadcastScanData(scanType, items)
end)
return
end
EnsureChannel()
if not private.channelName or not private.channelId then return end
local itemIDs = ChannelSync:CollectItemIDs(items)
if not itemIDs or #itemIDs == 0 then return end
local function QueueEncoded(encoded)
local hash = HashString(encoded)
if hash == private.lastBroadcastHash then return end
private.lastBroadcastHash = hash
local total = ceil(#encoded / CHUNK_SIZE)
if total > MAX_TOTAL_CHUNKS then return false end
tinsert(private.sendQueue, {hash = hash, encoded = encoded, total = total})
if not private.isSending then
private.isSending = true
TSMAPI.Threading:Start(ChannelSync.SendQueueThread, 0.3)
end
return true
end
local function QueueBatch(startIndex, endIndex)
local batch = {}
for i = startIndex, endIndex do
tinsert(batch, itemIDs[i])
end
local payload = ChannelSync:BuildPayloadFromItemIDs(scanType, batch)
if not payload then return end
local encoded = EncodePayload(payload)
if not encoded then return end
local total = ceil(#encoded / CHUNK_SIZE)
if total > MAX_TOTAL_CHUNKS and #batch > 1 then
local mid = floor((startIndex + endIndex) / 2)
QueueBatch(startIndex, mid)
QueueBatch(mid + 1, endIndex)
return
end
if total > MAX_TOTAL_CHUNKS then
TSM:Print("AuctionDB channel sync payload too large; skipping batch.")
return
end
QueueEncoded(encoded)
end
for i = 1, #itemIDs, MAX_ITEMS_PER_PAYLOAD do
QueueBatch(i, min(i + MAX_ITEMS_PER_PAYLOAD - 1, #itemIDs))
end
end
function ChannelSync:CollectItemIDs(items)
local itemIDs, list = {}, {}
if items then
if #items > 0 then
for _, itemString in ipairs(items) do
local itemID = TSMAPI:GetItemID(itemString)
if itemID and not itemIDs[itemID] then
itemIDs[itemID] = true
tinsert(list, itemID)
end
end
else
for itemString in pairs(items) do
local itemID = TSMAPI:GetItemID(itemString)
if itemID and not itemIDs[itemID] then
itemIDs[itemID] = true
tinsert(list, itemID)
end
end
end
else
local scanTime = TSM.db.realm.lastCompleteScan
for itemID in pairs(TSM.data) do
TSM:DecodeItemData(itemID)
if TSM.data[itemID].lastScan == scanTime then
tinsert(list, itemID)
end
end
end
return list
end
function ChannelSync:BuildPayloadFromItemIDs(scanType, itemIDList)
local payloadItems = {}
for _, itemID in ipairs(itemIDList) do
local data = TSM.data[itemID]
if data then
TSM:EncodeItemData(itemID)
if data.encoded then
payloadItems[itemID] = data.encoded
end
end
end
if not next(payloadItems) then return end
return {
v = 1,
scanType = scanType,
scanTime = TSM.db.realm.lastCompleteScan,
version = private.addonVersion,
items = payloadItems,
}
end
local function DecodeIncomingItem(itemID, encoded)
local tmp = {}
tmp[itemID] = {encoded = encoded}
TSM:DecodeItemData(itemID, tmp)
return tmp[itemID]
end
local function MergeIncomingData(payload, sender)
if type(payload) ~= "table" then return end
local incoming = {}
if payload.items then
incoming = payload.items
elseif type(payload.scanData) == "string" then
TSM:Deserialize(payload.scanData, incoming, true)
else
return
end
local updated = 0
local lastItemID
for itemID, data in pairs(incoming) do
local incomingData = data
if payload.items then
incomingData = DecodeIncomingItem(itemID, data)
end
if incomingData then
local existing = TSM.data[itemID]
if existing then
TSM:DecodeItemData(itemID)
if incomingData.lastScan and (not existing.lastScan or incomingData.lastScan > existing.lastScan) then
TSM.data[itemID] = incomingData
updated = updated + 1
lastItemID = itemID
end
else
TSM.data[itemID] = incomingData
updated = updated + 1
lastItemID = itemID
end
end
end
if payload.scanTime and (not TSM.db.realm.lastCompleteScan or payload.scanTime > TSM.db.realm.lastCompleteScan) then
TSM.db.realm.lastCompleteScan = payload.scanTime
end
TSM:Serialize()
if updated == 0 and sender then
DebugPrint("No items updated from " .. sender .. ".")
elseif updated > 0 and sender then
if updated == 1 and lastItemID then
local link = select(2, GetItemInfo(lastItemID)) or ("item:" .. tostring(lastItemID))
TSM:Printf("AuctionDB updated %s from %s.", link, sender)
else
TSM:Printf("AuctionDB updated %d items from %s.", updated, sender)
end
end
end
function ChannelSync:OnChannelMessage(_, msg, source, _, channelString, _, channelNumber, channelName, channelBaseName)
local displayName = GetChannelDisplayName(channelName, channelString, channelNumber, channelBaseName)
if displayName ~= CHANNEL_NAME then
if strsub(msg or "", 1, #COMM_PREFIX) == COMM_PREFIX then
DebugPrint("Prefix received; displayName=" .. tostring(displayName) .. " channelNumber=" .. tostring(channelNumber) .. " channelString=" .. tostring(channelString) .. " channelName=" .. tostring(channelName) .. " channelBaseName=" .. tostring(channelBaseName))
end
return
end
if strsub(msg or "", 1, #COMM_PREFIX) ~= COMM_PREFIX then
DebugPrint("Message on channel without prefix: '" .. strsub(msg or "", 1, #COMM_PREFIX) .. "'.")
return
end
source = ("-"):split(source or "")
if strlower(source or "") == strlower(UnitName("player") or "") then return end
local headerLen = #COMM_PREFIX + 16
if #msg <= headerLen then return end
local meta = strsub(msg, #COMM_PREFIX + 1, headerLen)
local chunk = strsub(msg, headerLen + 1)
local hashHex = strsub(meta, 1, 8)
local seqHex = strsub(meta, 9, 12)
local totalHex = strsub(meta, 13, 16)
local hash = tonumber(hashHex, 16)
local seq = tonumber(seqHex, 16)
local total = tonumber(totalHex, 16)
if not hash or not seq or not total then return end
local bundle = private.incoming[hash]
if not bundle or (time() - bundle.time) > BUNDLE_TIMEOUT then
if bundle then
DebugPrint("Bundle timeout " .. format("%08x", hash) .. " (" .. bundle.received .. "/" .. bundle.total .. ").")
end
bundle = {time = time(), total = total, chunks = {}, received = 0}
private.incoming[hash] = bundle
end
if bundle.total ~= total then
DebugPrint("Bundle total mismatch for " .. format("%08x", hash) .. ". Resetting.")
private.incoming[hash] = {time = time(), total = total, chunks = {}, received = 0}
bundle = private.incoming[hash]
end
if not bundle.chunks[seq] then
bundle.chunks[seq] = chunk
bundle.received = bundle.received + 1
end
if bundle.received < bundle.total then return end
local parts = {}
for i = 1, bundle.total do
if not bundle.chunks[i] then return end
tinsert(parts, bundle.chunks[i])
end
private.incoming[hash] = nil
local payload = DecodePayload(table.concat(parts))
if not payload then
DebugPrint("Payload decode failed for bundle " .. format("%08x", hash))
return
end
if payload.version then
MaybeNotifyNewerVersion(payload.version, source)
end
MergeIncomingData(payload, source)
end
function ChannelSync:SendQueueThread()
while #private.sendQueue > 0 do
local job = private.sendQueue[1]
for i = 1, job.total do
local chunk = strsub(job.encoded, (i - 1) * CHUNK_SIZE + 1, i * CHUNK_SIZE)
local msg = COMM_PREFIX .. string.format("%08x%04x%04x", job.hash, i, job.total) .. chunk
SendChatMessage(msg, "CHANNEL", nil, private.channelId)
self:Sleep(SEND_INTERVAL)
end
DebugPrint("Sent bundle " .. format("%08x", job.hash) .. " (" .. job.total .. " chunks).")
tremove(private.sendQueue, 1)
end
private.isSending = false
end

293
TradeSkillMaster_AuctionDB/Modules/GUI.lua

@ -20,14 +20,33 @@ function GUI:Show(frame)
private.startScanContent = private.startScanContent or private:CreateStartScanContent(frame)
private.startScanContent:Show()
if TSM.Scan.isScanning and not TSMAPI.AuctionScan:IsScanning() then
TSM.Scan:DoneScanning()
end
if TSM.Scan.isScanning then
GUI:SetPauseEnabled(true)
GUI:SetPaused(TSMAPI.AuctionScan:IsPaused())
else
GUI:SetPauseEnabled(false)
GUI:SetPaused(false)
end
end
function GUI:Hide()
private.statusBar:Hide()
private.startScanContent:Hide()
if TSM.Scan.isScanning and not TSMAPI.AuctionScan:IsScanning() then
TSM.Scan:DoneScanning()
end
if TSM.Scan.isScanning then
TSM.Scan:PauseScan()
else
TSM.Scan:DoneScanning()
TSMAPI.AuctionScan:StopScan()
GUI:SetPauseEnabled(false)
GUI:SetPaused(false)
end
end
function GUI:UpdateStatus(text, major, minor)
@ -39,6 +58,20 @@ function GUI:UpdateStatus(text, major, minor)
end
end
function GUI:SetPauseEnabled(enabled)
if not private.startScanContent or not private.startScanContent.pauseFullScanButton then return end
if enabled then
private.startScanContent.pauseFullScanButton:Enable()
else
private.startScanContent.pauseFullScanButton:Disable()
end
end
function GUI:SetPaused(isPaused)
if not private.startScanContent or not private.startScanContent.pauseFullScanButton then return end
private.startScanContent.pauseFullScanButton:SetText(isPaused and L["Resume"] or L["Pause"])
end
function private:CreateStatusBar(parent)
local frame = TSMAPI.GUI:CreateStatusBar(parent, "TSMAuctionDBStatusBar")
TSMAPI.GUI:CreateHorizontalLine(frame, -30, parent)
@ -51,59 +84,29 @@ function private:CreateStartScanContent(parent)
frame:SetAllPoints(parent)
frame:Hide()
-- Don't create or handle the GetAll button if player has disabled GetAll.
-- NOTE: This GUI creation is only done once per game reload, so this choice
-- won't change until the user does a UI "/reload" or logs out of the game.
local includeGetAll = not TSM.db.profile.disableGetAll
local function UpdateGetAllButton()
if not frame.startGetAllButton then
return -- Do nothing if the GetAll feature is disabled.
end
if TSM.Scan.isScanning then
frame:Disable()
elseif not select(2, CanSendAuctionQuery()) then
-- Server says that GetAll isn't ready. Check our stored cooldown value.
local previous = TSM.db.profile.lastGetAll or time()
if previous > (time() - 15*60) then -- 15 minute enforced cooldown between GetAll scans...
local diff = previous + 15*60 - time()
local diffMin = math.floor(diff/60)
local diffSec = diff - diffMin*60
frame.getAllStatusText:SetText("|cff990000"..format(L["Ready in %s min and %s sec"], diffMin, diffSec))
else
frame.getAllStatusText:SetText("|cff990000"..L["Not Ready"])
end
frame:Enable()
frame.startGetAllButton:Disable()
else
-- Server says that GetAll is ready.
frame:Enable()
frame.getAllStatusText:SetText("|cff009900"..L["Ready"])
frame.startGetAllButton:Enable()
end
end
if includeGetAll then
frame:SetScript("OnShow", function(self)
TSMAPI:CreateTimeDelay("auctionDBGetAllStatus", 0, UpdateGetAllButton, 0.2)
end)
frame:SetScript("OnHide", function(self)
TSMAPI:CancelFrame("auctionDBGetAllStatus")
end)
end
frame.Enable = function(self)
if self.startGetAllButton then self.startGetAllButton:Enable() end
self.startFullScanButton:Enable()
self.pauseFullScanButton:Disable()
self.cancelScanButton:Disable()
self.startGroupScanButton:Enable()
self.startInventoryScanButton:Enable()
self.startBagScanButton:Enable()
self.startBankScanButton:Enable()
end
frame.Disable = function(self)
if self.startGetAllButton then self.startGetAllButton:Disable() end
self.startFullScanButton:Disable()
if TSM.Scan.isScanning then
self.pauseFullScanButton:Enable()
self.cancelScanButton:Enable()
else
self.pauseFullScanButton:Disable()
self.cancelScanButton:Disable()
end
self.startGroupScanButton:Disable()
self.startInventoryScanButton:Disable()
self.startBagScanButton:Disable()
self.startBankScanButton:Disable()
end
-- Top row: Auto updater.
@ -141,33 +144,8 @@ function private:CreateStartScanContent(parent)
buttonFrame:SetPoint("TOPLEFT", content, "TOPRIGHT", -200, 0)
buttonFrame:SetPoint("BOTTOMRIGHT")
-- Row: GetAll Scan.
-- NOTE: We hide this button if the player has disabled GetAll scans.
-- Row: Full Scan.
local yOffset = -50
if includeGetAll then
local btn = TSMAPI.GUI:CreateButton(buttonFrame, 18)
btn:SetPoint("TOPLEFT", 6, yOffset)
btn:SetPoint("TOPRIGHT", -6, yOffset)
btn:SetHeight(22)
btn:SetScript("OnClick", TSM.Scan.StartGetAllScan)
btn:SetText(L["Run GetAll Scan"])
btn.tooltip = L["A GetAll scan is the fastest in-game method for scanning every item on the auction house. However, there are many possible bugs on Blizzard's end with it including the chance for it to disconnect you from the game. Also, it has a 15 minute cooldown. You can disable the GetAll button via TSM's AuctionDB options if this feature doesn't work well on your server."]
frame.startGetAllButton = btn
local text = TSMAPI.GUI:CreateLabel(buttonFrame)
text:SetPoint("TOPLEFT", btn, "BOTTOMLEFT", 0, -3)
text:SetPoint("TOPRIGHT", btn, "BOTTOMRIGHT", 0, -3)
text:SetHeight(16)
text:SetJustifyH("CENTER")
text:SetJustifyV("CENTER")
frame.getAllStatusText = text
yOffset = yOffset - 50
TSMAPI.GUI:CreateHorizontalLine(buttonFrame, yOffset)
yOffset = yOffset - 20
end
-- Row: Full Scan.
local btn = TSMAPI.GUI:CreateButton(buttonFrame, 18)
@ -176,7 +154,7 @@ function private:CreateStartScanContent(parent)
btn:SetHeight(22)
btn:SetScript("OnClick", TSM.Scan.StartFullScan)
btn:SetText(L["Run Full Scan"])
btn.tooltip = L["A full auction house scan will scan every item on the auction house but is far slower than a GetAll scan. Expect this scan to take several minutes or longer."]
btn.tooltip = L["A full auction house scan will scan every item on the auction house. Expect this scan to take several minutes or longer."]
frame.startFullScanButton = btn
yOffset = yOffset - 40
@ -195,6 +173,72 @@ function private:CreateStartScanContent(parent)
btn.tooltip = L["This will do a slow auction house scan of every item in the selected groups and update their AuctionDB prices. This may take several minutes."]
frame.startGroupScanButton = btn
yOffset = yOffset - 40
TSMAPI.GUI:CreateHorizontalLine(buttonFrame, yOffset)
yOffset = yOffset - 20
-- Row: Inventory Scan.
local btn = TSMAPI.GUI:CreateButton(buttonFrame, 18)
btn:SetPoint("TOPLEFT", 6, yOffset)
btn:SetPoint("TOPRIGHT", -6, yOffset)
btn:SetHeight(22)
btn:SetScript("OnClick", GUI.StartInventoryScan)
btn:SetText(L["Scan Inventory"])
btn.tooltip = L["Scans bags and bank for all characters."]
frame.startInventoryScanButton = btn
yOffset = yOffset - 30
-- Row: Bag Scan.
local btn = TSMAPI.GUI:CreateButton(buttonFrame, 18)
btn:SetPoint("TOPLEFT", 6, yOffset)
btn:SetPoint("TOPRIGHT", -6, yOffset)
btn:SetHeight(22)
btn:SetScript("OnClick", GUI.StartBagScan)
btn:SetText(L["Scan Bags"])
btn.tooltip = L["Scan bag items."]
frame.startBagScanButton = btn
yOffset = yOffset - 30
-- Row: Bank Scan.
local btn = TSMAPI.GUI:CreateButton(buttonFrame, 18)
btn:SetPoint("TOPLEFT", 6, yOffset)
btn:SetPoint("TOPRIGHT", -6, yOffset)
btn:SetHeight(22)
btn:SetScript("OnClick", GUI.StartBankScan)
btn:SetText(L["Scan Bank"])
btn.tooltip = L["Scan bank items."]
frame.startBankScanButton = btn
yOffset = yOffset - 40
TSMAPI.GUI:CreateHorizontalLine(buttonFrame, yOffset)
yOffset = yOffset - 20
local pauseBtn = TSMAPI.GUI:CreateButton(buttonFrame, 16)
pauseBtn:SetPoint("TOPLEFT", 6, yOffset)
pauseBtn:SetPoint("TOPRIGHT", -6, yOffset)
pauseBtn:SetHeight(18)
pauseBtn:SetScript("OnClick", TSM.Scan.TogglePause)
pauseBtn:SetText(L["Pause"])
pauseBtn:Disable()
frame.pauseFullScanButton = pauseBtn
yOffset = yOffset - 24
local cancelBtn = TSMAPI.GUI:CreateButton(buttonFrame, 16)
cancelBtn:SetPoint("TOPLEFT", 6, yOffset)
cancelBtn:SetPoint("TOPRIGHT", -6, yOffset)
cancelBtn:SetHeight(18)
cancelBtn:SetScript("OnClick", GUI.CancelScan)
cancelBtn:SetText(L["Cancel Scan"])
cancelBtn:Disable()
frame.cancelScanButton = cancelBtn
return frame
end
@ -208,3 +252,108 @@ function GUI:StartGroupScan()
end
TSM.Scan:StartGroupScan(items)
end
function GUI:StartInventoryScan()
local players = TSMAPI:ModuleAPI("ItemTracker", "playerlist")
if not players then
TSM:Print(L["ItemTracker data unavailable."])
return
end
local specificItems, baseCandidates, hasSpecific = {}, {}, {}
for _, player in ipairs(players) do
local bags = TSMAPI:ModuleAPI("ItemTracker", "playerbags", player)
local bank = TSMAPI:ModuleAPI("ItemTracker", "playerbank", player)
if bags then
private:CollectItemStrings(bags, specificItems, baseCandidates, hasSpecific)
end
if bank then
private:CollectItemStrings(bank, specificItems, baseCandidates, hasSpecific)
end
end
for baseItemString in pairs(baseCandidates) do
if not hasSpecific[baseItemString] then
specificItems[baseItemString] = true
end
end
local items = {}
for itemString in pairs(specificItems) do
tinsert(items, itemString)
end
if #items == 0 then
TSM:Print(L["No inventory items found."])
return
end
TSM.Scan:StartGroupScan(items)
end
function private:CollectItemStrings(data, specificItems, baseCandidates, hasSpecific)
for itemString in pairs(data) do
if type(itemString) == "string" then
local baseItemString = TSMAPI:GetBaseItemString(itemString) or itemString
if itemString ~= baseItemString then
specificItems[itemString] = true
hasSpecific[baseItemString] = true
else
baseCandidates[baseItemString] = true
end
end
end
end
function private:GetItemTrackerItems(key)
local data = TSMAPI:ModuleAPI("ItemTracker", key)
if not data then
TSM:Print(L["ItemTracker data unavailable."])
return
end
local specificItems, baseCandidates, hasSpecific = {}, {}, {}
private:CollectItemStrings(data, specificItems, baseCandidates, hasSpecific)
for baseItemString in pairs(baseCandidates) do
if not hasSpecific[baseItemString] then
specificItems[baseItemString] = true
end
end
local items = {}
for itemString in pairs(specificItems) do
tinsert(items, itemString)
end
return items
end
function GUI:StartBagScan()
local items = private:GetItemTrackerItems("playerbags")
if not items then
return
end
if #items == 0 then
TSM:Print(L["No bag items found."])
return
end
TSM.Scan:StartGroupScan(items)
end
function GUI:StartBankScan()
local items = private:GetItemTrackerItems("playerbank")
if not items then
return
end
if #items == 0 then
TSM:Print(L["No bank items found."])
return
end
TSM.Scan:StartGroupScan(items)
end
function GUI:CancelScan()
TSMAPI.AuctionScan:StopScan(true)
TSM.Scan:DoneScanning()
end

303
TradeSkillMaster_AuctionDB/Modules/Scanning.lua

@ -27,10 +27,8 @@ local function FullScanCallback(event, ...)
-- We're running a "Full Scan" and have received an auction page.
-- NOTE: These normal per-page scans receive 50 items per page, and will
-- successfully download ALL auctions on private servers, thanks to pagination.
-- For example, while Warmane's "GetAll" only returns 55000 of 126559 auctions,
-- the regular "Full Scan" mode retrieves all 2532 pages of 50 items each,
-- meaning that it covers 126600 auctions (and therefore grabs them all)
-- in this example. Users should always prefer "Full Scan" when "GetAll" fails.
-- For example, a full scan retrieves all pages of 50 items each,
-- meaning that it covers the entire auction list.
local page, total = ...
-- Calculate the current page progress and the remainder as floating-point values.
@ -161,240 +159,12 @@ local function FullScanCallback(event, ...)
-- NOTE: "SCAN_INTERRUPTED" is from LibAuctionScan-1.0, which isn't used
-- by TSM anymore, and "INTERRUPTED" is from "TSM/Auction/AuctionScanning.lua",
-- which is what this scanner uses nowadays.
local data = ...
Scan:ProcessScanData(data)
Scan:DoneScanning()
end
end
function Scan.ProcessGetAllScan(self)
-- Await the "AUCTION_ITEM_LIST_UPDATE" event with the results of the "get all" scan.
-- NOTE: This event won't be received if the server ignored/throttled the "get all" scan.
local time_start = time()
local progress_bar = 0
while true do
-- NOTE: The fake progress bar takes 20 seconds to fill to 100% with this interval.
progress_bar = min(progress_bar + 1, 100) -- Fake progress bar from 0-100%.
self:Sleep(0.2) -- Update progress bar with this interval while waiting for server data.
if not Scan.isScanning then return end -- Failed or aborted scan.
if Scan.getAllLoaded then -- We have received the list of auctions via "AUCTION_ITEM_LIST_UPDATE" event!
break
end
-- NOTE: If it has taken more than 30 seconds, we can assume that the server
-- didn't send any data and won't be replying, most likely due to hidden
-- throttling (happens on many private servers, if scanning too soon after another).
local time_elapsed = time() - time_start
if time_elapsed >= 30 then
-- If this happens, the user has most likely been throttled and should try again later.
TSM.GUI:UpdateStatus(L["Running query... Server not responding due to throttling? Try again later..."], nil, 100)
else
TSM.GUI:UpdateStatus(L["Running query..."], nil, progress_bar)
end
end
-- IMPORTANT: As explained in the "Scan:AUCTION_ITEM_LIST_UPDATE()" code
-- comments, we're allowing GetAll scans even when the server DOESN'T return
-- all auctions. For example, Warmane has chosen to limit GetAll results to
-- only 55k auctions (even though they usually have 120k+ auctions). This
-- means that the market data calculations will be missing data. We should
-- therefore warn the user and tell them to use "Full Scan" instead, when
-- they're playing on such "limited GetAll" servers. But we'll still allow
-- use of "GetAll", with a warning message for people who INSIST on using it,
-- since even when it only looks at half the auctions, it can still give a
-- "pretty decent" idea of market values.
-- NOTE: It WOULD be "best" to always throw away (ignore) incomplete "GetAll"
-- data, but most casual players will prefer to have this feature even if those
-- incomplete scans are much less accurate than a proper "Full Scan".
-- NOTE: The inaccuracy of an incomplete "GetAll" scan completely depends on
-- how the server implements "GetAll". If the server sorts the cheapest
-- auctions first, then it's somewhat acceptable for popular items which
-- have many stacks (and are therefore likely to contain enough data points
-- in the "GetAll" result), and those items will "just" tend to be undervalued
-- by 2-10% less than their real market value (since their percentile-based
-- scans will look at less auctions than the real amount). But for very rare
-- or unpopular items, the partial "GetAll" scan is very dangerous, since
-- you might only receive the massively overpriced auctions of a certain
-- item, and thereby calculate an insanely high market value for it. These
-- dangers are increased if the server sends the auctions in a totally
-- random order, which means an even greater risk of market price pollution
-- by only seeing overpriced auctions for many of the items.
-- NOTE: Furthermore, receiving incomplete "GetAll" results means that TSM
-- will wipe all of its "cheapest, current buyout price" data for all items,
-- and then fills them with incorrect data (or nothing at all if an item
-- wasn't seen in the latest fetch), thus hindering your ability to look up
-- the correct "best current buyout prices" too.
-- NOTE: In summary, you'll have to use partial "GetAll" results at your
-- own risk! A "Full Scan" is ALWAYS much better when your server's "GetAll"
-- doesn't provide all auction data!
local shown, total = GetNumAuctionItems("list")
if total ~= Scan.getAllLoaded then -- getAllLoaded = Same as "shown", but cached via our GetAll event.
-- NOTE: Message is not localized, because who the hell is gonna provide
-- translations to this project? It'd be a waste of time to translate it,
-- especially since we dynamically insert a word based on the result.
TSM:Print(format(
"WARNING: Your server has %d auctions, but it%s sent %d auctions to us. Please use the normal \"Full Scan\" instead, if you want to accurately calculate the real market values of items. Your server's \"GetAll\" scan doesn't fetch all auctions!",
total, ((total > shown) and " only" or ""), shown
))
else
TSM:Print(format("All auctions received from server (%d auctions)...", Scan.getAllLoaded))
end
-- Collect relevant data about the auctions that we've received.
-- NOTE: "Scan.getAllLoaded" is the count of auctions we've received from
-- the server. One per listing. We keep this cached value to constantly verify
-- that we're still looking at the same auction-list while we're processing.
local data = {}
progress_bar = 0
for auction_idx=1, Scan.getAllLoaded do
if (auction_idx == 1) or (auction_idx == Scan.getAllLoaded) or ((auction_idx % 100) == 0) then
-- Update progress bar for the 1st, last, and every 100th auction.
progress_bar = min(100*(auction_idx/Scan.getAllLoaded), 100) -- Calculate progress bar from 0-100%.
TSM.GUI:UpdateStatus(format(L["Scanning page %s/%s"], 1, 1), progress_bar)
-- Yield the CPU every 100th "auction listing", to prevent freezing the game.
self:Yield()
-- Verify the currently visible auction count, to ensure that we're
-- still analyzing the same list of "GetAll" auction items.
if GetNumAuctionItems("list") ~= Scan.getAllLoaded then
-- This can happen due to server/game issues, or if the user
-- closes the auction GUI while we're processing. If that
-- happens, we'll abort the scan without processing the data.
--TSM:Print(L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."])
TSM:Print("GetAll: Scan failed or aborted by user.")
Scan:DoneScanning() -- Sets isScanning and getAllLoaded to nil.
return
end
end
-- Retrieve information about this auction.
-- * itemID = The numeric ID of the item (such as 52021, which would represent "Iceblade Arrow" for example).
-- * stack_size = How many are in this auction's exact "stack" (such as 940, if they're selling a stack of 940).
-- * buyout = The buyout price of the auction (to get the per-item price, we need to divide "buyout / stack_size").
local itemID = TSMAPI:GetItemID(GetAuctionItemLink("list", auction_idx))
--local _, _, stack_size, _, _, _, _, _, _, buyout = GetAuctionItemInfo("list", auction_idx)
local _, _, stack_size, _, _, _, _, _, buyout = GetAuctionItemInfo("list", auction_idx)
-- Only process this auction if we saw a valid buyout price (ignore bid-only auctions, etc).
if itemID and buyout and (buyout > 0) then
-- Calculate the price per item, always rounded downwards.
-- NOTE: TSM's "buyout per item" calculations are actually a freaking
-- mess. They basically use "floor(buyout/count)" everywhere in the
-- code, EXCEPT in SOME places where they use "floor(buyout/count + 0.5)"
-- which means that those round to the nearest number instead, which
-- is definitely more correct. In fact, it would probably be even more
-- correct to enforce a minimum value of "1", otherwise a stack of
-- 100 items for a total buyout price of 10 copper would end up as
-- "0 copper per item" by the basic flooring algorithm (floor(0.1) = 0).
-- But whatever, it's extremely inconsistently used everywhere in TSM's
-- codebase, and most places use the plain "floor downwards", so we'll
-- do that too. It would be way too much effort to rewrite the HUNDREDS of
-- other code locations that deal with money (all via differently named
-- variables), just to handle smarter rounding, and it would require
-- a lot of effort to ensure that TSM's algorithms still work afterwards.
-- But yeah, just realize this: TSM's per-item calculations aren't great,
-- however it really DOESN'T MATTER MUCH since it only affects items
-- whose prices are less than 1 copper per item, which is probably
-- why TSM's author never noticed any issues with the basic "floor()".
local buyout_per_item = floor(buyout / stack_size)
-- Append to the existing "data to process" for this item ID if exists, else create new item.
data[itemID] = data[itemID]
if not data[itemID] then
data[itemID] = {records={}, minBuyout=math.huge, quantity=0}
end
-- Calculate the lowest "per-item buyout price" we're seeing for this item.
data[itemID].minBuyout = min(data[itemID].minBuyout, buyout_per_item)
-- Count the total amount of this item that exists on the auction house (adds together all stacks).
-- NOTE: This is super useful when we're calculating the market value in data.lua,
-- since it tells us immediately what the total count (quantity) of all new records is.
data[itemID].quantity = data[itemID].quantity + stack_size
-- BRAINDEAD OLD TSM CODE WHICH ADDS 1 RECORD PER ITEM IN THE STACK,
-- MEANING 500 STACKS OF 1000 ARROWS WOULD BE HALF A MILLION TABLE
-- ROWS AND WOULD LEAD TO "OUT OF MEMORY" ERRORS. DON'T DO THIS!
-- for j=1, stack_size do
-- tinsert(data[itemID].records, buyout_per_item)
-- end
-- Rewritten, intelligent code which adds 1 record per "stack" (auction) instead.
-- NOTE: We avoid using hash-keys, saving memory by using a numeric array instead.
-- NOTE: We'll store 1 table per unique itemID, and 1 record per "auction"
-- which passed our filters (has a buyout price), meaning that it's usually
-- a bit less than the total auction count on the server, and will never
-- be too many rows for WoW's Lua memory system to handle.
-- NOTE: If there is ever a server with so many auctions that even this
-- would reach WoW's Lua memory limits, then we could possibly store
-- the records as a concatenated, semicolon-separated string instead,
-- which would be slower but would handle millions of auctions with ease.
tinsert(data[itemID].records, {stack_size, buyout_per_item})
end
end
-- Process the collected "GetAll" auction data as a new "complete scan" with today's date.
TSM.db.realm.lastCompleteScan = time()
TSM.Data:ProcessData(data, nil, verifyNewAlgorithm)
-- Show GUI progress while we're waiting for the processing.
-- NOTE: The status text will be set to "complete" elsewhere, automatically.
TSM.GUI:UpdateStatus(L["Processing data..."])
while TSM.processingData do
self:Sleep(0.2)
end
-- Processing is complete, so warn the user that they should reload the UI now.
TSM:Print(L["It is strongly recommended that you reload your ui (type '/reload') after running a GetAll scan. Otherwise, any other scans (Post/Cancel/Search/etc) will be much slower than normal."])
end
function Scan:AUCTION_ITEM_LIST_UPDATE()
Scan:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
-- shown = How many auctions we received in the current data batch.
-- total = The total number of auction items.
local shown, total = GetNumAuctionItems("list")
-- OLD TSM CODE: It assumes that "GetAll" returns ALL auctions on official
-- Blizzard servers, so it also checks if "shown equals the total".
--if shown ~= total or shown == 0 then
-- WORKAROUND: Because places like Warmane with huge auction houses DON'T
-- return all items even via "GetAll" scans, we must ignore shown-vs-total
-- mismatches. The server won't let us query the subsequent pages since
-- there isn't any pagination in the "GetAll" API, and wouldn't work anyway
-- due to the 15-30 minute cooldown for "GetAll" calls, so we can't fetch
-- all auctions if the server is too popular. For example, typical
-- Warmane-Icecrown "GetAll" results will be: shown=55000, total=122523,
-- meaning that "GetAll" only receives about half of the total auctions.
if shown <= 0 then
--TSM:Print(L["GetAll scan did not run successfully due to issues on Blizzard's end. Using the TSM application for your scans is recommended."])
TSM:Print("GetAll: Scan failed due to server issues.")
Scan:DoneScanning() -- Sets isScanning and getAllLoaded to nil.
return
end
-- Cache the amount of auctions we received on the current "page/batch", so that
-- we can validate that we're looking at this "page" while processing later.
Scan.getAllLoaded = shown
end
function Scan:GetAllScanQuery()
-- NOTE: This API doesn't work properly on some servers. For example, on Warmane,
-- it always claims that you can do a "GetAll" scan after you log in, even
-- if you're on cooldown and the server will actually be ignoring your request,
-- which is why we had to implement timeout detection in "ProcessGetAllScan()".
local canScan, canGetAll = CanSendAuctionQuery()
if not canGetAll then return TSM:Print(L["Can't run a GetAll scan right now."]) end
if not canScan then return TSMAPI:CreateTimeDelay(0.5, Scan.GetAllScanQuery) end
Scan:RegisterEvent("AUCTION_ITEM_LIST_UPDATE")
QueryAuctionItems("", nil, nil, 0, 0, 0, 0, 0, 0, true)
TSMAPI.Threading:Start(Scan.ProcessGetAllScan, 1, function()
-- Pass through the cached "full scan complete elapsed" value, which ONLY
-- contains a value if the latest full scan was successfully completed.
-- NOTE: This callback runs when the thread is finished, no matter what
-- reason. That's why we must preserve the "time elapsed" value for display.
Scan:DoneScanning(Scan.fullScanCompleteElapsed)
end)
end
local function GroupScanCallback(event, ...)
if event == "QUERY_COMPLETE" then
@ -462,12 +232,13 @@ end
function Scan:StartGroupScan(items)
Scan.isScanning = "Group"
Scan.isBuggedGetAll = nil
Scan.groupItems = items
wipe(Scan.filterList)
wipe(Scan.groupScanData)
Scan.numFilters = 0
TSMAPI.AuctionScan:StopScan()
TSM.GUI:SetPauseEnabled(true)
TSM.GUI:SetPaused(false)
Scan.groupScanStartTime = time() -- Keep track of when we started the "Group Scan".
TSMAPI:GenerateQueries(items, GroupScanCallback)
TSM.GUI:UpdateStatus(L["Preparing Filters..."])
@ -476,34 +247,16 @@ end
function Scan:StartFullScan()
Scan.isScanning = "Full"
TSM.GUI:UpdateStatus(L["Running query..."])
Scan.isBuggedGetAll = nil
Scan.groupItems = nil
TSMAPI.AuctionScan:StopScan()
TSM.GUI:SetPauseEnabled(true)
TSM.GUI:SetPaused(false)
Scan.fullScanStartTime = time() -- Keep track of when we started the "Full Scan".
Scan.fullScanSecondsPerPage = -1 -- Reset the page-speed timer.
Scan.fullScanCompleteElapsed = nil -- Reset the "full scan completed" information.
TSMAPI.AuctionScan:RunQuery({name=""}, FullScanCallback)
end
function Scan:StartGetAllScan()
-- Refuse to perform "GetAll" if we're called while "GetAll" is disabled.
-- NOTE: Only happens if the player has visited the auction house and looked
-- at the AuctionDB GUI, and THEN gone into TSM's options to disable "GetAll".
-- The "Run GetAll Scan" button remains until /reload, so we must block it.
if TSM.db.profile.disableGetAll then
TSM:Print(L["You have disabled GetAll scans via AuctionDB's options."])
return
end
-- Begin the "GetAll" scan.
TSM.db.profile.lastGetAll = time()
Scan.isScanning = "GetAll"
Scan.isBuggedGetAll = nil
Scan.groupItems = nil
TSMAPI.AuctionScan:StopScan()
Scan:GetAllScanQuery()
end
function Scan:DoneScanning(seconds_elapsed)
if seconds_elapsed then
-- If given the "time elapsed", display it as "Done Scanning (1:35:27)".
@ -513,12 +266,40 @@ function Scan:DoneScanning(seconds_elapsed)
TSM.GUI:UpdateStatus(L["Done Scanning"], 100)
end
Scan.isScanning = nil
Scan.getAllLoaded = nil
TSM.GUI:SetPauseEnabled(false)
TSM.GUI:SetPaused(false)
end
function Scan:PauseScan()
if not Scan.isScanning then return end
if TSMAPI.AuctionScan:PauseScan("AuctionDB") then
TSM.GUI:SetPaused(true)
TSM.GUI:UpdateStatus(L["Scan Paused"])
end
end
function Scan:ResumeScan()
if not Scan.isScanning then return end
if not TSMAPI.AuctionScan:IsScanning() then
Scan:DoneScanning()
return
end
if TSMAPI.AuctionScan:ResumeScan() then
TSM.GUI:SetPaused(false)
TSM.GUI:UpdateStatus(L["Resuming Scan..."])
end
end
function Scan:TogglePause()
if TSMAPI.AuctionScan:IsPaused() then
Scan:ResumeScan()
else
Scan:PauseScan()
end
end
function Scan:ProcessScanData(scanData)
-- Handle scans performed via "Full Scan" and "Group Scan", but not "GetAll".
-- NOTE: See "Scan.ProcessGetAllScan()" for full explanation of this algorithm.
-- Handle scans performed via "Full Scan" and "Group Scan".
local data = {}
for itemString, obj in pairs(scanData) do
@ -556,8 +337,7 @@ function Scan:ProcessScanData(scanData)
-- Add this item to "data to process" even if there's zero records,
-- which can happen if they're all bid-only auctions.
-- NOTE: This differs from the behavior of "ProcessGetAllScan", which
-- only adds items that have at least 1 record with a buyout value.
-- NOTE: Empty records are fine; ProcessData ignores items without buyout prices.
-- NOTE: Empty records are totally fine either way, since "ProcessData"
-- simply ignores items that don't contain any buyout prices.
-- NOTE: If no buyout records were found, the "minBuyout" and "quantity"
@ -574,6 +354,9 @@ function Scan:ProcessScanData(scanData)
-- Process the collected auction data.
TSM.Data:ProcessData(data, Scan.groupItems, verifyNewAlgorithm)
if TSM.ChannelSync then
TSM.ChannelSync:BroadcastScanData(Scan.isScanning, Scan.groupItems)
end
end
function Scan:ProcessImportedData(auctionData)

7
TradeSkillMaster_AuctionDB/Modules/config.lua

@ -390,13 +390,6 @@ function Config:LoadOptions(container)
relativeWidth = 0.5,
tooltip = L["If checked, AuctionDB will add a tab to the AH to allow for in-game scans. If you are using the TSM app exclusively for your scans, you may want to hide it by unchecking this option. This option requires a reload to take effect."],
},
{
type = "CheckBox",
label = L["Disable \"GetAll\" Auction Scans"],
settingInfo = { TSM.db.profile, "disableGetAll" },
relativeWidth = 0.5,
tooltip = L["If checked, AuctionDB will not perform \"GetAll\" scans. This is useful if your server doesn't return all auctions in its \"GetAll\" results, which means that you'll get incorrect market value calculations for all items. If you're playing on such servers, it's best to disable the \"GetAll\" feature to avoid accidentally polluting your price database with incorrect data. This option takes effect immediately, but requires a reload to completely hide the \"Run GetAll Scan\" button."],
},
},
},
{

63
TradeSkillMaster_AuctionDB/Modules/data.lua

@ -130,24 +130,72 @@ function Data:GetMarketValue(scans)
return totalWeight > 0 and floor(totalAmount / totalWeight + 0.5) or 0
end
function Data:ProcessExternalScanData(scanData, groupItems, scanTime)
if type(scanData) ~= "table" then return end
local data = {}
local groupSet = {}
if type(groupItems) == "table" then
if #groupItems > 0 then
for _, itemString in ipairs(groupItems) do
groupSet[itemString] = true
end
else
for itemString in pairs(groupItems) do
groupSet[itemString] = true
end
end
end
for itemString, obj in pairs(scanData) do
if TSMAPI:GetBaseItemString(itemString) == itemString then
local itemID = obj:GetItemID()
local quantity, minBuyout = 0, 0
local records = {}
for _, record in ipairs(obj.records) do
if record.buyout and record.buyout > 0 then
local itemBuyout = record:GetItemBuyout()
if itemBuyout then
if (itemBuyout < minBuyout or minBuyout == 0) then
minBuyout = itemBuyout
end
quantity = quantity + record.count
tinsert(records, {record.count, itemBuyout})
end
end
end
data[itemID] = {records=records, minBuyout=minBuyout, quantity=quantity}
groupSet[itemString] = true
end
end
if not next(data) then return end
Data:ProcessData(data, groupSet, nil, scanTime or time(), true)
if TSM.ChannelSync then
TSM.ChannelSync:BroadcastScanData("Search", groupSet)
end
end
--- Process a table of new market scan data.
-- @param scanData The market scan data.
-- @param[opt] groupItems Affects how the minBuyout data is wiped. Use nil for regular behavior.
-- @param[opt] verifyNewAlgorithm Boolean 'true' if you want to benchmark and verify the new market value algorithm.
function Data:ProcessData(scanData, groupItems, verifyNewAlgorithm)
-- @param[opt] scanTime Unix time to use for lastScan values.
-- @param[opt] skipMinBuyoutWipe If true, do not wipe minBuyout data before processing.
function Data:ProcessData(scanData, groupItems, verifyNewAlgorithm, scanTime, skipMinBuyoutWipe)
-- If we're currently processing data, retry in 0.2 seconds.
-- NOTE: This will retry itself over and over until it's able to process.
if TSM.processingData then
return TSMAPI:CreateTimeDelay(0.2, function() Data:ProcessData(scanData, groupItems, verifyNewAlgorithm) end)
return TSMAPI:CreateTimeDelay(0.2, function() Data:ProcessData(scanData, groupItems, verifyNewAlgorithm, scanTime, skipMinBuyoutWipe) end)
end
-- Wipe all of our existing "minBuyout" data for the items included in the
-- new, incoming scan data in case of "Item Group scan", or for ALL currently
-- cached items in memory in other cases (such as "Full" and "GetAll" scans).
-- new, incoming scan data in case of "Item Group scan", or for all items
-- in memory for full scans.
-- NOTE: It's no problem if we leave some items empty with "nil" minBuyout
-- values. That's how TSM is supposed to work, with items having an empty "minBuyout"
-- if there wasn't any "minBuyout" data for that item in the newest data batch.
if not skipMinBuyoutWipe then
if groupItems then
-- A list of items ("group scan") was provided. Wipe data for those items.
for itemString in pairs(groupItems) do
@ -167,6 +215,7 @@ function Data:ProcessData(scanData, groupItems, verifyNewAlgorithm)
TSM:EncodeItemData(itemID)
end
end
end
-- Convert the incoming "scanData" hashmap to a numerically indexed table,
@ -184,6 +233,7 @@ function Data:ProcessData(scanData, groupItems, verifyNewAlgorithm)
-- pausing between each chunk to allow the game client to avoid freezing.
local index = 1
local day = Data:GetDay()
local scanTimestamp = scanTime or TSM.db.realm.lastCompleteScan
local function DoDataProcessing()
for i = 1, 500 do
-- Abort if we've reached the end of the processing queue.
@ -273,12 +323,9 @@ function Data:ProcessData(scanData, groupItems, verifyNewAlgorithm)
-- NOTE: This can happen if the scan data only contained "bid without
-- buyout" items, meaning they didn't have any per-item buyout data,
-- which can ONLY happen via "Scanning.lua:ProcessScanData()" when
-- doing a normal "Full Scan" or "Group Scan" (not "GetAll"). If
-- there aren't any buyout prices for the item, it still gets added
-- without any "records". This differs from "GetAll" which only adds
-- items to the queue if they had at least one "buyout price" auction.
-- NOTE: We're skipping the empty/indeterminable items to ensure that
-- we have identical behavior for both "GetAll" and all other scan
-- types, so that we NEVER add "empty/missing" market values for items!
-- NOTE: We allow a market value of "0", since it means there was
-- valid data in the calculations. However, "0" is extremely unlikely
@ -316,7 +363,7 @@ function Data:ProcessData(scanData, groupItems, verifyNewAlgorithm)
-- item contains a "greater than 0" buyout value. That was mostly
-- necessary in the past, when TSM sloppily included bid-only items
-- in the data, but should no longer be able to happen with our new code!
TSM.data[itemID].lastScan = TSM.db.realm.lastCompleteScan
TSM.data[itemID].lastScan = scanTimestamp
TSM.data[itemID].minBuyout = data.minBuyout > 0 and data.minBuyout or nil
TSM.data[itemID].quantity = data.quantity -- Counts all items of all stacks.
Data:UpdateMarketValue(TSM.data[itemID])

30
TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.lua

@ -15,6 +15,8 @@ local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_AuctionDB") -- lo
TSM.MAX_AVG_DAY = 1
local SECONDS_PER_DAY = 60 * 60 * 24
local eventObj
local private = {}
local savedDBDefaults = {
realm = {
@ -34,7 +36,6 @@ local savedDBDefaults = {
marketValueTooltip = true,
minBuyoutTooltip = true,
showAHTab = true,
disableGetAll = false,
},
}
@ -75,6 +76,7 @@ function TSM:RegisterModule()
{ key = "lastCompleteScan", callback = TSM.GetLastCompleteScan },
{ key = "lastCompleteScanTime", callback = TSM.GetLastCompleteScanTime },
{ key = "adbScans", callback = TSM.GetScans },
{ key = "processScanData", callback = "Data:ProcessExternalScanData" },
--{ key = "adbOppositeFaction", callback = TSM.GetOppositeFactionData },
}
TSM.tooltipOptions = {callback = "Config:LoadTooltipOptions"}
@ -223,6 +225,32 @@ function TSM:OnEnable()
end
TSM:LoadAuctionData()
eventObj = eventObj or TSMAPI:GetEventObject()
eventObj:SetCallback("TSM:AUCTIONCONTROL:ITEMBOUGHT", private.OnItemBought)
end
function private.OnItemBought(_, data)
if type(data) ~= "table" or not data.itemString then return end
local itemID = TSMAPI:GetItemID(data.itemString)
if not itemID or not TSM.data[itemID] then return end
TSM:DecodeItemData(itemID)
local link = data.link or select(2, TSMAPI:GetSafeItemInfo(data.itemString)) or data.itemString
local buyoutText = data.buyout and (TSMAPI:FormatTextMoney(data.buyout, "|cffffffff", true) or "---") or "---"
local total = 0
local baseItemString = TSMAPI:GetBaseItemString(data.itemString)
for _, _, itemString, quantity in TSMAPI:GetBagIterator() do
if TSMAPI:GetBaseItemString(itemString) == baseItemString then
total = total + quantity
end
end
local mailData = TSMAPI:ModuleAPI("ItemTracker", "playermail")
if type(mailData) == "table" then
total = total + (mailData[baseItemString] or 0)
end
local marketValue = TSM:GetMarketValue(itemID)
local count = data.count or 1
local marketText = marketValue and (TSMAPI:FormatTextMoney(marketValue * count, "|cffffffff", true) or "---") or "---"
TSM:Printf("Bought %s for %s (x%d). bags=%s dbmarket=%s (x%d)", link, buyoutText, count, tostring(total), marketText, count)
end
function TSM:OnTSMDBShutdown()

5
TradeSkillMaster_AuctionDB/TradeSkillMaster_AuctionDB.toc

@ -2,10 +2,10 @@
## Title: |cff00ff00TradeSkillMaster_AuctionDB|r
## Notes: Stores auction house data and calculates market prices.
## Author: Sapu94, Bart39
## Version: v2.3.10
## Version: 2.3.24
## SavedVariables: AscensionTSM_AuctionDB
## Dependency: TradeSkillMaster
## X-Curse-Packaged-Version: v2.3.10
## X-Curse-Packaged-Version: 2.3.24
## X-Curse-Project-Name: TradeSkillMaster_AuctionDB
## X-Curse-Project-ID: tradeskillmaster_auctiondb
## X-Curse-Repository-ID: wow/tradeskillmaster_auctiondb/mainline
@ -25,6 +25,7 @@ Locale\ptBR.lua
TradeSkillMaster_AuctionDB.lua
AppData.lua
Modules\data.lua
Modules\ChannelSync.lua
Modules\Scanning.lua
Modules\GUI.lua
Modules\config.lua

5
TradeSkillMaster_Auctioning/locale/enUS.lua

@ -121,6 +121,7 @@ L["If checked, TSM will not print out a chat message when you have an invalid pr
L["If checked, a cancel scan will cancel any auctions which can be reposted for a higher price."] = true
L["If checked, a cancel scan will cancel any auctions which have been undercut and are still above your minimum price."] = true
L["If checked, groups which the opperation applies to will be included in a reset scan."] = true
L["If checked, the game client icon will flash when a scan is complete."] = true
L["If checked, the minimum, normal and maximum prices of the first operation for the item will be shown in tooltips."] = true
L["If checked, whenever you post an item at its normal price, the buyout will be rounded up to the nearest gold."] = true
L["If enabled, instead of not posting when a whitelisted player has an auction posted, Auctioning will match their price."] = true
@ -184,6 +185,7 @@ L["Operation"] = true
L["Operations"] = true
L["Options"] = true
L["Other Auctioning Searches"] = true
L["Pause"] = true
L["Percentage of the buyout as bid, if you set this to 90% then a 100g buyout will have a 90g bid."] = true
L["Player name"] = true
L["Play the selected sound when a post / cancel scan is complete and items are ready to be posted / canceled (the gray bar is all the way across).Select None to disable sounds"] = true
@ -217,13 +219,16 @@ L["Reset Scan Finished"] = true
L["Reset Settings"] = true
L["Reset"] = true
L["Resetting enabled."] = true
L["Resume"] = true
L["Restart"] = true
L["Return to Summary"] = true
L["Right-Click to add %s to your friends list."] = true
L["Round Normal Price"] = true
L["Resuming Scan..."] = true
L["Running Scan..."] = true
L["Save New Price"] = true
L["Scan Complete!"] = true
L["Scan Paused"] = true
L["Scanning %d / %d (Page 1 / ?)"] = true
L["Scanning %d / %d (Page %d / %d)"] = true
L["Scanning %d / %d"] = true

30
TradeSkillMaster_Auctioning/modules/GUI.lua

@ -16,7 +16,7 @@ function private:CreateButtons(parent)
local height = 24
local frame = CreateFrame("Frame", nil, parent)
frame:SetHeight(height)
frame:SetWidth(210)
frame:SetWidth(265)
frame:SetPoint("BOTTOMRIGHT", -92, 5)
frame.Enable = function(self)
@ -26,6 +26,7 @@ function private:CreateButtons(parent)
self.cancel:Enable()
end
self.skip:Enable()
self.pause:Disable()
self.stop:Enable()
end
@ -36,6 +37,7 @@ function private:CreateButtons(parent)
self.cancel:Disable()
end
self.skip:Disable()
self.pause:Enable()
end
frame.UpdateMode = function(self)
@ -51,6 +53,11 @@ function private:CreateButtons(parent)
self.stop:Enable()
end
frame.SetPaused = function(self, isPaused)
self.pause:SetText(isPaused and L["Resume"] or L["Pause"])
self.pause.isPaused = isPaused
end
local function OnClick(self)
if self.which == "stop" and self.isDone then
GUI:HideSelectionFrame()
@ -90,7 +97,16 @@ function private:CreateButtons(parent)
local button = TSMAPI.GUI:CreateButton(frame, 18)
button:SetPoint("TOPLEFT", frame.skip, "TOPRIGHT", 5, 0)
button:SetWidth(70)
button:SetWidth(60)
button:SetHeight(height)
button:SetText(L["Pause"])
button.which = "pause"
button:SetScript("OnClick", OnClick)
frame.pause = button
local button = TSMAPI.GUI:CreateButton(frame, 18)
button:SetPoint("TOPLEFT", frame.pause, "TOPRIGHT", 5, 0)
button:SetWidth(60)
button:SetHeight(height)
button:SetText(L["Stop"])
button.which = "stop"
@ -681,6 +697,7 @@ function private:Stopped(notDone)
private.buttons:Disable(true)
private.statusBar:UpdateStatus(100, 100)
private.contentButtons.currAuctionsButton:Hide()
private.buttons.pause:Disable()
if private.mode == "Post" then
TSMAPI:CreateTimeDelay(0.5, SetGoldText)
@ -697,6 +714,7 @@ function private:Stopped(notDone)
end
private.buttons.stop:SetText(L["Restart"])
private.buttons.stop.isDone = true
private.buttons:SetPaused(false)
end
@ -871,7 +889,7 @@ function GUI:CreateScanFrame(parent)
local statusBarFrame = CreateFrame("Frame", nil, frame.content)
statusBarFrame:SetPoint("TOPLEFT", frame.content, "BOTTOMLEFT", 165, -2)
statusBarFrame:SetWidth(355)
statusBarFrame:SetWidth(300)
statusBarFrame:SetHeight(30)
private.statusBar = TSMAPI.GUI:CreateStatusBar(statusBarFrame, "TSMAuctioningStatusBar")
@ -897,6 +915,8 @@ function GUI:StartScan(frame)
private.buttons:Show()
private.buttons:UpdateMode()
private.buttons:Disable()
private.buttons:SetPaused(false)
private.buttons.pause:Enable()
private.buttons.stop.isDone = nil
private.buttons.stop:SetText(L["Stop"])
private.contentButtons:Show()
@ -947,12 +967,12 @@ function GUI:ShowSelectionFrame(frame)
if private.scanFrame then private.scanFrame:Hide() end
private.selectionFrame = private.selectionFrame or GUI:CreateSelectionFrame(frame)
private.selectionFrame:Show()
TSMAPI.AuctionScan:StopScan()
TSMAPI.AuctionScan:StopScan(false)
end
function GUI:HideSelectionFrame()
private.selectionFrame:Hide()
if private.scanFrame then private.scanFrame:Hide() end
TSMAPI.AuctionScan:StopScan()
TSMAPI.AuctionScan:StopScan(false)
TSM.Reset:Hide()
end

8
TradeSkillMaster_Auctioning/modules/PostScan.lua

@ -13,6 +13,7 @@ local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Auctioning") -- l
local bagInfo, bagState = {}, {}
local bagInfoUpdate = 0
local postQueue, currentItem, itemLocations = {}, {}, {}
local scanItems
local totalToPost, totalPosted, count = 0, 0, 0
local isScanning, GUI
@ -129,6 +130,10 @@ function Post:GetScanListAndSetup(GUIRef, options)
tinsert(scanList, itemString)
end
end
scanItems = {}
for _, itemString in ipairs(scanList) do
tinsert(scanItems, itemString)
end
TSMAPI:FireEvent("AUCTIONING:POST:START", {numItems=#scanList, isGroup=true})
return scanList
@ -523,5 +528,8 @@ end
function Post:DoneScanning()
isScanning = false
if scanItems and next(TSM.Scan.auctionData) then
TSMAPI:ModuleAPI("AuctionDB", "processScanData", TSM.Scan.auctionData, scanItems, time())
end
return totalToPost
end

10
TradeSkillMaster_Auctioning/modules/ResetScan.lua

@ -14,6 +14,7 @@ local resetData, summarySTCache, showCache, itemsReset, justBought = {}, {}, {},
local isScanning, doneScanningText, currentItem, GUI
local summaryST, auctionST, resetButtons
local currentAuction
local pendingBuyout
function Reset:Show(frame)
summaryST = summaryST or Reset:CreateSummaryST(frame.content)
@ -604,6 +605,10 @@ function Reset:RemoveCurrentAuction()
scanData:RemoveRecord(row.index)
itemsReset[row.itemString] = true
if pendingBuyout then
TSMAPI:FireEvent("TSM:AUCTIONCONTROL:ITEMBOUGHT", pendingBuyout)
pendingBuyout = nil
end
Reset:UpdateAuctionST()
if #auctionST.rowData == 0 then
@ -697,6 +702,11 @@ function Reset:BuyAuction()
PlaceAuctionBid("list", mainIndex or altIndex, currentAuction.buyout)
foundAuction = true
justBought[mainIndex or altIndex] = true
pendingBuyout = {
itemString = currentAuction.itemString,
count = currentAuction.count,
buyout = currentAuction.buyout,
}
end
resetButtons.buyout:Disable()

20
TradeSkillMaster_Auctioning/modules/manage.lua

@ -72,9 +72,25 @@ function Manage:OnGUIEvent(event)
end
elseif event == "skip" then
Util:SkipItem()
elseif event == "pause" then
if TSMAPI.AuctionScan:IsPaused() then
if TSMAPI.AuctionScan:ResumeScan() then
GUI.buttons:SetPaused(false)
GUI.statusBar:SetStatusText(L["Resuming Scan..."])
GUI.infoText:SetInfo(L["Running Scan..."])
end
else
if TSMAPI.AuctionScan:PauseScan() then
GUI.buttons:SetPaused(true)
GUI.statusBar:SetStatusText(L["Scan Paused"])
GUI.infoText:SetInfo(L["Scan Paused"])
end
end
elseif event == "stop" then
TSMAPI:CancelFrame("auctioningNoScanProcessing")
TSMAPI.AuctionScan:StopScan()
GUI.buttons:SetPaused(false)
GUI.buttons.pause:Disable()
Util:Stop()
end
TSMAPI:CreateTimeDelay("aucManageSTUpdate", 0.01, GUI.UpdateAuctionsSTData)
@ -88,6 +104,8 @@ function Manage:ProcessScannedItem(itemString, noUpdate)
end
function Manage:ScanComplete(interrupted)
GUI.buttons:SetPaused(false)
GUI.buttons.pause:Disable()
if interrupted then
-- If our scan has been interrupted by the Auction House closing,
-- simply act as if the user clicked "Stop", but with an extra flag
@ -101,7 +119,7 @@ function Manage:ScanComplete(interrupted)
elseif TSM.db.global.scanCompleteSound ~= 1 then
PlaySound(TSM.Options:GetScanCompleteSound(TSM.db.global.scanCompleteSound), "Master")
end
end
end
end
-- these functions help display the status text which goes inside the statusbar

3
TradeSkillMaster_Crafting/Locale/enUS.lua

@ -40,6 +40,9 @@ L["Craft Value Method"] = true
L["Craft Queue"] = true
L["Crafter"] = true
L["Crafts"] = true
L["All Professions"] = true
L["Available"] = true
L["Cost"] = true
L["Crafting Cost"] = true
L["Crafting Material Cost"] = true
L["Crafting operations contain settings for restocking the items in a group. Type the name of the new operation into the box below and hit 'enter' to create a new Crafting operation."] = true

427
TradeSkillMaster_Crafting/Modules/CraftingGUI.lua

@ -15,6 +15,7 @@ local priceTextCache = { lastClear = 0 }
local private = {}
private.gather = {}
private.shown = {}
private.eventObj = nil
-- list of profession skills that do not have crafting. used by UpdateTradeSkills
local invalidTrade = {
@ -63,6 +64,7 @@ function GUI:OnEnable()
GUI:RegisterEvent("UPDATE_PENDING_MAIL", "GatheringEventHandler")
GUI:RegisterEvent("AUCTION_HOUSE_SHOW", "GatheringEventHandler")
GUI:RegisterEvent("AUCTION_HOUSE_CLOSED", "GatheringEventHandler")
GUI:RegisterEvent("MAIL_INBOX_UPDATE", "QueueUpdateHandler")
TSMAPI:CreateTimeDelay("craftingUpdateTradeSkill", 1, function() GUI:UpdateSelectedTradeSkill() end, 0.1)
TSMAPI:RegisterForBagChange(function()
@ -75,6 +77,9 @@ function GUI:OnEnable()
TSMAPI:CreateTimeDelay("gatheringUpdateThrottle", 0.3, GUI.UpdateGathering)
end)
private.eventObj = private.eventObj or TSMAPI:GetEventObject()
private.eventObj:SetCallback("TSM:AUCTIONCONTROL:ITEMBOUGHT", private.OnAuctionBought)
GUI:UpdateTradeSkills()
GUI.gatheringFrame = GUI:CreateGatheringFrame()
if next(TSM.db.realm.gathering.neededMats) then
@ -82,6 +87,18 @@ function GUI:OnEnable()
end
end
function private.OnAuctionBought()
if not GUI.frame or not GUI.frame:IsVisible() then return end
TSMAPI:CreateTimeDelay("craftingQueueUpdateThrottle", 0.1, GUI.UpdateQueue)
TSMAPI:CreateTimeDelay("craftingProfessionUpdateThrottle", 0.2, GUI.UpdateProfessionsTabST)
end
function GUI:QueueUpdateHandler()
if not GUI.frame or not GUI.frame:IsVisible() then return end
TSMAPI:CreateTimeDelay("craftingQueueUpdateThrottle", 0.1, GUI.UpdateQueue)
TSMAPI:CreateTimeDelay("craftingProfessionUpdateThrottle", 0.2, GUI.UpdateProfessionsTabST)
end
function GUI:ShowGatheringFrame()
if GUI.gatheringFrame then
GUI.gatheringFrame:Show()
@ -123,7 +140,12 @@ function GUI:ShowProfessionWindow()
GUI:UpdateTradeSkills(GetTradeSkillLine())
TradeSkillFrame:SetScript("OnHide", nil)
HideUIPanel(TradeSkillFrame)
TradeSkillFrame:SetScript("OnHide", function() if not GUI.noClose then GUI.switchBtn:Hide() CloseTradeSkill() end end)
TradeSkillFrame:SetScript("OnHide", function()
if not GUI.noClose and not GUI.switchingProfession then
GUI.switchBtn:Hide()
CloseTradeSkill()
end
end)
priceTextCache.lastClear = 0
GUI.switchBtn:Update()
@ -185,6 +207,7 @@ function GUI:EventHandler(event, ...)
local unittest = ...
if unittest == "player" or unittest==nil then --Changing tradeskill frames and stuff has "nil" unit, when other players cast this also triggers with nil
if event == "TRADE_SKILL_CLOSE" then
if GUI.switchingProfession then return end
GUI.frame:Hide()
elseif event == "TRADE_SKILL_UPDATE" or event == "TRADE_SKILL_FILTER_UPDATE" then
if GetTradeSkillLine() ~= GUI.currentTradeSkill or select(2, IsTradeSkillLinked()) ~= GUI.currentLinkedPlayer then
@ -458,13 +481,13 @@ function GUI:CreateGUI()
local frameDefaults = {
x = 100,
y = 300,
width = 450,
width = 520,
height = 500,
scale = 1,
}
local frame = TSMAPI:CreateMovableFrame("TSMCraftingTradeSkillFrame", frameDefaults)
frame:SetResizable(true)
frame:SetMinResize(450, 400)
frame:SetMinResize(520, 400)
TSMAPI.Design:SetFrameBackdropColor(frame)
frame:Show()
frame:SetScript("OnHide", function() if not GUI.noClose then GUI.switchBtn:Hide() TradeSkillFrame:Show() CloseTradeSkill() end end)
@ -883,17 +906,52 @@ function GUI:CreateProfessionsTab(parent)
local currentSelection
local player = UnitName("player")
local function SetNoClose()
GUI.noClose = true
GUI.switchingProfession = true
TSMAPI:CreateTimeDelay("craftingNoCloseClear", 0.6, function() GUI.noClose = nil end)
TSMAPI:CreateTimeDelay("craftingSwitchingClear", 0.8, function() GUI.switchingProfession = nil end)
end
local function SelectSpellInProfession(profession, spellID)
if GetTradeSkillLine() ~= profession then
local link = TSM.db.realm.tradeSkills[player] and TSM.db.realm.tradeSkills[player][profession] and TSM.db.realm.tradeSkills[player][profession].link
if not link then return end
local tradeString = strsub(select(3, ("|"):split(link)), 2)
SetNoClose()
SetItemRef(tradeString, link, "LeftButton", ChatFrame1)
return
end
for i = 1, GetNumTradeSkills() do
if TSM.Util:GetSpellID(i) == spellID then
TradeSkillFrame_SetSelection(i)
TradeSkillFrame_Update()
GUI:UpdateSelectedTradeSkill(true)
return
end
end
end
local HaveMatsCheckBox
local function UpdateProfession(self)
local list = {}
list["ALL"] = L["All Professions"]
for playerName, professionData in pairs(TSM.db.realm.tradeSkills) do
for name, data in pairs(professionData) do
if not data.isSecondary and playerName == player then -- only display current player profs until blizz fix it
if playerName == player then -- only display current player profs until blizz fix it
list[playerName .. "~" .. name] = format("%s %d/%d - %s", name, data.level or "?", data.maxLevel or "?", playerName)
end
end
end
self.dropdown:SetList(list)
if TSM.db.global.showAllProfessions then
self.dropdown:SetValue("ALL")
self.dropdown:SetText(L["All Professions"])
return
end
local playerName = select(2, IsTradeSkillLinked()) or player
local professionName, level, maxLevel = GetTradeSkillLine()
local professionString = format("%s %d/%d - %s", professionName, level, maxLevel, playerName)
@ -908,13 +966,36 @@ function GUI:CreateProfessionsTab(parent)
frame:SetScript("OnShow", UpdateProfession)
local function OnValueChanged(_, _, index)
if index == "ALL" then
TSM.db.global.showAllProfessions = true
frame:UpdateProfession()
GUI:UpdateProfessionsTabST()
return
end
if TSM.db.global.showAllProfessions then
SetNoClose()
TSM.db.global.showAllProfessions = false
end
local playerName, profession = ("~"):split(index)
local currentPlayer = select(2, IsTradeSkillLinked()) or player
local currentProfession = GetTradeSkillLine()
if playerName == currentPlayer and profession == currentProfession then
frame:UpdateProfession()
GUI:ShowProfessionsTab()
currentSelection = index
return
end
if playerName == player then
if profession == "Mining" then
CastSpellByName("Smelting")
else
CastSpellByName(profession)
local link = TSM.db.realm.tradeSkills[playerName] and TSM.db.realm.tradeSkills[playerName][profession] and TSM.db.realm.tradeSkills[playerName][profession].link
if not link then
TSM:Printf(L["Profession data not found for %s on %s. Logging into this player and opening the profession may solve this issue."], profession, playerName)
return OnValueChanged(_, _, currentSelection)
end
local tradeString = strsub(select(3, ("|"):split(link)), 2)
SetNoClose()
SetItemRef(tradeString, link, "LeftButton", ChatFrame1)
else
local link = TSM.db.realm.tradeSkills[playerName][profession].link
if not link then
@ -922,7 +1003,8 @@ function GUI:CreateProfessionsTab(parent)
return OnValueChanged(_, _, currentSelection)
end
local tradeString = strsub(select(3, ("|"):split(link)), 2)
SetItemRef(tradeString, link)
SetNoClose()
SetItemRef(tradeString, link, "LeftButton", ChatFrame1)
end
currentSelection = index
end
@ -934,15 +1016,19 @@ function GUI:CreateProfessionsTab(parent)
dd:SetCallback("OnValueChanged", OnValueChanged)
frame.dropdown = dd
local HaveMatsCheckBox = TSMAPI.GUI:CreateCheckBox(frame)
HaveMatsCheckBox = TSMAPI.GUI:CreateCheckBox(frame)
HaveMatsCheckBox:SetLabel(" Have Mats")
HaveMatsCheckBox:SetPoint("TOPLEFT", 252, -4)
HaveMatsCheckBox:SetWidth(90)
HaveMatsCheckBox:SetHeight(26)
frame.HaveMatsCheckBox = HaveMatsCheckBox
HaveMatsCheckBox:SetCallback("OnValueChanged", function(_, _, value)
if TSM.db.global.showAllProfessions then
GUI:UpdateProfessionsTabST()
else
TradeSkillFrameAvailableFilterCheckButton:SetChecked(value)
TradeSkillOnlyShowMakeable(value)
end
end)
local RestockBtn = TSMAPI.GUI:CreateButton(frame, 14)
@ -954,6 +1040,7 @@ function GUI:CreateProfessionsTab(parent)
QuickRestock = true
GUI.ShowGroupsTab()
end)
frame.restockBtn = RestockBtn
local linkBtn = TSMAPI.GUI:CreateButton(frame, 14)
linkBtn:SetPoint("TOPRIGHT", -5, -4)
@ -1012,7 +1099,9 @@ function GUI:CreateProfessionsTab(parent)
end)
searchBar:SetScript("OnTextChanged", function(self)
local text = self:GetText()
if not TSM.db.global.showAllProfessions then
SetTradeSkillItemNameFilter(text == SEARCH and "" or text)
end
GUI:UpdateProfessionsTabST()
end)
searchBar:SetScript("OnEnterPressed", searchBar.ClearFocus)
@ -1053,7 +1142,17 @@ function GUI:CreateProfessionsTab(parent)
TSMAPI.GUI:CreateHorizontalLine(frame, -64)
local function OnSTRowClick(_, data, _, button)
if data.isCollapseAll then
if TSM.db.global.showAllProfessions and data.spellID then
if IsModifiedClick() then
local itemID = TSM.db.realm.crafts[data.spellID] and TSM.db.realm.crafts[data.spellID].itemID
if itemID then
HandleModifiedItemClick(itemID)
end
else
SelectSpellInProfession(data.profession, data.spellID)
end
return
elseif data.isCollapseAll then
TradeSkillCollapseAllButton:Click()
GUI:UpdateProfessionsTabST()
elseif button == "LeftButton" then
@ -1067,26 +1166,29 @@ function GUI:CreateProfessionsTab(parent)
end
end
local function GetPriceColumnText()
if TSM.db.global.priceColumn == 1 then
return L["Crafting Cost"]
elseif TSM.db.global.priceColumn == 2 then
return L["Item Value"]
elseif TSM.db.global.priceColumn == 3 then
return L["Profit"]
end
end
local function OnSTColumnClick(self)
if self.colNum == 2 then
TSM.db.global.priceColumn = TSM.db.global.priceColumn + 1
TSM.db.global.priceColumn = TSM.db.global.priceColumn > 3 and 1 or TSM.db.global.priceColumn
self:SetText(GetPriceColumnText())
wipe(priceTextCache)
priceTextCache.lastClear = time()
if TSM.db.global.showAllProfessions then
local sortKey
if self.colNum == 1 then
sortKey = "available"
elseif self.colNum == 3 then
sortKey = "cost"
elseif self.colNum == 4 then
sortKey = "profit"
elseif self.colNum == 5 then
sortKey = "profitPercent"
end
if sortKey then
if TSM.db.global.showAllProfessionsSortKey ~= sortKey then
TSM.db.global.showAllProfessionsSortKey = sortKey
TSM.db.global.showAllProfessionsSortDescending = true
else
TSM.db.global.showAllProfessionsSortDescending = not TSM.db.global.showAllProfessionsSortDescending
end
GUI:UpdateProfessionsTabST()
end
end
end
local stContainer = CreateFrame("Frame", nil, frame)
stContainer:SetPoint("TOPLEFT", 5, -70)
@ -1095,11 +1197,11 @@ function GUI:CreateProfessionsTab(parent)
TSMAPI.Design:SetFrameColor(stContainer)
local stCols = {
-- { name = L["Name"], width = 0.725, align = "LEFT" },
-- { name = GetPriceColumnText(), width = 0.275, align = "LEFT" },
{ name = L["Name"], width = 0.7, align = "LEFT" },
{ name = GetPriceColumnText(), width = 0.2, align = "LEFT" },
{ name = "P. %", width = 0.1, align = "LEFT" }
{ name = "#", width = 0.08, align = "LEFT", headAlign = "LEFT" },
{ name = L["Name"], width = 0.40, align = "LEFT", headAlign = "LEFT" },
{ name = L["Cost"], width = 0.16, align = "LEFT", headAlign = "LEFT" },
{ name = L["Profit"], width = 0.18, align = "LEFT", headAlign = "LEFT" },
{ name = "%", width = 0.08, align = "LEFT", headAlign = "LEFT" }
}
frame.st = TSMAPI:CreateScrollingTable(stContainer, stCols, { OnClick = OnSTRowClick, OnColumnClick = OnSTColumnClick })
@ -1493,6 +1595,8 @@ function GUI:UpdateProfessionsTabST()
local stData = {}
TSM:UpdateCraftReverseLookup()
local showAll = TSM.db.global.showAllProfessions
local player = UnitName("player")
local function RGBPercToHex(tbl)
local r = tbl.r
@ -1509,25 +1613,206 @@ function GUI:UpdateProfessionsTabST()
priceTextCache.lastClear = time()
end
local function GetInventoryTotals()
local totals = {}
local bags = TSMAPI:ModuleAPI("ItemTracker", "playerbags", player)
local bank = TSMAPI:ModuleAPI("ItemTracker", "playerbank", player)
if bags or bank then
for itemString, quantity in pairs(bags or {}) do
totals[itemString] = (totals[itemString] or 0) + quantity
end
for itemString, quantity in pairs(bank or {}) do
totals[itemString] = (totals[itemString] or 0) + quantity
end
return totals
end
return select(4, TSM.Inventory:GetTotals())
end
local ts = ""
local numAvailableAllCache = {}
local inventoryTotals = GetInventoryTotals()
if showAll then
local searchText = GUI.frame and GUI.frame.content and GUI.frame.content.professionsTab and GUI.frame.content.professionsTab.searchBar and GUI.frame.content.professionsTab.searchBar:GetText() or ""
if searchText == SEARCH then
searchText = ""
end
searchText = strlower(searchText or "")
local filterHaveMats = GUI.frame and GUI.frame.content and GUI.frame.content.professionsTab and GUI.frame.content.professionsTab.HaveMatsCheckBox and GUI.frame.content.professionsTab.HaveMatsCheckBox:GetValue()
local craftList = {}
for spellID, data in pairs(TSM.db.realm.crafts) do
if data.players and data.players[UnitName("player")] then
tinsert(craftList, {
spellID = spellID,
name = data.name or "",
profession = data.profession or UNKNOWN,
numResult = data.numResult or 1,
})
end
end
for _, craft in ipairs(craftList) do
craft.cost, craft.buyout, craft.profit = TSM.Cost:GetCraftPrices(craft.spellID)
if craft.profit and craft.cost and craft.cost > 0 then
craft.profitPercent = (craft.profit / craft.cost) * 100
else
craft.profitPercent = nil
end
local available = math.huge
if TSM.db.realm.crafts[craft.spellID] then
for itemString, quantity in pairs(TSM.db.realm.crafts[craft.spellID].mats) do
available = min(available, floor((inventoryTotals[itemString] or 0) / quantity))
end
end
craft.available = available ~= math.huge and available or 0
end
if searchText ~= "" or filterHaveMats then
local filtered = {}
for _, craft in ipairs(craftList) do
if (searchText == "" or strfind(strlower(craft.name or ""), searchText, 1, true)) and (not filterHaveMats or craft.available > 0) then
tinsert(filtered, craft)
end
end
craftList = filtered
end
if TSM.db.global.showAllProfessionsSortKey == "cost" then
sort(craftList, function(a, b)
local aValue = tonumber(a.cost) or math.huge
local bValue = tonumber(b.cost) or math.huge
if aValue == bValue then
return a.name < b.name
end
if TSM.db.global.showAllProfessionsSortDescending then
return aValue > bValue
end
return aValue < bValue
end)
elseif TSM.db.global.showAllProfessionsSortKey == "profit" then
sort(craftList, function(a, b)
local aValue = tonumber(a.profit) or -math.huge
local bValue = tonumber(b.profit) or -math.huge
if aValue == bValue then
return a.name < b.name
end
if TSM.db.global.showAllProfessionsSortDescending then
return aValue > bValue
end
return aValue < bValue
end)
elseif TSM.db.global.showAllProfessionsSortKey == "available" then
sort(craftList, function(a, b)
local aValue = tonumber(a.available) or -math.huge
local bValue = tonumber(b.available) or -math.huge
if aValue == bValue then
return a.name < b.name
end
if TSM.db.global.showAllProfessionsSortDescending then
return aValue > bValue
end
return aValue < bValue
end)
elseif TSM.db.global.showAllProfessionsSortKey == "profitPercent" then
sort(craftList, function(a, b)
local aValue = tonumber(a.profitPercent) or -math.huge
local bValue = tonumber(b.profitPercent) or -math.huge
if aValue == bValue then
return a.name < b.name
end
if TSM.db.global.showAllProfessionsSortDescending then
return aValue > bValue
end
return aValue < bValue
end)
else
sort(craftList, function(a, b)
if TSM.db.global.showAllProfessionsSortDescending then
return a.name > b.name
end
return a.name < b.name
end)
end
for _, craft in ipairs(craftList) do
local spellID = craft.spellID
local craftName = craft.name
if not numAvailableAllCache[spellID] then
local numAvailableAll = math.huge
if spellID and TSM.db.realm.crafts[spellID] then
for itemString, quantity in pairs(TSM.db.realm.crafts[spellID].mats) do
numAvailableAll = min(numAvailableAll, floor((inventoryTotals[itemString] or 0) / quantity))
end
end
if numAvailableAll ~= math.huge then
numAvailableAllCache[spellID] = numAvailableAll
end
end
local available = craft.available or 0
local displayName = craftName
local costText
local profitText
local cost, _, profit = craft.cost, craft.buyout, craft.profit
if cost and cost > 0 then
costText = TSMAPI:FormatTextMoney(cost, TSMAPI.Design:GetInlineColor("link"))
else
costText = "---"
end
if profit then
if profit < 0 then
profitText = "|cffff0000-|r" .. TSMAPI:FormatTextMoney(-profit, "|cffff0000")
else
profitText = TSMAPI:FormatTextMoney(profit, "|cff00ff00")
end
else
profitText = "---"
end
local profitPercent = "---"
if profit and cost and cost > 0 then
profitPercent = craft.profitPercent
if profit < 0 then
profitPercent = format("%s%.0f%%|r", "|cffff0000", profitPercent)
else
profitPercent = format("%s%.0f%%|r", "|cff00ff00", profitPercent)
end
end
tinsert(stData, {
cols = {
{ value = available },
{ value = displayName },
{ value = costText or "" },
{ value = profitText or "" },
{ value = profitPercent or "" },
},
spellID = spellID,
profession = craft.profession,
})
end
local frame = GUI.frame.content.professionsTab
frame.st:SetData(stData)
return
end
local collapseAllRow = {
cols = {
{ value = "" },
{
value = "|cff" .. RGBPercToHex(TradeSkillTypeColor.header) .. ALL .. " [" .. (TradeSkillCollapseAllButton.collapsed and "+" or "-") .. "]|r",
},
{
value = "",
},
{
value = "",
},
{ value = "" },
{ value = "" },
{ value = "" },
},
isCollapseAll = true,
}
tinsert(stData, collapseAllRow)
local ts = ""
local numAvailableAllCache = {}
local inventoryTotals = select(4, TSM.Inventory:GetTotals())
for i = 1, GetNumTradeSkills() do
-- local skillName, skillType, numAvailable, isExpanded, _, numSkillUps, _, showProgressBar, currentRank, maxRank = GetTradeSkillInfo(i)
local skillName, skillType, numAvailable, isExpanded, _ = GetTradeSkillInfo(i)
@ -1563,50 +1848,29 @@ function GUI:UpdateProfessionsTabST()
end
end
if numAvailable > 0 or (numAvailableAllCache[spellID] and numAvailableAllCache[spellID] > 0) then
local availableText = numAvailable .. " (" .. (numAvailableAllCache[spellID] or 0) .. ")"
skillName = ts .. "|cff" .. RGBPercToHex(TradeSkillTypeColor[skillType]) .. skillName .. " [" .. availableText .. "]|r"
else
local available = numAvailableAllCache[spellID] or 0
skillName = ts .. "|cff" .. RGBPercToHex(TradeSkillTypeColor[skillType]) .. skillName .. "|r"
end
local priceText = priceTextCache[spellID]
local cost, buyout, profit = TSM.Cost:GetCraftPrices(spellID)
if spellID and not priceText then
--local cost, buyout, profit = TSM.Cost:GetCraftPrices(spellID)
if TSM.db.global.priceColumn == 1 then -- Crafting Cost
local costText
local profitText
local cost, _, profit = TSM.Cost:GetCraftPrices(spellID)
if cost and cost > 0 then
priceText = TSMAPI:FormatTextMoney(cost, TSMAPI.Design:GetInlineColor("link"))
end
elseif TSM.db.global.priceColumn == 2 then -- Item Value
if buyout and buyout > 0 then
priceText = TSMAPI:FormatTextMoney(buyout, TSMAPI.Design:GetInlineColor("link"))
costText = TSMAPI:FormatTextMoney(cost, TSMAPI.Design:GetInlineColor("link"))
else
costText = "---"
end
elseif TSM.db.global.priceColumn == 3 then -- Profit
if profit then
-- if profit < 0 then
-- priceText = "|cffff0000-|r" .. TSMAPI:FormatTextMoney(-profit, "|cffff0000") .. format(" (%s%.0f%%|r)", "|cffff0000", profitPercent)
-- else
-- priceText = TSMAPI:FormatTextMoney(profit, "|cff00ff00") .. format(" (%s%.0f%%|r)", "|cff00ff00", profitPercent)
-- end
if profit < 0 then
priceText = "|cffff0000-|r" .. TSMAPI:FormatTextMoney(-profit, "|cffff0000")
profitText = "|cffff0000-|r" .. TSMAPI:FormatTextMoney(-profit, "|cffff0000")
else
priceText = TSMAPI:FormatTextMoney(profit, "|cff00ff00")
end
profitText = TSMAPI:FormatTextMoney(profit, "|cff00ff00")
end
end
if priceText then
priceTextCache[spellID] = priceText
else
priceText = "---"
end
profitText = "---"
end
local profitPercent = "---"
if profit then
if profit and cost and cost > 0 then
profitPercent = profit / cost * 100
if profit < 0 then
profitPercent = format("%s%.0f%%|r", "|cffff0000", profitPercent)
@ -1617,11 +1881,17 @@ function GUI:UpdateProfessionsTabST()
local row = {
cols = {
{
value = spellID and available or "",
},
{
value = skillName,
},
{
value = spellID and priceText or "",
value = spellID and costText or "",
},
{
value = spellID and profitText or "",
},
{
value = spellID and profitPercent or "",
@ -1648,7 +1918,12 @@ function GUI:UpdateSelectedTradeSkill(forceUpdate)
local frame = GUI.frame.content.professionsTab
TradeSkillFrame.selectedSkill = TradeSkillFrame.selectedSkill or 1
if forceUpdate or frame.st:GetSelection() - 1 ~= TradeSkillFrame.selectedSkill then
if TSM.db.global.showAllProfessions then
frame.craftInfoFrame:SetTradeSkillIndex(TradeSkillFrame.selectedSkill)
return
end
local selection = frame.st:GetSelection() or 0
if forceUpdate or selection - 1 ~= TradeSkillFrame.selectedSkill then
frame.st:SetSelection(TradeSkillFrame.selectedSkill + 1)
frame.craftInfoFrame:SetTradeSkillIndex(TradeSkillFrame.selectedSkill)
end

4
TradeSkillMaster_Crafting/TradeSkillMaster_Crafting.lua

@ -29,12 +29,16 @@ local savedDBDefaults = {
frameQueueOpen = nil,
showingDefaultFrame = nil,
matsInTooltip = true,
showAllProfessions = false,
showAllProfessionsSortKey = "name",
showAllProfessionsSortDescending = false,
},
realm = {
tradeSkills = {},
crafts = {},
mats = {},
queueStatus = { collapsed = {} },
professionsTabCollapsed = {},
sourceStatus = { collapsed = {} },
gathering = { crafter = nil, professions = {}, neededMats = {}, availableMats = {}, gatheredMats = false, gatherAll = false, destroyingMats = {}, destroyDisable = false, evenStacks = true },
craftingCostCache = {}

4
TradeSkillMaster_Shopping/modules/Search.lua

@ -39,9 +39,9 @@ end
function Search:Hide()
if not private.searchBar then return end
private.searchBar:Hide()
TSM.Util:HideSearchFrame()
TSM.Util:HideSearchFrame(false)
TSMAPI.AuctionControl:HideControlButtons()
TSMAPI.AuctionScan:StopScan()
TSMAPI.AuctionScan:StopScan(false)
TSM.Sidebar:Hide()
end

12
TradeSkillMaster_Shopping/modules/Util.lua

@ -115,7 +115,7 @@ function Util:SetParent(parent)
private.parent = parent
end
function Util:ShowSearchFrame(isDestroying, pctColName, clearRT)
function Util:ShowSearchFrame(isDestroying, pctColName, clearRT, forceStop)
if private.searchFrame and private.searchFrame:IsVisible() then
Util:HideSearchFrame()
end
@ -130,7 +130,7 @@ function Util:ShowSearchFrame(isDestroying, pctColName, clearRT)
private.controlButtons.buyout:Disable()
private.controlButtons.cancel:Disable()
private.controlButtons.post:Disable()
TSMAPI.AuctionScan:StopScan()
TSMAPI.AuctionScan:StopScan(forceStop ~= false)
TSMAPI.AuctionScan:ClearCache()
private.searchFrame.statusBar:SetStatusText("")
private.searchFrame.statusBar:UpdateStatus(0, 0)
@ -138,10 +138,10 @@ function Util:ShowSearchFrame(isDestroying, pctColName, clearRT)
TSM.Search:SetMode(private.mode)
end
function Util:HideSearchFrame()
function Util:HideSearchFrame(forceStop)
private.searchFrame:Hide()
TSMAPI.AuctionControl:HideControlButtons()
TSMAPI.AuctionScan:StopScan()
TSMAPI.AuctionScan:StopScan(forceStop ~= false)
TSMAPI.AuctionScan:ClearCache()
end
@ -231,6 +231,10 @@ function private.ScanCallback(event, ...)
elseif event == "SCAN_COMPLETE" then
if not private.filterList or not private.filterList[1] then return end -- protect against sniper scan starts causing issues
local data = ...
local groupItems = private.filterList[1].items
if not private.isLastPageScan then
TSMAPI:ModuleAPI("AuctionDB", "processScanData", data, groupItems, time())
end
if private.filterList[1].items then
for _, itemString in ipairs(private.filterList[1].items) do
if data[itemString] then

5
TradeSkillMaster_Warehousing/Locale/enUS.lua

@ -55,18 +55,23 @@ L["Nothing to Move"] = true
L["Nothing to Move"] = true
L["Nothing to Move"] = true
L["Nothing to Restock"] = true
L["No Auctions found for %s"] = true
L["Operation Name"] = true
L["Operations"] = true
L["Preparing to Move"] = true
L["Preparing to Move"] = true
L["Preparing to Move"] = true
L["Please switch to the Shopping tab to restock from the auction house."] = true
L["Puts items matching the itemstring, itemID or partial text entered into the bank or guild bank."] = true
L["Relationships"] = true
L["Restock Bags"] = true
L["Restock from AH"] = true
L["Restocks items from the AH based on your Warehousing group selections."] = true
L["Restock Quantity"] = true
L["Restock Settings"] = true
L["Restock Settings"] = true
L["Restocking"] = true
L["No groups selected."] = true
L["Restore Bags"] = true
L["Set Keep in Bags Quantity"] = true
L["Set Keep in Bank Quantity"] = true

13
TradeSkillMaster_Warehousing/Modules/bankui.lua

@ -74,13 +74,16 @@ function bankui:createTab(parent)
buttonFrame:SetPoint("BOTTOMRIGHT")
buttonFrame.btnToBags = createButton(L["Move Group To Bags"], buttonFrame, nil)
buttonFrame.btnToBags:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 75)
buttonFrame.btnToBags:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 85)
buttonFrame.btnToBank = createButton(L["Move Group To Bank"], buttonFrame, nil)
buttonFrame.btnToBank:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 95)
buttonFrame.btnToBank:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 105)
buttonFrame.btnRestock = createButton(L["Restock Bags"], buttonFrame, nil)
buttonFrame.btnRestock:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 45)
buttonFrame.btnRestock:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 65)
buttonFrame.btnRestockAH = createButton(L["Restock from AH"], buttonFrame, nil)
buttonFrame.btnRestockAH:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 45)
buttonFrame.btnEmpty = createButton(L["Empty Bags"], buttonFrame, nil)
buttonFrame.btnEmpty:SetPoint("BOTTOM", buttonFrame, "BOTTOM", 0, 25)
@ -124,4 +127,8 @@ function bankui:updateButtons()
TSM.move:restockGroup(groupTree:GetSelectedGroupInfo())
end)
buttonFrame.btnRestockAH:SetScript("OnClick", function(self)
TSM.move:restockGroupAuction(groupTree:GetSelectedGroupInfo())
end)
end

75
TradeSkillMaster_Warehousing/Modules/move.lua

@ -13,6 +13,50 @@ local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster_Warehousing")
local TSM = select(2, ...)
local move = TSM:NewModule("move", "AceEvent-3.0")
local AceGUI = LibStub("AceGUI-3.0") -- load the AceGUI libraries
local private = { shoppingItems = {} }
local function GetSelectedGroupInfoFallback()
local selection = TSM.db.profile.groupTreeSelectedGroupStatus and TSM.db.profile.groupTreeSelectedGroupStatus["Warehousing_Bank"]
if not selection then return end
local groupInfo = {}
for groupPath, isSelected in pairs(selection) do
if isSelected then
local operations = TSM:GetGroupOperations(groupPath, "Warehousing")
if operations then
groupInfo[groupPath] = { operations = operations, items = TSM:GetGroupItems(groupPath) }
end
end
end
if next(groupInfo) == nil then return end
return groupInfo
end
local function ShoppingNextSearch()
if next(private.shoppingItems) then
move:ShoppingSearch(private.shoppingItems[1].itemString, private.shoppingItems[1].quantity)
end
end
local function ShoppingCallback(remainingQty, boughtItem)
if not boughtItem then
if next(private.shoppingItems) then
local name = TSMAPI:GetSafeItemInfo(private.shoppingItems[1].itemString)
TSM:Print(format(L["No Auctions found for %s"], name or private.shoppingItems[1].itemString))
tremove(private.shoppingItems, 1)
TSMAPI:CreateTimeDelay("warehousingShoppingSearchThrottle", 0.5, ShoppingNextSearch)
end
elseif math.max(remainingQty or 0, 0) == 0 and next(private.shoppingItems) then
tremove(private.shoppingItems, 1)
TSMAPI:CreateTimeDelay("warehousingShoppingSearchThrottle", 0.5, ShoppingNextSearch)
end
end
function move:ShoppingSearch(itemString, need)
if not itemString or not need or need <= 0 then return end
local name = TSMAPI:GetSafeItemInfo(itemString)
if not name then return end
TSMAPI:ModuleAPI("Shopping", "runSearch", name .. "/exact/x" .. need, ShoppingCallback)
end
function move:restockGroup(grpInfo)
local restockItems, next = TSM.data:unIndexRestockGroupTree(grpInfo), next
@ -24,6 +68,37 @@ function move:restockGroup(grpInfo)
end
end
function move:restockGroupAuction(grpInfo)
if not TSMAPI:AHTabIsVisible("Shopping") then
TSM:Print(L["Please switch to the Shopping tab to restock from the auction house."])
return
end
if not grpInfo then
grpInfo = GetSelectedGroupInfoFallback()
end
if not grpInfo or next(grpInfo) == nil then
TSM:Print(L["No groups selected."])
return
end
local restockItems = TSM.data:unIndexRestockGroupTree(grpInfo)
private.shoppingItems = {}
for itemString, quantity in pairs(restockItems) do
if quantity > 0 then
tinsert(private.shoppingItems, { itemString = itemString, quantity = quantity })
end
end
if next(private.shoppingItems) == nil then
TSM:Print(L["Nothing to Restock"])
return
end
TSM:Print(L["Restocking"])
move:ShoppingSearch(private.shoppingItems[1].itemString, private.shoppingItems[1].quantity)
end
function move:groupTree(grpInfo, src)
local moveItems, next = TSM.data:unIndexMoveGroupTree(grpInfo, src), next
if next(moveItems) == nil then

5
TradeSkillMaster_Warehousing/TradeSkillMaster_Warehousing.lua

@ -89,6 +89,7 @@ function TSM:RegisterModule()
{ key = "movedata", label = L["Displays realtime move data."], callback = "SetLogFlag" },
{ key = "get", label = L["Gets items from the bank or guild bank matching the itemstring, itemID or partial text entered."], callback = "GetItem" },
{ key = "put", label = L["Puts items matching the itemstring, itemID or partial text entered into the bank or guild bank."], callback = "PutItem" },
{ key = "restockah", label = L["Restocks items from the AH based on your Warehousing group selections."], callback = "RestockFromAH" },
}
TSM.operations = { maxOperations = 1, callbackOptions = "Options:Load", callbackInfo = "GetOperationInfo" }
TSM.bankUiButton = { callback = "bankui:createTab" }
@ -238,3 +239,7 @@ function TSM:PutItem(args)
TSM:Print(L["Invalid criteria entered."])
end
end
function TSM:RestockFromAH()
TSM.move:restockGroupAuction()
end

40
bin/link_addons.sh

@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TARGET_DIR="../Games/ascension-wow/drive_c/Program Files/Ascension Launcher/resources/client/Interface/AddOns"
ADDONS=(
"TradeSkillMaster"
"TradeSkillMaster_Accounting"
"TradeSkillMaster_AuctionDB"
"TradeSkillMaster_Auctioning"
"TradeSkillMaster_Crafting"
"TradeSkillMaster_Destroying"
"TradeSkillMaster_ItemTracker"
"TradeSkillMaster_Mailing"
"TradeSkillMaster_Shopping"
"TradeSkillMaster_Warehousing"
)
if [[ ! -d "$TARGET_DIR" ]]; then
echo "Target AddOns directory not found: $TARGET_DIR" >&2
exit 1
fi
for addon in "${ADDONS[@]}"; do
live_path="${TARGET_DIR}/${addon}"
src_path="${ROOT_DIR}/${addon}"
if [[ ! -d "$src_path" ]]; then
echo "Source addon folder missing: $src_path" >&2
continue
fi
if [[ -L "$live_path" || -d "$live_path" ]]; then
rm -rf "$live_path"
fi
ln -s "$src_path" "$live_path"
echo "Linked ${addon}"
done

32
bin/sshfs-mounts.sh

@ -0,0 +1,32 @@
#!/usr/bin/env bash
set -euo pipefail
USER_NAME="${USER_NAME:-$USER}"
HOST="sarvern.no"
PORT="36"
REMOTE_BASE="/home/TradeSkillMaster"
TARGET_BASE="/home/TradeSkillMaster" # edit this if you want a different mount root
mkdir -p "$TARGET_BASE/scripts"
mkdir -p "$TARGET_BASE/TradeSkillMaster"
mkdir -p "$TARGET_BASE/TradeSkillMaster_Accounting"
mkdir -p "$TARGET_BASE/TradeSkillMaster_AuctionDB"
mkdir -p "$TARGET_BASE/TradeSkillMaster_Auctioning"
mkdir -p "$TARGET_BASE/TradeSkillMaster_Crafting"
mkdir -p "$TARGET_BASE/TradeSkillMaster_Destroying"
mkdir -p "$TARGET_BASE/TradeSkillMaster_ItemTracker"
mkdir -p "$TARGET_BASE/TradeSkillMaster_Mailing"
mkdir -p "$TARGET_BASE/TradeSkillMaster_Shopping"
mkdir -p "$TARGET_BASE/TradeSkillMaster_Warehousing"
sshfs -p "$PORT" "$USER_NAME@$HOST:$REMOTE_BASE/scripts" "$TARGET_BASE/scripts"
sshfs -p "$PORT" "$USER_NAME@$HOST:$REMOTE_BASE/TradeSkillMaster" "$TARGET_BASE/TradeSkillMaster"
sshfs -p "$PORT" "$USER_NAME@$HOST:$REMOTE_BASE/TradeSkillMaster_Accounting" "$TARGET_BASE/TradeSkillMaster_Accounting"
sshfs -p "$PORT" "$USER_NAME@$HOST:$REMOTE_BASE/TradeSkillMaster_AuctionDB" "$TARGET_BASE/TradeSkillMaster_AuctionDB"
sshfs -p "$PORT" "$USER_NAME@$HOST:$REMOTE_BASE/TradeSkillMaster_Auctioning" "$TARGET_BASE/TradeSkillMaster_Auctioning"
sshfs -p "$PORT" "$USER_NAME@$HOST:$REMOTE_BASE/TradeSkillMaster_Crafting" "$TARGET_BASE/TradeSkillMaster_Crafting"
sshfs -p "$PORT" "$USER_NAME@$HOST:$REMOTE_BASE/TradeSkillMaster_Destroying" "$TARGET_BASE/TradeSkillMaster_Destroying"
sshfs -p "$PORT" "$USER_NAME@$HOST:$REMOTE_BASE/TradeSkillMaster_ItemTracker" "$TARGET_BASE/TradeSkillMaster_ItemTracker"
sshfs -p "$PORT" "$USER_NAME@$HOST:$REMOTE_BASE/TradeSkillMaster_Mailing" "$TARGET_BASE/TradeSkillMaster_Mailing"
sshfs -p "$PORT" "$USER_NAME@$HOST:$REMOTE_BASE/TradeSkillMaster_Shopping" "$TARGET_BASE/TradeSkillMaster_Shopping"
sshfs -p "$PORT" "$USER_NAME@$HOST:$REMOTE_BASE/TradeSkillMaster_Warehousing" "$TARGET_BASE/TradeSkillMaster_Warehousing"

78
scripts/link_and_go.sh

@ -0,0 +1,78 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
MODE="${MODE:-local}" # local or ssh
if [[ "${1:-}" == "--ssh" ]]; then
MODE="ssh"
elif [[ "${1:-}" == "--local" ]]; then
MODE="local"
fi
ADDONS_DIR="${ADDONS_DIR:-../Games/ascension-wow/drive_c/Program Files/Ascension Launcher/resources/client/Interface/AddOns}"
MOUNT_DIR="${MOUNT_DIR:-$ROOT_DIR}"
LOCAL_BASE="${LOCAL_BASE:-$ROOT_DIR}"
SSH_USER="${SSH_USER:-$USER}"
SSH_HOST="${SSH_HOST:-sarvern.no}"
SSH_PORT="${SSH_PORT:-36}"
SSH_REMOTE_BASE="${SSH_REMOTE_BASE:-/home/TradeSkillMaster}"
ADDONS=(
"TradeSkillMaster"
"TradeSkillMaster_Accounting"
"TradeSkillMaster_AuctionDB"
"TradeSkillMaster_Auctioning"
"TradeSkillMaster_Crafting"
"TradeSkillMaster_Destroying"
"TradeSkillMaster_ItemTracker"
"TradeSkillMaster_Mailing"
"TradeSkillMaster_Shopping"
"TradeSkillMaster_Warehousing"
)
MOUNT_ITEMS=("scripts" "${ADDONS[@]}")
if [[ "$MODE" == "ssh" ]]; then
mkdir -p "$MOUNT_DIR"
for item in "${MOUNT_ITEMS[@]}"; do
target_path="${MOUNT_DIR}/${item}"
mkdir -p "$target_path"
if command -v mountpoint >/dev/null && mountpoint -q "$target_path"; then
echo "Already mounted: $target_path"
continue
fi
sshfs -p "$SSH_PORT" "$SSH_USER@$SSH_HOST:$SSH_REMOTE_BASE/$item" "$target_path"
echo "Mounted $item"
done
SOURCE_BASE="$MOUNT_DIR"
else
SOURCE_BASE="$LOCAL_BASE"
fi
if [[ ! -d "$ADDONS_DIR" ]]; then
echo "AddOns directory not found: $ADDONS_DIR" >&2
exit 1
fi
for addon in "${ADDONS[@]}"; do
live_path="${ADDONS_DIR}/${addon}"
src_path="${SOURCE_BASE}/${addon}"
if [[ ! -d "$src_path" ]]; then
echo "Source addon folder missing: $src_path" >&2
continue
fi
if [[ -L "$live_path" || -d "$live_path" ]]; then
rm -rf "$live_path"
fi
ln -s "$src_path" "$live_path"
echo "Linked ${addon}"
done
Loading…
Cancel
Save