diff --git a/scripts/mods/dmf/dmf_loader.lua b/scripts/mods/dmf/dmf_loader.lua
index ff0b3c9..99a2bf4 100644
--- a/scripts/mods/dmf/dmf_loader.lua
+++ b/scripts/mods/dmf/dmf_loader.lua
@@ -22,27 +22,25 @@ function dmf_mod_object:init()
dofile("scripts/mods/dmf/modules/core/misc")
dofile("scripts/mods/dmf/modules/core/persistent_tables")
dofile("scripts/mods/dmf/modules/core/io")
+ dofile("scripts/mods/dmf/modules/debug/dev_console")
+ dofile("scripts/mods/dmf/modules/debug/table_dump")
+ dofile("scripts/mods/dmf/modules/core/hooks")
+ dofile("scripts/mods/dmf/modules/core/require")
+ dofile("scripts/mods/dmf/modules/core/toggling")
+ dofile("scripts/mods/dmf/modules/core/keybindings")
+ dofile("scripts/mods/dmf/modules/core/chat")
+ dofile("scripts/mods/dmf/modules/core/localization")
+ dofile("scripts/mods/dmf/modules/core/options")
+ dofile("scripts/mods/dmf/modules/core/network")
+ dofile("scripts/mods/dmf/modules/core/commands")
+ dofile("scripts/mods/dmf/modules/gui/custom_textures")
+ dofile("scripts/mods/dmf/modules/gui/custom_views")
+ dofile("scripts/mods/dmf/modules/ui/chat/chat_actions")
+ dofile("scripts/mods/dmf/modules/ui/options/mod_options")
+ dofile("scripts/mods/dmf/modules/dmf_options")
+ dofile("scripts/mods/dmf/modules/core/mutators/mutators_manager")
dmf = get_mod("DMF")
-
- dmf:dofile("scripts/mods/dmf/modules/debug/dev_console")
- dmf:dofile("scripts/mods/dmf/modules/debug/table_dump")
- dmf:dofile("scripts/mods/dmf/modules/core/hooks")
- dmf:dofile("scripts/mods/dmf/modules/core/require")
- dmf:dofile("scripts/mods/dmf/modules/core/toggling")
- dmf:dofile("scripts/mods/dmf/modules/core/keybindings")
- dmf:dofile("scripts/mods/dmf/modules/core/chat")
- dmf:dofile("scripts/mods/dmf/modules/core/localization")
- dmf:dofile("scripts/mods/dmf/modules/core/options")
- dmf:dofile("scripts/mods/dmf/modules/core/network")
- dmf:dofile("scripts/mods/dmf/modules/core/commands")
- dmf:dofile("scripts/mods/dmf/modules/gui/custom_textures")
- dmf:dofile("scripts/mods/dmf/modules/gui/custom_views")
- dmf:dofile("scripts/mods/dmf/modules/ui/chat/chat_actions")
- dmf:dofile("scripts/mods/dmf/modules/ui/options/mod_options")
- dmf:dofile("scripts/mods/dmf/modules/dmf_options")
- dmf:dofile("scripts/mods/dmf/modules/core/mutators/mutators_manager")
-
dmf.delayed_chat_messages_hook()
end
diff --git a/scripts/mods/dmf/modules/core/io.lua b/scripts/mods/dmf/modules/core/io.lua
index b9308ab..3e5e3ca 100644
--- a/scripts/mods/dmf/modules/core/io.lua
+++ b/scripts/mods/dmf/modules/core/io.lua
@@ -95,7 +95,7 @@ local function handle_io(mod, local_path, file_name, file_extension, args, safe_
-- If this is a safe call, wrap it in a pcall
if safe_call then
- status, result = pcall(function ()
+ status, result = pcall(function()
return read_or_execute(file_path, args, return_type)
end)
@@ -114,15 +114,50 @@ local function handle_io(mod, local_path, file_name, file_extension, args, safe_
-- If the initial open failed, report failure
else
- mod:error("Error opening '" .. file_path .. "': " .. tostring(err_io))
+ mod:error("Error during I/O: %s\n%s", tostring(err_io), Script.callstack())
return false
end
end
+
-- #####################################################################################################################
-- ##### DMFMod ########################################################################################################
-- #####################################################################################################################
+-- Use the io library to execute the given file with a pcall, without return
+function DMFMod:io_exec(local_path, file_name, file_extension, args)
+ return handle_io(self, local_path, file_name, file_extension, args, true, "exec_boolean")
+end
+
+-- Use the io library to execute the given file without a pcall, without return
+function DMFMod:io_exec_unsafe(local_path, file_name, file_extension, args)
+ return handle_io(self, local_path, file_name, file_extension, args, false, "exec_boolean")
+end
+
+-- Use the io library to execute the given file with a pcall and return the result
+function DMFMod:io_exec_with_return(local_path, file_name, file_extension, args)
+ return handle_io(self, local_path, file_name, file_extension, args, true, "exec_result")
+end
+
+-- Use the io library to execute the given file without a pcall and return the result
+function DMFMod:io_exec_unsafe_with_return(local_path, file_name, file_extension, args)
+ return handle_io(self, local_path, file_name, file_extension, args, false, "exec_result")
+end
+
+-- Use the io library to execute the given file with a pcall and return the result,
+-- but treat the first parameter as the entire path to the file, and assume .lua.
+-- IO version of the dofile method with a pcall.
+function DMFMod:io_dofile(file_path)
+ return handle_io(self, file_path, nil, nil, nil, true, "exec_result")
+end
+
+-- Use the io library to execute the given file without a pcall and return the result,
+-- but treat the first parameter as the entire path to the file, and assume .lua.
+-- IO version of the dofile method.
+function DMFMod:io_dofile_unsafe(file_path)
+ return handle_io(self, file_path, nil, nil, nil, false, "exec_result")
+end
+
-- Use the io library to return the contents of the given file
function DMFMod:io_read_content(file_path, file_extension)
return handle_io(self, file_path, nil, file_extension, nil, true, "data")
diff --git a/scripts/mods/dmf/modules/core/require.lua b/scripts/mods/dmf/modules/core/require.lua
index aed69b8..a52feb4 100644
--- a/scripts/mods/dmf/modules/core/require.lua
+++ b/scripts/mods/dmf/modules/core/require.lua
@@ -35,24 +35,63 @@ end
-- ##### DMFMod ########################################################################################################
-- #####################################################################################################################
--- Add a file path to be loaded through io instead of require()
+--- Loads the given file with the same semantics as Lua's `require`.
+---
+--- This provides a unified API for both bundled and non-bundled mods.
+---
+--- @param path string The file to load
+function DMFMod:require(path)
+ local is_bundled = self:get_internal_data("is_bundled")
+
+ if is_bundled then
+ return require(path)
+ else
+ local loaded = self:io_dofile_unsafe(path)
+ if loaded then
+ package.loaded[path] = loaded
+ end
+ return loaded
+ end
+end
+
+--- Loads the given file with the same semantics as Lua's `dofile`.
+---
+--- This provides a unified API for both bundled and non-bundled mods.
+---
+--- @param path string The file to load
+function DMFMod:dofile(path)
+ local is_bundled = self:get_internal_data("is_bundled")
+
+ if is_bundled then
+ return dofile(path)
+ else
+ return dmf.io_dofile_unsafe(self, path)
+ end
+end
+
+-- Add a file path to be loaded through `io` instead of `require`.
+--
+-- Certain game systems will be given a path value and then call `require`
+-- internally, where a mod cannot easily hook and replace the call.
+--
+-- This function allows non-bundled mods to inject a file such that these systems
+-- can `require` them without additional hooks.
+--
+-- Bundled mods already have all their files available through regular `require`.
function DMFMod:add_require_path(path)
add_io_require_path(path)
end
-
-- Remove a file path that was previously loaded through io instead of require()
function DMFMod:remove_require_path(path)
remove_io_require_path(path)
end
-
-- Get all instances of a file created through require()
function DMFMod:get_require_store(path)
return get_require_store(path)
end
-
-- Get a file through the original, unhooked require() function
function DMFMod:original_require(path, ...)
return original_require(path, ...)
@@ -63,7 +102,7 @@ end
-- #####################################################################################################################
-- Handles the swap to io for registered files and the application of file hooks
-dmf:hook(_G, "require", function (func, path, ...)
+dmf:hook(_G, "require", function(func, path, ...)
if _io_requires[path] then
return dmf:dofile(path)
else
diff --git a/scripts/mods/dmf/modules/core/safe_calls.lua b/scripts/mods/dmf/modules/core/safe_calls.lua
index c8683ed..e2b2777 100644
--- a/scripts/mods/dmf/modules/core/safe_calls.lua
+++ b/scripts/mods/dmf/modules/core/safe_calls.lua
@@ -8,21 +8,22 @@ local print = __print
-- #####################################################################################################################
local function pack_pcall(status, ...)
- return status, {n = select('#', ...), ...}
+ return status, { n = select('#', ...), ... }
end
-local function print_error_callstack(error_message)
- if type(error_message) == "table" and error_message.error then
- print(string.format(
- "<>\n<>%s<>\n<>%s<>\n<>%s<>",
- error_message.error, error_message.traceback, error_message.locals, error_message.self
- ))
+local function print_error_callstack(err)
+ if type(err) == "table" and err.error then
+ Log.error(
+ "DMF",
+ "%s\n<>%s<>\n<>%s<>\n<>%s<>",
+ err.error, err.traceback, err.locals, err.self
+ )
else
- print("Error: " .. tostring(error_message) .. "\n" .. Script.callstack())
+ Log.error("DMF", "Error: %s\n%s", tostring(err), Script.callstack())
end
- return error_message
+ return err
end
@@ -45,12 +46,6 @@ function DMFMod:pcall(...)
return dmf.safe_call(self, "(pcall)", ...)
end
-
-function DMFMod:dofile(file_path)
- local _, return_values = pack_pcall(dmf.safe_call_dofile(self, "(dofile)", file_path))
- return unpack(return_values, 1, return_values.n)
-end
-
-- #####################################################################################################################
-- ##### DMF internal functions and variables ##########################################################################
-- #####################################################################################################################
@@ -65,7 +60,6 @@ function dmf.safe_call(mod, error_prefix_data, func, ...)
return success, unpack(return_values, 1, return_values.n)
end
-
-- Safe Call [No return values]
function dmf.safe_call_nr(mod, error_prefix_data, func, ...)
local success, error_message = xpcall(func, print_error_callstack, ...)
@@ -75,7 +69,6 @@ function dmf.safe_call_nr(mod, error_prefix_data, func, ...)
return success
end
-
-- Safe Call [No return values and error callstack]
function dmf.safe_call_nrc(mod, error_prefix_data, func, ...)
local success, error_message = pcall(func, ...)
@@ -85,7 +78,6 @@ function dmf.safe_call_nrc(mod, error_prefix_data, func, ...)
return success
end
-
-- Safe Call [dofile]
function dmf.safe_call_dofile(mod, error_prefix_data, file_path)
if type(file_path) ~= "string" then
@@ -95,6 +87,14 @@ function dmf.safe_call_dofile(mod, error_prefix_data, file_path)
return dmf.safe_call(mod, error_prefix_data, dofile, file_path)
end
+-- Safe Call [io_dofile]
+function dmf.safe_call_io_dofile(mod, error_prefix_data, file_path)
+ if type(file_path) ~= "string" then
+ show_error(mod, error_prefix_data, "file path should be a string.")
+ return false
+ end
+ return dmf.safe_call(mod, error_prefix_data, mod.io_dofile_unsafe, mod, file_path)
+end
-- Format error message and throw error.
function dmf.throw_error(error_message, ...)
diff --git a/scripts/mods/dmf/modules/dmf_mod_data.lua b/scripts/mods/dmf/modules/dmf_mod_data.lua
index 048d299..43baaef 100644
--- a/scripts/mods/dmf/modules/dmf_mod_data.lua
+++ b/scripts/mods/dmf/modules/dmf_mod_data.lua
@@ -24,7 +24,7 @@ function DMFMod:init(mod_name)
self._data = setmetatable({}, {
__index = {},
- __newindex = function(t_, k)
+ __newindex = function(_, k)
self:warning("Attempt to change internal mod data value (\"%s\"). Changing internal mod data is forbidden.", k)
end
})
@@ -37,7 +37,8 @@ function DMFMod:init(mod_name)
local vanilla_mod_data = Managers.mod:mod_data(mod_name)
set_internal_data(self, "workshop_id", vanilla_mod_data.id)
set_internal_data(self, "workshop_name", vanilla_mod_data.name)
- set_internal_data(self, "mod_handle", vanilla_mod_data.handle)
+ set_internal_data(self, "mod_handle", vanilla_mod_data.handle)
+ set_internal_data(self, "is_bundled", vanilla_mod_data.bundled or false)
print(string.format("Init DMF mod '%s' [workshop_name: '%s', workshop_id: %s]", mod_name, vanilla_mod_data.name,
vanilla_mod_data.id))
diff --git a/scripts/mods/dmf/modules/dmf_mod_manager.lua b/scripts/mods/dmf/modules/dmf_mod_manager.lua
index 5399aab..31bc1f5 100644
--- a/scripts/mods/dmf/modules/dmf_mod_manager.lua
+++ b/scripts/mods/dmf/modules/dmf_mod_manager.lua
@@ -50,7 +50,11 @@ local function resolve_resource(mod, error_prefix_data, resource, resource_value
local type_value = type(resource_value)
if type_value == "string" then
- return dmf.safe_call_dofile(mod, error_prefix_data, resource_value)
+ if mod:get_internal_data("is_bundled") then
+ return dmf.safe_call_dofile(mod, error_prefix_data, resource_value)
+ else
+ return dmf.safe_call_io_dofile(mod, error_prefix_data, resource_value)
+ end
elseif type_value == "function" then
return dmf.safe_call(mod, error_prefix_data, resource_value, mod)
elseif type_value == "table" then
@@ -131,7 +135,6 @@ function new_mod(mod_name, mod_resources)
return mod
end
-
function get_mod(mod_name)
return _mods[mod_name]
end