#if MIRROR using System; using System.Linq; using System.Collections.Generic; using Mirror; using Adrenak.BRW; using UnityEngine; namespace Adrenak.UniVoice.Networks { /// /// Activate this class by including the UNIVOICE_MIRROR_NETWORK compilaton symbol /// in your project. /// This is the implementation of interface for Mirror. /// It uses the Mirror transport to send and receive UniVoice data to the server. /// public class MirrorClient : IAudioClient { const string TAG = "[MirrorClient]"; public int ID { get; private set; } = -1; public List PeerIDs { get; private set; } public VoiceSettings YourVoiceSettings { get; private set; } public event Action> OnJoined; public event Action OnLeft; public event Action OnPeerJoined; public event Action OnPeerLeft; public event Action OnReceivedPeerAudioFrame; readonly MirrorModeObserver mirrorEvents; public MirrorClient() { PeerIDs = new List(); YourVoiceSettings = new VoiceSettings(); mirrorEvents = MirrorModeObserver.New("for MirrorClient"); mirrorEvents.ModeChanged += OnModeChanged; NetworkClient.RegisterHandler(OnReceivedMessage, false); } public void Dispose() { PeerIDs.Clear(); } void OnModeChanged(NetworkManagerMode oldMode, NetworkManagerMode newMode) { // For some reason, handlers don't always work as expected when the connection mode changes NetworkClient.ReplaceHandler(OnReceivedMessage); bool clientOnlyToOffline = newMode == NetworkManagerMode.Offline && oldMode == NetworkManagerMode.ClientOnly; bool hostToServerOnlyOrOffline = oldMode == NetworkManagerMode.Host; if (clientOnlyToOffline || hostToServerOnlyOrOffline) { // We unregister the handler only when the device was a client. // If it was a Host that's now a ServerOnly, we still need the handler as it's used in MirrorServer if (clientOnlyToOffline) NetworkClient.UnregisterHandler(); OnClientDisconnected(); } } void OnClientDisconnected() { YourVoiceSettings = new VoiceSettings(); var oldPeerIds = PeerIDs; PeerIDs.Clear(); ID = -1; foreach (var peerId in oldPeerIds) OnPeerLeft?.Invoke(peerId); OnLeft?.Invoke(); } void OnReceivedMessage(MirrorMessage msg) { var reader = new BytesReader(msg.data); var tag = reader.ReadString(); switch (tag) { // When the server sends the data to initial this client with. // This includes the ID of this client along with the IDs of all the // peers that are already connected to the server case MirrorMessageTags.PEER_INIT: ID = reader.ReadInt(); PeerIDs = reader.ReadIntArray().ToList(); string log = $"Initialized with ID {ID}. "; if (PeerIDs.Count > 0) log += $"Peer list: {string.Join(", ", PeerIDs)}"; else log += "There are currently no peers."; Debug.unityLogger.Log(LogType.Log, TAG, log); OnJoined?.Invoke(ID, PeerIDs); foreach (var peerId in PeerIDs) OnPeerJoined?.Invoke(peerId); break; // When the server notifies that a new peer has joined the network case MirrorMessageTags.PEER_JOINED: var newPeerID = reader.ReadInt(); if (!PeerIDs.Contains(newPeerID)) { PeerIDs.Add(newPeerID); Debug.unityLogger.Log(LogType.Log, TAG, $"Peer {newPeerID} joined. Peer list is now {string.Join(", ", PeerIDs)}"); OnPeerJoined?.Invoke(newPeerID); } break; // When the server notifies that a peer has left the network case MirrorMessageTags.PEER_LEFT: var leftPeerID = reader.ReadInt(); if (PeerIDs.Contains(leftPeerID)) { PeerIDs.Remove(leftPeerID); string log2 = $"Peer {leftPeerID} left. "; if (PeerIDs.Count == 0) log2 += "There are no peers anymore."; else log2 += $"Peer list is now {string.Join(", ", PeerIDs)}"; Debug.unityLogger.Log(LogType.Log, TAG, log2); OnPeerLeft?.Invoke(leftPeerID); } break; // When the server sends audio from a peer meant for this client case MirrorMessageTags.AUDIO_FRAME: var sender = reader.ReadInt(); if (sender == ID || !PeerIDs.Contains(sender)) return; var frame = new AudioFrame { timestamp = reader.ReadLong(), frequency = reader.ReadInt(), channelCount = reader.ReadInt(), samples = reader.ReadByteArray() }; OnReceivedPeerAudioFrame?.Invoke(sender, frame); break; } } /// /// Sends an audio frame captured on this client to the server /// /// public void SendAudioFrame(AudioFrame frame) { if (ID == -1) return; var writer = new BytesWriter(); writer.WriteString(MirrorMessageTags.AUDIO_FRAME); writer.WriteInt(ID); writer.WriteLong(frame.timestamp); writer.WriteInt(frame.frequency); writer.WriteInt(frame.channelCount); writer.WriteByteArray(frame.samples); var message = new MirrorMessage { data = writer.Bytes }; NetworkClient.Send(message, Channels.Unreliable); } /// /// Updates the server with the voice settings of this client /// public void SubmitVoiceSettings() { if (ID == -1) return; Debug.unityLogger.Log(TAG, "Submitting : " + YourVoiceSettings); var writer = new BytesWriter(); writer.WriteString(MirrorMessageTags.VOICE_SETTINGS); writer.WriteInt(YourVoiceSettings.muteAll ? 1 : 0); writer.WriteIntArray(YourVoiceSettings.mutedPeers.ToArray()); writer.WriteInt(YourVoiceSettings.deafenAll ? 1 : 0); writer.WriteIntArray(YourVoiceSettings.deafenedPeers.ToArray()); writer.WriteStringArray(YourVoiceSettings.myTags.ToArray()); writer.WriteStringArray(YourVoiceSettings.mutedTags.ToArray()); writer.WriteStringArray(YourVoiceSettings.deafenedTags.ToArray()); var message = new MirrorMessage { data = writer.Bytes }; NetworkClient.Send(message, Channels.Reliable); } public void UpdateVoiceSettings(Action modification) { modification?.Invoke(YourVoiceSettings); SubmitVoiceSettings(); } } } #endif