Player-to-Player Messaging#
Xsolla Backend supports asynchronous Player-to-Player messaging as well as realtime chat. This article describes how to implement the former.
Each player has their own inbox and outbox of messages both received and sent respectively. Access to these mailboxes
is accessible via the MessageService class in the SDK or via the IOnlineMessage interface in Unreal.
Messages follow a standard e-mail structure that includes the following properties:
Property |
Description |
Required |
|---|---|---|
|
The unique identifier of the user that is to receive the message. |
|
|
The unique identifier of the user that sent the message. |
|
|
The summary of the message contents. |
|
|
The message contents. |
|
|
A map of key-value pairs representing a list of attachments that have been appended that have been appended to the message. The key of each pair is the can represent any data including binary data using base64 encoding. |
|
Note that the attachments is an arbitrary map of key-value pairs. The value can be any valid JSON type
including strings, numbers, objects and even base64 encoded binary. This makes it possible to implement any manner of
game-specific features such as invites, embedded media and so on.
Unreal Considerations#
Unreal’s OnlineSubsystem handles player messaging a little differently than Xsolla Backend. This affects the way the IOnlineMessage is both implemented and used.
The following table details how the IOnlineMessage interface functions are intended to behave and how Xsolla
Backend’s implementation differs:
Function |
Intended Behavior |
OnlineSubsystemXBE Behavior |
|---|---|---|
|
Retrieves a list of message metadata for available messages. |
Retrieves the complete list of available message metadata and contents. |
|
Get the cached list of message headers for a user. |
Same |
|
Clear the cached list of message headers. |
Same. Message data/contents retained. |
|
Download a message and its payload from user’s inbox. |
Marks a given message as having been read. |
|
Get the cached message and its contents for a user. |
Same. Does not require call to |
|
Send a message from the currently logged in user to a list of recipients. |
Same |
|
Delete a message from currently logged in user’s inbox. |
Same |
Sending a Message#
Messages can be sent from a player to exactly one other player. If multiple recipients are desired you must send
multiple message requests with the uid of each receiving user:
1using namespace xbe::sdk;
2
3auto message = std::make_shared<models::Message>();
4message->SetReceiverUid(_XPLATSTR("<other_uid>"));
5message->SetSubject(_XPLATSTR("Test Message"));
6message->SetBody(_XPLATSTR("This is only a test."));
7
8auto service = CoreSDK->GetServiceFactory<services::MessageService>();
9service->Create(message).then([](pplx::task<std::shared_ptr<models::Message>> task)
10{
11 try
12 {
13 task.get();
14 }
15 catch (const xbe::sdk::Exception& e)
16 {
17 // Handle error here
18 }
19});
1try
2{
3 Message message = new Message();
4 message.ReceiverUid = "<other_uid>";
5 message.Subject = "Test Message";
6 message.Body = "This is only a test.";
7
8 MessageService service = CoreSDK.ServiceFactory.GetService<MessageService>();
9 await service.Create(message);
10}
11catch (Exception error)
12{
13 // Handle error here
14}
1try
2{
3 const message: Message = new Message();
4 message.receiverUid = "<other_uid>";
5 message.subject = "Test Message";
6 message.body = "This is only a test.";
7
8 const service: MessageService = ServiceFactory.getService(MessageService);
9 await service.create(message);
10}
11catch (error: any)
12{
13 // Handle error here
14}
1try
2{
3 XBECoreSDK SDK = XBECoreSDK.GetInstance();
4 Message message = new Message();
5 message.ReceiverUid = "<other_uid>";
6 message.Subject = "Test Message";
7 message.Body = "This is only a test.";
8
9 MessageService service = SDK.Instance.ServiceFactory.GetService<MessageService>();
10 await service.Create(message);
11}
12catch (Exception error)
13{
14 Debug.LogError(error.Message);
15}
1FOnlineSubsystemXBE* OSS = (FOnlineSubsystemXBE*)(IOnlineSubsystem::Get(XBE_SUBSYSTEM));
2check(OnlineSub != nullptr);
3const IOnlineMessagePtr MessageInterface = OnlineSub->GetMessageInterface();
4check(MessageInterface.IsValid());
5
6TArray<TSharedRef<const FUniqueNetId>> RecipientIds;
7FOnlineMessagePayload Payload;
8
9// Note the MessageType argument is ignored
10MessageInterface->SendMessage(LocalUserNum, RecipientIds, TEXT(""), Payload);
Note that when push notifications are enabled on the SDK instance, sent messages will automatically be forwarded to to the receiving client if one is connected and available.
Retrieving the Inbox#
The following example shows how to retrieve a list of all messages that a user has received, including those already marked as read:
1using namespace xbe::sdk;
2
3auto service = CoreSDK->GetServiceFactory<services::MessageService>();
4service->FindInbox().then([](
5 pplx::task<std::vector<std::shared_ptr<models::Message>>> task)
6{
7 try
8 {
9 auto messages = task.get();
10 // TODO Process inbox messages
11 }
12 catch (const xbe::sdk::Exception& e)
13 {
14 // Handle error here
15 }
16});
1try
2{
3 MessageService service = CoreSDK.ServiceFactory.GetService<MessageService>();
4 List<Message> messages = await service.FindInbox();
5}
6catch (Exception error)
7{
8 // Handle error here
9}
1try
2{
3 const service: MessageService = ServiceFactory.getService(MessageService);
4 const messages: Message[] | undefined = await service.findInbox();
5}
6catch (error: any)
7{
8 // Handle error here
9}
1try
2{
3 XBECoreSDK SDK = XBECoreSDK.GetInstance();
4 MessageService service = SDK.Instance.ServiceFactory.GetService<MessageService>();
5 List<Message> messages = await service.FindInbox();
6}
7catch (Exception error)
8{
9 Debug.LogError(error.Message);
10}
1FOnlineSubsystemXBE* OSS = (FOnlineSubsystemXBE*)(IOnlineSubsystem::Get(XBE_SUBSYSTEM));
2check(OnlineSub != nullptr);
3const IOnlineMessagePtr MessageInterface = OnlineSub->GetMessageInterface();
4check(MessageInterface.IsValid());
5
6FDelegateHandle DelegateHandler;
7auto Delegate = FOnEnumerateMessagesComplete::CreateLambda([=](
8 int32 InLocalUserNum,
9 bool bWasSuccessful,
10 const FString& Error)
11{
12 if (bWasSuccessful)
13 {
14 TArray<TSharedRef<class FOnlineMessageHeader>> headers;
15 if (MessageInterface->GetMessageHeaders(LocalUserNum, headers))
16 {
17 TArray<TSharedPtr<class FOnlineMessage>> messages;
18 for (auto header : headers)
19 {
20 messages.Add(MessageInterface->GetMessage(LocalUserNum, header->MessageId));
21 }
22
23 // TODO Do something with messages
24 }
25 }
26 else
27 {
28 // Handle error here
29 }
30
31 MessageInterface->ClearOnEnumerateMessagesComplete_Handle(
32 InLocalUserNum, DelegateHandler);
33});
34DelegateHandler = MessageInterface->AddOnEnumerateMessagesComplete_Handle(
35 0, LoginDelegate);
36
37MessageInterface->EnumerateMessages(LocalUserNum);
Marking a Message as Read#
It is often useful to know when a user has already read a message or opened a message (in the case of an invite). The below example shows how to mark a message as already having been read:
1using namespace xbe::sdk;
2
3auto service = CoreSDK->GetServiceFactory<services::MessageService>();
4service->MarkRead(message->GetUid()).then([](pplx::task<void> task)
5{
6 try
7 {
8 task.get();
9 }
10 catch (const xbe::sdk::Exception& e)
11 {
12 // Handle error here
13 }
14});
1try
2 {
3 MessageService service = CoreSDK.ServiceFactory.GetService<MessageService>();
4 await service.MarkRead(message.Uid);
5 }
6 catch (Exception error)
7 {
8 // Handle error here
9 }
1try
2{
3 const service: MessageService = ServiceFactory.getService(MessageService);
4 await service.markRead(message.uid);
5}
6catch (error: any)
7{
8 // Handle error here
9}
1try
2{
3 MessageService service = SDK.Instance.ServiceFactory.GetService<MessageService>();
4 await service.MarkRead(message.Uid);
5}
6catch (Exception error)
7{
8 Debug.LogError(error.Message);
9}
1FOnlineSubsystemXBE* OSS = (FOnlineSubsystemXBE*)(IOnlineSubsystem::Get(XBE_SUBSYSTEM));
2check(OnlineSub != nullptr);
3const IOnlineMessagePtr MessageInterface = OnlineSub->GetMessageInterface();
4check(MessageInterface.IsValid());
5
6MessageInterface->ReadMessage(LocalUserNum, MessageId);
Deleting a Message#
To delete a message simply call the Delete function on MessageService or DeleteMessage on
IOnlineMessage when using the Unreal plugin:
1using namespace xbe::sdk;
2
3auto service = CoreSDK->GetServiceFactory<services::MessageService>();
4service->Delete(message->GetUid()).then([](pplx::task<void> task)
5{
6 try
7 {
8 task.get();
9 }
10 catch (const xbe::sdk::Exception& e)
11 {
12 // Handle error here
13 }
14});
1try
2{
3 MessageService service = CoreSDK.ServiceFactory.GetService<MessageService>();
4 await service.Delete(message.Uid);
5}
6catch (Exception error)
7{
8 // Handle error here
9}
1try
2{
3 const service: MessageService = ServiceFactory.getService(MessageService);
4 await service.delete(message.uid);
5}
6catch (error: any)
7{
8 // Handle error here
9}
1try
2{
3 MessageService service = SDK.Instance.ServiceFactory.GetService<MessageService>();
4 await service.Delete(message.Uid);
5}
6catch (Exception error)
7{
8 Debug.LogError(error.Message);
9}
1FOnlineSubsystemXBE* OSS = (FOnlineSubsystemXBE*)(IOnlineSubsystem::Get(XBE_SUBSYSTEM));
2check(OnlineSub != nullptr);
3const IOnlineMessagePtr MessageInterface = OnlineSub->GetMessageInterface();
4check(MessageInterface.IsValid());
5
6MessageInterface->DeleteMessage(LocalUserNum, MessageId);