Intro
So in my last post, I mentioned that I used PlayFab to create dedicated servers for my multiplayer game in Unreal Engine 5. Since there is very little documentation and help available online, this process was not easy and I would like to write out this blog in order to help anyone who wishes to do the same. I followed a Youtube Series : https://www.youtube.com/playlist?list=PL0jFyH3meZDMf5X6Hndo9UpwwzpCpkX2D by Betide Studios, a wonderful guy from India, and you can do the same and follow my steps side by side, because there were some unaddressed issues in the videos which can be a problem. You can also join his Discord : https://discord.com/invite/Zq2m2vfepc, where you can ask questions regarding the series and the people there are very helpful.
Note : I am going to assume that you are familiar with Unreal Engine, since this is quite an advanced topic, so some of the small things, I would assume you would be able to do on your own(stuff like UMG, Game Modes, Player controllers, creating functions and variables in blueprints, basic stuff). I will also be posting links of the Blueprints on blueprintUE, so you can refer or simply copy paste them.
Lets Begin!
Step 1 : Download and install a source build of UE5
There are plenty of tutorials for this and its the easiest part of this process. Do keep in mind that it takes a while to install and you are going to need a lot of space(mine came around 359GB). You can refer to these videos : Source Build Tutorial, Source Build Tutorial 2.
Step 2 : Create a C++ Project
You are going to need a C++ project for this, so go ahead and make a new Unreal Project and select C++. However, we will be doing almost everything using Blueprints so don't worry if you're uncomfortable using C++.
Step 3 : Installing Playfab SDKs
Download the GSDK plugin from PlayFab's Github page : Unreal GSDK Plugin.
Create a "Plugins" folder in your Project folder.
Copy the downloaded GSDK folder to your Plugins folder.
Install the Playfab SDK from the Unreal Marketplace to any of the Epic Launcher versions of the engine(you won't be able to install it to your source build version).
Go to the directory where this SDK is installed(in the Plugins folder of your Launcher Version of Unreal Engine)
Again, copy it to the Plugins folder of your Project.
Right click on your uproject file and click "Generate Visual Studio Files". This should take a few mins and then you can open your project.
Open the Plugins tab from Edit->Plugins and check that you have both the PlayFabGSDK and the PlayFab Marketplace Plugin installed and enabled.
Step 4 : Make a login system using Playfab
The first real step of the tutorial. This step is fairly simple and you can follow Betide's video : Login Tutorial, if you want to.
First, head to the PlayFab website and create an account. You will need to link your card details in order to use their services, but do not worry, the free version will be good enough for testing and development.
Create a New Studio, name it anything you like and hit Save Studio.
You'll see an ID for your game in the dashboard. Note this down as we'll be needing it later.
Then click on your game, click on the Gear icon->Title Settings->Secret Keys, and you'll find a secret key for your game. Save that as well.
Back to Unreal, create 2 levels, Login and MainMenu.
Create 2 Widget Blueprints, one for Login and one for Register.
Its upto you how you want your widgets to look like. I will be doing a very basic UI for this tutorial. If you need help creating these widgets, feel free to refer to the link I mentioned in the beginning of this step.
For the Login Level, create a new GameMode BP, name it something like GM_Login and also create a Pawn Class BP. Set the World Settings GameMode and set the Default Pawn Class to the newly created ones.
If you don't want to use a Pawn class, you can also do the following logic in the level blueprint.
In begin play of either the Pawn class, or the level blueprint, do the following:
We simply add the Widget Blueprint to our screen and set the Input mode to UI only.
Create a new Game Instance blueprint, and set it to be the Game Instance Class of the project in Edit->Project Settings->Maps & Modes, and scroll all the way down to Game Instance. I named mine BP_GameInstance.
Next, create a Blueprint Interface by right clicking in the Content Browser, Blueprint->Blueprint Interface. We will be using this interface to call our functions later.
I named mine BI_GameInstance. Open this, and create 2 new functions called Login and Register account, with 2 string inputs : Username/Email for Login, and 3 string inputs :Username, EmailAddress and Password for Register Account. You can ignore the other functions for now.
Go to your BP_GameInstance, open class settings, in the details panel, under Interfaces->Implemented Interfaces, Add a new interface and select the BI_GameInstance.
Compile and Save. Refer to the image below for the next steps.
You can either right click the Login function on the left side and click Implement event, or simply right click anywhere on the event graph and search the Login Function.
Drag out from the node of EventLogin and search "Login with PlayFab". From the request node(blue) of this function, you can drag out and click "Make Client Login With PlayFab Request". This is something that we'll be doing pretty often, dragging out from requests.
Drag out from the on Success node and click create a Custom Event, called OnLoginWithPlayFabSuccess.
From the result pin of this function, drag out and click Break ClientLoginResult. Promote the Entity Token and PlayFabID to variables by right clicking them and selecting Promote To Variable. Connect the execution pins. (Refer to image below)
For On Failure, you can use anything you want to debug. I use an event dispatcher and call a custom event which debugs the errors on screen.
We have to do similar logic for logging in with email address and finally open up the MainMenu level on Successful.
The Registration part is similar, except we use the Register PlayFab User function. The rest is identical.
Next, right click anywhere, and look for Event Init. From its execution pin, find a function called "Set PlayFab Settings".
You can promote the Game Title ID and PlayFab Secret API Key to variables and paste the default values saved from earlier.
And you're done with the Login Part. After registering and logging in, you can check for players on the PlayFab Dashboard by going to Players and then clicking on Search.
Step 5 : Matchmaking
In the BI_GameInstance, create a new function called "Start Matchmaking".
Create a custom event called "Reset Matchmaking".
Connect them to a the pins of a Do Once Node.
As mentioned by Betide Studios, this prevents crashes, hence we'll be blindly following him.
One extra function I added here was "Cancel All Matchmaking Tickets for Player", since sometimes, I was getting an error that ticket already exists for this player. Its upto you if you want to add this.
Right click and Construct a JSON object. This next part is quite long, and I'll have the final result split into two screenshots below. I'll also paste the BP code so you can directly copy paste it.
Its all inside this custom function : Set Attributes.
You'll need to create the following variables of type PlayFabJsonObject(ref type): latenices, attributes, latencyItem, CreatorSetting and then set their values from the return value of the Construct JSON Object node.
And then set the following fields: region, latency, Latencies, DataObject, Attributes, Entity.
In the region string value, set whichever server you want to use. Rest you can keep the same.
Go to the PlayFab dashboar, Multiplayer->Matchmaking.
Create a new queue, name it anything you like. I named mine "TestQueue".
Set match size to whatever you want(min should be 2) and max ticket size to 1(this is optional).
Add 2 additional rules as shown below.
Save and head back to Unreal.
From the Execution pin of Set Attributes, call CreateMatchmakingTicket. Again as usual, OnFailure make an event dispatcher and set the error function. OnSuccess make a custom event called "Matchmaking Success". And off the request pin, select MakeMultiplayerCreateMatchmakingTicketRequest.
Connect your creator setting, and make a new string "QueueName" and set its default value to the one you set in the Playfab Queue.(again mine was "TestQueue")
Set GiveUpAfterSeconds to a value like 60 or 120.
Break the result of MatchmakingSuccess and promote Ticket ID to a variable and set it.
After that, make a timer event, by calling "SetTimerByEvent". From its event pin drag and create a custom event "FindMatchTimer".
Set it to looping and interval time to something like 5 or 10 seconds.
Promote the return value to a variable and give it a name like "MatchFinderTimerHandle".
The function being called from the "FindMatchTimer" is a custom event we will create next.
Now we make that custom function "GetMatchmakingTicket". From that we call the GetMatchmakingTicket function from PlayFab. Again, OnFailure an event dispatcher, OnSuccess a custom event "GetMatchmakingSuccess".
Remember to attach this custom event "GetMatchmakingTicket" to the execution node of the custom event "FindMatchTimer".
Set the queue name and ticket ID to the variables created earlier.
Break the result of the custom event. Promote Match ID and status to variables. You can use print statements for debugging.
Set the execution of the custom event "GetMatchmakingSuccess" to a branch. To its true node, clear and invalidate the timer created earlier.
After invalidating, we call create and call a new custom function "GetMatchmaking".
This is the last step of matchmaking. Call "GetMatch" from the custom event. Again do the same, OnFailure, OnSuccess, Make a request node, assign the match ID and queue name.
From the success event, break its result, and check if its valid. If not, set it to a delay node of 2 seconds and on completed attach it to "GetMatch" so it keeps looping.
On valid, from ServerDetails, call GetObjectArrayField and set the field value to "Ports". From its return array, get the first element. Again from ServerDetails, Get the fields "IPv4Address" and "Num" as strings. Set the target to the first array element.
Append them in the following manner "open " + the IP address + ": " + the Num value.
Keep in mind the spaces. Then after a delay of 2-3 secs, call Execute Console Command and attach the appended string to the command ping.
Matchmaking done!
Step 6 : Creating the Servers
Now we do the servers part.
Go back to the init part, and on the execution pin of SetPlayFabSettings, call the following PlayFab functions. From some of them, we create custom events.
From the Shutdown delegate, we call QuitGame.
From Health Check Delegate, we create an event dispatcher and make a matching function.
Make sure the functions match as shown above.
In your {ProjectName}.Build.cs file, we add some code. There will be an array of strings(modules), and we add "PlayFabGSDK", "PlayFab", "PlayFabCpp" and "PlayFabCommon".
Open your project folder->Source. There you will find a {YourProjectName}Editor.target.cs file.
Duplicate this file twice and name them {ProjectName}Client.target.cs and {ProjectName}Server.target.cs.
In the end it should look a something like this:
Open the server and client files and just change to "Server" and "Client" where it says "Editor". Something like this :
Same goes for the Server file.
Close Visual Studio and click on your project and generate visual studio files again.
Open the project and select the following configuration :
You can use development or shipping config; shipping is smaller in size but you won't be able to view any logs.
Next, open Project Settings->Maps & Modes->Default Maps->Server Default Map and select the map where your multiplayer game will take place. This will be the server map and when players connect to the server they will travel to this map.
For the client side, Game Default Map should be set to the Login level.
Now we add the code for shutting down the server when there are no players.
First, in your GameInstance interace, BI_GameInstance, create a new function called "GetSessionID" and add an output "SessionID" of type string.
In BP_GameInstance, implement the function and pass Match ID as the output.
In the player controller blueprint of the server level, do the following : (if you do not have a custom player controller in this level, just create a new one derived from the default PlayerController. Don't forget to use it in the World Settings)
In begin play, if its the local player controller, we get the game instance and call the function we just created "GetSessionID".
Then we create a custom event "SendSessionIdToServer",
Important : the custom event created here needs to have the following attributes. Click on it and select "Run on Server" and "Reliable". Also, create an input string called "SessionID".
Then we get the game mode, cast it, get the session ID, if it is empty we just set it.(refer to the blueprint graph above).
Create a new C++ class called Shutdown. In its header file add the following: UFUNCTION(BlueprintCallable, Category = "Custom", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject", Keywords = "Shut Down")) static void ShutDown();
In the CPP file, define the function :
#include "ShutDownDS.h" void UShutDownDS::ShutDown() { GIsRequestingExit = true; }
Save and compile.
Open the server game mode blueprint, and do the following shutdown logic.
OnPostLogin, we create and add to an array which keeps count of the number of players. OnLogout, we remove the player from the array.
Then every 10 seconds, we run a custom event, which checks if the player count is less than 1.
Then we call the ShutdownMultiplayerServer from PlayFab, and then our custom C++ function Shutdown.
That's it for the server!
Step 7 : Uploading the Server build
With the build target set to Server, package your project and zip the contents into a single zip file.
Head to the PlayFab dashboard. Click on Multiplayer->Servers->New build.
Give it a name, select how many cores you want and servers per machine.
Enable VM metrics preview, and Crash dump collection(if you want logs)
Upload the zip file, and in the start command, add the following: C:\Assets\{YourProjectName}Binaries\Win64\{YourProjectName}Server.exe
Make sure this path is correct, otherwise the build will be unhealthy.
It should match with the path of the exe in your Binaries\Win64 folder. If its a shipping build, it will end with Shipping.exe.
Add a region, standby servers should be at least 1.
Add the following ports :
Add the build. It should say deploying and after around 10 mins, it should be deployed. (sometimes, it may take longer)
Go to Matchmaking. Select the Queue you created earlier and turn on Enable Server allocation->Build ID-> and select the build you just uploaded.
Server done!!
Step 8 : Connecting Clients
Finally, we now connect the clients and test our dedicated server.
Open your MainMenu widget BP in your MainMenu Level. If you don't have one, just create a new one, with a simple UI where it will have a button to search for servers. (this should be the level that opens up after login or register as we did in the beginning)
I have a very simple UI like this :
Click the Find a Match Button and implement its on clicked event.
We get the game instance, and call the custom event we created "Start Matchmaking".
We disable the Find Match button and then call "Get Matchmaking Status", set the text returned to the Find Match Button and repeat it every 1 second. This keeps updating the status and we can see it.
With the build target set as Client, package the project.
Open 2 instances of the client build and register/login.
Start matchmaking and after a while, they should connect to the server and the map should be loaded.
It will say "WaitingForMatch" and then "Matched".
And then voila! (pardon the low res image here)
You can test with more than 2 players as well and it should work fine.
Thank you for following this tutorial and I hope it worked well for you. If there are any issues, please make sure everything is implemented properly and if there are still any problems, feel free to contact me on my discord or email.
Making a dedicated server is a painstaking and lengthy task. I hope Unreal slowly gets the amount of content and help available online as much as Unity(although that would take a while), and I wish you all the best on your project!
Comments