Why NetworkObject.GetNetworkBehaviourAtOrderIndex is internal?

Hi,

I am making a game with multiplayer. I found the NetworkBehaviourReference really useful to transfer NetworkObject through RPC & networkVariable.

But with the code base growing, it’s become hard to get when a RPC is expecting multiple NetworkBehaviourReference to ensure to have the good order.

So I was planning to duplicate NetworkBehaviourReference and do my own templated version of it. While doing it, I face the issue that NetworkObject.GetNetworkBehaviourAtOrderIndex is an internal method. I don’t get why as in changelog I saw it have been set in public in the past see:
https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.1/changelog/CHANGELOG.html#170---2023-10-11

[1.7.0] - 2023-10-11
Added
exposed NetworkObject.GetNetworkBehaviourAtOrderIndex as a public API (#2724)

So if anyone have an info and why it have been moved back from public to internal, I am looking for it. An if you have a turn around for my problem, it’s welcomed

Thanks

Your best course of action is to get rid of these references!

Really, this “solution” is nothing but a lookup of the behaviour on the remote machines. This is super easy to implement yourself and henceforth you only need to send an int (or ushort or byte depending on how many indexes you can have).

The problem with NetworkBehaviourReference is that you’ll conveniently pass along some component for the remote side. But what you really need is a NetworkObject and nothing but. Recalled from memory:

void TheRpc(NetworkObjectReference netObjRef)
{
    var netObj = netObjRef.TryGet(); // not sure about the name
    var theComponentYouWant = netObj.GetComponent<TheComponentYouWant>();
}

Now if you intend to send an Rpc with a reference to a NetworkBehaviour that isn’t actually on that very object that’s sending the Rpc, you may have or are creating an architectural problem. You don’t want to send around reference to objects other than the one you are working with (best practice) as this object may be getting despawned, and the remote side’s lookup may fail without recovery (or at least requires catching exceptions rather than doing a simple null test).

Thanks for your answer :slight_smile:

I don’t get why it’s a bad practice to send rpc with reference of objects :thinking:. NetworkBehaviour reference “tryget” check if the object exist on network and allow me to cancel the call on client if the object was despawned.

Have you an example steps by steps which can highlight a problem?

__

Finally yesterday I wrapped NetworkBehaviorReference into a templated struct

using System.Runtime.CompilerServices;
using System;
using Unity.Netcode;

/// <summary>
/// A helper struct to force type of a  <see cref="NetworkBehaviourReference"/>.
/// </summary>
public struct NetworkTemplatedBehaviorReference<T> : INetworkSerializable, IEquatable<NetworkTemplatedBehaviorReference<T>> where T : NetworkBehaviour
{
    private NetworkBehaviourReference m_networkBehaviourReference;

    public NetworkTemplatedBehaviorReference(T a_networkBehaviour) : this()
    {
        m_networkBehaviourReference = a_networkBehaviour;
    }

    public bool TryGet(out T o_networkBehaviour)
    {
        o_networkBehaviour = this;
        return o_networkBehaviour != null;
    }

    private static T GetInternal(NetworkTemplatedBehaviorReference<T> a_templateBehaviorRef, NetworkManager a_networkManager = null)
    {
        if (a_templateBehaviorRef.m_networkBehaviourReference.TryGet(out T networkObject, a_networkManager))
        {
            return networkObject;
        }

        return null;
    }

    public void NetworkSerialize<U>(BufferSerializer<U> a_serializer) where U : IReaderWriter
    {
        a_serializer.SerializeValue(ref m_networkBehaviourReference);
    }

    public bool Equals(NetworkTemplatedBehaviorReference<T> other)
    {
        return m_networkBehaviourReference.Equals(other.m_networkBehaviourReference);
    }

    public static implicit operator T(NetworkTemplatedBehaviorReference<T> a_templateBehaviorRef) => GetInternal(a_templateBehaviorRef);

    public static implicit operator NetworkTemplatedBehaviorReference<T>(T a_networkBehaviour) => new NetworkTemplatedBehaviorReference<T>(a_networkBehaviour);

}

And I use it like that:

[Rpc(SendTo.Everyone, RequireOwnership = true)]
public void AddUnitOnTileRpc(NetworkTemplatedBehaviorReference<Tile> a_tileReference, NetworkTemplatedBehaviorReference<Unit> a_unitReference)
{
    if (!a_unitReference.TryGet(out Unit unit))
        return; //unit not found

    if (!a_tileReference.TryGet(out Tile tile))
        return; //tile not found

//from here manpulate tile & unit