GitHub Icon

Platform

Solutions

Resources

Company

How to add matchmaking to a multiplayer first-person shooter (FPS) game

Key Insights

Key Insights

Key Insights

Edgegap's matchmaker is fully-managed, infinitely customizable matchmaking system that optimally group players worldwide – and its usage is free during development of your first-person shooter multiplayer game.

It is also the only matchmaking system (that we know of) with latency-based matchmaking rules to provide the ideal online multiplayer experience for your game regardless of engine (Unity, Unreal, etc.) or game services (EOS, UGS, PlayFab, Heroic Labs Nakama, Braincloud, etc.).

As our matchmaker is based on parameters, there is no need to write code. Integration is therefore very easy and if needed, our onboarding guides you every step of the way.

When your game is live, as our matchmaking system is fully managed, you do not need handle infrastructure, bugs, outages, scalability, or database management. We take care of it all for you. Reducing your DevOps workload to nearly zero.

How to integrate matchmaking in your multiplayer first-person-shooter (FPS) game

-> This article is based on Matchmaking documentation. If you encounter issues or discrepancies, please make sure to refer to the original guide, as they are more frequently kept up to date.

The following example will help you test the core matchmaking player flow, namely:

There are five steps implement our matchmaker to your game:

  1. The first step to is create an account and use our FPS game example. Voilà, you’re (technically) halfway done! You would only need to integrate the matchmaker in your game (see step 5).

  2. Now, you should never blindly follow a JSON example you found on the internet, and thus it is strongly recommended to adapt the rules above to your game. Step 2 (“Explore Configuration”) is our “how to read” that goes into the function of each matchmaking rules functions (“Explore Configuration”).

  3. Step 3 (“Review Instance Details”) covers your personal, specific matchmaker as to ensure it deployed and works with your game’s design.

  4. Step 4, as the name implies (“4. Test Ticket API”), is all about testing your matchmaking requests from players are received by the matchmaker, called tickets.

  5. Step 5 (“Integrate Matchmaking in your Game”) highlights how to integrate the matchmaker within your engine’s project.

If you have troubleshooting challenges, our in-depth Learning Center has additional troubleshooting tips.

1. Setting up the Free Tier

Register for your free Edgegap account, and navigate to the Matchmaker dashboard page.

From there, click on Create Matchmaker first, then input:

  • A name for your matchmaker – which is purely for your own reference, e.g. quickstart-dev,

  • Then, upload the following simple example as a JSON configuration below for your FPS game:

{
  "version": "1.0.0",
  "max_deployment_retry_count": 3,
  "ticket_expiration_period": "5m",
  "ticket_removal_period": "1m",
  "profiles": {
    "casual-example": {
      "application": {
        "name": "my-game-server=>CHANGE-THIS-NAME-HERE",
        "version": "2024.01.30-16.23.00-UTC=>CHANGE-THIS-HERE "
      },
      "rules": {
        "initial": {
          "match_size": {
            "type": "player_count",
            "attributes": {
              "team_count": 2,
              "team_size": 5
            }
          },
          "beacons": {
            "type": "latencies",
            "attributes": {
              "difference": 25,
              "max_latency": 100
            }
          },
          "league_rank": {
            "type": "number_difference",
            "attributes": {
              "max_difference": 1
            }
          },
          "selected_maps": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          },
          "selected_beacons": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          }
        },
        "expansions": {
          "10": {
            "beacons": {
              "difference": 40,
              "max_latency": 150
            }
          },
          "30": {
            "beacons": {
              "difference": 50
            }
          },
          "60": {
            "league_rank": {
              "max_difference": 2
            }
          },
          "180": {
            "beacons": {
              "difference": 100,
              "max_latency": 500
            }
          }
        }
      }
    },
    "competitive-example": {
      "application": {
        "name": "my-game-server",
        "version": "2024.01.30-16.23.00-UTC"
      },
      "rules": {
        "initial": {
          "match_size": {
            "type": "player_count",
            "attributes": {
              "team_count": 2,
              "team_size": 5
            }
          },
          "beacons": {
            "type": "latencies",
            "attributes": {
              "difference": 25,
              "max_latency": 100
            }
          },
          "league_rank": {
            "type": "number_difference",
            "attributes": {
              "max_difference": 0
            }
          },
          "selected_maps": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          },
          "selected_beacons": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          }
        },
        "expansions": {
          "30": {
            "beacons": {
              "difference": 40,
              "max_latency": 150
            }
          },
          "60": {
            "beacons": {
              "difference": 50
            }
          },
          "180": {
            "beacons": {
              "max_latency": 250
            }
          }
        }
      }
    },
    "challenger-example": {
      "application": {
        "name": "my-game-server",
        "version": "2024.01.30-16.23.00-UTC"
      },
      "rules": {
        "initial": {
          "match_size": {
            "type": "player_count",
            "attributes": {
              "team_count": 2,
              "team_size": 5
            }
          },
          "beacons": {
            "type": "latencies",
            "attributes": {
              "difference": 25,
              "max_latency": 100
            }
          },
          "league_rank": {
            "type": "number_difference",
            "attributes": {
              "max_difference": 0
            }
          },
          "selected_maps": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          },
          "selected_beacons": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          }
        },
        "expansions": {
          "30": {
            "beacons": {
              "difference": 40,
              "max_latency": 150
            }
          },
          "180": {
            "beacons": {
              "difference": 50
            }
          },
          "240": {
            "beacons": {
              "max_latency": 250
            }
          }
        }
      }
    }
  }
}

(kind reminder to please make sure to change the application name and version to match your Application and Versions!)

If no validation errors appear, hit Create and Start and wait for the process to complete. This will result in a new free cluster starting, with your Simple Example matchmaker.

You may now proceed to the next step.

2. Explore Configuration

Unique FPS Game Rules

Specifically for FPS games, you can define multiple Matchmaking Profiles for game modes specific rules and settings:

  • restrict rank within a difference between two players for more casual games,

  • restrict rank difference to only allow opponents with the same rank for ranked games,

  • let players provide their map preferences and choose a map suitable for everyone,

  • add Hub Selection UI to restrict opponents to specified Ping Beacons,

  • restrict matchmaking latency to a maximum threshold to prevent matching players far away,

  • restrict matchmaking latency to a maximum difference to maximize ping fairness,

  • allocate more CPU or memory using different App Versions when more players are allowed,

  • Join as Group for pre-made lobbies or to fill teams without exceeding team sizes.

Start with the ideal conditions, and expand restrictions to ensure quick matches:

  • slowly relax latency restrictions over time to find more players,

  • slowly increase allowed rank difference to find more players,

  • increase time between expansions for the highest ranks (challengers), as less players are available.

Create tickets with a higher rank for promotion matches, to match with tougher opponents.

Define separate cheater profiles to ensure that flagged cheaters or players with high amount of moderation reports do not negatively impact experience of legitimate players in ranked matches.

Semantic Versioning

Each new version uses Semantic Versioning to clearly communicate the impact of changes by interpreting format major.minor.patch:

  • major versions include breaking changes and require integration review,

  • minor versions include substantial backwards-compatible improvements,

  • patch versions include bug fixes and minor improvements.

Some deployments may result in Errors. We attempt to resolve this by retrying deployment up to max_deployment_retry_count times automatically (without client confirmation).

To ensure that unexpected client crashes or abandoned tickets do not linger and take up your matchmaker resources, tickets will be cancelled after ticket_expiration_period causing their status to change to CANCELLED and then permanently deleted after ticket_removal_period.

The core of our matchmaking logic is configured in Matchmaking Profiles. Each profile is a completely isolated matchmaking queue, pointing to App Versions with pre-defined amount of required CPU and memory (RAM) resources.

Matchmaking Rules in the initial rule set must be met for players to be grouped together, each defined by three properties:

  • name of your choosing, e.g. - match size,

  • rule type, also known as operator, e.g. - player_count,

  • and lastly operator attributes, e.g. team_count or team_size.

Player Count Rule

This is a special rule defining how many players need to match to initiate assignment:

  • team_count refers to number of teams, 1 team may be used for cooperative or free-for-all modes,

  • team_size refers to the number of players per team.

Our simple example demonstrates a cooperative game with 2 players.

Please note that "Player Count" rule is required and may only be defined once in your initial configuration rules.

Latencies Rule

Use this rule to provide the lowest possible ping for all players. Once clients measure and submit their round-trip time (ping) against all available beacons, Gen2 will only consider matches within a specific difference in ping values, measured against Ping Beacons. This presents a “soft” solution to splitting your player base, enabling matching with neighboring regions, especially improving match speed for less populated regions. Use max_latencyto prevent matching against players located far away.

You may now proceed to the next step.

Our example beacons rule above with "difference": 50, "max_latency": 200 initially:

  • Alice and Bob will match, since Beijing is discarded (>200) and the rest is within | A-B | < 50:

    • Alice {Montreal: 12.3, Newark: 45.6, Dallas: 59.9, Beijing: 264.4}; and

    • Bob {Montreal: 27.3, Newark: 32.4, Dallas: 23.1, Beijing: 252.2}.

  • Charlie and Dave will not match, since | C-D | > 50 for Dallas Beacon:

    • Alice {Montreal: 5.7 Newark: 44.2, Dallas: 59.5, Beijing: 263.2}; and

    • Bob {Montreal: 57.8, Newark: 32.0, Dallas: 24.2, Beijing: 272.3}.

Please note that "Latencies Rules" may only be defined once in your initial configuration rules.

3. Review Instance Details

Review details of your new matchmaker in our dashboard once it’s initialized:

Status indicates service health, may be ONLINE, OFFLINE, or ERROR.

  • Identifier helps Edgegap staff find your matchmaker quickly if you need help troubleshooting.

  • Started at can be useful to track down the latest update time.

  • Size corresponds to one of our Pricing Tiers.

  • API URL will be used by Game Clients and Game Servers to communicate with Gen2.

  • Swagger URL is a handy openAPI specification GUI we provide to explore API schema.

  • Auth Token is a unique secret token used by Game Clients and Game Server for authentication.

To test your new matchmaker using the API, you will need the Swagger URL, API URL and Auth Token.

You may now proceed to the next step.

4. Test Tickets API

First, open your Swagger URL to inspect your openAPI schema in the swagger GUI

Click the /...swagger.json URL underneath the “Matchmaker” title to open the raw JSON schema:

Save this page as a file on your drive (CTRL/CMD+S).

Open your Postman application and sign into your free account.

Import your swagger.json file from the previous step:

  • keep Postman Collection selected,

  • select View Import Settings and change setting Parameter generation to Example.

Confirm the import, this will result in a new collection appearing in the list of Collections on the left, titled Matchmaker.

View more actions, open tab Authorization and choose:

  • Auth Type - API Key,

  • Key - “Authorization

  • Value - insert your AuthToken value here,

  • Add to - Header.

Press (CTRL/CMD+S) or the Save icon to save changes. The orange dot in your Postman tab should disappear.

In your Matchmaker collection, select tickets and Create a matchmaking ticket, opening a new tab.

Select tab Body to preview your player ticket request:

notice player_ip set to null- this will cause to use the IP address automatically added to your request (see Server to Server Integration for alternatives),

  • profile refers to your Matchmaking Profiles,

  • attributes include values for your matchmaker rules, in this case for the latencies rule,

    • rule player_count is the only rule which doesn’t require any attributes in player tickets.

NOTE: Make sure to refer to the sample’s Swagger’s import configuration

Click Send and review the response to your player ticket request:

  • id is your unique matchmaking ticket ID, keep this saved to check on your ticket later,

  • profile confirming the choice of Matchmaking Profiles,

  • group_id is a unique group ID issued to every ticket, a solo player is represented as a group of 1,

    • see Join as Group for matchmaking with your friends or lobbies,

  • player_ip is the resolved public IP address of the player, regardless of the identification method used,

  • assignment is set to null to indicate the ticket has not been matched or assigned to a server yet,

  • created_at provides information about when the player ticket was created for game UI usage,

  • status indicates the current status of the ticket, all tickets start in SEARCHING (see Matchmaking Process for details).

Create a second ticket by hitting Send again, so our two players match and a server is started.

In your Matchmaker collection, select {ticketId} and Read a matchmaking ticket.

Input ticket ID from the response in previous step and click Send.

Review the updated assignment for your player ticket:

  • status changed to MATCH_FOUND first, while keeping assignment set to null to indicate players have matched and a server is being assigned,

Click Send again to check on your ticket, and review the updated assignment for your player ticket:

  • status changed to HOST_ASSIGNED with assignment containing details of the assigned server.

 Inspect your new deployment in our dashboard:

  • notice each deployment is tagged with all ticket IDs and profile for added traceability.

Try connecting from your game client to the assigned server.

Once you verify you’re able to connect to your Deployment without issues and are done testing, Stop your Deployment to free up capacity in your account for the next build.

You may now proceed to the next step.

5. Integrate the Matchmaker in your Game

Edgegap’s matchmaking integrates:

  • with Game Client, to manage Player Tickets,

  • with Game Server, to:

    • process player preferences passed through their tickets,

    • optionally to support Backfill to add or replace players after starting.

For in Game Client, we recommend providing ticket status updates throughout Matchmaking Process to players using in-game UI for best player experience. See:

In Game Client, ensure you’re handling non-retryable errors:

  • HTTP 404 Not Found - ticket has been deleted,

  • HTTP 500 Internal Server Error - temporary service outage.

In Game Server, process player preferences and initial server context. No API integration is required:

  1. Read Injected Environment Variables (Gen2) to retrieve initial players’ matchmaking data.

  2. Read Injected Environment Variables (App Versions) for version-specific parameters, settings (player capacity), and secrets.

  3. Read Injected Environment Variables (Deployment) for deployment information, such as IP address, location, or more.

Once players connect, Game Server and Game Clients start a loading scene to perform synchronization steps (e.g. selecting and loading a map/scene/level). We recommend a full fledged 3D scene, a lobby-like social UI, or a loading screen with a progress bar, to indicate initialization is progressing.

Once Game Clients fully loaded, players load/travel to the main gameplay scene.

Optionally, Game Server may create and manage Backfill and player capacity (add or replace players who leave).

Ensure your deployment will be stopped properly using Injected DELETE_URL, if:

  • no players join the match,

  • all players have left the match,

  • match concludes correctly.

Congratulations, you’ve completed Edgegap Matchmaker integration! If you’d like to learn more, read all about it in our Learning Center.