204 lines
5.2 KiB
C#
204 lines
5.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Dissonance;
|
|
using UnityEngine;
|
|
|
|
namespace VelNetUnity
|
|
{
|
|
/// <summary>
|
|
/// This should be added to your player object
|
|
/// </summary>
|
|
[AddComponentMenu("VelNetUnity/Dissonance/VelNet Dissonance Player")]
|
|
public class VelNetDissonancePlayer : NetworkObject, 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 => owner.isLocal ? NetworkPlayerType.Local : NetworkPlayerType.Remote;
|
|
public bool IsTracking => true;
|
|
|
|
public Vector3 targetPosition;
|
|
public Quaternion targetRotation;
|
|
|
|
private static readonly List<VelNetDissonancePlayer> allPlayers = new List<VelNetDissonancePlayer>();
|
|
public List<int> closePlayers = new List<int>();
|
|
|
|
[Tooltip("Maximum distance to transmit voice data. 0 to always send voice to all players.")]
|
|
public float maxDistance;
|
|
|
|
// Start is called before the first frame update
|
|
private void Start()
|
|
{
|
|
comms = FindObjectOfType<VelCommsNetwork>();
|
|
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 (owner.isLocal)
|
|
{
|
|
SetDissonanceID(comms.dissonanceId);
|
|
comms.VoiceQueued += SendVoiceData;
|
|
|
|
//we also need to know when other players join, so we can send the dissonance ID again
|
|
|
|
NetworkManager.instance.OnPlayerJoined += (player) =>
|
|
{
|
|
byte[] b = Encoding.UTF8.GetBytes(dissonanceID);
|
|
owner.SendMessage(this, "d", b);
|
|
};
|
|
NetworkManager.instance.SetupMessageGroup("close", closePlayers.ToArray());
|
|
}
|
|
}
|
|
|
|
public override void HandleMessage(string identifier, byte[] message)
|
|
{
|
|
switch (identifier)
|
|
{
|
|
case "a": //audio data
|
|
{
|
|
if (isSpeaking)
|
|
{
|
|
comms.VoiceReceived(dissonanceID, message);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case "d": //dissonance id (player joined)
|
|
{
|
|
if (dissonanceID == "") // I don't have this yet
|
|
{
|
|
dissonanceID = Encoding.UTF8.GetString(message);
|
|
// 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 "x": // speaking state
|
|
{
|
|
if (message[0] == 0)
|
|
{
|
|
comms.SetPlayerStoppedSpeaking(dissonanceID);
|
|
isSpeaking = false;
|
|
}
|
|
else
|
|
{
|
|
comms.SetPlayerStartedSpeaking(dissonanceID);
|
|
isSpeaking = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SendVoiceData(ArraySegment<byte> data)
|
|
{
|
|
// need to send it
|
|
if (owner == null || !owner.isLocal) return;
|
|
|
|
using MemoryStream mem = new MemoryStream();
|
|
using BinaryWriter writer = new BinaryWriter(mem);
|
|
writer.Write(BitConverter.GetBytes(lastAudioId++));
|
|
writer.Write(data.ToArray());
|
|
// send voice data unreliably
|
|
owner.SendGroupMessage(this, "close", "a", mem.ToArray(), false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This sort of all initializes dissonance
|
|
/// </summary>
|
|
/// <param name="id">Dissonance ID</param>
|
|
public void SetDissonanceID(string id)
|
|
{
|
|
dissonanceID = id;
|
|
byte[] b = Encoding.UTF8.GetBytes(dissonanceID);
|
|
owner.SendMessage(this, "d", b);
|
|
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 (owner == null) return;
|
|
if (!owner.isLocal) 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)
|
|
{
|
|
NetworkManager.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;
|
|
byte[] toSend = { isSpeaking ? (byte)1 : (byte)0 };
|
|
owner.SendMessage(this, "x", toSend);
|
|
|
|
if (!isSpeaking)
|
|
{
|
|
lastAudioId = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |