GS2-Experience
In Game as a Service, growth elements are indispensable. GS2-Experience provides experience and rank features for implementing a game cycle in which players grow their characters and use those grown characters to take on more challenging content.
GS2-Experience has a flexible design that lets you centrally manage not only the level of a single character but also the growth of multiple properties (characters, weapons, parties, guilds, etc.) within a single microservice.
Use cases
Typical use cases for GS2-Experience are:
- Managing the level of player characters
- Managing the level of trainable equipment
- Managing the rank of guilds or teams
- Managing the level of titles and battle passes
- Managing the proficiency of each skill in a skill tree
graph LR Player[Player] -->|Add experience| Exp[GS2-Experience] Exp -->|Compute rank| Status[Status] Status -->|Threshold exceeded| RankUp[Rank up] RankUp -->|Grant reward| Inventory[GS2-Inventory] RankUp -->|Update stamina max| Stamina[GS2-Stamina]
Rank
GS2-Experience automatically calculates rank from the experience value. To do so, you must use master data to define a table of experience values that serve as rank-up thresholds. An Experience Model manages rank-up thresholds, and property IDs with arbitrary values can be hung beneath it. The experience value is managed per property ID, and the rank is determined based on the experience value.
Relationship between Experience Models and property IDs
graph TD N[Namespace] --> EM1["ExperienceModel<br/>character_ssr"] N --> EM2["ExperienceModel<br/>character_sr"] N --> EM3["ExperienceModel<br/>weapon"] EM1 --> P11["propertyId:<br/>character-001"] EM1 --> P12["propertyId:<br/>character-002"] EM3 --> P31["propertyId:<br/>weapon-001"] EM3 --> P32["propertyId:<br/>weapon-002"]
For example, cases such as “I want to change the required experience table per character type” or “I want to set different rank caps per individual character” can be addressed by preparing an ExperienceModel per character type and assigning each character’s individual ID as the property ID.
Rank Cap
A rank can have an upper limit. If experience is gained while at the rank cap, that experience is discarded.
The initial rank cap is set in the Experience Model, but can be raised individually per property ID. This makes it possible to implement specifications such as raising the rank cap when performing a limit break.
| Setting | Description |
|---|---|
defaultExperience |
Initial experience |
defaultRankCap |
Initial rank cap |
maxRankCap |
Upper limit of the rank cap (SetRankCap exceeding this is rejected) |
rankThreshold |
Array of experience thresholds required for rank-ups |
Status information
A Status is uniquely determined by the combination of user × ExperienceModel × propertyId, and holds the following information:
| Attribute | Description |
|---|---|
experienceValue |
Current experience |
rankValue |
Current rank |
rankCapValue |
Current rank cap |
nextRankUpExperienceValue |
Experience required for the next rank-up |
By referencing nextRankUpExperienceValue on the client, you can easily display the experience required until the next rank-up or draw a progress bar.
Reward addition table
The Experience Model allows you to configure acquireActionRates to adjust reward amounts based on rank.
In addition to the standard rates, bigRates, which can handle values exceeding int64, can also be defined, enabling precise reward control even in games with severe inflation.
This is useful for adjustments such as “grant more high-difficulty quest rewards to higher-rank players”. When reflecting a rank-based multiplier in an acquire action of a quest reward, incorporate “apply rank-based reward multiplier” as a transaction action in the clear reward of GS2-Quest.
Script Triggers
By setting various scripts on the namespace, you can invoke custom scripts before and after experience changes, rank changes, and rank cap changes. Scripts can be executed synchronously or asynchronously, with asynchronous execution also supporting external processing via GS2-Script or Amazon EventBridge.
Main event triggers and script setting names are:
changeExperienceScript(completion notification:changeExperienceDone): before and after experience changeschangeRankScript(completion notification:changeRankDone): before and after rank changeschangeRankCapScript(completion notification:changeRankCapDone): before and after rank cap changes
In addition, the following fields can take a script GRN directly, and the corresponding script is invoked when its event occurs.
rankCapScriptId: handling on rank cap acquisitionoverflowExperienceScript: handling of surplus experience (overflow experience) after reaching the rank cap
Using overflowExperienceScript, you can automate handling such as converting surplus experience after the rank cap is reached into in-game currency stored in GS2-Inventory.
Master data operations
Registering master data allows you to configure data and behavior that can be used by the microservice.
Available master data types:
ExperienceModel: Rank thresholds and reward addition table
Master data can be registered via the management console, imported from GitHub, or registered from CI using GS2-Deploy or various language CDKs.
JSON example of ExperienceModel
{
"version": "2019-09-04",
"experienceModels": [
{
"name": "character_ssr",
"metadata": "SSR character",
"defaultExperience": 0,
"defaultRankCap": 50,
"maxRankCap": 99,
"rankThreshold": {
"metadata": "SSR rank table",
"values": [
100, 300, 600, 1000, 1500
]
},
"acquireActionRates": [
{
"name": "boost",
"mode": "double",
"rates": [1.0, 1.1, 1.2, 1.3, 1.4]
}
]
}
]
}Correction by Buff
When integrated with GS2-Buff, buffs can adjust the rankCapValue of a Status and the experienceValue of actions such as AddExperienceByUserId and SubExperienceByUserId. This enables flexible adjustments such as temporarily increasing or decreasing acquisition rates or rank caps during events or campaigns.
Typical buff targets are as follows:
| Type | Target | Adjustable value |
|---|---|---|
| Model | Status | rankCapValue |
| Action | AddExperienceByUserId | experienceValue |
| Action | SubExperienceByUserId | experienceValue |
| Action | AddRankCapByUserId | rankCapValue |
Transaction Actions
GS2-Experience provides the following transaction actions:
- Verify actions: Verify rank, verify rank cap
- Consume actions: Subtract experience, subtract rank cap
- Acquire actions: Add experience, set experience, add rank cap, set rank cap, apply rank-based reward multiplier
By using “Add experience” as an acquire action, it is possible to perform processes such as directly increasing a character’s experience and ranking it up as a reward for purchasing an item in a shop or for completing a mission. Also, by setting “Add rank cap” as a reward, you can automatically unlock the level cap upon meeting specific conditions.
By using “Verify rank” as a verify action, game specifications such as “a quest that can only be attempted by characters of rank 10 or higher” or “an enhancement material exchangeable only at rank 50 or higher” can be implemented purely through master data configuration.
Implementation Example
Adding experience
Adding experience cannot be handled by the SDK for game engines.
Add experience as a clear reward of a GS2-Quest quest or as an enhancement reward of GS2-Enhance.
Raising the rank cap
Raising the rank cap cannot be handled by the SDK for game engines.
Use GS2-Exchange to raise the rank cap as a reward for exchanging enhancement materials or duplicate copies of the same character.
Get a list of experience values
Retrieves all Status the player holds at once. Useful on home screens or character list screens to display status.
var items = await gs2.Experience.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).StatusesAsync(
).ToListAsync(); const auto Domain = Gs2->Experience->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
);
const auto It = Domain->Statuses(
);
TArray<Gs2::UE5::Experience::Model::FEzStatusPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}Get an experience value
var item = await gs2.Experience.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
experienceName: "character_ssr",
propertyId: "property-0001"
).ModelAsync(); const auto Domain = Gs2->Experience->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Status(
"character_ssr", // experienceName
"property-0001" // propertyId
);
const auto Future = Domain->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Item = Future->GetTask().Result();Get a list of ExperienceModels
Used when the client needs to reference the experience tables required for rank-ups.
var items = await gs2.Experience.Namespace(
namespaceName: "namespace-0001"
).ExperienceModelsAsync(
).ToListAsync(); const auto It = Gs2->Experience->Namespace(
"namespace-0001" // namespaceName
)->ExperienceModels();
TArray<Gs2::UE5::Experience::Model::FEzExperienceModelPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}Get a specific ExperienceModel
var item = await gs2.Experience.Namespace(
namespaceName: "namespace-0001"
).ExperienceModel(
experienceName: "character_ssr"
).ModelAsync(); const auto Domain = Gs2->Experience->Namespace(
"namespace-0001" // namespaceName
)->ExperienceModel(
"character_ssr" // experienceName
);
const auto Future = Domain->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Item = Future->GetTask().Result();Obtaining a possession-proof signature
When linking with other microservices within GS2, you may be required to provide data that guarantees the rank or experience in GS2-Experience is really correct.
For example, suppose GS2-Experience manages a player’s rank and GS2-Stamina determines the maximum stamina value based on that rank. GS2-Stamina requires that the rank be specified together with a possession-proof signature.
This way, GS2-Stamina does not need to communicate with GS2-Experience behind the scenes to determine whether the player has really reached that rank.
sequenceDiagram participant Player as Player participant Exp as GS2-Experience participant Other as Other Microservice Player->>Exp: GetStatusWithSignature Exp-->>Player: Body + Signature Player->>Other: API + Body + Signature Other->>Other: Verify signature Other-->>Player: Result
var result = await gs2.Experience.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
experienceName: "character_ssr",
propertyId: "property-0001"
).GetStatusWithSignatureAsync(
keyId: "grn:gs2:{region}:{yourOwnerId}:key:namespace-0001:key:key-0001"
);
var body = result.Body;
var signature = result.Signature; const auto Domain = Gs2->Experience->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Status(
"character_ssr", // experienceName
"property-0001" // propertyId
);
const auto Future = Domain->GetStatusWithSignature(
"grn:gs2:{region}:{yourOwnerId}:key:namespace-0001:key:key-0001"
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
// obtain changed values / result values
const auto Future2 = Future->GetTask().Result()->Model();
Future2->StartSynchronousTask();
if (Future2->GetTask().IsError()) return false;
const auto Result = Future2->GetTask().Result();
const auto Body = Result->Body;
const auto Signature = Result->Signature;