Lobby
1. Overview
The Lobby Service works with the DS Hosting and Management service and provides a way for the game to gather players together to start a battle. A player can create a private lobby to play battles with specific people or create an open lobby which anyone can join. The Lobby Service also provides lobby management, search, text chat, and other features.
2. Architecture Diagram
The Lobby Service includes two types of interactions:
- Calling lobby interfaces using the PGOS SDK.
- Receiving lobby notifications pushed by the Lobby Service when specific events happen.
A lobby will only be destroyed when the following event happens:
- The lobby owner dismissed the lobby
- The last member has left the lobby
- The lobby has been unactive for more than 7 days
A player can only leave lobby in the following ways:
- The client explicitly invokes
LeaveLobby
function - The lobby owner can remove any member out of lobby
- When player get disconnected from PGOS, he will be automatically removed from lobby
Please note that when a battle is started by lobby, players are all treated as still in lobby and core lobby features still work as normal.
3. Create a Lobby Configuration
You must create a Lobby Configuration before using the Lobby Service to implement your own features. A lobby configuration defines the basic lobby rules and is required by the PGOS SDK to create a lobby.
Since the Lobby Service works with the DS Hosting and Management service, make sure there is an available DS placer for placing battle sessions. If not, create one first. Refer to the following link for more details: Create a Placer.
Next, follow the steps below:
Open the PGOS Web Portal and enter Lobby page. Create a new lobby configuration or clone from an existing one.
Fill in the configuration details.
- Name: The configuration name, which must start with a letter and have a maximum of 128 characters. The name must be unique within the title region.
- Preparing Timeout: The maximum length of time for the Preparing stage before all the lobby players enter into battle. 0s means preparation does not time out. The maximum value is 3600s.
- Default Placer: Choose a default placer for battle placement.
- Flexible Placer: You can set up various game modes and assign a specific placer to each mode. After initiating a battle, the system will determine the corresponding placer based on the game modes provided by the lobby. if the lobby's game modes are not specified, or if the game mode configuration cannot be found on the portal, the default placer will be used.
Teams Configuration [Simple Mode]
Start Battle Condition: The action when the lobby owner invokes the
StartBattle
interface depends on theReady
flag of lobby members:All Ready Otherwise Waiting
: The lobby will enter the Preparing stage, waiting for unready players to get ready.All Ready Otherwise Error
:StartBattle
interface will get an error if not all players are ready.Ignoring Ready Flag
: The lobby will enter the Playing stage directly, ignoring the players'Ready
flag.
Team Selection Strategy: Decide how to select slots for new lobby members between teams.
Balance player count
: To ensure a balanced number of players, prioritize joining the team with the fewest members and available slots.Concentrate players
: To concentrate the players, prioritize joining the team with the most members and available slots.
Teams: The team configurations in the lobby.
- Name: Name of the team. Must be unique in the lobby.
- Capacity: Maximum number of players in the team. The sum of the capacities of all the teams is the lobby capacity.
Teams Configuration [Advanced Mode]
In Advanced mode, you can create multiple team groups, and each team group can have its own configuration for the Start Battle Condition.
- Team Selection Strategy: Same as simple mode. Decide how to select slots between teams with the same priority.
- Team Groups: Allow to configure multiple team groups. Each group can configure multiple teams.
- Start Battle Condition: The action of the lobby owner when invoking the
StartBattle
interface depends on theReady
flag of lobby members. You can set up ‘start battle conditions' for each group.- Ignoring Ready Flag: The lobby will disregard the ready status of this group's members.
- All Ready Otherwise Waiting/All Ready Otherwise Error: The lobby will enter the Preparing stage or receive an error if not all members in this group are ready. When these two conditions occur simultaneously, the priority of 'All Ready Otherwise Error' is higher, so the lobby will receive an error.
4. Core Features
This section demonstrates basic functions of lobby service, for the error handling steps, please refer the last section.
4.1 Create a Lobby
In the API for lobby creation, the following options can be set:
lobby_config
(required): The name of a lobby configuration created via the PGOS Web Portal.name
(required): The name of the lobby.privacy
(required):LobbyPrivacy::Visible
orLobbyPrivacy::Invisible
. If set to Invisible, the lobby can only be accessed using the lobby id.enable_chatting
(optional): Passtrue
to create a chat channel for the lobby, orfalse
to not.password
(optional): password needed to join the lobby.kv_data
(optional): A map containing global custom lobby data.protection
(optional, since v0.23.0): the access protection policy of the lobby. Enumerated values are:PasswordNeeded
: Player need to give the password when joining a lobby. If this policy is selected, thepassword
must not be null or the create lobby interface will return a failure.FreeAccess
: No restrictions on players joining.ApplicationNeeded
: A join request will be sent to the owner after calling the JoinLobby interface, and the owner's approval is required to successfully join the lobby.game_mode
(optional): the game mode of the lobby. After starting the battle, the placer corresponding to the game mode on the portal will be used. If this game mode does not have a placer configured, the default placer configured on the portal will be used.
Call CreateLobby
from Lobby module as follows:
- UE C++
- UE BP
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::CreateLobby()
{
FClientCreateLobbyParams Params;
Params.lobby_config = TEXT("config_name");
Params.name = TEXT("A Lobby");
Params.privacy = EClientLobbyPrivacy::LobbyPrivacy_Visible;
Params.enable_chatting = true;
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->CreateLobby(Params, [](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("CreateLobbySuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("CreateLobbyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
#include "PgosClientLobbyAPI.h"
void SomeUObjectClass::CreateLobby()
{
FClientCreateLobbyParams Params;
Params.lobby_config = TEXT("config_name");
Params.name = TEXT("A Lobby");
Params.privacy = EClientLobbyPrivacy::LobbyPrivacy_Visible;
Params.enable_chatting = true;
PGOS_CALL_ASYNC_API(UPgosClientAPICreateLobby::CreateLobby,
this,
&SomeUObjectClass::OnCreateLobbySuccess,
&SomeUObjectClass::OnCreateLobbyFailed,
Params);
}
void SomeUObjectClass::OnCreateLobbySuccess(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("CreateLobbySuccess"))
}
void SomeUObjectClass::OnCreateLobbyFailed(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("CreateLobbyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
4.2 Team Slot
There are multiple teams with ordered player slots in each lobby,. PGOS's Lobby service starts indexing these slots from 1.
The team structure can be set in the lobby configuration.
When a lobby is created, the lobby owner is assigned to the first slot in the first team.
New players entering the lobby will automatically assigned to a team slot.
Lobby members can freely switch to unoccupied slots by calling API
SwitchTeamSlot
.Lobby members can send slot switch requests to others by calling API
RequestSwitchTeamSlot
.If the lobby is in the Preparing stage, switching the team slot will cause the lobby to fall back to the Waiting stage.
4.2.1 Switch to Unoccupied Slot
Events:
OnLobbyTeamUpdated
: Sent to all members in the lobby if the API callSwitchTeamSlot
is successful.
Call SwitchTeamSlot
from Lobby module as follows:
- UE C++
- UE BP
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::SwitchTeamSlot()
{
FString TargetTeamName = ObtainFromSomeWhere();
int32 TargetTeamSlot = ObtainFromSomeWhere();
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->SwitchTeamSlot(TargetTeamName, TargetTeamSlot, [](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnSwitchTeamSlotSuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnSwitchTeamSlotFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
#include "PgosClientLobbyAPI.h"
void SomeUObjectClass::SwitchTeamSlot()
{
FString TargetTeamName = ObtainFromSomeWhere();
int32 TargetTeamSlot = ObtainFromSomeWhere();
PGOS_CALL_ASYNC_API(UPgosClientAPISwitchTeamSlot::SwitchTeamSlot,
this,
&SomeUObjectClass::OnSwitchTeamSlotSuccess,
&SomeUObjectClass::OnSwitchTeamSlotFailed,
TargetTeamName,
TargetTeamSlot);
}
void SomeUObjectClass::OnSwitchTeamSlotSuccess(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnSwitchTeamSlotSuccess"))
}
void SomeUObjectClass::OnSwitchTeamSlotFailed(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnSwitchTeamSlotFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
4.2.2 Request Switch to Occupied Slot
Events:
OnLobbySwitchTeamSlotRequest
: This event will be sent to the requested member.OnLobbyTeamUpdated
: Sent to all members in the lobby if the slot-switch request has been confirmed.
Call SwitchTeamSlot
from Lobby module as follows:
- UE C++
- UE BP
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::RequestSwitchTeamSlot()
{
FString TargetTeamName = ObtainFromSomeWhere();
int32 TargetTeamSlot = ObtainFromSomeWhere();
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->RequestSwitchTeamSlot(TargetTeamName, TargetTeamSlot, [](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnRequestSwitchTeamSlotSuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnRequestSwitchTeamSlotFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
#include "PgosClientLobbyAPI.h"
void SomeUObjectClass::RequestSwitchTeamSlot()
{
FString TargetTeamName = ObtainFromSomeWhere();
int32 TargetTeamSlot = ObtainFromSomeWhere();
PGOS_CALL_ASYNC_API(UPgosClientAPIRequestSwitchTeamSlot::RequestSwitchTeamSlot,
this,
&SomeUObjectClass::OnRequestSwitchTeamSlotSuccess,
&SomeUObjectClass::OnRequestSwitchTeamSlotFailed,
TargetTeamName,
TargetTeamSlot);
}
void SomeUObjectClass::OnRequestSwitchTeamSlotSuccess(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnRequestSwitchTeamSlotSuccess"))
}
void SomeUObjectClass::OnRequestSwitchTeamSlotFailed(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnRequestSwitchTeamSlotFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
4.3 Lobby Custom Data
We provide two types of custom lobby data to assist in extending the lobby functionality for games.
Lobby global data: These key-value pairs apply to the entire game lobby.
- Only the owner of the a lobby can modify these properties.
- All players can read this data, whether or not they have joined the lobby.
- The lifetime of the lobby global data is consistent with the lobby.
- The lobby global data will write into battle properties when a battle session is started through the lobby.
Lobby player data: Each member in the game lobby has a unique set of member properties.
- This data is visible only to lobby members.
- Members can only modify their own member properties.
- Lobby player data will be removed when a player leaves the lobby.
Both the two type lobby custom data can be updated without modifying other lobby options.
SetLobbyGlobalData
: This interface is used for a complete update of the lobby global data. The data update operation will be notified to all lobby members through theOnLobbyGlobalCustomDataUpdated
event.SetLobbyPlayerData
: This interface is used for updating the custom data of a lobby member. The data update operation will be notified to all lobby members through theOnLobbyPlayerCustomDataUpdated
event.
Event OnLobbyDataUpdated
is deprecated since v0.23.0. Please use event OnLobbyGlobalCustomDataUpdated
and OnLobbyPlayerCustomDataUpdated
instead.
4.4 Join/Leave a Lobby
4.4.1 Join a Freely Accessible Lobby
Players are free to join lobbies which the protection option is LobbyProtection::FreeAccess
by calling JoinLobby
:
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::JoinLobby()
{
FClientJoinLobbyParams Params;
Params.lobby_id = TEXT("123456");
Params.password = "";
Params.join_token = "";
Params.payload = "";
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->JoinLobby(Params, [](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnJoinLobbySuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnJoinLobbyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
4.4.2 Join a Lobby with Password
Players need to provide a password to join lobbies which the protection option is LobbyProtection::PasswordNeeded
by calling JoinLobby
:
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::JoinLobby()
{
FClientJoinLobbyParams Params;
Params.lobby_id = TEXT("123456");
Params.password = "password";
Params.join_token = "";
Params.payload = "";
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->JoinLobby(Params, [](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnJoinLobbySuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnJoinLobbyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
4.4.3 Join a Lobby by Invitation
The join_token
obtained in the OnLobbyInvitation
event can be used as the token for joining the invited lobby, which can be used to successfully join a lobby if the lobby protection option is not FreeAccess
.
The workflow of lobby invitation is as fellows:
The sample code is as follows:
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::JoinLobby()
{
FClientJoinLobbyParams Params;
Params.lobby_id = TEXT("123456");
Params.password = "";
Params.join_token = "join token";
Params.payload = "";
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->JoinLobby(Params, [](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnJoinLobbySuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnJoinLobbyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
4.4.4 Join a Application Needed Lobby
A player can send a join request to a lobby owner by calling the JoinLobby
interface when the lobby's protection policy is LobbyProtection::ApplicationNeeded
, and the house owner will decide whether to approve the join request.
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::JoinLobby()
{
FClientJoinLobbyParams Params;
Params.lobby_id = TEXT("123456");
Params.password = "";
Params.join_token = "";
Params.payload = "custom payload data";
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->JoinLobby(Params, [](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnJoinLobbySuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnJoinLobbyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
The OnLobbyJoinRequest
event will be pushed to the lobby owner and JoinLobbyParams::payload
data will be written to the event.
Lobby owner can call the ApproveJoinRequest/RejectJoinRequest
interface to handle join requests from players.
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::ApproveJoinRequest()
{
FApproveJoinRequestParams Params;
Params.lobby_id = TEXT("123456");
Params.player_id = "";
Params.token = "token from event OnLobbyJoinRequest";
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->ApproveJoinRequest(Params, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("ApproveJoinRequest"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("ApproveJoinRequest: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
void SomeUObjectClass::RejectJoinRequest()
{
FRejectJoinRequestParams Params;
Params.lobby_id = TEXT("123456");
Params.player_id = "";
Params.token = "token from event OnLobbyJoinRequest";
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->RejectJoinRequest(Params, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("RejectJoinRequest"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("RejectJoinRequest: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
Event OnLobbyJoinRequestApproved/OnLobbyJoinRequestRejected
will be triggered to the game client once the request has been processed by the lobby owner.
If the lobby owner approves the request, PGOS will immediately add the request initiato to the lobby.
It is important to note that there are a number of scenarios that can cause the OnLobbyJoinRequestApproved
calling to fail:
- The request has expired. Each request token has a validity period of 10min.
- The player is offline. PGOS will not add offline players to lobby.
4.4.5 Join a Lobby with Groups
Try to join the specified groups in the lobby according to the specified priority value from small to large. If no available slots are found in all specified groups, the interface will return a failure. Slot selection within a group will follow the Team Selection Strategy configuration on the lobby configuration.
Teams Configuration
of the lobby configuration needs to be switched to advanced mode to enable team groups.
Interface prototype:
/**
* Join the specified group in the specified lobby. Try to join the specified groups in the lobby according to the specified priority value from small to large.
* Slot selection within the group follows the Team Selection Strategy configuration on the portal.
* The portal's lobby configuration needs to be switched to advanced mode.
*
* @param Params The information needed when join the lobby.
*/
void JoinLobbyWithGroups(
const FClientJoinLobbyWithGroupsParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientLobbyDetailInfo* Data)> Callback) const;
struct FClientJoinLobbyWithGroupsParams : public FClientJoinLobbyParams
{
/** Try to join the specified groups in the lobby according to the specified priority value from small to large. key: group, value: priority. (priority cannot be less than 0) */
TMap<FString, int32> selected_groups;
};
struct FClientJoinLobbyParams
{
/** The lobby id to join */
FString lobby_id;
/** The lobby password. If the lobby is not encrypted, it doesn't need to be filled. */
FString password;
/**
* It is from the event of 'OnLobbyInvitation' if the player is invited to join a lobby,
* Otherwise it doesn't need to be filled.
*/
FString join_token;
/** Join request payload will be passed to the lobby owner in OnLobbyJoinRequest event for a lobby with LobbyProtection::ApplicationNeeded. */
FString payload;
};
Example Code:
#include "PgosSDKCpp.h"
void SomeUObjectClass::SomeFunction()
{
auto Lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (Lobby)
{
FClientJoinLobbyWithGroupsParams Params;
// Fill Params
Lobby->JoinLobbyWithGroups(Params, [](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("JoinLobbyWithGroups Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("JoinLobbyWithGroups Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
4.4.6 Join a Lobby with Teams
Try to join the specified teams in a lobby according to the specified priority value from small to large. If no available slots are found in all specified teams, the interface will return a failure. When multiple teams have the same priority, slot selection follows the Team Selection Strategy configuration on the portal.
The portal's lobby configuration needs to be switched to advanced mode.
Interface prototype:
/**
* Join the specified team in the specified lobby. Try to join the specified teams in the lobby according to the specified priority value from small to large.
* When multiple teams have the same priority, slot selection follows the Team Selection Strategy configuration on the portal.
* The portal's lobby configuration needs to be switched to advanced mode.
*
* @param Params The information needed when join the lobby.
*/
void JoinLobbyWithTeams(
const FClientJoinLobbyWithTeamsParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientLobbyDetailInfo* Data)> Callback) const;
struct FClientJoinLobbyWithTeamsParams : public FClientJoinLobbyParams
{
/** Try to join the specified teams in the lobby according to the specified priority value from small to large. key: team name, value: priority. (priority cannot be less than 0) */
TMap<FString, int32> selected_teams;
};
Example Code:
#include "PgosSDKCpp.h"
void SomeUObjectClass::SomeFunction()
{
auto Lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (Lobby)
{
FClientJoinLobbyWithTeamsParams Params;
// Fill Params
Lobby->JoinLobbyWithTeams(Params, [](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("JoinLobbyWithTeams Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("JoinLobbyWithTeams Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
4.4.7 Quick join with filter data
PGOS will attempt to place the player into a lobby instance that meets the lobby global kv data filtering conditions, note that this operation does not guarantee success. If the QuickJoin
interface returns a failure, the game can perform several automatic retries or allow the player to initiate a retry request.
Lobbies that meet any of the following conditions CANNT be joined via the QuickJoin
interface:
- Lobby with none
Waiting
status. - Invisible lobby.
- Lobby with none
FreeAccess
protection strategy.
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::TryQuickJoin()
{
FQuickJoinLobbyParams Params;
Params.lobby_config = TEXT("123456");
Params.lobby_data;
Params.selected_groups;
Params.selected_teams;
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->QuickJoinLobby(Params, [](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnQuickJoinSuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnQuickJoinFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
4.4.8 Leave Lobby
The player leaves the lobby with the following scenarios:
- Leave a lobby by calling
LeaveLobby
. - Players already in a lobby who join a new lobby will leave their previous lobby.
- Players offline.
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::LeaveLobby()
{
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->LeaveLobby([](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnLeaveLobbySuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnLeaveLobbyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
4.4.9 Related Events
Event Name | Event Description |
---|---|
OnLobbyMemberJoined | The event will be triggered when someone joined current lobby. |
OnLobbyMemberLeft | The event will be triggered when someone left current lobby. |
OnLobbyInvitation | The event will be triggered when receive lobby invitations. |
OnLobbyJoinRequest | The event will be triggered when a player request to join a lobby with protection type LobbyProtection::ApplicationNeeded. |
OnLobbyJoinRequestApproved | The event will be triggered when a join request has been approved by lobby owner. |
OnLobbyJoinRequestRejected | The event will be triggered when a join request has been reject by lobby owner. |
4.5 Lobby Invitations
Anyone who has joined a lobby can invite other players to join the lobby. When a player receives a lobby invitation, they can either accept the invitation to join the lobby or ignore it.
Events:
OnLobbyInvitation
: Sent to the invited player. The player can accept the invitation to join the lobby or ignore it.
Call InvitePlayer
from Lobby module as follows:
- UE C++
- UE BP
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::InvitePlayer()
{
FString PlayerId = ObtainFromSomeWhere();
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->InvitePlayer(PlayerId, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnInviteToLobbySuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnInviteToLobbyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
#include "PgosClientLobbyAPI.h"
void SomeUObjectClass::InvitePlayer()
{
FString PlayerId = ObtainFromSomeWhere();
PGOS_CALL_ASYNC_API(UPgosClientAPIInvitePlayer::InvitePlayer,
this,
&SomeUObjectClass::OnInviteToLobbySuccess,
&SomeUObjectClass::OnInviteToLobbyFailed,
PlayerID);
}
void SomeUObjectClass::OnInviteToLobbySuccess(FPgosResult Error)
{
UE_LOG(LogTemp, Log, TEXT("OnInviteToLobbySuccess"))
}
void SomeUObjectClass::OnInviteToLobbyFailed(FPgosResult Error)
{
UE_LOG(LogTemp, Log, TEXT("OnInviteToLobbyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
4.6 Search for a Lobby
4.6.1 Basic Search Capabilities
Lobbies can be searched using one or more of the options below:
lobby_config
: The name of a lobby configuration.status
: You can search lobbies by status:LobbyStatus::Waiting
,LobbyStatus::Preparing
orLobbyStatus::Playing
. Alternatively, passLobbyStatus::Dummy
to the API to search lobbies with any status.protection
: You can search lobbies by the protection typeLobbyProtection::PasswordNeeded
orLobbyProtection::PasswordUnneeded
. Alternatively, passLobbyProtection::Dummy
to the API to search lobbies with any type of protection.order_by
: Various sorting options are available for search results,LOBBY_ORDER_BY_DEFAULT
: Sorting by status, then sorting by create time.LOBBY_ORDER_BY_CREATE_TIME
: Sort by lobby create time.LOBBY_ORDER_BY_UPDATE_TIME
: Sort by lobby last update time.LOBBY_ORDER_BY_JOINED_MEMBER_COUNT
: Sort by joined member count.LOBBY_ORDER_BY_MISSING_MEMBER_COUNT
: Sort by empty slot count.
order_type
: Order direction of the search result,LOBBY_ORDER_TYPE_ASCENDING
: Ascending order for search result.LOBBY_ORDER_TYPE_DESCENDING
: Descending order for search result.
❗Note
- Lobbies where
privacy
is set toLobbyPrivacy::Invisible
can only be found using the exactlobby_id
.- Set
count
inFClientSearchLobbyParams
to 0 to only query the number of lobbies meeting the search criteria.- Set
count
inFClientSearchLobbyParams
to a non-zero value to query both the count and a list of lobbies that meet the search criteria up to the number set incount
.
Call SearchLobby
from Lobby module as follows:
- UE C++
- UE BP
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::SearchLobby()
{
FClientSearchLobbyParams Params;
Params.offset = 0;
Params.count = 20;
Params.protection = EClientLobbyProtection::LobbyProtection_PasswordUnneeded;
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->SearchLobby(Params, [](const FPgosResult& Ret, const FClientSearchLobbyResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnSearchLobbySuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnSearchLobbyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
#include "PgosClientLobbyAPI.h"
void SomeUObjectClass::SearchLobby()
{
FClientSearchLobbyParams Params;
Params.offset = 0;
Params.count = 20;
Params.protection = EClientLobbyProtection::LobbyProtection_PasswordUnneeded;
PGOS_CALL_ASYNC_API(UPgosClientAPISearchLobby::SearchLobby,
this,
&SomeUObjectClass::OnSearchLobbySuccess,
&SomeUObjectClass::OnSearchLobbySuccessFailed,
Params);
}
void SomeUObjectClass::OnSearchLobbySuccess(
FPgosResult Error, FClientSearchLobbyResult SearchResult)
{
UE_LOG(LogTemp, Log, TEXT("OnSearchLobbySuccess"))
}
void SomeUObjectClass::OnSearchLobbyFailed(
FPgosResult Error, FClientSearchLobbyResult SearchResult)
{
UE_LOG(LogTemp, Log, TEXT("OnSearchLobbyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
4.6.2 Custom Filter Options
The SearchLobby
interface allows filtering searching result with Lobby Global Data. Games can freely modify the lobby global data within the lobby instance and configure the fields that need to be filtered in the SearchLobbyParams::lobby_data
parameter of the SearchLobby
interface.
Two types of filtering are supported,
SearchLobbyDataFilter::equal_values
: means to filter lobby instances that match any value in the values list for given key.SearchLobbyDataFilter::not_equal_values
: lobby instances that match any value in the value list for given key will not appear in the search results.
4.6.3 Example for Custom Filter Options
Here is a typical example of defining and filtering game modes using global data.
First, define the lobby instance data set:
lobby: 001
kv_data:
"game_mode":"solo_adventure"
lobby: 002
kv_data:
"game_mode":"co_adventure"
lobby: 003
kv_data:
"game_mode":"royale"
lobby: 004
kv_data:
"game_mode":"roguelike"
Here're examples of SearchLobbyDataFilter
and the corresponding filtered results.
Filter all lobbies with game modes being roguelike
and royale
:
# Show case of SearchLobbyParams::lobby_data
"game_mode":
equal_values: ["roguelike", "royale"]
not_equal_values: []
Filter all lobbies with game modes not being roguelike
and royale
:
# Show case of SearchLobbyParams::lobby_data
"game_mode":
equal_values: []
not_equal_values: ["roguelike", "royale"]
4.7 Lobby Management
The owner of the lobby is able to:
- Modify lobby options
- Kick out members
- Dismiss the lobby
- Start a battle
- Transfer ownership to other members
4.7.1 Modify Lobby Options
After the lobby is created, the lobby owner can modify the following lobby options:
name
password
privacy
kv_data
game_mode
❗ Note
All the values passed to the API will replace the existing values, except
kv_data
.kv_data
will only be valid if it is not empty. To clearkv_data
, use theSetLobbyData
API and pass an empty map to it.
Events:
OnLobbyInfoUpdated
: Sent to all members of the lobby when the lobby owner updates the lobby options.
Call EditLobbyInfo
from Lobby module as follows:
- UE C++
- UE BP
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::EditLobbyInfo()
{
FClientEditLobbyInfoParams Params;
Params.name = TEXT("New Name");
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->EditLobbyInfo(Params, [](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnEditLobbySuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnEditLobbyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
#include "PgosClientLobbyAPI.h"
void SomeUObjectClass::EditLobbyInfo()
{
FClientEditLobbyInfoParams Params;
Params.name = TEXT("New Name");
PGOS_CALL_ASYNC_API(UPgosClientAPIEditLobbyInfo::EditLobbyInfo,
this,
&SomeUObjectClass::OnEditLobbySuccess,
&SomeUObjectClass::OnEditLobbyFailed,
Params);
}
void SomeUObjectClass::OnEditLobbySuccess(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnEditLobbySuccess"))
}
void SomeUObjectClass::OnEditLobbyFailed(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnEditLobbyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
4.7.2 Set Lobby Data
Lobby data can be updated without modifying other lobby options. Please note that the data passed to the SetLobbyData
API will replace the existing data, that is, if you pass an empty map to the API, the lobby data will be cleared.
Tip:
You can use lobby data to store custom lobby options such as the game mode and game map.
Events:
OnLobbyDataUpdated
: Sent to all members of the lobby when the lobby owner updates the custom lobby data.
Call SetLobbyData
from Lobby module as follows:
- UE C++
- UE BP
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::SetLobbyData()
{
TMap<FString, FString> CustomData;
CustomData.Add(TEXT("GameMode"), TEXT("5v5"));
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->SetLobbyData(CustomData, [](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnSetLobbyDataSuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnSetLobbyDataFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
#include "PgosClientLobbyAPI.h"
void SomeUObjectClass::SetLobbyData()
{
TMap<FString, FString> CustomData;
CustomData.Add(TEXT("GameMode"), TEXT("5v5"));
PGOS_CALL_ASYNC_API(UPgosClientAPISetLobbyData::SetLobbyData,
this,
&SomeUObjectClass::OnSetLobbyDataSuccess,
&SomeUObjectClass::OnSetLobbyDataFailed,
CustomData);
}
void SomeUObjectClass::OnSetLobbyDataSuccess(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnSetLobbyDataSuccess"))
}
void SomeUObjectClass::OnSetLobbyDataFailed(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnSetLobbyDataFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
4.7.3 Kick out Members
The lobby owner can kick out any other lobby member to make more team slots available.
Events:
OnLobbyMemberLeft
: Sent to the remaining members of the lobby when the lobby owner kicks someone out of the lobby.
Call KickOutMember
from Lobby module as follows:
- UE C++
- UE BP
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::KickOutMember()
{
FString MemberPlayerID = ObtainFromSomeWhere();
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->KickOutMember(MemberPlayerID, [](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnKickOutMemberSuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnKickOutMemberFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
#include "PgosClientLobbyAPI.h"
void SomeUObjectClass::KickOutMember()
{
FString MemberPlayerID = ObtainFromSomeWhere();
PGOS_CALL_ASYNC_API(UPgosClientAPIKickOutMember::KickOutMember,
this,
&SomeUObjectClass::OnKickOutMemberSuccess,
&SomeUObjectClass::OnKickOutMemberFailed,
MemberPlayerID);
}
void SomeUObjectClass::OnKickOutMemberSuccess(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnKickOutMemberSuccess"))
}
void SomeUObjectClass::OnKickOutMemberFailed(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnKickOutMemberFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
4.7.4 Dismiss the Lobby
A lobby will be dismissed in the following situations:
- The owner dismisses the lobby.
- Automatically if all the members have left or logged out.
Events:
OnLobbyDismiss
: Sent to all the members of the lobby when the lobby owner dismisses the lobby.
Call DismissLobby
from Lobby module as follows:
- UE C++
- UE BP
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::DismissLobby()
{
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->DismissLobby([](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnDismissLobbySuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnDismissLobbyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
#include "PgosClientLobbyAPI.h"
void SomeUObjectClass::DismissLobby()
{
PGOS_CALL_ASYNC_API(UPgosClientAPIDismissLobby::DismissLobby,
this,
&SomeUObjectClass::OnDismissLobbySuccess,
&SomeUObjectClass::OnDismissLobbyFailed);
}
void SomeUObjectClass::OnDismissLobbySuccess(FPgosResult Error)
{
UE_LOG(LogTemp, Log, TEXT("OnDismissLobbySuccess"))
}
void SomeUObjectClass::OnDismissLobbyFailed(FPgosResult Error)
{
UE_LOG(LogTemp, Log, TEXT("OnDismissLobbyFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
4.7.5 Start a Battle
4.7.5.1 Player Status
There is a ready flag for every lobby member. Players can update this flag at any point.
Events:
OnLobbyTeamUpdated
: Sent to all members of the lobby when players update their ready flags.
Call ReadyForBattle
from Lobby module as follows:
- UE C++
- UE BP
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::ReadyForBattle()
{
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->ReadyForBattle(true, [](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnReadyForBattleSuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnReadyForBattleFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
#include "PgosClientLobbyAPI.h"
void SomeUObjectClass::ReadyForBattle()
{
PGOS_CALL_ASYNC_API(UPgosClientAPIReadyForBattle::ReadyForBattle,
this,
&SomeUObjectClass::OnReadyForBattleSuccess,
&SomeUObjectClass::OnReadyForBattleFailed,
true);
}
void SomeUObjectClass::OnReadyForBattleSuccess(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnReadyForBattleSuccess"))
}
void SomeUObjectClass::OnReadyForBattleFailed(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnReadyForBattleFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
4.7.5.2 Lobby Status
A lobby can have one of the following statuses:
- Waiting: The battle has not started or has finished
- Preparing: Waiting for players to flag themselves ready to start a battle
- Playing: The battle has successfully started
The following chart shows how the lobby status changes from one to another:
- Waiting -> Preparing: The lobby owner starts a battle, but not all players are ready.
- Preparing -> Waiting: Preparing times out or members join or leave the lobby interrupting the Preparing state.
- Waiting -> Playing: The lobby owner starts a battle, and all members in the lobby are ready.
- Playing -> Waiting: A battle is finished.
- Preparing -> Playing: The players who are not ready update their ready flags to true.
If the StartBattle
API receives a successful response, it means that a new battle session will be placed on the dedicated server. When the battle session becomes ACTIVE, the player can connect to the dedicated server.
Events:
OnLobbyStatusChanged
: Sent to all members of the lobby when the lobby owner starts a battle.OnBattleSessionUpdated
: Sent to all members in the lobby when the status of the battle session updates.
Call StartBattle
from Lobby module as follows:
- UE C++
- UE BP
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::StartBattle()
{
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->StartBattle([](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnStartBattleSuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnStartBattleFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
#include "PgosClientLobbyAPI.h"
void SomeUObjectClass::StartBattle()
{
PGOS_CALL_ASYNC_API(UPgosClientAPIStartBattle::StartBattle,
this,
&SomeUObjectClass::OnStartBattleSuccess,
&SomeUObjectClass::OnStartBattleFailed);
}
void SomeUObjectClass::OnStartBattleSuccess(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnStartBattleSuccess"))
}
void SomeUObjectClass::OnStartBattleFailed(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnStartBattleFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
4.7.5.3 Handle Battle Sessions in the Dedicated Server
When a battle is started on the client side, the dedicated server is notified of the details of the battle through the callback OnStartBattleSession
.
- UE C++
- UE BP
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::SomeFunction()
{
auto Hosting = IPgosSDKCpp::Get().GetServerHostingAPI();
if (Hosting)
{
HostingAPI->OnHealthCheck().AddUObject(this, &APGOSBattleGameMode::OnHealthCheck);
HostingAPI->OnStartBattleSession().AddUObject(this, &APGOSBattleGameMode::OnStartBattleSession);
HostingAPI->OnProcessTerminate().AddUObject(this, &APGOSBattleGameMode::OnProcessTerminate);
HostingAPI->OnTerminateBattleSession().AddUObject(this, &APGOSBattleGameMode::OnTerminateBattleSession);
// Notify PGOS that a server process is ready to host a battle session
FPgosResult Result = HostingAPI->ProcessReady(Port, LogPathes,
OnHealthCheckCallback, OnStartBattleSessionCallback, OnProcessTerminateCallback);
}
}
bool SomeUObjectClass::OnHealthCheck()
{
...
}
void SomeUObjectClass::OnStartBattleSession(const FServerBattleSession& BattleSession)
{
...
for (const FServerBattleTeam& TeamData: BattleSession.battle_data.teams)
{
...
for (const FServerPlayerDesc& PlayerDesc: TeamData.players)
{
// PlayerDesc.player_info.player_id may be empty then the team slot has no player
}
}
}
void SomeUObjectClass::OnProcessTerminate(int64 TerminationTime)
{
...
}
void SomeUObjectClass::OnTerminateBattleSession(pgos::pstring battle_session_id)
{
...
}
#include "PgosClientLobbyAPI.h"
#include "PgosServerAPI.h"
void SomeUObjectClass::SomeFunction()
{
UPgosServerAPI::FDelegateOnHealthCheckCallback OnHealthCheckCallback;
UPgosServerAPI::FDelegateOnStartBattleSessionCallback OnStartBattleSessionCallback;
UPgosServerAPI::FDelegateOnProcessTerminateCallback OnProcessTerminateCallback;
OnHealthCheckCallback.BindDynamic(this, &SomeUObjectClass::OnHealthCheck);
OnStartBattleSessionCallback.BindDynamic(this, &SomeUObjectClass::OnStartBattleSession);
OnProcessTerminateCallback.BindDynamic(this, &SomeUObjectClass::OnProcessTerminate);
UPgosServerAPI::ProcessReady(Port, LogPathes,
OnHealthCheckCallback, OnStartBattleSessionCallback, OnProcessTerminateCallback);
}
bool SomeUObjectClass::OnHealthCheck()
{
...
}
void SomeUObjectClass::OnStartBattleSession(const FServerBattleSession& BattleSession)
{
...
for (const FServerBattleTeam& TeamData: BattleSession.battle_data.teams)
{
...
for (const FServerPlayerDesc& PlayerDesc: TeamData.players)
{
// PlayerDesc.player_id may be empty
}
}
}
void SomeUObjectClass::OnProcessTerminate(int64 TerminationTime)
{
...
}
void SomeUObjectClass::OnTerminateBattleSession(pgos::pstring battle_session_id)
{
...
}
As you can see from the above code, the dedicated server can retrieve all the team and player information from the OnStartBattleSession
. Unlike with matchmaking, the Lobby Service can start a battle when there are empty slots in the lobby. On the dedicated server, an empty player_id
in a PlayerDesc
means an empty slot in the lobby.
4.8.6 Transfer Ownership to other Members
Lobby ownership will be transferred to another player if:
- The lobby owner transfers ownership to a specific player.
- The lobby owner leaves the lobby and there are still players in the lobby. In this case, ownership will be randomly transferred to another player in the lobby.
Events:
OnLobbyOwnerTransferred
: Sent to all members of the lobby when ownership of the lobby is transferred to another player.
Call TransferOwnership
from Lobby module as follows:
- UE C++
- UE BP
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::TransferOwnership()
{
FString NewOwnerPlayerId = ObtainFromSomeWhere();
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->TransferOwnership(NewOwnerPlayerId, [](const FPgosResult& Ret, const FClientLobbyDetailInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("OnTransferSuccess"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("OnTransferFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
#include "PgosClientLobbyAPI.h"
void SomeUObjectClass::TransferOwnership()
{
FString NewOwnerPlayerId = ObtainFromSomeWhere();
PGOS_CALL_ASYNC_API(UPgosClientAPITransferOwnership::TransferOwnership,
this,
&SomeUObjectClass::OnTransferSuccess,
&SomeUObjectClass::OnTransferFailed,
NewOwnerPlayerId);
}
void SomeUObjectClass::OnTransferSuccess(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnTransferSuccess"))
}
void SomeUObjectClass::OnTransferFailed(
FPgosResult Error, FClientLobbyDetailInfo LobbyDetail)
{
UE_LOG(LogTemp, Log, TEXT("OnTransferFailed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
4.8 Lobby Chat
The Lobby Service has built-in chat functions which can be used directly. However, these services are valid only if enable_chatting
is set to true
when Creating a Lobby.
- Any member of the lobby can send chat messages
- Any member of the lobby can receive chat messages
- Members will not receive chat messages from themselves
Events:
OnReceiveLobbyChatMsg
: Sent to all members of the lobby except the sender.
Call SendLobbyChatTextMsg
/SendLobbyChatCustomMsg
from Lobby module as follows:
#include "PgosSDKCpp.h"
void SomeUObjectClass::SendLobbyChatTextMsg()
{
auto Lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (Lobby)
{
FClientSendLobbyChatMsgParams Params;
Lobby->SendLobbyChatTextMsg(Params, [](const FPgosResult& Ret, const FClientChatMsgInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SendLobbyChatTextMsg Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SendLobbyChatTextMsg Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
void SomeUObjectClass::SendLobbyChatCustomMsg()
{
auto Lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (Lobby)
{
FClientSendLobbyChatMsgParams Params;
Lobby->SendLobbyChatCustomMsg(Params, [](const FPgosResult& Ret, const FClientChatMsgInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SendLobbyChatCustomMsg Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SendLobbyChatCustomMsg Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
4.9 Lobby Events
To summarize, the events that will be sent to lobby members are listed below:
Event Name | Event Description |
---|---|
OnLobbyTeamUpdated | Sent when players update their ready flags or change their team slots. |
OnLobbyMemberJoined | Sent when a new player joins a lobby. |
OnLobbyMemberLeft | Sent when a member leaves a lobby or is kicked out of a lobby by the owner. |
OnLobbyInvitation | Sent when a player sends a lobby invitation to another player. |
OnLobbyInfoUpdated | Sent when lobby options are updated. |
OnLobbyDataUpdated | Sent when custom lobby data is updated. |
OnLobbyDismiss | Sent when a lobby is dismissed. |
OnLobbyStatusChanged | Sent when the lobby status is updated. |
OnBattleSessionUpdated | Sent when the battle session status is updated. |
OnLobbyOwnerTransferred | Sent when lobby ownership is transferred. |
OnReceiveLobbyChatMsg | Sent when a player sends a chat message in the lobby. |
OnLobbySwitchTeamSlotRequest | Sent when another lobby member send a team-slot switch request. |
OnLobbySwitchTeamSlotRequestConfirmed | Sent when a team-slot switch request has been confirmed. |
OnLobbySwitchTeamSlotRequestRejected | Sent when a team-slot switch request has been rejected. |
OnLobbyJoinRequest | Sent when a player request to join a lobby with protection type LobbyProtection::ApplicationNeeded. |
OnLobbyJoinRequestApproved | Sent when a join request has been approved by lobby owner. |
OnLobbyJoinRequestRejected | Sent when a join request has been reject by lobby owner. |
OnLobbyGlobalCustomDataUpdated | Sent when lobby global custom data updated. |
OnLobbyPlayerCustomDataUpdated | Sent when lobby player custom data updated. |
This section discusses a special event OnLobbyInfoUpdated
, which is triggered when there is a change in the lobby info.
Normally, changes in lobby info are indicated by more specific events. For example, the OnLobbyMemberLeft
event is triggered when a member leaves a lobby, the OnLobbyMemberJoined
event is triggered when new players join the lobby, and the OnLobbyInfoUpdated
event is triggered when the lobby owner calls EditLobbyInfo
to modify lobby info.
However, due to network issues or other reasons that may cause disruptions in the persistent connection, there might be changes in the lobby info (such as players entering or leaving the lobby) that the server cannot communicate to the client through the persistent connection. If the client is unable to detect changes in the lobby info for an extended period, it could lead to functional anomalies in the game. To address this issue, the PGOS SDK periodically (usually every 30 to 60 seconds) retrieves the latest lobby info when it detects a disruption in the persistent connection and then notifies the game through the OnLobbyInfoUpdated
event.
Therefore, when the game receives the OnLobbyInfoUpdated
event, it could be due to the lobby owner calling EditLobbyInfo
, or it could be due to changes associated with the following events:
OnLobbyTeamUpdated
OnLobbyMemberJoined
OnLobbyMemberLeft
OnLobbyInfoUpdated
OnLobbyDataUpdated
OnLobbyStatusChanged
OnLobbyOwnerTransferred
OnLobbyGlobalCustomDataUpdated
OnLobbyPlayerCustomDataUpdated
You can use UPgosClientEventDispatcher
to handle event binding issues for these events.
Bind dynamic delegate to OnLobbyMemberJoined
as follows:
- UE C++
- UE BP
#include "PgosSDKCpp.h"
#include "Core/PgosErrorCode.h"
void SomeUObjectClass::SomeFunction()
{
auto lobby = IPgosSDKCpp::Get().GetClientLobbyAPI();
if (lobby)
{
lobby->OnLobbyMemberJoined().AddUObject(this, &SomeUObjectClass::OnLobbyMemberJoined);
}
}
void SomeUObjectClass::OnLobbyMemberJoined(const FClientLobbyMemberJoinedEvt& event)
{
UE_LOG(LogTemp, Log, TEXT("OnLobbyMemberJoined"));
}
#include "PgosClientEventDispatcher.h"
void SomeUObjectClass::SomeFunction()
{
auto pDispatcher = UPgosClientEventDispatcher::GetPgosClientEventDispatcher();
if (pDispatcher)
{
pDispatcher->OnPlayerSessionChanged.Clear();
// add delegate callback
UPgosClientEventDispatcher::GetPgosClientEventDispatcher()->OnLobbyMemberJoined.AddDynamic(
this,
&SomeUObjectClass::OnLobbyMemberJoined);
// Before current object is destroyed, you should call
// `UPgosClientEventDispatcher::GetPgosClientEventDispatcher()->RemoveAll(this)`
// to clear event bindings.
}
}
void SomeUObjectClass::OnLobbyMemberJoined(const FClientLobbyMemberJoinedEvt& event)
{
UE_LOG(LogTemp, Log, TEXT("OnLobbyMemberJoined"));
}
5. Lobby Logs on the Web Portal
When a lobby is created, the PGOS backend automatically generates and stores a log that you can find in the PGOS Web Portal. On the console page, select the Title you want to query and navigate to Battle > Lobby to see a list of lobby logs:
The logs can be filtered by the following fields:
- Lobby id or uuid
- Creator id of a lobby
- Lobby configuration name
- Lobby status
Click a lobby id to view more details of the log:
- Lobby ID: A unique lobby ID which was displayed on the game client.
- Lobby UUID: An unique lobby ID used by the PGOS backend.
- Name: The name of the lobby.
- Lobby Configuration: The name of the lobby configuration.
- Status: The current lobby status.
- Owner: The name of the lobby owner.
- Battlesession: Details of the current battle; this is empty if the battle has not started.
- Chat Channel ID: An unique chat channel ID for the lobby's chat channel; empty if
enable_chatting
was set tofalse
when the lobby was created. - Members: The actions of lobby members, such as joining, leaving, and dismissing the lobby.
- Status Changes: Changes of the lobby status.
- Created Time: The time the lobby was created.
- Dismissed Time: The time the lobby was dismissed; empty if lobby has not been dismissed.
6. 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 Code | Relevant API | Handling Suggestion |
---|---|---|
kBackendAcquireLockFailed | All APIs on a specified lobby. Except for CreateLobby or SearchLobby. | It means that the operation on lobby failed. The concurrency safety is guaranteed by distributed lock mechanism. It's recommended to retry for limited times until it succeeds. |
kBackendHasNoWritePermission | Lobby management related APIs | It means that the operator has no write permission on the lobby. The game can prompt the player a message like "You need to be lobby owner to do this". |
kBackendPlayerNotInLobby | All APIs on a specified lobby. Except for JoinLobby. | It means that the operator is not in the lobby, thus has no permission to perform any operations on it. |
kBackendInvalidLobbyConfig | CreateLobby | It means the lobby config name does not exist. Developers should check on web portal if the lobby config had been removed or renamed accidentally. |
kBackendLobbyPasswordIsEmpty | CreateLobby | It means the protection indicates that the lobby is protected by password, but no password is specified. |
kBackendLobbyInvalidGameMode | CreateLobby EditLobbyInfo | It means the specified game mode is not pre-defined on lobby config. |
kBackendIllegalCredential | JoinLobby | It means the player entered the wrong password when joining lobby. The game can prompt the player a message like "Wrong password". |
kBackendNotEnoughSlots | JoinLobby | It means the lobby has no empty slot for new joiner. The game can prompt the player a message like "Lobby is full". |
kBackendLobbyWaitForApproval | JoinLobby | It means the player should wait for the lobby owner to approve his join request. |
kBackendFailedToNotifyLobbyOwner | JoinLobby | It means the system failed to notify lobby owner about the player's join request. |
kBackendLobbyGroupNotExist | JoinLobbyWithGroup | It means the lobby doesn't contain any one of selected groups. |
kBackendLobbyTeamNotExist | JoinLobbyWithTeam | It means the lobby doesn't contain any one of selected teams. |
kBackendAlreadyInBattle | StartBattle | It means the lobby is already has an active battle session. The lobby cannot start a new battle until the current battle session ended. The game can provide the player an entrance to rejoin the current battle session. |
kBackendAlreadyPreparing | StartBattle | It means the lobby is in preparing stage, thus the owner cannot start battle again. |
kBackendTitleRegionIsClosed | StartBattle | It means the title region is closed. The game should prompt the player a message like "Server is in maintenance, please come back later". |
kBackendLobbyStartBattleConditionNotMatch | StartBattle | It means not all members are ready to start battle. This error only occurs when preparing stage is disabled in lobby config. |
kBackendLobbyNameContainProfanityWords | CreateLobby EditLobbyInfo | It means the lobby name to change contains profanity words. The game can prompt the player a message like "Please remove profanity words in lobby name". |
kBackendLobbyPlayerBlockedInvitation | InviteMember | It means the operation is blocked by peer player. |
kBackendCannotSwitchToEmptySlot | RequestSwitchTeamSlot | It means the player cannot request switch to an empty slot. Should call SwitchTeamSlot directly. |
kBackendLobbyInvalidRequestId | ConfirmSwitchTeamSlot | It means the request id of switching slot is invalid. |
kBackendLobbyOutdatedRequestId | ConfirmSwitchTeamSlot | It means the player who requested switching slot has moved. |
kBackendLobbyCustomDataLengthExceeded | SetGlobalCustomData SetPlayerCustomData | It means the custom data exceeded the length limit. |
kBackendLobbyPlayerIsOffline | ApproveJoinLobby | It means the player who requested to join lobby has been offline. |
kBackendLobbyJoinTokenInvalid | ApproveJoinLobby | It means the token of the approve request is invalid. |