Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Additional documentation and release notes are available at [Multiplayer Documen

### Fixed

- Fixed issue where invoking an RPC, on another `NetworkBehaviour` associated with the same `NetworkObject` that is ordered before the `NetworkBehaviour` invoking the RPC, during `OnNetworkSpawn` could throw an exception if scene management is disabled. (#3782)
- Fixed issue where the `Axis to Synchronize` toggles didn't work with multi object editing in `NetworkTransform`. (#3781)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -595,9 +595,7 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly,
private const string k_NetworkManager_IsServer = nameof(NetworkManager.IsServer);
private const string k_NetworkManager_IsClient = nameof(NetworkManager.IsClient);
private const string k_NetworkManager_LogLevel = nameof(NetworkManager.LogLevel);

private const string k_NetworkBehaviour_rpc_func_table = nameof(NetworkBehaviour.__rpc_func_table);
private const string k_NetworkBehaviour_rpc_name_table = nameof(NetworkBehaviour.__rpc_name_table);
private const string k_NetworkBehaviour_rpc_exec_stage = nameof(NetworkBehaviour.__rpc_exec_stage);
private const string k_NetworkBehaviour_NetworkVariableFields = nameof(NetworkBehaviour.NetworkVariableFields);
private const string k_NetworkBehaviour_beginSendServerRpc = nameof(NetworkBehaviour.__beginSendServerRpc);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,12 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition)
{
fieldDefinition.IsFamilyOrAssembly = true;
}

#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE)
if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_name_table))
{
fieldDefinition.IsFamilyOrAssembly = true;
}
#endif
}

foreach (var methodDefinition in typeDefinition.Methods)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
"name": "com.unity.nuget.mono-cecil",
"expression": "(0,1.11.4)",
"define": "CECIL_CONSTRAINTS_ARE_TYPE_REFERENCES"
},
{
"name": "com.unity.multiplayer.tools",
"expression": "",
"define": "MULTIPLAYER_TOOLS"
}
],
"noEngineReferences": false
Expand Down
146 changes: 128 additions & 18 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Collections;
using UnityEngine;

Expand Down Expand Up @@ -40,7 +41,7 @@ public abstract class NetworkBehaviour : MonoBehaviour
internal static readonly Dictionary<Type, Dictionary<uint, RpcReceiveHandler>> __rpc_func_table = new Dictionary<Type, Dictionary<uint, RpcReceiveHandler>>();
internal static readonly Dictionary<Type, Dictionary<uint, RpcInvokePermission>> __rpc_permission_table = new Dictionary<Type, Dictionary<uint, RpcInvokePermission>>();

#if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE)
// RuntimeAccessModifiersILPP will make this `public`
internal static readonly Dictionary<Type, Dictionary<uint, string>> __rpc_name_table = new Dictionary<Type, Dictionary<uint, string>>();
#endif
Expand Down Expand Up @@ -142,16 +143,9 @@ internal void __endSendServerRpc(ref FastBufferWriter bufferWriter, uint rpcMeth
}

bufferWriter.Dispose();
#if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
if (__rpc_name_table[GetType()].TryGetValue(rpcMethodId, out var rpcMethodName))
{
networkManager.NetworkMetrics.TrackRpcSent(
NetworkManager.ServerClientId,
m_NetworkObject,
rpcMethodName,
__getTypeName(),
rpcWriteSize);
}

#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE)
TrackRpcMetricsSend(ref serverRpcMessage, rpcMethodId, rpcWriteSize);
#endif
}

Expand Down Expand Up @@ -275,7 +269,11 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth
}

bufferWriter.Dispose();
#if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE)
if (!ValidateRpcMessageMetrics(GetType()))
{
return;
}
if (__rpc_name_table[GetType()].TryGetValue(rpcMethodId, out var rpcMethodName))
{
if (clientRpcParams.Send.TargetClientIds != null)
Expand Down Expand Up @@ -755,6 +753,11 @@ public virtual void OnNetworkPreDespawn() { }

internal virtual void InternalOnNetworkPreSpawn(ref NetworkManager networkManager) { }

/// <summary>
/// Handles pre-spawn related initializations.
/// Invokes any <see cref="InternalOnNetworkPreSpawn"/> subscriptions.
/// Finally invokes <see cref="OnNetworkPreSpawn(ref NetworkManager)"/>.
/// </summary>
internal void NetworkPreSpawn(ref NetworkManager networkManager, NetworkObject networkObject)
{
m_NetworkObject = networkObject;
Expand Down Expand Up @@ -782,13 +785,28 @@ internal void NetworkPreSpawn(ref NetworkManager networkManager, NetworkObject n
}
}

/// <summary>
/// Initializes the:
/// - <see cref="IsSpawned"/> state.
/// - <see cref="NetworkVariableBase"/> instances.
/// - Spawned related properties are applied.
/// !! Note !!:
/// This also populates RPC related tables based on this <see cref="NetworkBehaviour"/>'s RPCs (if any).
/// </summary>
internal void InternalOnNetworkSpawn()
{
IsSpawned = true;
// Initialize the NetworkVariables so they are accessible in OnNetworkSpawn;
// Initialize the NetworkVariables and **RPC tables** so they are accessible in OnNetworkSpawn
InitializeVariables();
// Apply the spawned state/properties to this instance
UpdateNetworkProperties();
}

/// <summary>
/// Handles invoking <see cref="OnNetworkSpawn"/>.
/// </summary>
internal void NetworkSpawn()
{
try
{
OnNetworkSpawn();
Expand All @@ -799,6 +817,9 @@ internal void InternalOnNetworkSpawn()
}
}

/// <summary>
/// Handles invoking <see cref="OnNetworkPostSpawn"/>.
/// </summary>
internal void NetworkPostSpawn()
{
try
Expand All @@ -819,6 +840,9 @@ internal void NetworkPostSpawn()
}
}

/// <summary>
/// Handles invoking <see cref="OnNetworkSessionSynchronized"/>.
/// </summary>
internal void NetworkSessionSynchronized()
{
try
Expand All @@ -832,6 +856,9 @@ internal void NetworkSessionSynchronized()
}
}

/// <summary>
/// Handles invoking <see cref="OnInSceneObjectsSpawned"/>.
/// </summary>
internal void InSceneNetworkObjectsSpawned()
{
try
Expand All @@ -844,6 +871,9 @@ internal void InSceneNetworkObjectsSpawned()
}
}

/// <summary>
/// Handles invoking <see cref="OnNetworkPreDespawn"/>.
/// </summary>
internal void InternalOnNetworkPreDespawn()
{
try
Expand All @@ -863,6 +893,9 @@ internal void InternalOnNetworkPreDespawn()
}
}

/// <summary>
/// Handles invoking <see cref="OnNetworkDespawn"/>.
/// </summary>
internal void InternalOnNetworkDespawn()
{
IsSpawned = false;
Expand Down Expand Up @@ -965,13 +998,90 @@ internal virtual void __initializeRpcs()
internal void __registerRpc(uint hash, RpcReceiveHandler handler, string rpcMethodName, RpcInvokePermission permission)
#pragma warning restore IDE1006 // restore naming rule violation check
{
__rpc_func_table[GetType()][hash] = handler;
__rpc_permission_table[GetType()][hash] = permission;
#if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
__rpc_name_table[GetType()][hash] = rpcMethodName;
var rpcType = GetType();
__rpc_func_table[rpcType][hash] = handler;
__rpc_permission_table[rpcType][hash] = permission;
#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE)
__rpc_name_table[rpcType][hash] = rpcMethodName;
#endif
}

#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool ValidateRpcMessageMetrics(Type type)
{
if (m_NetworkManager == null)
{
Debug.LogError($"[{nameof(ValidateRpcMessageMetrics)}][{type.Name}] {nameof(NetworkBehaviour)} is attempting to invoking an RPC before {nameof(NetworkManager)} has been initialized!");
return false;
}

if (!__rpc_name_table.ContainsKey(type))
{
Debug.LogError($"[{nameof(ValidateRpcMessageMetrics)}][{type.Name}][{nameof(__rpc_name_table)}] RPC table initialization failure: Table does not contain an entry for {type.Name}!");
return false;
}
return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void TrackRpcMetricsSend(ref ServerRpcMessage message, uint rpcMethodId, int rpcWriteSize)
{
var type = GetType();
if (!ValidateRpcMessageMetrics(type))
{
return;
}
if (__rpc_name_table[type].TryGetValue(rpcMethodId, out var rpcMethodName))
{
m_NetworkManager.NetworkMetrics.TrackRpcSent(
NetworkManager.ServerClientId,
m_NetworkObject,
rpcMethodName,
__getTypeName(),
rpcWriteSize);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void TrackRpcMetricsSend(ulong clientId, ref RpcMessage message, int length)
{
var type = GetType();
if (!ValidateRpcMessageMetrics(type))
{
return;
}
if (__rpc_name_table[type].TryGetValue(message.Metadata.NetworkRpcMethodId, out var rpcMethodName))
{
m_NetworkManager.NetworkMetrics.TrackRpcSent(
m_NetworkManager.LocalClientId,
NetworkObject,
rpcMethodName,
__getTypeName(),
length);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void TrackRpcMetricsReceive(ref RpcMetadata metadata, ref NetworkContext context, int length)
{
var type = GetType();
if (!ValidateRpcMessageMetrics(type))
{
return;
}
if (__rpc_name_table[type].TryGetValue(metadata.NetworkRpcMethodId, out var rpcMethodName))
{
m_NetworkManager.NetworkMetrics.TrackRpcReceived(
context.SenderId,
NetworkObject,
rpcMethodName,
__getTypeName(),
length);
}
}
#endif

#pragma warning disable IDE1006 // disable naming rule violation check
// RuntimeAccessModifiersILPP will make this `protected`
// Using this method here because ILPP doesn't seem to let us do visibility modification on properties.
Expand Down Expand Up @@ -1014,7 +1124,7 @@ internal void InitializeVariables()
{
__rpc_func_table[GetType()] = new Dictionary<uint, RpcReceiveHandler>();
__rpc_permission_table[GetType()] = new Dictionary<uint, RpcInvokePermission>();
#if UNITY_EDITOR || DEVELOPMENT_BUILD || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE)
__rpc_name_table[GetType()] = new Dictionary<uint, string>();
#endif
__initializeRpcs();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,8 @@ public class NetworkManager : MonoBehaviour, INetworkUpdateSystem
// RuntimeAccessModifiersILPP will make this `public`
internal static readonly Dictionary<uint, RpcReceiveHandler> __rpc_func_table = new Dictionary<uint, RpcReceiveHandler>();

#if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
// RuntimeAccessModifiersILPP will make this `public`
// RuntimeAccessModifiersILPP will make this `public` (legacy table should be removed in v3.x.x)
internal static readonly Dictionary<uint, string> __rpc_name_table = new Dictionary<uint, string>();
#endif

#pragma warning restore IDE1006 // restore naming rule violation check

Expand Down
18 changes: 17 additions & 1 deletion com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2556,6 +2556,12 @@ internal void InvokeBehaviourNetworkSpawn()
{
NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId);

// Always invoke all InternalOnNetworkSpawn methods on each child NetworkBehaviour
// ** before ** invoking OnNetworkSpawn.
// This assures all NetworkVariables and RPC related tables have been initialized
// prior to invoking OnNetworkSpawn so cross NetworkBehaviour:
// - accessing of NetworkVariables will work correctly.
// - invocation of RPCs will work properly (and not throw exception under certain scenarios)
foreach (var childBehaviour in ChildNetworkBehaviours)
{
if (!childBehaviour.gameObject.activeInHierarchy)
Expand All @@ -2565,6 +2571,17 @@ internal void InvokeBehaviourNetworkSpawn()
}
childBehaviour.InternalOnNetworkSpawn();
}

// After initialization, we can then invoke OnNetworkSpawn on each child NetworkBehaviour.
foreach (var childBehaviour in ChildNetworkBehaviours)
{
if (!childBehaviour.gameObject.activeInHierarchy)
{
Debug.LogWarning($"{GenerateDisabledNetworkBehaviourWarning(childBehaviour)}");
continue;
}
childBehaviour.NetworkSpawn();
}
}

internal void InvokeBehaviourNetworkPostSpawn()
Expand All @@ -2578,7 +2595,6 @@ internal void InvokeBehaviourNetworkPostSpawn()
}
}


internal void InternalNetworkSessionSynchronized()
{
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,8 @@ public static unsafe bool Deserialize(ref FastBufferReader reader, ref NetworkCo
}

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);
}
#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE)
networkBehaviour.TrackRpcMetricsReceive(ref metadata, ref context, reader.Length);
#endif
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,10 @@ protected void CheckLockBeforeDispose()

private protected void SendMessageToClient(NetworkBehaviour behaviour, ulong clientId, ref RpcMessage message, NetworkDelivery delivery)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
var size =
#endif
behaviour.NetworkManager.MessageManager.SendMessage(ref message, delivery, clientId);

#if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
if (NetworkBehaviour.__rpc_name_table[behaviour.GetType()].TryGetValue(message.Metadata.NetworkRpcMethodId, out var rpcMethodName))
{
behaviour.NetworkManager.NetworkMetrics.TrackRpcSent(
clientId,
behaviour.NetworkObject,
rpcMethodName,
behaviour.__getTypeName(),
size);
}
var size = behaviour.NetworkManager.MessageManager.SendMessage(ref message, delivery, clientId);
#if MULTIPLAYER_TOOLS && (DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE)
// Send to a specific client
behaviour.TrackRpcMetricsSend(clientId, ref message, size);
#endif
}
}
Expand Down
Loading