Unity Netcode for Game Objects
Netcode for Game Objects on Arbitrium
This guide will help you create a headless server on Edgegap for a Unity project using Netcode for GameObjects as its networking solution.
We will use the open-source sample project 2DSpaceShooter
from Unity Bitesize Samples as an example.
This guide was written using Unity 2021.3.8f1 LTS
on Windows to produce all Unity builds. The Docker part was made using Linux Mint Cinnamon 20.3.
Get the base project
To begin, copy the sample project. This guide uses the project at this commit.
git clone https://github.com/Unity-Technologies/com.unity.multiplayer.samples.bitesize/
cd com.unity.multiplayer.samples.bitesize
git checkout 2ac6a9a38f9dd4ff1e748d1a73b3086f986f6d75
Open the project in Unity, open scene Assets/Scenes/network
. Hit play in the editor and choose Host (Server + Client)
to test the game locally.
Adapt your game to support Dedicated Game Server (DGS) mode
Dedicated Game Server (DGS) mode is the mode used by Netcode when you call NetworkManager.StartServer()
.
Add automatic server start
We need to change the sample HUD provided. The idea is to automatically start Netcode server if the build is a Unity server.
-
Server doesn't need HUD, so in
Assets/Scripts/NetworkManagerHud.cs
, enclose functionvoid OnGUI()
with preprocessor directive#if !UNITY_SERVER
and#endif
. -
Add a new
Start()
method, and a call tom_NetworkManager.StartServer();
inside. Enclose the call with preprocessor directive#if UNITY_SERVER
and#endif
. We will also add a log to show that the server started.
At the end, modifed part of NetworkManagerHud.cs
should look like this:
...
void Awake()
{
// Only cache networking manager but not transport here because transport could change anytime.
m_NetworkManager = GetComponent<NetworkManager>();
m_LabelTextStyle = new GUIStyle(GUIStyle.none);
m_Transport = (UnityTransport)m_NetworkManager.NetworkConfig.NetworkTransport;
}
private void Start()
{
#if UNITY_SERVER
m_NetworkManager.StartServer();
Debug.Log($"Started server on {m_Transport.ConnectionData.ServerListenAddress}:{m_Transport.ConnectionData.Port}");
#endif
}
#if !UNITY_SERVER
void OnGUI()
{
m_LabelTextStyle.normal.textColor = LabelColor;
GUILayout.BeginArea(new Rect(DrawOffset, new Vector2(200, 200)));
if (IsRunning(m_NetworkManager))
{
DrawStatusGUI();
}
else
{
DrawConnectGUI();
}
GUILayout.EndArea();
}
#endif
...
- To allow the server to receive connection from all IP, add the value
0.0.0.0
on the GameObjectNetworkManager
inUnityTransport -> Connection Data -> Server Listen Address
.
(OPTIONNAL) Making a windows server build to test locally
Build for Windows server. This build target is available since Unity 2021.2 (see Unity forum post).
-
If you use an older Unity version, add the variable
UNITY_SERVER
inProjects Settings -> Player -> Scripting Define Symbols
, and checkHeadless Mode
andServer Build
. -
Otherwise, simply select the
Dedicated Server
platform option.
-
Start the executable. You might have errors and warnings about shaders not being supported, this is not an issue.
-
Allow Windows defender to let incoming connections from the outside
-
Start the editor, click on
Client
to connect to 127.0.0.1 (localhost) -
You should have a spaceship and be able to control it!
-
If you have another computer, you can build an executable, and connect to the server with its local IP.
The sample as latency issue, game might have a different feeling when running the server on an other machine, even with very low local network latency. Unity Documentation
This method of running physics makes sure that there are no desyncs or other physics issues between the client and server, but it introduces more latency. With future prediction support of Netcode, the latency will no longer be an issue which makes this the best choice of a movement model for a game like this.
Contenerizing the dedicated game server
In this part, we will create a docker image containing the dedicated game server. You might also be interested in reading Unity on Docker.
- Make a build for linux server. Our build executable name will be
2d.x86_64
, inside aLinux_server
folder.
- Transfert
Linux_server
folder to a Linux system, in a empty folder, along the given Dockerfile.
Dockerfile
FROM debian:11.4
# Update root CA to ensure outbound HTTPS requests don't fail
RUN apt-get update && \
apt-get install -y ca-certificates && \
apt-get clean && \
update-ca-certificates
# Copy server file from host into the image
COPY Linux_server /Linux_server
# Gave permission in the image to run the executable
RUN chmod u+x /Linux_server/2d.x86_64
EXPOSE 7777/udp
ENTRYPOINT /Linux_server/2d.x86_64
- Start a command prompt in the new folder, then run the folowing Docker commands:
- Linux
- cmd
- Powershell
For ARM CPU (Mac M1, M2, etc.) user, see the dedicated page : ARM CPU
# build the image
docker build . -t <IMAGE_NAME>:<IMAGE_VERSION>
# login, a prompt will ask the password
docker login -u '<REGISTRY_USERNAME>' <REGISTRY_URL>
# add another tag to your image corresponding to the registry
docker image tag <IMAGE_NAME>:<IMAGE_VERSION> <REGISTRY_URL>/<PROJECT_NAME>/<IMAGE_NAME>:<IMAGE_VERSION>
#push the image
docker push <REGISTRY_URL>/<PROJECT_NAME>/<IMAGE_NAME>:<IMAGE_VERSION>
For ARM CPU (Mac M1, M2, etc.) user, see the dedicated page : ARM CPU
# build the image
docker build . -t <IMAGE_NAME>:<IMAGE_VERSION>
# login, a prompt will ask the password
docker login -u '<REGISTRY_USERNAME>' <REGISTRY_URL>
# add another tag to your image corresponding to the registry
docker image tag <IMAGE_NAME>:<IMAGE_VERSION> <REGISTRY_URL>/<PROJECT_NAME>/<IMAGE_NAME>:<IMAGE_VERSION>
#push the image
docker push <REGISTRY_URL>/<PROJECT_NAME>/<IMAGE_NAME>:<IMAGE_VERSION>
See this documentation if you want to use the Edgegap Registry. You can also use another private registry.
Deploying to Edgegap
-
Connect to the dashboard.
-
Choose your image with your registry and your credential.
-
You have to open UDP on port 7777. When deploying the application, the port 7777 in your container will be bound to another random host port, accessible publicly. Edgegap API can give you the host IP and port binding for each deployment.
-
After finished creating your application, click on
🚀 Deploy
button to make your first manual deployment. Let the default settings (random players IP) and deploy. -
When the deployment is ready, open the
Port Mapping
tab.
We can see that we deployed on 90bcde677d11.pr.edgegap.net
, and the publicly accessible port is 30588
.
- Netcode sample HUD can't resolve host IP from hostname, so we will manualy get the server IP.
nslookup 90bcde677d11.pr.edgegap.net
In our case, IP was 45.76.225.155
.
- Start a Unity client, enter IP and port, click on
Client
, and your client application is connected to your Edgegap deployment!
Of course, a player won't do all these steps, so let's automate this!
Add sample HUD in your client application
-
Import the script
EdgegapSampleHUD.cs
from our Github. -
Add this script to the
NetworkManager
GameObject. This will provide an in-app GUI to manage your deployments. -
Fill application informations in the editor
This HUD is great for this sample, but you should not send your credentials in a player build. You should have your own API to be sure a player doesn't abuse the system and launch too many deployments or kill other players' deployments.
Feel free to look inside EdgegapSampleHUD.cs
.
The most important thing is to get your deployment server IP and port from Edgegap API, and then put the value automatically in your transport, before starting the client.
private void Connect(string ip, ushort port, string fqdn)
{
transport.ConnectionData.Address = ip;
transport.ConnectionData.Port = port;
pendingStatus = null;
Debug.Log($"Connecting to {fqdn} [{ip}:{port}]");
networkManager.StartClient();
}
You can get the complete project sample on our GitHub.