Photon Server与Unity3D客户端的交互
Photon Server与Unity3D的交互分为3篇博文实现
(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客户端的交互的更多相关文章
- Photon Server的Unity3D客户端配置
Photon Server与Unity3D的交互分为3篇博文实现 (1)Photon Server的服务器端配置 (2)Photon Server的Unity3D客户端配置 (3)Photon Ser ...
- Photon Server初识(六) --- 客户端与服务端消息传递
前一章客户端与服务端连接成功,现在需要前后端进行数据传递. 一.前端发送消息.在项目Scripts目录中新建脚本 TestSer.cs.并挂载到相机上 二.客户端发送数据给服务端.编辑客户端代码 Te ...
- Photon Server的服务器端配置
Photon Server与Unity3D的交互分为3篇博文实现 (1)Photon Server的服务器端配置 (2)Photon Server的Unity3D客户端配置 (3)Photon Ser ...
- Photon Server 服务端编程
Photon Server 和 Unity3D 数据交互: Photon Server 服务端编程 Unity3D 客户端编程 VS2017 之 MYSQL实体数据模 一:Photon Server的 ...
- Unity3D 客户端编程
Photon Server 和 Unity3D 数据交互: Photon Server 服务端编程 Unity3D 客户端编程. VS2017 之 MYSQL实体数据模 1:打开unity新建新项目, ...
- Unity3d客户端与Photon服务器数据通信
今天先介绍一下Photon服务器是什么,可以做什么,为什么要使用它? Photon:开发多人联网游戏最轻松的方案!可以迅速简单实现多人实时在线网络游戏(pvp). Photon:透过位于各地的Phot ...
- 看过自会理解, Photon Server 常见概念分析.
http://stackoverflow.com/questions/10823915/photon-server-newbie-questions/11653419#11653419 Channel ...
- 开源分享 Unity3d客户端与C#分布式服务端游戏框架
很久之前,在博客园写了一篇文章,<分布式网游server的一些想法语言和平台的选择>,当时就有了用C#做网游服务端的想法.写了个Unity3d客户端分布式服务端框架,最近发布了1.0版本, ...
- coTurn 运行在Windows平台的方法及服务与客户端运行交互流程和原理
coTurn是一个开源的STUN和TURN及ICE服务项目,只是不支持Windows.为了在window平台上使用coTurn源码,需要在windows平台下安装Cygwin环境,并编译coTurn源 ...
随机推荐
- MySQL - 两种存储引擎 (MyISAM PK InnoDB) + MVCC
总结 1.两种存储引擎各有各的有点,MyISAM专注性能,InnoDB专注事务.两者最大的区别就是InnoDB支持事务,和行锁. 2.InnoDB采用MVCC(Multi-Version Concur ...
- Python 生成json文件
1.数据准备 数据下载 2.python代码 import datetime import os import mssqlhelper ms = mssqlhelper.MSSQL(host=&quo ...
- php 实现的功能
1.php写日志函数 (如:前端请求日志记录) : https://www.cnblogs.com/lvchenfeng/p/6794822.html 2.php中(服务器)使用CURL实现GET和P ...
- Python100天打卡
基于tkinter模块的GUIPython默认的GUI开发模块是tkinter(在Python 3以前的版本中名为Tkinter)使用tkinter来开发GUI应用需要以下5个步骤: 导入tkinte ...
- Transactional事务管理操作
Transactional的属性: alue String 可选的限定描述符,指定使用的事务管理器 propagation enum: Propagation 可选的事务传播行为设置 isolatio ...
- Selenium2Library中select frame关键字对没有name和id的frame或者iframe的处理
elenium2Library中原有的select_frame函数(对应的关键字为select frame)可根据locator选择frame,但是,若某个frame或者iframe没有id,没有na ...
- div + css 边框 虚线
div + css 边框 虚线 dotted:[点线|有点的|点线式边框|点虚线] .introduce { border:1px dotted gray; margin:8px 5px 8px 10 ...
- Spellchecker inspection helps locate typos and misspelling in your code, comments and literals, and fix them in one click
Pycharm设置 Pycharm总是很多的拼写检查波拉线 Spellchecker inspection helps locate typos and misspelling in your cod ...
- Xen的半虚拟化(Paravirtualization)
三个特权级 IA-32体系提供了4个特权级别,正常情况下只用了2个, 操作系统运行在Ring 0,而应用程序运行在Ring 3. Xen让自己运行在Ring 0, 而操作系统运行在Ring 1, 应用 ...
- Apache Flink:详细入门
Apache Flink是一个面向分布式数据流处理和批量数据处理的开源计算平台,它能够基于同一个Flink运行时(Flink Runtime),提供支持流处理和批处理两种类型应用的功能.现有的开源计算 ...