using System; using System.Collections.Generic; using System.IO; using System.Linq; using Dissonance; using UnityEngine; namespace VelNet { /// /// This should be added to your player object /// [AddComponentMenu("VelNet/Dissonance/VelNet Dissonance Player")] public class VelNetDissonancePlayer : NetworkComponent, IDissonancePlayer { private VelCommsNetwork comms; private bool isSpeaking; private uint lastAudioId; [Header("VelNet Dissonance Player Properties")] public string dissonanceID = ""; //required by dissonance for spatial audio public string PlayerId => dissonanceID; public Vector3 Position => transform.position; public Quaternion Rotation => transform.rotation; public NetworkPlayerType Type => IsMine ? NetworkPlayerType.Local : NetworkPlayerType.Remote; public bool IsTracking => true; private static readonly List allPlayers = new List(); public List closePlayers = new List(); [Tooltip("Maximum distance to transmit voice data. 0 to always send voice to all players.")] public float maxDistance; private enum MessageType : byte { AudioData, DissonanceId, SpeakingState } // This object should not be in the scene at the start. private void Awake() { comms = FindObjectOfType(); if (comms == null) { Debug.LogError("No VelCommsNetwork found. Make sure there is one in your scene.", this); return; } // add ourselves to the global list of all players in the scene if (!allPlayers.Contains(this)) { allPlayers.Add(this); } if (IsMine) { SetDissonanceID(comms.dissonanceId); comms.VoiceQueued += SendVoiceData; //we also need to know when other players join, so we can send the dissonance ID again VelNetManager.instance.OnPlayerJoined += (player) => { using MemoryStream mem = new MemoryStream(); using BinaryWriter writer = new BinaryWriter(mem); writer.Write((byte)MessageType.DissonanceId); writer.Write(dissonanceID); SendBytes(mem.ToArray()); }; VelNetManager.instance.SetupMessageGroup("close", closePlayers.ToArray()); } } private void SendVoiceData(ArraySegment data) { // need to send it if (!IsMine) return; using MemoryStream mem = new MemoryStream(); using BinaryWriter writer = new BinaryWriter(mem); writer.Write((byte)MessageType.AudioData); writer.Write(BitConverter.GetBytes(lastAudioId++)); writer.Write(data.ToArray()); // send voice data unreliably SendBytesToGroup("close", mem.ToArray(), false); } /// /// This sort of all initializes dissonance /// /// Dissonance ID public void SetDissonanceID(string id) { dissonanceID = id; using MemoryStream mem = new MemoryStream(); using BinaryWriter writer = new BinaryWriter(mem); writer.Write((byte)MessageType.DissonanceId); writer.Write(dissonanceID); SendBytes(mem.ToArray()); comms.dissonanceComms.TrackPlayerPosition(this); } private void VoiceInitialized(string id) { dissonanceID = id; } private void OnDestroy() { comms.SetPlayerLeft(dissonanceID); } // Update is called once per frame private void Update() { if (!IsMine) return; // handle nearness cutoff if (maxDistance > 0) { bool closePlayerListChanged = false; foreach (VelNetDissonancePlayer p in allPlayers) { if (p == this) { continue; } float dist = Vector3.Distance(p.transform.position, transform.position); if (dist < maxDistance && !closePlayers.Contains(p.Owner.userid)) { closePlayers.Add(p.Owner.userid); closePlayerListChanged = true; } else if (dist >= maxDistance && closePlayers.Contains(p.Owner.userid)) { closePlayers.Remove(p.Owner.userid); closePlayerListChanged = true; } } if (closePlayerListChanged) { VelNetManager.instance.SetupMessageGroup("close", closePlayers); } } //handle dissonance comms //if we're not speaking, and the comms say we are, send a speaking event, which will be received on other network players and sent to their comms accordingly if (comms.dissonanceComms.FindPlayer(dissonanceID)?.IsSpeaking != isSpeaking) //unfortunately, there does not seem to be an event for this { isSpeaking = !isSpeaking; using MemoryStream mem = new MemoryStream(); using BinaryWriter writer = new BinaryWriter(mem); writer.Write((byte)MessageType.SpeakingState); writer.Write(isSpeaking ? (byte)1 : (byte)0); SendBytes(mem.ToArray()); if (!isSpeaking) { lastAudioId = 0; } } } public override void ReceiveBytes(byte[] message) { using MemoryStream mem = new MemoryStream(message); using BinaryReader reader = new BinaryReader(mem); byte identifier = reader.ReadByte(); switch (identifier) { case 0: //audio data { if (isSpeaking) { comms.VoiceReceived(dissonanceID, message.Skip(1).ToArray()); } break; } case 1: //dissonance id (player joined) { if (dissonanceID == "") // I don't have this yet { dissonanceID = reader.ReadString(); // tell the comms network that this player joined the channel comms.SetPlayerJoined(dissonanceID); // tell dissonance comms.dissonanceComms.TrackPlayerPosition(this); // tell dissonance to track the remote player } break; } case 2: // speaking state { if (message[0] == 0) { comms.SetPlayerStoppedSpeaking(dissonanceID); isSpeaking = false; } else { comms.SetPlayerStartedSpeaking(dissonanceID); isSpeaking = true; } break; } } } } }