GS2-SkillTree SDK for Game Engine API Reference
Model
EzStatus
Status
Tracks the skill tree release state for a specific player and property. Maintains a list of released (unlocked) node names. Nodes can be released (added to the list), restrained (removed from the list), or reset (all cleared). Attempting to release an already-released node or restrain a non-released node results in an error. Auto-created when first accessed for a user.
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| statusId | string | * | ~ 1024 chars | Status GRN * Set automatically by the server | ||
| userId | string | ✓ | ~ 128 chars | User ID | ||
| releasedNodeNames | List<string> | [] | 0 ~ 1000 items | Released Node Names List of node model names that the player has unlocked in this skill tree. Updated by release (add), restrain (remove), and reset (clear all) operations. Maximum 1000 entries. |
EzNodeModel
Node Model
Defines a node within the skill tree, including its unlock cost, prerequisites, and refund behavior. Each node can have verify actions (conditions to check before release), consume actions (costs to pay), and prerequisite nodes that must be released first. When a node is restrained (reverted to unreleased), the consumed resources are partially refunded based on the restrain return rate. The return acquire actions are automatically calculated from the consume actions multiplied by the restrain return rate.
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| name | string | ✓ | ~ 128 chars | Node Model name Node Model-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| metadata | string | ~ 2048 chars | Metadata Arbitrary values can be set in the metadata. Since they do not affect GS2’s behavior, they can be used to store information used in the game. | |||
| releaseVerifyActions | List<EzVerifyAction> | [] | 0 ~ 10 items | Release Verify Actions List of verify actions executed before releasing this node to check whether the conditions are satisfied. For example, can verify that the player has a certain level or possesses a specific item. If any verify action fails, the node release is rejected. Maximum 10 actions. | ||
| releaseConsumeActions | List<EzConsumeAction> | [] | 1 ~ 10 items | Release Consume Actions List of consume actions executed when releasing this node, representing the cost to unlock it. These actions are also used to calculate the return acquire actions: each consume action is reverted at the restrain return rate when the node is restrained. At least 1 consume action is required. Maximum 10 actions. | ||
| returnAcquireActions | List<EzAcquireAction> | 0 ~ 10 items | Return Acquire Actions List of acquire actions executed when restraining (reverting) this node, representing the resources returned to the player. This field is auto-generated from the release consume actions multiplied by the restrain return rate. For example, if release costs 100 gold and the return rate is 0.8, restraining returns 80 gold. Maximum 10 actions. | |||
| restrainReturnRate | float | 1.0 | 0.0 ~ 1.0 | Restrain Return Rate The rate at which consumed resources are refunded when this node is restrained (reverted to unreleased state). A value of 1.0 means full refund, 0.5 means half refund, and 0.0 means no refund. Defaults to 1.0 (full refund). Valid range: 0.0 to 1.0. |
EzConfig
Configuration
Configuration values applied to transaction variables
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| key | string | ✓ | ~ 64 chars | Name | ||
| value | string | ~ 51200 chars | Value |
EzAcquireAction
Acquire Action
EzConsumeAction
Consume Action
EzVerifyAction
Verify Action
EzVerifyActionResult
Verify Action execution result
EzConsumeActionResult
Consume Action execution result
EzAcquireActionResult
Acquire Action execution result
EzTransactionResult
Transaction execution results
Result of a transaction executed using the server-side automatic transaction execution feature
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| transactionId | string | ✓ | 36 ~ 36 chars | Transaction ID | ||
| verifyResults | List<EzVerifyActionResult> | 0 ~ 10 items | List of verify action execution results | |||
| consumeResults | List<EzConsumeActionResult> | [] | 0 ~ 10 items | List of Consume Action execution results | ||
| acquireResults | List<EzAcquireActionResult> | [] | 0 ~ 100 items | List of Acquire Action execution results |
Methods
getNodeModel
Get the details of a specific skill tree node
Retrieves a single node definition by name, including its prerequisites, release cost, and refund settings.
Use this to display detailed information when the player taps on a node in the skill tree — for example:
- “Power Strike” — Requires: Basic Attack (released). Cost: 3 Skill Points. Effect: +50% attack damage.
- If the node has a restrain return rate of 80%, the player gets back 80% of the cost when reverting this node.
The response includes:
- Prerequisite node names: The list of nodes that must be released first
- Release consume actions: What the player pays to unlock (e.g., spend 3 skill points)
- Release verify actions: Conditions checked before allowing release (e.g., player level >= 10)
- Restrain return rate: The percentage of resources refunded when the node is reverted (0.0 to 1.0)
Request
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Namespace-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| nodeModelName | string | ✓ | ~ 128 chars | Node Model name Node Model-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). |
Result
| Type | Description | |
|---|---|---|
| item | EzNodeModel | Node Model |
Implementation Example
var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).NodeModel(
nodeModelName: "status-0001"
);
var item = await domain.ModelAsync(); var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).NodeModel(
nodeModelName: "status-0001"
);
var future = domain.ModelFuture();
yield return future;
var item = future.Result; const auto Domain = Gs2->SkillTree->Namespace(
"namespace-0001" // namespaceName
)->NodeModel(
"status-0001" // nodeModelName
);
const auto Future = Domain->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}Value change event handling
var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).NodeModel(
nodeModelName: "status-0001"
);
// Start event handling
var callbackId = domain.Subscribe(
value => {
// Called when the value changes
// The "value" is passed the value after the change.
}
);
// Stop event handling
domain.Unsubscribe(callbackId); var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).NodeModel(
nodeModelName: "status-0001"
);
// Start event handling
var callbackId = domain.Subscribe(
value => {
// Called when the value changes
// The "value" is passed the value after the change.
}
);
// Stop event handling
domain.Unsubscribe(callbackId); const auto Domain = Gs2->SkillTree->Namespace(
"namespace-0001" // namespaceName
)->NodeModel(
"status-0001" // nodeModelName
);
// Start event handling
const auto CallbackId = Domain->Subscribe(
[](TSharedPtr<Gs2::SkillTree::Model::FNodeModel> value) {
// Called when the value changes
// The "value" is passed the value after the change.
}
);
// Stop event handling
Domain->Unsubscribe(CallbackId);Warning
This event is triggered when the value stored in the SDK’s local cache changes.
The local cache is updated only when executing the SDK’s API, or by executing stamp sheets via GS2-Distributor with GS2-Gateway notification enabled, or by executing a GS2-JobQueue with GS2-Gateway notification enabled.
Therefore, callbacks will not be invoked if the value is changed in any other way.
listNodeModels
Get a list of all skill tree nodes
Retrieves all node definitions in the skill tree. Each node represents a skill, ability, or upgrade that the player can unlock (release) by spending resources.
A skill tree is a branching structure where nodes have prerequisites — you must unlock earlier nodes before you can unlock later ones. For example: “Basic Attack” → “Power Strike” → “Critical Slash”, where each node requires the previous one to be released first.
Each node definition includes:
- Prerequisite nodes: Which other nodes must be released before this one can be unlocked
- Release cost: What resources the player must spend to unlock this node (e.g., skill points, gold, materials)
- Release conditions: Any additional requirements that must be met (e.g., minimum level)
- Restrain return rate: What percentage of resources is refunded if the player later reverts (locks) this node
Use this to build the skill tree UI — draw all nodes, connect them based on prerequisites, and show which ones are available for release.
Request
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Namespace-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). |
Result
| Type | Description | |
|---|---|---|
| items | List<EzNodeModel> | List of Node Models |
Implementation Example
var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
);
var items = await domain.NodeModelsAsync(
).ToListAsync(); var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
);
var it = domain.NodeModels(
);
List<EzNodeModel> items = new List<EzNodeModel>();
while (it.HasNext())
{
yield return it.Next();
if (it.Error != null)
{
onError.Invoke(it.Error, null);
break;
}
if (it.Current != null)
{
items.Add(it.Current);
}
else
{
break;
}
} const auto Domain = Gs2->SkillTree->Namespace(
"namespace-0001" // namespaceName
);
const auto It = Domain->NodeModels(
);
TArray<Gs2::UE5::SkillTree::Model::FEzNodeModelPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}Value change event handling
var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
);
// Start event handling
var callbackId = domain.SubscribeNodeModels(
() => {
// Called when an element of the list changes.
}
);
// Stop event handling
domain.UnsubscribeNodeModels(callbackId); var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
);
// Start event handling
var callbackId = domain.SubscribeNodeModels(
() => {
// Called when an element of the list changes.
}
);
// Stop event handling
domain.UnsubscribeNodeModels(callbackId); const auto Domain = Gs2->SkillTree->Namespace(
"namespace-0001" // namespaceName
);
// Start event handling
const auto CallbackId = Domain->SubscribeNodeModels(
[]() {
// Called when an element of the list changes.
}
);
// Stop event handling
Domain->UnsubscribeNodeModels(CallbackId);Warning
This event is triggered when the value stored in the SDK’s local cache changes.
The local cache is updated only when executing the SDK’s API, or by executing stamp sheets via GS2-Distributor with GS2-Gateway notification enabled, or by executing a GS2-JobQueue with GS2-Gateway notification enabled.
Therefore, callbacks will not be invoked if the value is changed in any other way.
getStatus
Get the player’s skill tree progress
Retrieves which nodes the player has released (unlocked) in the skill tree for the specified property ID.
The propertyId identifies which skill tree instance to look up. If your game has one skill tree per character, the propertyId could be the character ID. For example, a warrior character and a mage character would each have their own skill tree progress tracked by different propertyIds.
Use this to render the skill tree screen — combine with ListNodeModels to get the full tree structure, then overlay the player’s release status to show:
- Released nodes (unlocked, highlighted)
- Available nodes (prerequisites met, can be released next)
- Locked nodes (prerequisites not yet met, greyed out)
Request
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Namespace-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| gameSession | GameSession | ✓ | GameSession | |||
| propertyId | string | ✓ | ~ 1024 chars | Property ID An identifier that allows multiple independent skill tree instances per user. Enables scenarios where a player has separate skill trees for different characters or contexts within the same namespace. Maximum 1024 characters. |
Result
| Type | Description | |
|---|---|---|
| item | EzStatus | Status |
Implementation Example
var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
propertyId: "property-0001"
);
var item = await domain.ModelAsync(); var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
propertyId: "property-0001"
);
var future = domain.ModelFuture();
yield return future;
var item = future.Result; const auto Domain = Gs2->SkillTree->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->Status(
"property-0001" // propertyId
);
const auto Future = Domain->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}Value change event handling
var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
propertyId: "property-0001"
);
// Start event handling
var callbackId = domain.Subscribe(
value => {
// Called when the value changes
// The "value" is passed the value after the change.
}
);
// Stop event handling
domain.Unsubscribe(callbackId); var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
propertyId: "property-0001"
);
// Start event handling
var callbackId = domain.Subscribe(
value => {
// Called when the value changes
// The "value" is passed the value after the change.
}
);
// Stop event handling
domain.Unsubscribe(callbackId); const auto Domain = Gs2->SkillTree->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->Status(
"property-0001" // propertyId
);
// Start event handling
const auto CallbackId = Domain->Subscribe(
[](TSharedPtr<Gs2::SkillTree::Model::FStatus> value) {
// Called when the value changes
// The "value" is passed the value after the change.
}
);
// Stop event handling
Domain->Unsubscribe(CallbackId);Warning
This event is triggered when the value stored in the SDK’s local cache changes.
The local cache is updated only when executing the SDK’s API, or by executing stamp sheets via GS2-Distributor with GS2-Gateway notification enabled, or by executing a GS2-JobQueue with GS2-Gateway notification enabled.
Therefore, callbacks will not be invoked if the value is changed in any other way.
release
Unlock nodes in the skill tree
Releases (unlocks) one or more nodes in the skill tree. The player spends the required resources and the nodes become active.
This is the “Learn Skill” or “Unlock” button in your skill tree UI. When the player taps a node they want to unlock:
- The system checks that all prerequisite nodes are already released
- The system checks release conditions (verify actions) — e.g., minimum player level
- The system deducts the release cost (consume actions) — e.g., 3 skill points
- The node is marked as released
You can release multiple nodes at once by passing multiple node names. All prerequisites and costs are validated for each node.
If any prerequisite is not met, or the player doesn’t have enough resources, the release fails and no resources are consumed.
Request
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Namespace-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| gameSession | GameSession | ✓ | GameSession | |||
| nodeModelNames | List<string> | ✓ | 1 ~ 1000 items | List of Node Model names | ||
| propertyId | string | ✓ | ~ 1024 chars | Property ID An identifier that allows multiple independent skill tree instances per user. Enables scenarios where a player has separate skill trees for different characters or contexts within the same namespace. Maximum 1024 characters. |
Result
| Type | Description | |
|---|---|---|
| item | EzStatus | Status |
| transactionId | string | Issued transaction ID |
| stampSheet | string | Stamp sheet used to execute the release process |
| stampSheetEncryptionKeyId | string | Cryptographic key GRN used for stamp sheet signature calculations |
| autoRunStampSheet | bool | Whether automatic transaction execution is enabled |
| atomicCommit | bool | Whether to commit the transaction atomically |
| transaction | string | Issued transaction |
| transactionResult | EzTransactionResult | Transaction execution result |
Implementation Example
var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
propertyId: "property-0001"
);
var result = await domain.ReleaseAsync(
nodeModelNames: new List<string> {
"node-0001",
}
);
// In New Experience, stamp sheets are automatically executed at the SDK level.
// If an error occurs, a TransactionException is thrown.
// you can retry with TransactionException::Retry(). var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
propertyId: "property-0001"
);
var future = domain.ReleaseFuture(
nodeModelNames: new List<string> {
"node-0001",
}
);
yield return future;
if (future.Error != null)
{
onError.Invoke(future.Error, null);
yield break;
}
// In New Experience, stamp sheets are automatically executed at the SDK level.
// If an error occurs, a TransactionException is thrown.
// you can retry with TransactionException::Retry(). const auto Domain = Gs2->SkillTree->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->Status(
"property-0001" // propertyId
);
const auto Future = Domain->Release(
[]
{
auto v = TOptional<TArray<FString>>();
v->Add("node-0001");
return v;
}() // nodeModelNames
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}reset
Reset the entire skill tree (full respec)
Reverts ALL released nodes in the skill tree back to unreleased state at once. Resources are refunded based on each node’s restrain return rate.
This is the “Reset All Skills” or “Full Respec” button — the player starts their skill tree over from scratch.
For example, if a player has released 10 nodes with various costs, all 10 are reverted and the player receives partial refunds for each one based on the configured return rates.
Common use cases:
- A “Respec” feature that lets players rebuild their skill tree with a different strategy
- Seasonal resets where all players start fresh
- When game balance changes make the current build suboptimal and you want to let players readjust
Unlike Restrain (which selectively reverts specific nodes), Reset affects the entire tree regardless of dependencies.
Request
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Namespace-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| gameSession | GameSession | ✓ | GameSession | |||
| propertyId | string | ✓ | ~ 1024 chars | Property ID An identifier that allows multiple independent skill tree instances per user. Enables scenarios where a player has separate skill trees for different characters or contexts within the same namespace. Maximum 1024 characters. |
Result
| Type | Description | |
|---|---|---|
| item | EzStatus | Status |
| transactionId | string | Issued transaction ID |
| stampSheet | string | Stamp sheet used to execute the reset process |
| stampSheetEncryptionKeyId | string | Cryptographic key GRN used for stamp sheet signature calculations |
| autoRunStampSheet | bool | Whether automatic transaction execution is enabled |
| atomicCommit | bool | Whether to commit the transaction atomically |
| transaction | string | Issued transaction |
| transactionResult | EzTransactionResult | Transaction execution result |
Implementation Example
var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
propertyId: "property-0001"
);
var result = await domain.ResetAsync(
);
// In New Experience, stamp sheets are automatically executed at the SDK level.
// If an error occurs, a TransactionException is thrown.
// you can retry with TransactionException::Retry(). var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
propertyId: "property-0001"
);
var future = domain.ResetFuture(
);
yield return future;
if (future.Error != null)
{
onError.Invoke(future.Error, null);
yield break;
}
// In New Experience, stamp sheets are automatically executed at the SDK level.
// If an error occurs, a TransactionException is thrown.
// you can retry with TransactionException::Retry(). const auto Domain = Gs2->SkillTree->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->Status(
"property-0001" // propertyId
);
const auto Future = Domain->Reset(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}restrain
Revert specific nodes back to locked state
Reverts (locks) one or more released nodes back to unreleased state. A portion of the resources spent on releasing these nodes is refunded based on each node’s restrain return rate.
This is a “partial respec” feature — instead of resetting the entire skill tree, the player can selectively undo specific nodes.
For example, if a node cost 100 Gold to release and has a restrain return rate of 0.8 (80%), the player gets back 80 Gold when restraining it.
Important rules:
- You cannot restrain a node if other released nodes depend on it (i.e., nodes that list it as a prerequisite)
- To restrain a node that is a prerequisite for other released nodes, you must restrain the dependent nodes first
- This ensures the skill tree always remains in a valid state (no orphaned nodes)
Use this for a “remove skill” or “undo” feature in your skill tree UI.
Request
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Namespace-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| gameSession | GameSession | ✓ | GameSession | |||
| nodeModelNames | List<string> | ✓ | 1 ~ 1000 items | List of Node Model names | ||
| propertyId | string | ✓ | ~ 1024 chars | Property ID An identifier that allows multiple independent skill tree instances per user. Enables scenarios where a player has separate skill trees for different characters or contexts within the same namespace. Maximum 1024 characters. |
Result
| Type | Description | |
|---|---|---|
| item | EzStatus | Status |
| transactionId | string | Issued transaction ID |
| stampSheet | string | Stamp sheet used to execute the restrain process |
| stampSheetEncryptionKeyId | string | Cryptographic key GRN used for stamp sheet signature calculations |
| autoRunStampSheet | bool | Whether automatic transaction execution is enabled |
| atomicCommit | bool | Whether to commit the transaction atomically |
| transaction | string | Issued transaction |
| transactionResult | EzTransactionResult | Transaction execution result |
Implementation Example
var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
propertyId: "property-0001"
);
var result = await domain.RestrainAsync(
nodeModelNames: new List<string> {
"node-0001",
}
);
// In New Experience, stamp sheets are automatically executed at the SDK level.
// If an error occurs, a TransactionException is thrown.
// you can retry with TransactionException::Retry(). var domain = gs2.SkillTree.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Status(
propertyId: "property-0001"
);
var future = domain.RestrainFuture(
nodeModelNames: new List<string> {
"node-0001",
}
);
yield return future;
if (future.Error != null)
{
onError.Invoke(future.Error, null);
yield break;
}
// In New Experience, stamp sheets are automatically executed at the SDK level.
// If an error occurs, a TransactionException is thrown.
// you can retry with TransactionException::Retry(). const auto Domain = Gs2->SkillTree->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->Status(
"property-0001" // propertyId
);
const auto Future = Domain->Restrain(
[]
{
auto v = TOptional<TArray<FString>>();
v->Add("node-0001");
return v;
}() // nodeModelNames
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}