Matchmaking: How to use MemoryStoreQueue

I’m going to start off by saying this, to pre-existing developers with http services and other matchmaking services. You should not use this unless you are paying for the http server.

Now that I’ve given that warning I will go into the tutorial.
All of the scripts that you are seeing are first-hand made. I used no tutorials and although I posted to scripting support, no one replied which told me I should probably make a tutorial to the community on how this works. I am open to advice.

Let’s start out with the functions. Just like any service start off by receiving it.

game:GetService("MemoryStoreService")

The MemoryStoreService contains SortedMaps too if you want to view these click this link to the entire overview of the service
Memory Store

Functions:

GetQueue
ReadAsync
AddAsync
RemoveAsync

Keep in mind that those are only the queue functions, they are the only ones used with queues.

GetQueue

1st variable: The key, which works exactly like a dataStore. It is even global.
2nd variable: The invisibility timeout. If a value is read by ReadAsync it cannot be read again for x, time. This one I’m going to model for you because it is very important to understand, for this demonstration it will be zero but keep it mind it can be useful.


--Incorrect usage below

local MSS = game:GetService("MemoryStoreService")

local Queue = MSS:GetQueue("Matchmaking-"..game.PlaceId.."-"..game.PrivateServerId, 30) -- defaults to 30 so I didn't have to add it but wanted to show you where it goes

--quick addition DO NOT WRITE THIS SCRIPT
function Main(plr)
     Queue:AddAsync(plr.Name, 30, 1) -- second value is very important, will explain
     wait(1)
     print("Testing")
     local read1 = Queue:ReadAsync(100, false, 2) -- 3rd value is timeout just incase it doesn't get, in this case, 100 values.
     print("Read 1: "..read1)
     wait(2)
     local read2 = Queue:ReadAsync(100, false, 2)
     print("Read 2: "..read2)

end

game.Players.PlayerAdded:Connect(function(plr) -- calls the main function
     Main(plr)
end
--Output

--[[ Multi-line comment for those that don't know
Read 1: {
 [1] = "(what the name is)"
}
Read 2: nil
]]--

As you can see because we did not wait 30 seconds the queue could not read the name because it was invisible
If we waited 30 seconds the value wouldn’t have been there because of AddAsync automatic removal timeout unless we set it over 31.

Here is the correct script for this scenario


--Correct usage below

local MSS = game:GetService("MemoryStoreService")

local Queue = MSS:GetQueue("Matchmaking-"..game.PlaceId.."-"..game.PrivateServerId, 0)

--quick addition DO NOT WRITE THIS SCRIPT

function Main(plr)
     Queue:AddAsync(plr.Name, 30, 1) -- second value is very important, will explain
     wait(1)
     print("Testing")
     local read1 = Queue:ReadAsync(100, false, 2) -- 3rd value is timeout just incase it doesn't get, in this case, 100 values.
     print("Read 1: "..read1)
     wait(2)
     local read2 = Queue:ReadAsync(100, false, 2)
     print("Read 2: "..read2)

end

game.Players.PlayerAdded:Connect(function(plr) -- calls the main function
     Main(plr)
end
--Output

--[[
Read 1: {
 [1] = "(what the name is)"
}
Read 2: {
 [1] = "(what the name is)"
}

Since the timeout for GetQueue is 0 we can read it instantly. Keep it 0 for the demonstration.

AddAsync

1st variable: Value wanted to be added
2nd variable: Automatic Removal time, keep this below a minute or 60.
3rd variable: Priority, you can change this to put parties in the front of the queue by saying 1, and solos in the back by a ridiculously high number like 1000.

The automatic removal is the most important thing. This service is pretty bad because you have to process a value for it to be able to be removed. Unfortunately, processing values are a little buggy and it removes values half the time. Making the values low ensures that you will not read the value twice because it will be automatically deleted in x time Or you can make another queue with an invisibility timeout separate from the first queue for your main matchmaking function.

RemoveAsync

1st variable: Value to remove

Put in the value DIRECTLY returned from ReadAsync.

ReadAsync

Pay attention here

1st variable: How many values for it to read, max = 100
2nd variable: If you don’t get the number of values you want, return nil if set to true. If set to nil will return all the values it has received in the 3rd variable time frame.
3rd variable: Time to read queue until it gets 1st variable values. Set to 0.01 usually.

ALL OF THESE FUNCTIONS YIELD: you do not need to do wait()

--All functions in coding format

local MSS = game.GetService:("MemoryStoreService")

local queue = MSS:GetQueue(whateveryouwanttocallit..SecurityPurposesAddSpecialCharacters..game.PlaceId, 0)

queue:AddAsync(value, timeout, priority)
local Read = queue:ReadAsync(valuesWanted, nilIfNotValuesWanted, waitTimeForValuesWanted)
print(Read)
--[[
Output: 
{
[1] = (value)
}
]]--

queue:RemoveAsync(valueDirectlyReturnedFromReadAsync)

Implementing Matchmaking Via MemoryStoreQueue

Let’s start off by receiving the services we might want. This will not be a tutorial on how to get players from touching parts or GUI. This is purely how to make a matchmaking service.

local Players = game:GetService("Players")
local MSS = game:GetService("MemoryStoreService")
local TS: game:GetService("TeleportService")
local RS = game:GetService("ReplicatedStorage")

Next, let’s set up the framework for ourselves.

local Players = game:GetService("Players")
local MSS = game:GetService("MemoryStoreService")
local TS: game:GetService("TeleportService")
local RS = game:GetService("ReplicatedStorage")
local targetPlaceID = WHATEVERYOURSIS -- LOOK HERE: this is if you want to teleport all of this stuff
local teleportOptions = Instance.new("TeleportOptions")
teleportOptions.ShouldReserveServer = true
local TeleportModule = require(RS:WaitForChild("TeleportModule"))
--^ Variables in Game

local currentServer = game.PrivateServerId
local currentAmount = 0 --just to calculate players received
local plrTable = {} -- needed to hold read

--^ tables and counters

--change these
local playersNeeded = 2
local playersWanted = 4
--change these


function Main()

end

function Add()

end

function Remove()
--unfortunately, I will not be demonstrating this today. 
--They will only be removed once they are teleported or the function is complete
end

function RemoveAll()

end

--calls

Players.PlayerAdded:Connect(function(plr)
     Add(plr)
end

game.BindToClose(function()
     RemoveAll()
end

Great! Our framework is complete. Let’s go ahead and add the Add function

local Players = game:GetService("Players")
local MSS = game:GetService("MemoryStoreService")
local TS: game:GetService("TeleportService")
local RS = game:GetService("ReplicatedStorage")
local targetPlaceID = WHATEVERYOURSIS -- LOOK HERE: this is if you want to teleport all of this stuff
local teleportOptions = Instance.new("TeleportOptions")
teleportOptions.ShouldReserveServer = true
local TeleportModule = require(RS:WaitForChild("TeleportModule"))
--^ Variables in Game

local currentServer = game.PrivateServerId
local currentAmount = 0 --just to calculate players received
local plrTable = {} -- needed to hold read

--^ tables and counters

--change these
local playersNeeded = 2
local playersWanted = 4
--change these

function Main()

end

function Add()
     	local success, err = pcall(function()
		MMNS:AddAsync(plr.Name, 30, 1) -- change timeout to whatever you want as long as it is not above 3 minutes.
	end)

	if success then
		print("Player Added to Queue: "..plr.Name)
		Main()	
	else
		print(err)
	end
end

function Remove()
--unfortunately, I will not be demonstrating this today. 
--They will only be removed once they are teleported or the function is complete
end

function RemoveAll()

end

--calls--

Players.PlayerAdded:Connect(function(plr)
     Add(plr)
end

game.BindToClose(function()
     RemoveAll()
end

Next the main function.

local Players = game:GetService("Players")
local MSS = game:GetService("MemoryStoreService")
local TS: game:GetService("TeleportService")
local RS = game:GetService("ReplicatedStorage")
local targetPlaceID = WHATEVERYOURSIS -- LOOK HERE: this is if you want to teleport all of this stuff
local teleportOptions = Instance.new("TeleportOptions")
teleportOptions.ShouldReserveServer = true
local TeleportModule = require(RS:WaitForChild("TeleportModule"))
--^ Variables in Game

local currentServer = game.PrivateServerId
local currentAmount = 0 --just to calculate players received
local plrTable = {} -- needed to hold read

--^ tables and counters

--change these
local playersNeeded = 2
local playersWanted = 4
--change these

function Main()
	local success, err = pcall(function()
		plrTable = MMNS:ReadAsync(100, false, 0.01)
	end)
	
	print(success, err)
	if plrTable ~= nil then
		print("check")
		for i, player in ipairs(plrTable) do
			currentAmount += 1
			print("processed")
		end
		
		if currentAmount >= playersNeeded then
			currentAmount = 0
			local playersSelected = {}
			local forRemoval = {} -- very important
			print("Game Found")
			if game.CreatorId ~= "0" then
				print("check")
				local playersToTP = {} -- still need
				for i, plyr in ipairs(MMNS:ReadAsync(playersWanted, false, 2)) do
					print(plyr.." Teleporting")
					local playeR = Players:FindFirstChild(plyr)
					table.insert(playersToTP, 1, playeR)
					forRemoval[i] = plyr
				end
				print(forRemoval)
				print(MMNS:ReadAsync(100, false, 0.01))
				MMNS:RemoveAsync(forRemoval)
				local teleportResult = TeleportModule.teleportWithRetry(targetPlaceID, playersToTP, teleportOptions) 
				--can be ingame via CFrames and MoveTo
			else
				print("In studio")
			end
		else
			currentAmount = 0
			print("not enough")
		end
	else
		print("error table = nil")
	end
end

function Add()
     	local success, err = pcall(function()
		MMNS:AddAsync(plr.Name, 30, 1) -- change timeout to whatever you want as long as it is not above 3 minutes.
	end)

	if success then
		print("Player Added to Queue: "..plr.Name)
		Main()	
	else
		print(err)
	end
end

function Remove()
--unfortunately, I will not be demonstrating this today. 
--They will only be removed once they are teleported or the function is complete
end

function RemoveAll()

end

--calls--

Players.PlayerAdded:Connect(function(plr)
     Add(plr)
end

game.BindToClose(function()
     RemoveAll()
end

Finally, add remove all so the server can cleanup on shutdown

local Players = game:GetService("Players")
local MSS = game:GetService("MemoryStoreService")
local TS: game:GetService("TeleportService")
local targetPlaceID = WHATEVERYOURSIS -- LOOK HERE: this is if you want to teleport all of this stuff
local teleportOptions = Instance.new("TeleportOptions")
teleportOptions.ShouldReserveServer = true
local TeleportModule = require(RS:WaitForChild("TeleportModule"))
--^ Variables in Game

local currentServer = game.PrivateServerId
local currentAmount = 0 --just to calculate players received
local plrTable = {} -- needed to hold read

--^ tables and counters

--change these
local playersNeeded = 2
local playersWanted = 4
--change these

function Main()
	local success, err = pcall(function()
		plrTable = MMNS:ReadAsync(100, false, 0.01)
	end)
	
	print(success, err)
	if plrTable ~= nil then
		print("check")
		for i, player in ipairs(plrTable) do
			currentAmount += 1
			print("processed")
		end
		
		if currentAmount >= playersNeeded then
			currentAmount = 0
			local playersSelected = {}
			local forRemoval = {} -- very important
			print("Game Found")
			if game.CreatorId ~= "0" then
				print("check")
				local playersToTP = {} -- still need
				for i, plyr in ipairs(MMNS:ReadAsync(playersWanted, false, 2)) do
					print(plyr.." Teleporting")
					local playeR = Players:FindFirstChild(plyr)
					table.insert(playersToTP, 1, playeR)
					forRemoval[i] = plyr
				end
				print(forRemoval)
				print(MMNS:ReadAsync(100, false, 0.01))
				MMNS:RemoveAsync(forRemoval)
				local teleportResult = TeleportModule.teleportWithRetry(targetPlaceID, playersToTP, teleportOptions) 
				--can be ingame via CFrames and MoveTo
			else
				print("In studio")
			end
		else
			currentAmount = 0
			print("not enough")
		end
	else
		print("error table = nil")
	end
end

function Add()
     	local success, err = pcall(function()
		MMNS:AddAsync(plr.Name, 30, 1) -- change timeout to whatever you want as long as it is not above 3 minutes.
	end)

	if success then
		print("Player Added to Queue: "..plr.Name)
		Main()	
	else
		print(err)
	end
end

function Remove()
--unfortunately, I will not be demonstrating this today. 
--They will only be removed once they are teleported or the function is complete
end

function RemoveAll()
	local success, err = pcall(function()
		local read = MMNS:ReadAsync(100, false, 5)
		MMNS:RemoveAsync(read)
	end)

	print(success, err)
	print("done")
end

--calls--

Players.PlayerAdded:Connect(function(plr)
     Add(plr)
end

game.BindToClose(function()
     RemoveAll()
end

Closing Notes

This used Roblox’s pre-existing Teleport Module, you can make your own I will link Roblox’s right here
[Teleporting Between Places | Roblox Creator Documentation](Teleporting Between Places)

Good luck!

Any advice? Feel free to comment.
Any edits? Feel free to let me know what to edit.

31 Likes

One of THE MOST underrated posts! Thank you soo much!!

5 Likes

yea, srry for asking, but let’s say, if we make an ui with multiple buttons, each button representing an map.

how can I make it so when u click the btn it’ll tp the player to the map that is said idk in like an table or value.

I’d prefer tables:D

Sorry I logged on to the dev forum for the first time in forever and I don’t know if you’re still wondering what to do.

I’d make different places in your experience for each map and teleport them to it, let roblox handle everything.

The only time I’d recommend using this system is if you were making a game like ABA

yea sorry, but I didnt explained it good, I need to also use the matchmaking code

1 Like