some more cleanup and documentation, split dissonance player into its own componenent for simple adding, added some binarywriter extensions

upm
Anton Franzluebbers 2022-01-06 17:58:20 -05:00
parent 55b405c5b8
commit 8861888ade
18 changed files with 598 additions and 452 deletions

View File

@ -8,6 +8,7 @@ using System.Net;
namespace VelNetUnity namespace VelNetUnity
{ {
[AddComponentMenu("VelNetUnity/VelNet Network Manager")]
public class NetworkManager : MonoBehaviour public class NetworkManager : MonoBehaviour
{ {
public enum MessageType public enum MessageType
@ -21,6 +22,8 @@ namespace VelNetUnity
public string host; public string host;
public int port; public int port;
public static NetworkManager instance;
#region private members #region private members
private TcpClient socketConnection; private TcpClient socketConnection;
@ -36,14 +39,14 @@ namespace VelNetUnity
public GameObject playerPrefab; public GameObject playerPrefab;
public Dictionary<int, NetworkPlayer> players = new Dictionary<int, NetworkPlayer>(); public Dictionary<int, NetworkPlayer> players = new Dictionary<int, NetworkPlayer>();
public Action<NetworkPlayer> onJoinedRoom = delegate { }; public Action<NetworkPlayer> OnJoinedRoom;
public Action<NetworkPlayer> onPlayerJoined = delegate { }; public Action<NetworkPlayer> OnPlayerJoined;
public Action<NetworkPlayer> onPlayerLeft = delegate { }; public Action<NetworkPlayer> OnPlayerLeft;
public List<NetworkObject> prefabs = new List<NetworkObject>(); public List<NetworkObject> prefabs = new List<NetworkObject>();
public NetworkObject[] sceneObjects; public NetworkObject[] sceneObjects;
public List<string> deletedSceneObjects = new List<string>(); public List<string> deletedSceneObjects = new List<string>();
public Dictionary<string, NetworkObject> objects = new Dictionary<string, NetworkObject>(); //maintains a list of all known objects on the server (ones that have ids) public readonly Dictionary<string, NetworkObject> objects = new Dictionary<string, NetworkObject>(); //maintains a list of all known objects on the server (ones that have ids)
private NetworkPlayer masterPlayer; private NetworkPlayer masterPlayer;
#endregion #endregion
@ -58,6 +61,15 @@ namespace VelNetUnity
public readonly List<Message> receivedMessages = new List<Message>(); public readonly List<Message> receivedMessages = new List<Message>();
private void Awake()
{
if (instance != null)
{
Debug.LogError("Multiple NetworkManagers detected! Bad!", this);
}
instance = this;
}
private void Start() private void Start()
{ {
ConnectToTcpServer(); ConnectToTcpServer();
@ -112,8 +124,7 @@ namespace VelNetUnity
player.userid = m.sender; player.userid = m.sender;
players.Add(userid, player); players.Add(userid, player);
player.room = m.text; player.room = m.text;
player.manager = this; OnJoinedRoom?.Invoke(player);
onJoinedRoom(player);
} }
} }
else //not for me, a player is joining or leaving else //not for me, a player is joining or leaving
@ -150,9 +161,8 @@ namespace VelNetUnity
player.isLocal = false; player.isLocal = false;
player.room = m.text; player.room = m.text;
player.userid = m.sender; player.userid = m.sender;
player.manager = this;
players.Add(m.sender, player); players.Add(m.sender, player);
onPlayerJoined(player); OnPlayerJoined?.Invoke(player);
} }
} }
} }
@ -498,10 +508,12 @@ namespace VelNetUnity
} }
} }
//changes the designated group that sendto(4) will go to /// <summary>
public void SetupMessageGroup(string groupname, int[] userids) /// changes the designated group that sendto(4) will go to
/// </summary>
public void SetupMessageGroup(string groupName, IEnumerable<int> userIds)
{ {
SendNetworkMessage("5:" + groupname + ":" + String.Join(":", userids)); SendNetworkMessage($"5:{groupName}:{string.Join(":", userIds)}");
} }
public void DeleteNetworkObject(string networkId) public void DeleteNetworkObject(string networkId)

View File

@ -8,8 +8,14 @@ namespace VelNetUnity
public abstract class NetworkObject : MonoBehaviour public abstract class NetworkObject : MonoBehaviour
{ {
public NetworkPlayer owner; public NetworkPlayer owner;
public string networkId; //this is forged from the combination of the creator's id (-1 in the case of a scene object) and an object id, so it's always unique for a room /// <summary>
public string prefabName; //this may be empty if it's not a prefab (scene object) /// This is forged from the combination of the creator's id (-1 in the case of a scene object) and an object id, so it's always unique for a room
/// </summary>
public string networkId;
/// <summary>
/// This may be empty if it's not a prefab (scene object)
/// </summary>
public string prefabName;
public bool isSceneObject; public bool isSceneObject;
public abstract void HandleMessage(string identifier, byte[] message); public abstract void HandleMessage(string identifier, byte[] message);
} }

View File

@ -5,28 +5,37 @@ using System;
namespace VelNetUnity namespace VelNetUnity
{ {
/// <summary>
/// One of these will be instantiated for each player that joins the "room".
/// </summary>
[RequireComponent(typeof(NetworkObject))] [RequireComponent(typeof(NetworkObject))]
[AddComponentMenu("VelNetUnity/VelNet Network Player")]
public class NetworkPlayer : MonoBehaviour public class NetworkPlayer : MonoBehaviour
{ {
public NetworkObject myObject; private NetworkObject myObject;
public int userid; public int userid;
public string username; public string username;
public string room; public string room;
public NetworkManager manager;
public bool isLocal; public bool isLocal;
public int lastObjectId; //for instantiation private NetworkManager manager;
/// <summary>
/// For instantiation
/// </summary>
public int lastObjectId;
private bool isMaster; private bool isMaster;
private void Start() private void Start()
{ {
myObject = GetComponent<NetworkObject>();
myObject.owner = this; myObject.owner = this;
manager = FindObjectOfType<NetworkManager>(); manager = NetworkManager.instance;
manager.onPlayerJoined += HandlePlayerJoined; manager.OnPlayerJoined += HandlePlayerJoined;
} }
public void HandlePlayerJoined(NetworkPlayer player) public void HandlePlayerJoined(NetworkPlayer player)
@ -111,7 +120,7 @@ namespace VelNetUnity
NetworkObject temp = manager.prefabs.Find((prefab) => prefab.name == prefabName); NetworkObject temp = manager.prefabs.Find((prefab) => prefab.name == prefabName);
if (temp != null) if (temp != null)
{ {
NetworkObject instance = Instantiate<NetworkObject>(temp); NetworkObject instance = Instantiate(temp);
instance.networkId = networkId; instance.networkId = networkId;
instance.prefabName = prefabName; instance.prefabName = prefabName;
instance.owner = this; instance.owner = this;
@ -184,7 +193,7 @@ namespace VelNetUnity
NetworkObject temp = manager.prefabs.Find((prefab) => prefab.name == prefabName); NetworkObject temp = manager.prefabs.Find((prefab) => prefab.name == prefabName);
if (temp != null) if (temp != null)
{ {
NetworkObject instance = Instantiate<NetworkObject>(temp); NetworkObject instance = Instantiate(temp);
instance.networkId = networkId; instance.networkId = networkId;
instance.prefabName = prefabName; instance.prefabName = prefabName;
instance.owner = this; instance.owner = this;

View File

@ -0,0 +1,38 @@
using System.IO;
using UnityEngine;
namespace VelNetUnity
{
public static class BinaryWriterExtensions
{
public static void Write(this BinaryWriter writer, Vector3 v)
{
writer.Write(v.x);
writer.Write(v.y);
writer.Write(v.z);
}
public static void Write(this BinaryWriter writer, Quaternion q)
{
writer.Write(q.x);
writer.Write(q.y);
writer.Write(q.z);
writer.Write(q.w);
}
public static Vector3 ReadVector3(this BinaryReader reader)
{
return new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
}
public static Quaternion ReadQuaternion(this BinaryReader reader)
{
return new Quaternion(
reader.ReadSingle(),
reader.ReadSingle(),
reader.ReadSingle(),
reader.ReadSingle()
);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 239a48a05eea49c583384b4113cf4729
timeCreated: 1641508695

View File

@ -1,5 +1,5 @@
using System;
using System.Collections; using System.Collections;
using System.IO;
using UnityEngine; using UnityEngine;
@ -8,6 +8,7 @@ namespace VelNetUnity
/// <summary> /// <summary>
/// A simple class that will sync the position and rotation of a network object /// A simple class that will sync the position and rotation of a network object
/// </summary> /// </summary>
[AddComponentMenu("VelNetUnity/VelNet Sync Transform")]
public class SyncTransform : NetworkObject public class SyncTransform : NetworkObject
{ {
public Vector3 targetPosition; public Vector3 targetPosition;
@ -16,18 +17,13 @@ namespace VelNetUnity
public byte[] GetSyncMessage() public byte[] GetSyncMessage()
{ {
float[] data = new float[7]; using MemoryStream mem = new MemoryStream();
for (int i = 0; i < 3; i++) using BinaryWriter writer = new BinaryWriter(mem);
{
data[i] = transform.position[i];
data[i + 3] = transform.rotation[i];
}
data[6] = transform.rotation[3]; writer.Write(transform.position);
writer.Write(transform.rotation);
byte[] toReturn = new byte[sizeof(float) * data.Length]; return mem.ToArray();
Buffer.BlockCopy(data, 0, toReturn, 0, toReturn.Length);
return toReturn;
} }
public override void HandleMessage(string identifier, byte[] message) public override void HandleMessage(string identifier, byte[] message)
@ -35,18 +31,17 @@ namespace VelNetUnity
switch (identifier) switch (identifier)
{ {
case "s": case "s":
float[] data = new float[7];
Buffer.BlockCopy(message, 0, data, 0, message.Length);
for (int i = 0; i < 3; i++)
{ {
targetPosition[i] = data[i]; using MemoryStream mem = new MemoryStream(message);
targetRotation[i] = data[i + 3]; using BinaryReader reader = new BinaryReader(mem);
}
targetPosition = reader.ReadVector3();
targetRotation = reader.ReadQuaternion();
targetRotation[3] = data[6];
break; break;
} }
} }
}
// Start is called before the first frame update // Start is called before the first frame update
private void Start() private void Start()

View File

@ -0,0 +1,104 @@
using System;
using Dissonance;
using Dissonance.Networking;
using UnityEngine;
namespace VelNetUnity
{
[AddComponentMenu("VelNetUnity/Dissonance/VelNet Comms Network")]
public class VelCommsNetwork : MonoBehaviour, ICommsNetwork
{
public ConnectionStatus Status => manager.connected ? ConnectionStatus.Connected : ConnectionStatus.Disconnected;
public NetworkMode Mode => NetworkMode.Client;
public event Action<NetworkMode> ModeChanged;
public event Action<string, CodecSettings> PlayerJoined;
public event Action<string> PlayerLeft;
public event Action<VoicePacket> VoicePacketReceived;
public event Action<TextMessage> TextPacketReceived;
public event Action<string> PlayerStartedSpeaking;
public event Action<string> PlayerStoppedSpeaking;
public event Action<RoomEvent> PlayerEnteredRoom;
public event Action<RoomEvent> PlayerExitedRoom;
private ConnectionStatus _status = ConnectionStatus.Disconnected;
private CodecSettings initSettings;
public string dissonanceId;
public DissonanceComms comms;
private NetworkManager manager;
/// <summary>
/// listen to this if you want to send voice
/// </summary>
public Action<ArraySegment<byte>> VoiceQueued;
// Start is called before the first frame update
private void Start()
{
_status = ConnectionStatus.Connected;
comms = GetComponent<DissonanceComms>();
manager = NetworkManager.instance;
}
public void Initialize(string playerName, Rooms rooms, PlayerChannels playerChannels, RoomChannels roomChannels, CodecSettings codecSettings)
{
dissonanceId = playerName;
initSettings = codecSettings;
comms.ResetMicrophoneCapture();
}
public void VoiceReceived(string sender, byte[] data)
{
uint sequenceNumber = BitConverter.ToUInt32(data, 0);
VoicePacket vp = new VoicePacket(sender, ChannelPriority.Default, 1, true, new ArraySegment<byte>(data, 4, data.Length - 4), sequenceNumber);
VoicePacketReceived?.Invoke(vp);
}
public void SendText(string data, ChannelType recipientType, string recipientId)
{
Debug.Log("sending text");
}
public void SendVoice(ArraySegment<byte> data)
{
VoiceQueued?.Invoke(data);
}
public void SetPlayerJoined(string id)
{
Debug.Log("Dissonance player joined");
PlayerJoined?.Invoke(id, initSettings);
RoomEvent re = new RoomEvent
{
Joined = true,
Room = "Global",
PlayerName = id
};
PlayerEnteredRoom?.Invoke(re);
}
public void SetPlayerLeft(string id)
{
RoomEvent re = new RoomEvent
{
Joined = false,
Room = "Global",
PlayerName = id
};
PlayerExitedRoom?.Invoke(re);
PlayerLeft?.Invoke(id);
}
public void SetPlayerStartedSpeaking(string id)
{
PlayerStartedSpeaking?.Invoke(id);
}
public void SetPlayerStoppedSpeaking(string id)
{
PlayerStoppedSpeaking?.Invoke(id);
}
}
}

View File

@ -0,0 +1,195 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Dissonance;
using UnityEngine;
namespace VelNetUnity
{
[RequireComponent(typeof(VelCommsNetwork))]
[AddComponentMenu("VelNetUnity/Dissonance/VelNet Dissonance Player")]
public class VelNetDissonancePlayer : NetworkObject, IDissonancePlayer
{
private VelCommsNetwork comms;
private bool isSpeaking;
private uint lastAudioId;
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;
public 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 (!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.comms.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.comms.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.comms.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;
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c773e094326d413bb1bca7f91cbf7f8c
timeCreated: 1641506874

View File

@ -1,109 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Dissonance;
using Dissonance.Extensions;
using Dissonance.Networking;
using UnityEngine;
public class VelCommsNetwork : MonoBehaviour, ICommsNetwork
{
public ConnectionStatus Status
{
get
{
return manager.connected?ConnectionStatus.Connected:ConnectionStatus.Disconnected;
}
}
public NetworkMode Mode
{
get
{
return NetworkMode.Client;
}
}
public event Action<NetworkMode> ModeChanged;
public event Action<string, CodecSettings> PlayerJoined;
public event Action<string> PlayerLeft;
public event Action<VoicePacket> VoicePacketReceived;
public event Action<TextMessage> TextPacketReceived;
public event Action<string> PlayerStartedSpeaking;
public event Action<string> PlayerStoppedSpeaking;
public event Action<RoomEvent> PlayerEnteredRoom;
public event Action<RoomEvent> PlayerExitedRoom;
ConnectionStatus _status = ConnectionStatus.Disconnected;
CodecSettings initSettings;
public string dissonanceId;
public DissonanceComms comms;
public NetworkManager manager;
public Action<ArraySegment<byte>> voiceQueued = delegate { }; //listen to this if you want to send voice
public void Initialize(string playerName, Rooms rooms, PlayerChannels playerChannels, RoomChannels roomChannels, CodecSettings codecSettings)
{
dissonanceId = playerName;
initSettings = codecSettings;
comms.ResetMicrophoneCapture();
}
public void voiceReceived(string sender,byte[] data)
{
uint sequenceNumber = BitConverter.ToUInt32(data, 0);
VoicePacket vp = new VoicePacket(sender, ChannelPriority.Default, 1, true, new ArraySegment<byte>(data,4,data.Length-4), sequenceNumber);
VoicePacketReceived(vp);
}
public void SendText(string data, ChannelType recipientType, string recipientId)
{
Debug.Log("sending text");
}
public void SendVoice(ArraySegment<byte> data)
{
voiceQueued(data);
}
// Start is called before the first frame update
void Start()
{
_status = ConnectionStatus.Connected;
comms = GetComponent<DissonanceComms>();
}
public void playerJoined(string id)
{
Debug.Log("dissonance player joined");
PlayerJoined(id, initSettings);
RoomEvent re = new RoomEvent();
re.Joined = true;
re.Room = "Global";
re.PlayerName = id;
PlayerEnteredRoom(re);
}
public void playerLeft(string id)
{
RoomEvent re = new RoomEvent();
re.Joined = false;
re.Room = "Global";
re.PlayerName = id;
PlayerExitedRoom(re);
PlayerLeft(id);
}
public void playerStartedSpeaking(string id)
{
PlayerStartedSpeaking(id);
}
public void playerStoppedSpeaking(string id)
{
PlayerStoppedSpeaking(id);
}
}

View File

@ -1,7 +1,9 @@
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
namespace VelNetUnity
{
public class NetworkGUI : MonoBehaviour public class NetworkGUI : MonoBehaviour
{ {
public NetworkManager networkManager; public NetworkManager networkManager;
@ -12,50 +14,51 @@ public class NetworkGUI : MonoBehaviour
public List<string> messageBuffer; public List<string> messageBuffer;
public Dropdown microphones; public Dropdown microphones;
Dissonance.DissonanceComms comms; Dissonance.DissonanceComms comms;
public void handleSend()
public void HandleSend()
{ {
if (sendInput.text != "") if (sendInput.text != "")
{ {
networkManager.sendTo(NetworkManager.MessageType.OTHERS,sendInput.text); networkManager.SendTo(NetworkManager.MessageType.OTHERS, sendInput.text);
} }
} }
public void handleLogin()
public void HandleLogin()
{ {
if (userInput.text != "") if (userInput.text != "")
{ {
networkManager.login(userInput.text, "nopass"); networkManager.Login(userInput.text, "nopass");
} }
} }
public void handleJoin()
public void HandleJoin()
{ {
if (roomInput.text != "") if (roomInput.text != "")
{ {
networkManager.Join(roomInput.text);
networkManager.join(roomInput.text);
} }
} }
// Start is called before the first frame update // Start is called before the first frame update
void Start() private void Start()
{ {
comms = GameObject.FindObjectOfType<Dissonance.DissonanceComms>(); comms = FindObjectOfType<Dissonance.DissonanceComms>();
microphones.AddOptions(new List<string>(Microphone.devices)); microphones.AddOptions(new List<string>(Microphone.devices));
networkManager.messageReceived += (m) => { networkManager.MessageReceived += (m) =>
{
string s = m.type + ":" + m.sender + ":" + m.text; string s = m.type + ":" + m.sender + ":" + m.text;
messageBuffer.Add(s); messageBuffer.Add(s);
messages.text = ""; messages.text = "";
if (messageBuffer.Count > 10) if (messageBuffer.Count > 10)
{ {
messageBuffer.RemoveAt(0); messageBuffer.RemoveAt(0);
} }
for(int i = 0; i < messageBuffer.Count; i++)
foreach (string msg in messageBuffer)
{ {
messages.text = messages.text + messageBuffer[i] + "\n"; messages.text = messages.text + msg + "\n";
} }
}; };
} }
@ -64,10 +67,5 @@ public class NetworkGUI : MonoBehaviour
{ {
comms.MicrophoneName = microphones.options[microphones.value].text; comms.MicrophoneName = microphones.options[microphones.value].text;
} }
// Update is called once per frame
void Update()
{
} }
} }

View File

@ -1,192 +1,66 @@
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using UnityEngine; using UnityEngine;
using Dissonance;
using System.Text;
public class PlayerController : NetworkObject, Dissonance.IDissonancePlayer
{
VelCommsNetwork comms;
bool isSpeaking = false;
uint lastAudioId = 0;
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 => this.owner.isLocal ? NetworkPlayerType.Local : NetworkPlayerType.Remote;
public bool IsTracking => true;
namespace VelNetUnity
{
public class PlayerController : NetworkObject
{
public Vector3 targetPosition; public Vector3 targetPosition;
public Quaternion targetRotation; public Quaternion targetRotation;
public List<int> closePlayers = new List<int>(); public byte[] GetSyncMessage()
public byte[] getSyncMessage()
{ {
float[] data = new float[7]; using MemoryStream mem = new MemoryStream();
for (int i = 0; i < 3; i++) using BinaryWriter writer = new BinaryWriter(mem);
{
data[i] = transform.position[i];
data[i + 3] = transform.rotation[i];
}
data[6] = transform.rotation[3];
byte[] toReturn = new byte[sizeof(float) * data.Length]; writer.Write(transform.position);
Buffer.BlockCopy(data, 0, toReturn, 0, toReturn.Length); writer.Write(transform.rotation);
return toReturn;
return mem.ToArray();
} }
public override void handleMessage(string identifier, byte[] message) public override void HandleMessage(string identifier, byte[] message)
{ {
switch (identifier) switch (identifier)
{ {
case "s": // sync message case "s": // sync message
float[] data = new float[7];
Buffer.BlockCopy(message, 0, data, 0, message.Length);
for (int i = 0; i < 3; i++)
{
targetPosition[i] = data[i];
targetRotation[i] = data[i + 3];
}
targetRotation[3] = data[6];
break;
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.playerJoined(dissonanceID); //tell dissonance
comms.comms.TrackPlayerPosition(this); //tell dissonance to track the remote player
}
break;
}
case "x": //speaking state
{
if (message[0]==0)
{
comms.playerStoppedSpeaking(dissonanceID);
isSpeaking = false;
}
else
{ {
using MemoryStream mem = new MemoryStream(message);
using BinaryReader reader = new BinaryReader(mem);
targetPosition = reader.ReadVector3();
targetRotation = reader.ReadQuaternion();
comms.playerStartedSpeaking(dissonanceID);
isSpeaking = true;
}
break; break;
} }
} }
} }
// Start is called before the first frame update // Start is called before the first frame update
void Start() private void Start()
{
//handle dissonance stuff
comms = GameObject.FindObjectOfType<VelCommsNetwork>();
if(comms != null)
{ {
// player controller shouldn't change ownership, so we can check here once
if (owner.isLocal) if (owner.isLocal)
{ {
setDissonanceID(comms.dissonanceId); StartCoroutine(SyncBehavior());
comms.voiceQueued += sendVoiceData;
//we also need to know when other players join, so we can send the dissonance ID again
owner.manager.onPlayerJoined += (player) =>
{
byte[] b = Encoding.UTF8.GetBytes(dissonanceID);
owner.sendMessage(this, "d", b);
};
owner.manager.setupMessageGroup("close", closePlayers.ToArray());
}
}
if (owner.isLocal)
{
StartCoroutine(syncBehavior());
} }
} }
void sendVoiceData(ArraySegment<byte> data)
{
//need to send it
if(owner != null && owner.isLocal)
{
byte[] toSend = new byte[data.Count+4];
byte[] lastAudioIdBytes = BitConverter.GetBytes(lastAudioId++);
Buffer.BlockCopy(lastAudioIdBytes, 0, toSend, 0, 4);
Buffer.BlockCopy(data.Array, data.Offset, toSend, 4, data.Count);
owner.sendGroupMessage(this,"close", "a", toSend, false); //send voice data unreliably
}
}
public void setDissonanceID(string id) //this sort of all initializes dissonance private IEnumerator SyncBehavior()
{
dissonanceID = id;
byte[] b = Encoding.UTF8.GetBytes(dissonanceID);
owner.sendMessage(this, "d", b);
comms.comms.TrackPlayerPosition(this);
}
void voiceInitialized(string id)
{
dissonanceID = id;
}
void OnDestroy()
{
comms.playerLeft(dissonanceID);
}
IEnumerator syncBehavior()
{ {
while (true) while (true)
{ {
owner.sendMessage(this, "s", getSyncMessage()); owner.SendMessage(this, "s", GetSyncMessage());
yield return new WaitForSeconds(.1f); yield return new WaitForSeconds(.1f);
} }
} }
// Update is called once per frame // Update is called once per frame
void Update() private void Update()
{ {
if (owner != null && owner.isLocal) {
PlayerController[] players = GameObject.FindObjectsOfType<PlayerController>();
bool shouldUpdate = false;
for (int i = 0; i < players.Length; i++)
{
if (players[i] == this) { continue; }
float dist = Vector3.Distance(players[i].transform.position, this.transform.position);
if (dist < 2 && !closePlayers.Contains(players[i].owner.userid))
{
closePlayers.Add(players[i].owner.userid);
shouldUpdate = true;
}
else if(dist >=2 && closePlayers.Contains(players[i].owner.userid))
{
closePlayers.Remove(players[i].owner.userid);
shouldUpdate = true;
}
}
if (shouldUpdate)
{
owner.manager.setupMessageGroup("close", closePlayers.ToArray());
}
}
if (owner != null && !owner.isLocal) if (owner != null && !owner.isLocal)
{ {
transform.position = Vector3.Lerp(transform.position, targetPosition, .1f); transform.position = Vector3.Lerp(transform.position, targetPosition, .1f);
@ -194,26 +68,6 @@ public class PlayerController : NetworkObject, Dissonance.IDissonancePlayer
} }
else if (owner != null && owner.isLocal) else if (owner != null && owner.isLocal)
{ {
//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.comms.FindPlayer(dissonanceID).IsSpeaking != isSpeaking) //unfortunately, there does not seem to be an event for this
{
isSpeaking = !isSpeaking;
byte[] toSend = new byte[1];
toSend[0] = isSpeaking ? (byte)1 : (byte)0;
owner.sendMessage(this, "x", toSend);
if (!isSpeaking)
{
lastAudioId = 0;
}
}
Vector3 movement = new Vector3(); Vector3 movement = new Vector3();
movement.x += Input.GetAxis("Horizontal"); movement.x += Input.GetAxis("Horizontal");
movement.y += Input.GetAxis("Vertical"); movement.y += Input.GetAxis("Vertical");
@ -222,21 +76,23 @@ public class PlayerController : NetworkObject, Dissonance.IDissonancePlayer
if (Input.GetKeyDown(KeyCode.Space)) if (Input.GetKeyDown(KeyCode.Space))
{ {
owner.networkInstantiate("TestNetworkedGameObject"); owner.NetworkInstantiate("TestNetworkedGameObject");
} }
if (Input.GetKeyDown(KeyCode.BackQuote)) if (Input.GetKeyDown(KeyCode.BackQuote))
{ {
foreach(KeyValuePair<string,NetworkObject> kvp in owner.manager.objects) foreach (KeyValuePair<string, NetworkObject> kvp in NetworkManager.instance.objects)
{ {
owner.takeOwnership(kvp.Key); owner.TakeOwnership(kvp.Key);
} }
} }
if (Input.GetKeyDown(KeyCode.Backspace)) if (Input.GetKeyDown(KeyCode.Backspace))
{ {
foreach (KeyValuePair<string, NetworkObject> kvp in owner.manager.objects) foreach (KeyValuePair<string, NetworkObject> kvp in NetworkManager.instance.objects)
{ {
owner.networkDestroy(kvp.Key); owner.NetworkDestroy(kvp.Key);
}
} }
} }
} }

View File

@ -14,6 +14,8 @@ GameObject:
- component: {fileID: 5845716565458182149} - component: {fileID: 5845716565458182149}
- component: {fileID: 5713671764962751473} - component: {fileID: 5713671764962751473}
- component: {fileID: -4404668399269848200} - component: {fileID: -4404668399269848200}
- component: {fileID: 7065383111165629622}
- component: {fileID: 1181612843795795320}
m_Layer: 0 m_Layer: 0
m_Name: PlayerPrefab m_Name: PlayerPrefab
m_TagString: Untagged m_TagString: Untagged
@ -109,15 +111,11 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d8d3b6de660834e3e898725928251405, type: 3} m_Script: {fileID: 11500000, guid: d8d3b6de660834e3e898725928251405, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
myObject: {fileID: -4404668399269848200}
userid: 0 userid: 0
username: username:
room: room:
manager: {fileID: 0}
isLocal: 0 isLocal: 0
lastObjectId: 0 lastObjectId: 0
commsNetwork: {fileID: 0}
dissonanceID:
--- !u!114 &-4404668399269848200 --- !u!114 &-4404668399269848200
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -132,5 +130,43 @@ MonoBehaviour:
m_EditorClassIdentifier: m_EditorClassIdentifier:
owner: {fileID: 5713671764962751473} owner: {fileID: 5713671764962751473}
networkId: networkId:
prefabName:
isSceneObject: 0
targetPosition: {x: 0, y: 0, z: 0} targetPosition: {x: 0, y: 0, z: 0}
targetRotation: {x: 0, y: 0, z: 0, w: 0} targetRotation: {x: 0, y: 0, z: 0, w: 0}
--- !u!114 &7065383111165629622
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6139051692386484099}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5d2009d8e264649749c0315d48765749, type: 3}
m_Name:
m_EditorClassIdentifier:
dissonanceId:
comms: {fileID: 0}
--- !u!114 &1181612843795795320
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6139051692386484099}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c773e094326d413bb1bca7f91cbf7f8c, type: 3}
m_Name:
m_EditorClassIdentifier:
owner: {fileID: 5713671764962751473}
networkId:
prefabName:
isSceneObject: 0
dissonanceID:
targetPosition: {x: 0, y: 0, z: 0}
targetRotation: {x: 0, y: 0, z: 0, w: 0}
allPlayers: []
closePlayers:
maxDistance: 0

View File

@ -2121,7 +2121,7 @@ MonoBehaviour:
m_Calls: m_Calls:
- m_Target: {fileID: 244561621} - m_Target: {fileID: 244561621}
m_TargetAssemblyTypeName: NetworkGUI, Assembly-CSharp m_TargetAssemblyTypeName: NetworkGUI, Assembly-CSharp
m_MethodName: handleJoin m_MethodName: HandleJoin
m_Mode: 1 m_Mode: 1
m_Arguments: m_Arguments:
m_ObjectArgument: {fileID: 0} m_ObjectArgument: {fileID: 0}
@ -2725,7 +2725,7 @@ MonoBehaviour:
m_Calls: m_Calls:
- m_Target: {fileID: 244561621} - m_Target: {fileID: 244561621}
m_TargetAssemblyTypeName: NetworkGUI, Assembly-CSharp m_TargetAssemblyTypeName: NetworkGUI, Assembly-CSharp
m_MethodName: handleLogin m_MethodName: HandleLogin
m_Mode: 1 m_Mode: 1
m_Arguments: m_Arguments:
m_ObjectArgument: {fileID: 0} m_ObjectArgument: {fileID: 0}
@ -2858,7 +2858,7 @@ MonoBehaviour:
m_Calls: m_Calls:
- m_Target: {fileID: 244561621} - m_Target: {fileID: 244561621}
m_TargetAssemblyTypeName: NetworkGUI, Assembly-CSharp m_TargetAssemblyTypeName: NetworkGUI, Assembly-CSharp
m_MethodName: handleSend m_MethodName: HandleSend
m_Mode: 1 m_Mode: 1
m_Arguments: m_Arguments:
m_ObjectArgument: {fileID: 0} m_ObjectArgument: {fileID: 0}

View File

@ -9,7 +9,7 @@
"self-hosted" "self-hosted"
], ],
"author": { "author": {
"name": "Kyle Johnsen", "name": "Virtual Experiences Laboratory",
"email": "kjohnsen@uga.edu", "email": "kjohnsen@uga.edu",
"url": "https://vel.engr.uga.edu" "url": "https://vel.engr.uga.edu"
}, },