Untested, though in theory should work without issue. The syntax / api is as should be and will not change in future additions/improvements.
105 lines
No EOL
2.5 KiB
Lua
105 lines
No EOL
2.5 KiB
Lua
local module = {}
|
|
|
|
local FF_COMPUTED_CONSTRUCTION_CALLBACK = false
|
|
local FF_COMPUTED_REFS = {}
|
|
|
|
local refValuesById = {}
|
|
|
|
function module.ref(value: any)
|
|
table.insert(refValuesById, { Value = value, OnSet = {} })
|
|
local id = #refValuesById
|
|
|
|
local ref = setmetatable({
|
|
Id = id,
|
|
get = function(self)
|
|
if FF_COMPUTED_CONSTRUCTION_CALLBACK then
|
|
table.insert(FF_COMPUTED_REFS, self.Id)
|
|
end;
|
|
|
|
return refValuesById[self.Id].Value;
|
|
end,
|
|
set = function(self, value: any)
|
|
self.Value = value
|
|
|
|
task.spawn(function()
|
|
local callbacks = refValuesById[self.Id].OnSet
|
|
for _, callback in callbacks do
|
|
callback(value)
|
|
end
|
|
end)
|
|
end,
|
|
}, {})
|
|
|
|
return ref
|
|
end
|
|
|
|
local computedsById = {}
|
|
|
|
function module.computed(computationCallback: () -> (any))
|
|
FF_COMPUTED_CONSTRUCTION_CALLBACK = true
|
|
|
|
table.insert(computedsById, { Value = computationCallback(), Callack = computationCallback, Refs = FF_COMPUTED_REFS, OnChange = {} })
|
|
|
|
local notifyChange = function(refId, compId)
|
|
return function()
|
|
local computedData: { Value: any, Callack: () -> any, OnChange: { } } = computedsById[refId]
|
|
local newComputedValue = computedData.Callack()
|
|
computedsById[compId].Value = newComputedValue
|
|
|
|
for _, callback in computedData.OnChange do
|
|
callback(newComputedValue)
|
|
end
|
|
end
|
|
end
|
|
|
|
local refs = FF_COMPUTED_REFS
|
|
if refs[1] then
|
|
for _, ref in refs do
|
|
table.insert(refValuesById[ref].OnSet, notifyChange(ref, #computedsById))
|
|
end
|
|
end
|
|
|
|
FF_COMPUTED_CONSTRUCTION_CALLBACK = false
|
|
FF_COMPUTED_REFS = {}
|
|
|
|
return setmetatable({ Value = computationCallback(), }, { Id = #computedsById, __index = function(self, key)
|
|
if key:upper() == "VALUE" then
|
|
return computedsById[#computedsById].Value
|
|
else
|
|
return rawget(self, key)
|
|
end
|
|
end})
|
|
end
|
|
|
|
function module.listenEvent(computed: typeof(setmetatable({ Id = 1}, {})), callback: (newValue: any) -> ())
|
|
table.insert(computedsById[computed.Id].OnChange, callback)
|
|
end
|
|
|
|
|
|
local Computed = module.computed
|
|
local Ref = module.ref
|
|
local ListenTo = module.listenEvent
|
|
|
|
|
|
|
|
|
|
local a = Ref(1)
|
|
local b = Ref(2)
|
|
|
|
local aAndB = Computed(function()
|
|
return a:get() * b:get()
|
|
end)
|
|
|
|
print(aAndB.Value) -- 2
|
|
|
|
ListenTo(aAndB, print)
|
|
|
|
a:set(2)
|
|
|
|
-- aAndB = 4
|
|
|
|
b:set(3)
|
|
|
|
-- aAndB = 6
|
|
|
|
return module |