diff --git a/changelog/snippets/fix.6569.md b/changelog/snippets/fix.6569.md new file mode 100644 index 00000000000..6d32706c6b8 --- /dev/null +++ b/changelog/snippets/fix.6569.md @@ -0,0 +1 @@ +- (#6569) Fix the matchmaker lobby not passing ratings, divisions and clan tags to the session diff --git a/engine/User/CLobby.lua b/engine/User/CLobby.lua index eb132bd52e1..a5c0099bdd6 100644 --- a/engine/User/CLobby.lua +++ b/engine/User/CLobby.lua @@ -43,6 +43,8 @@ local CLobby = {} ---@field Timeouts any # Read by the engine to determine the behavior of time outs. ---@field CivilianAlliance any # Read by the engine to determine the alliance towards civilians. ---@field GameSpeed any # Read by the engine to determine the behavior of game speed (adjustments). +---@field Ratings table +---@field Divisions table ---@class UILobbyLaunchGameModsConfiguration ---@field name string # Read by the engine, TODO diff --git a/lua/ui/lobby/autolobby/AutolobbyController.lua b/lua/ui/lobby/autolobby/AutolobbyController.lua index 364fc8df4aa..180d2b652f3 100644 --- a/lua/ui/lobby/autolobby/AutolobbyController.lua +++ b/lua/ui/lobby/autolobby/AutolobbyController.lua @@ -29,6 +29,8 @@ local DebugComponent = import("/lua/shared/components/DebugComponent.lua").Debug local AutolobbyServerCommunicationsComponent = import("/lua/ui/lobby/autolobby/components/AutolobbyServerCommunicationsComponent.lua") .AutolobbyServerCommunicationsComponent +local AutolobbyArgumentsComponent = import("/lua/ui/lobby/autolobby/components/AutolobbyArguments.lua").AutolobbyArgumentsComponent + local AutolobbyMessages = import("/lua/ui/lobby/autolobby/AutolobbyMessages.lua").AutolobbyMessages local AutolobbyEngineStrings = { @@ -62,6 +64,7 @@ local AutolobbyEngineStrings = { ---@field DIV string # Related to rating/divisions ---@field SUBDIV string # Related to rating/divisions ---@field PL number # Related to rating/divisions +---@field PlayerClan string ---@alias UIAutolobbyConnections boolean[][] ---@alias UIAutolobbyStatus UIPeerLaunchStatus[] @@ -86,7 +89,7 @@ local AutolobbyEngineStrings = { ---@field DesiredPeerId UILobbyPeerId --- Responsible for the behavior of the automated lobby. ----@class UIAutolobbyCommunications : moho.lobby_methods, DebugComponent, UIAutolobbyServerCommunicationsComponent +---@class UIAutolobbyCommunications : moho.lobby_methods, DebugComponent, UIAutolobbyServerCommunicationsComponent, UIAutolobbyArgumentsComponent ---@field Trash TrashBag ---@field LocalPeerId UILobbyPeerId # a number that is stringified ---@field LocalPlayerName string # nickname @@ -100,7 +103,7 @@ local AutolobbyEngineStrings = { ---@field LobbyParameters? UIAutolobbyParameters # Used for rejoining functionality ---@field HostParameters? UIAutolobbyHostParameters # Used for rejoining functionality ---@field JoinParameters? UIAutolobbyJoinParameters # Used for rejoining functionality -AutolobbyCommunications = Class(MohoLobbyMethods, AutolobbyServerCommunicationsComponent, DebugComponent) { +AutolobbyCommunications = Class(MohoLobbyMethods, AutolobbyServerCommunicationsComponent, AutolobbyArgumentsComponent, DebugComponent) { ---@param self UIAutolobbyCommunications __init = function(self) @@ -108,7 +111,7 @@ AutolobbyCommunications = Class(MohoLobbyMethods, AutolobbyServerCommunicationsC self.LocalPeerId = "-2" self.LocalPlayerName = "Charlie" - self.PlayerCount = tonumber(GetCommandLineArg("/players", 1)[1]) or 2 + self.PlayerCount = self:GetCommandLineArgumentNumber("/players", 2) self.HostID = "-2" self.GameMods = {} @@ -147,20 +150,21 @@ AutolobbyCommunications = Class(MohoLobbyMethods, AutolobbyServerCommunicationsC end -- retrieve team and start spot - info.Team = tonumber(GetCommandLineArg("/team", 1)[1]) - info.StartSpot = tonumber(GetCommandLineArg("/startspot", 1)[1]) or -1 -- TODO + info.Team = self:GetCommandLineArgumentNumber("/team", -1) + info.StartSpot = self:GetCommandLineArgumentNumber("/startspot", -1) -- determine army color based on start location info.PlayerColor = GameColors.MapToWarmCold(info.StartSpot) info.ArmyColor = GameColors.MapToWarmCold(info.StartSpot) -- retrieve rating - info.DEV = tonumber(GetCommandLineArg("/deviation", 1)[1]) or 500 - info.MEAN = tonumber(GetCommandLineArg("/mean", 1)[1]) or 1500 - info.NG = tonumber(GetCommandLineArg("/numgames", 1)[1]) or 0 - info.DIV = (GetCommandLineArg("/division", 1)[1]) or "" - info.SUBDIV = (GetCommandLineArg("/subdivision", 1)[1]) or "" + info.DEV = self:GetCommandLineArgumentNumber("/deviation", 500) + info.MEAN = self:GetCommandLineArgumentNumber("/mean", 1500) + info.NG = self:GetCommandLineArgumentNumber("/numgames", 0) + info.DIV = self:GetCommandLineArgumentString("/division", "") + info.SUBDIV = self:GetCommandLineArgumentString("/subdivision", "") info.PL = math.floor(info.MEAN - 3 * info.DEV) + info.PlayerClan = self:GetCommandLineArgumentString("/clan", "") return info end, @@ -193,7 +197,7 @@ AutolobbyCommunications = Class(MohoLobbyMethods, AutolobbyServerCommunicationsC } -- process game options from the command line - for name, value in Utils.GetCommandLineArgTable("/gameoptions") do + for name, value in self:GetCommandLineArgumentArray("/gameoptions") do if name and value then options[name] = value else @@ -294,6 +298,59 @@ AutolobbyCommunications = Class(MohoLobbyMethods, AutolobbyServerCommunicationsC return 'Ready' end, + ---@param self UIAutolobbyCommunications + ---@param playerOptions UIAutolobbyPlayer[] + ---@return table + CreateRatingsTable = function(self, playerOptions) + ---@type table + local allRatings = {} + + for slot, options in pairs(playerOptions) do + if options.Human and options.PL then + allRatings[options.PlayerName] = options.PL + end + end + + return allRatings + end, + + ---@param self UIAutolobbyCommunications + ---@param playerOptions UIAutolobbyPlayer[] + ---@return table + CreateDivisionsTable = function(self, playerOptions) + ---@type table + local allDivisions = {} + + for slot, options in pairs(playerOptions) do + if options.Human and options.PL then + if options.DIV ~= "unlisted" then + local division = options.DIV + if options.SUBDIV and options.SUBDIV ~= "" then + division = division .. ' ' .. options.SUBDIV + end + allDivisions[options.PlayerName] = division + end + end + end + + return allDivisions + end, + + ---@param self UIAutolobbyCommunications + ---@param playerOptions UIAutolobbyPlayer[] + ---@return table + CreateClanTagsTable = function(self, playerOptions) + local allClanTags = {} + + for slot, options in pairs(playerOptions) do + if options.PlayerClan then + allClanTags[options.PlayerName] = options.PlayerClan + end + end + + return allClanTags + end, + --- Verifies whether we can launch the game. ---@param self UIAutolobbyCommunications ---@param peerStatus UIAutolobbyStatus @@ -488,6 +545,12 @@ AutolobbyCommunications = Class(MohoLobbyMethods, AutolobbyServerCommunicationsC self:SendPlayerOptionToServer(ownerId, 'Faction', playerOptions.Faction) end + -- tuck them into the game options. By all means a hack, but + -- this way they are available in both the sim and the UI + self.GameOptions.Ratings = self:CreateRatingsTable(self.PlayerOptions) + self.GameOptions.Divisions = self:CreateDivisionsTable(self.PlayerOptions) + self.GameOptions.ClanTags = self:CreateClanTagsTable(self.PlayerOptions) + -- create game configuration local gameConfiguration = { GameMods = self.GameMods, @@ -496,7 +559,7 @@ AutolobbyCommunications = Class(MohoLobbyMethods, AutolobbyServerCommunicationsC Observers = {}, } - -- send it to all players and tell them to launch + -- send it to all players and tell them to launch with the configuration self:BroadcastData({ Type = "Launch", GameConfig = gameConfiguration }) self:LaunchGame(gameConfiguration) end diff --git a/lua/ui/lobby/autolobby/components/AutolobbyArguments.lua b/lua/ui/lobby/autolobby/components/AutolobbyArguments.lua new file mode 100644 index 00000000000..c17f4fa4bd7 --- /dev/null +++ b/lua/ui/lobby/autolobby/components/AutolobbyArguments.lua @@ -0,0 +1,127 @@ +--****************************************************************************************************** +--** Copyright (c) 2024 Willem 'Jip' Wijnia +--** +--** Permission is hereby granted, free of charge, to any person obtaining a copy +--** of this software and associated documentation files (the "Software"), to deal +--** in the Software without restriction, including without limitation the rights +--** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +--** copies of the Software, and to permit persons to whom the Software is +--** furnished to do so, subject to the following conditions: +--** +--** The above copyright notice and this permission notice shall be included in all +--** copies or substantial portions of the Software. +--** +--** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +--** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +--** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +--** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +--** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +--** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +--** SOFTWARE. +--****************************************************************************************************** + +--- A component that represent all the supported lobby <-> server communications. +---@class UIAutolobbyArgumentsComponent +AutolobbyArgumentsComponent = ClassSimple { + + --- Represent all valid command line arguments for the lobby + ArgumentKeys = { + ["/init"] = true, + ["/joincustom"] = true, + ["/gpgnet"] = true, + + -- related to player info + ["/clan"] = true, + ["/country"] = true, + ["/numgames"] = true, + + -- related to player settings + ["/team"] = true, + ["/uef"] = true, + ["/cybran"] = true, + ["/aeon"] = true, + ["/seraphim"] = true, + ["/startspot"] = true, + + -- related to rating + ["/deviation"] = true, + ["/mean"] = true, + + -- related to divisions + ["division"] = true, + ["/subdivision"] = true, + + -- related to game settings + ["/gameoptions"] = true, + ["/players"] = true, + }, + + --- Verifies that it is an expected command line argument + ---@param self UIAutolobbyArgumentsComponent | UIAutolobbyCommunications + ---@param option string + ---@return boolean + ValidCommandLineKey = function(self, option) + if not self.ArgumentKeys[option] then + self:DebugWarn("Unknown command line argument: ", option) + return false + end + + return true + end, + + --- Attempts to retrieve a string-like command line argument + ---@param self UIAutolobbyArgumentsComponent | UIAutolobbyCommunications + ---@param option string + ---@param default string + ---@return string + GetCommandLineArgumentString = function(self, option, default) + if not self:ValidCommandLineKey(option) then + return default + end + + -- try to get the first argument + local arguments = GetCommandLineArg(option, 1) + if arguments and (not option[ arguments[1] ]) then + return arguments[1] + end + + return default + end, + + --- Attempts to retrieve a number-like command line argument + ---@param self UIAutolobbyArgumentsComponent | UIAutolobbyCommunications + ---@param option string + ---@param default number + ---@return number + GetCommandLineArgumentNumber = function(self, option, default) + if not self:ValidCommandLineKey(option) then + return default + end + + -- try to get the first argument and parse it as a number + local arguments = GetCommandLineArg(option, 1) + if arguments and (not option[ arguments[1] ]) then + local parsed = tonumber(arguments[1]) + if parsed then + return parsed + else + self:DebugWarn("Failed to parse as a number: ", arguments[1], " for key ", option) + return default + end + end + + return default + end, + + --- Attempts to retrieve a table-like command line argument + ---@param self UIAutolobbyArgumentsComponent | UIAutolobbyCommunications + ---@param option string + ---@return table + GetCommandLineArgumentArray = function(self, option) + if not self:ValidCommandLineKey(option) then + return {} + end + + return import("/lua/system/utils.lua").GetCommandLineArgTable(option) + end, +} diff --git a/scripts/LaunchFAInstances.ps1 b/scripts/LaunchFAInstances.ps1 index 8a22fca4a6e..123faab0e14 100644 --- a/scripts/LaunchFAInstances.ps1 +++ b/scripts/LaunchFAInstances.ps1 @@ -34,6 +34,7 @@ $gameName = "MyGame" # Array of factions to choose from $factions = @("UEF", "Seraphim", "Cybran", "Aeon") +$clans = @("Yps", "Nom", "Cly", "Mad", "Gol", "Kur", "Row", "Jip", "Bal", "She") # Get the screen resolution (for placing and resizing the windows) Add-Type -AssemblyName System.Windows.Forms @@ -91,7 +92,7 @@ if ($players -eq 1) { $hostLogFile = "host_dev_1.log" $hostFaction = $factions | Get-Random $hostTeamArgument = Get-TeamArgument -instanceNumber 0 - $hostArguments = "/log $hostLogFile /showlog /hostgame $hostProtocol $port $hostPlayerName $gameName $map /startspot 1 /players $players /$hostFaction $hostTeamArgument $baseArguments" + $hostArguments = "/log $hostLogFile /showlog /hostgame $hostProtocol $port $hostPlayerName $gameName $map /startspot 1 /players $players /$hostFaction $hostTeamArgument $baseArguments /division HostDivision /subdivision 1 /clan $($clans | Get-Random)" # Launch host game instance Launch-GameInstance -instanceNumber 1 -xPos 0 -yPos 0 -arguments $hostArguments @@ -107,7 +108,7 @@ if ($players -eq 1) { $clientPlayerName = "ClientPlayer_$($i + 1)" $clientFaction = $factions | Get-Random $clientTeamArgument = Get-TeamArgument -instanceNumber $i - $clientArguments = "/log $clientLogFile /joingame $hostProtocol localhost:$port $clientPlayerName /startspot $($i + 1) /players $players /$clientFaction $clientTeamArgument $baseArguments" + $clientArguments = "/log $clientLogFile /joingame $hostProtocol localhost:$port $clientPlayerName /startspot $($i + 1) /players $players /$clientFaction $clientTeamArgument $baseArguments /division Diamond /subdivision $($i + 1) /clan $($clans | Get-Random)" Launch-GameInstance -instanceNumber ($i + 1) -xPos $xPos -yPos $yPos -arguments $clientArguments }