diff --git a/.luacheckrc b/.luacheckrc new file mode 100644 index 0000000..552b17e --- /dev/null +++ b/.luacheckrc @@ -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", + }, +} + diff --git a/binaries/mod_loader b/binaries/mod_loader deleted file mode 100644 index b762483..0000000 --- a/binaries/mod_loader +++ /dev/null @@ -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 diff --git a/bundle/9ba626afa44a3aa3.patch_999 b/bundle/9ba626afa44a3aa3.patch_999 deleted file mode 100644 index a0e417c..0000000 Binary files a/bundle/9ba626afa44a3aa3.patch_999 and /dev/null differ diff --git a/dtmt.cfg b/dtmt.cfg new file mode 100644 index 0000000..3831232 --- /dev/null +++ b/dtmt.cfg @@ -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" +] diff --git a/mods/base/function/class.lua b/mods/base/function/class.lua deleted file mode 100644 index 19b0c91..0000000 --- a/mods/base/function/class.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/mods/base/function/hook.lua b/mods/base/function/hook.lua deleted file mode 100644 index b4b6b61..0000000 --- a/mods/base/function/hook.lua +++ /dev/null @@ -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, -} diff --git a/mods/base/function/require.lua b/mods/base/function/require.lua deleted file mode 100644 index 6e876f7..0000000 --- a/mods/base/function/require.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/mods/base/mod_manager.lua b/mods/base/mod_manager.lua deleted file mode 100644 index 0814072..0000000 --- a/mods/base/mod_manager.lua +++ /dev/null @@ -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", "\n%s\n", 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 diff --git a/mods/mod_load_order.txt b/mods/mod_load_order.txt deleted file mode 100644 index 086a2e7..0000000 --- a/mods/mod_load_order.txt +++ /dev/null @@ -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. --- ################################################################ diff --git a/packages/dml.package b/packages/dml.package new file mode 100644 index 0000000..1bd97ef --- /dev/null +++ b/packages/dml.package @@ -0,0 +1,3 @@ +lua = [ + "scripts/mods/dml/*" +] diff --git a/toggle_darktide_mods.bat b/toggle_darktide_mods.bat deleted file mode 100644 index 0862cb0..0000000 --- a/toggle_darktide_mods.bat +++ /dev/null @@ -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 \ No newline at end of file diff --git a/tools/README.md b/tools/README.md deleted file mode 100644 index bb184d4..0000000 --- a/tools/README.md +++ /dev/null @@ -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 \ No newline at end of file diff --git a/tools/dtkit-patch.exe b/tools/dtkit-patch.exe deleted file mode 100644 index 177d2d7..0000000 Binary files a/tools/dtkit-patch.exe and /dev/null differ