External Game Backend integration
What is the purpose of External Game Backend?
If you want to add player progression, play-to-earn functionality or smart contract integration to your game, you have to somehow exchange information about matches running on Elympics with your own backend.
To make that straightforward, we have implemented a feature to enable additional callbacks to your API from our services.
At the moment, there are four possible callbacks that you can configure at different stages of the match flow. All of them will be described on this page.
OnQueueJoin
OnQueueLeave
OnMatchCreate
OnMatchFinish
If you want to use OnMatchCreate and/or OnMatchFinish you need to have an external game backend that will implement both of those endpoints.
OnQueueJoin and OnQueueLeave are independent from each other, and can be used separately.
How to turn it on?
Using Web Panel
All you have to do is define endpoints in the game settings (don't forget to click the "Save" button):
For OnMatchCreate
and OnMatchFinish
:
And accordingly for OnQueueJoin
and OnQueueLeave
:
Using Elympics CLI
You can use the Elympics CLI Tool to manage queues by following CLI documentation.
OnMatchCreate
and OnMatchFinish
events can be modified with the --external-backend
argument, while OnQueueJoin
and OnQueueLeave
are modified through --queue-join-url
and --queue-leave-url
respectively.
Endpoints to implement
Match create: /elympics/match/create
A request to /elympics/match/create
is sent after a match is created in our database, but before bootstrapping real game on one of our game servers. It could be used to:
- insert information about the match in your database e.g. for statistical / progression purposes,
- verify player-provided data for match initiation,
- add and/or replace players initial data using one stored in your database,
- block some players, game versions, etc.
Response values in all fields but GameEngineData
and MatchmakerData
must stay the same as in the request.
- Example request
- Request schema
- Test cURL
- Example response
- Response schema
{
"MatchId": "d060f00d-0000-0000-0000-1d0000001234",
"GameId": "d060f00d-0000-0000-0000-1d0000006a3e",
"GameVersion": "1.2.3",
"UserDatas": [
{
"UserId": "d060f00d-0000-0000-0000-1d0000085e70",
"IsBot": false,
"BotDifficulty": 0,
"GameEngineData": "eyJlcXVpcG1lbnQiOlsia2F0YW5hIl0sImRvY3MtZXhhbXBsZSI6dHJ1ZX0=",
"MatchmakerData": [
1.0
]
},
{
"UserId": "d060f00d-0000-0000-0000-1d0000000b07",
"IsBot": true,
"BotDifficulty": 6.62607,
"GameEngineData": null,
"MatchmakerData": null
}
]
}
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$ref": "#/definitions/MatchCreateRequestModel",
"definitions": {
"MatchCreateRequestModel": {
"type": "object",
"additionalProperties": false,
"properties": {
"MatchId": {
"type": "string",
"format": "uuid"
},
"GameId": {
"type": "string",
"format": "uuid"
},
"GameVersion": {
"type": "string"
},
"UserDatas": {
"type": "array",
"items": {
"$ref": "#/definitions/UserData"
}
}
},
"required": [
"MatchId",
"GameId",
"GameVersion",
"UserDatas"
],
"title": "MatchCreateRequestModel"
},
"UserData": {
"type": "object",
"additionalProperties": false,
"properties": {
"UserId": {
"type": "string",
"format": "uuid"
},
"IsBot": {
"type": "boolean"
},
"BotDifficulty": {
"type": "number"
},
"GameEngineData": {
"type": ["string", "null"],
"media": {
"binaryEncoding": "base64",
"type": "application/octet-stream"
}
},
"MatchmakerData": {
"type": ["array", "null"],
"items": {
"type": "number"
}
}
},
"required": [
"UserId",
"IsBot",
"BotDifficulty"
],
"title": "UserData"
}
}
}
curl --location --request POST '{YOUR_BACKEND_URL}/elympics/match/create' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJoYXNoIjoiNGRZTVVVcHMvUVpRRWpzZmIrU1BJUGNTYXBsUTRUc1JpZUZBUDViNFZYRT0iLCJoYXNoLWFsZyI6InNoYTI1NiIsIm5iZiI6MTY3MjY0NTA1NCwiZXhwIjoxOTg4MjY0NjE4LCJpYXQiOjE2NzI2NDUwNTR9.KDm3rbNFTL3pGFl1Lgrqj6Hh7URUj75nC5lLzAcZL65I_9TcXMDXMrvl8pclb-Ct_uh-ObKkq_OJ6iL3ZyToNFVzI3QJdfAzvCufz6VlOP1YS7WlW4ef2mfH9hQJ1dK2FkPYmVLWQAgvvNerosJGJcbwAUk764KWvZMONu2X1kaWa54-iiXt1CnUhat5td4FUzjDDnOD7DPH5d13EIap1qVTNCrwvHyG-hZ6u_uPwNcqTXtgeyYO4-_QAbWfnE-C1laVlocXSPJGCtdyKa3vQUnpJPa5RTwg0Q9rXvEoO43T2GiKI_HWWmGScipYgW1XZihVHMJhO13xiy3NaA8BOH3SFXLwyPdhK9RPQ31J42sIqrrnwnlWgMXDELTt-HvySt82GnK5ktnXzLJsD-SHo86jUrA9NxklLhe0ycFABIKHtonyUsagqitgnO6Di77sdm-SMtkl4ecJgFkOWO3SQjflEZdQPvrg-N1JD5veCXvpzx0zFDwbWkmxrJf_qlS8qLx4d1LmfEh7_1xXP6Cf40-s-Mx4z348GLmLKz5K1RwMBmb6VSivm2agWSRs-zWnZd0rS2_cyThxTX30xOms7p3TlRexH-7yHAEVe-1BnTP-u47yT-nwdPIFKDwGd4bxsDlnN8U4OmiNI9BhXpxf6K8Mt0DA8ueTrbp3q6niY38' \
--header 'Content-Type: application/json' \
--data-raw $'{\n\t"MatchId": "d060f00d-0000-0000-0000-1d0000001234",\n\t"GameId": "d060f00d-0000-0000-0000-1d0000006a3e",\n\t"GameVersion": "1.2.3",\n\t"UserDatas": [\n\t\t{\n\t\t\t"UserId": "d060f00d-0000-0000-0000-1d0000085e70",\n\t\t\t"IsBot": false,\n\t\t\t"BotDifficulty": 0,\n\t\t\t"GameEngineData": "eyJlcXVpcG1lbnQiOlsia2F0YW5hIl0sImRvY3MtZXhhbXBsZSI6dHJ1ZX0=",\n\t\t\t"MatchmakerData": [\n\t\t\t\t1.0\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t"UserId": "d060f00d-0000-0000-0000-1d0000000b07",\n\t\t\t"IsBot": true,\n\t\t\t"BotDifficulty": 6.62607,\n\t\t\t"GameEngineData": null,\n\t\t\t"MatchmakerData": null\n\t\t}\n\t]\n}'
{
"Allow": true,
"RejectionCause": null,
"UserDatas": [
{
"UserId": "d060f00d-0000-0000-0000-1d0000085e70",
"IsBot": false,
"BotDifficulty": 0,
"GameEngineData": "eyJlcXVpcG1lbnQiOlsia2F0YW5hIl19",
"MatchmakerData": [
1
]
},
{
"UserId": "d060f00d-0000-0000-0000-1d0000000b07",
"IsBot": true,
"BotDifficulty": 6.62607,
"GameEngineData": "eyJlcXVpcG1lbnQiOlsibWluaWd1biJdfQ==",
"MatchmakerData": null
}
]
}
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$ref": "#/definitions/MatchCreateResponseModel",
"definitions": {
"MatchCreateResponseModel": {
"type": "object",
"additionalProperties": false,
"properties": {
"Allow": {
"type": "boolean"
},
"RejectionCause": {
"type": ["string", "null"]
},
"UserDatas": {
"type": ["array", "null"],
"items": {
"$ref": "#/definitions/UserData"
}
}
},
"required": [
"Allow"
],
"if": {
"properties": {
"Allow": { "const": true }
}
},
"then": {
"required": ["UserDatas"]
},
"title": "MatchCreateResponseModel"
},
"UserData": {
"type": "object",
"additionalProperties": false,
"properties": {
"UserId": {
"type": "string",
"format": "uuid"
},
"IsBot": {
"type": "boolean"
},
"BotDifficulty": {
"type": "number"
},
"GameEngineData": {
"type": ["string", "null"],
"media": {
"binaryEncoding": "base64",
"type": "application/octet-stream"
}
},
"MatchmakerData": {
"type": ["array", "null"],
"items": {
"type": "number"
}
}
},
"required": [
"UserId",
"IsBot",
"BotDifficulty"
],
"title": "UserData"
}
}
}
Match finish: /elympics/match/finish
A request to /elympics/match/finish
is made after a match is finished. It contains the information about match results that can be e.g. inserted into your database.
- Example request
- Request schema
- Test cURL
- Response
- Response schema
{
"MatchId": "d060f00d-0000-0000-0000-1d0000001234",
"GameId": "d060f00d-0000-0000-0000-1d0000006a3e",
"GameVersion": "1.2.3",
"UserDatas": [
{
"UserId": "d060f00d-0000-0000-0000-1d0000085e70",
"IsBot": false,
"BotDifficulty": 0,
"GameEngineData": "",
"MatchmakerData": [0.0, 3.0]
},
{
"UserId": "d060f00d-0000-0000-0000-1d0000000b07",
"IsBot": true,
"BotDifficulty": 6.62607,
"GameEngineData": "eyJkb2NzLWV4YW1wbGUiOnRydWV9",
"MatchmakerData": [1.0, -1.0]
}
]
}
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$ref": "#/definitions/MatchFinishRequestModel",
"definitions": {
"MatchFinishRequestModel": {
"type": "object",
"additionalProperties": false,
"properties": {
"MatchId": {
"type": "string",
"format": "uuid"
},
"GameId": {
"type": "string",
"format": "uuid"
},
"GameVersion": {
"type": "string"
},
"UserDatas": {
"type": ["array", "null"],
"items": {
"$ref": "#/definitions/UserData"
}
}
},
"required": [
"MatchId",
"GameId",
"GameVersion"
],
"title": "MatchFinishRequestModel"
},
"UserData": {
"type": "object",
"additionalProperties": false,
"properties": {
"UserId": {
"type": "string",
"format": "uuid"
},
"IsBot": {
"type": "boolean"
},
"BotDifficulty": {
"type": "number"
},
"GameEngineData": {
"type": ["string", "null"],
"media": {
"binaryEncoding": "base64",
"type": "application/octet-stream"
}
},
"MatchmakerData": {
"type": ["array", "null"],
"items": {
"type": "number"
}
}
},
"required": [
"UserId",
"IsBot",
"BotDifficulty"
],
"title": "UserData"
}
}
}
curl --location --request POST '{YOUR_BACKEND_URL}/elympics/match/finish' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJoYXNoIjoiTk93ZWM3WTNkMk1UdEh0ZXAra3MxWkNFcEZLbmd5d1VITGVuNStERW5vRT0iLCJoYXNoLWFsZyI6InNoYTI1NiIsIm5iZiI6MTY3MjY0NTA1NCwiZXhwIjoxOTg4MjY0NjE4LCJpYXQiOjE2NzI2NDUwNTR9.Z9yA8gQnqxlAJlmz-sghm5KaKEbQ5-6Yjf1LLtWkYBUWWM10voJczEuoC6DzcppMkKLpYTTEaJo_PRQdxRTtCXI2DrREd5kniacfw6WojldbL3Lp_Y_ZIGW9DSkOpaP9FDq21JM0dS1BUipAV537pIx6pIbcALoqYvxgyMsMOpDVvppImU7hXQdTmdzwNbZism56U5uV-AcGvpGgCSwBV8R-tfTC8Mtt6rE0SizVdYv8ciZ6poBPiOBS6HAIOWPjwqdkCCz5aUo5yFAgXhRv-4LV0CfZ6HmxXgWIrtktN6QFNNFyFJG3v5tPvcEr8aJ0N0E7SRaLaMbqZ8J2h9U8NHjrOt1WgXH54a1IIInLZJuPfEr97nInw47j9eFzNDaQR-qU_T1d_hDavawC2JhD8JetG5I1r_aXFCPcD5bEDudJxA9Kd7G1xUo4TD1cRscNkGp21bloqGEIBF_EW0j5MpqmtbRLmqEpjfoSZeMwW26Hj1FAmpWrwRQaxc8fRz6WgF6pdc5NPPfQ90uxP0c6ST1go_DTqwFD_frfci193lcTYha9dRbpq6lpgh4EkLpVZyrWgwmDbWVH5ZCaBH7BbdClaL3eODMoJC_BxlRjOKQtAbVAddQAdCJzH--mprxiOKXKzqLOW2AJX3pq3JAKESDSqOJtx5CbVRBtSkXa9sk' \
--header 'Content-Type: application/json' \
--data-raw $'{\n\t"MatchId": "d060f00d-0000-0000-0000-1d0000001234",\n\t"GameId": "d060f00d-0000-0000-0000-1d0000006a3e",\n\t"GameVersion": "1.2.3",\n\t"UserDatas": [\n\t\t{\n\t\t\t"UserId": "d060f00d-0000-0000-0000-1d0000085e70",\n\t\t\t"IsBot": false,\n\t\t\t"BotDifficulty": 0,\n\t\t\t"GameEngineData": "",\n\t\t\t"MatchmakerData": [0.0, 3.0]\n\t\t},\n\t\t{\n\t\t\t"UserId": "d060f00d-0000-0000-0000-1d0000000b07",\n\t\t\t"IsBot": true,\n\t\t\t"BotDifficulty": 6.62607,\n\t\t\t"GameEngineData": "eyJkb2NzLWV4YW1wbGUiOnRydWV9",\n\t\t\t"MatchmakerData": [1.0, -1.0]\n\t\t}\n\t]\n}'
{}
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$ref": "#/definitions/MatchFinishResponseModel",
"definitions": {
"MatchFinishResponseModel": {
"type": "object",
"additionalProperties": false,
"properties": {},
"title": "MatchFinishResponseModel"
}
}
}
OnQueueJoin
The OnQueueJoin endpoint is activated before a player physically joins the matchmaking queue. The external backend could:
- overwrite the player's GameEngineData and MatchmakerData.
- prevent player from joining the queue and pass a RejectionCause.
- collect information about actual matchmaking process
- Example request
- Request schema
- Test cURL
- Example response
- Response schema
{
"UserId": "d060f00d-0000-0000-0000-1d0000001234",
"GameId": "d060f00d-0000-0000-0000-1d0000006a3e",
"QueueName": "Training",
"GameVersion": "1.2.3",
"RegionName": "warsaw",
"GameEngineData": "eyJlcXVpcG1lbnQiOlsia2F0YW5hIl0sImRvY3MtZXhhbXBsZSI6dHJ1ZX0=",
"MatchmakerData": [
1.0
]
}
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$ref": "#/definitions/OnQueueJoinRequestModel",
"definitions": {
"OnQueueJoinRequestModel": {
"type": "object",
"properties": {
"UserId": {
"type": "string",
"format": "uuid"
},
"GameId": {
"type": "string",
"format": "uuid"
},
"QueueName": {
"type": "string"
},
"GameVersion": {
"type": "string"
},
"RegionName": {
"type": "string"
},
"GameEngineData": {
"type": ["string", "null"],
"media": {
"binaryEncoding": "base64",
"type": "application/octet-stream"
}
},
"MatchmakerData": {
"type": ["array", "null"],
"items": {
"type": "number"
}
}
},
"required": [
"UserId",
"GameId",
"QueueName",
"GameVersion",
"RegionName",
"GameEngineData",
"MatchmakerData"
]
},
}
}
curl --location --request POST '{YOUR ON QUEUE JOIN URL}' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJoYXNoIjoiNGRZTVVVcHMvUVpRRWpzZmIrU1BJUGNTYXBsUTRUc1JpZUZBUDViNFZYRT0iLCJoYXNoLWFsZyI6InNoYTI1NiIsIm5iZiI6MTY3MjY0NTA1NCwiZXhwIjoxOTg4MjY0NjE4LCJpYXQiOjE2NzI2NDUwNTR9.KDm3rbNFTL3pGFl1Lgrqj6Hh7URUj75nC5lLzAcZL65I_9TcXMDXMrvl8pclb-Ct_uh-ObKkq_OJ6iL3ZyToNFVzI3QJdfAzvCufz6VlOP1YS7WlW4ef2mfH9hQJ1dK2FkPYmVLWQAgvvNerosJGJcbwAUk764KWvZMONu2X1kaWa54-iiXt1CnUhat5td4FUzjDDnOD7DPH5d13EIap1qVTNCrwvHyG-hZ6u_uPwNcqTXtgeyYO4-_QAbWfnE-C1laVlocXSPJGCtdyKa3vQUnpJPa5RTwg0Q9rXvEoO43T2GiKI_HWWmGScipYgW1XZihVHMJhO13xiy3NaA8BOH3SFXLwyPdhK9RPQ31J42sIqrrnwnlWgMXDELTt-HvySt82GnK5ktnXzLJsD-SHo86jUrA9NxklLhe0ycFABIKHtonyUsagqitgnO6Di77sdm-SMtkl4ecJgFkOWO3SQjflEZdQPvrg-N1JD5veCXvpzx0zFDwbWkmxrJf_qlS8qLx4d1LmfEh7_1xXP6Cf40-s-Mx4z348GLmLKz5K1RwMBmb6VSivm2agWSRs-zWnZd0rS2_cyThxTX30xOms7p3TlRexH-7yHAEVe-1BnTP-u47yT-nwdPIFKDwGd4bxsDlnN8U4OmiNI9BhXpxf6K8Mt0DA8ueTrbp3q6niY38' \
--header 'Content-Type: application/json' \
--data-raw $'{\n\t"UserId": "d060f00d-0000-0000-0000-1d0000001234",\n\t"GameId": "d060f00d-0000-0000-0000-1d0000006a3e",\n\t"QueueName": "Training",\n\t"GameVersion": "1.2.3",\n\t"RegionName": "warsaw",\n\t\"GameEngineData": "eyJlcXVpcG1lbnQiOlsia2F0YW5hIl0sImRvY3MtZXhhbXBsZSI6dHJ1ZX0=",\n\t"MatchmakerData": [\n\t\1.0\n\t]}'
{
"Allow": true,
"RejectionCause": null,
"GameEngineData": "eyJlcXVpcG1lbnQiOlsia2F0YW5hIl19",
"MatchmakerData": [
1
]
}
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$ref": "#/definitions/OnQueueJoinResponseModel",
"definitions": {
"OnQueueJoinResponseModel": {
"type": "object",
"additionalProperties": false,
"properties": {
"Allow": {
"type": "boolean"
},
"RejectionCause": {
"type": ["string", "null"]
},
"GameEngineData": {
"type": ["string", "null"],
"media": {
"binaryEncoding": "base64",
"type": "application/octet-stream"
}
},
"MatchmakerData": {
"type": ["array", "null"],
"items": {
"type": "number"
}
}
},
"required": [
"Allow"
],
}
}
}
OnQueueLeave
The OnQueueLeave endpoint is activated just before a player leaves the matchmaking queue. There are many different reasons why this might occur, and an Enum is provided with all possible cases. This endpoint is only informational for your backend, you can't alter the player state using the response.
This endpoint can be used to unblock a player on your backend, check how long people waited in the queue before leaving, what is the average time for finding a match, and other purposes.
This endpoint can be enabled by adding the EventQueueLeaveUrl to the game configuration.
- Example request
- Request schema
- Test cURL
- Example response
- Response schema
{
"UserId": "d060f00d-0000-0000-0000-1d0000001234",
"GameId": "d060f00d-0000-0000-0000-1d0000006a3e",
"QueueName": "Training",
"GameVersion": "1.2.3",
"RegionName": "warsaw",
"GameEngineData": "eyJlcXVpcG1lbnQiOlsia2F0YW5hIl0sImRvY3MtZXhhbXBsZSI6dHJ1ZX0=",
"MatchmakerData": [
1.0
],
"Reason": "MatchmakingFinished"
}
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$ref": "#/definitions/OnQueueLeaveRequestModel",
"definitions": {
"QueueLeaveReason": {
"type": "string",
"enum": ["Canceled", "MatchmakingFailed", "MatchmakingFinished", "Timeout", "Unknown"]
},
"OnQueueLeaveRequestModel": {
"type": "object",
"properties": {
"UserId": {
"type": "string",
"format": "uuid"
},
"GameId": {
"type": "string",
"format": "uuid"
},
"QueueName": {
"type": "string"
},
"GameVersion": {
"type": "string"
},
"RegionName": {
"type": "string"
},
"GameEngineData": {
"type": ["string", "null"],
"media": {
"binaryEncoding": "base64",
"type": "application/octet-stream"
}
},
"MatchmakerData": {
"type": ["array", "null"],
"items": {
"type": "number"
}
},
"Reason": {
"$ref": "#/definitions/QueueLeaveReason"
}
},
"required": [
"UserId",
"GameId",
"QueueName",
"GameVersion",
"RegionName",
"GameEngineData",
"MatchmakerData",
"Reason"
]
},
}
}
curl --location --request POST '{YOUR ON QUEUE LEAVE URL}' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJoYXNoIjoiNGRZTVVVcHMvUVpRRWpzZmIrU1BJUGNTYXBsUTRUc1JpZUZBUDViNFZYRT0iLCJoYXNoLWFsZyI6InNoYTI1NiIsIm5iZiI6MTY3MjY0NTA1NCwiZXhwIjoxOTg4MjY0NjE4LCJpYXQiOjE2NzI2NDUwNTR9.KDm3rbNFTL3pGFl1Lgrqj6Hh7URUj75nC5lLzAcZL65I_9TcXMDXMrvl8pclb-Ct_uh-ObKkq_OJ6iL3ZyToNFVzI3QJdfAzvCufz6VlOP1YS7WlW4ef2mfH9hQJ1dK2FkPYmVLWQAgvvNerosJGJcbwAUk764KWvZMONu2X1kaWa54-iiXt1CnUhat5td4FUzjDDnOD7DPH5d13EIap1qVTNCrwvHyG-hZ6u_uPwNcqTXtgeyYO4-_QAbWfnE-C1laVlocXSPJGCtdyKa3vQUnpJPa5RTwg0Q9rXvEoO43T2GiKI_HWWmGScipYgW1XZihVHMJhO13xiy3NaA8BOH3SFXLwyPdhK9RPQ31J42sIqrrnwnlWgMXDELTt-HvySt82GnK5ktnXzLJsD-SHo86jUrA9NxklLhe0ycFABIKHtonyUsagqitgnO6Di77sdm-SMtkl4ecJgFkOWO3SQjflEZdQPvrg-N1JD5veCXvpzx0zFDwbWkmxrJf_qlS8qLx4d1LmfEh7_1xXP6Cf40-s-Mx4z348GLmLKz5K1RwMBmb6VSivm2agWSRs-zWnZd0rS2_cyThxTX30xOms7p3TlRexH-7yHAEVe-1BnTP-u47yT-nwdPIFKDwGd4bxsDlnN8U4OmiNI9BhXpxf6K8Mt0DA8ueTrbp3q6niY38' \
--header 'Content-Type: application/json' \
--data-raw $'{\n\t"UserId": "d060f00d-0000-0000-0000-1d0000001234",\n\t"GameId": "d060f00d-0000-0000-0000-1d0000006a3e",\n\t"QueueName": "Training",\n\t"GameVersion": "1.2.3",\n\t"RegionName": "warsaw",\n\t\"GameEngineData": "eyJlcXVpcG1lbnQiOlsia2F0YW5hIl0sImRvY3MtZXhhbXBsZSI6dHJ1ZX0=",\n\t"MatchmakerData": [\n\t\1.0\n\t],\n\t"Reason": "MatchmakingFinished"}'
{}
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$ref": "#/definitions/OnQueueLeaveResponseModel",
"definitions": {
"OnQueueLeaveResponseModel": {
"type": "object",
"additionalProperties": false,
"properties": { }
}
}
}
Request verification
JWT tokens used by Elympics in External Game Backend requests can be verified using the "internal" public key available through Elympics CLI.
Besides the standard not-before (nbf
), expiration (exp
) and issued-at (iat
) times, each token provides hash
and hash-alg
claims that can be used to ensure the integrity of request body.
hash
stores a checksum calculated using hashing algorithm described by hash-alg
. The checksum is based solely on the contents of HTTP message body.
By validating both the token and the content hash you ensure that the request was indeed sent by us and no one tampered with the contents.
If you want to calculate checksum for comparison on your end, you have to use the original content string of HTTP message. Hashing parsed JSON objects makes no sense as it is completely non-portable.
Player UserData Model
Each player's UserData
model, which is sent to all configured endpoints, contains two important fields: MatchmakerData
and GameEngineData
. These fields allow data to be passed from your backend to the game server. The OnQueueJoin
and OnMatchCreate
functions can override the values that were sent to them. Lets say that you want to track the win streak of each player and view it in the game. To do so, you need to enrich the GameEngineData
with that information using your backend which track all win streaks by collecting MatchmakerData
results on the OnMatchFinish
. Possibilities are endless.
GameEngineData
GameEngineData is a byte array represented as a base64-encoded string. It can be used to enrich the initial player data. This value can be decoded on your backend, modified, and then re-encoded.
The most common and convenient way to store data in this field is using a markup language, such as JSON or YAML. Here's an example utility function written in Python that shows how to decode and encode this data:
import base64
import json
from typing import Any
def decode_game_engine_data(game_engine_data: str | None) -> dict[str, Any]:
if game_engine_data is None:
return {}
return json.loads(base64.b64decode(game_engine_data.encode("utf8")))
def encode_game_engine_data(game_engine_data: dict[str, Any]) -> str:
json_data = json.dumps(game_engine_data)
base64_data = base64.b64encode(json_data.encode("utf8"))
return base64_data.decode("utf8")
MatchmakerData
MatchmakerData is a float array intended to represent the initial setup of the player if it's critical for matchmaking. For duel games, this data is utilized by our machine learning service to provide enhanced matchmaking that takes into account not only player skill, but also additional features.
At the conclusion of a match (OnMatchFinish), the first value of the array should represent the player's score for that game (the higher the better). This score is used in player ranking calculations, our leaderboard service, and for training and evaluating our smart matchmaking models. If you don't pass any number in it or something that will not represent the player score, all mentioned features will not take this match into account.
Additional endpoints
Your backend can provide an arbitrary number of additional endpoints to be utilized by game code. Those functions are not handled by Elympics in any way – you have to send web requests manually from your code.
When performing such requests, you can use a JWT authentication token granted to each user by Elympics. The token is accessible through Authenticated
event of ElympicsLobbyClient
singleton. You can find more information in the article about matchmaking.
Example cURL:
curl --location --request POST 'https://{YOUR_BACKEND_URL}/arbitrary/endpoint' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1laWQiOiJ0ZXN0LXVzZXIiLCJuYmYiOjE2NjQ1MzkxMDcsImV4cCI6MTk3OTg5OTEwNywiaWF0IjoxNjY0NTM5MTA3fQ.bBJmzPrybXDZMrwP9u9oWKs8v-6dRYhT-mLwnNJY3i6V4FdbceCZIb_IJYTHTKtCixHRAjG1--fvBYKt5zJRqfKlD6ic4VQajN0TVCljREHxHtEdtKcZSEKl9syyUpVDOnork77m3wgAUAagd7BDjuN6fXXmX_U0g0r4S-QAZdwsrSEMBrHJjJXw3cQ222pl4pR6JmWpV5gxeB_VIwLbLU-mLSvJZWDQa49t4lx2o0DM0_HcoKuVMjW-_LwtQusCl140PFJI18ecP3CAYFyAUgwcSH7OBw_VbWpu6-MVXLgRqJaovVEIibHXok26C9_3LjULlx_VaeuYncR-eAJ_A9qsCUlI77xuA0Azm3GoguUUdnbXN3UPOeBKx4q92HWD9gI9fFHiBDaRYoW7OqyckytHiu1wTMCAnF_UbRrwMQJrM6bJSp5rM6CfZanYuyvglD9KX5dWPMjaWDfyctKKHQ8nRy1ZZ45aBgIjzw-TioaNgDHDx0BMGvdRRohNYCTQQOZ7vToSebAMuyD9UDRZXGfq_u6XWabayE0OnxTXhn7QfMu2jm5_5MbgbBvk4uqPC1TETzo8Te2aCZLLXPO2YlW683gja7GGaNG-9xT9kb294dLJMD6hvUa96DK0h_XISG5iE1sM3B7MG1BU4iy-JGkkffDwK7hbhzODV2pFhFE' \
--header 'Content-Type: application/json' \
--data-raw '{
"ExampleField": "Example data"
}'
Request verification
JWT tokens granted to users can be verified using the "users" public key available through Elympics CLI.
These tokens contain additional nameid
claim storing the ID of a player.