From 1df5f47c2cb1d1c780331dc014b935b9728a64fc Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 16 Nov 2023 00:06:16 +0100 Subject: [PATCH 1/2] Prevent excessive debug logs --- crates/dtmm/src/controller/worker.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index 518c0a8..152030f 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -38,7 +38,9 @@ async fn handle_action( action_queue: Arc>>, ) { while let Some(action) = action_queue.write().await.recv().await { - tracing::debug!(?action); + if cfg!(debug_assertions) && !matches!(action, AsyncAction::Log(_)) { + tracing::debug!(?action); + } let event_sink = event_sink.clone(); match action { From 98827206751a7356c626e8d9675633b8beecbe97 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 16 Nov 2023 14:29:06 +0100 Subject: [PATCH 2/2] Delay mod loading The initial implementation of DML ended up loading mods quite late, which did give it the benefit of all `Manager`s being available. This change therefore moves mod loading until after those are initialized. But contrary to old DML, we still create a separate game state to make sure the game doesn't advance until mods are loaded. This avoids race conditions like the one where LogMeIn needs to come early in the load order. --- crates/dtmm/assets/mod_main.lua | 186 ++++++++++++++++---------------- 1 file changed, 92 insertions(+), 94 deletions(-) diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua index 2b329bf..e4006f6 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua @@ -11,100 +11,6 @@ local log = function(category, format, ...) end end --- Patch `GameStateMachine.init` to add our own state for loading mods. --- In the future, Fatshark might provide us with a dedicated way to do this. -local function patch_mod_loading_state() - local StateBootSubStateBase = require("scripts/game_states/boot/state_boot_sub_state_base") - - -- A necessary override. - -- The original does not proxy `dt` to `_state_update`, but we need that. - StateBootSubStateBase.update = function(self, dt) - local done, error = self:_state_update(dt) - local params = self._params - - if error then - return StateError, { error } - elseif done then - local next_index = params.sub_state_index + 1 - params.sub_state_index = next_index - local next_state_data = params.states[next_index] - - if next_state_data then - return next_state_data[1], self._params - else - self._parent:sub_states_done() - end - end - end - - local StateBootLoadMods = class("StateBootLoadMods", "StateBootSubStateBase") - - StateBootLoadMods.on_enter = function(self, parent, params) - log("StateBootLoadMods", "Entered") - StateBootLoadMods.super.on_enter(self, parent, params) - - local state_params = self:_state_params() - local package_manager = state_params.package_manager - - self._state = "load_package" - self._package_manager = package_manager - self._package_handles = { - ["packages/mods"] = package_manager:load("packages/mods", "StateBootLoadMods", nil), - ["packages/dml"] = package_manager:load("packages/dml", "StateBootLoadMods", nil), - } - end - - StateBootLoadMods._state_update = function(self, dt) - local state = self._state - local package_manager = self._package_manager - - if state == "load_package" and package_manager:update() then - log("StateBootLoadMods", "Packages loaded, loading mods") - self._state = "load_mods" - local DML = require("scripts/mods/dml/init") - - local mod_data = require("scripts/mods/mod_data") - local mod_loader = DML.create_loader(mod_data, self._parent:gui()) - - self._dml = DML - Managers.mod = mod_loader - elseif state == "load_mods" and self._dml.update(Managers.mod, dt) then - log("StateBootLoadMods", "Mods loaded, exiting") - return true, false - end - - return false, false - end - - local GameStateMachine = require("scripts/foundation/utilities/game_state_machine") - - local patched = false - - local GameStateMachine_init = GameStateMachine.init - GameStateMachine.init = function(self, parent, start_state, params, ...) - if not patched then - log("mod_main", "Injecting mod loading state") - patched = true - - -- Hardcoded position after `StateRequireScripts`. - -- We do want to wait until then, so that most of the game's core - -- systems are at least loaded and can be hooked, even if they aren't - -- running, yet. - local pos = 4 - table.insert(params.states, pos, { - StateBootLoadMods, - { - package_manager = params.package_manager, - }, - }) - end - - GameStateMachine_init(self, parent, start_state, params, ...) - end - - log("mod_main", "Mod patching complete") -end - log("mod_main", "Initializing mods...") local require_store = {} @@ -199,6 +105,98 @@ end require("scripts/main") log("mod_main", "'scripts/main' loaded") +-- Inject our state into the game. The state needs to run after `StateGame._init_managers`, +-- since some parts of DMF, and presumably other mods, depend on some of those managers to exist. +local function patch_mod_loading_state() + local StateBootSubStateBase = require("scripts/game_states/boot/state_boot_sub_state_base") + local StateBootLoadDML = class("StateBootLoadDML", "StateBootSubStateBase") + local StateGameLoadMods = class("StateGameLoadMods") + + StateBootLoadDML.on_enter = function(self, parent, params) + log("StateBootLoadDML", "Entered") + StateBootLoadDML.super.on_enter(self, parent, params) + + local state_params = self:_state_params() + local package_manager = state_params.package_manager + + self._package_manager = package_manager + self._package_handles = { + ["packages/mods"] = package_manager:load("packages/mods", "StateBootDML", nil), + ["packages/dml"] = package_manager:load("packages/dml", "StateBootDML", nil), + } + end + + StateBootLoadDML._state_update = function(self, dt) + local package_manager = self._package_manager + + if package_manager:update() then + local DML = require("scripts/mods/dml/init") + local mod_data = require("scripts/mods/mod_data") + local mod_loader = DML.create_loader(mod_data) + Managers.mod = mod_loader + log("StateBootLoadDML", "DML loaded, exiting") + return true, false + end + + return false, false + end + + + function StateGameLoadMods:on_enter(_, params) + log("StateGameLoadMods", "Entered") + self._next_state = require("scripts/game_states/game/state_splash") + self._next_state_params = params + end + + function StateGameLoadMods:update(main_dt) + local state = self._loading_state + + -- We're relying on the fact that DML internally makes sure + -- that `Managers.mod:update()` is being called appropriately. + -- The implementation as of this writing is to hook `StateGame.update`. + if Managers.mod:all_mods_loaded() then + Log.info("StateGameLoadMods", "Mods loaded, exiting") + return self._next_state, self._next_state_params + end + end + + local GameStateMachine = require("scripts/foundation/utilities/game_state_machine") + local GameStateMachine_init = GameStateMachine.init + GameStateMachine.init = function(self, parent, start_state, params, creation_context, state_change_callbacks, name) + if name == "Main" then + log("mod_main", "Injecting StateBootLoadDML") + + -- Hardcoded position after `StateRequireScripts`. + -- We need to wait until then to even begin most of our stuff, + -- so that most of the game's core systems are at least loaded and can be hooked, + -- even if they aren't running, yet. + local pos = 4 + table.insert(params.states, pos, { + StateBootLoadDML, + { + package_manager = params.package_manager, + }, + }) + + GameStateMachine_init(self, parent, start_state, params, creation_context, state_change_callbacks, name) + elseif name == "Game" then + log("mod_main", "Injection StateGameLoadMods") + -- The second time around, we want to be the first, so we pass our own + -- 'start_state'. + -- We can't just have the state machine be initialized and then change its `_next_state`, as by the end of + -- `init`, a bunch of stuff will already be initialized. + GameStateMachine_init(self, parent, StateGameLoadMods, params, creation_context, state_change_callbacks, name) + -- And since we're done now, we can revert the function to its original + GameStateMachine.init = GameStateMachine_init + + return + else + -- In all other cases, simply call the original + GameStateMachine_init(self, parent, start_state, params, creation_context, state_change_callbacks, name) + end + end +end + -- Override `init` to run our injection function init() patch_mod_loading_state()