diff --git a/sources/AcediaObjectPool.uc b/sources/CoreRealm/API/Memory/AcediaObjectPool.uc
similarity index 100%
rename from sources/AcediaObjectPool.uc
rename to sources/CoreRealm/API/Memory/AcediaObjectPool.uc
diff --git a/sources/CoreRealm/API/Memory/MemoryAPI.uc b/sources/CoreRealm/API/Memory/MemoryAPI.uc
new file mode 100644
index 0000000..c63b7ba
--- /dev/null
+++ b/sources/CoreRealm/API/Memory/MemoryAPI.uc
@@ -0,0 +1,434 @@
+/**
+ * API that provides functions for managing object of classes, derived from
+ * `AcediaObject`. It takes care of managing their object pools, as well as
+ * ensuring that constructors and finalizers are called properly.
+ * Almost all `AcediaObject`s should use this API's methods for their own
+ * creation and destruction.
+ * Copyright 2020-2022 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 MemoryAPI extends AcediaObject;
+
+/**
+ * # Memory API
+ *
+ * This is most-basic API that must be created before anything else in Acedia,
+ * 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 finalizers are called properly.
+ * Almost all `AcediaObject`s should use this API's methods for their own
+ * creation and destruction.
+ *
+ * ## Usage
+ *
+ * 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 are dealing with that. `MemoryAPI` is designed to work in
+ * the absence of any level (and, therefore, `Actor`s) at all.
+ * Simply use `MemoryAPI.Allocate()` to create a new object and
+ * `MemoryAPI.Free()` to get rid on unneeded reference. Do note that
+ * `AcediaObject`s use reference counting and object will be deallocated and
+ * pooled only after every trackable reference was released by
+ * `MemoryAPI.Free()`.
+ * Best practice is to only care about what object reference you're
+ * keeping, properly release them with `MemoryAPI.Free()` and to NEVER EVER USE
+ * THEM after you've release them. Regardless of whether they were actually
+ * deallocated.
+ *
+ * There's also a set of auxiliry methods for either loading `class`es from
+ * their `BaseText`/`string`-given names or even directly creating objects of
+ * said classes.
+ *
+ * ## Motivation
+ *
+ * UnrealScript lacks any practical way to destroy non-actor objects on
+ * demand: the best one can do is remove any references to the object and wait
+ * for garbage collection. But garbage collection itself is too slow and causes
+ * noticeable lag spikes for players, making it suitable only for cleaning
+ * objects when switching levels. To alleviate this problem, there exists
+ * a standard class `ObjectPool` that stores unused objects (mostly resources
+ * such as textures) inside dynamic array until they are needed.
+ * Unfortunately, using a single ObjectPool for a large volume of objects
+ * is impractical from performance perspective, since it stores objects of all
+ * 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
+// garbage collection
+var private array registeredPools;
+
+/**
+ * Creates a class instance from its `Text` representation.
+ *
+ * Does not generate log messages upon failure.
+ *
+ * @param classReference Text representation of the class to return.
+ * @return Loaded class, corresponding to its name from `classReference`.
+ */
+public final function class LoadClass(
+ BaseText classReference)
+{
+ if (classReference == none) {
+ return none;
+ }
+ return class(
+ DynamicLoadObject(classReference.ToString(),
+ class'Class',
+ true));
+}
+
+/**
+ * Creates a class instance from its `string` representation.
+ *
+ * Does not generate log messages upon failure.
+ *
+ * @param classReference `string` representation of the class to return.
+ * @return Loaded class, corresponding to its name from `classReference`.
+ */
+public final function class LoadClass_S(string classReference)
+{
+ return class(DynamicLoadObject(classReference, class'Class', true));
+}
+
+/**
+ * Creates a new `Object` of a given class.
+ *
+ * For `AcediaObject`s calls constructors and tries (uses them only if they
+ * aren't forbidden for a given class) to make use of their classes' object
+ * pools.
+ *
+ * If Acedia's object does make use of object pools, -
+ * guarantees to return last pooled object (in a LIFO queue),
+ * unless `forceNewInstance` is set to `true`.
+ *
+ * @see `AllocateByReference()`, `AllocateByReference_S()`
+ *
+ * @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 final function Object Allocate(
+ class 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 class acediaObjectClassToAllocate;
+ local class acediaActorClassToAllocate;
+ local class actorClassToAllocate;
+
+ if (classToAllocate == none) {
+ return none;
+ }
+ // Try using pool first (only if new instance is not required)
+ acediaObjectClassToAllocate = class(classToAllocate);
+ acediaActorClassToAllocate = class(classToAllocate);
+ if (!forceNewInstance)
+ {
+ if (acediaObjectClassToAllocate != none) {
+ relevantPool = acediaObjectClassToAllocate.static._getPool();
+ }
+ // `relevantPool == none` is expected if object / actor of is setup to
+ // not use object pools.
+ if (relevantPool != none) {
+ allocatedObject = relevantPool.Fetch();
+ }
+ }
+ // If pools did not work - spawn / create object through regular methods
+ if (allocatedObject == none)
+ {
+ actorClassToAllocate = class(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);
+ }
+ }
+ // Call constructors
+ 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;
+}
+
+/**
+ * Creates a new `Object` of a given class using its `BaseText`
+ * respresentation.
+ *
+ * For `AcediaObject`s calls constructors and tries (uses them only if they
+ * aren't forbidden for a given class) to make use of their classes' object
+ * pools.
+ *
+ * If Acedia's object does make use of object pools, -
+ * 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 final function Object AllocateByReference(
+ BaseText refToClassToAllocate,
+ optional bool forceNewInstance)
+{
+ return Allocate(LoadClass(refToClassToAllocate), forceNewInstance);
+}
+
+/**
+ * Creates a new `Object` of a given class using its `string`
+ * respresentation.
+ *
+ * For `AcediaObject`s calls constructors and tries (uses them only if they
+ * aren't forbidden for a given class) to make use of their classes' object
+ * pools.
+ *
+ * If Acedia's object does make use of object pools, -
+ * 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 final function Object AllocateByReference_S(
+ string refToClassToAllocate,
+ optional bool forceNewInstance)
+{
+ return Allocate(LoadClassS(refToClassToAllocate), forceNewInstance);
+}
+
+/**
+ * Releases one reference to a given `AcediaObject`, calling its finalizers in
+ * case all references were released.
+ *
+ * Method will attempt to store `objectToDeallocate` in its object pool once
+ * deallocated, unless it is forbidden by its class' settings.
+ *
+ * @see `FreeMany()`
+ *
+ * @param objectToDeallocate Object that to deallocate.
+ */
+public final function Free(Object objectToDeallocate)
+{
+ // 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 Actor objectAsActor;
+ local AcediaActor objectAsAcediaActor;
+ local AcediaObject objectAsAcediaObject;
+
+ if (objectToDeallocate == none) {
+ return;
+ }
+ // Call finalizers for Acedia's objects and actors
+ objectAsAcediaObject = AcediaObject(objectToDeallocate);
+ objectAsAcediaActor = AcediaActor(objectToDeallocate);
+ if (objectAsAcediaObject != none)
+ {
+ if (!objectAsAcediaObject.IsAllocated()) {
+ return;
+ }
+ 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(objectToDeallocate);
+ if (objectAsActor != none) {
+ objectAsActor.Destroy();
+ }
+}
+
+/**
+ * Releases one reference to each `AcediaObject` inside the given array
+ * `objectsToDelete`, calling finalizers for the ones that got all of their
+ * references released.
+ *
+ * Method will attempt to store objects inside `objectsToDelete` in their
+ * object pools, unless it is forbidden by their class' settings.
+ *
+ * @see `Free()`
+ *
+ * @param objectToDeallocate Object that to deallocate.
+ */
+public final function FreeMany(array objectsToDelete)
+{
+ // 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;
+
+ for (i = 0; i < objectsToDelete.length; i += 1) {
+ Free(objectsToDelete[i]);
+ }
+}
+
+/**
+ * Forces Unreal Engine to perform garbage collection.
+ * By default also cleans up all of the Acedia's objects pools.
+ *
+ * Process of garbage collection causes significant lag spike during the game
+ * and should be used sparingly and at right moments.
+ *
+ * If not `LevelCore` was setup, Acedia doesn't have access to the level and
+ * cannot perform garbage collection, meaning that this method can fail.
+ *
+ * @param keepAcediaPools Set this to `true` to NOT garbage collect
+ * objects inside pools. Otherwise keep it `false`.
+ * Pools won't be dropped regardless of this parameter if no `LevelCore` is
+ * found.
+ * @return `true` if garbage collection successfully happened and `false` if it
+ * failed. Garbage colelction can only fail if no `LevelCore` was yet
+ * setup.
+ */
+public final function bool CollectGarbage(optional bool keepAcediaPools)
+{
+ local LevelCore core;
+
+ // Try to find level core
+ core = class'ServerLevelCore'.static.GetInstance();
+ if (core == none) {
+ core = class'ClientLevelCore'.static.GetInstance();
+ }
+ if (core == none) {
+ return false;
+ }
+ // Drop content of all `AcediaObjectPools` first
+ if (!keepAcediaPools) {
+ DropPools();
+ }
+ // This makes Unreal Engine do garbage collection
+ core.ConsoleCommand("obj garbage");
+ return true;
+}
+
+/**
+ * Registers new object pool to auto-clean before Acedia's garbage collection.
+ *
+ * @param newPool New object pool that can get cleaned if `CollectGarbage()`
+ * is called with appropriate parameters.
+ * @return `true` if `newPool` was registered,
+ * `false` if `newPool == none` or was already registered.
+ */
+public final function bool RegisterNewPool(AcediaObjectPool newPool)
+{
+ local int i;
+
+ if (newPool == none) {
+ return false;
+ }
+ registeredPools = default.registeredPools;
+ for (i = 0; i < registeredPools.length; i += 1)
+ {
+ if (registeredPools[i] == newPool) {
+ return false;
+ }
+ }
+ registeredPools[registeredPools.length] = newPool;
+ default.registeredPools = registeredPools;
+ return true;
+}
+
+// Forgets about all stored object references in registered object pools
+private final 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
+{
+}
\ No newline at end of file
diff --git a/sources/Memory/Tests/MockActor.uc b/sources/CoreRealm/API/Memory/Tests/MockActor.uc
similarity index 100%
rename from sources/Memory/Tests/MockActor.uc
rename to sources/CoreRealm/API/Memory/Tests/MockActor.uc
diff --git a/sources/Memory/Tests/MockObject.uc b/sources/CoreRealm/API/Memory/Tests/MockObject.uc
similarity index 100%
rename from sources/Memory/Tests/MockObject.uc
rename to sources/CoreRealm/API/Memory/Tests/MockObject.uc
diff --git a/sources/Memory/Tests/MockObjectNoPool.uc b/sources/CoreRealm/API/Memory/Tests/MockObjectNoPool.uc
similarity index 100%
rename from sources/Memory/Tests/MockObjectNoPool.uc
rename to sources/CoreRealm/API/Memory/Tests/MockObjectNoPool.uc
diff --git a/sources/Memory/Tests/TEST_Memory.uc b/sources/CoreRealm/API/Memory/Tests/TEST_Memory.uc
similarity index 99%
rename from sources/Memory/Tests/TEST_Memory.uc
rename to sources/CoreRealm/API/Memory/Tests/TEST_Memory.uc
index e32b77e..3110edb 100644
--- a/sources/Memory/Tests/TEST_Memory.uc
+++ b/sources/CoreRealm/API/Memory/Tests/TEST_Memory.uc
@@ -1,7 +1,7 @@
/**
* Set of tests related to `MemoryAPI` class and the chain of events related to
* creating/destroying Acedia's objects / actors.
- * Copyright 2020 - 2021 Anton Tarasenko
+ * Copyright 2020-2022 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
diff --git a/sources/Memory/MemoryAPI.uc b/sources/Memory/MemoryAPI.uc
deleted file mode 100644
index 96c04df..0000000
--- a/sources/Memory/MemoryAPI.uc
+++ /dev/null
@@ -1,289 +0,0 @@
-/**
- * API that provides functions for managing objects and actors that make
- * use of Acedia's object pools and perform Acedia-specific initialization and
- * cleanup procedures.
- * This API's functions should be used for all Acedia's objects and actors.
- * Copyright 2020 - 2021 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 MemoryAPI extends AcediaObject;
-
-/**
- * Creates a class instance from its `Text` representation.
- *
- * Does not generate log messages upon failure.
- *
- * @param classReference Text representation of the class to return.
- * @return Loaded class, corresponding to its name from `classReference`.
- */
-public final function class LoadClass(BaseText classReference)
-{
- if (classReference == none) {
- return none;
- }
- return class( DynamicLoadObject(classReference.ToString(),
- class'Class', true));
-}
-
-/**
- * Creates a class instance from its `string` representation.
- *
- * Does not generate log messages upon failure.
- *
- * @param classReference `string` representation of the class to return.
- * @return Loaded class, corresponding to its name from `classReference`.
- */
-public final function class LoadClassS(string classReference)
-{
- return class(DynamicLoadObject(classReference, class'Class', true));
-}
-
-/**
- * Creates a new `Object` / `Actor` of a given class.
- *
- * If uses a proper spawning mechanism for both objects (`new`)
- * and actors (`Spawn`).
- *
- * For Acedia's objects / actors calls constructors.
- * For Acedia's objects tries to make use of their object pools.
- *
- * If Acedia's object does make use of object pools, -
- * guarantees to return last pooled object (in a LIFO queue),
- * unless `forceNewInstance == true`.
- *
- * @param classToAllocate Class of the `Object` / `Actor` that this method
- * must create.
- * @param forceNewInstance Set this to `true` if you require this method to
- * create a new instance, bypassing any object pools.
- * @return Newly created object,
- * `none` if creation has failed (only possible for actors).
- */
-public final function Object Allocate(
- class classToAllocate,
- optional bool forceNewInstance)
-{
- local LevelCore core;
- local Object allocatedObject;
- local AcediaObjectPool relevantPool;
- local class acediaObjectClassToAllocate;
- local class acediaActorClassToAllocate;
- local class actorClassToAllocate;
- if (classToAllocate == none) {
- return none;
- }
- // Try using pool first (only if new instance is not required)
- acediaObjectClassToAllocate = class(classToAllocate);
- acediaActorClassToAllocate = class(classToAllocate);
- if (!forceNewInstance)
- {
- if (acediaObjectClassToAllocate != none) {
- relevantPool = acediaObjectClassToAllocate.static._getPool();
- }
- // `relevantPool == none` is expected if object / actor of is setup to
- // not use object pools.
- if (relevantPool != none) {
- allocatedObject = relevantPool.Fetch();
- }
- }
- // If pools did not work - spawn / create object through regular methods
- if (allocatedObject == none)
- {
- actorClassToAllocate = class(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);
- }
- }
- // Call constructors
- 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;
-}
-
-/**
- * Creates a new `Object` / `Actor` of a class, given by its
- * string representation.
- *
- * If uses a proper spawning mechanism for both objects (`new`)
- * and actors (`Spawn`).
- *
- * For Acedia's objects / actors calls constructors.
- * For Acedia's objects tries to make use of their object pools.
- *
- * If Acedia's object does make use of object pools, -
- * guarantees to return last pooled object (in a LIFO queue),
- * unless `forceNewInstance == true`.
- *
- * @param refToClassToAllocate `BaseText` representation of the class of
- * the `Object` / `Actor` that this method must create.
- * @param forceNewInstance Set this to `true` if you require this method to
- * create a new instance, bypassing any object pools.
- * @return Newly created object,
- * `none` if creation has failed (only possible for actors).
- */
-public final function Object AllocateByReference(
- BaseText refToClassToAllocate,
- optional bool forceNewInstance)
-{
- return Allocate(LoadClass(refToClassToAllocate), forceNewInstance);
-}
-
-/**
- * Creates a new `Object` / `Actor` of a class, given by its
- * string representation.
- *
- * If uses a proper spawning mechanism for both objects (`new`)
- * and actors (`Spawn`).
- *
- * For Acedia's objects / actors calls constructors.
- * For Acedia's objects tries to make use of their object pools.
- *
- * If Acedia's object does make use of object pools, -
- * guarantees to return last pooled object (in a LIFO queue),
- * unless `forceNewInstance == true`.
- *
- * @param classToAllocate `string` representation of the class of
- * the `Object` / `Actor` that this method must create.
- * @param forceNewInstance Set this to `true` if you require this method to
- * create a new instance, bypassing any object pools.
- * @return Newly created object,
- * `none` if creation has failed (only possible for actors).
- */
-public final function Object AllocateByReferenceS(
- string refToClassToAllocate,
- optional bool forceNewInstance)
-{
- return Allocate(LoadClassS(refToClassToAllocate), forceNewInstance);
-}
-
-/**
- * Deallocates given `Object` / `Actor` resource, calling finalizers for
- * Acedia's objects and actors.
- *
- * If Acedia's object is passed, method will try to store it
- * in an object pool.
- *
- * Actors will be destroyed.
- *
- * @param objectToDeallocate `Object` / `Actor` to deallocate.
- */
-public final function Free(Object objectToDeallocate)
-{
- local AcediaObjectPool relevantPool;
- local Actor objectAsActor;
- local AcediaActor objectAsAcediaActor;
- local AcediaObject objectAsAcediaObject;
- if (objectToDeallocate == none) {
- return;
- }
- // Call finalizers for Acedia's objects and actors
- objectAsAcediaObject = AcediaObject(objectToDeallocate);
- objectAsAcediaActor = AcediaActor(objectToDeallocate);
- if (objectAsAcediaObject != none)
- {
- if (!objectAsAcediaObject.IsAllocated()) {
- return;
- }
- 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(objectToDeallocate);
- if (objectAsActor != none) {
- objectAsActor.Destroy();
- }
-}
-
-/**
- * Frees given array of `Object` / `Actor` resources.
- *
- * If Acedia's object or actor is contained in the passed array,
- * method will try to store it in an object pool.
- *
- * @param objectsToDelete `Object` / `Actor` that must be freed.
- */
-public final function FreeMany(array objectsToDelete)
-{
- local int i;
- for (i = 0; i < objectsToDelete.length; i += 1) {
- Free(objectsToDelete[i]);
- }
-}
-
-/**
- * Forces Unreal Engine to perform garbage collection.
- * By default also cleans up all the objects pools registered in
- * `MemoryService`, which includes all of the pools for
- * Acedia's built-in classes.
- *
- * Process of garbage collection causes significant lag spike during the game
- * and should be used sparingly and at right moments..
- *
- * @param keepAcediaPools Set this to `true` to NOT garbage collect
- * objects in a borrow pool. Otherwise keep it `false`.
- */
-public final function CollectGarbage(optional bool keepAcediaPools)
-{
- local MemoryService service;
- // Drop content of all `AcediaObjectPools` first
- if (!keepAcediaPools)
- {
- service = MemoryService(class'MemoryService'.static.Require());
- if (service != none) {
- service.ClearAll();
- }
- }
- // This makes Unreal Engine do garbage collection
- class'ServerLevelCore'.static.GetInstance().ConsoleCommand("obj garbage");
-}
-
-defaultproperties
-{
-}
\ No newline at end of file
diff --git a/sources/Memory/MemoryService.uc b/sources/Memory/MemoryService.uc
deleted file mode 100644
index fbe30df..0000000
--- a/sources/Memory/MemoryService.uc
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * This service is meant to perform auxiliary functions for `MemoryAPI`.
- * It's main task is to keep track of all `AcediaObjectPool`s to force them to
- * get rid of object references before garbage collection.
- * Copyright 2020 - 2021 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 MemoryService extends Service;
-
-var private array registeredPools;
-
-/**
- * Registers new object pool to auto-clean before Acedia's garbage collection.
- *
- * Registered `AcediaObjectPool`s will persist even if `MemoryService` is
- * destroyed and re-created.
- *
- * @param newPool Pool that service must clean during a `ClearAll()` call.
- * @return `true` if `newPool` was registered,
- * `false` if `newPool == none` or was already registered.
- */
-public final function bool RegisterNewPool(AcediaObjectPool newPool)
-{
- local int i;
- if (newPool == none) {
- return false;
- }
- registeredPools = default.registeredPools;
- for (i = 0; i < registeredPools.length; i += 1) {
- if (registeredPools[i] == newPool) return false;
- }
- registeredPools[registeredPools.length] = newPool;
- default.registeredPools = registeredPools;
- return true;
-}
-
-/**
- * Clears all registered (via `RegisterNewPool()`) pools.
- */
-public final function ClearAll()
-{
- local int i;
- registeredPools = default.registeredPools;
- for (i = 0; i < registeredPools.length; i += 1)
- {
- if (registeredPools[i] == none) continue;
- registeredPools[i].Clear();
- }
-}
-
-defaultproperties
-{
-}
\ No newline at end of file
diff --git a/sources/Types/AcediaObject.uc b/sources/Types/AcediaObject.uc
index 6435836..340fb62 100644
--- a/sources/Types/AcediaObject.uc
+++ b/sources/Types/AcediaObject.uc
@@ -87,17 +87,14 @@ var protected const array stringConstants;
*/
public final static function AcediaObjectPool _getPool()
{
- local MemoryService service;
if (!default.usesObjectPool) {
return none;
}
- if (default._objectPool == none) {
+ if (default._objectPool == none)
+ {
default._objectPool = new class'AcediaObjectPool';
default._objectPool.Initialize(default.class);
- service = MemoryService(class'MemoryService'.static.Require());
- if (service != none) {
- service.RegisterNewPool(default._objectPool);
- }
+ __().memory.RegisterNewPool(default._objectPool);
}
return default._objectPool;
}