diff --git a/sources/AcediaLauncherMut.uc b/sources/AcediaLauncherMut.uc
new file mode 100644
index 0000000..70519ab
--- /dev/null
+++ b/sources/AcediaLauncherMut.uc
@@ -0,0 +1,119 @@
+/**
+ * Main and only Acedia mutator. Used for providing access to mutator
+ * events' calls and detecting server travel.
+ * 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 AcediaLauncherMut extends Mutator;
+
+// Acedia's reference to a `Global` object.
+var private Global _;
+
+// Responsible for setting up Acedia's game modes in current voting system
+var private VotingHandlerAdapter votingAdapter;
+
+var Mutator_OnMutate_Signal onMutateSignal;
+var Mutator_OnModifyLogin_Signal onModifyLoginSignal;
+var Mutator_OnCheckReplacement_Signal onCheckReplacementSignal;
+
+simulated function PreBeginPlay()
+{
+ local StartUp startUpActor;
+
+ if (level.netMode == NM_DedicatedServer)
+ {
+ foreach AllActors(class'StartUp', startUpActor)
+ {
+ votingAdapter = startUpActor.GetVotingHandlerAdapter();
+ startUpActor.Destroy();
+ break;
+ }
+ if (votingAdapter != none) {
+ votingAdapter.InjectIntoVotingHandler();
+ }
+ SetupMutatorSignals();
+ }
+ else
+ {
+ _ = class'Global'.static.GetInstance();
+ class'ClientLevelCore'.static.CreateLevelCore(self);
+ }
+}
+
+function ServerTraveling(string URL, bool bItems)
+{
+ if (votingAdapter != none)
+ {
+ votingAdapter.PrepareForServerTravel();
+ votingAdapter.RestoreVotingHandlerConfigBackup();
+ _.memory.Free(votingAdapter);
+ votingAdapter = none;
+ }
+ _.environment.ShutDown();
+ if (nextMutator != none) {
+ nextMutator.ServerTraveling(URL, bItems);
+ }
+ Destroy();
+}
+
+// Fetches and sets up signals that `Mutator` needs to provide
+private function SetupMutatorSignals()
+{
+ local ServerUnrealService service;
+
+ service = ServerUnrealService(class'ServerUnrealService'.static.Require());
+ onMutateSignal = Mutator_OnMutate_Signal(
+ service.GetSignal(class'Mutator_OnMutate_Signal'));
+ onModifyLoginSignal = Mutator_OnModifyLogin_Signal(
+ service.GetSignal(class'Mutator_OnModifyLogin_Signal'));
+ onCheckReplacementSignal = Mutator_OnCheckReplacement_Signal(
+ service.GetSignal(class'Mutator_OnCheckReplacement_Signal'));
+}
+
+function bool CheckReplacement(Actor other, out byte isSuperRelevant)
+{
+ if (onCheckReplacementSignal != none) {
+ return onCheckReplacementSignal.Emit(other, isSuperRelevant);
+ }
+ return true;
+}
+
+function Mutate(string command, PlayerController sendingController)
+{
+ if (onMutateSignal != none) {
+ onMutateSignal.Emit(command, sendingController);
+ }
+ super.Mutate(command, sendingController);
+}
+
+function ModifyLogin(out string portal, out string options)
+{
+ if (onModifyLoginSignal != none) {
+ onModifyLoginSignal.Emit(portal, options);
+ }
+ super.ModifyLogin(portal, options);
+}
+
+defaultproperties
+{
+ remoteRole = ROLE_SimulatedProxy
+ bAlwaysRelevant = true
+ // Mutator description
+ GroupName = "Package loader"
+ FriendlyName = "Acedia loader"
+ Description = "Launcher for Acedia packages"
+}
\ No newline at end of file
diff --git a/sources/Packages.uc b/sources/Packages.uc
index 7c47ccf..a3a3523 100644
--- a/sources/Packages.uc
+++ b/sources/Packages.uc
@@ -1,8 +1,7 @@
/**
- * Main and only Acedia mutator used for loading Acedia packages
- * and providing access to mutator events' calls.
- * Name is chosen to make config files more readable.
- * Copyright 2020-2022 Anton Tarasenko
+ * Object for storing launcher's core settings and defining types that will be
+ * used everywhere in this package.
+ * Copyright 2022 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
@@ -19,29 +18,15 @@
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see .
*/
-class Packages extends Mutator
+class Packages extends Object
config(AcediaLauncher);
-// Acedia's reference to a `Global` object.
-var private Global _;
-var private ServerGlobal _server;
-var private ClientGlobal _client;
-
-// Load Acedia on the client as well? DON NOT TOUCH THIS
-var private config bool clientside;
+// Load Acedia on the client as well? DO NOT TOUCH THIS
+var public config bool clientside;
// Array of predefined services that must be started along with Acedia mutator.
-var private config array package;
+var public config array package;
// Set to `true` to activate Acedia's game modes system
-var private config bool useGameModes;
-// Responsible for setting up Acedia's game modes in current voting system
-var VotingHandlerAdapter votingAdapter;
-
-var Mutator_OnMutate_Signal onMutateSignal;
-var Mutator_OnModifyLogin_Signal onModifyLoginSignal;
-var Mutator_OnCheckReplacement_Signal onCheckReplacementSignal;
-
-var private LoggerAPI.Definition infoFeatureEnabled;
-var private LoggerAPI.Definition errNoServerLevelCore, errorCannotRunTests;
+var public config bool useGameModes;
struct FeatureConfigPair
{
@@ -49,226 +34,8 @@ struct FeatureConfigPair
var public Text configName;
};
-// "Constructor"
-simulated function PreBeginPlay()
-{
- if (level.netMode == NM_DedicatedServer) {
- InitializeServer();
- }
- else {
- InitializeClient();
- }
-}
-
-private simulated function InitializeClient()
-{
- _ = class'Global'.static.GetInstance();
- class'ClientLevelCore'.static.CreateLevelCore(self);
-}
-
-private function InitializeServer()
-{
- local int i;
- local LevelCore serverCore;
- local GameMode currentGameMode;
- local array availableFeatures;
-
- if (clientside) {
- AddToPackageMap("AcediaLauncher");
- }
- CheckForGarbage();
- // Launch and setup core Acedia
- _ = class'Global'.static.GetInstance();
- _server = class'ServerGlobal'.static.GetInstance();
- _client = class'ClientGlobal'.static.GetInstance();
- serverCore = class'ServerLevelCore'.static.CreateLevelCore(self);
- for (i = 0; i < package.length; i += 1) {
- _.environment.RegisterPackage_S(package[i]);
- }
- if (serverCore != none) {
- _server.ConnectServerLevelCore();
- }
- else
- {
- _.logger.Auto(errNoServerLevelCore);
- return;
- }
- if (class'TestingService'.default.runTestsOnStartUp) {
- RunStartUpTests();
- }
- SetupMutatorSignals();
- // Determine required features and launch them
- availableFeatures = GetAutoConfigurationInfo();
- if (useGameModes)
- {
- votingAdapter = VotingHandlerAdapter(
- _.memory.Allocate(class'VotingHandlerAdapter'));
- currentGameMode = votingAdapter.SetupGameModeAfterTravel();
- if (currentGameMode != none) {
- currentGameMode.UpdateFeatureArray(availableFeatures);
- }
- }
- EnableFeatures(availableFeatures);
- if (votingAdapter != none) {
- votingAdapter.InjectIntoVotingHandler();
- }
-}
-
-// "Finalizer"
-function ServerTraveling(string URL, bool bItems)
-{
- if (votingAdapter != none)
- {
- votingAdapter.PrepareForServerTravel();
- votingAdapter.RestoreVotingHandlerConfigBackup();
- _.memory.Free(votingAdapter);
- votingAdapter = none;
- }
- _.environment.ShutDown();
- if (nextMutator != none) {
- nextMutator.ServerTraveling(URL, bItems);
- }
- Destroy();
-}
-
-// Checks whether Acedia has left garbage after the previous map.
-// This can lead to serious problems, so such diagnostic check is warranted.
-private function CheckForGarbage()
-{
- local int leftoverObjectAmount;
- local int leftoverActorAmount;
- local int leftoverDBRAmount;
- local AcediaObject nextObject;
- local AcediaActor nextActor;
- local DBRecord nextRecord;
-
- foreach AllObjects(class'AcediaObject', nextObject) {
- leftoverObjectAmount += 1;
- }
- foreach AllActors(class'AcediaActor', nextActor) {
- leftoverActorAmount += 1;
- }
- foreach AllObjects(class'DBRecord', nextRecord) {
- leftoverDBRAmount += 1;
- }
- if ( leftoverObjectAmount == 0 && leftoverActorAmount == 0
- && leftoverDBRAmount == 0)
- {
- Log("Acedia garbage check: nothing was found.");
- }
- else
- {
- Log("Acedia garbage check: garbage was found." @
- "This can cause problems, report it.");
- Log("Leftover object:" @ leftoverObjectAmount);
- Log("Leftover actors:" @ leftoverActorAmount);
- Log("Leftover database records:" @ leftoverDBRAmount);
- }
-}
-
-public final function array GetAutoConfigurationInfo()
-{
- local int i;
- local array< class > availableFeatures;
- local FeatureConfigPair nextPair;
- local array result;
-
- availableFeatures = _.environment.GetAvailableFeatures();
- for (i = 0; i < availableFeatures.length; i += 1)
- {
- nextPair.featureClass = availableFeatures[i];
- nextPair.configName = availableFeatures[i].static
- .GetAutoEnabledConfig();
- result[result.length] = nextPair;
- }
- return result;
-}
-
-private function EnableFeatures(array features)
-{
- local int i;
-
- for (i = 0; i < features.length; i += 1)
- {
- if (features[i].featureClass == none) continue;
- if (features[i].configName == none) continue;
- features[i].featureClass.static.EnableMe(features[i].configName);
- _.logger.Auto(infoFeatureEnabled)
- .Arg(_.text.FromString(string(features[i].featureClass)))
- .Arg(features[i].configName); // consumes `configName`
- }
-}
-
-// Fetches and sets up signals that `Mutator` needs to provide
-private function SetupMutatorSignals()
-{
- local ServerUnrealService service;
-
- service = ServerUnrealService(class'ServerUnrealService'.static.Require());
- onMutateSignal = Mutator_OnMutate_Signal(
- service.GetSignal(class'Mutator_OnMutate_Signal'));
- onModifyLoginSignal = Mutator_OnModifyLogin_Signal(
- service.GetSignal(class'Mutator_OnModifyLogin_Signal'));
- onCheckReplacementSignal = Mutator_OnCheckReplacement_Signal(
- service.GetSignal(class'Mutator_OnCheckReplacement_Signal'));
-}
-
-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);
- }
-}
-
-/**
- * Below `Mutator` events are redirected into appropriate signals.
- */
-function bool CheckReplacement(Actor other, out byte isSuperRelevant)
-{
- if (onCheckReplacementSignal != none) {
- return onCheckReplacementSignal.Emit(other, isSuperRelevant);
- }
- return true;
-}
-
-function Mutate(string command, PlayerController sendingController)
-{
- if (onMutateSignal != none) {
- onMutateSignal.Emit(command, sendingController);
- }
- super.Mutate(command, sendingController);
-}
-
-function ModifyLogin(out string portal, out string options)
-{
- if (onModifyLoginSignal != none) {
- onModifyLoginSignal.Emit(portal, options);
- }
- super.ModifyLogin(portal, options);
-}
-
defaultproperties
{
clientside = false
useGameModes = false
- // This is a server-only mutator
- remoteRole = ROLE_SimulatedProxy
- bAlwaysRelevant = true
- // Mutator description
- GroupName = "Package loader"
- FriendlyName = "Acedia loader"
- Description = "Launcher for Acedia packages"
- infoFeatureEnabled = (l=LOG_Info,m="Feature `%1` enabled with config \"%2\".")
- errNoServerLevelCore = (l=LOG_Error,m="Cannot create `ServerLevelCore`!")
- errorCannotRunTests = (l=LOG_Error,m="Could not perform Acedia's tests.")
}
\ No newline at end of file
diff --git a/sources/StartUp.uc b/sources/StartUp.uc
index 0755d39..5fdd78d 100644
--- a/sources/StartUp.uc
+++ b/sources/StartUp.uc
@@ -1,5 +1,5 @@
/**
- * This actor's role is to add Acedia mutator on listen and dedicated servers.
+ * This actor's role is to perform Acedia's server startup.
* Copyright 2019-2022 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
@@ -17,20 +17,168 @@
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see .
*/
+class StartUp extends Actor
+ dependson(Packages);
-class StartUp extends Actor;
+// Acedia's reference to a `Global` object.
+var private Global _;
+var private ServerGlobal _server;
+// Responsible for setting up Acedia's game modes in current voting system
+var private VotingHandlerAdapter votingAdapter;
+
+var private LoggerAPI.Definition infoFeatureEnabled;
+var private LoggerAPI.Definition errNoServerLevelCore, errorCannotRunTests;
function PreBeginPlay()
{
super.PreBeginPlay();
+ InitializeServer();
if (level != none && level.game != none) {
- level.game.AddMutator(string(class'Packages'));
+ level.game.AddMutator(string(class'AcediaLauncherMut'));
+ }
+}
+
+public function VotingHandlerAdapter GetVotingHandlerAdapter()
+{
+ if (votingAdapter != none) {
+ votingAdapter.NewRef();
+ }
+ return votingAdapter;
+}
+
+private function InitializeServer()
+{
+ local int i;
+ local LevelCore serverCore;
+ local GameMode currentGameMode;
+ local array availableFeatures;
+
+ if (class'Packages'.default.clientside) {
+ AddToPackageMap("AcediaLauncher");
+ }
+ CheckForGarbage();
+ // Launch and setup core Acedia
+ _ = class'Global'.static.GetInstance();
+ _server = class'ServerGlobal'.static.GetInstance();
+ serverCore = class'ServerLevelCore'.static.CreateLevelCore(self);
+ for (i = 0; i < class'Packages'.default.package.length; i += 1) {
+ _.environment.RegisterPackage_S(class'Packages'.default.package[i]);
+ }
+ if (serverCore != none) {
+ _server.ConnectServerLevelCore();
+ }
+ else
+ {
+ _.logger.Auto(errNoServerLevelCore);
+ return;
+ }
+ if (class'TestingService'.default.runTestsOnStartUp) {
+ RunStartUpTests();
+ }
+ // Determine required features and launch them
+ availableFeatures = GetAutoConfigurationInfo();
+ if (class'Packages'.default.useGameModes)
+ {
+ votingAdapter = VotingHandlerAdapter(
+ _.memory.Allocate(class'VotingHandlerAdapter'));
+ currentGameMode = votingAdapter.SetupGameModeAfterTravel();
+ if (currentGameMode != none) {
+ currentGameMode.UpdateFeatureArray(availableFeatures);
+ }
+ }
+ EnableFeatures(availableFeatures);
+}
+
+// Checks whether Acedia has left garbage after the previous map.
+// This can lead to serious problems, so such diagnostic check is warranted.
+private function CheckForGarbage()
+{
+ local int leftoverObjectAmount;
+ local int leftoverActorAmount;
+ local int leftoverDBRAmount;
+ local AcediaObject nextObject;
+ local AcediaActor nextActor;
+ local DBRecord nextRecord;
+
+ foreach AllObjects(class'AcediaObject', nextObject) {
+ leftoverObjectAmount += 1;
+ }
+ foreach AllActors(class'AcediaActor', nextActor) {
+ leftoverActorAmount += 1;
+ }
+ foreach AllObjects(class'DBRecord', nextRecord) {
+ leftoverDBRAmount += 1;
+ }
+ if ( leftoverObjectAmount == 0 && leftoverActorAmount == 0
+ && leftoverDBRAmount == 0)
+ {
+ Log("Acedia garbage check: nothing was found.");
+ }
+ else
+ {
+ Log("Acedia garbage check: garbage was found." @
+ "This can cause problems, report it.");
+ Log("Leftover object:" @ leftoverObjectAmount);
+ Log("Leftover actors:" @ leftoverActorAmount);
+ Log("Leftover database records:" @ leftoverDBRAmount);
+ }
+}
+
+public function array GetAutoConfigurationInfo()
+{
+ local int i;
+ local array< class > availableFeatures;
+ local Packages.FeatureConfigPair nextPair;
+ local array result;
+
+ availableFeatures = _.environment.GetAvailableFeatures();
+ for (i = 0; i < availableFeatures.length; i += 1)
+ {
+ nextPair.featureClass = availableFeatures[i];
+ nextPair.configName = availableFeatures[i].static
+ .GetAutoEnabledConfig();
+ result[result.length] = nextPair;
+ }
+ return result;
+}
+
+private function EnableFeatures(array features)
+{
+ local int i;
+
+ for (i = 0; i < features.length; i += 1)
+ {
+ if (features[i].featureClass == none) continue;
+ if (features[i].configName == none) continue;
+ features[i].featureClass.static.EnableMe(features[i].configName);
+ _.logger.Auto(infoFeatureEnabled)
+ .Arg(_.text.FromString(string(features[i].featureClass)))
+ .Arg(features[i].configName); // consumes `configName`
+ }
+}
+
+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);
}
- Destroy();
}
defaultproperties
{
// This is a server-only actor
remoteRole = ROLE_None
+ infoFeatureEnabled = (l=LOG_Info,m="Feature `%1` enabled with config \"%2\".")
+ errNoServerLevelCore = (l=LOG_Error,m="Cannot create `ServerLevelCore`!")
+ errorCannotRunTests = (l=LOG_Error,m="Could not perform Acedia's tests.")
}
\ No newline at end of file