[v1.3.2] MatchmakingService

MatchmakingService

Preface

Current Version: V1.3.2
Github. Asset. Uncopylocked hub/receiver game.

Check out these games that use the service

Want to get your game added to that list? Send me a message and I’ll check your game out!

Introduction

MatchmakingService is a way to easily make games that involve matchmaking. It utilizes the new MemoryStoreService for high through-put potential. MatchmakingService is as easy to use as:

(On your hub server where players queue from)

-- Obtain the service
local  MatchmakingService = require(7567983240).GetSingleton()

-- Set the game place
MatchmakingService:AddGamePlace("Map 1", 7584483307)
MatchmakingService:SetPlayerRange("Map 1", NumberRange.new(2, 2))

-- Queue players (you can call QueuePlayer from anywhere)
game.Players.PlayerAdded:Connect(function(p)
  MatchmakingService:QueuePlayer(p, "queue", "Map 1")
end)

for i, p in ipairs(game.Players:GetPlayers()) do
  MatchmakingService:QueuePlayer(p, "queue", "Map 1")
end

On the game where players are teleported to:

-- Obtain the singleton
local MatchmakingService = require(7567983240).GetSingleton()

-- It's important game servers know how large they can get. You don't really need every map here,
-- but you do need whichever map this is.
MatchmakingService:SetPlayerRange("Map 1", NumberRange.new(2, 2))

-- Tell the service this is a game server
MatchmakingService:SetIsGameServer(true)

local t1 = {}
local t2 = {}
-- Basic start function
function Start()
  print("Started")
  MatchmakingService:StartGame(_G.gameId)
  -- Simple teams for a 1v1.
  local p = game.Players:GetPlayers()
  table.insert(t1, p[1])
  table.insert(t2, p[2])
end

-- YOU MUST CALL UpdateRatings BEFORE THE GAME IS CLOSED. YOU CANNOT PUT THIS IN BindToClose!
function EndGame(winner)
  MatchmakingService:UpdateRatings(_G.ratingType, {if winner == 1 then 1 else 2, if winner == 2 then 1, else 2}, {t1, t2})
  for i, v in ipairs(game.Players:GetPlayers()) do
    -- You can teleport them back to the hub here, I just kick them
    v:Kick()
  end
end

-- Basic start game function. Will start the game once both players connect
game.Players.PlayerAdded:Connect(function(player)
  local joinData = player:GetJoinData()
  -- You should obtain the join data that you need here
  if _G.gameId == nil and joinData then
    -- Global so its accessible from other scripts if it needs to be.
    _G.gameId = joinData.TeleportData.gameCode
    _G.ratingType = joinData.TeleportData.ratingType
  end
  if #game.Players:GetPlayers() >= 2 then
    Start()
  end
end)

-- You can remove them if they leave
game.Players.PlayerRemoving:Connect(function(player)
  MatchmakingService:RemovePlayerFromGame(player, _G.gameId)
end)

-- THIS IS EXTREMELY IMPORTANT
game:BindToClose(function()
  MatchmakingService:RemoveGame(_G.gameId)
end)

Small note before we start

Due to the lack of tools that MemoryQueue allows, current this version of MatchmakingService solely uses different SortedMaps which are slightly less tuned for this process. If/when MemoryQueue gets more tools that allow us to reliably manage it, then I will write this all utilizing MemoryQueues where possible.

How does it work?

MatchmakingService utilizes the new MemoryStoreService for cross-game ephemeral memory storage. This storage is kind of like the RAM in your computer or phone, except it’s not exactly the same. There’s a great article on it here which describes more of the technical details if you’re interested in it. MemoryStores have a much higher throughput potential than other services which makes them great for queuing items that will be quickly removed or changed. It has overwrite protections as well! Basically MemoryStores are the best way to make a system like this (it will be even better when they give us more tools to manage a MemoryQueue which will speed this up even more).

Basically, however, one server will manage making matches across the entire queue. Think of this server as the centralized handler (I was thinking of ways to have this be completely random access, but it gets a little messy, though has potential to happen in the future). When a player queues for a specific skill level they will only be matched up against a certain number of people in the same skill level. The number that are put into the game depends on what you set it to (read the docs below!).

Users are in a first-in-first-out queue. This means that the first players to queue are the first players to get into a game. When a game is created it can either be joinable or not. By default it is still joinable if the game hasn’t started and the server isn’t full, the MatchmakingService will prioritize these existing games before trying to make new ones. When a game starts, by default, the server will be locked and no new joiners are permitted.

Documentation

You can find up-to-date documentation here.

Future Plans

  • Switch to MemoryQueues if/when we get more ways to manage it
  • Fully random access management that doesn’t use a central server
  • Party parity

Updates

I will be available to add new features and bug fixes. There will be a beta branch on the github for testing these new features.

167 Likes

This is awesome… Thank you :smiley:

()

6 Likes

Amazing resource and an epix use of the new memory service! 10/10! ;D

4 Likes

Work pretty well. I would use this as my Game Ideas

2 Likes

You should check this out for the skill level things :slight_smile:

4 Likes

I was actually thinking about implementing that at a base level when I first saw it a few days ago. I’ll just have to read through how it works and think of a good way to implement it. Thank you for the suggestion though!

Here’s what I’m thinking:
I’ll make some sort of MatchmakingService:GetRating(plr, ratingType) where ratingType is a string of a defined type of rating (e.g. “ranked” or “normal”) that will default if not existing. This will allow you to have many separate ratings for different queues, much like most games already work where they have a rating for ranked and a rating for unranked. While this would be for internal use when queueing a player instead of providing a skill level, you provide a ratingType and it will get the data for you out of the back end (i.e. MatchmakingService:QueuePlayer(plr, skillLevel) becomes MatchmakingService:QueuePlayer(plr, ratingType)), you could still get their rating yourself if you want to display it to them. Instead of single queue pools for each skill level, they will be put into a pool in a range of levels that gets increasingly bigger the longer they’re in queue. I will also add a default ratingType of none that basically queues all the players that were queued with it in one queue with no rating taken into account and no rating is updated at the end, a pure non-rated game (most games still use some sort of rating in unrated gameplay, but I will provide the option to skip it completely).

Feedback on this idea?

6 Likes

I’m about 70% done with my rewrite that involves adding a rating system into this service. Right now all necessary functions have been rewritten to accept these new rating values. There’s basically 2 new things, a rating type, think of this as ranked or normal (the gamemode essentailly), but it can be any string you want to call it, and a rating itself. For every rating type, a player is assigned a glicko-2 rating. So if you have 5 different rated queues you can have 5 different ratings for each queue if it’s a different gamemode. See figure below:

Glicko-2 is a pretty good rating system but it does have a flaw called volatility farming. I won’t go into detail about this flaw, but I will be looking into making some changes to glicko-2 to fix this, but it’s currently not something I’m worried about.

There will also be a queue that does not use any rating system. This will be done by using “none” as the rating. This queue will be entirely separate and have no ratings attached.

This update will not be backwards compatible with the current version.

2 Likes

I just pushed version 2.0.0-beta out! I’ve updated the post with additional information and how the system works now. This version is not fully backwards compatible with the v1 versions. I will soon add the party system and the none rating type for non-skill based match making.

5 Likes

Considering the tragedy of skill-based matchmaking, I will be using this wonderful resource. However, I have one question (idk if it’s too dumb or not): is this using SBMM ( or is it using something different)? I’m asking because I have serious concerns about SBMM regarding players and I am planning to add to my game a ranked version of a new game mode I just added.

As of version 2.0.0-beta, there is skill-based match making using the glicko-2 rating system (glicko-2 is used in games like TF2, CSGO, Splatoon and more). Players are queued into a pool based on their rating and the service prioritizes getting players of a similar rating but does expand its search every now and then if it can’t find people of the same level. I will soon be adding a none rating type that will not use any skill based match making or rating system to pool them into a queue, but I am currently working on the party system.

4 Likes

Wow, sorry I didn’t see your messages but nice work on this! I’m definitely going to be using it on a future project as this simplifies things so much! I’d love to help you in some way, if there’s anything I can do.

Any testing you can do on a larger scale to ensure that it’s stable at more than 2 players would be amazing. I can only test with 2 players, so before I make an official release version I really want to make sure it’s stable.

3 Likes

Is there a way to get how many players are in a queue?

1 Like

Not currently I will add that in with the next update though! Thanks for the suggestion!

1 Like

2.1.0 is now out with MatchmakingService:GetQueueCounts()

2 Likes

Would it be best to constantly refresh it or is there a smarter way of updating it?

Im a little confused with what you mean. If you want I could add an event you can subscribe to in code to listen to when someone gets added or removed from the queue which would be significantly more performant and wouldn’t add to the rate limits.

1 Like

Version 2.2.0-beta has been released!
Changes (breaking):

  • None

Changes (non-breaking):

  • [Addition] MatchmakingService:SetMaxPartySkillGap(newMaxGap)
  • [Addition] MatchmakingService:GetPlayerInfo(player)
  • [Addition] MatchmakingService:QueueParty(players, ratingType)
  • [Addition] MatchmakingService:GetPlayerParty(player)
  • [Addition] MatchmakingService.PlayerAddedToQueue signal.
  • [Addition] MatchmakingService.PlayerRemovedFromQueue signal.
  • [Change] MatchmakingService:SetPlayerInfo now accepts a new argument, party, which is a table of player ids in their party including the player’s own id.

Fixes:

  • None

The party system did work with 2 players, but I would appreciate if someone could test it with more to make sure it works! Please remember this is a beta, bugs may exist!

Currently the signals are not global across servers, this is planned though!

1 Like

Does this service work across servers or is it just within one server?

As long as all of the games are in the same universe it will work between them all and queue is global.

Meaning they’re like this:
df907351cea63b77f62deac109f918ca674b38d6_2_690x453

My recommendation is a hub place where players can queue up (all hub server instances would be connected, so if you have say 5000 players across 100 servers, all 100 servers would share the same queue pool). At the moment, only one game place is allowed. The game place is where players are teleported when they find a game. In the teleport data you can get information like the game type, the game code, etc and generate a map based on that data. In the future I may look into allowing multiple game places for different maps, but for now you can clone a map into the workspace based on teleport data if you need multiple maps.