How would I go about developing ranked matchmaking?

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear! "I want to achieve ranked matchmaking.

  2. What is the issue? Include screenshots / videos if possible! “I don’t know how to go about it.”

  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub? “I have looked around the DevForum for solutions to ranked matchmaking, but I couldn’t find anything. I have thought about using global leaderboards for the ranks, but it didn’t work.”

I am making a retro-themed FPS game. One of the ways I connect players together is through matchmaking. In this case, I want to figure out ranked matchmaking. Ranked matchmaking is connecting players together based on their rank which I don’t know how to do.

local Matchmaking = {}

local MemoryStoreService = game:GetService("MemoryStoreService")
local LocalizationService = game:GetService("LocalizationService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local HttpService = game:GetService("HttpService")
local ServerStorage = game:GetService("ServerStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local MessagingService = game:GetService("MessagingService")
local TeleportService = game:GetService("TeleportService")
local Debris = game:GetService("Debris")
local Players = game:GetService("Players")

local MainModule = require(script.Parent)

local MatchmakingInterval = 5

local QueuesTable = {
	["RandomizedQueue1"] = {
		Name = "RandomizedQueue1",
		MinimumPlayers = 2,
		MaxPlayers = 15, 
		MatchmakingType = "Locational",
		Active = true
	},
	
	["ZombieQueue1"] = {
		Name = "ZombieQueue1",
		MinimumPlayers = 1,
		MaxPlayers = 15, 
		MatchmakingType = "Locational",
		Active = true
	},
	
	["SkilledQueue1"] = {
		Name = "SkilledQueue1",
		MinimumPlayers = 2,
		MaxPlayers = 15, 
		MatchmakingType = "SkilledLocational",
		MinimumKillsForSkilled = 500,
		Active = true,
	},
}

local BRQ1List = {}
local BZQ1List = {}

Matchmaking.QueuesTable = QueuesTable
Matchmaking.MatchmakingInterval = MatchmakingInterval

function Matchmaking:GetPlayerData(player)
	if ServerStorage.GameStorage.PlayerData:FindFirstChild(player.Name) then
		local ClonedData = ServerStorage.GameStorage.PlayerData:FindFirstChild(player.Name):Clone()
		ClonedData.Parent = ServerStorage:WaitForChild("PlayerDataDump")

		return ClonedData
	else
		return false
	end
end

function Matchmaking:GetPlayerLocation(player)
	local PlayerLocation = LocalizationService:GetCountryRegionForPlayerAsync(player)

	return PlayerLocation
end

function Matchmaking:AddPlayerToQueue(player, queue, specializedLocation)
	if Players:FindFirstChild(player.Name) then
		if QueuesTable[queue] then
			local QueueInfo = QueuesTable[queue]
			local MemoryStoreQueue = MemoryStoreService:GetSortedMap(QueueInfo.Name)
			
			if QueueInfo.Active == true then
				if QueueInfo.MatchmakingType == "Locational" then
					local PlayerLocation
					-- local Priority = -1

					if specializedLocation == nil then
						PlayerLocation = Matchmaking:GetPlayerLocation(player)
					else
						PlayerLocation = specializedLocation
					end
					
					--[[
					task.spawn(function()
						local IsLocationAvailable, queuedPlayers = Matchmaking:IsSameLocationAvailable(MemoryStoreQueue, 10, PlayerLocation)

						for index, player in ipairs(queuedPlayers) do
							if queuedPlayers.Location == PlayerLocation then
								-- Priority = 1
							end
						end
					end)
					--]]

					if QueueInfo.Name:find("RandomizedQueue") then
						if not table.find(BRQ1List, player.Name) then
							local success, err = pcall(function()
								MemoryStoreQueue:SetAsync(player.UserId, {PlayerName = player.Name, PlayerId = player.UserId}, 3600)
							end)	
							if not success then
								print(err)
							else
								print("Added player.")

								if ServerStorage.GameStorage.PlayerData:FindFirstChild(player.Name) then
									local PlayerData = ServerStorage.GameStorage.PlayerData:FindFirstChild(player.Name)

									if PlayerData:FindFirstChild("CurrentQueue") then
										PlayerData:FindFirstChild("CurrentQueue").Value = QueueInfo.Name
									end
								end

								if not ServerStorage.MatchmakingQueues:FindFirstChild(QueueInfo.Name):FindFirstChild(player.Name) then
									local PlayerEntry = Instance.new("BoolValue")
									PlayerEntry.Name = player.Name
									PlayerEntry.Parent = ServerStorage.MatchmakingQueues:FindFirstChild(QueueInfo.Name)
								end
							end
						else
							print("player is already in queue.")
						end
					elseif QueueInfo.Name:find("ZombieQueue") then
						if not table.find(BZQ1List, player.Name) then
							local success, err = pcall(function()
								MemoryStoreQueue:SetAsync(player.UserId, {PlayerName = player.Name, PlayerId = player.UserId, Timestamp = os.time()}, 3600)
							end)	
							if not success then
								print(err)
							else
								print("Added player.")

								if ServerStorage.GameStorage.PlayerData:FindFirstChild(player.Name) then
									local PlayerData = ServerStorage.GameStorage.PlayerData:FindFirstChild(player.Name)

									if PlayerData:FindFirstChild("CurrentQueue") then
										PlayerData:FindFirstChild("CurrentQueue").Value = QueueInfo.Name
									end
								end

								if not ServerStorage.MatchmakingQueues:FindFirstChild(QueueInfo.Name):FindFirstChild(player.Name) then
									local PlayerEntry = Instance.new("BoolValue")
									PlayerEntry.Name = player.Name
									PlayerEntry.Parent = ServerStorage.MatchmakingQueues:FindFirstChild(QueueInfo.Name)
								end
							end
						else
							print("player is already in queue.")
						end
					end
				elseif QueueInfo.MatchmakingType == "SkilledLocational" then
					local PlayerLocation
					local PlayerData = Matchmaking:GetPlayerData(player)
					
					if PlayerData == nil or PlayerData == false then
						warn("could not place player into skilled queue because data could not be provided. (error type: data could not be retrieved [matchmaking module])")
						return
					end
					
					local Priority = -1

					if specializedLocation == nil then
						PlayerLocation = Matchmaking:GetPlayerLocation(player)
					else
						PlayerLocation = specializedLocation
					end
					
					--[[
					task.spawn(function()
						local IsLocationAvailable, queuedPlayers = Matchmaking:IsSameLocationAvailable(MemoryStoreQueue, 10, PlayerLocation)

						for index, player in ipairs(queuedPlayers) do
							if queuedPlayers.Location == PlayerLocation then
								Priority = 1
							end
						end
					end)
					--]]

					if not table.find(BRQ1List, player.Name) then
						if PlayerData.Kills.Value >= 500 then
							local success, err = pcall(function()
								MemoryStoreQueue:SetAsync(player.UserId, {PlayerName = player.Name, PlayerId = player.UserId}, 3600)
							end)	
							if not success then
								warn(err .. " (error type: queue entry denial [matchmaking module])")
							else
								print("Added player.")
								
								if ServerStorage.GameStorage.PlayerData:FindFirstChild(player.Name) then
									local PlayerData = ServerStorage.GameStorage.PlayerData:FindFirstChild(player.Name)

									if PlayerData:FindFirstChild("CurrentQueue") then
										PlayerData:FindFirstChild("CurrentQueue").Value = QueueInfo.Name
									end
								end
								
								if not ServerStorage.MatchmakingQueues:FindFirstChild(QueueInfo.Name):FindFirstChild(player.Name) then
									local PlayerEntry = Instance.new("BoolValue")
									PlayerEntry.Name = player.Name
									PlayerEntry.Parent = ServerStorage.MatchmakingQueues:FindFirstChild(QueueInfo.Name)
								end
							end
						else
							warn("player's kills were not greater than or equal to 500. (error type: non-sufficent kills value [matchmaking module])")
						end
					else
						warn("player is already in queue. (error type: trying to add player to queue they are already in [matchmaking module])")
					end
				end
			else
				warn("queue is offline. (error type: attempting to access offline queue [matchmaking module])")
			end
		else
			warn("queue does not exist. (error type: attempting to access non-existent queue [matchmaking module])")
		end
	end
end

function Matchmaking:RemovePlayerFromQueue(player, queue)
	if Players:FindFirstChild(player.Name) then
		if QueuesTable[queue] then
			local QueueInfo = QueuesTable[queue]
			local MemoryStoreQueue = MemoryStoreService:GetSortedMap(QueueInfo.Name)

			local success, err = pcall(function()
				MemoryStoreQueue:RemoveAsync(player.UserId)
			end)

			if ServerStorage.GameStorage.PlayerData:FindFirstChild(player.Name) then
				local PlayerData = ServerStorage.GameStorage.PlayerData:FindFirstChild(player.Name)

				if PlayerData:FindFirstChild("CurrentQueue") then
					PlayerData:FindFirstChild("CurrentQueue").Value = ""
				end
			end

			if table.find(BRQ1List, player.Name) then
				local index = table.find(BRQ1List, player.Name)
				table.remove(BRQ1List, index)
			end
			
			if table.find(BZQ1List, player.Name) then
				local index = table.find(BZQ1List, player.Name)
				table.remove(BZQ1List, index)
			end

			if ServerStorage.MatchmakingQueues:FindFirstChild(QueueInfo.Name):FindFirstChild(player.Name) then
				ServerStorage.MatchmakingQueues:FindFirstChild(QueueInfo.Name):FindFirstChild(player.Name):Destroy()
			end
		end
	end
end

function Matchmaking:RunMatchmaking()
	task.spawn(function()
		local QueueName = "RandomizedQueue1"
		local QueueInfo = QueuesTable[QueueName]

		while true do
			local success, playersInQueue = pcall(function()
				return MemoryStoreService:GetSortedMap(QueueName):GetRangeAsync(Enum.SortDirection.Descending, 15)
			end)

			if success then
				for _, playerData in ipairs(playersInQueue) do
					local DataValue = playerData.value

					if not table.find(BZQ1List, DataValue.PlayerName) then
						table.insert(BZQ1List, DataValue.PlayerName)
						print(#playersInQueue)
						print(#BZQ1List)
					end
				end

				if #BZQ1List >= 2 then
					for _, playerData in ipairs(BZQ1List) do
						local player = Players:FindFirstChild(tostring(playerData.PlayerName))
						if player then
							--player:SetAttribute("MatchFound", true)
						end
					end

					for _, data in ipairs(BZQ1List) do
						if Players:FindFirstChild(tostring(data)) then
							local success, result = pcall(function()
								return Players:GetUserIdFromNameAsync(tostring(data))
							end)

							if Players:FindFirstChild(tostring(data)) then
								if ServerStorage.GameStorage.PlayerData:FindFirstChild(tostring(data)) then
									local PlayerData = ServerStorage.GameStorage.PlayerData:FindFirstChild(tostring(data))

									if PlayerData:FindFirstChild("CurrentQueue").Value == QueueName then
										ReplicatedStorage.Remotes.MatchFoundVisual:FireClient(Players:FindFirstChild(tostring(data)))

										local TeleportOptions = Instance.new("TeleportOptions")
										local TeleportData = {
											Gamemode = MainModule:ToBase64("Randomized")
										}
										TeleportOptions:SetTeleportData(TeleportData)

										wait(2)
										local success2, err = pcall(function()
											TeleportService:TeleportAsync(0, {Players:FindFirstChild(tostring(data))}, TeleportOptions)
										end)
									end
								end
							end

							if success then
								print(result)
								MemoryStoreService:GetSortedMap(QueueName):RemoveAsync(Players:FindFirstChild(tostring(data)).UserId)
							end
						end
					end

					for index, entry in ipairs(ServerStorage.MatchmakingQueues:FindFirstChild(QueueInfo.Name):GetChildren()) do
						entry:Destroy()
					end

					table.clear(BRQ1List)
				end
			end

			wait(Matchmaking.MatchmakingInterval)
		end
	end)
	
	task.spawn(function()
		local QueueName = "ZombieQueue1"
		local QueueInfo = QueuesTable[QueueName]

		while true do
			local success, playersInQueue = pcall(function()
				return MemoryStoreService:GetSortedMap(QueueName):GetRangeAsync(Enum.SortDirection.Descending, 15)
			end)

			if success then
				for _, playerData in ipairs(playersInQueue) do
					local DataValue = playerData.value

					if not table.find(BZQ1List, DataValue.PlayerName) then
						table.insert(BZQ1List, DataValue.PlayerName)
						print(#playersInQueue)
						print(#BZQ1List)
					end
				end

				if #BZQ1List >= 2 then
					for _, playerData in ipairs(BZQ1List) do
						local player = Players:FindFirstChild(tostring(playerData.PlayerName))
						if player then
							--player:SetAttribute("MatchFound", true)
						end
					end

					for _, data in ipairs(BZQ1List) do
						if Players:FindFirstChild(tostring(data)) then
							local success, result = pcall(function()
								return Players:GetUserIdFromNameAsync(tostring(data))
							end)

							if Players:FindFirstChild(tostring(data)) then
								if ServerStorage.GameStorage.PlayerData:FindFirstChild(tostring(data)) then
									local PlayerData = ServerStorage.GameStorage.PlayerData:FindFirstChild(tostring(data))

									if PlayerData:FindFirstChild("CurrentQueue").Value == QueueName then
										ReplicatedStorage.Remotes.MatchFoundVisual:FireClient(Players:FindFirstChild(tostring(data)))
										
										local TeleportOptions = Instance.new("TeleportOptions")
										local TeleportData = {
											Gamemode = MainModule:ToBase64("Zombie Outbreak")
										}
										TeleportOptions:SetTeleportData(TeleportData)

										wait(2)
										local success2, err = pcall(function()
											TeleportService:TeleportAsync(0, {Players:FindFirstChild(tostring(data))}, TeleportOptions)
										end)
									end
								end
							end

							if success then
								print(result)
								MemoryStoreService:GetSortedMap(QueueName):RemoveAsync(Players:FindFirstChild(tostring(data)).UserId)
							end
						end
					end

					for index, entry in ipairs(ServerStorage.MatchmakingQueues:FindFirstChild(QueueInfo.Name):GetChildren()) do
						entry:Destroy()
					end

					table.clear(BRQ1List)
				end
			end

			wait(Matchmaking.MatchmakingInterval)
		end
	end)
end

return Matchmaking

The code block above is my current matchmaking code. (I know that the place ids are 0, I don’t want to leak anything.)

If you can provide anything, I’ll try to make use of it.

Just loop through all the players waiting to be deployed and check their rank. then put them in a table just for that rank. teleport those table’s to their own respective games.

Thank you. Didn’t think of it that way. I’ll be using this.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.