Fix #75, determine the RPC target (#93)

* Fix issue #75, determining the command target

If you add the same networkbehavior twice to a gameobject,  only the commands in the first one work.

This can also happen if you have a base class,  you inherit it,  and you add more than one of the inherited classes to the same gameobject.

* Improve error message

* Fix issue #75 for all RPC methods

* Fix formatting consistency

* Fix build,  replace logDebug with Debug

* Use new NetworkBehaviours cache

* Use FindIndex instead than for loop

* No need for local variable

* use int instead of byte for simplicity

* component index are int now
This commit is contained in:
Paul Pacheco 2018-10-23 11:17:42 -05:00 committed by vis2k
parent 1795cf8c2c
commit 39ad76cad1
5 changed files with 57 additions and 55 deletions

View File

@ -555,7 +555,7 @@ static void OnRPCMessage(NetworkMessage netMsg)
NetworkIdentity uv;
if (s_NetworkScene.GetNetworkIdentity(message.netId, out uv))
{
uv.HandleRPC(message.rpcHash, new NetworkReader(message.payload));
uv.HandleRPC(message.componentIndex, message.rpcHash, new NetworkReader(message.payload));
}
else
{
@ -573,7 +573,7 @@ static void OnSyncEventMessage(NetworkMessage netMsg)
NetworkIdentity uv;
if (s_NetworkScene.GetNetworkIdentity(message.netId, out uv))
{
uv.HandleSyncEvent(message.eventHash, new NetworkReader(message.payload));
uv.HandleSyncEvent(message.componentIndex, message.eventHash, new NetworkReader(message.payload));
}
else
{

View File

@ -149,12 +149,14 @@ public class RemovePlayerMessage : MessageBase
class CommandMessage : MessageBase
{
public uint netId;
public int componentIndex;
public int cmdHash;
public byte[] payload; // the parameters for the Cmd function
public override void Deserialize(NetworkReader reader)
{
netId = reader.ReadPackedUInt32();
componentIndex = (int)reader.ReadPackedUInt32();
cmdHash = reader.ReadInt32(); // hash is always 4 full bytes, WritePackedInt would send 1 extra byte here
payload = reader.ReadBytesAndSize();
}
@ -162,6 +164,7 @@ public override void Deserialize(NetworkReader reader)
public override void Serialize(NetworkWriter writer)
{
writer.WritePackedUInt32(netId);
writer.WritePackedUInt32((uint)componentIndex);
writer.Write(cmdHash);
writer.WriteBytesAndSize(payload);
}
@ -170,12 +173,14 @@ public override void Serialize(NetworkWriter writer)
class RpcMessage : MessageBase
{
public uint netId;
public int componentIndex;
public int rpcHash;
public byte[] payload; // the parameters for the Rpc function
public override void Deserialize(NetworkReader reader)
{
netId = reader.ReadPackedUInt32();
componentIndex = (int)reader.ReadPackedUInt32();
rpcHash = reader.ReadInt32(); // hash is always 4 full bytes, WritePackedInt would send 1 extra byte here
payload = reader.ReadBytesAndSize();
}
@ -183,6 +188,7 @@ public override void Deserialize(NetworkReader reader)
public override void Serialize(NetworkWriter writer)
{
writer.WritePackedUInt32(netId);
writer.WritePackedUInt32((uint)componentIndex);
writer.Write(rpcHash);
writer.WriteBytesAndSize(payload);
}
@ -191,12 +197,14 @@ public override void Serialize(NetworkWriter writer)
class SyncEventMessage : MessageBase
{
public uint netId;
public int componentIndex;
public int eventHash;
public byte[] payload; // the parameters for the Rpc function
public override void Deserialize(NetworkReader reader)
{
netId = reader.ReadPackedUInt32();
componentIndex = (int)reader.ReadPackedUInt32();
eventHash = reader.ReadInt32(); // hash is always 4 full bytes, WritePackedInt would send 1 extra byte here
payload = reader.ReadBytesAndSize();
}
@ -204,6 +212,7 @@ public override void Deserialize(NetworkReader reader)
public override void Serialize(NetworkWriter writer)
{
writer.WritePackedUInt32(netId);
writer.WritePackedUInt32((uint)componentIndex);
writer.Write(eventHash);
writer.WriteBytesAndSize(payload);
}

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using UnityEngine;
@ -48,6 +48,21 @@ NetworkIdentity myView
}
}
public int ComponentIndex
{
get
{
int index = Array.FindIndex(m_MyView.NetworkBehaviours, component => component == this);
if (index < 0)
{
// this should never happen
Debug.LogError("Could not find component in gameobject. You shoud not add/remove components in networked objects dinamically", this);
}
return index;
}
}
// this gets called in the constructor by the weaver
// for every SyncObject in the component (e.g. SyncLists).
// We collect all of them and we synchronize them with OnSerialize/OnDeserialize
@ -77,6 +92,7 @@ protected void SendCommandInternal(int cmdHash, NetworkWriter writer, string cmd
// construct the message
CommandMessage message = new CommandMessage();
message.netId = netId;
message.componentIndex = ComponentIndex;
message.cmdHash = cmdHash;
message.payload = writer.ToArray();
@ -104,6 +120,7 @@ protected void SendRPCInternal(int rpcHash, NetworkWriter writer, string rpcName
// construct the message
RpcMessage message = new RpcMessage();
message.netId = netId;
message.componentIndex = ComponentIndex;
message.rpcHash = rpcHash;
message.payload = writer.ToArray();
@ -123,6 +140,7 @@ protected void SendTargetRPCInternal(NetworkConnection conn, int rpcHash, Networ
// construct the message
RpcMessage message = new RpcMessage();
message.netId = netId;
message.componentIndex = ComponentIndex;
message.rpcHash = rpcHash;
message.payload = writer.ToArray();
@ -149,6 +167,7 @@ protected void SendEventInternal(int eventHash, NetworkWriter writer, string eve
// construct the message
SyncEventMessage message = new SyncEventMessage();
message.netId = netId;
message.componentIndex = ComponentIndex;
message.eventHash = eventHash;
message.payload = writer.ToArray();
@ -244,28 +263,27 @@ internal static string GetInvoker(int cmdHash)
}
// wrapper fucntions for each type of network operation
internal static bool GetInvokerForHashCommand(int cmdHash, out Type invokeClass, out CmdDelegate invokeFunction)
internal static bool GetInvokerForHashCommand(int cmdHash, out CmdDelegate invokeFunction)
{
return GetInvokerForHash(cmdHash, UNetInvokeType.Command, out invokeClass, out invokeFunction);
return GetInvokerForHash(cmdHash, UNetInvokeType.Command, out invokeFunction);
}
internal static bool GetInvokerForHashClientRpc(int cmdHash, out Type invokeClass, out CmdDelegate invokeFunction)
internal static bool GetInvokerForHashClientRpc(int cmdHash, out CmdDelegate invokeFunction)
{
return GetInvokerForHash(cmdHash, UNetInvokeType.ClientRpc, out invokeClass, out invokeFunction);
return GetInvokerForHash(cmdHash, UNetInvokeType.ClientRpc, out invokeFunction);
}
internal static bool GetInvokerForHashSyncEvent(int cmdHash, out Type invokeClass, out CmdDelegate invokeFunction)
internal static bool GetInvokerForHashSyncEvent(int cmdHash, out CmdDelegate invokeFunction)
{
return GetInvokerForHash(cmdHash, UNetInvokeType.SyncEvent, out invokeClass, out invokeFunction);
return GetInvokerForHash(cmdHash, UNetInvokeType.SyncEvent, out invokeFunction);
}
static bool GetInvokerForHash(int cmdHash, UNetInvokeType invokeType, out Type invokeClass, out CmdDelegate invokeFunction)
static bool GetInvokerForHash(int cmdHash, UNetInvokeType invokeType, out CmdDelegate invokeFunction)
{
Invoker invoker = null;
if (!s_CmdHandlerDelegates.TryGetValue(cmdHash, out invoker))
{
if (LogFilter.Debug) { Debug.Log("GetInvokerForHash hash:" + cmdHash + " not found"); }
invokeClass = null;
invokeFunction = null;
return false;
}
@ -273,7 +291,6 @@ static bool GetInvokerForHash(int cmdHash, UNetInvokeType invokeType, out Type i
if (invoker == null)
{
if (LogFilter.Debug) { Debug.Log("GetInvokerForHash hash:" + cmdHash + " invoker null"); }
invokeClass = null;
invokeFunction = null;
return false;
}
@ -281,12 +298,10 @@ static bool GetInvokerForHash(int cmdHash, UNetInvokeType invokeType, out Type i
if (invoker.invokeType != invokeType)
{
Debug.LogError("GetInvokerForHash hash:" + cmdHash + " mismatched invokeType");
invokeClass = null;
invokeFunction = null;
return false;
}
invokeClass = invoker.invokeClass;
invokeFunction = invoker.invokeFunction;
return true;
}

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
@ -546,24 +546,8 @@ internal void HandleClientAuthority(bool authority)
ForceAuthority(authority);
}
// helper function for Handle** functions
bool GetInvokeComponent(int cmdHash, Type invokeClass, out NetworkBehaviour invokeComponent)
{
// dont use GetComponent(), already have a list - avoids an allocation
invokeComponent = Array.Find(m_NetworkBehaviours,
comp => comp.GetType() == invokeClass || comp.GetType().IsSubclassOf(invokeClass)
);
if (invokeComponent == null)
{
string errorCmdName = NetworkBehaviour.GetCmdHashHandlerName(cmdHash);
Debug.LogError("Found no behaviour for incoming [" + errorCmdName + "] on " + gameObject + ", the server and client should have the same NetworkBehaviour instances [netId=" + netId + "].");
return false;
}
return true;
}
// happens on client
internal void HandleSyncEvent(int cmdHash, NetworkReader reader)
internal void HandleSyncEvent(int componentIndex, int cmdHash, NetworkReader reader)
{
// this doesn't use NetworkBehaviour.InvokeSyncEvent function (anymore). this method of calling is faster.
// The hash is only looked up once, insted of twice(!) per NetworkBehaviour on the object.
@ -577,8 +561,7 @@ internal void HandleSyncEvent(int cmdHash, NetworkReader reader)
// find the matching SyncEvent function and networkBehaviour class
NetworkBehaviour.CmdDelegate invokeFunction;
Type invokeClass;
bool invokeFound = NetworkBehaviour.GetInvokerForHashSyncEvent(cmdHash, out invokeClass, out invokeFunction);
bool invokeFound = NetworkBehaviour.GetInvokerForHashSyncEvent(cmdHash, out invokeFunction);
if (!invokeFound)
{
// We don't get a valid lookup of the command name when it doesn't exist...
@ -588,19 +571,18 @@ internal void HandleSyncEvent(int cmdHash, NetworkReader reader)
}
// find the right component to invoke the function on
NetworkBehaviour invokeComponent;
if (!GetInvokeComponent(cmdHash, invokeClass, out invokeComponent))
if (componentIndex >= m_NetworkBehaviours.Length)
{
string errorCmdName = NetworkBehaviour.GetCmdHashHandlerName(cmdHash);
Debug.LogWarning("SyncEvent [" + errorCmdName + "] handler not found [netId=" + netId + "]");
Debug.LogWarning("Component [" + componentIndex + "] not found for [netId=" + netId + "]");
return;
}
NetworkBehaviour invokeComponent = m_NetworkBehaviours[componentIndex];
invokeFunction(invokeComponent, reader);
}
// happens on server
internal void HandleCommand(int cmdHash, NetworkReader reader)
internal void HandleCommand(int componentIndex, int cmdHash, NetworkReader reader)
{
// this doesn't use NetworkBehaviour.InvokeCommand function (anymore). this method of calling is faster.
// The hash is only looked up once, insted of twice(!) per NetworkBehaviour on the object.
@ -614,8 +596,7 @@ internal void HandleCommand(int cmdHash, NetworkReader reader)
// find the matching Command function and networkBehaviour class
NetworkBehaviour.CmdDelegate invokeFunction;
Type invokeClass;
bool invokeFound = NetworkBehaviour.GetInvokerForHashCommand(cmdHash, out invokeClass, out invokeFunction);
bool invokeFound = NetworkBehaviour.GetInvokerForHashCommand(cmdHash, out invokeFunction);
if (!invokeFound)
{
// We don't get a valid lookup of the command name when it doesn't exist...
@ -625,19 +606,18 @@ internal void HandleCommand(int cmdHash, NetworkReader reader)
}
// find the right component to invoke the function on
NetworkBehaviour invokeComponent;
if (!GetInvokeComponent(cmdHash, invokeClass, out invokeComponent))
if (componentIndex >= m_NetworkBehaviours.Length)
{
string errorCmdName = NetworkBehaviour.GetCmdHashHandlerName(cmdHash);
Debug.LogWarning("Command [" + errorCmdName + "] handler not found [netId=" + netId + "]");
Debug.LogWarning("Component [" + componentIndex + "] not found for [netId=" + netId + "]");
return;
}
NetworkBehaviour invokeComponent = m_NetworkBehaviours[componentIndex];
invokeFunction(invokeComponent, reader);
}
// happens on client
internal void HandleRPC(int cmdHash, NetworkReader reader)
internal void HandleRPC(int componentIndex, int cmdHash, NetworkReader reader)
{
// this doesn't use NetworkBehaviour.InvokeClientRpc function (anymore). this method of calling is faster.
// The hash is only looked up once, insted of twice(!) per NetworkBehaviour on the object.
@ -651,8 +631,7 @@ internal void HandleRPC(int cmdHash, NetworkReader reader)
// find the matching ClientRpc function and networkBehaviour class
NetworkBehaviour.CmdDelegate invokeFunction;
Type invokeClass;
bool invokeFound = NetworkBehaviour.GetInvokerForHashClientRpc(cmdHash, out invokeClass, out invokeFunction);
bool invokeFound = NetworkBehaviour.GetInvokerForHashClientRpc(cmdHash, out invokeFunction);
if (!invokeFound)
{
// We don't get a valid lookup of the command name when it doesn't exist...
@ -662,13 +641,12 @@ internal void HandleRPC(int cmdHash, NetworkReader reader)
}
// find the right component to invoke the function on
NetworkBehaviour invokeComponent;
if (!GetInvokeComponent(cmdHash, invokeClass, out invokeComponent))
if (componentIndex >= m_NetworkBehaviours.Length)
{
string errorCmdName = NetworkBehaviour.GetCmdHashHandlerName(cmdHash);
Debug.LogWarning("ClientRpc [" + errorCmdName + "] handler not found [netId=" + netId + "]");
Debug.LogWarning("Component [" + componentIndex + "] not found for [netId=" + netId + "]");
return;
}
NetworkBehaviour invokeComponent = m_NetworkBehaviours[componentIndex];
invokeFunction(invokeComponent, reader);
}

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
@ -902,7 +902,7 @@ static void OnCommandMessage(NetworkMessage netMsg)
}
if (LogFilter.Debug) { Debug.Log("OnCommandMessage for netId=" + message.netId + " conn=" + netMsg.conn); }
uv.HandleCommand(message.cmdHash, new NetworkReader(message.payload));
uv.HandleCommand(message.componentIndex, message.cmdHash, new NetworkReader(message.payload));
}
internal static void SpawnObject(GameObject obj)