Skip to content

Commit

Permalink
Rework the matchmaker lobby (autolobby) (#6479)
Browse files Browse the repository at this point in the history
Reworks the matchmaker lobby from the bottom up with a focus on documentation and a better maintainable code structure. Introduces a similar setup to the MVC pattern. The pattern is simplified. This is an automated lobby and therefore the view never has to communicate back to the controller.
  • Loading branch information
Garanas authored Nov 19, 2024
1 parent 1edef55 commit b872a71
Show file tree
Hide file tree
Showing 47 changed files with 2,813 additions and 661 deletions.
7 changes: 7 additions & 0 deletions changelog/snippets/features.6479.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- (#6479) Rework the in-game matchmaker lobby from the ground up

From a user perspective the matchmaker lobby now has a map preview and a connection matrix. The map preview can help the players to understand what they'll be gating into. The connection matrix can help players to understand what source (a player) is connected to what source (another player). The diagonal represents what sources (other players) the local client is connected to. When you receive a message from a peer then it blinks the corresponding box in the matrix.

From a developers perspective the matchmaker lobby is now maintainable. You can now start the matchmaker lobby locally through your development environment. This allows you to run the matchmaker lobby as if you would find a match in the client. The matchmaker lobby is build from the ground up with maintainability in mind. It now supports a hot reload-like functionality for the interface. This allows you to change the interface on the go, without having to relaunch the game.

All taken together this is still very much a work in progress and we would love to hear the feedback of the community. We welcome you on Discord in the dev-talk channel.
13 changes: 7 additions & 6 deletions engine/User.lua
Original file line number Diff line number Diff line change
Expand Up @@ -624,9 +624,9 @@ end
function GpgNetActive()
end

---@param cmd string
---@param ... any
function GpgNetSend(cmd, ...)
---@param command string
---@param ... number | string
function GpgNetSend(command, ...)
end

---
Expand Down Expand Up @@ -709,14 +709,15 @@ end
---@alias UILobbyProtocols "UDP" | "TCP" | "None

--- For internal use by `CreateLobbyComm()`
---@param lobbyComClass fa-class
---@generic T
---@param lobbyComClass T
---@param protocol UILobbyProtocols
---@param localPort number
---@param maxConnections number
---@param playerName string
---@param playerUID? string
---@param natTraversalProvider? userdata
---@return UILobbyCommunication
---@return T
function InternalCreateLobby(lobbyComClass, protocol, localPort, maxConnections, playerName, playerUID, natTraversalProvider)
end

Expand Down Expand Up @@ -918,7 +919,7 @@ end

--- Start a background load with the given map and mods.
--- If `hipri` is true, this will interrupt any previous loads in progress.
---@param mapname string
---@param mapname string # path to the `scmap` file
---@param mods ModInfo[]
---@param hipri? boolean
function PrefetchSession(mapname, mods, hipri)
Expand Down
104 changes: 82 additions & 22 deletions engine/User/CLobby.lua
Original file line number Diff line number Diff line change
@@ -1,28 +1,86 @@
---@meta

---@class moho.lobby_methods : Destroyable


---@class moho.lobby_methods : Destroyable, InternalObject
local CLobby = {}

--- "0", "1", "2", but also "32254" and the like.
---@alias UILobbyPeerId string

---@alias GPGNetAddress string | number

---@alias UILobbyProtocol 'UDP' | 'TCP'

---@alias UIPeerConnectionStatus 'None' | 'Pending' | 'Connecting' | 'Answering' | 'Established' | 'TimedOut' | 'Errored'

---@class Peer
---@field establishedPeers string[]
---@field id string
---@field establishedPeers UILobbyPeerId[]
---@field id UILobbyPeerId # Is -1 when the status is pending
---@field ping number
---@field name string
---@field quiet number
---@field status string
---@field status UIPeerConnectionStatus

--- A piece of data that is one can send with `BroadcastData` or `SendData` to other player(s) in the lobby.
---@class UILobbyReceivedMessage : table
---@field SenderID UILobbyPeerId # Set by the engine, allows us to identify the source.
---@field SenderName string # Set by the engine, nickname of the source.
---@field Type string # Type of message

--- A piece of data that is one can send with `BroadcastData` or `SendData` to other player(s) in the lobby.
---@class UILobbyData : table
---@field Type string # Type of message

--- All the following fields are read by the engine upon launching the lobby to setup the scenario.
---@class UILobbyLaunchGameOptionsConfiguration
---@field UnitCap any # Read by the engine to determine the initial unit cap. See also the globals `GetArmyUnitCap`, `GetArmyUnitCostTotal` and `SetArmyUnitCap` to manipulate it throughout the scenario.
---@field CheatsEnabled any # Read by the engine to determine whether cheats are enabled.
---@field FogOfWar any # Read by the engine to determine how to manage the fog of war.
---@field NoRushOption any # Read by the engine to create the anti-rush mechanic.
---@field PrebuiltUnits any # Read by the engine to create initial, prebuilt units.
---@field ScenarioFile any # Read by the engine to load the scenario of the game.
---@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).

---@class UILobbyLaunchGameModsConfiguration
---@field name string # Read by the engine, TODO
---@field uid string # Read by the engine, TODO

---@class UILobbyLaunchObserverConfiguration
---@field OwnerID UILobbyPeerId # Read by the engine, TODO
---@field PlayerName string # Read by the engine, TODO

---@class UILobbyLaunchPlayerConfiguration
---@field StartSpot number # Read by Lua code to determine start locations
---@field ArmyName string # Read by the engine, TODO
---@field PlayerName string # Read by the engine, TODO
---@field Civilian boolean # Read by the engine, TODO
---@field Human boolean # Read by the engine, TODO
---@field AIPersonality string # Read by the engine iff Human is false
---@field ArmyColor number # Read by the engine, is mapped to a color by reading the values of `lua\GameColors.lua`.
---@field PlayerColor number # Read by the engine, is mapped to a color by reading the values of `lua\GameColors.lua`
---@field Faction number # Read by the engine to determine the faction of the player.
---@field OwnerID UILobbyPeerId # Read by the engine, TODO

--- All the following fields are read by the engine upon launching the lobby.
---@class UILobbyLaunchConfiguration
---@field GameMods UILobbyLaunchGameModsConfiguration[] # ModInfo[]
---@field GameOptions UILobbyLaunchGameOptionsConfiguration # GameOptions
---@field Observers UILobbyLaunchObserverConfiguration # PlayerData[]
---@field PlayerOptions UILobbyLaunchPlayerConfiguration[] # PlayerData[]

--- Broadcasts information to all peers. See `SendData` for sending to a specific peer.
---@param data CommunicationData
---@param data UILobbyData
function CLobby:BroadcastData(data)
end

--- Connect to a new peer. The peer will now show up in `GetPeer` and `GetPeers`
---@param address GPGNetAddress # includes the port
---@param name string
---@param uid string
function CLobby:ConnectToPeer(address, name, uid)
---@param peerId UILobbyPeerId
function CLobby:ConnectToPeer(address, name, peerId)
end

---
Expand All @@ -34,18 +92,18 @@ function CLobby:Destroy()
end

--- Disconnect from a peer. The peer will no longer show in `GetPeer` and `GetPeers`.
---@param uid string
function CLobby:DisconnectFromPeer(uid)
---@param peerId UILobbyPeerId
function CLobby:DisconnectFromPeer(peerId)
end

--- Eject a peer from the lobby. The peer will no longer show in `GetPeer` and `GetPeers`.
---@param targetID string
---@param peerId UILobbyPeerId
---@param reason string
function CLobby:EjectPeer(targetID, reason)
function CLobby:EjectPeer(peerId, reason)
end

--- Retrieves the local client identifier.
---@return number
---@return UILobbyPeerId
function CLobby:GetLocalPlayerID()
end

Expand All @@ -60,9 +118,9 @@ function CLobby:GetLocalPort()
end

--- Retrieves a specific peer
---@param uid string
---@param peerId UILobbyPeerId
---@return Peer
function CLobby:GetPeer(uid)
function CLobby:GetPeer(peerId)
end

--- Retrieves all peers
Expand All @@ -80,14 +138,16 @@ function CLobby:IsHost()
end

--- Joins a lobby hosted by another peer. See `HostGame` to host a game.
---
--- Is not idempotent - joining twice will generate an error.
---@param address GPGNetAddress
---@param remotePlayerName? string | nil
---@param remotePlayerUID? string
function CLobby:JoinGame(address, remotePlayerName, remotePlayerUID)
---@param remotePlayerPeerId? UILobbyPeerId
function CLobby:JoinGame(address, remotePlayerName, remotePlayerPeerId)
end

---
---@param gameConfig GameData
---@param gameConfig UILobbyLaunchConfiguration
function CLobby:LaunchGame(gameConfig)
end

Expand All @@ -98,16 +158,16 @@ function CLobby:MakeValidGameName(origName)
end

--- Creates a unique, alternative player name if that is required
---@param uid string
---@param peerId UILobbyPeerId
---@param origName string
---@return string
function CLobby:MakeValidPlayerName(uid, origName)
function CLobby:MakeValidPlayerName(peerId, origName)
end

--- Sends data to a specific peer. See `BroadcastData` for sending to all peers.
---@param targetID string
---@param data CommunicationData
function CLobby:SendData(targetID, data)
---@param peerId UILobbyPeerId
---@param data UILobbyData
function CLobby:SendData(peerId, data)
end

return CLobby
6 changes: 6 additions & 0 deletions engine/User/CMauiBitmap.lua
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,10 @@ end
function CMauiBitmap:UseAlphaHitTest(doHit)
end

--- Defines the color mask to be applied to bitmap during rendering (white images will get this color for example).
--- Introduced by [binary patch #42](https://github.com/FAForever/FA-Binary-Patches/pull/42)
---@param color string
function CMauiBitmap:SetColorMask(color)
end

return CMauiBitmap
2 changes: 2 additions & 0 deletions engine/User/CUIMapPreview.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ end

---
---@param textureName string
---@return boolean
function CUIMapPreview:SetTexture(textureName)
end

---
---@param mapName string
---@return boolean
function CUIMapPreview:SetTextureFromMap(mapName)
end

Expand Down
64 changes: 63 additions & 1 deletion lua/GameColors.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,66 @@
--******************************************************************************************************
--** 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.
--******************************************************************************************************

-- When launching a lobby each player has a configuration. This configuration has the
-- fields `ArmyColor` and `PlayerColor`. The values of these fields are numbers. This
-- module is responsible for converting the integer-based player and army colors
-- into a hex-based color string that the engine understands.

local WarmColdMapping = {
-- 1v1
11, -- "ff436eee" (11) new blue1
01, -- "FFe80a0a" (01) Cybran red

-- 2v2
12, -- "FF2929e1" (12) UEF blue
02, -- "ff901427" (02) dark red

-- 3v3
14, -- "ff9161ff" (14) purple
03, -- "FFFF873E" (03) Nomads orange

-- 4v4
15, -- "ff66ffcc" (15) aqua
05, -- "ffa79602" (05) Sera golden

-- beyond 4v4, which we'll not likely support any time soon.
08,
19,
07,
05,
13,
16,
17,
04
}

--- Maps the start location of a player into a a warm vs cold color scheme. Read the
--- introduction of this module for more context.
---@param startSpot number
---@return number
MapToWarmCold = function(startSpot)
return WarmColdMapping[startSpot]
end

--- Determines the available colors for players and the default color order for
-- matchmaking. See autolobby.lua and lobby.lua for more information.
GameColors = {
Expand All @@ -7,7 +70,6 @@ GameColors = {
-- Default color order used for lobbies/TMM if not otherwise specified. Tightly coupled
-- with the ArmyColors and the PlayerColors tables.
LobbyColorOrder = { 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }, -- rainbow-like color for Fearghal
TMMColorOrder = { 11, 01, 12, 02, 14, 03, 15, 05, 08, 19, 07, 05, 13, 16, 17, 04 }, -- warm vs cold

-- If you end up working with this file, suggestion to install the Color Highlight extension:
-- - https://marketplace.visualstudio.com/items?itemName=naumovs.color-highlight
Expand Down
2 changes: 1 addition & 1 deletion lua/maui/bitmap.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ local ScaleNumber = import("/lua/maui/layouthelpers.lua").ScaleNumber
Bitmap = ClassUI(moho.bitmap_methods, Control) {
---@param self Bitmap
---@param parent Control
---@param filename Lazy<FileName>
---@param filename? Lazy<FileName>
---@param debugname? string
__init = function(self, parent, filename, debugname)
InternalCreateBitmap(self, parent)
Expand Down
1 change: 0 additions & 1 deletion lua/shared/color.lua
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,6 @@ end

--- Map of named colors the Moho engine can recognize and their representation
---@see EnumColorNames()
---@type table<EnumColor, Color>
EnumColors = {
AliceBlue = "F7FBFF",
AntiqueWhite = "FFEBD6",
Expand Down
3 changes: 3 additions & 0 deletions lua/system/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,9 @@ function StringJoin(items, delimiter)
end

--- "explode" a string into a series of tokens, using a separator character `sep`
---@param str string
---@param sep string
---@return string[]
function StringSplit(str, sep)
local sep, fields = sep or ":", {}
local pattern = string.format("([^%s]+)", sep)
Expand Down
Loading

0 comments on commit b872a71

Please sign in to comment.