using System.Collections.Generic;
using UnityEngine;
using System;
using Photon.Pun;
using System.Linq;
using Photon.Realtime;
using Adrenak.BRW;
using ExitGames.Client.Photon;
namespace Adrenak.UniVoice.PUN2Network {
///
/// UniVoice Network implementation that uses the existing Photon PUN2 connection
/// of the application to enable voice chat.
///
///
/// Use this class in applications that are already using PUN2 for networking or
/// multiplayer and you want to add voice chat to it. This will allow voice chat
/// between the Players that are in the room.
///
/// This network implementation doesn't "Create" or "Join" a chatroom, it simply listens
/// to when the application joins a Photon room and makes voice chat between the Photon
/// players happen.
///
/// Create, Join, Leave and Close Chatroom APIs are disabled here.
///
/// TIP: To some extent, all events except ,
/// are not even useful they merely forward PhotonNetwork events. For example, you can keep using
/// Photon events for your voice chat UI (Unless you don't want to be tightly bound to Photon, in
/// which case use them so that you can change the UniVoice network later.)
///
public class UniVoicePUN2NetworkEmbedded : MonoBehaviourPunCallbacks, IChatroomNetwork, IOnEventCallback {
public short OwnID =>
(short)PhotonNetwork.LocalPlayer.ActorNumber;
public List PeerIDs {
get {
return PhotonNetwork.PlayerList
.Where(x => x.ActorNumber != PhotonNetwork.LocalPlayer.ActorNumber)
.Select(x => (short)x.ActorNumber).ToList();
}
}
public event Action OnCreatedChatroom;
public event Action OnChatroomCreationFailed;
public event Action OnClosedChatroom;
public event Action OnJoinedChatroom;
public event Action OnChatroomJoinFailed;
public event Action OnLeftChatroom;
public event Action OnPeerJoinedChatroom;
public event Action OnPeerLeftChatroom;
public event Action OnAudioReceived;
public event Action OnAudioSent;
[Obsolete("Cannot use new keyword to create instance. Use UniVoicePun2NetworkEmbedded.New() instead")]
public UniVoicePUN2NetworkEmbedded() { }
bool isHost;
public byte PhotonEventCode { get; private set; }
///
/// Creates a new instance
///
///
/// The Photon Event Code to be used to send audio event data.
/// If you're using ,
/// be sure to pass a code that you are NOT using.
/// For more info, go to this link: https://web.archive.org/web/20220611162035/https://doc.photonengine.com/en-us/pun/current/gameplay/rpcsandraiseevent
/// and read the RaiseEvent section
///
///
/// Whether the audio data should be sent reliably or not.
/// If you're sending audio in short segments (100ms or less), I advise this be false
/// as reliable data transfer *may* cause congesion in data transfer. (Not too sure)
/// If you're sending larger chunks of audio less frequently, it may not be an issue.
/// Default: false
///
///
public static UniVoicePUN2NetworkEmbedded New(byte photonEventCode, bool reliable = false) {
var go = new GameObject("UniVoicePUN2NetworkEmbedded");
go.hideFlags = HideFlags.DontSave;
var instance = go.AddComponent();
instance.PhotonEventCode = photonEventCode;
return instance;
}
#region PUN2 CALLBACKS
public override void OnJoinedRoom() {
isHost = PhotonNetwork.LocalPlayer.IsMasterClient;
if (isHost)
OnCreatedChatroom?.Invoke();
else {
// Invoke peer joined for each person already in the network
foreach (var other in PeerIDs)
OnPeerJoinedChatroom?.Invoke(other);
// Invoke event that we have joined the network
OnJoinedChatroom?.Invoke((short)PhotonNetwork.LocalPlayer.ActorNumber);
}
}
public override void OnCreateRoomFailed(short returnCode, string message) {
OnChatroomCreationFailed?.Invoke(new Exception(returnCode + " " + message));
}
public override void OnLeftRoom() {
OnClosedChatroom?.Invoke();
OnLeftChatroom?.Invoke();
}
public override void OnJoinRoomFailed(short returnCode, string message) {
OnChatroomJoinFailed?.Invoke(new Exception(returnCode + " " + message));
}
public override void OnJoinRandomFailed(short returnCode, string message) {
OnChatroomJoinFailed?.Invoke(new Exception(returnCode + " " + message));
}
public override void OnDisconnected(DisconnectCause cause) {
OnClosedChatroom?.Invoke();
OnLeftChatroom?.Invoke();
}
public override void OnPlayerEnteredRoom(Player newPlayer) {
OnPeerJoinedChatroom?.Invoke((short)newPlayer.ActorNumber);
}
public override void OnPlayerLeftRoom(Player otherPlayer) {
OnPeerLeftChatroom?.Invoke((short)otherPlayer.ActorNumber);
}
public override void OnMasterClientSwitched(Player newMasterClient) {
isHost = newMasterClient.ActorNumber == PhotonNetwork.LocalPlayer.ActorNumber;
}
#endregion
#region IChatroomNetwork Methods
public void CloseChatroom(object data = null) => ShowStandardWarning();
public void HostChatroom(object data = null) => ShowStandardWarning();
public void JoinChatroom(object data = null) => ShowStandardWarning();
public void LeaveChatroom(object data = null) => ShowStandardWarning();
public void SendAudioSegment(short peerID, ChatroomAudioSegment data) {
var player = GetPlayerByID(peerID);
if (player == null) {
Debug.LogError("No peer with ID " + peerID + " found! Cannot send audio");
return;
}
var w = new BytesWriter();
w.WriteInt(data.segmentIndex);
w.WriteInt(data.frequency);
w.WriteInt(data.channelCount);
w.WriteFloatArray(data.samples);
var options = new RaiseEventOptions { Receivers = ReceiverGroup.Others };
PhotonNetwork.RaiseEvent(PhotonEventCode, w.Bytes, options, SendOptions.SendUnreliable);
OnAudioSent?.Invoke(peerID, data);
}
#endregion
// Photon Event
public void OnEvent(EventData photonEvent) {
if(photonEvent.Code == PhotonEventCode) {
var segment = new ChatroomAudioSegment();
// Data is read in the same order as it was written.
var r = new BytesReader((byte[])photonEvent.CustomData);
segment.segmentIndex = r.ReadInt();
segment.frequency = r.ReadInt();
segment.channelCount = r.ReadInt();
segment.samples = r.ReadFloatArray();
OnAudioReceived?.Invoke((short)photonEvent.Sender, segment);
}
}
Player GetPlayerByID(short id) {
foreach (var player in PhotonNetwork.PlayerList)
if (player.ActorNumber == id)
return player;
return null;
}
public void Dispose() {
Destroy(gameObject);
}
void ShowStandardWarning() =>
Debug.LogWarning($"{GetType().Name} doesn't host, close, join or leave chatrooms. " +
$"It uses the Photon SDK connection. Ignoring...");
}
}