Implement VMF Package Manager (#29)
This commit is contained in:
commit
7e2faac683
3 changed files with 197 additions and 1 deletions
|
@ -116,6 +116,14 @@ function vmf.initialize_mod_data(mod, mod_data)
|
|||
vmf.set_internal_data(mod, "is_mutator", mod_data.is_mutator)
|
||||
vmf.set_internal_data(mod, "allow_rehooking", mod_data.allow_rehooking)
|
||||
|
||||
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
|
||||
vmf.register_mod_as_mutator(mod, mod_data.mutator_settings)
|
||||
|
@ -163,4 +171,4 @@ end
|
|||
-- VARIABLES
|
||||
|
||||
vmf.mods = _mods
|
||||
vmf.mods_unloading_order = _mods_unloading_order
|
||||
vmf.mods_unloading_order = _mods_unloading_order
|
||||
|
|
185
vmf/scripts/mods/vmf/modules/vmf_package_manager.lua
Normal file
185
vmf/scripts/mods/vmf/modules/vmf_package_manager.lua
Normal file
|
@ -0,0 +1,185 @@
|
|||
local vmf = get_mod("VMF")
|
||||
|
||||
local NOOP = function() end
|
||||
|
||||
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
|
||||
* sync [boolean] : (optional) load the packages synchronously, freezing the game until it is loaded
|
||||
--]]
|
||||
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") or
|
||||
vmf.check_wrong_argument_type(self, "load_package", "sync", sync, "boolean", "nil")
|
||||
then
|
||||
return
|
||||
end
|
||||
|
||||
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 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 sync then
|
||||
if not is_loading then
|
||||
resource_package:load()
|
||||
end
|
||||
|
||||
resource_package:flush()
|
||||
|
||||
_loaded_packages[self][package_name] = resource_package
|
||||
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 or NOOP,
|
||||
})
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
--[[
|
||||
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
|
||||
end
|
||||
|
||||
local loaded_packages = _loaded_packages[self]
|
||||
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
|
||||
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
|
||||
|
||||
-- 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)
|
||||
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
|
||||
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
|
|
@ -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")
|
||||
|
@ -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()
|
||||
|
@ -90,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
|
||||
|
|
Loading…
Add table
Reference in a new issue