-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathControllerServiceLoader.lua
More file actions
160 lines (132 loc) · 5.35 KB
/
ControllerServiceLoader.lua
File metadata and controls
160 lines (132 loc) · 5.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
local ControllerServiceLoader = {}
ControllerServiceLoader.__index = ControllerServiceLoader
local MODULE_TYPES = {
CONTROLLER = "Controllers",
SERVICE = "Services"
}
local function getModulePath(moduleType, moduleName)
return game:GetService("ReplicatedStorage"):WaitForChild(moduleType):WaitForChild(moduleName)
end
--- Creates a new ControllerServiceLoader instance.
-- @param baseFolder The base folder containing Controllers and Services folders (optional, defaults to ReplicatedStorage)
-- @return ControllerServiceLoader A new instance of the loader.
function ControllerServiceLoader.new(baseFolder)
local self = setmetatable({}, ControllerServiceLoader)
self.baseFolder = baseFolder or game:GetService("ReplicatedStorage")
self.controllers = setmetatable({}, {
__index = function(t, k)
return self:_loadModule(MODULE_TYPES.CONTROLLER, k)
end
})
self.services = setmetatable({}, {
__index = function(t, k)
return self:_loadModule(MODULE_TYPES.SERVICE, k)
end
})
self.loadedModules = {}
self.loadingModules = {}
self.dependencies = {}
return self
end
--- Registers dependencies for a module
-- @param moduleName string The name of the module
-- @param dependencies table Array of dependency module names
function ControllerServiceLoader:registerDependencies(moduleName, dependencies)
self.dependencies[moduleName] = dependencies
end
--- Loads a module's dependencies
-- @param moduleName string The name of the module
-- @private
function ControllerServiceLoader:_loadDependencies(moduleName)
local deps = self.dependencies[moduleName]
if not deps then return end
for _, depName in ipairs(deps) do
if not self.loadedModules[depName] then
self:_loadModule(self:_getModuleType(depName), depName)
end
end
end
--- Determines the type of a module based on its name or location
-- @param moduleName string The name of the module
-- @return string The module type (CONTROLLER or SERVICE)
-- @private
function ControllerServiceLoader:_getModuleType(moduleName)
-- First check if the module exists in Controllers
if self.baseFolder:FindFirstChild(MODULE_TYPES.CONTROLLER):FindFirstChild(moduleName) then
return MODULE_TYPES.CONTROLLER
else
return MODULE_TYPES.SERVICE -- Default to Service if not found in Controllers
end
end
--- Internal function to load a module on demand.
-- Handles lazy loading, dependencies, and circular dependency checks.
-- @param moduleType string The type of module (CONTROLLER or SERVICE)
-- @param moduleName string The name of the module to load
-- @return table The loaded module
-- @throws error If a circular dependency is detected
function ControllerServiceLoader:_loadModule(moduleType, moduleName)
if self.loadingModules[moduleName] then
error(string.format("Circular dependency detected for module: %s", moduleName))
end
if self.loadedModules[moduleName] then
return self.loadedModules[moduleName]
end
self.loadingModules[moduleName] = true
-- Load dependencies first
self:_loadDependencies(moduleName)
local success, result = pcall(function()
local moduleInstance = getModulePath(moduleType, moduleName)
local module = require(moduleInstance)
-- Initialize the module if it has an init function
if type(module) == "table" and type(module.init) == "function" then
module:init()
end
return module
end)
self.loadingModules[moduleName] = nil
if not success then
error(string.format("Failed to load module %s: %s", moduleName, result))
end
self.loadedModules[moduleName] = result
-- Cache in appropriate table
if moduleType == MODULE_TYPES.CONTROLLER then
self.controllers[moduleName] = result
else
self.services[moduleName] = result
end
return result
end
--- Loads a list of modules in the specified order.
-- @param moduleNames table An array of module names to load in order
-- @param forceReload boolean Whether to reload modules even if already loaded
function ControllerServiceLoader:loadInOrder(moduleNames, forceReload)
for _, moduleName in ipairs(moduleNames) do
if forceReload or not self.loadedModules[moduleName] then
self:_loadModule(self:_getModuleType(moduleName), moduleName)
end
end
end
--- Loads all modules in the Controllers and Services folders
function ControllerServiceLoader:loadAll()
local function loadFromFolder(folder)
for _, moduleScript in ipairs(folder:GetChildren()) do
if moduleScript:IsA("ModuleScript") then
self:_loadModule(folder.Name, moduleScript.Name)
end
end
end
loadFromFolder(self.baseFolder:WaitForChild(MODULE_TYPES.CONTROLLER))
loadFromFolder(self.baseFolder:WaitForChild(MODULE_TYPES.SERVICE))
end
--- Returns a loaded module by name
-- @param moduleName string The name of the module
-- @return table The loaded module or nil if not found
function ControllerServiceLoader:getModule(moduleName)
return self.loadedModules[moduleName]
end
--- Returns all loaded modules
-- @return table A table of all loaded modules
function ControllerServiceLoader:getLoadedModules()
return self.loadedModules
end
return ControllerServiceLoader