-- Compiled with roblox-ts v3.0.0 local TS = _G[script] local CollectionService = TS.import(script, TS.getModule(script, "@rbxts", "services")).CollectionService local ATOMIC_MODES = { [Enum.ModelStreamingMode.Atomic] = true, [Enum.ModelStreamingMode.Persistent] = true, [Enum.ModelStreamingMode.PersistentPerPlayer] = true, } local ComponentTracker do ComponentTracker = setmetatable({}, { __tostring = function() return "ComponentTracker" end, }) ComponentTracker.__index = ComponentTracker function ComponentTracker.new(...) local self = setmetatable({}, ComponentTracker) return self:constructor(...) or self end function ComponentTracker:constructor(identifier, criteria) self.identifier = identifier self.criteria = criteria self.instances = {} end function ComponentTracker:getInstanceTracker(instance, create) if create == nil then create = true end local _instances = self.instances local _instance = instance local tracker = _instances[_instance] if not tracker and create then tracker = { unmetCriteria = {}, listeners = {}, cleanup = {}, isQualified = true, } local _instances_1 = self.instances local _instance_1 = instance local _tracker = tracker _instances_1[_instance_1] = _tracker end return tracker end function ComponentTracker:updateListeners(instance, tracker) local isQualified = next(tracker.unmetCriteria) == nil if isQualified ~= tracker.isQualified then tracker.isQualified = isQualified for listener in tracker.listeners do listener(isQualified, instance) end local warningThread = tracker.timeoutWarningThread if isQualified and warningThread then tracker.timeoutWarningThread = nil task.cancel(warningThread) end end end function ComponentTracker:setupTracker(instance, tracker) local _binding = self.criteria local typeGuard = _binding.typeGuard local typeGuardPoll = _binding.typeGuardPoll local typeGuardPollAtomic = _binding.typeGuardPollAtomic local dependencies = _binding.dependencies local _condition = instance:IsA("Model") if _condition then local _modelStreamingMode = instance.ModelStreamingMode _condition = ATOMIC_MODES[_modelStreamingMode] ~= nil end local isAtomicModel = _condition if typeGuard and typeGuardPoll and (typeGuardPollAtomic or not isAtomicModel) then local addedConnection local removingConnection local connectRemoving local connectAdded = function() if removingConnection then removingConnection:Disconnect() removingConnection = nil end local isScheduled = false addedConnection = instance.DescendantAdded:Connect(function() if not isScheduled then isScheduled = true task.defer(function() isScheduled = false if typeGuard(instance) then connectRemoving() tracker.unmetCriteria["type guard"] = nil self:updateListeners(instance, tracker) end end) end end) end connectRemoving = function() if addedConnection then addedConnection:Disconnect() addedConnection = nil end local isScheduled = false removingConnection = instance.DescendantRemoving:Connect(function() if not isScheduled then isScheduled = true task.defer(function() isScheduled = false if not typeGuard(instance) then connectAdded() tracker.unmetCriteria["type guard"] = true self:updateListeners(instance, tracker) end end) end end) end tracker.cleanup[function() local _result = addedConnection if _result ~= nil then _result:Disconnect() end local _result_1 = removingConnection if _result_1 ~= nil then _result_1:Disconnect() end end] = true if tracker.unmetCriteria["type guard"] ~= nil then connectAdded() else connectRemoving() end end if dependencies then for _, dependency in dependencies do local listener = function(isQualified) if isQualified then tracker.unmetCriteria[dependency] = nil else tracker.unmetCriteria[dependency] = true end self:updateListeners(instance, tracker) end dependency:trackInstance(instance, listener) tracker.cleanup[function() dependency:untrackInstance(instance, listener) end] = true end end if not tracker.isQualified and self.criteria.warningTimeout ~= 0 then local _condition_1 = self.criteria.warningTimeout if _condition_1 == nil then _condition_1 = 5 end tracker.timeoutWarningThread = task.delay(_condition_1, function() local reasons = {} for criteria in tracker.unmetCriteria do if type(criteria) == "string" then table.insert(reasons, criteria) end end if dependencies then for _, dependency in dependencies do if tracker.unmetCriteria[dependency] ~= nil then local _arg0 = `dependency '{dependency.identifier}'` table.insert(reasons, _arg0) end end end warn(`[Flamework] Infinite yield possible on instance '{instance:GetFullName()}'`) warn(`Waiting for component '{self.identifier}'`) warn(`Waiting for the following criteria: {table.concat(reasons, ", ")}`) end) end end function ComponentTracker:testInstance(instance, tracker) local result = true if self.criteria.dependencies then for _, dependency in self.criteria.dependencies do if not dependency:checkInstance(instance) then result = false if tracker then tracker.unmetCriteria[dependency] = true self:updateListeners(instance, tracker) else return result end end end end if self.criteria.typeGuard then if not self.criteria.typeGuard(instance) then result = false if tracker then tracker.unmetCriteria["type guard"] = true self:updateListeners(instance, tracker) else return result end end end if self.criteria.tag ~= nil then if not CollectionService:HasTag(instance, self.criteria.tag) then result = false if tracker then tracker.unmetCriteria["CollectionService tag"] = true self:updateListeners(instance, tracker) else return result end end end return result end function ComponentTracker:setHasTag(instance, hasTag) local tracker = self:getInstanceTracker(instance, false) if tracker then if hasTag then tracker.unmetCriteria["CollectionService tag"] = nil else tracker.unmetCriteria["CollectionService tag"] = true end self:updateListeners(instance, tracker) end end function ComponentTracker:checkInstance(instance) local tracker = self:getInstanceTracker(instance, false) if tracker then return tracker.isQualified end return self:testInstance(instance, tracker) end function ComponentTracker:isTracked(instance) local _instances = self.instances local _instance = instance return _instances[_instance] ~= nil end function ComponentTracker:trackInstance(instance, listener) local _instances = self.instances local _instance = instance local isNewInstance = not (_instances[_instance] ~= nil) local tracker = self:getInstanceTracker(instance) if isNewInstance then self:testInstance(instance, tracker) self:setupTracker(instance, tracker) end local _listeners = tracker.listeners local _listener = listener _listeners[_listener] = true listener(tracker.isQualified, instance) end function ComponentTracker:untrackInstance(instance, listener) local tracker = self:getInstanceTracker(instance, false) if tracker then local _listeners = tracker.listeners local _listener = listener _listeners[_listener] = nil if next(tracker.listeners) == nil then for cleanup in tracker.cleanup do cleanup() end local _instances = self.instances local _instance = instance _instances[_instance] = nil end end end end return { ComponentTracker = ComponentTracker, }