Distributed Programming  
Lecture 05 - REST Web Services and HTTP  
Communication in C++ on Unreal Engine  
Edirlei Soares de Lima  
<edirlei.lima@universidadeeuropeia.pt>  
What is a REST Web Service?  
A Web Service is a software system identified by a URI/URL,  
whose public interfaces and bindings are defined and  
described using XML (or JSON).  
It is designed to support interoperable machine-to-machine  
interaction over a network (HTTP or HTTPS).  
REST (Representational State Transfer) is an architectural style  
for creating web services.  
The underlying protocol for REST is HTTP, which is the basic web  
protocol.  
REST web services are lightweight, maintainable, and scalable.  
REST Web Services  
REST Web Services allow the requesting systems to access and  
manipulate textual representations of web resources by using  
a uniform and predefined set of stateless operations.  
A resource is identified by a URI/URL;  
A request returns a document with a representation of the requested  
resource;  
Each request contains all the information needed to be processed  
(stateless operations).  
All resources are accessed by a well-known set of HTTP operations:  
POST, GET, PUT, DELETE.  
Information transmitted in the requests and answers is encoded as  
XML or JSON.  
XML vs. JSON  
XML:  
<players>  
<player>  
<
<
name>John</name>  
score>100</score>  
<
<
/player>  
player>  
<
<
name>Anna</name>  
score>200</score>  
</player>  
</players>  
JSON:  
{"players":[  
{
{
"name":"John", "score":"100"},  
"name":"Anna", "score":"200"}  
]}  
REST Web Services Key Elements  
Resources:  
Considering a REST web service that has records of several players’  
scores. We can access the score of a player (resource) via a URL:  
http://example.com/player/1, where 1 would be the ID of a player.  
Request Verbs:  
Describe what the client want to do with the resource. For example, a  
GET verb is used to get data. Other verbs: POST, PUT, and DELETE.  
Request Headers:  
Additional instructions sent with a request. These might define the type  
of response required or the authorization details.  
REST Web Services Key Elements  
Request Body:  
Is the data that is sent with the request to the REST web service.  
Response Body:  
Is the data that comes from the REST web service as response of  
request.  
Response Status:  
Are general codes that are returned along with the response from the  
web server. For example, 404 indicates that the resource was not found.  
REST Architectural Characteristics  
Distributed resources:  
Every resource should be accessible via the normal HTTP commands of  
GET, POST, PUT, or DELETE.  
Client/Server:  
The client send requests to the web service (server). The server either  
reject the requests or comply and provide an adequate response to  
the client.  
Stateless:  
The server should not maintain any sort of information between  
requests from clients.  
REST Architectural Characteristics  
Layered:  
Any additional layer can be inserted between the client and the actual  
server hosting the REST web service.  
Supports Caching:  
Since each server client request is independent in nature, sometimes  
the client might ask the server for the same request again.  
The cache is implemented on the client. If the same request is  
necessary, instead of going to the server, the client go to the cache and  
get the required information.  
Game Project  
Player Data  
+
+
+
+
+
+
ID: int  
Game Server  
Name: string  
Email: string  
Password: string  
TotalCoins: int  
TotalDeaths: int  
REST  
WebService  
Clients  
REST Web Service in Node.js  
Requirements:  
Linux (local virtual machine or a cloud virtual machine)  
Cloud option: DigitalOcean (https://www.digitalocean.com/)  
Virtual machine: https://www.virtualbox.org/  
MySQL  
Node.js  
Setup:  
sudo apt update  
sudo apt install mysql-server  
sudo mysql_secure_installation  
curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh  
sudo bash nodesource_setup.sh  
sudo apt install nodejs  
npm install mysql --save  
npm install express --save  
npm install cors body-parser --save  
REST Web Service in Node.js  
MySQL User and Database Creation:  
CREATE DATABASE nodejs_db;  
CREATE USER 'nodeuser'@'localhost' IDENTIFIED BY 'yourpassword’;  
GRANT ALL ON nodejs_db.* TO 'nodeuser'@'localhost’;  
ALTER USER 'nodeuser'@'localhost' IDENTIFIED WITH  
mysql_native_password BY 'yourpassword’;  
USE nodejs_db;  
CREATE TABLE users(  
id INT AUTO_INCREMENT PRIMARY KEY,  
name VARCHAR(40),  
email VARCHAR(40),  
password VARCHAR(40),  
hash VARCHAR(48),  
totalcoins INT,  
totaldeaths INT  
)
;
REST Web Service in Node.js  
index.js:  
var http = require('http');  
var mysql = require('mysql');  
var express = require('express');  
var cors = require('cors');  
var bodyParser = require('body-parser');  
var playersRouter = require('./routes/user');  
const hostname = '127.0.0.1';  
const port = 3131;  
dbcon = mysql.createConnection({  
host: "localhost",  
user: "nodeuser",  
password: "kjsdfDSFfefd21fd",  
database: 'nodejs_db'  
}
);  
dbcon.connect(function(err) {  
if (err) throw err;  
console.log("MySQL Connected!");  
}
);  
REST Web Service in Node.js  
index.js:  
var app = express();  
app.use(cors())  
app.use(bodyParser.json());  
app.use('/user', usersRouter);  
app.listen(port, hostname, () => console.log(`Server running at  
http://${hostname}:${port}/`));  
routes/user.js:  
var express = require('express');  
var crypto = require('crypto');  
var router = express.Router();  
router.get('/:id', function(req, res) {  
var userid = req.params.id;  
let sql = 'SELECT name, totalcoins, totaldeaths FROM users WHERE id = ?';  
dbcon.query(sql, userid, function(err, users, fields) {  
if (err) throw err;  
res.json({users});  
}
);  
}
);  
REST Web Service in Node.js  
routes/user.js:  
router.post('/login', function(req, res) {  
let sql1 = `SELECT * FROM users WHERE email = ? AND password = ?`;  
let values = [ req.body.email, req.body.password];  
dbcon.query(sql1, values, function(err, user, fields) {  
if (err) throw err;  
if (user.length > 0) {  
var loginhash = crypto.randomBytes(24).toString('hex');  
let sql2 = `UPDATE users SET hash = ? WHERE id = ?`;  
let values2 = [loginhash, user[0].id];  
dbcon.query(sql2, values2, function(err2, data2, fields2) {  
if (err2) throw err2;  
res.json({status: 200, id: user[0].id, name: user[0].name,  
hash: loginhash, totalcoins: user[0].totalcoins,  
totaldeaths: user[0].totaldeaths});  
}
);  
}
else {  
res.json({status: 200, id: -1, name: "", hash: "", totalcoins: -1,  
totalcoins: -1});  
}
}
)
}
);  
REST Web Service in Node.js  
routes/user.js:  
router.post('/coin', function(req, res) {  
let sql1 = `SELECT * FROM users WHERE id = ? AND hash = ?`;  
let values = [ req.body.id, req.body.hash ];  
dbcon.query(sql1, values, function(err, user, fields) {  
if (err) throw err;  
if (user.length > 0){  
let sql2 = `UPDATE users SET totalcoins = ? WHERE id = ?`;  
let values2 = [ user[0].totalcoins + 1, user[0].id ];  
dbcon.query(sql2, values2, function(err2, data2, fields2) {  
if (err2) throw err2;  
res.json({status: 200, message: "Successed"});  
}
);  
}
else{  
res.json({status: 200, message: "Failed"});  
}
}
)
}
);  
REST Web Service in Node.js  
routes/user.js:  
router.post('/death', function(req, res) {  
let sql1 = `SELECT * FROM users WHERE id = ? AND hash = ?`;  
let values = [req.body.id, req.body.hash];  
dbcon.query(sql1, values, function(err, user, fields) {  
if (err) throw err;  
if (user.length > 0){  
let sql2 = `UPDATE users SET totaldeaths = ? WHERE id = ?`;  
let values2 = [ user[0].totaldeaths + 1, user[0].id];  
dbcon.query(sql2, values2, function(err2, data2, fields2) {  
if (err2) throw err2;  
res.json({status: 200, message: "Successed"});  
}
);  
}
else{  
res.json({status: 200, message: "Failed"});  
}
}
)
}
);  
module.exports = router;  
REST WebService  
Test the WebService (post messages):  
REST Web Service in Node.js  
Test the Web Service (GET):  
http://SERVER_IP:3131/user/0  
Test the Web Service (POST):  
http://SERVER_IP:3131/user/login  
Header:  
User-Agent: Fiddler  
Content-Type: application/json  
Body:  
{
"email":"user@iade.pt", "password":"test"}  
http://SERVER_IP:3131/user/coin  
Header:  
User-Agent: Fiddler  
Content-Type: application/json  
Body:  
{
"id":"1", "hash":"b8fe06edaa1e…e7e4c7ee4b1ed"}  
HTTP Communication in Unreal Engine  
Project dependencies to be added to the project build file  
(YourGameName.Build.cs):  
Http  
Json  
JsonUtilities  
...  
PrivateDependencyModuleNames.AddRange(new string[] {  
"Http", "Json", "JsonUtilities" });  
...  
HTTP Communication in Unreal Engine  
Create a new C++ class (Actor): HTTPService  
HTTP Communication in Unreal Engine  
HTTPService implementation: data structures  
HTTPService.h  
HTTPService.h  
USTRUCT()  
USTRUCT()  
struct FLoginRequest {  
struct FLoginResponse {  
GENERATED_BODY()  
GENERATED_BODY()  
UPROPERTY()  
int status;  
UPROPERTY()  
int id;  
UPROPERTY()  
FString email;  
UPROPERTY()  
FString password;  
UPROPERTY()  
FString name;  
UPROPERTY()  
FString hash;  
UPROPERTY()  
int totalcoins;  
UPROPERTY()  
int totaldeaths;  
};  
};  
HTTP Communication in Unreal Engine  
HTTPService implementation: data structures  
HTTPService.h  
HTTPService.h  
USTRUCT()  
USTRUCT()  
struct FAddCoinRequest {  
GENERATED_BODY()  
struct FAddCoinResponse {  
GENERATED_BODY()  
UPROPERTY()  
FString id;  
UPROPERTY()  
int status;  
UPROPERTY()  
FString hash;  
UPROPERTY()  
FString message;  
};  
};  
HTTP Communication in Unreal Engine  
HTTPService implementation: basic http communication  
protected:  
HTTPService.h  
FHttpModule* Http;  
FString APIBaseUrl;  
TSharedRef<IHttpRequest> RequestWithRoute(FString Subroute);  
TSharedRef<IHttpRequest> GetRequest(FString Subroute);  
TSharedRef<IHttpRequest> PostRequest(FString Subroute,  
FString ContentJsonString);  
void Send(TSharedRef<IHttpRequest>& Request);  
bool ResponseIsValid(FHttpResponsePtr Response,  
bool bWasSuccessful);  
HTTP Communication in Unreal Engine  
HTTPService implementation: basic http communication  
HTTPService.cpp  
AHTTPService::AHTTPService()  
{
APIBaseUrl = TEXT("http://127.0.0.1:3131/");  
}
TSharedRef<IHttpRequest> AHTTPService::RequestWithRoute(Fstring  
Subroute)  
{
TSharedRef<IHttpRequest> Request = Http->CreateRequest();  
Request->SetURL(APIBaseUrl + Subroute);  
Request->SetHeader(TEXT("User-Agent"), TEXT("X-UnrealEngine-Agent"));  
Request->SetHeader(TEXT("Content-Type"), TEXT("application/json"));  
Request->SetHeader(TEXT("Accepts"), TEXT("application/json"));  
return Request;  
}
HTTP Communication in Unreal Engine  
HTTPService implementation: basic http communication  
HTTPService.cpp  
TSharedRef<IHttpRequest> AHTTPService::GetRequest(FString Subroute){  
TSharedRef<IHttpRequest> Request = RequestWithRoute(Subroute);  
Request->SetVerb("GET");  
return Request;  
}
TSharedRef<IHttpRequest> AHTTPService::PostRequest(FString Subroute,  
FString ContentJsonString){  
TSharedRef<IHttpRequest> Request = RequestWithRoute(Subroute);  
Request->SetVerb("POST");  
Request->SetContentAsString(ContentJsonString);  
return Request;  
}
void AHTTPService::Send(TSharedRef<IHttpRequest>& Request){  
Request->ProcessRequest();  
}
HTTP Communication in Unreal Engine  
HTTPService implementation: basic http communication  
HTTPService.cpp  
bool AHTTPService::ResponseIsValid(FHttpResponsePtr Response,  
bool bWasSuccessful){  
if ((!bWasSuccessful) || (!Response.IsValid())){  
return false;  
}
if (EHttpResponseCodes::IsOk(Response->GetResponseCode())){  
return true;  
}
else{  
UE_LOG(LogTemp, Warning, TEXT("Http Response returned error code:  
%d"), Response->GetResponseCode());  
return false;  
}
}
void AHTTPService::BeginPlay(){  
Super::BeginPlay();  
Http = &FHttpModule::Get();  
}
HTTP Communication in Unreal Engine  
HTTPService implementation: login  
Create a new widget blueprint for the login interface:  
HTTP Communication in Unreal Engine  
HTTPService implementation: login  
protected:  
class AMyCharacter* LocalCharacter;  
HTTPService.h  
public:  
UPROPERTY(EditDefaultsOnly, Category = "UI")  
TSubclassOf<class UUserWidget> LoginWidgetClass;  
UUserWidget* LoginWidget;  
void Login(FLoginRequest LoginCredentials, class APlayerState*  
playerState);  
void LoginResponse(FHttpRequestPtr Request, FHttpResponsePtr  
Response, bool bWasSuccessful, APlayerState* playerState);  
UFUNCTION()  
void OnLoginClicked();  
HTTP Communication in Unreal Engine  
HTTPService implementation: login  
void AHTTPService::BeginPlay(){  
HTTPService.cpp  
...  
if (LoginWidgetClass){  
LoginWidget=CreateWidget<UUserWidget>(GetWorld(),LoginWidgetClass);  
LoginWidget->AddToViewport();  
UButton* LoginButton = Cast<UButton>(LoginWidget->  
GetWidgetFromName(TEXT("LoginButton")));  
if (LoginButton){  
LoginButton->OnClicked.AddDynamic(this,  
&AHTTPService::OnLoginClicked);  
}
.
..  
HTTP Communication in Unreal Engine  
HTTPService implementation: login  
...  
HTTPService.cpp  
for (TActorIterator<AActor> ActorItr(GetWorld()); ActorItr;  
++ActorItr){  
AMyCharacter* character = Cast<AMyCharacter>(*ActorItr);  
if (character){  
if (character->IsLocallyControlled()){  
LocalCharacter = character;  
LocalCharacter->DisableInput(nullptr);  
APlayerController* pController = Cast<APlayerController>(  
LocalCharacter->GetController());  
pController->bShowMouseCursor = true;  
pController->bEnableClickEvents = true;  
pController->bEnableMouseOverEvents = true;  
}
}
}
}
}
HTTP Communication in Unreal Engine  
HTTPService implementation: login  
HTTPService.cpp  
void AHTTPService::OnLoginClicked(){  
UEditableTextBox* EmailText = Cast<UEditableTextBox>(LoginWidget->  
GetWidgetFromName(TEXT("EmailTextBox")));  
UEditableTextBox* PasswordText = Cast<UEditableTextBox>(LoginWidget->  
GetWidgetFromName(TEXT("PasswordTextBox")));  
UTextBlock* ErrorText = Cast<UTextBlock>(LoginWidget->  
GetWidgetFromName(TEXT("ErrorMessage")));  
if ((EmailText) && (PasswordText) && (ErrorText)){  
FLoginRequest LoginCredentials;  
LoginCredentials.email = EmailText->GetText().ToString();  
LoginCredentials.password = PasswordText->GetText().ToString();  
ErrorText->SetVisibility(ESlateVisibility::Hidden);  
Login(LoginCredentials, LocalCharacter->PlayerState);  
}
}
HTTP Communication in Unreal Engine  
HTTPService implementation: login  
HTTPService.cpp  
void AHTTPService::Login(FLoginRequest LoginCredentials,  
APlayerState* playerState)  
{
FString ContentJsonString;  
FJsonObjectConverter::UStructToJsonObjectString(FLoginRequest::  
StaticStruct(), &LoginCredentials, ContentJsonString, 0, 0);  
TSharedRef<IHttpRequest> Request = PostRequest("user/login/",  
ContentJsonString);  
Request->OnProcessRequestComplete().BindUObject(this,  
&AHTTPService::LoginResponse, playerState);  
Send(Request);  
}
HTTP Communication in Unreal Engine  
HTTPService implementation: login  
HTTPService.cpp  
void AHTTPService::LoginResponse(FHttpRequestPtr Request,  
FHttpResponsePtr Response, bool bWasSuccessful,  
APlayerState* playerState){  
if (!ResponseIsValid(Response, bWasSuccessful)){  
return;  
}
FLoginResponse LoginResponse;  
FString JsonString = Response->GetContentAsString();  
FJsonObjectConverter::JsonObjectStringToUStruct<FLoginResponse>(  
JsonString, &LoginResponse, 0, 0);  
if (LoginResponse.LoginResult.status == 200){  
LoginWidget->RemoveFromViewport();  
LocalCharacter->EnableInput(nullptr);  
APlayerController* pController = Cast<APlayerController>(  
LocalCharacter->GetController());  
...  
HTTP Communication in Unreal Engine  
HTTPService implementation: login  
.
..  
HTTPService.cpp  
pController->bShowMouseCursor = false;  
pController->bEnableClickEvents = false;  
pController->bEnableMouseOverEvents = false;  
AMyPlayerState* pState = Cast<AMyPlayerState>(playerState);  
pState->InitPlayerSession(this, LoginResponse.hash,  
LoginResponse.id,  
LoginResponse.totalcoins,  
LoginResponse.totaldeaths);  
}
else{  
UTextBlock* ErrorText = Cast<UTextBlock>(LoginWidget->  
GetWidgetFromName(TEXT("ErrorMessage")));  
if (ErrorText){  
ErrorText->SetVisibility(ESlateVisibility::Visible);  
}
}
}
HTTP Communication in Unreal Engine  
HTTPService implementation: login  
Create a new C++ class to define a new Player State:  
HTTP Communication in Unreal Engine  
HTTPService implementation: login  
private:  
class AHTTPService* HTTPServiceRef;  
MyPlayerState.h  
protected:  
UPROPERTY(Replicated)  
FString AuthenticationHash;  
UPROPERTY(Replicated)  
int PID;  
UPROPERTY(Replicated)  
int TotalCoins;  
UPROPERTY(Replicated)  
int TotalDeaths;  
public:  
void InitPlayerSession(AHTTPService* HTTPService, FString hash,  
int id, int coins, int deaths);  
HTTP Communication in Unreal Engine  
HTTPService implementation: login  
MyPlayerState.cpp  
void AMyPlayerState::InitPlayerSession(AHTTPService* HTTPService,  
FString hash, int id, int coins, int deaths){  
HTTPServiceRef = HTTPService;  
AuthenticationHash = hash;  
Totalcoins = coins;  
TotalDeaths = deaths;  
PID = id;  
}
void AMyPlayerState::GetLifetimeReplicatedProps(TArray<  
FLifetimeProperty>& OutLifetimeProps) const {  
Super::GetLifetimeReplicatedProps(OutLifetimeProps);  
DOREPLIFETIME(AMyPlayerState, AuthenticationHash);  
DOREPLIFETIME(AMyPlayerState, TotalCoins);  
DOREPLIFETIME(AMyPlayerState, TotalDeaths);  
DOREPLIFETIME(AMyPlayerState, PID);  
}
HTTP Communication in Unreal Engine  
HTTPService implementation: login  
Create a blueprint for the HTTPService and select the widget blueprint  
of the login interface.  
In the Game Mode blueprint, select the new player state class:  
HTTP Communication in Unreal Engine  
Test the login process in the game:  
HTTP Communication in Unreal Engine  
HTTPService implementation: adding player coins (webservice)  
.
..  
public:  
void AddCoin(FAddCoinRequest AddCoinInfo);  
HTTPService.h  
void AddCoinResponse(FHttpRequestPtr Request, FHttpResponsePtr  
Response, bool bWasSuccessful);  
HTTP Communication in Unreal Engine  
HTTPService implementation: adding player coins (webservice)  
HTTPService.cpp  
void AHTTPService::AddCoin(FAddCoinRequest AddCoinInfo)  
{
FString ContentJsonString;  
FJsonObjectConverter::UStructToJsonObjectString(FAddCoinRequest::  
StaticStruct(), &AddCoinInfo, ContentJsonString, 0, 0);  
TSharedRef<IHttpRequest> Request = PostRequest("user/coin/",  
ContentJsonString);  
Request->OnProcessRequestComplete().BindUObject(this,  
&AHTTPService::AddCoinResponse);  
Send(Request);  
}
HTTP Communication in Unreal Engine  
HTTPService implementation: adding player coins (webservice)  
HTTPService.cpp  
void AHTTPService::AddCoinResponse(FHttpRequestPtr Request,  
FHttpResponsePtr Response, bool bWasSuccessful){  
if (!ResponseIsValid(Response, bWasSuccessful)){  
return;  
}
FAddCoinResponse AddCoinResponse;  
FString JsonString = Response->GetContentAsString();  
FJsonObjectConverter::JsonObjectStringToUStruct<FAddCoinResponse>(  
JsonString, &AddCoinResponse, 0, 0);  
if (AddCoinResponse.AddUserCoinResult.status == 200){  
UE_LOG(LogTemp, Log, TEXT("AddCoin Succeeded!"));  
}
}
HTTP Communication in Unreal Engine  
HTTPService implementation: adding player coins (webservice)  
protected:  
...  
MyPlayerState.h  
UPROPERTY(Replicated)  
int TotalCoins;  
...  
public:  
...  
void AddPlayerCoin();  
HTTP Communication in Unreal Engine  
HTTPService implementation: adding player coins (webservice)  
MyPlayerState.cpp  
void AMyPlayerState::AddPlayerCoin()  
{
TotalCoins++;  
if (HTTPServiceRef)  
{
FAddCoinRequest AddCoinInfo;  
AddCoinInfo.hash = AuthenticationHash;  
AddCoinInfo.id = FString::FromInt(PID);  
HTTPServiceRef->AddCoin(AddCoinInfo);  
}
}
HTTP Communication in Unreal Engine  
HTTPService implementation: adding player coins (webservice)  
...  
CollectibleCoin.cpp  
void ACollectibleCoin::NotifyActorBeginOverlap(AActor* OtherActor)  
{
Super::NotifyActorBeginOverlap(OtherActor);  
AMyCharacter *mycharacter = dynamic_cast<AMyCharacter*>  
(OtherActor);  
if (mycharacter != nullptr){  
AMyPlayerState* pState = Cast<AMyPlayerState>(  
mycharacter->GetPlayerState());  
if (pState){  
pState->AddPlayerCoin();  
}
Destroy(this);  
PlayEffects();  
}
}
...  
HTTP Communication in Unreal Engine  
Test the game and check if the total of coins is updated in the  
server:  
Exercise 1  
1
) Continue the development of the game and implement the  
following features:  
a) Implement the process to add player deaths (using the webservice  
function AddUserDeath).  
The total of deaths must be incremented every time the player dies.  
b) Disable the movement of the enemies while the player is at the login  
screen.  
c) (Optional - OK+ Challenge) Implement a ranking to be displayed on  
the game over screen (when the player dies).  
The ranking must include the players’ names, total of coins and total of deaths.  
ꢁꢀꢂꢃ ꢁꢄ ꢅꢁꢆꢇꢈ  
The ranking must be ordered by: ꢀ  
ꢁꢀꢂꢃ ꢁꢄ ꢉꢊꢂꢀℎꢈ  
Only the information of the first 10 players must be shown on screen.  
Further Reading  
Carnall, B. (2016). Unreal Engine 4.X By Example. Packt  
Publishing. ISBN: 978-1785885532.  
Web Resources:  
HTTP - Unreal Engine -  
https://api.unrealengine.com/INT/API/Runtime/HTTP/index.html  
Http-Requests - https://wiki.unrealengine.com/Http-requests