Skip to main content

Public Chat

1. Overview

The Public Chat is a service that allows all the players to join the same global chat channel and take free talk with each other. It is like somehow a very big chat room. With this feature, players can send free chat messages, or party/group/lobby invitations to other online players. For the game operators, they can also broadcast system notice messages to all the players on the Portal.

After completing this tutorial, developers will know:

  • The basic concepts of the PGOS public chat.
  • How to join/leave public chat.
  • How to send and receive public chat messages.
  • public chat events introduction and how to monitor them.

2. General Sequence

Here is a general sequence for using the public chat:

Client(Jerry)PGOS BackendPGOS PortalOthers on Same ChannelJoinPublicChat(channel)return channel infogame admin send a system messageEvent(OnReceivePublicChatMsg):system msgEvent(OnReceivePublicChatMsg):system msgSendPublicChatTextMsg/SendPublicChatCustomMsgEvent(OnReceivePublicChatMsg):text/custom msg from JerryLeavePublicChat(channel)return leave resultClient(Jerry)PGOS BackendPGOS PortalOthers on Same Channel

3. Definition

  • Public Chat Channel: The channel of public chat. For now, only one channel is available for one title region. This channel name is Default
  • Public Sub Channel: The channel will be separated into many sub-channels automatically by PGOS backend. A player will be placed on only one sub-channel once he joins the public channel. The detailed mechanism will be introduced below.
  • System Message: The game admin can send system messages on the Portal. These messages will be broadcast gradually to everyone in the channel.

4 Key Mechanism

4.1 Sub Channel

Why Sub Channel? All the players can join into the same channel. In general, the game client would join the channel by default automatically (depends on the game design). So, if the large scale players join(eg. >1million), there would be two key problems to us:

  • The backend performance issue: once anyone sends a message in the channel, all others need to be notified of the message. This will bring up the message broadcast performance issue for the servers. Meanwhile, it will be a challenge to the network bandwidth. These problems will raise a high-cost problem for game operations.
  • The user experience issue: For over 1 million players case, if there is only 0.01% rate to send a message for every second. All others would receive over 100 messages every second. Everyone isn't able to get the messages clear on the screen, and would say: "Oh, save my eyes!". That is really a disaster for players and the game.

To solve these issues, PGOS separates the players into different sub channel automatically. The players can only receive the messages in the same sub-channel, and not able to see the messages from other sub-channels. By this method, the two problems mentioned earlier would be solved easily.

For players, they aren't able to see and recognize the sub-channel and just believe they joined a channel. For game developers, they don't need to handle any sub-channel things. All the sub-channel things are handled by the PGOS backend.

PGOS would control the sub-channel division to keep the sub-channels not to overloading. For now, a sub-channel is limited to a maximum of 10k players. On the other hand, if some sub-channels contain too few players, they will be merged into a sub-channel together. The game developers don't need to care about how and when these things happened, just only need to handle the message sending and receiving.

image-20220714191853746

tip

In the under-layer, PGOS uses buckets to hold the players in the server. The bucket where the player is in would not be changed once the player joined. The sub-channel assignment and division happen on the bucket tier. If you are not interested in the algorithm of the sub-channel, just ignore them and use the APIs.

4.1 System Message

The game admin can send a system on the Portal. This message will be broadcast gradually to everyone in the channel. With this feature, the game admin is able to send a global notice to every online players. The players will receive the system messages when they join the channel, even though they join after the system messages are sent.

A system message contains the following key fields:

  • Content: Content of the message
  • Duration: A field in seconds that indicates how long a system message will expire. The players will receive the system messages that were sent before they join, as long as the message doesn't expire.

5. Using Public Chat

5.1 Configure on the Portal

Just like the screenshot shown, you can enable or disable the public chat of the title region, view the sub-channel status and system message list, and add a system message as well.

image-20220714192728926

5.2 Join the Channel

Before calling other public chat functions, you must join a channel first, otherwise, these functions will not work. You can join a channel by calling the JoinPublicChat API.

Channel name can be retrieved from the portal, currently, only the name Default is valid.

Interface prototype:

/**
* Join a public chat.
*
* @param ChannelName The name of the channel to join which can be retrieved from the portal, currently only the name `Default` is valid.
*/
void JoinPublicChat(
const FString& ChannelName,
TFunction<void(const FPgosResult& Ret, const FClientJoinPublicChatResult* Data)> Callback) const;

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto PublicChat = IPgosSDKCpp::Get().GetClientPublicChatAPI();
if (PublicChat)
{
FString ChannelName;
PublicChat->JoinPublicChat(ChannelName, [](const FPgosResult& Ret, const FClientJoinPublicChatResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("JoinPublicChat Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("JoinPublicChat Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

5.3 Leave the Channel

If doesn't need to chat anymore in one public chat channel, you can leave the current channel by calling the LeavePublicChat API.

When players go offline, PGOS will automatically remove them from the public chat channels they joined.

Interface prototype:

/**
* Leave a public chat.
*
* @param ChannelName The channel to leave.
*/
void LeavePublicChat(
const FString& ChannelName,
TFunction<void(const FPgosResult& Ret)> Callback) const;

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto PublicChat = IPgosSDKCpp::Get().GetClientPublicChatAPI();
if (PublicChat)
{
FString ChannelName;
PublicChat->LeavePublicChat(ChannelName, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("LeavePublicChat Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("LeavePublicChat Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

5.4 Send Public Chat Messages

After joining a public chat channel, players can send messages to the channel. Once a message is sent, other players on the same channel will receive it if they are online.

There are two ways to send messages:

  • SendPublicChatTextMsg: Send a simple text message. The msg_type of the message is MsgTypeText, and the text content of the message is stored in content.text_content.
  • SendPublicChatCustomMsg: Used to send game custom messages. The msg_type of the message is MsgTypeCustom, and the content of the message is stored in content.custom_content.

Here you need to pay attention to the input parameters. Take the parameter FClientSendPublicChatMsgParams of the SendPublicChatTextMsg interface as an example:

struct FClientSendPublicChatMsgParams
{
/** The name of the channel on which the message was sent. */
FString channel_name;
/** The content of the message to be sent. */
FString content;
/** Custom data for message. */
FString custom_data;
/**
* The mentioned player list.
* The mentioned players will receive the corresponding events. When the value of player_id is '@ALL', it means mentioning all players.
*/
TArray<FClientMentionedPlayer> mentioned_players;
};

content is generally the input content of the user, which will be filtered by content moderation. custom_data is used for additional information added by game developers and will not be filtered by content moderation. The caveat here is not to store user input in custom_data. mentioned_players is optional, used to mention specified players. Player_id of FClientMentionedPlayer is "@ALL" to mention all players. The mentioned players will receive the OnPublicChatNewMentioned event.

Interface prototype:

/**
* Send public chat text message.
*
* @param Params Request parameters for sending text message.
*/
void SendPublicChatTextMsg(
const FClientSendPublicChatMsgParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientSendPublicChatMsgResult* Data)> Callback) const;

/**
* Send public chat custom message.
*
* @param Params Request parameters for sending custom message.
*/
void SendPublicChatCustomMsg(
const FClientSendPublicChatMsgParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientSendPublicChatMsgResult* Data)> Callback) const;

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto PublicChat = IPgosSDKCpp::Get().GetClientPublicChatAPI();
if (PublicChat)
{
FClientSendPublicChatMsgParams Params;
Params.channel_name = FString("Default");
Params.content = FString("hello");
Params.custom_data = FString("");
FClientMentionedPlayer MentionedAll;
MentionedAll.player_id = FString("@ALL"); // mention all members in the channel
Params.mentioned_players.Add(MentionedAll);
PublicChat->SendPublicChatTextMsg(Params, [](const FPgosResult& Ret, const FClientSendPublicChatMsgResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SendPublicChatTextMsg Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SendPublicChatTextMsg Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

void SomeUObjectClass::SomeFunction2()
{
auto PublicChat = IPgosSDKCpp::Get().GetClientPublicChatAPI();
if (PublicChat)
{
FClientSendPublicChatMsgParams Params;
Params.channel_name = FString("Default");
Params.content = FString("hello");
Params.custom_data = FString("");
FClientMentionedPlayer MentionedPlayer;
MentionedPlayer.player_id = FString("123456"); // mention player "123456"
Params.mentioned_players.Add(MentionedPlayer);
PublicChat->SendPublicChatCustomMsg(Params, [](const FPgosResult& Ret, const FClientSendPublicChatMsgResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SendPublicChatCustomMsg Successfully"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SendPublicChatCustomMsg Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

5.5 Receiving the Messages

OnReceivePublicChatMsg event will be triggered when receiving public chat messages. The event results are saved in FClientReceivePublicChatMsgEvt.

struct FClientReceivePublicChatMsgEvt 
{
/** Public chat message. */
FClientPublicChatMsgInfo msg;
/** Public chat channel info. */
FClientPublicChatChannelInfo channel_info;
};

Monitor events from PublicChat module, as follows:

#include "PgosSDKCpp.h"

void SomeClass::MonitorPublicChatEvents()
{
auto PublicChat = IPgosSDKCpp::Get().GetClientPublicChatAPI();
if (PublicChat)
{
PublicChat->OnReceivePublicChatMsg().AddUObject(this, &SomeClass::OnReceivePublicChatMsg);
}
}

void SomeClass::OnReceivePublicChatMsg(const FClientReceivePublicChatMsgEvt& Event)
{
UE_LOG(LogTemp, Log, TEXT("OnReceivePublicChatMsg"));
const auto MsgType = Event.msg.msg_type;
if (MsgType == EClientChatMsgType::MsgTypeText)
{
UE_LOG(LogTemp, Log, TEXT("receive text msg, content=(%s)"), *Event.msg.content.text_content);
} else if (MsgType == EClientChatMsgType::MsgTypeCustom)
{
UE_LOG(LogTemp, Log, TEXT("receive custom msg, content=(%s)"), *Event.msg.content.custom_content);
} else if (MsgType == EClientChatMsgType::MsgTypeSystem)
{
UE_LOG(LogTemp, Log, TEXT("receive system msg, content=(%s), duration_sec=(%d)"), *Event.msg.content.system_content.msg, Event.msg.content.system_content.duration_sec);
}
}

5.6 Mention Players

Specified players can be mentioned when sending a message. For details, please view Send Public Chat Messages

If someone in the channel mentioned you, you will receive the OnPublicChatNewMentioned event.

You can query the last mentioned record of the specified channel by calling the interface GetPublicChatMentionedInfo.

You can clear the records of the specified channel mentioned you by calling the interface ClearPublicChatMentionedInfo. When channel_name is empty, it means clearing the mentioned records of all channels.

Interface prototype:

/**
* Get latest record mentioning me in the channel.
*
* @param ChannelName The name of the channel.
* @param Dst The latest record mentioned me.
* @return True if there are any mentions of me.
*/
bool GetPublicChatMentionedInfo(
const FString& ChannelName,
FClientPublicChatMentionedInfo& Dst) const;

/**
* Clear record mentioning me in the channel.
*
* @param ChannelName The name of the channel. If empty, it means all channels.
*/
void ClearPublicChatMentionedInfo(const FString& ChannelName) const;

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction1()
{
auto PublicChat = IPgosSDKCpp::Get().GetClientPublicChatAPI();
if (PublicChat)
{
FString ChannelName = TEXT("Default");
FClientPublicChatMentionedInfo Dst;
PublicChat->GetPublicChatMentionedInfo(ChannelName, Dst);
if (result)
{
UE_LOG(LogTemp, Log, TEXT("%s mentioned you"), *Dst.msg.sender.display_name);
}
else
{
UE_LOG(LogTemp, Log, TEXT("No one mentioned you"));
}
}
}

void SomeUObjectClass::SomeFunction2()
{
auto PublicChat = IPgosSDKCpp::Get().GetClientPublicChatAPI();
if (PublicChat)
{
FString ChannelName = TEXT("Default");
PublicChat->ClearPublicChatMentionedInfo(ChannelName);
}
}

void SomeClass::MonitorGroupChatEvents()
{
auto PublicChat = IPgosSDKCpp::Get().GetClientPublicChatAPI();
if (PublicChat)
{
PublicChat->OnPublicChatNewMentioned().AddUObject(this, &SomeClass::OnPublicChatNewMentioned);
}
}

void SomeClass::OnPublicChatNewMentioned(const FClientPublicChatNewMentionedEvt& Event)
{
UE_LOG(LogTemp, Log, TEXT("OnPublicChatNewMentioned"));
}

6 Key Errors Handling

Error CodeRelevant APIHandling Suggestion
kBackendPubChatChannelNameInvalidJoinPublicChatIt means the channel name is invalid, you can retrieve channel name from the portal.
kBackendPubChatChannelDisabledJoinPublicChat
SendPublicChatTextMsg
SendPublicChatCustomMsg
It means the channel is disabled.
kCltSdkPublicChatNeedJoinFirstSendPublicChatTextMsg
SendPublicChatCustomMsg
It means the player has not joined the channel.
kBackendIMChatFrequencyLimitSendPublicChatTextMsg
SendPublicChatCustomMsg
It means that the operation frequency exceeds limit. It's recommended to retry with exponentially expanded interval: 5s, 10s, 20s, etc.
kBackendIMChatMsgIsTooLongSendPublicChatTextMsg
SendPublicChatCustomMsg
It means that the message content is too long. The limit is 2000 characters for personal/group chat and 500 characters for public chat. The game should prompt player with a message like "Message too long, please shorten it or split it in multiple messages."
kBackendIMChatCustomDataIsTooLongSendPublicChatTextMsg
SendPublicChatCustomMsg
It means that the custom data is too long. The limit is 8000 bytes for personal/group chat and 1000 bytes for public chat. The game catch this error and avoid using super long custom data.
kBackendProfaneWordsSendPublicChatTextMsg
SendPublicChatCustomMsg
It means the message content contains profane words and cannot be masked. The game should prompt the player with a message like "Please remove profane words to send message."
kBackendErrPlayerIsPunishedSendPublicChatTextMsg
SendPublicChatCustomMsg
The player is punished. msg is the json serialized string of deadline(unix timestamp (in seconds) at the end of the punishment, <= 0 means permanent punishment) and reason.