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