Best way to start UE4 Dedicated Servers on demand?

Hey there,

so I’ve went through the setup steps and have my UE4 Dedicated Server setup to register at GameLift (Local for now).

It also seems to react to the (local) AWS CLI thingy.

Now my setup is the following:

Used Services: GameSparks, GameLift
Entities: GameSparks Server (Cloud Code), GameLift Server, Game Client, Dedicated Server(s)

Game Client connects to GameSparks and logs in. Game Client then performs a “FindMatch” request, that starts GameSparks matchmaking process.
Other Game Client does the same.

Now as an example, the match may be 1vs1 and the Match has been found (both players are matched).
GameSparks now offers an interrupt point at which I could execute so called “Cloud Code”.

Now at this point, I would like to tell GameLift to start a Dedicated Server instance.
This DediServer would connect to GameSparks and tell GameSparks its IP and Port via Message, as well as then forwarding this to the Game Clients, so they can connect.

My problem right now is understanding how to do the “Tell GameLift to start a Dedicated Server Instance”. Cloud Code can use HTTP Requests (at least I think so) but I haven’t seen anything regarding that to connect to GameLift.

What is the correct way of starting a new Dedicated Server instance?
How do I handle multiple instances? They would also listen to different Ports.
At which point would (and how) I need to create a new Fleet(?) to get a new, fresh range of Servers due to new Ports? Because at some point, the Instances would cover all Ports.

I’m a bit lost in the documentation of GameLift at the moment and would love me some help here :confused:

I would let GameSparks CloudCode take the GameSparks UserIDs, pass them to GameLift (together with the StartServer request) and later use these to let the DediServer report back to the CloudCode, which Players should receive the IP and Port (together with ConnectToServer) message.

Hello eXi

There is a lot here.

GameLift takes care of starting as many dedicated server instances as needed to supply you with the servers, (server processes, really) that you need to host the games. During fleet creation, the number of instances is set to one. You can adjust this manually in the console. You can and should also supply scaling rules to allow the fleet to scale up and down according to demand. You will always have a float of ‘idle instances’ which are needed to accommodate new demand (i.e. an imbalance between games starting and games ending). See here:

https://console.aws.amazon.com/gamelift/home?region=us-east-1#/r/fleets

to look at fleets in the N.Virginia region.

The procedure your matchmaking code should now go through is pretty straightforward. You need to ‘use up’ one of the running server processes. This you do by starting a game session.

http://docs.aws.amazon.com/gamelift/latest/apireference/API_CreateGameSession.html

See the section at the end of this answer as for HOW to make this call with GameSparks as it doesn’t support the AWS SDK like most other environments, which is available in the language of your choice:

https://aws.amazon.com/tools/#sdk

The call to create a game session returns a GameSessionId.

http://docs.aws.amazon.com/gamelift/latest/apireference/API_GameSession.html#gamelift-Type-GameSession-GameSessionId

The game session has player slots in it that you can reserve (for 60 seconds max) whilst the player connects. In your case you have already found all of the players and they are ready to connect. So you fill all of the player slots with players. To do this, you use the GameSessionId to call CreatePlayerSession for each of the two (or however many) players in the match that you made.

http://docs.aws.amazon.com/gamelift/latest/apireference/API_CreatePlayerSession.html

This returns the IP address and port that the player should connect to on the dedicated server. It also returns a PlayerSessionId for each of the players that are joining. Your GameSparks server (or your matchmaking server, if you are not using GameSparks) receives that and passes the information back to the client, as part of the response to the matching request that the client made earlier before all this began.

The player client connects to the IP and port it receives, and passes the player session ID to the server. We don’t determine the communication mechanism used, as this is your client talking to your server at this point. But the server must get the PlayerSessionId.

When it does, the server now needs to contact the GameLift Session management service using the GameLift Server SDK from this page:

https://aws.amazon.com/gamelift/getting-started/

currently

https://s3-us-west-2.amazonaws.com/gamelift-release/GameLift_04_11_2017.zip

The server has already called InitSDK() and ProcessReady() by this point and it has already responded to the onStartGameSession() callback. Now it calls AcceptPlayerSession(playerSessionId) and this lets GameLift know that the reserved slot was correctly occupied. Don’t forget that the time from CreatePlayerSession to AcceptPlayerSession must be under 60 seconds, so no reserving a slot then hanging around in a lobby, you must join right away.

So your player is in! And gameplay starts whenever your server decides. When it decides to end the game, use the GameLift Server SDK call TerminateGameSession(), then either ProcessReady() again, or ProcessEnding(). See also this page for server integration:

http://docs.aws.amazon.com/gamelift/latest/developerguide/gamelift-sdk-server-api.html

See also this page for client (which includes matchmaking server) integration:

http://docs.aws.amazon.com/gamelift/latest/developerguide/gamelift-sdk-client-api.html

Note also the availability of Game Session Queues. Game Session Queues allocate games globally to the most appropriate region where infrastructure is ready to accept it. See also here:

http://docs.aws.amazon.com/gamelift/latest/developerguide/gamelift-sdk-client-queues.html

I want to add some notes about how to call GameLift from GameSparks too. GameSparks doesn’t support calling any of our client SDKs (which is what you would use if you are not using GameSparks i.e. much easier). So at this moment we have to go through an extra step of creating a RESTful call within GameSparks. All AWS calls need to be signed correctly, see:

http://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html

Signing requests requires that the caller has an AWS Access Key and Secret Key with appropriate permissions. Let’s get that first.

Go to IAM policies:

https://console.aws.amazon.com/iam/home?region=us-east-1#/policies

and Create Policy. Select Create your own policy. Name the policy Gamelift. Leave description blank. Paste the following json into the policy document:

    {
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Action": "gamelift:*",
"Resource": "*"
}
}

Create a user that has that policy and get the access and secret key. Go to:

https://console.aws.amazon.com/iam/home?region=us-east-1#/users

and Add user. User name can be anything, but check Programmatic access and leave AWS Management Console access unchecked. Go to Next: Permissions.

Choose Attach existing policies directly. Search for the name of the policy you created, Gamelift, in the search box, and check the item with the checkbox. Go to Next: Review.

Continue to Create user. Download the .CSV file and put it somewhere safe, where you will be able to find it again. Get the keys out of the CSV file you downloaded. The access key looks like this:

AKIAIPYL2NP2VRQ4MQLU

and the secret key, which looks like this:

QWCep2kL5EvwZ74jx92QLZMzvQn4neVc2eMoOlrd

Step One complete.

This example is to build a command to list GameLift aliases. Let’s do everything in us-east-1. Make sure you have an alias in your GameLift console. Go to:

https://console.aws.amazon.com/gamelift/home?region=us-east-1#/r/aliases/create

and create an alias called Test. Give it a blank description, make it a terminal type, and a termination message of “sorry”.

Step Two complete.

Let’s also talk about the format of the call.

Gamelift only uses HTTPS POST messages, not GET.

The resource URI is

https://gamelift.us-east-1.amazonaws.com/

If the call is to a different region this changes somewhat.

There are no query parameters.

HTTP Headers are as follows:

    X-Amz-Content-SHA256 this is the hash of the payload
content-length this is the length of the payload
content-type application/x-amz-json-1.1 <! json-1.0 DOESNT WORK
X-Amz-Target GameLift.ListAliases
Host gamelift.us-east-1.amazonaws.com
X_Amz_Date this is the time of the request in ISO 8601 format. It must be at one-second resolution, i.e. do not include milliseconds in the timestamp. It is valid 15 minutes before or after the server’s current time when AWS processes it.
Authorization this is the signature string. We will worry about how to create the signature shortly. It has this format. E.g.:
AWS4-HMAC-SHA256 Credential=/20170407/us-east-1/gamelift/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-target, Signature=c8997a634bed0f4ef7f3a8beaa16531f8c89cbe134dcd8671ecceaab9c2a06e3

The request body contains the payload. The payload at a minimum is

{}

i.e. the empty JSON form. If there are request parameters, these are included in this JSON. The hash of this string and the length of the string are given in the headers.

That’s it. *Almost.

Attached to the answer is some GameSparks code. Please note that I was unable to fully test it, but I think it constains everything else you need to build the above RESTful call.

It does depend on CryptoJS v3.1.2 available from the Google Code Archive here:
code.google.com/p/crypto-js

specifically sha256.js and sha256hmac.js


One more thing. If you are trying this and get stuck, let me know and we will figure out how to get you going again.

Thank you!

Al Murray :)
Solutions Architect
[4398-gamelift-rest-call.txt|attachment](upload://soNgFhp5sxlpxFb40OKdlrwHvpt.txt) (13.5 KB)gamelift-rest-call.txtAmazon Game Services

We don’t have any fleets setup yet. Without knowing how to get the bridge to GameSparks, there was no need yet.
So we will simply add an accounts for me to the “main” account. (:

This is one of the most awesome answer to a question that I ever got.
As a UE4 Moderator I know how much work it is too always give such detailed answers.
I highly appreciate the time you took to write all of this, thank you a lot!

I’ll go through it tomorrow and check back as soon as I get stuck and can’t help myself in any other way.

There is one small question, not totally related: We had trouble earlier getting my account into the account of the game “owner”. We tried an organization but seeing IAM, I assume we just need to create an account there and let me login through that?
I need to work on the GameLift Service, so I need access to the Dashboard of it.

If I understand you correctly, you have two AWS accounts. You have your GameLift fleets and stuff in one. But you need to access the other to move the fleets to it, so that it is billed correctly to the second accountholder. You currently have no permissions to access the second account. If that is right then:

Either, a) the second accountholder can take over ownership of the first account and pay using consolidated billing.
See here:

http://docs.aws.amazon.com/organizations/latest/userguide/orgs_getting-started_from-consolidatedbilling.html

Or, b) you can gain access to the second account to administer the fleets. You need the second account holder to create an IAM policy in that account that gives permissions that you need. Then he attaches that policy to a user that he creates for you, and gives you the access URL, username and password for that account so that you can log on and do what you need.

In this case you would need to recreate the fleets in the new account with your gamelift admin user, point the clients at them etc.

Does that get you where you need to be?

Al Murray :)
Solutions Architect
Amazon Game Services

Hey, so I had a few minutes to add the code to GameSparks CloudCode, but it seems like they do not support encryption/decryption. So CryptoJS is not a thing :confused:
I assume I need to build me something in between then that receives the GameSparks stuff and then performs the POST correctly?

“So CryptoJS is not a thing :/”

I’m not sure that I understand the problem you are having. As I mentioned in the original response, the code I provided does depend on CryptoJS v3.1.2 available from the Google Code Archive here:

code.google.com/p/crypto-js

specifically sha256.js and sha256hmac.js

I have attached those files for your convenience. 4410-sha256.zip (5.8 KB)sha256.zip

These create hashes for the code signing. Are you saying that you cant make an HTTPS POST call in GameSparks? That would appear to be in the AWSRequest.js file at line 185 in the function:

AWSRequest.prototype.makeRequest

Once you have created your AWSRequest object, you are expected to call that function to make the HTTPS call take place.

I hope that helps!

Al Murray :)
Solutions Architect
Amazon Game Services

Hey there (:

What I mean is that GameSparks CloudCode does not support CryptoJS.
I can’t add it. GameSparks would need to add it from their side.

I added the AWSRequest definition to a module in GameSparks CloudCode and named it “GameLiftRequest”. Then I made an event called “gameliftRequest” with the following code (removed the key/secret here):


require('GameLiftRequest');
var creds = {
key: '---',
secret: '---'
};
var target = 'GameLift.ListAliases';
var requestBody = '{}';
var Request = new AWSRequest(
'gamelift.us-east-1.amazonaws.com',
'/',
'',
[
{
name : 'x-amz-target',
value : target
},
{
name: 'content-type',
value: 'application/x-amz-json-1.1'
}
],
requestBody,
"us-east-1",
"gamelift",
creds
); function OnResponse(result, error)
{
Spark.getLog().debug(result.data);
}
Request.makeRequest(OnResponse);

Here is a screenshot of the error when trying to use the code:

I googled for CryptoJS and GameSparks and only found a question/answer by GameSparks saying that they don’t support it (or any other en-/decryption).

So I assume I need to perform an HTTP Request that sends the data to a selfmade service that then transforms it into the correct HTTP Request (by using CryptoJS).

I’m not so used to HTTP Requests. I know what they do etc but I barely actually used them.
So if I’m doing something obviously wrong here, please excuse me.

Kind regards,
Cedric

Okay, so I redid quite some parts of the code and tested everything with the test values from the docs. All my keys are now correct. Then I also fixed some other functions that weren’t doing what they were supposed to do.

Now I get a different Error:

“Error Code: 400 | Error Headers: {“Server”:“Server”,“Connection”:“keep-alive”,“x-amzn-RequestId”:“0e238484-2cde-11e7-a87d-7d05f90cfb35”,“Vary”:“Accept-Encoding,User-Agent”,“Date”:“Sat, 29 Apr 2017 13:16:28 GMT”,“Content-Type”:“application/x-amz-json-1.1”} | Error Data: {”__type":“SerializationException”}"

Any idea what this could be? Google only brings me non-gamelift queries that are solved by changing something that I don’t even have.

EDIT: Here is the log of all the HTTP stuff directly before it got sent:

"PostURL: https://gamelift.us-east-1.amazonaws.com/
Headers: {“x-amz-target”:“GameLift.ListAliases”,“content-type”:“application/x-amz-json-1.1”,“x-amz-date”:“20170429T132142Z”,“host”:“gamelift.us-east-1.amazonaws.com”,“Authorization”:“AWS4-HMAC-SHA256 Credential=-removed-/20170429/us-east-1/gamelift/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-target, Signature=-removed-”}

Payload: "

EDIT2: @Al…Murray

Hey there,

so some time passed and I found out that the Forum answer of one of the GameSparks people seems to be kinda outdated.
They support encryption: https://docs2.gamesparks.com/api-documentation/cloud-code-api/utilities/sparkdigest.html

I replaced the CryptoJS calls with the matching Digester calls from GameSparks, but I still get BadRequest answers from Amazon.

The Error Data (from the Response) tells me:
“The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.”

Then, in addition, it also tells me how two parts should be formatted: The “Canonical String” and the “String to Sign”. It tells me how they should have looked like.
So I went ahead and printed my version of it and fixed the functions by also using Amazons docs about signing the request.

Now the 2 parts are exactly the same as mine. Everything is at the correct place and the encrypted hashs etc are also exactly the same. Only small difference is, that the response in the script has “\n” instead of “\n” but when changing that, the hex changes, so I assume that is just from GameSparks logs and it should be “\n”. The Amazon Docs say the same.

So at this point I don’t understand anymore why it’s a bad request if the “Canonical String” and the “String to Sign” are correct. It doesn’t give me any other information in the response.

Hey @eXi, Al is actually out of the office at the moment so I’ve created a ticket with the other GameLift techs to see if someone can help you out while Al is away.

Hi there, sorry about the delays. I have cut an internal ticket for the GameLift team to get back to you.

Hello @eXi,

The headers for your request seems to be working, so that’s good.

Now, I believe you are still missing data in the body of the request for it to go through.
Would you mind trying out the same ListAliases call with this JSON in the body?

{"__type":"ListAliasesInput"}

Hope this helps.

Alexis

Hey Alexis,

thanks a lot for your response. This solved the issue and I now get the Aliases listed.
Is there documentation on what I need to put into the RequestBody for GameLift calls?
Al’s example said I don’t need a request body (for ListAliases), that’s why it was empty.

I would like to understand WHY this solves the issue, before I ask again next time.

Kind regards,
Cedric

hey Cedric,

So, I think we are entering ‘reverse engineer’ territory.

I do not think there is a list anywhere we can base ourselves off of. We are kind of manually forging AWS requests at this point.

I’m sure I’m missing something, but I’m wondering why the JavaScript AWS SDK can’t be used in this case. Any thoughts?

Alexis

Hey,

GameSparks uses their own coding framework called “Cloud Code”.
I can’t actually add any libraries to it, but only write code and use the classes that are available.
To avoid having to create a Service in the middle, between GameSparks and GameLift, I want to do this via Requests.
I’ll see if I run into any issues. The main requests that will happen are “CreateGameSession” and similar. It should be mostly explained in my first post.

Kind regards,
Cedric