Access
Connect cross-platform accounts & identity management
Armada is a dynamic game server manager that utilizes a mixture of bare metal and cloud services to help reduce hosting costs for you, while still maintaining the best performance and experience for your players. Armada allows you to have multiple fleets of servers, which can be spread across different regions, providers, or infrastructure types.
Armada allows you to take advantage of several features designed to ensure your players have the best gaming experience possible.
Provider-agnostic approach Armada allows you to choose between multiple providers, depending on your hardware requirements. You can decide to have 100% of your servers in AWS cloud if you have free credits, or you can choose to have 90% of your game servers hosted using dedicated bare metal with the last 10% being scaled into cloud if you wish to bring your costs down. Armada allows you to pick and choose exactly what hardware you want, from whoever you want without any limitations!
Cost-aware scaling Armada ensures that hosting costs are taken into account when scaling up or down. When scaling up, Armada will prioritize filling bare metal machines before bursting into the cloud. When scaling down, Armada looks for opportunities to reduce the load on the cloud machines without impacting performance or player experience.
Multi-region hosting Armada has been designed for greater global coverage and better game performance across more regions. With Armada you aren’t tied to one hosting solution, which means you can make sure that your hosting providers cover each other’s blind spots. Mix and match providers, hardware types, and regions based on your player demands.
Integrated matchmaking and lobby Armada connects directly with AccelByte’s Matchmaking and Lobby services to provide geolocation support, so that you can match players in the same region for optimal player experience. If you already have a matchmaking and lobby service you prefer, we can help you integrate Armada with that instead of using AccelByte’s.
Unsure what hardware to use? If you are unsure what dedicated bare metal provider to use, what regions you want or you have only ever used cloud, the Armada team is here to help! We can advise on everything from specific hardware types to use, how your game servers will perform, and which regions will be best to support all your players. We have partnered with a number of dedicated hardware providers who already cover the majority of popular regions across the globe.
Before we can get you set up with Armada, our team will need some information about your game and players. This will help us accurately price out the hardware you’ll need, as well as make sure your hosting covers the right locations to give your players the best experience possible.
Player counts For us to provide the best experience possible, we need to know some information about your players. First, we need to know the maximum number of players that can connect to your game server. We also need to know what your estimated average number of players will be over the course of a month, as well as the estimated peak number of players. With these player numbers, we can ensure that you have enough bare metal to cover the average player count, as well as ensuring that you can scale effectively into cloud to meet peak demand when things get busy.
Player locations With Armada, you can host your game server sessions pretty much anywhere in the world. While this is a great achievement, we are also aware that some games are more popular in different parts of the world than they are elsewhere. It is important for us to know where the majority of your players are based, so that we can deploy bare metal and cloud instances as close to them as possible. The closer the hardware, the better the performance. Your players deserve the best possible experience every time they play your game!
Game server resource requirements Every game server that you deploy will have some form of CPU and RAM usage. In order to provide the best possible performance for your players, Armada needs to know what the worst case scenario is in terms of resource usage requirements. To do this, you will need to run a session with the maximum number of players connected, using the most resource intensive map and game mode possible. During this session, you should record the CPU, RAM and network usage over a full match.
Once you have this data, the Armada team can then run it through our calculator. This will tell us exactly how many game server sessions we can safely fit on your bare metal and cloud server instances without impacting the game server performance. More importantly, it will also allow us to calculate the best distribution of bare metal and cloud to ensure you always have capacity for your players as well as lowering the cost for hosting.
Here are some common terms in the Armada documentation, and their definitions:
Armada AccelByte’s product. A suite of tools, services, and SDKs used to enable multiplayer for a game
DSM / DSMC / Dedicated Server Manager One of Armada’s components that acts as the brain of Armada. This service stores deployment configurations that are defined from the Admin Portal and communicates with Nomad to deploy dedicated servers to be used in matchmaking.
DS / Dedicated Server / Game server / Authoritative server A counterpart for game clients in multiplayer architecture. Game clients connect to the game server during play sessions and have the server run all the game logic. In Armada, the traditional game server binary is containerized and deployed as a Docker container in a Nomad cluster. This running DS container is sometimes referred to as a pod.
DS Logs Logs generated by a running dedicated server. Game developers can download full DS logs from the Admin Portal.
DS Uploader A tool for game developers to upload the dedicated server binary to Armada. Upon upload, Armada’s services will containerize the server and store it in a Docker image repository.
Game Session An active play session where a game client is connected to a game server. In Armada, a game session can be created through matchmaking.
Nomad A container orchestration suite that is used by Armada to deploy game server containers. In a nutshell, the main components of Nomad are:
Armada configuration / Dedicated server configuration Settings where game developers specify how the game’s dedicated servers will be deployed. The configuration consists of:
QOS / Quality Of Service A set of services that are deployed in each of the regions where Armada can spawn dedicated servers. Using the SDK, game clients can call the QOS in each region to measure network latency and determine the nearest region to ensure that matchmaking pairs players that are near each other.
Make sure you’re authorized to use our services and have the following permissions before you use Armada:
Usage | Permissions | Action |
---|---|---|
Register a New Game Session | NAMESPACE:{namespace}:DSM:SESSION | Create |
Claim a DS for a Game Session | NAMESPACE:{namespace}:DSM:SESSION | Update |
Retrieve terminated servers in a specific namespace | ADMIN:NAMESPACE:{namespace}:DSAM:SERVER | Read |
Download the DS Artifact file. | ADMIN:NAMESPACE:{namespace}:DSAM:ARTIFACT | Read |
Check DS Artifact's existence before downloading the file. | ADMIN:NAMESPACE:{namespace}:DSAM:ARTIFACT | Read |
Retrieve terminated servers in all namespace | ADMIN:NAMESPACE:{namespace}:DSAM:SERVER | Read |
Permissions work slightly differently depending on whether they are assigned to IAM Clients or Roles assigned to users. For more information, read the Authentication and Authorization documentation.
For each multiplayer game that you wish to host using Armada, you will need a fleet of bare metal servers as well as access to some virtual cloud instances. This will ensure that you have the capacity needed to service your players.
As Armada is completely provider agnostic, you can source hardware from different providers depending on your needs, as well as utilize any and all cloud providers. What’s more, if you are unsure what providers to use or do not have the ability to organize your own hardware, the Armada team can source and deploy this for you using our recommended providers.
Each bare metal server and virtual cloud instance will have your multiplayer game server deployed to it so that when players make a matchmaking request, Armada will be able to start an instance based on the required location and instance priority.
Armada has a powerful priority system that allows you to specify what hardware or instance types should be used first to service your players. You may have a large fleet of bare metal servers, and also a bunch of credits provided to you from one of the many cloud providers. With Armada, you can choose if you want to burn through your credits first, or place your players on the bare metal capacity until the point where they are completely full and then burst into the cloud. The choice is entirely yours!
Armada works by connecting dedicated servers from different providers. The diagram below shows the flow and different services involved in connecting these servers. Any dedicated servers required are hosted directly on bare metal or cloud infrastructure that is specific to your game.
Below is a diagram of how Armada can work with our Matchmaking service.
Deployment override is a feature that enables you to override an existing deployment’s configurations if part of that deployment turns out to be unstable, so you can re-enable the previous working version. You can also use deployment override if you want to test a certain feature that has a different game version from your current deployment.
For example, to run the game you need to have a matching Game Client and Game Server. When you override a deployment, if you have 3 different game versions on 3 different development stages, you don't have to create 3 different deployments for each Game Version. You only need to create one Deployment Server that you can override based on the Game Version.
As seen in the diagram above, the override will be activated depending on the version requested by the game client.
After you upload a new server image, you can configure that server and its deployment in the Admin Portal.
In the Admin Portal, choose the namespace for the game for which you want to create the configuration.
Go to the Dedicated Server Management section and open the Configurations menu.
In the Server Configurations menu, click Add Configuration.
The Create Server Configuration wizard appears. Fill in the required fields for each step, then click Next to move on to the next step.
a. Namespace
If you create the configuration from the Publisher Namespace, you’ll need to choose the game namespace you want the configuration to apply to.
b. Server Timeout (Optional)
You can set your server timeout or click Next to use the default value.
c. Ports
Input the ports you want the dedicated server to listen to.
If you want to input multiple ports, select the Use Multiple Ports option.
If you want to add more ports, click Add More Port and repeat the last step. Repeat as needed until you’ve defined all of the ports.
d. Image Versions
Input the dedicated server image you received after uploading your build. You can add more image versions after you have created the configuration.
e. Pod Configurations
Input your server’s CPU and memory requirements and capabilities into the fields below.
If you don't specify the Param, the server will run with default options.
NOTE
The values for CPU Limit and Memory Limit must be formatted in Kubernetes, e.g. CPU of 1000m is equal to 1 core and Memory of 512Mi is equal to 512MB.
f. Deployments
Input the server count configuration for the deployment in the fields below. You can add more server count configurations later.
CAUTION
Be sure to input accurate values for Min. Count, Max Count, and Buffer Count, based on your server's capabilities. Values that exceed server capability can potentially cause your server to crash.
NOTE
Max. Count value can not be less than Min. Count value.
h. Summary
Before creating the Server Configuration you must confirm that the data you filled in on the previous pages is correct. You can always go back to correct the fields you want to fix by clicking the Back button. When all of the configuration data is correct, click Create Configuration. The configuration will be created according to your specifications.
After you have configured a different Image Version and Pod Configuration, next you can configure the Deployment. You can add another deployment with a different pod, version, and server region. Follow the steps below to add a new pod configuration.
Once completed, click Next to continue.
NOTE
If you set the Buffer type of Static to 0 or Dynamic buffer to 0%, there will not be spawned any server by default. Be sure to input Min, Max, Buffer counts according to your game's needs. Idle servers will still incur cost, while having no buffer will cause players to wait for servers to start.
Once completed, click Next to continue.
Once completed, click Next to continue.
You can patch the DS Image using the same version of the image without having to upload a whole new version. To patch the image version, follow the steps below:
In the Admin Portal, go to Dedicated Server Management and select Configuration page.
On the Server Configuration page, scroll down to the Image Server and Deployment section. Under the Image versions tab, you can see a list of the available image versions. Click the ellipsis button for your desired image and select View.
On the Image Server Detail page, under the Image Patch section, click the Add Image Patch button.
Download the version of the dedicated server uploader that matches your operating system. Once completed, click the Next button.
On the Generate Command page, fill in the fields with the data from your game server:
Once completed, click the Next button.
A command will be automatically generated. You can use this command to upload your game server, but before doing so, ensure that you have replaced the client_id and client_secret with the Client ID and Client Secret for the IAM Client for your DS Uploader. Open the CLI in the folder that contains your DS Uploader and game server, and then run the command.
The command will contain multiple flags denoting the information you entered earlier. The flags and their meanings are listed below.
Flag | Shortcut | Description |
--artifact string | -a | The directory path where artifact files will be stored. |
--bucket string | -b | The name of your S3 bucket. |
--command string | -c | The server’s executable command. |
--s3dirname string | -d | The directory path of your S3 bucket. |
--debug-enabled | Use this flag to install a debugging package into the server. | |
--default-image | Use this flag to make this server the default server image. | |
--deployment string | The deployment that will be used for this image server. | |
--hostname string | -H | The hostname path of the platform. |
--id string | -i | The IAM Client ID of your DS Uploader. |
--namespace string | -n | The namespace of your game. |
--path string | -p | The local path of the game server. |
--patch | Use this flag to set the image as the patch of the version. If you don't include this patch, your upload will be uploaded as a new image version. | |
--secret string | -s | The IAM Client Secret of your DS Uploader. |
--show-progress | Use this flag to make the server image upload appear in your CLI during the upload. | |
--version string | -v | The game server version. |
You can also view this information in your CLI by using the command <ds-uploader>.exe --help.
After you create a Dedicated Server Configuration, you should verify that the server has spawned correctly. In the Admin Portal, go to Dedicated Server Management and choose Servers.
On the Servers page you’ll see the elements listed below:
The Local Server shows the number of dedicated servers that run on your local computer. This type of dedicated server is used to perform testing before a game is published.
The Server Overview shows the number of dedicated servers you have configured, from both AWS and GCP hosting providers.
You can also sort servers by region.
You can see the details of each server by clicking on the three dots next to the desired server and selecting View.
The Available Regions shows the number of registered regions that you can run servers in. On this page you can see the status of each region.
The Total Sessions shows the number of player sessions currently active.
You can search the active player sessions using the Session ID or Match ID.
You can filter the active player sessions based on its region.
To see the session details, click View in the Action column next to the desired session.
The Session Details page appears. Here you can see detailed information about the session.
Armada is able to utilize multiple bare metal providers as well as the cloud. For this to work, each provider you use must be given a priority value, which can be changed at anytime from the Admin Portal. These priority values determine which servers Armada fills up first before moving to other providers.
TIP
Don't see your provider on the provider list? Only unsorted providers will be available in the Add Provider popup. If you want to add a new provider to your environment, contact the Armada team.
In the Admin Portal, go to Dedicated Server Management in the sidebar and open the Configurations menu.
In the Server Configuration, choose the Server Configuration that you want to see the deployment and click View.
On your server configuration, scroll down and switch to the Deployment tab.
Choose one of the Deployments by clicking the three-dots button and click View.
In the Deployment Details, there will be an option to enable or disable the Overriding Session.
Note that in the Default Deployment, you will not be able to edit or update the region.
In the Admin Portal, go to Dedicated Server Management in the sidebar and open the Configurations menu.
On the Server Configuration, choose the Server Configuration that you want to see the deployment and click View in the Action column for that configuration.
On the Server Configuration page, scroll down and switch to the Deployment tab.
Choose one of the Deployments and click the ellipsis to open the deployment details.
On the Deployment Details page, choose if you want to override the deployment version or deployment region.
Deployment version
a. To override the deployment version, enable the overriding version.
b. Go to the Override Deployment section and click the Add button.
c. The Add Deployment form appears. Fill in the fields with the required information:
When you’re done, click Add and your override deployment will be added to the list.
Deployment Region
a. To override the Deployment Region, enable the Override Region Configuration option.
b. When the Override Region Configuration box is selected, the Region section appears. Click the Add button.
c. The Add Override Region form appears. Fill in the following fields with the required information:
When you’re done, click the Add button. The new region-based buffer configuration will be added to the list.
In the Historical Logs menu, you can download the DS Logs and DS Artifacts from a server, even after the server has been terminated. DS Logs are the automatically produced and time-stamped documentation of a server’s events from the entire lifetime of the server, whereas DS Artifacts are the generated files that are written and stored during a server’s lifetime. These files enable you to debug the DS even after it has been terminated. After termination, the DS Logs and DS Artifacts will appear in the Admin Portal.
In the Admin Portal, choose the desired namespace.
TIP
Choose Publisher Namespace to see a list of the historical logs from all namespaces. You will be able to see a list of historical logs from a specific game namespace by filtering the logs.
Choose a Game Namespace to see the historical logs from that individual namespace.
In the Admin Portal, go to the Dedicated Server Management section in the sidebar and open the Historical Logs menu.
On the Historical Logs page, you can see a list of the historical logs.
You can filter the historical log results using the following filters:
Use the Namespace filter to show logs from a specific namespace only. This filter will only appear if you are in the publisher namespace. Choose All to show results from all namespaces.
INFO
NOTE
If you choose All, you will also see results from all deployments.
Use the Deployment filter to show logs from a particular deployment only. Choose All to show results from all deployments in the selected namespace.
INFO
NOTE If you want to select one specific deployment, you must first specify a game namespace in the Namespace field.
Use the Start Date and End Date fields to filter your search results to a particular period of time (optional).
Select Apply Filter to show the filtered results or Clear All to clear the filters.
You can also search for a historical log by Match ID, Party ID, or Pod Name.
To view historical log’s details, select View.
In the Details page, you can see detailed information about the selected historical log.
In the Admin Portal, choose the desired namespace.
TIP
Choose Publisher Namespace to see a list of historical logs from all the available namespaces. From here, you can filter the results for a specific game namespace.
Choose a Game Namespace to see the historical logs from that individual namespace only.
In the Admin Portal, go to the Dedicated Server Management section in the sidebar and open the Historical Logs menu.
On the Historical Logs page, you can see a list of the historical logs. You can use the following filters to refine your search:
Select Apply Filter to show the results or Clear All to clear the filters.
Select the logs you want to download by clicking the box on the right hand side of a log.
To download the log, select the Download Logs button.
NOTE
DS Artifacts can only be downloaded if you have filled the Artifact Directory field when uploading the image version.
If you didn’t fill in the Artifact Directory field when you uploaded your image version, you can patch the image version at any time and fill in the Artifact Directory field before you attempt to download DS Artifacts.
On the Details page of the selected historical log, select the Download Artifacts button.
NOTE
You can only download the DS Artifacts of a server that has been claimed before. Claimed servers are labeled Busy.
Follow the steps below to export a Dedicated Server Configuration from your game namespace.
In the Admin Portal, open the game namespace that contains the configuration you want to export. On the Server Configurations page, click the Export/Import button. Then, click Export.
Your Server Configuration will be exported in a JSON file. Here is an example of the contents:
IMPORTANT
If you use our Matchmaking service, don’t forget to also export your Matchmaking Configuration. You will need both configurations if you want to use them in a different game namespace.
Follow the steps below to import a Dedicated Server Configuration into a game namespace.
In the Admin Portal, go to the game namespace where you want to import the configuration. On the Server Configurations page, click the Export/Import button. Then, click Import.
The Import Configuration form appears. Here, select the configuration file that you want to import.
A confirmation window appears. To confirm your selection, type IMPORT in the text box, then click the Import button.
Here you can see the configuration has been added to the namespace.
IMPORTANT
If you use our Matchmaking service, don’t forget to also import your Matchmaking Configuration. You will need both configurations in this namespace to use Matchmaking with Armada.
When you use our Lobby service with Armada, it will create sessions and reserve servers for those sessions for you. If you prefer to use your own lobby, you can communicate with Armada via REST API to create a session and then reserve a server for that session. To do so, follow the steps below.
Upon successful request, a new session will be created.
You can then check on the status of your session using the GET /dsmcontroller/namespaces/{namespace}/sessions/{sessionID} endpoint.
Upon successful request, the DS will be claimed by your session.
using AccelByte.Server;
AccelByteServerPlugin.GetDedicatedServer().LoginWithClientCredentials(
result =>
{
if (result.IsError)
{
Debug.Log($"Server login failed");
}
else
{
Debug.Log("Server login successful");
}
});
AccelByteServerPlugin.GetDedicatedServerManager().RegisterLocalServer(ip, portNumber, name, registerResult =>
{
if (registerResult.IsError)
{
Debug.Log("Register Local Server to DSM failed");
}
else
{
Debug.Log("Register Local Server to DSM successful");
}
});
AccelByteServerPlugin.GetDedicatedServerManager().RegisterServer(port,
registerResult =>
{
if (registerResult.IsError)
{
Debug.Log("Register Server to DSM failed");
}
else
{
Debug.Log("Register Server to DSM successful");
}
});
AccelByteServerPlugin.GetDedicatedServerManager().GetSessionId(dsmResult =>
{
if (dsmResult.IsError)
{
Debug.Log("Failed Get Session Id");
}
else
{
Debug.Log("Successfully Get Session Id");
}
});
AccelByteServerPlugin.GetMatchmaking().QuerySessionStatus(dsmResult.Value.session_id,
queryResult =>
{
if (queryResult.IsError)
{
Debug.Log("Failed Query Session Status");
}
else
{
Debug.Log("Successfully Query Session Status");
}
});
AccelByteServerPlugin.GetDedicatedServerManager().DeregisterLocalServer(result =>
{
if (result.IsError)
{
Debug.Log("Failed Deregister Local Server");
}
else
{
Debug.Log("Successfully Deregister Local Server");
}
});
AccelByteServerPlugin.GetDedicatedServerManager().ShutdownServer(true,
result =>
{
if (result.IsError)
{
Debug.Log("Failed Shutdown Server");
}
else
{
Debug.Log("Successfully Shutdown Server");
}
});
In this tutorial, you will learn how to integrate Armada with your game and test it on your local PC or in the AccelByte Server by uploading the server image. This guide assumes that you have already implemented the Lobby (opens new window), Friends (opens new window), Party (opens new window), and Matchmaking (opens new window) services.
Since server implementation with Armada can vary for each game, you can familiarize yourself with other concepts and classes in the ServerModels.cs file inside the plugin SDK.
Before continuing, ensure that you have configured your AccelByteServerSDKConfig.json. Follow this tutorial (opens new window) to configure the server SDK.
We will start by adding simple Armada logic into the game.
using AccelByte.Server;
// Function called to Login by client credentials for the server.
public void LoginServer(int port, bool isLocal)
{
AccelByteServerPlugin.GetDedicatedServer().LoginWithClientCredentials(result =>
{
if (result.IsError)
{
// If we error, grab the Error Code and Message to print in the Log
Debug.Log($"Server login failed : {result.Error.Code}: {result.Error.Message}");
}
else
{
Debug.Log("Server login successful");
// Some actions
}
});
}
Now you can begin to integrate Armada into your game with the local Dedicated Server. You will need to register your local Dedicated Server to the Dedicated Server Manager (DSM). This will allow your Dedicated Server to be tracked by AccelByte’s DSM.
Log in with your client credentials and replace the actions comments as per the following code:
// Function called to Log in by client credentials for the server.
public static void LoginServer(int port, bool isLocal)
{
...
else
{
Debug.Log("Server login successful");
if (isLocal)
{
// Set local IP, server name, port
string ip = "127.0.0.1";
string name = $"localds-{DeviceProvider.GetFromSystemInfo().DeviceId}";
uint portNumber = Convert.ToUInt32(port);
// Register Local Server to DSM
AccelByteServerPlugin.GetDedicatedServerManager().RegisterLocalServer(ip, portNumber, name, registerResult =>
{
if (registerResult.IsError)
{
Debug.Log("Register Local Server to DSM failed");
}
else
{
Debug.Log("Register Local Server to DSM successful");
}
});
}
}
});
}
void Start()
{
#if UNITY_SERVER
LoginServer("7777", false);
#endif
}
Build and run the project as a server build. Your server log will read Server login successful and Register Local Server to DSM successful if the action has been completed successfully. You can also check in the Admin Portal to see if your local Dedicated Server has successfully registered in the DSM.
TROUBLESHOOTING
If you encounter a Server login failed: message, check your login credentials, API URLs, or the other configs in your AccelByteServerSDKConfig.json file.
public void GetMatchInfo()
{
// Get session id/ match id from DSM
AccelByteServerPlugin.GetDedicatedServerManager().GetSessionId(dsmResult =>
{
if (dsmResult.IsError)
{
Debug.Log("Failed Get Session Id");
}
else
{
Debug.Log("Successfully Get Session Id");
// Query Session Status to get match info from Matchmaking
AccelByteServerPlugin.GetMatchmaking().QuerySessionStatus(dsmResult.Value.session_id, queryResult =>
{
if (queryResult.IsError)
{
Debug.Log("Failed Query Session Status");
}
else
{
Debug.Log("Successfully Query Session Status. The game mode is: " + queryResult.Value.game_mode);
}
});
}
});
}
public void UnregisterServer(bool isLocal)
{
if (isLocal)
{
// Deregister Local Server to DSM
AccelByteServerPlugin.GetDedicatedServerManager().DeregisterLocalServer(result =>
{
if (result.IsError)
{
Debug.Log("Failed Deregister Local Server");
}
else
{
Debug.Log("Successfully Deregister Local Server");
}
});
}
}
void OnApplicationQuit()
{
#if UNITY_SERVER
UnregisterServer(false);
#endif
}
// Function called to Login by client credentials for the server.
public void LoginServer(int port, bool isLocal)
{
...
else
{
Debug.Log("Server login successful");
if (!isLocal)
{
// Register Server to DSM
AccelByteServerPlugin.GetDedicatedServerManager().RegisterServer(port, registerResult =>
{
if (registerResult.IsError)
{
Debug.Log("Register Server to DSM failed");
}
else
{
Debug.Log("Register Server to DSM successful");
}
});
}
});
}
To obtain match information, use the same logic from Step 6.
Create a Deregister or Shutdown Dedicated Server from Dedicated Server Manager script by modifying the following in the deregistration script that you have already created in Step 7:
public static void UnregisterServer(bool isLocal)
{
if (!isLocal)
{
// Shutdown Server to DSM
AccelByteServerPlugin.GetDedicatedServerManager().ShutdownServer(true, result =>
{
if (result.IsError)
{
Debug.Log("Failed Shutdown Server");
}
else
{
Debug.Log("Successfully Shutdown Server");
}
});
}
}
void Start()
{
#if UNITY_SERVER
LoginServer("7777", true);
#endif
}
Modify the boolean in the OnApplicationQuit() function.
void OnApplicationQuit()
{
#if UNITY_SERVER
UnregisterServer(true);
#endif
}
Now you can build your server and upload the image into the AccelByte Server. Follow this guide (opens new window) to upload the image to the AccelByte Server. Once completed, your server should be automatically created when the client gets a DS Updated notification event from the Matchmaking service.
NOTE
You can also configure the timeout for the Dedicated Server in the Admin Portal by following this guide. We recommend uploading your server image to the AccelByte Server with the Linux Server build.
Congratulations! You have now learnt how to use Armada!
Continue on for a step by step example of the code implementation.
public static class ArmadaHandler
public static void LoginServer(int port, bool isLocal) { … }
public static void LoginServer(int port, bool isLocal)
{
AccelByteServerPlugin.GetDedicatedServer().LoginWithClientCredentials(result =>
{
if (result.IsError)
{
// If we error, grab the Error Code and Message to print in the Log
Debug.Log($"Server login failed : {result.Error.Code}: {result.Error.Message}");
}
else
{
Debug.Log("Server login successful");
if (!isLocal)
{
// Register Server to DSM
AccelByteServerPlugin.GetDedicatedServerManager().RegisterServer(port, registerResult =>
{
if (registerResult.IsError)
{
Debug.Log("Register Server to DSM failed");
}
else
{
Debug.Log("Register Server to DSM successful");
}
});
}
else
{
string ip = "127.0.0.1";
string name = $"localds-{DeviceProvider.GetFromSystemInfo().DeviceId}";
uint portNumber = Convert.ToUInt32(port);
// Register Local Server to DSM
AccelByteServerPlugin.GetDedicatedServerManager().RegisterLocalServer(ip, portNumber, name, registerResult =>
{
if (registerResult.IsError)
{
Debug.Log("Register Local Server to DSM failed");
}
else
{
Debug.Log("Register Local Server to DSM successful");
}
});
}
}
});
}
public static void GetPlayerInfo(ResultCallback<MatchmakingResult> callback)
{
// Get session id/ match id from DSM
AccelByteServerPlugin.GetDedicatedServerManager().GetSessionId(dsmResult =>
{
if (dsmResult.IsError)
{
Debug.Log("Failed Get Session Id");
callback.TryError(dsmResult.Error);
}
else
{
// Query Session Status to get match info from Matchmaking
AccelByteServerPlugin.GetMatchmaking().QuerySessionStatus(dsmResult.Value.session_id, queryResult =>
{
if (queryResult.IsError)
{
Debug.Log("Failed Query Session Status");
callback.TryError(queryResult.Error);
}
else
{
// Return error if status is not matched
if (queryResult.Value.status != AccelByte.Models.MatchmakingStatus.matched)
{
Debug.Log("Matchmaking status is not matched");
// Return error callback
callback.TryError(queryResult.Error);
}
// Return success callback
callback.TryOk(queryResult.Value);
}
});
}
});
}
public static void UnregisterServer(bool isLocal) { … }
public static void UnregisterServer(bool isLocal)
{
if (isLocal)
{
// Deregister Local Server to DSM
AccelByteServerPlugin.GetDedicatedServerManager().DeregisterLocalServer(result =>
{
if (result.IsError)
{
Debug.Log("Failed Deregister Local Server");
}
else
{
Debug.Log("Successfully Deregister Local Server");
Application.Quit();
}
});
}
else
{
// Shutdown Server to DSM
AccelByteServerPlugin.GetDedicatedServerManager().ShutdownServer(true, result =>
{
if (result.IsError)
{
Debug.Log("Failed Shutdown Server");
}
else
{
Debug.Log("Successfully Shutdown Server");
Application.Quit();
}
});
}
}
private bool isLocal = false;
internal void OnAccelByteServerStarted(int port)
{
// Get the local command line argument for the local test
isLocal = ConnectionHandler.GetLocalArgument();
ArmadaHandler.LoginServer(port, isLocal);
}
public override void OnStartServer()
{
base.OnStartServer();
GameplayManager.OnAccelByteServerStarted(transport.ServerUri().Port);
GameplayManager.OnServerStarted();
}
void OnServerStartClient(NetworkConnection conn, ServerStartClientMessage msg)
{
playerInfos.Add(conn, new PlayerInfo { playerId = msg.playerId, displayName = msg.displayName });
PlayerInfo playerInfo = playerInfos[conn];
ArmadaHandler.GetPlayerInfo(result =>
{
if (result.IsError) return;
bool isPartyA = true;
bool foundPlayer = false;
// Get total player from game mode in result
totalPlayers = result.Value.game_mode.ToGameMode().GetTotalPlayers();
// Check if the user exists and assign the party
foreach (var ally in result.Value.matching_allies)
{
foreach (var party in ally.matching_parties)
{
foreach (var user in party.party_members)
{
if (user.user_id == playerInfo.playerId)
{
playerInfo.isPartyA = isPartyA;
foundPlayer = true;
break;
}
}
if (foundPlayer) break;
}
if (foundPlayer) break;
isPartyA = !isPartyA;
}
// Remove player info if the player is not registered in the current match
if (!foundPlayer)
{
playerInfos.Remove(conn);
return;
}
totalPlayersConnected++;
Debug.Log($"Total player Connected : {totalPlayersConnected}/{totalPlayers}");
// Update player infos dictionary
playerInfos[conn] = playerInfo;
Debug.Log(string.Format("Player {0} is joining in the party {1}", playerInfo.displayName, playerInfo.isPartyA ? "A" : "B"));
// Start the game if total players connected and total players are same
if (totalPlayersConnected == totalPlayers)
{
foreach (NetworkConnection connection in playerInfos.Keys)
{
connection.Send(new ClientStartClientResponseMessage { });
}
if (isServer)
{
StartCoroutine(CountdownTimer());
}
}
});
}
private void OnApplicationQuit()
{
#if UNITY_SERVER
ArmadaHandler.UnregisterServer(isLocal);
#endif
}
IEnumerator CloseServer(int timeout = 30)
{
Debug.Log("Start countdown to close server");
for (int i = 0; i < timeout; i++)
{
yield return new WaitForSeconds(1.0f);
}
ArmadaHandler.UnregisterServer(isLocal);
}
void OnServerStopTimerMessage(NetworkConnection conn, ServerRequestStopTimerMessage msg)
{
totalPlayersStop++;
PlayerInfo playerInfo = playerInfos[conn];
playerInfo.playerScoreTime = mainTime;
playerInfos[conn] = playerInfo;
Debug.Log($"Total player Stop: {totalPlayersStop}/{totalPlayers}");
if (totalPlayersStop == totalPlayers)
{
StartCoroutine(CloseServer());
OnServerStopGameplay();
}
}
Build your server and upload your server image to the AccelByte Server.
Once completed, you can test your build. To test on your local PC, run the local server with the command line argument -local. You can either create a shortcut or a batch file to test local servers. If you want to create a batch file, you can use the following example: batch file.
@ECHO ON
start Server/Justice-Unity-Tutorial-Project.exe local
Congratulations! You have fully implemented the Armada Service and successfully installed the AccelByte Unity SDK and AccelByte Config file.
// Copyright (c) 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.
using System;
using System.Collections.Generic;
using UnityEngine;
using AccelByte.Server;
using AccelByte.Api;
using AccelByte.Core;
using AccelByte.Models;
public static class ArmadaHandler
{
/// <summary>
/// Server login with the server client credentials and register DS to DSM
/// </summary>
/// <param name="port"> </param>
/// <param name="isLocal"></param>
public static void LoginServer(int port, bool isLocal)
{
AccelByteServerPlugin.GetDedicatedServer().LoginWithClientCredentials(result =>
{
if (result.IsError)
{
// If we error, grab the Error Code and Message to print in the Log
Debug.Log($"Server login failed : {result.Error.Code}: {result.Error.Message}");
}
else
{
Debug.Log("Server login successful");
if (!isLocal)
{
// Register Server to DSM
AccelByteServerPlugin.GetDedicatedServerManager().RegisterServer(port, registerResult =>
{
if (registerResult.IsError)
{
Debug.Log("Register Server to DSM failed");
}
else
{
Debug.Log("Register Server to DSM successful");
}
});
}
else
{
string ip = "127.0.0.1";
string name = $"localds-{DeviceProvider.GetFromSystemInfo().DeviceId}";
uint portNumber = Convert.ToUInt32(port);
// Register Local Server to DSM
AccelByteServerPlugin.GetDedicatedServerManager().RegisterLocalServer(ip, portNumber, name, registerResult =>
{
if (registerResult.IsError)
{
Debug.Log("Register Local Server to DSM failed");
}
else
{
Debug.Log("Register Local Server to DSM successful");
}
});
}
}
});
}
/// <summary>
/// Unregister DS from DSM and quit the app
/// </summary>
/// <param name="isLocal"> Unregister local DS if the value is true</param>
public static void UnregisterServer(bool isLocal)
{
if (isLocal)
{
// Deregister Local Server to DSM
AccelByteServerPlugin.GetDedicatedServerManager().DeregisterLocalServer(result =>
{
if (result.IsError)
{
Debug.Log("Failed Deregister Local Server");
}
else
{
Debug.Log("Successfully Deregister Local Server");
Application.Quit();
}
});
}
else
{
// Shutdown Server to DSM
AccelByteServerPlugin.GetDedicatedServerManager().ShutdownServer(true, result =>
{
if (result.IsError)
{
Debug.Log("Failed Shutdown Server");
}
else
{
Debug.Log("Successfully Shutdown Server");
Application.Quit();
}
});
}
}
/// <summary>
/// DS queries match info from Matchmaking (MM)
/// </summary>
/// <param name="callback"> Return match info callback</param>
public static void GetPlayerInfo(ResultCallback<MatchmakingResult> callback)
{
// Get session id/ match id from DSM
AccelByteServerPlugin.GetDedicatedServerManager().GetSessionId(dsmResult =>
{
if (dsmResult.IsError)
{
Debug.Log("Failed Get Session Id");
callback.TryError(dsmResult.Error);
}
else
{
// Query Session Status to get match info from Matchmaking
AccelByteServerPlugin.GetMatchmaking().QuerySessionStatus(dsmResult.Value.session_id, queryResult =>
{
if (queryResult.IsError)
{
Debug.Log("Failed Query Session Status");
callback.TryError(queryResult.Error);
}
else
{
// Return error if status is not matched
if (queryResult.Value.status != AccelByte.Models.MatchmakingStatus.matched)
{
Debug.Log("Matchmaking status is not matched");
// Return error callback
callback.TryError(queryResult.Error);
}
// Return success callback
callback.TryOk(queryResult.Value);
}
});
}
});
}
}
// Copyright (c) 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.
using Mirror;
using System;
using UnityEngine;
public class WatchTimeNetworkManager : NetworkManager
{
[SerializeField]
private GameplayManager GameplayManager;
public override void Start()
{
base.Start();
#if !UNITY_SERVER
// Change ip and port based on DS info in the client
networkAddress = ConnectionHandler.ip;
gameObject.GetComponent<kcp2k.KcpTransport>().Port = ConnectionHandler.uPort;
// Auto start the client connection
StartClient();
#endif
}
#region Client System Callbacks
/// <summary>
/// Called on the client when connected to a server.
/// <para>The default implementation of this function sets the client as ready and adds a player. Override the function to dictate what happens when the client connects.</para>
/// </summary>
/// <param name="conn">Connection to the server.</param>
public override void OnClientConnect(NetworkConnection conn)
{
base.OnClientConnect(conn);
GameplayManager.OnPlayerStarted();
}
#endregion
#region Start & Stop Callbacks
/// <summary>
/// Called when a server is started - including when a host is started.
/// <para>StartServer has multiple signatures, but they all cause this hook to be called.</para>
/// </summary>
public override void OnStartServer()
{
base.OnStartServer();
GameplayManager.OnAccelByteServerStarted(transport.ServerUri().Port);
GameplayManager.OnServerStarted();
}
#endregion
/// <summary>
/// Called when the server stop the client connections
/// </summary>
public override void OnStopClient()
{
base.OnStopClient();
GameplayManager.OnPlayerDisconnected();
}
}
// Copyright (c) 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using AccelByte.Api;
using Mirror;
public class GameplayManager : NetworkBehaviour
{
[SerializeField]
private GameplayInterface gameCanvas;
private int totalPlayersConnected = 0;
private int totalPlayersStop = 0;
private int totalPlayers = 0;
private bool isLocal = false;
private double targetTime;
private double mainTime;
internal static readonly Dictionary<NetworkConnection, PlayerInfo> playerInfos = new Dictionary<NetworkConnection, PlayerInfo>();
/// <summary>
/// Called by server to login by credentials
/// </summary>
/// <param name="port"></param>
internal void OnAccelByteServerStarted(int port)
{
// Get the local command line argument for the local test
isLocal = ConnectionHandler.GetLocalArgument();
ArmadaHandler.LoginServer(port, isLocal);
}
/// <summary>
/// Called on Start Server
/// </summary>
internal void OnServerStarted()
{
if (!NetworkServer.active) return;
NetworkServer.RegisterHandler<ServerStartClientMessage>(OnServerStartClient);
NetworkServer.RegisterHandler<ServerRequestStopTimerMessage>(OnServerStopTimerMessage);
}
/// <summary>
/// Called on Client connect to server
/// </summary>
internal void OnPlayerStarted()
{
if (!NetworkClient.active) return;
NetworkClient.RegisterHandler<ClientStartClientResponseMessage>(OnStartClientResponse);
NetworkClient.RegisterHandler<ClientUpdateCountdownTimerMessage>(OnUpdateCountdownTime);
NetworkClient.RegisterHandler<ClientChangeToGameplayStateMessage>(OnChangeToGameplayState);
NetworkClient.RegisterHandler<ClientUpdateMainTimeMessage>(OnUpdateMainTime);
NetworkClient.RegisterHandler<ClientOnAllPlayerStopTime>(OnAllClientStopTime);
// Current user's userId and displayName
string userId = AccelBytePlugin.GetUser().Session.UserId;
string displayName = LobbyHandler.Instance.partyHandler.partyMembers[userId];
NetworkClient.connection.Send(new ServerStartClientMessage { playerId = userId, displayName = displayName });
// Set the user id inside the gameplay interface player id. this to check after gameplay ended, the interface will know where their current player information by matching the player id
gameCanvas.playerId = userId;
}
/// <summary>
/// Called on Client disconnect
/// </summary>
internal void OnPlayerDisconnected()
{
gameCanvas.ChangePanel(GameplayInterfaceState.None);
}
/// <summary>
/// Send message to server that player press the stop button
/// </summary>
public void RequestStopTime()
{
NetworkClient.connection.Send(new ServerRequestStopTimerMessage { });
}
/// <summary>
/// Server set player's info
/// </summary>
/// <param name="conn"> player's network connection</param>
/// <param name="msg"> message that contains player's info</param>
void OnServerStartClient(NetworkConnection conn, ServerStartClientMessage msg)
{
playerInfos.Add(conn, new PlayerInfo { playerId = msg.playerId, displayName = msg.displayName });
PlayerInfo playerInfo = playerInfos[conn];
ArmadaHandler.GetPlayerInfo(result =>
{
if (result.IsError) return;
bool isPartyA = true;
bool foundPlayer = false;
// Get total player from game mode in result
totalPlayers = result.Value.game_mode.ToGameMode().GetTotalPlayers();
// Check if the user exists and assign the party
foreach (var ally in result.Value.matching_allies)
{
foreach (var party in ally.matching_parties)
{
foreach (var user in party.party_members)
{
if (user.user_id == playerInfo.playerId)
{
playerInfo.isPartyA = isPartyA;
foundPlayer = true;
break;
}
}
if (foundPlayer) break;
}
if (foundPlayer) break;
isPartyA = !isPartyA;
}
// Remove player info if the player is not registered in the current match
if (!foundPlayer)
{
playerInfos.Remove(conn);
return;
}
totalPlayersConnected++;
Debug.Log($"Total player Connected : {totalPlayersConnected}/{totalPlayers}");
// Update player infos dictionary
playerInfos[conn] = playerInfo;
Debug.Log(string.Format("Player {0} is joining in the party {1}", playerInfo.displayName, playerInfo.isPartyA ? "A" : "B"));
// Start the game if total players connected and total players are same
if (totalPlayersConnected == totalPlayers)
{
foreach (NetworkConnection connection in playerInfos.Keys)
{
connection.Send(new ClientStartClientResponseMessage { });
}
if (isServer)
{
StartCoroutine(CountdownTimer());
}
}
});
}
/// <summary>
/// Server set the player stop time
/// </summary>
/// <param name="conn"> player's network connection</param>
/// <param name="msg"> server's message</param>
void OnServerStopTimerMessage(NetworkConnection conn, ServerRequestStopTimerMessage msg)
{
totalPlayersStop++;
PlayerInfo playerInfo = playerInfos[conn];
playerInfo.playerScoreTime = mainTime;
playerInfos[conn] = playerInfo;
Debug.Log($"Total player Stop: {totalPlayersStop}/{totalPlayers}");
if (totalPlayersStop == totalPlayers)
{
StartCoroutine(CloseServer());
OnServerStopGameplay();
}
}
/// <summary>
/// Server finish the round since all players have pressed the stop button
/// </summary>
void OnServerStopGameplay()
{
StopCoroutine("StopWatch");
List<NetworkConnection> keysToUpdate = new List<NetworkConnection>();
keysToUpdate.AddRange(playerInfos.Keys.ToArray());
List<double> scores = new List<double>();
for (int i = 0; i < keysToUpdate.Count; i++)
{
scores.Add(Mathf.Abs((float)(targetTime - playerInfos.Values.ToArray()[i].playerScoreTime)));
}
double currentHigherScore = 99999999.0f; // in this case the lower value is the winner
for (int i = 0; i < scores.Count; i++)
{
if (scores[i] < currentHigherScore)
{
currentHigherScore = scores[i];
}
}
int highscoreIndex = scores.FindIndex(x => x == currentHigherScore);
for (int i = 0; i < keysToUpdate.Count; i++)
{
PlayerInfo playerInformation = playerInfos[keysToUpdate[i]];
if (playerInformation.isPartyA == playerInfos[keysToUpdate[highscoreIndex]].isPartyA)
{
playerInformation.isWin = true;
}
else
{
playerInformation.isWin = false;
}
playerInfos[keysToUpdate[i]] = playerInformation;
}
foreach (NetworkConnection connection in playerInfos.Keys)
{
connection.Send(new ClientOnAllPlayerStopTime { allPlayerInfos = playerInfos.Values.ToArray(), targetTime = targetTime });
}
}
/// <summary>
/// Coroutine: Update loading countdown from 3 to 0
/// </summary>
/// <returns> wait for 1 second</returns>
IEnumerator CountdownTimer()
{
for (int countdown = 3; countdown >= 0; countdown--)
{
foreach (NetworkConnection connection in playerInfos.Keys)
{
if (isServer)
{
connection.Send(new ClientUpdateCountdownTimerMessage { time = countdown });
}
}
yield return new WaitForSeconds(1.0f);
if (countdown == 0)
{
// Set target time
// random target time with range a to b seconds
targetTime = Random.Range(3.0f, 9.0f);
// send targetTime value to all client
foreach (NetworkConnection connection in playerInfos.Keys)
{
connection.Send(new ClientChangeToGameplayStateMessage { targetTime = targetTime });
}
StartCoroutine(MainTime());
}
}
}
/// <summary>
/// Coroutine: Update current running mainTime
/// </summary>
/// <returns></returns>
IEnumerator MainTime()
{
while (true)
{
mainTime += Time.deltaTime;
foreach (NetworkConnection connection in playerInfos.Keys)
{
connection.Send(new ClientUpdateMainTimeMessage { mainTime = mainTime });
}
yield return null;
}
}
/// <summary>
/// Unregister server and close the server automatically after the time is timeout
/// </summary>
/// <param name="timeout"></param>
/// <returns></returns>
IEnumerator CloseServer(int timeout = 30)
{
Debug.Log("Start countdown to close server");
for (int i = 0; i < timeout; i++)
{
yield return new WaitForSeconds(1.0f);
}
ArmadaHandler.UnregisterServer(isLocal);
}
/// <summary>
/// On client start, change panel to ReadyPanel
/// </summary>
/// <param name="msg"> client's message</param>
void OnStartClientResponse(ClientStartClientResponseMessage msg)
{
gameCanvas.ChangePanel(GameplayInterfaceState.Loading);
}
/// <summary>
/// On loading countdown, update LoadingPanel's UI
/// </summary>
/// <param name="msg"></param>
void OnUpdateCountdownTime(ClientUpdateCountdownTimerMessage msg)
{
gameCanvas.UpdateLoadingPanelUI(msg.time);
}
/// <summary>
/// Change panel to GameplayPanel and start the game
/// </summary>
/// <param name="msg"></param>
void OnChangeToGameplayState(ClientChangeToGameplayStateMessage msg)
{
gameCanvas.ChangePanel(GameplayInterfaceState.Gameplay);
gameCanvas.UpdateTargetTimeUI(msg.targetTime);
}
/// <summary>
/// On current mainTime update, update mainTime to its UI
/// </summary>
/// <param name="msg"></param>
void OnUpdateMainTime(ClientUpdateMainTimeMessage msg)
{
gameCanvas.UpdateMainTimeUI(msg.mainTime);
}
/// <summary>
/// On all players have pressed the stop button, finish the game
/// </summary>
/// <param name="msg"></param>
void OnAllClientStopTime(ClientOnAllPlayerStopTime msg)
{
gameCanvas.ChangePanel(GameplayInterfaceState.Result);
gameCanvas.UpdateResultPanelUI(msg.allPlayerInfos, msg.targetTime);
}
private void OnApplicationQuit()
{
#if UNITY_SERVER
ArmadaHandler.UnregisterServer(isLocal);
#endif
}
}