Force a user to join an authoritative match

Hello everyone, I am working with matches in Nakama, we have a RPC written in Lua that allows users to start authoritative matches and we want to limit calls to this RPC. So I thought that two methods we could use are:

  1. Check if the user calling the RPC is already in a match
  2. Force the user to join the match he created so that if he creates another match and match.max_empty_sec is set the old match is stopped

For the first point the solution mentioned by @novabyte is to check if the user is already on a stream but for the second one I have no idea. What do you suggest ?

Sadly for the first point, with a bit of debugging I found that checking if an user is already in a match with the code below

local stream_id = { mode = 6, label = "nakama" }
local meta = nk.stream_user_get(context.user_id, context.session_id, stream_id)

doesn’t work and you need to pass the match id as the subject in the stream_id like this

local stream_id = { mode = 6, subject = match_id, label = "nakama" }

So, at this point, the only solution I can think of is to create a table in Lua and add, when a player creates a match, his id with the relative match_id

Why create a table? You can just use the storage system to keep track. Actually, personally I just use the metadata field to store the match:

  "match": {
    "type": "match-type",
    "uid": "match-id"

I set it when the match is joined and remove it when the match is done. The only gotcha is if something goes wrong and that match is never cleared. What I do is that I have an afterAuthenticate hook that checks if the match is valid:

  const metadata: Base.UserMetadata = user.metadata as Base.UserMetadata;

  if (metadata.match.type != "") {
    // See if the match is active:          
    try {
      const match: nkruntime.Match | null = nk.matchGet(metadata.match.uid);
      if (match == null) {
        throw new Error("Invalid Match specified.");
      return match;
    } catch (error) {
      // Looks like match is invalid -- clear it out!
      metadata.match.type = "";
      metadata.match.uid = "";
      nk.accountUpdateId(user.userId, null, null, null, null, null, null, metadata);

Sorry, this is typescript, but you get it…

Oh, thanks, I think this might be a good solution for the first point

@JudGenie If I’m reading this right then your main aim is to ensure users can only be part of one match at any given time.

The first thing I would recommend is to use the session.single_match config option. Setting this to true ensures the server removes users from any previous match when they join a new one. This would prevent users from creating many matches and holding them open by remaining connected.

As a second part you might also consider reworking your match create RPC to function like a “find or create”. The distinction here is “find or create” looks for an available space for the user in an existing match, and only creates a new match if absolutely necessary. This prevents users being able to directly create matches beyond a certain threshold since the RPC would always find free spaces after enough matches are created - usually a small number in fact.

If direct match creation via RPC is absolutely required, I recommend you add throttling to your RPC function so any given user cannot create matches more often than some time threshold. Perhaps scope this to something like 1-2x your idle match shutdown time so users cannot create matches faster than they shut down.

Hope this helps!

I didn’t know about single_socket and single_match, actually I thought single_match was enabled by default.

I can’t use a function like “find or create” because users can create matches by passing some metadata like a minimum level or the match type.

Definitely limiting the match creation to 2x the idle match shutdown time can be a great solution.


1 Like