diff --git a/MlapiClient.cs b/MlapiClient.cs index a2c3ac7..bb78776 100644 --- a/MlapiClient.cs +++ b/MlapiClient.cs @@ -1,11 +1,7 @@ using Dissonance.Networking; -using MLAPI; -using MLAPI.Messaging; -using MLAPI.Serialization.Pooled; -using MLAPI.Transports.UNET; +using Unity.Netcode; using System; -using System.IO; -using UnityEngine; +using Allocator = Unity.Collections.Allocator; public class MlapiClient : BaseClient { @@ -19,17 +15,19 @@ public MlapiClient(MlapiCommsNetwork network) : base(network) public override void Connect() { // Register receiving packets on the client from the server - CustomMessagingManager.RegisterNamedMessageHandler("DissonanceToClient", (senderClientId, stream) => - { - Int32 length = stream.Length > Int32.MaxValue ? Int32.MaxValue : Convert.ToInt32(stream.Length); - Byte[] buffer = new Byte[length]; - stream.Read(buffer, 0, length); - - base.NetworkReceivedPacket(new ArraySegment(buffer)); - }); + NetworkManager.Singleton.CustomMessagingManager.RegisterNamedMessageHandler("DissonanceToClient", OnDissonanceToClient); Connected(); } + protected void OnDissonanceToClient(ulong id, FastBufferReader reader) + { + reader.ReadValueSafe(out int length); + Byte[] buffer = new Byte[length]; + reader.ReadBytesSafe(ref buffer, length); + + base.NetworkReceivedPacket(new ArraySegment(buffer)); + } + protected override void ReadMessages() { @@ -37,48 +35,40 @@ protected override void ReadMessages() protected override void SendReliable(ArraySegment packet) { - if (NetworkingManager.Singleton.IsHost) + if (NetworkManager.Singleton.IsServer) { - // As we are the host in this scenario we should send the packet directly to the server rather than over the network and avoid loopback issues + // As we are the host in this scenario we should send the packet directly to the server rather than over the network and avoid loop-back issues _network.server.NetworkReceivedPacket(new MlapiConn(), packet); } else { - - using (PooledBitStream stream = PooledBitStream.Get()) - { - using (PooledBitWriter writer = PooledBitWriter.Get(stream)) - { - for (var i = 0; i < packet.Count; i++) - { - writer.WriteByte(packet.Array[i + packet.Offset]); - } - CustomMessagingManager.SendNamedMessage("DissonanceToServer", NetworkingManager.Singleton.ServerClientId, stream, "MLAPI_ANIMATION_UPDATE"); - } + using (FastBufferWriter writer = new FastBufferWriter(packet.Count + 4, Allocator.TempJob)) + { + writer.WriteValueSafe(packet.Count); + writer.WriteBytesSafe(packet.Array, packet.Count, packet.Offset); + + NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("DissonanceToServer", + NetworkManager.Singleton.ServerClientId, writer, NetworkDelivery.Reliable); } } } protected override void SendUnreliable(ArraySegment packet) { - if (NetworkingManager.Singleton.IsHost) + if (NetworkManager.Singleton.IsServer) { // As we are the host in this scenario we should send the packet directly to the server rather than over the network and avoid loopback issues _network.server.NetworkReceivedPacket(new MlapiConn(), packet); } else { + using (FastBufferWriter writer = new FastBufferWriter(packet.Count + 4, Allocator.TempJob)) + { + writer.WriteValueSafe(packet.Count); + writer.WriteBytesSafe(packet.Array, packet.Count, packet.Offset); - using (PooledBitStream stream = PooledBitStream.Get()) - { - using (PooledBitWriter writer = PooledBitWriter.Get(stream)) - { - for (var i = 0; i < packet.Count; i++) - { - writer.WriteByte(packet.Array[i + packet.Offset]); - } - CustomMessagingManager.SendNamedMessage("DissonanceToServer", NetworkingManager.Singleton.ServerClientId, stream, "MLAPI_TIME_SYNC"); - } + NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("DissonanceToServer", + NetworkManager.Singleton.ServerClientId, writer, NetworkDelivery.Unreliable); } } diff --git a/MlapiCommsNetwork.cs b/MlapiCommsNetwork.cs index e298d4c..708f3dd 100644 --- a/MlapiCommsNetwork.cs +++ b/MlapiCommsNetwork.cs @@ -1,7 +1,7 @@ using Dissonance; using Dissonance.Networking; using JetBrains.Annotations; -using MLAPI; +using Unity.Netcode; public class MlapiCommsNetwork : BaseCommsNetwork< @@ -39,13 +39,13 @@ protected override void Update() if (IsInitialized) { // Check if the MLAPI is ready - var networkActive = NetworkingManager.Singleton.isActiveAndEnabled && - (NetworkingManager.Singleton.IsClient || NetworkingManager.Singleton.IsHost); + var networkActive = NetworkManager.Singleton.isActiveAndEnabled && + (NetworkManager.Singleton.IsClient || NetworkManager.Singleton.IsServer); if (networkActive) { // Check what mode the MLAPI is in - var server = NetworkingManager.Singleton.IsHost; - var client = NetworkingManager.Singleton.IsClient; + var server = NetworkManager.Singleton.IsServer; + var client = NetworkManager.Singleton.IsClient; // Check what mode Dissonance is in and if // they're different then call the correct method diff --git a/UNetCommsNetworkEditor.cs b/MlapiCommsNetworkEditor.cs similarity index 89% rename from UNetCommsNetworkEditor.cs rename to MlapiCommsNetworkEditor.cs index 65b3785..862b056 100644 --- a/UNetCommsNetworkEditor.cs +++ b/MlapiCommsNetworkEditor.cs @@ -4,7 +4,7 @@ #if UNITY_EDITOR [CustomEditor(typeof(MlapiCommsNetwork))] - public class UNetCommsNetworkEditor + public class MlapiCommsNetworkEditor : Dissonance.Editor.BaseDissonnanceCommsNetworkEditor< MlapiCommsNetwork, MlapiServer, diff --git a/UNetCommsNetworkEditor.cs.meta b/MlapiCommsNetworkEditor.cs.meta similarity index 100% rename from UNetCommsNetworkEditor.cs.meta rename to MlapiCommsNetworkEditor.cs.meta diff --git a/MlapiPlayer.cs b/MlapiPlayer.cs new file mode 100644 index 0000000..4172ae1 --- /dev/null +++ b/MlapiPlayer.cs @@ -0,0 +1,174 @@ +using UnityEngine; +using Unity.Netcode; +using Dissonance; + + /// + /// When added to the player prefab, allows Dissonance to automatically track + /// the location of remote players for positional audio for games using the + /// MLAPI / NFGO + /// +[RequireComponent(typeof (NetworkObject))] +public class MlapiPlayer : NetworkBehaviour, IDissonancePlayer +{ + private static readonly Log Log = Logs.Create(LogCategory.Network, "HLAPI Player Component"); + + private DissonanceComms _comms; + + public bool IsTracking { get; private set; } + + /// + /// The name of the player + /// + /// + private string _playerId; + public string PlayerId { get { return _playerId; } } + + public Vector3 Position + { + get { return transform.position; } + } + + public Quaternion Rotation + { + get { return transform.rotation; } + } + + public NetworkPlayerType Type + { + get + { + if (_comms == null || _playerId == null) + return NetworkPlayerType.Unknown; + return _comms.LocalPlayerName.Equals(_playerId) ? NetworkPlayerType.Local : NetworkPlayerType.Remote; + } + } + + public override void OnDestroy() + { + base.OnDestroy(); + if (_comms != null) + _comms.LocalPlayerNameChanged -= SetPlayerName; + } + + public void OnEnable() + { + _comms = FindObjectOfType(); + } + + public void OnDisable() + { + if (IsTracking) + StopTracking(); + } + + public override void OnNetworkSpawn() + { + base.OnNetworkSpawn(); + + if(IsClient) + { + OnStartClient(); + + if (IsLocalPlayer) + OnStartLocalPlayer(); + } + } + + protected void OnStartLocalPlayer() + { + var comms = FindObjectOfType(); + if (comms == null) + { + throw Log.CreateUserErrorException( + "cannot find DissonanceComms component in scene", + "not placing a DissonanceComms component on a game object in the scene", + "", + "9A79FDCB-263E-4124-B54D-67EDA39C09A5" + ); + } + + Log.Debug("Tracking `OnStartLocalPlayer` Name={0}", comms.LocalPlayerName); + + // This method is called on the client which has control authority over this object. This will be the local client of whichever player we are tracking. + if (comms.LocalPlayerName != null) + SetPlayerName(comms.LocalPlayerName); + + //Subscribe to future name changes (this is critical because we may not have run the initial set name yet and this will trigger that initial call) + comms.LocalPlayerNameChanged += SetPlayerName; + } + + private void SetPlayerName(string playerName) + { + //We need the player name to be set on all the clients and then tracking to be started (on each client). + //To do this we send a command from this client, informing the server of our name. The server will pass this on to all the clients (with an RPC) + // Client -> Server -> Client + + //We need to stop and restart tracking to handle the name change + if (IsTracking) + StopTracking(); + + //Perform the actual work + _playerId = playerName; + StartTracking(); + + //Inform the server the name has changed + if (IsLocalPlayer) + SetPlayerNameServerRpc(playerName); + } + + protected void OnStartClient() + { + //A client is starting. Start tracking if the name has been properly initialised. + if (!string.IsNullOrEmpty(PlayerId)) + StartTracking(); + } + + /// + /// Invoking on client will cause it to run on the server + /// + /// + [ServerRpc] + private void SetPlayerNameServerRpc(string playerName) + { + _playerId = playerName; + + //Now call the RPC to inform clients they need to handle this changed value + SetPlayerNameClientRpc(playerName); + } + + /// + /// Invoking on the server will cause it to run on all the clients + /// + /// + [ClientRpc] + private void SetPlayerNameClientRpc(string playerName) + { + //received a message from server (on all clients). If this is not the local player then apply the change + if (!IsLocalPlayer) + SetPlayerName(playerName); + } + + private void StartTracking() + { + if (IsTracking) + throw Log.CreatePossibleBugException("Attempting to start player tracking, but tracking is already started", "B7D1F25E-72AF-4E93-8CFF-90CEBEAC68CF"); + + if (_comms != null) + { + _comms.TrackPlayerPosition(this); + IsTracking = true; + } + } + + private void StopTracking() + { + if (!IsTracking) + throw Log.CreatePossibleBugException("Attempting to stop player tracking, but tracking is not started", "EC5C395D-B544-49DC-B33C-7D7533349134"); + + if (_comms != null) + { + _comms.StopTracking(this); + IsTracking = false; + } + } +} \ No newline at end of file diff --git a/MlapiPlayer.cs.meta b/MlapiPlayer.cs.meta new file mode 100644 index 0000000..cf72fa7 --- /dev/null +++ b/MlapiPlayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e68b96cd0875e5a4ea3c05a1185c0c7f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/MlapiServer.cs b/MlapiServer.cs index cdbc656..7b241b7 100644 --- a/MlapiServer.cs +++ b/MlapiServer.cs @@ -1,14 +1,7 @@ using Dissonance.Networking; -using JetBrains.Annotations; -using MLAPI; -using MLAPI.Messaging; -using MLAPI.Serialization.Pooled; -using MLAPI.Transports.UNET; using System; -using System.Collections.Generic; -using System.IO; -using UnityEngine; - +using Unity.Netcode; +using Allocator = Unity.Collections.Allocator; public class MlapiServer : BaseServer { private MlapiCommsNetwork _network; @@ -20,18 +13,23 @@ public MlapiServer(MlapiCommsNetwork network) public override void Connect() { // Register receiving packets on the server from the client - CustomMessagingManager.RegisterNamedMessageHandler("DissonanceToServer", (senderClientId, stream) => - { - var client = new MlapiConn(); - client.clientId = senderClientId; - - Int32 length = stream.Length > Int32.MaxValue ? Int32.MaxValue : Convert.ToInt32(stream.Length); - Byte[] buffer = new Byte[length]; - stream.Read(buffer, 0, length); - - base.NetworkReceivedPacket(client, new ArraySegment(buffer)); - }); + NetworkManager.Singleton.CustomMessagingManager.RegisterNamedMessageHandler("DissonanceToServer", OnDissonanceToServer); base.Connect(); + } + + protected void OnDissonanceToServer(ulong client_id, FastBufferReader reader) + { + var client = new MlapiConn(); + client.clientId = client_id; + + //JP : I think there would be a way to access the raw buffer from the reader and just send that to the array segment. Maybe with unsafe c# + //Would skip the allocation below. + //Alternativly, we could just preallocate a big buffer and reuse it for each message? + reader.ReadValueSafe(out int length); + Byte[] buffer = new Byte[length]; + reader.ReadBytesSafe(ref buffer, length); + + base.NetworkReceivedPacket(client, new ArraySegment(buffer)); } public override void Disconnect() @@ -45,54 +43,43 @@ protected override void ReadMessages() } protected override void SendReliable(MlapiConn destination, ArraySegment packet) - { - using (PooledBitStream stream = PooledBitStream.Get()) - { - using (PooledBitWriter writer = PooledBitWriter.Get(stream)) - { - var count = packet.Count; - var offset = packet.Offset; - for (var i = 0; i < count; i++) - { - writer.WriteByte(packet.Array[i + offset]); - } - if (NetworkingManager.Singleton.LocalClientId == destination.clientId) - { - // As we are the host in this scenario we should send the packet directly to the client rather than over the network and avoid loopback issues - _network.client.NetworkReceivedPacket(packet); - } - else - { - CustomMessagingManager.SendNamedMessage("DissonanceToClient", destination.clientId, stream, "MLAPI_ANIMATION_UPDATE"); - } - } + { + if (NetworkManager.Singleton.LocalClientId == destination.clientId) + { + // As we are the host in this scenario we should send the packet directly to the client rather than over the network and avoid loopback issues + _network.client.NetworkReceivedPacket(packet); + } + else + { + using (FastBufferWriter writer = new FastBufferWriter(packet.Count + 4, Allocator.TempJob)) + { + writer.WriteValueSafe(packet.Count); + writer.WriteBytesSafe(packet.Array, packet.Count, packet.Offset); + + NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("DissonanceToClient", + destination.clientId, writer, NetworkDelivery.Reliable); + } + } } protected override void SendUnreliable(MlapiConn destination, ArraySegment packet) - { - using (PooledBitStream stream = PooledBitStream.Get()) - { - using (PooledBitWriter writer = PooledBitWriter.Get(stream)) - { - var count = packet.Count; - var offset = packet.Offset; - for (var i = 0; i < count; i++) - { - writer.WriteByte(packet.Array[i + offset]); - } - if (NetworkingManager.Singleton.LocalClientId == destination.clientId) - { - // As we are the host in this scenario we should send the packet directly to the client rather than over the network and avoid loopback issues - _network.client.NetworkReceivedPacket(packet); - } - else - { - CustomMessagingManager.SendNamedMessage("DissonanceToClient", destination.clientId, stream, "MLAPI_TIME_SYNC"); - } - } + { + if (NetworkManager.Singleton.LocalClientId == destination.clientId) + { + // As we are the host in this scenario we should send the packet directly to the client rather than over the network and avoid loop-back issues + _network.client.NetworkReceivedPacket(packet); + } + else + { + using (FastBufferWriter writer = new FastBufferWriter(packet.Count + 4, Allocator.TempJob)) + { + writer.WriteValueSafe(packet.Count); + writer.WriteBytesSafe(packet.Array, packet.Count, packet.Offset); + NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("DissonanceToClient", + destination.clientId, writer, NetworkDelivery.Unreliable); + } } - } public void ClientDisconnected()