Photon Server与Unity3D客户端的交互

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()); } } } }

 

转载于:https://www.cnblogs.com/DonYao/p/8558466.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值