Browse Source
Squashed commit of the following: commitpull/8/headf7cbf54045
Author: Anton Tarasenko <dkanus@gmail.com> Date: Thu Jun 23 02:45:20 2022 +0700 Done commit3cf67a3ca5
Author: Anton Tarasenko <dkanus@gmail.com> Date: Wed Jun 22 20:49:28 2022 +0700 Almost done commit6dfb9dc204
Author: Anton Tarasenko <dkanus@gmail.com> Date: Wed Jun 22 17:03:06 2022 +0700 Now it even works commit2be4656f51
Author: Anton Tarasenko <dkanus@gmail.com> Date: Wed Jun 22 16:30:35 2022 +0700 Kind of compiles now commit73914e9b7e
Author: Anton Tarasenko <dkanus@gmail.com> Date: Tue Jun 21 04:24:04 2022 +0700 Daily dirty commit
Anton Tarasenko
2 years ago
22 changed files with 991 additions and 476 deletions
@ -0,0 +1,471 @@ |
|||||||
|
/** |
||||||
|
* 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. |
||||||
|
* |
||||||
|
* ## `Feature`s |
||||||
|
* |
||||||
|
* Whether `Feature` is enabled is governed by the `AcediaEnvironment` added |
||||||
|
* into the `Global` class. It is possible to create several `Feature` |
||||||
|
* instances of the same class instance of each class, but only one can be |
||||||
|
* considered enabled at the same time. |
||||||
|
*/ |
||||||
|
|
||||||
|
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 array<int> enabledFeaturesLifeVersions; |
||||||
|
|
||||||
|
var private string manifestSuffix; |
||||||
|
|
||||||
|
var private LoggerAPI.Definition infoRegisteringPackage, infoAlreadyRegistered; |
||||||
|
var private LoggerAPI.Definition errNotRegistered, errFeatureAlreadyEnabled; |
||||||
|
var private LoggerAPI.Definition warnFeatureAlreadyEnabled; |
||||||
|
var private LoggerAPI.Definition errFeatureClassAlreadyEnabled; |
||||||
|
|
||||||
|
var private Environment_FeatureEnabled_Signal onFeatureEnabledSignal; |
||||||
|
var private Environment_FeatureDisabled_Signal onFeatureDisabledSignal; |
||||||
|
|
||||||
|
/** |
||||||
|
* Signal that will be emitted when new `Feature` is enabled. |
||||||
|
* Emitted after `Feature`'s `OnEnabled()` method was called. |
||||||
|
* |
||||||
|
* [Signature] |
||||||
|
* void <slot>(Feature enabledFeature) |
||||||
|
* |
||||||
|
* @param enabledFeature `Feature` instance that was just enabled. |
||||||
|
*/ |
||||||
|
/* SIGNAL */ |
||||||
|
public final function Environment_FeatureEnabled_Slot OnFeatureEnabled( |
||||||
|
AcediaObject receiver) |
||||||
|
{ |
||||||
|
return Environment_FeatureEnabled_Slot( |
||||||
|
onFeatureEnabledSignal.NewSlot(receiver)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Signal that will be emitted when new `Feature` is disabled. |
||||||
|
* Emitted after `Feature`'s `OnDisabled()` method was called. |
||||||
|
* |
||||||
|
* [Signature] |
||||||
|
* void <slot>(class<Feature> disabledFeatureClass) |
||||||
|
* |
||||||
|
* @param disabledFeatureClass Class of the `Feature` instance that was |
||||||
|
* just disabled. |
||||||
|
*/ |
||||||
|
/* SIGNAL */ |
||||||
|
public final function Environment_FeatureDisabled_Slot OnFeatureDisabled( |
||||||
|
AcediaObject receiver) |
||||||
|
{ |
||||||
|
return Environment_FeatureDisabled_Slot( |
||||||
|
onFeatureEnabledSignal.NewSlot(receiver)); |
||||||
|
} |
||||||
|
|
||||||
|
protected function Constructor() |
||||||
|
{ |
||||||
|
// Always register our core package |
||||||
|
RegisterPackage_S("AcediaCore"); |
||||||
|
onFeatureEnabledSignal = Environment_FeatureEnabled_Signal( |
||||||
|
_.memory.Allocate(class'Environment_FeatureEnabled_Signal')); |
||||||
|
onFeatureDisabledSignal = Environment_FeatureDisabled_Signal( |
||||||
|
_.memory.Allocate(class'Environment_FeatureDisabled_Signal')); |
||||||
|
} |
||||||
|
|
||||||
|
protected function Finalizer() |
||||||
|
{ |
||||||
|
_.memory.Free(onFeatureEnabledSignal); |
||||||
|
_.memory.Free(onFeatureDisabledSignal); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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.ToString() $ 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; |
||||||
|
} |
||||||
|
|
||||||
|
// CleanRemove `Feature`s that got deallocated. |
||||||
|
// This shouldn't happen unless someone messes up. |
||||||
|
private final function CleanEnabledFeatures() |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
while (i < enabledFeatures.length) |
||||||
|
{ |
||||||
|
if ( enabledFeatures[i].GetLifeVersion() |
||||||
|
!= enabledFeaturesLifeVersions[i]) |
||||||
|
{ |
||||||
|
enabledFeatures.Remove(i, 1); |
||||||
|
} |
||||||
|
else { |
||||||
|
i += 1; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks if `Feature` of given class `featureClass` is enabled. |
||||||
|
* |
||||||
|
* NOTE: even if If feature of class `featureClass` is enabled, it's not |
||||||
|
* necessarily that the instance you have reference to is enabled. |
||||||
|
* Although unlikely, it is possible that someone spawned another instance |
||||||
|
* of the same class that isn't considered enabled. If you want to check |
||||||
|
* whether some particular instance of given class `featureClass` is enabled, |
||||||
|
* use `IsFeatureEnabled()` method instead. |
||||||
|
* |
||||||
|
* @param featureClass Feature class to check for being enabled. |
||||||
|
* @return `true` if feature of class `featureClass` is currently enabled and |
||||||
|
* `false` otherwise. |
||||||
|
*/ |
||||||
|
public final function bool IsFeatureClassEnabled(class<Feature> featureClass) |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
if (featureClass == none) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
CleanEnabledFeatures(); |
||||||
|
for (i = 0; i < enabledFeatures.length; i += 1) |
||||||
|
{ |
||||||
|
if (featureClass == enabledFeatures[i].class) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks if given `Feature` instance is enabled. |
||||||
|
* |
||||||
|
* If you want to check if any instance instance of given class |
||||||
|
* `classToCheck` is enabled (and not `feature` specifically), use |
||||||
|
* `IsFeatureClassEnabled()` method instead. |
||||||
|
* |
||||||
|
* @param feature Feature instance to check for being enabled. |
||||||
|
* @return `true` if feature `feature` is currently enabled and |
||||||
|
* `false` otherwise. |
||||||
|
*/ |
||||||
|
public final function bool IsFeatureEnabled(Feature feature) |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
if (feature == none) return false; |
||||||
|
if (!feature.IsAllocated()) return false; |
||||||
|
|
||||||
|
CleanEnabledFeatures(); |
||||||
|
for (i = 0; i < enabledFeatures.length; i += 1) |
||||||
|
{ |
||||||
|
if (feature == enabledFeatures[i]) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns enabled `Feature` instance of the given class `featureClass`. |
||||||
|
* |
||||||
|
* @param featureClass Feature class to find enabled instance for. |
||||||
|
* @return Enabled `Feature` instance of the given class `featureClass`. |
||||||
|
* If no feature of `featureClass` is enabled, returns `none`. |
||||||
|
*/ |
||||||
|
public final function Feature GetEnabledFeature(class<Feature> featureClass) |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
if (featureClass == none) { |
||||||
|
return none; |
||||||
|
} |
||||||
|
CleanEnabledFeatures(); |
||||||
|
for (i = 0; i < enabledFeatures.length; i += 1) |
||||||
|
{ |
||||||
|
if (featureClass == enabledFeatures[i].class) |
||||||
|
{ |
||||||
|
enabledFeatures[i].NewRef(); |
||||||
|
return enabledFeatures[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
return none; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Enables given `Feature` instance `newEnabledFeature` with a given config. |
||||||
|
* |
||||||
|
* @see `Feature::EnableMe()`. |
||||||
|
* |
||||||
|
* @param newEnabledFeature Instance to enable. |
||||||
|
* @param configName Name of the config to enable `newEnabledFeature` |
||||||
|
* feature with. `none` means "default" config (will be created, if |
||||||
|
* necessary). |
||||||
|
* @return `true` if given `newEnabledFeature` was enabled and `false` |
||||||
|
* otherwise (including if feature of the same class has already been |
||||||
|
* enabled). |
||||||
|
*/ |
||||||
|
public final function bool EnableFeature( |
||||||
|
Feature newEnabledFeature, |
||||||
|
BaseText configName) |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
if (newEnabledFeature == none) return false; |
||||||
|
if (!newEnabledFeature.IsAllocated()) return false; |
||||||
|
|
||||||
|
CleanEnabledFeatures(); |
||||||
|
for (i = 0; i < enabledFeatures.length; i += 1) |
||||||
|
{ |
||||||
|
if (newEnabledFeature.class == enabledFeatures[i].class) |
||||||
|
{ |
||||||
|
if (newEnabledFeature == enabledFeatures[i]) |
||||||
|
{ |
||||||
|
_.logger |
||||||
|
.Auto(warnFeatureAlreadyEnabled) |
||||||
|
.Arg(_.text.FromClass(newEnabledFeature.class)); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
_.logger |
||||||
|
.Auto(errFeatureClassAlreadyEnabled) |
||||||
|
.Arg(_.text.FromClass(newEnabledFeature.class)); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
newEnabledFeature.NewRef(); |
||||||
|
enabledFeatures[enabledFeatures.length] = newEnabledFeature; |
||||||
|
enabledFeaturesLifeVersions[enabledFeaturesLifeVersions.length] = |
||||||
|
newEnabledFeature.GetLifeVersion(); |
||||||
|
newEnabledFeature.EnableInternal(configName); |
||||||
|
onFeatureEnabledSignal.Emit(newEnabledFeature); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Disables given `Feature` instance `featureToDisable`. |
||||||
|
* |
||||||
|
* @see `Feature::EnableMe()`. |
||||||
|
* |
||||||
|
* @param featureToDisable Instance to disable. |
||||||
|
* @return `true` if given `newEnabledFeature` was disabled and `false` |
||||||
|
* otherwise (including if it already was disabled). |
||||||
|
*/ |
||||||
|
public final function bool DisableFeature(Feature featureToDisable) |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
if (featureToDisable == none) return false; |
||||||
|
if (!featureToDisable.IsAllocated()) return false; |
||||||
|
|
||||||
|
CleanEnabledFeatures(); |
||||||
|
for (i = 0; i < enabledFeatures.length; i += 1) |
||||||
|
{ |
||||||
|
if (featureToDisable == enabledFeatures[i]) |
||||||
|
{ |
||||||
|
enabledFeatures.Remove(i, 1); |
||||||
|
enabledFeaturesLifeVersions.Remove(i, 1); |
||||||
|
featureToDisable.DisableInternal(); |
||||||
|
onFeatureDisabledSignal.Emit(featureToDisable.class); |
||||||
|
_.memory.Free(featureToDisable); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Disables all currently enabled `Feature`s. |
||||||
|
* |
||||||
|
* Mainly intended for the clean up when Acedia shuts down. |
||||||
|
*/ |
||||||
|
public final function DisableAllFeatures() |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
local array<Feature> featuresCopy; |
||||||
|
|
||||||
|
CleanEnabledFeatures(); |
||||||
|
featuresCopy = enabledFeatures; |
||||||
|
enabledFeatures.length = 0; |
||||||
|
enabledFeaturesLifeVersions.length = 0; |
||||||
|
for (i = 0; i < enabledFeatures.length; i += 1) |
||||||
|
{ |
||||||
|
featuresCopy[i].DisableInternal(); |
||||||
|
onFeatureDisabledSignal.Emit(featuresCopy[i].class); |
||||||
|
} |
||||||
|
_.memory.FreeMany(featuresCopy); |
||||||
|
} |
||||||
|
|
||||||
|
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.") |
||||||
|
warnFeatureAlreadyEnabled = (l=LOG_Warning,m="Same instance of `Feature` class `%1` is already enabled.") |
||||||
|
errFeatureClassAlreadyEnabled = (l=LOG_Error,m="Different instance of the same `Feature` class `%1` is already enabled.") |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
/** |
||||||
|
* Signal class for `AcediaEnvironment`'s `FeatureDisabled()` signal. |
||||||
|
* 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 Environment_FeatureDisabled_Signal extends Signal; |
||||||
|
|
||||||
|
public final function Emit(class<Feature> disabledFeatureClass) |
||||||
|
{ |
||||||
|
local Slot nextSlot; |
||||||
|
StartIterating(); |
||||||
|
nextSlot = GetNextSlot(); |
||||||
|
while (nextSlot != none) |
||||||
|
{ |
||||||
|
Environment_FeatureDisabled_Slot(nextSlot).connect(disabledFeatureClass); |
||||||
|
nextSlot = GetNextSlot(); |
||||||
|
} |
||||||
|
CleanEmptySlots(); |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties |
||||||
|
{ |
||||||
|
relatedSlotClass = class'Environment_FeatureDisabled_Slot' |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
/** |
||||||
|
* Slot class for `AcediaEnvironment`'s `FeatureDisabled()` signal. |
||||||
|
* 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 Environment_FeatureDisabled_Slot extends Slot; |
||||||
|
|
||||||
|
delegate connect(class<Feature> disabledFeatureClass) |
||||||
|
{ |
||||||
|
DummyCall(); |
||||||
|
} |
||||||
|
|
||||||
|
protected function Constructor() |
||||||
|
{ |
||||||
|
connect = none; |
||||||
|
} |
||||||
|
|
||||||
|
protected function Finalizer() |
||||||
|
{ |
||||||
|
super.Finalizer(); |
||||||
|
connect = none; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties |
||||||
|
{ |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
/** |
||||||
|
* Signal class for `AcediaEnvironment`'s `FeatureEnabled()` signal. |
||||||
|
* 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 Environment_FeatureEnabled_Signal extends Signal; |
||||||
|
|
||||||
|
public final function Emit(Feature enabledFeature) |
||||||
|
{ |
||||||
|
local Slot nextSlot; |
||||||
|
StartIterating(); |
||||||
|
nextSlot = GetNextSlot(); |
||||||
|
while (nextSlot != none) |
||||||
|
{ |
||||||
|
Environment_FeatureEnabled_Slot(nextSlot).connect(enabledFeature); |
||||||
|
nextSlot = GetNextSlot(); |
||||||
|
} |
||||||
|
CleanEmptySlots(); |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties |
||||||
|
{ |
||||||
|
relatedSlotClass = class'Environment_FeatureEnabled_Slot' |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
/** |
||||||
|
* Slot class for `AcediaEnvironment`'s `FeatureEnabled()` signal. |
||||||
|
* 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 Environment_FeatureEnabled_Slot extends Slot; |
||||||
|
|
||||||
|
delegate connect(Feature enabledFeature) |
||||||
|
{ |
||||||
|
DummyCall(); |
||||||
|
} |
||||||
|
|
||||||
|
protected function Constructor() |
||||||
|
{ |
||||||
|
connect = none; |
||||||
|
} |
||||||
|
|
||||||
|
protected function Finalizer() |
||||||
|
{ |
||||||
|
super.Finalizer(); |
||||||
|
connect = none; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties |
||||||
|
{ |
||||||
|
} |
@ -0,0 +1,107 @@ |
|||||||
|
/** |
||||||
|
* Provides Acedia with access to actors of a certain level (server, client or |
||||||
|
* even entry level). |
||||||
|
* 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 LevelCore extends AcediaActor |
||||||
|
abstract; |
||||||
|
|
||||||
|
/** |
||||||
|
* # `LevelCore` |
||||||
|
* |
||||||
|
* Since in UnrealScript mixing objects and actors (or, more precisely, storing |
||||||
|
* `Actor`s in non-actor `Object`s) is dangerous, `LevelCore`s are needed to |
||||||
|
* provide an access to a certain level and its `Actor`s. |
||||||
|
* |
||||||
|
* `LevelCore is a singleton, however this isn't a design choice, but |
||||||
|
* a side-effect of granting `Object`s safe access to an `Actor` through class |
||||||
|
* and `static` methods and `default` fields. |
||||||
|
* |
||||||
|
* ## Using `LevelCore`s |
||||||
|
* |
||||||
|
* `LevelCore` itself is `abstract` and cannot be instantiated, so to make use |
||||||
|
* of it, child classes must be declared. This allows one to specialize |
||||||
|
* `LevelCore`: for example, Acedia's `ServerLevelCore` checks that net mode of |
||||||
|
* the level it is created on is either `NM_DedicatedServer` or |
||||||
|
* `NM_ListenServer`. |
||||||
|
* Then Acedia's server API can only be created and used with such a level. |
||||||
|
* |
||||||
|
* To create you own `LevelCore`, extend simply this base class. |
||||||
|
* If you also want to specialize your level core, overload `CreateLevelCore()` |
||||||
|
* 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` |
||||||
|
var private LevelCore activeInstance; |
||||||
|
|
||||||
|
protected function Finalizer() |
||||||
|
{ |
||||||
|
default.activeInstance = none; |
||||||
|
} |
||||||
|
|
||||||
|
public static function LevelCore CreateLevelCore(Actor source) |
||||||
|
{ |
||||||
|
if (GetInstance() != none) return none; |
||||||
|
if (source == none) return none; |
||||||
|
|
||||||
|
default.blockSpawning = false; |
||||||
|
default.activeInstance = source.Spawn(default.class); |
||||||
|
default.blockSpawning = true; |
||||||
|
return default.activeInstance; |
||||||
|
} |
||||||
|
|
||||||
|
public final static function LevelCore GetInstance() |
||||||
|
{ |
||||||
|
local bool instanceExists; |
||||||
|
instanceExists = default.activeInstance != none |
||||||
|
&& !default.activeInstance.bPendingDelete; |
||||||
|
if (instanceExists) { |
||||||
|
return default.activeInstance; |
||||||
|
} |
||||||
|
return none; |
||||||
|
} |
||||||
|
|
||||||
|
// Make sure only one instance of 'LevelCore' exists at any point in time. |
||||||
|
event PreBeginPlay() |
||||||
|
{ |
||||||
|
if (default.blockSpawning || GetInstance() != none) |
||||||
|
{ |
||||||
|
Destroy(); |
||||||
|
return; |
||||||
|
} |
||||||
|
default.activeInstance = self; |
||||||
|
super.PreBeginPlay(); |
||||||
|
} |
||||||
|
|
||||||
|
// Clean up |
||||||
|
event Destroyed() |
||||||
|
{ |
||||||
|
if (self == default.activeInstance) { |
||||||
|
default.activeInstance = none; |
||||||
|
} |
||||||
|
super.Destroyed(); |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties |
||||||
|
{ |
||||||
|
blockSpawning = true |
||||||
|
} |
@ -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