253 lines
7.3 KiB
Lua
253 lines
7.3 KiB
Lua
-- ------------------------------------------------------------------------------ --
|
|
-- TradeSkillMaster --
|
|
-- https://tradeskillmaster.com --
|
|
-- All Rights Reserved - Detailed license information included with addon. --
|
|
-- ------------------------------------------------------------------------------ --
|
|
|
|
-- This is loaded before anything else and simply sets up the addon table
|
|
|
|
local ADDON_NAME, TSM = ...
|
|
local VERSION_RAW = GetAddOnMetadata("TradeSkillMaster", "Version")
|
|
local IS_DEV_VERSION = strmatch(VERSION_RAW, "^@tsm%-project%-version@$") and true or false
|
|
local private = {
|
|
context = {},
|
|
initOrder = {},
|
|
loadOrder = {},
|
|
frame = nil,
|
|
gotAddonLoaded = false,
|
|
gotPlayerLogin = false,
|
|
gotPlayerLogout = false,
|
|
}
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Module Metatable
|
|
-- ============================================================================
|
|
|
|
local MODULE_METHODS = {
|
|
OnModuleLoad = function(self, func)
|
|
local context = private.context[self]
|
|
assert(context and not context.moduleLoadFunc and not context.moduleLoadTime and type(func) == "function")
|
|
context.moduleLoadFunc = func
|
|
end,
|
|
OnSettingsLoad = function(self, func)
|
|
local context = private.context[self]
|
|
assert(context and not context.settingsLoadFunc and not context.settingsLoadTime and type(func) == "function")
|
|
context.settingsLoadFunc = func
|
|
end,
|
|
OnGameDataLoad = function(self, func)
|
|
local context = private.context[self]
|
|
assert(context and not context.gameDataLoadFunc and not context.gameDataLoadTime and type(func) == "function")
|
|
context.gameDataLoadFunc = func
|
|
end,
|
|
OnModuleUnload = function(self, func)
|
|
local context = private.context[self]
|
|
assert(context and not context.moduleUnloadFunc and not context.moduleUnloadTime and type(func) == "function")
|
|
context.moduleUnloadFunc = func
|
|
end,
|
|
}
|
|
|
|
local MODULE_MT = {
|
|
__index = MODULE_METHODS,
|
|
__newindex = function(self, key, value)
|
|
local context = private.context[self]
|
|
assert(context and not MODULE_METHODS[key] and not context.moduleLoadTime)
|
|
rawset(self, key, value)
|
|
end,
|
|
__metatable = false,
|
|
}
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Addon Object Functions
|
|
-- ============================================================================
|
|
|
|
function TSM.Init(path)
|
|
assert(type(path) == "string")
|
|
if private.context[path] then
|
|
error("Module already exists for path: "..tostring(path))
|
|
end
|
|
local module = setmetatable({}, MODULE_MT)
|
|
private.context[path] = {
|
|
path = path,
|
|
module = module,
|
|
moduleLoadFunc = nil,
|
|
moduleLoadTime = nil,
|
|
settingsLoadFunc = nil,
|
|
settingsLoadTime = nil,
|
|
gameDataLoadFunc = nil,
|
|
gameDataLoadTime = nil,
|
|
moduleUnloadFunc = nil,
|
|
moduleUnloadTime = nil,
|
|
}
|
|
-- store a reference to the context by both the module object and the path
|
|
private.context[module] = private.context[path]
|
|
tinsert(private.initOrder, path)
|
|
return module
|
|
end
|
|
|
|
function TSM.Include(path)
|
|
local context = private.context[path]
|
|
if not context then
|
|
error("Module doesn't exist for path: "..tostring(path))
|
|
end
|
|
private.ProcessModuleLoad(path)
|
|
return context.module
|
|
end
|
|
|
|
function TSM.IsDevVersion()
|
|
return IS_DEV_VERSION
|
|
end
|
|
|
|
function TSM.GetVersion()
|
|
return IS_DEV_VERSION and "Dev" or VERSION_RAW
|
|
end
|
|
|
|
function TSM.ModuleInfoIterator()
|
|
return private.ModuleInfoIterator, nil, 0
|
|
end
|
|
|
|
function TSM.IsWowClassic()
|
|
return WOW_PROJECT_ID == WOW_PROJECT_CLASSIC
|
|
end
|
|
|
|
function TSM.IsShadowlands()
|
|
return select(4, GetBuildInfo()) >= 90000
|
|
end
|
|
|
|
function TSM.DebugLogout()
|
|
private.UnloadAll()
|
|
end
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Private Helper Functions
|
|
-- ============================================================================
|
|
|
|
function private.ModuleInfoIterator(_, index)
|
|
index = index + 1
|
|
local path = private.initOrder[index]
|
|
if not path then
|
|
return
|
|
end
|
|
local context = private.context[path]
|
|
assert(context)
|
|
return index, path, context.moduleLoadTime, context.settingsLoadTime, context.gameDataLoadTime, context.moduleUnloadTime
|
|
end
|
|
|
|
function private.ProcessModuleLoad(path)
|
|
local context = private.context[path]
|
|
assert(context)
|
|
if context.moduleLoadTime then
|
|
-- already loaded
|
|
return
|
|
end
|
|
tinsert(private.loadOrder, path)
|
|
context.moduleLoadTime = 0
|
|
if context.moduleLoadFunc then
|
|
local st = debugprofilestop()
|
|
context.moduleLoadFunc()
|
|
context.moduleLoadTime = debugprofilestop() - st
|
|
end
|
|
end
|
|
|
|
function private.ProcessSettingsLoad(path)
|
|
local context = private.context[path]
|
|
assert(context)
|
|
if context.settingsLoadTime then
|
|
-- already loaded
|
|
return
|
|
end
|
|
context.settingsLoadTime = 0
|
|
if context.settingsLoadFunc then
|
|
local st = debugprofilestop()
|
|
context.settingsLoadFunc()
|
|
context.settingsLoadTime = debugprofilestop() - st
|
|
end
|
|
end
|
|
|
|
function private.ProcessGameDataLoad(path)
|
|
local context = private.context[path]
|
|
assert(context)
|
|
if context.gameDataLoadTime then
|
|
-- already loaded
|
|
return
|
|
end
|
|
context.gameDataLoadTime = 0
|
|
if context.gameDataLoadFunc then
|
|
local st = debugprofilestop()
|
|
context.gameDataLoadFunc()
|
|
context.gameDataLoadTime = debugprofilestop() - st
|
|
end
|
|
end
|
|
|
|
function private.ProcessModuleUnload(path)
|
|
local context = private.context[path]
|
|
assert(context)
|
|
if context.moduleUnloadTime then
|
|
-- already unloaded
|
|
return
|
|
end
|
|
context.moduleUnloadTime = 0
|
|
if context.moduleUnloadFunc then
|
|
local st = debugprofilestop()
|
|
context.moduleUnloadFunc()
|
|
context.moduleUnloadTime = debugprofilestop() - st
|
|
end
|
|
end
|
|
|
|
function private.UnloadAll()
|
|
-- unload in the opposite order we loaded
|
|
for i = #private.loadOrder, 1, -1 do
|
|
private.ProcessModuleUnload(private.loadOrder[i])
|
|
end
|
|
end
|
|
|
|
function private.OnEvent(_, event, arg)
|
|
if event == "ADDON_LOADED" and arg == ADDON_NAME and not private.gotAddonLoaded then
|
|
assert(not private.gotAddonLoaded and not private.gotPlayerLogin and not private.gotPlayerLogout)
|
|
private.gotAddonLoaded = true
|
|
-- load any module which haven't already
|
|
for _, path in ipairs(private.initOrder) do
|
|
private.ProcessModuleLoad(path)
|
|
end
|
|
-- settings are now available
|
|
for _, path in ipairs(private.loadOrder) do
|
|
private.ProcessSettingsLoad(path)
|
|
end
|
|
private.frame:UnregisterEvent("ADDON_LOADED")
|
|
elseif event == "PLAYER_LOGIN" then
|
|
assert(private.gotAddonLoaded and not private.gotPlayerLogin and not private.gotPlayerLogout)
|
|
private.gotPlayerLogin = true
|
|
-- game data is now available
|
|
for _, path in ipairs(private.loadOrder) do
|
|
private.ProcessGameDataLoad(path)
|
|
end
|
|
elseif event == "PLAYER_LOGOUT" then
|
|
assert(private.gotAddonLoaded and not private.gotPlayerLogout)
|
|
private.gotPlayerLogout = true
|
|
if not private.gotPlayerLogin then
|
|
-- this can happen if the player exists the game during the loading screen, in which case we just ignore it
|
|
return
|
|
end
|
|
private.UnloadAll()
|
|
end
|
|
end
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Initialization Code
|
|
-- ============================================================================
|
|
|
|
do
|
|
private.frame = CreateFrame("Frame")
|
|
private.frame:RegisterEvent("ADDON_LOADED")
|
|
private.frame:RegisterEvent("PLAYER_LOGIN")
|
|
private.frame:RegisterEvent("PLAYER_LOGOUT")
|
|
private.frame:SetScript("OnEvent", private.OnEvent)
|
|
end
|