--[[ A generic pool to contain "active" and "free" objects. Active objects are typically objects which: 1. Have a relatively high construction cost 2. Are not lightweight enough to create many of them at once 3. Tend to be reused as dynamic elements of a larger container. The pool should "rapidly" reach a high-water mark of contained objects which should flow between active and free states on a regular basis. Ideal uses of the ZO_ObjectPool would be to contain objects such as: 1. Scrolling combat text 2. Tracked quests 3. Buff icons The pools are not intended to be used to track a dynamic set of contained objects whose membership grows to a predetermined size. As such, do NOT use the pool to track: 1. Chat filters 2. Inventory slots 3. Action buttons (unless creating something like AutoBar) A common usage pattern is instantiating templated controls. To facilitate this without bloating your own code you should use ZO_ObjectPool_CreateControl which has been written here as a convenience. It creates a control named "template"..id where id is an arbitrary value that will not conflict with other generated id's. If your system depends on having well-known names for controls, you should not use the convenience function. --]] ZO_ObjectPool = ZO_InitializingObject:Subclass() function ZO_ObjectPool:Initialize(factoryFunctionOrObjectClass, resetFunction) assert(factoryFunctionOrObjectClass ~= nil) self.m_Active = {} self.m_Free = {} self:SetFactory(factoryFunctionOrObjectClass) self:SetResetFunction(resetFunction) self.m_NextFree = 1 -- Just in case the user would like the pool to generate object keys. self.m_NextControlId = 0 -- Just in case the user would like the pool to generate id-based control suffixes end -- Define the primary factory behavior function ZO_ObjectPool:SetFactory(factoryFunctionOrObjectClass) if type(factoryFunctionOrObjectClass) == "function" then self.m_Factory = factoryFunctionOrObjectClass -- Signature: function(ZO_ObjectPool, objectKey) else self.m_Factory = function(pool, objectKey) return factoryFunctionOrObjectClass:New(pool, objectKey) end end end -- Define the primary reset behavior function ZO_ObjectPool:SetResetFunction(resetFunction) self.m_Reset = resetFunction -- Signature: function(objectBeingReset, ZO_ObjectPool) end function ZO_ObjectPool:GetNextFree() local freeKey, object = next(self.m_Free) if freeKey == nil or object == nil then local nextPotentialFree = self.m_NextFree self.m_NextFree = self.m_NextFree + 1 return nextPotentialFree, nil end return freeKey, object end function ZO_ObjectPool:GetNextControlId() self.m_NextControlId = self.m_NextControlId + 1 return self.m_NextControlId end function ZO_ObjectPool:GetTotalObjectCount() return self:GetActiveObjectCount() + self:GetFreeObjectCount() end function ZO_ObjectPool:GetActiveObjectCount() return NonContiguousCount(self.m_Active) end function ZO_ObjectPool:HasActiveObjects() return next(self.m_Active) ~= nil end function ZO_ObjectPool:GetActiveObjects() return self.m_Active end function ZO_ObjectPool:GetActiveObject(objectKey) return self.m_Active[objectKey] end function ZO_ObjectPool:ActiveObjectIterator(filterFunctions) return ZO_FilteredNonContiguousTableIterator(self.m_Active, filterFunctions) end function ZO_ObjectPool:GetFreeObjectCount() return NonContiguousCount(self.m_Free) end function ZO_ObjectPool:ActiveAndFreeObjectIterator(filterFunctions) local objects = {} ZO_CombineNonContiguousTables(objects, self.m_Active, self.m_Free) return ZO_FilteredNonContiguousTableIterator(objects, filterFunctions) end function ZO_ObjectPool:SetCustomAcquireBehavior(customAcquireBehavior) self.customAcquireBehavior = customAcquireBehavior -- Signature: function(objectBeingAcquired, objectKey) end -- Optional supplementary behavior that can be run after the factory has created the object -- Often used in conjunction with extension pool classes (e.g.: ZO_ControlPool) when you want all the default factory behavior, plus something extra function ZO_ObjectPool:SetCustomFactoryBehavior(customFactoryBehavior) self.customFactoryBehavior = customFactoryBehavior -- Signature: function(objectBeingCreated, objectKey, ZO_ObjectPool) end -- Optional supplementary behavior that can be run after the primary reset function has been run on the object -- Often used in conjunction with extension pool classes (e.g.: ZO_ControlPool) when you want all the default reset behavior, plus something extra function ZO_ObjectPool:SetCustomResetBehavior(customResetBehavior) self.customResetBehavior = customResetBehavior -- Signature: function(objectBeingReset) end function ZO_ObjectPool:AcquireObject(objectKey) -- If the object referred to by this key is already -- active there is very little work to do...just return it. local object = objectKey and self.m_Active[objectKey] or nil if object then return object, objectKey end -- If we know the key that we want, use that object first, otherwise just return the first object from the free pool -- A nil objectKey means that the caller doesn't care about tracking unique keys for these objects, or that the keys -- the system uses can't directly be used to look up the data. Just manage them with pool-generated id's if objectKey == nil then objectKey, object = self:GetNextFree() else object = self.m_Free[objectKey] end -- -- If the object is valid it was reclaimed from the free list, otherwise it needs to be created. -- Creation uses the m_Factory member which receives this pool as its only argument. -- Either way, after this, object must be non-nil -- if object then self.m_Free[objectKey] = nil else object = self:CreateObject(objectKey) end self.m_Active[objectKey] = object if self.customAcquireBehavior then self.customAcquireBehavior(object, objectKey) end return object, objectKey end function ZO_ObjectPool:CreateObject(objectKey) local object = self:m_Factory(objectKey) if self.customFactoryBehavior then self.customFactoryBehavior(object, objectKey, self) end return object end function ZO_ObjectPool:ReleaseObject(objectKey) local object = self.m_Active[objectKey] if object then self:ResetObject(object) self.m_Active[objectKey] = nil self.m_Free[objectKey] = object end end function ZO_ObjectPool:ReleaseAllObjects() for k, v in pairs(self.m_Active) do self:ResetObject(v) self.m_Free[k] = v end ZO_ClearTable(self.m_Active) end function ZO_ObjectPool:ResetObject(object) if self.m_Reset then self.m_Reset(object, self) end if self.customResetBehavior then self.customResetBehavior(object) end end function ZO_ObjectPool:DestroyFreeObject(objectKey, destroyFunction) local object = self.m_Free[objectKey] destroyFunction(object) self.m_Free[objectKey] = nil end function ZO_ObjectPool:DestroyAllFreeObjects(destroyFunction) for _, object in pairs(self.m_Free) do destroyFunction(object) end ZO_ClearTable(self.m_Free) end function ZO_ObjectPool_CreateControl(templateName, objectPool, parentControl) return CreateControlFromVirtual(templateName, parentControl, templateName, objectPool:GetNextControlId()) end function ZO_ObjectPool_CreateNamedControl(name, templateName, objectPool, parentControl) return CreateControlFromVirtual(name, parentControl, templateName, objectPool:GetNextControlId()) end function ZO_ObjectPool_DefaultResetObject(object) object:Reset() end function ZO_ObjectPool_DefaultResetControl(control) control:SetHidden(true) end