chore: Rework project structure
Initialize as new DTMT-based project.
This commit is contained in:
parent
f2b1609320
commit
97a29c03f1
13 changed files with 63 additions and 1246 deletions
44
.luacheckrc
Normal file
44
.luacheckrc
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
max_line_length = 120
|
||||||
|
|
||||||
|
include_files = {
|
||||||
|
"scripts/",
|
||||||
|
}
|
||||||
|
|
||||||
|
ignore = {
|
||||||
|
"12.", -- ignore "Setting a read-only global variable/Setting a read-only field of a global variable."
|
||||||
|
"542", -- disable warnings for empty if branches. These are useful sometime and easy to notice otherwise.
|
||||||
|
"212/self", -- Disable unused self warnings.
|
||||||
|
}
|
||||||
|
|
||||||
|
std = "+DT"
|
||||||
|
|
||||||
|
stds["DT"] = {
|
||||||
|
read_globals = {
|
||||||
|
string = { fields = { "split" }},
|
||||||
|
table = { fields = {
|
||||||
|
"merge", "table_to_array", "mirror_table", "tostring", "is_empty", "array_to_table", "reverse", "shuffle",
|
||||||
|
"merge_recursive", "unpack_map", "remove_unordered_items", "append", "mirror_array_inplace", "size", "dump",
|
||||||
|
"clear_array", "append_varargs", "find", "for_each", "crop", "mirror_array", "set", "create_copy", "clone",
|
||||||
|
"contains", "add_meta_logging", "table_as_sorted_string_arrays", "clone_instance", "max", "clear", "find_by_key",
|
||||||
|
}},
|
||||||
|
math = { fields = {
|
||||||
|
"ease_exp", "lerp", "polar_to_cartesian", "smoothstep", "easeCubic", "round", "point_is_inside_2d_triangle",
|
||||||
|
"radians_to_degrees", "circular_to_square_coordinates", "uuid", "easeInCubic", "round_with_precision",
|
||||||
|
"clamp", "get_uniformly_random_point_inside_sector", "angle_lerp", "ease_out_exp", "rand_normal",
|
||||||
|
"bounce", "point_is_inside_2d_box", "catmullrom", "clamp_direction", "ease_in_exp", "random_seed",
|
||||||
|
"sign", "degrees_to_radians", "sirp", "ease_pulse", "cartesian_to_polar", "ease_out_quad",
|
||||||
|
"easeOutCubic", "radian_lerp", "auto_lerp", "rand_utf8_string", "point_is_inside_oobb",
|
||||||
|
}},
|
||||||
|
Managers = { fields = {
|
||||||
|
"mod", "event", "chat"
|
||||||
|
}},
|
||||||
|
Mods = { fields = {
|
||||||
|
lua = { fields = { "debug", "io", "ffi", "os" }},
|
||||||
|
"original_require",
|
||||||
|
"require_store",
|
||||||
|
}},
|
||||||
|
"Crashify","Keyboard","Mouse","Application","Color","Quarternion","Vector3","Vector2","RESOLUTION_LOOKUP",
|
||||||
|
"ModManager", "Utf8", "StateGame", "ResourcePackage", "class", "Gui", "fassert", "printf", "__print", "ffi",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -1,475 +0,0 @@
|
||||||
local mod_directory = "./../mods"
|
|
||||||
|
|
||||||
local assert = assert
|
|
||||||
local ipairs = ipairs
|
|
||||||
local loadstring = loadstring
|
|
||||||
local pairs = pairs
|
|
||||||
local pcall = pcall
|
|
||||||
local print = print
|
|
||||||
local rawget = rawget
|
|
||||||
local rawset = rawset
|
|
||||||
local select = select
|
|
||||||
local setmetatable = setmetatable
|
|
||||||
local string = string
|
|
||||||
local table = table
|
|
||||||
local tonumber = tonumber
|
|
||||||
local tostring = tostring
|
|
||||||
|
|
||||||
-- Mod initialization code --
|
|
||||||
local debug = rawget(_G, "debug")
|
|
||||||
local io = rawget(_G, "io")
|
|
||||||
local ffi = require("ffi")
|
|
||||||
|
|
||||||
Mods = {
|
|
||||||
file = {},
|
|
||||||
message = {},
|
|
||||||
lua = {
|
|
||||||
debug = debug,
|
|
||||||
io = io,
|
|
||||||
ffi = ffi,
|
|
||||||
loadstring = loadstring,
|
|
||||||
os = os
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
local chat_sound = "wwise/events/ui/play_ui_click"
|
|
||||||
|
|
||||||
local notify = function(message)
|
|
||||||
local event_manager = Managers and Managers.event
|
|
||||||
|
|
||||||
if event_manager then
|
|
||||||
event_manager:trigger("event_add_notification_message", "default", message, nil, chat_sound)
|
|
||||||
end
|
|
||||||
|
|
||||||
print(message)
|
|
||||||
end
|
|
||||||
Mods.message.notify = notify
|
|
||||||
|
|
||||||
local echo = function(message, sender)
|
|
||||||
local chat_manager = Managers and Managers.chat
|
|
||||||
local event_manager = Managers and Managers.event
|
|
||||||
|
|
||||||
if chat_manager and event_manager then
|
|
||||||
local message_obj = {
|
|
||||||
message_body = message,
|
|
||||||
is_current_user = false,
|
|
||||||
}
|
|
||||||
|
|
||||||
local participant = {
|
|
||||||
displayname = sender or "SYSTEM",
|
|
||||||
}
|
|
||||||
|
|
||||||
local message_sent = false
|
|
||||||
|
|
||||||
local channel_handle, channel = next(chat_manager:connected_chat_channels())
|
|
||||||
if channel then
|
|
||||||
event_manager:trigger("chat_manager_message_recieved", channel_handle, participant, message_obj)
|
|
||||||
message_sent = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if not message_sent then
|
|
||||||
notify(message)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
print(message)
|
|
||||||
end
|
|
||||||
Mods.message.echo = echo
|
|
||||||
|
|
||||||
local get_file_path = function(local_path, file_name, file_extension)
|
|
||||||
local file_path = mod_directory
|
|
||||||
|
|
||||||
if local_path and local_path ~= "" then
|
|
||||||
file_path = file_path .. "/" .. local_path
|
|
||||||
end
|
|
||||||
|
|
||||||
if file_name and file_name ~= "" then
|
|
||||||
file_path = file_path .. "/" .. file_name
|
|
||||||
end
|
|
||||||
|
|
||||||
if file_extension and file_extension ~= "" then
|
|
||||||
file_path = file_path .. "." .. file_extension
|
|
||||||
else
|
|
||||||
file_path = file_path .. ".lua"
|
|
||||||
end
|
|
||||||
|
|
||||||
if string.find(file_path, "\\") then
|
|
||||||
file_path = string.gsub(file_path, "\\", "/")
|
|
||||||
end
|
|
||||||
|
|
||||||
return file_path
|
|
||||||
end
|
|
||||||
|
|
||||||
local function read_or_execute(file_path, args, return_type)
|
|
||||||
local f = io.open(file_path, "r")
|
|
||||||
|
|
||||||
local result
|
|
||||||
if return_type == "lines" then
|
|
||||||
result = {}
|
|
||||||
for line in f:lines() do
|
|
||||||
if line then
|
|
||||||
-- Trim whitespace
|
|
||||||
line = line:gsub("^%s*(.-)%s*$", "%1")
|
|
||||||
|
|
||||||
-- Handle empty lines and single-line comments
|
|
||||||
if line ~= "" and line:sub(1, 2) ~= "--" then
|
|
||||||
table.insert(result, line)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
result = f:read("*all")
|
|
||||||
|
|
||||||
-- Either execute the data or leave it unmodified
|
|
||||||
if return_type == "exec_result" or return_type == "exec_boolean" then
|
|
||||||
local func = loadstring(result, file_path)
|
|
||||||
result = func(args)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
f:close()
|
|
||||||
if return_type == "exec_boolean" then
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function handle_io(local_path, file_name, file_extension, args, safe_call, return_type)
|
|
||||||
|
|
||||||
local file_path = get_file_path(local_path, file_name, file_extension)
|
|
||||||
print("[Mod] Loading " .. file_path)
|
|
||||||
|
|
||||||
-- Check for the existence of the path
|
|
||||||
local ff, err_io = io.open(file_path, "r")
|
|
||||||
if ff ~= nil then
|
|
||||||
ff:close()
|
|
||||||
|
|
||||||
-- Initialize variables
|
|
||||||
local status, result
|
|
||||||
|
|
||||||
-- If this is a safe call, wrap it in a pcall
|
|
||||||
if safe_call then
|
|
||||||
status, result = pcall(function ()
|
|
||||||
return read_or_execute(file_path, args, return_type)
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- If status is failed, notify the user and return false
|
|
||||||
if not status then
|
|
||||||
notify("[Mod] Error processing '" .. file_path .. "': " .. tostring(result))
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If this isn't a safe call, load without a pcall
|
|
||||||
else
|
|
||||||
result = read_or_execute(file_path, args, return_type)
|
|
||||||
end
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
-- If the initial open failed, report failure
|
|
||||||
else
|
|
||||||
notify("[Mod] Error opening '" .. file_path .. "': " .. tostring(err_io))
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function exec(local_path, file_name, file_extension, args)
|
|
||||||
return handle_io(local_path, file_name, file_extension, args, true, "exec_boolean")
|
|
||||||
end
|
|
||||||
Mods.file.exec = exec
|
|
||||||
|
|
||||||
local function exec_unsafe(local_path, file_name, file_extension, args)
|
|
||||||
return handle_io(local_path, file_name, file_extension, args, false, "exec_boolean")
|
|
||||||
end
|
|
||||||
Mods.file.exec_unsafe = exec_unsafe
|
|
||||||
|
|
||||||
local function exec_with_return(local_path, file_name, file_extension, args)
|
|
||||||
return handle_io(local_path, file_name, file_extension, args, true, "exec_result")
|
|
||||||
end
|
|
||||||
Mods.file.exec_with_return = exec_with_return
|
|
||||||
|
|
||||||
local function exec_unsafe_with_return(local_path, file_name, file_extension, args)
|
|
||||||
return handle_io(local_path, file_name, file_extension, args, false, "exec_result")
|
|
||||||
end
|
|
||||||
Mods.file.exec_unsafe_with_return = exec_unsafe_with_return
|
|
||||||
|
|
||||||
local function mod_dofile(file_path, args)
|
|
||||||
return handle_io(file_path, nil, nil, args, true, "exec_result")
|
|
||||||
end
|
|
||||||
Mods.file.dofile = mod_dofile
|
|
||||||
|
|
||||||
local function read_content(file_path, file_extension)
|
|
||||||
return handle_io(file_path, nil, file_extension, nil, true, "data")
|
|
||||||
end
|
|
||||||
Mods.file.read_content = read_content
|
|
||||||
|
|
||||||
local function read_content_to_table(file_path, file_extension)
|
|
||||||
return handle_io(file_path, nil, file_extension, nil, true, "lines")
|
|
||||||
end
|
|
||||||
Mods.file.read_content_to_table = read_content_to_table
|
|
||||||
|
|
||||||
local file_exists = function(name)
|
|
||||||
print(name)
|
|
||||||
local f = io.open(name,"r")
|
|
||||||
|
|
||||||
if f ~= nil then
|
|
||||||
print("[Mod]: File exists")
|
|
||||||
io.close(f)
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
print("[Mod]: File does not exist")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Mods.file.exists = file_exists
|
|
||||||
|
|
||||||
-- Load remaining base modules
|
|
||||||
exec("base/function", "require")
|
|
||||||
|
|
||||||
local init_mod_framework = function()
|
|
||||||
|
|
||||||
print("[DMF]: Initializing basic mod hook system...")
|
|
||||||
exec("base/function", "hook")
|
|
||||||
|
|
||||||
-- The mod manager isn't in the bundles, so load our version from the mods folder
|
|
||||||
local ModManager = exec_with_return("base", "mod_manager")
|
|
||||||
|
|
||||||
-- Initialize mods after loading managers and state_game files
|
|
||||||
Mods.hook.set("Base", "StateRequireScripts._require_scripts", function (req_func, ...)
|
|
||||||
local req_result = req_func(...)
|
|
||||||
|
|
||||||
Managers.mod = Managers.mod or ModManager:new()
|
|
||||||
|
|
||||||
-- Update mod manager
|
|
||||||
Mods.hook.set("Base", "StateGame.update", function (func, self, dt, ...)
|
|
||||||
Managers.mod:update(dt)
|
|
||||||
|
|
||||||
return func(self, dt, ...)
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Skip splash view
|
|
||||||
Mods.hook.set("Base", "StateSplash.on_enter", function (func, self, ...)
|
|
||||||
local result = func(self, ...)
|
|
||||||
|
|
||||||
self._should_skip = true
|
|
||||||
self._continue = true
|
|
||||||
|
|
||||||
return result
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Trigger state change events
|
|
||||||
Mods.hook.set("Base", "GameStateMachine._change_state", function (func, self, ...)
|
|
||||||
local old_state = self._state
|
|
||||||
local old_state_name = old_state and self:current_state_name()
|
|
||||||
|
|
||||||
if old_state_name then
|
|
||||||
Managers.mod:on_game_state_changed("exit", old_state_name, old_state)
|
|
||||||
end
|
|
||||||
|
|
||||||
local result = func(self, ...)
|
|
||||||
|
|
||||||
local new_state = self._state
|
|
||||||
local new_state_name = new_state and self:current_state_name()
|
|
||||||
|
|
||||||
if new_state_name then
|
|
||||||
Managers.mod:on_game_state_changed("enter", new_state_name, new_state)
|
|
||||||
end
|
|
||||||
|
|
||||||
return result
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Trigger ending state change event
|
|
||||||
Mods.hook.set("Base", "GameStateMachine.destroy", function (func, self, ...)
|
|
||||||
local old_state = self._state
|
|
||||||
local old_state_name = old_state and self:current_state_name()
|
|
||||||
|
|
||||||
if old_state_name then
|
|
||||||
Managers.mod:on_game_state_changed("exit", old_state_name)
|
|
||||||
end
|
|
||||||
|
|
||||||
return func(self, ...)
|
|
||||||
end)
|
|
||||||
|
|
||||||
return req_result
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Set crashify modded property
|
|
||||||
Mods.hook.set("Base", "StateRequireScripts._get_is_modded", function ()
|
|
||||||
return true
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Original main script
|
|
||||||
Main = Main or {}
|
|
||||||
|
|
||||||
require("scripts/boot_init")
|
|
||||||
require("scripts/foundation/utilities/class")
|
|
||||||
|
|
||||||
-- Expose classes at the global table
|
|
||||||
exec("base/function", "class")
|
|
||||||
|
|
||||||
require("scripts/foundation/utilities/patches")
|
|
||||||
require("scripts/foundation/utilities/settings")
|
|
||||||
require("scripts/foundation/utilities/table")
|
|
||||||
|
|
||||||
local GameStateMachine = require("scripts/foundation/utilities/game_state_machine")
|
|
||||||
local LocalizationManager = require("scripts/managers/localization/localization_manager")
|
|
||||||
local PackageManager = require("scripts/foundation/managers/package/package_manager")
|
|
||||||
local PackageManagerEditor = require("scripts/foundation/managers/package/package_manager_editor")
|
|
||||||
local ParameterResolver = require("scripts/foundation/utilities/parameters/parameter_resolver")
|
|
||||||
local StateBoot = require("scripts/game_states/state_boot")
|
|
||||||
local StateLoadAudioSettings = require("scripts/game_states/boot/state_load_audio_settings")
|
|
||||||
local StateLoadBootAssets = require("scripts/game_states/boot/state_load_boot_assets")
|
|
||||||
local StateLoadRenderSettings = require("scripts/game_states/boot/state_load_render_settings")
|
|
||||||
local StateRequireScripts = require("scripts/game_states/boot/state_require_scripts")
|
|
||||||
|
|
||||||
function Main:init()
|
|
||||||
Script.configure_garbage_collection(Script.ACCEPTABLE_GARBAGE, 0.1, Script.MAXIMUM_GARBAGE, 0.5, Script.FORCE_FULL_COLLECT_GARBAGE_LEVEL, 1, Script.MINIMUM_COLLECT_TIME_MS, 0.1, Script.MAXIMUM_COLLECT_TIME_MS, 0.5)
|
|
||||||
ParameterResolver.resolve_command_line()
|
|
||||||
ParameterResolver.resolve_game_parameters()
|
|
||||||
ParameterResolver.resolve_dev_parameters()
|
|
||||||
Application.set_time_step_policy("throttle", DEDICATED_SERVER and 1 / GameParameters.fixed_time_step or 30)
|
|
||||||
|
|
||||||
if type(GameParameters.window_title) == "string" and GameParameters.window_title ~= "" then
|
|
||||||
Window.set_title(GameParameters.window_title)
|
|
||||||
end
|
|
||||||
|
|
||||||
local package_manager = LEVEL_EDITOR_TEST and PackageManagerEditor:new() or PackageManager:new()
|
|
||||||
local localization_manager = LocalizationManager:new()
|
|
||||||
local params = {
|
|
||||||
next_state = "StateGame",
|
|
||||||
index_offset = 1,
|
|
||||||
states = {
|
|
||||||
{
|
|
||||||
StateLoadBootAssets,
|
|
||||||
{
|
|
||||||
package_manager = package_manager,
|
|
||||||
localization_manager = localization_manager
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
StateRequireScripts,
|
|
||||||
{
|
|
||||||
package_manager = package_manager
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
StateLoadAudioSettings,
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
package_manager = package_manager,
|
|
||||||
localization_manager = localization_manager
|
|
||||||
}
|
|
||||||
|
|
||||||
if PLATFORM == "win32" and not LEVEL_EDITOR_TEST then
|
|
||||||
table.insert(params.states, 1, {
|
|
||||||
StateLoadRenderSettings,
|
|
||||||
{}
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
if LEVEL_EDITOR_TEST then
|
|
||||||
Wwise.load_bank("wwise/world_sound_fx")
|
|
||||||
end
|
|
||||||
|
|
||||||
self._package_manager = package_manager
|
|
||||||
self._sm = GameStateMachine:new(nil, StateBoot, params, nil, nil, "Main")
|
|
||||||
|
|
||||||
-- #######################
|
|
||||||
-- ## Mod intialization ##
|
|
||||||
init_mod_framework()
|
|
||||||
-- #######################
|
|
||||||
end
|
|
||||||
|
|
||||||
function Main:update(dt)
|
|
||||||
self._sm:update(dt)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Main:render()
|
|
||||||
self._sm:render()
|
|
||||||
end
|
|
||||||
|
|
||||||
function Main:on_reload(refreshed_resources)
|
|
||||||
self._sm:on_reload(refreshed_resources)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Main:on_close()
|
|
||||||
local should_close = self._sm:on_close()
|
|
||||||
|
|
||||||
return should_close
|
|
||||||
end
|
|
||||||
|
|
||||||
function Main:shutdown()
|
|
||||||
Application.force_silent_exit_policy()
|
|
||||||
|
|
||||||
if rawget(_G, "Crashify") then
|
|
||||||
Crashify.print_property("shutdown", true)
|
|
||||||
end
|
|
||||||
|
|
||||||
local owns_package_manager = true
|
|
||||||
|
|
||||||
if rawget(_G, "Managers") and Managers.package then
|
|
||||||
Managers.package:shutdown_has_started()
|
|
||||||
|
|
||||||
owns_package_manager = false
|
|
||||||
end
|
|
||||||
|
|
||||||
local on_shutdown = true
|
|
||||||
|
|
||||||
self._sm:destroy(on_shutdown)
|
|
||||||
|
|
||||||
if owns_package_manager then
|
|
||||||
self._package_manager:delete()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function init()
|
|
||||||
Main:init()
|
|
||||||
end
|
|
||||||
|
|
||||||
function update(dt)
|
|
||||||
Main:update(dt)
|
|
||||||
end
|
|
||||||
|
|
||||||
function render()
|
|
||||||
Main:render()
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_reload(refreshed_resources)
|
|
||||||
Main:on_reload(refreshed_resources)
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_activate(active)
|
|
||||||
print("LUA window => " .. (active and "ACTIVATED" or "DEACTIVATED"))
|
|
||||||
|
|
||||||
if active and rawget(_G, "Managers") then
|
|
||||||
if Managers.dlc then
|
|
||||||
Managers.dlc:evaluate_consumables()
|
|
||||||
end
|
|
||||||
|
|
||||||
if Managers.account then
|
|
||||||
Managers.account:refresh_communication_restrictions()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_close()
|
|
||||||
local should_close = Main:on_close()
|
|
||||||
|
|
||||||
if should_close then
|
|
||||||
Application.force_silent_exit_policy()
|
|
||||||
|
|
||||||
if rawget(_G, "Crashify") then
|
|
||||||
Crashify.print_property("shutdown", true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return should_close
|
|
||||||
end
|
|
||||||
|
|
||||||
function shutdown()
|
|
||||||
Main:shutdown()
|
|
||||||
end
|
|
Binary file not shown.
16
dtmt.cfg
Normal file
16
dtmt.cfg
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
id = "dml"
|
||||||
|
name = "Darktide Mod Loader"
|
||||||
|
description = "This is my new mod 'Darktide Mod Loader'!"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
resources = {
|
||||||
|
init = "scripts/mods/dml/init"
|
||||||
|
}
|
||||||
|
|
||||||
|
packages = [
|
||||||
|
"packages/dml"
|
||||||
|
]
|
||||||
|
|
||||||
|
depends = [
|
||||||
|
"dmf"
|
||||||
|
]
|
|
@ -1,22 +0,0 @@
|
||||||
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
|
|
|
@ -1,276 +0,0 @@
|
||||||
--[[
|
|
||||||
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 _loadstring = Mods.lua.loadstring
|
|
||||||
|
|
||||||
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,35 +0,0 @@
|
||||||
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
|
|
|
@ -1,417 +0,0 @@
|
||||||
local ModManager = class("ModManager")
|
|
||||||
|
|
||||||
local Keyboard = Keyboard
|
|
||||||
local BUTTON_INDEX_R = Keyboard.button_index("r")
|
|
||||||
local BUTTON_INDEX_LEFT_SHIFT = Keyboard.button_index("left shift")
|
|
||||||
local BUTTON_INDEX_LEFT_CTRL = Keyboard.button_index("left ctrl")
|
|
||||||
|
|
||||||
local LOG_LEVELS = {
|
|
||||||
spew = 4,
|
|
||||||
info = 3,
|
|
||||||
warning = 2,
|
|
||||||
error = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
local string_format = string.format
|
|
||||||
local function printf(f, ...)
|
|
||||||
print(string.format(f, ...))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function deepcopy(orig)
|
|
||||||
local orig_type = type(orig)
|
|
||||||
local copy
|
|
||||||
if orig_type == 'table' then
|
|
||||||
copy = {}
|
|
||||||
for orig_key, orig_value in next, orig, nil do
|
|
||||||
copy[deepcopy(orig_key)] = deepcopy(orig_value)
|
|
||||||
end
|
|
||||||
setmetatable(copy, deepcopy(getmetatable(orig)))
|
|
||||||
else -- number, string, boolean, etc
|
|
||||||
copy = orig
|
|
||||||
end
|
|
||||||
return copy
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Clone the global mod file library to a local table
|
|
||||||
local _io = deepcopy(Mods.file)
|
|
||||||
|
|
||||||
ModManager.init = function (self, boot_gui)
|
|
||||||
self._mods = {}
|
|
||||||
self._num_mods = nil
|
|
||||||
self._state = "not_loaded"
|
|
||||||
self._settings = Application.user_setting("mod_manager_settings") or {
|
|
||||||
log_level = 1,
|
|
||||||
developer_mode = false
|
|
||||||
}
|
|
||||||
self._chat_print_buffer = {}
|
|
||||||
self._reload_data = {}
|
|
||||||
self._gui = boot_gui
|
|
||||||
self._ui_time = 0
|
|
||||||
self._network_callbacks = {}
|
|
||||||
|
|
||||||
Crashify.print_property("realm", "modded")
|
|
||||||
|
|
||||||
print("[ModManager] Starting mod manager...")
|
|
||||||
|
|
||||||
self._mod_metadata = {}
|
|
||||||
self._state = "scanning"
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.developer_mode_enabled = function (self)
|
|
||||||
return self._settings.developer_mode
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager._draw_state_to_gui = function (self, gui, dt)
|
|
||||||
local state = self._state
|
|
||||||
local t = self._ui_time + dt
|
|
||||||
self._ui_time = t
|
|
||||||
local status_str = "Loading mods"
|
|
||||||
|
|
||||||
if state == "scanning" then
|
|
||||||
status_str = "Scanning for mods"
|
|
||||||
elseif state == "loading" then
|
|
||||||
local mod = self._mods[self._mod_load_index]
|
|
||||||
status_str = string.format("Loading mod %q", mod.name)
|
|
||||||
end
|
|
||||||
|
|
||||||
--Gui.text(gui, status_str .. string.rep(".", (2 * t) % 4), "materials/fonts/arial", 16, nil, Vector3(5, 10, 1))
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.remove_gui = function (self)
|
|
||||||
self._gui = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager._has_enabled_mods = function (self)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager._check_reload = function (self)
|
|
||||||
return Keyboard.pressed(BUTTON_INDEX_R) and
|
|
||||||
Keyboard.button(BUTTON_INDEX_LEFT_SHIFT) + Keyboard.button(BUTTON_INDEX_LEFT_CTRL) == 2
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.update = function (self, dt)
|
|
||||||
local chat_print_buffer = self._chat_print_buffer
|
|
||||||
local num_delayed_prints = #chat_print_buffer
|
|
||||||
|
|
||||||
if num_delayed_prints > 0 and Managers.chat then
|
|
||||||
for i = 1, num_delayed_prints, 1 do
|
|
||||||
Mods.message.echo(chat_print_buffer[i])
|
|
||||||
|
|
||||||
chat_print_buffer[i] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local old_state = self._state
|
|
||||||
|
|
||||||
if self._settings.developer_mode and self:_check_reload() then
|
|
||||||
self._reload_requested = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if self._reload_requested and self._state == "done" then
|
|
||||||
self:_reload_mods()
|
|
||||||
end
|
|
||||||
|
|
||||||
if self._state == "done" then
|
|
||||||
if self._num_mods then
|
|
||||||
for i = 1, self._num_mods, 1 do
|
|
||||||
local mod = self._mods[i]
|
|
||||||
|
|
||||||
if mod and mod.enabled and not mod.callbacks_disabled then
|
|
||||||
self:_run_callback(mod, "update", dt)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif self._state == "scanning" then
|
|
||||||
self:_build_mod_table()
|
|
||||||
self._state = self:_load_mod(1)
|
|
||||||
self._ui_time = 0
|
|
||||||
|
|
||||||
elseif self._state == "loading" then
|
|
||||||
local mod = self._mods[self._mod_load_index]
|
|
||||||
local mod_data = mod.data
|
|
||||||
|
|
||||||
mod.state = "running"
|
|
||||||
local ok, object = pcall(mod_data.run)
|
|
||||||
|
|
||||||
if not ok then
|
|
||||||
self:print("error", "%s", object)
|
|
||||||
end
|
|
||||||
|
|
||||||
local name = mod.name
|
|
||||||
mod.object = object or {}
|
|
||||||
|
|
||||||
self:_run_callback(mod, "init", self._reload_data[mod.id])
|
|
||||||
self:print("info", "%s loaded.", name)
|
|
||||||
|
|
||||||
self._state = self:_load_mod(self._mod_load_index + 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local gui = self._gui
|
|
||||||
|
|
||||||
if gui then
|
|
||||||
self:_draw_state_to_gui(gui, dt)
|
|
||||||
end
|
|
||||||
|
|
||||||
if old_state ~= self._state then
|
|
||||||
self:print("info", "%s -> %s", old_state, self._state)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.all_mods_loaded = function (self)
|
|
||||||
return self._state == "done"
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.destroy = function (self)
|
|
||||||
self:unload_all_mods()
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager._run_callback = function (self, mod, callback_name, ...)
|
|
||||||
local object = mod.object
|
|
||||||
local cb = object[callback_name]
|
|
||||||
|
|
||||||
if not cb then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local success, val = pcall(cb, object, ...)
|
|
||||||
|
|
||||||
if success then
|
|
||||||
return val
|
|
||||||
else
|
|
||||||
self:print("error", "%s", val or "[unknown error]")
|
|
||||||
self:print("error", "Failed to run callback %q for mod %q with id %d. Disabling callbacks until reload.",
|
|
||||||
callback_name, mod.name, mod.id)
|
|
||||||
|
|
||||||
mod.callbacks_disabled = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager._build_mod_table = function (self)
|
|
||||||
fassert(table.is_empty(self._mods), "Trying to add mods to non-empty mod table")
|
|
||||||
|
|
||||||
-- Get the mods' load order from mod_load_order file
|
|
||||||
local mod_load_order = _io.read_content_to_table("mod_load_order", "txt")
|
|
||||||
if not mod_load_order then
|
|
||||||
print("ERROR executing mod_load_order: " .. tostring(mod_load_order))
|
|
||||||
mod_load_order = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Add DMF to the mod load order
|
|
||||||
table.insert(mod_load_order, 1, "dmf")
|
|
||||||
|
|
||||||
-- Read the .mod files of given mods and, if everything's fine, add mods' entries to the mods list.
|
|
||||||
for i = 1, #mod_load_order do
|
|
||||||
local mod_name = mod_load_order[i]
|
|
||||||
self._mods[i] = {
|
|
||||||
state = "not_loaded",
|
|
||||||
callbacks_disabled = false,
|
|
||||||
id = i,
|
|
||||||
name = mod_name,
|
|
||||||
enabled = true,
|
|
||||||
handle = mod_name,
|
|
||||||
loaded_packages = {}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
self._num_mods = #self._mods
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager._load_mod = function (self, index)
|
|
||||||
self._ui_time = 0
|
|
||||||
local mods = self._mods
|
|
||||||
local mod = mods[index]
|
|
||||||
|
|
||||||
while mod and not mod.enabled do
|
|
||||||
index = index + 1
|
|
||||||
mod = mods[index]
|
|
||||||
end
|
|
||||||
|
|
||||||
if not mod then
|
|
||||||
table.clear(self._reload_data)
|
|
||||||
return "done"
|
|
||||||
end
|
|
||||||
|
|
||||||
local id = mod.id
|
|
||||||
local mod_name = mod.name
|
|
||||||
|
|
||||||
self:print("info", "loading mod %s", id)
|
|
||||||
Crashify.print_property("modded", true)
|
|
||||||
|
|
||||||
local mod_data = _io.exec_with_return(mod_name, mod_name, "mod")
|
|
||||||
|
|
||||||
if not mod_data then
|
|
||||||
self:print("error", "Mod file is invalid or missing. Mod %q with id %d skipped.", mod.name, mod.id)
|
|
||||||
|
|
||||||
mod.enabled = false
|
|
||||||
|
|
||||||
return self:_load_mod(index + 1)
|
|
||||||
end
|
|
||||||
self:print("spew", "<mod info>\n%s\n</mod info>", mod_data)
|
|
||||||
|
|
||||||
mod.data = mod_data
|
|
||||||
mod.name = mod.name or mod_data.NAME or "Mod " .. mod.id
|
|
||||||
mod.state = "loading"
|
|
||||||
|
|
||||||
Crashify.print_property(string.format("Mod:%s:%s", id, mod.name), true)
|
|
||||||
|
|
||||||
self._mod_load_index = index
|
|
||||||
|
|
||||||
return "loading"
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.unload_all_mods = function (self)
|
|
||||||
if self._state ~= "done" then
|
|
||||||
self:print("error", "Mods can't be unloaded, mod state is not \"done\". current: %q", self._state)
|
|
||||||
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
self:print("info", "Unload all mod packages")
|
|
||||||
|
|
||||||
for i = self._num_mods, 1, -1 do
|
|
||||||
local mod = self._mods[i]
|
|
||||||
|
|
||||||
if mod and mod.enabled then
|
|
||||||
self:unload_mod(i)
|
|
||||||
end
|
|
||||||
|
|
||||||
self._mods[i] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
self._num_mods = nil
|
|
||||||
self._state = "unloaded"
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.unload_mod = function (self, index)
|
|
||||||
local mod = self._mods[index]
|
|
||||||
|
|
||||||
if mod then
|
|
||||||
self:print("info", "Unloading %q.", mod.name)
|
|
||||||
self:_run_callback(mod, "on_unload")
|
|
||||||
|
|
||||||
mod.state = "not_loaded"
|
|
||||||
else
|
|
||||||
self:print("error", "Mod index %i can't be unloaded, has not been loaded", index)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager._reload_mods = function (self)
|
|
||||||
for i = 1, self._num_mods, 1 do
|
|
||||||
local mod = self._mods[i]
|
|
||||||
|
|
||||||
if mod and mod.state == "running" then
|
|
||||||
self:print("info", "reloading %s", mod.name)
|
|
||||||
|
|
||||||
self._reload_data[mod.id] = self:_run_callback(mod, "on_reload")
|
|
||||||
else
|
|
||||||
self:print("info", "not reloading mod, state: %s", mod.state)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self:unload_all_mods()
|
|
||||||
self._state = "scanning"
|
|
||||||
self._reload_requested = false
|
|
||||||
|
|
||||||
Mods.message.notify("Mods reloaded.")
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.on_game_state_changed = function (self, status, state_name, state_object)
|
|
||||||
if self._state == "done" then
|
|
||||||
for i = 1, self._num_mods, 1 do
|
|
||||||
local mod = self._mods[i]
|
|
||||||
|
|
||||||
if mod and mod.enabled and not mod.callbacks_disabled then
|
|
||||||
self:_run_callback(mod, "on_game_state_changed", status, state_name, state_object)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self:print("warning", "Ignored on_game_state_changed call due to being in state %q", self._state)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager._visit = function (self, mod_list, visited, sorted, mod_data)
|
|
||||||
self:print("debug", "Visiting mod %q with id %d", mod_data.name, mod_data.id)
|
|
||||||
|
|
||||||
if visited[mod_data] then
|
|
||||||
return mod_data.enabled
|
|
||||||
end
|
|
||||||
|
|
||||||
if visited[mod_data] ~= nil then
|
|
||||||
self:print("error", "Dependency cycle detected at mod %q with id %d", mod_data.name, mod_data.id)
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
visited[mod_data] = false
|
|
||||||
local enabled = mod_data.enabled or false
|
|
||||||
|
|
||||||
for i = 1, mod_data.num_children or 0 do
|
|
||||||
local child_id = mod_data.children[i]
|
|
||||||
local child_index = table.find_by_key(mod_list, "id", child_id)
|
|
||||||
local child_mod_data = mod_list[child_index]
|
|
||||||
|
|
||||||
if not child_mod_data then
|
|
||||||
self:print("warning", "Mod with id %d not found", child_id)
|
|
||||||
elseif not self:_visit(mod_list, visited, sorted, child_mod_data) and enabled then
|
|
||||||
self:print("warning", "Disabled mod %q with id %d due to missing dependency %d.",
|
|
||||||
mod_data.name, mod_data.id, child_id)
|
|
||||||
|
|
||||||
enabled = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
mod_data.enabled = enabled
|
|
||||||
visited[mod_data] = true
|
|
||||||
sorted[#sorted + 1] = mod_data
|
|
||||||
|
|
||||||
return enabled
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.print = function (self, level, str, ...)
|
|
||||||
local message = string.format("[ModManager][" .. level .. "] " .. tostring(str), ...)
|
|
||||||
local log_level = LOG_LEVELS[level] or 99
|
|
||||||
|
|
||||||
if log_level <= 2 then
|
|
||||||
print(message)
|
|
||||||
end
|
|
||||||
|
|
||||||
if log_level <= self._settings.log_level then
|
|
||||||
self._chat_print_buffer[#self._chat_print_buffer + 1] = message
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.network_bind = function (self)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.network_unbind = function (self)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.network_is_occupied = function (self)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.network_send = function (self)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.rpc_mod_user_data = function (self)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.register_network_event_delegate = function (self)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.unregister_network_event_delegate = function (self)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
ModManager.network_context_created = function (self)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
return ModManager
|
|
|
@ -1,6 +0,0 @@
|
||||||
-- ################################################################
|
|
||||||
-- Enter user mod names below, separated by line.
|
|
||||||
-- Order in the list determines the order in which mods are loaded.
|
|
||||||
-- Do not rename a mod's folders.
|
|
||||||
-- You do not need to include 'base' or 'dmf' mod folders.
|
|
||||||
-- ################################################################
|
|
3
packages/dml.package
Normal file
3
packages/dml.package
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
lua = [
|
||||||
|
"scripts/mods/dml/*"
|
||||||
|
]
|
|
@ -1,10 +0,0 @@
|
||||||
@echo off
|
|
||||||
echo Starting Darktide patcher from %~dp0...
|
|
||||||
.\tools\dtkit-patch --toggle .\bundle
|
|
||||||
if errorlevel 1 goto failure
|
|
||||||
pause
|
|
||||||
exit
|
|
||||||
:failure
|
|
||||||
echo Error patching the Darktide bundle database. See logs.
|
|
||||||
pause
|
|
||||||
exit
|
|
|
@ -1,5 +0,0 @@
|
||||||
# dtkit-patch
|
|
||||||
|
|
||||||
Simple tool based on Aussiemon's nodejs script for patching `bundle_database.data` to load Darktide mods.
|
|
||||||
|
|
||||||
https://github.com/ManShanko/dtkit-patch
|
|
Binary file not shown.
Loading…
Add table
Reference in a new issue