Skip to main content

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.

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:

  1. Implementing our Sync Server SDK
  2. Implementing a custom game server
  3. 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​

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.

note

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:

Match Starting

Server Requirements​

To support a synchronous game server on the Skillz platform, Skillz requires the following:

  1. A game server must be provided via a Docker Image Archive (tar.gz file) for deployment.
  2. The game server must be Linux based.
  3. It must be able to run as a non-root user.
  4. A game server must be able to service multiple clients simultaneously on a single server instance.
  5. A game server must expose only a single TCP/IP port that is TLS-enabled over IPv4
Windows Hosting is currently not currently supported although we have plans to support it in the future.

Developer Workflow​

  1. Build your Real-Time Gameplay server
  2. Integrate the Skillz SDK
  3. Test your game connectivity with your server running locally by hard coding the connection received client-side from the Skillz SDK
  4. Please reference the Testing Real-Time Games section for common use cases to test against
  5. Once you have verified things work locally, work with the Skillz team to spin up a Sandbox server to validate the workflow.
  6. Once Sandbox testing is complete, Skillz will work with you to rollout the game into production.

Creating a Custom Real-Time Gameplay Server​

In this section we are going to learn how to deploy the [skillz-example-sync-server](https://github.com/skillz/skillz-example-sync-server) project locally.

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:

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.

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.

info

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)​

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. Then proceed to check the `Match` object to see if you have a server IP and port. If so, you have a real-time match and can connect to the server and begin your game logic. If not you simply have an async match and can continue accordingly.
info

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
}
}

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​

danger

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.

  1. 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...
  2. Delete the server_sdk.jar file in the example_sync_server/libs/ folder (the top-level folder example_sync_server may be named differently in your project)

Implementation​

  • In your custom Game class, in the overridden broadcast() or process() function, call the newly available reportScores() function when the game is over.
  • This functionality relies on the following:
    • Client.score must be set accordingly
    • If Game.isAborted() and Game.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();
}
}

Setting up Sandbox sync templates​

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​
  1. Install Docker Desktop on Mac (This export task is only supported on macOS development machines.)
  2. Execute the exportDockerImageArchive gradle task

    Use --platform linux/amd64 when running docker build

  3. 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

Contacting Skillz​

Now that you are ready, please email Skillz at integrations@skillz.com and request the Skillz Sync Server functionality to be enabled.