Open
Description
It would be amazing if RPC arguments could just actually be any NetworkBehaviour
derivative class instead of only NetworkBehaviourReference
so that the compiler could enforce type on those RPC arguments instead of boilerplate cast / TryGet code.
Short of that, doing an RPC with NetworkBehaviourReference<MyNetworkBehaviourClass>
at least gives me peace of mind that the compiler can catch me trying to pass the RPC a reference to a NetworkBehaviour
that isn't a MyNetworkBehaviourClass
.
I did it like this:
using System;
using System.Runtime.CompilerServices;
namespace Unity.Netcode
{
public struct NetworkBehaviourReference<T> : INetworkSerializable, IEquatable<NetworkBehaviourReference<T>>
where T : NetworkBehaviour
{
private NetworkObjectReference m_NetworkObjectReference;
private ushort m_NetworkBehaviourId;
public NetworkBehaviourReference(NetworkBehaviour networkBehaviour)
{
if (networkBehaviour == null)
{
m_NetworkObjectReference = new NetworkObjectReference((NetworkObject)null);
m_NetworkBehaviourId = 0;
return;
// throw new ArgumentNullException(nameof(networkBehaviour));
}
if (networkBehaviour.NetworkObject == null)
{
throw new ArgumentException($"Cannot create {nameof(NetworkBehaviourReference<T>)} from {nameof(NetworkBehaviour)} without a {nameof(NetworkObject)}.");
}
m_NetworkObjectReference = networkBehaviour.NetworkObject;
m_NetworkBehaviourId = networkBehaviour.NetworkBehaviourId;
}
public bool TryGet(out T networkBehaviourSubtype, NetworkManager networkManager = null)
{
networkBehaviourSubtype = GetInternal(this, null) as T;
return networkBehaviourSubtype != null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static NetworkBehaviour GetInternal(NetworkBehaviourReference<T> networkBehaviourRef, NetworkManager networkManager = null)
{
if (networkBehaviourRef.m_NetworkObjectReference.TryGet(out NetworkObject networkObject, networkManager))
{
return networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourRef.m_NetworkBehaviourId);
}
return null;
}
public bool Equals(NetworkBehaviourReference<T> other)
{
return m_NetworkObjectReference.Equals(other.m_NetworkObjectReference) && m_NetworkBehaviourId == other.m_NetworkBehaviourId;
}
public override bool Equals(object obj)
{
return obj is NetworkBehaviourReference<T> other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
return (m_NetworkObjectReference.GetHashCode() * 397) ^ m_NetworkBehaviourId.GetHashCode();
}
}
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
m_NetworkObjectReference.NetworkSerialize(serializer);
serializer.SerializeValue(ref m_NetworkBehaviourId);
}
public static implicit operator T(NetworkBehaviourReference<T> networkBehaviourRef) => GetInternal(networkBehaviourRef) as T;
public static implicit operator NetworkBehaviourReference<T>(T networkBehaviourSubtype) => new NetworkBehaviourReference<T>(networkBehaviourSubtype);
}
}