§ How to · with Forge AI

How to make a Roblox matchmaking system (with AI)

By Sametcan Tasgiran, Founder & Developer·Published ·Updated

A Roblox matchmaking system needs five pieces: queue management, room creation, skill-based matching (MMR), TeleportService routing, and a queue UI. Forge AI generates all five in 1m 18s.

7 files · 280 lines · 1m 18s · 1 credit. MessagingService-coordinated cross-server queue.

Queue management

Players join queue via UI button. Server holds queue state, attempts to fill rooms continuously. UI shows estimated wait time.

Room creation

ReserveServer-based private rooms. Rooms hold 2-12 players (configurable). New room created when no matchable existing room available.

Skill-based matching

Each player has an MMR score (default 1000). Matchmaker prefers rooms with similar MMR (±100). MMR adjusts on win/loss.

TeleportService routing

Players teleport to reserved server when room fills. Teleport carries player MMR + party info via TeleportData.

Cross-server queue

MessagingService syncs queue state across all servers — players across any server can match into the same room.

Party support

Players can party up before queuing. Parties stay together when matched. Up to 4-player parties supported by default.

Files Forge AI ships for this prompt

7 files · 280 lines · 1m 18s · 1 credit

ServerScriptService/QueueManager.lua

Queue state, match attempts, MMR matching

92 lines

ServerScriptService/CrossServerSync.lua

MessagingService queue broadcast

52 lines

ServerScriptService/MMRTracker.lua

Player MMR persistence + win/loss adjustment

38 lines

ServerScriptService/PartyManager.lua

Party invites, join, leave

42 lines

ReplicatedStorage/Remotes/JoinQueue

Client → queue

instance

ReplicatedStorage/Remotes/PartyInvite

Send/accept party invites

instance

StarterGui/MatchmakingUI.lua

Queue button, wait time, party display

56 lines

Sample output: ServerScriptService/QueueManager.lua

--!strict
-- ServerScriptService/QueueManager.lua  (Forge AI · excerpt)
local Players = game:GetService("Players")
local TeleportService = game:GetService("TeleportService")
local MMRTracker = require(script.Parent.MMRTracker)

type QueueEntry = { player: Player, mmr: number, partyId: string?, joinedAt: number }

local ROOM_SIZE = 6
local MMR_TOLERANCE = 100
local PLACE_ID = 0  -- set to your room place ID

local queue: { QueueEntry } = {}

local function tryMatch()
    if #queue < ROOM_SIZE then return end

    -- Find ROOM_SIZE players with MMR within tolerance
    table.sort(queue, function(a, b) return a.mmr < b.mmr end)

    for i = 1, #queue - ROOM_SIZE + 1 do
        local window = queue[i + ROOM_SIZE - 1].mmr - queue[i].mmr
        if window <= MMR_TOLERANCE then
            -- Match found — extract these players
            local matched: { QueueEntry } = {}
            for j = i, i + ROOM_SIZE - 1 do table.insert(matched, queue[j]) end
            for j = i + ROOM_SIZE - 1, i, -1 do table.remove(queue, j) end

            -- Reserve server and teleport
            local code = TeleportService:ReserveServer(PLACE_ID)
            local players = {}
            for _, entry in ipairs(matched) do table.insert(players, entry.player) end
            TeleportService:TeleportToPrivateServer(PLACE_ID, code, players)
            return
        end
    end
end

return { join = function(p: Player) table.insert(queue, { player = p, mmr = MMRTracker:get(p), partyId = nil, joinedAt = os.clock() }); tryMatch() end }

Building a Roblox matchmaking system

Matchmaking is the invisible system that decides if your competitive Roblox game feels fair. Players don't notice good matchmaking — they just enjoy the games. Players notice bad matchmaking immediately — stomps, long queues, repeated opponents. Forge AI ships the invisible version.

The Forge AI matchmaking prompt produces a 7-file system in 1m 18s. QueueManager holds the queue state and attempts to fill rooms. MMRTracker persists per-player skill ratings. CrossServerSync uses MessagingService to share queue state across all running servers, so the entire game's player base is one matchmaking pool.

MMR-based matching is the fairness layer. Each player has a Matchmaking Rating starting at 1000. The matchmaker prefers rooms where MMR is within ±100 of all players. After a win, MMR increases by 25; after a loss, decreases by 20. The asymmetry slightly favors progression over time, which keeps players engaged.

Queue starvation is the production failure mode. New games have small player bases — strict MMR matching means long queues and players quit. Forge's mitigation: MMR tolerance widens over time (60s → ±250, 120s → ±500, 180s → any match). This guarantees a match in under 3 minutes regardless of player count.

Reserved server rooms use TeleportService:ReserveServer to spin up private game instances. When a room fills, all 6 matched players teleport simultaneously. The reserved server holds them as a closed lobby — random players cannot join mid-match. This is the standard pattern for ranked games in Roblox.

Party support is the social layer. Players can party up via PartyManager and queue together. Match attempts require the entire party to fit in the same room. This is what makes friends-playing-together work — without it, ranked games feel anti-social.

See more on the Luau generator, the game builder, or browse the full blog.

Frequently asked

How does the cross-server queue work?+

MessagingService:PublishAsync broadcasts queue events. Each server hears them and updates its local view of the global queue. Match attempts use the unified view. Race conditions on simultaneous matches are resolved by 'first to publish wins'.

What if no match is found in a long time?+

After 60 seconds in queue, MMR tolerance widens to 250. After 120 seconds, to 500. After 180 seconds, any 6 players match. This prevents queue starvation while preferring close matches when possible.

How is MMR adjusted on win/loss?+

MMRTracker uses a simplified Elo: +25 MMR on win, -20 MMR on loss. Adjustments are clamped to ±50 to prevent wild swings. MMR persists in DataStore.

How do parties stay together?+

PartyManager tracks party membership. When any party member queues, all members are added with the same partyId. Match attempts require all party members to be included in the same room or the match is rejected.

What happens if a player disconnects mid-queue?+

PlayerRemoving removes them from the queue. If they were in a party that's mid-match, the match aborts for the whole party. They forfeit any rank changes.

Related Forge AI prompts