Put it all together - Session chat - (Unreal Engine module)
Connect the UI with session chat
In this tutorial, you will connect your implementation in SessionChatSubsystem_Starter
to the session chat widget.
-
Open the
SessionChatWidget_Starter
CPP file and start with connecting the UI to send session chat messages. Replace theSendSessionChatMessage()
code with the following code:void USessionChatWidget_Starter::SendChatMessage(const FText& MessageText)
{
if (!SessionChatSubsystem)
{
UE_LOG_SESSIONCHAT(Warning, TEXT("Cannot send chat message. Session Chat subsystem is not valid."));
return;
}
if (!ensure(GetOwningPlayer()))
{
UE_LOG_SESSIONCHAT(Warning, TEXT("Cannot send chat message. PlayerController is not valid."));
return;
}
const ULocalPlayer* LocalPlayer = GetOwningPlayer()->GetLocalPlayer();
if (!ensure(LocalPlayer))
{
UE_LOG_SESSIONCHAT(Warning, TEXT("Cannot send chat message. LocalPlayer is not valid."));
return;
}
// Send room chat message.
SessionChatSubsystem->SendChatMessage(
LocalPlayer->GetPreferredUniqueNetId().GetUniqueNetId(),
SessionChatSubsystem->GetChatRoomIdBasedOnType(CurrentChatRoomType),
MessageText.ToString());
} -
Next, replace the
OnSendChatComplete()
function with the code below to check whether the sent chat should be displayed depending on the current widget chat type:void USessionChatWidget_Starter::OnSendChatComplete(FString UserId, FString MsgBody, FString RoomId, bool bWasSuccessful)
{
// Abort and push a notification if failed.
if (!bWasSuccessful)
{
if (PromptSubsystem)
{
PromptSubsystem->PushNotification(SEND_CHAT_FAILED_MESSAGE);
}
return;
}
if (!SessionChatSubsystem)
{
UE_LOG_SESSIONCHAT(Warning, TEXT("Cannot display a sent chat message. Session Chat subsystem is not valid."));
return;
}
// Only show the chat if the type is valid.
if (SessionChatSubsystem->GetChatRoomType(RoomId) != CurrentChatRoomType)
{
return;
}
// Display the chat if success.
UChatData* ChatData = NewObject<UChatData>();
ChatData->Message = MsgBody;
ChatData->bIsSenderLocal = true;
W_ActiveChat->AppendChatMessage(ChatData);
} -
Replace the
OnChatRoomMessageReceived()
function with the code below to check the new chat type first before appending it to the displayed chats:void USessionChatWidget_Starter::OnChatRoomMessageReceived(const FUniqueNetId& UserId, const FChatRoomId& RoomId, const TSharedRef<FChatMessage>& Message)
{
if (!SessionChatSubsystem)
{
UE_LOG_SESSIONCHAT(Warning, TEXT("Cannot display a received chat message. Session Chat subsystem is not valid."));
return;
}
// Only show the chat if the type is valid.
if (SessionChatSubsystem->GetChatRoomType(RoomId) != CurrentChatRoomType)
{
return;
}
// Show the received chat message.
AppendChatMessage(Message.Get());
} -
Replace the
GetLastChatMessages()
function with the code below to get and display the chat message history based on the current selected chat type:void USessionChatWidget_Starter::GetLastChatMessages()
{
if (!SessionChatSubsystem)
{
UE_LOG_SESSIONCHAT(Warning, TEXT("Cannot get last chat messages. Session Chat subsystem is not valid."));
return;
}
if (!ensure(GetOwningPlayer()))
{
UE_LOG_SESSIONCHAT(Warning, TEXT("Cannot get last chat messages. PlayerController is not valid."));
return;
}
const ULocalPlayer* LocalPlayer = GetOwningPlayer()->GetLocalPlayer();
if (!ensure(LocalPlayer))
{
UE_LOG_SESSIONCHAT(Warning, TEXT("Cannot get last chat messages. LocalPlayer is not valid."));
return;
}
if (!ensure(W_ActiveChat))
{
UE_LOG_SESSIONCHAT(Warning, TEXT("Cannot get last chat messages. Chat widget component is not valid."));
return;
}
// Get chat room id.
FString ChatRoomId = SessionChatSubsystem->GetChatRoomIdBasedOnType(CurrentChatRoomType);
// Abort if room id was not found.
if (ChatRoomId.IsEmpty())
{
FText ChatRoomNotFoundMessage = INVALID_CHAT_ROOM_MESSAGE;
switch (CurrentChatRoomType)
{
case EAccelByteChatRoomType::PARTY_V1:
case EAccelByteChatRoomType::PARTY_V2:
ChatRoomNotFoundMessage = INVALID_PARTY_CHAT_ROOM_MESSAGE;
break;
case EAccelByteChatRoomType::SESSION_V2:
ChatRoomNotFoundMessage = INVALID_GAMESESSION_CHAT_ROOM_MESSAGE;
break;
}
W_ActiveChat->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Error, ChatRoomNotFoundMessage);
return;
}
// Get last chat messages.
TArray<TSharedRef<FChatMessage>> OutMessages;
if (SessionChatSubsystem->GetLastChatMessages(
LocalPlayer->GetPreferredUniqueNetId().GetUniqueNetId(),
ChatRoomId,
W_ActiveChat->GetMaxChatHistory(),
OutMessages))
{
// Abort if last messages is empty.
if (OutMessages.IsEmpty())
{
W_ActiveChat->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Empty, NO_CHAT_MESSAGE);
return;
}
// Display last chat messages.
Algo::Reverse(OutMessages);
for (const TSharedRef<FChatMessage>& Message : OutMessages)
{
AppendChatMessage(Message.Get());
}
}
// Show error message if failed.
else
{
W_ActiveChat->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Error, FAILED_TO_LOAD_CHAT_MESSAGE);
}
} -
Next, replace the
SwitchChatMessageType()
function with the code below to immediately get chat message history when the widget chat type is changed:void USessionChatWidget_Starter::SwitchChatMessageType(const EAccelByteChatRoomType ChatRoomType)
{
// Switch chat message active panel based on type.
switch(ChatRoomType)
{
case EAccelByteChatRoomType::SESSION_V2:
Ws_ChatMessageType->SetActiveWidget(W_GameSessionChatOuter);
W_ActiveChat = W_GameSessionChat;
break;
case EAccelByteChatRoomType::PARTY_V2:
Ws_ChatMessageType->SetActiveWidget(W_PartyChatOuter);
W_ActiveChat = W_PartyChat;
break;
}
CurrentChatRoomType = ChatRoomType;
// Try to display last chat messages if the current one is empty.
if (W_ActiveChat)
{
W_ActiveChat->ClearChatMessages();
W_ActiveChat->ClearInput();
GetLastChatMessages();
}
} -
Replace the
AppendChatMessage()
function with the code below to append the chat message to be displayed with additional safeguards:void USessionChatWidget_Starter::AppendChatMessage(const FChatMessage& Message)
{
if (!SessionChatSubsystem)
{
UE_LOG_SESSIONCHAT(Warning, TEXT("Cannot append a chat message to display. Session Chat subsystem is not valid."));
return;
}
if (!ensure(GetOwningPlayer()))
{
UE_LOG_SESSIONCHAT(Warning, TEXT("Cannot append a chat message to display. PlayerController is not valid."));
return;
}
const ULocalPlayer* LocalPlayer = GetOwningPlayer()->GetLocalPlayer();
if (!ensure(LocalPlayer))
{
UE_LOG_SESSIONCHAT(Warning, TEXT("Cannot append a chat message to display. LocalPlayer is not valid."));
return;
}
const FUniqueNetIdAccelByteUserRef SenderABId = StaticCastSharedRef<const FUniqueNetIdAccelByteUser>(Message.GetUserId());
if (!SenderABId.Get().IsValid())
{
UE_LOG_SESSIONCHAT(Warning, TEXT("Cannot append a chat message to display. Sender User Id is invalid."));
return;
}
// Display chat message.
UChatData* ChatData = NewObject<UChatData>();
ChatData->Message = Message.GetBody();
ChatData->bIsSenderLocal = SessionChatSubsystem->IsMessageFromLocalUser(LocalPlayer->GetPreferredUniqueNetId().GetUniqueNetId(), Message);
// If the sender doesn't have display name, then use the default display name.
ChatData->Sender = Message.GetNickname();
if (ChatData->Sender.IsEmpty() || SenderABId.Get().GetAccelByteId().Equals(ChatData->Sender))
{
ChatData->Sender = UTutorialModuleOnlineUtility::GetUserDefaultDisplayName(Message.GetUserId().Get());
}
// Display chat message.
W_ActiveChat->AppendChatMessage(ChatData);
} -
Now, complete the code by binding the chat events with the widget. To do this, replace the pre-defined
NativeOnActivated()
function with the following code:void USessionChatWidget_Starter::NativeOnActivated()
{
Super::NativeOnActivated();
// Reset chat widget
W_GameSessionChat->ClearChatMessages();
W_GameSessionChat->ClearInput();
W_PartyChat->ClearChatMessages();
W_PartyChat->ClearInput();
// Bind event to switch between chat message type.
Btn_SessionChat->OnClicked().AddUObject(this, &ThisClass::SwitchChatMessageType, EAccelByteChatRoomType::SESSION_V2);
Btn_PartyChat->OnClicked().AddUObject(this, &ThisClass::SwitchChatMessageType, EAccelByteChatRoomType::PARTY_V2);
// Setup widget
W_GameSessionChat->OnSubmitDelegates.AddUObject(this, &ThisClass::SendChatMessage);
W_PartyChat->OnSubmitDelegates.AddUObject(this, &ThisClass::SendChatMessage);
Btn_Back->OnClicked().AddUObject(this, &ThisClass::DeactivateWidget);
// Bind chat events.
if (SessionChatSubsystem)
{
SessionChatSubsystem->GetOnSendChatCompleteDelegates()->AddUObject(this, &ThisClass::OnSendChatComplete);
SessionChatSubsystem->GetOnChatRoomMessageReceivedDelegates()->AddUObject(this, &ThisClass::OnChatRoomMessageReceived);
}
SwitchChatMessageType(CurrentChatRoomType);
} -
Finally, unbind those events when the widget is closed. To this, replace the pre-defined
NativeOnDeactivated()
function with the following code:void USessionChatWidget_Starter::NativeOnDeactivated()
{
CurrentChatRoomType = EAccelByteChatRoomType::SESSION_V2;
// Unbind chat events.
if (SessionChatSubsystem)
{
SessionChatSubsystem->GetOnSendChatCompleteDelegates()->RemoveAll(this);
SessionChatSubsystem->GetOnChatRoomMessageReceivedDelegates()->RemoveAll(this);
}
// Cleanup widget
Btn_SessionChat->OnClicked().Clear();
Btn_PartyChat->OnClicked().Clear();
W_GameSessionChat->OnSubmitDelegates.RemoveAll(this);
W_PartyChat->OnSubmitDelegates.RemoveAll(this);
Btn_Back->OnClicked().RemoveAll(this);
Super::NativeOnDeactivated();
}
Resources
-
The files used in this tutorial section are available in the Byte Wars GitHub repository.