diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f7c1047..af511aa 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -156,7 +156,7 @@ { "label": "join server", "type": "shell", - "command": "ucx ut unreal://127.0.0.1:7777 -log", + "command": "ucx ut unreal://127.0.0.1:7777", "problemMatcher": [], "presentation": { "echo": true, diff --git a/MVES/Classes/MV_Parser.uc b/MVES/Classes/MV_Parser.uc index c781fc3..6302afe 100644 --- a/MVES/Classes/MV_Parser.uc +++ b/MVES/Classes/MV_Parser.uc @@ -2,6 +2,7 @@ class MV_Parser expands MV_Util abstract; const EMPTY_STRING = ""; +// Full functional parse function splits off a token from the input, returns it separately static function bool TrySplit(string input, string separator, out string first, out string rest) { local int pos; @@ -24,7 +25,7 @@ static function bool TrySplit(string input, string separator, out string first, else if (pos == -1) { first = input; - rest = ""; + rest = EMPTY_STRING; return True; } } diff --git a/MVES/Classes/MV_Result.uc b/MVES/Classes/MV_Result.uc index 2755f8e..3d0720b 100644 --- a/MVES/Classes/MV_Result.uc +++ b/MVES/Classes/MV_Result.uc @@ -31,6 +31,10 @@ var int SettingsCount; var string SettingsKey[256]; var string SettingsValue[256]; +const MaxUrlParameters = 256; +var int UrlParametersCount; +var string UrlParametersKey[256], UrlParametersValue[256]; + var LevelInfo LevelInfo; var bool LevelInfoCached; @@ -119,11 +123,14 @@ function string GetWrappedPackages() function bool AddMutators(string list) { local string mutator; + local bool success; + success = True; while ( class'MV_Parser'.static.TrySplit(list, ",", mutator, list) ) - { - AddMutator(mutator); - } + if ( !AddMutator(mutator) ) + success = False; + + return success; } function bool AddMutator(string mutator) @@ -149,11 +156,14 @@ function bool AddMutator(string mutator) function bool AddActors(string list) { local string actor; + local bool success; + success = True; while ( class'MV_Parser'.static.TrySplit(list, ",", actor, list) ) - { - AddActor(actor); - } + if ( !AddActor(actor) ) + success = False; + + return success; } function bool AddActor(string actor) @@ -181,19 +191,74 @@ function bool AddGameSettings(string settingsList) local string keyvalue; local string key; local string value; - local bool found; + local bool success; + success = True; while ( class'MV_Parser'.static.TrySplit(settingsList, ",", keyvalue, settingsList) ) { if ( class'MV_Parser'.static.TrySplit(keyvalue, "=", key, value) ) { - UpdateSingleGameSetting(key, value); + if ( !UpdateSingleGameSetting(key, value) ) + { + success = False; + } } else { Err("Ignoring invalid setting `"$keyvalue$"` from `"$settingsList$"`"); + success = False; + } + } + + return success; +} + +function bool AddUrlParameters(string params) +{ + local string param, key, value; + local int i; + local bool success; + success = False; + + while ( class'MV_Parser'.static.TrySplit(params, "?", param, params) ) + { + if ( class'MV_Parser'.static.TrySplit(param, "=", key, value) ) + { + for ( i = 0;i < UrlParametersCount;i+=1 ) + { + if ( UrlParametersKey[i] ~= key ) + { + UrlParametersValue[i] = value; + goto NEXT_PARAM; + } + } + if ( UrlParametersCount < MaxUrlParameters ) + { + UrlParametersKey[UrlParametersCount] = key; + UrlParametersValue[UrlParametersCount] = value; + UrlParametersCount += 1; + } + else + { + Err("Cannot add `"$param$"`, max server actor count reached!"); + return False; + } } + NEXT_PARAM: } + return success; +} + +function string GetUrlParametersString() +{ + local string result; + local int i; + + for ( i = 0; i < UrlParametersCount; i+=1 ) + { + result = result$"?"$UrlParametersKey[i]$"="$UrlParametersValue[i]; + } + return result; } function bool UpdateSingleGameSetting(string key, string value) diff --git a/MVES/Classes/MapVote.uc b/MVES/Classes/MapVote.uc index 4f0eb2c..d240c9e 100644 --- a/MVES/Classes/MapVote.uc +++ b/MVES/Classes/MapVote.uc @@ -67,7 +67,7 @@ var bool bXCGE_DynLoader; struct GameType { var() config bool bEnabled; - var() config string GameName; //For displayable rule + var() config string GameName; var() config string RuleName; var() config string GameClass; var() config string FilterCode; @@ -79,10 +79,12 @@ struct GameType var() config int TickRate; var() config string ServerActors; var() config string Extends; + var() config string UrlParameters; }; var() config string DefaultSettings; var() config int DefaultTickRate; +var() config string DefaultUrlParameters; var int pos; var() config GameType CustomGame[100]; var GameType EmptyGame; @@ -1481,6 +1483,7 @@ final function MV_Result GenerateMapResult(string map, int idx) function PopulateResultWithDefaults(MV_Result r) { r.SetTickRate(DefaultTickRate); + r.AddUrlParameters(DefaultUrlParameters); } function PopulateResultWithRule(MV_Result r, int idx) @@ -1512,6 +1515,7 @@ function PopulateResultWithRule(MV_Result r, int idx) r.AddActors(CustomGame[idx].ServerActors); r.AddMutators(CustomGame[idx].MutatorList); r.AddPackages(CustomGame[idx].Packages); + r.AddUrlParameters(CustomGame[idx].UrlParameters); } function PrintCircularExtendsError(MV_Result r, int idx) @@ -1564,7 +1568,7 @@ final function bool SetupTravelString( string mapStringWithIdx ) return False; } - TravelInfo.TravelString = Result.Map$"?Game="$ParseAliases(GameClassName); + TravelInfo.TravelString = Result.Map$"?Game="$ParseAliases(GameClassName)$Result.GetUrlParametersString(); TravelInfo.TravelIdx = Result.GameIndex; Nfo("-> TravelString: `"$TravelInfo.TravelString$"`"); Nfo("-> GameIdx: `"$TravelInfo.TravelIdx$"`"); diff --git a/TestMVE/Classes/TestMapVoteResult.uc b/TestMVE/Classes/TestMapVoteResult.uc index fd764c1..584692b 100644 --- a/TestMVE/Classes/TestMapVoteResult.uc +++ b/TestMVE/Classes/TestMapVoteResult.uc @@ -12,6 +12,7 @@ function TestMain() TestActors(); TestSettings(); TestWrappedPackages(); + TestUrlParameters(); } function TestPackages() @@ -125,6 +126,29 @@ function TestSettings() AssertEquals(s.SettingsCount, 7, "new property can be added"); } +function TestUrlParameters() +{ + Describe("url parameters are appended and concatenated"); + AssertEquals(s.UrlParametersCount, 0, "starts with 0 params"); + AssertEquals(s.GetUrlParametersString(), "", "returns empty string"); + s.AddUrlParameters("?Key=Value"); + AssertEquals(s.UrlParametersCount, 1, "added one param"); + s.AddUrlParameters("?ProfileX=1?ProfileY=2"); + AssertEquals(s.UrlParametersCount, 3, "added more params"); + AssertEquals(s.GetUrlParametersString(), "?Key=Value?ProfileX=1?ProfileY=2", "returns merged params"); + + Describe("adding same url parameters overwrite each other"); + s.AddUrlParameters("?Key=Value"); + s.AddUrlParameters("?Key=AnotherValue"); + s.AddUrlParameters("?Key=LastValue"); + AssertEquals(s.GetUrlParametersString(), "?Key=LastValue", "returns merged params"); + + Describe("adding same url parameter without value overwrite each other"); + s.AddUrlParameters("?Dummy=0"); + s.AddUrlParameters("?Dummy?Dummy??"); + AssertEquals(s.GetUrlParametersString(), "?Dummy=", "returns single param"); +} + function Describe(string subject) { Super.Describe(subject);