1. 客户端保存数据
1.1 创建组件AccountInfoComponent
1.2 创建组件的处理事件AccountInfoComponentSystem
首先定义组件的Destory方法,那么就要清空内部的数据,我们继承自DestorySystem来实现这个抽象方法
1.3 我们为我们的scene挂载上我们的组件
1.4 在Login函数中存储我们的账户信息进入组件
我们在16节就了解到,我们客户端的登录流程是从HotfixView视图层去调用我们Hotfix逻辑层的LoginHelper里面的Login方法的
我们还改写了返回值成int,代表登录结果
所以,在这里我们拿到登录结果的同时,我们就可以将我们的账户信息存到我们的AccountInfoComponent组件中
2.服务端登录消息请求处理
2.1 判断请求的SceneType(进程)是否为Account
2.2 卸载掉计时断开的组件(避免自动断开连接)代表我们的连接通过了验证
2.3 验证账户密码的正确性
2.4 通过游戏服务器查询是否存在账号
走到这一步就代表我们登录成功了
下面就要对我们账户通讯信息进行在服务器上面的存储了
因为我们服务器得同时面对n个客户端
2.5 创建通讯的令牌
2.6 创建令牌的组件来管理令牌
2.6 我们进入创建场景工厂
在创建账户为Account的时候,我们应该添加我们的组件到Scene上
2.7 添加通讯的令牌
2.8 返回消息给客户端
至此我们已经完成了服务端处理登录消息的所有逻辑,并且返回消息给客户端表示当前状态
但是,有过服务器开发经验的程序员估计会发现我们的服务器处理逻辑有很多问题,对数据的验证还有session的使用过于草率
能跑,但是不安全
所以,下一节会优化我们的逻辑
客户端请求登录消息代码
namespace ET
{
public static class LoginHelper
{
public static async ETTask<int> Login(Scene zoneScene, string address, string account, string password)
{
A2C_LoginAccount a2C_LoginAccount =null;
Session accountSession = null;
try
{
//我们从zoneScene上面获取NetKcpComponent的IP地址,通过这个来创建一个新的Session
accountSession = zoneScene.GetComponent<NetKcpComponent>().Create(NetworkHelper.ToIPEndPoint(address));
//发送一条登录消息给服务器 拿到服务器的返回消息
a2C_LoginAccount = (A2C_LoginAccount)await accountSession.Call(
new C2A_LoginAccount() {
AccountName=account,Password=password});
}
catch (Exception e)
{
//如果出错
accountSession?.Dispose(); //关掉session
Log.Error(e.ToString()); //debug error
return ErrorCode.ERR_NetWorkError; //返回错误码,提示登录失败
}
//如果错误码不是成功的话
if (a2C_LoginAccount.Error != ErrorCode.ERR_Success)
{
accountSession?.Dispose();
return a2C_LoginAccount.Error; //返回Error
}
//如果成功,则将session保留给zoneScene,作为通讯的接口
zoneScene.AddComponent<SessionComponent>().Session = accountSession;
//记录我们登录的账户信息
zoneScene.GetComponent<AccountInfoComponent>().Token = a2C_LoginAccount.Token;
zoneScene.GetComponent<AccountInfoComponent>().AccountId = a2C_LoginAccount.AccountId;
return ErrorCode.ERR_Success;
}
}
}
服务端处理登录消息代码
namespace ET
{
public class C2A_LoginAccountHandler : AMRpcHandler<C2A_LoginAccount, A2C_LoginAccount>
{
protected override async ETTask Run(Session session, C2A_LoginAccount request, A2C_LoginAccount response, Action reply)
{
//1.判断请求的SceneType(进程)是否为Account
if (session.DomainScene().SceneType != SceneType.Account)
{
Log.Error($"请求的Scene错误,当前Scene为:{
session.DomainScene().SceneType}");
session.Dispose();
return;
}
//2.卸载掉计时断开的组件(避免自动断开连接)代表我们的连接通过了验证
session.RemoveComponent<SessionAcceptTimeoutComponent>();
//3.验证账户密码的正确性
if (string.IsNullOrEmpty(request.AccountName) || string.IsNullOrEmpty(request.Password))
{
//账号或者密码存在空 断开连接
response.Error = ErrorCode.ERR_LoginInfoError;
reply();
session.Dispose();
return;
}
if (!Regex.IsMatch(request.AccountName.Trim(), @"^(?=.*[0-9].*)(?=.*[A-Z].*)(?=.*[a-z].*).{6,15}$"))
{
//账号不满足正则表达式的条件
response.Error = ErrorCode.ERR_LoginInfoError;
reply();
session.Dispose();
return;
}
if (!Regex.IsMatch(request.Password.Trim(), @"^(?=.*[0-9].*)(?=.*[A-Z].*)(?=.*[a-z].*).{6,15}$"))
{
//密码不满足正则表达式的条件
response.Error = ErrorCode.ERR_LoginInfoError;
reply();
session.Dispose();
return;
}
//4.通过游戏服务器查询是否存在账号
var accountInfoList = await DBManagerComponent.Instance.GetZoneDB(session.DomainZone()).
Query<Account>(d => d.AccountName.Equals(request.AccountName.Trim()));
Account account = null;
if (accountInfoList.Count > 0)
{
//存在就对账户信息进行校验
account = accountInfoList[0];
session.AddChild(account);
if (account.AccountType == (int)AccountType.BlackList)
{
//如果登录的是黑名单用户,那么断开连接
response.Error = ErrorCode.ERR_LoginBlackListError;
reply();
session.Dispose();
return;
}
//然后校验登录的账号的密码
if(!account.Password.Equals(request.Password))
{
response.Error = ErrorCode.ERR_LoginInfoError;
reply();
session.Dispose();
return;
}
}
else
{
//不存在即注册
account = session.AddChild<Account>();
account.AccountName = request.AccountName.Trim();
account.Password = request.Password;
account.CreateTime = TimeHelper.ServerNow();
account.AccountType = (int)AccountType.General;
//zone是区服的概念
await DBManagerComponent.Instance.GetZoneDB(session.DomainZone()).Save<Account>(account);
}
//走到这一步就代表我们登录成功了
//5.创建通讯的令牌
string Token = TimeHelper.ServerNow().ToString() +
RandomHelper.RandomNumber(int.MinValue, int.MaxValue).ToString();
//6.添加通讯的令牌
session.DomainScene().GetComponent<TokenComponent>().Remove(account.Id);
session.DomainScene().GetComponent<TokenComponent>().Add(account.Id, Token);
//7.返回消息
response.AccountId = account.Id;
response.Token = Token;
reply();
await ETTask.CompletedTask;
}
}
}