105 lines
2.5 KiB
Lua
105 lines
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
|