Real-Time Gameplay Setup
This article will walk you through setting up your game for real-time gameplay on Skillz. Make sure you have implemented the Skillz Delegate before proceeding. We recommend reviewing the documentation for Standard Gameplay in order to understand the basics of the Skillz SDK integration.
Requirements
In order to enable real-time gameplay on your Skillz title, your game must meet several requirements. The game must be:
- Setup for Standard Gameplay
- Real Prize Enabled
- Over 2000 DAU
- Competitively playable where gameplay ping is >200ms
If your game meets all these requirements, contact us about enabling real-time functionality in your game.
Overview
Skillz synchronous technology defines a real-time game as a multiplayer game where two or more players interact in real time. Most online games fall into this category, and can be thought of as an action by one player affects another player’s gamestate.
Skillz supports multiplayer games in the following configurations:
- Implementing our Sync Server SDK
- Implementing a custom game server
- Using our integration with Photon Unity Networking (PUN)
Below we will briefly describe the high level architecture on how we support multiplayer games on the Skillz platform.
Sync Architecture
- Skillz Sync SDK
- Custom Server
- PUN
The Skillz Sync Server SDK synchronous technology is based on a server authoritative model. That is, an authoritative game server owns the game state to help protect against unauthorized operations by a client (e.g. cheating). Skillz hosts the servers on a scalable cloud hosting solution which you have the option to customize based upon your games needs.
We currently use AWS Amazon Elastic Compute Cloud (or EC2) instances and Kubernetes (or K8s) to host our platform.
The diagram below provides an overview of of the key components:
The Skillz custom server synchronous technology is based on a server authoritative model. That is, an authoritative game server owns the game state to help protect against unauthorized operations by a client (e.g. cheating). Skillz hosts the servers on a scalable cloud hosting solution which you have the option to customize based upon your games needs.
We currently use AWS Amazon Elastic Compute Cloud (or EC2) instances and Kubernetes (or K8s) to host our platform.
The diagram below provides an overview of of the key components:
Skillz synchronous technology integration with PUN is based on a client authoritative model. One client is designated the master and serves as the authority. Photon's hosted servers, act as relays between the master client and the others. More information about PUN can be found on their website
The diagram below provides an overview of of the key components:
Server Requirements
- Skillz Sync SDK
- Custom Server
- PUN
To support a synchronous game server on the Skillz platform, Skillz requires the following:
- A game server must be provided via a Docker Image Archive (tar.gz file) for deployment.
- The game server must be Linux based.
- It must be able to run as a non-root user.
- A game server must be able to service multiple clients simultaneously on a single server instance.
- A game server must expose only a single TCP/IP port that is TLS-enabled over IPv4
To support a synchronous game server on the Skillz platform, Skillz requires the following:
- A game server must be provided via a Docker Image Archive (tar.gz file) for deployment.
- The game server must be Linux based.
- It must be able to run as a non-root user.
Skillz supports both Ephemeral and Room-based server architecture. Based on which architecture you choose there are additional requirements.
- Ephemeral
- Room Based
- The server application must exit the process at the end of the match
- The Server application must also exit the process if the match never starts or is ended prematurely (e.g. such if one player never shows up in a 1:1 match)
- The server must expose only a single port over IPv4 or IPv6.
- We support UDP + TCP protocols.
- A game server must be able to service multiple clients simultaneously on a single server instance.
- A game server must expose only a single TCP/IP port that is TLS-enabled over IPv4
PUN uses cloud servers hosted by Photon. There are no server requirements necessary on your part to get started.
However, PUN is only available as a Unity package, and is not available for use with the native Android or iOS SDK. If your app is build in native Android or iOS, please look into one of our other synchronous technology options.
Developer Workflow
- Skillz Sync SDK
- Custom Server
- PUN
- Build your Real-Time Gameplay server
- Integrate the Skillz SDK
- Test your game connectivity with your server running locally by hard coding the connection received client-side from the Skillz SDK
- Please reference the Testing Real-Time Games section for common use cases to test against
- Once you have verified things work locally, work with the Skillz team to spin up a Sandbox server to validate the workflow.
- Once Sandbox testing is complete, Skillz will work with you to rollout the game into production.
- Build your Real-Time Gameplay server
- Integrate the Skillz SDK
- Test your game connectivity with your server running locally by hard coding the connection received client-side from the Skillz SDK
- Please reference the Testing Real-Time Games section for common use cases to test against
- Once you have verified things work locally, work with the Skillz team to spin up a Sandbox server to validate the workflow.
- Once Sandbox testing is complete, Skillz will work with you to rollout the game into production.
- Build a Sync game using PUN. You can use a free account from photon while developing.
- Integrate the Skillz SDK
- Test your game connectivity with PUN by hard coding the room information received client-side from the Skillz SDK
- Please reference the Testing Real-Time Games section for common use cases to test against
- Once you have verified things work locally, work with the Skillz team to spin up a Sandbox Sync template to validate the workflow.
- Once Sandbox testing is complete, Skillz will work with you to rollout the game into production.
Creating a Custom Real-Time Gameplay Server
- Skillz Sync SDK
- Custom Server
- PUN
NOTE: Click here to learn more about the Example Sync Server and the Skillz Server SDK.
Please note that the complete implementation of server-side gameplay logic is up to the developer. The Skillz Example Sync Server, as the name implies, only provides sample code to handle common gameplay logic.
In the Reporting Scores section, you will learn how to implement logic to report match scores back to Skillz from the server.
Install Java
If you are developing and testing your code locally, you will need to install Java.
NOTE: There are many implementations of Java, so pick the one that suits you best.
In the Example Sync Server, we use Amazon Corretto 11, which we will use in this guide.
To install Amazon Corretto 11, follow the instructions below:
- Amazon Corretto 11 Installation Instructions for Windows 7 or Later
- Amazon Corretto 11 Installation Instructions for macOS 10.13 or later
- Amazon Corretto 11 Installation Instructions for Debian-Based, RPM-Based and Alpine Linux Distributions
Install Docker
Since the Gameplay Server needs to be submitted to Skillz as a Docker Image, you will need to install Docker.
To do so, follow the instructions below:
Running the Example Sync Server Locally
Now that we have Java and Docker installed, let's try to get the Sample Sync Server running locally.
To do so, run the instructions below:
# Clone the example sync server
git clone https://github.com/skillz/skillz-example-sync-server.git
# Go to directory
cd skillz-example-sync-server
# Compile the gameplay server code
./gradlew build
# Start the gameplay server
./gradlew run
If you get logs similar to the following, that means the sample server was started successfully:
...
> Task :example_sync_server:run
2022-02-17 | 10:18:06.883 | main | INFO | com.skillz.server.Server | Running with INFO level logging.
2022-02-17 | 10:18:06.910 | main | INFO | com.skillz.server.Server | Starting Skillz Server 1.3.3...
2022-02-17 | 10:18:08.050 | main | INFO | com.skillz.server.World | Loading ExampleSkillzGame 1.0...
2022-02-17 | 10:18:08.071 | main | INFO | com.skillz.server.World | Using custom TICK_RATE of 100ms
2022-02-17 | 10:18:08.073 | main | INFO | com.skillz.server.World | Using custom WARNING_SECONDS value of 4
2022-02-17 | 10:18:08.073 | main | INFO | com.skillz.server.World | Using custom DISCONNECT_SECONDS value of 15
2022-02-17 | 10:18:08.073 | main | INFO | com.skillz.server.World | Using custom MAX_RECONNECTS value of -1
2022-02-17 | 10:18:08.073 | main | INFO | com.skillz.server.World | Using custom MAX_ALLOWED_APP_PAUSES value of -1
2022-02-17 | 10:18:08.073 | main | INFO | com.skillz.server.World | Using custom MIN_TIME_IN_SECONDS_FOR_NEW_PAUSE value of 1
2022-02-17 | 10:18:08.073 | main | INFO | com.skillz.server.World | Using custom MAX_ALLOWED_APP_CONNECTION_WARNINGS value of -1
2022-02-17 | 10:18:08.076 | main | INFO | com.skillz.server.World | Using custom USE_CUMULATIVE_PAUSE_DISCONNECT_TIMER value of true
2022-02-17 | 10:18:09.220 | main | INFO | c.s.server.ContentLoader | Loaded 2 MessageHandlers
2022-02-17 | 10:18:09.362 | main | INFO | com.skillz.server.Server | Server started successfully on port 10140!
<==========---> 80% EXECUTING [1m 4s]
> :example_sync_server:run
To end the server process, press CTRL + C
.
Running the Example Sync Server on Docker
Now that you know how to run the sync-server locally, let's try running it from a Docker container with the following commands:
# Clone the example sync server
git clone https://github.com/skillz/skillz-example-sync-server.git
# Go to directory
cd skillz-example-sync-server
# Compile the gameplay server code
./gradlew jar
# Go to app directory
cd example_sync_server
# Create Docker Directory
mkdir -p build/docker/resources
# Go to Docker Directory
cd build/docker
# Copy all files needed for sync-server to run
cp ../libs/*.jar ./server.jar
cp -rf ../../resources/certs resources
cp ../../../{Dockerfile,healthcheck.sh} .
# Build the Docker Image
docker build --platform linux/amd64 -t "sync-server:latest" .
# Start the Sync Server Container
docker run --name sync-server -e SYNC_RELEASE_CONFIGURATION="info" -p 10140:10140 -d "sync-server:latest"
# Get logs from Docker
docker logs -f sync-server
If you get logs similar to the following, that means the sample server was started in Docker successfully.
2022-03-03 | 19:45:53.838 | main | INFO | com.skillz.server.Server | Running with INFO level logging.
2022-03-03 | 19:45:53.855 | main | INFO | com.skillz.server.Server | Starting Skillz Server 1.3.4...
2022-03-03 | 19:45:54.959 | main | INFO | com.skillz.server.World | Loading ExampleSkillzGame 1.0...
2022-03-03 | 19:45:54.978 | main | INFO | com.skillz.server.World | Using custom TICK_RATE of 100ms
2022-03-03 | 19:45:54.979 | main | INFO | com.skillz.server.World | Using custom WARNING_SECONDS value of 4
2022-03-03 | 19:45:54.979 | main | INFO | com.skillz.server.World | Using custom DISCONNECT_SECONDS value of 15
2022-03-03 | 19:45:54.979 | main | INFO | com.skillz.server.World | Using custom MAX_RECONNECTS value of -1
2022-03-03 | 19:45:54.979 | main | INFO | com.skillz.server.World | Using custom MAX_ALLOWED_APP_PAUSES value of -1
2022-03-03 | 19:45:54.980 | main | INFO | com.skillz.server.World | Using custom MIN_TIME_IN_SECONDS_FOR_NEW_PAUSE value of 1
2022-03-03 | 19:45:54.980 | main | INFO | com.skillz.server.World | Using custom MAX_ALLOWED_APP_CONNECTION_WARNINGS value of -1
2022-03-03 | 19:45:54.982 | main | INFO | com.skillz.server.World | Using custom USE_CUMULATIVE_PAUSE_DISCONNECT_TIMER value of true
2022-03-03 | 19:45:55.062 | main | INFO | c.s.server.ContentLoader | Loaded 1 MessageHandlers
2022-03-03 | 19:45:55.120 | main | INFO | com.skillz.server.Server | Server started successfully on port 10140!
To end the server process, run the following commands:
# Stop the sync-server container
docker stop sync-server
# Make the sync-server container name available
docker rm sync-server
Now that you know how to run the sync-server, in the following sections you will learn how to start a match and report scores from the server.
There are many great options for building out custom Real-Time Gameplay servers. Some options include:
With Pun, Photon takes cares of all the server work for you. All you need to do is import the PUN 2 Package into Unity and follow these steps to get a access to a hosted server.
Real-Time Gameplay Integration
The Skillz Client SDK integration will be the same as for asynchronous game modes. The difference comes with how you handle things when the match begins. Once the match begins you will need to validate the match is a Synchronous match then connect to the game server and start sending and receiving game state updates based upon your needs.
Currently we only have Unity example apps. You can still implement your real-time game utilizing the Skillz iOS or Android SDKs. Work with the Skillz team to answer questions regarding your specific platform.
Starting a Game (Match Start)
- Skillz Sync SDK
- Custom Server
- PUN
Please note that the MatchId from the CustomServerConnectionInfo instance is the unique id for the sync match. This will be the same for the matched players and should be used to send the players to the same room.
string matchID = matchInfo.CustomServerConnectionInfo.MatchId;
void SkillzMatchDelegate.OnMatchWillBegin(Match matchInfo)
{
// Please note Unity provides a helper property to determine the match type
// if IsCustomSynchronousMatch is true we have a sync v2 match
if (matchInfo.IsCustomSynchronousMatch)
{
// get the needed info and handle your real-time game start
string matchID = matchInfo.CustomServerConnectionInfo.MatchId;
string hostName = matchInfo.CustomServerConnectionInfo.ServerIp;
string port = matchInfo.CustomServerConnectionInfo.ServerIp;
// ...
}
else
{
// handle your async game start
}
}
Please note that the MatchId from the CustomServerConnectionInfo instance is the unique id for the sync match. This will be the same for the matched players and should be used to send the players to the same room.
string matchID = matchInfo.CustomServerConnectionInfo.MatchId;
::: caution If you are building an ephemeral game server, the client will receive the IP in the following format: {IPv4,IPv6}. This will require the client to parse this value and handle connecting over IPv6 or IPv4 as desired.
Example: {"ip":"18.236.89.133,2600:1f13:2aa:4600:2735:6920:31c5:8812"} :::
void SkillzMatchDelegate.OnMatchWillBegin(Match matchInfo)
{
// Please note Unity provides a helper property to determine the match type
// if IsCustomSynchronousMatch is true we have a sync v2 match
if (matchInfo.IsCustomSynchronousMatch)
{
// get the needed info and handle your real-time game start
string matchID = matchInfo.CustomServerConnectionInfo.MatchId;
string hostName = matchInfo.CustomServerConnectionInfo.ServerIp;
string port = matchInfo.CustomServerConnectionInfo.ServerIp;
// ...
}
else
{
// handle your async game start
}
}
When a match begins the Skillz SDK will call the OnMatchWillBegin
method (Unity implementation). Use matchInfo.IsCustomSynchronousMatch
to determine if the present match is a real-time match. If so, you have a real-time match and can connect to Photon Server, Join a room and begin your game logic. If not you simply have an async match and can continue accordingly.
Please note that the MatchId from the CustomServerConnectionInfo instance is the unique id for the sync match. This will be the same for the matched players and should be used to send the players to the same room.
string matchID = matchInfo.CustomServerConnectionInfo.MatchId;
public sealed class SkillzGameController : SkillzMatchDelegate
{
public void OnMatchWillBegin(Match matchInfo)
{
// Please note Unity provides a helper property to determine the match type
// if IsCustomSynchronousMatch is true we have a sync v2 match
if (matchInfo.IsCustomSynchronousMatch)
{
SceneManager.LoadScene(SyncGameScene);
// ...
}
else
{
// handle your async game start
SceneManager.LoadScene(ASyncGameScene);
}
}
public sealed class SyncGameController : MonoBehaviourPunCallbacks
{
//keep track of connection status
bool isConnecting;
public void ConnectToPUN()
{
// #Critical, we must first and foremost connect to Photon Master Server.
//Will call OnConnectedToMaster() on successful connection
isConnecting = PhotonNetwork.ConnectUsingSettings();
}
public override void OnConnectedToMaster()
{
if (isConnecting)
{
// #Critical: The first we try to do is to join the room or create the room if it doesn't exist
Debug.Log("First time connecting to Master Server, creating and joining room");
//use sync match id as the room name
string skillzRoomName = SkillzCrossPlatform.GetMatchInfo().CustomServerConnectionInfo.MatchId;
PhotonNetwork.JoinOrCreateRoom(skillzRoomName, new RoomOptions { MaxPlayers = 2 }, default);
isConnecting = false;
}
}
}
Reporting Scores
Depending on your specific game setup, we provide either client-side or server-side score reporting.
Client-Side Score Reporting
For client-side score reporting, you submit scores the same way you would for an async game. Please read our score submitting docs for step-by-step instructions.
Server-Side Score Reporting
- Skillz Sync SDK
- Custom Server
- PUN
Ensure you are on the latest version of the Sync Server SDK library jarfile. This has been updated to be sourced from a remote repository in recent versions of the Sync Server SDK Example Server Project.Follow the upgrade steps below if you have not done so, already.
- Make the following changes to the your top-level build.gradle file:
https://github.com/skillz/skillz-example-sync-server/pull/1/files#diff-49a96...
- If you've made no other customizations to the original build.gradle file, you can simply replace it with the completed file located here: https://github.com/skillz/skillz-example-sync-server/blob/v1.2.1/build.gradle
- Delete the
server_sdk.jar
file in theexample_sync_server/libs/
folder (the top-level folderexample_sync_server
may be named differently in your project)
Implementation
- In your custom Game class, in the overridden
broadcast()
orprocess()
function, call the newly availablereportScores()
function when the game is over. - This functionality relies on the following:
Client.score
must be set accordingly- If
Game.isAborted()
andGame.isForfeited()
are used to determine the end game state, ensure the following functions are used when tracking/setting game state:Game.forfeitGame(Client forfeitingClient)
Game.abortGame(Client abortingClient)
@Override
def broadcast() {
for (Player player in players as List<Player>) {
if (isGamePaused() || isResuming()) {
player.getMessageSender().sendOpponentConnection()
}
player.getMessageSender().sendGameStateUpdate();
}
if (completed || aborted) {
log.info("Game over for matchId: " + matchId + ", completed: " + completed + ", aborted: " + aborted);
// The game has ended and we want to report the scores to the Skillz platform now
reportScores()
for (Player player in players as List<Player>) {
log.debug("Sending GameOver to player: " + player.getUserId());
player.getMessageSender().sendGameOver()
}
reset();
}
}
The Sync Event Service is responsible for handling HTTP POST requests from Sync game servers hosted on the Skillz platform. The URI of the service is exposed as an environment variable EVENT_SERVICE, while the endpoint for reporting scores is /realtime/v1/scores
. (The full URI of the report-score endpoint is thus ${EVENT_SERVICE}/realtime/v1/scores
.)
Your Skillz Game ID must be included as a header. For convenience, your game ID is exposed as an environment variable SKILLZ_GAME_ID
. The header must be included as X-Skillz-GameId
(see below for examples). In the future, this requirement will be removed and the Skillz platform will handle the inclusion of this Game ID header.
JSON Payload
The payload is sent as JSON and follows this schema:
matchmaker_match_id
- The match UUID sent down by the Skillz Matchmaker to both clients upon finding a successful matchscores
- List of two JSON objects, each containing the following values:user_id
- User ID of the playerscore
- Score for this playerabort_state
- If the user has aborted, we omitscore
and instead include a value ofUNKNOWN
for abort_state. In the future, this functionality will be expanded to allow for more verbose abort states
::: warning
- The
matchmaker_match_id
must be included with all requests. - At least one of the scores objects must include a
user_id
. (This might be the case if only one player were to ever connect to the match.) - Either a
score
orabort_state
must be sent for a given player. Including both ascore
andabort_state
object will result in an invalid request. ::: Below are examples of valid requests:
- Two Scores
- One Score and One Abort
- Two Aborts
- Only One Player Connected
{
"matchmaker_match_id": "f7c1e04f-6dc2-4af3-a875-5bdf541ae7c5",
"scores": [
{
"user_id": 2021938685,
"score": 100
},
{
"user_id": 14117213,
"score": 9001
}
]
}
{
"matchmaker_match_id": "f7c1e04f-6dc2-4af3-a875-5bdf541ae7c5",
"scores": [
{
"user_id": 2021938685,
"abort_state": "UNKNOWN"
},
{
"user_id": 14117213,
"score": 9004
}
]
}
{
"matchmaker_match_id": "f7c1e04f-6dc2-4af3-a875-5bdf541ae7c5",
"scores": [
{
"user_id": 2021938685,
"abort_state": "UNKNOWN"
},
{
"user_id": 14117213,
"abort_state": "UNKNOWN"
}
]
}
{
"matchmaker_match_id": "f7c1e04f-6dc2-4af3-a875-5bdf541ae7c5",
"scores": [
{
"user_id": 2021938685,
"score": 0
},
{
"abort_state": "UNKNOWN"
}
]
}
Example Implementation
Below is an example of reporting score in Groovy:
In the following example, the EventReporter
class exposes the following public methods:
reportScores(Long userId, Long userScore, Long opponentId, Long opponentScore, String matchmakerMatchId)
reportScoreAndAbort(Long userId, Long userScore, Long abortingOpponentId, String matchmakerMatchId)
reportDoubleAbort(Long abortingUserId, Long abortingOpponentId, String matchmakerMatchId)
Implementation
import com.google.gson.FieldNamingPolicy
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import io.netty.handler.codec.http.HttpResponseStatus
import okhttp3.MediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.Response
@CompileStatic
class Event {
String matchmakerMatchId
}
@CompileStatic
class ScoreEvent extends Event {
List<Score> scores
}
@CompileStatic
class Score extends Event {
Long userId
Long score
String abortState
}
@Slf4j
@CompileStatic
class EventReporter {
private static final String EVENT_SERVICE = System.getenv('EVENT_SERVICE')
private static final String GAME_ID = System.getenv('SKILLZ_GAME_ID')
private static final String REPORT_SCORE_ENDPOINT = "/realtime/v1/scores"
private static final MediaType JSON = MediaType.get("application/json; charset=utf-8")
private static final int MAX_REQUEST_RETRIES = 3
private final OkHttpClient client = new OkHttpClient()
private final Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create()
void reportScores(Long userId, Long userScore, Long opponentId, Long opponentScore, String matchmakerMatchId) {
ScoreEvent scoreEvent = new ScoreEvent(
matchmakerMatchId: matchmakerMatchId,
scores: [
new Score(
userId: userId,
score: userScore
),
new Score(
userId: opponentId,
score: opponentScore
)])
log.debug("Reporting score for matchmaker match ID ${matchmakerMatchId}")
postEventWithRetry(REPORT_SCORE_ENDPOINT, scoreEvent)
}
void reportScoreAndAbort(Long userId, Long userScore, Long abortingOpponentId, String matchmakerMatchId) {
ScoreEvent abortEvent = new ScoreEvent(
matchmakerMatchId: matchmakerMatchId,
scores: [
new Score(
userId: userId,
score: userScore
),
new Score(
userId: abortingOpponentId,
abortState: "UNKNOWN"
)])
log.debug("Reporting score for matchmaker match ID ${matchmakerMatchId}")
postEventWithRetry(REPORT_SCORE_ENDPOINT, abortEvent)
}
void reportDoubleAbort(Long abortingUserId, Long abortingOpponentId, String matchmakerMatchId) {
ScoreEvent abortEvent = new ScoreEvent(
matchmakerMatchId: matchmakerMatchId,
scores: [
new Score(
userId: abortingUserId,
abortState: "UNKNOWN"
),
new Score(
userId: abortingOpponentId,
abortState: "UNKNOWN"
)])
log.debug("Reporting score for matchmaker match ID ${matchmakerMatchId}")
postEventWithRetry(REPORT_SCORE_ENDPOINT, abortEvent)
}
private void postEventWithRetry(String endpoint, Event event, int retries = 0) {
try {
if (retries >= MAX_REQUEST_RETRIES) {
log.error("Request to submit score was not accepted after ${retries} failures for matchmaker match ID ${event.matchmakerMatchId}!")
return
}
Response response = postEvent(endpoint, event)
if (response.code() != HttpResponseStatus.ACCEPTED.code()
&& response.code() != HttpResponseStatus.BAD_REQUEST.code()
&& response.code() != HttpResponseStatus.UNPROCESSABLE_ENTITY.code()) {
log.error("Request to submit score was not accepted for matchmaker match ID ${event.matchmakerMatchId}:\n${response.code()} - ${response.body()}")
postEventWithRetry(endpoint, event, ++retries)
}
} catch (IOException e) {
log.error("Exception thrown while attempting to report score for matchmaker match ID ${event.matchmakerMatchId}:\n${e.toString()}")
postEventWithRetry(endpoint, event, ++retries)
}
}
private Response postEvent(String endpoint, Event event) throws IOException {
def json = gson.toJson(event)
log.debug("Sending POST HTTP request to endpoint: ${endpoint} with json: ${json}")
RequestBody body = RequestBody.create(json, JSON)
Request request = new Request.Builder()
.url("https://" + EVENT_SERVICE + endpoint)
.header("X-Skillz-GameId", GAME_ID)
.post(body)
.build()
try (Response response = client.newCall(request).execute()) {
return response
}
}
}
PUN does not support server side score reporting. You must use client score reporting.
Setting up Sandbox sync templates
- Skillz Sync SDK
- Custom Server
- PUN
Exporting the Docker Image Archive
To simplify the export of a Docker Image Archive, we prebuilt a Gradle Task that takes care of all the heavy lifting. You can also export it manually following the steps further down.
Gradle Task
- Install Docker Desktop on Mac (This export task is only supported on macOS development machines.)
- Execute the exportDockerImageArchive gradle task
Use --platform linux/amd64 when running docker build
- Locate the exported Docker Image Archive tarball at
<project_root>/<server_name>/docker/
Manually
Run the following commands in your terminal
IMAGE_NAME="INSERT_IMAGE_NAME_HERE"
# Go to directory with Dockerfile
cd <FOLDER_WITH_DOCKERFILE>
# Build container image
docker build -t ${IMAGE_NAME} .
# Create image archive
docker save -o ./${IMAGE_NAME}-$(date +%Y%m%dT%H%M%S).tar.gz ${IMAGE_NAME}
The final image archive file will have the name of ${IMAGE_NAME}-$(date +%Y%m%dT%H%M%S).tar.gz
. This image archive will be used to deploy your Sync Server on Skillz infrastructure.
Documentation for the docker save command is available on their website
Run the following commands in your terminal
IMAGE_NAME="INSERT_IMAGE_NAME_HERE"
# Go to directory with Dockerfile
cd <FOLDER_WITH_DOCKERFILE>
# Build container image
docker build -t ${IMAGE_NAME} .
# Create image archive
docker save -o ./${IMAGE_NAME}-$(date +%Y%m%dT%H%M%S).tar.gz ${IMAGE_NAME}
The final image archive file will have the name of ${IMAGE_NAME}-$(date +%Y%m%dT%H%M%S).tar.gz
. This image archive will be used to deploy your Sync Server on Skillz infrastructure.
Documentation for the docker save command is available on their website
Contacting Skillz
Now that you are ready, please contact us](https://developers.skillz.com/support/contact_us) and request the Skillz Sync Server functionality to be enabled.