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

在使用中摸索,在摸索中使用!

当然若有问题,请随时联系!!!

乌云之上有蓝天

时间: 2024-08-06 09:51:23

photon Unity RPC 调用流程的相关文章

unity集成openinstall流程

目的 1.Unity集成openinstall sdk? 最近在使用一个叫openinstall的SDK,通过它实现免填邀请码的功能,集成到unity游戏开发中.对App安装流程的优化,尤其是免填写邀请码安装,App推广的有奖邀请活动更大程度的达到推广爆炸式的效果. 在分享链接自定义各种动态参数(如推广渠道号,邀请码,游戏房间号,用户id等等).通过在分享链接url中附带app邀请人的用户id,就可达到免填邀请码的效果;或者app通过在url中附带游戏房间号实现直达游戏房间也可建立上下级关系:u

tars framework 源码解读(三) servant部分章节。客户端部分。完整的tars调用流程详解

一般tars客户端使用方式: 我们用客户端进行tars rpc调用时候,一般如下面这样写: 方式一. //直连方式 TC_Endpoint ep; AdminFPrx pAdminPrx; //服务管理代理 string sAdminPrx = "[email protected]"+_serverObjectPtr->getLocalEndpoint().toString(); pAdminPrx = Application::getCommunicator()->str

【转】 Android的NDK开发(1)————Android JNI简介与调用流程

原文网址:http://blog.csdn.net/conowen/article/details/7521340 ******************************************************************************************** * author:[email protected]大钟                                                                      

(转)android从应用到驱动之—camera(1)---程序调用流程

一.开篇 写博客还得写开篇介绍,可惜,这个不是我所擅长的.就按我自己的想法写吧. 话说camera模块,从上层到底层一共包含着这么几个部分: 1.apk------java语言 2.camera的java接口----java语言 3.camera的java接口的具体实现,即所谓的JNI-----(java—>C++) 4.camera客户端-----C++语言 5.camera服务器----C++语言 6.camera硬件抽象层,即所谓的HAL------C++语言 7.camera驱动 如上也

给Pomelo的聊天室添加time的RPC调用

为了练手,给聊天应用增加一个rpc调用和一个time类型的服务器,在servers/time/remote/timeRemote.js中,添加如下代码: module.exports.getCurrentTime = function (arg1, arg2, cb) { console.log("timeRemote - arg1: " + arg1+ "; " + "arg2: " + arg2); var d = new Date(); v

InnoDB主要数据结构及调用流程

InnoDB是MySQL中常用的数据引擎.本文将从源码级别对InnoDB重点数据结构和调用流程进行分析. 主要数据结构(buf0buf.h) Buf_pool Buf_pool是整个buffer系统中核心数据结构,数据库中所有的操作都会在这缓冲层得到体现.我们可以在配置文件中(InnoDB_buffer_pool_size)指定该缓冲池的大小.Buffer pool中又包含了多层数据结构:为了实现对buffer_pool的在线大小调整,引入了chunk数据结构:? Chunk在chunk数据结构

Hadoop源码学习笔记(4) ——Socket到RPC调用

Hadoop源码学习笔记(4) ——Socket到RPC调用 Hadoop是一个分布式程序,分布在多台机器上运行,事必会涉及到网络编程.那这里如何让网络编程变得简单.透明的呢? 网络编程中,首先我们要学的就是Socket编程,这是网络编程中最底层的程序接口,分为服务器端和客户端,服务器负责监听某个端口,客户端负责连接服务器上的某个端口,一旦连接通过后,服务器和客户端就可以双向通讯了,我们看下示例代码: ServerSocket server = new ServerSocket(8111); S

针对Android平台我们需要学习如何在Unity中调用Android的JAVA代码。

Unity for Android 比较特殊,Unity for IOS 打包是将XCODE工程直接交给开发者,开发者可以在工程的基础上继续添加新的视图,最后由开发者自行打包生成IPA包,发布程序.而Unity for Android打包直接生成APK包,等于说源代码开发者是看不到的,但是Unity的自身确实有些局限,针对Android平台我们需要学习如何在Unity中调用Android的JAVA代码.本章我们的目标是使用Unity的脚本打开Activity.首先我们创建一个普通的Android

android从应用到驱动之—camera(1)---程序调用流程(转)

一.开篇 写博客还得写开篇介绍,可惜,这个不是我所擅长的.就按我自己的想法写吧. 话说camera模块,从上层到底层一共包含着这么几个部分: 1.apk------java语言 2.camera的java接口----java语言 3.camera的java接口的具体实现,即所谓的JNI-----(java—>C++) 4.camera客户端-----C++语言 5.camera服务器----C++语言 6.camera硬件抽象层,即所谓的HAL------C++语言 7.camera驱动 如上也