Cached Events 缓存事件
Usually, the operation RaiseEvent sends your event to anyone else, currently in the same room. Cached events are … cached … and will be sent to joining (new) players when they arrive.
通常,操作RaiseEvent 发送你的事件到其他当前游戏房间中的玩家,缓存事件是被缓存的,将被发送给新加入的玩家。
Use case: Position updates are sent 10 times a second, so joining players will get your position quickly. Your player’s nickname, color and clothing don’t need ten updates per second. Once sent, it would be nice to “remember” and update new players when they arrive. This is what cached events do.
用例:位置更新是每秒发送10次,所以加入的玩家会很快获得你的位置。你的昵称、颜色、衣服不需要每秒更新10次。一旦发送,它很高兴的记住和更新新的玩家,这是缓存事件做的事情。
Cached events can be modified and removed as needed. Also, they are sorted for delivery. See below.
当需要的时候,缓存事件可以被修改和删除,他们是被排序的。
Disclaimer 免责声明
Cached events are a feature of the Lite Application and Lite Lobby Application of Photon 3. They are not supported by the MMO Application or if you start from scratch.
缓存事件是Lite和Lobby的特性,他们不被MMO和你自己开发的应用所支持。
Cache control 缓存控制
Events are cached per actor and per event code. This way, every player can send event “MyProfile” (a.k.a. (byte)0) and set some values.
事件被缓存为每个玩家和每个事件代码,每一个玩家都可以发送事件 “MyProfile”和设置一些值。
Per event, the cache can be updated and replaced or simply removed.
每个事件,这缓存可以被更新和替换或者移除。
Updating an event is done by merging new content into the previously cached one. The newer event’s data hashtable will update (key by key) any existing value.
更新一个事件是并入一个新的内容到缓存中,这新的事件的数据将更新现有的值。
Photon 3 allows you to send Null as value in hashtables (and several other places), so to get rid of a key in a cached event: Send the same event, assign null to the key you want deleted and merge the event.
Photon3允许你去发送Null作为一个值,发送这事件,null值将会删除已有的与Key相同的事件缓存。
Ordered Delivery 下令交付
Cached events are sent to players when they join, which means they make up the fist few events they get inside a room.
当玩家加入时缓存事件将被发送,这意味着,他们进入房间时将得到所有的事件
New players get cached events, grouped by player, sorted by ascending actor number. This gives you all cached events of player 1 before you get other player’s events.
新的玩家获取缓存事件,按照玩家编号排序,你将在获取他们人之前获取玩家1的所以事件缓存。
Per (sending) player, cached events are ordered by ascending eventCode. No matter if event 10 was sent before event 1, if it’s coming from the cache, event 1 will precede event 10 (and 2 and so on).
每个玩家,事件缓存是按照事件代码排序的。无论事件10是否是在事件1之前发送的,事件1都将排在事件10的前面。
This should give you all control about the order you need. You can even cache stuff like “map” or “match rules” this way: put them in the event with lowest number and new players know this first.
这将给你所有你需要的控制命令,你甚至可以缓存的地图、匹配规则,把它们放在事件中,新的玩家就能第一时间知道。
Calling Operations 调用操作
As described in the “
Basic Concepts ”, operations are Remote Procedure Calls, defined by some Photon Application.
作为基础概念,操作是远程过程调用,由Photon应用程序定义。
The client APIs includes a LitePeer class (or similar construct), which provides methods to call operations on the server, because we use the Lite Application most often for demos and to make your life easier
客户端APIs包括了一个LitePeer ,提供了一些方法可以去调用服务端的操作,所以我们使用Lite 应用。
For example, LitePeer.OpJoin wraps up the call of operation Join. The code below will call OpJoin when the connection is established:
例如,LitePeer.OpJoin 概括了加入操作。以下是连接时调用加入的代码:
public
void
PeerStatusCallback(StatusCode returnCode)
{
// handle returnCodes for connect, disconnect and errors (non-operations)
switch
(returnCode)
{
case
StatusCode.Connect:
this
.DebugReturn(
"Connect(ed)"
);
this
.peer.OpJoin(
this
.gameID);
break
;
//[...]
The LitePeer covers all Lite Application operations this way.
Operation Results from Photon 来自Photon的操作结果
Per Operation, the application on the server can send a result. Imagine a “GetHighscores” operation and the use for a result becomes obvious and would contain a list of scores. Other RPCs, like RaiseEvent, can safely omit the result if it’s not useful.
每个操作,服务端上的应用程序都可以发送一个结果。想象一个GetHighscores操作和使用结果成为显而易见的一个列表。其他的远程过程调用,像RaiseEvent,如果它不是有用的可以被安全的忽略掉。
Getting the result of an operation takes a moment in general, as there is network lag. That’s why any result is provided asynchronously by a call to IPhotonPeerListener.OperationResult
获取操作的结果,一般都存在网络延迟,所以这是通过异步调用 IPhotonPeerListener.OperationResult 实现的
Per platform, the result callback might look different. The following code is from the C# callback interface, which a game must implement:
每个平台这回调的结果可能看起来有点不同。以下是C#代码必须要实现的接口
public
interface
IPhotonPeerListener
{
//[...]
void
OnOperationResponse(OperationResponse operationResponse);
The callback OperationResult is only called when your game calls peer.DispatchIncomingCommands. This makes it easier to get the callback in the right thread context. In most cases it’s enough to call DispatchIncomingCommands every few frames in your game loop.
当你的程序调用peer.DispatchIncomingCommands时回调OperationResult 。这使得它容易获得回调在正确的线程上下文。在大部分情况下在你的游戏循环中定期调用DispatchIncomingCommands
Custom Operations 自定义操作
Custom Operations are just Operations which are not defined by Exit Games in our applications (Lite, LoadBalancing, etc.). Basically, any operation that’s not covered by the LitePeer is “custom”.
自定义操作是一个没有定义在 Exit Games中的操作。基本上,任意操作不会覆盖LitePeer 的即是自定义的。
If you look behind the scenes you will see that even non-custom operations, like Join, use the very same methods! As example, here is the code from LitePeer Join:
以下是加入操作的代码示例:
public
virtual
bool
OpJoin(
string
gameName, Hashtable gameProperties, Hashtable actorProperties,
bool
broadcastActorProperties)
{
if
(
this
.DebugOut >= DebugLevel.ALL)
{
this
.Listener.DebugReturn(DebugLevel.ALL,
"OpJoin("
+ gameName +
")"
);
}
Dictionary<
byte
,
object
> opParameters =
new
Dictionary<
byte
,
object
>();
opParameters[(
byte
)LiteOpKey.GameId] = gameName;
if
(actorProperties !=
null
)
{
opParameters[(
byte
)LiteOpKey.ActorProperties] = actorProperties;
}
if
(gameProperties !=
null
)
{
opParameters[(
byte
)LiteOpKey.GameProperties] = gameProperties;
}
if
(broadcastActorProperties)
{
opParameters[(
byte
)LiteOpKey.Broadcast] = broadcastActorProperties;
}
return
this
.OpCustom((
byte
)LiteOpCode.Join, opParameters,
true
, 0,
false
);
}
As you can see, OpJoin internally uses OpCustom to be sent. The method is just a wrapper to streamline the usage. This is what you should do as well.
正如你看到的,OpJoin 内部使用的是OpCustom 。
The call to OpCustom shows the parts needed to send an operation:
调用OpCustom 需要以下部分发送在一个操作里:
- OperationCode: A single byte to identify an operation (to minimize overhead). (byte)LiteOpCode.Join equals 255, as example.
-
- Operation Parameters: Is the set of parameters expected by the server. Again, bytes are used instead of names, to minimize overhead per parameter.
-
- Reliability: The third OpCustom parameter defines if the operation must arrive at the server or might become lost (unreliable). Data that’s replaced quickly can be unreliable. Join, Authentication or similar are better sent reliable (that’s why there’s a “true” in this case).
-
- ChannelId: Is the sequence, this operation is placed into for ordering. Can be 0 in most cases.
-
- Encrypt: Optionally encrypts data between the client and the server. Should be used only where definitely needed, as it takes away some performance and it not always needed. Here: false.
-
The latter three values are not exactly operation parts, obviously. SendReliable, ChannelId and Encrypt are communication settings which can be changed on a per-operation basis.
后三个值不是必须的操作部分。
The byte codes for the operation, parameters and result values are defined on the server-side. To call it, you need to use those on the client correspondingly.
字节码操作、参数和结果值定义在服务器端。 调用它,你需要使用这些客户端相应的代码。
Operation-call Return Value 操作调用
For simplicity, we just ignored that OpCustom (OpJoin and all other operations) have a return value. However, it has a practical impact:
简单起见,我们仅仅忽略OpCustom 的返回值,它包括以下值:
Depending on the current client state an operation might be impossible to call. The result of OpCustom tells you if the operation can be send or is ignored right away (client side):
- True: The operation is possible and will be sent to the server (by SendOutgoingCommands()). All values you passed to OpCustom are serialized now and you can modify your data at will.
-
- False: The operation can not be sent (e.g. because the client is not connected). If this happens, you should check the debug hints that the client library provides (see DebugReturn).
-
Defining New Operations 定义新的操作
In most cases, the definition of new operations is done server-side, so the process to implement and use those is explained in that context. Read: “
Adding Operations ”.
大部分情况下,操作被定义在服务端
Firewall Settings 防火墙设置
If Photon should be accessible from remote machines (especially through the internet), make sure the Windows Firewall does not block connections.
如果Photon可以访问远程机器,确保Windows防火墙不阻止该连接。
To check settings, open the “Windows Firewall” in the Windows Control Panel. Click “Allow a program through Windows Firewall” and verify PhotonSocketServer is listed and checked.
去检查设置,打开控制面板中的“Windows Firewall”。点击 “Allow a program through Windows Firewall”并确认PhotonSocketServer是被勾选的。
Cloud Security Settings 云安全设置
If Photon should run within a Cloud Service, you have to make sure the UDP and TCP ports are available.
如果Photon运行在一个云服务上,你需要确保UDP和TCP端口是可用的。
In Amazon EC2, the Security Group should at least open these ports:
在Amazon EC2中,这安全组至少需要打开这些端口:
Exception Handling 异常处理
With Photon 3.0 the exception handling went through a complete redesign. In previous versions the strategy was to catch all exceptions inside the Photon Framework (photonsocketserver.dll) and log them through a logging facade. This design had several drawbacks:
Photon3的异常处理进行了完整的重新设计。在旧版中,策略是内部捕获所有异常的Photon框架和通过日志记录他们。这样的设计有以下的缺陷:
- unhandled exception event handler not being called: The expected CLR default behavior is when exceptions are not handled by the developers code to be notified on the unhandled exception event handler.
- 未处理的异常事件不被调用
- exception handling dependency on logging: Developers had to implement a logging facade, failing to do so could lead to “lost” exceptions.
- 异常处理依赖于日志
- only one behavior for not handled exceptions: Exceptions not handled by the developer where caught by photon, logged and then ignored. Effectively working in a similar way the “runtime backstop” in .Net 1.x did.
- 有一种行为是不处理异常的
- inconsistent behavior: Unhandled exceptions in threads controlled by the developer raised the unhandled exceptions handler and terminated the process
- 不一致的行为
Content 目录
- New With Photon 3.0 新的
- Suggestions For the Usage Of the UnhandledExceptionPolicies 推荐使用UnhandledExceptionPolicies
- Logging Unhandled Exceptions 记录未处理的异常
- Configuring the UnhandledException Policy 配置未处理异常的策略
- Photon Core Debugging Photon核心调试
New With Photon 3.0: 新的
- unhandled exception event handler is always called: Exceptions not handled by the developers code will always raise the unhandled exception event handler.
- 未处理的异常事件总是被调用
- non-intrusive logging: We encourage developers to implement the unhandled exceptions handler, but independently of the developers code unhandled exceptions are logged by photon. For details see “Logging” section below.
- 非侵入性的日志记录
- three policies for unhandled exceptions: Photon supports one of three policies when an unhandled exception is raised - ignore, restart application, end process
- 三种针对未处理异常的策略
- consistent behavior
- 一致的行为
有2种情况异常的出来策略是继承于CLR,ThreadAbortException 不必要的重启或停止进程,StackOverflowException 不能被忽略的。
Note: the Application Compatibility Flag (legacyUnhandledExceptionPolicy) described in
Exceptions in Managed Threads isn't supported by Photon on a per application config. This mode is set per instance (UnhandledExceptionPolicy = "Ignore") - see "Configuring the UnhandledException Policy" section below.
Suggestions For the Usage Of the UnhandledExceptionPolicies 推荐使用UnhandledExceptionPolicies
Microsoft introduced the “Terminate Process” as new default policy with .Net 2.0 with following statement quoted from
Exceptions in Managed Threads :When threads are allowed to fail silently, without terminating the application, serious programming problems can go undetected. This is a particular problem for services and other applications which run for extended periods. As threads fail, program state gradually becomes corrupted. Application performance may degrade, or the application might hang.
微软推荐“Terminate Process” (终止进程)作为新的默认策略:当线程是允许静默失败,没有终止应用程序,严重的编程问题可以不被发现。这是一个特定的问题和其他应用程序的服务。因为线程失败,程序状态逐渐成为损坏的。应用程序的性能可能会降低,或应用程序可能挂起。
Allowing unhandled exceptions in threads to proceed naturally, until the operating system terminates the program, exposes such problems during development and testing. Error reports on program terminations support debugging.
允许未处理的异常在线程中继续,直到操作系统终止该问题,暴露此类的问题在开发和测试中。错误报告在程序终止时支持调试。
But depending on the stage or situation your project is (in development, in QA, LIVE without known issues, etc.) you might consider using one of the different policies supported by Photon:
但这取决于你项目的阶段或情况(在开发中,,在QA没有已知的问题,等等),你可能会考虑使用不同的Photon支持的策略:
- Restart Application 重启应用程序
- Ignore 忽略
- Terminate Process 终止进程
While developing setting “Terminate Process” launches the debugger/visual studio on process termination. Are you handling with a multithreaded issue you might prefer to keep the process running and the application loaded then setting the policy “Ignore” would be what you prefer.
虽然开发中设置“ 终止进程 “会在进程终止时启动visual studio调试器。你处理一个多线程问题,你可能倾向于保持进程运行和应用程序加载,然后你可能喜欢设置策略为” 忽略 “。
During a QA Phase, either during the run of stress tests or manual testing, using “Terminate Process” ensures you won’t get flooded by errors that stem from the root-error.
在QA阶段,要么压力测试,要么手动运行测试,使用” 终止进程 “确保你不会被洪水淹没,错误源自根误差。
Live Stable - assuming your service is stable a policy you can consider using is the “Restart Application”. In the rare event of an unhandled exception the application would be restarted by Photon minimizing the risks - this is the fastest way for the system to be up again. Faster than setup Photon as a Service with “Terminate Process” as policy and the auto-restart windows services feature. Note: you should monitor your service and log files in case this happens more often.
稳定——假设你的服务是稳定的一个策略,可以考虑使用“ 重启应用程序 “。在罕见的事件未处理的异常中会通过Photon重新启动应用程序来减少风险——这是最快的使系统重新运行的方法。快速的设置Photon为 “终止进程” 策略和自动重启Windows服务。注意:你应该监视你的服务和日志文件以防这种情况经常发生。
Live with a known issue - if the system shows an unexpected exception quite often and you need time to fix - depending on the exception it might be possible to keep a reasonably stable system by setting the policy to “Ignore”.
一个已知的问题——如果系统显示一个未处理异常,通常你需要时间来修复——根据异常,保持一个合理稳定的系统可能需要设置策略为“ 忽略 “。
Note on StackOverflows: Usually the stacktrace logged during the unhandled exception will point you to the fix. The StackOverflowException is a case where the stacktrace can get lost if the unhandled exception policy is set to “Ignore” or “RestartApplication”.
注意StackOverflows:通常情况下,在未处理的异常堆栈跟踪记录将指引你去修复。StackOverflowException 是一种情况,堆栈跟踪可以获取你设置忽略或重启应用程序而丢失的未处理的异常信息。
Note on Debugging Core: For the rare cases where the Photon core raises an unexpected exception we recommend setting “ProduceDump” is always set for details see Photon Core Debugging” - sending us the generated dump files and logs will help us to fix the issue.
注意在调试核心时:罕见的情况下提升Photon核心触发一个未处理的异常,我们建议总是设置为“ProduceDump”,详细信息请参阅Photon核心调试——我们生成的转储文件和日志将帮助我们解决这个问题。
Logging Unhandled Exceptions 记录未处理的异常
As depicted in the figure below an unhandled exception event is raised first in the context of the custom application (1) where the developer can log the exception in any way he requires to c) - please also refer to sample code Lite, Lite Lobby, etc. In a second step the CLR then raises the event in the default application domain (2) - which in the case of Photon is the PhotonHostRuntime. Because of this Photon is able to log the exception in b).
下面的图中描绘了事件引发未处理的异常,首先在自定义应用程序的上下文(1)开发人员可以以任何方式记录异常他需要c)——也请参考样本代码Lite,Lite Lobby,等等。在第二步CLR触发事件在默认应用程序域(2),而在此例中Photon是PhotonHostRuntime的。 由于这种Photon能记录异常在b)。
Photon Loggingfile default path/filenames
Photon日志文件默认路径/文件名
a [$PhotonBaseDir]\bin_[$OS]\log\Photon-[$InstanceName]-[$date].log
b [$PhotonBaseDir]\bin_[$OS]\log\PhotonCLR.log
c [$PhotonBaseDir]\log\[$ApplicationName].log
Configuring the UnhandledException Policy 配置未处理异常的策略
<Runtime
Assembly="PhotonHostRuntime, Culture=neutral"
Type="PhotonHostRuntime.PhotonDomainManager"
UnhandledExceptionPolicy="Ignore"
UnhandledExceptionPolicy can be: ReloadAppDomain, Ignore or TerminateProcess
未处理异常策略可以是:重新加载程序域、忽略、终止进程
Photon Core Debugging Photon核心调试
<!-- Instance settings -->
<Instance1
...
ProduceDumps = "TRUE"
DumpType = "Full"
MaxDumpsToProduce = "2"
...
ProduceDumps can be: TRUE or FALSE. DumpType: Full, Maxi or Mini
转储文件可以是:True或者False。转储类型可以是:全部、最大、最小
Secure Websockets Setup 安全Websockets设置
To allow your clients connecting your Photon Server using secure websockets with ‘wss://…’ please proceed as follows.
允许你的客户端连接你的Photon服务器使用安全websockets。
- Obtain a SSL certificate. 获取SSL证书
-
- For development purposes, you can generate a self-signed SSL certificate. 对于开发者而言,你可以生成一个自签名的SSL证书
如果你已安装了IIS7,按照以下步骤:
The “friendly name” should be the same name which your web clients use to access your server, e.g.: photon.example.com:
“friendly name” 必须是相同的名字当你的web客户端访问服务器的时候。
After you are done, make sure that the certificate is installed into the correct certificate store.
当您完成后,请确保该证书被安装到正确的证书存储中
-
-
- Open the Microsoft Management Console by typing “mmc.exe” on the command line.
- From the “File” menu, choose “Add / Remove snap-in”.
- Select “Certificates” -> “Add” -> “Computer Account” -> “Local Computer” -> “OK”.
- Expand the nodes on the left hand to “Certificates” -> “Personal” -> “Certificates”.
- Your certificate should show up like this:
-
这有更多的细节去解释如何使用Management Console。
-
- If you don’t have IIS 7 installed on your machine, follow one of the various tutorials that are available on the internet to generate a self-signed SSL certificate for your machine.如果你的机器上没有安装IIS 7,遵循互联网上的各种教程为你的机器生成一个自签名的SSL证书。
Once you have generated the certificate, open the “Local computer” certificate store as described in 1.1
一旦你已经生成了证书,打开“本地计算机”证书存储区
Right-click on the “Personal” node, choose “All Tasks” -> “Import”. Follow the wizard to import your certificate.
右击 “Personal” 节点,选择“All Tasks”-> “Import”。按照向导来导入您的证书
-
- For production servers, please acquire a certificate that is signed by a trusted Certificate Authority instead of using a self-signed certificate. Follow the steps from 1.2 to install it into the “Local Computer” certificate store on your server.对于生产服务器,请获得由受信任的证书颁发机构签署的证书,而不是使用自签名证书。按照以下步骤从1.2把它安装到你的服务器上的“本地计算机”证书存储区。
-
- Add these attributes to the WebSocket listener in the PhotonServer.config:
- 在PhotonServer.config添加这些属性到WebSocket监听器:
attribute |
default |
description |
Secure |
TRUE |
True defines a listener to be secured by SSL. |
StoreName |
MY |
Name of store where certificate can be found. If you have installed the certificate into the "Personal" store, like described above, set the value to "MY" (or omit the StoreName setting, so the default is used). |
CertificateName |
Photon |
Name of certificate. Enter the value of the 'IssuedTo' field that is shown in the certificate store (see step 1), NOT the "Friendly Name". |
UseMachineStore |
FALSE |
Defines if machine store ("local computer store") should be used. We recommend to install the certificate into the machine store, like described above, and set this value to "TRUE", because the certificate will be available to Photon regardless of the account under which it runs and no matter if Photon is started as an application or as an service. If you set this value to "FALSE", Photon will look for the certificate in the "Current User" certificate store if it is started as an applicaton; if it is started as a Service, it will look in the associated "Service" certificate store - so make sure that you install the certificate in the right store. |
-
-
- Restart your Photon Server … voila!
- 重启你的Photon服务器
This is an example Photon Server configuration for secure websockets:
这是一个Photon服务器配置安全websockets的例子
...
<
WebSocketListeners
>
<-- Web Sockets Listener with SSL -->
<
WebSocketListener
IPAddress
=
"0.0.0.0"
Port
=
"9091"
DisableNagle
=
"true"
InactivityTimeout
=
"10000"
OverrideApplication
=
"Lite"
Secure
=
"true"
StoreName
=
"My"
CertificateName
=
"server1.example.com"
UseMachineStore
=
"true"
>
</
WebSocketListener
>
</
WebSocketListeners
>
...
Receiver Groups 接收组
Receiver groups are an alternative way to define who receives an event. It’s an option available with Lite’s operation RaiseEvent.
接收组是一个替代的方法来定义接收到的事件,他是一个操作,可能是Lite的RaiseEvent操作
Usually, Lite sends your events to everyone else in the same room. With the receiver group parameter for RaiseEvent, you can send an event to “All”, including yourself. If needed, you can also send to only the “MasterClient”.
通常,Lite发送你的事件给同一个房间里的每个人。带有接收组的参数,你可以发送一个事件到 “All”,包括你自己。如果需要的话,你也可以仅仅发送到“MasterClient”。
This is an alternative to listing all target players, which is still possible in RaiseEvent.
这是代替的列出所有的目标玩家,这在RaiseEvent中仍然是可能的
MasterClient 主客户端
The MasterClient is the one with the lowest actorNumber in the room and thus the one who’s there the longest time.
这主客户端是一个编号最低以及在房间内时间最长的玩家。
This doesn’t give the MasterClient any privileges. But it could. Example: The MasterClient is always the one to start a new round of your game (in the same room).
这并没有给主客户端什么特权,但是它可能:主客户端总是房间内开始游戏的玩家。
If the current MasterClient leaves, a new one is assigned immediately. This is done by convention, not explicitly by events.
如果当前主客户端离开,立刻分配一个新的,这是通过约定的,不是通过事件。
Disclaimer 拒绝
Receiver groups are a feature of the Lite and Lite Lobby Applications of Photon 3. They are not supported by the MMO Application or if you start from scratch.
接收组是Photon3的Lite和Lite Lobby应用的一个功能。他们是不支持MMO应用或者你自己从头开始开发的应用。
posted on 2015-11-25 11:39
思月行云 阅读(1076)
评论(0) 编辑 收藏 引用 所属分类:
Photon