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.