diff --git a/MLAPI/Attributes/ClientRpc.cs b/MLAPI/Attributes/ClientRpc.cs new file mode 100644 index 0000000..3a01756 --- /dev/null +++ b/MLAPI/Attributes/ClientRpc.cs @@ -0,0 +1,10 @@ +using System; + +namespace MLAPI.Attributes +{ + [AttributeUsage(AttributeTargets.Method)] + public class ClientRpc : Attribute + { + + } +} diff --git a/MLAPI/Attributes/Command.cs b/MLAPI/Attributes/Command.cs new file mode 100644 index 0000000..5eb7f60 --- /dev/null +++ b/MLAPI/Attributes/Command.cs @@ -0,0 +1,10 @@ +using System; + +namespace MLAPI.Attributes +{ + [AttributeUsage(AttributeTargets.Method)] + public class Command : Attribute + { + + } +} diff --git a/MLAPI/Attributes/TargetRpc.cs b/MLAPI/Attributes/TargetRpc.cs new file mode 100644 index 0000000..da78777 --- /dev/null +++ b/MLAPI/Attributes/TargetRpc.cs @@ -0,0 +1,10 @@ +using System; + +namespace MLAPI.Attributes +{ + [AttributeUsage(AttributeTargets.Method)] + public class TargetRpc : Attribute + { + + } +} diff --git a/MLAPI/Data/Cache.cs b/MLAPI/Data/Cache.cs new file mode 100644 index 0000000..8b2d74d --- /dev/null +++ b/MLAPI/Data/Cache.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Text; + +namespace MLAPI.Data +{ + internal static class Cache + { + internal static Dictionary messageAttributeHashes = new Dictionary(); + internal static Dictionary messageAttributeNames = new Dictionary(); + + internal static ulong GetMessageAttributeHash(string name) + { + if (messageAttributeHashes.ContainsKey(name)) + return messageAttributeHashes[name]; + + using (SHA256Managed sha = new SHA256Managed()) + { + byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(name)); + ulong value = hash[0] | ((ulong)hash[1] << 8) | ((ulong)hash[2] << 16) | ((ulong)hash[3] << 24) | ((ulong)hash[4] << 32) | ((ulong)hash[5] << 40) | ((ulong)hash[6] << 48) | ((ulong)hash[7] << 56); + //ulong value = hash[0] | ((uint)hash[1] << 8) | ((uint)hash[2] << 16) | ((uint)hash[3] << 24); + messageAttributeHashes.Add(name, value); + messageAttributeNames.Add(value, name); + return value; + } + } + + internal static string GetAttributeMethodName(ulong hash) + { + if (messageAttributeNames.ContainsKey(hash)) + return messageAttributeNames[hash]; + else + return string.Empty; + } + + internal static void RegisterMessageAttributeName(string name) + { + if (messageAttributeHashes.ContainsKey(name)) + return; + + using (SHA256Managed sha = new SHA256Managed()) + { + byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(name)); + ulong value = hash[0] | ((ulong)hash[1] << 8) | ((ulong)hash[2] << 16) | ((ulong)hash[3] << 24) | ((ulong)hash[4] << 32) | ((ulong)hash[5] << 40) | ((ulong)hash[6] << 48) | ((ulong)hash[7] << 56); + //ulong value = hash[0] | ((uint)hash[1] << 8) | ((uint)hash[2] << 16) | ((uint)hash[3] << 24); + messageAttributeHashes.Add(name, value); + messageAttributeNames.Add(value, name); + } + } + } +} diff --git a/MLAPI/Data/FieldType.cs b/MLAPI/Data/FieldType.cs index abd8c31..3c761e3 100644 --- a/MLAPI/Data/FieldType.cs +++ b/MLAPI/Data/FieldType.cs @@ -45,6 +45,137 @@ namespace MLAPI.Data internal static class FieldTypeHelper { + internal static void WriteFieldType(BitWriter writer, object value, FieldType fieldType) + { + switch (fieldType) + { + case FieldType.Bool: + writer.WriteBool((bool)value); + break; + case FieldType.Byte: + writer.WriteByte((byte)value); + break; + case FieldType.Double: + writer.WriteDouble((double)value); + break; + case FieldType.Single: + writer.WriteFloat((float)value); + break; + case FieldType.Int: + writer.WriteInt((int)value); + break; + case FieldType.Long: + writer.WriteLong((long)value); + break; + case FieldType.SByte: + writer.WriteSByte((sbyte)value); + break; + case FieldType.Short: + writer.WriteShort((short)value); + break; + case FieldType.UInt: + writer.WriteUInt((uint)value); + break; + case FieldType.ULong: + writer.WriteULong((ulong)value); + break; + case FieldType.UShort: + writer.WriteUShort((ushort)value); + break; + case FieldType.String: + writer.WriteString((string)value); + break; + case FieldType.Vector3: + Vector3 vector3 = (Vector3)value; + writer.WriteFloat(vector3.x); + writer.WriteFloat(vector3.y); + writer.WriteFloat(vector3.z); + break; + case FieldType.Vector2: + Vector2 vector2 = (Vector2)value; + writer.WriteFloat(vector2.x); + writer.WriteFloat(vector2.y); + break; + case FieldType.Quaternion: + Vector3 euler = ((Quaternion)value).eulerAngles; + writer.WriteFloat(euler.x); + writer.WriteFloat(euler.y); + writer.WriteFloat(euler.z); + break; + case FieldType.BoolArray: + bool[] bools = (bool[])value; + writer.WriteUShort((ushort)bools.Length); + for (int j = 0; j < bools.Length; j++) + writer.WriteBool(bools[j]); + break; + case FieldType.ByteArray: + writer.WriteByteArray((byte[])value); + break; + case FieldType.DoubleArray: + writer.WriteDoubleArray((double[])value); + break; + case FieldType.SingleArray: + writer.WriteFloatArray((float[])value); + break; + case FieldType.IntArray: + writer.WriteIntArray((int[])value); + break; + case FieldType.LongArray: + writer.WriteLongArray((long[])value); + break; + case FieldType.SByteArray: + writer.WriteSByteArray((sbyte[])value); + break; + case FieldType.ShortArray: + writer.WriteShortArray((short[])value); + break; + case FieldType.UIntArray: + writer.WriteUIntArray((uint[])value); + break; + case FieldType.ULongArray: + writer.WriteULongArray((ulong[])value); + break; + case FieldType.UShortArray: + writer.WriteUShortArray((ushort[])value); + break; + case FieldType.StringArray: + string[] strings = (string[])value; + writer.WriteUShort((ushort)strings.Length); + for (int j = 0; j < strings.Length; j++) + writer.WriteString(strings[j]); + break; + case FieldType.Vector3Array: + Vector3[] vector3s = (Vector3[])value; + writer.WriteUShort((ushort)vector3s.Length); + for (int j = 0; j < vector3s.Length; j++) + { + writer.WriteFloat(vector3s[j].x); + writer.WriteFloat(vector3s[j].y); + writer.WriteFloat(vector3s[j].z); + } + break; + case FieldType.Vector2Array: + Vector2[] vector2s = (Vector2[])value; + writer.WriteUShort((ushort)vector2s.Length); + for (int j = 0; j < vector2s.Length; j++) + { + writer.WriteFloat(vector2s[j].x); + writer.WriteFloat(vector2s[j].y); + } + break; + case FieldType.QuaternionArray: + Quaternion[] quaternions = (Quaternion[])value; + writer.WriteUShort((ushort)quaternions.Length); + for (int j = 0; j < quaternions.Length; j++) + { + writer.WriteFloat(quaternions[j].eulerAngles.x); + writer.WriteFloat(quaternions[j].eulerAngles.y); + writer.WriteFloat(quaternions[j].eulerAngles.z); + } + break; + } + } + internal static void WriteFieldType(BitWriter writer, FieldInfo field, object fieldInstance, FieldType fieldType) { switch (fieldType) @@ -241,5 +372,155 @@ namespace MLAPI.Data else return FieldType.Invalid; } + + internal static object[] ReadObjects(BitReader reader, byte paramCount) + { + object[] returnVal = new object[paramCount]; + for (int i = 0; i < paramCount; i++) + { + FieldType fieldType = (FieldType)reader.ReadBits(5); + + switch (fieldType) + { + case FieldType.Bool: + returnVal[i] = reader.ReadBool(); + break; + case FieldType.Byte: + returnVal[i] = reader.ReadByte(); + break; + case FieldType.Double: + returnVal[i] = reader.ReadDouble(); + break; + case FieldType.Single: + returnVal[i] = reader.ReadFloat(); + break; + case FieldType.Int: + returnVal[i] = reader.ReadInt(); + break; + case FieldType.Long: + returnVal[i] = reader.ReadLong(); + break; + case FieldType.SByte: + returnVal[i] = reader.ReadSByte(); + break; + case FieldType.Short: + returnVal[i] = reader.ReadShort(); + break; + case FieldType.UInt: + returnVal[i] = reader.ReadUInt(); + break; + case FieldType.ULong: + returnVal[i] = reader.ReadULong(); + break; + case FieldType.UShort: + returnVal[i] = reader.ReadUShort(); + break; + case FieldType.String: + returnVal[i] = reader.ReadString(); + break; + case FieldType.Vector3: + Vector3 vector3 = Vector3.zero; + vector3.x = reader.ReadFloat(); + vector3.y = reader.ReadFloat(); + vector3.z = reader.ReadFloat(); + returnVal[i] = vector3; + break; + case FieldType.Vector2: + Vector2 vector2 = Vector2.zero; + vector2.x = reader.ReadFloat(); + vector2.y = reader.ReadFloat(); + returnVal[i] = vector2; + break; + case FieldType.Quaternion: + Vector3 eulerAngle = Vector3.zero; + eulerAngle.x = reader.ReadFloat(); + eulerAngle.y = reader.ReadFloat(); + eulerAngle.z = reader.ReadFloat(); + returnVal[i] = Quaternion.Euler(eulerAngle); + break; + case FieldType.BoolArray: + ushort boolCount = reader.ReadUShort(); + for (int j = 0; j < boolCount; j++) + returnVal[i] = reader.ReadBool(); + break; + case FieldType.ByteArray: + returnVal[i] = reader.ReadByteArray(); + break; + case FieldType.DoubleArray: + returnVal[i] = reader.ReadDoubleArray(); + break; + case FieldType.SingleArray: + returnVal[i] = reader.ReadFloatArray(); + break; + case FieldType.IntArray: + returnVal[i] = reader.ReadIntArray(); + break; + case FieldType.LongArray: + returnVal[i] = reader.ReadLongArray(); + break; + case FieldType.SByteArray: + returnVal[i] = reader.ReadSByteArray(); + break; + case FieldType.ShortArray: + returnVal[i] = reader.ReadShortArray(); + break; + case FieldType.UIntArray: + returnVal[i] = reader.ReadUIntArray(); + break; + case FieldType.ULongArray: + returnVal[i] = reader.ReadULongArray(); + break; + case FieldType.UShortArray: + returnVal[i] = reader.ReadUShortArray(); + break; + case FieldType.StringArray: + ushort stringCount = reader.ReadUShort(); + string[] strings = new string[stringCount]; + for (int j = 0; j < stringCount; j++) + strings[j] = reader.ReadString(); + returnVal[i] = strings; + break; + case FieldType.Vector3Array: + ushort vector3Count = reader.ReadUShort(); + Vector3[] vector3s = new Vector3[vector3Count]; + for (int j = 0; j < vector3Count; j++) + { + Vector3 vec3 = Vector3.zero; + vec3.x = reader.ReadFloat(); + vec3.y = reader.ReadFloat(); + vec3.z = reader.ReadFloat(); + vector3s[j] = vec3; + } + returnVal[i] = vector3s; + break; + case FieldType.Vector2Array: + ushort vector2Count = reader.ReadUShort(); + Vector2[] vector2s = new Vector2[vector2Count]; + for (int j = 0; j < vector2Count; j++) + { + Vector2 vec2 = Vector2.zero; + vec2.x = reader.ReadFloat(); + vec2.y = reader.ReadFloat(); + vector2s[j] = vec2; + } + returnVal[i] = vector2s; + break; + case FieldType.QuaternionArray: + ushort quaternionCount = reader.ReadUShort(); + Quaternion[] quaternions = new Quaternion[quaternionCount]; + for (int j = 0; j < quaternionCount; j++) + { + Vector3 vec3 = Vector3.zero; + vec3.x = reader.ReadFloat(); + vec3.y = reader.ReadFloat(); + vec3.z = reader.ReadFloat(); + quaternions[j] = Quaternion.Euler(vec3); + } + returnVal[i] = quaternions; + break; + } + } + return returnVal; + } } } diff --git a/MLAPI/MLAPI.csproj b/MLAPI/MLAPI.csproj index b429cfa..73dadbd 100644 --- a/MLAPI/MLAPI.csproj +++ b/MLAPI/MLAPI.csproj @@ -67,6 +67,10 @@ + + + + diff --git a/MLAPI/MonoBehaviours/Core/NetworkedBehaviour.cs b/MLAPI/MonoBehaviours/Core/NetworkedBehaviour.cs index 2cb942e..186709d 100644 --- a/MLAPI/MonoBehaviours/Core/NetworkedBehaviour.cs +++ b/MLAPI/MonoBehaviours/Core/NetworkedBehaviour.cs @@ -76,7 +76,7 @@ namespace MLAPI.MonoBehaviours.Core { get { - if(_networkedObject == null) + if (_networkedObject == null) { _networkedObject = GetComponentInParent(); } @@ -111,9 +111,9 @@ namespace MLAPI.MonoBehaviours.Core private void OnEnable() { if (_networkedObject == null) - { _networkedObject = GetComponentInParent(); - } + + CacheAttributedMethods(); NetworkedObject.NetworkedBehaviours.Add(this); } @@ -139,6 +139,121 @@ namespace MLAPI.MonoBehaviours.Core { } + + internal Dictionary cachedMethods = new Dictionary(); + + private void CacheAttributedMethods() + { + MethodInfo[] methods = GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); + for (int i = 0; i < methods.Length; i++) + { + if (methods[i].IsDefined(typeof(Command), true) || methods[i].IsDefined(typeof(ClientRpc), true) || methods[i].IsDefined(typeof(TargetRpc), true)) + { + Data.Cache.RegisterMessageAttributeName(methods[i].Name); + if (!cachedMethods.ContainsKey(methods[i].Name)) + cachedMethods.Add(methods[i].Name, methods[i]); + } + } + } + + protected void InvokeCommand(string methodName, params object[] methodParams) + { + if (NetworkingManager.singleton.isServer) + { + Debug.LogWarning("MLAPI: Cannot invoke commands from server"); + return; + } + if (ownerClientId != NetworkingManager.singleton.MyClientId) + { + Debug.LogWarning("MLAPI: Cannot invoke command for object without ownership"); + return; + } + if (!methodName.StartsWith("Cmd")) + { + Debug.LogWarning("MLAPI: Invalid Command name. Command methods have to start with Cmd"); + return; + } + + ulong hash = Data.Cache.GetMessageAttributeHash(methodName); + using (BitWriter writer = new BitWriter()) + { + writer.WriteUInt(networkId); + writer.WriteUShort(networkedObject.GetOrderIndex(this)); + writer.WriteULong(hash); + writer.WriteBits((byte)methodParams.Length, 5); + for (int i = 0; i < methodParams.Length; i++) + { + FieldType fieldType = FieldTypeHelper.GetFieldType(methodParams[i].GetType()); + writer.WriteBits((byte)fieldType, 5); + FieldTypeHelper.WriteFieldType(writer, methodParams[i], fieldType); + } + + InternalMessageHandler.Send(NetId.ServerNetId.GetClientId(), "MLAPI_COMMAND", "MLAPI_INTERNAL", writer.Finalize()); + } + } + + protected void InvokeClientRpc(string methodName, params object[] methodParams) + { + if (!NetworkingManager.singleton.isServer) + { + Debug.LogWarning("MLAPI: Cannot invoke ClientRpc from client"); + return; + } + if (!methodName.StartsWith("Rpc")) + { + Debug.LogWarning("MLAPI: Invalid Command name. Command methods have to start with Cmd"); + return; + } + + ulong hash = Data.Cache.GetMessageAttributeHash(methodName); + using (BitWriter writer = new BitWriter()) + { + writer.WriteUInt(networkId); + writer.WriteUShort(networkedObject.GetOrderIndex(this)); + writer.WriteULong(hash); + writer.WriteBits((byte)methodParams.Length, 5); + + for (int i = 0; i < methodParams.Length; i++) + { + FieldType fieldType = FieldTypeHelper.GetFieldType(methodParams[i].GetType()); + writer.WriteBits((byte)fieldType, 5); + FieldTypeHelper.WriteFieldType(writer, methodParams[i], fieldType); + } + + InternalMessageHandler.Send("MLAPI_RPC", "MLAPI_INTERNAL", writer.Finalize()); + } + } + + protected void InvokeTargetRpc(string methodName, params object[] methodParams) + { + if (!NetworkingManager.singleton.isServer) + { + Debug.LogWarning("MLAPI: Cannot invoke ClientRpc from client"); + return; + } + if (!methodName.StartsWith("Target")) + { + Debug.LogWarning("MLAPI: Invalid Command name. Command methods have to start with Cmd"); + return; + } + + ulong hash = Data.Cache.GetMessageAttributeHash(methodName); + using (BitWriter writer = new BitWriter()) + { + writer.WriteUInt(networkId); + writer.WriteUShort(networkedObject.GetOrderIndex(this)); + writer.WriteULong(hash); + writer.WriteBits((byte)methodParams.Length, 5); + for (int i = 0; i < methodParams.Length; i++) + { + FieldType fieldType = FieldTypeHelper.GetFieldType(methodParams[i].GetType()); + writer.WriteBits((byte)fieldType, 5); + FieldTypeHelper.WriteFieldType(writer, methodParams[i], fieldType); + } + + InternalMessageHandler.Send(ownerClientId, "MLAPI_RPC", "MLAPI_INTERNAL", writer.Finalize()); + } + } /// /// Registers a message handler /// diff --git a/MLAPI/MonoBehaviours/Core/NetworkingManager.cs b/MLAPI/MonoBehaviours/Core/NetworkingManager.cs index 123a84a..d5c2bc4 100644 --- a/MLAPI/MonoBehaviours/Core/NetworkingManager.cs +++ b/MLAPI/MonoBehaviours/Core/NetworkingManager.cs @@ -219,6 +219,8 @@ namespace MLAPI.MonoBehaviours.Core connectedClients = new Dictionary(); messageBuffer = new byte[NetworkConfig.MessageBufferSize]; diffieHellmanPublicKeys = new Dictionary(); + Data.Cache.messageAttributeHashes = new Dictionary(); + Data.Cache.messageAttributeNames = new Dictionary(); MessageManager.channels = new Dictionary(); MessageManager.messageTypes = new Dictionary(); MessageManager.messageCallbacks = new Dictionary>>(); @@ -378,6 +380,9 @@ namespace MLAPI.MonoBehaviours.Core MessageManager.messageTypes.Add("MLAPI_SYNC_VAR_UPDATE", 9); MessageManager.messageTypes.Add("MLAPI_ADD_OBJECTS", 10); MessageManager.messageTypes.Add("MLAPI_TIME_SYNC", 11); + MessageManager.messageTypes.Add("MLAPI_COMMAND", 12); + MessageManager.messageTypes.Add("MLAPI_RPC", 13); + MessageManager.messageTypes.Add("MLAPI_TARGET", 14); List messageTypes = new List(NetworkConfig.MessageTypes) { @@ -1000,6 +1005,24 @@ namespace MLAPI.MonoBehaviours.Core InternalMessageHandler.HandleTimeSync(clientId, incommingData, channelId); } break; + case 12: + if (isServer) + { + InternalMessageHandler.HandleCommand(clientId, incommingData, channelId); + } + break; + case 13: + if (isClient) + { + InternalMessageHandler.HandleRpc(clientId, incommingData, channelId); + } + break; + case 14: + if (isClient) + { + InternalMessageHandler.HandleTargetRpc(clientId, incommingData, channelId); + } + break; } #endregion } diff --git a/MLAPI/NetworkingManagerComponents/Binary/BitReader.cs b/MLAPI/NetworkingManagerComponents/Binary/BitReader.cs index 02beb75..79529ae 100644 --- a/MLAPI/NetworkingManagerComponents/Binary/BitReader.cs +++ b/MLAPI/NetworkingManagerComponents/Binary/BitReader.cs @@ -54,6 +54,13 @@ namespace MLAPI.NetworkingManagerComponents.Binary public int[] ReadIntArray(int known = -1) => ReadArray(ReadInt, known); public long[] ReadLongArray(int known = -1) => ReadArray(ReadLong, known); public string ReadString() => Encoding.UTF8.GetString(ReadByteArray()); + public byte ReadBits(int bits) + { + byte b = 0; + for (int i = 0; --bits >= 0; ++i) + b |= (byte)((ReadBool() ? 1 : 0) << i); + return b; + } public ulong ReadULong() { diff --git a/MLAPI/NetworkingManagerComponents/Binary/BitWriter.cs b/MLAPI/NetworkingManagerComponents/Binary/BitWriter.cs index a0167c4..9550b4a 100644 --- a/MLAPI/NetworkingManagerComponents/Binary/BitWriter.cs +++ b/MLAPI/NetworkingManagerComponents/Binary/BitWriter.cs @@ -105,6 +105,10 @@ namespace MLAPI.NetworkingManagerComponents.Binary public void WriteShortArray(short[] s, bool known = false) => PushArray(s, known); public void WriteIntArray(int[] i, bool known = false) => PushArray(i, known); public void WriteLongArray(long[] l, bool known = false) => PushArray(l, known); + public void WriteBits(byte value, int bits) + { + for (int i = 0; i < bits; ++i) WriteBool((value & (1 << i)) != 0); + } private void PushArray(T[] t, bool knownSize = false) { @@ -268,7 +272,7 @@ namespace MLAPI.NetworkingManagerComponents.Binary WriteByte(writeTo, (value >> 32) & 255, bitOffset + 40, isAligned); if (value > 1099511627775) { - WriteByte(writeTo, (value >> 40) & 55, bitOffset + 48, isAligned); + WriteByte(writeTo, (value >> 40) & 255, bitOffset + 48, isAligned); if (value > 281474976710655) { WriteByte(writeTo, (value >> 48) & 255, bitOffset + 56, isAligned); diff --git a/MLAPI/NetworkingManagerComponents/Core/InternalMessageHandler.Receive.cs b/MLAPI/NetworkingManagerComponents/Core/InternalMessageHandler.Receive.cs index 24cc620..a823c2e 100644 --- a/MLAPI/NetworkingManagerComponents/Core/InternalMessageHandler.Receive.cs +++ b/MLAPI/NetworkingManagerComponents/Core/InternalMessageHandler.Receive.cs @@ -1,4 +1,5 @@ -using System.Security.Cryptography; +using System.Reflection; +using System.Security.Cryptography; using MLAPI.Data; using MLAPI.MonoBehaviours.Core; using MLAPI.NetworkingManagerComponents.Binary; @@ -384,6 +385,53 @@ namespace MLAPI.NetworkingManagerComponents.Core if ((NetworkError)error != NetworkError.Ok) msDelay = 0; netManager.networkTime = netTime + (msDelay / 1000f); - } + } + + internal static void HandleCommand(uint clientId, byte[] incommingData, int channelId) + { + BitReader reader = new BitReader(incommingData); + uint networkId = reader.ReadUInt(); + ushort orderId = reader.ReadUShort(); + ulong hash = reader.ReadULong(); + NetworkedBehaviour behaviour = SpawnManager.spawnedObjects[networkId].GetBehaviourAtOrderIndex(orderId); + if (clientId != behaviour.ownerClientId) + return; // Not owner + MethodInfo targetMethod = null; + if (behaviour.cachedMethods.ContainsKey(Data.Cache.GetAttributeMethodName(hash))) + targetMethod = behaviour.cachedMethods[Data.Cache.GetAttributeMethodName(hash)]; + byte paramCount = reader.ReadBits(5); + object[] methodParams = FieldTypeHelper.ReadObjects(reader, paramCount); + targetMethod.Invoke(behaviour, methodParams); + } + + internal static void HandleRpc(uint clientId, byte[] incommingData, int channelId) + { + BitReader reader = new BitReader(incommingData); + uint networkId = reader.ReadUInt(); + ushort orderId = reader.ReadUShort(); + ulong hash = reader.ReadULong(); + NetworkedBehaviour behaviour = SpawnManager.spawnedObjects[networkId].GetBehaviourAtOrderIndex(orderId); + MethodInfo targetMethod = null; + if (behaviour.cachedMethods.ContainsKey(Data.Cache.GetAttributeMethodName(hash))) + targetMethod = behaviour.cachedMethods[Data.Cache.GetAttributeMethodName(hash)]; + byte paramCount = reader.ReadBits(5); + object[] methodParams = FieldTypeHelper.ReadObjects(reader, paramCount); + targetMethod.Invoke(behaviour, methodParams); + } + + internal static void HandleTargetRpc(uint clientId, byte[] incommingData, int channelId) + { + BitReader reader = new BitReader(incommingData); + uint networkId = reader.ReadUInt(); + ushort orderId = reader.ReadUShort(); + ulong hash = reader.ReadULong(); + NetworkedBehaviour behaviour = SpawnManager.spawnedObjects[networkId].GetBehaviourAtOrderIndex(orderId); + MethodInfo targetMethod = null; + if (behaviour.cachedMethods.ContainsKey(Data.Cache.GetAttributeMethodName(hash))) + targetMethod = behaviour.cachedMethods[Data.Cache.GetAttributeMethodName(hash)]; + byte paramCount = reader.ReadBits(5); + object[] methodParams = FieldTypeHelper.ReadObjects(reader, paramCount); + targetMethod.Invoke(behaviour, methodParams); + } } }