Ranking system for authoritative multiplayer

From my understanding clients specify the parameters for their matchmaking, (? Is there a way to do it in an authoritative way, let the server decide on what basis does it match users? I don’t mind allowing users to add parameters like their region but I’d like to specify the metrics for fair matchmaking myself rather than trusting the user.

Basically, I want a simple ranking/ MMR system that indicates the users’ skill level and then let the users be matched based on that.

Best idea, on the top of my head, is to basically check for parameters in the MakeMatch fun and return nil if it isn’t a proper match.

Are you asking how to develop a ranking system or how to use ranks and MMR values in matchmaking?

@Mahdad-Baghani I’ll formulate it better, it was really vague, basically is this possible, “check for parameters in the MakeMatch fun and return nil if it isn’t a proper match” (this is far-fetched, but basically if someone tempered with the request)?

And to add to this now, yes how do I develop this kind of a rating system with MMR values that indicate your skill score? I would probably want to store this rating inside users’ metadata and update it after matches, and then use this value for matchmaker query?

As for your second question: Yes. I think you are on the right track. Basically, you handle the match rewards, xp gains, rank gains, etc. in your match handler; Then you can add these levelups to your player stats. The best place is metadata as the name suggests, but cannot see a reason why you cannot store it in storage or even wallet (if you are too caring about user privacy :slight_smile: ).

For your first question, I think you can use Nakama Mathcmaker for that:

const query = "+properties.region:europe +properties.rank:>=5 +properties.rank:<=10";
const minCount = 2;
const maxCount = 4;

const stringProperties = {
  region: "europe"
const numericProperties = {
  rank: 8

var ticket = await socket.addMatchmaker(query, minCount, maxCount, stringProperties, numericProperties);

use this example as it simply, and I quote, “searches for opponents that must be in europe and must have a rank between 5 and 10, inclusive”.

Now the only remaining thing is trusting the player and the possibility of him/her lying about his rank, MMR, etc. Suppose this function is hooked to handle matchmaking requests using initializer.RegisterMatchmakingMatched:

func doMatchmaking(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, entries []runtime.MatchmakerEntry) (string, error) {
	for i, e := range entries {
		// fetch user metadata for entries[i].GetPresence()
		// check if the actual rank or MMR matches what he/she provided in the query
		for k, v := range e.GetProperties() {
			logger.Info("Matched on '%s' value '%v'", k, v)
			// return nil if a presence has not been honest with his "rank" property, e.g.

	matchId, err := nk.MatchCreate(... params passed to match create)
	if err != nil {
		return "", err

	return matchId, nil
1 Like

@Mahdad-Baghani Yeah this is the perfect answer, exactly what I was looking for, thank you!

1 Like

This is one of the use cases before hooks are designed for - check if input from the client is acceptable, or outright discard it and replace it with designated values.

In your case I’d set up a before hook on the MatchmakerAdd operation. In this hook look up the player information from their metadata (rank, MMR, whatever is relevant) and replace any incoming matchmaking parameters/filters with values you compute based on the account information you just looked up.

This means you can then rely on the integrity of information in the matchmaker, so no “fake” player ranks are ever submitted and you can create the authoritative match as @Mahdad-Baghani suggests but with no need to check the matchmaker data. Either option will work, but this one ensures there will never be any “invalid” matches formed, and no need to send players back to matchmaking as a result. :+1:


Yeah, your approach is the the best one possible, since it does not waste server resources on DB read operations for a possible “tampered” matchmaking request that will never be fulfilled.