Photon Server与Unity3D的交互分为3篇博文实现

  (1)Photon Server的服务器端配置

  (2)Photon Server的Unity3D客户端配置

  (3)Photon Server与Unity3D客户端的交互

1.客户端向服务器端发送请求

PhotonEngine.Peer.OpCustom(byte customOpCode, Dictionary<byte, object> customOpParameters, bool sendReliable);

  在客户端任何位置都能调用此方法。

  customOpCode,请求类型,不推荐直接传数字,用枚举类型enum。

  customOpParameters,传递的数据是Dictionary<byte, object>类型,object类是所有类的父类。

  sendReliable,是否使用稳定的连接。

  示例:

  登录的时候客户端向服务器端发送用户名跟密码。本示例创建一个Common类库存放客户端与服务器端都需要的一些枚举类、工具类。每次Common修改都要重新生成dll文件重新添加到客户端跟服务器端。

namespace Common
{
//客户端请求类型。枚举类型默认时是int,-2147483648~2147483647,byte 是 0~255之间的整数
public enum OperationCode:byte
{
Login,
Register,
Default,
SyncPosition,
SyncPlayer
}
}
namespace Common
{
//数据Dictionary<byte, object> customOpParameters里object数据的类型
public enum ParameterCode:byte
{
Username,
Password,
Position,
X,Y,Z,
UsernameList,
PlayerDataList
}
}
//客户端向服务器端发送用户名跟密码请求登录
Dictionary<byte, object> opParameters = new Dictionary<byte, object>();
opParameters.Add((byte)ParameterCode.Username, usename);
opParameters.Add((byte)ParameterCode.Password, password);
PhotonEngine.Peer.OpCustom((byte)OperationCode.Login, opParameters, true);

2.服务器端接收客户端的请求

protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)

  在服务器端ClientPeer类里的OnOperationRequest里处理客户端的请求。每一个客户端连接进来都会创建一个ClientPeer,并且Photon Server为我们管理好它。

  operationRequest.OperationCode可以得到客户端传过来的byte customOpCode。

  operationRequest.Parameters可以得到客户端传过来的Dictionary<byte, object> customOpParameters。

  opParameters.TryGetValue((byte)ParameterCode.Username, out object username)可以得到operationRequest.Parameters里面对应的数据。

  sendParameters是发送请求的一些相关属性。

SendOperationResponse(OperationResponse operationResponse, SendParameters sendParameters);

  响应客户端需要调用SendOperationResponse方法,只能在ClientPeer类里的OnOperationRequest方法里调用。

  OperationResponse operationResponse = new OperationResponse(operationRequest.OperationCode);

  operationResponse.SetParameters(newCustomOpParameters)可以设置Parameters。

  operationResponse.ReturnCode可以设置一个short返回值对应一种请求结果,用枚举类型enum。

  示例:

namespace Common
{
public enum ReturnCode:short
{
Success,
Failed
}
}
using Common;
namespace MinecraftServer
{
//每一个客户端连接进来都会创建一个ClientPeer
public class ClientPeer : Photon.SocketServer.ClientPeer
{
     //处理客户端的请求
protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)
{
       //不建议直接使用switch,推荐把每一种请求的处理封装成类
       switch (operationRequest.OperationCode)
{
case (byte)OperationCode.Login:
           //接收客户端请求
Dictionary<byte, object> opParameters1= operationRequest.Parameters;
opParameters1.TryGetValue((byte)ParameterCode.Username, out object username);
opParameters1.TryGetValue((byte)ParameterCode.Password, out object password);
MyGameServer.log.Info("得到的参数是" + username.ToString() + "和" + password.ToString());             //此处省略判断用户名密码是否正确的操作 //响应客户端用SendOperationResponse,只能在OnOperationRequest方法里调用
OperationResponse operationResponse = new OperationResponse(operationRequest.OperationCode);
            operationResponse.ReturnCode = (short)ReturnCode.Success;
            //Dictionary<byte, object> opParameters2 = new Dictionary<byte, object>();
            //opParameters2.Add(,);
            //opParameters2.Add(,);
            //operationResponse.SetParameters(opParameters2);
SendOperationResponse(operationResponse, sendParameters);
            break;
case (byte)OperationCode.Register:
break;
default:
break;
}
}
}
}

3.客户端接收服务器端的响应

public void OnOperationResponse(OperationResponse operationResponse)

  在客户端的PhotonEngine类里的OnOperationResponse类里接收服务器端的响应。

  operationResponse.OperationCode可以得到operationResponse构造函数里的operationRequest.OperationCode。

  operationResponse.Parameters可以得到服务器端operationResponse.SetParameters(opParameters2)的opParameters2。

  opParameters.TryGetValue((byte)ParameterCode.Username, out object username)可以得到operationResponse.Parameters里面对应的数据。

  operationResponse.ReturnCode可以得到服务器端operationResponse.ReturnCode=(short)ReturnCode.Success的(short)ReturnCode.Success。

4.服务器端向客户端发送事件

  peer.SendEvent向客户端peer发送事件,可以在任何地方调用。

EventData eventData = new EventData((byte)EventCode.SyncPosition);
Dictionary<byte, object> opParameters = new Dictionary<byte, object>();
opParameters.Add(,);
opParameters.Add(,);
eventData.Parameters = opParameters;
peer.SendEvent(eventData, new SendParameters());

5.客户端接收服务器端发送的事件 

public void OnEvent(EventData eventData)

  在PhotonEngine类的OnEvent方法接收服务器端发送的事件。

  eventData.Code可以得到eventData构造函数里的(byte)EventCode.SyncPosition。

  eventData.Parameters可以得到服务器端eventData.Parameters。

  opParameters.TryGetValue((byte)ParameterCode.Username, out object username)可以得到operationResponse.Parameters里面对应的数据。

6.数据的序列化跟反序列化

  序列化

  一般序列化List<T> list。如果有多种数据,新建一个类例如Player,将人物的各种属性字段赋值后存入list。然后序列化list

List<PlayerData> playDataList = new List<PlayerData>(); StringWriter sw = new StringWriter();
XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<PlayerData>));
xmlSerializer.Serialize(sw, playDataList);
sw.Close();
string playDataListString = sw.ToString();

  反序列化

using (StringReader sr = new StringReader(playerDataListString))
{
  XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<PlayerData>));
  List<PlayerData> playerDataList = (List<PlayerData>)xmlSerializer.Deserialize(sr);
}

7.将请求、事件封装类

  (1)把客户端的请求封装成类,里面包括请求的OperationCode、发送请求和接收响应的方法。

public abstract class ARequest:MonoBehaviour{
public OperationCode OpCode;//设置public自动识别在Inspector里面,并且会识别枚举类型选择
public abstract void DefaultRequest();
public abstract void OnOperationResponse(OperationResponse operationResponse); public virtual void Start()
{
PhotonEngine.Instance.AddRequest(this);
} public void OnDestroy()
{
PhotonEngine.Instance.RemoveRequest(this);
}
} public class LoginRequest : ARequest { [HideInInspector]
public string usename;
[HideInInspector]
public string password; private LoginPanel loginPanel; public override void Start()
{
base.Start();
loginPanel=GetComponent<LoginPanel>();
} public override void DefaultRequest()
{
Dictionary<byte, object> data = new Dictionary<byte, object>();
data.Add((byte)ParameterCode.Username, usename);
data.Add((byte)ParameterCode.Password, password); PhotonEngine.Peer.OpCustom((byte)OpCode,data, true);
}
public override void OnOperationResponse(OperationResponse operationResponse)
{
ReturnCode returnCode= (ReturnCode)operationResponse.ReturnCode;
loginPanel.OnLoginResponse(returnCode);
}
}

  (2)把服务器端处理客户端请求封装成类,里面包括请求的OperationCode和处理请求的方法。

public abstract class AHandler
{
public OperationCode opCode;
public abstract void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, ClientPeer peer);
}
class LoginHandler : AHandler
{
public LoginHandler()
{
opCode = OperationCode.Login;
} public override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, ClientPeer peer)
{
operationRequest.Parameters.TryGetValue((byte)ParameterCode.Username, out object username);
operationRequest.Parameters.TryGetValue((byte)ParameterCode.Password, out object password); UserManager userManager = new UserManager();
bool isSuccess = userManager.Vertify(username.ToString(), password.ToString()); OperationResponse operationResponse = new OperationResponse(operationRequest.OperationCode);
if (isSuccess)
{
operationResponse.ReturnCode = (short)ReturnCode.Success;
peer.username = username.ToString();
}
else
{
operationResponse.ReturnCode = (short)ReturnCode.Failed;
} peer.SendOperationResponse(operationResponse, sendParameters);
}
}

  (3)服务器端发送事件有2种情况,一种是处理客户端请求时发送,二是需要不断同步的数据由线程来发送。

  把客户端接收服务器事件封装成类,里面包括事件的EventCode和处理事件的方法。

public abstract class AEvent : MonoBehaviour {

    public EventCode eventCode;//设置public自动识别在Inspector里面,并且会识别枚举类型选择
public abstract void OnEvent(EventData eventData); public virtual void Start()
{
PhotonEngine.Instance.AddEvent(this);
} public void OnDestroy()
{
PhotonEngine.Instance.RemoveEvent(this);
}
} public class NewPlayerEvent :AEvent{ private SyncController player; public override void Start()
{
base.Start();
player = GetComponent<SyncController>();
} public override void OnEvent(EventData eventData)
{
object username;
eventData.Parameters.TryGetValue((byte)ParameterCode.Username, out username);
player.OnNewPlayerEvent(username.ToString());
}
}

  (4)对应的客户端的PhotonEngine、服务器端ClientPeer修改

public class PhotonEngine : MonoBehaviour, IPhotonPeerListener
{ //存放所有可能出现的请求和事件
private Dictionary<OperationCode, ARequest> RequestDict = new Dictionary<OperationCode, ARequest>();
private Dictionary<EventCode, AEvent> EventDict = new Dictionary<EventCode, AEvent>(); //服务器端向客户端发送请求时调用
public void OnEvent(EventData eventData)
{
AEvent tempEvent;
EventDict.TryGetValue((EventCode)eventData.Code, out tempEvent);
tempEvent.OnEvent(eventData);
} //客户端向服务器端发送请求后,服务器端响应客户端时调用
public void OnOperationResponse(OperationResponse operationResponse)
{
ARequest tempRequest;
bool temp = RequestDict.TryGetValue((OperationCode)operationResponse.OperationCode, out tempRequest); if (temp)
{
request.OnOperationResponse(operationResponse);
}
else
{
Debug.Log("没找到对应的响应对象");
}
} public void AddRequest(ARequest request)
{
RequestDict.Add(request.OpCode, request);
}
public void RemoveRequest(ARequest request)
{
RequestDict.Remove(request.OpCode);
}
public void AddEvent(AEvent tempEvent)
{
EventDict.Add(tempEvent.eventCode, tempEvent);
}
public void RemoveEvent(AEvent tempEvent)
{
EventDict.Remove(tempEvent.eventCode);
}
}
public class ClientPeer : Photon.SocketServer.ClientPeer
{
public float x, y, z;
public string username; public ClientPeer(InitRequest initRequest) : base(initRequest)
{
MyGameServer.Instance.peerList.Add(this);
} //处理客户端断开连接的后续工作
protected override void OnDisconnect(DisconnectReason reasonCode, string reasonDetail)
{
MyGameServer.Instance.peerList.Remove(this);
} //处理客户端的请求
protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)
{
MyGameServer.Instance.HandleDict.TryGetValue((OperationCode)operationRequest.OperationCode, out AHandler handler);
if (handler != null)
{
handler.OnOperationRequest(operationRequest, sendParameters, this);
}
else
{
AHandler defaultHandler = DictTool.GetValue<OperationCode, AHandler>(MyGameServer.Instance.HandleDict, OperationCode.Default);
defaultHandler.OnOperationRequest(operationRequest, sendParameters, this);
}
}
}

8.线程

  服务器持续向服务器端发送事件时需要用到线程,下面是线程简单的应用。

public class SyncPositionThread
{
private Thread t;
public void Run()
{
t = new Thread(UpdatePosition);
t.IsBackground = true;
t.Start();
} public void Stop()
{
t.Abort();
} private void UpdatePosition()
{
Thread.Sleep(5000);
while (true)
{
Thread.Sleep(50);
//执行上传位置
SendPosition();
}
} private void SendPosition()
{
//如果没有客户端连接上则不用传数据
if (MyGameServer.Instance.peerList.Count == 0)
return; //把所有客户端的位置装箱到playerDataList
List<PlayerData> playDataList = new List<PlayerData>();
foreach (ClientPeer peer in MyGameServer.Instance.peerList)
{
if (string.IsNullOrEmpty(peer.username) == false)
{
PlayerData playerData = new PlayerData();
playerData.Username = peer.username;
playerData.Position = new Vector3Data() { X = peer.x, Y = peer.y, Z = peer.z };
playDataList.Add(playerData);
}
} //如果有客户端登录但是还没连接上,也不用传数据
if (playDataList.Count == 0)
return; //playDataList序列化为playDataListString然后放在data里面
StringWriter sw = new StringWriter();
XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<PlayerData>));
xmlSerializer.Serialize(sw, playDataList);
sw.Close();
string playDataListString = sw.ToString();
Dictionary<byte, object> data = new Dictionary<byte, object>();
data.Add((byte)ParameterCode.PlayerDataList, playDataListString); //playDataListString发送到各个客户端
foreach (ClientPeer peer in MyGameServer.Instance.peerList)
{
if (string.IsNullOrEmpty(peer.username) == false)
{
EventData eventData = new EventData((byte)EventCode.SyncPosition);
eventData.Parameters = data;
peer.SendEvent(eventData, new SendParameters());
}
} }
}

Photon Server与Unity3D客户端的交互的更多相关文章

  1. Photon Server的Unity3D客户端配置

    Photon Server与Unity3D的交互分为3篇博文实现 (1)Photon Server的服务器端配置 (2)Photon Server的Unity3D客户端配置 (3)Photon Ser ...

  2. Photon Server初识(六) --- 客户端与服务端消息传递

    前一章客户端与服务端连接成功,现在需要前后端进行数据传递. 一.前端发送消息.在项目Scripts目录中新建脚本 TestSer.cs.并挂载到相机上 二.客户端发送数据给服务端.编辑客户端代码 Te ...

  3. Photon Server的服务器端配置

    Photon Server与Unity3D的交互分为3篇博文实现 (1)Photon Server的服务器端配置 (2)Photon Server的Unity3D客户端配置 (3)Photon Ser ...

  4. Photon Server 服务端编程

    Photon Server 和 Unity3D 数据交互: Photon Server 服务端编程 Unity3D 客户端编程 VS2017 之 MYSQL实体数据模 一:Photon Server的 ...

  5. Unity3D 客户端编程

    Photon Server 和 Unity3D 数据交互: Photon Server 服务端编程 Unity3D 客户端编程. VS2017 之 MYSQL实体数据模 1:打开unity新建新项目, ...

  6. Unity3d客户端与Photon服务器数据通信

    今天先介绍一下Photon服务器是什么,可以做什么,为什么要使用它? Photon:开发多人联网游戏最轻松的方案!可以迅速简单实现多人实时在线网络游戏(pvp). Photon:透过位于各地的Phot ...

  7. 看过自会理解, Photon Server 常见概念分析.

    http://stackoverflow.com/questions/10823915/photon-server-newbie-questions/11653419#11653419 Channel ...

  8. 开源分享 Unity3d客户端与C#分布式服务端游戏框架

    很久之前,在博客园写了一篇文章,<分布式网游server的一些想法语言和平台的选择>,当时就有了用C#做网游服务端的想法.写了个Unity3d客户端分布式服务端框架,最近发布了1.0版本, ...

  9. coTurn 运行在Windows平台的方法及服务与客户端运行交互流程和原理

    coTurn是一个开源的STUN和TURN及ICE服务项目,只是不支持Windows.为了在window平台上使用coTurn源码,需要在windows平台下安装Cygwin环境,并编译coTurn源 ...

随机推荐

  1. Ubuntu 图形桌面死机重启(机器不重启)

    Ubuntu的图形界面容易死机,如果正在跑程序的话又不能重启.这时候可以通过终端来_重启_图形界面. 首先按Alt+Ctrl+F1进入终端界面.查看图形界面的进程: ps -t tty7 查看到名为X ...

  2. vs code 自定义代码片段

    1.快捷键Ctrl+Shift+P打开命令输入 snippet : (也可以点击文件=>首选项=>用户代码片段) 2.选择代码语言类型(这里以JavaScrpt为例) 3.在javascr ...

  3. SparkSQL的一些用法建议和Spark的性能优化

    1.写在前面 Spark是专为大规模数据处理而设计的快速通用的计算引擎,在计算能力上优于MapReduce,被誉为第二代大数据计算框架引擎.Spark采用的是内存计算方式.Spark的四大核心是Spa ...

  4. Robot Framework:环境安装

    Windows Python2.7                                                前置条件:安装python2.7,下载地址:https://www.p ...

  5. Luogu P2042 [NOI2005]维护数列

    题目描述 请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线' _ '表示实际输入文件中的空格) 输入输出格式 输入格式: 输入文件的第 1 行包含两个数 N 和 M, ...

  6. BZOJ 1303: [CQOI2009]中位数图(思路题)

    传送门 解题思路 比较好想的思路题.首先肯定要把原序列转化一下,大于\(k\)的变成\(1\),小于\(k\)的变成\(-1\),然后求一个前缀和,还要用\(cnt[]\)记录一下前缀和每个数出现了几 ...

  7. Bootstrap 避免模态框在用户点击背景空白处时,会自动关闭。

    问题: Bootstrap 模态框在用户点击背景空白处时,会自动关闭. 解决方法: 在HTML页面中编写模态框时,在div初始化时添加属性 aria-hidden=”true” data-backdr ...

  8. eclipse spring boot 项目出现java.lang.ClassCastException 解决方法

    问题 eclipse spring boot 项目出现java.lang.ClassCastException 解决方法: 重新生成项目

  9. Cuckoo架构

    cuckoo在部署阶段,只在Guest系统里塞了一个agent,这个agent在运行阶段负责与Host端程序进行通信,从Host端接收sample, 整个客户端程序,以及配置文件. 在Host端主要的 ...

  10. Linux用户空间内存区域的匿名映射

    1 在调用mmap系统调用时,可以指定的标志(flag)参数: 1: #define MAP_SHARED 0x01 /* Share changes */ 2: #define MAP_PRIVAT ...