Skip to content

A real-time matchmaking system, built with Elixir and focused on high-performance, scalability, and massive concurrency. Its main goal is to group users with compatible profiles or criteria, forming pairs or groups automatically based on configurable rules.

Notifications You must be signed in to change notification settings

matheuscamarques/matchmaking_ex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MatchmakingEx

A simple, robust, and extensible matchmaking system for Elixir applications, built on OTP principles.

MatchmakingEx provides a complete workflow for matching players, including a central queue, filter-based searching, and a two-way confirmation system where both players must accept the match.


Features

  • Simple API: Easy-to-use functions for finding matches and managing player state.
  • Filter-Based Matching: Match players based on custom criteria like skill, rank, or game mode.
  • Two-Way Match Confirmation: Ensures both players are ready by requiring them to accept a pending match.
  • Configurable Timeouts: Matches are automatically cancelled if not confirmed within a specified time.
  • Automatic Re-queuing: Players who accept a cancelled match are automatically put back in the queue.
  • Robust and Scalable: Built with GenServers, Supervisors, and a Registry for fault-tolerance and performance.

Installation

Add matchmaking_ex to your list of dependencies in mix.exs:

def deps do
  [
    {:matchmaking_ex, "~> 0.1.0"}
  ]
end

Then run:

mix deps.get

Getting Started

Integrating MatchmakingEx into your application involves three main steps:

  1. Adding it to your supervision tree
  2. Starting player processes
  3. Handling the matchmaking flow

1. Add to Your Supervision Tree

In your application.ex:

# lib/my_app/application.ex
defmodule MyApp.Application do
  use Application

  @impl true
  def start(_type, _args) do
    children = [
      # Your other application processes
      {Registry, keys: :unique, name: MatchmakingEx.Registry},
      {DynamicSupervisor, strategy: :one_for_one, name: MatchmakingEx.PendingMatchSupervisor},
      MatchmakingEx.Server
    ]

    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

2. Start Player Processes

Each user who wants to find a match needs their own dedicated Player process.

# When a player with the ID "player-123" connects:
{:ok, _pid} = MatchmakingEx.Player.start_link("player-123")

3. The Matchmaking Flow

A. Find a Match

# Simple search with no filters
MatchmakingEx.Player.search("player-123")

# Search with filters
filters = %{rank: "gold", mode: "1v1"}
MatchmakingEx.Player.search("player-456", filters)

B. Handle Match Confirmation

When a match is found, both players receive a :match_ready_for_confirmation message.

# Accept the match
MatchmakingEx.Player.accept("player-123")

# Or decline
MatchmakingEx.Player.decline("player-456")

C. Handle Final Status

If both players accept, a :match_confirmed message is sent.

If one declines or times out, a :match_cancelled message is sent and accepted players are re-queued.


Matchmaking State Machine (FSM)

stateDiagram-v2
    [*] --> WaitingConfirmation : Match Started

    WaitingConfirmation --> MatchConfirmed : Both Accept (✅, ✅)
    note right of MatchConfirmed
        Result:
        - Match Confirmed
        - End of cycle
    end note

    WaitingConfirmation --> MatchRejected : P1 Rejects (❌, Any)
    note right of MatchRejected
        Result:
        - P1 enters cooldown
        - P2 returns to queue (if accepted)
        - End of cycle
    end note

    WaitingConfirmation --> MatchRejected : P2 Rejects (Any, ❌)
    note right of MatchRejected
        Result:
        - P2 enters cooldown
        - P1 returns to queue (if accepted)
        - End of cycle
    end note

    WaitingConfirmation --> MatchTimeout : Timeout (15s)
    note right of MatchTimeout
        Result:
        - No cooldown applied
        - Players who accepted return to queue
        - End of cycle
    end note

    MatchConfirmed --> [*]
    MatchRejected --> [*]
    MatchTimeout --> [*]
Loading

Truth Table for Match Confirmation

This table defines the expected outcome of each possible combination of player responses.

P1 P2 Status P1 Cooldown P2 Cooldown P1 Back to Queue P2 Back to Queue Scenario
✅ Accept ✅ Accept :confirmed No No No No 1. Both Accept
✅ Accept ❌ Reject :rejected No Yes Yes No 2. P1 Accepts, P2 Rejects
❌ Reject ✅ Accept :rejected Yes No No Yes 3. P1 Rejects, P2 Accepts
❌ Reject ❌ Reject :rejected Yes Yes No No 4. Both Reject
⏳ Timeout ⏳ Timeout :timeout No No No No 5. Both Timeout
✅ Accept ⏳ Timeout :timeout No No Yes No 6. P1 Accepts, P2 Timeout
⏳ Timeout ✅ Accept :timeout No No No Yes 7. P1 Timeout, P2 Accepts
❌ Reject ⏳ Timeout :rejected Yes No No No 8. P1 Rejects, P2 Timeout
⏳ Timeout ❌ Reject :rejected No Yes No No 9. P1 Timeout, P2 Rejects

Logical Rules

  • Confirmed Match: Only occurs when both players accept.
  • Rejection Is Punitive: Any player who rejects is placed in cooldown and removed from the queue.
  • Timeout Is Not Punitive: A timeout does not penalize the player.
  • Innocent Requeue: Players who accept but the match fails are re-queued.
  • Rejection Overrides Timeout: If one rejects while the other times out, the rejection takes precedence.

Full Example in iex

$ iex -S mix
# 1. Start processes for two players
iex> MatchmakingEx.Player.start_link("aragorn")
{:ok, #PID<...>}

iex> MatchmakingEx.Player.start_link("gimli")
{:ok, #PID<...>}

# 2. Both search with compatible filters
iex> MatchmakingEx.Player.search("aragorn", %{quest: "fellowship"})
:ok

iex> MatchmakingEx.Player.search("gimli", %{quest: "fellowship"})
:ok

# Console output:
# [Player aragorn] Match ready for confirmation! (ID: 1511933547)
# Players: ["aragorn", "gimli"]
# You have 15 seconds to accept.
# To accept: MatchmakingEx.Player.accept("aragorn")
# To decline: MatchmakingEx.Player.decline("aragorn")

About

A real-time matchmaking system, built with Elixir and focused on high-performance, scalability, and massive concurrency. Its main goal is to group users with compatible profiles or criteria, forming pairs or groups automatically based on configurable rules.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages