local RunService = game:GetService("RunService")
local DEFAULT_NAMESPACE = "game"
local Promise = require(script.Parent.Parent.Promise)
local UnitCollection = require(script.UnitCollection)
local Reactor = require(script.Reactor)
local Serializer = require(script.Serializer)
local HotReloader = require(script.HotReloader)
local Symbol = require(script.Parent.Shared.Symbol)
local Fabric = {
reducers = require(script.Operators.Reducers);
comparators = require(script.Operators.Comparators);
t = require(script.Parent.Parent.t);
DEBUG = true;
Heartbeat = RunService.Heartbeat;
None = Symbol.named("None");
Unit = setmetatable({}, {
__index = function(_, key)
error(("Unit %q is not registered!"):format(key))
end
});
}
Fabric.__index = Fabric
function Fabric.new(namespace)
local self = setmetatable({
namespace = namespace or DEFAULT_NAMESPACE;
_listeners = {};
}, Fabric)
self.serializer = Serializer.new(self)
self._collection = UnitCollection.new(self)
self._reactor = Reactor.new(self)
if RunService:IsStudio() then
self._hotReloader = HotReloader.new(self)
end
return self
end
function Fabric:registerUnit(unitDefinition)
assert(unitDefinition ~= nil, "unitDefinition is nil")
self._collection:register(unitDefinition)
self:fire("unitRegistered", unitDefinition)
return unitDefinition
end
function Fabric:registerUnitsIn(container)
for _, object in ipairs(container:GetChildren()) do
if object:IsA("ModuleScript") then
if not object.Name:match("%.spec$") then
local unitDefinition = require(object)
if unitDefinition.name == nil then
unitDefinition.name = object.Name
end
self:registerUnit(unitDefinition)
if self._hotReloader then
self._hotReloader:giveModule(object, unitDefinition)
end
end
else
self:registerUnitsIn(object)
end
end
end
function Fabric:getUnitByRef(unitResolvable, ref)
return self._collection:getUnitByRef(unitResolvable, ref)
end
function Fabric:getOrCreateUnitByRef(unitResolvable, ref)
return self._collection:getOrCreateUnitByRef(unitResolvable, ref)
end
function Fabric:getLoadedUnitByRef(unitResolvable, ref)
local unit = self._collection:getUnitByRef(unitResolvable, ref)
if unit == nil then
error(("Attempt to get loaded unit %q on %s, but it does not exist."):format(
tostring(unitResolvable),
tostring(ref)
))
end
if not (unit._loaded or unit._loading) then
error(("Attempt to call getLoadedUnitByRef on %q on %s, but it will never be loaded."):format(
tostring(unitResolvable),
tostring(ref)
))
end
return Promise.new(function(resolve, reject)
if unit._loaded then
return resolve(unit)
else
unit:on("loaded", function()
resolve(unit)
end)
unit:on("loadingFailed", function(...)
reject(...)
end)
end
end)
end
function Fabric:removeAllUnitsWithRef(ref)
self._collection:removeAllUnitsWithRef(ref)
end
function Fabric:fire(eventName, ...)
if not self._listeners[eventName] then
return end
for _, callback in ipairs(self._listeners[eventName]) do
local success, errorValue = coroutine.resume(coroutine.create(callback), ...)
if not success then
warn(("Event listener for %s encountered an error: %s"):format(
tostring(eventName),
tostring(errorValue)
))
end
end
end
function Fabric:on(eventName, callback)
self._listeners[eventName] = self._listeners[eventName] or {}
table.insert(self._listeners[eventName], callback)
return function()
for i, listCallback in ipairs(self._listeners[eventName]) do
if listCallback == callback then
table.remove(self._listeners[eventName], i)
break
end
end
end
end
function Fabric:debug(...)
if self.DEBUG then
warn("[Fabric]", ...)
end
end
return Fabric