Match query does not find matches

So I create matches manually instead of using the match maker. It seems the built in match maker is really slow and it gives me no insight into what it does and why it takes a long time.

Matches are created like this:

	limit := 5
	authoritative := true
	label := ""
	minSize := 0  //matchMode.PlayersMin
	maxSize := 10 //matchMode.PlayersMax
	query := "+mode:" + strconv.Itoa(matchMode.Ident)
	matches, err := nk.MatchList(ctx, limit, authoritative, label, &minSize, &maxSize, query)
	if err != nil {
		log.Printf("failed to list matches: %v", err)
		return "", err
	}

	log.Printf("query '%s' found %d matches", query, len(matches))

	for _, match := range matches {
		log.Printf("found match: %v", match)
	}

	var matchId string

	if len(matches) == 0 {
		var params = map[string]interface{}{"mode": matchMode.Ident}
		if matchId, err = nk.MatchCreate(ctx, "xxx", params); err != nil {
			return "", err
		}
		log.Printf("created a new match: %s", matchId)
	} else {
		log.Printf("using old match")
		matchId = matches[0].MatchId
	}
    ...

A new match is created if there’s no old matching match. I see that our callbacks for setting up a match handler is created and the match loop starts to run, so the match seems to be valid and fine. The next time the same code is executed the query +mode:X where X is a number that basically indicates the exact number of players wanted. The created matches have that property set and the query requires it. However MatchList never finds anything matching. If I set the query to "" it does find the existing match.

Is there something that I’ve fundamentally missed about how the queries and/or properties work? When using the match maker the same query does seem to find matches. The code above is pretty much copied from the docs, except that there’s a small bug as MatchList wants pointers for &minSize, &maxSize.

It seems the built in match maker is really slow and it gives me no insight into what it does and why it takes a long time.

Have you look at updating the matchmaker settings to allow less time for matchmaking: Heroic Labs Documentation | Configuration

Is there something that I’ve fundamentally missed about how the queries and/or properties work? When using the match maker the same query does seem to find matches. The code above is pretty much copied from the docs, except that there’s a small bug as MatchList wants pointers for &minSize, &maxSize .

How are you creating the Match (match handler) and how are you persisting the match label? Remember it must be a JSON literal object:

https://heroiclabs.com/docs/nakama/concepts/server-authoritative-multiplayer/#match-label

You can only use search queries if you use a JSON value for the match label. If you instead use a simple value (e.g. "team-deathmatch" ) you can only perform an exact match using the label parameter in the Match Listings API.

I would say I don’t even query for the label as it’s set to "". So it should work? The docs for the match label don’t really say what it should be used for, other than that it’s a label. I’ve seen no reason to use it and thus use "". I assume this is a case where the docs could use some improvement.

The example here:
https://heroiclabs.com/docs/nakama/concepts/server-authoritative-multiplayer/#search-query

does not use the label either. Our MatchInit handler sets up an own MatchState struct and returns that along with a tick rate of 10 and an empty label.

This is confusing.

Yep we ought to improve this section of docs (cc @Sean )

If you set the match label (in the match handler) to a non-json object (like what the examples have shown), then you can only do exact queries using the label field in the MatchListing API.

However, if you set the label to a JSON object in the match handler, then Nakama will index the label and make it ready for search, and you can use the query field in the MatchListing API.

Ok, so what is the purpose of the label? Is it only to enable queries on the properties? I mean, can I just set it to "foo":42 and be done with it and then use the property queries to find matches?

Hm, no, that doesn’t work.

2021/12/03 14:04:15.222594 match.go:58: MatchInit: match dfd432f5-65f8-4290-a53b-71d58aa548ec initialized, label: {"foo":42}
...
2021/12/03 14:04:17.374009 battle_api.go:61: query 'mode:2' found 0 matches

Note that mode:2 is even optional, it should certainly have found the created match. Or is the label somehow significant for MatchList too? I leave it as empty in order to find matches with any label.

Ok, so what is the purpose of the label? Is it only to enable queries on the properties? I mean, can I just set it to "foo":42 and be done with it and then use the property queries to find matches?

The purpose of the label field in the MatchListing API is to do exact query look up without indexing.

Hm, no, that doesn’t work.

Your match label is {"foo": 42} whilst your query is mode:2 - this will never match.

Or is the label somehow significant for MatchList too

Yes the match label you set in the match handler directly affects the query you are performing in the MatchListing call.

The example at:

https://heroiclabs.com/docs/nakama/concepts/server-authoritative-multiplayer/#match-listings

Uses label := "skill=100-150" as an argument to MatchList. Does this mean that the label is also a form of query and this would list matches created with labels skill:111 and skill:142?

Uses label := "skill=100-150" as an argument to MatchList . Does this mean that the label is also a form of query and this would list matches created with labels skill:111 and skill:142 ?

Yes if that label was not a string but a JSON literal like:

{"skill": 100}

Then you can use the query field in the MatchList API.

Even more confusing… Isn’t my query parameter supposed to match things I set in the parameters for MatchCreate? Or are those parameters only for my own use? Let me test and see.

So given a match set up in MatchInit with a label:

	label, err := json.Marshal(map[string]int{"mode": matchMode.Ident})

i.e. {"mode":2}, then what am I supposed to use with MatchList to find it?

	limit := 1
	authoritative := true
	label := ""
	minSize := 0  
	maxSize := 10
	query := "+mode:2"
	matches, err := nk.MatchList(ctx, limit, authoritative, label, &minSize, &maxSize, query)

Or does the label also be filled in with +mode:2? No wonder this is hard to get right, the docs really need some sweet love.

AFAIK there are no “JSON literals” in Go, but I could be wrong here. The above json.Marshal should create the required JSON.

I feel so extremely stupid when I can’t grok how this is supposed to work. Granted, I’m far from the sharpest tool in the shed, but this shouldn’t be impossible.

Aha, this seems to work:

func (m *Match) MatchInit(ctx context.Context, ...) (interface{}, int, string) {

    ...
	tickRate := 10
	label := "{\"mode\":" + strconv.Itoa(matchMode.Ident) + "}"

	return state, tickRate, label
}

And then querying with:

	limit := 1
	label := ""
	minSize := 0
	maxSize := 10
	query := "+label.mode:" + strconv.Itoa(matchMode.Ident)
	matches, err := nk.MatchList(ctx, limit, true, label, &minSize, &maxSize, query)

The key here was to return the label as a string from MatchInit, ignore label when searching and add label. to the query to make it target the label.

Thank you for the help!

1 Like

I’m glad that this has worked. I apologise for the misdirections in the docs, we will improve on this.

My role is to be confused about everything and find things that don’t make sense. :slight_smile:

1 Like