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; NetworkIdentity uv;
if (s_NetworkScene.GetNetworkIdentity(message.netId, out 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 else
{ {
@ -573,7 +573,7 @@ static void OnSyncEventMessage(NetworkMessage netMsg)
NetworkIdentity uv; NetworkIdentity uv;
if (s_NetworkScene.GetNetworkIdentity(message.netId, out 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 else
{ {

View File

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

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using UnityEngine; 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 // this gets called in the constructor by the weaver
// for every SyncObject in the component (e.g. SyncLists). // for every SyncObject in the component (e.g. SyncLists).
// We collect all of them and we synchronize them with OnSerialize/OnDeserialize // 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 // construct the message
CommandMessage message = new CommandMessage(); CommandMessage message = new CommandMessage();
message.netId = netId; message.netId = netId;
message.componentIndex = ComponentIndex;
message.cmdHash = cmdHash; message.cmdHash = cmdHash;
message.payload = writer.ToArray(); message.payload = writer.ToArray();
@ -104,6 +120,7 @@ protected void SendRPCInternal(int rpcHash, NetworkWriter writer, string rpcName
// construct the message // construct the message
RpcMessage message = new RpcMessage(); RpcMessage message = new RpcMessage();
message.netId = netId; message.netId = netId;
message.componentIndex = ComponentIndex;
message.rpcHash = rpcHash; message.rpcHash = rpcHash;
message.payload = writer.ToArray(); message.payload = writer.ToArray();
@ -123,6 +140,7 @@ protected void SendTargetRPCInternal(NetworkConnection conn, int rpcHash, Networ
// construct the message // construct the message
RpcMessage message = new RpcMessage(); RpcMessage message = new RpcMessage();
message.netId = netId; message.netId = netId;
message.componentIndex = ComponentIndex;
message.rpcHash = rpcHash; message.rpcHash = rpcHash;
message.payload = writer.ToArray(); message.payload = writer.ToArray();
@ -149,6 +167,7 @@ protected void SendEventInternal(int eventHash, NetworkWriter writer, string eve
// construct the message // construct the message
SyncEventMessage message = new SyncEventMessage(); SyncEventMessage message = new SyncEventMessage();
message.netId = netId; message.netId = netId;
message.componentIndex = ComponentIndex;
message.eventHash = eventHash; message.eventHash = eventHash;
message.payload = writer.ToArray(); message.payload = writer.ToArray();
@ -244,28 +263,27 @@ internal static string GetInvoker(int cmdHash)
} }
// wrapper fucntions for each type of network operation // 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; Invoker invoker = null;
if (!s_CmdHandlerDelegates.TryGetValue(cmdHash, out invoker)) if (!s_CmdHandlerDelegates.TryGetValue(cmdHash, out invoker))
{ {
if (LogFilter.Debug) { Debug.Log("GetInvokerForHash hash:" + cmdHash + " not found"); } if (LogFilter.Debug) { Debug.Log("GetInvokerForHash hash:" + cmdHash + " not found"); }
invokeClass = null;
invokeFunction = null; invokeFunction = null;
return false; return false;
} }
@ -273,7 +291,6 @@ static bool GetInvokerForHash(int cmdHash, UNetInvokeType invokeType, out Type i
if (invoker == null) if (invoker == null)
{ {
if (LogFilter.Debug) { Debug.Log("GetInvokerForHash hash:" + cmdHash + " invoker null"); } if (LogFilter.Debug) { Debug.Log("GetInvokerForHash hash:" + cmdHash + " invoker null"); }
invokeClass = null;
invokeFunction = null; invokeFunction = null;
return false; return false;
} }
@ -281,12 +298,10 @@ static bool GetInvokerForHash(int cmdHash, UNetInvokeType invokeType, out Type i
if (invoker.invokeType != invokeType) if (invoker.invokeType != invokeType)
{ {
Debug.LogError("GetInvokerForHash hash:" + cmdHash + " mismatched invokeType"); Debug.LogError("GetInvokerForHash hash:" + cmdHash + " mismatched invokeType");
invokeClass = null;
invokeFunction = null; invokeFunction = null;
return false; return false;
} }
invokeClass = invoker.invokeClass;
invokeFunction = invoker.invokeFunction; invokeFunction = invoker.invokeFunction;
return true; return true;
} }

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
@ -546,24 +546,8 @@ internal void HandleClientAuthority(bool authority)
ForceAuthority(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 // 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. // 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. // 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 // find the matching SyncEvent function and networkBehaviour class
NetworkBehaviour.CmdDelegate invokeFunction; NetworkBehaviour.CmdDelegate invokeFunction;
Type invokeClass; bool invokeFound = NetworkBehaviour.GetInvokerForHashSyncEvent(cmdHash, out invokeFunction);
bool invokeFound = NetworkBehaviour.GetInvokerForHashSyncEvent(cmdHash, out invokeClass, out invokeFunction);
if (!invokeFound) if (!invokeFound)
{ {
// We don't get a valid lookup of the command name when it doesn't exist... // 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 // find the right component to invoke the function on
NetworkBehaviour invokeComponent; if (componentIndex >= m_NetworkBehaviours.Length)
if (!GetInvokeComponent(cmdHash, invokeClass, out invokeComponent))
{ {
string errorCmdName = NetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogWarning("Component [" + componentIndex + "] not found for [netId=" + netId + "]");
Debug.LogWarning("SyncEvent [" + errorCmdName + "] handler not found [netId=" + netId + "]");
return; return;
} }
NetworkBehaviour invokeComponent = m_NetworkBehaviours[componentIndex];
invokeFunction(invokeComponent, reader); invokeFunction(invokeComponent, reader);
} }
// happens on server // 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. // 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. // 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 // find the matching Command function and networkBehaviour class
NetworkBehaviour.CmdDelegate invokeFunction; NetworkBehaviour.CmdDelegate invokeFunction;
Type invokeClass; bool invokeFound = NetworkBehaviour.GetInvokerForHashCommand(cmdHash, out invokeFunction);
bool invokeFound = NetworkBehaviour.GetInvokerForHashCommand(cmdHash, out invokeClass, out invokeFunction);
if (!invokeFound) if (!invokeFound)
{ {
// We don't get a valid lookup of the command name when it doesn't exist... // 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 // find the right component to invoke the function on
NetworkBehaviour invokeComponent; if (componentIndex >= m_NetworkBehaviours.Length)
if (!GetInvokeComponent(cmdHash, invokeClass, out invokeComponent))
{ {
string errorCmdName = NetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogWarning("Component [" + componentIndex + "] not found for [netId=" + netId + "]");
Debug.LogWarning("Command [" + errorCmdName + "] handler not found [netId=" + netId + "]");
return; return;
} }
NetworkBehaviour invokeComponent = m_NetworkBehaviours[componentIndex];
invokeFunction(invokeComponent, reader); invokeFunction(invokeComponent, reader);
} }
// happens on client // 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. // 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. // 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 // find the matching ClientRpc function and networkBehaviour class
NetworkBehaviour.CmdDelegate invokeFunction; NetworkBehaviour.CmdDelegate invokeFunction;
Type invokeClass; bool invokeFound = NetworkBehaviour.GetInvokerForHashClientRpc(cmdHash, out invokeFunction);
bool invokeFound = NetworkBehaviour.GetInvokerForHashClientRpc(cmdHash, out invokeClass, out invokeFunction);
if (!invokeFound) if (!invokeFound)
{ {
// We don't get a valid lookup of the command name when it doesn't exist... // 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 // find the right component to invoke the function on
NetworkBehaviour invokeComponent; if (componentIndex >= m_NetworkBehaviours.Length)
if (!GetInvokeComponent(cmdHash, invokeClass, out invokeComponent))
{ {
string errorCmdName = NetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogWarning("Component [" + componentIndex + "] not found for [netId=" + netId + "]");
Debug.LogWarning("ClientRpc [" + errorCmdName + "] handler not found [netId=" + netId + "]");
return; return;
} }
NetworkBehaviour invokeComponent = m_NetworkBehaviours[componentIndex];
invokeFunction(invokeComponent, reader); invokeFunction(invokeComponent, reader);
} }

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; 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); } 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) internal static void SpawnObject(GameObject obj)