From b60b3280de146bb0ff2465fb2a7b2903014f77fc Mon Sep 17 00:00:00 2001 From: Shtoyan Date: Tue, 7 Mar 2023 17:08:55 +0400 Subject: [PATCH 01/11] Add initial implementation for map lists --- config/AcediaMaps.ini | 7 +++ sources/AcediaLauncherMut.uc | 4 +- sources/GameModes/GameMode.uc | 10 ++-- sources/MapList/MapList.uc | 75 ++++++++++++++++++++++++++++++ sources/MapList/MapList_Feature.uc | 25 ++++++++++ sources/StartUp.uc | 6 ++- sources/VotingHandlerAdapter.uc | 53 ++++++++++++++++++++- 7 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 config/AcediaMaps.ini create mode 100644 sources/MapList/MapList.uc create mode 100644 sources/MapList/MapList_Feature.uc diff --git a/config/AcediaMaps.ini b/config/AcediaMaps.ini new file mode 100644 index 0000000..c0ed975 --- /dev/null +++ b/config/AcediaMaps.ini @@ -0,0 +1,7 @@ +[default MapList] +autoEnable=true +map="KF-BioticsLab" +map="KF-Farm" +map="KF-Manor" +map="KF-Offices" +map="KF-WestLondon" diff --git a/sources/AcediaLauncherMut.uc b/sources/AcediaLauncherMut.uc index 82ee6ca..9a36456 100644 --- a/sources/AcediaLauncherMut.uc +++ b/sources/AcediaLauncherMut.uc @@ -1,7 +1,8 @@ /** * Main and only Acedia mutator. Used for providing access to mutator * events' calls and detecting server travel. - * Copyright 2020-2022 Anton Tarasenko + * Copyright 2020-2023 Anton Tarasenko + * 2023 Shtoyan *------------------------------------------------------------------------------ * This file is part of Acedia. * @@ -45,6 +46,7 @@ simulated function PreBeginPlay() } if (votingAdapter != none) { votingAdapter.InjectIntoVotingHandler(); + votingAdapter.TrySetupMapList(); } SetupMutatorSignals(); } diff --git a/sources/GameModes/GameMode.uc b/sources/GameModes/GameMode.uc index 13589bd..5eeff6e 100644 --- a/sources/GameModes/GameMode.uc +++ b/sources/GameModes/GameMode.uc @@ -77,6 +77,7 @@ protected function HashTable ToData() result.SetString(P("gameTypeClass"), gameTypeClass); result.SetString(P("acronym"), acronym); result.SetString(P("mapPrefix"), mapPrefix); + nextArray = _.collections.EmptyArrayList(); for (i = 0; i < option.length; i += 1) { @@ -103,9 +104,10 @@ protected function FromData(HashTable source) return; } gameTypeClass = source.GetString(P("gameTypeClass")); - acronym = source.GetString(P("acronym")); - mapPrefix = source.GetString(P("mapPrefix")); - nextArray = source.GetArrayList(P("option")); + acronym = source.GetString(P("acronym")); + mapPrefix = source.GetString(P("mapPrefix")); + + nextArray = source.GetArrayList(P("option")); if (nextArray == none) { return; } @@ -271,6 +273,8 @@ defaultproperties hardSynonyms(0) = "harder" // "hard" is prefix of this, so it will count hardSynonyms(1) = "difficult" suicidalSynonyms(0) = "suicidal" + // DONE! + suicidalSynonyms(1) = "sui" hoeSynonyms(0) = "hellonearth" hoeSynonyms(1) = "hellon earth" hoeSynonyms(2) = "hell onearth" diff --git a/sources/MapList/MapList.uc b/sources/MapList/MapList.uc new file mode 100644 index 0000000..f2a5aa0 --- /dev/null +++ b/sources/MapList/MapList.uc @@ -0,0 +1,75 @@ +/** + * Config for `MapList_Feature`. + * Copyright 2023 Anton Tarasenko + * 2023 Shtoyan + *------------------------------------------------------------------------------ + * 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 MapList extends FeatureConfig + perObjectConfig + config(AcediaMaps); + +var public config array map; + +protected function HashTable ToData() { + local int i; + local ArrayList mapArray; + local HashTable result; + + result = _.collections.EmptyHashTable(); + mapArray = _.collections.EmptyArrayList(); + + for (i = 0; i < map.length; i += 1) { + mapArray.AddString(map[i]); + } + + result.SetItem(P("maps"), mapArray); + _.memory.Free(mapArray); + + return result; +} + +protected function FromData(HashTable source) { + local int i; + local ArrayList mapArray; + + if (source == none) { + return; + } + + mapArray = source.GetArrayList(P("maps")); + if (mapArray == none) { + return; + } + + map.length = 0; + for (i = 0; i < mapArray.GetLength(); i += 1) { + map[map.length] = mapArray.GetString(i); + } + _.memory.Free(mapArray); +} + +protected function DefaultIt() { + map[0] = "KF-BioticsLab"; + map[1] = "KF-Farm"; + map[2] = "KF-Manor"; + map[3] = "KF-Offices"; + map[4] = "KF-WestLondon"; +} + +defaultproperties { + configName = "AcediaMaps" +} \ No newline at end of file diff --git a/sources/MapList/MapList_Feature.uc b/sources/MapList/MapList_Feature.uc new file mode 100644 index 0000000..0000ff6 --- /dev/null +++ b/sources/MapList/MapList_Feature.uc @@ -0,0 +1,25 @@ +/** + * This feature is a dumb proxy for loading map list configs. + * Copyright 2023 Anton Tarasenko + * 2023 Shtoyan + *------------------------------------------------------------------------------ + * 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 MapList_Feature extends Feature; + +defaultproperties { + configClass = class'MapList' +} \ No newline at end of file diff --git a/sources/StartUp.uc b/sources/StartUp.uc index 478883d..b9c698d 100644 --- a/sources/StartUp.uc +++ b/sources/StartUp.uc @@ -1,6 +1,7 @@ /** * This actor's role is to perform Acedia's server startup. - * Copyright 2019-2022 Anton Tarasenko + * Copyright 2019-2023 Anton Tarasenko + * 2023 Shtoyan *------------------------------------------------------------------------------ * This file is part of Acedia. * @@ -125,6 +126,9 @@ public function array GetAutoConfigurationInfo() local array result; availableFeatures = _.environment.GetAvailableFeatures(); + // We only have a single feature, so instead of adding our own manifest, simply add it here + class'MapList_Feature'.static.LoadConfigs(); + availableFeatures[availableFeatures.length] = class'MapList_Feature'; for (i = 0; i < availableFeatures.length; i += 1) { autoConfig = availableFeatures[i].static.GetAutoEnabledConfig(); diff --git a/sources/VotingHandlerAdapter.uc b/sources/VotingHandlerAdapter.uc index d0538d1..1589d98 100644 --- a/sources/VotingHandlerAdapter.uc +++ b/sources/VotingHandlerAdapter.uc @@ -5,7 +5,8 @@ * data from Acedia's game modes. * Requires `GameInfo`'s voting handler to be derived from * `XVotingHandler`, which is satisfied by pretty much every used handler. - * Copyright 2021-2022 Anton Tarasenko + * Copyright 2021-2023 Anton Tarasenko + * 2023 Shtoyan *------------------------------------------------------------------------------ * This file is part of Acedia. * @@ -102,6 +103,7 @@ protected function Finalizer() * Backup of replaced configs is made internally, so that they can be restored * on map change. */ +// TODO ADD ME! public final function InjectIntoVotingHandler() { local int i; @@ -138,6 +140,55 @@ public final function InjectIntoVotingHandler() votingHandler.gameConfig = newVotingHandlerConfig; } +public function TrySetupMapList() { + local MapList_Feature mapListFeature; + local Text currentConfigName; + local MapList currentConfig; + + mapListFeature = MapList_Feature(class'MapList_Feature'.static.GetEnabledInstance()); + if (mapListFeature == none) { + return; + } + currentConfigName = mapListFeature.GetCurrentConfig(); + if (currentConfigName != none) { + currentConfig = MapList(class'MapList'.static.GetConfigInstance(currentConfigName)); + } + if (currentConfig != none) { + ReplaceHandlerMaps(XVotingHandler(votingHandlerReference.Get()), currentConfig.map); + } else { + warn("currentConfig is none! Aborting!"); + } + _.memory.Free(currentConfig); + _.memory.Free(currentConfigName); + _.memory.Free(mapListFeature); +} + +// TODO add map reps and play count from KFMapVoteHistory.ini +public function ReplaceHandlerMaps(XVotingHandler votingHandler, array maps) { + local VotingHandler.MapVoteMapList nextRecord; + local array recordArray; + local int i; + + if (votingHandler == none) { + warn("votingHandler is none!"); + return; + } + if (maps.length == 0) { + warn("maps.length is 0!"); + return; + } + recordArray = votingHandler.mapList; + warn(">>> recordArray.length =" $ recordArray.length); + recordArray.length = maps.length; + votingHandler.mapCount = maps.length; + nextRecord.bEnabled = true; + for (i = 0; i < maps.length; i += 1) { + nextRecord.mapName = maps[i]; + recordArray[i] = nextRecord; + } + votingHandler.mapList = recordArray; +} + private function VotingHandler.MapVoteGameConfig BuildVotingHandlerConfig( GameMode gameMode) { -- 2.20.1 From 0ae9e0571d36882923c1e355c120f274ed30d2db Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Wed, 8 Mar 2023 02:47:00 +0700 Subject: [PATCH 02/11] Change `MapList` to regular `AcediaConfig` --- sources/MapList/MapList.uc | 17 ++++++----------- sources/MapList/MapList_Feature.uc | 25 ------------------------- 2 files changed, 6 insertions(+), 36 deletions(-) delete mode 100644 sources/MapList/MapList_Feature.uc diff --git a/sources/MapList/MapList.uc b/sources/MapList/MapList.uc index f2a5aa0..d5891ce 100644 --- a/sources/MapList/MapList.uc +++ b/sources/MapList/MapList.uc @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Acedia. If not, see . */ -class MapList extends FeatureConfig +class MapList extends AcediaConfig perObjectConfig config(AcediaMaps); @@ -31,14 +31,11 @@ protected function HashTable ToData() { result = _.collections.EmptyHashTable(); mapArray = _.collections.EmptyArrayList(); - for (i = 0; i < map.length; i += 1) { mapArray.AddString(map[i]); } - result.SetItem(P("maps"), mapArray); _.memory.Free(mapArray); - return result; } @@ -49,12 +46,10 @@ protected function FromData(HashTable source) { if (source == none) { return; } - mapArray = source.GetArrayList(P("maps")); if (mapArray == none) { return; } - map.length = 0; for (i = 0; i < mapArray.GetLength(); i += 1) { map[map.length] = mapArray.GetString(i); @@ -63,11 +58,11 @@ protected function FromData(HashTable source) { } protected function DefaultIt() { - map[0] = "KF-BioticsLab"; - map[1] = "KF-Farm"; - map[2] = "KF-Manor"; - map[3] = "KF-Offices"; - map[4] = "KF-WestLondon"; + map[0] = "KF-BioticsLab"; + map[1] = "KF-Farm"; + map[2] = "KF-Manor"; + map[3] = "KF-Offices"; + map[4] = "KF-WestLondon"; } defaultproperties { diff --git a/sources/MapList/MapList_Feature.uc b/sources/MapList/MapList_Feature.uc deleted file mode 100644 index 0000ff6..0000000 --- a/sources/MapList/MapList_Feature.uc +++ /dev/null @@ -1,25 +0,0 @@ -/** - * This feature is a dumb proxy for loading map list configs. - * Copyright 2023 Anton Tarasenko - * 2023 Shtoyan - *------------------------------------------------------------------------------ - * 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 MapList_Feature extends Feature; - -defaultproperties { - configClass = class'MapList' -} \ No newline at end of file -- 2.20.1 From 8bdf2062a08e49159a4b98cf685b96d59d0f4059 Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Wed, 8 Mar 2023 02:48:11 +0700 Subject: [PATCH 03/11] Remove unnecessary comments --- sources/GameModes/GameMode.uc | 1 - 1 file changed, 1 deletion(-) diff --git a/sources/GameModes/GameMode.uc b/sources/GameModes/GameMode.uc index 5eeff6e..1e78bb2 100644 --- a/sources/GameModes/GameMode.uc +++ b/sources/GameModes/GameMode.uc @@ -273,7 +273,6 @@ defaultproperties hardSynonyms(0) = "harder" // "hard" is prefix of this, so it will count hardSynonyms(1) = "difficult" suicidalSynonyms(0) = "suicidal" - // DONE! suicidalSynonyms(1) = "sui" hoeSynonyms(0) = "hellonearth" hoeSynonyms(1) = "hellon earth" -- 2.20.1 From 470d878c456122033e5afee493d04cdb7d16be34 Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Wed, 8 Mar 2023 02:52:53 +0700 Subject: [PATCH 04/11] Add "includeMaps" config variable into `BaseGameMode` --- sources/GameModes/BaseGameMode.uc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/sources/GameModes/BaseGameMode.uc b/sources/GameModes/BaseGameMode.uc index 3b7a017..7b45f1e 100644 --- a/sources/GameModes/BaseGameMode.uc +++ b/sources/GameModes/BaseGameMode.uc @@ -44,6 +44,8 @@ var protected config array includeFeature; // `Feature`s to exclude from game mode, regardless of other settings // (this one has highest priority) var protected config array excludeFeature; +// Lists of maps to include for this game mode +var protected config array includeMaps; struct FeatureConfigPair { @@ -75,17 +77,23 @@ protected function HashTable ToData() _.memory.Free(nextArray); nextArray = _.collections.EmptyArrayList(); for (i = 0; i < excludeFeature.length; i += 1) { - nextArray.AddItem(_.text.FromString(excludeFeature[i])); + nextArray.AddString(excludeFeature[i]); } result.SetItem(P("excludeFeature"), nextArray); _.memory.Free(nextArray); nextArray = _.collections.EmptyArrayList(); for (i = 0; i < includeMutator.length; i += 1) { - nextArray.AddItem(_.text.FromString(includeFeature[i])); + nextArray.AddString(includeFeature[i]); } result.SetItem(P("includeMutator"), nextArray); _.memory.Free(nextArray); nextArray = _.collections.EmptyArrayList(); + for (i = 0; i < includeMaps.length; i += 1) { + nextArray.AddString(includeMaps[i]); + } + result.SetItem(P("includeMaps"), nextArray); + _.memory.Free(nextArray); + nextArray = _.collections.EmptyArrayList(); for (i = 0; i < includeFeatureAs.length; i += 1) { nextPair = _.collections.EmptyHashTable(); @@ -120,6 +128,9 @@ protected function FromData(HashTable source) nextArray = source.GetArrayList(P("includeMutator")); includeMutator = DynamicIntoStringArray(nextArray); _.memory.Free(nextArray); + nextArray = source.GetArrayList(P("includeMaps")); + includeMaps = DynamicIntoStringArray(nextArray); + _.memory.Free(nextArray); nextArray = source.GetArrayList(P("includeFeatureAs")); if (nextArray == none) { return; -- 2.20.1 From 11610dc9d2fb93741f541626f078b423e4c4356e Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Wed, 8 Mar 2023 02:55:06 +0700 Subject: [PATCH 05/11] Remove code that used unneeded `MapList_Feature` --- sources/MapList/MapList.uc | 2 +- sources/StartUp.uc | 3 --- sources/VotingHandlerAdapter.uc | 6 ------ 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/sources/MapList/MapList.uc b/sources/MapList/MapList.uc index d5891ce..c9f0e7a 100644 --- a/sources/MapList/MapList.uc +++ b/sources/MapList/MapList.uc @@ -1,5 +1,5 @@ /** - * Config for `MapList_Feature`. + * Config class for storing map lists. * Copyright 2023 Anton Tarasenko * 2023 Shtoyan *------------------------------------------------------------------------------ diff --git a/sources/StartUp.uc b/sources/StartUp.uc index b9c698d..184603c 100644 --- a/sources/StartUp.uc +++ b/sources/StartUp.uc @@ -126,9 +126,6 @@ public function array GetAutoConfigurationInfo() local array result; availableFeatures = _.environment.GetAvailableFeatures(); - // We only have a single feature, so instead of adding our own manifest, simply add it here - class'MapList_Feature'.static.LoadConfigs(); - availableFeatures[availableFeatures.length] = class'MapList_Feature'; for (i = 0; i < availableFeatures.length; i += 1) { autoConfig = availableFeatures[i].static.GetAutoEnabledConfig(); diff --git a/sources/VotingHandlerAdapter.uc b/sources/VotingHandlerAdapter.uc index 1589d98..b892455 100644 --- a/sources/VotingHandlerAdapter.uc +++ b/sources/VotingHandlerAdapter.uc @@ -141,15 +141,9 @@ public final function InjectIntoVotingHandler() } public function TrySetupMapList() { - local MapList_Feature mapListFeature; local Text currentConfigName; local MapList currentConfig; - mapListFeature = MapList_Feature(class'MapList_Feature'.static.GetEnabledInstance()); - if (mapListFeature == none) { - return; - } - currentConfigName = mapListFeature.GetCurrentConfig(); if (currentConfigName != none) { currentConfig = MapList(class'MapList'.static.GetConfigInstance(currentConfigName)); } -- 2.20.1 From 36aed1a2d368630c1e29d858f99a79403958d7b0 Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Wed, 8 Mar 2023 04:48:12 +0700 Subject: [PATCH 06/11] Change how map lists work This patch enables support of per-game mode lists --- sources/AcediaLauncherMut.uc | 1 - sources/GameModes/BaseGameMode.uc | 5 + sources/StartUp.uc | 1 + sources/VotingHandlerAdapter.uc | 153 +++++++++++++++++++++++------- 4 files changed, 125 insertions(+), 35 deletions(-) diff --git a/sources/AcediaLauncherMut.uc b/sources/AcediaLauncherMut.uc index 9a36456..05c71d5 100644 --- a/sources/AcediaLauncherMut.uc +++ b/sources/AcediaLauncherMut.uc @@ -46,7 +46,6 @@ simulated function PreBeginPlay() } if (votingAdapter != none) { votingAdapter.InjectIntoVotingHandler(); - votingAdapter.TrySetupMapList(); } SetupMutatorSignals(); } diff --git a/sources/GameModes/BaseGameMode.uc b/sources/GameModes/BaseGameMode.uc index 7b45f1e..533fe51 100644 --- a/sources/GameModes/BaseGameMode.uc +++ b/sources/GameModes/BaseGameMode.uc @@ -431,6 +431,11 @@ public function array GetIncludedMutators() return StringToTextArray(validatedMutators); } +public function array GetIncludedMapLists() +{ + return StringToTextArray(includeMaps); +} + defaultproperties { configName = "AcediaGameModes" diff --git a/sources/StartUp.uc b/sources/StartUp.uc index 184603c..2210404 100644 --- a/sources/StartUp.uc +++ b/sources/StartUp.uc @@ -61,6 +61,7 @@ private function InitializeServer() _ = class'Global'.static.GetInstance(); _server = class'ServerGlobal'.static.GetInstance(); class'ServerLevelCore'.static.CreateLevelCore(self); + class'MapList'.static.Initialize(); for (i = 0; i < class'Packages'.default.package.length; i += 1) { _.environment.RegisterPackage_S(class'Packages'.default.package[i]); } diff --git a/sources/VotingHandlerAdapter.uc b/sources/VotingHandlerAdapter.uc index b892455..b2936c7 100644 --- a/sources/VotingHandlerAdapter.uc +++ b/sources/VotingHandlerAdapter.uc @@ -66,6 +66,9 @@ var private NativeActorRef votingHandlerReference; // otherwise Acedia will alter its config var private array backupVotingHandlerConfig; +// Maps map pseudonims we've used in voting handler to real map names +var private HashTable pseudonimToMap; + // Setting value of this flag to `true` indicates that map switching just // occurred and we need to recover some information from the previous map. var private config bool isServerTraveling; @@ -88,7 +91,7 @@ var private const array normalSynonyms; var private const array longSynonyms; var private LoggerAPI.Definition fatNoXVotingHandler, fatBadGameConfigIndexVH; -var private LoggerAPI.Definition fatBadGameConfigIndexAdapter; +var private LoggerAPI.Definition fatBadGameConfigIndexAdapter, warnMissingMapList; protected function Finalizer() { @@ -103,10 +106,10 @@ protected function Finalizer() * Backup of replaced configs is made internally, so that they can be restored * on map change. */ -// TODO ADD ME! public final function InjectIntoVotingHandler() { local int i; + local string nextGameModePrefix; local GameMode nextGameMode; local XVotingHandler votingHandler; local array newVotingHandlerConfig; @@ -121,13 +124,17 @@ public final function InjectIntoVotingHandler() _.logger.Auto(fatNoXVotingHandler); return; } + pseudonimToMap = _.collections.EmptyHashTable(); votingHandlerReference = _server.unreal.ActorRef(votingHandler); availableGameModes = class'GameMode'.static.AvailableConfigs(); - for (i = 0; i < availableGameModes.length; i += 1) - { + votingHandler.mapCount = 0; + votingHandler.mapList.length = 0; + for (i = 0; i < availableGameModes.length; i += 1) { + nextGameModePrefix = "GM" $ i; nextGameMode = GameMode(class'GameMode'.static .GetConfigInstance(availableGameModes[i])); - newVotingHandlerConfig[i] = BuildVotingHandlerConfig(nextGameMode); + newVotingHandlerConfig[i] = BuildVotingHandlerConfig(nextGameMode, nextGameModePrefix); + LoadGameModeMaps(nextGameMode, nextGameModePrefix, votingHandler); // Setup proper game mode index if (availableGameModes[i].ToString() == targetGameMode) { votingHandler.currentGameConfig = i; @@ -140,57 +147,134 @@ public final function InjectIntoVotingHandler() votingHandler.gameConfig = newVotingHandlerConfig; } -public function TrySetupMapList() { - local Text currentConfigName; - local MapList currentConfig; +private function LoadGameModeMaps( + GameMode gameMode, + string gameModePrefix, + XVotingHandler votingHandler +) { + local int i; + local ArrayList gameModeMaps; + local Text mapNameReal, mapNamePseudonim; + local VotingHandler.MapHistoryInfo nextMapInfo; + local VotingHandler.MapVoteMapList nextRecord; + local array newMaps; + + nextRecord.bEnabled = true; + gameModeMaps = GetAllGameModeMaps(gameMode); + for (i = 0; i < gameModeMaps.GetLength(); i += 1) { + // Make a pseudonim to map connection + mapNameReal = gameModeMaps.GetText(i); + mapNamePseudonim = MakeMapPseudonim(mapNameReal, gameModePrefix); + pseudonimToMap.SetItem(mapNamePseudonim, mapNameReal); + // Setup `VotingHandler.MapVoteMapList` struct for next map + nextRecord.mapName = mapNamePseudonim.ToString(); + if (votingHandler.history != none) { + nextMapInfo = votingHandler.history.GetMapHistory(nextRecord.mapName); + nextRecord.playCount = nextMapInfo.p; + nextRecord.sequence = nextMapInfo.s; + } else { + nextRecord.playCount = 0; + nextRecord.sequence = 0; + } + newMaps[newMaps.length] = nextRecord; + } + AppendMapsIntoHandler(votingHandler, newMaps); +} - if (currentConfigName != none) { - currentConfig = MapList(class'MapList'.static.GetConfigInstance(currentConfigName)); +private function Text MakeMapPseudonim(Text realName, string gameModePrefix) { + local Parser parser; + local MutableText prefix, nameBody; + local MutableText result; + + result = _.text.FromStringM(gameModePrefix); + result.Append(P("-")); + parser = realName.Parse(); + parser.MUntil(prefix, _.text.GetCharacter("-")); + parser.Match(P("-")); + if (parser.Ok()) { + nameBody = parser.GetRemainderM(); + result.Append(nameBody); } - if (currentConfig != none) { - ReplaceHandlerMaps(XVotingHandler(votingHandlerReference.Get()), currentConfig.map); - } else { - warn("currentConfig is none! Aborting!"); + else { + result.Append(realName); } - _.memory.Free(currentConfig); - _.memory.Free(currentConfigName); - _.memory.Free(mapListFeature); + _.memory.Free(nameBody); + _.memory.Free(prefix); + _.memory.Free(parser); + return result.IntoText(); } -// TODO add map reps and play count from KFMapVoteHistory.ini -public function ReplaceHandlerMaps(XVotingHandler votingHandler, array maps) { - local VotingHandler.MapVoteMapList nextRecord; - local array recordArray; +private function ArrayList GetAllGameModeMaps(GameMode gameMode) { + local int i, j; + local HashTable uniqueMapSet; + local ArrayList result; + local array usedMapLists; + local MapList nextMapConfig; + local array nextMapArray; + local Text nextMapName, lowerMapName; + + uniqueMapSet = _.collections.EmptyHashTable(); // to quickly make sure we add each map only once + result = _.collections.EmptyArrayList(); + usedMapLists = gameMode.GetIncludedMapLists(); + for (i = 0; i < usedMapLists.length; i += 1) { + // Get maps from `MapList` config + nextMapConfig = MapList(class'MapList'.static.GetConfigInstance(usedMapLists[i])); + nextMapArray.length = 0; + if (nextMapConfig != none) { + nextMapArray = nextMapConfig.map; + } else { + _.logger.Auto(warnMissingMapList).Arg(usedMapLists[i].Copy()); + } + _.memory.Free(nextMapConfig); + // Add maps we haven't yet added from other lists + for (j = 0; j < nextMapArray.length; j += 1) { + nextMapName = _.text.FromString(nextMapArray[j]); + lowerMapName = nextMapName.LowerCopy(); + if (!uniqueMapSet.HasKey(lowerMapName)) { + uniqueMapSet.SetItem(lowerMapName, none); + result.AddItem(nextMapName); + } + _.memory.Free(lowerMapName); + _.memory.Free(nextMapName); + } + } + _.memory.Free(uniqueMapSet); + _.memory.FreeMany(usedMapLists); + return result; +} + +private function AppendMapsIntoHandler( + XVotingHandler votingHandler, + array newMaps +) { local int i; + local array mapListCopy; if (votingHandler == none) { warn("votingHandler is none!"); return; } - if (maps.length == 0) { - warn("maps.length is 0!"); + if (newMaps.length == 0) { + warn("newMaps.length is 0!"); return; } - recordArray = votingHandler.mapList; - warn(">>> recordArray.length =" $ recordArray.length); - recordArray.length = maps.length; - votingHandler.mapCount = maps.length; - nextRecord.bEnabled = true; - for (i = 0; i < maps.length; i += 1) { - nextRecord.mapName = maps[i]; - recordArray[i] = nextRecord; + mapListCopy = votingHandler.mapList; + for (i = 0; i < newMaps.length; i += 1) { + mapListCopy[mapListCopy.length] = newMaps[i]; } - votingHandler.mapList = recordArray; + votingHandler.mapList = mapListCopy; + votingHandler.mapCount += newMaps.length; } private function VotingHandler.MapVoteGameConfig BuildVotingHandlerConfig( - GameMode gameMode) + GameMode gameMode, + string gameModePrefix) { local MutableText nextColoredName; local VotingHandler.MapVoteGameConfig result; result.gameClass = _.text.IntoString(gameMode.GetGameTypeClass()); - result.prefix = _.text.IntoString(gameMode.GetMapPrefix()); + result.prefix = gameModePrefix $ "-"; nextColoredName = gameMode .GetTitle() .IntoMutableText() @@ -404,6 +488,7 @@ defaultproperties normalSynonyms(1) = "medium" normalSynonyms(2) = "regular" longSynonyms(0) = "long" + warnMissingMapList = (l=LOG_Warning,m="Cannot find map list `%1`.") fatNoXVotingHandler = (l=LOG_Fatal,m="`XVotingHandler` class is missing. Make sure your server setup supports Acedia's game modes (by used voting handler derived from `XVotingHandler`).") fatBadGameConfigIndexVH = (l=LOG_Fatal,m="`XVotingHandler`'s `currentGameConfig` variable value of %1 is out-of-bounds for `XVotingHandler.gameConfig` of length %2. Report this issue.") fatBadGameConfigIndexAdapter = (l=LOG_Fatal,m="`XVotingHandler`'s `currentGameConfig` variable value of %1 is out-of-bounds for `VHAdapter` of length %2. Report this issue.") -- 2.20.1 From 8c5a60dccd3fa892b3022086db65feb610fce799 Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Wed, 8 Mar 2023 13:13:30 +0700 Subject: [PATCH 07/11] Refactor map replacement to be done by separate object --- sources/MapList/MapTool.uc | 187 ++++++++++++++++++++++++++++++++ sources/VotingHandlerAdapter.uc | 132 ++-------------------- 2 files changed, 194 insertions(+), 125 deletions(-) create mode 100644 sources/MapList/MapTool.uc diff --git a/sources/MapList/MapTool.uc b/sources/MapList/MapTool.uc new file mode 100644 index 0000000..a1d8dc8 --- /dev/null +++ b/sources/MapList/MapTool.uc @@ -0,0 +1,187 @@ +/** + * Copyright 2023 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 MapTool extends AcediaObject; + +// Finding voting handler is not cheap, so only do it once and then store it. +var private NativeActorRef votingHandlerReference; +// Maps map pseudonims we've used in voting handler to real map names +var private HashTable pseudonimToMap; +var private array outputMapList; +var private int gameModesSeen; + +protected function Constructor() { + pseudonimToMap = _.collections.EmptyHashTable(); +} + +protected function Finalizer() { + _.memory.Free(votingHandlerReference); + _.memory.Free(pseudonimToMap); + votingHandlerReference = none; + pseudonimToMap = none; + outputMapList.length = 0; + gameModesSeen = 0; +} + +public function bool Initialize(NativeActorRef initVotingHandlerReference) { + if (initVotingHandlerReference == none) return false; + if (XVotingHandler(initVotingHandlerReference.Get()) == none) return false; + + initVotingHandlerReference.NewRef(); + votingHandlerReference = initVotingHandlerReference; + return true; +} + +public function string LoadGameModeMaps(GameMode gameMode) { + local int i; + local XVotingHandler votingHandler; + local ArrayList gameModeMaps; + local Text mapNameReal, mapNamePseudonim; + local VotingHandler.MapHistoryInfo nextMapInfo; + local VotingHandler.MapVoteMapList nextRecord; + local array newMaps; + local string gameModePrefix; + + votingHandler = GetVotingHandler(); + if (votingHandler == none) { + return "!!!"; + } + gameModePrefix = ("KF" $ gameModesSeen); + nextRecord.bEnabled = true; + gameModeMaps = GetAllGameModeMaps(gameMode); + for (i = 0; i < gameModeMaps.GetLength(); i += 1) { + // Make a pseudonim to map connection + mapNameReal = gameModeMaps.GetText(i); + mapNamePseudonim = MakeMapPseudonim(mapNameReal, gameModePrefix); + pseudonimToMap.SetItem(mapNamePseudonim, mapNameReal); + // Setup `VotingHandler.MapVoteMapList` struct for next map + nextRecord.mapName = mapNamePseudonim.ToString(); + if (votingHandler.history != none) { + nextMapInfo = votingHandler.history.GetMapHistory(mapNameReal.ToString()); + nextRecord.playCount = nextMapInfo.p; + nextRecord.sequence = nextMapInfo.s; + } else { + nextRecord.playCount = 0; + nextRecord.sequence = 0; + } + newMaps[newMaps.length] = nextRecord; + } + AppendMapsIntoHandler(newMaps); + gameModesSeen += 1; + return gameModePrefix; +} + +private function Text MakeMapPseudonim(Text realName, string gameModePrefix) { + local Parser parser; + local MutableText prefix, nameBody; + local MutableText result; + + result = _.text.FromStringM(gameModePrefix); + result.Append(P("-")); + parser = realName.Parse(); + parser.MUntil(prefix, _.text.GetCharacter("-")); + parser.Match(P("-")); + if (parser.Ok()) { + nameBody = parser.GetRemainderM(); + result.Append(nameBody); + } + else { + result.Append(realName); + } + _.memory.Free(nameBody); + _.memory.Free(prefix); + _.memory.Free(parser); + return result.IntoText(); +} + +private function ArrayList GetAllGameModeMaps(GameMode gameMode) { + local int i, j; + local HashTable uniqueMapSet; + local ArrayList result; + local array usedMapLists; + local MapList nextMapConfig; + local array nextMapArray; + local Text nextMapName, lowerMapName; + + uniqueMapSet = _.collections.EmptyHashTable(); // to quickly make sure we add each map only once + result = _.collections.EmptyArrayList(); + usedMapLists = gameMode.GetIncludedMapLists(); + for (i = 0; i < usedMapLists.length; i += 1) { + // Get maps from `MapList` config + nextMapConfig = MapList(class'MapList'.static.GetConfigInstance(usedMapLists[i])); + nextMapArray.length = 0; + if (nextMapConfig != none) { + nextMapArray = nextMapConfig.map; + } else { + //_.logger.Auto(warnMissingMapList).Arg(usedMapLists[i].Copy()); + } + _.memory.Free(nextMapConfig); + // Add maps we haven't yet added from other lists + for (j = 0; j < nextMapArray.length; j += 1) { + nextMapName = _.text.FromString(nextMapArray[j]); + lowerMapName = nextMapName.LowerCopy(); + if (!uniqueMapSet.HasKey(lowerMapName)) { + uniqueMapSet.SetItem(lowerMapName, none); + result.AddItem(nextMapName); + } + _.memory.Free(lowerMapName); + _.memory.Free(nextMapName); + } + } + _.memory.Free(uniqueMapSet); + _.memory.FreeMany(usedMapLists); + return result; +} + +private function AppendMapsIntoHandler(array newMaps) { + local int i; + local XVotingHandler votingHandler; + + votingHandler = GetVotingHandler(); + if (votingHandler == none) { + warn("votingHandler is none!"); + return; + } + if (newMaps.length == 0) { + warn("newMaps.length is 0!"); + return; + } + for (i = 0; i < newMaps.length; i += 1) { + outputMapList[outputMapList.length] = newMaps[i]; + } +} + +public final function InjectMaps() { + local XVotingHandler votingHandler; + + votingHandler = GetVotingHandler(); + if (votingHandler != none) { + votingHandler.mapList = outputMapList; + votingHandler.mapCount = outputMapList.length; + } +} + +private function XVotingHandler GetVotingHandler() { + if (votingHandlerReference != none) { + return XVotingHandler(votingHandlerReference.Get()); + } + return none; +} + +defaultproperties { +} \ No newline at end of file diff --git a/sources/VotingHandlerAdapter.uc b/sources/VotingHandlerAdapter.uc index b2936c7..e338685 100644 --- a/sources/VotingHandlerAdapter.uc +++ b/sources/VotingHandlerAdapter.uc @@ -66,9 +66,6 @@ var private NativeActorRef votingHandlerReference; // otherwise Acedia will alter its config var private array backupVotingHandlerConfig; -// Maps map pseudonims we've used in voting handler to real map names -var private HashTable pseudonimToMap; - // Setting value of this flag to `true` indicates that map switching just // occurred and we need to recover some information from the previous map. var private config bool isServerTraveling; @@ -113,6 +110,7 @@ public final function InjectIntoVotingHandler() local GameMode nextGameMode; local XVotingHandler votingHandler; local array newVotingHandlerConfig; + local MapTool mapTool; if (votingHandlerReference != none) { return; @@ -124,17 +122,17 @@ public final function InjectIntoVotingHandler() _.logger.Auto(fatNoXVotingHandler); return; } - pseudonimToMap = _.collections.EmptyHashTable(); votingHandlerReference = _server.unreal.ActorRef(votingHandler); + mapTool = MapTool(_.memory.Allocate(class'MapTool')); + mapTool.Initialize(votingHandlerReference); //TODO check return value availableGameModes = class'GameMode'.static.AvailableConfigs(); votingHandler.mapCount = 0; votingHandler.mapList.length = 0; for (i = 0; i < availableGameModes.length; i += 1) { - nextGameModePrefix = "GM" $ i; nextGameMode = GameMode(class'GameMode'.static .GetConfigInstance(availableGameModes[i])); + nextGameModePrefix = mapTool.LoadGameModeMaps(nextGameMode); newVotingHandlerConfig[i] = BuildVotingHandlerConfig(nextGameMode, nextGameModePrefix); - LoadGameModeMaps(nextGameMode, nextGameModePrefix, votingHandler); // Setup proper game mode index if (availableGameModes[i].ToString() == targetGameMode) { votingHandler.currentGameConfig = i; @@ -142,128 +140,12 @@ public final function InjectIntoVotingHandler() // Report omitted mutators / server options nextGameMode.ReportBadMutatorNames(); nextGameMode.ReportBadOptions(); + _.memory.Free(nextGameMode); } backupVotingHandlerConfig = votingHandler.gameConfig; votingHandler.gameConfig = newVotingHandlerConfig; -} - -private function LoadGameModeMaps( - GameMode gameMode, - string gameModePrefix, - XVotingHandler votingHandler -) { - local int i; - local ArrayList gameModeMaps; - local Text mapNameReal, mapNamePseudonim; - local VotingHandler.MapHistoryInfo nextMapInfo; - local VotingHandler.MapVoteMapList nextRecord; - local array newMaps; - - nextRecord.bEnabled = true; - gameModeMaps = GetAllGameModeMaps(gameMode); - for (i = 0; i < gameModeMaps.GetLength(); i += 1) { - // Make a pseudonim to map connection - mapNameReal = gameModeMaps.GetText(i); - mapNamePseudonim = MakeMapPseudonim(mapNameReal, gameModePrefix); - pseudonimToMap.SetItem(mapNamePseudonim, mapNameReal); - // Setup `VotingHandler.MapVoteMapList` struct for next map - nextRecord.mapName = mapNamePseudonim.ToString(); - if (votingHandler.history != none) { - nextMapInfo = votingHandler.history.GetMapHistory(nextRecord.mapName); - nextRecord.playCount = nextMapInfo.p; - nextRecord.sequence = nextMapInfo.s; - } else { - nextRecord.playCount = 0; - nextRecord.sequence = 0; - } - newMaps[newMaps.length] = nextRecord; - } - AppendMapsIntoHandler(votingHandler, newMaps); -} - -private function Text MakeMapPseudonim(Text realName, string gameModePrefix) { - local Parser parser; - local MutableText prefix, nameBody; - local MutableText result; - - result = _.text.FromStringM(gameModePrefix); - result.Append(P("-")); - parser = realName.Parse(); - parser.MUntil(prefix, _.text.GetCharacter("-")); - parser.Match(P("-")); - if (parser.Ok()) { - nameBody = parser.GetRemainderM(); - result.Append(nameBody); - } - else { - result.Append(realName); - } - _.memory.Free(nameBody); - _.memory.Free(prefix); - _.memory.Free(parser); - return result.IntoText(); -} - -private function ArrayList GetAllGameModeMaps(GameMode gameMode) { - local int i, j; - local HashTable uniqueMapSet; - local ArrayList result; - local array usedMapLists; - local MapList nextMapConfig; - local array nextMapArray; - local Text nextMapName, lowerMapName; - - uniqueMapSet = _.collections.EmptyHashTable(); // to quickly make sure we add each map only once - result = _.collections.EmptyArrayList(); - usedMapLists = gameMode.GetIncludedMapLists(); - for (i = 0; i < usedMapLists.length; i += 1) { - // Get maps from `MapList` config - nextMapConfig = MapList(class'MapList'.static.GetConfigInstance(usedMapLists[i])); - nextMapArray.length = 0; - if (nextMapConfig != none) { - nextMapArray = nextMapConfig.map; - } else { - _.logger.Auto(warnMissingMapList).Arg(usedMapLists[i].Copy()); - } - _.memory.Free(nextMapConfig); - // Add maps we haven't yet added from other lists - for (j = 0; j < nextMapArray.length; j += 1) { - nextMapName = _.text.FromString(nextMapArray[j]); - lowerMapName = nextMapName.LowerCopy(); - if (!uniqueMapSet.HasKey(lowerMapName)) { - uniqueMapSet.SetItem(lowerMapName, none); - result.AddItem(nextMapName); - } - _.memory.Free(lowerMapName); - _.memory.Free(nextMapName); - } - } - _.memory.Free(uniqueMapSet); - _.memory.FreeMany(usedMapLists); - return result; -} - -private function AppendMapsIntoHandler( - XVotingHandler votingHandler, - array newMaps -) { - local int i; - local array mapListCopy; - - if (votingHandler == none) { - warn("votingHandler is none!"); - return; - } - if (newMaps.length == 0) { - warn("newMaps.length is 0!"); - return; - } - mapListCopy = votingHandler.mapList; - for (i = 0; i < newMaps.length; i += 1) { - mapListCopy[mapListCopy.length] = newMaps[i]; - } - votingHandler.mapList = mapListCopy; - votingHandler.mapCount += newMaps.length; + mapTool.InjectMaps(); + _.memory.Free(mapTool); } private function VotingHandler.MapVoteGameConfig BuildVotingHandlerConfig( -- 2.20.1 From e6ef9428f07379e02aa60d06017e4ff363083c28 Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Wed, 8 Mar 2023 14:04:03 +0700 Subject: [PATCH 08/11] Fix map switching Previously map switching didn't work because of the map name replacement. This patch fixes that fatal flaw for the voting handler. --- sources/MapList/MapTool.uc | 79 +++++++++++++++++++++++++++------ sources/VotingHandlerAdapter.uc | 2 +- 2 files changed, 66 insertions(+), 15 deletions(-) diff --git a/sources/MapList/MapTool.uc b/sources/MapList/MapTool.uc index a1d8dc8..ba5ec86 100644 --- a/sources/MapList/MapTool.uc +++ b/sources/MapList/MapTool.uc @@ -22,19 +22,27 @@ class MapTool extends AcediaObject; var private NativeActorRef votingHandlerReference; // Maps map pseudonims we've used in voting handler to real map names var private HashTable pseudonimToMap; -var private array outputMapList; +var private array pseudonimMapList; +var private array realMapList; var private int gameModesSeen; +var private string backupMessageMapWon; +var private string backupMessageAdminMapChange; +var private const string ACEDIA_MAP_FORCED_COMMAND; +var private const string ACEDIA_MAP_WON_COMMAND; + protected function Constructor() { pseudonimToMap = _.collections.EmptyHashTable(); } protected function Finalizer() { + _server.unreal.broadcasts.OnHandleText(self).Disconnect(); _.memory.Free(votingHandlerReference); _.memory.Free(pseudonimToMap); votingHandlerReference = none; pseudonimToMap = none; - outputMapList.length = 0; + pseudonimMapList.length = 0; + realMapList.length = 0; gameModesSeen = 0; } @@ -54,7 +62,7 @@ public function string LoadGameModeMaps(GameMode gameMode) { local Text mapNameReal, mapNamePseudonim; local VotingHandler.MapHistoryInfo nextMapInfo; local VotingHandler.MapVoteMapList nextRecord; - local array newMaps; + local array newMapsPseudonim, newMapReal; local string gameModePrefix; votingHandler = GetVotingHandler(); @@ -70,7 +78,6 @@ public function string LoadGameModeMaps(GameMode gameMode) { mapNamePseudonim = MakeMapPseudonim(mapNameReal, gameModePrefix); pseudonimToMap.SetItem(mapNamePseudonim, mapNameReal); // Setup `VotingHandler.MapVoteMapList` struct for next map - nextRecord.mapName = mapNamePseudonim.ToString(); if (votingHandler.history != none) { nextMapInfo = votingHandler.history.GetMapHistory(mapNameReal.ToString()); nextRecord.playCount = nextMapInfo.p; @@ -79,9 +86,12 @@ public function string LoadGameModeMaps(GameMode gameMode) { nextRecord.playCount = 0; nextRecord.sequence = 0; } - newMaps[newMaps.length] = nextRecord; + nextRecord.mapName = mapNamePseudonim.ToString(); + newMapsPseudonim[newMapsPseudonim.length] = nextRecord; + nextRecord.mapName = mapNameReal.ToString(); + newMapReal[newMapReal.length] = nextRecord; } - AppendMapsIntoHandler(newMaps); + AppendMaps(newMapsPseudonim, newMapReal); gameModesSeen += 1; return gameModePrefix; } @@ -148,7 +158,9 @@ private function ArrayList GetAllGameModeMaps(GameMode gameMode) { return result; } -private function AppendMapsIntoHandler(array newMaps) { +private function AppendMaps( + array newMapsPseudonim, + array newMapsReal) { local int i; local XVotingHandler votingHandler; @@ -157,12 +169,11 @@ private function AppendMapsIntoHandler(array newMa warn("votingHandler is none!"); return; } - if (newMaps.length == 0) { - warn("newMaps.length is 0!"); - return; + for (i = 0; i < newMapsPseudonim.length; i += 1) { + pseudonimMapList[pseudonimMapList.length] = newMapsPseudonim[i]; } - for (i = 0; i < newMaps.length; i += 1) { - outputMapList[outputMapList.length] = newMaps[i]; + for (i = 0; i < newMapsReal.length; i += 1) { + realMapList[realMapList.length] = newMapsReal[i]; } } @@ -171,9 +182,47 @@ public final function InjectMaps() { votingHandler = GetVotingHandler(); if (votingHandler != none) { - votingHandler.mapList = outputMapList; - votingHandler.mapCount = outputMapList.length; + votingHandler.mapList = pseudonimMapList; + votingHandler.mapCount = pseudonimMapList.length; + backupMessageMapWon = votingHandler.lmsgMapWon; + backupMessageAdminMapChange = votingHandler.lmsgAdminMapChange; + votingHandler.lmsgMapWon = ACEDIA_MAP_WON_COMMAND $ "::%mapname%"; + votingHandler.lmsgAdminMapChange = ACEDIA_MAP_FORCED_COMMAND $ "::%mapname%"; + _server.unreal.broadcasts.OnHandleText(self).connect = HandleMapChange; + } +} + +private function bool HandleMapChange( + Actor sender, + out string message, + name type, + bool teamMessage +) { + local Parser parser; + local XVotingHandler votingHandler; + + votingHandler = GetVotingHandler(); + if (sender == none) return true; + if (votingHandler != sender) return true; + + parser = _.text.ParseString(message); + parser.Match(P(ACEDIA_MAP_WON_COMMAND)); + parser.Match(P("::")); + if (parser.Ok()) { + message = Repl(backupMessageMapWon, "%mapname%", parser.GetRemainderS()); + } else { + parser.Match(P(ACEDIA_MAP_FORCED_COMMAND)); + parser.Match(P("::")); + if (parser.Ok()) { + message = Repl(backupMessageAdminMapChange, "%mapname%", parser.GetRemainderS()); + } } + if (parser.Ok()) { + votingHandler.mapList = realMapList; + votingHandler.mapCount = realMapList.length; + } + _.memory.Free(parser); + return true; } private function XVotingHandler GetVotingHandler() { @@ -184,4 +233,6 @@ private function XVotingHandler GetVotingHandler() { } defaultproperties { + ACEDIA_MAP_FORCED_COMMAND = "ACEDIA_LAUNCHER:MAP_FORCED:DEADBEEF" + ACEDIA_MAP_WON_COMMAND = "ACEDIA_LAUNCHER:MAP_WON:DEADBEEF" } \ No newline at end of file diff --git a/sources/VotingHandlerAdapter.uc b/sources/VotingHandlerAdapter.uc index e338685..d6e6216 100644 --- a/sources/VotingHandlerAdapter.uc +++ b/sources/VotingHandlerAdapter.uc @@ -145,7 +145,7 @@ public final function InjectIntoVotingHandler() backupVotingHandlerConfig = votingHandler.gameConfig; votingHandler.gameConfig = newVotingHandlerConfig; mapTool.InjectMaps(); - _.memory.Free(mapTool); + //_.memory.Free(mapTool); } private function VotingHandler.MapVoteGameConfig BuildVotingHandlerConfig( -- 2.20.1 From e8cc1421c7a851e42230ac115940bb8fbd2d1cd4 Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Sat, 11 Mar 2023 16:00:19 +0700 Subject: [PATCH 09/11] Add optimizations for shared map sequences Fixes an issue where map list could become bloated, even if every game mode uses the same map set --- sources/GameModes/BaseGameMode.uc | 7 +- sources/GameModes/GameMode.uc | 18 +-- sources/MapList/MapTool.uc | 232 ++++++++++++++++++++++-------- sources/VotingHandlerAdapter.uc | 69 ++++----- 4 files changed, 213 insertions(+), 113 deletions(-) diff --git a/sources/GameModes/BaseGameMode.uc b/sources/GameModes/BaseGameMode.uc index 533fe51..144dede 100644 --- a/sources/GameModes/BaseGameMode.uc +++ b/sources/GameModes/BaseGameMode.uc @@ -10,7 +10,7 @@ * be used based on game info's settings; * 3. `Report...()` methods that perform various validation checks * (and log them) on config data. - * Copyright 2021-2022 Anton Tarasenko + * Copyright 2021-2023 Anton Tarasenko *------------------------------------------------------------------------------ * This file is part of Acedia. * @@ -436,6 +436,11 @@ public function array GetIncludedMapLists() return StringToTextArray(includeMaps); } +public function array GetIncludedMapLists_S() +{ + return includeMaps; +} + defaultproperties { configName = "AcediaGameModes" diff --git a/sources/GameModes/GameMode.uc b/sources/GameModes/GameMode.uc index 1e78bb2..72310db 100644 --- a/sources/GameModes/GameMode.uc +++ b/sources/GameModes/GameMode.uc @@ -1,7 +1,7 @@ /** * The only implementation for `BaseGameMode` suitable for standard * killing floor game types. - * Copyright 2021-2022 Anton Tarasenko + * Copyright 2021-2023 Anton Tarasenko *------------------------------------------------------------------------------ * This file is part of Acedia. * @@ -35,9 +35,6 @@ var protected config string gameTypeClass; // Short version of the name of the game mode players will see in // voting handler messages sometimes (plain string) var protected config string acronym; -// Map prefix - only maps that start with specified prefix will be voteable for -// this game mode (plain string) -var protected config string mapPrefix; // Aliases are an unnecessary overkill for difficulty names, so just define // them in special `string` arrays. @@ -57,7 +54,6 @@ protected function DefaultIt() difficulty = "Hell On Earth"; gameTypeClass = "KFMod.KFGameType"; acronym = ""; - mapPrefix = "KF"; includeFeature.length = 0; excludeFeature.length = 0; includeMutator.length = 0; @@ -76,7 +72,6 @@ protected function HashTable ToData() } result.SetString(P("gameTypeClass"), gameTypeClass); result.SetString(P("acronym"), acronym); - result.SetString(P("mapPrefix"), mapPrefix); nextArray = _.collections.EmptyArrayList(); for (i = 0; i < option.length; i += 1) @@ -105,7 +100,6 @@ protected function FromData(HashTable source) } gameTypeClass = source.GetString(P("gameTypeClass")); acronym = source.GetString(P("acronym")); - mapPrefix = source.GetString(P("mapPrefix")); nextArray = source.GetArrayList(P("option")); if (nextArray == none) { @@ -146,16 +140,6 @@ public function Text GetAcronym() } } -public function Text GetMapPrefix() -{ - if (mapPrefix == "") { - return _.text.FromString("KF-"); - } - else { - return _.text.FromString(mapPrefix); - } -} - /** * Checks option-related settings (`option`) for correctness and reports * any issues. diff --git a/sources/MapList/MapTool.uc b/sources/MapList/MapTool.uc index ba5ec86..b736b70 100644 --- a/sources/MapList/MapTool.uc +++ b/sources/MapList/MapTool.uc @@ -1,5 +1,8 @@ /** - * Copyright 2023 Anton Tarasenko + * Author: Anton Tarasenko + * Home repo: https://insultplayers.ru/git/AcediaFramework/AcediaCore/ + * License: GPL + * Copyright 2023 Anton Tarasenko *------------------------------------------------------------------------------ * This file is part of Acedia. * @@ -18,35 +21,74 @@ */ class MapTool extends AcediaObject; -// Finding voting handler is not cheap, so only do it once and then store it. +//! Tool for adapting AcediaLauncher's map lists for [`xVotingHandler`]. +//! +//! This class is responsible for making [`xVotingHandler`] use specific maps (defined in +//! AcediaLauncer's configs) for specific game modes. +//! To achieve that it abuses [`xVotingHandler`]'s ability to filter maps by +//! game mode-specific prefix. +//! Normally prefix filtering for [`xVotingHandler`] is of limited use, because most Killing Floor +//! maps start with the same prefix `KF-` (and some objective ones starting with `KFO-`). +//! However we swap that prefix for something unique for each game mode: `MapSet0-`, `MapSet1-`, +//! etc, allowing us to pick the precise map set we want. +//! +//! There's two main challenges: +//! +//! 1. *Altered map names break voting* - since [`xVotingHandler`] expects to be provided real map +//! names and our mangled ones. We deal with it by catching a map change message broadcasted +//! right before actual map change occurs and swap our names with real ones. +//! 2. *Increased amount of maps to replicate* - if we implement this name mangling by using +//! a naive approach, in which we separately add maps for every game mode, then it will lead to +//! drastic increase in replication time of the complete map list to players. +//! Consider, for example, that you have 10 different game modes with exactly the same maps: +//! we will be needlessly replicating the exact same thing 10 times! +//! To solve this issue we specifically track map lists other game modes use, along with +//! prefixes assigned to them, and reuse already added maps in case two game modes are defined +//! to use the exactly same ones. + +/// For storing which map sequences have which prefixes. Storage order is important. +struct MapSequenceRecord { + var public array sequence; + var public string prefix; +}; + +// To avoid doing excesive work when injecting maps for a second time +var private bool injectedMaps; + +// Finding voting handler is not cheap, so only do it once and then store it. var private NativeActorRef votingHandlerReference; -// Maps map pseudonims we've used in voting handler to real map names -var private HashTable pseudonimToMap; +// Resulting full map list with pseudonim (with replaced prefixes) and real names of maps. var private array pseudonimMapList; var private array realMapList; -var private int gameModesSeen; +// Map sequences used by game modes we've seen so far. +var private array usedMapSequences; + +// To more easily detect broadcasted message about map change we replace it with our own that is +// both unlikely to occur and is easy to get voted map name from. var private string backupMessageMapWon; var private string backupMessageAdminMapChange; -var private const string ACEDIA_MAP_FORCED_COMMAND; +var private const string ACEDIA_ADMIN_MAP_CHANGE_COMMAND; var private const string ACEDIA_MAP_WON_COMMAND; -protected function Constructor() { - pseudonimToMap = _.collections.EmptyHashTable(); -} +var private LoggerAPI.Definition fatVotingHandlerMissing, warnMissingMapList; protected function Finalizer() { _server.unreal.broadcasts.OnHandleText(self).Disconnect(); _.memory.Free(votingHandlerReference); - _.memory.Free(pseudonimToMap); votingHandlerReference = none; - pseudonimToMap = none; pseudonimMapList.length = 0; realMapList.length = 0; - gameModesSeen = 0; + usedMapSequences.length = 0; + injectedMaps = false; } -public function bool Initialize(NativeActorRef initVotingHandlerReference) { +/// Initializes [`MapTool`] by associating it with an [`XVotingHandler`]. +/// +/// Initialization fails if [`initVotingHandlerReference`] doesn't provide reference to +/// [`XVotingHandler`] or caller [`MapTool`] already was initialized. +/// Returns `true` iff initialization was successful. +public final function bool Initialize(NativeActorRef initVotingHandlerReference) { if (initVotingHandlerReference == none) return false; if (XVotingHandler(initVotingHandlerReference.Get()) == none) return false; @@ -55,47 +97,121 @@ public function bool Initialize(NativeActorRef initVotingHandlerReference) { return true; } -public function string LoadGameModeMaps(GameMode gameMode) { - local int i; +/// Adds map information from the new game mode. +/// +/// Returns prefix that given game mode must use to display maps configured for it. +public final function string AddGameMode(GameMode gameMode) { + local XVotingHandler votingHandler; + local string gameModePrefix; + + votingHandler = GetVotingHandler(); + if (votingHandler == none) { + _.logger.Auto(fatVotingHandlerMissing); + return "KF"; + } + if (CheckNeedToLoadMaps(gameMode, gameModePrefix)) { + LoadGameModeMaps(gameMode, gameModePrefix, votingHandler); + } + return gameModePrefix; +} + +/// Injects final map list into [`XVotingHandler`]. +/// +/// Call this after all game modes have been added. +/// Shouldn't be called more than once. +public final function Inject() { local XVotingHandler votingHandler; + + votingHandler = GetVotingHandler(); + if (votingHandler == none) { + _.logger.Auto(fatVotingHandlerMissing); + return; + } + votingHandler.mapList = pseudonimMapList; + votingHandler.mapCount = pseudonimMapList.length; + // Replace map change messages with our commands and make sure it is done only once, + // in case we mess up somewhere else and call this method second time + if (!injectedMaps) { + backupMessageMapWon = votingHandler.lmsgMapWon; + backupMessageAdminMapChange = votingHandler.lmsgAdminMapChange; + votingHandler.lmsgMapWon = ACEDIA_MAP_WON_COMMAND $ "::%mapname%"; + votingHandler.lmsgAdminMapChange = ACEDIA_ADMIN_MAP_CHANGE_COMMAND $ "::%mapname%"; + _server.unreal.broadcasts.OnHandleText(self).connect = HandleMapChange; + } + injectedMaps = true; +} + +/// Builds arrays of [`VotingHandler::MapVoteMapList`] (each such item describes a map + +/// its meta data in a way [`XVotingHandler`] understands). +private function string LoadGameModeMaps( + GameMode gameMode, + string gameModePrefix, + XVotingHandler votingHandler +) { + local int i; local ArrayList gameModeMaps; local Text mapNameReal, mapNamePseudonim; local VotingHandler.MapHistoryInfo nextMapInfo; local VotingHandler.MapVoteMapList nextRecord; local array newMapsPseudonim, newMapReal; - local string gameModePrefix; - votingHandler = GetVotingHandler(); - if (votingHandler == none) { - return "!!!"; - } - gameModePrefix = ("KF" $ gameModesSeen); nextRecord.bEnabled = true; gameModeMaps = GetAllGameModeMaps(gameMode); for (i = 0; i < gameModeMaps.GetLength(); i += 1) { - // Make a pseudonim to map connection mapNameReal = gameModeMaps.GetText(i); mapNamePseudonim = MakeMapPseudonim(mapNameReal, gameModePrefix); - pseudonimToMap.SetItem(mapNamePseudonim, mapNameReal); - // Setup `VotingHandler.MapVoteMapList` struct for next map if (votingHandler.history != none) { nextMapInfo = votingHandler.history.GetMapHistory(mapNameReal.ToString()); nextRecord.playCount = nextMapInfo.p; nextRecord.sequence = nextMapInfo.s; - } else { - nextRecord.playCount = 0; - nextRecord.sequence = 0; } - nextRecord.mapName = mapNamePseudonim.ToString(); + nextRecord.mapName = _.text.IntoString(mapNamePseudonim); newMapsPseudonim[newMapsPseudonim.length] = nextRecord; - nextRecord.mapName = mapNameReal.ToString(); + nextRecord.mapName = _.text.IntoString(mapNameReal); newMapReal[newMapReal.length] = nextRecord; } - AppendMaps(newMapsPseudonim, newMapReal); - gameModesSeen += 1; + AppendMapsIntoVotingHandler(newMapsPseudonim, newMapReal); + _.memory.Free(gameModeMaps); return gameModePrefix; } +private function bool CheckNeedToLoadMaps(GameMode gameMode, out string prefix) { + local int mapSequenceIndex, mapListIndex; + local bool sameMapList, foundMatch; + local array existingMapSequence, newMapSequence; + local MapSequenceRecord newRecord; + + // We don't need to load maps for the `gameMode` only when we've already added the exactly same + // map sequence, order being important + newMapSequence = gameMode.GetIncludedMapLists_S(); + for (mapSequenceIndex = 0; mapSequenceIndex < usedMapSequences.length; mapSequenceIndex += 1) { + existingMapSequence = usedMapSequences[mapSequenceIndex].sequence; + if (existingMapSequence.length != newMapSequence.length) { + continue; + } + foundMatch = true; + for (mapListIndex = 0; mapListIndex < newMapSequence.length; mapListIndex += 1) { + // Map lists are ASCII config names, so we can compare them with case-ignoring + // built-in `~=` operator works (it can only handle properly ASCII input) + sameMapList = (existingMapSequence[mapListIndex] ~= newMapSequence[mapListIndex]); + if (!sameMapList) { + foundMatch = false; + break; + } + } + if (foundMatch) { + prefix = usedMapSequences[mapSequenceIndex].prefix; + return false; + } + } + newRecord.sequence = newMapSequence; + newRecord.prefix = "MapSet" $ usedMapSequences.length; + usedMapSequences[usedMapSequences.length] = newRecord; + prefix = newRecord.prefix; + return true; +} + +// Replaces prefixes like "KF-", "KFO-" or "KFS-" with "{gameModePrefix}-". private function Text MakeMapPseudonim(Text realName, string gameModePrefix) { local Parser parser; local MutableText prefix, nameBody; @@ -124,26 +240,17 @@ private function ArrayList GetAllGameModeMaps(GameMode gameMode) { local HashTable uniqueMapSet; local ArrayList result; local array usedMapLists; - local MapList nextMapConfig; local array nextMapArray; local Text nextMapName, lowerMapName; - uniqueMapSet = _.collections.EmptyHashTable(); // to quickly make sure we add each map only once + uniqueMapSet = _.collections.EmptyHashTable(); // for testing map name uniqueness result = _.collections.EmptyArrayList(); usedMapLists = gameMode.GetIncludedMapLists(); for (i = 0; i < usedMapLists.length; i += 1) { - // Get maps from `MapList` config - nextMapConfig = MapList(class'MapList'.static.GetConfigInstance(usedMapLists[i])); - nextMapArray.length = 0; - if (nextMapConfig != none) { - nextMapArray = nextMapConfig.map; - } else { - //_.logger.Auto(warnMissingMapList).Arg(usedMapLists[i].Copy()); - } - _.memory.Free(nextMapConfig); - // Add maps we haven't yet added from other lists + nextMapArray = GetMapNameFromConfig(usedMapLists[i]); for (j = 0; j < nextMapArray.length; j += 1) { nextMapName = _.text.FromString(nextMapArray[j]); + // Use lower case version of map name for uniqueness testing to ignore characters' case lowerMapName = nextMapName.LowerCopy(); if (!uniqueMapSet.HasKey(lowerMapName)) { uniqueMapSet.SetItem(lowerMapName, none); @@ -158,7 +265,21 @@ private function ArrayList GetAllGameModeMaps(GameMode gameMode) { return result; } -private function AppendMaps( +private function array GetMapNameFromConfig(Text configName) { + local MapList mapConfig; + local array result; + + mapConfig = MapList(class'MapList'.static.GetConfigInstance(configName)); + if (mapConfig == none) { + _.logger.Auto(warnMissingMapList).Arg(configName.Copy()); + } else { + result = mapConfig.map; + _.memory.Free(mapConfig); + } + return result; +} + +private function AppendMapsIntoVotingHandler( array newMapsPseudonim, array newMapsReal) { local int i; @@ -166,7 +287,7 @@ private function AppendMaps( votingHandler = GetVotingHandler(); if (votingHandler == none) { - warn("votingHandler is none!"); + _.logger.Auto(fatVotingHandlerMissing); return; } for (i = 0; i < newMapsPseudonim.length; i += 1) { @@ -177,21 +298,6 @@ private function AppendMaps( } } -public final function InjectMaps() { - local XVotingHandler votingHandler; - - votingHandler = GetVotingHandler(); - if (votingHandler != none) { - votingHandler.mapList = pseudonimMapList; - votingHandler.mapCount = pseudonimMapList.length; - backupMessageMapWon = votingHandler.lmsgMapWon; - backupMessageAdminMapChange = votingHandler.lmsgAdminMapChange; - votingHandler.lmsgMapWon = ACEDIA_MAP_WON_COMMAND $ "::%mapname%"; - votingHandler.lmsgAdminMapChange = ACEDIA_MAP_FORCED_COMMAND $ "::%mapname%"; - _server.unreal.broadcasts.OnHandleText(self).connect = HandleMapChange; - } -} - private function bool HandleMapChange( Actor sender, out string message, @@ -211,7 +317,7 @@ private function bool HandleMapChange( if (parser.Ok()) { message = Repl(backupMessageMapWon, "%mapname%", parser.GetRemainderS()); } else { - parser.Match(P(ACEDIA_MAP_FORCED_COMMAND)); + parser.Match(P(ACEDIA_ADMIN_MAP_CHANGE_COMMAND)); parser.Match(P("::")); if (parser.Ok()) { message = Repl(backupMessageAdminMapChange, "%mapname%", parser.GetRemainderS()); @@ -233,6 +339,8 @@ private function XVotingHandler GetVotingHandler() { } defaultproperties { - ACEDIA_MAP_FORCED_COMMAND = "ACEDIA_LAUNCHER:MAP_FORCED:DEADBEEF" + ACEDIA_ADMIN_MAP_CHANGE_COMMAND = "ACEDIA_LAUNCHER:ADMIN_MAP_CHANGE:DEADBEEF" ACEDIA_MAP_WON_COMMAND = "ACEDIA_LAUNCHER:MAP_WON:DEADBEEF" + fatVotingHandlerMissing = (l=LOG_Fatal,m="No voting `XVotingHandler` available. This is unexpected at this stage. Report this issue.") + warnMissingMapList = (l=LOG_Warning,m="Cannot find map list `%1`.") } \ No newline at end of file diff --git a/sources/VotingHandlerAdapter.uc b/sources/VotingHandlerAdapter.uc index d6e6216..fcd8a3f 100644 --- a/sources/VotingHandlerAdapter.uc +++ b/sources/VotingHandlerAdapter.uc @@ -60,25 +60,28 @@ class VotingHandlerAdapter extends AcediaObject // the same index. var private array availableGameModes; -// Finding voting handler is not cheap, so only do it once and then store it. -var private NativeActorRef votingHandlerReference; -// Save `VotingHandler`'s config to restore it before server travel - -// otherwise Acedia will alter its config -var private array backupVotingHandlerConfig; +// Finding voting handler is not cheap, so only do it once and then store it. +var private NativeActorRef votingHandlerReference; +// Save `VotingHandler`'s config to restore it before server travel - otherwise Acedia will alter +// its config +var private array backupVotingHandlerConfig; +// Map list management logic +var private MapTool mapTool; // Setting value of this flag to `true` indicates that map switching just // occurred and we need to recover some information from the previous map. -var private config bool isServerTraveling; +var private config bool isServerTraveling; // We should not rely on "VotingHandler" to inform us from which game mode its // selected config option originated after server travel, so we need to // remember it in this config variable before switching maps. -var private config string targetGameMode; +var private config string targetGameMode; // Acedia's game modes intend on supporting difficulty switching, but // `KFGameType` does not support appropriate flags, so we enforce default // difficulty by overwriting default value of its `gameDifficulty` variable. // But to not affect game's configs we must restore old value after new map is // loaded. Store it in config variable for that. -var private config int storedGameLength; +var private config int storedGameLength; + // Aliases are an unnecessary overkill for difficulty names, so just define // them in special `string` arrays. @@ -90,12 +93,17 @@ var private const array longSynonyms; var private LoggerAPI.Definition fatNoXVotingHandler, fatBadGameConfigIndexVH; var private LoggerAPI.Definition fatBadGameConfigIndexAdapter, warnMissingMapList; -protected function Finalizer() -{ +protected function Constructor() { + mapTool = MapTool(_.memory.Allocate(class'MapTool')); +} + +protected function Finalizer() { + _.memory.Free(mapTool); _.memory.Free(votingHandlerReference); _.memory.FreeMany(availableGameModes); - votingHandlerReference = none; - availableGameModes.length = 0; + mapTool = none; + votingHandlerReference = none; + availableGameModes.length = 0; } /** @@ -105,33 +113,30 @@ protected function Finalizer() */ public final function InjectIntoVotingHandler() { - local int i; - local string nextGameModePrefix; - local GameMode nextGameMode; - local XVotingHandler votingHandler; - local array newVotingHandlerConfig; - local MapTool mapTool; + local int i; + local string nextGameModePrefix; + local GameMode nextGameMode; + local XVotingHandler votingHandler; + local array newVotingHandlerConfig; + // `votingHandlerReference != none` means that we've already injected into voting handler if (votingHandlerReference != none) { return; } votingHandler = XVotingHandler(_server.unreal.FindActorInstance( - _server.unreal.GetGameType().VotingHandlerClass)); - if (votingHandler == none) - { + _server.unreal.GetGameType().votingHandlerClass)); + if (votingHandler == none) { _.logger.Auto(fatNoXVotingHandler); return; } votingHandlerReference = _server.unreal.ActorRef(votingHandler); - mapTool = MapTool(_.memory.Allocate(class'MapTool')); - mapTool.Initialize(votingHandlerReference); //TODO check return value + // This cannot actuall fail at this point - we have valid `votingHandler` reference and + // `mapTool` is only initialized here (which can be executed only once) + mapTool.Initialize(votingHandlerReference); availableGameModes = class'GameMode'.static.AvailableConfigs(); - votingHandler.mapCount = 0; - votingHandler.mapList.length = 0; for (i = 0; i < availableGameModes.length; i += 1) { - nextGameMode = GameMode(class'GameMode'.static - .GetConfigInstance(availableGameModes[i])); - nextGameModePrefix = mapTool.LoadGameModeMaps(nextGameMode); + nextGameMode = GameMode(class'GameMode'.static.GetConfigInstance(availableGameModes[i])); + nextGameModePrefix = mapTool.AddGameMode(nextGameMode); newVotingHandlerConfig[i] = BuildVotingHandlerConfig(nextGameMode, nextGameModePrefix); // Setup proper game mode index if (availableGameModes[i].ToString() == targetGameMode) { @@ -142,10 +147,9 @@ public final function InjectIntoVotingHandler() nextGameMode.ReportBadOptions(); _.memory.Free(nextGameMode); } - backupVotingHandlerConfig = votingHandler.gameConfig; - votingHandler.gameConfig = newVotingHandlerConfig; - mapTool.InjectMaps(); - //_.memory.Free(mapTool); + backupVotingHandlerConfig = votingHandler.gameConfig; + votingHandler.gameConfig = newVotingHandlerConfig; + mapTool.Inject(); } private function VotingHandler.MapVoteGameConfig BuildVotingHandlerConfig( @@ -370,7 +374,6 @@ defaultproperties normalSynonyms(1) = "medium" normalSynonyms(2) = "regular" longSynonyms(0) = "long" - warnMissingMapList = (l=LOG_Warning,m="Cannot find map list `%1`.") fatNoXVotingHandler = (l=LOG_Fatal,m="`XVotingHandler` class is missing. Make sure your server setup supports Acedia's game modes (by used voting handler derived from `XVotingHandler`).") fatBadGameConfigIndexVH = (l=LOG_Fatal,m="`XVotingHandler`'s `currentGameConfig` variable value of %1 is out-of-bounds for `XVotingHandler.gameConfig` of length %2. Report this issue.") fatBadGameConfigIndexAdapter = (l=LOG_Fatal,m="`XVotingHandler`'s `currentGameConfig` variable value of %1 is out-of-bounds for `VHAdapter` of length %2. Report this issue.") -- 2.20.1 From b427acf0df6974210cb58eddea6d95f8e56eff61 Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Sat, 11 Mar 2023 16:30:47 +0700 Subject: [PATCH 10/11] Change configs for changes --- config/AcediaGameModes.ini | 43 +++++++++++++++++++++++++++++++++- config/AcediaMaps.ini | 44 ++++++++++++++++++++++++++++++----- sources/GameModes/GameMode.uc | 1 - 3 files changed, 80 insertions(+), 8 deletions(-) diff --git a/config/AcediaGameModes.ini b/config/AcediaGameModes.ini index 951715e..c71b0fe 100644 --- a/config/AcediaGameModes.ini +++ b/config/AcediaGameModes.ini @@ -1,7 +1,48 @@ [hard GameMode] -title={$green Hard difficulty} +;= Add a section like this one for every voting option +;= +;= `title` will be displayed in a drop-down list in the voting dialog +title={$green Hard} +;= `difficulty` determines... difficulty and for vanilla Killing Floor can be one of +;= the following values: +;= +;= * Beginner: "easy", "beginner" +;= * Normal: "normal", "default", "regular" +;= * Hard: "harder" +;= * Suicidal: "suicidal" +;= * Hell On Earth: "hellonearth", "hell on earth", "hoe" +;= +;= Any prefixes will also work, e.g. "sui", "hard", etc. difficulty=hard +;= How long should game last? For vanilla Killing Floor its "short", +;= "normal" (also "default" and "regular"), "long". +length=long +;= A short game mode name that will be displayed after map vote has finished +acronym={$green hard} +;= Use this to add map lists (from "AcediaMaps.ini") to this game mode +;= NOTE: map lists, NOT maps +includeMaps=default +;= Use this to add mutators to this game mode, one mutator per line +;includeMutator= +;= Use this do add one of Acedia's Features into this game mode with "default" config +;includeFeature= +;= Use this to add feature with specified config +;includeFeatureAs=(feature=,config=) +;= Use this to disable one of the Acedia's features for this game mode. +;= This overrides all other settings. +;= Purpose of this setting is to disable auto-enabled features. +;excludeFeature= + +[sui GameMode] +title={$orange Suicidal} +difficulty=suicidal +length=long +acronym={$orange sui} +includeMaps=default [hell GameMode] title={$crimson Hell On Earth} difficulty=hoe +length=long +acronym={$crimson hoe} +includeMaps=default \ No newline at end of file diff --git a/config/AcediaMaps.ini b/config/AcediaMaps.ini index c0ed975..9d1d898 100644 --- a/config/AcediaMaps.ini +++ b/config/AcediaMaps.ini @@ -1,7 +1,39 @@ [default MapList] -autoEnable=true -map="KF-BioticsLab" -map="KF-Farm" -map="KF-Manor" -map="KF-Offices" -map="KF-WestLondon" +map=KF-AbusementPark +map=KF-Aperture +map=KF-Bedlam +map=KF-Biohazard +map=KF-BioticsLab +map=KF-Clandestine +map=KF-Crash +map=KF-Departed +map=KF-EvilSantasLair +map=KF-Farm +map=KF-FilthsCross +map=KF-Forgotten +map=KF-Foundry +map=KF-FrightYard +map=KF-Hell +map=KF-Hellride +map=KF-HillbillyHorror +map=KF-Hospitalhorrors +map=KF-Icebreaker +map=KF-IceCave +map=KF-Manor +map=KF-MoonBase +map=KF-MountainPass +map=KF-Offices +map=KF-SirensBelch +map=KF-Steamland +map=KF-Stronghold +map=KF-Suburbia +map=KF-ThrillsChills +map=KF-Transit +map=KF-Waterworks +map=KF-WestLondon +map=KF-Wyre + +[objective MapList] +map=KFO-FrightYard +map=KFO-Steamland +map=KFO-Transit \ No newline at end of file diff --git a/sources/GameModes/GameMode.uc b/sources/GameModes/GameMode.uc index 72310db..faab86a 100644 --- a/sources/GameModes/GameMode.uc +++ b/sources/GameModes/GameMode.uc @@ -257,7 +257,6 @@ defaultproperties hardSynonyms(0) = "harder" // "hard" is prefix of this, so it will count hardSynonyms(1) = "difficult" suicidalSynonyms(0) = "suicidal" - suicidalSynonyms(1) = "sui" hoeSynonyms(0) = "hellonearth" hoeSynonyms(1) = "hellon earth" hoeSynonyms(2) = "hell onearth" -- 2.20.1 From 6a21ceb2bcb2a5a551f4312bf7519147c064808f Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Sat, 11 Mar 2023 16:38:16 +0700 Subject: [PATCH 11/11] Fix wrong default in `DefaultIt()` for `MapList` --- sources/MapList/MapList.uc | 39 +++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/sources/MapList/MapList.uc b/sources/MapList/MapList.uc index c9f0e7a..6ff5a19 100644 --- a/sources/MapList/MapList.uc +++ b/sources/MapList/MapList.uc @@ -58,11 +58,40 @@ protected function FromData(HashTable source) { } protected function DefaultIt() { - map[0] = "KF-BioticsLab"; - map[1] = "KF-Farm"; - map[2] = "KF-Manor"; - map[3] = "KF-Offices"; - map[4] = "KF-WestLondon"; + map.length = 0; + map[0] = "KF-AbusementPark"; + map[1] = "KF-Aperture"; + map[2] = "KF-Bedlam"; + map[3] = "KF-Biohazard"; + map[4] = "KF-BioticsLab"; + map[5] = "KF-Clandestine"; + map[6] = "KF-Crash"; + map[7] = "KF-Departed"; + map[8] = "KF-EvilSantasLair"; + map[9] = "KF-Farm"; + map[10] = "KF-FilthsCross"; + map[11] = "KF-Forgotten"; + map[12] = "KF-Foundry"; + map[13] = "KF-FrightYard"; + map[14] = "KF-Hell"; + map[15] = "KF-Hellride"; + map[16] = "KF-HillbillyHorror"; + map[17] = "KF-Hospitalhorrors"; + map[18] = "KF-Icebreaker"; + map[19] = "KF-IceCave"; + map[20] = "KF-Manor"; + map[21] = "KF-MoonBase"; + map[22] = "KF-MountainPass"; + map[23] = "KF-Offices"; + map[24] = "KF-SirensBelch"; + map[25] = "KF-Steamland"; + map[26] = "KF-Stronghold"; + map[27] = "KF-Suburbia"; + map[28] = "KF-ThrillsChills"; + map[29] = "KF-Transit"; + map[30] = "KF-Waterworks"; + map[31] = "KF-WestLondon"; + map[32] = "KF-Wyre"; } defaultproperties { -- 2.20.1