3 changed files with 210 additions and 79 deletions
-
187Assets/Scripts/EOSVoiceChat.cs
-
11Assets/Scripts/EOSVoiceChat.cs.meta
-
91Assets/Scripts/MagnificentVoiceChat.cs
@ -0,0 +1,187 @@ |
|||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Collections.Generic; |
||||
|
using Epic.OnlineServices; |
||||
|
using Epic.OnlineServices.Lobby; |
||||
|
using Epic.OnlineServices.RTC; |
||||
|
using Epic.OnlineServices.RTCAudio; |
||||
|
using UnityEngine; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Generic re-usable cross-platform class to provide common voice chat functionality based on the EOS Voice Chat service.
|
||||
|
/// This class does not know anything about EOS platform initialization or authentication; it just takes the required
|
||||
|
/// EOS interfaces and exposes a number of game-related voice functions.
|
||||
|
/// </summary>
|
||||
|
public class EOSVoiceChat |
||||
|
{ |
||||
|
private const uint DefaultMaxChatPlayers = 16; // "Lobbies that generate conference rooms must have <= 16 max players"
|
||||
|
|
||||
|
private readonly LobbyInterface lobbyInterface; |
||||
|
private readonly RTCInterface rtcInterface; |
||||
|
private readonly RTCAudioInterface audioInterface; |
||||
|
private readonly Func<ProductUserId> productUserProvider; |
||||
|
|
||||
|
private string connectedLobbyId; |
||||
|
public bool IsConnected => lobbyInterface != null && rtcInterface != null && audioInterface != null && !string.IsNullOrEmpty(connectedLobbyId); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provide the required interfaces for voice chat. Product User ID is provided through a callback, so that the
|
||||
|
/// same instance of this class can remain in use even if the logged in user changes.
|
||||
|
/// </summary>
|
||||
|
public EOSVoiceChat( |
||||
|
LobbyInterface lobbyInterface, RTCInterface rtcInterface, RTCAudioInterface audioInterface, |
||||
|
Func<ProductUserId> productUserProvider) |
||||
|
{ |
||||
|
this.lobbyInterface = lobbyInterface; |
||||
|
this.rtcInterface = rtcInterface; |
||||
|
this.audioInterface = audioInterface; |
||||
|
this.productUserProvider = productUserProvider; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Join an existing chat lobby or create a new one.
|
||||
|
/// The completion callback is invoked on both success and failure, and can be used to set up the chat lobby's initial parameters.
|
||||
|
/// </summary>
|
||||
|
public void ConnectToChat(string chatLobbyName, Action<bool> onCompleted = null, uint maxChatPlayers = DefaultMaxChatPlayers) |
||||
|
{ |
||||
|
DisconnectChat(); // Leave any currently connected chat lobby
|
||||
|
|
||||
|
lobbyInterface.CreateLobbySearch(new CreateLobbySearchOptions { MaxResults = 1 }, out var searchHandle); |
||||
|
searchHandle.SetLobbyId(new LobbySearchSetLobbyIdOptions { LobbyId = chatLobbyName }); |
||||
|
|
||||
|
var localUserId = productUserProvider.Invoke(); |
||||
|
searchHandle.Find(new LobbySearchFindOptions { LocalUserId = localUserId }, null, findData => |
||||
|
{ |
||||
|
switch (findData.ResultCode) |
||||
|
{ |
||||
|
case Result.Success: |
||||
|
searchHandle.CopySearchResultByIndex(new LobbySearchCopySearchResultByIndexOptions { LobbyIndex = 0 }, out var lobbyDetails); |
||||
|
|
||||
|
Debug.Log("Found existing lobby, joining..."); |
||||
|
lobbyInterface.JoinLobby |
||||
|
( |
||||
|
new JoinLobbyOptions |
||||
|
{ |
||||
|
LocalUserId = localUserId, |
||||
|
LobbyDetailsHandle = lobbyDetails, |
||||
|
PresenceEnabled = false, |
||||
|
}, |
||||
|
null, |
||||
|
data => HandleLobbyJoined(data, onCompleted) |
||||
|
); |
||||
|
break; |
||||
|
default: |
||||
|
Debug.Log($"Creating new chat lobby..."); |
||||
|
lobbyInterface.CreateLobby |
||||
|
( |
||||
|
new CreateLobbyOptions |
||||
|
{ |
||||
|
LocalUserId = localUserId, |
||||
|
AllowInvites = false, |
||||
|
PermissionLevel = LobbyPermissionLevel.Publicadvertised, |
||||
|
PresenceEnabled = false, |
||||
|
MaxLobbyMembers = maxChatPlayers, |
||||
|
DisableHostMigration = false, |
||||
|
LobbyId = chatLobbyName, |
||||
|
BucketId = Application.productName, // TODO: do we need anything more specific than this?
|
||||
|
EnableRTCRoom = true, |
||||
|
}, |
||||
|
chatLobbyName, |
||||
|
data => HandleLobbyCreated(data, onCompleted) |
||||
|
); |
||||
|
break; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private void HandleLobbyCreated(CreateLobbyCallbackInfo data, Action<bool> onCompleted) |
||||
|
{ |
||||
|
switch (data.ResultCode) |
||||
|
{ |
||||
|
case Result.Success: |
||||
|
connectedLobbyId = data.LobbyId; |
||||
|
Debug.Log($"Chat lobby creation successful, lobby ID = {connectedLobbyId}"); |
||||
|
onCompleted?.Invoke(true); |
||||
|
break; |
||||
|
case Result.LobbyLobbyAlreadyExists: |
||||
|
// This can happen if two clients try to create the same lobby at the same time, a classic race condition.
|
||||
|
// Try to join the other client's newly created chat lobby instead.
|
||||
|
connectedLobbyId = null; |
||||
|
Debug.Log("Chat lobby already exists, attempting to join it..."); |
||||
|
ConnectToChat((string)data.ClientData, onCompleted); |
||||
|
break; |
||||
|
default: |
||||
|
connectedLobbyId = null; |
||||
|
Debug.LogError($"Chat lobby creation failed, result code = {data.ResultCode}"); |
||||
|
onCompleted?.Invoke(false); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void HandleLobbyJoined(JoinLobbyCallbackInfo data, Action<bool> onCompleted) |
||||
|
{ |
||||
|
switch (data.ResultCode) |
||||
|
{ |
||||
|
case Result.Success: |
||||
|
connectedLobbyId = data.LobbyId; |
||||
|
Debug.Log($"Chat lobby joined successfully, lobby ID = {connectedLobbyId}"); |
||||
|
onCompleted?.Invoke(true); |
||||
|
break; |
||||
|
default: |
||||
|
connectedLobbyId = null; |
||||
|
Debug.LogError($"Chat lobby join failed, result code = {data.ResultCode}"); |
||||
|
onCompleted?.Invoke(false); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void DisconnectChat() |
||||
|
{ |
||||
|
if (!IsConnected) |
||||
|
return; |
||||
|
|
||||
|
lobbyInterface.LeaveLobby |
||||
|
( |
||||
|
new LeaveLobbyOptions |
||||
|
{ |
||||
|
LocalUserId = productUserProvider.Invoke(), |
||||
|
LobbyId = connectedLobbyId, |
||||
|
}, |
||||
|
null, |
||||
|
data => { } |
||||
|
); |
||||
|
|
||||
|
connectedLobbyId = null; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Mute or unmute the local player's voice chat. This can be used to implement push-to-talk.
|
||||
|
/// </summary>
|
||||
|
public void SetLocalMuted(bool muted) |
||||
|
{ |
||||
|
if (!IsConnected) |
||||
|
return; |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Mute or unmute a specific remove player. This can be used to filter out specific players in the chat lobby,
|
||||
|
/// or for manually muting toxic players.
|
||||
|
/// </summary>
|
||||
|
public void SetRemoteMuted(string playerId, bool muted) |
||||
|
{ |
||||
|
if (!IsConnected) |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Set all remote players to muted. This can be used during loading screens, or to initialize the chat lobby
|
||||
|
/// when chatting with only a small subset of players.
|
||||
|
/// </summary>
|
||||
|
public void MuteAllRemote() |
||||
|
{ |
||||
|
if (!IsConnected) |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
fileFormatVersion: 2 |
||||
|
guid: f668ddd1628206e4b83baeb572784acd |
||||
|
MonoImporter: |
||||
|
externalObjects: {} |
||||
|
serializedVersion: 2 |
||||
|
defaultReferences: [] |
||||
|
executionOrder: 0 |
||||
|
icon: {instanceID: 0} |
||||
|
userData: |
||||
|
assetBundleName: |
||||
|
assetBundleVariant: |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue