Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions modules/corelib/ui/uipathresolver.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
-- Resolves module-relative UI paths in Lua before they reach the C++ layer.
--
-- Root cause being worked around: ResourceManager::resolvePath() resolves a
-- relative path against g_lua.getCurrentSourcePath(), but when the call happens
-- inside a callback (UI button, protocol packet, scheduled event) that context
-- can be unavailable — and the isPreDrawing() branch skips it entirely — so
-- 'file' resolves to '/file.otui' (the root) and the load fails. This breaks
-- every g_ui.loadUI/displayUI/importStyle('barename') call made from a
-- callback (imbuing, outfit, console channels, VIP add/edit, shop, stash,
-- character list, ...), while the same call works from a module's init().
--
-- The fix: wrap the three entry points and, when the argument is a relative
-- path, resolve it against the *calling file's* directory (debug.getinfo),
-- but only if the target file actually exists there. In every other case the
-- argument is forwarded untouched, so behavior is identical to before.

local function resolveCallerPath(path)
if type(path) ~= 'string' or path == '' or path:sub(1, 1) == '/' then
return path
end

-- level 3: 1 = resolveCallerPath, 2 = wrapper, 3 = caller
local info = debug.getinfo(3, 'S')
local src = info and info.source
if not src or src:sub(1, 1) ~= '@' then
return path
end

src = src:sub(2)
if src:sub(1, 1) ~= '/' then
return path
end

local dir = src:match('^(.*)/[^/]*$')
if not dir or dir == '' then
return path
end

local candidate = dir .. '/' .. path
-- mirror g_resources.guessFilePath(candidate, 'otui')
local candidateFile = candidate
if candidate:sub(-5) ~= '.otui' then
candidateFile = candidate .. '.otui'
end

if g_resources.fileExists(candidateFile) then
return candidate
end
return path
end

if not g_ui.__pathResolverInstalled then
g_ui.__pathResolverInstalled = true

local origLoadUI = g_ui.loadUI
local origDisplayUI = g_ui.displayUI
local origImportStyle = g_ui.importStyle

g_ui.loadUI = function(path, ...)
return origLoadUI(resolveCallerPath(path), ...)
end

g_ui.displayUI = function(path, ...)
return origDisplayUI(resolveCallerPath(path), ...)
end

g_ui.importStyle = function(path, ...)
return origImportStyle(resolveCallerPath(path), ...)
end
end
Loading