Access
Connect cross-platform accounts & identity management
In this tutorial, you will learn how to build and customize your own Matchmaking service using AccelByte’s Python SDK. This guide will show you how to create a Matchmaking service where two players can play together. The implementation covered in this guide will flow as follows:.
Ensure that you have followed the prerequisites when creating the AWS .yaml script
Your .yaml script is configured
You have a clone of the latest AccelByte Python SDK (opens new window) repository
IMPORTANT
We recommend using the specific latest version of the AccelByte Python SDK (opens new window) when starting a new project, instead of the command "latest" to avoid issues when the latest SDK version is updated.
Once you have cloned the AccelByte Python SDK (opens new window) repository, go to the samples/title-matchmaking-app (opens new window) folder. You will use the following files:
File | Usage |
samples/title-matchmaking-app/title_matchmaking/app.py | The code of the Title Matchmaking |
samples/title-matchmaking-app/client.py | This file will be used when testing your matchmaking later in this tutorial |
Configure the repository by following Accelbyte’s Python SDK Getting Started Guide.
Authorize access to AccelByte’s API using the following steps:
Validate the user’s request (opens new window) by extracting the authorization header value Bearer Auth Token from the user’s request.
# 02. Extract the Bearer Auth Token from the Authorization header.
authorization = str(event.get("headers").get("Authorization"))
bearer_auth_token, error = extract_bearer_auth_token(authorization)
if error:
return error
log_done("extract Bearer Auth Token")
Convert the Bearer Auth Token (opens new window) you obtained into an instance of OauthmodelTokenResponseV3 (opens new window).
# 03. Convert Bearer Auth Token into an OAuth Token object.
oauth_token, error = convert_bearer_auth_token_to_oauth_token(bearer_auth_token)
if error:
return error
log_done("convert Bearer Auth Token")
Create a Redis client (opens new window) to connect to the Elasticache for Redis (opens new window) to store matchmaking requests.
# 04. Create Redis Client.
redis_client = create_redis_client(env.redis_host, env.redis_port)
log_done(f"create redis client ({redact(f'{env.redis_host}:{env.redis_port}')})")
Create an instance of an IAM Client (opens new window) of the IAM-Python-SDK and use it to validate the OAuth Token and its Permissions and Claims.
# 05. Create IAM Client.
iam_client = create_iam_client(env.iam_base_url, env.iam_client_id, env.iam_client_secret)
log_done("create IAM client")
# 06. Validate IAM Client locally.
error = validate_iam_client_locally(iam_client)
if error:
return error
log_done("validate IAM Client locally")
# 07. Grant IAM Client an Access Token.
error = grant_iam_client_access_token(iam_client)
if error:
return error
log_done("grant IAM Client an Access Token")
# 08. Validate Access Token.
error = validate_access_token(iam_client, bearer_auth_token)
if error:
return error
log_done("validate Access Token")
# 09. Validate permissions.
error = validate_permissions(iam_client, bearer_auth_token, oauth_token)
if error:
return error
log_done("validate permissions")
# 10. Parse and validate claims.
claims, error = validate_claims(iam_client, bearer_auth_token)
if error:
return error
log_done("parse and validate claims")
user_id = claims.Sub
namespace = claims.Namespace
Initialize the AccelByte Python SDK (opens new window) and use it to store the OAuth Token.
# 11. Initialize the AccelByte Python SDK.
accelbyte_py_sdk.initialize()
# 11.a. Create IAM ConfigRepository and TokenRepository
iam_config_repo = MyConfigRepository(env.iam_base_url, env.iam_client_id, env.iam_client_secret)
iam_token_repo = MyTokenRepository(oauth_token)
iam_token_repo.store_token(oauth_token)
# 11.b. Create Game ConfigRepository and TokenRepository
game_config_repo = MyConfigRepository(env.game_base_url, env.game_client_id, env.game_client_secret, env.game_namespace)
game_token_repo = MyTokenRepository(None)
# 11.c. Store the 2 different pairs of repositories
global IAM_REPO, GAME_REPO
IAM_REPO = iam_config_repo, iam_token_repo
GAME_REPO = game_config_repo, game_token_repo
Using your previously generated User ID, create a matchmaking request (opens new window) that contains a new channel and party.
# 13. Create matchmaking request
error = create_matchmaking_request(redis_client, namespace, env.game_mode, user_id)
if error:
return error
log_done("create matchmaking request")
Set up a function to send a match search notification (opens new window) to the player via WebSockets. Players will receive this notification while waiting for a match.
# 14. Send search for match notification to user
error = send_notif_match_searching(namespace, user_id)
if error:
return error
log_done(f"send free form notification (match searching) to player ({user_id})")
Set the maximum time (opens new window) the service will wait for another player while matchmaking.
# 15. Wait for another user.
elapsed_time = 0.0
active_matchmaking_requests_summary = None
other_matchmaking_request = None
while elapsed_time < FIND_OTHER_USER_MAX_DURATION:
active_matchmaking_requests_summary, error = get_active_matchmaking_requests_summary(
redis_client,
namespace,
env.game_mode
)
if error:
return error
other_matchmaking_request = active_matchmaking_requests_summary.get_other_matchmaking_request(user_id)
if other_matchmaking_request:
break
time.sleep(FIND_OTHER_USER_CHECK_INTERVAL)
elapsed_time += FIND_OTHER_USER_CHECK_INTERVAL
log_wait(f"waiting for other players.. {elapsed_time:.2f}/{FIND_OTHER_USER_MAX_DURATION:.2f}s")
if not other_matchmaking_request:
return create_response(408, "Timed out! Not enough players.")
log_done(f"find other user ({other_matchmaking_request['user_id']})")
party_id = other_matchmaking_request["party_id"]
user_ids = list(active_matchmaking_requests_summary.unique_user_ids)
other_user_ids = list(active_matchmaking_requests_summary.get_other_user_ids(user_id))
In this section, we will be registering the game session to the session browser (opens new window). This step will also check whether there are enough players in the database. When the game has enough players, the game session will be registered in the session browser and a Session ID will be created. This ID will be used to register the session in the DSMC.
# 16. Create session
sb_session, error = create_session(
env.game_namespace,
env.session_browser_game_version,
env.session_browser_map_name,
env.session_browser_mode,
env.session_browser_password,
env.session_browser_type,
env.session_browser_username
)
if error:
return error
log_done(f"create session on session browser")
sb_session_id = sb_session.
Once you have successfully created a session, register it to the DSMC (opens new window).
# 17. Register session on DSMC
_, error = register_session_on_dsmc(
sb_session_id,
env.dsmc_deployment,
env.game_mode,
env.game_namespace,
party_id=party_id,
user_ids=user_ids
)
if error:
return error
log_done(f"create session on dsmc")
# 17.a. Wait for DSMC to register the session
time.sleep(5.0)
This function is used to claim a game server (opens new window) from the DSMC service.
# 18. Claim server
_, error = claim_server(
env.game_namespace,
sb_session_id
)
if error:
return error
log_done(f"claim server from dsmc")
This function adds players to the server in the Session browser (opens new window).
# 20. Add players to server in Session Browser
for idx, uid in enumerate(user_ids):
_, error = add_player_to_sb_sesion(env.game_namespace, sb_session_id, uid)
if error:
return error
log_done(f"add player ({uid}) to session [{(idx + 1):02d}/{len(user_ids):02d}]")
After adding players to the Matchmaking session, create a function to obtain the latest session information and to check if the session is now able to be joined.
# 21. Get session update from Session Browser
sb_session, error = get_session_from_session_browser(
env.game_namespace,
sb_session_id
)
# 22. Check if session is joinable
if not sb_session.joinable:
return create_response(400, "Unable to join the session.")
This function sends a notification to all players that a match has been found and displays the server IP and port.
# 23. Send notification to all users
for idx, uid in enumerate(user_ids):
error = send_notif_match_found(namespace, server_ip, server_port, uid)
if error:
log_warn(f"failed to send free form notification (match found) to player ({uid}) [{(idx + 1):02d}/{len(user_ids):02d}]")
else:
log_done(f"send free form notification (match found) to player ({uid}) [{(idx + 1):02d}/{len(user_ids):02d}]")
response = {
"message": f"successfully matchmade "
f"server:(ip:{server_ip}|port:{server_port})|"
f"session:(id:{sb_session_id})|"
f"users:({user_id},{other_user_ids})",
"server": {
"ip": server_ip,
"port": server_port,
},
"session": {
"id": sb_session_id
},
"users": [
user_id,
other_user_ids
]
}
In this section, you will learn how to run and test your Matchmaking service locally. You can test your service with your client.py files. When client.py is running, it will prompt the player to enter their username and password. Once authorized, client.py sends a a matchmaking request invokes the Lambda function. When a match is found, client.py listens to the notification service and sends a notification.
r = rq.post(
url=mm_uri,
headers={
"Authorization": f"Bearer {access_token}"
}
)
print(f"[done]: sent matchmaking request")
def print_wsm(message: str, tag: str, indent="| ", prefix="\n", suffix="\n"):
message = "\n".join([f"{indent}{line}" for line in message.rstrip().splitlines(keepends=False)])
print(f"[{tag}]:{prefix}{message}{suffix}")
async def on_message(message: str):
print_wsm(message, "recv")
scheme, uri = base_url.split("://")
ws_uri = f"wss://{uri}/lobby"
ws_client = Websockets2WSClient(
uri=ws_uri,
username=username,
password=password,
access_token=access_token
)
ws_client.listeners.append(on_message)
await ws_client.connect()
print("[done]: connect to websocket")
Now you can test your Matchmaking service. In the Python terminal, run the client.py script and enter the player’s email and password. Once authorized, matchmaking will begin..
To upload the Matchmaking function that you created to AWS Lambda, first ensure that you have filled in all of the required fields in the AWS SAM Template and have tested the SAM locally both in AWS SAM and Client.
In your terminal, go to the root directory of aws-lambda (opens new window).
Run the build (opens new window) using the following function:
sam build --use-container
You can find more detailed information about Deploying using the AWS SAM CLI (opens new window) in AWS’s Documentation.
Deploy (opens new window) the application to the AWS Cloud using the following function:
sam deploy --guided
Follow the on-screen prompts. You can find more detailed information about Deploying serverless applications (opens new window) in AWS’s Documentation.