diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..80643f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.psd +/ignore \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..27ed978 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tools"] + path = tools + url = https://github.com/GenZmeY/KF2-BuildTools diff --git a/PublicationContent/description.txt b/PublicationContent/description.txt new file mode 100644 index 0000000..3568c0d --- /dev/null +++ b/PublicationContent/description.txt @@ -0,0 +1,8 @@ +[h1]KF2-SafeMutLoader[/h1] + +[h1]Description[/h1] +[b]Mutator(s):[/b] SML.Mut + +[h1]Author[/h1] +[url=https://github.com/GenZmeY]GenZmeY[/url] + diff --git a/PublicationContent/preview.png b/PublicationContent/preview.png new file mode 100644 index 0000000..7fb463e Binary files /dev/null and b/PublicationContent/preview.png differ diff --git a/PublicationContent/tags.txt b/PublicationContent/tags.txt new file mode 100644 index 0000000..bcf8fe8 --- /dev/null +++ b/PublicationContent/tags.txt @@ -0,0 +1 @@ +Mutators diff --git a/PublicationContent/title.txt b/PublicationContent/title.txt new file mode 100644 index 0000000..e2230aa --- /dev/null +++ b/PublicationContent/title.txt @@ -0,0 +1 @@ +KF2-SafeMutLoader diff --git a/SML/Classes/Mut.uc b/SML/Classes/Mut.uc new file mode 100644 index 0000000..cccc3c0 --- /dev/null +++ b/SML/Classes/Mut.uc @@ -0,0 +1,110 @@ +class Mut extends KFMutator; + +const SML = class'SafeMutLoader'; + +const OptAC = "AccessControl"; +const OptMut = "Mutator"; + +var private E_LogLevel LogLevel; + +public function PreBeginPlay() +{ + Super.PreBeginPlay(); + + LogLevel = SML.default.LogLevel; + if (LogLevel == LL_WrongLevel) + { + LogLevel = LL_Info; + } + + `Log_Trace(); + + if (CorrectLoadOrder()) + { + ModifyLoad(); + } + else + { + `Log_Fatal(SML.static.GetName(Self) @ "must be loaded first."); + } +} + +public function AddMutator(Mutator Mut) +{ + `Log_Trace(); + + if (CorrectLoadOrder() || Mut == Self) return; + + if (Mut.Class == Class) + Mut.Destroy(); + else + Super.AddMutator(Mut); +} + +private function bool CorrectLoadOrder() +{ + `Log_Trace(); + + return ( + WorldInfo.Game.BaseMutator == None || + WorldInfo.Game.BaseMutator == Self); +} + +private function ModifyLoad() +{ + local String LoadURL; + local String LoadParams; + local String MutatorsRaw; + local String AccessControlRaw; + local Array Mutators; + local int Index; + + `Log_Trace(); + + LoadURL = WorldInfo.GetLocalURL(); + LoadParams = Mid(LoadURL, InStr(LoadURL, "?")); + MutatorsRaw = WorldInfo.Game.ParseOption(LoadParams, OptMut); + AccessControlRaw = WorldInfo.Game.ParseOption(LoadParams, OptAC); + + LoadURL = Repl(LoadURL, Subst(OptMut) $ MutatorsRaw, ""); + LoadURL = Repl(LoadURL, Subst(OptAC) $ AccessControlRaw, ""); + + SML.static.ClearMutators(); + ParseStringIntoArray(MutatorsRaw, Mutators, ",", true); + + Index = 0; + while (Index < Mutators.Length) + { + if (SML.static.AddMutator(Mutators[Index]) || + Mutators[Index] ~= SML.static.GetName(Self)) + { + Mutators.Remove(Index, 1); + } + else + { + ++Index; + } + } + + JoinArray(Mutators, MutatorsRaw); + LoadURL $= (Subst(OptMut) $ MutatorsRaw); + if (SML.static.WantsToSpawn()) + { + SML.static.StaticSaveConfig(); + LoadURL $= (Subst(OptAC) $ SML.static.GetName()); + } + + `Log_Info("Loader modified, do server travel..."); + + WorldInfo.ServerTravel(LoadURL, true); +} + +private static function String Subst(String Option) +{ + return ("?" $ Option $ "="); +} + +defaultproperties +{ + +} \ No newline at end of file diff --git a/SML/Classes/SafeMutLoader.uc b/SML/Classes/SafeMutLoader.uc new file mode 100644 index 0000000..94a5422 --- /dev/null +++ b/SML/Classes/SafeMutLoader.uc @@ -0,0 +1,148 @@ +class SafeMutLoader extends KFAccessControl + config(SML); + +var private Array ServerActors; +var public config E_LogLevel LogLevel; +var private config Array Mutators; + +public function PreBeginPlay() +{ + `Log_Trace(); + + if (LogLevel == LL_WrongLevel) + { + LogLevel = LL_Info; + StaticSaveConfig(); + } + + LoadActors(); + + Super.PreBeginPlay(); +} + +private function LoadActors() +{ + local String MutString; + local class MutClass; + local class ActClass; + local Actor ServerActor; + + `Log_Trace(); + + foreach Mutators(MutString) + { + MutClass = class(DynamicLoadObject(MutString, class'Class')); + if (MutClass == None) + { + `Log_Error("Can't load mutator:" @ MutString); + continue; + } + + ActClass = GetMutReplacement(MutClass); + if (ActClass == None) + { + `Log_Warn("Incompatible:" @ MutString @ "(skip)"); + continue; + } + + ServerActor = WorldInfo.Spawn(ActClass); + if (ServerActor == None) + { + `Log_Error("Can't spawn:" @ MutString); + continue; + } + + ServerActors.AddItem(ServerActor); + `Log_Info("Loaded:" @ MutString); + } +} + +public static function bool AddMutator(String MutString) +{ + if (GetMutStringReplacement(MutString) != None) + { + if (default.Mutators.Find(MutString) == INDEX_NONE) + { + default.Mutators.AddItem(MutString); + } + return true; + } + + return false; +} + +public static function ClearMutators() +{ + default.Mutators.Length = 0; +} + +public static function bool WantsToSpawn() +{ + return (default.Mutators.Length > 0); +} + +public static function String GetName(optional Object O) +{ + if (O == None) + { + return (default.class.GetPackageName() $ "." $ String(default.class)); + } + else + { + return (O.class.GetPackageName() $ "." $ String(O.class)); + } +} + +public function PostLogin(PlayerController C) +{ + local Actor A; + + `Log_Trace(); + + if (C != None) + { + foreach ServerActors(A) + { + A.GetTargetLocation(C, false); + } + } + + Super.PostLogin(C); +} + +public function OnClientConnectionClose(Player ClientConnection) +{ + local Controller C; + local Actor A; + + `Log_Trace(); + + C = ClientConnection.Actor; + if (C != None) + { + foreach ServerActors(A) + { + A.GetTargetLocation(C, true); + } + } + + Super.OnClientConnectionClose(ClientConnection); +} + +private static function class GetMutReplacement(class MutClass) +{ + if (MutClass == None || MutClass.static.GetLocalString() == "") return None; + return class(DynamicLoadObject( + MutClass.GetPackageName() $ "." $ + MutClass.static.GetLocalString(), class'Class')); +} + +private static function class GetMutStringReplacement(String MutString) +{ + return GetMutReplacement(class(DynamicLoadObject(MutString, class'Class'))); +} + +defaultproperties +{ + +} \ No newline at end of file diff --git a/SML/Classes/SafeMutLoader.upkg b/SML/Classes/SafeMutLoader.upkg new file mode 100644 index 0000000..7d148dd --- /dev/null +++ b/SML/Classes/SafeMutLoader.upkg @@ -0,0 +1,4 @@ +[Flags] +AllowDownload=False +ClientOptional=False +ServerSideOnly=True diff --git a/SML/Classes/_Logger.uc b/SML/Classes/_Logger.uc new file mode 100644 index 0000000..93fc28a --- /dev/null +++ b/SML/Classes/_Logger.uc @@ -0,0 +1,20 @@ +class _Logger extends Object + abstract; + +enum E_LogLevel +{ + LL_WrongLevel, + LL_None, + LL_Fatal, + LL_Error, + LL_Warning, + LL_Info, + LL_Debug, + LL_Trace, + LL_All +}; + +defaultproperties +{ + +} diff --git a/SML/Constants.uci b/SML/Constants.uci new file mode 100644 index 0000000..1003f19 --- /dev/null +++ b/SML/Constants.uci @@ -0,0 +1,2 @@ +// Constants +`define NO_CONFIG 0 diff --git a/SML/Globals.uci b/SML/Globals.uci new file mode 100644 index 0000000..a48ac52 --- /dev/null +++ b/SML/Globals.uci @@ -0,0 +1,3 @@ +// Imports +`include(Logger.uci) +`include(Constants.uci) diff --git a/SML/Logger.uci b/SML/Logger.uci new file mode 100644 index 0000000..076215a --- /dev/null +++ b/SML/Logger.uci @@ -0,0 +1,15 @@ +// Logger +`define Log_Tag 'SML' + +`define LocationStatic "`{ClassName}::" $ GetFuncName() + +`define Log_Base(msg, cond) `log(`msg `if(`cond), `cond`{endif}, `Log_Tag) + +`define Log_Fatal(msg) `log("FATAL:" @ `msg, (LogLevel >= LL_Fatal), `Log_Tag) +`define Log_Error(msg) `log("ERROR:" @ `msg, (LogLevel >= LL_Error), `Log_Tag) +`define Log_Warn(msg) `log("WARN:" @ `msg, (LogLevel >= LL_Warning), `Log_Tag) +`define Log_Info(msg) `log("INFO:" @ `msg, (LogLevel >= LL_Info), `Log_Tag) +`define Log_Debug(msg) `log("DEBUG:" @ `msg, (LogLevel >= LL_Debug), `Log_Tag) + +`define Log_Trace(msg) `log("TRACE:" @ `Location `if(`msg) @ `msg`{endif}, (LogLevel >= LL_Trace), `Log_Tag) +`define Log_TraceStatic(msg) `log("TRACE:" @ `LocationStatic `if(`msg) @ `msg`{endif}, (LogLevel >= LL_Trace), `Log_Tag) diff --git a/builder.cfg b/builder.cfg new file mode 100644 index 0000000..2a62a4f --- /dev/null +++ b/builder.cfg @@ -0,0 +1,61 @@ +### Build parameters ### + +# If True - compresses the mutator when compiling +# Scripts will be stored in binary form +# (reduces the size of the output file) +StripSource="True" + +# Mutators to be compiled +# Specify them with a space as a separator, +# Mutators will be compiled in the specified order +PackageBuildOrder="SML" + + +### Brew parameters ### + +# Packages you want to brew using @peelz's patched KFEditor. +# Useful for cases where regular brew doesn't put *.upk inside the package. +# Specify them with a space as a separator, +# The order doesn't matter +PackagePeelzBrew="" + + +### Steam Workshop upload parameters ### + +# Mutators that will be uploaded to the workshop +# Specify them with a space as a separator, +# The order doesn't matter +PackageUpload="SML" + + +### Test parameters ### + +# Map: +Map="KF-Nuked" + +# Game: +# Survival: KFGameContent.KFGameInfo_Survival +# WeeklyOutbreak: KFGameContent.KFGameInfo_WeeklySurvival +# Endless: KFGameContent.KFGameInfo_Endless +# Objective: KFGameContent.KFGameInfo_Objective +# Versus: KFGameContent.KFGameInfo_VersusSurvival +Game="KFGameContent.KFGameInfo_Survival" + +# Difficulty: +# Normal: 0 +# Hard: 1 +# Suicide: 2 +# Hell: 3 +Difficulty="0" + +# GameLength: +# 4 waves: 0 +# 7 waves: 1 +# 10 waves: 2 +GameLength="0" + +# Mutators +Mutators="SML.Mut" + +# Additional parameters +Args="" diff --git a/tools b/tools new file mode 160000 index 0000000..3efdb9f --- /dev/null +++ b/tools @@ -0,0 +1 @@ +Subproject commit 3efdb9f4d13cb730264cf17498ed1e1ee908934c