/** * 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 . */ 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 objectClass; var int maxPoolSize; }; var private config const array poolSizeOverwrite; // Class of objects that this `AcediaObjectPool` stores. // if `== none`, - object pool is considered uninitialized. var private class 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 objectPool; // Determines default object pool size for the initialization. private final function int GetMaxPoolSizeForClass(class 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 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 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 { }