New product announcement 🎉 Satori - LiveOps to know your players, deliver features, run experiments and schedule events

Match Listing

Where the matchmaker can be used by players to find opponents (or teammates) to play with in a new match, match listing is used to show players currently active matches that they can join right away.

You can use match listing to simply return any given number of active matches, but using the match label to query active matches and filter the results according to your player’s desired criteria will provide more relevant matches and a more engaging user experience.

In addition the a match label, you can filter results by their authoritative status and player count. See the function reference for all available parameters.

Filter vs. query

There are two ways of using match label fields to list results: exact-match filtering and querying.

For example, if we want to list matches with a label skill=100-150, exact-match filtering would be as follows:

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
local nk = require("nakama")

local limit = 10
local isAuthoritative = true
local label = "skill=100-150"
local min_size = 0
local max_size = 4
local matches = nk.match_list(limit, isAuthoritative, label, min_size, max_size)

for _, match in ipairs(matches) do
  nk.logger_info(string.format("Match id %s", match.match_id))
end
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
limit := 10
isAuthoritative := true
label := "skill=100-150"
min_size := 0
max_size := 4

if matches, err := nk.MatchList(ctx, limit, isAuthoritative, label, min_size, max_size, ""); err != nil {
    logger.WithField("err", err).Error("Match list error.")
} else {
    for _, match := range matches {
        logger.Info("Match id %s", match.GetMatchId())
    }
}
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function getMatchListings(context: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama) {
  const limit = 10
  const isAuthoritative = true;
  const label = "skill=100-150";
  const minSize = 0;
  const maxSize = 4;
  const matches = nk.matchList(limit, isAuthoritative, label, minSize, maxSize, "");

  matches.forEach(function (match) {
    logger.info("Match id '%s'", match.matchId);
  });
}

Remember that here we are filtering based on the string value of the label, so results will only include matches with the exact label skill=100-150. A match with label skill=100 or skill=150 would not be returned.

While any returned result would be precisely what the player is searching for, this can lead to frustration when no matching results are found at all.

The alternative would be using a query to list available matches filtered based on our desired criteria, in this case a range in skill:

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
local nk = require("nakama")

local limit = 10
local isauthoritative = true
local label = ""
local min_size = 0
local max_size = 4
local query = "+label.skill>=100 +label.skill<=150"
local matches = nk.match_list(limit, isauthoritative, label, min_size, max_size, query)

for _, match in ipairs(matches) do
  nk.logger_info(string.format("Match id %s", match.match_id))
end
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
limit := 10
authoritative := true
label := ""
minSize := 0
maxSize := 4
query := "+label.skill>=100 +label.skill<=150"
matches, err := nk.MatchList(ctx, limit, authoritative, label, minSize, maxSize, query)

if err != nil {
    logger.WithField("err", err).Error("Match listings error.")
    return
}

for _, match := range matches {
    logger.Info("Match id %s", match.MatchId)
}
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function findMatch(context: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama) {
  const limit = 10
  const isAuthoritative = true;
  const minSize = 0;
  const maxSize = 4;
  const query = "+label.skill>=100 +label.skill<=150";
  var matches = nk.matchList(limit, isAuthoritative, minSize, maxSize, query);

  matches.forEach(function (match) {
    logger.info("Match id '%s'", match.matchId);
  });
}

With this method, the results will include any match will a skill label between 100 and 150, inclusive.

Learn how to use the query syntax grammar to filter and sort results.

Find or create

To provide a seamless gameplay experience for your users, if match listing fails to return any matching results you can create a new match for the user directly.

Using our filter example from above:

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
local nk = require("nakama")
local function findorcreatematch(limit, label, min_size, max_size)
  local matches = nk.match_list(limit, true, label, min_size, max_size)

  if (#matches > 0) then
    table.sort(matches, function(a, b)
      return a.size > b.size;
    end)
    return matches[1].match_id
  end

  local modulename = "supermatch"
  local initialstate = {}
  local match_id = nk.match_create(modulename, initialstate)
  return match_id
end
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
if matches, err := nk.MatchList(ctx, limit, true, label, min_size, max_size, "*"); err != nil {
    return "", err
} else {
    if len(matches) > 0 {
        sort.Slice(matches, func(i, j int) bool {
          return matches[i].Size < matches[j].Size
        })

        return matches[0].MatchId
    }
}

modulename := "supermatch"

if matchId, err := nk.MatchCreate(ctx, modulename, nil); err != nil {
    return "", err
} else {
    return matchId, nil
}

return "", nil
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function findOrCreateMatch(context: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama): string {
  var matches = nk.matchList(limit, isAuthoritative, label, minSize, maxSize, "");

  // If matches exist, sort by match size and return the largest.
  if (matches.length > 0) {
    matches.sort(function (a, b) {
      return a.size >= b.size ? 1 : -1;
    });
    return matches[0].matchId;
  }

  // If no matches exist, create a new one using the "lobby" module and return it's ID.
  var matchId = nk.matchCreate('supermatch', {});
  return JSON.stringify({ matchId });
}

The same can be done when querying matches as well:

Server
1
2
3
4
5
6
7
8
9
local query = "+label.skill>=100 +label.skill<=150"
local matches = nk.match_list(10, true, "", 2, 4, query)

if #matches > 0 then
  nk.logger_info(matches[0].match_id)
else
  local match_id = nk.match_create("matchname", {})
  nk.logger_info(match_id)
end
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
query := "+label.skill>=100 +label.skill<=150"
matches, err := nk.MatchList(ctx, 1, true, "", 2, 4, query)

if err != nil {
    logger.WithField("err", err).Error("List match error.")
    return
}

if len(matches) > 0 {
    logger.Info(matches[0].MatchId)
} else {
    matchId, err := nk.MatchCreate(ctx, "matchname", nil)

    if err != nil {
        logger.WithField("err", err).Error("Match create error.")
        return
    }

    logger.Info(matchId)
}
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function findOrCreateMatch(context: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama) {
  const query = "+label.skill>=100 +label.skill<=150";
  var matches = nk.matchList(10, true, "", 2, maxSize, query);

  // If matches exist, sort by match size and return the largest.
  if (matches.length > 0) {
    logger.info("Match id '%s'", matches[0].matchId);
  }

  // If no matches exist, create a new one using the "lobby" module and return it's ID.
  var matchId = nk.matchCreate('supermatch', {});
  logger.info(matchId);
}

Examples

Player count

The minSize and maxSize parameters can be used to list only matches containing the specified number of players. This can be useful for returning results that are already nearly full (and so the match is almost ready to start).

For example, let’s assume a match will begin once 8 players join so we’ll only list matches with between 5-7 players:

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
local nk = require("nakama")

local limit = 10
local isAuthoritative = true
local min_size = 5
local max_size = 7
local matches = nk.match_list(limit, isAuthoritative, min_size, max_size)

for _, match in ipairs(matches) do
  nk.logger_info(string.format("Match id %s", match.match_id))
end
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
limit := 10
isAuthoritative := true
min_size := 5
max_size := 7

if matches, err := nk.MatchList(ctx, limit, isAuthoritative, min_size, max_size, ""); err != nil {
    logger.WithField("err", err).Error("Match list error.")
} else {
    for _, match := range matches {
        logger.Info("Match id %s", match.GetMatchId())
    }
}
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function getMatchListings(context: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama) {
  const limit = 10
  const isAuthoritative = true;
  const minSize = 5;
  const maxSize = 7;
  const matches = nk.matchList(limit, isAuthoritative, minSize, maxSize, "");

  matches.forEach(function (match) {
    logger.info("Match id '%s'", match.matchId);
  });
}

Related Pages