From 1fbe20793050aa5437528f114142dcfcf739cf64 Mon Sep 17 00:00:00 2001 From: bi Date: Mon, 28 May 2018 17:44:38 +0300 Subject: [PATCH] Reworked mods initialization process. Reworked 'new_mod' functionality. Reworked pcalls (added error callstack). Got rid of 'mod:initialize', 'mod:initialize_data', 'mod:localization'. --- .../mods/vmf/modules/core/core_functions.lua | 31 --- .../mods/vmf/modules/core/localization.lua | 43 ++-- vmf/scripts/mods/vmf/modules/mods.lua | 208 +++++++++++++++--- vmf/scripts/mods/vmf/modules/vmf_options.lua | 2 +- 4 files changed, 202 insertions(+), 82 deletions(-) diff --git a/vmf/scripts/mods/vmf/modules/core/core_functions.lua b/vmf/scripts/mods/vmf/modules/core/core_functions.lua index ae29274..51da0f6 100644 --- a/vmf/scripts/mods/vmf/modules/core/core_functions.lua +++ b/vmf/scripts/mods/vmf/modules/core/core_functions.lua @@ -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 ######################################################################### diff --git a/vmf/scripts/mods/vmf/modules/core/localization.lua b/vmf/scripts/mods/vmf/modules/core/localization.lua index 63a4c2e..5c6cd83 100644 --- a/vmf/scripts/mods/vmf/modules/core/localization.lua +++ b/vmf/scripts/mods/vmf/modules/core/localization.lua @@ -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") \ No newline at end of file +local localization_table = vmf:dofile("localization/vmf") +vmf.load_mod_localization(vmf, localization_table) \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/mods.lua b/vmf/scripts/mods/vmf/modules/mods.lua index 822b6e0..8407447 100644 --- a/vmf/scripts/mods/vmf/modules/mods.lua +++ b/vmf/scripts/mods/vmf/modules/mods.lua @@ -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 \ No newline at end of file diff --git a/vmf/scripts/mods/vmf/modules/vmf_options.lua b/vmf/scripts/mods/vmf/modules/vmf_options.lua index 76da811..34f1245 100644 --- a/vmf/scripts/mods/vmf/modules/vmf_options.lua +++ b/vmf/scripts/mods/vmf/modules/vmf_options.lua @@ -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