流程
客户端
服务端
---------------------------------------------------------------------------------------------------------------------------------
客户端
PhotonEngine.cs:差不多算是主类吧,将其构造成单例,到时候方便调用
里面有处理服务端返回的响应的方法,还有接受客户端发来的事件的方法等等。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ExitGames.Client.Photon;
using Common;
public class PhotonEngine : MonoBehaviour,IPhotonPeerListener {
public static PhotonEngine Instance;
//用字典来缓存每次的请求和码
//这里就能体现出来我们Request抽象类的好处了,不然每次这边都是不同的类型,使用抽象类就统一了
private Dictionary<OperationCode, Request> RequestDic = new Dictionary<OperationCode, Request>();
public static PhotonPeer peer;
private void Awake()
{
if (Instance == null)
{
Instance = this;
//当前Gameobject不会随场景的销毁而销毁
DontDestroyOnLoad(this.gameObject);
}
}
void Start () {
peer = new PhotonPeer(this,ConnectionProtocol.Udp);
peer.Connect("127.0.0.1:5055", "MyGame6");//通过ip连接到到对应的我们config中配置的Allpication
}
void Update () {
peer.Service();//开启服务
}
private void OnDestroy()//销毁的时候
{
if(peer!=null&&peer.PeerState == PeerStateValue.Connected)//如果存在且保持连接状态
{
peer.Disconnect();//就断开连接
}
}
public void DebugReturn(DebugLevel level, string message)
{
}
public void OnEvent(EventData eventData)
{
switch (eventData.Code)
{
case 1:
//解析数据
var data = eventData.Parameters;
object intValue, StringValue;
data.TryGetValue(1, out intValue);
data.TryGetValue(2, out StringValue);
Debug.Log("收到服务器的响应Event推送,OpCode:1" + intValue.ToString() + ":" + StringValue.ToString());
break;
default:
break;
}
}
public void OnOperationResponse(OperationResponse operationResponse)
{
Request request = null;
//此处就是工具类
DicTool.GetValue(RequestDic, (OperationCode)operationResponse.OperationCode)
.OnOprationRespionse(operationResponse);
//下面注释的代码可以用上方工具类代替
//bool b =RequestDic.TryGetValue((OperationCode)operationResponse.OperationCode, out request);
//if (b)
//{
// request.OnOprationRespionse(operationResponse);
//}
//else
//{
// Debug.LogError("未找到对应code的请求request");
//}
return;
//switch (operationResponse.OperationCode)
//{
// case 1:
// Debug.Log("收到服务器的响应,OpCode:1");
// //解析数据
// var data = operationResponse.Parameters;
// object intValue;
// data.TryGetValue(1, out intValue);
// object StringValue;
// data.TryGetValue(2, out StringValue);
// Debug.Log("收到客户端的请求,OpCode:1" + intValue.ToString() + ":" + StringValue.ToString());
// break;
// default:
// break;
// }
}
public void OnStatusChanged(StatusCode statusCode)
{
Debug.LogError(statusCode);
}
//private Dictionary<OperationCode, Request> RequestDic = new Dictionary<OperationCode, Request>();
public void AddRequest(Request r)
{
RequestDic.Add(r.OpCode, r);
}
public void RemoveRequest(Request r)
{
RequestDic.Remove(r.OpCode);
}
}
Request.cs:抽象了,生命code和request以及对服务器返回的响应的响应response
using Common;
using ExitGames.Client.Photon;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class Request: MonoBehaviour
{
public OperationCode OpCode;
//抽象方法就是只声明,具体的实现有子类完成
public abstract void DefaultRequest();
public abstract void OnOprationRespionse(OperationResponse operationResponse);
public void Start()
{
PhotonEngine.Instance.AddRequest(this);
}
public void OnDestroy()
{
PhotonEngine.Instance.RemoveRequest(this);
}
}
(登录)LoginRequest.cs:实现了抽象类中的方法
using Common;
using ExitGames.Client.Photon;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LoginRequest : Request
{
//在unity面板隐藏
[HideInInspector]
public string UserName;
[HideInInspector]
public string Password;
public override void DefaultRequest()
{
//构造参数
var data = new Dictionary<byte, object>();
//构造参数
data.Add((byte)ParaCode.UserName,UserName);
data.Add((byte)ParaCode.Password, Password);
//发送
PhotonEngine.peer.OpCustom((byte)OpCode, data, true);
}
public override void OnOprationRespionse(OperationResponse operationResponse)
{
Debug.Log(operationResponse.ReturnCode);
}
}
(登录)LoginPanel.cs:用于获得输入的账号密码,以及点击的时候调用request
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LoginPanel : MonoBehaviour {
public GameObject SignPanel;
void Start() {
// SignPanel = GameObject.Find("SignInPanel");
}
void Update() {
}
public void OnLoginBtn()
{
print("OnLoginBtn()");
var Request = GetComponent<LoginRequest>();
Request.UserName = transform.Find("NameField").GetComponent<InputField>().text;
Request.Password = transform.Find("PWField").GetComponent<InputField>().text;
Request.DefaultRequest();
}
public void OnSign()
{
SignPanel.SetActive(true);
gameObject.SetActive(false);
}
}
(注册)LoginRequest.cs:实现抽象类中的方法
using Common;
using ExitGames.Client.Photon;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LoginRequest : Request
{
//在unity面板隐藏
[HideInInspector]
public string UserName;
[HideInInspector]
public string Password;
public override void DefaultRequest()
{
//构造参数
var data = new Dictionary<byte, object>();
//构造参数
data.Add((byte)ParaCode.UserName,UserName);
data.Add((byte)ParaCode.Password, Password);
//发送
PhotonEngine.peer.OpCustom((byte)OpCode, data, true);
}
public override void OnOprationRespionse(OperationResponse operationResponse)
{
Debug.Log(operationResponse.ReturnCode);
}
}
(注册)LoginPanel.cs:用于获得输入的账号密码,以及点击的时候调用request
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LoginPanel : MonoBehaviour {
public GameObject SignPanel;
void Start() {
// SignPanel = GameObject.Find("SignInPanel");
}
void Update() {
}
public void OnLoginBtn()
{
print("OnLoginBtn()");
var Request = GetComponent<LoginRequest>();
Request.UserName = transform.Find("NameField").GetComponent<InputField>().text;
Request.Password = transform.Find("PWField").GetComponent<InputField>().text;
Request.DefaultRequest();
}
public void OnSign()
{
SignPanel.SetActive(true);
gameObject.SetActive(false);
}
}
---------------------------------------------------------------------------------------------------------------------------------
服务端
其中Nhibernate就不发了,之前的文章里发过了。(有用的)
然后这里面net连接mysql那个文件夹是之前测试用的,这里也不发了。(没用)
就发剩余的了(有用的)
Class1.cs:枚举,我认为这是协议
namespace Common
{
public enum OperationCode : byte
{
Login,
SignIn
}
public enum ParaCode : byte
{
UserName,
Password
}
public enum ReturnCode : short
{
Success,
Failed
}
}
DicTools.cs:字典工具类,照着写,拿着用就完事了
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Common
{
public class DicTool
{
public static T2 GetValue<T1,T2>(Dictionary<T1,T2> dic, T1 key)
{
T2 value;
bool isSuccess = dic.TryGetValue(key, out value);
if (isSuccess)
{
return value;
}
else
{
return default(T2);
}
}
}
}
BaseHandler.cs:和客户端的Request差不多,都是抽象类。定义code以及抽象方法
using Common;
using Photon.SocketServer;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RRGameServer.Handler
{
public abstract class BaseHandler
{
public OperationCode OpCode;
public abstract void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, ClientPeer peer);
}
}
LoginHandler.cs:拿到登陆的请求,实现抽象类的方法,返回响应
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common;
using Photon.SocketServer;
using RRGameServer.Manager;
namespace RRGameServer.Handler
{
class LoginHandler : BaseHandler
{
public LoginHandler()
{
OpCode = Common.OperationCode.Login;
}
//服务端收到客户端的request,并且对其做出响应返回给客户端
public override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, ClientPeer peer)
{
string username = DicTool.GetValue<byte, object>(operationRequest.Parameters, (byte)ParaCode.UserName) as string;
string password = DicTool.GetValue<byte, object>(operationRequest.Parameters, (byte)ParaCode.Password) as string;
bool b= UserManager.Instance.VerifyUser(username, password);
OperationResponse response = new OperationResponse(operationRequest.OperationCode);
if (b)
{
response.ReturnCode = (short)Common.ReturnCode.Success;
}
else
{
response.ReturnCode = (short)Common.ReturnCode.Failed;
}
peer.SendOperationResponse(response, sendParameters);
}
}
}
SignHandler.cs:拿到注册的请求,实现抽象类的方法,返回响应
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common;
using Photon.SocketServer;
using RRGameServer.Manager;
using RRGameServer.Model;
namespace RRGameServer.Handler
{
class SignHandler:BaseHandler
{
public SignHandler()
{
OpCode = Common.OperationCode.SignIn;
}
public override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, ClientPeer peer)
{
string username = DicTool.GetValue<byte, object>(operationRequest.Parameters, (byte)ParaCode.UserName) as string;
string password = DicTool.GetValue<byte, object>(operationRequest.Parameters, (byte)ParaCode.Password) as string;
var user = UserManager.Instance.GetUserByName(username);
OperationResponse response = new OperationResponse(operationRequest.OperationCode);
if (user==null)
{
user = new User();
user.Username = username;
user.Password = password;
UserManager.Instance.Add(user);
response.ReturnCode = (short)Common.ReturnCode.Success;
}
else
{
response.ReturnCode = (short)Common.ReturnCode.Failed;
}
peer.SendOperationResponse(response, sendParameters);
}
}
}
UserManager.cs:可以对数据库进行操作,,增删改查以及比对校验啥的
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NHibernate;
using NHibernate.Criterion;
using RRGameServer.Model;
namespace RRGameServer.Manager
{
class UserManager
{
public static UserManager Instance = new UserManager();
public void Add(User user)
{
//using大括号之间的代码执行完毕之后,()里面的内存就会被回收
using (ISession session= NHibernateHelper.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
session.Save(user);
transaction.Commit();
}
}
}
public void Update(User user)
{
//using大括号之间的代码执行完毕之后,()里面的内存就会被回收
using (ISession session = NHibernateHelper.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
session.Update(user);
transaction.Commit();
}
}
}
//做用户名和密码的校验
internal bool VerifyUser(string username, string password)
{
using (ISession session = NHibernateHelper.OpenSession())
{
User user= session
.CreateCriteria(typeof(User))
.Add(Restrictions.Eq("Username", username))
.Add(Restrictions.Eq("Password", password))
.UniqueResult<User>();
if (user == null)
return false;
return true;
}
}
public void Remove(User user)
{
//using大括号之间的代码执行完毕之后,()里面的内存就会被回收
using (ISession session = NHibernateHelper.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
session.Delete(user);
transaction.Commit();
}
}
}
public User GetUserById(int id)
{
//using大括号之间的代码执行完毕之后,()里面的内存就会被回收
using (ISession session = NHibernateHelper.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
var u = session.Get<User>(id);
transaction.Commit();
return u;
}
}
}
public User GetUserByName(string name)
{
//using大括号之间的代码执行完毕之后,()里面的内存就会被回收
using (ISession session = NHibernateHelper.OpenSession())
{
return session.CreateCriteria(typeof(User)).Add(Restrictions.Eq("Username", name)).UniqueResult<User>();
}
}
public IList<User> GetAll()
{
//using大括号之间的代码执行完毕之后,()里面的内存就会被回收
using (ISession session = NHibernateHelper.OpenSession())
{
return session.CreateCriteria(typeof(User)).List<User>();
}
}
}
}
User.hbm.xml:配置表,配置与数据库交互
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="RRGameServer"
namespace="RRGameServer.Model">
<!-- more mapping info here -->
<class name="User" table="users">
<id name="Id" column="id" type="Int32">
<generator class="native"></generator>
</id>
<property name="Username" column="username" type="string"></property>
<property name="Password" column="password" type="string"></property>
<property name="Registerdate" column="registerdate" type="Date"></property>
</class>
</hibernate-mapping>
users.cs:用户类,跟NHibernate一起用的
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RRGameServer.Model
{
class User
{
public virtual int Id { get; set; }
public virtual string Username { get; set; }
public virtual string Password { get; set; }
public virtual DateTime Registerdate { get; set; }
}
}
ClientPeer.cs:处理客户端请求等功能
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common;
using Photon.SocketServer;
using PhotonHostRuntimeInterfaces;
using RRGameServer.Handler;
namespace RRGameServer
{
public class ClientPeer : Photon.SocketServer.ClientPeer
{
public ClientPeer(InitRequest initRequest) : base(initRequest)
{
}
//当每个客户端断开时
protected override void OnDisconnect(DisconnectReason reasonCode, string reasonDetail)
{
}
//当客户端发起请求的时候
protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)
{
BaseHandler handler = DicTool.GetValue(MyGameServer.Instance.HandlerDic, (OperationCode)operationRequest.OperationCode);
if (handler != null)
{
handler.OnOperationRequest(operationRequest, sendParameters,this);
}
else
{
MyGameServer.log.Info("找不到操作码:" + (OperationCode)operationRequest.OperationCode);
}
return;
//switch (operationRequest.OperationCode)
//{
// case 1:
// //解析数据
// var data = operationRequest.Parameters;
// object intValue;
// data.TryGetValue(1, out intValue);
// object StringValue;
// data.TryGetValue(2, out StringValue);
// //输出参数
// MyGameServer.log.Info("收到客户端的请求,opcode:1"+intValue.ToString()+":"+StringValue.ToString());
// //返回响应
// OperationResponse opResponse = new OperationResponse(operationRequest.OperationCode);
// //构造参数
// var data2 = new Dictionary<byte, object>();
// data2.Add(1, 100);
// data2.Add(2, "这是服务端做的响应");
// opResponse.SetParameters(data2);
// //返回code,为发送过来的code,返回的参数,为发送过来的参数
// SendOperationResponse(opResponse, sendParameters);
// //推送一个Event
// EventData ed = new EventData(1);
// ed.Parameters = data2;
// SendEvent(ed, new SendParameters());
// break;
// default:
// break;
//}
}
}
}
hibernate.cfg.xml:配置用户连接数据库啥的
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.MsSqlCeDialect</property>
<property name="connection.driver_class">NHibernate.Driver.MySqlDataDriver</property>
<property name="connection.connection_string">server=localhost;port=3306;database=gamedb;user=root;password=root</property>
<property name="show_sql">true</property>
</session-factory>
</hibernate-configuration>
MyGameServer.cs:主类,用于初始化字典,以及服务器的响应初始化,以及日志等操作。
(重要!)(●'◡'●)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common;
using ExitGames.Logging;
using ExitGames.Logging.Log4Net;
using log4net.Config;
using Photon.SocketServer;
using RRGameServer.Handler;
using RRGameServer.Manager;
namespace RRGameServer
{
//所有的Server,都要继承ApplicationBase,然后实现ApplicationBase的三个方法
public class MyGameServer : ApplicationBase
{
public static MyGameServer Instance;
public static readonly ILogger log = LogManager.GetCurrentClassLogger();
//当有一个客户端连接上以后,就会执行此方法
protected override PeerBase CreatePeer(InitRequest initRequest)
{
log.Info("一个客户端连接");
return new ClientPeer(initRequest);
}
//服务器初始化函数
protected override void Setup()
{
Instance = this;
log4net.GlobalContext.Properties["Photon:ApplicationLogPath"] = Path.Combine(
Path.Combine(this.ApplicationRootPath, "bin_Win64"), "log");
FileInfo configFileInfo = new FileInfo(Path.Combine(this.BinaryPath, "log4net.config"));
if (configFileInfo.Exists)
{
LogManager.SetLoggerFactory(Log4NetLoggerFactory.Instance);//photon知道日志输出
XmlConfigurator.ConfigureAndWatch(configFileInfo);//读取配置
}
log.Info("服务器启动啦");
//log.Info(UserManager.Instance.GetUserById(7).Username);
InitHandler();//初始化
}
//服务器关闭函数
protected override void TearDown()
{
log.Info("服务器关闭啦");
}
public Dictionary<OperationCode, BaseHandler> HandlerDic = new Dictionary<OperationCode, BaseHandler>();
public void InitHandler()
{
//一开始就全部初始化
LoginHandler LH = new LoginHandler();
//log.Info("初始化操作码:" + LH.OpCode);
HandlerDic.Add(LH.OpCode, LH);
SignHandler handler = new SignHandler();
HandlerDic.Add(handler.OpCode, handler);
}
}
}
NHibernateHelper.cs:会话工厂,用于搞Nhibernate的那玩意,直接用就完事了。
using NHibernate;
using NHibernate.Cfg;
using RRGameServer.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RRGameServer
{
class NHibernateHelper
{
private static ISessionFactory _sessionFactory;
private static ISessionFactory sessionFactory
{
get
{
if (_sessionFactory == null)
{
var cfg = new Configuration();
cfg.Configure();//解析配置文件
cfg.AddAssembly("RRGameServer");
_sessionFactory = cfg.BuildSessionFactory();
}
return _sessionFactory;
}
}
public static ISession OpenSession()
{
return sessionFactory.OpenSession();
}
}
}
最后效果也懒得贴图了,就是unity的客户端和服务端以及数据库三者打通了。
客户端注册,服务端接受到注册,判断有没有,与数据库做对比,如果有的话,返回失败。如果没有,添加到数据库,返回给客户端成功。然后客户端在根据返回的码,做相应的操作。
客户端登陆,服务端接收到登录,判断账号密码和数据库里面是否相同,如果相同,返回成功,如果不同,返回失败。然后客户端在根据返回的码,做相应的操作。
基本上就三个函数,一个requst是客户端发送给服务端的请求。然后服务端会有一个response函数,用于接受客户端发过来的请求,并且你在处理这个请求的时候,还可以掉个api给客户端发回去你的处理结果。然后第三个函数是客户端用于接受服务端传回来的响应的函数,可以根据传回来的响应,做一些操作,比如弹出成功登录的页面啥的呀。
(●'◡'●)
( •̀ ω •́ )y
呜呜终于一个人配置、搭建、搞通了客户端和服务端还有数据库,搭建了架构QAQ