GS2-Grade
A common specification for character and equipment development is to provide grade-up in addition to level-up as a short-term growth goal. Grading up can raise the level cap and make it possible to develop characters and equipment more powerfully.
There are several possible methods of upgrading: some can be done by consuming growth materials, while others involve synthesizing characters or equipment of the same kind. GS2-Grade does not care about the method. Its purpose is to make the operation of GS2-Experience’s level cap simpler to implement by setting a level cap for each grade.
graph LR Player["Player's possessions"] -- Grade Up --> Grade["GS2-Grade<br/>(grade value increases)"] Grade -- ApplyRankCap --> Experience["GS2-Experience<br/>(rank cap is reflected)"] Experience -- Gain experience points --> LevelUp["Level Up"]
Grades
For each grade, you can define the value to be set as the rank cap of GS2-Experience.
Grades are managed for each propertyId that uniquely points to a target such as a character or piece of equipment, and are held individually as Status.
| Field | Description |
|---|---|
gradeName |
Grade model name |
propertyId |
The GRN indicating the resource to which the grade is applied (e.g., a GS2-Inventory ItemSet) |
gradeValue |
The current grade value (integer starting from 0) |
Initial values for a grade
You can set the initial grade value depending on whether the property ID matches a regular expression. For example, by starting at grade 3 when the GS2-Inventory ItemModel name begins with SSR, and at grade 2 when it begins with SR, you can change the initial rank cap depending on the type of ItemModel.
You declare initial grades by registering multiple pairs of propertyIdRegex and defaultGradeValue in the master data’s defaultGrades.
Grade-up material determination
In the case of “synthesizing the same kind of character or equipment” as a method of upgrading, a feature is provided to support determining whether the resource being used as material is “the same kind of character or equipment.” This is achieved by using a regular expression to extract parameters from the property ID of the grade, constructing a regular expression, and determining whether the property ID of the resource to be used as material matches.
For example, to determine if an ItemSet in GS2-Inventory is from the same kind of ItemModel, set the regular expression as follows:
propertyIdRegex: grn:gs2:{region}:{ownerId}:inventory:namespace-0001:user:(.*):inventory:character:item:(.*):.*
gradeUpPropertyIdRegex: grn:gs2:{region}:{ownerId}:inventory:namespace-0001:user:$1:inventory:character:item:$2:.*Now use propertyIdRegex to retrieve the “user ID” and the “ItemModel name”. If the user ID is “user-0001”, the name of the ItemModel is “item-0001”, and the name of the ItemSet is “item-set-0001”, then the GRN (property ID) of the ItemSet is:
grn:gs2:{region}:{ownerId}:inventory:namespace-0001:user:user-0001:inventory:character:item:item-0001:item-set-0001and the replaced value of gradeUpPropertyIdRegex becomes:
gradeUpPropertyIdRegex: grn:gs2:{region}:{ownerId}:inventory:namespace-0001:user:user-0001:inventory:character:item:item-0001:.*This allows you to express the ability to specify items of the same kind as material.
Reward multiplier table
The grade model can have acquireActionRates configured to adjust the reward amounts of acquire actions according to the grade.
Using this, you can incorporate growth elements such as “higher-grade characters earn more from quest rewards” or “the experience-point multiplier changes per grade”.
mode |
Description |
|---|---|
double |
Multiplies by the multiplier set in rates as a floating-point value. Used for standard corrections of item acquisition amounts. |
big |
Treats the multiplier set in bigRates as a string value and supports cases dealing with massive rewards such as BigItem of GS2-Inventory, which can handle numbers exceeding int64. |
By incorporating Gs2Grade:MultiplyAcquireActionsByUserId into the acquire actions of a transaction, you can convert the actions into a list of acquire actions with the multiplier of the specified grade applied.
Script Triggers
Setting changeGradeScript in the namespace allows custom scripts to be executed before and after grade changes.
Triggers support both synchronous and asynchronous execution. Asynchronous processing also supports external integration through GS2-Script or Amazon EventBridge.
The main event triggers and script setting names that can be configured are as follows:
changeGradeScript(completion notification:changeGradeDone): before and after grade changes
Transaction Actions
GS2-Grade provides the following transaction actions:
| Type | Action | Description |
|---|---|---|
| Verify | Gs2Grade:VerifyGradeByUserId |
Verify the grade value is the specified value |
| Verify | Gs2Grade:VerifyGradeUpMaterialByUserId |
Verify whether a resource is valid as a grade-up material (matches gradeUpPropertyIdRegex) |
| Consume | Gs2Grade:SubGradeByUserId |
Subtract from the grade |
| Acquire | Gs2Grade:AddGradeByUserId |
Add to the grade |
| Acquire | Gs2Grade:AddRankCapByUserId |
Reflect the rank cap corresponding to the grade into GS2-Experience |
| Acquire | Gs2Grade:MultiplyAcquireActionsByUserId |
Convert into acquire actions with the multiplier from acquireActionRates applied according to the grade |
By using “Verify grade-up material” as a verify action, you can validate within a transaction whether the material being used for synthesis (another item or character) is actually possessed. This prevents grade-ups from being performed with illegitimate material specifications.
Managing master data
By registering master data, you can configure the data and behavior available to the microservice.
The types of master data are as follows:
GradeModel: rank caps and reward amounts per grade
Below is an example JSON of master data.
{
"version": "2022-06-01",
"gradeModels": [
{
"name": "grade-0001",
"metadata": "Character Grade",
"experienceModelId": "grn:gs2:{region}:{ownerId}:experience:namespace-0001:model:experienceModel-0001",
"defaultGrades": [
{
"propertyIdRegex": ".*:item:SSR.*",
"defaultGradeValue": 3
},
{
"propertyIdRegex": ".*:item:SR.*",
"defaultGradeValue": 2
}
],
"gradeEntries": [
{ "metadata": "Grade 0", "rankCapValue": 30 },
{ "metadata": "Grade 1", "rankCapValue": 40 },
{ "metadata": "Grade 2", "rankCapValue": 50 },
{ "metadata": "Grade 3", "rankCapValue": 60 }
],
"acquireActionRates": [
{
"name": "experience",
"mode": "double",
"rates": [1.0, 1.2, 1.5, 2.0]
}
]
}
]
}Master data can be registered from the Management Console, or you can set up a workflow that reflects data from GitHub or registers via CI using GS2-Deploy.
Example implementation
Grade addition
Grade addition cannot be processed by the SDK for game engines.
Incorporate the transaction action Gs2Grade:AddGradeByUserId into the consumption of enhancement materials in GS2-Exchange, or as an enhancement reward in GS2-Enhance, to add to a grade.
This lets you implement growth elements while keeping server-side consistency and preventing grade-ups from being triggered by unauthorized client operations.
Get a list of grades
var items = await gs2.Grade.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).StatusesAsync(
gradeName: "grade-0001"
).ToListAsync(); const auto It = Gs2->Grade->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->Statuses(
"grade-0001" // gradeName
);
TArray<Gs2::UE5::Grade::Model::FEzStatusPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}Get the grade
var item = await gs2.Grade.Namespace(
namespaceName: "namespace-0001"
).GradeModel(
gradeName: "grade-0001"
).ModelAsync(); const auto Future = Gs2->Grade->Namespace(
"namespace-0001" // namespaceName
)->GradeModel(
"grade-0001" // gradeName
)->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}Get an individual grade (Status)
Get the current grade value for a specific propertyId (character, equipment, etc.).
var item = await gs2.Grade.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
gradeName: "grade-0001",
propertyId: "property-0001"
).ModelAsync(); const auto Future = Gs2->Grade->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->Status(
"grade-0001", // gradeName
"property-0001" // propertyId
)->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}Synchronize rank caps in GS2-Experience
When a grade changes, the rank cap reflection in GS2-Experience is performed automatically. However, when the grade model is updated and the rank cap value for a specific grade is changed, you can apply the updated rank cap by explicitly calling the following synchronization API.
var result = await gs2.Grade.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
gradeName: "grade-0001",
propertyId: "property-0001"
).ApplyRankCapAsync(
);
var item = await result.ModelAsync(); const auto Future = Gs2->Grade->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->Status(
"grade-0001", // gradeName
"property-0001" // propertyId
)->ApplyRankCap(
);
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 Future2->GetTask().Error();
}
const auto Result = Future2->GetTask().Result();