GS2-Formation
It is a common specification to combine the resources you own into a single composition. This is the case when organizing multiple characters into a party, or organizing items such as weapons and armor as an equipment set.
GS2-Formation is a microservice that defines “what slots exist”, “what can be attached to each slot”, and “how many of the same type of formation can be held” as master data, and manages each player’s formation contents.
Terminology
graph LR MoldModel --> FormModel FormModel --> SlotModel PropertyFormModel --> SlotModel2[SlotModel] Mold --> Form Form --> Slot PropertyForm --> Slot2[Slot]
| Term | Description |
|---|---|
MoldModel |
A type for holding multiple instances of the same kind of formation (parties, equipment sets, etc.). It has the concept of capacity (storage slots) |
FormModel |
The configuration definition of a Form associated with a Mold. Defines what slots it has |
PropertyFormModel |
A standalone formation definition referenced by property ID. Used in a form where one character has one formation, without going through Mold |
SlotModel |
Slot definition. The properties that can be attached to a slot can be restricted with a regular expression |
Mold |
The Mold instance per player. Has a capacity and can create Forms up to the maximum capacity |
Form |
A formation instance within a Mold. Identified by index |
PropertyForm |
A formation instance per property ID |
Slot |
Holds the resource actually attached by the player |
Forms
To implement character equipment functionality, you can prepare multiple slots such as weapons, helmets, gauntlets, torsos, legs, and feet, and configure settings so that only items compatible with each slot can be equipped.
In Form Model, the types of slots that exist and the types of items that can be organized into each slot are defined as master data.
Choosing between Mold and PropertyForm
flowchart LR
subgraph Mold
M1["Form #0"]
M2["Form #1"]
M3["Form #2 (empty)"]
end
subgraph PropertyForm
P1["Formation of character-001"]
P2["Formation of character-002"]
end
- Use
Moldwhen: you want to manage a set of formations (such as Party 1 to N) by number (index) and expand the maximum number held (capacity) as a growth element - Use
PropertyFormwhen: the formation is tied 1:1 to an external resource ID (propertyId), such as per-character or per-equipment-set
The MoldModel of Mold has initialMaxCapacity (initial capacity) and maxCapacity (expansion upper limit), allowing capacity to be increased or decreased according to player growth or monetization strategies.
Targets that can be attached to slots
The following resources can be attached to a Slot of a Form / PropertyForm. When attaching, the “proof-of-ownership signature” issued by GS2 is passed as SlotWithSignature.
propertyType |
Attachment Target |
|---|---|
gs2_inventory |
ItemSet managed by GS2-Inventory |
gs2_simple_inventory |
SimpleItem managed by GS2-Inventory (Simple) |
gs2_dictionary |
Entry managed by GS2-Dictionary |
By setting a regular expression in the slot model’s (SlotModel) propertyRegex, you can restrict which property IDs can be attached to a specific slot. For example, you can implement a restriction such as “only Items matching weapon-* can be attached to the weapon slot”.
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:
MoldModel: Mold storage slot configurationFormModel: Slot configuration of Forms under a MoldPropertyFormModel: PropertyForm slot configuration
Example JSON of master data
{
"version": "2019-09-09",
"moldModels": [
{
"name": "party",
"metadata": "Party formation",
"initialMaxCapacity": 3,
"maxCapacity": 10,
"formModel": {
"name": "party",
"slots": [
{ "name": "leader", "propertyRegex": "character-.*" },
{ "name": "member-1", "propertyRegex": "character-.*" },
{ "name": "member-2", "propertyRegex": "character-.*" }
]
}
}
],
"propertyFormModels": [
{
"name": "equipment",
"metadata": "Character equipment",
"slots": [
{ "name": "weapon", "propertyRegex": "weapon-.*" },
{ "name": "armor", "propertyRegex": "armor-.*" }
]
}
]
}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.
Script Triggers
Setting updateMoldScript updateFormScript updatePropertyFormScript in the namespace allows custom scripts to be executed before and after formation data updates.
Scripts support both synchronous and asynchronous execution, with asynchronous execution supporting external integration via GS2-Script or Amazon EventBridge.
The main event triggers and script setting names that can be configured are as follows:
updateMoldScript(completion notification:updateMoldDone): before and after Mold updatesupdateFormScript(completion notification:updateFormDone): before and after Form updatesupdatePropertyFormScript(completion notification:updatePropertyFormDone): before and after PropertyForm updates
Transaction Actions
GS2-Formation provides the following transaction actions:
- Verify Action: Verify capacity (maximum storage quantity)
- Consume Action: Subtract capacity
- Acquire Action: Add capacity, set capacity, set formation contents (Form / PropertyForm), apply acquire actions to organized resources
By using “Apply acquire actions to organized resources” as an acquire action, it is possible to perform processes such as directly adding experience points to a character organized in a specific party slot. Additionally, by setting “Add capacity” as a reward, it is possible to automatically expand formation slots upon achieving a specific mission.
Implementation example
Persisting formation in Mold
Form Slots can register ItemSets/SimpleItems managed by GS2-Inventory or Entries managed by GS2-Dictionary. To set either of these, a proof-of-ownership signature must be added.
See the description of each service for information on how to obtain a proof-of-ownership signature.
propertyType Types
gs2_inventory– Attach an ItemSet managed by GS2-Inventorygs2_simple_inventory– Attach a SimpleItem managed by GS2-Inventorygs2_dictionary– Attach an Entry managed by GS2-Dictionary
var result = await gs2.Formation.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Mold(
moldName: "mold-0001"
).Form(
index: 0
).SetFormAsync(
slots: new [] {
new Gs2.Unity.Gs2Formation.Model.EzSlotWithSignature
{
Name = "slot-0001",
PropertyType = "gs2_dictionary",
Body = "body",
Signature = "signature",
},
},
keyId: "key-0001"
); const auto Domain = Gs2->Formation->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Mold(
"mold-0001" // moldName
)->Form(
0 // index
);
const auto Future = Domain->SetForm(
[]
{
const auto v = MakeShared<TArray<TSharedPtr<Gs2::Formation::Model::FSlotWithSignature>>>();
v->Add({'name': 'slot-0001', 'propertyType': 'gs2_dictionary', 'body': 'body', 'signature': 'signature'});
return v;
}(),
"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();Increasing Mold Capacity
Capacity (maximum storage quantity) cannot be increased directly via the SDK for game engines. Allowing client-driven capacity manipulation would expose the value to tampering, so the design intentionally requires capacity changes to be performed through transaction actions.
Use GS2-Exchange to increase capacity as a reward, such as through exchange with premium currency.
By configuring the Formation capacity-add action in the acquireActions of GS2-Exchange, capacity expansion can be realized as an extension of granting paid items or mission rewards.
Get Mold information
Get information about a single Mold (current capacity).
var item = await gs2.Formation.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Mold(
moldName: "mold-0001"
).ModelAsync(); const auto Domain = Gs2->Formation->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Mold(
"mold-0001" // moldName
);
const auto Future = Domain->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Item = Future->GetTask().Result();Get a list of formation contents in Mold
var items = await gs2.Formation.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Mold(
moldName: "mold-0001"
).FormsAsync(
).ToListAsync(); const auto Domain = Gs2->Formation->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Mold(
"mold-0001" // moldName
);
const auto It = Domain->Forms(
);
TArray<Gs2::UE5::Formation::Model::FEzMoldPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}Get formation content in Mold
var item = await gs2.Formation.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Mold(
moldName: "mold-0001"
).Form(
index: 0
).ModelAsync(); const auto Domain = Gs2->Formation->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Mold(
"mold-0001" // moldName
)->Form(
0 // index
);
const auto Future = Domain->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Item = Future->GetTask().Result();Deleting a Form
Delete formation contents that are no longer needed. The Mold capacity itself is preserved, and the slot at index becomes reusable as an empty slot.
var result = await gs2.Formation.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Mold(
moldName: "mold-0001"
).Form(
index: 0
).DeleteFormAsync(
); const auto Future = Gs2->Formation->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Mold(
"mold-0001" // moldName
)->Form(
0 // index
)->DeleteForm(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Persisting formation in Property Form
Form Slots can register ItemSets managed by GS2-Inventory or Entries managed by GS2-Dictionary. To set either of these, a proof-of-ownership signature must be added.
See the description of each service for information on how to obtain a proof-of-ownership signature.
var result = await gs2.Formation.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).PropertyForm(
formModelName: "form-0001",
propertyId: "property-0001"
).SetPropertyFormAsync(
slots: new [] {
new Gs2.Unity.Gs2Formation.Model.EzSlotWithSignature
{
Name = "slot-0001",
PropertyType = "gs2_dictionary",
Body = "body",
Signature = "signature",
},
},
keyId: "key-0001"
); const auto Domain = Gs2->Formation->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->PropertyForm(
"form-0001", // formModelName
"property-0001" // propertyId
);
const auto Future = Domain->SetPropertyForm(
[]
{
const auto v = MakeShared<TArray<TSharedPtr<Gs2::Formation::Model::FSlotWithSignature>>>();
v->Add({'name': 'slot-0001', 'propertyType': 'gs2_dictionary', 'body': 'body', 'signature': 'signature'});
return v;
}(),
"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();Get formation content in Property Form
var item = await gs2.Formation.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).PropertyForm(
formModelName: "form-0001",
propertyId: "property-0001"
).ModelAsync(); const auto Domain = Gs2->Formation->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->PropertyForm(
"form-0001", // formModelName
"property-0001" // propertyId
);
const auto Future = Domain->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Item = Future->GetTask().Result();Deleting a Property Form
var result = await gs2.Formation.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).PropertyForm(
formModelName: "form-0001",
propertyId: "property-0001"
).DeletePropertyFormAsync(
); const auto Future = Gs2->Formation->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->PropertyForm(
"form-0001", // formModelName
"property-0001" // propertyId
)->DeletePropertyForm(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Obtain a signature for formation information
When you want to pass the contents of a Form / PropertyForm to another system in a form that guarantees it has not been tampered with, you can obtain signed formation information. This can be used, for example, in a use case that sends the formation contents to the server at the start of a battle.
var domain = await gs2.Formation.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Mold(
moldName: "mold-0001"
).Form(
index: 0
).GetFormWithSignatureAsync(
keyId: "key-0001"
); const auto Future = Gs2->Formation->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Mold(
"mold-0001" // moldName
)->Form(
0 // index
)->GetFormWithSignature(
"key-0001" // keyId
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Resource Management and Notes
Specifying Properties
Properties to be placed in formation slots are specified by property ID.
By setting a regular expression for property IDs in the slot model’s propertyRegex, you can restrict which values can be assigned to that slot.
Proof-of-Ownership Signatures and Post-Formation Caveats
When setting items managed by GS2-Inventory to a slot, you can specify the formation target using a signed item set (proof-of-ownership signature) issued by GS2-Inventory. The ownership guarantee provided by the signed item set is valid only at the time of composition. If the item is subsequently sold or consumed, the formation data remains unchanged. Therefore, when consuming or selling an item in GS2-Inventory, you must first verify — in the client or a script — that the item is not currently in use in a GS2-Formation slot.
Preventing Deletion of Items in Formation via ItemSet.referenceOf
When using Standard Inventory (GS2-Inventory), you can prevent accidental consumption or deletion of items that are currently in formation by using the referenceOf field on ItemSet.
When placing an item into a formation slot, set a string that identifies the slot (e.g., a GRN) in ItemSet.referenceOf.
The Inventory Model settings in Standard Inventory include an option to “prohibit consumption when referenceOf is set”.
When this option is enabled, any item whose ItemSet.referenceOf is non-empty cannot be consumed or sold, effectively locking the item at the system level while it is in use in a formation.