From 4307777316f5b350bb5de4f6949af8f2f3c5e6bb Mon Sep 17 00:00:00 2001 From: Lexon <44340857+L-e-x-o-n@users.noreply.github.com> Date: Mon, 24 Feb 2025 15:53:49 +0100 Subject: [PATCH] User map pool, games and engines in matchmaking (#601) - Grabs the relevant maps when a queue process starts - Return this list of map in the response to matchmaking/list - If the map list is empty, joining a queue should result in an internal_error - Pass the map info along to the pairing room process - When a match is confirmed, the pairing room process picks a map at random and create the start script data - Added games and engines to the queue process and their selection to pairing room, passing them to start_script and showing them in matchmaking/list --- lib/teiserver/asset.ex | 3 + lib/teiserver/asset/queries/map_queries.ex | 10 + lib/teiserver/matchmaking/pairing_room.ex | 83 ++++-- lib/teiserver/matchmaking/queue_server.ex | 47 ++- lib/teiserver/player/tachyon_handler.ex | 17 +- .../schema/matchmaking/list/request.json | 2 - .../schema/matchmaking/list/response.json | 69 ++++- test/support/fixtures/asset.ex | 1 + test/teiserver/matchmaking/queue_test.exs | 22 +- .../tachyon/matchmaking_test.exs | 281 +++++++++++++++--- 10 files changed, 443 insertions(+), 92 deletions(-) diff --git a/lib/teiserver/asset.ex b/lib/teiserver/asset.ex index a088c62d9..e7edd7e20 100644 --- a/lib/teiserver/asset.ex +++ b/lib/teiserver/asset.ex @@ -9,6 +9,9 @@ defmodule Teiserver.Asset do @spec get_map(String.t()) :: Asset.Map.t() | nil defdelegate get_map(spring_name), to: MapQueries + @spec get_maps_for_queue(Teiserver.Matchmaking.queue_id()) :: [Asset.Map.t()] | nil + defdelegate get_maps_for_queue(spring_name), to: MapQueries + @spec get_all_maps() :: [Asset.Map.t()] defdelegate get_all_maps(), to: MapQueries diff --git a/lib/teiserver/asset/queries/map_queries.ex b/lib/teiserver/asset/queries/map_queries.ex index 28e11a6fe..762b2eebd 100644 --- a/lib/teiserver/asset/queries/map_queries.ex +++ b/lib/teiserver/asset/queries/map_queries.ex @@ -8,6 +8,11 @@ defmodule Teiserver.Asset.MapQueries do base_query() |> where_spring_name(spring_name) |> Repo.one() end + @spec get_map(Teiserver.Matchmaking.queue_id()) :: [Asset.Map.t()] | nil + def get_maps_for_queue(queue) do + base_query() |> where_has_queue(queue) |> Repo.all() + end + @spec get_all_maps() :: [Asset.Map.t()] def get_all_maps() do base_query() |> Repo.all() @@ -27,4 +32,9 @@ defmodule Teiserver.Asset.MapQueries do from [map: map] in query, where: map.spring_name == ^name end + + defp where_has_queue(query, queue) do + from [map: map] in query, + where: ^queue in map.matchmaking_queues + end end diff --git a/lib/teiserver/matchmaking/pairing_room.ex b/lib/teiserver/matchmaking/pairing_room.ex index f92d5b965..c75b2002b 100644 --- a/lib/teiserver/matchmaking/pairing_room.ex +++ b/lib/teiserver/matchmaking/pairing_room.ex @@ -117,40 +117,48 @@ defmodule Teiserver.Matchmaking.PairingRoom do {:stop, :normal, state} id -> - start_script = hardcoded_start_script(state) + engine = select_engine(state.queue.engines) + %{spring_game: game} = select_game(state.queue.games) + %{spring_name: map} = select_map(state.queue.maps) - case Teiserver.TachyonBattle.start_battle(id, start_script) do - {:error, reason} -> - QueueServer.disband_pairing(state.queue_id, self()) + start_battle(state, id, engine, game, map) + end + end - for team <- state.teams, member <- team, p_id <- member.player_ids do - Teiserver.Player.matchmaking_notify_lost(p_id, {:server_error, reason}) - end + defp start_battle(state, host_id, engine, game, map) do + start_script = start_script(state, engine, game, map) - {:stop, :normal, state} + case Teiserver.TachyonBattle.start_battle(host_id, start_script) do + {:error, reason} -> + QueueServer.disband_pairing(state.queue_id, self()) - {:ok, host_data} -> - QueueServer.disband_pairing(state.queue_id, self()) + for team <- state.teams, member <- team, p_id <- member.player_ids do + Teiserver.Player.matchmaking_notify_lost(p_id, {:server_error, reason}) + end - ids = - for team <- state.teams, member <- team, p_id <- member.player_ids do - p_id - end + {:stop, :normal, state} - Logger.debug("Pairing completed for players " <> Enum.join(ids, ",")) + {:ok, host_data} -> + QueueServer.disband_pairing(state.queue_id, self()) - battle_start_data = - host_data - |> Map.put(:engine, %{version: start_script.engineVersion}) - |> Map.put(:game, %{springName: start_script.gameName}) - |> Map.put(:map, %{springName: start_script.mapName}) + ids = + for team <- state.teams, member <- team, p_id <- member.player_ids do + p_id + end - for team <- state.teams, member <- team, p_id <- member.player_ids do - Teiserver.Player.battle_start(p_id, battle_start_data) - end + Logger.debug("Pairing completed for players " <> Enum.join(ids, ",")) - {:stop, :normal, state} + battle_start_data = + host_data + |> Map.put(:engine, engine) + |> Map.put(:game, %{springName: game}) + |> Map.put(:map, %{springName: map}) + + for team <- state.teams, member <- team, p_id <- member.player_ids do + Teiserver.Player.battle_start(p_id, battle_start_data) end + + {:stop, :normal, state} end end @@ -217,12 +225,13 @@ defmodule Teiserver.Matchmaking.PairingRoom do {:stop, :normal, state} end - @spec hardcoded_start_script(state()) :: Teiserver.TachyonBattle.start_script() - defp hardcoded_start_script(state) do + @spec start_script(state(), %{version: String.t()}, String.t(), String.t()) :: + Teiserver.TachyonBattle.start_script() + defp start_script(state, engine, game, map) do %{ - engineVersion: "105.1.1-2590-gb9462a0 bar", - gameName: "Beyond All Reason test-26929-d709d32", - mapName: "Red Comet Remake 1.8", + engineVersion: engine.version, + gameName: game, + mapName: map, startPosType: :ingame, allyTeams: get_ally_teams(state) } @@ -240,4 +249,20 @@ defmodule Teiserver.Matchmaking.PairingRoom do %{teams: teams} end end + + # TODO implement some smarter engine/game selection logic here in the future, get first for now + @spec select_engine([%{version: String.t()}]) :: %{version: String.t()} + def select_engine(engines) do + Enum.at(engines, 0) + end + + @spec select_game([%{spring_game: String.t()}]) :: %{spring_game: String.t()} + def select_game(games) do + Enum.at(games, 0) + end + + @spec select_map([Teiserver.Asset.Map.t()]) :: Teiserver.Asset.Map.t() + def select_map(maps) do + Enum.random(maps) + end end diff --git a/lib/teiserver/matchmaking/queue_server.ex b/lib/teiserver/matchmaking/queue_server.ex index 8880c5702..e571a0c59 100644 --- a/lib/teiserver/matchmaking/queue_server.ex +++ b/lib/teiserver/matchmaking/queue_server.ex @@ -12,6 +12,7 @@ defmodule Teiserver.Matchmaking.QueueServer do require Logger alias Teiserver.Matchmaking.{QueueRegistry, PairingRoom} alias Teiserver.Data.Types, as: T + alias Teiserver.Asset @typedoc """ member of a queue. Holds of the information required to match members together. @@ -51,7 +52,10 @@ defmodule Teiserver.Matchmaking.QueueServer do name: String.t(), team_size: pos_integer(), team_count: pos_integer(), - ranked: boolean() + ranked: boolean(), + engines: [%{version: String.t()}], + games: [%{spring_game: String.t()}], + maps: [Teiserver.Asset.Map.t()] } @type state :: %{ @@ -81,6 +85,9 @@ defmodule Teiserver.Matchmaking.QueueServer do required(:name) => String.t(), required(:team_size) => pos_integer(), required(:team_count) => pos_integer(), + optional(:engines) => [%{version: String.t()}], + optional(:games) => [%{spring_game: String.t()}], + optional(:maps) => [Teiserver.Asset.Map.t()], optional(:settings) => settings(), optional(:members) => [member()] }) :: state() @@ -91,7 +98,10 @@ defmodule Teiserver.Matchmaking.QueueServer do name: attrs.name, team_size: attrs.team_size, team_count: attrs.team_count, - ranked: true + ranked: true, + engines: Map.get(attrs, :engines, []), + games: Map.get(attrs, :games, []), + maps: Map.get(attrs, :maps, []) }, settings: Map.merge(default_settings(), Map.get(attrs, :settings, %{})), members: Map.get(attrs, :members, []), @@ -108,7 +118,15 @@ defmodule Teiserver.Matchmaking.QueueServer do QueueRegistry.via_tuple(queue_id, queue) end - @type join_result :: :ok | {:error, :invalid_queue | :already_queued | :too_many_players} + @type join_result :: + :ok + | {:error, + :invalid_queue + | :already_queued + | :too_many_players + | :missing_engines + | :missing_games + | :missing_maps} @doc """ Join the specified queue @@ -162,7 +180,19 @@ defmodule Teiserver.Matchmaking.QueueServer do :timer.send_interval(state.settings.tick_interval_ms, :tick) end - {:ok, state} + {:ok, state, {:continue, :init_engines_games_maps}} + end + + @impl true + def handle_continue(:init_engines_games_maps, state) do + # TODO Get engines and games from somewhere else + engines = state.queue.engines + games = state.queue.games + maps = Asset.get_maps_for_queue(state.id) + + queue = %{state.queue | engines: engines, games: games, maps: maps} + + {:noreply, %{state | queue: queue}} end @impl true @@ -185,6 +215,15 @@ defmodule Teiserver.Matchmaking.QueueServer do Enum.count(new_member.player_ids) > state.queue.team_size -> {:reply, {:error, :too_many_players}, state} + Enum.empty?(state.queue.engines) -> + {:reply, {:error, :missing_engines}, state} + + Enum.empty?(state.queue.games) -> + {:reply, {:error, :missing_games}, state} + + Enum.empty?(state.queue.maps) -> + {:reply, {:error, :missing_maps}, state} + !is_queuing && !is_pairing -> monitors = Enum.map(new_member.player_ids, fn user_id -> diff --git a/lib/teiserver/player/tachyon_handler.ex b/lib/teiserver/player/tachyon_handler.ex index 6f54fa22e..42f528009 100644 --- a/lib/teiserver/player/tachyon_handler.ex +++ b/lib/teiserver/player/tachyon_handler.ex @@ -155,12 +155,18 @@ defmodule Teiserver.Player.TachyonHandler do queues = Matchmaking.list_queues() |> Enum.map(fn {qid, queue} -> + game_names = Enum.map(queue.games, fn game -> %{springName: game.spring_game} end) + map_names = Enum.map(queue.maps, fn map -> %{springName: map.spring_name} end) + %{ id: qid, name: queue.name, numOfTeams: queue.team_count, teamSize: queue.team_size, - ranked: queue.ranked + ranked: queue.ranked, + engines: queue.engines, + games: game_names, + maps: map_names } end) @@ -186,6 +192,15 @@ defmodule Teiserver.Player.TachyonHandler do :too_many_players -> %{reason: :invalid_request, details: "too many player for a playlist"} + :missing_engines -> + %{reason: :internal_error, details: "missing engine list"} + + :missing_games -> + %{reason: :internal_error, details: "missing game list"} + + :missing_maps -> + %{reason: :internal_error, details: "missing map list"} + x -> %{reason: x} end diff --git a/priv/tachyon/schema/matchmaking/list/request.json b/priv/tachyon/schema/matchmaking/list/request.json index 25f247f7e..f50dac76d 100644 --- a/priv/tachyon/schema/matchmaking/list/request.json +++ b/priv/tachyon/schema/matchmaking/list/request.json @@ -1,6 +1,4 @@ { - "$id": "https://schema.beyondallreason.dev/tachyon/matchmaking/list/request.json", - "$schema": "http://json-schema.org/draft-07/schema#", "title": "MatchmakingListRequest", "tachyon": { "source": "user", diff --git a/priv/tachyon/schema/matchmaking/list/response.json b/priv/tachyon/schema/matchmaking/list/response.json index e9391273c..be0535d5c 100644 --- a/priv/tachyon/schema/matchmaking/list/response.json +++ b/priv/tachyon/schema/matchmaking/list/response.json @@ -1,6 +1,4 @@ { - "$id": "https://schema.beyondallreason.dev/tachyon/matchmaking/list/response.json", - "$schema": "http://json-schema.org/draft-07/schema#", "title": "MatchmakingListResponse", "tachyon": { "source": "server", @@ -29,14 +27,51 @@ "name": { "type": "string" }, "numOfTeams": { "type": "integer" }, "teamSize": { "type": "integer" }, - "ranked": { "type": "boolean" } + "ranked": { "type": "boolean" }, + "engines": { + "type": "array", + "items": { + "type": "object", + "properties": { + "version": { "type": "string" } + }, + "required": ["version"] + } + }, + "games": { + "type": "array", + "items": { + "type": "object", + "properties": { + "springName": { + "type": "string" + } + }, + "required": ["springName"] + } + }, + "maps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "springName": { + "type": "string" + } + }, + "required": ["springName"] + } + } }, "required": [ "id", "name", "numOfTeams", "teamSize", - "ranked" + "ranked", + "engines", + "games", + "maps" ] } } @@ -50,14 +85,36 @@ "name": "Duel", "numOfTeams": 2, "teamSize": 1, - "ranked": true + "ranked": true, + "engines": [{ "version": "2025.01.6" }], + "games": [ + { + "springName": "Beyond All Reason test-27414-a84d7e6" + } + ], + "maps": [ + { "springName": "Theta Crystals 1.3" }, + { + "springName": "Comet Catcher Remake 1.8" + }, + { "springName": "Aurelia v4.1" } + ] }, { "id": "1v1v1", "name": "3 Way FFA", "numOfTeams": 3, "teamSize": 1, - "ranked": true + "ranked": true, + "engines": [{ "version": "2025.01.6" }], + "games": [ + { + "springName": "Beyond All Reason test-27414-a84d7e6" + } + ], + "maps": [ + { "springName": "Ghenna Rising 4.0.1" } + ] } ] } diff --git a/test/support/fixtures/asset.ex b/test/support/fixtures/asset.ex index e25904ba8..657a5e8bb 100644 --- a/test/support/fixtures/asset.ex +++ b/test/support/fixtures/asset.ex @@ -1,4 +1,5 @@ defmodule Teiserver.AssetFixtures do + require Logger alias Teiserver.Asset alias Teiserver.Repo diff --git a/test/teiserver/matchmaking/queue_test.exs b/test/teiserver/matchmaking/queue_test.exs index 79945d3a8..9df84f6e1 100644 --- a/test/teiserver/matchmaking/queue_test.exs +++ b/test/teiserver/matchmaking/queue_test.exs @@ -1,14 +1,32 @@ defmodule Teiserver.Matchmaking.QueueTest do - use Teiserver.DataCase, async: true + use Teiserver.DataCase alias Teiserver.Matchmaking alias Teiserver.Matchmaking.QueueServer + alias Teiserver.AssetFixtures + + defp stg_attr(id), + do: %{ + spring_name: "Supreme that glitters", + display_name: "Supreme That Glitters", + thumbnail_url: "https://www.beyondallreason.info/map/?!", + matchmaking_queues: [id] + } setup _context do user = Central.Helpers.GeneralTestLib.make_user(%{"data" => %{"roles" => ["Verified"]}}) id = UUID.uuid4() + AssetFixtures.create_map(stg_attr(id)) + {:ok, pid} = - QueueServer.init_state(%{id: id, name: id, team_size: 1, team_count: 2}) + QueueServer.init_state(%{ + id: id, + name: id, + team_size: 1, + team_count: 2, + engines: ["spring", "recoil"], + games: ["BAR test version", "BAR release version"] + }) |> QueueServer.start_link() {:ok, user: user, queue_id: id, queue_pid: pid} diff --git a/test/teiserver_web/tachyon/matchmaking_test.exs b/test/teiserver_web/tachyon/matchmaking_test.exs index 0ebf06b10..3f816cd42 100644 --- a/test/teiserver_web/tachyon/matchmaking_test.exs +++ b/test/teiserver_web/tachyon/matchmaking_test.exs @@ -3,55 +3,91 @@ defmodule Teiserver.Tachyon.MatchmakingTest do alias Teiserver.Support.Tachyon alias Teiserver.OAuthFixtures alias Teiserver.Player + alias Teiserver.AssetFixtures + alias Teiserver.Asset + alias Teiserver.Matchmaking.{QueueSupervisor, QueueServer} + + defp altair_attr(id), + do: %{ + spring_name: "Altair Crossing Remake " <> id, + display_name: "Altair Crossing", + thumbnail_url: "https://www.beyondallreason.info/map/altair-crossing", + matchmaking_queues: [id] + } - describe "list" do - setup {Tachyon, :setup_client} + defp rosetta_attr(id), + do: %{ + spring_name: "Rosetta " <> id, + display_name: "Rosetta", + thumbnail_url: "https://www.beyondallreason.info/map/rosetta", + matchmaking_queues: [id] + } - test "works", %{client: client} do - resp = Tachyon.list_queues!(client) + defp map_attrs(id), + do: [altair_attr(id), rosetta_attr(id)] - # convert into a set since the order must not impact test result - expected_playlists = - MapSet.new([ - %{ - "id" => "1v1", - "name" => "Duel", - "numOfTeams" => 2, - "teamSize" => 1, - "ranked" => true - }, - %{ - "id" => "2v2", - "name" => "2v2", - "numOfTeams" => 2, - "teamSize" => 2, - "ranked" => true - } - ]) + defp map_names(id) do + map_attrs(id) + |> Enum.map(fn map -> %{"springName" => map.spring_name} end) + end - assert MapSet.new(resp["data"]["playlists"]) == expected_playlists - end + defp engine_versions(), + do: [%{version: "105.1.1-2590-gb9462a0 bar"}, %{version: "100.2.1-2143-test bar"}] + + defp game_versions(), + do: [ + %{spring_game: "Beyond All Reason test-26929-d709d32"}, + %{spring_game: "BAR test version"} + ] + + defp engine_names() do + engine_versions() + |> Enum.map(fn engine -> %{"version" => engine.version} end) + end + + defp game_names() do + game_versions() + |> Enum.map(fn game -> %{"springName" => game.spring_game} end) end - defp mk_queue_attrs(team_size) do + defp setup_queue(context) when is_map(context) do + setup_queue(1) + end + + defp setup_queue(team_size) when is_integer(team_size) do id = UUID.uuid4() + setup_maps(id) + setup_queue(id, team_size) + end + + defp setup_queue(id, team_size) do + queue_attrs(id, team_size) + |> mk_queue() + end + + defp setup_maps(id) do + map_attrs(id) + |> Enum.each(&AssetFixtures.create_map/1) + end + + defp queue_attrs(id, team_size) do + maps = + Asset.get_maps_for_queue(id) + %{ id: id, name: id, team_size: team_size, team_count: 2, - settings: %{tick_interval_ms: :manual, max_distance: 15} + settings: %{tick_interval_ms: :manual, max_distance: 15}, + engines: engine_versions(), + games: game_versions(), + maps: maps } end - defp mk_queue(team_size) when is_integer(team_size) do - mk_queue(mk_queue_attrs(team_size)) - end - defp mk_queue(attrs) do - alias Teiserver.Matchmaking.QueueServer - {:ok, pid} = QueueServer.init_state(attrs) |> QueueServer.start_link() @@ -59,8 +95,74 @@ defmodule Teiserver.Tachyon.MatchmakingTest do {:ok, queue_id: attrs.id, queue_pid: pid} end - defp setup_queue(_context) do - mk_queue(1) + describe "list" do + setup [{Tachyon, :setup_client}, :setup_queue] + + test "works", %{client: client, queue_id: q1v1_id} do + {:ok, queue_id: q2v2_id, queue_pid: _} = setup_queue(2) + + resp = Tachyon.list_queues!(client) + + # Convert into a set since the order must not impact test result + expected_playlists = + MapSet.new([ + %{ + "id" => q1v1_id, + "name" => q1v1_id, + "numOfTeams" => 2, + "teamSize" => 1, + "ranked" => true, + "engines" => engine_names(), + "games" => game_names(), + "maps" => map_names(q1v1_id) + }, + %{ + "id" => q2v2_id, + "name" => q2v2_id, + "numOfTeams" => 2, + "teamSize" => 2, + "ranked" => true, + "engines" => engine_names(), + "games" => game_names(), + "maps" => map_names(q2v2_id) + } + ]) + + # Checking for subset because the response also contains default queues + assert MapSet.subset?(expected_playlists, MapSet.new(resp["data"]["playlists"])) + end + + test "empty map list", %{client: client} do + :ok = + Teiserver.Matchmaking.QueueServer.init_state(%{ + id: "mapless", + name: "mapless", + team_size: 1, + team_count: 2, + engines: engine_versions(), + games: game_versions(), + maps: [] + }) + |> QueueSupervisor.start_queue!() + + resp = Tachyon.list_queues!(client) + + expected_playlists = + MapSet.new([ + %{ + "id" => "mapless", + "name" => "mapless", + "numOfTeams" => 2, + "teamSize" => 1, + "ranked" => true, + "engines" => engine_names(), + "games" => game_names(), + "maps" => [] + } + ]) + + assert MapSet.subset?(expected_playlists, MapSet.new(resp["data"]["playlists"])) + end end describe "joining queues" do @@ -74,7 +176,7 @@ defmodule Teiserver.Tachyon.MatchmakingTest do end test "multiple", %{client: client, queue_id: queue_id} do - {:ok, queue_id: other_queue_id, queue_pid: _} = setup_queue(nil) + {:ok, queue_id: other_queue_id, queue_pid: _} = setup_queue(2) resp = Tachyon.join_queues!(client, [queue_id, other_queue_id]) assert %{"status" => "success"} = resp end @@ -105,16 +207,75 @@ defmodule Teiserver.Tachyon.MatchmakingTest do test "too many player", %{client: client} do id = "emptyqueue" - {:ok, _} = + Teiserver.Matchmaking.QueueServer.init_state(%{ + id: id, + name: id, + team_size: 0, + team_count: 2, + engines: engine_versions(), + games: game_versions(), + maps: map_attrs(id) + }) + |> QueueSupervisor.start_queue!() + + assert %{"status" => "failed", "reason" => "invalid_request"} = + Tachyon.join_queues!(client, [id]) + end + + test "empty engines", %{client: client} do + id = "emptyengines" + + :ok = Teiserver.Matchmaking.QueueServer.init_state(%{ id: id, name: id, - team_size: 0, - team_count: 2 + team_size: 1, + team_count: 2, + engines: [], + games: game_versions(), + maps: map_attrs(id) }) - |> Teiserver.Matchmaking.QueueServer.start_link() + |> QueueSupervisor.start_queue!() - assert %{"status" => "failed", "reason" => "invalid_request"} = + assert %{"status" => "failed", "reason" => "internal_error"} = + Tachyon.join_queues!(client, [id]) + end + + test "empty games", %{client: client} do + id = "emptygames" + + :ok = + Teiserver.Matchmaking.QueueServer.init_state(%{ + id: id, + name: id, + team_size: 1, + team_count: 2, + engines: engine_versions(), + games: [], + maps: map_attrs(id) + }) + |> QueueSupervisor.start_queue!() + + assert %{"status" => "failed", "reason" => "internal_error"} = + Tachyon.join_queues!(client, [id]) + end + + test "empty maps", %{client: client} do + id = "emptymaps" + + :ok = + Teiserver.Matchmaking.QueueServer.init_state(%{ + id: id, + name: id, + team_size: 1, + team_count: 2, + engines: engine_versions(), + games: game_versions(), + maps: [] + }) + |> QueueSupervisor.start_queue!() + + assert %{"status" => "failed", "reason" => "internal_error"} = Tachyon.join_queues!(client, [id]) end end @@ -268,8 +429,8 @@ defmodule Teiserver.Tachyon.MatchmakingTest do end test "two pairings on different queues", %{app: app} do - {:ok, queue_id: q1v1_id, queue_pid: q1v1_pid} = mk_queue(1) - {:ok, queue_id: q2v2_id, queue_pid: q2v2_pid} = mk_queue(2) + {:ok, queue_id: q1v1_id, queue_pid: q1v1_pid} = setup_queue(1) + {:ok, queue_id: q2v2_id, queue_pid: q2v2_pid} = setup_queue(2) clients = Enum.map(1..5, fn _ -> @@ -312,7 +473,7 @@ defmodule Teiserver.Tachyon.MatchmakingTest do end test "foundUpdate event", %{app: app} do - {:ok, queue_id: q_id, queue_pid: q_pid} = mk_queue(2) + {:ok, queue_id: q_id, queue_pid: q_pid} = setup_queue(2) clients = Enum.map(1..4, fn _ -> @@ -341,9 +502,12 @@ defmodule Teiserver.Tachyon.MatchmakingTest do test "timeout puts ready players back in queue", %{app: app} do timeout_ms = 5 + uuid = UUID.uuid4() + + setup_maps(uuid) {:ok, queue_id: q_id, queue_pid: q_pid} = - mk_queue_attrs(1) + queue_attrs(uuid, 1) |> put_in([:settings, :pairing_timeout], timeout_ms) |> mk_queue() @@ -448,7 +612,29 @@ defmodule Teiserver.Tachyon.MatchmakingTest do assert is_pid(Player.SessionRegistry.lookup(user_id)) end - for client <- clients do + # Map will be randomly selected so we first check if the first client's map is in the map pool + [first_client | rest_clients] = clients + first_message = Tachyon.recv_message!(first_client) + + assert %{ + "commandId" => "battle/start", + "data" => %{ + "ip" => _ip, + "port" => _port, + "engine" => %{"version" => "105.1.1-2590-gb9462a0 bar"}, + "game" => %{"springName" => "Beyond All Reason test-26929-d709d32"}, + "map" => %{"springName" => spring_name} + } + } = first_message + + maps = + map_attrs(queue_id) + |> Enum.map(fn map -> map.spring_name end) + + assert spring_name in maps + + # and then if that the rest of clients have the same map + for client <- rest_clients do assert %{ "commandId" => "battle/start", "data" => %{ @@ -456,10 +642,9 @@ defmodule Teiserver.Tachyon.MatchmakingTest do "port" => _port, "engine" => %{"version" => "105.1.1-2590-gb9462a0 bar"}, "game" => %{"springName" => "Beyond All Reason test-26929-d709d32"}, - "map" => %{"springName" => "Red Comet Remake 1.8"} + "map" => %{"springName" => ^spring_name} } - } = - Tachyon.recv_message!(client) + } = Tachyon.recv_message!(client) end end