Skip to main content

World

1. Overview

World is an entity that players can move in and out of freely and smoothly. PGOS divides players who enter the world into several world buckets based on the world filter. PGOS will allocate players from the world bucket to multiple battle sessions based on factors such as player count, player latency, and the configured capacity of battle sessions in the world.

While both World and Matchmaking can be used to aggregate similar players, there are significant differences between the two services:

  • Players experience minimal wait time when joining a world. PGOS efficiently identifies the bucket that corresponds to each player based on their joining attributes and promptly assigns them to the appropriate battle session.
  • The battle session generated by the World service is opened to each player, new players can join and leave at any time.
  • The World service does not support complicated matchmaking rules, and developers can only define simple filters that will be used as the basis for assigning buckets to players.

World service can be used to implement a variety of online gaming features. Typical application scenarios include:

  • A persistent and freely accessible gaming scene, such as a game lobby or game hub.
  • A large game that does not require complex matchmaking rules.

2. Key Concepts

  • World: World is an entity that players can move in and out of freely and smoothly. PGOS divides players who enter the world into several world buckets based on the world filter.
  • World Filters: A world filter is configured by the game and it defines how to slice and dice players who enter the world into different world buckets.
  • World Buckets: Groups of players that meet certain specific filtering criteria. Players in different buckets will be strictly segregated. PGOS follows the principle of load centralization to allocate battle sessions to players in a bucket and coordinate DS resources. In this process, PGOS will fully consider players' latency in order to provide a better gaming experience.
  • Battle session: A battle session hosts some players in a bucket and maintains the access information of DS resources.

3. Architecture Diagram

The World service includes two types of interactions:

  • Call world service interfaces using the PGOS SDK.
  • Receive world service notifications pushed by the PGOS when specific events occur.

image-20240826162650709

4. Setup World Configurations

4.1 Create a Filter

A world filter defines how to slice and dice players who enter the world into different world buckets. Each filter can be reused by multiple world configurations. Use the PGOS web portal console to manage world filters.

4.1.1 Follow the steps below to create a filter

  1. Access to the web portal

  2. Go to the Battle/World module on the portal.

  3. Select the Filter tab at the top of the page.

  4. You can create a new filter or edit an existing one.

    image-20231117104944427

  5. Fill in the filter details.

    • Name: Create a meaningful name so you can easily identify it in a list. The filter name must be unique in a title region, which will be used when creating world configurations later.
    • Description: (Optional) Description of the filter.
    • Attributes: Define the player attribute data needed by the filter, these attributes will be available in filter rules, whenever a player requests to join a world, the World service will read the player's attribute and adhere to the constraints defined in filter rules to determine the bucket the player belongs to.
    • Rules: Here you define how the players in the world are sliced and diced. Please note that these rules are stacked on top of each other, which means that the more filtering rules you define, the more buckets will be generated at runtime.

4.1.2 Attributes Fields

image-20231120110139821

  • Data Source: The data source tells the World service where to read the player attributes used by the filter rules. The enumeration of supported data sources is as follows,
    • Client Request: The World service will read this attribute value from the client's JoinWorld request.
    • Player Data: The World service will retrieve the required attribute value from the PGOS Player KV-Data service. The Player KV-Data Key is required if this type is selected.
    • System: Predefined attributes provided by PGOS, currently only the publishing platform is included.
  • Key: This field is populated only when the Data Source is Player Data, and specifies the Player KV-Data field that the World service will read.
  • Name: Names the defined attributes, which cannot be duplicated in the same filter.
  • Default Value: This default value is used when the World service is unable to obtain a value for this attribute from the specified data source.
  • Value Declare: You can choose whether to declare the available enumerations for this attribute here. If declared values are populated here, the handling of undeclared values can be defined in the filter rule.

4.1.3 Rules Fields

image-20240131103019280

  • Rule Name: Names the defined rule, which cannot be duplicated in the same filter.
  • Attribute: Select the player attributes to be filtered by this rule. Please note that only one rule can be configured for each attribute.
  • Filter Type: Currently only supports the Enum type. It means that the rule will check the attribute of each joined player, and then assign the players with different attribute values to different buckets.
  • Undeclared Data Strategy: This field defines how the World service handles join requests for World when the attribute value is not registered in the Filter Attribute Value Declare.
    • Reject: Any world join request will be rejected when the attribute value is not registered in the Filter Attribute Value Declare.
    • Accept: Every world join request will be accepted when the attribute value is not registered in the Filter Attribute Value Declare.
  • Equivalent Values: Allows multiple sets of equivalent values for the attribute. The game can use this configuration to merge attribute values for which it does not wish to divert players into different buckets.
Caution
  1. Setting Undeclared Data Strategy to Accept means that a large runtime world bucket may be generated. Please make your decision carefully!
  2. The order in which the filter rule items are declared DOES MATTER! It determines the composition of the bucket path, which will be used to quickly locate the bucket belonging to the player who joined a world.
  3. If the player submits a value that hits an equivalent value when generating the bucket path, the value of this attribute will be changed to the first item in the equivalent value configuration list. For example, the equivalent values for the attribute mode are [a,b,c]. Regardless of whether the player submits a, b, or c, the value written to the bucket path will always be a.

4.1.4 Delete a filter

  1. You must make sure that there are no associated matchmaking configurations with the current ruleset. Otherwise, an error notification will appear and the delete operation will fail. In this case, you must change the matchmaking configuration and use a different ruleset before you delete the ruleset.
  2. On the rulesets console page, select a ruleset and Click Delete.

4.2 Create a DS Placer

A placer is used to determine the best fleet to run a battle session. Use an existing placer or create a new one for matchmaking. Click the Create Placer link for more details:

4.3 Create a World Configuration

Once you have created World Filter and DS Hosting Placer, World Configuration should be set up to complete a world creation. Use the PGOS web portal console to manage world configuration.

Tips

You can create and maintain multiple worlds in PGOS by setting up multiple world configurations. Players may join multiple worlds at the same time, and PGOS will only assign one battle session to a player in each world.

Follow the steps below to create a world configuration :

  1. Open the web portal, and select the Battle/World module on the portal. Then select the World Configuration tab on the top of the page.

  2. Click the Create button to create a new world configuration.

    image-20231122113806272

  3. Fill in the match configuration details.

    • Name: Create a meaningful world configuration name. The world configuration name must be unique within a title region.

    • Description: (Optional) Add a description of the world configuration.

    • World Filter: Choose the filter to use with this world configuration.

    • Placer: Choose a placer to use with this world configuration. All battle sessions generated by the world will be placed in the DS resource under this Placer.

    • DS Approval: Choose a DS approval strategy to specify how to handle clients JoinWorldBattleSession requests.

      • Require approval when session is locked: DS approval is only required when directly joining a locked world battle session.

      • Require approval for all session join request: All join requests from the JoinWorldBattleSession interface require DS approval, regardless of whether the world battle session is locked or not.

    • Max Players Per Session: Maximum number of players for each battle session generated by this world.

    • Max Sessions Count per Bucket: The max running battle sessions of each world bucket, 0 means no limit, must be greater than 0. JoinWorld interface will return a failure when running battle sessions for the bucket already reached the limit.

    • Max Runtime Buckets Count: The maximum count of non-empty buckets during a world running. Once the number of buckets hits this value, any world join requests to create a new bucket will be rejected, until the number of buckets falls below this limit.

      Tips

      This parameter is intended to limit the potential surge in the number of buckets due to game misuse or malicious attacks from the outside. A surge in the number of buckets can lead to unexpectedly large overheads in DS resources and it must strongly be avoided.

    • World Session Clean-Up Time: When the world battle session is empty for a certain period of time, PGOS will clean it up. Setting it to 0 means cleaning it up immediately, the max time is 3600, and the unit is seconds.
    • Battle Properties: (Optional) Battle properties will be passed to your game server when a new battle session is created through matchmaking.

5. Integrate Worlds Into Your Game Client

5.1 Join Word

Players are free to join the world as long as they comply with the world filter's restrictions. It is important to note that while a player can join multiple worlds, they can only join one battle session of a specified world at a time. They cannot simultaneously join multiple battle sessions of the same world.

5.1.1 Single-player Join World

A player can use the JoinWorld API to request the backend to add themselves to a specified world. The backend will then allocate a suitable world battle session for the player. Here is the JoinWorld API prototype:

/**
* Request to join a world.
*
* @param Params Request struct for join a world.
*/
void JoinWorld(
const FClientJoinWorldParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientJoinWorldResult* Data)> Callback) const;

struct FClientJoinWorldParams
{
/** World config name that request to join. */
FString world_config;
/** Attributes needed by world buckets filters, map key: attribute name, map value: attribute value. */
TMap<FString, FKVDataValue> filter_attributes;
};

struct FClientJoinWorldResult
{
/** The battle session that the world assigned for the players. */
FClientBattleSessionInfo battle_session;
};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto World = IPgosSDKCpp::Get().GetClientWorldAPI();
if (World)
{
FClientJoinWorldParams Params;
Params.world_config = TEXT("World_001");
Params.filter_attributes.Add(TEXT("game_mode"), TEXT("happy_play"));
World->JoinWorld(Params, [](const FPgosResult& Ret, const FClientJoinWorldResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("JoinWorld Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("JoinWorld Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
NOTE

Game client needs to keep an eye on the status of the battle session after the JoinWorld API returns success. Because the ip_address and port in BattleSessionInfo will be populated only if the battle session status is marked as Active. When the join behaviour of a player triggers the creation of a new battle session or even the scaling up of a CVM instance, the battle session returned by the JoinWorld API will not be actived.

In this case, you need to monitor the OnWorldBattleSessionUpdated event to get the ip_address and port when the battle session is marked as Active.

5.1.2 Multi-player Join World

A player can use the InviteJoinWorld API to invite other players to join a specified world together. If this API call is successful, these invited players will receive the OnJoinWorldInvitationUpdated event on their game client. Players who accept the invitation ( call AcceptJoinWorldInvitation API) will be assigned to the same world battle session as the inviter player. Players who reject the invitation (call RejectJoinWorldInvitation API) will skip the invitation and will not be affected. The inviter will also receive the OnJoinWorldInvitationUpdated event to know who accepted or rejected the invitation. The inviter is assumed to have accepted the invitation automatically. The general sequence is shown in the following diagram:

sequenceDiagram participant Player A participant PGOS Backend participant Player B participant Player C participant DS Player A->>PGOS Backend: Invite A and B to join world together PGOS Backend-->>Player B: Push 'OnJoinWorldInvitationUpdated' event PGOS Backend-->>Player C: Push 'OnJoinWorldInvitationUpdated' event Player B->>PGOS Backend: Accept or reject the invitation PGOS Backend-->>Player A: Push 'OnJoinWorldInvitationUpdated' event PGOS Backend-->>Player B: Push 'OnJoinWorldInvitationUpdated' event PGOS Backend-->>Player C: Push 'OnJoinWorldInvitationUpdated' event Player C->>PGOS Backend: Accept or reject the invitation PGOS Backend->>PGOS Backend: Find or create a world battle session to place A, B, and C in, since all players have made their decision. PGOS Backend-->>Player A: Push 'OnJoinWorldInvitationUpdated' event: Completed PGOS Backend-->>Player B: Push 'OnJoinWorldInvitationUpdated' event: Completed PGOS Backend-->>Player C: Push 'OnJoinWorldInvitationUpdated' event: Completed PGOS Backend-->>Player A: Push 'OnWorldBattleSessionUpdated' event Player A->>DS: Connect (if not connected yet) PGOS Backend-->>Player B: Push 'OnWorldBattleSessionUpdated' event Player B->>DS: Connect (if not connected yet) PGOS Backend-->>Player C: Push 'OnWorldBattleSessionUpdated' event Player C->>DS: Connect (if not connected yet)
NOTE

In the above process, typically, the player who accepts the invitation will receive the OnWorldBattleSessionUpdated event after the invitation status is marked as 'Completed'. However, if the player is already in the assigned battle session before the invitation, and has obtained the battle session's IP&port (which means he has received the relevant event of the battle session status being Active), then he may not be pushed the OnWorldBattleSessionUpdated event.

The join invitation has a timeout configuration (it's configurable in the console), and players who have not made a decision when the invitation times out will be ignored.

Here is the InviteJoinWorld API prototype:

/**
* Invite other players to join a world together.
*
* @param Params Request struct for inviting other players to join a world together.
*/
void InviteJoinWorld(
const FClientInviteJoinWorldParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientInviteJoinWorldResult* Data)> Callback) const;

struct FClientInviteJoinWorldParams
{
/** World config name that request to join. */
FString world_config;
/** Attributes needed by world buckets filters, map key: attribute name, map value: attribute value. */
TMap<FString, FKVDataValue> filter_attributes;
/**
* Player IDs who will be invited to join the same world. (Must include at least 1 player)
*/
TArray<FString> invited_player_ids;
};

struct FClientInviteJoinWorldResult
{
/** The world configuration name associated with the invitation ticket. */
FString world_config;
/** The ticket of the join world invitation. */
FString invitation_ticket;
};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto World = IPgosSDKCpp::Get().GetClientWorldAPI();
if (World)
{
FClientInviteJoinWorldParams Params;
Params.world_config = TEXT("World_001");
Params.filter_attributes.Add(TEXT("game_mode"), TEXT("happy_play"));
Params.invited_player_ids.Add("11223344");
Params.invited_player_ids.Add("55667788");
World->InviteJoinWorld(Params, [](const FPgosResult& Ret, const FClientInviteJoinWorldResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("InviteJoinWorld Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("InviteJoinWorld Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

// monitor the OnJoinWorldInvitationUpdated event
void SomeUObjectClass::OnJoinWorldInvitationUpdated(const FClientJoinWorldInvitationUpdatedEvt& event) {
auto World = IPgosSDKCpp::Get().GetClientWorldAPI();
if (event.info.my_action_to_invitation == EClientJoinWorldInvitationAction::Undecided && World)
{
BOOL WantAccept = TRUE;
if (WantAccept)
{
FClientAcceptJoinWorldInvitationParams Params;
Params.invitation_ticket = event.info.invitation_ticket;
World->AcceptJoinWorldInvitation(Params, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("AcceptJoinWorldInvitation Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("AcceptJoinWorldInvitation Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
else
{
FClientRejectJoinWorldInvitationParams Params;
Params.invitation_ticket = event.info.invitation_ticket;
World->RejectJoinWorldInvitation(Params, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("RejectJoinWorldInvitation Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("RejectJoinWorldInvitation Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
}

5.2 Join World Battle Session

PGOS provides APIs to join an existing world battle session, and we also offer an QueryPlayerWorldBattleSessions API to query any player's world battle session. By utilizing these two APIs, you can easily join any player's world battle session, as long as there are enough slots available.

Please note that a player's world battle session join request requires DS approval in the following two scenarios:

  1. The requested world battle session is locked by the DS.
  2. The DS Approval property of the world configuration is set to Require approval for all session join requests.

You will receive a battle session with Pending player battle session status in both of these cases, which indicating that DS approval is required before the player battle session can be used. The player battle session status will change to Reserved once the DS completes the approval process, indicating that the player battle session is now available, and you can connect your game client to the DS.

5.2.1 Single-player Join Session

Players can join a specified world battle session directly using the JoinWorldBattleSession API. As mentioned in the 5.1.1 Single-player Join World section: after the API call is successful, you still need to keep an eye on the status of the battle session you get. If its status is Placing, you need to pay attention to the subsequent OnWorldBattleSessionUpdated event, so that you can get the ip_address and port when the battle session is marked as 'Active'.

Here is the InviteJoinWorld API prototype:

/**
* Request to join a world battle session directly.
* The API will return failure when there are not enough slots left in the battle session.
* The API will return failure when the battle session is locked.
*
* @param Params Request struct for join a world battle session.
*/
void JoinWorldBattleSession(
const FClientJoinWorldBattleSessionParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientJoinWorldBattleSessionResult* Data)> Callback) const;

struct FClientJoinWorldBattleSessionParams
{
/** World config name that request to join. */
FString world_config;
/** The world battle session id to join. */
FString battle_session_id;
/**
* The game custom data, which will be passed to the DS (dedicated server).
* It can be obtained by monitoring the 'OnBattleSessionUpdated' event of the Hosting module: FServerBattleSessionUpdatedEvt.new_player_battle_sessions.payload.
*/
FString payload;
};

struct FClientJoinWorldBattleSessionResult
{
/** Battle session detail info. */
FClientBattleSessionInfo battle_session;
};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction(const FString& battle_session_id)
{
auto World = IPgosSDKCpp::Get().GetClientWorldAPI();
if (World)
{
FClientJoinWorldBattleSessionParams Params;
Params.world_config = TEXT("World_001");
Params.battle_session_id = battle_session_id;
Params.payload = TEXT("{}");
World->JoinWorldBattleSession(Params, [](const FPgosResult& Ret, const FClientJoinWorldBattleSessionResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("JoinWorldBattleSession Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("JoinWorldBattleSession Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

5.2.2 Multi-player Join Session

A player can use the InviteJoinWorldBattleSession API to invite other players to join a specified world battle session together. If this API call is successful, these invited players will receive the OnJoinWorldInvitationUpdated event on their game client. Players who accept the invitation ( call AcceptJoinWorldInvitation API) will be assigned to the same world battle session as the inviter player. Players who reject the invitation (call RejectJoinWorldInvitation API) will skip the invitation and will not be affected. The inviter will also receive the OnJoinWorldInvitationUpdated event to know who accepted or rejected the invitation. The inviter is assumed to have accepted the invitation automatically. The general sequence is shown in the following diagram:

sequenceDiagram participant Player A participant PGOS Backend participant Player B participant Player C participant DS Player A->>PGOS Backend: Invite A and B to join world battle session together PGOS Backend-->>Player B: Push 'OnJoinWorldInvitationUpdated' event PGOS Backend-->>Player C: Push 'OnJoinWorldInvitationUpdated' event Player B->>PGOS Backend: Accept or reject the invitation PGOS Backend-->>Player A: Push 'OnJoinWorldInvitationUpdated' event PGOS Backend-->>Player B: Push 'OnJoinWorldInvitationUpdated' event PGOS Backend-->>Player C: Push 'OnJoinWorldInvitationUpdated' event Player C->>PGOS Backend: Accept or reject the invitation PGOS Backend->>PGOS Backend: Try to place A,B and C to the target world battle session, since all players have made their decision. PGOS Backend-->>Player A: Push 'OnJoinWorldInvitationUpdated' event: Completed PGOS Backend-->>Player B: Push 'OnJoinWorldInvitationUpdated' event: Completed PGOS Backend-->>Player C: Push 'OnJoinWorldInvitationUpdated' event: Completed PGOS Backend-->>Player A: Push 'OnWorldBattleSessionUpdated' event Player A->>DS: Connect (if not connected yet) PGOS Backend-->>Player B: Push 'OnWorldBattleSessionUpdated' event Player B->>DS: Connect (if not connected yet) PGOS Backend-->>Player C: Push 'OnWorldBattleSessionUpdated' event Player C->>DS: Connect (if not connected yet)
NOTE

In the above process, typically, the player who accepts the invitation will receive the OnWorldBattleSessionUpdated event after the invitation status is marked as 'Completed'. However, if the player is already in the assigned battle session before the invitation, and has obtained the battle session's IP&port (which means he has received the relevant event of the battle session status being Active), then he may not be pushed the OnWorldBattleSessionUpdated event.

The join invitation has a timeout configuration (it's configurable in the console), and players who have not made a decision when the invitation times out will be ignored.

Here is the InviteJoinWorldBattleSession API prototype:

/**
* Invite other players to join a world battle session directly together.
* The API will return failure when there are not enough slots left in the battle session.
* The API will return failure when the battle session is locked.
*
* @param Params Request struct for inviting other players to join a world battle session.
*/
void InviteJoinWorldBattleSession(
const FClientInviteJoinWorldBattleSessionParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientInviteJoinWorldBattleSessionResult* Data)> Callback) const;

struct FClientInviteJoinWorldBattleSessionParams
{
/** World config name that request to join. */
FString world_config;
/** The world battle session id to join. */
FString battle_session_id;
/**
* The game custom data, which will be passed to the DS (dedicated server).
* It can be obtained by monitoring the Hosting module event (OnBattleSessionUpdatedEvt): BattleSessionUpdatedEvt.new_player_battle_sessions.payload.
*/
FString payload;
/**
* Player IDs who will be invited to join the same world battle session. (Must include at least 1 player)
*/
TArray<FString> invited_player_ids;
};

struct FClientInviteJoinWorldBattleSessionResult
{
/** The world configuration name associated with the invitation ticket. */
FString world_config;
/** The ticket of the join world invitation. */
FString invitation_ticket;
};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction(const FString& battle_session_id)
{
auto World = IPgosSDKCpp::Get().GetClientWorldAPI();
if (World)
{
FClientInviteJoinWorldBattleSessionParams Params;
Params.world_config = TEXT("World_001");
Params.battle_session_id = battle_session_id;
Params.payload = TEXT("{}");
Params.invited_player_ids.Add("11223344");
Params.invited_player_ids.Add("55667788");
World->InviteJoinWorldBattleSession(Params, [](const FPgosResult& Ret, const FClientInviteJoinWorldBattleSessionResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("InviteJoinWorldBattleSession Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("InviteJoinWorldBattleSession Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

// monitor the OnJoinWorldInvitationUpdated event
void SomeUObjectClass::OnJoinWorldInvitationUpdated(const FClientJoinWorldInvitationUpdatedEvt& event) {
auto World = IPgosSDKCpp::Get().GetClientWorldAPI();
if (event.info.my_action_to_invitation == EClientJoinWorldInvitationAction::Undecided && World)
{
BOOL WantAccept = TRUE;
if (WantAccept)
{
FClientAcceptJoinWorldInvitationParams Params;
Params.invitation_ticket = event.info.invitation_ticket;
World->AcceptJoinWorldInvitation(Params, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("AcceptJoinWorldInvitation Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("AcceptJoinWorldInvitation Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
else
{
FClientRejectJoinWorldInvitationParams Params;
Params.invitation_ticket = event.info.invitation_ticket;
World->RejectJoinWorldInvitation(Params, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("RejectJoinWorldInvitation Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("RejectJoinWorldInvitation Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
}

5.3 Leave World

5.3.1 Single-player Leave World

If a player wants to leave the world battle session they are currently in, they can use the LeaveWorld API to notify the backend and release the world battle session slot. Here is the LeaveWorld API prototype:

/**
* Request to join a world
*
* @param Params Request struct for leave a world.
*/
void LeaveWorld(
const FClientLeaveWorldParams& Params,
TFunction<void(const FPgosResult& Ret)> Callback) const;

struct FClientLeaveWorldParams
{
/** World config name that request to leave. */
FString world_config;
};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto World = IPgosSDKCpp::Get().GetClientWorldAPI();
if (World)
{
FClientLeaveWorldParams Params;
Params.world_config = TEXT("World_001");
World->LeaveWorld(Params, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("LeaveWorld Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("LeaveWorld Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

5.3.2 Multi-player Leave World

A player can use the InviteLeaveWorld API to invite other players to leave a specified world. If this API call is successful, these invited players will receive the OnLeaveWorldInvitationUpdated event on their game client. Players who accept the invitation ( call AcceptLeaveWorldInvitation API) will leave the world battle directly. Players who reject the invitation (call RejectLeaveWorldInvitation API) will skip the invitation and will not be affected. The inviter will also receive the OnLeaveWorldInvitationUpdated event to know who accepted or rejected the invitation. The inviter will leave the world directly when calling the InviteLeaveWorld interface. The general sequence is shown in the following diagram:

sequenceDiagram participant Player A participant PGOS Backend participant Player B Player A->>PGOS Backend: A invite B to leave a world together PGOS Backend->>PGOS Backend: Remove A from the world(battle session) PGOS Backend-->>Player A: Push OnWorldBattleSessionUpdated with player battle session: Completed PGOS Backend-->>Player B: Push 'OnLeaveWorldInvitationUpdated' event Player B->>PGOS Backend: Accept the leave invitation PGOS Backend->>PGOS Backend: Remove B from the world(battle session) PGOS Backend-->>Player B: Push OnWorldBattleSessionUpdated with player battle session: Completed PGOS Backend-->>Player A: Push 'OnLeaveWorldInvitationUpdated' event: Completed PGOS Backend-->>Player B: Push 'OnLeaveWorldInvitationUpdated' event: Completed

Here is the InviteLeaveWorld API prototype:

/**
* Invite other players to leave a world together.
*
* @param Params Request struct for inviting other players to leave a world together.
* @param ResultDelegate The result delegate after the API execution ends, and it will be called in the GAME THREAD.
*/
void InviteLeaveWorld(
const FClientInviteLeaveWorldParams& Params,
FPgosClientOnInviteLeaveWorld ResultDelegate) const;

struct PGOSSDKCPP_API FClientInviteLeaveWorldParams
{
/** World config name that request to join. */
FString world_config;
/**
* Player IDs who will be invited to leave the same world. (Must include at least 1 player)
* These invited players will receive the 'OnLeaveWorldInvitationUpdated' event on their game client.
* Players who accept (AcceptLeaveWorldInvitation API) the invitation will be removed from the world.
* Players who reject (RejectLeaveWorldInvitation API) the invitation will skip the invitation and will not be affected.
* The inviter (current player) will also receive the 'OnLeaveWorldInvitationUpdated' event to know the status of the invitation being accepted or rejected.
* The inviter will automatically leave the world.
*/
TArray<FString> invited_player_ids;
};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto World = IPgosSDKCpp::Get().GetClientWorldAPI();
if (World)
{
FClientInviteLeaveWorldParams Params;
Params.world_config = TEXT("World_001");
Params.invited_player_ids.Add("player_to_invite");
World->InviteLeaveWorld(Params, [](const FPgosResult& Ret, const FClientInviteLeaveWorldResult* Data)> ResultCallback) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("InviteLeaveWorld Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("InviteLeaveWorld Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

5.4 Query World Information

PGOS provides APIs to query information related to the World.

5.4.1 Query My World Battle Sessions

A player can use the QueryMyWorldBattleSessions API to query their world battle sessions.

Here is the QueryMyWorldBattleSessions API prototype:

/**
* Specify the world config names to query if the current player has joined them.
* Corresponding battle session id will be returned for the worlds in which the player has joined.
*/
void QueryMyWorldBattleSessions(
const FClientQueryMyWorldBattleSessionsParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientQueryMyWorldBattleSessionsResult* Data)> Callback) const;

struct FClientQueryMyWorldBattleSessionsParams
{
/** Worlds to query. */
TArray<FString> world_configs;
};

struct FClientQueryMyWorldBattleSessionsResult
{
/** A map from world config name to the battle session. Only contains the worlds that the player has joined. */
TMap<FString, FClientBattleSessionInfo> world_battle_sessions;
};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto World = IPgosSDKCpp::Get().GetClientWorldAPI();
if (World)
{
FClientQueryMyWorldBattleSessionsParams Params;
Params.world_configs.Add(TEXT("World_001"));
Params.world_configs.Add(TEXT("World_002"));
World->QueryMyWorldBattleSessions(Params, [](const FPgosResult& Ret, const FClientQueryMyWorldBattleSessionsResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("QueryMyWorldBattleSessions Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("QueryMyWorldBattleSessions Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

5.4.2 Query Player World Battle Sessions

A player can use the QueryPlayerWorldBattleSessions API to query the world battle sessions of another player.

Here is the QueryPlayerWorldBattleSessions API prototype:

/**
* Specify the world config names to query if a player has joined them.
* Corresponding battle session id will be returned for the worlds in which the player has joined.
*/
void QueryPlayerWorldBattleSessions(
const FClientQueryPlayerWorldBattleSessionsParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientQueryPlayerWorldBattleSessionsResult* Data)> Callback) const;

struct FClientQueryPlayerWorldBattleSessionsParams
{
/** Player to query. */
FString player_id;
/** Worlds to query. */
TArray<FString> world_configs;
};

struct FClientQueryPlayerWorldBattleSessionsResult
{
/** A map from world config name to the battle session. Only contains the worlds that the player has joined. */
TMap<FString, FClientBattleSessionInfo> world_battle_sessions;
};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto World = IPgosSDKCpp::Get().GetClientWorldAPI();
if (World)
{
FClientQueryPlayerWorldBattleSessionsParams Params;
Params.player_id = TEXT("11223344");
Params.world_configs.Add(TEXT("World_001"));
Params.world_configs.Add(TEXT("World_002"));
World->QueryPlayerWorldBattleSessions(Params, [](const FPgosResult& Ret, const FClientQueryPlayerWorldBattleSessionsResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("QueryPlayerWorldBattleSessions Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("QueryPlayerWorldBattleSessions Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

5.4.3 Query Join World Invitation

A player can use the QueryJoinWorldInvitation API to query the detail information of the join world invitation for the specified invitation ticket.

Here is the QueryJoinWorldInvitation API prototype:

/**
* Query the detail information of the join world invitation for the specified invitation ticket.
*/
void QueryJoinWorldInvitation(
const FClientQueryJoinWorldInvitationParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientQueryJoinWorldInvitationResult* Data)> Callback) const;

struct FClientQueryJoinWorldInvitationParams
{
/** The invitation ticket to query. */
FString invitation_ticket;
};

struct FClientQueryJoinWorldInvitationResult
{
/** The join world invitation detail information. */
FClientJoinWorldInvitationInfo info;
};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction(const FString& invitation_ticket)
{
auto World = IPgosSDKCpp::Get().GetClientWorldAPI();
if (World)
{
FClientQueryJoinWorldInvitationParams Params;
Params.invitation_ticket = invitation_ticket;
World->QueryJoinWorldInvitation(Params, [](const FPgosResult& Ret, const FClientQueryJoinWorldInvitationResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("QueryJoinWorldInvitation Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("QueryJoinWorldInvitation Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

5.5 Monitoring Events

Game developers need to monitor and handle the following events to ensure your worlds can function properly. You can read here to know how to monitor events.

5.5.1 OnWorldBattleSessionUpdated Event

The event will be triggered when battle session status changed. It is important to note that only when the battle session is in the Active status, you can obtain the valid IP and port of DS from the FClientBattleSessionInfo structure.

struct FClientWorldBattleSessionUpdatedEvt
{
/** The world configuration name associated with this update. */
FString world_config;
/** The updated details of the battle session that the current player joined. */
FClientBattleSessionInfo battle_session;
};

struct FClientBattleSessionInfo
{
/** The battle session id to query */
FString battle_session_id;
/** The Unix timestamp(in seconds) when the battle session was created. */
int64 created_time = -1;
/** The IP address of the DS holding the battle session, and it will be assigned the value when the status is 'Active'. */
FString ip_address;
/** The port of the DS holding the battle session, and it will be assigned the value when the status is 'Active'. */
int32 port = 0;
/** The status of the battle session. */
EClientBattleSessionStatus status = EClientBattleSessionStatus::Dummy;
/** The message of the battle status */
FString status_msg;
/** The Unix timestamp(in seconds) for the last update in the battle status */
int64 last_updated_time = -1;
/** The Unix timestamp(in seconds) when the player disconnected from game server. */
int64 terminated_time = -1;
/** Timeout for placing status */
int32 placing_timeout = -1;
/** Player battle session of current player */
FClientPlayerBattleSessionInfo cur_player_battle_session_info;
/** K-V pair property of a battle session */
TArray<FBattleProperty> battle_properties;
/** Custom string data of a battle session. Preset in matchmaking configuration */
FString battle_session_data;
/** What service that the battle session generated from */
EBattleSourceType source_type = EBattleSourceType::None;
/** Configuration name of matchmaking, lobby or world service */
FString configuration_name;
/** Mapping from bucket filter attribute name to attribute value. [for WORLD battle session only] */
TMap<FString, FString> bucket_path;
/** Ruleset name hit. Filled when battle session source type is BattleSourceType::Matchmaking */
FString ruleset_name;
/** True if the battle session is locked by game server. */
bool locked = false;
};

5.5.2 OnJoinWorldInvitationUpdated Event

The event is triggered when someone invites you to join a world or the invitation info has been updated. Typically, the player who accepts the invitation will receive the OnWorldBattleSessionUpdated event after the invitation status is marked as 'Completed'. However, if the player is already in the assigned battle session before the invitation, and has obtained the battle session's IP&port (which means he has received the relevant event of the battle session status being Active), then he may not be pushed the OnWorldBattleSessionUpdated event.

struct FClientJoinWorldInvitationUpdatedEvt
{
/** The join world invitation detail information. */
FClientJoinWorldInvitationInfo info;
};

struct FClientJoinWorldInvitationInfo
{
/** The world configuration name associated with the invitation ticket. */
FString world_config;
/** The player who initiated the invitation. */
FPlayerInfo inviter;
/** The ticket of the join world invitation. */
FString invitation_ticket;
/** The status of the Join World invitation. */
EClientJoinWorldInvitationStatus status = EClientJoinWorldInvitationStatus::Dummy;
/** The error message when the status is 'Error'. */
FString error_msg;
/** The current player's action to this join world invitation. */
EClientJoinWorldInvitationAction my_action_to_invitation = EClientJoinWorldInvitationAction::Undecided;
/** Players who have accepted the invitation. (including the inviter) */
TArray<FPlayerInfo> accepted_players;
/** Players who have rejected the invitation. */
TArray<FPlayerInfo> rejected_players;
/** Players who have not yet made a decision. */
TArray<FPlayerInfo> undecided_players;
/** The Unix timestamp(in seconds) when the invitation was created. */
int64 invitation_created_time = 0;
/**
* The maximum time(in seconds) to wait for all invited players to accept/reject.
* After the time (invitation_created_time+invitation_timeout), the event status will be updated to 'Completed'.
*/
int32 invitation_timeout = 0;
/**
* The world battle session joined by the players who accepted the invitation.
* It will be empty until the 'status' changes to 'JoinWorldInvitationStatus::Completed'.
*/
FString battle_session_id;
};

5.5.3 OnWorldBattlePropertiesUpdated Event

The event will be pushed to the player game client in the world battle session when DS updates the battle properties.

struct FClientWorldBattlePropertiesUpdatedEvt
{
/** World config name. */
FString configuration_name;
/** World battle session id. */
FString battle_session_id;
/** */
TArray<FBattleProperty> battle_properties;
};

5.5.4 OnLeaveWorldInvitationUpdated Event

The event is triggered when someone invites you to leave a world or the invitation info has been updated. Typically, the player who accepts the invitation will receive the OnWorldBattleSessionUpdated event after the invitation status is marked as 'Completed'. However, if the player is already in the assigned battle session before the invitation, and has obtained the battle session's IP&port (which means he has received the relevant event of the battle session status being Active), then he may not be pushed the OnWorldBattleSessionUpdated event.

struct PGOSSDKCPP_API FClientLeaveWorldInvitationUpdatedEvt
{
/** The leave world invitation detail information. */
FClientLeaveWorldInvitationInfo info;
};

/** The leave world invitation detail information. */
struct PGOSSDKCPP_API FClientLeaveWorldInvitationInfo
{
/** The world configuration name associated with the invitation ticket. */
FString world_config;
/** The player who initiated the invitation. */
FPlayerInfo inviter;
/** The ticket of the leave world invitation. */
FString invitation_ticket;
/** The status of the leave World invitation. */
EClientLeaveWorldInvitationStatus status = EClientLeaveWorldInvitationStatus::Dummy;
/** The error message when the status is 'Error'. */
FString error_msg;
/**
* The current player's action to this leave world invitation.
* Call 'AcceptLeaveWorldInvitation' API to accept the invitation and will receive the 'OnLeaveWorldInvitationUpdated' event until the status is 'Completed'.
* Alternatively, call the 'RejectLeaveWorldInvitation' API to reject the invitation and stop receiving the 'OnLeaveWorldInvitationUpdated' event for this 'invitation_ticket'.
* Note: Once you have made your choice, you cannot change your decision.
*/
EClientLeaveWorldInvitationAction my_action_to_invitation = EClientLeaveWorldInvitationAction::Undecided;
/** Players who have accepted the invitation. (including the inviter) */
TArray<FPlayerInfo> accepted_players;
/** Players who have rejected the invitation. */
TArray<FPlayerInfo> rejected_players;
/** Players who have not yet made a decision. */
TArray<FPlayerInfo> undecided_players;
/** The Unix timestamp(in seconds) when the invitation was created. */
int64 invitation_created_time = 0;
/**
* The maximum time(in seconds) to wait for all invited players to accept/reject.
* After the time (invitation_created_time+invitation_timeout), the event status will be updated to 'Completed'.
*/
int32 invitation_timeout = 0;
};

6. Integrate Worlds Into Your Game Server

6.1 Preparations

Please go through DS Hosting Integration before reading this section. This will help you understand the basics of managing battle sessions on the game server side.

6.2 Battle Session in World Service

The following describes the key fields of the battle session generated by World service for the game server:

  • battle_properties: Custom K-V pairs of a battle session. Preset in world configuration and can be updated by calling the SetBattleProperties interface.
  • bucket_path: Bucket path of the current world battle session. This is a mapping from the world filter attribute name to the attribute value. The bucket path can not be modified after a battle session is created.
  • configuration_name: Configuration name of the world that the battle session belongs to.
  • max_players: Maximum number of players in the battle session. The value of this field is variable and will trigger an OnBattleSessionUpdated event when it changes.
  • total_players: Total number of players in the battle session. Players whose battle session status is COMPLETED will be excluded.

6.3 Track Players in Battle Session

This section describes how the game server tracks player changes (join and leave) in a battle session.

6.3.1 Handle New Player Joining

Whenever a new player is added to a battle session by world service, the PGOS Server SDK's OnBattleSessionUpdated event will be triggered. The game server could listen to this event to identify if the battle session is full or to allocate game resources for the player in advance before the player's game client connects. Key fields of the event are as below:

  • update_reason: The value of this field should be WORLD_ADDED_PLAYERS in a battle session generated from World service.
  • new_player_battle_sessions: New players are added to the battle session.
  • max_players: Maximum number of players in the battle session. The value of this field is variable and will trigger an OnBattleSessionUpdated event when it changes.
  • total_players: Total number of players in the battle session. Only players whose battle session status is COMPLETED will be excluded.
Caution

Caution that this event is asynchronous with the game client's JoinWorld calling. Theoretically, there are cases where a player's connection request arrives before this event.

Therefore, please be careful not to use the data from this interface as a basis for whether to accept a player 's connection. We provide a interface AcceptPlayerBattleSession to handle this.

6.3.2 Handle Player Leaving

Generally speaking, there are two ways a player can leave a World or a world battle session:

  1. Removing a player from the game server: The game server can actively remove a player from the battle session by calling the RemovePlayerBattleSession interface. This action will trigger the OnWorldBattleSessionUpdated event in the PGOS client SDK, where the player battle session status will be filled as Completed.
  2. Leaving initiated by the game client: When the game client actively calls interfaces such as LeaveWorld, AcceptJoinWorldInvitation, or AcceptLeaveWorldInvitation. The player will be removed from the current world battle session, and the event OnPlayerBattleSessionsTerminated will be in the PGOS server SDK.
Tips

We recommend that the game server invokes RemovePlayerBattleSession for players who have not logged in to the game server for a long time to prevent the battle session from becoming a ghost(filled with players who will hardly ever connect again) and wasting resources.

6.3.3 Query Players in Battle Session

Use DescribePlayerBattleSessions interface to enumerate players in a battle session.

Tips

This is a multi-purpose interface:

  1. Query single player's session info by populating the DescribePlayerBattleSessionsParams::player_battle_session_id field.
  2. Enumerate all players in a battle session by leaving the DescribePlayerBattleSessionsParams::player_battle_session_id field empty.

Query single player's session info:

#include "PgosSDKCpp/Public/PgosSDKCpp.h"
#include "PgosSDKCpp/Public/Core/PgosErrorCode.h"

void SomeUObjectClass::DescribeSinglePlayerBattleSession()
{
auto hosting = IPgosSDKCpp::Get().GetServerHostingAPI();
FDescribePlayerBattleSessionsParams params;
params.battle_session_id = "some_battle_session_id";
params.player_battle_session_id = "some_player_battle_session_id";
if (world)
{
world->DescribePlayerBattleSessions(params,
[](const FPgosResult& Ret, const DescribePlayerBattleSessionsResult* Response) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("DescribePlayerBattleSessions Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("DescribePlayerBattleSessions Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

Enumerating all players in a battle session:

#include "PgosSDKCpp/Public/PgosSDKCpp.h"
#include "PgosSDKCpp/Public/Core/PgosErrorCode.h"

void SomeUObjectClass::DescribePlayerBattleSessions()
{
auto hosting = IPgosSDKCpp::Get().GetServerHostingAPI();
FDescribePlayerBattleSessionsParams params;
params.battle_session_id = "some_battle_session_id";
params.offset = 0;
params.count = 10;
if (world)
{
world->DescribePlayerBattleSessions(params,
[](const FPgosResult& Ret, const DescribePlayerBattleSessionsResult* Response) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("DescribePlayerBattleSessions Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("DescribePlayerBattleSessions Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

6.3.4 Handle Pending Player Battle Sessions

New players may join the battle session with a Pending player battle session in the following two scenarios:

  1. The requested world battle session is locked by the DS.
  2. The DS Approval property of the world configuration is set to Require approval for all session join requests.

A Pending player battle session need to be approved by DS, otherwise it cannot be accept by AcceptPlayerBattleSession interface. DS should call ReservedPlayerBattleSession to approve a Pending player battle session. A Pending player battle session still occupies a slot in the world session, so it is important to promptly call RemovePlayerBattleSession to explicitly reject the Pending player battle session.

The player battle session state machine is as shown in the following diagram:

stateDiagram-v2 state if_arrrove <<choice>> [*] --> if_arrrove: A new player join battle sesison if_arrrove -->Pending: Need approve Pending-->Reserved: [Approve] a Pending player by calling \nReservePlayerBattleSession Pending-->Completed: [Reject] a Pending player by calling \nRemovePlayerBattleSession if_arrrove --> Reserved: Not need approve Reserved-->Active: AcceptPlayerBattleSession Active-->Completed: RemovePlayerBattleSession

6.4 Update Battle Properties

Use SetBattleProperties interface to update battle session properties.

This operation will trigger OnWorldBattlePropertiesUpdated event in PGOS client SDK. Please subscribe to this event in your game client to receive the latest battle properties in a timely manner.

Tips

This interface is used to update the battle properties data in full.

#include "PgosSDKCpp/Public/PgosSDKCpp.h"
#include "PgosSDKCpp/Public/Core/PgosErrorCode.h"

void SomeUObjectClass::SetBattleProperties()
{
auto hosting = IPgosSDKCpp::Get().GetServerHostingAPI();
FSetBattlePropertiesParams params;
params.battle_session_id = "some_battle_session_id";
params.battle_properties = [];
if (world)
{
world->SetBattleProperties(params,
[](const FPgosResult& Ret, const SetBattlePropertiesResult* Response) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SetBattleProperties Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SetBattleProperties Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

6.5 Lock/Unlock Battle Session

Use LockWorldBattleSession interface to lock a world battle session. PGOS cannot add new players to a locked battle session.

#include "PgosSDKCpp/Public/PgosSDKCpp.h"
#include "PgosSDKCpp/Public/Core/PgosErrorCode.h"

void SomeUObjectClass::LockWorldBattleSession()
{
auto hosting = IPgosSDKCpp::Get().GetServerHostingAPI();
FLockWorldBattleSessionParams params;
params.battle_session_id = "some_battle_session_id";
if (world)
{
world->LockWorldBattleSession(params,
[](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("LockWorldBattleSession Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("LockWorldBattleSession Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

Use UnlockWorldBattleSession interface to unlock a world battle session. New players can be added to the battle session after it is locked.

#include "PgosSDKCpp/Public/PgosSDKCpp.h"
#include "PgosSDKCpp/Public/Core/PgosErrorCode.h"

void SomeUObjectClass::UnlockWorldBattleSession()
{
auto hosting = IPgosSDKCpp::Get().GetServerHostingAPI();
FUnlockWorldBattleSessionParams params;
params.battle_session_id = "some_battle_session_id";
if (world)
{
world->UnlockWorldBattleSession(params,
[](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("UnlockWorldBattleSession Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("UnlockWorldBattleSession Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

6.6 Battle Session Cleaning Up

Battle sessions generated in the world service are likely to be persistent, with players joining and leaving constantly. However, the game server still needs to keep an eye on and clean up battle sessions in time that have been idle for a long time. Because CVMs with running battle sessions will not be scaled down, which may result in a waste of resources.

You can solve this problem in two ways:

  1. Enable PGOS to automatically complete the recycling of idle battle sessions by adjusting the configuration item World Session Recycling Time.
  2. DS actively ends the battle session at the right time by calling the interface ProcessEnding.

7. Check the Operation of World

7.1 Check Runtime World Buckets

Open the Buckets tab in Battle/World to view all runtime world buckets for the current title region.

  • Bucket Path / Bucket Values: A unique identifier for each bucket, which is composed of the values of the filter attributes.
  • Player Count: The number of players currently assigned to this bucket.
  • Battle Session Count: The number of battle sessions currently running in this bucket.

image-20231121102440134

7.2 Check Battle Sessions

Open the Battle/Battle Session page to view the battle session logs. All battle sessions created in the current title region are included here.

image-20231121103021799

Click on the Battle Session ID to view the details:

  • Player Battle Session: List of all current player battle sessions in the battle. If a player joins this battle session multiple times, the list will only contain information about the last session the player joined.
  • Player In-out: Change logs of player battle session status.
  • World Bucket Path: World bucket of the battle session.

image-20231121111313750

8. Key Errors Handling

These are key errors but not all of them. For the errors not on the list, the error message should already indicate the problem. Please contact us if you encounter obscure error messages or any other critical errors.

Error CodeRelevant APIHandling Suggestion
kSdkInvalidParameterInviteJoinWorld
InviteJoinWorldBattleSession
AcceptJoinWorldInvitation
RejectJoinWorldInvitation
The input parameter value is invalid, please refer to error msg for details
kBackendDSMInvalidAttributesJoinWorld
InviteJoinWorld
Illegal filter_attributes populated in Join requests. This error cannot be recovered by retrying.
kBackendDSMInvalidWorldConfigJoinWorld
InviteJoinWorld
The specified world config does not exist when requesting to join world.
kBackendDSMWorldSessionPlacementFailedJoinWorld
InviteJoinWorld
Failed to place a newly created battle session when requesting to join a world. Can usually be resolved by retrying.
kBackendDSMWorldSessionAddPlayerFailedJoinWorld
InviteJoinWorld
Failed to join an existing battle session when requesting to join a world. Can usually be resolved by retrying.
kBackendDSMBucketPathExceedLimitJoinWorld
InviteJoinWorld
The number of BucketPaths has exceeded the limit when requesting to join world.Please check if the BucketPath is not being misused when this error occurs.
kBackendDSMPlayerCountExceedLimitJoinWorld
InviteJoinWorld
Players in the world joining request has exceeded WorldConfig's maximum number of people in a single battle session.
kBackendDSMWorldSessionFullJoinWorld
InviteJoinWorld
World session is full or has not enough slots for players to join.
kBackendDSMWorldSessionIsLockedJoinWorldBattleSession
InviteJoinWorldBattleSession
World session is unavailable to join because it's locked.
kBackendDSMWorldConfigMismatchJoinWorldBattleSession
InviteJoinWorldBattleSession
The world battle session does not belong to the specified world config.
kBackendDSMWorldPlayerInvitationConflictedInviteJoinWorld
InviteJoinWorldBattleSession
Player(s) in invitation list are already in another invitation procedure.
kBackendDSMWorldPlayerIDsDuplicatedInviteJoinWorld
InviteJoinWorldBattleSession
Player(s) in invitation list are duplicated.
kBackendDSMWorldPlayerIDsIllegalInviteJoinWorld
InviteJoinWorldBattleSession
Player(s) in invitation list are illegal.
kBackendDSMLeaveWorldSessionFailedLeaveWorldFailed to leave a world. Can usually be resolved by retrying.
kSdkNetworkErrorAll Network APInetwork error, check error msg for detail.