commit b584fe0ba6c10f9ba1916f036aa1b690843e4636 Author: Sovereignty Date: Tue Nov 19 17:08:28 2024 +0000 Computed Variables as seen in Weaver / Vue.js / Svelte / React Untested, though in theory should work without issue. The syntax / api is as should be and will not change in future additions/improvements. diff --git a/ModuleScripts/Computed.lua b/ModuleScripts/Computed.lua new file mode 100644 index 0000000..832cebe --- /dev/null +++ b/ModuleScripts/Computed.lua @@ -0,0 +1,105 @@ +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 \ No newline at end of file