Hello there,
In this tutorial, you will be able to learn how to make a cross-server matchmaking system based on game modes or simply matchmaking across servers.
You need to know these before we start
- What is a data store, key, and value
- Basics of scripting
- Knowing what is a proximity prompt and remote events/functions
- Client and Server side differences and understanding
Let’s begin
Step 1
Make your GUI and the proximity prompt or click detector based on how you want players to join the queue.
Here’s what I made for this tutorial:
Step 2
Alright since you have done your front-end work or Gui and trigger action
It’s time to make them work
Now, we should tell the client to show the Gui on top when he/she joins the queue, how can we do this? We use a remote event and communicate between server and client, it’s that easy : )
Now add a remote event, name it whatever you want, and add a script in ServerScriptService
Step 3
Now firing the remote and receiving the remote from the client
Add a script in the place where you triggering the action (ProximityPrompt or ClickDetector etc)
If you took click detector:
local clickdetector = script.Parent -- make sure you keep this correct
clickdetector.MouseClick:Connect(function(pl)
game.ReplicatedStorage.ConfirmQueueEvent:FireClient(plr,true) -- your firing to the player, and here true means your says confirm queue is true, it means it will tell the client that queue is confirmed
end)
If you took ProximityPrompt:
local proxprompt = script.Parent
proxprompt.Triggered:Connect(function(plr)
game.ReplicatedStorage.ConfirmQueueEvent:FireClient(plr,true) -- your firing to the player, and here true means your says confirm queue is true, it means it will tell the client that queue is confirmed
end)
Now you are firing successfully when the player triggers the function
Now on client, we make the GUI appear when the event is received by the client.
game.ReplicatedStorage.ConfirmQueueEvent.OnClientEvent:Connect(function(confirmed)
if confirmed == true then
script.Parent.Frame.Visible = true
else
script.Parent.Frame.Visible = false
end
end)
Now in case the queue got cancelled due to an error or any reasons in the main script later, we can fire this event again with false so the GUI will be hidden, here confirmed is the boolean we fired along with the event before if you remember, check it out.
Now change the Visible
property of your GUI to false
Run it and see it work in action for now
Congrats you finished 40% of this tutorial successfully:)
Step 4
Now comes the main part, you should make it work right, shouldn’t you?
So the player should join the queue when the event is triggered, along with showing UI it should add you to the queue.
So add a bindable event in the replicated storage and name it whatever you want as usual.
Always remember we can fire any data while firing a remote event/function or bindable event/function, it can be a 2 letter word or even a big dictionary of data
Now we will tell the main script which handles the match making process to add the player
Add this 1 line below or above the function where you fire the ConfirmQueueEvent to the player
game.ReplicatedStorage.JoinQueueEvent:Fire(plr,"GameMode1")
Now you can receive it from the main script
Step 5
Making the core script for this matchmaking process
We start by defining some variables for the script
local mss = game:GetService("MemoryStoreService")
local ms1 = mss:GetSortedMap("GameMode1")
local serverid = game.JobId
Workaround (How does this process work)
Now imagine it as a clothing rack, when you keep some new clothing, you keep it at the front and then when you add another piece of clothing, that will be added to the front too and the old clothing will be sent back and back.
Similarly, you add a value to this MemoryStoreService and then it will be at first, and slowly it will you will be adding more data and its position in the queue will go back, so what we do is, we read the players in the queue from the back, it means oldest player is first to get in the queue, so this will avoid a lot of time wasting for the person, and imagine a lot of players play your game, and if you do last player is first to get in queue, imagine some player missed out, and new players keep on coming, and until the new players joining queue stops that player won’t get the queue, so to avoid this chaos we do first player is first in the queue, aka descending order based system.
Now when you add players to the queue, there should be 1 more thing, you should confirm a queue and teleport them to the round or another place, you cannot do it from every server, imagine there are 100 servers, and the queue gets confirmed on 100 servers, only 1 server will be able to teleport them and all other 99 servers will error, player not found in lobby or in game to teleport.
So here comes the concept of host server. Only one server will be managing this confirming process of a queue, it might be mode-based too. For example if easy, medium, and hard were modes, I would have 3 hosts, and each host would be managing each mode, this would avoid a lot of work on a single server and an organized process.
Also, one thing is, the player can join the queue only once, so we keep an attribute such as IsInQueue
for the player if he/she joins the queue, and before adding a player to the queue, we check if they are in the queue already by seeing if they have this attribute simply.
Now we add 2 more variables and a function
local mss = game:GetService("MemoryStoreService")
local ms1 = mss:GetSortedMap("GameMode1")
local serverid = game.JobId
local isserverhost = false
local dss = game:GetService("DataStoreService")
local ds1 = dss:GetDataStore("HostServerData")
local function addplrtoqueue(plr,mode)
if plr:GetAttribute("IsInQueue") ~= nil then
return
end
plr:SetAttribute("IsInQueue")
if mode == "GameMode1" then
ms1:SetAsync(tostring(plr.UserId),game.JobId)
end
end
game.ReplicatedStorage.JoinQueueEvent.Event:Connect(function(plr,gamemode)
addplrtoqueue(plr,gamemode)
end)
And we are gonna use UpdateAsync
instead of SetAsync
for being more efficient here
So we add a starting value for our DataStore
Run this in the command bar:
game:GetService("DataStoreService"):GetDataStore("HostServerData"):SetAsync("HostData",{})
Also note that if you have a different name for your DataStore, replace HostServerData with that name
The script:
local mss = game:GetService("MemoryStoreService")
local ms1 = mss:GetSortedMap("GameMode1")
local ms = game:GetService("MessagingService")
local serverid = game.JobId
local isserverhost = false
local dss = game:GetService("DataStoreService")
local serverhostforwhatmode = ""
local ds1 = dss:GetDataStore("HostServerData")
local numberofplayersperround = 5 -- keep it as any number you want
local tps = game:GetService("TeleportService")
-- if your teleporting a player to that place if they get confirmed
local placeid = 000 --
local dataretrived = ds1:GetAsync("HostData")
local function checkforconfirmedqueue(gamemode)
if isserverhost == true then
local playerlist = ms1:GetRangeAsync(Enum.SortDirection.Descending,5)
if #playerlist == numberofplayersperround then
local createdplaceid = tps:ReserveServer(placeid)
local missingplayertable = {}
local playerstobeteleported = {}
for i,v in pairs(playerlist) do
if game.Players:FindFirstChild(v) then
table.insert(playerstobeteleported,game.Players:FindFirstChild(v))
else
table.insert(missingplayertable,v)
end
end
tps:TeleportToPrivateServer(placeid,createdplaceid,playerstobeteleported)
if #playerstobeteleported < numberofplayersperround then
ms:PublishAsync("teleportplayer",{["ServerId"] = createdplaceid,["Players"] = missingplayertable})
end
end
else
if dataretrived[gamemode] == nil then
isserverhost = true
dataretrived[gamemode] = serverid
ds1:UpdateAsync("HostData",dataretrived)
ms:PublishAsync("updatedata",dataretrived)
end
end
end
ms:SubscribeAsync("updatedata",function(message)
dataretrived = message.Data or ds1:GetAsync("HostData")
-- update the host data of servers when host is changed
end)
ms:SubscribeAsync("teleportplayer",function(message)
local playerlist = message.Data["Players"]
local serverid = message.Data["ServerId"]
for i,v in pairs(playerlist) do
if game.Players:FindFirstChild(v) then
-- if that player is in the game, then teleport him to the round
tps:TeleportToPrivateServer(placeid,serverid,game.Players:FindFirstChild(v))
end
end
end)
local function addplrtoqueue(plr,mode)
if plr:GetAttribute("IsInQueue") ~= nil then
return
end
plr:SetAttribute("IsInQueue")
if mode == "GameMode1" then
ms1:SetAsync(plr.Name,game.JobId)
end
checkforconfirmedqueue(mode)
end
game.ReplicatedStorage.JoinQueueEvent.Event:Connect(function(plr,gamemode)
addplrtoqueue(plr,gamemode)
end)
-- removing the server as host when server is closing
game.Players.PlayerRemoving:Connect(function(plr)
if isserverhost == true and #game.Players:GetPlayers() <= 1 then
dataretrived[serverhostforwhatmode] = nil
ms:PublishAsync(dataretrived)
end
-- if player is in queue, remove him from the queue
if plr:GetAttribute("IsInQueue") == true then
ms1:RemoveAsync(plr.Name)
end
end)
I hope you understood the workaround and the script accordingly.
Congrats you finished the tutorial
Tutorial is done
You made your working cross-server match-making in just 75 Lines of code and it works alright.
Remember you can always edit your script, and match it according to your game concept and system and this is only a basic full tutorial of making a match-making system. Please don’t copy-paste scripts, understand it and you will become an expert at it.
Thank you for reading and coming this far.
Have a nice day!