VelNet-Unity/TestVelGameServer/Packages/VelNetUnity/Samples~/DissonanceIntegration/VelNetDissonancePlayer.cs

222 lines
5.7 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
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 : 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<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;
private enum MessageType : byte
{
AudioData,
DissonanceId,
SpeakingState
}
// This object should not be in the scene at the start.
private void Awake()
{
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 (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<byte> 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);
}
/// <summary>
/// This sort of all initializes dissonance
/// </summary>
/// <param name="id">Dissonance ID</param>
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;
}
}
}
}
}