From 1fd566b6ade4f128daced0f42727d9347b7c44ed Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Mon, 12 Sep 2022 02:15:39 +0700 Subject: [PATCH] Refactor loading to init Acedia's server API faster This change was mostly done in an attempt to create Acedia's server API faster and, consecuently, load required `Feature`s faster as well. As a byproduct - Acedia launcer's core settings (inside `[AcediaLauncher.Packages]` serction) were moved to a separate `Object`, accessible from all Acedia classes. This is also the cleaner way to do it. --- sources/AcediaLauncherMut.uc | 119 +++++++++++++++++ sources/Packages.uc | 249 ++--------------------------------- sources/StartUp.uc | 156 +++++++++++++++++++++- 3 files changed, 279 insertions(+), 245 deletions(-) create mode 100644 sources/AcediaLauncherMut.uc 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