162 lines
5.5 KiB
C#
162 lines
5.5 KiB
C#
using Fantasy;
|
||
using Fantasy.Async;
|
||
using Fantasy.Entitas;
|
||
using Fantasy.Helper;
|
||
using Fantasy.Network;
|
||
|
||
namespace NB.Game;
|
||
|
||
public static class PlayerHelper
|
||
{
|
||
#region 数据保存
|
||
|
||
public static async FTask Save(this Player self)
|
||
{
|
||
//先立马保存,后续做缓存
|
||
await self.Scene.World.Database.Save(self);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从数据库中读取GameAccount
|
||
/// </summary>
|
||
/// <param name="scene"></param>
|
||
/// <param name="accountId">账号Id</param>
|
||
/// <returns></returns>
|
||
public static async FTask<Player?> LoadDataBase(Scene scene, long accountId)
|
||
{
|
||
var account = await scene.World.Database.First<Player>(d => d.Id == accountId);
|
||
if (account == null)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
account.Deserialize(scene);
|
||
return account;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 执行该账号的断开逻辑,不要非必要不要使用这个接口,这个接口是内部使用。
|
||
/// </summary>
|
||
/// <param name="self"></param>
|
||
public static async FTask Disconnect(this Player self)
|
||
{
|
||
// 保存该账号信息到数据库中。
|
||
await Save(self);
|
||
var itemContainer = self.GetComponent<PlayerItemContainerComponent>();
|
||
if (itemContainer != null)
|
||
{
|
||
await itemContainer.Save();
|
||
}
|
||
|
||
var wallet = self.GetComponent<PlayerWalletComponent>();
|
||
if (wallet != null)
|
||
{
|
||
await wallet.Save();
|
||
}
|
||
|
||
// 在缓存中移除自己,并且执行自己的Dispose方法。
|
||
self.Scene.GetComponent<PlayerManageComponent>().Remove(self.Id);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 账号完整的断开逻辑,执行了这个接口后,该账号会完全下线。
|
||
/// </summary>
|
||
/// <param name="scene"></param>
|
||
/// <param name="accountId"></param>
|
||
/// <param name="timeOut"></param>
|
||
public static async FTask Disconnect(Scene scene, long accountId, int timeOut = 1000 * 60 * 3)
|
||
{
|
||
// 调用该方法有如下几种情况:
|
||
// 1、客户端主动断开,如:退出游戏、切换账号、等。 客户端会主动发送一个协议给服务器通知服务器断开。
|
||
// 2、客户端断断线 客户端不会主动发送一个协议给服务器,是由服务器的心跳来检测是否断开了。
|
||
// 如果是心跳检测断开的Session,我怎么能拿到当前的这个账号来进行下线处理呢?
|
||
// 通过给当前的Session挂载一个组件,当销毁这个Session时候呢,也会销毁这个组件。
|
||
// 这样的话,是不是可以在登录的时候,给这个组件把AccountId存到这个组件呢?
|
||
|
||
// 要检查当前缓存中是否存在该账号的数据
|
||
var gameAccountManageComponent = scene.GetComponent<PlayerManageComponent>();
|
||
if (!gameAccountManageComponent.TryGet(accountId, out var account))
|
||
{
|
||
// 如果缓存中没有、那表示已经下线或者根本不存在该账号,应该在打印一个警告,因为正常的情况下是不会出现的。
|
||
Log.Warning($"GameAccountHelper Disconnect accountId : {accountId} not found");
|
||
return;
|
||
}
|
||
|
||
// 为了防止逻辑的错误,加一个警告来排除下
|
||
if (!scene.TryGetEntity<Session>(account.SessionRunTimeId, out var session))
|
||
{
|
||
// 如果没有找到对应的Session,那只有一种可能就是当前的链接会话已经断开了,一般的情况下也不会出现的,所以咱们也要打印一个警告。
|
||
Log.Warning(
|
||
$"GameAccountHelper Disconnect accountId : {accountId} SessionRunTimeId : {account.SessionRunTimeId} not found");
|
||
return;
|
||
}
|
||
|
||
// 如果不存在定时任务的组件,那就添加并设置定时任务
|
||
if (account.IsTimeOutComponent())
|
||
{
|
||
// 如果已经存在了,那就表示当然已经有一个延时断开的任务了,那就不需要重复添加了
|
||
return;
|
||
}
|
||
|
||
// 立即下线处理
|
||
if (timeOut <= 0)
|
||
{
|
||
// 如果timeOut销毁或等会0的情况下,执行立即下线。
|
||
await account.Disconnect();
|
||
return;
|
||
}
|
||
|
||
// 设置延迟下线
|
||
account.SetTimeout(timeOut, account.Disconnect);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 结构转换
|
||
|
||
public static RoleSimpleInfo GetRoleSimpleInfo(this Player self)
|
||
{
|
||
return new RoleSimpleInfo()
|
||
{
|
||
RoleId = self.Id,
|
||
NickName = self.NickName,
|
||
Head = self.Head,
|
||
Country = self.Country,
|
||
Level = self.Level,
|
||
Vip = self.Vip,
|
||
};
|
||
}
|
||
|
||
public static RoleInfo GetRoleInfo(this Player self)
|
||
{
|
||
var info = new RoleInfo();
|
||
info.BaseInfo = GetRoleBaseInfo(self);
|
||
// info.Items = self.ItemContainer.GetItemInfos();
|
||
info.RoleId = self.Address;
|
||
return info;
|
||
}
|
||
|
||
public static RoleBaseInfo GetRoleBaseInfo(this Player self)
|
||
{
|
||
var ret = new RoleBaseInfo()
|
||
{
|
||
NickName = self.NickName,
|
||
Head = self.Head,
|
||
Country = self.Country,
|
||
Level = self.Level,
|
||
Exp = self.Exp,
|
||
};
|
||
if (self.Vip > 0)
|
||
{
|
||
ret.VipInfo = new VipInfo();
|
||
ret.VipInfo.Level = self.Vip;
|
||
ret.VipInfo.OpenTime = self.VipGetTime;
|
||
ret.VipInfo.ExpirationTime = self.VipExpirationTime;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
#endregion
|
||
} |