Skip to content

Instantly share code, notes, and snippets.

@cnsoft
Last active December 29, 2015 01:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cnsoft/7594445 to your computer and use it in GitHub Desktop.
Save cnsoft/7594445 to your computer and use it in GitHub Desktop.
Pun是如何处理 RPC的 通过 RaiseEvent 消息. 筛选接收者.(按id group ) NetworkPeer.onSerializedRead() > // Use incoming data according to observed type if (view.observed is MonoBehaviour) { object[] contents = data[(byte)1] as object[]; PhotonStream pStream = new PhotonStream(false, contents); PhotonMessageInfo info = new PhotonMessageInfo(sender, networkTime, view…
case PunEvent.RPC:
//ts: each event now contains a single RPC. execute this
this.ExecuteRPC(photonEvent[ParameterCode.Data] as Hashtable, originatingPlayer);
break;
case PunEvent.SendSerialize:
case PunEvent.SendSerializeReliable:
Hashtable serializeData = (Hashtable)photonEvent[ParameterCode.Data];
//Debug.Log(serializeData.ToStringFull());
int remoteUpdateServerTimestamp = (int)serializeData[(byte)0];
short remoteLevelPrefix = -1;
short initialDataIndex = 1;
if (serializeData.ContainsKey((byte)1))
{
remoteLevelPrefix = (short)serializeData[(byte)1];
initialDataIndex = 2;
}
for (short s = initialDataIndex; s < serializeData.Count; s++)
{
this.OnSerializeRead(serializeData[s] as Hashtable, originatingPlayer, remoteUpdateServerTimestamp, remoteLevelPrefix);
}
break;
case PunEvent.Instantiation:
this.DoInstantiate((Hashtable)photonEvent[ParameterCode.Data], originatingPlayer, null);
break;
//
// PHOTONVIEW/RPC related
// 接收到RPC请求后 呼叫本地. PhotonView
/// <summary>
/// Executes a received RPC event
/// </summary>
public void ExecuteRPC(Hashtable rpcData, PhotonPlayer sender)
{
if (rpcData == null || !rpcData.ContainsKey((byte)0))
{
this.DebugReturn(DebugLevel.ERROR, "Malformed RPC; this should never occur.");
return;
}
// ts: updated with "flat" event data
int netViewID = (int)rpcData[(byte)0]; // LIMITS PHOTONVIEWS&PLAYERS
int otherSidePrefix = 0; // by default, the prefix is 0 (and this is not being sent)
if (rpcData.ContainsKey((byte)1))
{
otherSidePrefix = (short)rpcData[(byte)1];
}
string inMethodName;
if (rpcData.ContainsKey((byte)5))
{
int rpcIndex = (byte)rpcData[(byte)5]; // LIMITS RPC COUNT
if (rpcIndex > PhotonNetwork.PhotonServerSettings.RpcList.Count - 1)
{
Debug.LogError("Could not find RPC with index: " + rpcIndex + ". Going to ignore! Check PhotonServerSettings.RpcList");
return;
}
else
{
inMethodName = PhotonNetwork.PhotonServerSettings.RpcList[rpcIndex];
}
}
else
{
inMethodName = (string)rpcData[(byte)3];
}
object[] inMethodParameters = null;
if (rpcData.ContainsKey((byte)4))
{
inMethodParameters = (object[])rpcData[(byte)4];
}
if (inMethodParameters == null)
{
inMethodParameters = new object[0];
}
PhotonView photonNetview = this.GetPhotonView(netViewID);
if (photonNetview == null)
{
int viewOwnerId = netViewID/PhotonNetwork.MAX_VIEW_IDS;
bool owningPv = (viewOwnerId == this.mLocalActor.ID);
bool ownerSent = (viewOwnerId == sender.ID);
if (owningPv)
{
Debug.LogWarning("Received RPC \"" + inMethodName + "\" for viewID " + netViewID + " but this PhotonView does not exist! View was/is ours." + (ownerSent ? " Owner called." : " Remote called."));
}
else
{
Debug.LogError("Received RPC \"" + inMethodName + "\" for viewID " + netViewID + " but this PhotonView does not exist! Was remote PV." + (ownerSent ? " Owner called." : " Remote called."));
}
return;
}
if (photonNetview.prefix != otherSidePrefix)
{
Debug.LogError(
"Received RPC \"" + inMethodName + "\" on viewID " + netViewID + " with a prefix of " + otherSidePrefix
+ ", our prefix is " + photonNetview.prefix + ". The RPC has been ignored.");
return;
}
// Get method name
if (inMethodName == string.Empty)
{
this.DebugReturn(DebugLevel.ERROR, "Malformed RPC; this should never occur.");
return;
}
if (this.DebugOut >= DebugLevel.ALL)
{
this.DebugReturn(DebugLevel.ALL, "Received RPC; " + inMethodName);
}
// SetReceiving filtering
if (photonNetview.group != 0 && !allowedReceivingGroups.Contains(photonNetview.group))
{
return; // Ignore group
}
Type[] argTypes = new Type[0];
if (inMethodParameters.Length > 0)
{
argTypes = new Type[inMethodParameters.Length];
int i = 0;
for (int index = 0; index < inMethodParameters.Length; index++)
{
object objX = inMethodParameters[index];
if (objX == null)
{
argTypes[i] = null;
}
else
{
argTypes[i] = objX.GetType();
}
i++;
}
}
int receivers = 0;
int foundMethods = 0;
MonoBehaviour[] mbComponents = photonNetview.GetComponents<MonoBehaviour>(); // NOTE: we could possibly also cache MonoBehaviours per view?!
for (int componentsIndex = 0; componentsIndex < mbComponents.Length; componentsIndex++)
{
MonoBehaviour monob = mbComponents[componentsIndex];
if (monob == null)
{
Debug.LogError("ERROR You have missing MonoBehaviours on your gameobjects!");
continue;
}
Type type = monob.GetType();
// Get [RPC] methods from cache
List<MethodInfo> cachedRPCMethods = null;
if (this.monoRPCMethodsCache.ContainsKey(type))
{
cachedRPCMethods = this.monoRPCMethodsCache[type];
}
if (cachedRPCMethods == null)
{
List<MethodInfo> entries = SupportClass.GetMethods(type, typeof(UnityEngine.RPC));
this.monoRPCMethodsCache[type] = entries;
cachedRPCMethods = entries;
}
if (cachedRPCMethods == null)
{
continue;
}
// Check cache for valid methodname+arguments
for (int index = 0; index < cachedRPCMethods.Count; index++)
{
MethodInfo mInfo = cachedRPCMethods[index];
if (mInfo.Name == inMethodName)
{
foundMethods++;
ParameterInfo[] pArray = mInfo.GetParameters();
if (pArray.Length == argTypes.Length)
{
// Normal, PhotonNetworkMessage left out
if (this.CheckTypeMatch(pArray, argTypes))
{
receivers++;
object result = mInfo.Invoke((object)monob, inMethodParameters);
if (mInfo.ReturnType == typeof(IEnumerator))
{
monob.StartCoroutine((IEnumerator)result);
}
}
}
else if ((pArray.Length - 1) == argTypes.Length)
{
// Check for PhotonNetworkMessage being the last
if (this.CheckTypeMatch(pArray, argTypes))
{
if (pArray[pArray.Length - 1].ParameterType == typeof(PhotonMessageInfo))
{
receivers++;
int sendTime = (int)rpcData[(byte)2];
object[] deParamsWithInfo = new object[inMethodParameters.Length + 1];
inMethodParameters.CopyTo(deParamsWithInfo, 0);
deParamsWithInfo[deParamsWithInfo.Length - 1] = new PhotonMessageInfo(sender, sendTime, photonNetview);
object result = mInfo.Invoke((object)monob, deParamsWithInfo);
if (mInfo.ReturnType == typeof(IEnumerator))
{
monob.StartCoroutine((IEnumerator)result);
}
}
}
}
else if (pArray.Length == 1 && pArray[0].ParameterType.IsArray)
{
receivers++;
object result = mInfo.Invoke((object)monob, new object[] { inMethodParameters });
if (mInfo.ReturnType == typeof(IEnumerator))
{
monob.StartCoroutine((IEnumerator)result);
}
}
}
}
}
// Error handling
if (receivers != 1)
{
string argsString = string.Empty;
for (int index = 0; index < argTypes.Length; index++)
{
Type ty = argTypes[index];
if (argsString != string.Empty)
{
argsString += ", ";
}
if (ty == null)
{
argsString += "null";
}
else
{
argsString += ty.Name;
}
}
if (receivers == 0)
{
if (foundMethods == 0)
{
this.DebugReturn(
DebugLevel.ERROR,
"PhotonView with ID " + netViewID + " has no method \"" + inMethodName
+ "\" marked with the [RPC](C#) or @RPC(JS) property! Args: " + argsString);
}
else
{
this.DebugReturn(
DebugLevel.ERROR,
"PhotonView with ID " + netViewID + " has no method \"" + inMethodName + "\" that takes "
+ argTypes.Length + " argument(s): " + argsString);
}
}
else
{
this.DebugReturn(
DebugLevel.ERROR,
"PhotonView with ID " + netViewID + " has " + receivers + " methods \"" + inMethodName
+ "\" that takes " + argTypes.Length + " argument(s): " + argsString + ". Should be just one?");
}
}
}
//
//PhotonSteam PhotonMessageInfo Define
/// <summary>
/// Container class for info about a particular message, RPC or update.
/// </summary>
/// \ingroup publicApi
public class PhotonMessageInfo
{
private int timeInt;
public PhotonPlayer sender;
public PhotonView photonView;
/// <summary>
/// Initializes a new instance of the <see cref="PhotonMessageInfo"/> class.
/// To create an empty messageinfo only!
/// </summary>
public PhotonMessageInfo()
{
this.sender = PhotonNetwork.player;
this.timeInt = (int)(PhotonNetwork.time * 1000);
this.photonView = null;
}
public PhotonMessageInfo(PhotonPlayer player, int timestamp, PhotonView view)
{
this.sender = player;
this.timeInt = timestamp;
this.photonView = view;
}
public double timestamp
{
get { return ((double)(uint)this.timeInt) / 1000.0f; }
}
public override string ToString()
{
return string.Format("[PhotonMessageInfo: player='{1}' timestamp={0}]", this.timestamp, this.sender);
}
}
public class PBitStream
{
List<byte> streamBytes;
private int currentByte;
private int totalBits = 0;
public int ByteCount
{
get { return BytesForBits(this.totalBits); }
}
public int BitCount
{
get { return this.totalBits; }
private set { this.totalBits = value; }
}
public PBitStream()
{
this.streamBytes = new List<byte>(1);
}
public PBitStream(int bitCount)
{
this.streamBytes = new List<byte>(BytesForBits(bitCount));
}
public PBitStream(IEnumerable<byte> bytes, int bitCount)
{
this.streamBytes = new List<byte>(bytes);
this.BitCount = bitCount;
}
public static int BytesForBits(int bitCount)
{
if (bitCount <= 0)
{
return 0;
}
return ((bitCount - 1) / 8) + 1;
}
public void Add(bool val)
{
int bytePos = this.totalBits / 8;
if (bytePos > this.streamBytes.Count-1 || totalBits == 0)
{
this.streamBytes.Add(0);
}
if (val)
{
int currentByteBit = 7 - (this.totalBits % 8);
this.streamBytes[bytePos] |= (byte)(1 << currentByteBit);
}
this.totalBits++;
}
public byte[] ToBytes()
{
return streamBytes.ToArray();
}
public int Position { get; set; }
public bool GetNext()
{
if (this.Position > this.totalBits)
{
throw new Exception("End of PBitStream reached. Can't read more.");
}
return Get(this.Position++);
}
public bool Get(int bitIndex)
{
int byteIndex = bitIndex / 8;
int bitInByIndex = 7 - (bitIndex % 8);
return ((streamBytes[byteIndex] & (byte)(1 << bitInByIndex)) > 0);
}
public void Set(int bitIndex, bool value)
{
int byteIndex = bitIndex / 8;
int bitInByIndex = 7 - (bitIndex % 8);
this.streamBytes[byteIndex] |= (byte)(1 << bitInByIndex);
}
}
/// <summary>
/// This "container" class is used to carry your data as written by OnPhotonSerializeView.
/// </summary>
/// <seealso cref="PhotonNetworkingMessage"/>
/// \ingroup publicApi
public class PhotonStream
{
bool write = false;
internal List<object> data;
byte currentItem = 0; //Used to track the next item to receive.
public PhotonStream(bool write, object[] incomingData)
{
this.write = write;
if (incomingData == null)
{
this.data = new List<object>();
}
else
{
this.data = new List<object>(incomingData);
}
}
public bool isWriting
{
get { return this.write; }
}
public bool isReading
{
get { return !this.write; }
}
public int Count
{
get
{
return data.Count;
}
}
public object ReceiveNext()
{
if (this.write)
{
Debug.LogError("Error: you cannot read this stream that you are writing!");
return null;
}
object obj = this.data[this.currentItem];
this.currentItem++;
return obj;
}
public void SendNext(object obj)
{
if (!this.write)
{
Debug.LogError("Error: you cannot write/send to this stream that you are reading!");
return;
}
this.data.Add(obj);
}
public object[] ToArray()
{
return this.data.ToArray();
}
public void Serialize(ref bool myBool)
{
if (this.write)
{
this.data.Add(myBool);
}
else
{
if (this.data.Count > currentItem)
{
myBool = (bool)data[currentItem];
this.currentItem++;
}
}
}
public void Serialize(ref int myInt)
{
if (write)
{
this.data.Add(myInt);
}
else
{
if (this.data.Count > currentItem)
{
myInt = (int)data[currentItem];
currentItem++;
}
}
}
public void Serialize(ref string value)
{
if (write)
{
this.data.Add(value);
}
else
{
if (this.data.Count > currentItem)
{
value = (string)data[currentItem];
currentItem++;
}
}
}
public void Serialize(ref char value)
{
if (write)
{
this.data.Add(value);
}
else
{
if (this.data.Count > currentItem)
{
value = (char)data[currentItem];
currentItem++;
}
}
}
public void Serialize(ref short value)
{
if (write)
{
this.data.Add(value);
}
else
{
if (this.data.Count > currentItem)
{
value = (short)data[currentItem];
currentItem++;
}
}
}
public void Serialize(ref float obj)
{
if (write)
{
this.data.Add(obj);
}
else
{
if (this.data.Count > currentItem)
{
obj = (float)data[currentItem];
currentItem++;
}
}
}
public void Serialize(ref PhotonPlayer obj)
{
if (write)
{
this.data.Add(obj);
}
else
{
if (this.data.Count > currentItem)
{
obj = (PhotonPlayer)data[currentItem];
currentItem++;
}
}
}
public void Serialize(ref Vector3 obj)
{
if (write)
{
this.data.Add(obj);
}
else
{
if (this.data.Count > currentItem)
{
obj = (Vector3)data[currentItem];
currentItem++;
}
}
}
public void Serialize(ref Vector2 obj)
{
if (write)
{
this.data.Add(obj);
}
else
{
if (this.data.Count > currentItem)
{
obj = (Vector2)data[currentItem];
currentItem++;
}
}
}
public void Serialize(ref Quaternion obj)
{
if (write)
{
this.data.Add(obj);
}
else
{
if (this.data.Count > currentItem)
{
obj = (Quaternion)data[currentItem];
currentItem++;
}
}
}
//
@cnsoft
Copy link
Author

cnsoft commented Nov 22, 2013

// 把标记成RPC类型的函数都存起来.然后发送索引.互相调用.
// 有可能破坏不同版本client间的通讯. 索引不同就有问题.

public static void UpdateRpcList()
{
HashSet additionalRpcs = new HashSet();
HashSet currentRpcs = new HashSet();

    var types = GetAllSubTypesInScripts(typeof(MonoBehaviour));

    foreach (var mono in types)
    {
        MethodInfo[] methods = mono.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        foreach (MethodInfo method in methods)
        {
            if (method.IsDefined(typeof(UnityEngine.RPC), false))
            {
                currentRpcs.Add(method.Name);

                if (!PhotonEditor.Current.RpcList.Contains(method.Name))
                {
                    additionalRpcs.Add(method.Name);
                }
            }
        }
    }

    if (additionalRpcs.Count > 0)
    {
        // LIMITS RPC COUNT
        if (additionalRpcs.Count + PhotonEditor.Current.RpcList.Count >= byte.MaxValue)
        {
            if (currentRpcs.Count <= byte.MaxValue)
            {
                bool clearList = EditorUtility.DisplayDialog(CurrentLang.IncorrectRPCListTitle, CurrentLang.IncorrectRPCListLabel, CurrentLang.RemoveOutdatedRPCsLabel, CurrentLang.CancelButton);
                if (clearList)
                {
                    PhotonEditor.Current.RpcList.Clear();
                    PhotonEditor.Current.RpcList.AddRange(currentRpcs);
                }
                else
                {
                    return;
                }
            }
            else
            {
                EditorUtility.DisplayDialog(CurrentLang.FullRPCListTitle, CurrentLang.FullRPCListLabel, CurrentLang.SkipRPCListUpdateLabel);
                return;
            }
        }

        PhotonEditor.Current.RpcList.AddRange(additionalRpcs);
        EditorUtility.SetDirty(PhotonEditor.Current);
    }
}

//
public static System.Type[] GetAllSubTypesInScripts(System.Type aBaseClass)
{
var result = new System.Collections.Generic.List<System.Type>();
System.Reflection.Assembly[] AS = System.AppDomain.CurrentDomain.GetAssemblies();
foreach (var A in AS)
{
// this skips all but the Unity-scripted assemblies for RPC-list creation. You could remove this to search all assemblies in project
if (!A.FullName.StartsWith("Assembly-"))
{
// Debug.Log("Skipping Assembly: " + A);
continue;
}

        //Debug.Log("Assembly: " + A.FullName);
        System.Type[] types = A.GetTypes();
        foreach (var T in types)
        {
            if (T.IsSubclassOf(aBaseClass))
                result.Add(T);
        }
    }
    return result.ToArray();
}

//

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment