Skip to content

Commit

Permalink
feat(gamelift): add MatchmakingRuleSet L2 Construct for GameLift (aws…
Browse files Browse the repository at this point in the history
…#23091)

Following aws/aws-cdk-rfcs#436 I have written the Gamelift MatchmakingRuleSet L2 resource which create an MatchmakingRuleSet resource.

----

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features

* [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
stevehouel authored and Brennan Ho committed Jan 20, 2023
1 parent 1d2f4cd commit 1e15d7d
Show file tree
Hide file tree
Showing 16 changed files with 1,109 additions and 2 deletions.
71 changes: 69 additions & 2 deletions packages/@aws-cdk/aws-gamelift/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,73 @@ deliver inexpensive, resilient game hosting for your players
This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. It allows you to define components for your matchmaking
configuration or game server fleet management system.

## GameLift FlexMatch

### Matchmaking RuleSet

Every FlexMatch matchmaker must have a rule set. The rule set determines the
two key elements of a match: your game's team structure and size, and how to
group players together for the best possible match.

For example, a rule set might describe a match like this: Create a match with
two teams of four to eight players each, one team is the cowboy and the other
team the aliens. A team can have novice and experienced players, but the
average skill of the two teams must be within 10 points of each other. If no
match is made after 30 seconds, gradually relax the skill requirements.

```ts
new gamelift.MatchmakingRuleSet(this, 'RuleSet', {
matchmakingRuleSetName: 'my-test-ruleset',
content: gamelift.RuleSetContent.fromJsonFile(path.join(__dirname, 'my-ruleset/ruleset.json')),
});
```

### FlexMatch Monitoring

You can monitor GameLift FlexMatch activity for matchmaking configurations and
matchmaking rules using Amazon CloudWatch. These statistics are used to provide
a historical perspective on how your Gamelift FlexMatch solution is performing.

#### FlexMatch Metrics

GameLift FlexMatch sends metrics to CloudWatch so that you can collect and
analyze the activity of your matchmaking solution, including match acceptance
workflow, ticket consumtion.

You can then use CloudWatch alarms to alert you, for example, when matches has
been rejected (potential matches that were rejected by at least one player
since the last report) exceed a certain thresold which could means that you may
have an issue in your matchmaking rules.

CDK provides methods for accessing GameLift FlexMatch metrics with default configuration,
such as `metricRuleEvaluationsPassed`, or `metricRuleEvaluationsFailed` (see
[`IMatchmakingRuleSet`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-gamelift.IMatchmakingRuleSet.html)
for a full list). CDK also provides a generic `metric` method that can be used
to produce metric configurations for any metric provided by GameLift FlexMatch;
the configurations are pre-populated with the correct dimensions for the
matchmaking configuration.

```ts
declare const matchmakingRuleSet: gamelift.MatchmakingRuleSet;
// Alarm that triggers when the per-second average of not placed matches exceed 10%
const ruleEvaluationRatio = new cloudwatch.MathExpression({
expression: '1 - (ruleEvaluationsPassed / ruleEvaluationsFailed)',
usingMetrics: {
ruleEvaluationsPassed: matchmakingRuleSet.metricRuleEvaluationsPassed({ statistic: cloudwatch.Statistic.SUM }),
ruleEvaluationsFailed: matchmakingRuleSet.metric('ruleEvaluationsFailed'),
},
});
new cloudwatch.Alarm(this, 'Alarm', {
metric: ruleEvaluationRatio,
threshold: 0.1,
evaluationPeriods: 3,
});
```

See: [Monitoring Using CloudWatch Metrics](https://docs.aws.amazon.com/gamelift/latest/developerguide/monitoring-cloudwatch.html)
in the *Amazon GameLift Developer Guide*.


## GameLift Hosting

### Uploading builds and scripts to GameLift
Expand Down Expand Up @@ -344,7 +411,7 @@ in the *Amazon GameLift Developer Guide*.
GameLift is integrated with CloudWatch, so you can monitor the performance of
your game servers via logs and metrics.

#### Metrics
#### Fleet Metrics

GameLift Fleet sends metrics to CloudWatch so that you can collect and analyze
the activity of your Fleet, including game and player sessions and server
Expand Down Expand Up @@ -517,7 +584,7 @@ new gamelift.GameServerGroup(this, 'GameServerGroup', {
});
```

### Monitoring
### FleetIQ Monitoring

GameLift FleetIQ sends metrics to CloudWatch so that you can collect and
analyze the activity of your Game server fleet, including the number of
Expand Down
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-gamelift/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export * from './game-server-group';
export * from './ingress-rule';
export * from './fleet-base';
export * from './build-fleet';
export * from './matchmaking-ruleset';
export * from './matchmaking-ruleset-body';

// AWS::GameLift CloudFormation Resources:
export * from './gamelift.generated';
119 changes: 119 additions & 0 deletions packages/@aws-cdk/aws-gamelift/lib/matchmaking-ruleset-body.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import * as fs from 'fs';
import { Construct } from 'constructs';

/**
* Interface to represent Matchmaking RuleSet schema
*/
export interface IRuleSetBody {}

/**
* Interface to represent output result of a RuleSetContent binding
*/
export interface RuleSetBodyConfig {
/**
* Inline ruleSet body.
*/
readonly ruleSetBody: string;
}

/**
* Interface to represent a Matchmaking RuleSet content
*/
export interface IRuleSetContent {

/**
* RuleSet body content
*
* @attribute
*/
readonly content: IRuleSetBody;

/**
* Called when the matchmaking ruleSet is initialized to allow this object to bind
* to the stack and add resources.
*
* @param _scope The binding scope.
*/
bind(_scope: Construct): RuleSetBodyConfig;
}

/**
* Properties for a new matchmaking ruleSet content
*/
export interface RuleSetContentProps {

/**
* RuleSet body content
*
* @default use a default empty RuleSet body
*/
readonly content?: IRuleSetBody;
}

/**
* The rule set determines the two key elements of a match: your game's team structure and size, and how to group players together for the best possible match.
*
* For example, a rule set might describe a match like this:
* - Create a match with two teams of five players each, one team is the defenders and the other team the invaders.
* - A team can have novice and experienced players, but the average skill of the two teams must be within 10 points of each other.
* - If no match is made after 30 seconds, gradually relax the skill requirements.
*/
export class RuleSetContent implements IRuleSetContent {

/**
* Matchmaking ruleSet body from a file
* @returns `RuleSetContentBase` based on JSON file content.
* @param path The path to the ruleSet body file
*/
public static fromJsonFile(path: string): IRuleSetContent {
if (!fs.existsSync(path)) {
throw new Error(`RuleSet path does not exist, please verify it, actual ${path}`);
}

if (!fs.lstatSync(path).isFile()) {
throw new Error(`RuleSet path is not link to a single file, please verify your path, actual ${path}`);
}
const file = fs.readFileSync(path);

return this.fromInline(file.toString());
}

/**
* Inline body for Matchmaking ruleSet
* @returns `RuleSetContent` with inline code.
* @param body The actual ruleSet body (maximum 65535 characters)
*/
public static fromInline(body: string): IRuleSetContent {
if (body && body.length > 65535) {
throw new Error(`RuleSet body cannot exceed 65535 characters, actual ${body.length}`);
}
try {
return new RuleSetContent({
content: JSON.parse(body),
});
} catch (err) {
throw new Error('RuleSet body has an invalid Json format');
}
}

/**
* RuleSet body content
*/
public readonly content: IRuleSetBody;

constructor(props: RuleSetContentProps) {
this.content = props.content || {};
}

/**
* Called when the matchmaking ruleSet is initialized to allow this object to bind
* to the stack and add resources.
*
* @param _scope The binding scope.
*/
public bind(_scope: Construct): RuleSetBodyConfig {
return {
ruleSetBody: JSON.stringify(this.content),
};
}
}
Loading

0 comments on commit 1e15d7d

Please sign in to comment.