- Added a new system to dynamically require source from multiple locations (including web)
- Added the Collection Element and moved parts of the List logic to collection - Added a State Management System - Added a better system to change the position/size of elements - Removed the state plugin
This commit is contained in:
@@ -17,6 +17,16 @@ local ElementManager = {}
|
||||
ElementManager._elements = {}
|
||||
ElementManager._plugins = {}
|
||||
ElementManager._APIs = {}
|
||||
ElementManager._config = {
|
||||
autoLoadMissing = false,
|
||||
allowRemoteLoading = false,
|
||||
allowDiskLoading = true,
|
||||
remoteSources = {},
|
||||
diskMounts = {},
|
||||
useGlobalCache = false,
|
||||
globalCacheName = "_BASALT_ELEMENT_CACHE"
|
||||
}
|
||||
|
||||
local elementsDirectory = fs.combine(dir, "elements")
|
||||
local pluginsDirectory = fs.combine(dir, "plugins")
|
||||
|
||||
@@ -29,7 +39,9 @@ if fs.exists(elementsDirectory) then
|
||||
ElementManager._elements[name] = {
|
||||
class = nil,
|
||||
plugins = {},
|
||||
loaded = false
|
||||
loaded = false,
|
||||
source = "local",
|
||||
path = nil
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -66,7 +78,9 @@ if(minified)then
|
||||
ElementManager._elements[name:gsub(".lua", "")] = {
|
||||
class = nil,
|
||||
plugins = {},
|
||||
loaded = false
|
||||
loaded = false,
|
||||
source = "local",
|
||||
path = nil
|
||||
}
|
||||
end
|
||||
if(minified_pluginDirectory==nil)then
|
||||
@@ -90,20 +104,225 @@ if(minified)then
|
||||
end
|
||||
end
|
||||
|
||||
local function saveToGlobalCache(name, element)
|
||||
if not ElementManager._config.useGlobalCache then return end
|
||||
|
||||
if not _G[ElementManager._config.globalCacheName] then
|
||||
_G[ElementManager._config.globalCacheName] = {}
|
||||
end
|
||||
|
||||
_G[ElementManager._config.globalCacheName][name] = element
|
||||
log.debug("Cached element in _G: "..name)
|
||||
end
|
||||
|
||||
local function loadFromGlobalCache(name)
|
||||
if not ElementManager._config.useGlobalCache then return nil end
|
||||
|
||||
if _G[ElementManager._config.globalCacheName] and
|
||||
_G[ElementManager._config.globalCacheName][name] then
|
||||
log.debug("Loaded element from _G cache: "..name)
|
||||
return _G[ElementManager._config.globalCacheName][name]
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Configures the ElementManager
|
||||
--- @param config table Configuration options
|
||||
function ElementManager.configure(config)
|
||||
for k, v in pairs(config) do
|
||||
if ElementManager._config[k] ~= nil then
|
||||
ElementManager._config[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Registers a disk mount point for loading elements
|
||||
--- @param mountPath string The path to the disk mount
|
||||
function ElementManager.registerDiskMount(mountPath)
|
||||
if not fs.exists(mountPath) then
|
||||
error("Disk mount path does not exist: "..mountPath)
|
||||
end
|
||||
table.insert(ElementManager._config.diskMounts, mountPath)
|
||||
log.info("Registered disk mount: "..mountPath)
|
||||
|
||||
local elementsPath = fs.combine(mountPath, "elements")
|
||||
if fs.exists(elementsPath) then
|
||||
for _, file in ipairs(fs.list(elementsPath)) do
|
||||
local name = file:match("(.+).lua")
|
||||
if name then
|
||||
if not ElementManager._elements[name] then
|
||||
log.debug("Found element on disk: "..name)
|
||||
ElementManager._elements[name] = {
|
||||
class = nil,
|
||||
plugins = {},
|
||||
loaded = false,
|
||||
source = "disk",
|
||||
path = fs.combine(elementsPath, file)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Registers a remote source for an element
|
||||
--- @param elementName string The name of the element
|
||||
--- @param url string The URL to load the element from
|
||||
function ElementManager.registerRemoteSource(elementName, url)
|
||||
if not ElementManager._config.allowRemoteLoading then
|
||||
error("Remote loading is disabled. Enable with ElementManager.configure({allowRemoteLoading = true})")
|
||||
end
|
||||
ElementManager._config.remoteSources[elementName] = url
|
||||
|
||||
if not ElementManager._elements[elementName] then
|
||||
ElementManager._elements[elementName] = {
|
||||
class = nil,
|
||||
plugins = {},
|
||||
loaded = false,
|
||||
source = "remote",
|
||||
path = url
|
||||
}
|
||||
else
|
||||
ElementManager._elements[elementName].source = "remote"
|
||||
ElementManager._elements[elementName].path = url
|
||||
end
|
||||
|
||||
log.info("Registered remote source for "..elementName..": "..url)
|
||||
end
|
||||
|
||||
local function loadFromRemote(url)
|
||||
if not http then
|
||||
error("HTTP API is not available. Enable it in your CC:Tweaked config.")
|
||||
end
|
||||
|
||||
log.info("Loading element from remote: "..url)
|
||||
|
||||
local response = http.get(url)
|
||||
if not response then
|
||||
error("Failed to download from: "..url)
|
||||
end
|
||||
|
||||
local content = response.readAll()
|
||||
response.close()
|
||||
|
||||
if not content or content == "" then
|
||||
error("Empty response from: "..url)
|
||||
end
|
||||
|
||||
local func, err = load(content, url, "t", _ENV)
|
||||
if not func then
|
||||
error("Failed to load element from "..url..": "..tostring(err))
|
||||
end
|
||||
|
||||
local element = func()
|
||||
return element
|
||||
end
|
||||
|
||||
local function loadFromDisk(path)
|
||||
if not fs.exists(path) then
|
||||
error("Element file does not exist: "..path)
|
||||
end
|
||||
|
||||
log.info("Loading element from disk: "..path)
|
||||
|
||||
local func, err = loadfile(path)
|
||||
if not func then
|
||||
error("Failed to load element from "..path..": "..tostring(err))
|
||||
end
|
||||
|
||||
local element = func()
|
||||
return element
|
||||
end
|
||||
|
||||
--- Tries to load an element from any available source
|
||||
--- @param name string The element name
|
||||
--- @return boolean success Whether the element was loaded
|
||||
function ElementManager.tryAutoLoad(name)
|
||||
-- Try disk mounts first
|
||||
if ElementManager._config.allowDiskLoading then
|
||||
for _, mountPath in ipairs(ElementManager._config.diskMounts) do
|
||||
local elementsPath = fs.combine(mountPath, "elements")
|
||||
local filePath = fs.combine(elementsPath, name..".lua")
|
||||
|
||||
if fs.exists(filePath) then
|
||||
ElementManager._elements[name] = {
|
||||
class = nil,
|
||||
plugins = {},
|
||||
loaded = false,
|
||||
source = "disk",
|
||||
path = filePath
|
||||
}
|
||||
ElementManager.loadElement(name)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if ElementManager._config.allowRemoteLoading and ElementManager._config.remoteSources[name] then
|
||||
ElementManager.loadElement(name)
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--- Loads an element by name. This will load the element and apply any plugins to it.
|
||||
--- @param name string The name of the element to load
|
||||
--- @usage ElementManager.loadElement("Button")
|
||||
function ElementManager.loadElement(name)
|
||||
if not ElementManager._elements[name] then
|
||||
-- Try to auto-load if enabled
|
||||
if ElementManager._config.autoLoadMissing then
|
||||
local success = ElementManager.tryAutoLoad(name)
|
||||
if not success then
|
||||
error("Element '"..name.."' not found and could not be auto-loaded")
|
||||
end
|
||||
else
|
||||
error("Element '"..name.."' not found")
|
||||
end
|
||||
end
|
||||
|
||||
if not ElementManager._elements[name].loaded then
|
||||
package.path = main.."rom/?"
|
||||
local element = require(fs.combine("elements", name))
|
||||
package.path = defaultPath
|
||||
local source = ElementManager._elements[name].source or "local"
|
||||
local element
|
||||
local loadedFromCache = false
|
||||
|
||||
element = loadFromGlobalCache(name)
|
||||
if element then
|
||||
loadedFromCache = true
|
||||
log.info("Loaded element from _G cache: "..name)
|
||||
elseif source == "local" then
|
||||
package.path = main.."rom/?"
|
||||
element = require(fs.combine("elements", name))
|
||||
package.path = defaultPath
|
||||
elseif source == "disk" then
|
||||
if not ElementManager._config.allowDiskLoading then
|
||||
error("Disk loading is disabled for element: "..name)
|
||||
end
|
||||
element = loadFromDisk(ElementManager._elements[name].path)
|
||||
saveToGlobalCache(name, element)
|
||||
elseif source == "remote" then
|
||||
if not ElementManager._config.allowRemoteLoading then
|
||||
error("Remote loading is disabled for element: "..name)
|
||||
end
|
||||
element = loadFromRemote(ElementManager._elements[name].path)
|
||||
saveToGlobalCache(name, element)
|
||||
else
|
||||
error("Unknown source type: "..source)
|
||||
end
|
||||
|
||||
ElementManager._elements[name] = {
|
||||
class = element,
|
||||
plugins = element.plugins,
|
||||
loaded = true
|
||||
loaded = true,
|
||||
source = loadedFromCache and "cache" or source,
|
||||
path = ElementManager._elements[name].path
|
||||
}
|
||||
log.debug("Loaded element: "..name)
|
||||
|
||||
if not loadedFromCache then
|
||||
log.debug("Loaded element: "..name.." from "..source)
|
||||
end
|
||||
|
||||
if(ElementManager._plugins[name]~=nil)then
|
||||
for _, plugin in pairs(ElementManager._plugins[name]) do
|
||||
@@ -148,6 +367,17 @@ end
|
||||
--- @param name string The name of the element to get
|
||||
--- @return table Element The element class
|
||||
function ElementManager.getElement(name)
|
||||
if not ElementManager._elements[name] then
|
||||
if ElementManager._config.autoLoadMissing then
|
||||
local success = ElementManager.tryAutoLoad(name)
|
||||
if not success then
|
||||
error("Element '"..name.."' not found")
|
||||
end
|
||||
else
|
||||
error("Element '"..name.."' not found")
|
||||
end
|
||||
end
|
||||
|
||||
if not ElementManager._elements[name].loaded then
|
||||
ElementManager.loadElement(name)
|
||||
end
|
||||
@@ -167,4 +397,55 @@ function ElementManager.getAPI(name)
|
||||
return ElementManager._APIs[name]
|
||||
end
|
||||
|
||||
--- Checks if an element exists (is registered)
|
||||
--- @param name string The element name
|
||||
--- @return boolean exists Whether the element exists
|
||||
function ElementManager.hasElement(name)
|
||||
return ElementManager._elements[name] ~= nil
|
||||
end
|
||||
|
||||
--- Checks if an element is loaded
|
||||
--- @param name string The element name
|
||||
--- @return boolean loaded Whether the element is loaded
|
||||
function ElementManager.isElementLoaded(name)
|
||||
return ElementManager._elements[name] and ElementManager._elements[name].loaded or false
|
||||
end
|
||||
|
||||
--- Clears the global cache (_G)
|
||||
--- @usage ElementManager.clearGlobalCache()
|
||||
function ElementManager.clearGlobalCache()
|
||||
if _G[ElementManager._config.globalCacheName] then
|
||||
_G[ElementManager._config.globalCacheName] = nil
|
||||
log.info("Cleared global element cache")
|
||||
end
|
||||
end
|
||||
|
||||
--- Gets cache statistics
|
||||
--- @return table stats Cache statistics with size and element names
|
||||
function ElementManager.getCacheStats()
|
||||
if not _G[ElementManager._config.globalCacheName] then
|
||||
return {size = 0, elements = {}}
|
||||
end
|
||||
|
||||
local elements = {}
|
||||
for name, _ in pairs(_G[ElementManager._config.globalCacheName]) do
|
||||
table.insert(elements, name)
|
||||
end
|
||||
|
||||
return {
|
||||
size = #elements,
|
||||
elements = elements
|
||||
}
|
||||
end
|
||||
|
||||
--- Preloads elements into the global cache
|
||||
--- @param elementNames table List of element names to preload
|
||||
function ElementManager.preloadElements(elementNames)
|
||||
for _, name in ipairs(elementNames) do
|
||||
if ElementManager._elements[name] and not ElementManager._elements[name].loaded then
|
||||
ElementManager.loadElement(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return ElementManager
|
||||
Reference in New Issue
Block a user