So recently I have been trying to make a Quick Play Matchmaking system for my Tower Defense game where a Player can Queue up for either a Solo, Duo, Trio, or a Squad match. Although I have run into an issue where when queueing for a Match it adds a Duplicate of the Player’s User ID to the Queue Table. For context I had set up a test game where the main place was only allowed one player per server so Me and my alt account would be placed in separate servers. And when I had queued for a Duo Match on my main account before I had even queued up for a Duo match on my alt account a Match was instantly found and teleported my main account into the match. I then checked the developer console and in the script there is a print statement that prints out when a match is starting and for which players the match is starting for. And I saw my User ID pasted in twice. I then tried queuing up for a Trio match on both my accounts and it worked and it still yet again showed my User ID Twice as seen here:
I had implemented multiple checks to remove any duplicate ID’s from the table but it still creates a duplicate. Here is the entire server script:
local QuickPlayQueueEvent = game.ReplicatedStorage.MatchRS.QuickPlayQueue
local Players = game:GetService("Players")
local TeleportService = game:GetService("TeleportService")
local MessagingService = game:GetService("MessagingService")
local RunService = game:GetService("RunService")
local PLACE_ID = 0 -- Match ID
local QUEUE_UPDATE_TOPIC = "QueueUpdate"
local MATCH_FOUND_TOPIC = "MatchFound"
local matchmakingQueue = {
solo = {},
duo = {},
trio = {},
squad = {}
}
local reservedServerCodes = {}
local CHECK_INTERVAL = 5 -- Seconds between each match check
-- Utility function to get table size
local function tableSize(tbl)
local size = 0
for _ in pairs(tbl) do
size = size + 1
end
return size
end
-- Function to remove duplicate entries of a player in the queue
local function removeDuplicateFromQueue(playerId, queueType)
if matchmakingQueue[queueType] then
-- Loop through the queue and remove any duplicate entries
for id, _ in pairs(matchmakingQueue[queueType]) do
if id == playerId then
matchmakingQueue[queueType][id] = nil -- Remove the duplicate
print("Removed duplicate entry for player with UserID: " .. playerId)
end
end
end
end
-- Function to check if a player is already in the queue
local function isPlayerInQueue(queueType, playerId)
return matchmakingQueue[queueType] and matchmakingQueue[queueType][playerId] ~= nil
end
-- Function to queue a player, with strict check to prevent duplicates
local function queuePlayer(player, queueType)
if matchmakingQueue[queueType] then
-- Ensure player is not already in the queue
if not isPlayerInQueue(queueType, player.UserId) then
matchmakingQueue[queueType][player.UserId] = player.UserId
print(player.Name .. " queued for " .. queueType)
MessagingService:PublishAsync(QUEUE_UPDATE_TOPIC, matchmakingQueue)
else
-- If player is already in the queue, remove duplicate
print(player.Name .. " is already in the queue. Removing duplicate...")
removeDuplicateFromQueue(player.UserId, queueType)
matchmakingQueue[queueType][player.UserId] = player.UserId -- Re-add correctly
end
end
end
-- Function to broadcast queue updates
local function broadcastQueueUpdate()
local success, result = pcall(function()
MessagingService:PublishAsync(QUEUE_UPDATE_TOPIC, matchmakingQueue)
end)
if not success then
warn("Failed to publish queue update: " .. result)
end
end
-- Function to handle updates from other servers
local function onQueueUpdate(message)
local incomingQueueData = message.Data
-- Merge incoming queue data, but avoid duplicating any player
for queueType, incomingPlayers in pairs(incomingQueueData) do
for userId, _ in pairs(incomingPlayers) do
if not isPlayerInQueue(queueType, userId) then
matchmakingQueue[queueType][userId] = userId
end
end
end
end
-- Function to check if enough players are in the queue for a match
local function checkForMatch()
for queueType, players in pairs(matchmakingQueue) do
local teamSize
if queueType == "solo" then
teamSize = 1
elseif queueType == "duo" then
teamSize = 2
elseif queueType == "trio" then
teamSize = 3
elseif queueType == "squad" then
teamSize = 4
end
-- Deduplicate any players in case they somehow appear twice in the queue
local uniquePlayers = {}
for userId, _ in pairs(players) do
if not uniquePlayers[userId] then
uniquePlayers[userId] = true
else
-- Remove duplicate player
--players[userId] = nil
table.remove(uniquePlayers, userId)
print("Removed duplicate player with UserID: " .. userId)
end
end
-- Now check if the required number of players is met
if teamSize and tableSize(players) >= teamSize then
print("Starting match for " .. queueType)
local matchPlayers = {}
for userId, _ in pairs(players) do
table.insert(matchPlayers, userId)
matchmakingQueue[queueType][userId] = nil -- Remove from queue
if #matchPlayers == teamSize then
break
end
end
-- Only proceed if we have exactly enough players
if #matchPlayers == teamSize then
print("Match found for players: " .. table.concat(matchPlayers, ", "))
-- Reserve a server for the match
local success, reservedServerCode = pcall(function()
return TeleportService:ReserveServer(PLACE_ID)
end)
if success and reservedServerCode then
reservedServerCodes[table.concat(matchPlayers, "-")] = reservedServerCode
-- Publish the match found message
local successPublish, result = pcall(function()
return MessagingService:PublishAsync(MATCH_FOUND_TOPIC, {
teamSize = teamSize,
playerIds = matchPlayers,
reservedServerCode = reservedServerCode
})
end)
if not successPublish then
warn("Failed to publish match found message: " .. result)
end
else
warn("Failed to reserve server: " .. (reservedServerCode or "Unknown error"))
end
end
else
print("Not enough players for " .. queueType .. " match. Current: " .. tableSize(players))
end
end
end
-- Function to handle match found notifications
local function onMatchFound(message)
local matchData = message.Data
local reservedServerCode = matchData.reservedServerCode
local playerIds = matchData.playerIds
for _, playerId in ipairs(playerIds) do
local player = Players:GetPlayerByUserId(playerId)
if player then
game.ReplicatedStorage.MatchRS.MatchFoundEvent:FireClient(player)
TeleportService:TeleportToPlaceInstance(PLACE_ID, reservedServerCode, player)
end
end
end
-- Subscribe to MessagingService topics
local function subscribeToTopics()
local success, result
-- Subscribe to the match found topic
success, result = pcall(function()
MessagingService:SubscribeAsync(MATCH_FOUND_TOPIC, onMatchFound)
end)
if not success then
warn("Failed to subscribe to match found topic: " .. result)
end
-- Subscribe to queue updates
success, result = pcall(function()
MessagingService:SubscribeAsync(QUEUE_UPDATE_TOPIC, onQueueUpdate)
end)
if not success then
warn("Failed to subscribe to queue update topic: " .. result)
end
end
-- Event for queuing players
QuickPlayQueueEvent.OnServerEvent:Connect(function(player, queueType)
-- Ensure player is not already in any queue
if not isPlayerInQueue(queueType, player.UserId) then
queuePlayer(player, queueType)
else
print(player.Name .. " is already in a queue!")
end
end)
-- Continuous match checking
local function startContinuousMatchCheck()
while true do
checkForMatch()
wait(CHECK_INTERVAL)
end
end
-- Start the system
subscribeToTopics()
startContinuousMatchCheck()