本文章由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 调用流程的更多相关文章

  1. HBase之RPC调用流程简介

    首先分析hbase中对于master协议的调用: 在ConnectionImplementation的方法getKeepAliveMasterService被调用时,会通过MasterServiceS ...

  2. OpenStack Trove组件WSGI和RPC调用流程(参考调用的整个过程)

    参考博文:https://blog.csdn.net/bill_xiang_/article/details/72909927

  3. 在这个应用中,我使用了 MQ 来处理异步流程、Redis 缓存热点数据、MySQL 持久化数据,还有就是在系统中调用另外一个业务系统的接口,对我的应用来说这些都是属于 RPC 调用,而 MQ、MySQL 持久化的数据也会存在于一个分布式文件系统中,他们之间的调用也是需要用 RPC 来完成数据交互的。

    在这个应用中,我使用了 MQ 来处理异步流程.Redis 缓存热点数据.MySQL 持久化数据,还有就是在系统中调用另外一个业务系统的接口,对我的应用来说这些都是属于 RPC 调用,而 MQ.MySQ ...

  4. RabbitMQ学习笔记5-简单的RPC调用

    利用空的queue名字("")让rabbitMQ生成一个唯一的队列名称,同时指定队列是:临时的(auto-delete).私有的(exclusive). 在发送的RPC调用消息里设 ...

  5. Unity3D RPC调用顺序问题

    使用Unity自带的Network实现多人协同任务时,因为使用RPC传递消息.RPC即远程过程调用,对于它的使用,第一反应的问题就是如果连续两次调用RPC,RPC的函数会顺序执行吗?还是只要RPC的消 ...

  6. rpc调用过程

    在openstack中,各个组件之间的调用遵循RESTful风格,而组件内部各服务之间的相互调用采用rpc远程调用,比如nova-conductor和nova-compute rpc原理: 首先了解什 ...

  7. alluxio源码解析-rpc调用概述(1)

    alluxio中几种角色以及角色之间的rpc调用: 作为分布式架构的文件缓存系统,rpc调用必不可少 client作为客户端 master提供thrift rpc的服务,管理以下信息: block信息 ...

  8. netty 实现简单的rpc调用

    yls 2020/5/23 netty 实现简单rpc准备 使用netty传输java bean对象,可以使用protobuf,也可以通过json转化 客户端要将调用的接口名称,方法名称,参数列表的类 ...

  9. 闲话RPC调用

    原创文章转载请注明出处:@协思, http://zeeman.cnblogs.com 自SOA架构理念提出以来,应用程序间如何以最低耦合度通信的问题便呈现在所有架构师面前. 互联网系统的复杂度让我们不 ...

随机推荐

  1. JSP:目录

    ylbtech-JSP:目录 1.返回顶部   2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部     6.返回顶部   作者:ylbtech出处:http://ylbtech.c ...

  2. 继续不温不火Windows Phone

    已经辞了,人也离开帝都了.是否还会回去? 不知道,也许脑子突然正常了又跑回去了. 如题,继续不温不火的Windows Phone. 今年2014,没错,Windows Phone是新加了好几家厂商,微 ...

  3. 2017年最新VOS2009/VOS3000最新手机号段导入文件(手机归属地数据)

    VOS2009.vos3000.vos5000最新手机号段归属地数据库导入文件. 基于2017年4月最新版手机号段归属地制作 共360569条记录,兼容所有版本的昆石VOS,包括VOS2009.vos ...

  4. VS2015 razor 提示一闪而过

    出现的问题链接地址 https://social.microsoft.com/Forums/azure/zh-CN/ff308f71-c743-4f8c-b5e4-a7260c8b5f59/vs201 ...

  5. 略微讲讲最近的 webpack 该如何加快编译

    首先假设 基础的环境是有 creat-react-app 所创建的 即所有基础的loader,插件的 cache 都已经缓存了 在这种情况下想加速,真是很难 不过,有一个插件是可以观察 各个模块所花的 ...

  6. 使用Redis存储Nginx+Tomcat负载均衡集群的Session

    配置Tomcat的session共享可以有三种解决方案: 第一种是以负载均衡服务器本身提供的session共享策略,每种服务期的配置是不一样的并且nginx本身是没有的. 第二种是利用web容器本身的 ...

  7. bash、dash(/bin/bash和/bin/sh)的区别

    Linux中的shell有多种类型,其中最常用的几种是Bourne   shell(sh).C   shell(csh)和Korn   shell(ksh).三种shell各有优缺点. Bourne ...

  8. hbase本地调试环境搭建

    1,前言 想要深入的了解hbase,看hbase源码是必须的.以下描述了搭建hbase本地调试环境的经历 2,安装步骤 2.1,启动hbase 1,安装java和IDE IntelliJ,下载源码等. ...

  9. git tag管理

    操作 实例 创建标签 git tag -a V1.2 -m 'WebSite version 1.2' 查看标签 git tag / git show V1.2 远程推送 git push origi ...

  10. 新开的坑-python学习笔记(1)——连接符与转义

    1/print输出格式虽然知道怎么用却还要打破砂锅的问问题... 问题答案是 需要读很多基础文档教程 --------例如 "+"  . "," 作为连接符的作 ...