chore: Import base mod from DMF
Co-authored-by: Aussiemon <mattrohrlach+github@gmail.com>
This commit is contained in:
parent
a67c28ca94
commit
e369e692af
5 changed files with 347 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
dml.zip
|
22
scripts/mods/dml/class.lua
Normal file
22
scripts/mods/dml/class.lua
Normal file
|
@ -0,0 +1,22 @@
|
|||
Mods.original_class = Mods.original_class or class
|
||||
|
||||
local _G = _G
|
||||
local rawget = rawget
|
||||
local rawset = rawset
|
||||
|
||||
_G.CLASS = _G.CLASS or setmetatable({}, {
|
||||
__index = function(_, key)
|
||||
return key
|
||||
end
|
||||
})
|
||||
|
||||
class = function(class_name, super_name, ...)
|
||||
local result = Mods.original_class(class_name, super_name, ...)
|
||||
if not rawget(_G, class_name) then
|
||||
rawset(_G, class_name, result)
|
||||
end
|
||||
if not rawget(_G.CLASS, class_name) then
|
||||
rawset(_G.CLASS, class_name, result)
|
||||
end
|
||||
return result
|
||||
end
|
274
scripts/mods/dml/hook.lua
Normal file
274
scripts/mods/dml/hook.lua
Normal file
|
@ -0,0 +1,274 @@
|
|||
--[[
|
||||
Mods Hook v2:
|
||||
New version with better control
|
||||
--]]
|
||||
|
||||
-- Hook structure
|
||||
MODS_HOOKS = MODS_HOOKS or {}
|
||||
MODS_HOOKS_BY_FILE = MODS_HOOKS_BY_FILE or {}
|
||||
|
||||
local item_template = {
|
||||
name = "",
|
||||
func = EMPTY_FUNC,
|
||||
hooks = {},
|
||||
}
|
||||
|
||||
local item_hook_template = {
|
||||
name = "",
|
||||
func = EMPTY_FUNC,
|
||||
enable = false,
|
||||
exec = EMPTY_FUNC,
|
||||
}
|
||||
local Log = Log
|
||||
|
||||
local print_log_info = function(mod_name, message)
|
||||
Log = Log or rawget(_G, "Log")
|
||||
if Log then
|
||||
Log._info(mod_name, message)
|
||||
else
|
||||
print("[" .. mod_name .. "]: " .. message)
|
||||
end
|
||||
end
|
||||
|
||||
local print_log_warning = function(mod_name, message)
|
||||
Log = Log or rawget(_G, "Log")
|
||||
if Log then
|
||||
Log._warning(mod_name, message)
|
||||
else
|
||||
print("[" .. mod_name .. "]: " .. message)
|
||||
end
|
||||
end
|
||||
|
||||
Mods.hook = {
|
||||
--
|
||||
-- Set hook
|
||||
--
|
||||
set = function(mod_name, func_name, hook_func)
|
||||
local item = Mods.hook._get_item(func_name)
|
||||
local item_hook = Mods.hook._get_item_hook(item, mod_name)
|
||||
|
||||
print_log_info(mod_name, "Hooking " .. func_name)
|
||||
|
||||
item_hook.enable = true
|
||||
item_hook.func = hook_func
|
||||
|
||||
Mods.hook._patch()
|
||||
end,
|
||||
|
||||
--
|
||||
-- Set hook on every instance of the given file
|
||||
--
|
||||
set_on_file = function(mod_name, filepath, func_name, hook_func)
|
||||
-- Add hook create function to list for the file
|
||||
MODS_HOOKS_BY_FILE[filepath] = MODS_HOOKS_BY_FILE[filepath] or {}
|
||||
local hook_create_func = function(this_filepath, this_index)
|
||||
local dynamic_func_name = "Mods.require_store[\"" .. this_filepath .. "\"][" .. tostring(this_index) .. "]." .. func_name
|
||||
Mods.hook.set(mod_name, dynamic_func_name, hook_func, false)
|
||||
end
|
||||
table.insert(MODS_HOOKS_BY_FILE[filepath], hook_create_func)
|
||||
|
||||
-- Add the new hook to every instance of the file
|
||||
local all_file_instances = Mods.require_store[filepath]
|
||||
if all_file_instances then
|
||||
for i, item in ipairs(all_file_instances) do
|
||||
if item then
|
||||
hook_create_func(filepath, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
--
|
||||
-- Enable/Disable hook
|
||||
--
|
||||
enable = function(value, mod_name, func_name)
|
||||
for _, item in ipairs(MODS_HOOKS) do
|
||||
if item.name == func_name or func_name == nil then
|
||||
for _, hook in ipairs(item.hooks) do
|
||||
if hook.name == mod_name then
|
||||
hook.enable = value
|
||||
Mods.hook._patch()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end,
|
||||
|
||||
--
|
||||
-- Enable all hooks on a stored file
|
||||
--
|
||||
enable_by_file = function(filepath, store_index)
|
||||
local all_file_instances = Mods.require_store[filepath]
|
||||
local file_instance = all_file_instances and all_file_instances[store_index]
|
||||
|
||||
local all_file_hooks = MODS_HOOKS_BY_FILE[filepath]
|
||||
|
||||
if all_file_hooks and file_instance then
|
||||
for i, hook_create_func in ipairs(all_file_hooks) do
|
||||
hook_create_func(filepath, store_index)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
--
|
||||
-- Remove hook from chain
|
||||
--
|
||||
["remove"] = function(func_name, mod_name)
|
||||
for i, item in ipairs(MODS_HOOKS) do
|
||||
if item.name == func_name then
|
||||
if mod_name ~= nil then
|
||||
for j, hook in ipairs(item.hooks) do
|
||||
if hook.name == mod_name then
|
||||
table.remove(item.hooks, j)
|
||||
|
||||
Mods.hook._patch()
|
||||
end
|
||||
end
|
||||
else
|
||||
local item_name = "MODS_HOOKS[" .. tostring(i) .. "]"
|
||||
|
||||
-- Restore orginal function
|
||||
assert(loadstring(item.name .. " = " .. item_name .. ".func"))()
|
||||
|
||||
-- Remove hook function
|
||||
table.remove(MODS_HOOKS, i)
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end,
|
||||
|
||||
--
|
||||
-- Move hook to front of the hook chain
|
||||
--
|
||||
front = function(mod_name, func_name)
|
||||
for _, item in ipairs(MODS_HOOKS) do
|
||||
if item.name == func_name or func_name == nil then
|
||||
for i, hook in ipairs(item.hooks) do
|
||||
if hook.name == mod_name then
|
||||
local saved_hook = table.clone(hook)
|
||||
table.remove(item.hooks, i)
|
||||
table.insert(item.hooks, saved_hook)
|
||||
|
||||
Mods.hook._patch()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end,
|
||||
|
||||
--
|
||||
-- Get function by function name
|
||||
--
|
||||
_get_func = function(func_name)
|
||||
return assert(loadstring("return " .. func_name))()
|
||||
end,
|
||||
|
||||
--
|
||||
-- Get item by function name
|
||||
--
|
||||
_get_item = function(func_name)
|
||||
-- Find existing item
|
||||
for _, item in ipairs(MODS_HOOKS) do
|
||||
if item.name == func_name then
|
||||
return item
|
||||
end
|
||||
end
|
||||
|
||||
-- Create new item
|
||||
local item = table.clone(item_template)
|
||||
item.name = func_name
|
||||
item.func = Mods.hook._get_func(func_name)
|
||||
|
||||
-- Save
|
||||
table.insert(MODS_HOOKS, item)
|
||||
|
||||
return item
|
||||
end,
|
||||
|
||||
--
|
||||
-- Get item hook by mod name
|
||||
--
|
||||
_get_item_hook = function(item, mod_name)
|
||||
-- Find existing item
|
||||
for _, hook in ipairs(item.hooks) do
|
||||
if hook.name == mod_name then
|
||||
return hook
|
||||
end
|
||||
end
|
||||
|
||||
-- Create new item
|
||||
local item_hook = table.clone(item_hook_template)
|
||||
item_hook.name = mod_name
|
||||
|
||||
-- Save
|
||||
table.insert(item.hooks, 1, item_hook)
|
||||
|
||||
return item_hook
|
||||
end,
|
||||
|
||||
--
|
||||
-- If settings are changed the hook itself needs to be updated
|
||||
--
|
||||
_patch = function(mods_hook_item)
|
||||
for i, item in ipairs(MODS_HOOKS) do
|
||||
local item_name = "MODS_HOOKS[" .. i .. "]"
|
||||
|
||||
local last_j = 1
|
||||
for j, hook in ipairs(item.hooks) do
|
||||
local hook_name = item_name .. ".hooks[" .. j .. "]"
|
||||
local before_hook_name = item_name .. ".hooks[" .. (j - 1) .. "]"
|
||||
|
||||
if j == 1 then
|
||||
if hook.enable then
|
||||
assert(
|
||||
loadstring(
|
||||
hook_name .. ".exec = function(...)" ..
|
||||
" return " .. hook_name .. ".func(" .. item_name .. ".func, ...)" ..
|
||||
"end"
|
||||
)
|
||||
)()
|
||||
else
|
||||
assert(
|
||||
loadstring(
|
||||
hook_name .. ".exec = function(...)" ..
|
||||
" return " .. item_name .. ".func(...)" ..
|
||||
"end"
|
||||
)
|
||||
)()
|
||||
end
|
||||
else
|
||||
if hook.enable then
|
||||
assert(
|
||||
loadstring(
|
||||
hook_name .. ".exec = function(...)" ..
|
||||
" return " .. hook_name .. ".func(" .. before_hook_name .. ".exec, ...)" ..
|
||||
"end"
|
||||
)
|
||||
)()
|
||||
else
|
||||
assert(
|
||||
loadstring(
|
||||
hook_name .. ".exec = function(...)" ..
|
||||
" return " .. before_hook_name .. ".exec(...)" ..
|
||||
"end"
|
||||
)
|
||||
)()
|
||||
end
|
||||
end
|
||||
|
||||
last_j = j
|
||||
end
|
||||
|
||||
-- Patch orginal function call
|
||||
assert(loadstring(item.name .. " = " .. item_name .. ".hooks[" .. last_j .. "].exec"))()
|
||||
end
|
||||
end,
|
||||
}
|
|
@ -1 +1,16 @@
|
|||
-- The loader object that is used during game boot
|
||||
-- to initialize the modding environment.
|
||||
local loader = {}
|
||||
|
||||
Mods = {
|
||||
hook = {},
|
||||
lua = setmetatable({}, {
|
||||
__index = { debug = debug, io = io, ffi = ffi, os = os },
|
||||
}),
|
||||
}
|
||||
|
||||
dofile("scripts/mods/dml/require")
|
||||
dofile("scripts/mods/dml/class")
|
||||
dofile("scripts/mods/dml/hook")
|
||||
|
||||
return loader
|
||||
|
|
35
scripts/mods/dml/require.lua
Normal file
35
scripts/mods/dml/require.lua
Normal file
|
@ -0,0 +1,35 @@
|
|||
Mods.require_store = Mods.require_store or {}
|
||||
Mods.original_require = Mods.original_require or require
|
||||
|
||||
local can_insert = function(filepath, new_result)
|
||||
local store = Mods.require_store[filepath]
|
||||
if not store or #store == 0 then
|
||||
return true
|
||||
end
|
||||
|
||||
if store[#store] ~= new_result then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
require = function(filepath, ...)
|
||||
local Mods = Mods
|
||||
|
||||
local result = Mods.original_require(filepath, ...)
|
||||
if result and type(result) == "table" then
|
||||
|
||||
if can_insert(filepath, result) then
|
||||
Mods.require_store[filepath] = Mods.require_store[filepath] or {}
|
||||
local store = Mods.require_store[filepath]
|
||||
|
||||
table.insert(store, result)
|
||||
|
||||
--print("[Require] #" .. tostring(#store) .. " of " .. filepath)
|
||||
if Mods.hook then
|
||||
Mods.hook.enable_by_file(filepath, #store)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
Loading…
Add table
Reference in a new issue