You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
368 lines
14 KiB
368 lines
14 KiB
-- ------------------------------------------------------------------------------ -- |
|
-- TradeSkillMaster -- |
|
-- http://www.curse.com/addons/wow/tradeskill-master -- |
|
-- -- |
|
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) -- |
|
-- All Rights Reserved* - Detailed license information included with addon. -- |
|
-- ------------------------------------------------------------------------------ -- |
|
|
|
-- This file contains all the code for the new standardized module registration / format |
|
|
|
local TSM = select(2, ...) |
|
local Modules = TSM:NewModule("Modules", "AceConsole-3.0") |
|
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster") -- loads the localization table |
|
local moduleObjects = TSM.moduleObjects |
|
local moduleNames = TSM.moduleNames |
|
|
|
|
|
-- initialization stuff |
|
function Modules:OnEnable() |
|
-- register the chat commands (slash commands) - whenver '/tsm' or '/tradeskillmaster' is typed by the user, Modules:ChatCommand() will be called |
|
Modules:RegisterChatCommand("tsm", "ChatCommand") |
|
Modules:RegisterChatCommand("tradeskillmaster", "ChatCommand") |
|
|
|
-- tooltip setup |
|
TSM:SetupTooltips() |
|
|
|
-- no modules popup |
|
TSMAPI:CreateTimeDelay("noModulesPopup", 3, function() |
|
if #moduleNames == 1 then |
|
StaticPopupDialogs["TSMInfoPopup"] = { |
|
text = L["|cffffff00Important Note:|r You do not currently have any modules installed / enabled for TradeSkillMaster! |cff77ccffYou must download modules for TradeSkillMaster to have some useful functionality!|r\n\nPlease visit http://www.curse.com/addons/wow/tradeskill-master and check the project description for links to download modules."], |
|
button1 = L["I'll Go There Now!"], |
|
timeout = 0, |
|
whileDead = true, |
|
OnAccept = function() TSM:Print(L["Just incase you didn't read this the first time:"]) TSM:Print(L["|cffffff00Important Note:|r You do not currently have any modules installed / enabled for TradeSkillMaster! |cff77ccffYou must download modules for TradeSkillMaster to have some useful functionality!|r\n\nPlease visit http://www.curse.com/addons/wow/tradeskill-master and check the project description for links to download modules."]) end, |
|
preferredIndex = 3, |
|
} |
|
TSMAPI:ShowStaticPopupDialog("TSMInfoPopup") |
|
end |
|
end) |
|
end |
|
|
|
|
|
-- ************************************************************************** |
|
-- TSMAPI:NewModule API |
|
-- ************************************************************************** |
|
|
|
-- info on all the possible fields of the module objects which TSM core cares about |
|
local moduleFieldInfo = { |
|
-- operation fields |
|
{ key = "operations", type = "table", subFieldInfo = { maxOperations = "number", callbackOptions = "function", callbackInfo = "function" } }, |
|
-- tooltip fields |
|
{ key = "GetTooltip", type = "function" }, |
|
-- tooltip options |
|
{ key = "tooltipOptions", type = "table", subFieldInfo = { callback = "function" } }, |
|
-- shared feature fields |
|
{ key = "slashCommands", type = "table", subTableInfo = { key = "string", label = "string", callback = "function" } }, |
|
{ key = "icons", type = "table", subTableInfo = { side = "string", desc = "string", callback = "function", icon = "string" } }, |
|
{ key = "auctionTab", type = "table", subFieldInfo = { callbackShow = "function", callbackHide = "function" } }, |
|
{ key = "bankUiButton", type = "table", subFieldInfo = { callback = "function" } }, |
|
-- data access fields |
|
{ key = "priceSources", type = "table", subTableInfo = { key = "string", label = "string", callback = "function" } }, |
|
{ key = "moduleAPIs", type = "table", subTableInfo = { key = "string", callback = "function" } }, |
|
-- multi-account sync fields |
|
{ key = "sync", type = "table", subFieldInfo = { callback = "function" } }, |
|
} |
|
|
|
-- if the passed function is a string, will check if it's a method of the object and return a wrapper function |
|
function Modules:GetFunction(obj, func) |
|
if type(func) == "string" then |
|
local part1, part2 = (":"):split(func) |
|
if part2 and obj[part1] and obj[part1][part2] then |
|
return function(...) return obj[part1][part2](obj[part1], ...) end |
|
elseif obj[part1] then |
|
return function(...) return obj[part1](obj, ...) end |
|
end |
|
end |
|
return func |
|
end |
|
|
|
-- validates a simple list of sub-tables which have the basic key/label/callback fields |
|
function Modules:ValidateList(obj, val, keys) |
|
for i, v in ipairs(val) do |
|
if type(v) ~= "table" then |
|
return "invalid entry in list at index " .. i |
|
end |
|
for key, valType in pairs(keys) do |
|
if valType == "function" then |
|
v[key] = Modules:GetFunction(obj, v[key]) |
|
end |
|
if type(v[key]) ~= valType then |
|
return format("expected %s type for field %s, got %s at index %d", valType, key, type(v[key]), i) |
|
end |
|
end |
|
end |
|
end |
|
|
|
function Modules:ValidateModuleObject(obj) |
|
-- make sure it's a table |
|
if type(obj) ~= "table" then |
|
return format("Expected table, got %s.", type(obj)) |
|
end |
|
-- simple check that it's an AceAddon object which stores the name in .name and implements a .__tostring metamethod. |
|
if tostring(obj) ~= obj.name then |
|
return "Passed object is not an AceAddon-3.0 object." |
|
end |
|
|
|
-- validate all the fields |
|
for _, fieldInfo in ipairs(moduleFieldInfo) do |
|
local val = obj[fieldInfo.key] |
|
if val then |
|
-- make sure it's of the correct type |
|
if type(val) ~= fieldInfo.type then |
|
return format("For field '%s', expected type of %s, got %s.", fieldInfo.key, fieldInfo.type, type(val)) |
|
end |
|
-- if there's required subfields, check them |
|
if fieldInfo.subFieldInfo then |
|
for key, valType in pairs(fieldInfo.subFieldInfo) do |
|
if valType == "function" then |
|
val[key] = Modules:GetFunction(obj, val[key]) |
|
end |
|
if type(val[key]) ~= valType then |
|
return format("expected %s type for field %s, got %s at index %d", valType, key, type(val[key]), key) |
|
end |
|
end |
|
end |
|
-- if there's subTableInfo specified, run Modules:ValidateList on this field |
|
if fieldInfo.subTableInfo then |
|
local errMsg = Modules:ValidateList(obj, val, fieldInfo.subTableInfo) |
|
if errMsg then |
|
return format("Invalid value for '%s': %s.", fieldInfo.key, errMsg) |
|
end |
|
end |
|
end |
|
end |
|
end |
|
|
|
function Modules:GetInfo() |
|
local info = {} |
|
for _, name in ipairs(moduleNames) do |
|
local obj = moduleObjects[name] |
|
tinsert(info, { name = name, version = obj._version, author = obj._author, desc = obj._desc }) |
|
end |
|
return info |
|
end |
|
|
|
function TSMAPI:NewModule(obj) |
|
local errMsg |
|
if obj == TSM then |
|
local tmp = TSM.operations |
|
TSM.operations = nil |
|
errMsg = Modules:ValidateModuleObject(obj) |
|
TSM.operations = tmp |
|
else |
|
errMsg = Modules:ValidateModuleObject(obj) |
|
end |
|
if errMsg then |
|
error(errMsg, 2) |
|
end |
|
|
|
-- register the db callback |
|
if obj.db and obj.OnTSMDBShutdown then |
|
obj.db:RegisterCallback("OnDatabaseShutdown", TSM.ModuleOnDatabaseShutdown) |
|
end |
|
|
|
-- register it for debug tracing |
|
TSMAPI:RegisterForTracing(obj) |
|
for _, subModule in pairs(obj.modules or {}) do |
|
local name = obj.name.."."..subModule.moduleName |
|
TSMAPI:RegisterForTracing(subModule, name) |
|
end |
|
|
|
-- sets the _version, _author, and _desc fields |
|
local fullName = gsub(obj.name, "TSM_", "TradeSkillMaster_") |
|
obj._version = GetAddOnMetadata(fullName, "X-Curse-Packaged-Version") or GetAddOnMetadata(fullName, "Version") |
|
if strsub(obj._version, 1, 1) == "@" then |
|
obj._version = "Dev" |
|
end |
|
obj._author = GetAddOnMetadata(fullName, "Author") |
|
obj._desc = GetAddOnMetadata(fullName, "Notes") |
|
|
|
-- store the object in the local table |
|
local moduleName = gsub(obj.name, "TradeSkillMaster_", "") |
|
moduleName = gsub(obj.name, "TSM_", "") |
|
moduleObjects[moduleName] = obj |
|
tinsert(moduleNames, moduleName) |
|
sort(moduleNames, function(a, b) |
|
if a == "TradeSkillMaster" then |
|
return true |
|
elseif b == "TradeSkillMaster" then |
|
return false |
|
else |
|
return a < b |
|
end |
|
end) |
|
|
|
-- register icons with main frame code |
|
if obj.icons then |
|
for _, info in ipairs(obj.icons) do |
|
if info.slashCommand then |
|
obj.slashCommands = obj.slashCommands or {} |
|
tinsert(obj.slashCommands, {key=info.slashCommand, label=format("Opens the TSM window to the '%s' page", info.desc), callback=function() TSMAPI:OpenFrame() TSMAPI:SelectIcon(obj.name, info.desc) end}) |
|
end |
|
TSM:RegisterMainFrameIcon(info.desc, info.icon, info.callback, obj.name, info.side) |
|
end |
|
end |
|
|
|
-- register auction buttons with auction frame code |
|
if obj.auctionTab then |
|
TSM:RegisterAuctionFunction(moduleName, obj.auctionTab.callbackShow, obj.auctionTab.callbackHide) |
|
end |
|
if obj ~= TSM and obj.operations then |
|
-- conversion code from early beta versions |
|
if obj.db and obj.db.global.operations then |
|
TSM.operations[moduleName] = CopyTable(obj.db.global.operations) |
|
obj.db.global.operations = nil |
|
end |
|
TSM:RegisterOperationInfo(moduleName, obj.operations) |
|
TSM.operations[moduleName] = TSM.operations[moduleName] or {} |
|
obj.operations = TSM.operations[moduleName] |
|
for _, operation in pairs(obj.operations) do |
|
operation.ignorePlayer = operation.ignorePlayer or {} |
|
operation.ignorerealm = operation.ignorerealm or {} |
|
operation.relationships = operation.relationships or {} |
|
end |
|
TSM:CheckOperationRelationships(moduleName) |
|
end |
|
-- register tooltip options |
|
if obj.tooltipOptions then |
|
TSM:RegisterTooltipInfo(moduleName, obj.tooltipOptions) |
|
end |
|
-- register bankUi Tabs |
|
if obj.bankUiButton then |
|
TSM:RegisterBankUiButton(moduleName, obj.bankUiButton.callback) |
|
end |
|
-- -- register sync callback |
|
-- if obj.sync then |
|
-- TSM:RegisterSyncCallback(moduleName, obj.sync.callback) |
|
-- end |
|
|
|
-- replace default Print and Printf functions |
|
local Print = obj.Print |
|
obj.Print = function(self, ...) Print(self, TSMAPI:GetChatFrame(), ...) end |
|
local Printf = obj.Printf |
|
obj.Printf = function(self, ...) Printf(self, TSMAPI:GetChatFrame(), ...) end |
|
end |
|
|
|
function TSM:UpdateModuleProfiles() |
|
if TSM.db.global.globalOperations then |
|
for moduleName, obj in pairs(moduleObjects) do |
|
if obj.operations then |
|
TSM.db.global.operations[moduleName] = TSM.db.global.operations[moduleName] or {} |
|
obj.operations = TSM.db.global.operations[moduleName] |
|
end |
|
end |
|
TSM.operations = TSM.db.global.operations |
|
else |
|
for moduleName, obj in pairs(moduleObjects) do |
|
if obj.operations then |
|
TSM.db.profile.operations[moduleName] = TSM.db.profile.operations[moduleName] or {} |
|
obj.operations = TSM.db.profile.operations[moduleName] |
|
end |
|
end |
|
TSM.operations = TSM.db.profile.operations |
|
end |
|
for module, operations in pairs(TSM.operations) do |
|
for _, operation in pairs(operations) do |
|
operation.ignorePlayer = operation.ignorePlayer or {} |
|
operation.ignorerealm = operation.ignorerealm or {} |
|
operation.relationships = operation.relationships or {} |
|
end |
|
TSM:CheckOperationRelationships(module) |
|
end |
|
if not TSM.db.profile.design then |
|
TSM:LoadDefaultDesign() |
|
end |
|
end |
|
|
|
local didDBShutdown = false |
|
function TSM:ModuleOnDatabaseShutdown() |
|
if didDBShutdown then return end |
|
didDBShutdown = true |
|
local originalProfile = TSM.db:GetCurrentProfile() |
|
for _, obj in pairs(moduleObjects) do |
|
-- erroring here would cause the profile to be reset, so use pcall |
|
if obj.OnTSMDBShutdown and not pcall(obj.OnTSMDBShutdown) then |
|
-- the callback hit an error, so ensure the correct profile is restored |
|
TSM.db:SetProfile(originalProfile) |
|
end |
|
end |
|
-- ensure we're back on the correct profile |
|
TSM.db:SetProfile(originalProfile) |
|
end |
|
|
|
function TSM:IsOperationIgnored(module, operationName) |
|
local obj = moduleObjects[module] |
|
local operation = obj.operations[operationName] |
|
if not operation then return end |
|
local realm = TSM.db.keys.realm |
|
local playerKey = UnitName("player").." - "..realm |
|
return operation.ignorePlayer[playerKey] or operation.ignorerealm[realm] |
|
end |
|
|
|
function TSM:CheckOperationRelationships(moduleName) |
|
for _, operation in pairs(TSM.operations[moduleName]) do |
|
for key, target in pairs(operation.relationships or {}) do |
|
if not TSM.operations[moduleName][target] then |
|
operation.relationships[key] = nil |
|
end |
|
end |
|
end |
|
end |
|
|
|
|
|
-- ************************************************************************** |
|
-- Module APIs |
|
-- ************************************************************************** |
|
|
|
function TSMAPI:ModuleAPI(moduleName, key, ...) |
|
if type(moduleName) ~= "string" or type(key) ~= "string" then return nil, "Invalid args" end |
|
if not moduleObjects[moduleName] then return nil, "Invalid module" end |
|
|
|
for _, info in ipairs(moduleObjects[moduleName].moduleAPIs or {}) do |
|
if info.key == key then |
|
return info.callback(...) |
|
end |
|
end |
|
return nil, "Key not found" |
|
end |
|
|
|
|
|
-- ************************************************************************** |
|
-- Slash Commands |
|
-- ************************************************************************** |
|
|
|
function Modules:ChatCommand(input) |
|
local parts = { (" "):split(input) } |
|
local cmd, args = strlower(parts[1] or ""), table.concat(parts, " ", 2) |
|
|
|
if cmd == "" then |
|
TSMAPI:OpenFrame() |
|
TSMAPI:SelectIcon("TradeSkillMaster", L["TSM Status / Options"]) |
|
else |
|
local foundCmd |
|
for _, obj in pairs(moduleObjects) do |
|
if obj.slashCommands then |
|
for _, info in ipairs(obj.slashCommands) do |
|
if strlower(info.key) == cmd then |
|
info.callback(args) |
|
foundCmd = true |
|
end |
|
end |
|
end |
|
end |
|
-- If not a registered command, print out slash command help |
|
if not foundCmd then |
|
local chatFrame = TSMAPI:GetChatFrame() |
|
TSM:Print(L["Slash Commands:"]) |
|
chatFrame:AddMessage("|cffffaa00" .. L["/tsm|r - opens the main TSM window."]) |
|
chatFrame:AddMessage("|cffffaa00" .. L["/tsm help|r - Shows this help listing"]) |
|
for _, name in ipairs(moduleNames) do |
|
for _, info in ipairs(moduleObjects[name].slashCommands or {}) do |
|
chatFrame:AddMessage("|cffffaa00/tsm " .. info.key .. "|r - " .. info.label) |
|
end |
|
end |
|
end |
|
end |
|
end |