photon Unity RPC 调用流程
本文章由cartzhang编写,转载请注明出处。 所有权利保留。  
文章链接:http://blog.csdn.net/cartzhang/article/details/51425225 
作者:cartzhang
一、Photon简介
Photon是一款非常不错的游戏服务端引擎,是一个服务器应用程序,可以在你选择的机器上运行,并且完全是自定义和权威性的控制,你可以自由的配置和部署多人应用的基础设施。 
在客户端,Photon支持多样的平台,使用C,C#,Flash进行编程的方式是不同的,但是基本的工作流是相似的。在这文档中我们尝试去解释它的概念和背景,而语言的细节部分需要查阅每个语言平台的参考文档。 
下载地址:https://www.photonengine.com/en/OnPremise/Download 
解压后的相关文件夹说明: 
deploy:主要存放photon的服务器控制程序和服务端Demo 
doc:顾名思义,文档 
lib:Photon类库,开发服务端需要引用的 
src-server:服务端Demo源代码 
 
在网络段,使用了Photon,所以拿来研究了下。看看代码,梳理了一下RPC流程。 
记录下来,以供以后参考和使用。 
官方网站:https://www.photonengine.com/en/PUN 
分两个部分一个编辑器界面工作流程,一个是网络调用流程。
二、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执行调用过程。类型查找,函数解析,函数参数解析,函数调用等等过程。 
继续图: 
相关参考
https://www.photonengine.com/en/PUN 
https://www.photonengine.com/en/OnPremise/Download 
http://www.cnblogs.com/liusuqi/archive/2013/05/15/3079686.html 
http://www.digiart.com.tw/files/photon/PhotonCloud01.pdf 
在使用中摸索,在摸索中使用! 
当然若有问题,请随时联系!!!
乌云之上有蓝天
photon Unity RPC 调用流程的更多相关文章
- HBase之RPC调用流程简介
		
首先分析hbase中对于master协议的调用: 在ConnectionImplementation的方法getKeepAliveMasterService被调用时,会通过MasterServiceS ...
 - OpenStack Trove组件WSGI和RPC调用流程(参考调用的整个过程)
		
参考博文:https://blog.csdn.net/bill_xiang_/article/details/72909927
 - 在这个应用中,我使用了 MQ 来处理异步流程、Redis 缓存热点数据、MySQL 持久化数据,还有就是在系统中调用另外一个业务系统的接口,对我的应用来说这些都是属于 RPC 调用,而 MQ、MySQL 持久化的数据也会存在于一个分布式文件系统中,他们之间的调用也是需要用 RPC 来完成数据交互的。
		
在这个应用中,我使用了 MQ 来处理异步流程.Redis 缓存热点数据.MySQL 持久化数据,还有就是在系统中调用另外一个业务系统的接口,对我的应用来说这些都是属于 RPC 调用,而 MQ.MySQ ...
 - RabbitMQ学习笔记5-简单的RPC调用
		
利用空的queue名字("")让rabbitMQ生成一个唯一的队列名称,同时指定队列是:临时的(auto-delete).私有的(exclusive). 在发送的RPC调用消息里设 ...
 - Unity3D RPC调用顺序问题
		
使用Unity自带的Network实现多人协同任务时,因为使用RPC传递消息.RPC即远程过程调用,对于它的使用,第一反应的问题就是如果连续两次调用RPC,RPC的函数会顺序执行吗?还是只要RPC的消 ...
 - rpc调用过程
		
在openstack中,各个组件之间的调用遵循RESTful风格,而组件内部各服务之间的相互调用采用rpc远程调用,比如nova-conductor和nova-compute rpc原理: 首先了解什 ...
 - alluxio源码解析-rpc调用概述(1)
		
alluxio中几种角色以及角色之间的rpc调用: 作为分布式架构的文件缓存系统,rpc调用必不可少 client作为客户端 master提供thrift rpc的服务,管理以下信息: block信息 ...
 - netty 实现简单的rpc调用
		
yls 2020/5/23 netty 实现简单rpc准备 使用netty传输java bean对象,可以使用protobuf,也可以通过json转化 客户端要将调用的接口名称,方法名称,参数列表的类 ...
 - 闲话RPC调用
		
原创文章转载请注明出处:@协思, http://zeeman.cnblogs.com 自SOA架构理念提出以来,应用程序间如何以最低耦合度通信的问题便呈现在所有架构师面前. 互联网系统的复杂度让我们不 ...
 
随机推荐
- 数据读进set,进行后处理
			
#include <iostream> #include <vector> #include <cstddef> #include <string> # ...
 - div标签的闭合检查
			
什么叫DIV标签有没有闭合呢?有<div>开头就应该有</div>来结尾闭合了.有时候写代码写 了<div>,忘记</div>结尾,谓之没有闭合也. 如 ...
 - java笔记线程方式1线程暂停yield
			
public static void yield():暂停当前正在执行的线程对象,并执行其他线程. 让多个线程的执行更和谐,但是不能靠它保证一人一次. public class ThreadYield ...
 - E20171102-E
			
segment n. 环节; 部分,段落; [计算机] (字符等的) 分段; [动物学] 节片; distinct adj. 明显的,清楚的; 卓越的,不寻常的; 有区别的; 确切的;
 - bzoj 1604: [Usaco2008 Open]Cow Neighborhoods 奶牛的邻居【切比雪夫距离+并查集+multiset】
			
参考:http://hzwer.com/4361.html 坐标开long long,inf开大点 先曼哈顿转切比雪夫(x+y,x-y),距离就变成了max(x',y'): 先按x排序,维护两个指针, ...
 - HTML5常用知识点
			
github代码:https://github.com/showkawa/H5C3/tree/master/html5 1.自定义属性 data- 1.1 可以给html里的所有DOM对象都可以添加一 ...
 - Sql 主键设置
			
1.开发数据库时常用命名规范 1>.使用不包含数字的单数表名,如果必要的话可以增加模块名前缀. 2>.对于主键的命名,使用表名+ID的形式. 3>.作为外键的列的名字应该与它们所对应 ...
 - Too many open files故障解决一例
			
Linux环境WebSphere输出日志: [// ::: EDT] 000090b7 SystemErr R Caused by: java.io.FileNotFoundException: /o ...
 - 网站开发综合技术  HTML
			
HTML 内容(Hyper Text Markup Language,超文本标记语言) CSS 网页美化 Javascript 脚本语言 第一部 ...
 - FCC  基础JavaScript   练习1
			
1.JavaScript中的注释方式有以下两种 // This is an in-line comment. /* This is a multi-line comment */ 2.avaScrip ...