Skip to main content

Voice chat on listen servers

If you want to provide voice chat for players on a listen server, you'll need to create a voice-enabled lobby on the listen server, and get all connected clients to join it.

If you're using C++, you'll need to add the Online Interfaces headers to your project so you can access the lobby APIs.

Creating a voice chat lobby

First, you need to create a voice-enabled lobby on the listen server. The local player on the listen server will be joined to the voice chat room when the lobby is created.

To create a lobby, first get the online lobby interface:

#include "OnlineSubsystem.h"
#include "OnlineSubsystemUtils.h"
// If your project can't find this header, make sure you have installed the headers from here:
// https://src.redpoint.games/redpointgames/online-interfaces/
#include "OnlineLobbyInterface.h"

// ...

IOnlineSubsystem* Subsystem = Online::GetSubsystem(this->GetWorld());
TSharedPtr<IOnlineLobby, ESPMode::ThreadSafe> Lobby = Online::GetLobbyInterface(Subsystem);

Then, make a lobby transaction for creating the lobby:

TSharedPtr<const FUniqueNetId> LocalUserId = Identity->GetUniquePlayerId(0);
TSharedPtr<FOnlineLobbyTransaction> Txn = Lobby->MakeCreateLobbyTransaction(*LocalUserId.Get());

// To enable voice chat on a lobby, set the special "EOSVoiceChat_Enabled" metadata value.
Txn->SetMetadata.Add(TEXT("EOSVoiceChat_Enabled"), true);

// To allow clients connecting to the listen server to join the lobby based on just the ID, we need
// to set it to public.
Txn->Public = true;

// You can also set options like Capacity on the lobby, but the only necessary
// settings to enable voice chat are the ones shown above.

Then, create the lobby:

if (!Lobby->CreateLobby(
*LocalUserId.Get(),
*Txn,
FOnLobbyCreateOrConnectComplete::CreateLambda([](
const FOnlineError & Error,
const FUniqueNetId & UserId,
const TSharedPtr<class FOnlineLobby> & CreatedLobby)
{
if (Error.WasSuccessful())
{
// The lobby was created successfully and is now in CreatedLobby.
FString IdStr = CreatedLobby->Id->ToString();

// You'll need to store IdStr somewhere, as that is what needs to be sent to connecting clients.
}
else
{
// Lobby could not be created.
}
}
))
{
// Call failed to start.
}

Sending the voice chat lobby ID to the client, then connecting to the lobby on clients

When clients connect to the listen server, you'll need to send them the ID of the voice-enabled lobby. They'll then connect to the lobby by ID, which will connect them to the voice chat room.

In your custom player controller class, declare a function that runs on the owning clients:

class ACustomPlayerController : public APlayerController
{
// ...

public:
UFUNCTION(Client, Reliable)
void JoinVoiceLobby(const FString& InLobbyId);

// ...
};

Implement the JoinVoiceLobby function to make it join the lobby on the client:

void ACustomPlayerController::JoinVoiceLobby_Implementation(const FString& InLobbyId)
{
IOnlineSubsystem* Subsystem = Online::GetSubsystem(this->GetWorld());
TSharedPtr<IOnlineLobby, ESPMode::ThreadSafe> Lobby = Online::GetLobbyInterface(Subsystem);

TSharedPtr<FOnlineLobbyId> RealLobbyId = Lobby->ParseSerializedLobbyId(InLobbyId);

if (!Lobby->ConnectLobby(
*this->GetLocalPlayer()->GetPreferredUniqueNetId().GetUniqueNetId(),
*RealLobbyId,
FOnLobbyCreateOrConnectComplete::CreateLambda([](
const FOnlineError& Error,
const FUniqueNetId& UserId,
const TSharedPtr<FOnlineLobby>& ConnectedLobby)
{
if (Error.WasSuccessful())
{
// Player has been connected to lobby.
}
else
{
// Could not connect to lobby.
}
})
))
{
// Call failed to start.
}
}

In your custom game mode, override the PostLogin event:

class ACustomGameModeBase : public AGameModeBase
{
// ...

public:
virtual void PostLogin(APlayerController* NewPlayer) override;

// ...
};

Handle the PostLogin event to call JoinVoiceLobby:

void ACustomGameModeBase::PostLogin(APlayerController* NewPlayer)
{
Super::PostLogin(NewPlayer);

if (!NewPlayer->IsLocalPlayerController())
{
// You need to get IdStr from wherever you stored it after creating the lobby.
Cast<ACustomPlayerController>(NewPlayer)->JoinVoiceLobby(IdStr);
}
}

Disconnecting from a voice chat lobby

On the client, when you disconnect from the listen server, you need to disconnect from the lobby so the user is disconnected from the voice chat room. You'll need to store the ID of the lobby you originally connected to.

if (!Lobby->DisconnectLobby(
*LocalUserId,
*LobbyIdFromPreviousConnectOrCreate,
FOnLobbyOperationComplete::CreateLambda([](
const FOnlineError& Error,
const FUniqueNetId& UserId)
{
if (Error.WasSuccessful())
{
// Player has been disconnected from lobby.
}
else
{
// Could not disconnect from lobby.
}
})
))
{
// Call failed to start.
}

Get the lobby ID that a voice chat channel is associated with

You can discover the associated lobby IDs by iterating through the channel list, and calling GetSetting on the voice chat user for each channel:

for (const auto& ChannelName : VoiceChatUser->GetChannels())
{
FString LobbyId = VoiceChatUser->GetSetting(FString::Printf(TEXT("__EOS_LobbyId:%s"), *ChannelName));
if (!LobbyId.IsEmpty())
{
// LobbyId contains the ID of the lobby this voice chat channel is associated with.
}
}