---@class Loader local Loader = {} Loader.debugEnabled = false Loader.KEEP_OUT_MESSAGE = [[ -- ############################################################## -- This file was loaded through the Dynamic Plugin System. -- -- This file is not meant to be edited, and any changes made to -- this file will be overwritten the next time the plugin is -- loaded. -- -- thanks! - gart -- ############################################################## ]] ---@type Plugin Loader.plugin = ... ---@type {[string]: any} Loader.moduleCache = {} Loader.hasLoadedModules = false Loader.assetHost = { host = "https://assets.jpxs.io", path = "/plugins/", } Loader.bin = { host = "https://bin.gart.sh", } Loader.storagePath = ".jpxs/" Loader.pluginId = "JPXS" ---@param text string print logs function Loader:print(text) print("\27[30;1m[" .. os.date("%X") .. "]\27[0m \27[38;5;202m[" .. Loader.pluginId .. "]\27[0m " .. text) end ---@param text string print logs function Loader:debug(text) if Loader.debugEnabled then Loader:print(text) end end ---@param path string function Loader:requireOrThrow(path) local success, module = pcall(require, path) if not success then error("DPL: Failed to require " .. path .. " (" .. module .. ")") end return module end ---@param content string ---@param fileName string ---@return string function Loader.addFileHeader(content, fileName) return "--" .. fileName .. ".lua\n\n" .. Loader.KEEP_OUT_MESSAGE .. "\n" .. content end ---@private ---@param path string ---@param body string ---@param cb fun(name: string, module: any)? function Loader:loadModule(path, body, cb) local file = Loader.addFileHeader(body, path) Loader.moduleCache[path] = loadstring(file)(Loader) Loader:debug(string.format("Downloaded module %s", path)) if cb then cb(path, Loader.moduleCache[path]) end end ---@param path string ---@param cb fun(name: string, module: any)? ---@param showError? boolean function Loader:downloadModule(path, cb, showError) http.get(Loader.assetHost.host, Loader.assetHost.path .. "/" .. path .. ".lua", {}, function(response) if response and response.status == 200 then Loader:loadModule(path, response.body, cb) else (showError and Loader.print or Loader.debug)( Loader, string.format("Failed to download module %s (%s)", path, response and response.status or "no response") ) end end) end ---@param id string ---@param cb fun(name: string, module: any)? ---@param showError? boolean function Loader:loadGartBin(id, cb, showError) http.get(Loader.bin.host, "/" .. id .. "/raw", {}, function(response) if response and response.status == 200 then Loader:loadModule(id, response.body, cb) else (showError and Loader.print or Loader.debug)( Loader, string.format("Failed to download bin %s (%s)", id, response and response.status or "no response") ) end end) end ---@param id string ---@param cb fun(name: string, module: any)? function Loader:getOrDownloadModule(id, cb) if Loader.moduleCache[id] then if cb then cb(id, Loader.moduleCache[id]) end else Loader:downloadModule(id, cb) end end ---@param id string ---@return any function Loader:getModule(id) return Loader.moduleCache[id] end ---@param modules string[] ---@param cb fun(...: any[])? function Loader:getDependencies(modules, cb) local neededToLoad = {} for _, name in ipairs(modules) do table.insert(neededToLoad, name) end local function onLoad(name) table.remove(neededToLoad, table.find(neededToLoad, name)) if #neededToLoad == 0 then local deps = {} for _, name in pairs(modules) do table.insert(deps, Loader.moduleCache[name]) end if cb then cb(table.unpack(deps)) end end end for _, name in pairs(neededToLoad) do Loader:getOrDownloadModule(name, onLoad) end end ---@param id string ---@param cb fun(content: table) function Loader:loadJson(id, cb) http.get( Loader.assetHost.host, Loader.assetHost.path .. "/" .. Loader.pluginId .. "/" .. id .. ".json", {}, function(response) local json = Loader:requireOrThrow("main.json") if response and response.status == 200 then local content = json.decode(response.body) Loader:debug(string.format("Downloaded json %s", id)) cb(content) else Loader:print( string.format("Failed to download json %s (%s)", id, response and response.status or "no response") ) end end ) end ---@class LoaderOptions ---@field initialPluginFile string? default: "init" ---@field debug boolean? default: false ---@field storagePath string? default: ".jpxs/" ---@field assetHost string? default: "https://assets.jpxs.io" ---@field assetDir string? default: "/plugins/" ---@param pluginId string ---@param options LoaderOptions? function Loader:load(pluginId, options) options = options or {} Loader.debugEnabled = options.debug or false Loader.storagePath = options.storagePath or ".jpxs/" Loader.assetHost.host = options.assetHost or "https://assets.jpxs.io" Loader.assetHost.path = options.assetDir or "/plugins/" Loader.pluginId = pluginId options.initialPluginFile = options.initialPluginFile or "init" Loader:print("Loading plugin " .. pluginId) Loader:downloadModule(options.initialPluginFile, function(_, moduleLoaded) if moduleLoaded ~= nil then Loader.hasLoadedModules = true Loader:print("Loaded plugin " .. pluginId) else Loader:print("Failed to load plugin " .. pluginId) end end) end return Loader