|
|
@ -1,10 +1,8 @@ |
|
|
|
/** |
|
|
|
/** |
|
|
|
* API that provides functions for managing object of classes, derived from |
|
|
|
* Author: dkanus |
|
|
|
* `AcediaObject`. It takes care of managing their object pools, as well as |
|
|
|
* Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore |
|
|
|
* ensuring that constructors and finalizers are called properly. |
|
|
|
* License: GPL |
|
|
|
* Almost all `AcediaObject`s should use this API's methods for their own |
|
|
|
* Copyright 2020-2023 Anton Tarasenko |
|
|
|
* creation and destruction. |
|
|
|
|
|
|
|
* Copyright 2020-2022 Anton Tarasenko |
|
|
|
|
|
|
|
*------------------------------------------------------------------------------ |
|
|
|
*------------------------------------------------------------------------------ |
|
|
|
* This file is part of Acedia. |
|
|
|
* This file is part of Acedia. |
|
|
|
* |
|
|
|
* |
|
|
@ -23,325 +21,180 @@ |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
class MemoryAPI extends AcediaObject; |
|
|
|
class MemoryAPI extends AcediaObject; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
//! API that provides functions for managing object of classes, derived from `AcediaObject`. |
|
|
|
* # Memory API |
|
|
|
//! |
|
|
|
* |
|
|
|
//! This is most-basic API that must be created before anything else in Acedia, since it is |
|
|
|
* This is most-basic API that must be created before anything else in Acedia, |
|
|
|
//! responsible for the proper creation of `AcediaObject`s. |
|
|
|
* since it is responsible for the proper creation of `AcediaObject`s. |
|
|
|
//! It takes care of managing their object pools, as well as ensuring that constructors and |
|
|
|
* It takes care of managing their object pools, as well as ensuring that |
|
|
|
//! finalizers are called properly. |
|
|
|
* constructors and finalizers are called properly. |
|
|
|
//! |
|
|
|
* Almost all `AcediaObject`s should use this API's methods for their own |
|
|
|
//! Almost all `AcediaObject`s should use this API's methods for their own creation and destruction. |
|
|
|
* creation and destruction. |
|
|
|
//! |
|
|
|
* |
|
|
|
//! ## Usage |
|
|
|
* ## Usage |
|
|
|
//! |
|
|
|
* |
|
|
|
//! First of all, this API is only meant for non-actor `Object` creation. |
|
|
|
* First of all, this API is only meant for non-actor `Object` creation. |
|
|
|
//! `Actor` creation is generally avoided in Acedia and, when unavoidable, different APIs |
|
|
|
* `Actor` creation is generally avoided in Acedia and, when unavoidable, |
|
|
|
//! are dealing with that. |
|
|
|
* different APIs are dealing with that. `MemoryAPI` is designed to work in |
|
|
|
//! `MemoryAPI` is designed to work in the absence of any level (and, therefore, `Actor`s) at all. |
|
|
|
* the absence of any level (and, therefore, `Actor`s) at all. |
|
|
|
//! |
|
|
|
* Simply use `MemoryAPI.Allocate()` to create a new object and |
|
|
|
//! Simply use `MemoryAPI.Allocate()` to create a new object and `MemoryAPI.Free()` to get rid of |
|
|
|
* `MemoryAPI.Free()` to get rid on unneeded reference. Do note that |
|
|
|
//! unneeded reference. |
|
|
|
* `AcediaObject`s use reference counting and object will be deallocated and |
|
|
|
//! Do note that `AcediaObject`s use reference counting and object will be deallocated and pooled |
|
|
|
* pooled only after every trackable reference was released by |
|
|
|
//! only after every trackable reference was released by `MemoryAPI.Free()`. |
|
|
|
* `MemoryAPI.Free()`. |
|
|
|
//! |
|
|
|
* Best practice is to only care about what object reference you're |
|
|
|
//! Best practice is to only care about what object reference you're keeping, properly release them |
|
|
|
* keeping, properly release them with `MemoryAPI.Free()` and to NEVER EVER USE |
|
|
|
//! with `MemoryAPI.Free()` and to NEVER EVER USE THEM after you've release them. |
|
|
|
* THEM after you've release them. Regardless of whether they were actually |
|
|
|
//! Regardless of whether they were actually deallocated. |
|
|
|
* deallocated. |
|
|
|
//! |
|
|
|
* |
|
|
|
//! There's also a set of auxiliary methods for either loading `class`es from their |
|
|
|
* There's also a set of auxiliary methods for either loading `class`es from |
|
|
|
//! `BaseText`/`string`-given names or even directly creating objects of said classes. |
|
|
|
* their `BaseText`/`string`-given names or even directly creating objects of |
|
|
|
//! |
|
|
|
* said classes. |
|
|
|
//! ## Customizing object pools for your classes |
|
|
|
* |
|
|
|
//! |
|
|
|
* ## Motivation |
|
|
|
//! Object pool usage can be disabled completely for your class by setting `usesObjectPool = false` |
|
|
|
* |
|
|
|
//! in `defaultproperties` block. |
|
|
|
* UnrealScript lacks any practical way to destroy non-actor objects on |
|
|
|
//! Without object pools `MemoryAPI.Allocate()` will create a new instance of your class every |
|
|
|
* demand: the best one can do is remove any references to the object and wait |
|
|
|
//! single time. |
|
|
|
* for garbage collection. But garbage collection itself is too slow and causes |
|
|
|
//! |
|
|
|
* noticeable lag spikes for players, making it suitable only for cleaning |
|
|
|
//! You can also set a limit to how many objects will be stored in an object pool with |
|
|
|
* objects when switching levels. To alleviate this problem, there exists |
|
|
|
//! `defaultMaxPoolSize` variable. |
|
|
|
* a standard class `ObjectPool` that stores unused objects (mostly resources |
|
|
|
//! Negative number (default for `AcediaObject`) means that object pool can grow without a limit. |
|
|
|
* such as textures) inside dynamic array until they are needed. |
|
|
|
//! `0` effectively disables object pool, similar to setting `usesObjectPool = false`. |
|
|
|
* Unfortunately, using a single ObjectPool for a large volume of objects |
|
|
|
//! However, this can be overwritten by server's settings |
|
|
|
* is impractical from performance perspective, since it stores objects of all |
|
|
|
//! (see `AcediaSystem.ini`: `AcediaObjectPool`). |
|
|
|
* classes together and each object allocation from the pool can potentially |
|
|
|
|
|
|
|
* require going through the whole array (see `Engine/ObjectPool.uc`). |
|
|
|
|
|
|
|
* Acedia uses a separate object pool (implemented by `AcediaObjectPool`) |
|
|
|
|
|
|
|
* for every single class, making object allocation as trivial as grabbing |
|
|
|
|
|
|
|
* the last stored object from `AcediaObjectPool`'s internal dynamic array. |
|
|
|
|
|
|
|
* New pool is prepared for every class you create, as long as it is |
|
|
|
|
|
|
|
* derived from `AcediaObject`. `AcediaActors` do not use object pools and are |
|
|
|
|
|
|
|
* meant to be simply `Destroy()`ed. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* ## Customizing object pools for your classes |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* Object pool usage can be disabled completely for your class by setting |
|
|
|
|
|
|
|
* `usesObjectPool = false` in `defaultproperties` block. Without object pools |
|
|
|
|
|
|
|
* `MemoryAPI.Allocate()` will create a new instance of your class every single |
|
|
|
|
|
|
|
* time. |
|
|
|
|
|
|
|
* You can also set a limit to how many objects will be stored in an object |
|
|
|
|
|
|
|
* pool with defaultMaxPoolSize variable. Negative number (default for |
|
|
|
|
|
|
|
* `AcediaObject`) means that object pool can grow without a limit. |
|
|
|
|
|
|
|
* `0` effectively disables object pool, similar to setting |
|
|
|
|
|
|
|
* `usesObjectPool = false`. However, this can be overwritten by server's |
|
|
|
|
|
|
|
* settings (see `AcediaSystem.ini`: `AcediaObjectPool`). |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Store all created pools, so that we can quickly forget stored objects upon |
|
|
|
// Store all created pools, so that we can quickly forget stored objects upon garbage collection |
|
|
|
// garbage collection |
|
|
|
|
|
|
|
var private array<AcediaObjectPool> registeredPools; |
|
|
|
var private array<AcediaObjectPool> registeredPools; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Forgets about all stored (deallocated) object references in registered object pools. |
|
|
|
* Creates a class instance from its `Text` representation. |
|
|
|
protected function DropPools() { |
|
|
|
* |
|
|
|
local int i; |
|
|
|
* Does not generate log messages upon failure. |
|
|
|
|
|
|
|
* |
|
|
|
registeredPools = default.registeredPools; |
|
|
|
* @param classReference Text representation of the class to return. |
|
|
|
for (i = 0; i < registeredPools.length; i += 1) { |
|
|
|
* @return Loaded class, corresponding to its name from `classReference`. |
|
|
|
if (registeredPools[i] == none) { |
|
|
|
*/ |
|
|
|
continue; |
|
|
|
public function class<Object> LoadClass(BaseText classReference) |
|
|
|
} |
|
|
|
{ |
|
|
|
registeredPools[i].Clear(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Creates a class instance from its `BaseText` representation. |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
/// Does not generate log messages upon failure. |
|
|
|
|
|
|
|
public function class<Object> LoadClass(BaseText classReference) { |
|
|
|
if (classReference == none) { |
|
|
|
if (classReference == none) { |
|
|
|
return none; |
|
|
|
return none; |
|
|
|
} |
|
|
|
} |
|
|
|
return class<Object>( |
|
|
|
return class<Object>(DynamicLoadObject(classReference.ToString(), class'Class', true)); |
|
|
|
DynamicLoadObject(classReference.ToString(), |
|
|
|
|
|
|
|
class'Class', |
|
|
|
|
|
|
|
true)); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Creates a class instance from its `string` representation. |
|
|
|
* Creates a class instance from its `string` representation. |
|
|
|
/// |
|
|
|
* |
|
|
|
/// Does not generate log messages upon failure. |
|
|
|
* Does not generate log messages upon failure. |
|
|
|
public function class<Object> LoadClass_S(string classReference) { |
|
|
|
* |
|
|
|
|
|
|
|
* @param classReference `string` representation of the class to return. |
|
|
|
|
|
|
|
* @return Loaded class, corresponding to its name from `classReference`. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function class<Object> LoadClass_S(string classReference) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return class<Object>(DynamicLoadObject(classReference, class'Class', true)); |
|
|
|
return class<Object>(DynamicLoadObject(classReference, class'Class', true)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Creates a new `AcediaObject` of a given subclass using pool (if permitted by settings) and |
|
|
|
* Creates a new `Object` of a given class. |
|
|
|
/// calling its constructor. |
|
|
|
* |
|
|
|
/// |
|
|
|
* For `AcediaObject`s calls constructors and tries (uses them only if they |
|
|
|
/// If Acedia's object does make use of object pools, this method guarantees to return last pooled |
|
|
|
* aren't forbidden for a given class) to make use of their classes' object |
|
|
|
/// object (in a LIFO queue), unless `forceNewInstance` is set to `true`. |
|
|
|
* pools. |
|
|
|
/// |
|
|
|
* |
|
|
|
/// Return value will only be `none` if `classToAllocate` is `none` or abstract. |
|
|
|
* If Acedia's object does make use of object pools, - |
|
|
|
public function AcediaObject Allocate( |
|
|
|
* guarantees to return last pooled object (in a LIFO queue), |
|
|
|
class<AcediaObject> classToAllocate, |
|
|
|
* unless `forceNewInstance` is set to `true`. |
|
|
|
optional bool forceNewInstance |
|
|
|
* |
|
|
|
) { |
|
|
|
* @see `AllocateByReference()`, `AllocateByReference_S()` |
|
|
|
local AcediaObject allocatedObject; |
|
|
|
* |
|
|
|
|
|
|
|
* @param classToAllocate Class of the `Object` that this method will |
|
|
|
|
|
|
|
* create. Must not be subclass of `Actor`. |
|
|
|
|
|
|
|
* @param forceNewInstance Set this to `true` if you require this method to |
|
|
|
|
|
|
|
* create a new instance, bypassing any object pools. |
|
|
|
|
|
|
|
* @return Newly created object. Will only be `none` if: |
|
|
|
|
|
|
|
* 1. `classToAllocate` is `none`; |
|
|
|
|
|
|
|
* 2. `classToAllocate` is abstract; |
|
|
|
|
|
|
|
* 3. `classToAllocate` is derived from `Actor`. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function Object Allocate( |
|
|
|
|
|
|
|
class<Object> classToAllocate, |
|
|
|
|
|
|
|
optional bool forceNewInstance) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// TODO: this is an old code require while we still didn't get rid of |
|
|
|
|
|
|
|
// services - replace it later |
|
|
|
|
|
|
|
local LevelCore core; |
|
|
|
|
|
|
|
local Object allocatedObject; |
|
|
|
|
|
|
|
local AcediaObjectPool relevantPool; |
|
|
|
local AcediaObjectPool relevantPool; |
|
|
|
local class<AcediaObject> acediaObjectClassToAllocate; |
|
|
|
|
|
|
|
local class<AcediaActor> acediaActorClassToAllocate; |
|
|
|
|
|
|
|
local class<Actor> actorClassToAllocate; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (classToAllocate == none) { |
|
|
|
if (classToAllocate == none) { |
|
|
|
return none; |
|
|
|
return none; |
|
|
|
} |
|
|
|
} |
|
|
|
// Try using pool first (only if new instance is not required) |
|
|
|
// Try using pool first (but only if new instance is not required) |
|
|
|
acediaObjectClassToAllocate = class<AcediaObject>(classToAllocate); |
|
|
|
if (!forceNewInstance) { |
|
|
|
acediaActorClassToAllocate = class<AcediaActor>(classToAllocate); |
|
|
|
relevantPool = classToAllocate.static._getPool(); |
|
|
|
if (!forceNewInstance) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (acediaObjectClassToAllocate != none) { |
|
|
|
|
|
|
|
relevantPool = acediaObjectClassToAllocate.static._getPool(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// `relevantPool == none` is expected if object / actor of is setup to |
|
|
|
// `relevantPool == none` is expected if object / actor of is setup to |
|
|
|
// not use object pools. |
|
|
|
// not use object pools. |
|
|
|
if (relevantPool != none) { |
|
|
|
if (relevantPool != none) { |
|
|
|
allocatedObject = relevantPool.Fetch(); |
|
|
|
allocatedObject = relevantPool.Fetch(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// If pools did not work - spawn / create object through regular methods |
|
|
|
// If pools did not work - simply create object manually |
|
|
|
if (allocatedObject == none) |
|
|
|
if (allocatedObject == none) { |
|
|
|
{ |
|
|
|
|
|
|
|
actorClassToAllocate = class<Actor>(classToAllocate); |
|
|
|
|
|
|
|
if (actorClassToAllocate != none) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
core = class'ServerLevelCore'.static.GetInstance(); |
|
|
|
|
|
|
|
if (core == none) { |
|
|
|
|
|
|
|
core = class'ClientLevelCore'.static.GetInstance(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
allocatedObject = core.Spawn(actorClassToAllocate); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
|
|
|
|
allocatedObject = (new classToAllocate); |
|
|
|
allocatedObject = (new classToAllocate); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// Allocation through `new` cannot fail, so its safe to call constructor |
|
|
|
// Call constructors |
|
|
|
allocatedObject._constructor(); |
|
|
|
if (acediaObjectClassToAllocate != none) { |
|
|
|
|
|
|
|
AcediaObject(allocatedObject)._constructor(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (acediaActorClassToAllocate != none) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Call it here, just in case, to make sure constructor is called |
|
|
|
|
|
|
|
// as soon as possible |
|
|
|
|
|
|
|
AcediaActor(allocatedObject)._constructor(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return allocatedObject; |
|
|
|
return allocatedObject; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Creates a new `AcediaObject` of a given subclass using pool (if permitted by settings) and |
|
|
|
* Creates a new `Object` of a given class using its `BaseText` |
|
|
|
/// calling its constructor. |
|
|
|
* representation. |
|
|
|
/// |
|
|
|
* |
|
|
|
/// If Acedia's object does make use of object pools, this method guarantees to return last pooled |
|
|
|
* For `AcediaObject`s calls constructors and tries (uses them only if they |
|
|
|
/// object (in a LIFO queue), unless `forceNewInstance` is set to `true`. |
|
|
|
* aren't forbidden for a given class) to make use of their classes' object |
|
|
|
/// |
|
|
|
* pools. |
|
|
|
/// Return value will only be `none` if `refToClassToAllocate` is `none`, doesn't refer to |
|
|
|
* |
|
|
|
/// an existing class or refers to an abstract class. |
|
|
|
* If Acedia's object does make use of object pools, - |
|
|
|
public function AcediaObject AllocateByReference( |
|
|
|
* guarantees to return last pooled object (in a LIFO queue), |
|
|
|
|
|
|
|
* unless `forceNewInstance` is set to `true`. |
|
|
|
|
|
|
|
* @see `Allocate()`, `AllocateByReference_S()` |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param refToClassToAllocate `BaseText` representation of the class' name |
|
|
|
|
|
|
|
* of the `Object` that this method will create. Must not be subclass of |
|
|
|
|
|
|
|
* `Actor`. |
|
|
|
|
|
|
|
* @param forceNewInstance Set this to `true` if you require this method to |
|
|
|
|
|
|
|
* create a new instance, bypassing any object pools. |
|
|
|
|
|
|
|
* @return Newly created object. Will only be `none` if: |
|
|
|
|
|
|
|
* 1. `classToAllocate` is `none`; |
|
|
|
|
|
|
|
* 2. `classToAllocate` is abstract; |
|
|
|
|
|
|
|
* 3. `classToAllocate` is derived from `Actor`. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function Object AllocateByReference( |
|
|
|
|
|
|
|
BaseText refToClassToAllocate, |
|
|
|
BaseText refToClassToAllocate, |
|
|
|
optional bool forceNewInstance) |
|
|
|
optional bool forceNewInstance |
|
|
|
{ |
|
|
|
) { |
|
|
|
return Allocate(LoadClass(refToClassToAllocate), forceNewInstance); |
|
|
|
local class<Object> classToAllocate; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classToAllocate = LoadClass(refToClassToAllocate); |
|
|
|
|
|
|
|
return Allocate(class<AcediaObject>(classToAllocate), forceNewInstance); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Creates a new `AcediaObject` of a given subclass using pool (if permitted by settings) and |
|
|
|
* Creates a new `Object` of a given class using its `string` |
|
|
|
/// calling its constructor. |
|
|
|
* representation. |
|
|
|
/// |
|
|
|
* |
|
|
|
/// If Acedia's object does make use of object pools, this method guarantees to return last pooled |
|
|
|
* For `AcediaObject`s calls constructors and tries (uses them only if they |
|
|
|
/// object (in a LIFO queue), unless `forceNewInstance` is set to `true`. |
|
|
|
* aren't forbidden for a given class) to make use of their classes' object |
|
|
|
/// |
|
|
|
* pools. |
|
|
|
/// Return value will only be `none` if `refToClassToAllocate` is `none`, doesn't refer to |
|
|
|
* |
|
|
|
/// an existing class or refers to an abstract class. |
|
|
|
* If Acedia's object does make use of object pools, - |
|
|
|
public function AcediaObject AllocateByReference_S( |
|
|
|
* guarantees to return last pooled object (in a LIFO queue), |
|
|
|
|
|
|
|
* unless `forceNewInstance` is set to `true`. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @see `Allocate()`, `AllocateByReference()` |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param refToClassToAllocate `string` representation of the class' name |
|
|
|
|
|
|
|
* of the `Object` that this method will create. Must not be subclass of |
|
|
|
|
|
|
|
* `Actor`. |
|
|
|
|
|
|
|
* @param forceNewInstance Set this to `true` if you require this method to |
|
|
|
|
|
|
|
* create a new instance, bypassing any object pools. |
|
|
|
|
|
|
|
* @return Newly created object. Will only be `none` if: |
|
|
|
|
|
|
|
* 1. `classToAllocate` is `none`; |
|
|
|
|
|
|
|
* 2. `classToAllocate` is abstract; |
|
|
|
|
|
|
|
* 3. `classToAllocate` is derived from `Actor`. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function Object AllocateByReference_S( |
|
|
|
|
|
|
|
string refToClassToAllocate, |
|
|
|
string refToClassToAllocate, |
|
|
|
optional bool forceNewInstance) |
|
|
|
optional bool forceNewInstance |
|
|
|
{ |
|
|
|
) { |
|
|
|
return Allocate(LoadClass_S(refToClassToAllocate), forceNewInstance); |
|
|
|
local class<Object> classToAllocate; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classToAllocate = LoadClass_S(refToClassToAllocate); |
|
|
|
|
|
|
|
return Allocate(class<AcediaObject>(classToAllocate), forceNewInstance); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Releases one reference to a given `AcediaObject`, calling its finalizers in case all references |
|
|
|
* Releases one reference to a given `AcediaObject`, calling its finalizers in |
|
|
|
/// were released. |
|
|
|
* case all references were released. |
|
|
|
/// |
|
|
|
* |
|
|
|
/// Method will attempt to store `objectToRelease` in its object pool once deallocated, unless it is |
|
|
|
* Method will attempt to store `objectToRelease` in its object pool once |
|
|
|
/// forbidden by its class' settings. |
|
|
|
* deallocated, unless it is forbidden by its class' settings. |
|
|
|
public function Free(AcediaObject objectToRelease) { |
|
|
|
* |
|
|
|
|
|
|
|
* @see `FreeMany()` |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param objectToRelease Object, which reference method needs to release. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function Free(Object objectToRelease) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// TODO: this is an old code require while we still didn't get rid of |
|
|
|
|
|
|
|
// services - replace it later, changing argument to `AcediaObject` |
|
|
|
|
|
|
|
local AcediaObjectPool relevantPool; |
|
|
|
local AcediaObjectPool relevantPool; |
|
|
|
local Actor objectAsActor; |
|
|
|
|
|
|
|
local AcediaActor objectAsAcediaActor; |
|
|
|
|
|
|
|
local AcediaObject objectAsAcediaObject; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (objectToRelease == none) { |
|
|
|
if (objectToRelease == none) return; |
|
|
|
return; |
|
|
|
if (!objectToRelease.IsAllocated()) return; |
|
|
|
} |
|
|
|
|
|
|
|
// Call finalizers for Acedia's objects and actors |
|
|
|
objectToRelease._deref(); |
|
|
|
objectAsAcediaObject = AcediaObject(objectToRelease); |
|
|
|
// Finalize object if all of its references are gone |
|
|
|
objectAsAcediaActor = AcediaActor(objectToRelease); |
|
|
|
if (objectToRelease._getRefCount() <= 0) { |
|
|
|
if (objectAsAcediaObject != none) |
|
|
|
relevantPool = objectToRelease._getPool(); |
|
|
|
{ |
|
|
|
objectToRelease._finalizer(); |
|
|
|
if (!objectAsAcediaObject.IsAllocated()) { |
|
|
|
if (relevantPool != none) { |
|
|
|
return; |
|
|
|
relevantPool.Store(objectToRelease); |
|
|
|
} |
|
|
|
|
|
|
|
objectAsAcediaObject._deref(); |
|
|
|
|
|
|
|
if (objectAsAcediaObject._getRefCount() > 0) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
relevantPool = objectAsAcediaObject._getPool(); |
|
|
|
|
|
|
|
objectAsAcediaObject._finalizer(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (objectAsAcediaActor != none) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (!objectAsAcediaActor.IsAllocated()) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
objectAsAcediaActor._deref(); |
|
|
|
|
|
|
|
if (objectAsAcediaActor._getRefCount() > 0) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
objectAsAcediaActor._finalizer(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Try to store freed object in a pool |
|
|
|
|
|
|
|
if (relevantPool != none && relevantPool.Store(objectAsAcediaObject)) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
// Otherwise destroy actors and forget about objects |
|
|
|
|
|
|
|
objectAsActor = Actor(objectToRelease); |
|
|
|
|
|
|
|
if (objectAsActor != none) { |
|
|
|
|
|
|
|
objectAsActor.Destroy(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Releases one reference for each `AcediaObject` inside the given array `objectsToRelease`, |
|
|
|
* Releases one reference to each `AcediaObject` inside the given array |
|
|
|
/// calling finalizers for the ones that got all of their references released. |
|
|
|
* `objectsToRelease`, calling finalizers for the ones that got all of their |
|
|
|
/// |
|
|
|
* references released. |
|
|
|
/// Method will attempt to store objects inside `objectsToRelease` in their object pools, unless it |
|
|
|
* |
|
|
|
/// is forbidden by their class' settings. |
|
|
|
* Method will attempt to store objects inside `objectsToRelease` in their |
|
|
|
public function FreeMany(array<AcediaObject> objectsToRelease) { |
|
|
|
* object pools, unless it is forbidden by their class' settings. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @see `Free()` |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param objectToRelease Array of objects, which reference method needs |
|
|
|
|
|
|
|
* to release. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function FreeMany(array<Object> objectsToRelease) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// TODO: this is an old code require while we still didn't get rid of |
|
|
|
|
|
|
|
// services - replace it later, changing argument to `AcediaObject` |
|
|
|
|
|
|
|
local int i; |
|
|
|
local int i; |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < objectsToRelease.length; i += 1) { |
|
|
|
for (i = 0; i < objectsToRelease.length; i += 1) { |
|
|
@ -349,26 +202,21 @@ public function FreeMany(array<Object> objectsToRelease) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Forces engine to perform garbage collection. |
|
|
|
* Forces Unreal Engine to perform garbage collection. |
|
|
|
/// |
|
|
|
* By default also cleans up all of the Acedia's objects pools. |
|
|
|
/// Process of manual garbage collection causes significant lag spike during the game and should be |
|
|
|
* |
|
|
|
/// used sparingly and at right moments. |
|
|
|
* Process of garbage collection causes significant lag spike during the game |
|
|
|
/// |
|
|
|
* and should be used sparingly and at right moments. |
|
|
|
/// If no `LevelCore` was setup, Acedia doesn't have access to the level and cannot perform garbage |
|
|
|
* |
|
|
|
/// collection, meaning that this method can fail. |
|
|
|
* If not `LevelCore` was setup, Acedia doesn't have access to the level and |
|
|
|
/// |
|
|
|
* cannot perform garbage collection, meaning that this method can fail. |
|
|
|
/// By default also cleans up all of the Acedia's objects pools. |
|
|
|
* |
|
|
|
/// Set [`keepAcediaPools`] to `true` to NOT garbage collect objects inside pools. |
|
|
|
* @param keepAcediaPools Set this to `true` to NOT garbage collect |
|
|
|
/// Pools won't be dropped regardless of this parameter if no `LevelCore` is found. |
|
|
|
* objects inside pools. Otherwise keep it `false`. |
|
|
|
/// |
|
|
|
* Pools won't be dropped regardless of this parameter if no `LevelCore` is |
|
|
|
/// Returns `true` if garbage collection successfully happened and `false` if it failed. |
|
|
|
* found. |
|
|
|
/// Garbage collection can only fail if no `LevelCore` was yet setup. |
|
|
|
* @return `true` if garbage collection successfully happened and `false` if it |
|
|
|
public function bool /*unreal*/ CollectGarbage(optional bool keepAcediaPools) { |
|
|
|
* failed. Garbage collection can only fail if no `LevelCore` was yet |
|
|
|
|
|
|
|
* setup. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function bool CollectGarbage(optional bool keepAcediaPools) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
local LevelCore core; |
|
|
|
local LevelCore core; |
|
|
|
|
|
|
|
|
|
|
|
// Try to find level core |
|
|
|
// Try to find level core |
|
|
@ -388,24 +236,18 @@ public function bool CollectGarbage(optional bool keepAcediaPools) |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/// Registers new object pool to auto-clean before Acedia's garbage collection. |
|
|
|
* Registers new object pool to auto-clean before Acedia's garbage collection. |
|
|
|
/// |
|
|
|
* |
|
|
|
/// Returns `true` if `newPool` was registered and `false` if `newPool == none` or was already |
|
|
|
* @param newPool New object pool that can get cleaned if `CollectGarbage()` |
|
|
|
/// registered. |
|
|
|
* is called with appropriate parameters. |
|
|
|
public function bool RegisterNewPool(AcediaObjectPool newPool) { |
|
|
|
* @return `true` if `newPool` was registered, |
|
|
|
|
|
|
|
* `false` if `newPool == none` or was already registered. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function bool RegisterNewPool(AcediaObjectPool newPool) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
local int i; |
|
|
|
local int i; |
|
|
|
|
|
|
|
|
|
|
|
if (newPool == none) { |
|
|
|
if (newPool == none) { |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
registeredPools = default.registeredPools; |
|
|
|
registeredPools = default.registeredPools; |
|
|
|
for (i = 0; i < registeredPools.length; i += 1) |
|
|
|
for (i = 0; i < registeredPools.length; i += 1) { |
|
|
|
{ |
|
|
|
|
|
|
|
if (registeredPools[i] == newPool) { |
|
|
|
if (registeredPools[i] == newPool) { |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
@ -415,23 +257,5 @@ public function bool RegisterNewPool(AcediaObjectPool newPool) |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
defaultproperties { |
|
|
|
* Forgets about all stored (deallocated) object references in registered |
|
|
|
|
|
|
|
* object pools. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
protected function DropPools() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
local int i; |
|
|
|
|
|
|
|
registeredPools = default.registeredPools; |
|
|
|
|
|
|
|
for (i = 0; i < registeredPools.length; i += 1) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (registeredPools[i] == none) { |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
registeredPools[i].Clear(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
defaultproperties |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
} |
|
|
|
} |