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). ## [2.0.0-pre.3] - 2024-07-23 ### Added - Added: `UnityTransport.GetNetworkDriver` and `UnityTransport.GetLocalEndpoint` methods to expose the driver and local endpoint being used. (#2978) ### Fixed - Fixed issue where deferred despawn was causing GC allocations when converting an `IEnumerable` to a list. (#2983) - Fixed issue where the realtime network stats monitor was not able to display RPC traffic in release builds due to those stats being only available in development builds or the editor. (#2979) - Fixed issue where `NetworkManager.ScenesLoaded` was not being updated if `PostSynchronizationSceneUnloading` was set and any loaded scenes not used during synchronization were unloaded. (#2971) - Fixed issue where `Rigidbody2d` under Unity 6000.0.11f1 has breaking changes where `velocity` is now `linearVelocity` and `isKinematic` is replaced by `bodyType`. (#2971) - Fixed issue where `NetworkSpawnManager.InstantiateAndSpawn` and `NetworkObject.InstantiateAndSpawn` were not honoring the ownerClientId parameter when using a client-server network topology. (#2968) - Fixed issue where internal delta serialization could not have a byte serializer defined when serializing deltas for other types. Added `[GenerateSerializationForType(typeof(byte))]` to both the `NetworkVariable` and `AnticipatedNetworkVariable` classes to assure a byte serializer is defined.(#2962) - Fixed issue when scene management was disabled and the session owner would still try to synchronize a late joining client. (#2962) - Fixed issue when using a distributed authority network topology where it would allow a session owner to spawn a `NetworkObject` prior to being approved. Now, an error message is logged and the `NetworkObject` will not be spawned prior to the client being approved. (#2962) - Fixed issue where attempting to spawn during `NetworkBehaviour.OnInSceneObjectsSpawned` and `NetworkBehaviour.OnNetworkSessionSynchronized` notifications would throw a collection modified exception. (#2962) ### Changed - Changed logic where clients can now set the `NetworkSceneManager` client synchronization mode when using a distributed authority network topology. (#2985)
341 lines
13 KiB
C#
341 lines
13 KiB
C#
using System;
|
|
using Unity.Collections;
|
|
using UnityEngine;
|
|
|
|
namespace Unity.Netcode
|
|
{
|
|
internal static class RpcMessageHelpers
|
|
{
|
|
public static unsafe void Serialize(ref FastBufferWriter writer, ref RpcMetadata metadata, ref FastBufferWriter payload)
|
|
{
|
|
BytePacker.WriteValueBitPacked(writer, metadata.NetworkObjectId);
|
|
BytePacker.WriteValueBitPacked(writer, metadata.NetworkBehaviourId);
|
|
BytePacker.WriteValueBitPacked(writer, metadata.NetworkRpcMethodId);
|
|
writer.WriteBytesSafe(payload.GetUnsafePtr(), payload.Length);
|
|
}
|
|
|
|
public static unsafe bool Deserialize(ref FastBufferReader reader, ref NetworkContext context, ref RpcMetadata metadata, ref FastBufferReader payload, string messageType)
|
|
{
|
|
ByteUnpacker.ReadValueBitPacked(reader, out metadata.NetworkObjectId);
|
|
ByteUnpacker.ReadValueBitPacked(reader, out metadata.NetworkBehaviourId);
|
|
ByteUnpacker.ReadValueBitPacked(reader, out metadata.NetworkRpcMethodId);
|
|
|
|
var networkManager = (NetworkManager)context.SystemOwner;
|
|
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(metadata.NetworkObjectId))
|
|
{
|
|
networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnSpawn, metadata.NetworkObjectId, reader, ref context, messageType);
|
|
return false;
|
|
}
|
|
|
|
var networkObject = networkManager.SpawnManager.SpawnedObjects[metadata.NetworkObjectId];
|
|
var networkBehaviour = networkManager.SpawnManager.SpawnedObjects[metadata.NetworkObjectId].GetNetworkBehaviourAtOrderIndex(metadata.NetworkBehaviourId);
|
|
if (networkBehaviour == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!NetworkBehaviour.__rpc_func_table[networkBehaviour.GetType()].ContainsKey(metadata.NetworkRpcMethodId))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
payload = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.None, reader.Length - reader.Position);
|
|
|
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
|
|
if (NetworkBehaviour.__rpc_name_table[networkBehaviour.GetType()].TryGetValue(metadata.NetworkRpcMethodId, out var rpcMethodName))
|
|
{
|
|
networkManager.NetworkMetrics.TrackRpcReceived(
|
|
context.SenderId,
|
|
networkObject,
|
|
rpcMethodName,
|
|
networkBehaviour.__getTypeName(),
|
|
reader.Length);
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
public static void Handle(ref NetworkContext context, ref RpcMetadata metadata, ref FastBufferReader payload, ref __RpcParams rpcParams)
|
|
{
|
|
var networkManager = (NetworkManager)context.SystemOwner;
|
|
if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(metadata.NetworkObjectId, out var networkObject))
|
|
{
|
|
throw new InvalidOperationException($"An RPC called on a {nameof(NetworkObject)} that is not in the spawned objects list. Please make sure the {nameof(NetworkObject)} is spawned before calling RPCs.");
|
|
}
|
|
var networkBehaviour = networkObject.GetNetworkBehaviourAtOrderIndex(metadata.NetworkBehaviourId);
|
|
|
|
try
|
|
{
|
|
NetworkBehaviour.__rpc_func_table[networkBehaviour.GetType()][metadata.NetworkRpcMethodId](networkBehaviour, payload, rpcParams);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogException(new Exception("Unhandled RPC exception!", ex));
|
|
if (networkManager.LogLevel == LogLevel.Developer)
|
|
{
|
|
Debug.Log($"RPC Table Contents");
|
|
foreach (var entry in NetworkBehaviour.__rpc_func_table[networkBehaviour.GetType()])
|
|
{
|
|
Debug.Log($"{entry.Key} | {entry.Value.Method.Name}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal struct RpcMetadata : INetworkSerializeByMemcpy
|
|
{
|
|
public ulong NetworkObjectId;
|
|
public ushort NetworkBehaviourId;
|
|
public uint NetworkRpcMethodId;
|
|
}
|
|
|
|
internal struct ServerRpcMessage : INetworkMessage
|
|
{
|
|
public int Version => 0;
|
|
|
|
public RpcMetadata Metadata;
|
|
|
|
public FastBufferWriter WriteBuffer;
|
|
public FastBufferReader ReadBuffer;
|
|
|
|
private const string k_Name = "ServerRpcMessage";
|
|
|
|
public unsafe void Serialize(FastBufferWriter writer, int targetVersion)
|
|
{
|
|
RpcMessageHelpers.Serialize(ref writer, ref Metadata, ref WriteBuffer);
|
|
}
|
|
|
|
public unsafe bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
|
{
|
|
return RpcMessageHelpers.Deserialize(ref reader, ref context, ref Metadata, ref ReadBuffer, k_Name);
|
|
}
|
|
|
|
public void Handle(ref NetworkContext context)
|
|
{
|
|
var rpcParams = new __RpcParams
|
|
{
|
|
Server = new ServerRpcParams
|
|
{
|
|
Receive = new ServerRpcReceiveParams
|
|
{
|
|
SenderClientId = context.SenderId
|
|
}
|
|
}
|
|
};
|
|
RpcMessageHelpers.Handle(ref context, ref Metadata, ref ReadBuffer, ref rpcParams);
|
|
}
|
|
}
|
|
|
|
internal struct ClientRpcMessage : INetworkMessage
|
|
{
|
|
public int Version => 0;
|
|
|
|
public RpcMetadata Metadata;
|
|
|
|
public FastBufferWriter WriteBuffer;
|
|
public FastBufferReader ReadBuffer;
|
|
|
|
private const string k_Name = "ClientRpcMessage";
|
|
|
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
|
{
|
|
RpcMessageHelpers.Serialize(ref writer, ref Metadata, ref WriteBuffer);
|
|
}
|
|
|
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
|
{
|
|
return RpcMessageHelpers.Deserialize(ref reader, ref context, ref Metadata, ref ReadBuffer, k_Name);
|
|
}
|
|
|
|
public void Handle(ref NetworkContext context)
|
|
{
|
|
var rpcParams = new __RpcParams
|
|
{
|
|
Client = new ClientRpcParams
|
|
{
|
|
Receive = new ClientRpcReceiveParams
|
|
{
|
|
}
|
|
}
|
|
};
|
|
RpcMessageHelpers.Handle(ref context, ref Metadata, ref ReadBuffer, ref rpcParams);
|
|
}
|
|
}
|
|
|
|
internal struct RpcMessage : INetworkMessage
|
|
{
|
|
public int Version => 0;
|
|
|
|
public RpcMetadata Metadata;
|
|
public ulong SenderClientId;
|
|
|
|
public FastBufferWriter WriteBuffer;
|
|
public FastBufferReader ReadBuffer;
|
|
|
|
private const string k_Name = "RpcMessage";
|
|
|
|
public unsafe void Serialize(FastBufferWriter writer, int targetVersion)
|
|
{
|
|
BytePacker.WriteValuePacked(writer, SenderClientId);
|
|
RpcMessageHelpers.Serialize(ref writer, ref Metadata, ref WriteBuffer);
|
|
}
|
|
|
|
public unsafe bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
|
{
|
|
ByteUnpacker.ReadValuePacked(reader, out SenderClientId);
|
|
|
|
return RpcMessageHelpers.Deserialize(ref reader, ref context, ref Metadata, ref ReadBuffer, k_Name);
|
|
}
|
|
|
|
public void Handle(ref NetworkContext context)
|
|
{
|
|
var rpcParams = new __RpcParams
|
|
{
|
|
Ext = new RpcParams
|
|
{
|
|
Receive = new RpcReceiveParams
|
|
{
|
|
SenderClientId = SenderClientId
|
|
}
|
|
}
|
|
};
|
|
RpcMessageHelpers.Handle(ref context, ref Metadata, ref ReadBuffer, ref rpcParams);
|
|
}
|
|
}
|
|
|
|
// DANGO-EXP TODO: REMOVE THIS
|
|
internal struct ForwardServerRpcMessage : INetworkMessage
|
|
{
|
|
public int Version => 0;
|
|
public ulong OwnerId;
|
|
public NetworkDelivery NetworkDelivery;
|
|
public ServerRpcMessage ServerRpcMessage;
|
|
|
|
public unsafe void Serialize(FastBufferWriter writer, int targetVersion)
|
|
{
|
|
writer.WriteValueSafe(OwnerId);
|
|
writer.WriteValueSafe(NetworkDelivery);
|
|
ServerRpcMessage.Serialize(writer, targetVersion);
|
|
}
|
|
|
|
public unsafe bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
|
{
|
|
reader.ReadValueSafe(out OwnerId);
|
|
reader.ReadValueSafe(out NetworkDelivery);
|
|
ServerRpcMessage.ReadBuffer = new FastBufferReader(reader, Allocator.Persistent, reader.Length - reader.Position, sizeof(RpcMetadata));
|
|
|
|
// If deserializing failed or this message was deferred.
|
|
if (!ServerRpcMessage.Deserialize(reader, ref context, receivedMessageVersion))
|
|
{
|
|
// release this reader as the handler will either be invoked later (deferred) or will not be invoked at all.
|
|
ServerRpcMessage.ReadBuffer.Dispose();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void Handle(ref NetworkContext context)
|
|
{
|
|
var networkManager = (NetworkManager)context.SystemOwner;
|
|
if (networkManager.DAHost)
|
|
{
|
|
try
|
|
{
|
|
// Since this is temporary, we will not be collection metrics for this.
|
|
// DAHost just forwards the message to the owner
|
|
ServerRpcMessage.WriteBuffer = new FastBufferWriter(ServerRpcMessage.ReadBuffer.Length, Allocator.TempJob);
|
|
ServerRpcMessage.WriteBuffer.WriteBytesSafe(ServerRpcMessage.ReadBuffer.ToArray());
|
|
networkManager.ConnectionManager.SendMessage(ref ServerRpcMessage, NetworkDelivery, OwnerId);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogException(ex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NetworkLog.LogErrorServer($"Received {nameof(ForwardServerRpcMessage)} on client-{networkManager.LocalClientId}! Only DAHost may forward RPC messages!");
|
|
}
|
|
ServerRpcMessage.ReadBuffer.Dispose();
|
|
ServerRpcMessage.WriteBuffer.Dispose();
|
|
}
|
|
|
|
}
|
|
|
|
// DANGO-EXP TODO: REMOVE THIS
|
|
internal struct ForwardClientRpcMessage : INetworkMessage
|
|
{
|
|
public int Version => 0;
|
|
public bool BroadCast;
|
|
public ulong[] TargetClientIds;
|
|
public NetworkDelivery NetworkDelivery;
|
|
public ClientRpcMessage ClientRpcMessage;
|
|
|
|
public unsafe void Serialize(FastBufferWriter writer, int targetVersion)
|
|
{
|
|
if (TargetClientIds == null)
|
|
{
|
|
BroadCast = true;
|
|
writer.WriteValueSafe(BroadCast);
|
|
}
|
|
else
|
|
{
|
|
BroadCast = false;
|
|
writer.WriteValueSafe(BroadCast);
|
|
writer.WriteValueSafe(TargetClientIds);
|
|
}
|
|
writer.WriteValueSafe(NetworkDelivery);
|
|
ClientRpcMessage.Serialize(writer, targetVersion);
|
|
}
|
|
|
|
public unsafe bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
|
{
|
|
reader.ReadValueSafe(out BroadCast);
|
|
|
|
if (!BroadCast)
|
|
{
|
|
reader.ReadValueSafe(out TargetClientIds);
|
|
}
|
|
|
|
reader.ReadValueSafe(out NetworkDelivery);
|
|
|
|
ClientRpcMessage.ReadBuffer = new FastBufferReader(reader, Allocator.Persistent, reader.Length - reader.Position, sizeof(RpcMetadata));
|
|
// If deserializing failed or this message was deferred.
|
|
if (!ClientRpcMessage.Deserialize(reader, ref context, receivedMessageVersion))
|
|
{
|
|
// release this reader as the handler will either be invoked later (deferred) or will not be invoked at all.
|
|
ClientRpcMessage.ReadBuffer.Dispose();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void Handle(ref NetworkContext context)
|
|
{
|
|
var networkManager = (NetworkManager)context.SystemOwner;
|
|
if (networkManager.DAHost)
|
|
{
|
|
ClientRpcMessage.WriteBuffer = new FastBufferWriter(ClientRpcMessage.ReadBuffer.Length, Allocator.TempJob);
|
|
ClientRpcMessage.WriteBuffer.WriteBytesSafe(ClientRpcMessage.ReadBuffer.ToArray());
|
|
// Since this is temporary, we will not be collection metrics for this.
|
|
// DAHost just forwards the message to the clients
|
|
if (BroadCast)
|
|
{
|
|
networkManager.ConnectionManager.SendMessage(ref ClientRpcMessage, NetworkDelivery, networkManager.ConnectedClientsIds);
|
|
}
|
|
else
|
|
{
|
|
networkManager.ConnectionManager.SendMessage(ref ClientRpcMessage, NetworkDelivery, TargetClientIds);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NetworkLog.LogErrorServer($"Received {nameof(ForwardClientRpcMessage)} on client-{networkManager.LocalClientId}! Only DAHost may forward RPC messages!");
|
|
}
|
|
ClientRpcMessage.WriteBuffer.Dispose();
|
|
ClientRpcMessage.ReadBuffer.Dispose();
|
|
}
|
|
}
|
|
}
|