Files
Questie-X/Modules/QuestieInit.lua
T

668 lines
27 KiB
Lua

---@class QuestieInit
local QuestieInit = QuestieLoader:CreateModule("QuestieInit")
local _QuestieInit = QuestieInit.private
local compilationDeferred = false
local needsCompilation = false
---@type ThreadLib
local ThreadLib = QuestieLoader:ImportModule("ThreadLib")
---@type QuestEventHandler
local QuestEventHandler = QuestieLoader:ImportModule("QuestEventHandler")
---@type l10n
local l10n = QuestieLoader:ImportModule("l10n")
---@type ZoneDB
local ZoneDB = QuestieLoader:ImportModule("ZoneDB")
---@type Migration
local Migration = QuestieLoader:ImportModule("Migration")
---@type QuestieProfessions
local QuestieProfessions = QuestieLoader:ImportModule("QuestieProfessions")
---@type QuestieTracker
local QuestieTracker = QuestieLoader:ImportModule("QuestieTracker")
---@type QuestieMap
local QuestieMap = QuestieLoader:ImportModule("QuestieMap")
---@type QuestieLib
local QuestieLib = QuestieLoader:ImportModule("QuestieLib")
---@type QuestiePlayer
local QuestiePlayer = QuestieLoader:ImportModule("QuestiePlayer")
---@type QuestieDB
local QuestieDB = QuestieLoader:ImportModule("QuestieDB")
---@type Cleanup
local QuestieCleanup = QuestieLoader:ImportModule("Cleanup")
---@type DBCompiler
local QuestieDBCompiler = QuestieLoader:ImportModule("DBCompiler")
---@type QuestieCorrections
local QuestieCorrections = QuestieLoader:ImportModule("QuestieCorrections")
---@type QuestieMenu
local QuestieMenu = QuestieLoader:ImportModule("QuestieMenu")
---@type Townsfolk
local Townsfolk = QuestieLoader:ImportModule("Townsfolk")
---@type QuestieQuest
local QuestieQuest = QuestieLoader:ImportModule("QuestieQuest")
---@type IsleOfQuelDanas
local IsleOfQuelDanas = QuestieLoader:ImportModule("IsleOfQuelDanas")
---@type QuestieEventHandler
local QuestieEventHandler = QuestieLoader:ImportModule("QuestieEventHandler")
---@type QuestieJourney
local QuestieJourney = QuestieLoader:ImportModule("QuestieJourney")
---@type HBDHooks
local HBDHooks = QuestieLoader:ImportModule("HBDHooks")
---@type ChatFilter
local ChatFilter = QuestieLoader:ImportModule("ChatFilter")
---@type QuestieShutUp
local QuestieShutUp = QuestieLoader:ImportModule("QuestieShutUp")
---@type Hooks
local Hooks = QuestieLoader:ImportModule("Hooks")
---@type QuestieValidateGameCache
local QuestieValidateGameCache = QuestieLoader:ImportModule("QuestieValidateGameCache")
---@type MinimapIcon
local MinimapIcon = QuestieLoader:ImportModule("MinimapIcon")
---@type QuestieComms
local QuestieComms = QuestieLoader:ImportModule("QuestieComms");
local WOW_PROJECT_ID = QuestieCompat.WOW_PROJECT_ID
local WOW_PROJECT_CLASSIC = QuestieCompat.WOW_PROJECT_CLASSIC
---@type QuestieCompat
local QuestieCompat = QuestieLoader:ImportModule("QuestieCompat")
---@type QuestieOptions
local QuestieOptions = QuestieLoader:ImportModule("QuestieOptions");
---@type QuestieCoords
local QuestieCoords = QuestieLoader:ImportModule("QuestieCoords");
---@type QuestieArrow
local QuestieArrow = QuestieLoader:ImportModule("QuestieArrow")
---@type QuestieTooltips
local QuestieTooltips = QuestieLoader:ImportModule("QuestieTooltips");
---@type QuestieDBMIntegration
local QuestieDBMIntegration = QuestieLoader:ImportModule("QuestieDBMIntegration");
---@type TrackerQuestTimers
local TrackerQuestTimers = QuestieLoader:ImportModule("TrackerQuestTimers")
---@type QuestieCombatQueue
local QuestieCombatQueue = QuestieLoader:ImportModule("QuestieCombatQueue")
---@type QuestieSlash
local QuestieSlash = QuestieLoader:ImportModule("QuestieSlash")
---@type QuestXP
local QuestXP = QuestieLoader:ImportModule("QuestXP")
---@type Tutorial
local Tutorial = QuestieLoader:ImportModule("Tutorial")
---@type WorldMapButton
local WorldMapButton = QuestieLoader:ImportModule("WorldMapButton")
---@type AvailableQuests
local AvailableQuests = QuestieLoader:ImportModule("AvailableQuests")
---@type SeasonOfDiscovery
local SeasonOfDiscovery = QuestieLoader:ImportModule("SeasonOfDiscovery")
---@type QuestieLearner
local QuestieLearner = QuestieLoader:ImportModule("QuestieLearner")
---@type QuestieServer
local QuestieServer = QuestieLoader:ImportModule("QuestieServer")
--- COMPATIBILITY ---
local WOW_PROJECT_ID = QuestieCompat.WOW_PROJECT_ID
local C_Timer = QuestieCompat.C_Timer
-- Safe yield: only yields when we are actually inside a running coroutine.
-- Without this check, coroutine.yield() throws "attempt to yield across C-call boundary"
-- when Stage 1 runs synchronously inside AceAddon's pcall (a C stack frame).
local function coYield()
if coroutine.running() then
coroutine.yield()
end
end
local function GetCompiledDBMetadataBucket()
return Questie.dbCache and Questie.dbCache.global or Questie.db.global
end
local function GetCompiledDBMetadata()
local bucket = GetCompiledDBMetadataBucket()
if Questie.IsSoD then
bucket.sod = bucket.sod or {}
return bucket.sod, bucket
end
return bucket, bucket
end
local function _dbStats(t)
if type(t) ~= "table" then return "type=" .. type(t) end
local n, minK, maxK = 0, math.huge, 0
local k = next(t)
while k do
n = n + 1
if type(k) == "number" then
if k < minK then minK = k end
if k > maxK then maxK = k end
end
k = next(t, k)
end
return "count=" .. n .. " minID=" .. (minK == math.huge and 0 or minK) .. " maxID=" .. maxK
end
local function loadFullDatabase()
print("\124cFF4DDBFF [1/9] " .. l10n("Loading database") .. "...")
QuestieInit:LoadBaseDB()
if Questie.db and Questie.db.profile and Questie.db.profile.debugEnabled then
Questie:Debug(Questie.DEBUG_DEVELOP, "[DBDiag] After LoadBaseDB - quest:" .. _dbStats(QuestieDB.questData) .. " npc:" .. _dbStats(QuestieDB.npcData))
Questie:Debug(Questie.DEBUG_DEVELOP, "[DBDiag] obj:" .. _dbStats(QuestieDB.objectData) .. " item:" .. _dbStats(QuestieDB.itemData))
end
print("\124cFF4DDBFF [2/9] " .. l10n("Applying database corrections") .. "...")
coYield()
QuestieCorrections:Initialize()
if Questie.db and Questie.db.profile and Questie.db.profile.debugEnabled then
Questie:Debug(Questie.DEBUG_DEVELOP, "[DBDiag] After Corrections - quest:" .. _dbStats(QuestieDB.questData) .. " npc:" .. _dbStats(QuestieDB.npcData))
end
print("\124cFF4DDBFF [3/9] " .. l10n("Initializing townfolks") .. "...")
coYield()
Townsfolk.Initialize()
print("\124cFF4DDBFF [4/9] " .. l10n("Initializing locale") .. "...")
coYield()
l10n:Initialize()
coYield()
QuestieDB.private:DeleteGatheringNodes()
print("\124cFF4DDBFF [5/9] " .. l10n("Optimizing waypoints") .. "...")
coYield()
QuestieCorrections:PreCompile()
end
function QuestieInit:OnInitialize()
if QuestieInit.initialized then return end
QuestieInit.initialized = true
if QuestieInit.Stages and QuestieInit.Stages[1] and (not QuestieInit.stage1Done) then
QuestieInit.stage1Done = true
-- Run Stage 1 inside a coroutine so that all coroutine.yield() calls
-- in Stage 1 and its callees (Townsfolk, compiler, etc.) have a valid
-- coroutine context. AceAddon calls OnInitialize from a C pcall boundary
-- so calling coroutine.yield() without an enclosing coroutine crashes.
-- We drain the coroutine synchronously (resume until dead) so that
-- QuestieDB.QuestPointers is set before OnInitialize returns.
local co = coroutine.create(QuestieInit.Stages[1])
while coroutine.status(co) == "suspended" do
local ok, err = coroutine.resume(co)
if not ok then
print(debugstack(co))
break
end
end
else
end
end
---Run the validator
local function runValidator()
if type(QuestieDB.questData) == "string" or type(QuestieDB.npcData) == "string" or type(QuestieDB.objectData) == "string" or type(QuestieDB.itemData) == "string" then
Questie:Debug(Questie.DEBUG_CRITICAL, "Cannot run the validator on string data, load database first")
return
end
-- Run validator
if Questie.db.profile.debugEnabled then
coYield()
print("Validating NPCs...")
QuestieDBCompiler:ValidateNPCs()
coYield()
print("Validating objects...")
QuestieDBCompiler:ValidateObjects()
coYield()
print("Validating items...")
QuestieDBCompiler:ValidateItems()
coYield()
print("Validating quests...")
QuestieDBCompiler:ValidateQuests()
end
end
-- ********************************************************************************
-- Start of QuestieInit.Stages ******************************************************
-- stage worker functions. Most are coroutines.
QuestieInit.Stages = {}
QuestieInit.Stages[1] = function() -- run as a coroutine
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieInit:Stage1] Starting the real init.")
--? This was moved here because the lag that it creates is much less noticable here, while still initalizing correctly.
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieInit:Stage1] Starting QuestieOptions.Initialize Thread.")
ThreadLib.ThreadSimple(QuestieOptions.Initialize, 0)
if QuestieServer and QuestieServer.Init then
QuestieServer:Init()
end
MinimapIcon:Init()
if HBDHooks and HBDHooks.Init then
HBDHooks:Init()
end
Questie:SetIcons()
QuestieCompat.InitializeBlizzardPOISuppression()
if QUESTIE_LOCALES_OVERRIDE ~= nil then
l10n:InitializeLocaleOverride()
end
-- Set proper locale. Either default to client Locale or override based on user.
if Questie.db.global.questieLocaleDiff then
l10n:SetUILocale(Questie.db.global.questieLocale);
else
if QUESTIE_LOCALES_OVERRIDE ~= nil then
l10n:SetUILocale(QUESTIE_LOCALES_OVERRIDE.locale);
else
l10n:SetUILocale(GetLocale());
end
end
QuestieShutUp:ToggleFilters(Questie.db.profile.questieShutUp)
coYield()
ZoneDB:Initialize()
coYield()
Migration:Migrate()
IsleOfQuelDanas.Initialize() -- This has to happen before option init
QuestieProfessions:Init()
QuestXP.Init()
coYield()
local dbCompiled = false
local compiledMetadata, compiledRootMetadata = GetCompiledDBMetadata()
local dbIsCompiled = compiledMetadata.dbIsCompiled or false
local dbCompiledOnVersion = compiledMetadata.dbCompiledOnVersion
local dbCompiledLang = compiledMetadata.dbCompiledLang
if Questie.IsSoD then
coYield()
SeasonOfDiscovery.Initialize()
end
-- Check if the DB needs to be recompiled
needsCompilation = (not dbIsCompiled) or (QuestieLib:GetAddonVersionString() ~= dbCompiledOnVersion) or (l10n:GetUILocale() ~= dbCompiledLang) or (compiledRootMetadata.dbCompiledExpansion ~= WOW_PROJECT_ID)
-- Custom servers or presence of DB plugins: always defer to Stage3 to wait for plugin data injection
if Questie.IsAscension or Questie.IsEbonhold or Questie.IsValanior or QuestieServer:IsAnyDBPluginEnabled() then
compilationDeferred = true
l10n:Initialize()
coYield()
QuestieCorrections:MinimalInit() -- Needed for Stage2 which runs before Stage3
elseif needsCompilation then
print("|cFFAAEEFF" ..
l10n("Questie DB has updated!") ..
"|r|cFFFF6F22 " .. l10n("Data is being processed, this may take a few moments and cause some lag..."))
loadFullDatabase()
if Questie.db and Questie.db.profile and Questie.db.profile.debugEnabled then
Questie:Debug(Questie.DEBUG_DEVELOP, "[DBDiag] Before Compile - quest:" .. _dbStats(QuestieDB.questData) .. " npc:" .. _dbStats(QuestieDB.npcData))
end
QuestieDBCompiler:Compile()
Questie:Debug(Questie.DEBUG_DEVELOP, "[DBDiag] After Compile - quest:type=" .. type(QuestieDB.questData) .. " npc:type=" .. type(QuestieDB.npcData))
dbCompiled = true
else
Questie:Debug(Questie.DEBUG_DEVELOP, "[DBDiag] DB was CACHED (no recompile)")
l10n:Initialize()
coYield()
QuestieCorrections:MinimalInit()
end
local dbCompiledCount = compiledMetadata.dbCompiledCount
if (not Questie.db.char.townsfolk) or (dbCompiledCount ~= Questie.db.char.townsfolkVersion) or (Questie.db.char.townsfolkClass ~= UnitClass("player")) then
Questie.db.char.townsfolkVersion = dbCompiledCount
coYield()
Townsfolk:BuildCharacterTownsfolk()
end
coYield()
QuestieDB:Initialize()
coYield()
if QuestieLearner and QuestieLearner.Initialize then
QuestieLearner:Initialize()
end
coYield()
Tutorial.Initialize()
--? Only run the validator on recompile if debug is enabled, otherwise it's a waste of time.
--? Note: Validation is skipped if plugins are pending because plugins inject data after Stage 1,
--? so the compiled binary would be stale and validation would fail unnecessarily.
if Questie.db.profile.debugEnabled and dbCompiled then
local QuestiePluginAPI = QuestieLoader:ImportModule("QuestiePluginAPI")
if QuestiePluginAPI:HasPendingPlugins() then
Questie:Debug(Questie.DEBUG_DEVELOP, "[QuestieInit] Skipping validation - plugins are pending and will inject data after compilation")
print("\124cFF4DDBFF Validation skipped (plugins pending), load complete.")
elseif Questie.db.profile.skipValidation == true then
print("\124cFF4DDBFF Validation skipped, load complete.")
else
runValidator()
print("\124cFF4DDBFF Load and Validation complete.")
end
end
QuestieCleanup:Run()
end
QuestieInit.Stages[2] = function()
Questie:Debug(Questie.DEBUG_INFO, "[QuestieInit:Stage2] Stage 2 start.")
-- We do this while we wait for the Quest Cache anyway.
l10n:PostBoot()
QuestiePlayer:Initialize()
coYield()
QuestieJourney:Initialize()
local keepWaiting = true
-- We had users reporting that a quest did not reach a valid state in the game cache.
-- In this case we still need to continue the initialization process, even though a specific quest might be bugged
-- 30-second timeout for cache validation (increased from 10s for slow servers)
C_Timer.After(30, function()
if keepWaiting then
Questie:Debug(Questie.DEBUG_INFO, "[QuestieInit:Stage2] Quest cache validation timed out! Some data may still be loading.")
keepWaiting = false
local ok, err = coroutine.resume(QuestieInit.Thread)
if not ok then
Questie:Debug(Questie.DEBUG_INFO, "[QuestieInit:Stage2] Resume failed (timeout): " .. tostring(err))
end
end
end)
-- Continue to the next Init Stage once Game Cache's Questlog is good
while (not QuestieValidateGameCache:IsCacheGood()) and keepWaiting do
coYield()
end
keepWaiting = false
end
QuestieInit.Stages[3] = function() -- run as a coroutine
Questie:Debug(Questie.DEBUG_INFO, "[QuestieInit:Stage3] Stage 3 start.")
-- Wait for registered plugins to finish loading (ensure data injection is complete)
local QuestiePluginAPI = QuestieLoader:ImportModule("QuestiePluginAPI")
local waitStart = GetTime()
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieInit:Stage3] Waiting for plugins to register/finish. Initial pending: " .. QuestiePluginAPI.pendingPluginsCount)
-- Give DB plugins a short grace period only when a plugin addon is enabled
-- but none has registered yet. Avoid adding a fixed one-second delay to
-- every login after plugins are already registered.
if QuestieServer:IsAnyDBPluginEnabled() and (not QuestiePluginAPI:IsAnyPluginLoaded()) then
while (GetTime() - waitStart < 1.0) do
coYield()
end
end
local timeout = 10
local elapsed = GetTime() - waitStart
while QuestiePluginAPI:HasPendingPlugins() and (elapsed < timeout) do
coYield()
elapsed = GetTime() - waitStart
end
-- Recompile custom-server/plugin data only when the compiled cache no longer
-- matches the loaded plugin set. Learner data is applied as live overrides and
-- does not require rebuilding the binary DB cache on every login.
local isCustomServer = Questie.IsAscension or Questie.IsEbonhold or Questie.IsValanior or QuestieServer:IsAnyDBPluginEnabled()
local pluginSignature = QuestiePluginAPI:GetLoadedSignature()
local stage3CompiledMetadata, stage3CompiledRootMetadata = GetCompiledDBMetadata()
local cacheMatchesPlugins = stage3CompiledRootMetadata.dbCompiledPluginSignature == pluginSignature
or (Questie.db.global and Questie.db.global.dbCompiledPluginSignature == pluginSignature)
local cacheMatchesCore = stage3CompiledMetadata.dbIsCompiled
and (QuestieLib:GetAddonVersionString() == stage3CompiledMetadata.dbCompiledOnVersion)
and (l10n:GetUILocale() == stage3CompiledMetadata.dbCompiledLang)
and (stage3CompiledRootMetadata.dbCompiledExpansion == WOW_PROJECT_ID)
if needsCompilation or (not cacheMatchesCore) or (isCustomServer and (not cacheMatchesPlugins)) then
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieInit:Stage3] Starting compilation (Server=" .. tostring(isCustomServer) .. ", Needed=" .. tostring(needsCompilation) .. ")")
if not QuestieDB.questData then
loadFullDatabase()
end
QuestieDBCompiler:Compile()
QuestieDB:Initialize()
else
Questie:Debug(Questie.DEBUG_INFO, "[QuestieInit:Stage3] DB cache is current; skipping compilation.")
end
-- register events that rely on questie being initialized
QuestieEventHandler:RegisterLateEvents()
-- ** OLD ** Questie:ContinueInit() ** START **
QuestieTooltips:Initialize()
QuestieCoords:Initialize()
if QuestieArrow and QuestieArrow.Initialize then
QuestieArrow:Initialize()
else
Questie:Fatal("[QuestieArrow] Module not loaded correctly (missing Initialize).")
end
TrackerQuestTimers:Initialize()
QuestieComms:Initialize()
QuestieSlash.RegisterSlashCommands()
coYield()
if Questie.db.profile.dbmHUDEnable then
QuestieDBMIntegration:EnableHUD()
end
-- ** OLD ** Questie:ContinueInit() ** END **
coYield()
QuestEventHandler:RegisterEvents()
coYield()
ChatFilter:RegisterEvents()
QuestieMap:InitializeQueue()
coYield()
QuestieQuest:Initialize()
coYield()
WorldMapButton.Initialize()
coYield()
QuestieQuest:GetAllQuestIdsNoObjectives()
coYield()
Townsfolk.PostBoot()
coYield()
QuestieQuest:GetAllQuestIds()
-- Initialize the tracker
coYield()
QuestieTracker.Initialize()
Hooks:HookQuestLogTitle()
QuestieCombatQueue.Initialize()
local dateToday = date("%y-%m-%d")
if Questie.db.profile.showAQWarEffortQuests and ((not Questie.db.profile.aqWarningPrintDate) or (Questie.db.profile.aqWarningPrintDate < dateToday)) then
Questie.db.profile.aqWarningPrintDate = dateToday
C_Timer.After(2, function()
Questie:Print("|cffff0000-----------------------------|r")
Questie:Print(
"|cffff0000The AQ War Effort quests are shown for you. If your server is done you can hide those quests in the General settings of Questie!|r");
Questie:Print("|cffff0000-----------------------------|r")
end)
end
if Questie.IsTBC and (not Questie.db.global.isIsleOfQuelDanasPhaseReminderDisabled) then
C_Timer.After(2, function()
Questie:Print(l10n(
"Current active phase of Isle of Quel'Danas is '%s'. Check the General settings to change the phase or disable this message.",
IsleOfQuelDanas.localizedPhaseNames[Questie.db.global.isleOfQuelDanasPhase]))
end)
end
coYield()
QuestieMenu:OnLogin()
coYield()
if Questie.db.profile.debugEnabled then
QuestieLoader:PopulateGlobals()
end
Questie.started = true
if (Questie.IsWotlk or Questie.IsTBC) and QuestiePlayer.IsMaxLevel() then
local lastRequestWasYesterday = Questie.db.global.lastDailyRequestDate ~= date("%d-%m-%y"); -- Yesterday or some day before
local isPastDailyReset = Questie.db.global.lastDailyRequestResetTime < GetQuestResetTime();
if lastRequestWasYesterday or isPastDailyReset then
Questie.db.global.lastDailyRequestDate = date("%d-%m-%y");
Questie.db.global.lastDailyRequestResetTime = GetQuestResetTime();
end
end
-- We do this last because it will run for a while and we don't want to block the rest of the init
coYield()
AvailableQuests.CalculateAndDrawAll()
Questie:Debug(Questie.DEBUG_INFO, "[QuestieInit:Stage3] Questie init done.")
end
-- End of QuestieInit.Stages ******************************************************
-- ********************************************************************************
function QuestieInit:LoadDatabase(key)
local function MarkBaseDatabaseMissing()
QuestieDB.baseDatabaseMissing = true
QuestieDB.baseDatabaseMissingKeys = QuestieDB.baseDatabaseMissingKeys or {}
QuestieDB.baseDatabaseMissingKeys[key] = true
end
if type(QuestieDB[key]) == "string" then
-- Fix #6: `loadstring` at LOAD TIME is safe, but calling it here during
-- event-driven runtime taints any tables produced on WotLK/Era clients.
-- This path is for legacy single-file DB format. If we are on a
-- modern client (WOW_PROJECT_ID is defined natively and not ancient), refuse
-- and direct the user to reinstall the split-file DB instead.
local _, _, _, tocversion = GetBuildInfo()
local isModernClient = false
if tocversion then
if tocversion >= 100000 then isModernClient = true -- Retail
elseif tocversion >= 11300 and tocversion < 12000 then isModernClient = true -- Classic Era
elseif tocversion >= 20500 and tocversion < 30000 then isModernClient = true -- TBC Classic
elseif tocversion >= 30400 and tocversion < 40000 then isModernClient = true -- WotLK Classic
elseif tocversion >= 40400 and tocversion < 50000 then isModernClient = true -- Cata Classic
elseif tocversion >= 50000 and tocversion < 60000 then isModernClient = true -- MoP (e.g. 50400)
end
end
if isModernClient then
Questie:Debug(Questie.DEBUG_DEVELOP,
"[DBDiag] LEGACY DB ('" .. key .. "' is string) on modern client. "
.. "Runtime loadstring() would taint this data. "
.. "Please reinstall the Questie-X-WotLKDB addon in split-file format.")
MarkBaseDatabaseMissing()
QuestieDB[key] = {}
return
end
-- Lua 5.0 / ancient custom server: loadstring is the only option.
coYield()
local fn, loadErr = loadstring(QuestieDB[key])
coYield()
if fn then
local ok, result = pcall(fn)
if ok then
QuestieDB[key] = result
else
Questie:Debug(Questie.DEBUG_DEVELOP, "[DBDiag] ERROR executing('" .. key .. "'): " .. tostring(result))
MarkBaseDatabaseMissing()
QuestieDB[key] = nil
end
else
Questie:Debug(Questie.DEBUG_DEVELOP, "[DBDiag] ERROR loadstring('" .. key .. "'): " .. tostring(loadErr) .. " | len=" .. string.len(QuestieDB[key] or ""))
MarkBaseDatabaseMissing()
QuestieDB[key] = nil
end
elseif type(QuestieDB[key]) == "table" then
Questie:Debug(Questie.DEBUG_DEVELOP, "[LoadDatabase] '" .. key .. "' already a table (split-file format), skipping loadstring")
else
Questie:Debug(Questie.DEBUG_DEVELOP, "Database is missing, this is likely do to era vs tbc: ", key)
MarkBaseDatabaseMissing()
end
if not QuestieDB[key] then
QuestieDB[key] = {}
end
end
-- Stats are now managed via QuestiePluginAPI directly during injection.
-- Redundant UpdateWotLKDBStats removed to prevent duplication and inaccuracies.
function QuestieInit:LoadBaseDB()
-- Pointer compilation will look at npcDataOverrides etc, which are populated by plugins.
-- Base tables (Classic) are loaded here.
QuestieDB.baseDatabaseMissing = false
QuestieDB.baseDatabaseMissingKeys = {}
QuestieInit:LoadDatabase("npcData")
QuestieInit:LoadDatabase("objectData")
QuestieInit:LoadDatabase("questData")
QuestieInit:LoadDatabase("itemData")
end
function _QuestieInit.StartStageCoroutine()
for i = 1, #QuestieInit.Stages do
if i == 1 and QuestieInit.stage1Done then
Questie:Debug(Questie.DEBUG_INFO, "[QuestieInit:StartStageCoroutine] Stage 1 already done, skipping.")
else
if i == 1 then QuestieInit.stage1Done = true end
QuestieInit.Stages[i]()
Questie:Debug(Questie.DEBUG_INFO, "[QuestieInit:StartStageCoroutine] Stage " .. i .. " done.")
end
end
end
-- called by the PLAYER_LOGIN event handler
function QuestieInit:Init()
QuestieInit.Thread = coroutine.create(_QuestieInit.StartStageCoroutine)
local function resumeInit()
if not QuestieInit.Thread or coroutine.status(QuestieInit.Thread) == "dead" then
return -- coroutine finished or failed
end
local ok, err = coroutine.resume(QuestieInit.Thread)
if not ok then
local stack = debugstack(QuestieInit.Thread)
local msg = "QuestieInit Thread CRASHED: " .. tostring(err) .. "\n" .. tostring(stack)
Questie:Error(msg)
print("|cFFFF0000" .. msg .. "|r")
elseif coroutine.status(QuestieInit.Thread) ~= "dead" then
C_Timer.After(0.02, resumeInit) -- continue yielding using the timer shim
end
end
resumeInit()
if Questie.db.profile.trackerEnabled then
-- This needs to be called ASAP otherwise tracked Achievements in the Blizzard WatchFrame shows upon login
local WatchFrame = QuestTimerFrame or WatchFrame
if Questie.IsWotlk or QuestieCompat.Is335 then
-- Classic WotLK
WatchFrame:Hide()
else
-- Classic WoW: This moves the QuestTimerFrame off screen. A faux Hide().
-- Otherwise, if the frame is hidden then the OnUpdate doesn't work.
WatchFrame:ClearAllPoints()
WatchFrame:SetPoint("TOP", "UIParent", -10000, -10000)
end
if not (Questie.IsWotlk or QuestieCompat.Is335) then
-- Need to hook this ASAP otherwise the scroll bars show up
hooksecurefunc("ScrollFrame_OnScrollRangeChanged", function()
if TrackedQuestsScrollFrame then
TrackedQuestsScrollFrame.ScrollBar:Hide()
end
if QuestieProfilerScrollFrame then
QuestieProfilerScrollFrame.ScrollBar:Hide()
end
end)
end
end
end