166 lines
5.7 KiB
Ucode
166 lines
5.7 KiB
Ucode
/**
|
|
* Author: dkanus
|
|
* Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore
|
|
* License: GPL
|
|
* Copyright 2020-2024 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);
|
|
|
|
//! Acedia's implementation for object pool.
|
|
//!
|
|
//! Unlike generic built in [`Engine::ObjectPool`], that can only store objects
|
|
//! of one specific class, it specializes in a single class to allow for both
|
|
//! faster allocation and faster deallocation (we don't need to look for
|
|
//! an object of particular class to return an unused instance).
|
|
//!
|
|
//! Allows to set a maximum capacity in a config.
|
|
|
|
/// Represents config entry about pool capacity.
|
|
///
|
|
/// This struct and it's associated array [`poolSizeOverwrite`] allows server
|
|
/// admins to rewrite
|
|
/// the pool capacity for each class.
|
|
struct PoolSizeSetting {
|
|
var class<AcediaObject> objectClass;
|
|
var int maxPoolSize;
|
|
};
|
|
|
|
var private config const array<PoolSizeSetting> poolSizeOverwrite;
|
|
// Class of objects that this `AcediaObjectPool` stores.
|
|
// if `== none`, - object pool is considered uninitialized.
|
|
var private class<AcediaObject> storedClass;
|
|
|
|
// Capacity for object pool that we are using.
|
|
// Obtained from [`poolSizeOverwrite`] during initialization and cannot be changed later.
|
|
var private int usedMaxPoolSize;
|
|
|
|
// Actual storage, functions on LIFO principle.
|
|
var private array<AcediaObject> objectPool;
|
|
|
|
// Determines default object pool size for the initialization.
|
|
private final function int GetMaxPoolSizeForClass(class<AcediaObject> classToCheck) {
|
|
local int i;
|
|
local int result;
|
|
|
|
if (classToCheck != none) {
|
|
result = classToCheck.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;
|
|
}
|
|
|
|
/// Initializes caller object pool to store objects of the given class.
|
|
///
|
|
/// Returns `true` if initialization completed, `false` otherwise (including if
|
|
/// it was already completed with passed [`classToStore`]).
|
|
///
|
|
/// If successful, this action is irreversible: same pool cannot be
|
|
/// re-initialized.
|
|
///
|
|
/// [`forcedPoolSize`] defines 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, uses [`AcediaObject`]'s
|
|
/// [`AcediaObject::defaultMaxPoolSize`]
|
|
/// (ignoring [`AcediaObject::usesObjectPool`] setting).
|
|
public final function bool Initialize(
|
|
class<AcediaObject> classToStore,
|
|
optional int forcedPoolSize
|
|
) {
|
|
if (storedClass != none) return false;
|
|
if (classToStore == none) return false;
|
|
|
|
// If does not matter that we've set those variables until
|
|
// we set `storedClass`.
|
|
if (forcedPoolSize == 0) {
|
|
usedMaxPoolSize = GetMaxPoolSizeForClass(classToStore);
|
|
}
|
|
else {
|
|
usedMaxPoolSize = forcedPoolSize;
|
|
}
|
|
if (usedMaxPoolSize == 0) {
|
|
return false;
|
|
}
|
|
storedClass = classToStore;
|
|
return true;
|
|
}
|
|
|
|
/// Returns class of objects stored inside the caller [`AcediaObjectPool`].
|
|
///
|
|
/// `none` means object pool was not initialized.
|
|
public final function class<AcediaObject> GetClassOfStoredObjects() {
|
|
return storedClass;
|
|
}
|
|
|
|
/// Clear the storage of all its contents.
|
|
public final function Clear() {
|
|
objectPool.length = 0;
|
|
}
|
|
|
|
/// Adds object to the caller storage (that needs to be initialized to store
|
|
/// [`newObject.class`] classes).
|
|
///
|
|
/// Returns `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).
|
|
///
|
|
/// For performance purposes does not do duplicates checks, this should be
|
|
/// verified from outside [`AcediaObjectPool`].
|
|
///
|
|
/// Performs type checks and only allows objects of the class that caller
|
|
/// [`AcediaObjectPool`] was initialized for.
|
|
public final function bool Store(AcediaObject newObject) {
|
|
if (newObject == none) return true;
|
|
if (newObject.class != storedClass) return false;
|
|
|
|
if (usedMaxPoolSize >= 0 && objectPool.length >= usedMaxPoolSize) {
|
|
return false;
|
|
}
|
|
objectPool[objectPool.length] = newObject;
|
|
return true;
|
|
}
|
|
|
|
/// Returns last stored object from the pool, removing it from that pool in
|
|
/// the process.
|
|
///
|
|
/// Only returns `none` if caller `AcediaObjectPool` is either empty or not
|
|
/// initialized.
|
|
public final function AcediaObject Fetch() {
|
|
local AcediaObject result;
|
|
|
|
if (storedClass == none) return none;
|
|
if (objectPool.length <= 0) return none;
|
|
|
|
result = objectPool[objectPool.length - 1];
|
|
objectPool.length = objectPool.length - 1;
|
|
return result;
|
|
}
|
|
|
|
defaultproperties {
|
|
} |