This article was written using Unreal version 4.24.

Build Unreal and cross-compiling for Linux

To build a dedicated server in Unreal, you will need to build the Unreal Engine from source. Documentation about this can be found here:

Here is a brief rundown of the steps:

  1. Install Visual Studio Community 2017 (Include C++ support). 
  2. Clone the repository.
  3. Run Setup.bat
  4. Run GenerateProjectFiles.bat
  5. If above gave an error about .NET Developer pack, Install from here: https://aka.ms/msbuild/developerpacks
    Download the version that matches the error.
  6. Install cross-compiling for Linux, found here: https://docs.unrealengine.com/en-US/Platforms/Linux/GettingStarted/index.html
    This step will allow us to build a Linux dedicated later on.
  7. Open UE4.sln
  8. Choose configuration Development Editor / Win64
  9. Build UE4 target

Make a game

We are going to use the default Third Person example game. It already has built-in multiplayer working. Run UE4 from Visual Studio that you built in the previous section. Select Game -> Third Person -> C++.

Run default dedicate

You should be able to run the game with a dedicated with this setting:

The dedicated will spawn a character also. It's not what we would want in a final product, but we will use this hint as proof that our dedicated works and we didn't start a standalone. Clicking the Play button and you should see this:

While this works and runs a dedicated server, this is not really what we want. We want to connect to the server through IP.

Main Menu

We will create a main menu that will offer us a Host and Join options. In your Content Browser, browse to Content/ThirdPersonCPP/Maps/ folder. Right-click -> Create basic asset -> Level. Name the new level MainMenu. Next, create a User Interface for your MainMenu. Right Click -> User Interface -> Widget Blueprint. Call it wbMainMenu. You should now have those assets:

Double click wbMainMenu and create the following layout:

Now, if we run the MainMenu level, we will only see a black screen. We need to load the menu widgets. Open the MainMenu level by double-clicking it, then in the top menu Blueprints -> Open Level Blueprints. Create this blueprint to load the menu, show the mouse and change input mode to UI mode.

Now if we launch the MainMenu level, we should see the menu. Before we hook the Host and Join button, we need to change a launch setting. Currently, the client automatically connects to the dedicated server that Unreal is launching. We want to do those steps manually from now on. Go in Play -> Advanced Settings and uncheck Auto Connect To Server.

Implement the Host button like this:

This will open the level with Options "listen". This creates a dedicated.
Implement the Join button like this:

Txt IPAddress is the name of your IP text field. Default to localhost 127.0.0.1. We build and execute the command line "open 127.0.0.1". This will join the listening dedicated. Note in the Append blueprint, there is a space after the word open. "open ".

Game

In ThirdPersonExampleMap level, add this blueprint to switch back mouse control to game mode.

Uncheck dedicated run setting, set for 2 instances and in new windows. Now run 2 instances from the MainMenu level.

Click Host on one instance, and Connect on the second instance. You should now have a server and a client connected.

The Server is playable in this case because it is not dedicated.

Create a dedicated server

Add Server target type

In the following steps, replace "GameName" with yours.

In Visual Studio, create a new file GameNameServer.Target.cs located under your game's source folder.

using UnrealBuildTool;
using System.Collections.Generic;

[SupportedPlatforms(UnrealPlatformClass.Server)]
public class GameNameServerTarget : TargetRules
{
    public GameNameServerTarget(TargetInfo Target) : base(Target)
    {
        Type = TargetType.Server;
        DefaultBuildSettings = BuildSettingsVersion.V2;
        ExtraModuleNames.Add("RoomServerTest");
    }
}

From the editor, File -> Refresh Visual Studio Project.
Then close the editor.

brainCloud S2S

Install the brainCloud S2S (Server to Server) plugin, found here: https://github.com/getbraincloud/brainclouds2s-unreal

Follow the installation steps. This is required because you might need to do some initialization, level generation, etc. before accepting new connections. Our dedicated server needs to talk to brainCloud to communicate when it is ready.

The following code we are going to put it into the GameMode class. If you are not following using the ThirdPersonCPP example, just put this code where you are finished initializing your game and ready to talk to brainCloud.

AGameNameGameMode.h:

#include "brainclouds2s.h"

...

protected:
    virtual void Tick(float DeltaSeconds);

private:
    TSharedPtr<UBrainCloudS2S> pS2S;

AGameNameGameMode.cpp

if (GetNetMode() == NM_DedicatedServer) // Make sure we are dedicated
{
    // Load environment variables passed in by brainCloud to our container.
    auto appId =
        FGenericPlatformMisc::GetEnvironmentVariable(TEXT("APP_ID"));
    auto serverName =
        FGenericPlatformMisc::GetEnvironmentVariable(TEXT("SERVER_NAME"));
    auto serverSecret =
        FGenericPlatformMisc::GetEnvironmentVariable(TEXT("SERVER_SECRET"));
    auto lobbyId =
        FGenericPlatformMisc::GetEnvironmentVariable(TEXT("LOBBY_ID"));

    // Create S2S context
    pS2S = MakeShareable(new UBrainCloudS2S(appId,
                                            serverName,
                                            serverSecret));

    // Verbose log
    pS2S->setLogEnabled(true);

    // Start tick function so we can update the S2S lib
    PrimaryActorTick.bCanEverTick = true;

    // Request Lobby data.
    // Check out S2S Explorer in the portal to know more about calls that
    // can be done.
    pS2S->request("{\"service\":\"lobby\",\"operation\":\"GET_LOBBY_DATA\",\"data\":{\"lobbyId\":\"" + lobbyId + "\"}}",
        [this, lobbyId](const FString &result)
    {
        // ... Do server setup, decode result json,
        // cache user/passcode for validation, etc.

        // Notify brainCloud that our server is ready
        pS2S->request("{\"service\":\"lobby\",\"operation\":\"SYS_ROOM_READY\",\"data\":{\"lobbyId\":\"" + lobbyId + "\"}}", nullptr);
    });
}

...


// Update the S2S library inside our Tick callback.
void ARoomServerTestGameMode::Tick(float DeltaSeconds)
{
    pS2S->runCallbacks();
}

Compiling Linux Dedicated

Relaunch the editor from Visual Studio.

Next, we have to make sure we load directly into the game on dedicated, and on MainMenu on the client.
Go into Packaging settings. File -> Package Project -> Packaging Settings.
Set those two settings:

  • Maps & Modes -> Default Maps -> Game Default Map -> MainMenu
  • Maps & Modes -> Default Maps -> Server Default Map -> ThirdPersonExampleMap

Select Server build target.
File -> Package Project -> Build Target -> GameNameServer

Build the server.
File -> Package Project -> Linux -> Linux

If all worked, you should now have a dedicated server with a shell file to start it. You can try to run this on a Linux desktop or WSL (Windows Subsystem for Linux). Try to connect to it from the client. If this doesn't work, stop here and review the steps. Ignore the brainCloud S2S errors in the log. This will only work once you are running it on our servers.

Docker

We now create a docker image with our packaged dedicated server.

  1. Create an account and a repository for your game on https://hub.docker.com/
  2. Install docker daemon. https://www.docker.com/
  3. Go to your package dedicated folder, and create the following DockerFile:
FROM debian:latest

# Create a server directory
RUN mkdir -p /home/appuser/server
WORKDIR /home/appuser/server

# Copy files to the server directory
COPY . /home/appuser/server

# Unreal cannot run as root.
# Create a user without privileges and use him instead
RUN groupadd -g 999 appuser && \
    useradd -r -u 999 -g appuser appuser
USER appuser

# Ports (This does nothing, documentation purpose only)
EXPOSE 7777/tcp
EXPOSE 7777/udp

# Execute
ENTRYPOINT ["./RoomServerTestServer.sh"]
CMD []

We are exposing the port 7777. This is the default multiplayer port for Unreal.

4. Build the docker image using this command: 

docker build -t repository:tag .

Your repository and tag are usually in the form of team/gamename:1.0.

5. Run the container locally using this command:

docker run -p 7777:7777 -p 7777:7777/udp --name gamename team/gamename:1.0

Here we are binding the internal container port 7777 to the external port, 7777. We are also giving the container a name, gamename, so we can refer to it later. Try to connect to it using the client. If it doesn't work, review steps.

6. Stop the local container:

docker stop gamename

7. When all good, push the image to docker hub.

docker push team/gamename:1.0

It might ask you to log in to your docker hub user.

Lobby Setup in Portal

For those following steps, I have used a game named "ue4example". You will see that from now on instead of "gamename".

In brainCloud portal, create a new custom server. Fill in those settings:

Tweak the configuration depending on your game size and complexity. We've put 2 minutes session time here so the server doesn't stick around. In our simple example, we didn't code any behavior to stop the server.

Go in the Regions tab and configure at least one region.

Enable lobbies under Multiplayer -> Lobbies

Create a lobby with those settings:

Those last settings will start the game as soon as 1 player joins the lobby. This is so we can test quickly. You will want to tweak those to allow more players.

Client brainCloud

The server part is completed.

Download and install the brainCloud client plugin:
https://github.com/getbraincloud/braincloud-unreal/releases

proper instructions here:
https://getbraincloud.com/apidocs/tutorials/unreal-tutorials/setting-up-the-braincloud-plugin/

Install VaRest plugin. We are going to need this to parse Json data.

Initialize brainCloud in your MainMenu blueprint:

Upon success, enable RTT. brainCloud RTT enables us to get real-time events. We will need this to get Lobby events while matchmaking.

Make sure also that brainCloud is updated each Tick:

In our MainMenu widgets, add a new button called brainCloud Join.

On Clicked, launch MatchMaking by calling "Find or Create Lobby":

The actual matchmaking results are going to be received through RTT events. The Success and failure Callbacks here are only for this specific call and if you got the arguments right.

Going back to our MainMenu blueprint, upon receiving Lobby callbacks, decode the json. We only care about the event "ROOM_ASSIGNED" which will also include the connection data (IP, Port).

After extracting the UDP port and the address, we build the following command and execute it:

open IP:PORT

In the Append blueprint, please note that "open " string has a space at the end.

If everything goes well, you should now be able to press brainCloud Join, and join the dedicated. The first time the server is launched if none are warm, it could take up to 45.

Did this answer your question?