Anti-botting
Botting is the act of using an automated script or computer program to connect to and interact with a game. This guide will show three ways to prevent botting using Rivet.
Captcha
One popular method of bot prevention used across the entire internet is the CAPTCHA.
Configuration changes
To configure captcha for the Rivet matchmaker, add the following to your version config file:
matchmaker:
captcha:
# How many requests a connection can make before captcha reverification is required
requests_before_reverify: 10
# How much time before captcha reverification is required
verification_ttl: 240000 # milliseconds
# Chosen captcha provider here ...
The Rivet matchmaker currently supports two captcha providers:
Client-side changes
After setting up captcha in your version config, future calls to
lobbies.find
, lobbies.join
, and
lobbies.create
will fail if captcha is not provided when requested.
The response body will look something like this:
{
"code": "CAPTCHA_CAPTCHA_REQUIRED",
"message": "Captcha is required.",
"documentation": "https://rivet.gg/docs/general/errors/captcha/captcha-required",
"metadata": {
"hcaptcha": {
"site_id": "MY_HCAPTCHA_SITE_ID"
}
}
}
Use the metadata provided (or in the case of Turnstile, just your own site key) to have the user verify a
captcha. After a successful captcha completion, retry the lobbies.find
,
lobbies.join
, or lobbies.create
request with the captcha response:
Request
curl
-X POST \
-H "Content-Type: application/json" \
-d "{ \"captcha\": { \"turnstile\": { \"client_response\": \"CAPTCHA_RESPONSE\" } } }" \
'https://matchmaker.api.rivet.gg/v1/lobbies/find'
Custom external verification
The Rivet matchmaker allows for external verification requests to enable developers to arbitrarily allow/reject matchmaker requests by their own logic. This is useful for games that have their own account system or custom anti-botting mechanism that want to restrict API calls to the matchmaker.
This is done via a webhook-like system that sends a POST
request to a custom API
endpoint after every lobbies.find
, lobbies.join
,
or lobbies.create
request.
Configuration changes
In this example, Rivet will send https://my.app/verify
an HTTP POST request
after every lobbies.join
request it
receives.
matchmaker:
game_modes:
default:
actions:
join:
enabled: true
verification_config:
url: https://my.app/verify
headers:
my_header: SECRET_CODE
The request payload will look something like this:
{
// This is arbitrary JSON data provided by the user to the /find or /join
// endpoints. Can be null.
"verification_data": {
// ...
},
"game": {
"namespace_id": "NAMESPACE_ID",
"game_mode_id": "GAME_MODE_ID",
"game_mode_name_id": "default",
// Info about the lobby only if it is already running. Null otherwise.
// When this value is null and `kind` is "find", that implies this lobby
// is being auto-created.
"lobby": {
"lobby_id": "LOBBY_ID",
"region_id": "REGION_ID",
"region_name_id": "atl",
"create_ts": "Tue, 15 Nov 1994 12:45:26 GMT",
"is_closed": false
},
// This is arbitrary JSON data. Can be null. †
"state": {
// ...
},
// This is arbitrary JSON data provided by the user to the /create
// endpoint when creating a custom lobby. Can be null. It will be
// passed to the `RIVET_LOBBY_CONFIG` environment variable upon
// lobby creation. ††
"config": {
// ...
},
// This is an arbitrary string hashmap provided by /find or /create by
// the user
"tags": {
// ...
},
// Set by the user in /find and /create requests. Null if unset.
"dynamic_max_players": 4
},
// IP info about all connecting players in this request.
"clients": {
"1.2.3.4": {
// Null if `User-Agent` header was not set.
"user_agent": "...",
// Coordinates can be null if IP fetching failed.
"latitude": 0.0,
"longitude": 0.0
}
},
"join_kind": "normal", // Either "normal", or "party"
"kind": "join" // Either "find", "join", or "create"
}
† See lobbies.setState.
†† See Lobby environment variables for more info.
Server reply
Your server should reply to Rivet's request according to these rules:
- A success status code (
200
-299
) tells the matchmaker it should accept the request - Any other status code tells the matchmaker it should reject the request with the
MATCHMAKER_VERIFICATION_FAILED
error
Client-side changes
This code shows how to provide user data to your external verification server through Rivet:
Request
curl
-X POST \
-H "Content-Type: application/json" \
-d "{ \"verification_data\": { \"foo\": \"bar\" } }" \
'https://matchmaker.api.rivet.gg/v1/lobbies/find'
If your server returns a response that ends up rejecting the matchmaker request,
the user's request will fail with the MATCHMAKER_VERIFICATION_FAILED
error code. If Rivet's request to your server times out or fails for any reason, the matchmaker request will fail with the
MATCHMAKER_VERIFICATION_REQUEST_FAILED
error code.
Use case: Custom account verification
If you plan on using an external account system instead of Rivet's identities, you can leverage the external verification system discussed above.
For example, passing a user's account token into this request will allow you to verify that the user is who they say they are:
Request
curl
-X POST \
-H "Content-Type: application/json" \
-d "{ \"verification_data\": { \"my_secret_account_token\": \"my-external-account-token-here" } }" \
'https://matchmaker.api.rivet.gg/v1/lobbies/find'