Reworked mods initialization process.

Reworked 'new_mod' functionality.
Reworked pcalls (added error callstack).
Got rid of 'mod:initialize', 'mod:initialize_data', 'mod:localization'.
This commit is contained in:
bi 2018-05-28 17:44:38 +03:00
parent c2dabc69ac
commit 1fbe207930
4 changed files with 202 additions and 82 deletions

View file

@ -21,10 +21,6 @@ local function safe_format(mod, str, ...)
end
end
local function pack_pcall(status, ...)
return status, {...}
end
local function send_to_chat(message)
@ -96,33 +92,6 @@ function VMFMod:debug(message, ...)
end
function VMFMod:pcall(...)
local status, values = pack_pcall(pcall(...))
if not status then
self:error("(pcall): %s", tostring(values[1]))
end
return status, unpack(values)
end
function VMFMod:dofile(script_path)
local success, values = pack_pcall(pcall(dofile, script_path))
if not success then
if values[1].error then
self:error("(dofile): %s", values[1].error)
print("\nTRACEBACK:\n\n" .. values[1].traceback .. "\nLOCALS:\n\n" .. values[1].locals)
else
self:error("(dofile): %s", tostring(values[1]))
end
end
return unpack(values)
end
-- ####################################################################################################################
-- ##### VMF internal functions and variables #########################################################################

View file

@ -34,28 +34,6 @@ end
-- ##### VMFMod #######################################################################################################
-- ####################################################################################################################
VMFMod.localization = function (self, path)
local success, value = pcall(dofile, path)
if not success then
self:error("(localization): %s", value.error)
return
end
if type(value) ~= "table" then
self:error("(localization): localization file should return table")
return
end
if _LOCALIZATION_DATABASE[self:get_name()] then
self:warning("(localization): overwritting already loaded localization file")
end
_LOCALIZATION_DATABASE[self:get_name()] = value
end
VMFMod.localize = function (self, text_id, ...)
local mod_localization_table = _LOCALIZATION_DATABASE[self:get_name()]
@ -89,8 +67,27 @@ VMFMod.localize = function (self, text_id, ...)
return "<" .. tostring(text_id) .. ">"
end
-- ####################################################################################################################
-- ##### VMF internal functions and variables #########################################################################
-- ####################################################################################################################
vmf.load_mod_localization = function (mod, localization_table)
if type(localization_table) ~= "table" then
mod:error("(localization): localization file should return table")
return
end
if _LOCALIZATION_DATABASE[mod:get_name()] then
mod:warning("(localization): overwritting already loaded localization file")
end
_LOCALIZATION_DATABASE[mod:get_name()] = localization_table
end
-- ####################################################################################################################
-- ##### Script #######################################################################################################
-- ####################################################################################################################
vmf:localization("localization/vmf")
local localization_table = vmf:dofile("localization/vmf")
vmf.load_mod_localization(vmf, localization_table)

View file

@ -4,29 +4,118 @@ local _MODS = {}
local _MODS_UNLOADING_ORDER = {}
-- ####################################################################################################################
-- ##### Public functions #############################################################################################
-- ##### Local functions ##############################################################################################
-- ####################################################################################################################
function new_mod(mod_name)
-- WORKING WITH XPCALL
if type(mod_name) ~= "string" then
vmf:error("(new_mod): the mod name should be the string, not '%s'", type(mod_name)) -- @EARLY_CALL:
return nil
end
local function pack_pcall(status, ...)
return status, {n = select('#', ...), ...}
end
local function print_error_callstack(error_message)
if type(error_message) == "table" and error_message.error then
error_message = error_message.error
end
print("Error: " .. tostring(error_message) .. "\n" .. Script.callstack())
return error_message
end
-- CREATING MOD
local function create_mod(mod_name)
if _MODS[mod_name] then
vmf:error("(new_mod): you can't use name \"%s\" for your mod, because the mod with the same name already exists", mod_name) -- @EARLY_CALL:
return nil
vmf:error("(new_mod): you can't use name \"%s\" for your mod, because " ..
"the mod with the same name already exists.", mod_name)
return
end
table.insert(_MODS_UNLOADING_ORDER, 1, mod_name)
local mod = VMFMod:new(mod_name)
local mod = VMFMod:new()
_MODS[mod_name] = mod
mod._data = {}
mod._data.name = mod_name
mod._data.readable_name = mod_name
mod._data.is_enabled = true
mod._data.is_togglable = false
mod._data.is_mutator = false
return mod
end
-- ####################################################################################################################
-- ##### Public functions #############################################################################################
-- ####################################################################################################################
function new_mod(mod_name, mod_resources)
-- Checking for correct arguments
if type(mod_name) ~= "string" then
vmf:error("(new_mod): the mod name should be the string, not '%s'.", type(mod_name))
return
end
if type(mod_resources) ~= "table" then
--vmf:error("(new_mod): 'mod_resources' argument should have the 'table' type, not '%s'", type(mod_resources))
--return
vmf:error("ERROR: '%s' can't be loaded. Wait until its author updates their mod to the newest VMF structure.",
mod_name)
return {
localization = function() end,
initialize = function() end
}
end
if not mod_resources.mod_script then
vmf:error("(new_mod): 'mod_resources' table should have 'mod_script' field.", type(mod_name))
return
end
-- Creating a mod object
local mod = create_mod(mod_name)
if not mod then
return
end
-- Load localization data file
if mod_resources.mod_localization then
local success, localization_table = vmf.xpcall_dofile(mod, "(new_mod)('mod_localization' initialization)",
mod_resources.mod_localization)
if success then
vmf.load_mod_localization(mod, localization_table)
else
return
end
end
-- Load mod data file
if mod_resources.mod_data then
local success, mod_data_table = vmf.xpcall_dofile(mod, "(new_mod)('mod_data' initialization)",
mod_resources.mod_data)
if success then
vmf.initialize_mod_data(mod, mod_data_table)
else
return
end
end
-- Load mod
if not vmf.xpcall_dofile(mod, "(new_mod)('mod_script' initialization)", mod_resources.mod_script) then
return
end
-- Initialize mod state
if mod:is_togglable() then
vmf.initialize_mod_state(mod)
end
end
function get_mod(mod_name)
return _MODS[mod_name]
end
@ -37,55 +126,120 @@ end
VMFMod = class(VMFMod)
VMFMod.init = function (self, mod_name)
self._data = {}
-- @TODO: forbid changing _data table
self._data.name = mod_name
self._data.readable_name = mod_name
self._data.is_enabled = true
self._data.is_togglable = false
self._data.is_mutator = false
end
-- DATA
VMFMod.get_name = function (self)
function VMFMod:get_name()
return self._data.name
end
VMFMod.get_readable_name = function (self)
function VMFMod:get_readable_name()
return self._data.readable_name
end
VMFMod.get_description = function (self)
function VMFMod:get_description()
return self._data.description
end
VMFMod.is_enabled = function (self)
function VMFMod:is_enabled()
return self._data.is_enabled
end
VMFMod.is_togglable = function (self)
function VMFMod:is_togglable()
return self._data.is_togglable
end
VMFMod.is_mutator = function (self)
function VMFMod:is_mutator()
return self._data.is_mutator
end
VMFMod.get_config = function (self)
function VMFMod:get_config()
return self._data.config
end
-- CORE FUNCTIONS
function VMFMod:pcall(...)
return vmf.xpcall(self, "(pcall)", ...)
end
function VMFMod:dofile(file_path)
local _, return_values = pack_pcall(vmf.xpcall_dofile(self, "(dofile)", file_path))
return unpack(return_values, 1, return_values.n)
end
-- ####################################################################################################################
-- ##### VMF Initialization ###########################################################################################
-- ####################################################################################################################
vmf = new_mod("VMF")
vmf = create_mod("VMF")
-- ####################################################################################################################
-- ##### VMF internal functions and variables #########################################################################
-- ####################################################################################################################
-- XPCALL
function vmf.xpcall(mod, error_prefix, func, ...)
local success, return_values = pack_pcall(xpcall(func, print_error_callstack, ...))
if not success then
mod:error("%s: %s", error_prefix, return_values[1])
return success
end
return success, unpack(return_values, 1, return_values.n)
end
function vmf.xpcall_no_return_values(mod, error_prefix, func, ...)
local success, error_message = xpcall(func, print_error_callstack, ...)
if not success then
mod:error("%s: %s", error_prefix, error_message)
end
return success
end
function vmf.xpcall_dofile(mod, error_prefix, file_path)
if type(file_path) ~= "string" then
mod:error("%s: file path should be a string.", error_prefix)
return false
end
return vmf.xpcall(mod, error_prefix, dofile, file_path)
end
-- MOD DATA INITIALIZATION
function vmf.initialize_mod_data(mod, mod_data)
if type(mod_data) ~= "table" then
mod:error("(new_mod)(mod_data initialization): mod_data file should return a 'table' value.")
return
end
if mod_data.name then
mod._data.readable_name = mod_data.name
end
mod._data.description = mod_data.description
mod._data.is_togglable = mod_data.is_togglable or mod_data.is_mutator
mod._data.is_mutator = mod_data.is_mutator
if mod_data.is_mutator then
vmf.register_mod_as_mutator(mod, mod_data.mutator_settings)
end
if mod_data.options_widgets or (mod_data.is_togglable and not mod_data.is_mutator) then
vmf.create_options(mod, mod_data.options_widgets)
end
end
-- VARIABLES
vmf.mods = _MODS
vmf.mods_unloading_order = _MODS_UNLOADING_ORDER

View file

@ -270,7 +270,7 @@ end
-- ##### Script #######################################################################################################
-- ####################################################################################################################
vmf:initialize_data(vmf_mod_data)
vmf.initialize_mod_data(vmf, vmf_mod_data)
-- first VMF initialization
-- it will be run only 1 time, when the player launch the game with VMF for the first time