Photon Unity RPC 调用流程
发表于2017-11-11
一、Photon简介
Photon是一款非常不错的游戏服务端引擎,是一个服务器应用程序,可以在你选择的机器上运行,并且完全是自定义和权威性的控制,你可以自由的配置和部署多人应用的基础设施。
在客户端,Photon支持多样的平台,使用C,C#,Flash进行编程的方式是不同的,但是基本的工作流是相似的。在这文档中我们尝试去解释它的概念和背景,而语言的细节部分需要查阅每个语言平台的参考文档。
下载地址:https://www.photonengine.com/en/OnPremise/Download
解压后的相关文件夹说明:
deploy:主要存放photon的服务器控制程序和服务端Demo
doc:顾名思义,文档
lib:Photon类库,开发服务端需要引用的
src-server:服务端Demo源代码
在网络段,使用了Photon,所以拿来研究了下。看看代码,梳理了一下RPC流程。
把Photon Unity RPC 调用流程记录了下来,以供以后参考和使用。 流程主要分两个部分一个编辑器界面工作流程,一个是网络调用流程。
二、RPC编辑器调用代码
PhotonEditor.cs文件内,直接说代码
#region RPC List Handling public static void UpdateRpcList() { List<string> additionalRpcs = new List<string>(); HashSet<string> currentRpcs = new HashSet<string>(); var types = GetAllSubTypesInScripts(typeof(MonoBehaviour)); int countOldRpcs = 0; foreach (var mono in types) { MethodInfo[] methods = mono.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (MethodInfo method in methods) { bool isOldRpc = false; #pragma warning disable 618 // we let the Editor check for outdated RPC attributes in code. that should not cause a compile warning if (method.IsDefined(typeof (RPC), false)) { countOldRpcs++; isOldRpc = true; } #pragma warning restore 618 if (isOldRpc || method.IsDefined(typeof(PunRPC), false)) { currentRpcs.Add(method.Name); if (!additionalRpcs.Contains(method.Name) && !PhotonNetwork.PhotonServerSettings.RpcList.Contains(method.Name)) { additionalRpcs.Add(method.Name); } } } } if (additionalRpcs.Count > 0) { // LIMITS RPC COUNT if (additionalRpcs.Count + PhotonNetwork.PhotonServerSettings.RpcList.Count >= byte.MaxValue) { if (currentRpcs.Count <= byte.MaxValue) { bool clearList = EditorUtility.DisplayDialog(CurrentLang.IncorrectRPCListTitle, CurrentLang.IncorrectRPCListLabel, CurrentLang.RemoveOutdatedRPCsLabel, CurrentLang.CancelButton); if (clearList) { PhotonNetwork.PhotonServerSettings.RpcList.Clear(); PhotonNetwork.PhotonServerSettings.RpcList.AddRange(currentRpcs); } else { return; } } else { EditorUtility.DisplayDialog(CurrentLang.FullRPCListTitle, CurrentLang.FullRPCListLabel, CurrentLang.SkipRPCListUpdateLabel); return; } } additionalRpcs.Sort(); PhotonNetwork.PhotonServerSettings.RpcList.AddRange(additionalRpcs); EditorUtility.SetDirty(PhotonNetwork.PhotonServerSettings); } if (countOldRpcs > 0) { bool convertRPCs = EditorUtility.DisplayDialog(CurrentLang.RpcFoundDialogTitle, CurrentLang.RpcFoundMessage, CurrentLang.RpcReplaceButton, CurrentLang.RpcSkipReplace); if (convertRPCs) { PhotonConverter.ConvertRpcAttribute(""); } } }
这是一个这个Unity编辑器上的RPC 列表更新代码。
一图胜千言!
上个图!!!
这里有新旧版本的兼容,代码里面自动把原来的属性[Rpc]替换为[PUNRpc]。
注意到这里面还有对列表长度的限制为255,即为byte字节的最大值。
三、Rpc View调用
在文件NetworkingPeer.cs中,Rpc的执行过程,代码:
/// <summary> /// Executes a received RPC event /// </summary> protected internal void ExecuteRpc(object[] rpcData, PhotonPlayer sender) { if (rpcData == null) { Debug.LogError("Malformed RPC; this should never occur. Content: " + LogObjectArray(rpcData)); 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[1] != null) { otherSidePrefix = (short)rpcData[(byte)1]; } string inMethodName; if (rpcData[5] != null) { int rpcIndex = (byte)rpcData[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[3]; } object[] inMethodParameters = (object[])rpcData[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.") + " By: " + sender.ID); } else { Debug.LogWarning("Received RPC \"" + inMethodName + "\" for viewID " + netViewID + " but this PhotonView does not exist! Was remote PV." + (ownerSent ? " Owner called." : " Remote called.") + " By: " + sender.ID + " Maybe GO was destroyed but RPC not cleaned up."); } 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 (string.IsNullOrEmpty(inMethodName)) { Debug.LogError("Malformed RPC; this should never occur. Content: " + LogObjectArray(rpcData)); return; } if (PhotonNetwork.logLevel >= PhotonLogLevel.Full) Debug.Log("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; if (!PhotonNetwork.UseRpcMonoBehaviourCache || photonNetview.RpcMonoBehaviours == null || photonNetview.RpcMonoBehaviours.Length == 0) { photonNetview.RefreshRpcMonoBehaviourCache(); } for (int componentsIndex = 0; componentsIndex < photonNetview.RpcMonoBehaviours.Length; componentsIndex++) { MonoBehaviour monob = photonNetview.RpcMonoBehaviours[componentsIndex]; if (monob == null) { Debug.LogError("ERROR You have missing MonoBehaviours on your gameobjects!"); continue; } Type type = monob.GetType(); // Get [PunRPC] methods from cache List<MethodInfo> cachedRPCMethods = null; bool methodsOfTypeInCache = this.monoRPCMethodsCache.TryGetValue(type, out cachedRPCMethods); if (!methodsOfTypeInCache) { List<MethodInfo> entries = SupportClass.GetMethods(type, typeof(PunRPC)); 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.Equals(inMethodName)) { foundMethods++; ParameterInfo[] pArray = mInfo.GetParameters(); // TODO: this should be cached, too, in best case 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) { Debug.LogError("PhotonView with ID " + netViewID + " has no method \"" + inMethodName + "\" marked with the [PunRPC](C#) or @PunRPC(JS) property! Args: " + argsString); } else { Debug.LogError("PhotonView with ID " + netViewID + " has no method \"" + inMethodName + "\" that takes " + argTypes.Length + " argument(s): " + argsString); } } else { Debug.LogError("PhotonView with ID " + netViewID + " has " + receivers + " methods \"" + inMethodName + "\" that takes " + argTypes.Length + " argument(s): " + argsString + ". Should be just one?"); } } }
这个冗长的代码就是RPC执行调用过程。类型查找,函数解析,函数参数解析,函数调用等等过程。
继续图:
来自:http://blog.csdn.net/cartzhang/article/details/51425225