How to create online games / Unreal Engine 4 tutorial series

From IML Wiki
Jump to: navigation, search

#1 - Create listen server and main menu

  • Difference between listen and dedicated servers
Listen Server
Dedicated Server

  • Create third person template project v 4.24 by next technical task
Game block diagram

  • To create game (host) click on the "Host Game" button, it call "Open Level" function with the Gameplay map name and "listen" option.
  • To connect to the host write it IP-adress and click the "Join Game" button. It call "Open Level" function with IP-adress as map name.
Main Menu

Standard Issues

#2 - Create remote (dedicated) server

Tutorial Plan

  • Learn to works with application arguments. Create simple C# app and print on screen arguments. Form shortcut and .bat files.
static void Main(string[] args)
     foreach (var item in args)
  • Modify game project. Remove "Host Game" button from main menu. Define Server Default Map in the project settings.
  • Create two .bat files to start game and dedicated server.


"C:\UE4_4.24.3_S\Engine\Binaries\Win64\UE4Editor.exe" "I:\OnlineGame3\OnlineGame.uproject" -game


"C:\UE4_4.24.3_S\Engine\Binaries\Win64\UE4Editor.exe" "I:\OnlineGame3\OnlineGame.uproject" -server -log -port=7778
UE4 editor popular arguments

  • Build independent dedicated server
Compiling dedicated server as independent application

1. Change engine version from launcher to version from source
2. If needs, create some c++ classes in project
3. Create «[projectName]Server.Target.cs» file in the "Source" project folder

using UnrealBuildTool;
using System.Collections.Generic;

public class [projectName]ServerTarget : TargetRules
	public [projectName]ServerTarget(TargetInfo Target) : base(Target)
		Type = TargetType.Server;
		DefaultBuildSettings = BuildSettingsVersion.V2;

		ExtraModuleNames.AddRange( new string[] { "[projectName]" } );

4. Make «Generate Visual Studio project files».
5. Compile game
6. Compile dedicated server
7. Copy compiled dedicated server files to compiled game folder

projectNameServer.exe, projectNameServer.exp, projectNameServer.lib





#3 - Recreate project on new 4.26 version with c++ based architecture and new clean folder structure

  • Discuss about required Visual Studio and workloads version importance
  • Recreate old or create new 4.26 version project
  • Creating blueprint class inherited from custom c++ class
  • Change blueprint parent class
  • Recreate previous project with cpp based architecture and new clean folder structure
bp and c++ architecture differences

Clean folder structure

#4 - Git

#5 - Overview the course plan, what we want to create. Required skills and technologies stack.

  • Сourse plan as game features
  1. Web-Site with registration, authorization and download game client link.
  2. Join game via registered login and password.
  3. Opportunity to create your own custom character (race, gender, ect.).
  4. Getting your created character list.
  5. Impossibility to create two characters with the same NickName.
  6. Deleting characters.
  7. Join game server with selected character.
  8. Impossibility to join server twice with same account in one time.
  9. Display character nickname over character head.
  10. Saving characters data like location, inventory, ect.
  11. In-game chat for all players.
  • Join server schema
Join server schema

  • Game-server methods
Game-server methods

  • All LoginServer Methods

Client safe methods:
Log In ( Send: login, pass, GameClientVersion. Receive: Status, UserId, UserToken. )
Log Out ( Send: UserId, UserToken. Recieve: Status. )
Read Slots ( Send: UserId, UserToken. Recieve: Status, List of characters. )
Create Character ( Send: UserId, UserToken, NickName, Race, Gender. Recieve: Status. )
Delete Chatacter ( Send: UserId, UserToken, CharacterId. Recieve: Status. )
Start Game ( Send: UserId, UserToken. Recieve: Status, ServerIp. )

Server only methods:
Read Character ( Send: ServerLogin, ServerPassword, CharacterId. Recieve: Status, Character. )
GlobalServerSync ( Send: ServerLogin, ServerPassword, SyncData. Recieve: Status, SyncData. )
Log Out And Save ( Send: ServerLogin, ServerPassword, Character. Recieve: Status. )

List<Characters> Characters;
List<String> MustBeLogOutedUserIds;

Game Server to Login Server:
Users which was no safe disconnected, without LogOut method calls.
They are currently offline, but they cannot logIn anymore, cause in database they are still isOnline = true.
Method must set isOnline = false for all this users in database.

Login Server to Game Server:
Users which try to logIn with currently online accounts.
Game server must forced kick this users from game server and put them to MustBeLogOutedUserIds queue like non safe disconnected;

  • Required skills:
  1. Unreal Engine - intermediate.
  2. C# language - intermediate.
  3. Microsoft Asp.Net - basics.
  • Technologies stack:
  1. Unreal engine
  2. VaRest Unreal Engine plugin
  3. Asp.Net Core mvc + web api
  4. Asp.Net Core Entity Framework
  5. Asp.Net Core Identity
  6. Microsoft SQL Server

#6 - Install Microsoft SQL Server and SQL Server Management Studio. Create an empty ASP.NET Core project.

  • Install MSSQL Server and SQL Server Management Studio.
Download Microsoft SQL Server 2019 Express

Select Basic installation type


Click Install



SQL Server installed, then lets install SQL Server Management Studio

Download SQL Management Studio

Click Install


Click restart

Open SQL Server Management Studio

Click Connect


MSSQL Server and SQL Server Management Studio are successfully installed

  • Create an empty ASP.NET Core project.
Create new Visual Studio 2019 project

Select ASP.NET Core Web App

Type project name and Create

Select an Empty Project

Try to run our project


An empty ASP.NET Core project is created.

#7 - Install all NuGet packages we need and required client side libraries.

  • Install all NuGet packages we need
NuGet - official package manager for .NET

Reqired NuGet packages:

  1. Microsoft.EntityFrameworkCore.SqlServer - Entity Framework ORM system with SqlServer adapter to connect to MSSQL Server we installed in previous tutorial;
  2. Microsoft.EntityFrameworkCore.Tools - Entity Framework Tools for automatic database creation based on code first approach;
  3. Microsoft.AspNetCore.Identity.EntityFrameworkCore - Identity library for user registration, authentication and roles;
  4. Microsoft.AspNetCore.Mvc.NewtonsoftJson - Library that gives us ability to serialize advanced JSON requests;
  5. Swashbuckle.AspNetCore - Swagger. Great tool for testing and documenting web API.
How to open NuGet package manager

How to find and install NuGet package


Package is successfully installed

Then we need to install other required NuGet packages

All required NuGet packages are installed

  • Install required client side libraries from "cdnjs"
Cdnjs - another client side libraries provider

Required client side libraries:

  1. twitter-bootstrap - for basic web site design
  2. jquery - just for case
How to add client side library

How to find and add required library

Bootstrap successfully added

Inside of libman.json

Then we need to install JQuery as the last one library

Bootstrap and JQuery are installed


  "version": "1.0",
  "defaultProvider": "cdnjs",
  "libraries": [
      "library": "twitter-bootstrap@4.6.0",
      "destination": "wwwroot/lib/bootstrap/"
      "library": "jquery@3.6.0",
      "destination": "wwwroot/lib/jquery/"


#8 - Setup appsettings.json, create LoginServer folder structures and entity models

  • Setup appsettings.json

appsettings.json file is ASP.NET Core app configuration file where we can store our variables instead of hardcode it. By default it seems like this:

  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
  "AllowedHosts": "*"

But we need to place in it

  1. Database connection string
  2. Unreal Engine dedicated gameplay server IP address
  3. Game client version

Now our appsettings.json file must seems like this:

  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=testDB;Trusted_Connection=True;"
  "GameServerIPs": {
    "DefaultServer": ""
  "GameClienVersions": {
    "Current": "1"
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
  "AllowedHosts": "*"
  • Create folder structures

Now we have next folders:

Current folders structure

But we need to create next folders:

New folders structure

  • Create entity models

We need to create two model classes PlayerUser.cs and Character.cs
Lets create PlayerUser.cs file in the Models/Entities folder first:

Create PlayerUser.cs class 1/3

Create PlayerUser.cs class 2/3

Create PlayerUser.cs class 3/3


using Microsoft.AspNetCore.Identity;

namespace TutorialLoginServerV2.Models.Entities
    public class PlayerUser : IdentityUser
        public string AuthToken { get; set; }
        public bool IsOnline { get; set; } = false;
        public bool MustBeLogOuted { get; set; } = false;
        public bool IsBanned { get; set; } = false;

Next we need to create last one Character.cs model class in the Models/Entities folder

Create Character.cs class


namespace TutorialLoginServerV2.Models.Entities
    public class Character
        public int Id { get; set; }
        public string OwnerId { get; set; }
        public string Nickname { get; set; }
        public int Race { get; set; } // 0 - Human, 1 - Elf, 2 - Orc
        public int Gender { get; set; } // 0 - Male, 1 - Female
        public int Experiance { get; set; } = 0;
        public float LocationX { get; set; } = 0.0f;
        public float LocationY { get; set; } = 0.0f;
        public float LocationZ { get; set; } = 0.0f;
        public float RotationX { get; set; } = 1.0f;
        public float RotationY { get; set; } = 1.0f;
        public float RotationZ { get; set; } = 1.0f;


#9 - Create empty EngineManager class and MainDbContext class

  • Create empty EngineManager class

In the last episode we created the "Services" folder for the EngineManager module.
The EngineManager is not necessarily needed, but we will use it for incapsulating parts of the controllers classes and and creating most clean code.
The EngineManager will be included in the dependency injection technique as singleton.
Today we just create the empty EngineManager class inside of the "Services" folder.

Creating the EngineManager 1/3

Creating the EngineManager 2/3

Creating the EngineManager 3/3

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace TutorialLoginServerV2.Services
    public class EngineManager
  • Create MainDbContext class

The Database Context Class (DbContext for short) is part of imported before Entity Framework Core ORM system.
This class is representing our database as simple c# object.
Inside of it we will store our entity classes like PlayerUser.cs and Character.cs as array variables. Not standard array but specific DbSet collection.
In future we will do the specific "migration" operation and in our Microsoft SQL Server Database will be automaticly created tables based on our entity DbSet variables.
DbSet<PlayerUser> variable will create PlayerUser table and DbSet<Character> variable will create Characters table.
When we ended our setup we will have the opportunity to Create, Read, Update and Delete database operations used Database Context Class methods instead of regular SQL queries.

Create MainDbContext.cs 1/3

Create MainDbContext.cs 2/3

Create MainDbContext.cs 3/3

using TutorialLoginServerV2.Models.Entities;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

namespace TutorialLoginServerV2.Models
    public class MainDbContext : IdentityDbContext
        public MainDbContext(DbContextOptions<MainDbContext> options) : base(options)


        public DbSet<PlayerUser> PlayerUsers { get; set; }
        public DbSet<Character> Characters { get; set; }

Pay attention that our created MainDbContext derives from IdentityDbContext class, not from DbContext class.
It's because we using not the Entity Framework only, but the Entity Framework with the Identity system.
Simplified it looks like our "MainDbContext" derives from "IdentityDbContext" and then "IdentityDbContext" derives from "DbContext".
Just like the EngineManager, the MainDbContext class will be included into the dependency injection system.


#10 - Create Startup class and init database

  • Create Startup class

Startup.cs class is a most important class in any of a core application. This class runs every time when our application starts and configure it.
In this class we load settings files, add services and configure it, register dependency injections for any of another classes, define MVC patterns and much more.

Now your Startup.cs seems like this:

Startup.cs class now

But we need to create here next points:

  1. Create the constructor where we load created in 8th episode appsettings.json file to the Configuration variable from the integrated, out of box, dependency injection system.
  2. Register, created in last episode, EngineManager as service in the dependency injection system.
  3. Register, created in last episode, MainDbContext as service in the dependency injection system. Actually register Entity Framework ORM system.
  4. Register, added in 7th episode as NuGet package, Identity system as service in the dependency injection system.
  5. Customize Identity system servece.
  6. Register Model View Controller application pattern (MVC for short) as service. Actually make this app as MVC.
  7. Register, added in 7th episode as NuGet package, Swagger as service.
  8. Activate Swagger.
  9. Activate Swagger UI (html page).
  10. Activate Developer Exception Page.
  11. Allow to use static files from the "wwwroot" folder.
  12. Activate Authentication.
  13. Activate Authorization.
  14. Define default MVC controllers routing. For example when we open in our app will runs Register method in Account controller.
  15. Create specific method for creating admin role and admin user in database.

Short Startup.cs class:

Startup.cs overview

Full Startup.cs class:

using TutorialLoginServerV2.Models;
using TutorialLoginServerV2.Models.Entities;
using TutorialLoginServerV2.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using System;
using System.Threading.Tasks;

namespace TutorialLoginServerV2
    public class Startup
        public IConfiguration Configuration { get; }
        public Startup(IConfiguration configuration)
            Configuration = configuration;

        public void ConfigureServices(IServiceCollection services)
            services.AddDbContextPool<MainDbContext>(options => options.UseSqlServer(Configuration.GetSection("ConnectionStrings")["DefaultConnection"]));
            services.AddIdentity<PlayerUser, IdentityRole>().AddEntityFrameworkStores<MainDbContext>();

            services.Configure<IdentityOptions>(options =>
                options.Password.RequiredLength = 6;
                options.Password.RequiredUniqueChars = 0;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireLowercase = false;
                options.Password.RequireUppercase = false;
                options.Password.RequireDigit = false;
                options.User.RequireUniqueEmail = true;
                options.SignIn.RequireConfirmedEmail = false;


            services.AddSwaggerGen(c =>
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "Login Server", Version = "v2" });

            services.AddControllers().AddNewtonsoftJson(options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider services)
            app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "LoginServer v2"));



            app.UseEndpoints(endpoints =>
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}");


        private async Task CreateDefaultAdminRoleAndAdminUser(IServiceProvider serviceProvider)
            RoleManager<IdentityRole> RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
            UserManager<PlayerUser> UserManager = serviceProvider.GetRequiredService<UserManager<PlayerUser>>();

            string loginForNewAdmin = "Administrator";
            string emailForNewAdmin = "";
            string passForNewAdmin = "grefdsrthdfgrbd45";

            bool roleCheck = await RoleManager.RoleExistsAsync("Admin");
            if (!roleCheck)
                await RoleManager.CreateAsync(new IdentityRole("Admin"));

            PlayerUser userCheck = await UserManager.FindByEmailAsync(emailForNewAdmin);
            if (userCheck == null)
                PlayerUser newAdminUser = new PlayerUser { UserName = loginForNewAdmin, Email = emailForNewAdmin };
                await UserManager.CreateAsync(newAdminUser, passForNewAdmin);
                await UserManager.AddToRoleAsync(newAdminUser, "Admin");

Pattern: "{controller=Home}/{action=Index}"); will works next way, to open default website page, our app will try to find the "Home" controller and run the "Index" method inside of it.
In our case we created no "Home" controller and no "Index" method yet, so if we try to run application now, we will see "Error 404 page not found". It is Okay, we will create this classes in the next episodes.

  • Database initialization

For now we created our application entity model classes like PlayerUser.cs and Character.cs.
We created Database Context Class MainDbContext.cs where we defined our database tables based on our entities.
We store the database connection string in the appsettings.json file and then loaded it in the Startup.cs file to the Configuration variable.
We also registered our MainDbContext.cs Database Context Class as our application service in the ConfigureServices method in the Startup class.

services.AddDbContextPool<MainDbContext>(options => options.UseSqlServer(Configuration.GetSection("ConnectionStrings")["DefaultConnection"]));

But inside of our Database Server our database have not created yet.
And, if we try to run some database methods we will se an error.
Lets try uncomment next row:


And run our application..

Error! Cannot open database "TutorialTestDB"!

As you can remember, in the previous episodes I told you, to create Database automatically, from our c-sharp model classes, we need to do some magical "migration" operation.
To do it, we need to open "Package Manager Console":

How to open Package Manager Console

Then we need to make our first "Migration":

How to make the "Migration" 1/4

How to make the "Migration" 2/4

How to make the "Migration" 3/4

How to make the "Migration" 4/4

We can open SQL Server Management Studio and check new database:

Created Database

Let's see our PlayerUsers table:

No users in the "PlayerUsers" table

Now we can uncomment next line one more time:


And run application one more time:
Application works!! Yes, we still can see "404 Error", but it must be. We have no exceptions, app works with database!
Lets check our PlayerUsers table one more time:

We successfully added our firs user!


#11 - ...