Anton Tarasenko
2 years ago
13 changed files with 295 additions and 355 deletions
@ -0,0 +1,209 @@
|
||||
/** |
||||
* Container for the information about available resources from other packages. |
||||
* Copyright 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 <https://www.gnu.org/licenses/>. |
||||
*/ |
||||
class AcediaEnvironment extends AcediaObject; |
||||
|
||||
/** |
||||
* # `AcediaEnvironment` |
||||
* |
||||
* Instance of this class will be used by Acedia to manage resources available |
||||
* from different packages like `Feature`s, aliases, etc.. |
||||
* This is mostly necessary to implement Acedia loader (and, possibly, |
||||
* its alternatives) that would load available packages and enable `Feature`s |
||||
* admin wants to be enabled. |
||||
* |
||||
* ## Packages |
||||
* |
||||
* Any package to be used in Acedia should first be *registered* with |
||||
* `RegisterPackage()` method. Then a manifest class from it will be read and |
||||
* Acedia will become aware of all the resources that package contains. |
||||
* Once any of those resources is used, package gets marked as *loaded* and its |
||||
* *entry object* (if specified) will be created. |
||||
*/ |
||||
|
||||
var private array< class<_manifest> > availablePackages; |
||||
var private array< class<_manifest> > loadedPackages; |
||||
|
||||
var private array< class<Feature> > availableFeatures; |
||||
var private array<Feature> enabledFeatures; |
||||
|
||||
var private string manifestSuffix; |
||||
|
||||
var private LoggerAPI.Definition infoRegisteringPackage, infoAlreadyRegistered; |
||||
var private LoggerAPI.Definition errNotRegistered; |
||||
|
||||
protected function Constructor() |
||||
{ |
||||
// Always register our core package |
||||
RegisterPackage_S("AcediaCore.Manifest"); |
||||
} |
||||
|
||||
/** |
||||
* Registers an Acedia package with name given by `packageName`. |
||||
* |
||||
* @param packageName Name of the package to register. Must not be `none`. |
||||
* This package must exist and not have yet been registered in this |
||||
* environment. |
||||
* @return `true` if package was successfully registered, `false` if it |
||||
* either does not exist, was already registered or `packageName` is |
||||
* `none`. |
||||
*/ |
||||
public final function bool RegisterPackage(BaseText packageName) |
||||
{ |
||||
local class<_manifest> manifestClass; |
||||
|
||||
if (packageName == none) { |
||||
return false; |
||||
} |
||||
_.logger.Auto(infoRegisteringPackage).Arg(packageName.Copy()); |
||||
manifestClass = class<_manifest>(DynamicLoadObject( |
||||
packageName $ manifestSuffix, class'Class', true)); |
||||
if (manifestClass == none) |
||||
{ |
||||
_.logger.Auto(errNotRegistered).Arg(packageName.Copy()); |
||||
return false; |
||||
} |
||||
if (IsManifestRegistered(manifestClass)) |
||||
{ |
||||
_.logger.Auto(infoAlreadyRegistered).Arg(packageName.Copy()); |
||||
return false; |
||||
} |
||||
availablePackages[availablePackages.length] = manifestClass; |
||||
ReadManifest(manifestClass); |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Registers an Acedia package with name given by `packageName`. |
||||
* |
||||
* @param packageName Name of the package to register. |
||||
* This package must exist and not have yet been registered in this |
||||
* environment. |
||||
* @return `true` if package was successfully registered, `false` if it |
||||
* either does not exist or was already registered. |
||||
*/ |
||||
public final function RegisterPackage_S(string packageName) |
||||
{ |
||||
local Text wrapper; |
||||
|
||||
wrapper = _.text.FromString(packageName); |
||||
RegisterPackage(wrapper); |
||||
_.memory.Free(wrapper); |
||||
} |
||||
|
||||
private final function bool IsManifestRegistered(class<_manifest> manifestClass) |
||||
{ |
||||
local int i; |
||||
|
||||
for (i = 0; i < availablePackages.length; i += 1) |
||||
{ |
||||
if (manifestClass == availablePackages[i]) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
private final function ReadManifest(class<_manifest> manifestClass) |
||||
{ |
||||
local int i; |
||||
|
||||
|
||||
for (i = 0; i < manifestClass.default.aliasSources.length; i += 1) |
||||
{ |
||||
if (manifestClass.default.aliasSources[i] == none) { |
||||
continue; |
||||
} |
||||
_.memory.Allocate(manifestClass.default.aliasSources[i]); |
||||
} |
||||
for (i = 0; i < manifestClass.default.features.length; i += 1) |
||||
{ |
||||
if (manifestClass.default.features[i] == none) { |
||||
continue; |
||||
} |
||||
manifestClass.default.features[i].static.LoadConfigs(); |
||||
availableFeatures[availableFeatures.length] = |
||||
manifestClass.default.features[i]; |
||||
} |
||||
for (i = 0; i < manifestClass.default.testCases.length; i += 1) |
||||
{ |
||||
class'TestingService'.static |
||||
.RegisterTestCase(manifestClass.default.testCases[i]); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns all packages registered in the caller `AcediaEnvironment`. |
||||
* |
||||
* NOTE: package being registered doesn't mean it's actually loaded. |
||||
* Package must either be explicitly loaded or automatically when one of its |
||||
* resources is being used. |
||||
* |
||||
* @return All packages registered in caller `AcediaEnvironment`. |
||||
*/ |
||||
public final function array< class<_manifest> > GetAvailablePackages() |
||||
{ |
||||
return availablePackages; |
||||
} |
||||
|
||||
/** |
||||
* Returns all packages loaded in the caller `AcediaEnvironment`. |
||||
* |
||||
* NOTE: package being registered doesn't mean it's actually loaded. |
||||
* Package must either be explicitly loaded or automatically when one of its |
||||
* resources is being used. |
||||
* |
||||
* @return All packages loaded in caller `AcediaEnvironment`. |
||||
*/ |
||||
public final function array< class<_manifest> > GetLoadedPackages() |
||||
{ |
||||
return loadedPackages; |
||||
} |
||||
|
||||
/** |
||||
* Returns all `Feature`s available in the caller `AcediaEnvironment`. |
||||
* |
||||
* @return All `Feature`s available in the caller `AcediaEnvironment`. |
||||
*/ |
||||
public final function array< class<Feature> > GetAvailableFeatures() |
||||
{ |
||||
return availableFeatures; |
||||
} |
||||
|
||||
/** |
||||
* Returns all `Feature` instances enabled in the caller `AcediaEnvironment`. |
||||
* |
||||
* @return All `Feature`s enabled in the caller `AcediaEnvironment`. |
||||
*/ |
||||
public final function array<Feature> GetEnabledFeatures() |
||||
{ |
||||
local int i; |
||||
for (i = 0; i < enabledFeatures.length; i += 1) { |
||||
enabledFeatures[i].NewRef(); |
||||
} |
||||
return enabledFeatures; |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
manifestSuffix = ".Manifest" |
||||
infoRegisteringPackage = (l=LOG_Info,m="Registering package \"%1\".") |
||||
infoAlreadyRegistered = (l=LOG_Info,m="Package \"%1\" is already registered.") |
||||
errNotRegistered = (l=LOG_Error,m="Package \"%2\" has failed to be registered.") |
||||
} |
@ -1,307 +0,0 @@
|
||||
/** |
||||
* Core service that is always running alongside Acedia framework, must be |
||||
* created by a launcher. |
||||
* Used for booting up and shutting down Acedia. |
||||
* Also used for spawning `Actor`s as the only must-have `Service`. |
||||
* 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 <https://www.gnu.org/licenses/>. |
||||
*/ |
||||
class CoreService extends Service |
||||
dependson(BroadcastEventsObserver); |
||||
|
||||
// Package's manifest is supposed to always have a name of |
||||
// "<package_name>.Manifest", this variable stores the ".Manifest" part |
||||
var private const string manifestSuffix; |
||||
// Classes that will need to do some cleaning before Acedia shuts down |
||||
var private array< class<AcediaObject> > usedObjectClasses; |
||||
var private array< class<AcediaActor> > usedActorClasses; |
||||
// `Singleton`s are handled as a special case and cleaned up after |
||||
// the rest of the classes. |
||||
var private array< class<Singleton> > usedSingletonClasses; |
||||
|
||||
var private array< class<_manifest> > availableManifests; |
||||
|
||||
var private array<string> packagesToLoad; |
||||
|
||||
struct FeatureConfigPair |
||||
{ |
||||
var public class<Feature> featureClass; |
||||
var public Text configName; |
||||
}; |
||||
var private array<FeatureConfigPair> automaticConfigs; |
||||
var private array< class<Feature> > availableFeatures; |
||||
|
||||
var private LoggerAPI.Definition infoLoadingPackage; |
||||
var private LoggerAPI.Definition infoBootingUp, infoBootingUpFinished; |
||||
var private LoggerAPI.Definition infoShuttingDown; |
||||
var private LoggerAPI.Definition errorNoManifest, errorCannotRunTests; |
||||
|
||||
// We do not implement `OnShutdown()`, because total Acedia's clean up |
||||
// is supposed to happen before that event. |
||||
protected function OnLaunch() |
||||
{ |
||||
BootUp(); |
||||
default.packagesToLoad.length = 0; |
||||
} |
||||
|
||||
/** |
||||
* Static method that starts everything needed by Acedia framework to function. |
||||
* Must be called before attempting to use any of the Acedia's functionality. |
||||
* |
||||
* Acedia needs to be able to spawn actors and for that it first needs to |
||||
* spawn `CoreService`. To make that possible you need to provide |
||||
* an `Actor` instance from current level. It can be any valid actor. |
||||
* |
||||
* @param source Valid actor instance that Acedia will use to |
||||
* spawn `CoreService` |
||||
* @param packages List of acedia packages to load. |
||||
* Using array of `string`s since Acedia's `Text` wouldn't yet |
||||
* be available. |
||||
*/ |
||||
public final static function LaunchAcedia( |
||||
Actor source, |
||||
optional array<string> packages) |
||||
{ |
||||
default.packagesToLoad = packages; |
||||
default.blockSpawning = false; |
||||
// Actual work will be done inside `BootUp()` private method that will be |
||||
// called from `OnCreated()` event. |
||||
source.Spawn(class'CoreService'); |
||||
default.blockSpawning = true; |
||||
} |
||||
|
||||
/** |
||||
* Shuts down Acedia, cleaning up created actors, default values, |
||||
* changes made to the standard game classes, etc.. |
||||
* |
||||
* This method must be called before the level change (map change), otherwise |
||||
* Acedia is not guaranteed to work on the next map and you might |
||||
* even experience game crashes. |
||||
*/ |
||||
public final function ShutdownAcedia() |
||||
{ |
||||
local int i; |
||||
local AcediaActor nextActor; |
||||
local MemoryService memoryService; |
||||
_.logger.Auto(infoShuttingDown); |
||||
memoryService = MemoryService(class'MemoryService'.static.GetInstance()); |
||||
// Turn off gameplay-related stuff first |
||||
class'Global'.static.GetInstance().DropGameplayAPI(); |
||||
// Get rid of actors |
||||
foreach AllActors(class'AcediaActor', nextActor) |
||||
{ |
||||
if (nextActor == self) continue; |
||||
if (nextActor == memoryService) continue; |
||||
nextActor.Destroy(); |
||||
} |
||||
// Clean all used classes, except for singletons |
||||
for (i = 0; i < usedObjectClasses.length; i += 1) { |
||||
usedObjectClasses[i].static._cleanup(); |
||||
} |
||||
for (i = 0; i < usedActorClasses.length; i += 1) { |
||||
usedActorClasses[i].static._cleanup(); |
||||
} |
||||
// Remove remaining objects |
||||
_.unreal.broadcasts.Remove(class'BroadcastEventsObserver'); |
||||
memoryService.ClearAll(); |
||||
// Finally clean up singletons |
||||
for (i = 0; i < usedSingletonClasses.length; i += 1) { |
||||
usedSingletonClasses[i].static._cleanup(); |
||||
} |
||||
// Clean API |
||||
class'Global'.static.GetInstance().DropCoreAPI(); |
||||
_ = none; |
||||
// Get rid of the `MemoryService` and `CoreService` last |
||||
memoryService.Destroy(); |
||||
Destroy(); |
||||
Log("Acedia has shut down."); |
||||
} |
||||
|
||||
// Loads packages, injects broadcast handler and optionally runs tests |
||||
private final function BootUp() |
||||
{ |
||||
local int i; |
||||
local Text nextPackageName; |
||||
local class<_manifest> nextManifest; |
||||
_.logger.Auto(infoBootingUp); |
||||
LoadManifest(class'AcediaCore.Manifest'); |
||||
// Load packages |
||||
for (i = 0; i < packagesToLoad.length; i += 1) |
||||
{ |
||||
nextPackageName = _.text.FromString(packagesToLoad[i]); |
||||
_.logger.Auto(infoLoadingPackage).Arg(nextPackageName.Copy()); |
||||
nextManifest = LoadManifestClass(packagesToLoad[i]); |
||||
if (nextManifest == none) |
||||
{ |
||||
_.logger.Auto(errorNoManifest).Arg(nextPackageName.Copy()); |
||||
continue; |
||||
} |
||||
availableManifests[availableManifests.length] = nextManifest; |
||||
LoadManifest(nextManifest); |
||||
_.memory.Free(nextPackageName); |
||||
} |
||||
nextPackageName = none; |
||||
_.logger.Auto(infoBootingUpFinished); |
||||
// Other initialization |
||||
class'UnrealService'.static.Require(); |
||||
if (class'TestingService'.default.runTestsOnStartUp) { |
||||
RunStartUpTests(); |
||||
} |
||||
class'InfoQueryHandler'.static.StaticConstructor(); |
||||
_.unreal.mutator.OnMutate(_self).connect = EnableCommandsFeature; |
||||
} |
||||
|
||||
private final function LoadManifest(class<_manifest> manifestClass) |
||||
{ |
||||
local int i; |
||||
local FeatureConfigPair nextPair; |
||||
for (i = 0; i < manifestClass.default.aliasSources.length; i += 1) |
||||
{ |
||||
if (manifestClass.default.aliasSources[i] == none) continue; |
||||
_.memory.Allocate(manifestClass.default.aliasSources[i]); |
||||
} |
||||
LaunchManifestServices(manifestClass); |
||||
for (i = 0; i < manifestClass.default.features.length; i += 1) |
||||
{ |
||||
if (manifestClass.default.features[i] == none) continue; |
||||
manifestClass.default.features[i].static.LoadConfigs(); |
||||
nextPair.featureClass = manifestClass.default.features[i]; |
||||
nextPair.configName = manifestClass.default.features[i].static |
||||
.GetAutoEnabledConfig(); |
||||
automaticConfigs[automaticConfigs.length] = nextPair; |
||||
availableFeatures[availableFeatures.length] = |
||||
manifestClass.default.features[i]; |
||||
} |
||||
for (i = 0; i < manifestClass.default.testCases.length; i += 1) |
||||
{ |
||||
class'TestingService'.static |
||||
.RegisterTestCase(manifestClass.default.testCases[i]); |
||||
} |
||||
} |
||||
|
||||
public final function array<FeatureConfigPair> GetAutoConfigurationInfo() |
||||
{ |
||||
local int i; |
||||
local array<FeatureConfigPair> result; |
||||
for (i = 0; i < automaticConfigs.length; i += 1) |
||||
{ |
||||
result[i] = automaticConfigs[i]; |
||||
if (result[i].configName != none) { |
||||
result[i].configName = result[i].configName.Copy(); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
public final function array< class<Feature> > GetAvailableFeatures() |
||||
{ |
||||
return availableFeatures; |
||||
} |
||||
|
||||
private final function class<_manifest> LoadManifestClass(string packageName) |
||||
{ |
||||
return class<_manifest>(DynamicLoadObject( packageName $ manifestSuffix, |
||||
class'Class', true)); |
||||
} |
||||
|
||||
private final function LaunchManifestServices(class<_manifest> manifestClass) |
||||
{ |
||||
local int i; |
||||
for (i = 0; i < manifestClass.default.services.length; i += 1) |
||||
{ |
||||
if (manifestClass.default.services[i] != none) { |
||||
manifestClass.default.services[i].static.Require(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private final function RunStartUpTests() |
||||
{ |
||||
local TestingService testService; |
||||
testService = TestingService(class'TestingService'.static.Require()); |
||||
testService.PrepareTests(); |
||||
if (testService.filterTestsByName) { |
||||
testService.FilterByName(testService.requiredName); |
||||
} |
||||
if (testService.filterTestsByGroup) { |
||||
testService.FilterByGroup(testService.requiredGroup); |
||||
} |
||||
if (!testService.Run()) { |
||||
_.logger.Auto(errorCannotRunTests); |
||||
} |
||||
} |
||||
|
||||
private final function EnableCommandsFeature( |
||||
string command, |
||||
PlayerController sendingPlayer) |
||||
{ |
||||
if (command ~= "acediacommands") { |
||||
class'Commands_Feature'.static.EmergencyEnable(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Registers class derived from `AcediaObject` for clean up when |
||||
* Acedia shuts down. |
||||
* |
||||
* Does not check for duplicates. |
||||
* |
||||
* This is an internal function and should not be used outside of |
||||
* AcediaCore package. |
||||
*/ |
||||
public final function _registerObjectClass(class<AcediaObject> classToClean) |
||||
{ |
||||
if (classToClean != none) { |
||||
usedObjectClasses[usedObjectClasses.length] = classToClean; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Registers class derived from `AcediaActor` for clean up when |
||||
* Acedia shuts down. |
||||
* |
||||
* Does not check for duplicates. |
||||
* |
||||
* This is an internal function and should not be used outside of |
||||
* AcediaCore package. |
||||
*/ |
||||
public final function _registerActorClass(class<AcediaActor> classToClean) |
||||
{ |
||||
local class<Singleton> singletonClass; |
||||
if (classToClean == none) { |
||||
return; |
||||
} |
||||
singletonClass = class<Singleton>(classToClean); |
||||
if (singletonClass != none) { |
||||
usedSingletonClasses[usedSingletonClasses.length] = singletonClass; |
||||
} |
||||
else { |
||||
usedActorClasses[usedActorClasses.length] = classToClean; |
||||
} |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
manifestSuffix = ".Manifest" |
||||
|
||||
infoBootingUp = (l=LOG_Info,m="Initializing Acedia.") |
||||
infoBootingUpFinished = (l=LOG_Info,m="Acedia initialized.") |
||||
infoShuttingDown = (l=LOG_Info,m="Shutting down Acedia.") |
||||
infoLoadingPackage = (l=LOG_Info,m="Loading package \"%1\".") |
||||
errorNoManifest = (l=LOG_Error,m="Cannot load `Manifest` for package \"%1\". Check if it's missing or if its name is spelled incorrectly.") |
||||
errorCannotRunTests = (l=LOG_Error,m="Could not perform Acedia's tests.") |
||||
} |
@ -0,0 +1,31 @@
|
||||
/** |
||||
* Copyright 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 <https://www.gnu.org/licenses/>. |
||||
*/ |
||||
class ServerLevelCore extends LevelCore; |
||||
|
||||
public static function LevelCore CreateLevelCore(Actor source) |
||||
{ |
||||
if (source == none) return none; |
||||
if (source.level.netMode != NM_DedicatedServer) return none; |
||||
|
||||
return super.CreateLevelCore(source); |
||||
} |
||||
|
||||
defaultproperties |
||||
{ |
||||
} |
Loading…
Reference in new issue