UnrealScript library and basis for all Acedia Framework mods
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

225 lines
7.3 KiB

/**
* Acedia's implementation for object pool that can only store objects of
* one specific class to allow for their faster allocation.
* Allows to set a maximum capacity and can handle properly storing,
* auto-cleaning destroyed ones.
* Copyright 2020 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
* Acedia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* Acedia is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
*/
class AcediaObjectPool extends Object
config(AcediaSystem);
// Class of objects that this `AcediaObjectPool` stores.
// if `== none`, - object pool is considered uninitialized.
var private class<Object> storedClass;
// Actual storage, functions on LIFO principle.
var private array<Object> objectPool;
// This struct and it's associated array `poolSizeOverwrite` allows
// server admins to rewrite the pool capacity for each class.
struct PoolSizeSetting
{
var class<Object> objectClass;
var int maxPoolSize;
};
var private config const array<PoolSizeSetting> poolSizeOverwrite;
// Capacity for object pool that we are using.
// Set during initialization and cannot be changed later.
var private int usedMaxPoolSize;
/**
* Initialize caller object pool to store objects of `initStoredClass` class.
*
* If successful, this action is irreversible: same pool cannot be
* re-initialized.
*
* @param initStoredClass Class of objects that caller object pool will store.
* @param forcedPoolSize Max pool size for the caller `AcediaObjectPool`.
* Leaving it at default `0` value will cause method to auto-determine
* the size: gives priority to the `poolSizeOverwrite` config array;
* if not specified, for `AcediaActor`s and `AcediaObject`s uses their
* `defaultMaxPoolSize` (ignoring `usesActorPool` setting),
* for other objects uses `-1`, to remove the capacity limit.
* @return `true` if initialization completed, `false` otherwise
* (including if it was already completed with passed `initStoredClass`).
*/
public final function bool Initialize(
class<Object> initStoredClass,
optional int forcedPoolSize)
{
if (storedClass != none) return false;
if (initStoredClass == none) return false;
// If does not matter that we've set those variables until
// we set `storedClass`.
if (forcedPoolSize == 0) {
usedMaxPoolSize = GetMaxPoolSizeForClass(initStoredClass);
}
else {
usedMaxPoolSize = forcedPoolSize;
}
if (usedMaxPoolSize == 0) {
return false;
}
storedClass = initStoredClass;
return true;
}
// Determines default object pool size for the initialization.
private final function int GetMaxPoolSizeForClass(class<Object> classToCheck)
{
local int i;
local int result;
local class<AcediaObject> classAsAcediaObject;
local class<AcediaActor> classAsAcediaActor;
// Get hard-coded value
classAsAcediaObject = class<AcediaObject>(classToCheck);
classAsAcediaActor = class<AcediaActor>(classToCheck);
if (classAsAcediaActor != none) {
result = classAsAcediaActor.default.defaultMaxPoolSize;
}
if (classAsAcediaObject != none) {
result = classAsAcediaObject.default.defaultMaxPoolSize;
}
else {
result = -1;
}
// Try to replace it with server's settings
for (i = 0; i < poolSizeOverwrite.length; i += 1) {
if (poolSizeOverwrite[i].objectClass == classToCheck) {
result = poolSizeOverwrite[i].maxPoolSize;
break;
}
}
return result;
}
/**
* Returns class of objects inside the caller `AcediaObjectPool`.
*
* @return class of objects inside caller the caller object pool;
* `none` means object pool was not initialized.
*/
public final function class<Object> GetClassOfStoredObjects()
{
return storedClass;
}
/**
* Clear the storage of all it's contents. In case of stored actors also
* destroys them.
*
* Can be used before UnrealEngine's garbage collection to free pooled objects.
*/
public final function Clear()
{
local int i;
local Actor nextActor;
if (storedClass != none) {
return;
}
if (class<Actor>(storedClass) == none)
{
// We can't destroy non-actors, so just get rid of references
objectPool.length = 0;
return;
}
for (i = 0; i < objectPool.length; i += 1)
{
nextActor = Actor(objectPool[i]);
if (nextActor != none) {
nextActor.Destroy();
}
}
objectPool.length = 0;
}
/**
* Adds object to the caller storage
* (that needs to be initialized to store `newObject.class` classes).
*
* @param newObject Object to put inside caller pool. Must be not `none` and
* have precisely the class this object pool was initialized to store.
* @return `true` on success and `false` on failure
* (can happen if passed `newObject` reference was invalid, caller storage
* is not initialized yet or reached it's capacity).
*/
public final function bool Store(Object newObject)
{
local int i;
if (newObject == none) return false;
if (newObject.class != storedClass) return false;
// Check for duplicates and clear dead references
while (i < objectPool.length)
{
if (objectPool[i] == newObject) {
return false;
}
// Getting `none` in object pool is expected to be rare and abnormal
// occurrence (since objects and actors put in the pool are
// not supposed to be touched), so filtering `none` values here
// should be negligible performance-wise, even if it's expensive.
if (objectPool[i] == none) {
objectPool.Remove(i, 1);
}
else {
i += 1;
}
}
if (usedMaxPoolSize >= 0 && objectPool.length < usedMaxPoolSize) {
return false;
}
objectPool[objectPool.length] = newObject;
return true;
}
/**
* Extracts last stored last not destroyed object (can happen for actors)
* from the pool.
*
* Returned object is no longer stored in the pool.
*
* @return Reference to the last (not destroyed) stored object.
* Only returns `none` if either empty or not initialized.
*/
public final function Object Fetch()
{
local int i;
local int validObjectIndex;
local Object result;
if (storedClass == none) {
return none;
}
validObjectIndex = -1;
for (i = objectPool.length - 1; i >= 0; i -= 1)
{
if (objectPool[i] == none) continue;
validObjectIndex = i;
break;
}
if (validObjectIndex < 0) {
return none;
}
result = objectPool[validObjectIndex];
objectPool.length = validObjectIndex;
return result;
}
defaultproperties
{
}