Browse Source

Kind of compiles now

Anton Tarasenko 2 years ago
parent
commit
2be4656f51
  1. 209
      sources/AcediaEnvironment.uc
  2. 2
      sources/Config/AcediaConfig.uc
  3. 307
      sources/CoreService.uc
  4. 14
      sources/Global.uc
  5. 17
      sources/LevelCore.uc
  6. 1
      sources/Manifest.uc
  7. 4
      sources/Memory/MemoryAPI.uc
  8. 31
      sources/ServerLevelCore.uc
  9. 2
      sources/Types/AcediaActor.uc
  10. 2
      sources/Types/AcediaObject.uc
  11. 4
      sources/Unreal/BroadcastsAPI/BroadcastAPI.uc
  12. 14
      sources/Unreal/UnrealAPI.uc
  13. 3
      sources/_manifest.uc

209
sources/AcediaEnvironment.uc

@ -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.")
}

2
sources/Config/AcediaConfig.uc

@ -110,8 +110,6 @@ public static function Initialize()
if (default.existingConfigs != none) {
return;
}
CoreService(class'CoreService'.static.GetInstance())
._registerObjectClass(default.class);
default.existingConfigs = __().collections.EmptyAssociativeArray();
names = GetPerObjectNames( default.configName, string(default.class.name),
MaxInt);

307
sources/CoreService.uc

@ -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.")
}

14
sources/Global.uc

@ -43,6 +43,8 @@ var public JSONAPI json;
var public DBAPI db;
var public AvariceAPI avarice;
var public AcediaEnvironment environment;
var public KFFrontend kf;
public final static function Global GetInstance()
@ -79,7 +81,17 @@ protected function Initialize()
db = DBAPI(memory.Allocate(class'DBAPI'));
avarice = AvariceAPI(memory.Allocate(class'AvariceAPI'));
kf = KFFrontend(memory.Allocate(class'KF1_Frontend'));
json.StaticConstructor();
environment = AcediaEnvironment(memory.Allocate(class'AcediaEnvironment'));
}
public final function bool ConnectServerLevelCore()
{
if (class'ServerLevelCore'.static.GetInstance() == none) {
return false;
}
class'UnrealService'.static.Require();
class'ConnectionService'.static.Require();
return true;
}
public function DropGameplayAPI()

17
sources/LevelCore.uc

@ -46,11 +46,14 @@ class LevelCore extends AcediaActor
* method to add your checks.
*/
// Allows to force creation of `LevelCore` only through `CreateLevelCore()`
// method
var private bool blockSpawning;
// Default value of this variable will store one and only existing version
// `LevelCore`.
// `LevelCore`
var private LevelCore activeInstance;
protected static function StaticFinalizer()
protected function Finalizer()
{
default.activeInstance = none;
}
@ -60,7 +63,9 @@ public static function LevelCore CreateLevelCore(Actor source)
if (GetInstance() != none) return none;
if (source == none) return none;
default.activeInstance = source.Spawn(self.class);
default.blockSpawning = false;
default.activeInstance = source.Spawn(default.class);
default.blockSpawning = true;
return default.activeInstance;
}
@ -72,16 +77,13 @@ public final static function LevelCore GetInstance()
if (instanceExists) {
return default.activeInstance;
}
if (spawnIfMissing) {
return LevelCore(__().memory.Allocate(default.class));
}
return none;
}
// Make sure only one instance of 'LevelCore' exists at any point in time.
event PreBeginPlay()
{
if (GetInstance() != none)
if (default.blockSpawning || GetInstance() != none)
{
Destroy();
return;
@ -101,4 +103,5 @@ event Destroyed()
defaultproperties
{
blockSpawning = true
}

1
sources/Manifest.uc

@ -24,7 +24,6 @@ defaultproperties
{
features(0) = class'Commands_Feature'
features(1) = class'Avarice_Feature'
services(0) = class'ConnectionService'
aliasSources(0) = class'AliasSource'
aliasSources(1) = class'WeaponAliasSource'
aliasSources(2) = class'ColorAliasSource'

4
sources/Memory/MemoryAPI.uc

@ -104,7 +104,7 @@ public final function Object Allocate(
actorClassToAllocate = class<Actor>(classToAllocate);
if (actorClassToAllocate != none)
{
allocatedObject = class'CoreService'.static
allocatedObject = class'ServerLevelCore'.static
.GetInstance()
.Spawn(actorClassToAllocate);
}
@ -278,7 +278,7 @@ public final function CollectGarbage(optional bool keepAcediaPools)
}
}
// This makes Unreal Engine do garbage collection
class'CoreService'.static.GetInstance().ConsoleCommand("obj garbage");
class'ServerLevelCore'.static.GetInstance().ConsoleCommand("obj garbage");
}
defaultproperties

31
sources/ServerLevelCore.uc

@ -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
{
}

2
sources/Types/AcediaActor.uc

@ -114,8 +114,6 @@ protected final static function bool StaticConstructorGuard()
if (!default._staticConstructorWasCalled)
{
default._staticConstructorWasCalled = true;
CoreService(class'CoreService'.static.GetInstance())
._registerActorClass(default.class);
return false;
}
return true;

2
sources/Types/AcediaObject.uc

@ -158,8 +158,6 @@ protected final static function bool StaticConstructorGuard()
if (!default._staticConstructorWasCalled)
{
default._staticConstructorWasCalled = true;
CoreService(class'CoreService'.static.GetInstance())
._registerObjectClass(default.class);
return false;
}
return true;

4
sources/Unreal/BroadcastsAPI/BroadcastAPI.uc

@ -282,7 +282,9 @@ public final function BroadcastHandler Add(
// I don't know why, I don't know when exactly, but not resetting it
// can lead to certain issues, including infinite recursion crashes.
class'BroadcastHandler'.default.nextBroadcastHandlerClass = none;
newBroadcastHandler = class'CoreService'.static.Require().Spawn(newBHClass);
newBroadcastHandler = class'ServerLevelCore'.static
.GetInstance()
.Spawn(newBHClass);
if (injectionLevel == BHIJ_Registered)
{
// There is guaranteed to be SOME broadcast handler

14
sources/Unreal/UnrealAPI.uc

@ -111,7 +111,7 @@ public final function SimpleSlot OnDestructionFor(
*/
public final function LevelInfo GetLevel()
{
return class'CoreService'.static.GetInstance().level;
return class'ServerLevelCore'.static.GetInstance().level;
}
/**
@ -123,7 +123,7 @@ public final function LevelInfo GetLevel()
*/
public final function GameReplicationInfo GetGameRI()
{
return class'CoreService'.static.GetInstance().level.GRI;
return class'ServerLevelCore'.static.GetInstance().level.GRI;
}
/**
@ -150,7 +150,7 @@ public final function KFGameReplicationInfo GetKFGameRI()
*/
public final function GameInfo GetGameType()
{
return class'CoreService'.static.GetInstance().level.game;
return class'ServerLevelCore'.static.GetInstance().level.game;
}
/**
@ -178,9 +178,9 @@ public final function KFGameType GetKFGameType()
public final function Actor FindActorInstance(class<Actor> classToFind)
{
local Actor result;
local Service service;
service = class'CoreService'.static.Require();
foreach service.AllActors(classToFind, result)
local LevelCore core;
core = class'ServerLevelCore'.static.GetInstance();
foreach core.AllActors(classToFind, result)
{
if (result != none) {
break;
@ -198,7 +198,7 @@ public final function Actor FindActorInstance(class<Actor> classToFind)
*/
public final function PlayerController GetLocalPlayer()
{
return class'CoreService'.static.GetInstance().level
return class'ServerLevelCore'.static.GetInstance().level
.GetLocalPlayerController();
}

3
sources/_manifest.uc

@ -30,9 +30,6 @@ var public const array< class<Feature> > features;
// List of test cases in this manifest's package.
var public const array< class<TestCase> > testCases;
// List of required services.
var public const array< class<Service> > services;
defaultproperties
{
}
Loading…
Cancel
Save