Maximum RPC functions?

So, recently I think I’ve hit the cap for the number of RPC functions you can have… if that’s possible.

I started to get the following error:
Internal RPC error. Invalid RPC index, function not registered.

Then, when I comment out a few of the functions, it works fine again.

My question is, has anyone else encountered this maximum RPC issue?
And, if so, is there an easy fix - or am I just going to have to write a wrapper RPC function?

Check this, might solve it.

Yeah, saw that. Unfortunately, it’s not that simple.

The RPC works with less total functions, which means it’s not a naming error, or anything.

[EDIT] Hacky solution

Okay, I finally got around to making a wrapper class for RPC calls. It seems to work for me.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Runtime.InteropServices;

public class NetCode : MonoBehaviour
{
	const int MAX_PACKET_LENGTH = 1024;

	public string terminalScriptName = "";

	string mTerminalRPCName = "";
	NetCode mTerminalScript;

	void init()
	{
		mTerminalScript = (NetCode)GetComponent(terminalScriptName);

		mTerminalRPCName = terminalScriptName + "RPC";
	}

	protected void doRPC(string name, NetworkPlayer player, params object[] args)
	{
		if (mTerminalScript == null)
		{
			init();
		}

		networkView.RPC(mTerminalRPCName, player, name, getRPCBytes(name, args));
	}

	protected void doRPC(string name, RPCMode mode, params object[] args)
	{
		if (mTerminalScript == null)
		{
			init();
		}

		networkView.RPC(mTerminalRPCName, mode, name, getRPCBytes(name, args));
	}

	public byte[] getRPCBytes(string RPCname, params object[] objects)
	{
		MethodInfo tempMethod = mTerminalScript.GetType().GetMethod(RPCname, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

		if (tempMethod == null)
		{
			SQDebug.log("RPC function does not exist: '" + RPCname + "' in class " + mTerminalScript.GetType());
			return null;
		}

		ParameterInfo[] tempInfo = tempMethod.GetParameters();

		if (objects.Length != tempInfo.Length)
		{
			SQDebug.log("RPC function '" + RPCname + "' parameter counts don't match. Expected " + tempInfo.Length + " got " + objects.Length);
			return null;
		}

		List<byte[]> tempBytes = new List<byte[]>();

		for (int i = 0; i < tempInfo.Length; i++)
		{
			if (tempInfo_.ParameterType != objects*.GetType())*_

* {*
SQDebug.log(“RPC function '” + RPCname + "’ parameter " + (i + 1) + " is the wrong type. Expected " + tempInfo_.ParameterType + " got " + objects*.GetType());
return null;
}*_

tempBytes.Add(ToByteArray(objects*, MAX_PACKET_LENGTH));
_ }*_

* int totalSize = 0;*

* for (int i = 0; i < tempBytes.Count; i++)*
* {*
_ totalSize += tempBytes*.Length;
}*_

* if (totalSize > 0)*
* {*
* byte[] finalBytes = new byte[totalSize];*
* int currentOffset = 0;*

* for (int i = 0; i < tempBytes.Count; i++)*
* {*
System.Array.Copy(tempBytes_, 0, finalBytes, currentOffset, tempBytes*.Length);*_

_ currentOffset += tempBytes*.Length;
}*_

* return finalBytes;*
* }*
* else*
* {*
* byte[] finalBytes = new byte[1];*
* finalBytes[0] = 0;*

* return finalBytes;*
* }*
* }*

* protected void dynamicRPC(string name, byte[] data)*
* {*
* int size;*
* object tempObject = null;*
* int currentOffset = 0;*

* if (!FromByteArray(typeof(string), data, 0, out size, ref tempObject))*
* {*
* SQDebug.log(“Malformed RPC call”);*
* return;*
* }*

* string RPCname = name;*
* MethodInfo tempMethod = this.GetType().GetMethod(RPCname, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);*

* if (tempMethod == null)*
* {*
* SQDebug.log(“RPC function does not exist: '” + name + "’ in class " + GetType());*
* return;*
* }*

* ParameterInfo[] tempInfo = tempMethod.GetParameters();*

* object[] parameters = new object[tempInfo.Length];*

* for (int i = 0; i < tempInfo.Length; i++)*
* {*
_ if (FromByteArray(tempInfo*.ParameterType, data, currentOffset, out size, ref tempObject))
{
parameters = tempObject;
}
else*

* {
SQDebug.log(“RPC function '” + name + "’ was unable to convert parameter: " + (i + 1));
return;
}*_

* currentOffset += size;*
* }*

* tempMethod.Invoke(this, parameters);*
* }*

* protected bool FromByteArray(Type type, byte[] rawValue, int index, out int size, ref object data)*
* {*
* //Pack length info in the first 4 bytes*
* if (type == typeof(byte[]))*
* {*
* size = BitConverter.ToInt32(rawValue, index);*

* byte[] tempData = new byte;*

* Array.Copy(rawValue, index + 4, tempData, 0, size);*

* size += 4;*

* data = (object)tempData;*

* return true;*
* }*
* //Strings need to be handled seperately*
* else if (type == typeof(string))*
* {*
* for (int i = index; i < rawValue.Length; i++)*
* {*
_ if (rawValue == 0)
* {
//Found end of string*_

* size = i - index + 1;*

* data = (object)Encoding.ASCII.GetString(rawValue, index, i - index);*

* return true;*
* }*
* }*

* size = 0;*

* return false;*
* }*
* else*
* {*
* size = Marshal.SizeOf(type);*

* if (rawValue.Length >= index + size)*
* {*
* byte[] tempBytes = new byte;*
* Array.Copy(rawValue, index, tempBytes, 0, size);*

* GCHandle handle = GCHandle.Alloc(tempBytes, GCHandleType.Pinned);*
* data = (object)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), type);*
* handle.Free();*

* return true;*
* }*
* else*
* {*
* return false;*
* }*
* }*
* }*

* protected byte[] ToByteArray(object value, int maxLength)*
* {*
* //Pack length info in the first 4 bytes*
* if (value.GetType() == typeof(byte[]))*
* {*
* int tempLength = ((byte[])value).Length;*
* byte[] tempByteArray = new byte[tempLength + 4];*

* byte[] lengthBytes = BitConverter.GetBytes((Int32)tempLength);*

* Array.Copy(lengthBytes, 0, tempByteArray, 0, 4);*
* Array.Copy((byte[])value, 0, tempByteArray, 4, tempLength);*

* return tempByteArray;*
* }*
* //Strings need to be handled seperately*
* else if (value.GetType() == typeof(string))*
* {*
* byte[] rawdata = Encoding.ASCII.GetBytes((string)value + “\0”);*

* return rawdata;*
* }*
* else*
* {*
* int rawsize = Marshal.SizeOf(value);*
* byte[] rawdata = new byte[rawsize];*
* GCHandle handle =*
* GCHandle.Alloc(rawdata,*
* GCHandleType.Pinned);*
* Marshal.StructureToPtr(value,*
* handle.AddrOfPinnedObject(),*
* false);*
* handle.Free();*
* if (maxLength < rawdata.Length)*
* {*
* byte[] temp = new byte[maxLength];*
* Array.Copy(rawdata, temp, maxLength);*
* return temp;*
* }*
* else*
* {*
* return rawdata;*
* }*
* }*
* }*
}
Usage:
Inherit the script, and add a single RPC function to the new script:
RPC, like so:
[RPC] void ServerRPC(string name, byte[] data)
* {*
* dynamicRPC(name, data);*
* }*
You can then use doRPC to call RPC functions from the inherited script like you would normally. (NOTE: you do NOT need the RPC tag on functions).
For the settings, you should add the inherited versions of the script to the same Game Object (with a network view, of course). Then, you need to tell the code which script it should access via the “terminalScriptName” variable.
For example, if I have two scripts, Server.cs and Client.cs which have both inherited from the above script, then I would have Server.cs to point to “Client” and Client.cs to point to “Server”.
It’s not the most simple implementation, I know… but… meh!
TODO: Add the method calls to a dictionary to reduce bandwidth.