Fork me on GitHub
随笔 - 215  文章 - 13  trackbacks - 0
<2016年11月>
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910


专注即时通讯及网游服务端编程
------------------------------------
Openresty 官方模块
Openresty 标准模块(Opm)
Openresty 三方模块
------------------------------------
本博收藏大部分文章为转载,并在文章开头给出了原文出处,如有再转,敬请保留相关信息,这是大家对原创作者劳动成果的自觉尊重!!如为您带来不便,请于本博下留言,谢谢配合。

常用链接

留言簿(1)

随笔分类

随笔档案

相册

Awesome

Blog

Book

GitHub

Link

搜索

  •  

积分与排名

  • 积分 - 211162
  • 排名 - 118

最新评论

阅读排行榜

Hello World Part 1 第一部分

     The purpose of this tutorial is to give you an introduction to the client API and some of the basic concepts behind Photon. The tutorial is divided in parts. We begin with a very simple demo which will be refactored to more usable code step by step while digging deeper into the different concepts of Photon.
     本教程的目的是给你介绍客户端API和一些Photon的基本概念。本教程是分为几个部分。 我们从一个非常简单的演示开始,然后重构更多可用的代码一步一步的深入。

Contents 内容

Overview 概述

     The application we will create is a simple windows console application that connects to a locally hosted photon server.
     我们将创建的简单的windows控制台程序连接一个本地的Photon服务器
 
     Note: We assume you have followed the steps in "Photon in 5 Minutes" and have a locally running Photon Server.
     注意:我们假设您遵循“Photon 5分钟”的步骤和有一个本地运行的Photon服务器

     The basic concepts we will build the application on are:

  • To communicate with Photon Server we need a PhotonPeer where you call Connect to initiate the communication.
  • Outgoing/Incoming messages are queued in the PhotonPeer. In order to transfer the messages over the wire and dispatch (incoming) them to your application Service has to be called.
  • Changes in the status of the connection will be notified to the IPhotonListener.OnStatusChanged.
     我们将构建的应用程序的基本概念是:
  • 我们需要一个 PhotonPeer 与Photon服务器进行调用、连接、发起等通信。
  • 即将传出的/传入的消息是在PhotonPeer排队。 为了将消息传递和调度(输入)他们,你的应用程序服务也会被调用。
  • 改变连接的状态将被通知到 IPhotonListener.OnStatusChanged 。

Project Setup 项目设置

     We will start with a C#/Windows/Console project and name it Helloworld1:
     我们将从一个C#/Windows/Console项目开始,它的名字叫做Helloworld1
 

Photon Server: Hello World New Projekt

     We need to add a reference to PhotonDotNet.dll:
     我们需要去添加引用PhotonDotNet.dll

Photon Server: Hello World Add References

     Select the folder where you extracted the Photon-Dotnet SDK and browse to lib/debug where you will find PhotonDotNet.dll
     选择的文件夹是你提取Photon-Dotnet SDK的地方和浏览到lib /debug,你会发现PhotonDotNet.dll
 

Photon Server: Hello World Browse References

     The next step ist to add “using ExitGames.Client.Photon;”
     下一步是添加 “using ExitGames.Client.Photon;”
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ExitGames.Client.Photon;
 
     Note: without the using line you will get compile errors like: "The type or namespace name 'IPhotonPeerListener' could not be found (are you missing a using directive or an assembly reference?)...".And the "Implement interface 'IPhotonPeerListener'" Intellisense help (see below) will be missing. In our forums we've got the following hint: "For Mac / MonoDevelop users need to reference System.Core to get .linq". We don't require Linq for this tutorial so you may as well just remove the line.
     注意:没有using你会得到编译错误,如:“类型或名称空间的名字“IPhotonPeerListener”不能被发现(你错失了一个using指令或一个程序集引用)……”。和“实现接口的IPhotonPeerListener’”智能感知帮助(见下文)将会丢失。在我们的论坛我们已经得到了下面的提示:“Mac / MonoDevelop用户需要参考系统。核心得到linq”。 我们不需要Linq本教程所以你不妨就删除行。

OnStatusChanged OnStatusChanged事件

     Now we will implement the listener where we receive the PhotonPeer notifications. So the next step is adding the interface to the Class Program:
     现在我们将实现侦听器,为了我们能够收到PhotonPeer通知。所以下一步是为类程序添加接口:

Photon Server: Hello World Add Interface

     For now we will only look into OnStatusChanged where status changes of the PhotonPeer are notified and ignore the other three callbacks. So next we will replace the NotImplementedException as highlighted below:
     现在我们只会关注 OnStatusChanged 在状态更改时 PhotonPeer的通知,而忽视其他三个回调。 所以接下来我们将取代NotImplementedException,请看下面:
 
#region IPhotonPeerListener Members
 
public void DebugReturn(DebugLevel level, string message)
{
    //throw new NotImplementedException();
}
 
public void OnEvent(EventData eventData)
{
    //throw new NotImplementedException();
}
 
public void OnOperationResponse(OperationResponse operationResponse)
{
    //throw new NotImplementedException();
}
 
public void OnStatusChanged(StatusCode statusCode)
{
    Console.WriteLine("OnStatusChanged:" + statusCode);
}
 
#endregion

Peer Connect Peer连接

     So now we are getting to the main part of our tutorial. We add line 3 to create an instance of our listener. Line 4 will create an instance of the PhotonPeer and line 5 initiates the server connect to local host on port 5055. “Lite” is the default application on the server.
     所以现在我们获取的是教程的主要部分。 我们添加第3行,我们的侦听器创建一个实例。第4行将创建的一个PhotonPeer实例,第5行启动服务器连接到本地主机上的端口5055。 “Lite”是服务器上默认的应用程序。
 
static void Main(string[] args)
{
    var listener = new Program();
    var peer = new PhotonPeer(listener, ConnectionProtocol.Udp);
    peer.Connect("localhost:5055", "Lite");
    ...
 

Calling Service 调用服务器

     When running the code above - nothing would happen. We have to call peer.Service to trigger the network transfer and dispatching of notifications:
     当运行上面的代码——什么事都不会发生。 我们必须调用peer.Service触发网络传输和调度通知:
 
do
{
    Console.WriteLine(".");
    peer.Service();
    System.Threading.Thread.Sleep(500);
}
while (true);
 
     Ok, we are done. Now hit F5. When Photon Server is running you will see the following:
     好了,我们已经完成了任务。现在按F5。当Photon服务器运行,您将看到以下:
 

Photon Server: Hello World Photon Running

     That’s it. You are now connected to the Photon Server. In part 2 we will have a look on how to start using the connection to the Photon Server.
     就是这样。您现在已连接到Photon服务器。在第2部分中我们将看一看如何开始连接到Photon服务器。
 

Photon Server not found Photon服务未找到

     If Photon Server is not running you will get a disconnect notification:
     如果Photon服务器没有运行,你将会得到一个断开通知:

Photon Server: Hello World Disconnect Notification

     In the screenshot above you see the behavior when trying to connect against a locally hosted Photon Server.OnStatusChanged:InternalReceiveException: The client is trying to connect a not existent service on the same machine, because the windows socket lib raises an exception.A disconnect, triggered after a timeout, is the normal behavior when connecting to a remote server where photon is not running:
     在上面的截图里,你看到的是尝试连接本地服务器托管的Photon。OnStatusChanged:InternalReceiveException:客户端尝试连接一个不存在的服务在同一台机器上,因为windows socket引发一个异常。一个断开,导致了超时,当连接到一个远程服务器但Photon不运行时这是正常的行为:

Photon Server: Hello World Disconnect triggered after timeout

     You can try this out by simply connecting to a site you know it’s not running photon, “google.com” for instance.Connect returns false if the hostname is unknown. The state of the peer doesn’t change to connecting.
     你可以试试连接到一个网站,它不是Photon,比如“google.com”。如果主机名是未知的连接返回false。 peer的状态不会改变为连接
 
     To see the host unknown behavior, change your code as follows:
     看到主机未知的行为,改变你的代码如下:
 
        //peer.Connect("localhost:5055", "Lite")
        if (peer.Connect("xxx:5055", "Lite"))
        {
            do
            {
                Console.WriteLine(".");
                peer.Service();
                System.Threading.Thread.Sleep(500);
            }
            while (!Console.KeyAvailable);
        }
        else
            Console.WriteLine("Unknown hostname!");
 

Photon Server: Hello World Unknown Hostname

Final Demo Code 最后的演示代码

     Copy the code section below and replace the contents of your Program.cs in your project.
     复制下面的代码段和替换您的程序的内容,cs在您的项目。
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using ExitGames.Client.Photon;
 
namespace HelloWorld1
{
    class Program : IPhotonPeerListener
    {
        static void Main(string[] args)
        {
            var listener = new Program();
            var peer = new PhotonPeer(listener, ConnectionProtocol.Udp);
 
            if (peer.Connect("localhost:5055", "Lite"))
            //// if (peer.Connect("google.com:5055", "Lite"))
            //// if (peer.Connect("xxx:5055", "Lite"))
            {
                do
                {
                    Console.WriteLine(".");
                    peer.Service();
                    System.Threading.Thread.Sleep(500);
                }
                while (!Console.KeyAvailable);
            }
            else
                Console.WriteLine("Unknown hostname!");
            Console.ReadKey();
        }
 
        #region IPhotonPeerListener Members
        public void DebugReturn(DebugLevel level, string message)
        {
            //throw new NotImplementedException();
        }
 
        public void OnEvent(EventData eventData)
        {
            //throw new NotImplementedException();
        }
 
        public void OnOperationResponse(OperationResponse operationResponse)
        {
            //throw new NotImplementedException();
        }
 
        public void OnStatusChanged(StatusCode statusCode)
        {
            Console.WriteLine("OnStatusChanged:" + statusCode);
        }
        #endregion
    }
}

Hello World Part 2 第二部分

     In part 1 we introduced some basic concepts of the client API: PhotonPeer, Service, Connect, and the listener/callback design. Building on the application of part 1 (initial connection setup) we will have a look at how to use this connection to create a simple chat, where the application joins a room and sends a “Hello World!” message to the other connected clients.
     在第1部分我们介绍了一些客户端API的基本概念: PhotonPeer,服务,连接和侦听器/回调的设计。建立在应用程序的第1部分基础上(初始连接设置)我们将看看如何使用这个连接来创建一个简单的聊天,应用程序加入一个房间,发送一个“Hello World ! “消息给其他连接的客户端。
 
Content 内容
  • Overview  概述
  • Preparation: Refactoring the Main Flow 准备:重构主要的流程
  • Operations and Events 操作和事件
  • OnStatusChanged OnStatusChanged事件
  • OnOperationResponse OnOperationResponse事件
  • OnEvent OnEvent事件
  • Final Demo Code 最后的演示代码

Overview 概述

     We are going to look into two sets of concepts:
     Operations are remote procedure calls with a request and a response. An operation request consists of an OperationCode and Parameters (a Dictionary<byte, object\> containing the parameters). To send operation requests to the server OpCustom of the peer instance is called. The operation result is returned to your application in the OnOperationResponse callback.
     Note:a) Sending the request and receiving the response are decoupled and fully asynchronous. b)the operation response is optional.
     Events are messages or notifications pushed to the client. When the peer receives an event the application is notified through the OnEvent callback
     Rooms group client connections or peers and facilitate the communication between peers. Peers can join a room and send events to all peers of the room or parts of them.OpJoin is the operation called by the client to join the room.
OpRaiseEvent is the operation called to send events to other clients.
     Note: Rooms, OpJoin and OpRaiseEvent are implemented in the Lite Application. The room concept is essential to many games because this is the approach many developers take - although it's not the only one - Photon is committed to an open architecture philosophy.
     
     我们先看看两个概念:
     操作是一个远程过程调用,由一个请求和一个响应组成。一个操作请求包含一个OperationCode 和 参数(一个字典<字节,对象\ >包含了参数)。 操作请求发送到服务器peer实例的OpCustom被调用。OnOperationResponse回调时操作的结果被返回给你的应用程序中。
     注意:a)发送请求和接收响应是解耦和完全异步的。b)操作响应是可选的。
     事件是消息或通知被推送到客户端。当peer收到一个事件时,应用程序将通过OnEvent回调得到通知。 
     房间组织客户端的连接或peer并方便peer之间的交流。peer可以加入一个房间和发送事件到房间内的所有peer或其中部分。客户端加入房间时OpJoin操作被调用,将事件发送给其他客户端OpRaiseEvent操作被调用。
     注意:在Lite应用程序中实现房间,OpJoin和OpRaiseEvent。这房间的概念是至关重要的许多游戏,因为这是很多开发人员采取的方法——尽管它不是只有一个——Photon致力于一个开放式体系结构的理论研究。

Preparation: Refactoring the Main Flow 准备:重构主要流程

     We will start a new C#/Windows/Console project, name it Helloworld2, add a reference to PhotonDotNet.dll and copy the Program.cs of part 1:
     我们将开始一个新的c# / Windows /控制台项目,命名它为Helloworld2,添加一个引用PhotonDotNet.dll和复制第1部分的Program.cs:

     Next we’ll refactor the code in main as follows

  • We will move the core flow into the “Program” instance method Run (line 13).
  • Leaving a small stub of main just to create the instance and call Run (line 3).
  • Peer is declared as an instance variable and initialized in the constructor of “Program” (line 6).
     接下来我们将重构代码如下:
  • 我们将把核心流程加入到“Program”实例的Run方法(第13行)。
  • 留下一个Main的存根只是为了创建实例和调用Run (第3行)。
  • Peer是在“Program”的构造函数里声明为一个实例变量并初始化(第6行)。   
 
static void Main(string[] args)
{
new Program().Run();
}
PhotonPeer peer;
public Program()
{
peer = new PhotonPeer(this, ConnectionProtocol.Udp);
}
void Run()
{
if (peer.Connect("localhost:5055", "Lite"))
{
do
{
//Console.WriteLine(".");
peer.Service();
System.Threading.Thread.Sleep(500);
}
while (!Console.KeyAvailable);
}
else
Console.WriteLine("Unknown hostname!");
Console.WriteLine("Press any key to end program!");
Console.ReadKey();
}
     Note: in line 19 we commented the "." output. Optionally you can replace it by Debug.Write("."); to make sure your loop is working. To see this check the output-tab in your Visual Studio.
     注意:在第19行我们评论“.“的输出。选择你可以取代它通过调试写(“.”),确保你的循环工作。看到这检查你在Visual Studio的输出选项卡。

Operations and Events 操作和事件

     Now we are done with the preparation work we can start to see how we send an operation request to the server.
     现在我们已经完成了准备工作,我们可以开始看看我们是如何将一个操作发送请求到服务器。
 
     The flow we are implementing once we initialized the connection:
    • When status changes to connected: send the operation join request to join the room “MyRoomName”. (in OnStatusChanged)
    • When receiving the result “join ok”: send the operation raise event request with the event code 101 and a “Hello World” message. (in OnOperationResponse)
     当我们初始化连接时流程就会被实施:
    • 当状态更改为连接:发送操作加入请求,加入房间”MyRoomName”。 (在 OnStatusChanged )
    • 当接收到结果为“join ok”:发送操作触发事件的请求,事件代码101和一个“Hello World”消息。 (在 OnOperationResponse )
     Note: usually you would define event codes centrally for your application in an enumeration for instance. Because we are using Lite features the codes 255-251 (Join, Leave, SetProperties, GetProperties) are predefined.
     注意:通常你会定义事件代码集在应用程序的枚举实例中。例如, 因为我们使用Lite特性编码255 - 251(Join, Leave, SetProperties, GetProperties)是预定义的。
    • When receiving an event with the code 101: print the received message to the console. (in OnEvent)
    • 当收到一个事件代码101:打印接收到的消息到控制台里。 (在 OnEvent )

OnStatusChanged OnStatusChanged事件

     To start with the implementation of the flow we described above the first change to the code we’ll be to add a switch for the statusCode we get notified with and a case block for the StatusCode.Connect to send an OperationRequest to join a room with the name “MyRoomName” (lines 6-11):

  • where we create a hashtable opParams containing the parameters (lines 8+9).
  • and call peer.OpCustom with the OperationCode and passing in the opParams Dictionary<byte, object\> (line 10).
     首先实现上面描述的流程,我们的第一个要变化的代码是我们将添加一个当做开关的statusCode,我们得到的通知是StatusCode.Connect块发送一个OperationRequest加入一个叫做“MyRoomName”的房间 (行6-11):
  • 我们创建一个哈希表包含参数opParams (第8行+ 9)。
  • 和调用peer.OpCustom 与 OperationCode 并传递这个opParams字典<字节,对象\ >(第10行)。
 
public void OnStatusChanged(StatusCode statusCode)
{
Console.WriteLine("\n---OnStatusChanged:" + statusCode);
switch (statusCode)
{
case StatusCode.Connect:
Console.WriteLine("Calling OpJoin ...");
Dictionary<Byte, Object>opParams = new Dictionary<Byte,Object>();
opParams[(byte)LiteOpKey.GameId] = "MyRoomName";
peer.OpCustom((byte)LiteOpCode.Join, opParams, true);
break;
default:
break;
}
}
   

OnOperationResponse OnOperationResponse事件

     For an easy way to print out readable operation codes we will define the following enum:
     对于一个简单的方法来打印可读的操作码,我们将定义为以下的枚举:
 
 
enum OpCodeEnum : byte
{
Join = 255,
Leave = 254,
RaiseEvent = 253,
SetProperties = 252,
GetProperties = 251
}
 
     Next we change the OnOperationResponse callback as follows:
    1. We check the operationResponse.ReturnCode if it failed (not 0) we exit the method (lines 3,8).
    2. Add a switch-case for OperationCode (operationResponse.OperationCode) LiteOpCode.Join (lines 11,13)
    3. We create a RaiseEvent operation request (LiteOpCode.RaiseEvent). With the parameters LiteOpKey.Code = 101 and LiteOpKey.Data = “Hello World” (our message) (lines 18-21).
     接下来,我们改变OnOperationResponse的回调如下:
  1. 我们检查 operationResponse.ReturnCode 如果它失败了(不是0) 我们退出方法(行3-8)。
  2. 添加一个开关例子为OperationCode ( operationResponse.OperationCode ) LiteOpCode.Join (行11-13)
  3. 我们创建一个RaiseEvent操作请求( LiteOpCode.RaiseEvent )。参数LiteOpKey.Code= 101和LiteOpKey.Data= " Hello World”(我们的消息)(第18 - 21行)。
 
public void OnOperationResponse(OperationResponse operationResponse)
{
if (operationResponse.ReturnCode == 0)
Console.WriteLine("\n---OnOperationResponse: OK - " + (OpCodeEnum)operationResponse.OperationCode + "(" + operationResponse.OperationCode + ")");
else
{
Console.WriteLine("\n---OnOperationResponse: NOK - " + (OpCodeEnum)operationResponse.OperationCode + "(" + operationResponse.OperationCode + ")\n ->ReturnCode=" + operationResponse.ReturnCode + " DebugMessage=" + operationResponse.DebugMessage);
return;
}
switch (operationResponse.OperationCode)
{
case (byte)LiteOpCode.Join:
int myActorNr = (int)operationResponse.Parameters[LiteOpKey.ActorNr];
Console.WriteLine(" ->My PlayerNr (or ActorNr) is:" + myActorNr);
Console.WriteLine("Calling OpRaiseEvent ...");
Dictionary<byte, object> opParams = new Dictionary<byte, object>();
opParams[LiteOpKey.Code] = (byte)101;
opParams[LiteOpKey.Data] = "Hello World!";
peer.OpCustom((byte)LiteOpCode.RaiseEvent, opParams, true);
break;
}
}
 
     When you run this code the ouput will look like this:
     当您运行这个代码,输出将看起来像这样:
 
C:\...\HelloWorld2\bin\Debug>HelloWorld2.exe
---OnStatusChanged:Connect
Calling OpJoin ...
---OnOperationResponse: OK - Join(90)
->My PlayerNr (or ActorNr) is:1
Calling OpRaiseEvent ...
---OnOperationResponse: NOK - RaiseEvent(253)
->ReturnCode=-1 DebugMessage=Wrong parameter type 245
(RaiseEventRequest.Data): should be Hashtable but received String
 
     As you can see the first operation (Join) returned OK. It also returned the actor number 1 (->My PlayerNr (or ActorNr) is:1). If you start a second Helloworld2.exe you will see a “2” instead. The next client would get a “3”.
     正如您可以看到的第一个操作( 加入 )返回OK。它还返回1号的actor ( - >我的PlayerNr(或ActorNr)是:1 )。如果你开始运行第二个Helloworld2.exe,你将看到一个“2”。接下来的客户端将会得到一个“3”。
 
     Note: starting and stopping clients might lead to higher actor No. values. The server recognizes the disconnect after a certain timeout and removes those peers. Usually, a client would send a disconnect on shutdown - which is the recommended way, so others "see" faster that a peer disconnected - a peer removed from the room triggers a leave event broadcasting it to all peers connected.
     注:启动和停止客户端可能会导致更多的actor没有值。服务器断开后识别到某些超时并删除那些Peer。通常,客户端在关闭时会发送一个断开——这是被推荐的方式,所以其他人更快的“看到” peer断开连接——一个peer离开房间会触发一个离开事件并广播它给所有连接着的peer。
 
     The second operation OpRaiseEvent failed. This is because the parameter Data is expected to be a hashtable. So we will change our code as follows (lines 10-13):
     第二个操作OpRaiseEvent失败了。这是因为参数数据预计将是一个散列表。所以我们将改变我们的代码去遵循(第10行):
 
 
switch (operationResponse.OperationCode)
{
case LiteOpCode.Join:
int myActorNr = (int)operationResponse.Parameters[LiteOpKey.ActorNr];
Console.WriteLine(" ->My PlayerNr (or ActorNr) is:" + myActorNr);
Console.WriteLine("Calling OpRaiseEvent ...");
Dictionary<byte, object> opParams = new Dictionary<byte, object>();
opParams[LiteOpKey.Code] = (byte)101;
//opParams[LiteOpKey.Data] = "Hello World!"; //<- returns an error, server expects a hashtable
Hashtable evData = new Hashtable();
evData[(byte)1] = "Hello Wolrd!";
opParams[LiteOpKey.Data] = evData;
peer.OpCustom((byte)LiteOpCode.RaiseEvent, opParams, true);
break;
}
 
     When running the code with the changes we just made you will notice the OnOperationResponse for RaiseEvent doesn’t appear anymore - this happens, because the server only sends a response in case of an error!
     当运行代码发生变化时,我们只是让你注意到 OnOperationResponse 对于 RaiseEvent 不出现了——发生这一切是因为服务器在出错的情况下只发送一个响应!
 
     Note: The key (byte)1 of evData has nothing to do with the parameter opParams[LiteOpKey.Code] beeing (byte)101(line 9).
     Hint: It's recommended to use either byte or short keys in the evData hashtable, because events tend to be sent very often.
     注意:Key(字节)1的evData没有参数opParams[LiteOpKey.Code]可用(字节)101(第9行)。
     提示:这是推荐要么使用byte,要么使用短的Key在evData哈希表,这是因为事件往往会经常发送。

OnEvent OnEvent事件

     For an easy way to print out readable operation codes we will define the following Enum:
     用一个简单的方法来打印可读的操作码,我们将定义以下枚举:
 
 
enum EvCodeEnum : byte
{
Join = 255,
Leave = 254,
PropertiesChanged = 253
}       
 
     The last change we’re making is to display the message we receive in the eventData when we receive the event with code = 101:
     最后,当我们接收到事件eventData与code = 101的时候,在eventData里显示我们收到的消息:
 
 
public void OnEvent(EventData eventData)
{
Console.WriteLine("\n---OnEvent: " + (EvCodeEnum)eventData.Code + "(" + eventData.Code + ")");
switch (eventData.Code)
{
case 101:
int sourceActorNr = (int)eventData.Parameters[LiteEventKey.ActorNr];
Hashtable evData = (Hashtable)eventData.Parameters[LiteEventKey.Data];
Console.WriteLine(" ->Player" + sourceActorNr + " say's: " + evData[(byte)1]);
break;
}
}
 
     If you launch two clients you’ll now see the following:
     如果您启动两个客户你现在将看到如下:

     the first client: 第一个客户端

C:\...\HelloWorld2\bin\Debug>HelloWorld2.exe
---OnStatusChanged:Connect
Calling OpJoin ...
---OnOperationResponse: OK - Join(255)
->My PlayerNr (or ActorNr) is:1
Calling OpRaiseEvent ...
---OnEvent: Join(255)
->Player1 joined!
->Total num players in room:1, Actornr List: 1,
---OnEvent: Join(255)
->Player2 joined!
->Total num players in room:2, Actornr List: 1,2,
---OnEvent: 101(101)
->Player2 say's: Hello Wolrd!

     the second client: 第二个客户端

C:\...\HelloWorld2\bin\Debug>HelloWorld2.exe
---OnStatusChanged:Connect
Calling OpJoin ...
---OnOperationResponse: OK - Join(255)
->My PlayerNr (or ActorNr) is:2
Calling OpRaiseEvent ...
---OnEvent: Join(255)
->Player2 joined!
->Total num players in room:2, Actornr List: 1,2,
 
     The first client receives 3 events:
    1. the join event triggered by its own join.
    2. the join event triggered by the joining of the second client.
    3. the 101 event the second client sent to all others in the room
     第一个客户端接收3个事件:
  1. join事件被自己的加入所触发。
  2. join事件被第二个加入的客户端所触发。
  3. 101事件,第二个客户端发送给房间里的所有其他人。
     all others is the default behavior of OpRaisEvent (for more details see SDK documentation of LitePeer).The second client only receives one join event (triggered by its own join).
     所有其他人的事件触发是OpRaisEvent的默认行为 (更多细节 看到SDK文档的LitePeer)。第二个客户端只收到一个加入事件(被自己的加入触发)。
 
     In this tutorial, we only used PhotonPeer. The client SDK also includes a LitePeer to ease the use of Lite features. It extends the PhotonPeer, so when joining a room with LitePeer you only need to call peer.OpJoin(“MyRoomName”). To see how this works just replace PhotonPeer by LitePeer. Another powerful Lite feature worth looking into is Properties. In Lite you can set room and player (or actor) properties. As an example you could set initial configuration values (e.g. the game map, difficulty, …) as room properties or the players nicknames as actor properties (for more details see the client SDK documentation).
     在本教程中,我们只使用了PhotonPeer。客户端SDK还包括了一个LitePeer为了更方便的使用Lite。它扩展了PhotonPeer,所以当LitePeer加入一个房间,你只需要调用peer.OpJoin (“MyRoomName”)。接下来看看PhotonPeer接替LitePeer是如何工作的。另一个强大的Lite特性值得研究的是属性。在Lite可以设置房间和玩家的属性。作为一个示例,您可以设定初始配置值(例如游戏地图,困难,…)作为房间属性或玩家昵称作为玩家的属性(详情见客户端SDK文档)。

Final Demo Code 最后的演示代码

     In the code that follows we’ve added a couple of lines to increase debug-output, which should help you to dig deeper into the details of how Photon and Lite work.
     下列代码所示,我们已经添加了一对调试输出,这应该有助于您更深入的理解Photon和Lite的工作细节。
 
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using ExitGames.Client.Photon;
using ExitGames.Client.Photon.Lite;
using System.Diagnostics;
using System.Collections;
 
namespace HelloWorld2
{
    class Program : IPhotonPeerListener
    {
        static void Main(string[] args)
        {
            new Program().Run();
        }
 
        PhotonPeer peer;
 
        public Program()
        {
            peer = new PhotonPeer(this, ConnectionProtocol.Udp);
        }
 
        void Run()
        {
            //DebugLevel should usally be ERROR or Warning - ALL lets you "see" more details of what the sdk is doing.
            //Output is passed to you in the DebugReturn callback
            peer.DebugOut = DebugLevel.ALL;
            if (peer.Connect("localhost:5055", "Lite"))
            {
                do
                {
                    Debug.Write("."); //allows you to "see" the game loop is working, check your output-tab when running from within VS
                    peer.Service();
                    System.Threading.Thread.Sleep(50);
                }
                while (!Console.KeyAvailable);
            }
            else
                Console.WriteLine("Unknown hostname!");
            Console.WriteLine("Press any key to end program!");
            Console.ReadKey();
            //peer.Disconnect(); //<- uncomment this line to see a faster disconnect/leave on the other clients.
        }
 
        #region IPhotonPeerListener Members
        public void DebugReturn(DebugLevel level, string message)
        {
            // level of detail depends on the setting of peer.DebugOut
            Debug.WriteLine("\nDebugReturn:" + message); //check your output-tab when running from within VS
        }
 
        public void OnOperationResponse(OperationResponse operationResponse)
        {
            if (operationResponse.ReturnCode == 0)
                Console.WriteLine("\n---OnOperationResponse: OK - " + (OpCodeEnum)operationResponse.OperationCode + "(" + operationResponse.OperationCode + ")");
            else
            {
                Console.WriteLine("\n---OnOperationResponse: NOK - " + (OpCodeEnum)operationResponse.OperationCode + "(" + operationResponse.OperationCode + ")\n ->ReturnCode=" + operationResponse.ReturnCode
                  + " DebugMessage=" + operationResponse.DebugMessage);
                return;
            }
 
            switch (operationResponse.OperationCode)
            {
                case LiteOpCode.Join:
                    int myActorNr = (int)operationResponse.Parameters[LiteOpKey.ActorNr];
                    Console.WriteLine(" ->My PlayerNr (or ActorNr) is:" + myActorNr);
 
                    Console.WriteLine("Calling OpRaiseEvent ...");
                    Dictionary<byte, object> opParams = new Dictionary<byte, object>();
                    opParams[LiteOpKey.Code] = (byte)101;
                    //opParams[LiteOpKey.Data] = "Hello World!"; //<- returns an error, server expects a hashtable
 
                    Hashtable evData = new Hashtable();
                    evData[(byte)1] = "Hello Wolrd!";
                    opParams[LiteOpKey.Data] = evData;
                    peer.OpCustom((byte)LiteOpCode.RaiseEvent, opParams, true);
                    break;
            }
        }
 
        public void OnStatusChanged(StatusCode statusCode)
        {
            Console.WriteLine("\n---OnStatusChanged:" + statusCode);
            switch (statusCode)
            {
                case StatusCode.Connect:
                    Console.WriteLine("Calling OpJoin ...");
                    Dictionary<byte, object> opParams = new Dictionary<byte, object>();
                    opParams[LiteOpKey.GameId] = "MyRoomName";
                    peer.OpCustom((byte)LiteOpCode.Join, opParams, true);
                    break;
                default:
                    break;
            }
        }
 
        public void OnEvent(EventData eventData)
        {
            Console.WriteLine("\n---OnEvent: " + (EvCodeEnum)eventData.Code + "(" + eventData.Code + ")");
 
            switch (eventData.Code)
            {
                case LiteEventCode.Join:
                    int actorNrJoined = (int)eventData.Parameters[LiteEventKey.ActorNr];
                    Console.WriteLine(" ->Player" + actorNrJoined + " joined!");
 
                    int[] actorList = (int[])eventData.Parameters[LiteEventKey.ActorList];
                    Console.Write(" ->Total num players in room:" + actorList.Length + ", Actornr List: ");
                    foreach (int actorNr in actorList)
                    {
                        Console.Write(actorNr + ",");
                    }
                    Console.WriteLine("");
                    break;
 
                case 101:
                    int sourceActorNr = (int)eventData.Parameters[LiteEventKey.ActorNr];
                    Hashtable evData = (Hashtable)eventData.Parameters[LiteEventKey.Data];
                    Console.WriteLine(" ->Player" + sourceActorNr + " say's: " + evData[(byte)1]);
                    break;
            }
        }
 
        #endregion
    }
 
    enum OpCodeEnum : byte
    {
        Join = 255,
        Leave = 254,
        RaiseEvent = 253,
        SetProperties = 252,
        GetProperties = 251
    }
 
    enum EvCodeEnum : byte
    {
        Join = 255,
        Leave = 254,
        PropertiesChanged = 253
    }
}
posted on 2015-11-25 11:27 思月行云 阅读(508) 评论(0)  编辑 收藏 引用 所属分类: Photon

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理