Lesson 35 - Get Compute Auth Token Working

This commit is contained in:
Norman Lansing
2026-02-28 12:32:28 -05:00
parent 1d477ee42a
commit 4fde462bce
7743 changed files with 1397833 additions and 18 deletions

View File

@@ -0,0 +1,527 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
AWSTemplateFormatVersion: "2010-09-09"
Description: >
This CloudFormation template sets up a game backend service with a single Amazon GameLift fleet. After player
authenticates and start a game via POST /start_game, a lambda handler searches for an existing viable game session
with open player slot on the fleet, and if not found, creates a new game session. The game client is then expected
to poll POST /get_game_connection to receive a viable game session.
Parameters:
ApiGatewayStageNameParameter:
Type: String
Default: v1
Description: Name of the Api Gateway stage
BuildNameParameter:
Type: String
Default: Sample GameLift Build
Description: Name of the build
BuildOperatingSystemParameter:
Type: String
Default: WINDOWS_2016
Description: Operating system of the build
BuildServerSdkVersionParameter:
Type: String
Description: GameLift Server SDK version used in the server build
BuildS3BucketParameter:
Type: String
Description: Bucket that stores the server build
BuildS3KeyParameter:
Type: String
Description: Key of the server build in the S3 bucket
BuildVersionParameter:
Type: String
Description: Version number of the build
FleetDescriptionParameter:
Type: String
Default: Deployed by the Amazon GameLift Plug-in for Unreal.
Description: Description of the fleet
FleetNameParameter:
Type: String
Default: Sample GameLift Fleet
Description: Name of the fleet
FleetTcpFromPortParameter:
Type: Number
Default: 33430
Description: Starting port number for TCP ports to be opened
FleetTcpToPortParameter:
Type: Number
Default: 33440
Description: Ending port number for TCP ports to be opened
FleetUdpFromPortParameter:
Type: Number
Default: 33430
Description: Starting port number for UDP ports to be opened
FleetUdpToPortParameter:
Type: Number
Default: 33440
Description: Ending port number for UDP ports to be opened
GameNameParameter:
Type: String
Default: MyGame
Description: Game name to prepend before resource names
MaxLength: 30
LambdaZipS3BucketParameter:
Type: String
Description: S3 bucket that stores the lambda function zip
LambdaZipS3KeyParameter:
Type: String
Description: S3 key that stores the lambda function zip
LaunchParametersParameter:
Type: String
Description: Parameters used to launch the game server process
LaunchPathParameter:
Type: String
Description: Location of the game server executable in the build
MaxPlayersPerGameParameter:
Type: Number
Default: 10
Description: Maximum number of players per game session
MaxTransactionsPerFiveMinutesPerIpParameter:
Type: Number
Default: 100
MaxValue: 20000000
MinValue: 100
UnrealEngineVersionParameter:
Type: String
Description: "Unreal Engine Version being used by the plugin"
EnableMetricsParameter:
Type: String
Default: "false"
AllowedValues: ["true", "false"]
Description: "Enable telemetry metrics collection using OTEL collector"
Conditions:
ShouldCreateMetricsResources: !Equals [!Ref EnableMetricsParameter, "true"]
Resources:
ApiGatewayCloudWatchRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- apigateway.amazonaws.com
Action: "sts:AssumeRole"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
Account:
Type: "AWS::ApiGateway::Account"
Properties:
CloudWatchRoleArn: !GetAtt ApiGatewayCloudWatchRole.Arn
GameRequestLambdaFunctionExecutionRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- "sts:AssumeRole"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
Policies:
- PolicyName: !Sub ${GameNameParameter}GameRequestLambdaFunctionGameLiftPolicies
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "gamelift:CreateGameSession"
- "gamelift:CreatePlayerSession"
- "gamelift:SearchGameSessions"
Resource: "*"
RestApi:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: !Sub ${GameNameParameter}RestApi
ResultsRequestLambdaFunctionExecutionRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- "sts:AssumeRole"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
Policies:
- PolicyName: !Sub ${GameNameParameter}ResultsRequestLambdaFunctionGameLiftPolicies
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "gamelift:CreateGameSession"
- "gamelift:CreatePlayerSession"
- "gamelift:SearchGameSessions"
Resource: "*"
MetricsInstanceRole:
Type: "AWS::IAM::Role"
Condition: ShouldCreateMetricsResources
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: gamelift.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: AMPRemoteWriteAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- aps:RemoteWrite
Resource: !Sub 'arn:aws:aps:*:${AWS::AccountId}:workspace/*'
- PolicyName: OTelCollectorEMFExporter
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:PutLogEvents
- logs:CreateLogStream
- logs:CreateLogGroup
- logs:PutRetentionPolicy
Resource: !Sub 'arn:aws:logs:*:${AWS::AccountId}:log-group:*:log-stream:*'
UserPool:
Type: "AWS::Cognito::UserPool"
Properties:
AdminCreateUserConfig:
AllowAdminCreateUserOnly: false
AutoVerifiedAttributes:
- email
EmailConfiguration:
EmailSendingAccount: COGNITO_DEFAULT
EmailVerificationMessage: "Please verify your email to complete account registration for the GameLift Plugin Single-fleet deployment scenario. Confirmation Code {####}."
EmailVerificationSubject: GameLift Plugin - Deployment Scenario Account Verification
Policies:
PasswordPolicy:
MinimumLength: 8
RequireLowercase: true
RequireNumbers: true
RequireSymbols: true
RequireUppercase: true
Schema:
- Name: email
AttributeDataType: String
Mutable: false
Required: true
UserPoolName: !Sub ${GameNameParameter}UserPool
UsernameAttributes:
- email
BuildAccessRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- cloudformation.amazonaws.com
- gamelift.amazonaws.com
Action: "sts:AssumeRole"
Policies:
- PolicyName: !Sub ${GameNameParameter}BuildS3AccessPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "s3:GetObject"
- "s3:GetObjectVersion"
Resource:
- "Fn::Sub": "arn:aws:s3:::${BuildS3BucketParameter}/${BuildS3KeyParameter}"
RoleName: !Sub ${GameNameParameter}BuildIAMRole
GameRequestApiResource:
Type: "AWS::ApiGateway::Resource"
Properties:
ParentId: !GetAtt RestApi.RootResourceId
PathPart: start_game
RestApiId: !Ref RestApi
ResultsRequestApiResource:
Type: "AWS::ApiGateway::Resource"
Properties:
ParentId: !GetAtt RestApi.RootResourceId
PathPart: get_game_connection
RestApiId: !Ref RestApi
UserPoolClient:
Type: "AWS::Cognito::UserPoolClient"
Properties:
AccessTokenValidity: 1
ClientName: !Sub ${GameNameParameter}UserPoolClient
ExplicitAuthFlows:
- ALLOW_USER_PASSWORD_AUTH
- ALLOW_REFRESH_TOKEN_AUTH
GenerateSecret: false
IdTokenValidity: 1
PreventUserExistenceErrors: ENABLED
ReadAttributes:
- email
- preferred_username
RefreshTokenValidity: 30
SupportedIdentityProviders:
- COGNITO
UserPoolId: !Ref UserPool
WebACL:
Type: "AWS::WAFv2::WebACL"
DependsOn:
- ApiDeployment
Properties:
DefaultAction:
Allow:
{}
Description: !Sub "WebACL for game: ${GameNameParameter}"
Name: !Sub ${GameNameParameter}WebACL
Rules:
- Name: !Sub ${GameNameParameter}WebACLPerIpThrottleRule
Action:
Block:
{}
Priority: 0
Statement:
RateBasedStatement:
AggregateKeyType: IP
Limit: !Ref MaxTransactionsPerFiveMinutesPerIpParameter
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Sub ${GameNameParameter}WebACLPerIpThrottleRuleMetrics
SampledRequestsEnabled: true
Scope: REGIONAL
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Sub ${GameNameParameter}WebACLMetrics
SampledRequestsEnabled: true
ApiDeployment:
Type: "AWS::ApiGateway::Deployment"
DependsOn:
- GameRequestApiMethod
- ResultsRequestApiMethod
Properties:
RestApiId: !Ref RestApi
StageDescription:
DataTraceEnabled: true
LoggingLevel: INFO
MetricsEnabled: true
StageName: !Ref ApiGatewayStageNameParameter
Authorizer:
Type: "AWS::ApiGateway::Authorizer"
Properties:
IdentitySource: method.request.header.Auth
Name: CognitoAuthorizer
ProviderARNs:
- "Fn::GetAtt":
- UserPool
- Arn
RestApiId: !Ref RestApi
Type: COGNITO_USER_POOLS
GameRequestApiMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: COGNITO_USER_POOLS
AuthorizerId: !Ref Authorizer
HttpMethod: POST
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GameRequestLambdaFunction.Arn}/invocations"
OperationName: GameRequest
ResourceId: !Ref GameRequestApiResource
RestApiId: !Ref RestApi
ResultsRequestApiMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: COGNITO_USER_POOLS
AuthorizerId: !Ref Authorizer
HttpMethod: POST
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ResultsRequestLambdaFunction.Arn}/invocations"
OperationName: ResultsRequest
ResourceId: !Ref ResultsRequestApiResource
RestApiId: !Ref RestApi
WebACLAssociation:
Type: "AWS::WAFv2::WebACLAssociation"
DependsOn:
- ApiDeployment
- WebACL
Properties:
ResourceArn: !Sub
- "arn:aws:apigateway:${REGION}::/restapis/${REST_API_ID}/stages/${STAGE_NAME}"
- REGION: !Ref "AWS::Region"
REST_API_ID: !Ref RestApi
STAGE_NAME: !Ref ApiGatewayStageNameParameter
WebACLArn: !GetAtt WebACL.Arn
ServerBuild:
Type: "AWS::GameLift::Build"
Properties:
Name: !Ref BuildNameParameter
OperatingSystem: !Ref BuildOperatingSystemParameter
ServerSdkVersion: !Ref BuildServerSdkVersionParameter
StorageLocation:
Bucket: !Ref BuildS3BucketParameter
Key: !Ref BuildS3KeyParameter
RoleArn: !GetAtt BuildAccessRole.Arn
Version: !Ref BuildVersionParameter
FleetResource:
Type: "AWS::GameLift::Fleet"
Properties:
BuildId: !Ref ServerBuild
CertificateConfiguration:
CertificateType: GENERATED
Description: !Sub
- "${FleetDescriptionParameter} Using Unreal Engine Version ${UnrealEngineVersionParameter}${MetricsText}"
- MetricsText: !If [ShouldCreateMetricsResources, " with telemetry metrics enabled", ""]
DesiredEC2Instances: 1
EC2InboundPermissions:
- FromPort: !Ref FleetTcpFromPortParameter
IpRange: "0.0.0.0/0"
Protocol: TCP
ToPort: !Ref FleetTcpToPortParameter
- FromPort: !Ref FleetUdpFromPortParameter
IpRange: "0.0.0.0/0"
Protocol: UDP
ToPort: !Ref FleetUdpToPortParameter
EC2InstanceType: c5.large
FleetType: ON_DEMAND
InstanceRoleARN: !If [ShouldCreateMetricsResources, !GetAtt MetricsInstanceRole.Arn, !Ref "AWS::NoValue"]
InstanceRoleCredentialsProvider: !If [ShouldCreateMetricsResources, "SHARED_CREDENTIAL_FILE", !Ref "AWS::NoValue"]
Name: !Ref FleetNameParameter
NewGameSessionProtectionPolicy: FullProtection
ResourceCreationLimitPolicy:
NewGameSessionsPerCreator: 5
PolicyPeriodInMinutes: 2
RuntimeConfiguration:
GameSessionActivationTimeoutSeconds: 300
MaxConcurrentGameSessionActivations: 1
ServerProcesses:
- ConcurrentExecutions: 1
LaunchPath: !Ref LaunchPathParameter
Parameters: !Ref LaunchParametersParameter
AliasResource:
Type: "AWS::GameLift::Alias"
Properties:
Description: !Sub Alias to access ${GameNameParameter} fleet
Name: !Sub ${GameNameParameter}FleetAlias
RoutingStrategy:
Type: SIMPLE
FleetId: !Ref FleetResource
ResultsRequestLambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Code:
S3Bucket: !Ref LambdaZipS3BucketParameter
S3Key: !Ref LambdaZipS3KeyParameter
Description: Lambda function to handle game requests
Environment:
Variables:
FleetAlias: !Ref AliasResource
FunctionName: !Sub ${GameNameParameter}ResultsRequestLambda
Handler: results_request.handler
MemorySize: 128
Role: !GetAtt ResultsRequestLambdaFunctionExecutionRole.Arn
Runtime: python3.14
GameRequestLambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Code:
S3Bucket: !Ref LambdaZipS3BucketParameter
S3Key: !Ref LambdaZipS3KeyParameter
Description: Lambda function to handle game requests
Environment:
Variables:
FleetAlias: !Ref AliasResource
MaxPlayersPerGame: !Ref MaxPlayersPerGameParameter
FunctionName: !Sub ${GameNameParameter}GameRequestLambda
Handler: game_request.handler
MemorySize: 128
Role: !GetAtt GameRequestLambdaFunctionExecutionRole.Arn
Runtime: python3.14
ResultsRequestLambdaFunctionApiGatewayPermission:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt ResultsRequestLambdaFunction.Arn
Principal: apigateway.amazonaws.com
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/*/*/*"
GameRequestLambdaFunctionApiGatewayPermission:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt GameRequestLambdaFunction.Arn
Principal: apigateway.amazonaws.com
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/*/*/*"
Outputs:
ApiGatewayEndpoint:
Description: Url of ApiGateway Endpoint
Value: !Sub "https://${RestApi}.execute-api.${AWS::Region}.amazonaws.com/${ApiGatewayStageNameParameter}/"
UserPoolClientId:
Description: Id of UserPoolClient
Value: !Ref UserPoolClient
IdentityRegion:
Description: Region name
Value: !Ref "AWS::Region"

View File

@@ -0,0 +1,44 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
---
# THIS IS A SAMPLE CLOUDFORMATION PARAMETERS FILE
GameNameParameter:
value: "{{AWSGAMELIFT::SYS::GAMENAME}}"
LambdaZipS3BucketParameter:
value: "{{AWSGAMELIFT::VARS::LambdaZipS3BucketParameter}}"
LambdaZipS3KeyParameter:
value: "{{AWSGAMELIFT::VARS::LambdaZipS3KeyParameter}}"
ApiGatewayStageNameParameter:
value: "{{AWSGAMELIFT::VARS::ApiGatewayStageNameParameter}}"
BuildS3BucketParameter:
value: "{{AWSGAMELIFT::VARS::BuildS3BucketParameter}}"
BuildS3KeyParameter:
value: "{{AWSGAMELIFT::VARS::BuildS3KeyParameter}}"
LaunchPathParameter:
value: "{{AWSGAMELIFT::VARS::LaunchPathParameter}}"
LaunchParametersParameter:
value: "-port=7777 -UNATTENDED LOG=server.log"
BuildNameParameter:
value: "Sample GameLift Build for {{AWSGAMELIFT::SYS::GAMENAME}}"
BuildVersionParameter:
value: "1"
BuildOperatingSystemParameter:
value: "{{AWSGAMELIFT::VARS::BuildOperatingSystemParameter}}"
BuildServerSdkVersionParameter:
value: "5.4.0"
FleetTcpFromPortParameter:
value: "7770"
FleetTcpToPortParameter:
value: "7780"
FleetUdpFromPortParameter:
value: "7770"
FleetUdpToPortParameter:
value: "7780"
FleetNameParameter:
value: "Single-Region Fleet"
UnrealEngineVersionParameter:
value: "{{AWSGAMELIFT::VARS::UnrealEngineVersionParameter}}"
EnableMetricsParameter:
value: "{{AWSGAMELIFT::VARS::EnableMetricsParameter}}"

View File

@@ -0,0 +1,11 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
---
# Key/value pairs to be added to the game's awsGameLiftClientConfig.yml file
# These values will be replaced at the end of create/update of
# the feature's CloudFormation stack.
user_pool_client_id: "{{AWSGAMELIFT::CFNOUTPUT::UserPoolClientId}}"
identity_api_gateway_base_url: "{{AWSGAMELIFT::CFNOUTPUT::ApiGatewayEndpoint}}"
identity_region: "{{AWSGAMELIFT::CFNOUTPUT::IdentityRegion}}"

View File

@@ -0,0 +1,79 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import boto3
import os
import json
def handler(event, context):
"""
Handles requests to start games from the game client.
This function first looks for any game session
:param event: lambda event, contains the region to player latency mapping in `regionToLatencyMapping` key, as well
as the player information from the Cognito id tokens.
:param context: lambda context, not used by this function
:return:
- 202 (Accepted) if the matchmaking request is accepted and is now being processed
- 409 (Conflict) if the another matchmaking request is in progress
- 500 (Internal Error) if error occurred when processing the matchmaking request
"""
gamelift = boto3.client('gamelift')
fleet_alias = os.environ['FleetAlias']
max_players_per_game = int(os.environ['MaxPlayersPerGame'])
player_id = event["requestContext"]["authorizer"]["claims"]["sub"]
print(f'Handling start game request. PlayerId: {player_id}')
# NOTE: latency mapping is not used in this deployment scenario
region_to_latency_mapping = get_region_to_latency_mapping(event)
if region_to_latency_mapping:
print(f"Region to latency mapping: {region_to_latency_mapping}")
else:
print("No regionToLatencyMapping mapping provided")
if not has_viable_game_sessions(gamelift, fleet_alias):
create_game_session(gamelift, fleet_alias, max_players_per_game)
return {
'headers': {
'Content-Type': 'text/plain'
},
'statusCode': 202
}
def has_viable_game_sessions(gamelift, fleet_alias):
print(f"Checking for viable game sessions: {fleet_alias}")
# NOTE: SortExpression="creationTimeMillis ASC" is not needed because we are looking for any viable game sessions,
# hence the order does not matter.
search_game_sessions_response = gamelift.search_game_sessions(
AliasId=fleet_alias,
FilterExpression="hasAvailablePlayerSessions=true",
)
return len(search_game_sessions_response['GameSessions']) != 0
def create_game_session(gamelift, fleet_alias, max_players_per_game):
print(f"Creating game session: {fleet_alias}")
gamelift.create_game_session(
AliasId=fleet_alias,
MaximumPlayerSessionCount=max_players_per_game,
)
def get_region_to_latency_mapping(event):
request_body = event.get("body")
if not request_body:
return None
try:
request_body_json = json.loads(request_body)
except ValueError:
print(f"Error parsing request body: {request_body}")
return None
if request_body_json and request_body_json.get('regionToLatencyMapping'):
return request_body_json.get('regionToLatencyMapping')

View File

@@ -0,0 +1,73 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import boto3
import os
import json
def handler(event, context):
"""
Handles requests to describe the game session connection information after a StartGame request.
This function will look up the MatchmakingRequest table to find a pending matchmaking request by
the player, and if it is QUEUED, look up the GameSessionPlacement table to find the game's
connection information.
:param event: lambda event, contains the region to player latency mapping in `regionToLatencyMapping` key, as well
as the player information from the Cognito id tokens. Cognito provides a `sub` user attribute that we use as a
Player ID. Unlike `username` value `sub` is UUID for a user which is never reassigned to another user.
:param context: lambda context, not used by this function
:return:
- 200 (OK) if the game connection is ready, along with server info: "IpAddress", "Port", "DnsName", "PlayerSessionId", "PlayerId"
- 204 (No Content) if the requested game is still in progress of matchmaking
- 404 (Not Found) if no game has been started by the player, or if all started game were expired
- 500 (Internal Error) if errors occurred during matchmaking or placement
"""
gamelift = boto3.client('gamelift')
fleet_alias = os.environ['FleetAlias']
player_id = event["requestContext"]["authorizer"]["claims"]["sub"]
print(f'Handling request result request. PlayerId: {player_id}')
oldest_viable_game_session = get_oldest_viable_game_session(gamelift, fleet_alias)
if oldest_viable_game_session:
player_session = create_player_session(gamelift, oldest_viable_game_session['GameSessionId'], player_id)
game_session_connection_info = dict((k, player_session[k]) for k in ('IpAddress', 'Port', 'DnsName', 'PlayerSessionId', 'PlayerId'))
game_session_connection_info['GameSessionArn'] = player_session['GameSessionId']
print(f"Connection info: {game_session_connection_info}")
return {
'body': json.dumps(game_session_connection_info),
'headers': {
'Content-Type': 'text/plain'
},
'statusCode': 200
}
else:
return {
'headers': {
'Content-Type': 'text/plain'
},
'statusCode': 404
}
def get_oldest_viable_game_session(gamelift, fleet_alias):
print("Checking for viable game sessions:", fleet_alias)
search_game_sessions_response = gamelift.search_game_sessions(
AliasId=fleet_alias,
FilterExpression="hasAvailablePlayerSessions=true",
SortExpression="creationTimeMillis ASC",
)
print(f"Received search game session response: {search_game_sessions_response}")
return next(iter(search_game_sessions_response['GameSessions']), None)
def create_player_session(gamelift, game_session_id, player_id):
print(f"Creating PlayerSession session on GameSession: {game_session_id}, PlayerId: {player_id}")
create_player_session_response = gamelift.create_player_session(
GameSessionId=game_session_id,
PlayerId=player_id
)
print(f"Received create player session response: {create_player_session_response}")
return create_player_session_response['PlayerSession']

View File

@@ -0,0 +1,43 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
#
# GameLift Region Mappings
# Keep synchronized with: https://docs.aws.amazon.com/general/latest/gr/gamelift.html
#
# ----------------------------------------------------------------------------------------
# Region Name Region Endpoint Protocol
# ----------------------------------------------------------------------------------------
# US East (Ohio) us-east-2 gamelift.us-east-2.amazonaws.com HTTPS
# US East (N. Virginia) us-east-1 gamelift.us-east-1.amazonaws.com HTTPS
# US West (N. California) us-west-1 gamelift.us-west-1.amazonaws.com HTTPS
# US West (Oregon) us-west-2 gamelift.us-west-2.amazonaws.com HTTPS
# Asia Pacific (Mumbai) ap-south-1 gamelift.ap-south-1.amazonaws.com HTTPS
# Asia Pacific (Seoul) ap-northeast-2 gamelift.ap-northeast-2.amazonaws.com HTTPS
# Asia Pacific (Singapore) ap-southeast-1 gamelift.ap-southeast-1.amazonaws.com HTTPS
# Asia Pacific (Sydney) ap-southeast-2 gamelift.ap-southeast-2.amazonaws.com HTTPS
# Asia Pacific (Tokyo) ap-northeast-1 gamelift.ap-northeast-1.amazonaws.com HTTPS
# Canada (Central) ca-central-1 gamelift.ca-central-1.amazonaws.com HTTPS
# Europe (Frankfurt) eu-central-1 gamelift.eu-central-1.amazonaws.com HTTPS
# Europe (Ireland) eu-west-1 gamelift.eu-west-1.amazonaws.com HTTPS
# Europe (London) eu-west-2 gamelift.eu-west-2.amazonaws.com HTTPS
# South America (São Paulo) sa-east-1 gamelift.sa-east-1.amazonaws.com HTTPS
# ----------------------------------------------------------------------------------------
{
five_letter_region_codes: {
us-east-2 : usea2,
us-east-1 : usea1,
us-west-1 : uswe1,
us-west-2 : uswe2,
ap-south-1 : apso1,
ap-northeast-2 : apne2,
ap-southeast-1 : apse1,
ap-southeast-2 : apse2,
ap-northeast-1 : apne1,
ca-central-1 : cace1,
eu-central-1 : euce1,
eu-west-1 : euwe1,
eu-west-2 : euwe2,
sa-east-1 : saea1
}
}

View File

@@ -0,0 +1,161 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import boto3
import requests
import json
import time
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-g", "--game", help="game name", type=str, required=True)
parser.add_argument("-r", "--region", help="region name, e.g. eu-west-1", type=str, required=True)
parser.add_argument("-p", "--profile", help="profile name in the AWS shared credentials file ~/.aws/credentials", type=str, required=True)
args = parser.parse_args()
GAME_NAME = args.game.lower() # e.g. 'GameLiftSampleGame2ue4'
REGION = args.region # e.g. 'eu-west-1'
PROFILE_NAME = args.profile
USER_POOL_NAME = GAME_NAME + 'UserPool'
USER_POOL_CLIENT_NAME = GAME_NAME + 'UserPoolClient'
USERNAME = 'testuser@example.com'
PASSWORD = 'TestPassw0rd.'
REST_API_NAME = GAME_NAME + 'RestApi'
REST_API_STAGE = 'dev'
GAME_REQUEST_PATH = 'start_game'
RESULTS_REQUEST_PATH = 'get_game_connection'
session = boto3.Session(profile_name=PROFILE_NAME)
cognito_idp = session.client('cognito-idp', region_name=REGION)
apig = session.client('apigateway', region_name=REGION)
REGION_TO_LATENCY_MAPPING = {
"regionToLatencyMapping": {
"us-west-2": 50,
"us-east-1": 100,
"eu-west-1": 150,
"ap-northeast-1": 300
}
}
GAME_REQUEST_PAYLOAD = json.dumps(REGION_TO_LATENCY_MAPPING)
def main():
user_pool = find_user_pool(USER_POOL_NAME)
user_pool_id = user_pool['Id']
print("User Pool Id:", user_pool_id)
user_pool_client = find_user_pool_client(user_pool_id, USER_POOL_CLIENT_NAME)
user_pool_client_id = user_pool_client['ClientId']
print("User Pool Client Id:", user_pool_client_id)
try:
cognito_idp.sign_up(
ClientId=user_pool_client_id,
Username=USERNAME,
Password=PASSWORD,
)
print("Created user:", USERNAME)
cognito_idp.admin_confirm_sign_up(
UserPoolId=user_pool_id,
Username=USERNAME,
)
init_auth_result = cognito_idp.initiate_auth(
AuthFlow='USER_PASSWORD_AUTH',
AuthParameters={
'USERNAME': USERNAME,
'PASSWORD': PASSWORD,
},
ClientId=user_pool_client_id
)
assert init_auth_result['ResponseMetadata']['HTTPStatusCode'] == 200, "Unsuccessful init_auth"
print("Authenticated via username and password")
id_token = init_auth_result['AuthenticationResult']['IdToken']
headers = {
'Auth': id_token
}
results_request_url = get_rest_api_endpoint(REST_API_NAME, REGION, REST_API_STAGE, RESULTS_REQUEST_PATH)
game_request_url = get_rest_api_endpoint(REST_API_NAME, REGION, REST_API_STAGE, GAME_REQUEST_PATH)
print ("results_request_url: " + results_request_url)
print ("game_request_url: " + game_request_url)
results_request_response = requests.post(url=results_request_url, headers=headers)
assert results_request_response.status_code == 204 or results_request_response.status_code == 200, \
"Expect 'POST /get_game_info' status code to be 200 (Success) or 204 (No Content). Actual: " \
f"{str(results_request_response.status_code)}"
print("Verified mock ResultsRequest response", results_request_response)
game_request_response = requests.post(url=game_request_url, headers=headers, data=GAME_REQUEST_PAYLOAD)
assert game_request_response.status_code == 202, "Expect 'POST /start_game' status code to be 202 (Accepted)/ Actual: " \
f"{str(results_request_response.status_code)}"
print("Verified lambda GameRequest response", game_request_response)
#game_request_info = json.loads(game_request_response.content)
print(f"Received start game info: {game_request_response}")
print("Waiting for game session to be created...")
time.sleep(10) # 10 seconds
results_request_response = requests.post(url=results_request_url, headers=headers)
assert results_request_response.status_code == 200, "Expect 'POST /get_game_info' status code to be 200 (Success). Actual: " \
f"{str(results_request_response.status_code)}"
print("Verified lambda ResultsRequest response", results_request_response.content)
game_connection_info = json.loads(results_request_response.content)
print(f"Received game connection info: {game_connection_info}")
assert game_connection_info['IpAddress'] != ''
assert game_connection_info['Port'] > 0
assert REGION in game_connection_info['DnsName'], \
f"Expect {game_connection_info['DnsName']} to contain '{REGION}'"
assert "psess-" in game_connection_info['PlayerSessionId'], \
f"Expect {game_connection_info['PlayerSessionId']} to contain 'psess-'"
assert REGION in game_connection_info['GameSessionArn'], \
f"Expect {game_connection_info['GameSessionArn']} to contain '{REGION}'"
print("Verified game connection info:", game_connection_info)
finally:
cognito_idp.admin_delete_user(
UserPoolId=user_pool_id,
Username=USERNAME,
)
print("Deleted user:", USERNAME)
print("Test Succeeded!")
def find_user_pool(user_pool_name):
print("Finding user pool:", user_pool_name)
result = cognito_idp.list_user_pools(MaxResults=50)
pools = result['UserPools']
return next(x for x in pools if x['Name'] == user_pool_name)
def find_user_pool_client(user_pool_id, user_pool_client_name):
print("Finding user pool client:", user_pool_client_name)
results = cognito_idp.list_user_pool_clients(UserPoolId=user_pool_id)
clients = results['UserPoolClients']
return next(x for x in clients if x['ClientName'] == user_pool_client_name)
def find_rest_api(rest_api_name):
print("Finding rest api:", rest_api_name)
results = apig.get_rest_apis()
rest_apis = results['items']
return next(x for x in rest_apis if x['name'] == rest_api_name)
def get_rest_api_endpoint(rest_api_name, region, stage, path):
print("Getting rest api endpoint", rest_api_name)
rest_api = find_rest_api(rest_api_name)
rest_api_id = rest_api['id']
return f'https://{rest_api_id}.execute-api.{region}.amazonaws.com/{stage}/{path}'
if __name__ == '__main__':
main()