The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [1.0.0-pre.8] - 2022-04-27 ### Changed - `unmanaged` structs are no longer universally accepted as RPC parameters because some structs (i.e., structs with pointers in them, such as `NativeList<T>`) can't be supported by the default memcpy struct serializer. Structs that are intended to be serialized across the network must add `INetworkSerializeByMemcpy` to the interface list (i.e., `struct Foo : INetworkSerializeByMemcpy`). This interface is empty and just serves to mark the struct as compatible with memcpy serialization. For external structs you can't edit, you can pass them to RPCs by wrapping them in `ForceNetworkSerializeByMemcpy<T>`. (#1901) ### Removed - Removed `SIPTransport` (#1870) - Removed `ClientNetworkTransform` from the package samples and moved to Boss Room's Utilities package which can be found [here](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Packages/com.unity.multiplayer.samples.coop/Utilities/Net/ClientAuthority/ClientNetworkTransform.cs). ### Fixed - Fixed `NetworkTransform` generating false positive rotation delta checks when rolling over between 0 and 360 degrees. (#1890) - Fixed client throwing an exception if it has messages in the outbound queue when processing the `NetworkEvent.Disconnect` event and is using UTP. (#1884) - Fixed issue during client synchronization if 'ValidateSceneBeforeLoading' returned false it would halt the client synchronization process resulting in a client that was approved but not synchronized or fully connected with the server. (#1883) - Fixed an issue where UNetTransport.StartServer would return success even if the underlying transport failed to start (#854) - Passing generic types to RPCs no longer causes a native crash (#1901) - Fixed an issue where calling `Shutdown` on a `NetworkManager` that was already shut down would cause an immediate shutdown the next time it was started (basically the fix makes `Shutdown` idempotent). (#1877)
288 lines
11 KiB
C#
288 lines
11 KiB
C#
#if UNITY_UNET_PRESENT
|
|
#pragma warning disable 618 // disable is obsolete
|
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
|
using System;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
|
|
namespace Unity.Netcode.Transports.UNET
|
|
{
|
|
public class UNetTransport : NetworkTransport
|
|
{
|
|
public enum SendMode
|
|
{
|
|
Immediately,
|
|
Queued
|
|
}
|
|
|
|
private int m_UnreliableChannelId;
|
|
private int m_UnreliableSequencedChannelId;
|
|
private int m_ReliableChannelId;
|
|
private int m_ReliableSequencedChannelId;
|
|
private int m_ReliableFragmentedSequencedChannelId;
|
|
|
|
// Inspector / settings
|
|
public int MessageBufferSize = 1024 * 5;
|
|
public int MaxConnections = 100;
|
|
public int MaxSentMessageQueueSize = 128;
|
|
|
|
public string ConnectAddress = "127.0.0.1";
|
|
public int ConnectPort = 7777;
|
|
public int ServerListenPort = 7777;
|
|
|
|
public SendMode MessageSendMode = SendMode.Immediately;
|
|
|
|
// Runtime / state
|
|
private byte[] m_MessageBuffer;
|
|
private WeakReference m_TemporaryBufferReference;
|
|
|
|
// Lookup / translation
|
|
private int m_ServerConnectionId;
|
|
private int m_ServerHostId;
|
|
|
|
public override ulong ServerClientId => GetNetcodeClientId(0, 0, true);
|
|
|
|
internal NetworkManager NetworkManager;
|
|
|
|
protected void LateUpdate()
|
|
{
|
|
if (UnityEngine.Networking.NetworkTransport.IsStarted && MessageSendMode == SendMode.Queued)
|
|
{
|
|
#if UNITY_WEBGL
|
|
Debug.LogError("Cannot use queued sending mode for WebGL");
|
|
#else
|
|
if (NetworkManager != null && NetworkManager.IsServer)
|
|
{
|
|
foreach (var targetClient in NetworkManager.Singleton.ConnectedClientsList)
|
|
{
|
|
SendQueued(targetClient.ClientId);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SendQueued(NetworkManager.Singleton.LocalClientId);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public override void Send(ulong clientId, ArraySegment<byte> payload, NetworkDelivery networkDelivery)
|
|
{
|
|
GetUNetConnectionDetails(clientId, out byte hostId, out ushort connectionId);
|
|
|
|
byte[] buffer;
|
|
if (payload.Offset > 0)
|
|
{
|
|
// UNET cant handle this, do a copy
|
|
|
|
if (m_MessageBuffer.Length >= payload.Count)
|
|
{
|
|
buffer = m_MessageBuffer;
|
|
}
|
|
else
|
|
{
|
|
object bufferRef;
|
|
if (m_TemporaryBufferReference != null && ((bufferRef = m_TemporaryBufferReference.Target) != null) && ((byte[])bufferRef).Length >= payload.Count)
|
|
{
|
|
buffer = (byte[])bufferRef;
|
|
}
|
|
else
|
|
{
|
|
buffer = new byte[payload.Count];
|
|
m_TemporaryBufferReference = new WeakReference(buffer);
|
|
}
|
|
}
|
|
|
|
Buffer.BlockCopy(payload.Array, payload.Offset, buffer, 0, payload.Count);
|
|
}
|
|
else
|
|
{
|
|
buffer = payload.Array;
|
|
}
|
|
|
|
int channelId = -1;
|
|
switch (networkDelivery)
|
|
{
|
|
case NetworkDelivery.Unreliable:
|
|
channelId = m_UnreliableChannelId;
|
|
break;
|
|
case NetworkDelivery.UnreliableSequenced:
|
|
channelId = m_UnreliableSequencedChannelId;
|
|
break;
|
|
case NetworkDelivery.Reliable:
|
|
channelId = m_ReliableChannelId;
|
|
break;
|
|
case NetworkDelivery.ReliableSequenced:
|
|
channelId = m_ReliableSequencedChannelId;
|
|
break;
|
|
case NetworkDelivery.ReliableFragmentedSequenced:
|
|
channelId = m_ReliableFragmentedSequencedChannelId;
|
|
break;
|
|
}
|
|
|
|
if (MessageSendMode == SendMode.Queued)
|
|
{
|
|
#if UNITY_WEBGL
|
|
Debug.LogError("Cannot use queued sending mode for WebGL");
|
|
#else
|
|
UnityEngine.Networking.NetworkTransport.QueueMessageForSending(hostId, connectionId, channelId, buffer, payload.Count, out byte error);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
UnityEngine.Networking.NetworkTransport.Send(hostId, connectionId, channelId, buffer, payload.Count, out byte error);
|
|
}
|
|
}
|
|
|
|
#if !UNITY_WEBGL
|
|
private void SendQueued(ulong clientId)
|
|
{
|
|
GetUNetConnectionDetails(clientId, out byte hostId, out ushort connectionId);
|
|
|
|
UnityEngine.Networking.NetworkTransport.SendQueuedMessages(hostId, connectionId, out _);
|
|
}
|
|
#endif
|
|
|
|
public override NetworkEvent PollEvent(out ulong clientId, out ArraySegment<byte> payload, out float receiveTime)
|
|
{
|
|
var eventType = UnityEngine.Networking.NetworkTransport.Receive(out int hostId, out int connectionId, out _, m_MessageBuffer, m_MessageBuffer.Length, out int receivedSize, out byte error);
|
|
|
|
clientId = GetNetcodeClientId((byte)hostId, (ushort)connectionId, false);
|
|
receiveTime = Time.realtimeSinceStartup;
|
|
|
|
var networkError = (NetworkError)error;
|
|
if (networkError == NetworkError.MessageToLong)
|
|
{
|
|
byte[] tempBuffer;
|
|
|
|
if (m_TemporaryBufferReference != null && m_TemporaryBufferReference.IsAlive && ((byte[])m_TemporaryBufferReference.Target).Length >= receivedSize)
|
|
{
|
|
tempBuffer = (byte[])m_TemporaryBufferReference.Target;
|
|
}
|
|
else
|
|
{
|
|
tempBuffer = new byte[receivedSize];
|
|
m_TemporaryBufferReference = new WeakReference(tempBuffer);
|
|
}
|
|
|
|
eventType = UnityEngine.Networking.NetworkTransport.Receive(out hostId, out connectionId, out _, tempBuffer, tempBuffer.Length, out receivedSize, out error);
|
|
payload = new ArraySegment<byte>(tempBuffer, 0, receivedSize);
|
|
}
|
|
else
|
|
{
|
|
payload = new ArraySegment<byte>(m_MessageBuffer, 0, receivedSize);
|
|
}
|
|
|
|
if (networkError == NetworkError.Timeout)
|
|
{
|
|
// In UNET. Timeouts are not disconnects. We have to translate that here.
|
|
eventType = NetworkEventType.DisconnectEvent;
|
|
}
|
|
|
|
// Translate NetworkEventType to NetEventType
|
|
switch (eventType)
|
|
{
|
|
case NetworkEventType.DataEvent:
|
|
return NetworkEvent.Data;
|
|
case NetworkEventType.ConnectEvent:
|
|
return NetworkEvent.Connect;
|
|
case NetworkEventType.DisconnectEvent:
|
|
return NetworkEvent.Disconnect;
|
|
case NetworkEventType.BroadcastEvent:
|
|
case NetworkEventType.Nothing:
|
|
default:
|
|
return NetworkEvent.Nothing;
|
|
}
|
|
}
|
|
|
|
public override bool StartClient()
|
|
{
|
|
m_ServerHostId = UnityEngine.Networking.NetworkTransport.AddHost(new HostTopology(GetConfig(), 1), 0, null);
|
|
m_ServerConnectionId = UnityEngine.Networking.NetworkTransport.Connect(m_ServerHostId, ConnectAddress, ConnectPort, 0, out byte error);
|
|
return (NetworkError)error == NetworkError.Ok;
|
|
}
|
|
|
|
public override bool StartServer()
|
|
{
|
|
var topology = new HostTopology(GetConfig(), MaxConnections);
|
|
// Undocumented, but AddHost returns -1 in case of any type of failure. See UNET::NetLibraryManager::AddHost
|
|
return -1 != UnityEngine.Networking.NetworkTransport.AddHost(topology, ServerListenPort, null);
|
|
}
|
|
|
|
public override void DisconnectRemoteClient(ulong clientId)
|
|
{
|
|
GetUNetConnectionDetails(clientId, out byte hostId, out ushort connectionId);
|
|
|
|
UnityEngine.Networking.NetworkTransport.Disconnect((int)hostId, (int)connectionId, out byte error);
|
|
}
|
|
|
|
public override void DisconnectLocalClient()
|
|
{
|
|
UnityEngine.Networking.NetworkTransport.Disconnect(m_ServerHostId, m_ServerConnectionId, out byte error);
|
|
}
|
|
|
|
public override ulong GetCurrentRtt(ulong clientId)
|
|
{
|
|
GetUNetConnectionDetails(clientId, out byte hostId, out ushort connectionId);
|
|
|
|
return (ulong)UnityEngine.Networking.NetworkTransport.GetCurrentRTT((int)hostId, (int)connectionId, out byte error);
|
|
}
|
|
|
|
public override void Shutdown()
|
|
{
|
|
UnityEngine.Networking.NetworkTransport.Shutdown();
|
|
}
|
|
|
|
public override void Initialize(NetworkManager networkManager = null)
|
|
{
|
|
NetworkManager = networkManager;
|
|
|
|
m_MessageBuffer = new byte[MessageBufferSize];
|
|
|
|
UnityEngine.Networking.NetworkTransport.Init();
|
|
}
|
|
|
|
private ulong GetNetcodeClientId(byte hostId, ushort connectionId, bool isServer)
|
|
{
|
|
if (isServer)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return (connectionId | (ulong)hostId << 16) + 1;
|
|
}
|
|
|
|
private void GetUNetConnectionDetails(ulong clientId, out byte hostId, out ushort connectionId)
|
|
{
|
|
if (clientId == 0)
|
|
{
|
|
hostId = (byte)m_ServerHostId;
|
|
connectionId = (ushort)m_ServerConnectionId;
|
|
}
|
|
else
|
|
{
|
|
hostId = (byte)((clientId - 1) >> 16);
|
|
connectionId = (ushort)((clientId - 1));
|
|
}
|
|
}
|
|
|
|
private ConnectionConfig GetConfig()
|
|
{
|
|
var connectionConfig = new ConnectionConfig();
|
|
|
|
m_UnreliableChannelId = connectionConfig.AddChannel(QosType.Unreliable);
|
|
m_UnreliableSequencedChannelId = connectionConfig.AddChannel(QosType.UnreliableSequenced);
|
|
m_ReliableChannelId = connectionConfig.AddChannel(QosType.Reliable);
|
|
m_ReliableSequencedChannelId = connectionConfig.AddChannel(QosType.ReliableSequenced);
|
|
m_ReliableFragmentedSequencedChannelId = connectionConfig.AddChannel(QosType.ReliableFragmentedSequenced);
|
|
|
|
connectionConfig.MaxSentMessageQueueSize = (ushort)MaxSentMessageQueueSize;
|
|
|
|
return connectionConfig;
|
|
}
|
|
}
|
|
}
|
|
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
|
|
#pragma warning restore 618 // restore is obsolete
|
|
#endif
|