As you may know, roblox's matchmaking script is extremely broken and has bugs all over it. I have fixed most of the problems, but I still don't know how to fix an important one.
When searching for a game, it looks for another player's ID to create a game. If you cancel the matchmaking, your player's ID does not fully remove itself from the queue, resulting in a teleportation with two of your same ID's.
This only happens when you cancel the matchmaking, it works perfectly if you don't. But for a game, I need to to allow players to cancel matchmaking if they feel like it.
Here is the first script code:
local lookForGameEvent = game.ReplicatedStorage.LookForGameEvent local rankedList = require(game.ServerStorage.MatchmakingRankedListModule) local ratingData = game:GetService("DataStoreService"):GetDataStore("PlayerRating") local coinData = game:GetService("DataStoreService"):GetDataStore("PlayerCoin") local gemData = game:GetService("DataStoreService"):GetDataStore("PlayerGems") local teleportService = game:GetService("TeleportService") local arenaPlaceTemplateId = 1983540262 -- Table to hold the queue of players looking for a game local matchMakingQueue = {} -- Add player's id to the queue as well as the time that they entered the queue local addToMMQueue = function(playerId, enteredTime) local data = {} data.userId = playerId data.EnteredQueue = enteredTime table.insert(matchMakingQueue, data) end -- Remove player from the queue local removeFromMMQueue = function(playerId) for i, playerData in pairs(matchMakingQueue) do if playerData.userId == playerId then table.remove(matchMakingQueue, i) return end end end -- Handle player joining the game. If they are a new player we need to give them -- an initial rank. For existing players we need to fetch their rank from our -- DataStore. Lastly, we want to display the player's rank in the Leaderboard game.Players.PlayerAdded:connect(function(player) local playerData = {} -- Create a default rating of 100 if the player's data is not in the DataStore ratingData:UpdateAsync(player.userId, function(oldValue) local newValue = oldValue if not newValue then newValue = {Rating = 100} end return newValue end) coinData:UpdateAsync(player.userId, function(oldvalue2) local newValue2 = oldvalue2 if not newValue2 then newValue2 = {Coins = 10} end return newValue2 end) gemData:UpdateAsync(player.userId, function(oldvalue3) local newValue3 = oldvalue3 if not newValue3 then newValue3 = {Gems = 0} end return newValue3 end) playerData.Score = ratingData:GetAsync(tostring(player.userId)).Rating playerData.Score2 = coinData:GetAsync(tostring(player.userId)).Coins playerData.Score3 = gemData:GetAsync(tostring(player.userId)).Gems -- Display rating in Leaderboard local stats = Instance.new("Folder", player) stats.Name = "leaderstats" local displayRating = Instance.new("IntValue", stats) displayRating.Name = "Rating" displayRating.Value = playerData.Score local displayCoins = Instance.new("IntValue", stats) displayCoins.Name = "Coins" displayCoins.Value = playerData.Score2 local displayGems = Instance.new("IntValue", stats) displayGems.Name = "Gems" displayGems.Value = playerData.Score3 end) -- Adds player both to queue and list to search for match local function playerSearchingForMatch(userId, rank) local now = os.time() addToMMQueue(userId, now) rankedList:AddPlayer(userId, rank, now) end -- Remove player from list and queue when they leave the game game.Players.PlayerRemoving:connect(function(player) removeFromMMQueue(player.userId) rankedList:RemovePlayer(player.userId) end) -- Handle remote event to either add or remove player from the queue lookForGameEvent.OnServerEvent:connect(function(player, lookingForGame) if lookingForGame then print(player.Name .. " now looking for game") local enteredTime = os.time() playerSearchingForMatch(player.userId, player.leaderstats.Rating.Value) else print(player.Name .. " has left the queue") removeFromMMQueue(player.userId) rankedList:RemovePlayer(player.userId) end end) -- Returns a rank range to search through based on time waiting in the queue. -- If player has been waiting too long just return math.huge so the player can -- be matched with anyone. local getRange = function(timeWaiting) if timeWaiting < 10 then return 100 elseif timeWaiting >=10 and timeWaiting < 20 then return 200 elseif timeWaiting >=20 and timeWaiting <= 35 then return 300 end return math.huge end -- Creates place for game and teleports both players to it local startGame = function(playerAId, playerBId) local message = "" print("starting game with " .. playerAId .. " and " .. playerBId) --[[if playerAId == playerBId then print("same ID!") return end--]] -- Get both player objects local playerA = nil local playerB = nil for _, player in pairs(game.Players:GetPlayers()) do if player.userId == playerAId then playerA = player end if player.userId == playerBId then playerB = player end end -- Create arena place and get its id local arenaPlaceId = game:GetService("AssetService"):CreatePlaceAsync( "Match: " .. playerAId .. " VS. " .. playerBId, arenaPlaceTemplateId) -- Bind OnTeleport event to playerA (who is teleported first). If that teleport is successful -- then we want playerB to be teleported to the same instance local connection = playerA.OnTeleport:connect(function(teleportState, placeId) if teleportState == Enum.TeleportState.Started then local teleportStarted = os.time() -- Keep checking if playerA has arrived in other instance. while true do local success, error, placeId, arenaInstanceId = teleportService:GetPlayerPlaceInstanceAsync(playerAId) -- If playerA is in the correct place then we can teleport playerB there as well if success then -- placeId == arenaPlaceId teleportService:Teleport(arenaPlaceId, playerB) return end wait() end end end) wait(1) -- Teleport playerA to the arena teleportService:Teleport(arenaPlaceId, playerA) end -- Matchmaking loop. Cycles about every 5 seconds to match players. while true do local now = os.time() -- Cycle through queue, try to find players in range for _, mmData in pairs(matchMakingQueue) do print("attempting to find match for " .. mmData.userId) -- Get rank range to search local range = getRange(now - mmData.EnteredQueue) -- Use list to find a player in player's rank range local otherPlayerId = rankedList:FindPlayerInRange(mmData.userId, range) if otherPlayerId then -- Another player was found. Remove both players from the queue and list so they -- can't be matched with anyone else print("found player: " .. otherPlayerId) rankedList:RemovePlayer(mmData.userId) rankedList:RemovePlayer(otherPlayerId) removeFromMMQueue(mmData.userId) removeFromMMQueue(otherPlayerId) -- Start game with the two players. This function can take some times, so start a -- coroutine so the loop can continue. local thread = coroutine.create(function() startGame(mmData.userId, otherPlayerId) end) coroutine.resume(thread) end end wait(5) end
If you need the module script, please ask! Thanks!