From 4b6827f688800128eaa495c3639485eb3722bb42 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 5 Dec 2018 21:12:05 +0100 Subject: [PATCH 01/16] Port mod package manager --- .../mods/vmf/modules/core/packages.lua | 160 ++++++++++++++++++ .../mods/vmf/modules/vmf_mod_manager.lua | 3 +- vmf/scripts/mods/vmf/vmf_loader.lua | 5 +- 3 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 vmf/scripts/mods/vmf/modules/core/packages.lua diff --git a/vmf/scripts/mods/vmf/modules/core/packages.lua b/vmf/scripts/mods/vmf/modules/core/packages.lua new file mode 100644 index 0000000..d61d748 --- /dev/null +++ b/vmf/scripts/mods/vmf/modules/core/packages.lua @@ -0,0 +1,160 @@ +local vmf = get_mod("VMF") + +local _initialized = false + +local _mod_handles = {} +local _queued_packages = {} +local _loading_package = nil +local _loaded_packages = {} + +function VMFMod:load_package(package_name, callback) + if not _initialized then + self:error("Package manager has not been initialized yet. It can only be used after the `all_mods_loaded` event has been processed.") + return + elseif self:has_package_loaded(package_name) then + self:error("Package '%s' has already been loaded", package_name) + return + end + + if not _loaded_packages[self] then + _loaded_packages[self] = {} + end + + local package_handle = string.format("resource_packages/%s/%s", self:get_name(), package_name) + local workshop_id = self:get_internal_data("workshop_id") + + local resource_package = Mod.resource_package(_mod_handles[workshop_id], package_handle) + + local is_loading = self:is_package_loading(package_name) + + if not callback then + if not is_loading then + resource_package:load() + end + + resource_package:flush() + + _loaded_packages[self][package_name] = resource_package + + self:debug("Loaded package '%s' synchronously", package_name) + else + if is_loading then + self:error("Package '%s' is currently loading", package_name) + return + end + + table.insert(_queued_packages, { + mod = self, + package_name = package_name, + resource_package = resource_package, + callback = callback, + }) + self:debug("Queued package '%s'", package_name) + end +end + +function VMFMod:unload_package(package_name) + if not _initialized then + self:error("Package manager has not been initialized yet. It can only be used after the `all_mods_loaded` event has been processed.") + return + end + + if not self:has_package_loaded(package_name) then + self:error("Package '%s' has not been loaded", package_name) + return + end + + local resource_package = _loaded_packages[self][package_name] + + resource_package:unload() + Mod.release_resource_package(resource_package) + _loaded_packages[self][package_name] = nil + + self:debug("Unloaded package '%s'", package_name) +end + +function VMFMod:is_package_loading(package_name) + if not _initialized then + self:error("Package manager has not been initialized yet. It can only be used after the `all_mods_loaded` event has been processed.") + return + end + + if _loading_package and _loading_package.mod == self and _loading_package.package_name == package_name then + return true + end + + for _, queued_package in ipairs(_queued_packages) do + if queued_package.mod == self and queued_package.package_name == package_name then + return true + end + end + + return false +end + +function VMFMod:has_package_loaded(package_name) + if not _initialized then + self:error("Package manager has not been initialized yet. It can only be used after the `all_mods_loaded` event has been processed.") + return + end + + local loaded_packages = _loaded_packages[self] + return loaded_packages and loaded_packages[package_name] ~= nil +end + +function VMFMod:is_package_manager_initialized() + return _initialized +end + +function vmf.initialize_package_manager() + vmf:debug("Initializing package manager") + + for _, mod_data in ipairs(Managers.mod._mods) do + _mod_handles[mod_data.id] = mod_data.handle + end + + _initialized = true +end + +function vmf.update_package_manager() + local loading_package = _loading_package + + if loading_package then + if loading_package.resource_package:has_loaded() then + vmf:debug("Finished loading package '%s/%s' asynchronously", loading_package.mod:get_name(), loading_package.package_name) + _loaded_packages[loading_package.mod][loading_package.package_name] = loading_package.resource_package + _loading_package = nil + + -- The callback has to be called last, so that any calls to `has_package_loaded` or `is_package_loading` return the correct value + loading_package.callback() + else + vmf:debug("Package '%s/%s' is still loading asynchronously", loading_package.mod:get_name(), loading_package.package_name) + end + + return + -- else + -- vmf:debug("No package to load asynchronously") + end + + local queued_package = _queued_packages[1] + + if queued_package then + _loading_package = queued_package + table.remove(_queued_packages, 1) + + _loading_package.resource_package:load() + vmf:debug("Started loading package '%s/%s' asynchronously", _loading_package.mod:get_name(), _loading_package.package_name) + end +end + +function VMFMod:dump_package_manager() + self:debug("initialized = %s", _initialized) + self:dump(_mod_handles, "_mod_handles", 2) + self:dump(_queued_packages, "_queued_packages", 2) + if _loading_package then + self:dump(_loading_package, "_loading_package", 2) + else + self:debug("_loading_package = nil") + end + self:dump(_loaded_packages, "_loaded_packages", 2) +end diff --git a/vmf/scripts/mods/vmf/modules/vmf_mod_manager.lua b/vmf/scripts/mods/vmf/modules/vmf_mod_manager.lua index a64a6fe..ef69dd3 100644 --- a/vmf/scripts/mods/vmf/modules/vmf_mod_manager.lua +++ b/vmf/scripts/mods/vmf/modules/vmf_mod_manager.lua @@ -115,6 +115,7 @@ function vmf.initialize_mod_data(mod, mod_data) vmf.set_internal_data(mod, "is_togglable", mod_data.is_togglable or mod_data.is_mutator) vmf.set_internal_data(mod, "is_mutator", mod_data.is_mutator) vmf.set_internal_data(mod, "allow_rehooking", mod_data.allow_rehooking) + vmf.set_internal_data(mod, "workshop_id", mod_data.workshop_id) -- Register mod as mutator @TODO: calling this after options initialization would be better, I guess? if mod_data.is_mutator then @@ -163,4 +164,4 @@ end -- VARIABLES vmf.mods = _mods -vmf.mods_unloading_order = _mods_unloading_order \ No newline at end of file +vmf.mods_unloading_order = _mods_unloading_order diff --git a/vmf/scripts/mods/vmf/vmf_loader.lua b/vmf/scripts/mods/vmf/vmf_loader.lua index 8c998a7..29b28a7 100644 --- a/vmf/scripts/mods/vmf/vmf_loader.lua +++ b/vmf/scripts/mods/vmf/vmf_loader.lua @@ -29,6 +29,7 @@ function vmf_mod_object:init() dofile("scripts/mods/vmf/modules/core/options") dofile("scripts/mods/vmf/modules/legacy/options") dofile("scripts/mods/vmf/modules/core/network") + dofile("scripts/mods/vmf/modules/core/packages") dofile("scripts/mods/vmf/modules/core/commands") dofile("scripts/mods/vmf/modules/gui/custom_textures") dofile("scripts/mods/vmf/modules/gui/custom_views") @@ -55,6 +56,7 @@ end -- ##################################################################################################################### function vmf_mod_object:update(dt) + vmf.update_package_manager() vmf.mods_update_event(dt) vmf.check_keybinds() vmf.execute_queued_chat_command() @@ -63,6 +65,7 @@ function vmf_mod_object:update(dt) if not vmf.all_mods_were_loaded and Managers.mod._state == "done" then vmf.generate_keybinds() + vmf.initialize_package_manager() vmf.initialize_vmf_options_view() vmf.create_network_dictionary() vmf.ping_vmf_users() @@ -111,4 +114,4 @@ end -- ##### Return ######################################################################################################## -- ##################################################################################################################### -return vmf_mod_object \ No newline at end of file +return vmf_mod_object From 0b3b09bada5cd0a023c00f195fac3c09790c0bfb Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 5 Dec 2018 21:15:14 +0100 Subject: [PATCH 02/16] Remove debug prints --- .../mods/vmf/modules/core/packages.lua | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/core/packages.lua b/vmf/scripts/mods/vmf/modules/core/packages.lua index d61d748..0a35408 100644 --- a/vmf/scripts/mods/vmf/modules/core/packages.lua +++ b/vmf/scripts/mods/vmf/modules/core/packages.lua @@ -35,8 +35,6 @@ function VMFMod:load_package(package_name, callback) resource_package:flush() _loaded_packages[self][package_name] = resource_package - - self:debug("Loaded package '%s' synchronously", package_name) else if is_loading then self:error("Package '%s' is currently loading", package_name) @@ -49,7 +47,6 @@ function VMFMod:load_package(package_name, callback) resource_package = resource_package, callback = callback, }) - self:debug("Queued package '%s'", package_name) end end @@ -69,8 +66,6 @@ function VMFMod:unload_package(package_name) resource_package:unload() Mod.release_resource_package(resource_package) _loaded_packages[self][package_name] = nil - - self:debug("Unloaded package '%s'", package_name) end function VMFMod:is_package_loading(package_name) @@ -107,8 +102,6 @@ function VMFMod:is_package_manager_initialized() end function vmf.initialize_package_manager() - vmf:debug("Initializing package manager") - for _, mod_data in ipairs(Managers.mod._mods) do _mod_handles[mod_data.id] = mod_data.handle end @@ -121,19 +114,14 @@ function vmf.update_package_manager() if loading_package then if loading_package.resource_package:has_loaded() then - vmf:debug("Finished loading package '%s/%s' asynchronously", loading_package.mod:get_name(), loading_package.package_name) _loaded_packages[loading_package.mod][loading_package.package_name] = loading_package.resource_package _loading_package = nil -- The callback has to be called last, so that any calls to `has_package_loaded` or `is_package_loading` return the correct value loading_package.callback() - else - vmf:debug("Package '%s/%s' is still loading asynchronously", loading_package.mod:get_name(), loading_package.package_name) end return - -- else - -- vmf:debug("No package to load asynchronously") end local queued_package = _queued_packages[1] @@ -143,18 +131,5 @@ function vmf.update_package_manager() table.remove(_queued_packages, 1) _loading_package.resource_package:load() - vmf:debug("Started loading package '%s/%s' asynchronously", _loading_package.mod:get_name(), _loading_package.package_name) end end - -function VMFMod:dump_package_manager() - self:debug("initialized = %s", _initialized) - self:dump(_mod_handles, "_mod_handles", 2) - self:dump(_queued_packages, "_queued_packages", 2) - if _loading_package then - self:dump(_loading_package, "_loading_package", 2) - else - self:debug("_loading_package = nil") - end - self:dump(_loaded_packages, "_loaded_packages", 2) -end From 5d19c8953f4d7872360ae4e2068725b0b9739a29 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 10 Dec 2018 15:54:57 +0100 Subject: [PATCH 03/16] Automatically determine mod handle Remove the burden fromt he modder to keep the workshop ID in sync by determening the mod's handle during initial load. This also removes the requirement to wait until all mods have been loaded before being able to load packages. --- .../mods/vmf/modules/core/packages.lua | 43 +++++-------------- .../mods/vmf/modules/vmf_mod_manager.lua | 9 +++- vmf/scripts/mods/vmf/vmf_loader.lua | 1 - 3 files changed, 18 insertions(+), 35 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/core/packages.lua b/vmf/scripts/mods/vmf/modules/core/packages.lua index 0a35408..c55cf4a 100644 --- a/vmf/scripts/mods/vmf/modules/core/packages.lua +++ b/vmf/scripts/mods/vmf/modules/core/packages.lua @@ -1,29 +1,29 @@ local vmf = get_mod("VMF") -local _initialized = false - -local _mod_handles = {} local _queued_packages = {} local _loading_package = nil local _loaded_packages = {} function VMFMod:load_package(package_name, callback) - if not _initialized then - self:error("Package manager has not been initialized yet. It can only be used after the `all_mods_loaded` event has been processed.") - return - elseif self:has_package_loaded(package_name) then + if self:has_package_loaded(package_name) then self:error("Package '%s' has already been loaded", package_name) return end + local mod_handle = self:get_internal_data("mod_handle") + + if not mod_handle then + self:error("Failed to get mod handle. Package management is not available.") + return + end + if not _loaded_packages[self] then _loaded_packages[self] = {} end local package_handle = string.format("resource_packages/%s/%s", self:get_name(), package_name) - local workshop_id = self:get_internal_data("workshop_id") - local resource_package = Mod.resource_package(_mod_handles[workshop_id], package_handle) + local resource_package = Mod.resource_package(mod_handle, package_handle) local is_loading = self:is_package_loading(package_name) @@ -51,11 +51,6 @@ function VMFMod:load_package(package_name, callback) end function VMFMod:unload_package(package_name) - if not _initialized then - self:error("Package manager has not been initialized yet. It can only be used after the `all_mods_loaded` event has been processed.") - return - end - if not self:has_package_loaded(package_name) then self:error("Package '%s' has not been loaded", package_name) return @@ -69,11 +64,6 @@ function VMFMod:unload_package(package_name) end function VMFMod:is_package_loading(package_name) - if not _initialized then - self:error("Package manager has not been initialized yet. It can only be used after the `all_mods_loaded` event has been processed.") - return - end - if _loading_package and _loading_package.mod == self and _loading_package.package_name == package_name then return true end @@ -88,25 +78,12 @@ function VMFMod:is_package_loading(package_name) end function VMFMod:has_package_loaded(package_name) - if not _initialized then - self:error("Package manager has not been initialized yet. It can only be used after the `all_mods_loaded` event has been processed.") - return - end - local loaded_packages = _loaded_packages[self] return loaded_packages and loaded_packages[package_name] ~= nil end function VMFMod:is_package_manager_initialized() - return _initialized -end - -function vmf.initialize_package_manager() - for _, mod_data in ipairs(Managers.mod._mods) do - _mod_handles[mod_data.id] = mod_data.handle - end - - _initialized = true + return self:get_data("mod_handle") ~= nil end function vmf.update_package_manager() diff --git a/vmf/scripts/mods/vmf/modules/vmf_mod_manager.lua b/vmf/scripts/mods/vmf/modules/vmf_mod_manager.lua index ef69dd3..9b286c4 100644 --- a/vmf/scripts/mods/vmf/modules/vmf_mod_manager.lua +++ b/vmf/scripts/mods/vmf/modules/vmf_mod_manager.lua @@ -115,7 +115,14 @@ function vmf.initialize_mod_data(mod, mod_data) vmf.set_internal_data(mod, "is_togglable", mod_data.is_togglable or mod_data.is_mutator) vmf.set_internal_data(mod, "is_mutator", mod_data.is_mutator) vmf.set_internal_data(mod, "allow_rehooking", mod_data.allow_rehooking) - vmf.set_internal_data(mod, "workshop_id", mod_data.workshop_id) + + local mod_manager = Managers.mod + local current_mod_load_index = mod_manager._mod_load_index + if current_mod_load_index then + vmf.set_internal_data(mod, "mod_handle", mod_manager._mods[current_mod_load_index].handle) + else + mod:warning("Could not determine current mod load index. Package management won't be available for this mod.") + end -- Register mod as mutator @TODO: calling this after options initialization would be better, I guess? if mod_data.is_mutator then diff --git a/vmf/scripts/mods/vmf/vmf_loader.lua b/vmf/scripts/mods/vmf/vmf_loader.lua index 29b28a7..d3b373c 100644 --- a/vmf/scripts/mods/vmf/vmf_loader.lua +++ b/vmf/scripts/mods/vmf/vmf_loader.lua @@ -65,7 +65,6 @@ function vmf_mod_object:update(dt) if not vmf.all_mods_were_loaded and Managers.mod._state == "done" then vmf.generate_keybinds() - vmf.initialize_package_manager() vmf.initialize_vmf_options_view() vmf.create_network_dictionary() vmf.ping_vmf_users() From 09ce5e281cc388a228e3b33c510d0c1c277bbd80 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 10 Dec 2018 16:04:32 +0100 Subject: [PATCH 04/16] Allow fuly qualified resource package names Stop enforcing a naming convention for mod packages. --- vmf/scripts/mods/vmf/modules/core/packages.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/core/packages.lua b/vmf/scripts/mods/vmf/modules/core/packages.lua index c55cf4a..3006170 100644 --- a/vmf/scripts/mods/vmf/modules/core/packages.lua +++ b/vmf/scripts/mods/vmf/modules/core/packages.lua @@ -21,9 +21,7 @@ function VMFMod:load_package(package_name, callback) _loaded_packages[self] = {} end - local package_handle = string.format("resource_packages/%s/%s", self:get_name(), package_name) - - local resource_package = Mod.resource_package(mod_handle, package_handle) + local resource_package = Mod.resource_package(mod_handle, package_name) local is_loading = self:is_package_loading(package_name) From d0d0d5ef919d6d657af57607b2c033a9f9e69fed Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 10 Dec 2018 16:06:02 +0100 Subject: [PATCH 05/16] Check if package exists before attempting to load --- vmf/scripts/mods/vmf/modules/core/packages.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vmf/scripts/mods/vmf/modules/core/packages.lua b/vmf/scripts/mods/vmf/modules/core/packages.lua index 3006170..554963f 100644 --- a/vmf/scripts/mods/vmf/modules/core/packages.lua +++ b/vmf/scripts/mods/vmf/modules/core/packages.lua @@ -23,6 +23,11 @@ function VMFMod:load_package(package_name, callback) local resource_package = Mod.resource_package(mod_handle, package_name) + if not resource_package then + self:error("Could not find package '%s'.", package_name) + return + end + local is_loading = self:is_package_loading(package_name) if not callback then From f6dce4984a4f26e6fdd1fc0f33df19ad77d8fcd0 Mon Sep 17 00:00:00 2001 From: Manuel Blanc Date: Mon, 10 Dec 2018 16:07:31 +0100 Subject: [PATCH 06/16] Add parameter validation Co-Authored-By: SirAiedail --- vmf/scripts/mods/vmf/modules/core/packages.lua | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/vmf/scripts/mods/vmf/modules/core/packages.lua b/vmf/scripts/mods/vmf/modules/core/packages.lua index 554963f..51844e5 100644 --- a/vmf/scripts/mods/vmf/modules/core/packages.lua +++ b/vmf/scripts/mods/vmf/modules/core/packages.lua @@ -5,6 +5,12 @@ local _loading_package = nil local _loaded_packages = {} function VMFMod:load_package(package_name, callback) + if vmf.check_wrong_argument_type(self, "load_package", "package_name", package_name, "string") or + vmf.check_wrong_argument_type(self, "load_package", "callback", callback, "function", "nil") + then + return + end + if self:has_package_loaded(package_name) then self:error("Package '%s' has already been loaded", package_name) return @@ -54,6 +60,10 @@ function VMFMod:load_package(package_name, callback) end function VMFMod:unload_package(package_name) + if vmf.check_wrong_argument_type(self, "unload_package", "package_name", package_name, "string") then + return + end + if not self:has_package_loaded(package_name) then self:error("Package '%s' has not been loaded", package_name) return @@ -67,6 +77,10 @@ function VMFMod:unload_package(package_name) end function VMFMod:is_package_loading(package_name) + if vmf.check_wrong_argument_type(self, "is_package_loading", "package_name", package_name, "string") then + return + end + if _loading_package and _loading_package.mod == self and _loading_package.package_name == package_name then return true end @@ -81,6 +95,10 @@ function VMFMod:is_package_loading(package_name) end function VMFMod:has_package_loaded(package_name) + if vmf.check_wrong_argument_type(self, "has_package_loaded", "package_name", package_name, "string") then + return + end + local loaded_packages = _loaded_packages[self] return loaded_packages and loaded_packages[package_name] ~= nil end From 83635ecea51e9d082b9a4be9349a46ab918ab4b8 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 10 Dec 2018 16:10:40 +0100 Subject: [PATCH 07/16] Remove useless method --- vmf/scripts/mods/vmf/modules/core/packages.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/core/packages.lua b/vmf/scripts/mods/vmf/modules/core/packages.lua index 51844e5..86035b6 100644 --- a/vmf/scripts/mods/vmf/modules/core/packages.lua +++ b/vmf/scripts/mods/vmf/modules/core/packages.lua @@ -103,10 +103,6 @@ function VMFMod:has_package_loaded(package_name) return loaded_packages and loaded_packages[package_name] ~= nil end -function VMFMod:is_package_manager_initialized() - return self:get_data("mod_handle") ~= nil -end - function vmf.update_package_manager() local loading_package = _loading_package From 26f52622f6e626072839fd7462dfd3759add8018 Mon Sep 17 00:00:00 2001 From: Azumgi <4zumgi@gmail.com> Date: Tue, 11 Dec 2018 14:11:45 +0100 Subject: [PATCH 08/16] Wrap package loading callback in safe call Co-Authored-By: SirAiedail --- vmf/scripts/mods/vmf/modules/core/packages.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vmf/scripts/mods/vmf/modules/core/packages.lua b/vmf/scripts/mods/vmf/modules/core/packages.lua index 86035b6..5d7c228 100644 --- a/vmf/scripts/mods/vmf/modules/core/packages.lua +++ b/vmf/scripts/mods/vmf/modules/core/packages.lua @@ -112,7 +112,8 @@ function vmf.update_package_manager() _loading_package = nil -- The callback has to be called last, so that any calls to `has_package_loaded` or `is_package_loading` return the correct value - loading_package.callback() + vmf.safe_call_nr(loading_package.mod, {"'%s' package loaded callback", loading_package.package_name}, + loading_package.callback, loading_package.package_name) end return From 430fda7912ff711d8fe311f22982313e4be7c990 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 11 Dec 2018 15:36:29 +0100 Subject: [PATCH 09/16] Add function documentation --- .../mods/vmf/modules/core/packages.lua | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/vmf/scripts/mods/vmf/modules/core/packages.lua b/vmf/scripts/mods/vmf/modules/core/packages.lua index 5d7c228..069de5d 100644 --- a/vmf/scripts/mods/vmf/modules/core/packages.lua +++ b/vmf/scripts/mods/vmf/modules/core/packages.lua @@ -4,6 +4,15 @@ local _queued_packages = {} local _loading_package = nil local _loaded_packages = {} +-- ##################################################################################################################### +-- ##### VMFMod ######################################################################################################## +-- ##################################################################################################################### + +--[[ + Loads a mod package. + * package_name [string] : package name. needs to be the full path to the `.package` file without the extension + * callback [function]: (optional) callback for asynchronous loading +--]] function VMFMod:load_package(package_name, callback) if vmf.check_wrong_argument_type(self, "load_package", "package_name", package_name, "string") or vmf.check_wrong_argument_type(self, "load_package", "callback", callback, "function", "nil") @@ -59,6 +68,11 @@ function VMFMod:load_package(package_name, callback) end end + +--[[ + Unlaods a loaded mod package. + * package_name [string]: package name. needs to be the full path to the `.package` file without the extension +--]] function VMFMod:unload_package(package_name) if vmf.check_wrong_argument_type(self, "unload_package", "package_name", package_name, "string") then return @@ -76,6 +90,11 @@ function VMFMod:unload_package(package_name) _loaded_packages[self][package_name] = nil end + +--[[ + Returns whether the mod package is currently being loaded. + * package_name [string]: package name. needs to be the full path to the `.package` file without the extension +--]] function VMFMod:is_package_loading(package_name) if vmf.check_wrong_argument_type(self, "is_package_loading", "package_name", package_name, "string") then return @@ -94,6 +113,11 @@ function VMFMod:is_package_loading(package_name) return false end + +--[[ + Returns whether the mod package has been fully loaded. + * package_name [string]: package name. needs to be the full path to the `.package` file without the extension +--]] function VMFMod:has_package_loaded(package_name) if vmf.check_wrong_argument_type(self, "has_package_loaded", "package_name", package_name, "string") then return @@ -103,6 +127,11 @@ function VMFMod:has_package_loaded(package_name) return loaded_packages and loaded_packages[package_name] ~= nil end +-- ##################################################################################################################### +-- ##### VMF internal functions and variables ########################################################################## +-- ##################################################################################################################### + +-- Loads queued packages one at a time function vmf.update_package_manager() local loading_package = _loading_package From e7141099b5be14ef196817aa86f9512c681fa9d3 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 11 Dec 2018 15:42:16 +0100 Subject: [PATCH 10/16] Rework package loading timing Loading packages asynchronously would unnecessarily skip a frame before starting to load a new package after the previous one finished. --- .../mods/vmf/modules/core/packages.lua | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/core/packages.lua b/vmf/scripts/mods/vmf/modules/core/packages.lua index 069de5d..3240bda 100644 --- a/vmf/scripts/mods/vmf/modules/core/packages.lua +++ b/vmf/scripts/mods/vmf/modules/core/packages.lua @@ -134,26 +134,21 @@ end -- Loads queued packages one at a time function vmf.update_package_manager() local loading_package = _loading_package + if loading_package and loading_package.resource_package:has_loaded() then + _loaded_packages[loading_package.mod][loading_package.package_name] = loading_package.resource_package + _loading_package = nil - if loading_package then - if loading_package.resource_package:has_loaded() then - _loaded_packages[loading_package.mod][loading_package.package_name] = loading_package.resource_package - _loading_package = nil + -- The callback has to be called last, so that any calls to `has_package_loaded` or `is_package_loading` + -- return the correct value + vmf.safe_call_nr(loading_package.mod, {"'%s' package loaded callback", loading_package.package_name}, + loading_package.callback, loading_package.package_name) + else + local queued_package = _queued_packages[1] + if queued_package then + _loading_package = queued_package + table.remove(_queued_packages, 1) - -- The callback has to be called last, so that any calls to `has_package_loaded` or `is_package_loading` return the correct value - vmf.safe_call_nr(loading_package.mod, {"'%s' package loaded callback", loading_package.package_name}, - loading_package.callback, loading_package.package_name) + _loading_package.resource_package:load() end - - return - end - - local queued_package = _queued_packages[1] - - if queued_package then - _loading_package = queued_package - table.remove(_queued_packages, 1) - - _loading_package.resource_package:load() end end From 072a2f183fe0fd0390a5477b2f16586ba5eab3d4 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 11 Dec 2018 15:45:35 +0100 Subject: [PATCH 11/16] Flush packages after loading asynchronously --- vmf/scripts/mods/vmf/modules/core/packages.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vmf/scripts/mods/vmf/modules/core/packages.lua b/vmf/scripts/mods/vmf/modules/core/packages.lua index 3240bda..4047832 100644 --- a/vmf/scripts/mods/vmf/modules/core/packages.lua +++ b/vmf/scripts/mods/vmf/modules/core/packages.lua @@ -135,6 +135,8 @@ end function vmf.update_package_manager() local loading_package = _loading_package if loading_package and loading_package.resource_package:has_loaded() then + loading_package.resource_package:flush() + _loaded_packages[loading_package.mod][loading_package.package_name] = loading_package.resource_package _loading_package = nil From e9502a1adda4b6ac7301e8bfad5990602952bf00 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 11 Dec 2018 15:50:28 +0100 Subject: [PATCH 12/16] Make package loading asynchronous by default --- vmf/scripts/mods/vmf/modules/core/packages.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/core/packages.lua b/vmf/scripts/mods/vmf/modules/core/packages.lua index 4047832..8677fce 100644 --- a/vmf/scripts/mods/vmf/modules/core/packages.lua +++ b/vmf/scripts/mods/vmf/modules/core/packages.lua @@ -1,5 +1,7 @@ local vmf = get_mod("VMF") +local NOOP = function() end + local _queued_packages = {} local _loading_package = nil local _loaded_packages = {} @@ -12,10 +14,12 @@ local _loaded_packages = {} Loads a mod package. * package_name [string] : package name. needs to be the full path to the `.package` file without the extension * callback [function]: (optional) callback for asynchronous loading + * sync [boolean] : (optional) load the packages synchronously, freezing the game until it is loaded --]] -function VMFMod:load_package(package_name, callback) +function VMFMod:load_package(package_name, callback, sync) if vmf.check_wrong_argument_type(self, "load_package", "package_name", package_name, "string") or - vmf.check_wrong_argument_type(self, "load_package", "callback", callback, "function", "nil") + vmf.check_wrong_argument_type(self, "load_package", "callback", callback, "function", "nil") or + vmf.check_wrong_argument_type(self, "load_package", "sync", sync, "boolean", "nil") then return end @@ -26,7 +30,6 @@ function VMFMod:load_package(package_name, callback) end local mod_handle = self:get_internal_data("mod_handle") - if not mod_handle then self:error("Failed to get mod handle. Package management is not available.") return @@ -37,7 +40,6 @@ function VMFMod:load_package(package_name, callback) end local resource_package = Mod.resource_package(mod_handle, package_name) - if not resource_package then self:error("Could not find package '%s'.", package_name) return @@ -45,7 +47,7 @@ function VMFMod:load_package(package_name, callback) local is_loading = self:is_package_loading(package_name) - if not callback then + if sync then if not is_loading then resource_package:load() end @@ -63,7 +65,7 @@ function VMFMod:load_package(package_name, callback) mod = self, package_name = package_name, resource_package = resource_package, - callback = callback, + callback = callback or NOOP, }) end end From 115dd5eddcbc5ac4c095074d3bf08b97dc4ef74d Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 11 Dec 2018 16:06:53 +0100 Subject: [PATCH 13/16] Forcefully unload packages on VMF reload --- .../mods/vmf/modules/core/packages.lua | 26 +++++++++++++++++++ vmf/scripts/mods/vmf/vmf_loader.lua | 1 + 2 files changed, 27 insertions(+) diff --git a/vmf/scripts/mods/vmf/modules/core/packages.lua b/vmf/scripts/mods/vmf/modules/core/packages.lua index 8677fce..8f12b85 100644 --- a/vmf/scripts/mods/vmf/modules/core/packages.lua +++ b/vmf/scripts/mods/vmf/modules/core/packages.lua @@ -156,3 +156,29 @@ function vmf.update_package_manager() end end end + +-- Forcefully unloads all mods and cleans the queue. +function vmf.unload_all_resource_packages() + for mod, packages in pairs(_loaded_packages) do + for package_name in pairs(packages) do + mod:warning( + "Force-unloading package '%s'. Please make sure to properly release packages when the mod is unloaded", + package_name + ) + mod:unload_package(package_name) + end + end + + _queued_packages = {} + + if _loading_package then + _loading_package.mod:warning( + "Still loading package '%s'. Memory leaks may occur when unloading while a package is loading.", + _loading_package.package_name + ) + + _loading_package.callback = function(package_name) + _loading_package.mod:unload_package(package_name) + end + end +end diff --git a/vmf/scripts/mods/vmf/vmf_loader.lua b/vmf/scripts/mods/vmf/vmf_loader.lua index d3b373c..ef530ee 100644 --- a/vmf/scripts/mods/vmf/vmf_loader.lua +++ b/vmf/scripts/mods/vmf/vmf_loader.lua @@ -92,6 +92,7 @@ function vmf_mod_object:on_reload() if VT1 then vmf.reset_map_view() end vmf.mods_unload_event(false) vmf.remove_custom_views() + vmf.unload_all_resource_packages() vmf.hooks_unload() vmf.reset_guis() end From 2c7828e6a579dbc6abea573ba418c032522f7cc2 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 11 Dec 2018 16:14:40 +0100 Subject: [PATCH 14/16] Rename package manager module --- .../vmf/modules/{core/packages.lua => vmf_package_manager.lua} | 0 vmf/scripts/mods/vmf/vmf_loader.lua | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename vmf/scripts/mods/vmf/modules/{core/packages.lua => vmf_package_manager.lua} (100%) diff --git a/vmf/scripts/mods/vmf/modules/core/packages.lua b/vmf/scripts/mods/vmf/modules/vmf_package_manager.lua similarity index 100% rename from vmf/scripts/mods/vmf/modules/core/packages.lua rename to vmf/scripts/mods/vmf/modules/vmf_package_manager.lua diff --git a/vmf/scripts/mods/vmf/vmf_loader.lua b/vmf/scripts/mods/vmf/vmf_loader.lua index ef530ee..2247abd 100644 --- a/vmf/scripts/mods/vmf/vmf_loader.lua +++ b/vmf/scripts/mods/vmf/vmf_loader.lua @@ -13,6 +13,7 @@ local vmf_mod_object = {} function vmf_mod_object:init() dofile("scripts/mods/vmf/modules/vmf_mod_data") dofile("scripts/mods/vmf/modules/vmf_mod_manager") + dofile("scripts/mods/vmf/modules/vmf_package_manager") dofile("scripts/mods/vmf/modules/core/safe_calls") dofile("scripts/mods/vmf/modules/core/events") dofile("scripts/mods/vmf/modules/core/settings") @@ -29,7 +30,6 @@ function vmf_mod_object:init() dofile("scripts/mods/vmf/modules/core/options") dofile("scripts/mods/vmf/modules/legacy/options") dofile("scripts/mods/vmf/modules/core/network") - dofile("scripts/mods/vmf/modules/core/packages") dofile("scripts/mods/vmf/modules/core/commands") dofile("scripts/mods/vmf/modules/gui/custom_textures") dofile("scripts/mods/vmf/modules/gui/custom_views") From b7569e2c41086d7be89aa030c4f7c6a74d2bf01c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 11 Dec 2018 16:16:27 +0100 Subject: [PATCH 15/16] Remove final new lines --- vmf/scripts/mods/vmf/modules/vmf_package_manager.lua | 2 +- vmf/scripts/mods/vmf/vmf_loader.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/vmf_package_manager.lua b/vmf/scripts/mods/vmf/modules/vmf_package_manager.lua index 8f12b85..d8bc7e7 100644 --- a/vmf/scripts/mods/vmf/modules/vmf_package_manager.lua +++ b/vmf/scripts/mods/vmf/modules/vmf_package_manager.lua @@ -181,4 +181,4 @@ function vmf.unload_all_resource_packages() _loading_package.mod:unload_package(package_name) end end -end +end \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/vmf_loader.lua b/vmf/scripts/mods/vmf/vmf_loader.lua index 2247abd..018b7d9 100644 --- a/vmf/scripts/mods/vmf/vmf_loader.lua +++ b/vmf/scripts/mods/vmf/vmf_loader.lua @@ -114,4 +114,4 @@ end -- ##### Return ######################################################################################################## -- ##################################################################################################################### -return vmf_mod_object +return vmf_mod_object \ No newline at end of file From f113736db7339250734e8b9de382565d098b865d Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 11 Dec 2018 17:05:09 +0100 Subject: [PATCH 16/16] Fix package loading timing --- .../mods/vmf/modules/vmf_package_manager.lua | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/vmf_package_manager.lua b/vmf/scripts/mods/vmf/modules/vmf_package_manager.lua index d8bc7e7..704364c 100644 --- a/vmf/scripts/mods/vmf/modules/vmf_package_manager.lua +++ b/vmf/scripts/mods/vmf/modules/vmf_package_manager.lua @@ -146,17 +146,18 @@ function vmf.update_package_manager() -- return the correct value vmf.safe_call_nr(loading_package.mod, {"'%s' package loaded callback", loading_package.package_name}, loading_package.callback, loading_package.package_name) - else - local queued_package = _queued_packages[1] - if queued_package then - _loading_package = queued_package - table.remove(_queued_packages, 1) + end - _loading_package.resource_package:load() - end + local queued_package = _queued_packages[1] + if queued_package and not _loading_package then + _loading_package = queued_package + table.remove(_queued_packages, 1) + + _loading_package.resource_package:load() end end + -- Forcefully unloads all mods and cleans the queue. function vmf.unload_all_resource_packages() for mod, packages in pairs(_loaded_packages) do