From 2df1a99ebc1691817744c89618ef9e60f75e6f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Charvet=20=E9=BB=91=E7=93=9C?= Date: Sun, 25 Aug 2024 08:51:14 +0100 Subject: [PATCH] Handle matchmaking/list message --- lib/teiserver/matchmaking/queue_server.ex | 6 ++- lib/teiserver/player/tachyon_handler.ex | 20 ++++++++ .../schema/matchmaking/list/request.json | 17 +++++++ test/support/tachyon.ex | 49 +++++++++++++++++++ .../tachyon/matchmaking_test.exs | 31 ++++++++++++ 5 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 priv/tachyon/schema/matchmaking/list/request.json create mode 100644 test/teiserver_web/tachyon/matchmaking_test.exs diff --git a/lib/teiserver/matchmaking/queue_server.ex b/lib/teiserver/matchmaking/queue_server.ex index 122500abe..469cbc530 100644 --- a/lib/teiserver/matchmaking/queue_server.ex +++ b/lib/teiserver/matchmaking/queue_server.ex @@ -45,7 +45,8 @@ defmodule Teiserver.Matchmaking.QueueServer do @type queue :: %{ name: String.t(), team_size: pos_integer(), - team_count: pos_integer() + team_count: pos_integer(), + ranked: boolean() } @type state :: %{ @@ -80,7 +81,8 @@ defmodule Teiserver.Matchmaking.QueueServer do queue: %{ name: attrs.name, team_size: attrs.team_size, - team_count: attrs.team_count + team_count: attrs.team_count, + ranked: true }, settings: Map.merge(default_settings(), Map.get(attrs, :settings, %{})), members: Map.get(attrs, :members, []) diff --git a/lib/teiserver/player/tachyon_handler.ex b/lib/teiserver/player/tachyon_handler.ex index 6938119a1..f575c501d 100644 --- a/lib/teiserver/player/tachyon_handler.ex +++ b/lib/teiserver/player/tachyon_handler.ex @@ -3,9 +3,11 @@ defmodule Teiserver.Player.TachyonHandler do Player specific code to handle tachyon logins and actions """ + alias Teiserver.Tachyon.Schema alias Teiserver.Tachyon.Handler alias Teiserver.Data.Types, as: T alias Teiserver.Player + alias Teiserver.Matchmaking @behaviour Handler @@ -51,6 +53,24 @@ defmodule Teiserver.Player.TachyonHandler do term(), state() ) :: WebSock.handle_result() + def handle_command("matchmaking/list" = cmd_id, "request", message_id, _message, state) do + queues = + Matchmaking.list_queues() + |> Enum.map(fn {qid, queue} -> + %{ + id: qid, + name: queue.name, + numOfTeams: queue.team_count, + teamSize: queue.team_size, + ranked: queue.ranked + } + end) + + resp = Schema.response(cmd_id, message_id, %{playlists: queues}) |> Jason.encode!() + + {:reply, :ok, {:text, resp}, state} + end + def handle_command(command_id, _message_type, message_id, _message, state) do resp = Schema.error_response(command_id, message_id, :command_unimplemented) diff --git a/priv/tachyon/schema/matchmaking/list/request.json b/priv/tachyon/schema/matchmaking/list/request.json new file mode 100644 index 000000000..25f247f7e --- /dev/null +++ b/priv/tachyon/schema/matchmaking/list/request.json @@ -0,0 +1,17 @@ +{ + "$id": "https://schema.beyondallreason.dev/tachyon/matchmaking/list/request.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MatchmakingListRequest", + "tachyon": { + "source": "user", + "target": "server", + "scopes": ["tachyon.lobby"] + }, + "type": "object", + "properties": { + "type": { "const": "request" }, + "messageId": { "type": "string" }, + "commandId": { "const": "matchmaking/list" } + }, + "required": ["type", "messageId", "commandId"] +} diff --git a/test/support/tachyon.ex b/test/support/tachyon.ex index af053abcf..f69dfe7cb 100644 --- a/test/support/tachyon.ex +++ b/test/support/tachyon.ex @@ -38,6 +38,55 @@ defmodule Teiserver.Support.Tachyon do "ws://#{conf[:url][:host]}:#{conf[:http][:port]}/tachyon" end + # TODO tachyon_mvp: add a json validation here to make sure the request + # sent there is conforming + def request(command_id, data \\ nil) do + req = %{ + type: :request, + commandId: command_id, + messageId: UUID.uuid4() + } + + if is_nil(data) do + req + else + Map.put(req, :data, data) + end + end + + # TODO tachyon_mvp: create a version of this function that also check the + # the response against the expected json schema + def recv_response(client) do + case WSC.recv(client) do + {:ok, {:text, resp}} -> {:ok, Jason.decode!(resp)} + other -> other + end + end + + @doc """ + high level function to get the list of matchmaking queues + """ + def list_queues!(client) do + req = request("matchmaking/list") + :ok = WSC.send_message(client, {:text, req |> Jason.encode!()}) + {:ok, resp} = recv_response(client) + + message_id = req.messageId + + # This checks the server replies with the correct message_id + # it only needs to be done once in the test suite so might as well put + # put it here since this is the first request implemented + # This could (should?) be moved elsewhere later + %{ + "type" => "response", + "messageId" => ^message_id, + "commandId" => "matchmaking/list", + "status" => "success" + } = resp + + resp + end + @doc """ Run the given function `f` until `pred` returns true on its result. Waits `wait` ms between each tries. Raise an error if `pred` returns false diff --git a/test/teiserver_web/tachyon/matchmaking_test.exs b/test/teiserver_web/tachyon/matchmaking_test.exs new file mode 100644 index 000000000..4149d9825 --- /dev/null +++ b/test/teiserver_web/tachyon/matchmaking_test.exs @@ -0,0 +1,31 @@ +defmodule Teiserver.Matchmaking.MatchmakingTest do + use TeiserverWeb.ConnCase + alias Teiserver.Support.Tachyon + alias WebsocketSyncClient, as: WSC + + # redefinition because ExUnit < 1.15 doesn't support passing setup + # definition as {Tachyon, :setup_client} + defp setup_client(_context), do: Tachyon.setup_client() + + describe "list" do + setup :setup_client + + test "works", %{client: client} do + resp = Tachyon.list_queues!(client) + + # 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 + } + ]) + + assert MapSet.new(resp["data"]["playlists"]) == expected_playlists + end + end +end