自动定时数据落地
This commit is contained in:
@@ -29,7 +29,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Game\Activity\" />
|
||||
<Folder Include="Game\Skill\" />
|
||||
<Folder Include="Map\" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
using Fantasy.Entitas;
|
||||
|
||||
namespace NB;
|
||||
|
||||
public enum ContainerType : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// 背包
|
||||
/// </summary>
|
||||
Bag,
|
||||
|
||||
/// <summary>
|
||||
/// 鱼护
|
||||
/// </summary>
|
||||
FishBag,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 物品容器组件
|
||||
/// </summary>
|
||||
public class ContainerComponent : Entity
|
||||
{
|
||||
}
|
||||
@@ -24,4 +24,14 @@ public class Fish : Entity
|
||||
/// 失效时间
|
||||
/// </summary>
|
||||
[BsonElement("et")] public long ExpirationTime;
|
||||
|
||||
/// <summary>
|
||||
/// 获取地图
|
||||
/// </summary>
|
||||
[BsonElement("map")] public int Map;
|
||||
|
||||
/// <summary>
|
||||
/// 物品所属的容器
|
||||
/// </summary>
|
||||
[BsonIgnore] FishContainer Container;
|
||||
}
|
||||
19
Entity/Game/Fish/FishContainer.cs
Normal file
19
Entity/Game/Fish/FishContainer.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Fantasy.Entitas;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Bson.Serialization.Options;
|
||||
|
||||
namespace NB.Game;
|
||||
|
||||
public class FishContainer : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 最大格子数量
|
||||
/// </summary>
|
||||
public int CellCountMax;
|
||||
|
||||
/// <summary>
|
||||
/// 容器内的物品
|
||||
/// </summary>
|
||||
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfArrays)]
|
||||
public Dictionary<long, Item> Fishes = new Dictionary<long, Item>();
|
||||
}
|
||||
@@ -11,8 +11,6 @@ public enum ItemType
|
||||
Fish,
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class Item : Entity
|
||||
{
|
||||
/// <summary>
|
||||
@@ -43,5 +41,10 @@ public class Item : Entity
|
||||
/// <summary>
|
||||
/// 耐久度
|
||||
/// </summary>
|
||||
[BsonElement("a")] public int Abrasion;
|
||||
[BsonElement("abr")] public int Abrasion;
|
||||
|
||||
/// <summary>
|
||||
/// 物品所属的容器
|
||||
/// </summary>
|
||||
[BsonIgnore] ItemContainer Container;
|
||||
}
|
||||
@@ -6,34 +6,20 @@ using NB.Game;
|
||||
|
||||
namespace NB;
|
||||
|
||||
public sealed class Container : Entity
|
||||
public sealed class ItemContainer : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 可用格子数量
|
||||
/// </summary>
|
||||
public int CellCount;
|
||||
|
||||
/// <summary>
|
||||
/// 最大格子数量
|
||||
/// </summary>
|
||||
public int CellCountMax;
|
||||
|
||||
/// <summary>
|
||||
/// 当前已经使用格子数量
|
||||
/// </summary>
|
||||
public int CurrentCellCount;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 容器内的物品
|
||||
/// </summary>
|
||||
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfArrays)]
|
||||
public Dictionary<long, Item> Items = new Dictionary<long, Item>();
|
||||
|
||||
/// <summary>
|
||||
/// 容器内物品,按格子进行分组
|
||||
/// </summary>
|
||||
[BsonIgnore] public Dictionary<uint, Item> ItemsByCell = new Dictionary<uint, Item>();
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 按物品id分组
|
||||
/// </summary>
|
||||
10
Entity/Game/Mission/Mission.cs
Normal file
10
Entity/Game/Mission/Mission.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Fantasy.Entitas;
|
||||
|
||||
namespace NB.Game;
|
||||
|
||||
/// <summary>
|
||||
/// 角色任务
|
||||
/// </summary>
|
||||
public class Mission : Entity
|
||||
{
|
||||
}
|
||||
32
Entity/Game/Player/Child/PlayerBasic.cs
Normal file
32
Entity/Game/Player/Child/PlayerBasic.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using Fantasy.Entitas;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace NB.Game;
|
||||
|
||||
public class PlayerBasic : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 昵称
|
||||
/// </summary>
|
||||
public string NickName;
|
||||
|
||||
/// <summary>
|
||||
/// 头像
|
||||
/// </summary>
|
||||
public string Head;
|
||||
|
||||
/// <summary>
|
||||
/// 国家
|
||||
/// </summary>
|
||||
public string Country;
|
||||
|
||||
/// <summary>
|
||||
/// 等级
|
||||
/// </summary>
|
||||
public int Level;
|
||||
|
||||
/// <summary>
|
||||
/// 当前经验
|
||||
/// </summary>
|
||||
public int Exp;
|
||||
}
|
||||
34
Entity/Game/Player/Child/PlayerSlot.cs
Normal file
34
Entity/Game/Player/Child/PlayerSlot.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Fantasy.Entitas;
|
||||
|
||||
namespace NB.Game;
|
||||
|
||||
public enum SlotType
|
||||
{
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// 物品
|
||||
/// </summary>
|
||||
Item,
|
||||
|
||||
/// <summary>
|
||||
/// 钓组
|
||||
/// </summary>
|
||||
Tackle
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 快速使用插槽
|
||||
/// </summary>
|
||||
public class PlayerSlot : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 插槽类型
|
||||
/// </summary>
|
||||
public SlotType SlotType;
|
||||
|
||||
/// <summary>
|
||||
/// 绑定快速使用的id
|
||||
/// </summary>
|
||||
public long BindId;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Fantasy.Entitas;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace NB;
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
namespace NB;
|
||||
|
||||
/// <summary>
|
||||
/// 角色状态标志量
|
||||
/// 玩家钓组
|
||||
/// </summary>
|
||||
public class PlayerDayFlags
|
||||
public class PlayerTackle : Entity
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
namespace NB;
|
||||
using Fantasy.Entitas;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
public class PlayerVip
|
||||
namespace NB;
|
||||
|
||||
public class PlayerVip : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否是vip
|
||||
|
||||
27
Entity/Game/Player/Child/PlayerWallet.cs
Normal file
27
Entity/Game/Player/Child/PlayerWallet.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Fantasy.Entitas;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Bson.Serialization.Options;
|
||||
|
||||
namespace NB.Game;
|
||||
|
||||
/// <summary>
|
||||
/// 用户钱包
|
||||
/// </summary>
|
||||
public class PlayerWallet : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 余额
|
||||
/// </summary>
|
||||
public int Money;
|
||||
|
||||
/// <summary>
|
||||
/// 金币
|
||||
/// </summary>
|
||||
public int Gold;
|
||||
|
||||
/// <summary>
|
||||
/// 其他货币
|
||||
/// </summary>
|
||||
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfArrays)]
|
||||
public Dictionary<int, int> Other = new();
|
||||
}
|
||||
@@ -6,72 +6,35 @@ namespace NB.Game;
|
||||
|
||||
public sealed class Player : Entity
|
||||
{
|
||||
[BsonElement("ct")] public long CreateTime;
|
||||
[BsonElement("lt")] public long LoginTime;
|
||||
/// <summary>
|
||||
/// 基础信息
|
||||
/// </summary>
|
||||
public PlayerBasic Basic;
|
||||
|
||||
/// <summary>
|
||||
/// 昵称
|
||||
/// 统计信息
|
||||
/// </summary>
|
||||
[BsonElement("name")] public string NickName;
|
||||
public PlayerStatistics Statistics;
|
||||
|
||||
/// <summary>
|
||||
/// 头像
|
||||
/// 角色vip信息
|
||||
/// </summary>
|
||||
public string Head;
|
||||
public PlayerVip Vip;
|
||||
|
||||
/// <summary>
|
||||
/// 国家
|
||||
/// 钱包
|
||||
/// </summary>
|
||||
public string Country;
|
||||
public PlayerWallet Wallet;
|
||||
|
||||
/// <summary>
|
||||
/// 等级
|
||||
/// 背包
|
||||
/// </summary>
|
||||
[BsonElement("lv")] public int Level;
|
||||
|
||||
/// <summary>
|
||||
/// 当前经验
|
||||
/// </summary>
|
||||
public int Exp;
|
||||
|
||||
/// <summary>
|
||||
/// 余额
|
||||
/// </summary>
|
||||
public int Money;
|
||||
|
||||
/// <summary>
|
||||
/// 金币
|
||||
/// </summary>
|
||||
public int Gold;
|
||||
|
||||
// public PlayerStatistics Statistics = new PlayerStatistics();
|
||||
// public PlayerDayFlags DayFlags = new PlayerDayFlags();
|
||||
// public PlayerVip Vip = new PlayerVip();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 其他货币
|
||||
/// </summary>
|
||||
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfArrays)]
|
||||
public Dictionary<int, int> Currency = new();
|
||||
|
||||
/// <summary>
|
||||
/// 插槽
|
||||
/// </summary>
|
||||
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfArrays)]
|
||||
public Dictionary<uint, long> Slots = new Dictionary<uint, long>();
|
||||
|
||||
/// <summary>
|
||||
/// 背包物品
|
||||
/// </summary>
|
||||
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfArrays)]
|
||||
public Dictionary<long, Item> Items = new Dictionary<long, Item>();
|
||||
public ItemContainer ItemContainer;
|
||||
|
||||
/// <summary>
|
||||
/// 鱼护
|
||||
/// </summary>
|
||||
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfArrays)]
|
||||
public Dictionary<long, Fish> Fishes = new Dictionary<long, Fish>();
|
||||
public FishContainer FishContainer;
|
||||
|
||||
|
||||
[BsonIgnore] public long SessionRunTimeId;
|
||||
@@ -82,7 +45,7 @@ public sealed class Player : Entity
|
||||
[BsonIgnore] public bool NeedSave;
|
||||
|
||||
/// <summary>
|
||||
/// 最后保存时间
|
||||
/// 需要保存数据库时间
|
||||
/// </summary>
|
||||
[BsonIgnore] public long LastSaveTime;
|
||||
[BsonIgnore] public long NeedSaveTime;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
using Fantasy.Entitas;
|
||||
|
||||
namespace NB.Game;
|
||||
|
||||
public class PlayerAutoSaveComponent : Entity
|
||||
{
|
||||
|
||||
}
|
||||
@@ -4,5 +4,17 @@ namespace NB.Game;
|
||||
|
||||
public sealed class PlayerManageComponent : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 10分钟
|
||||
/// </summary>
|
||||
public const long AutoSaveTime = 60000; // 600000;
|
||||
|
||||
public long AutoSaveTimerId;
|
||||
|
||||
public readonly Dictionary<long, Player> Players = new();
|
||||
|
||||
/// <summary>
|
||||
/// 需要保存到数据库的玩家
|
||||
/// </summary>
|
||||
public readonly HashSet<long> NeedSavePlayer = new();
|
||||
}
|
||||
11
Entity/Game/Skill/Skill.cs
Normal file
11
Entity/Game/Skill/Skill.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Fantasy.Entitas;
|
||||
|
||||
namespace NB.Game;
|
||||
|
||||
/// <summary>
|
||||
/// 角色技能
|
||||
/// </summary>
|
||||
public class Skill : Entity
|
||||
{
|
||||
|
||||
}
|
||||
@@ -16,89 +16,13 @@ public class G2Game_EnterRequestHandler : RouteRPC<Scene, G2Game_EnterRequest, G
|
||||
{
|
||||
Log.Debug("收到 G2Game_EnterRequestHandler");
|
||||
|
||||
var accountId = request.AccountId;
|
||||
|
||||
// 在缓存中检查该账号是否存在
|
||||
var gameAccountManageComponent = scene.GetComponent<PlayerManageComponent>();
|
||||
Log.Debug("检查账号是否在缓存中");
|
||||
if (!gameAccountManageComponent.TryGet(accountId, out var account))
|
||||
{
|
||||
// 首先要先到数据库中查询是否有这个账号
|
||||
account = await PlayerHelper.LoadDataBase(scene, accountId);
|
||||
// 如果有的话,就直接加入在缓存中就可以了
|
||||
if (account == null)
|
||||
{
|
||||
Log.Debug("检查到账号没有在数据库中,需要创建一个新的账号并且保存到数据库中");
|
||||
// 如果没有,就要创建一个新的并且保存到数据库。
|
||||
// 如果不存在,表示这是一个新的账号,需要创建一下这个账号。
|
||||
account = await PlayerFactory.Create(scene, accountId);
|
||||
|
||||
account.Level = 99;
|
||||
account.NickName = "王麻子";
|
||||
account.Country = "cn";
|
||||
account.Exp = 999;
|
||||
account.Head = "xxx.png";
|
||||
|
||||
for (int i = 0; i < 500; i++)
|
||||
{
|
||||
var item = Entity.Create<Item>(scene, true, true);
|
||||
account.Items.Add(item.Id, item);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 500; i++)
|
||||
{
|
||||
var item = Entity.Create<Fish>(scene, true, true);
|
||||
account.Fishes.Add(item.Id, item);
|
||||
}
|
||||
// account.Items
|
||||
|
||||
account.NeedSave = true;
|
||||
|
||||
await account.SaveDataBase();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Debug("检查到账号在数据库中");
|
||||
}
|
||||
|
||||
Log.Debug("把当前账号添加到缓存中");
|
||||
// 把创建完成的Account放入到缓存中
|
||||
gameAccountManageComponent.Add(account);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Debug("检测到当前账号已经在缓存中了");
|
||||
// 如果有延迟下线的计划任务,那就先取消一下。
|
||||
account.CancelTimeout();
|
||||
// 如果在Gate的缓存中已经存在了该账号那只能以下几种可能:
|
||||
// 1、同一客户端发送了重复登录的请求数据。
|
||||
// 2、客户端经历的断线然后又重新连接到这个服务器上了(断线重连)。
|
||||
// 3、多个客户端同时登录了这个账号(顶号)。
|
||||
|
||||
if (request.GateRouteId == account.SessionRunTimeId)
|
||||
{
|
||||
// 如果执行到这里,说明是客户端发送了多次登录的请求,这样的情况下,直接返回就可以了,不需要做任何操作。
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
account.LoginTime = TimeHelper.Now;
|
||||
var account = await gameAccountManageComponent.Online(scene, request.AccountId, request.GateRouteId);
|
||||
|
||||
response.RoleRouteId = account.RuntimeId;
|
||||
if (account.GetComponent<MailComponent>() == null)
|
||||
{
|
||||
var mailComponent = await scene.World.DataBase.Query<MailComponent>(account.Id, true);
|
||||
if (mailComponent == null)
|
||||
{
|
||||
//如果没有邮件组件
|
||||
account.AddComponent<MailComponent>();
|
||||
}
|
||||
else
|
||||
{
|
||||
account.AddComponent(mailComponent);
|
||||
}
|
||||
}
|
||||
|
||||
await FTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,11 @@
|
||||
|
||||
namespace NB.Game;
|
||||
|
||||
public sealed class ContainerDestroySystem : DestroySystem<Container>
|
||||
public sealed class ItemContainerDestroySystem : DestroySystem<ItemContainer>
|
||||
{
|
||||
protected override void Destroy(Container self)
|
||||
protected override void Destroy(ItemContainer self)
|
||||
{
|
||||
self.CellCount = 0;
|
||||
self.CellCountMax = 0;
|
||||
self.CurrentCellCount = 0;
|
||||
|
||||
foreach (var (_, item) in self.Items)
|
||||
{
|
||||
@@ -16,13 +14,12 @@ public sealed class ContainerDestroySystem : DestroySystem<Container>
|
||||
}
|
||||
|
||||
self.Items.Clear();
|
||||
self.ItemsByCell.Clear();
|
||||
self.ItemsByConfigId.Clear();
|
||||
self.ItemsByType.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ContainerSystem
|
||||
public static class ItemContainerSystem
|
||||
{
|
||||
#region Get
|
||||
|
||||
@@ -33,30 +30,18 @@ public static class ContainerSystem
|
||||
/// <param name="id"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public static bool GetItemById(this Container self, uint id, out Item item)
|
||||
public static bool GetItemById(this ItemContainer self, uint id, out Item item)
|
||||
{
|
||||
return self.Items.TryGetValue(id, out item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过格子位置获取物品
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
/// <param name="cell"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public static bool GetItemByCell(this Container self, uint cell, out Item item)
|
||||
{
|
||||
return self.ItemsByCell.TryGetValue(cell, out item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过配置id获取物品
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
/// <param name="configId"></param>
|
||||
/// <param name="items"></param>
|
||||
public static void GetItemByConfigId(this Container self, uint configId, List<Item> items)
|
||||
public static void GetItemByConfigId(this ItemContainer self, uint configId, List<Item> items)
|
||||
{
|
||||
if (!self.ItemsByConfigId.TryGetValue(configId, out var itemList))
|
||||
{
|
||||
@@ -72,7 +57,7 @@ public static class ContainerSystem
|
||||
/// <param name="self"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="items"></param>
|
||||
public static void GetItemByType(this Container self, ItemType type, List<Item> items)
|
||||
public static void GetItemByType(this ItemContainer self, ItemType type, List<Item> items)
|
||||
{
|
||||
if (!self.ItemsByType.TryGetValue((uint)type, out var itemList))
|
||||
{
|
||||
178
Hotfix/Game/Player/Components/PlayerManageComponentSystem.cs
Normal file
178
Hotfix/Game/Player/Components/PlayerManageComponentSystem.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using Fantasy;
|
||||
using Fantasy.Async;
|
||||
using Fantasy.Entitas;
|
||||
using Fantasy.Entitas.Interface;
|
||||
using Fantasy.Helper;
|
||||
|
||||
#pragma warning disable CS8603 // 可能返回 null 引用。
|
||||
|
||||
namespace NB.Game;
|
||||
|
||||
public sealed class PlayerManageComponentAwakeSystem : AwakeSystem<PlayerManageComponent>
|
||||
{
|
||||
protected override void Awake(PlayerManageComponent self)
|
||||
{
|
||||
self.AutoSaveTimerId =
|
||||
self.Scene.TimerComponent.Net.RepeatedTimer(1000 * 60, () => { _ = self.CheckAutoSave(); });
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PlayerManageComponentDestroySystem : DestroySystem<PlayerManageComponent>
|
||||
{
|
||||
protected override void Destroy(PlayerManageComponent self)
|
||||
{
|
||||
foreach (var (_, gameAccount) in self.Players)
|
||||
{
|
||||
gameAccount.Dispose();
|
||||
}
|
||||
|
||||
self.Players.Clear();
|
||||
self.Scene.TimerComponent.Net.Remove(self.AutoSaveTimerId);
|
||||
self.AutoSaveTimerId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlayerManageComponentSystem
|
||||
{
|
||||
#region 上线下线
|
||||
|
||||
/// <summary>
|
||||
/// 玩家上线
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="accountId"></param>
|
||||
public static async FTask<Player> Online(this PlayerManageComponent self, Scene scene, long accountId,
|
||||
long gateRouteId)
|
||||
{
|
||||
Log.Debug("检查账号是否在缓存中");
|
||||
if (!self.TryGet(accountId, out var account))
|
||||
{
|
||||
// 首先要先到数据库中查询是否有这个账号
|
||||
account = await PlayerHelper.LoadDataBase(scene, accountId);
|
||||
// 如果有的话,就直接加入在缓存中就可以了
|
||||
if (account == null)
|
||||
{
|
||||
Log.Debug("检查到账号没有在数据库中,需要创建一个新的账号并且保存到数据库中");
|
||||
// 如果没有,就要创建一个新的并且保存到数据库。
|
||||
// 如果不存在,表示这是一个新的账号,需要创建一下这个账号。
|
||||
account = PlayerFactory.Create(scene, accountId);
|
||||
|
||||
|
||||
account.Basic.Level = 99;
|
||||
account.Basic.NickName = "王麻子";
|
||||
account.Basic.Country = "cn";
|
||||
account.Basic.Exp = 999;
|
||||
account.Basic.Head = "xxx.png";
|
||||
|
||||
// for (int i = 0; i < 500; i++)
|
||||
// {
|
||||
// var item = Entity.Create<Item>(scene, true, true);
|
||||
// account.ItemContainer.Add(item.Id, item);
|
||||
// }
|
||||
//
|
||||
// for (int i = 0; i < 500; i++)
|
||||
// {
|
||||
// var item = Entity.Create<Fish>(scene, true, true);
|
||||
// account.Fishes.Add(item.Id, item);
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Debug("检查到账号在数据库中");
|
||||
}
|
||||
|
||||
Log.Debug("把当前账号添加到缓存中");
|
||||
// 把创建完成的Account放入到缓存中
|
||||
self.Add(account);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Debug("检测到当前账号已经在缓存中了");
|
||||
// 如果有延迟下线的计划任务,那就先取消一下。
|
||||
account.CancelTimeout();
|
||||
// 如果在Gate的缓存中已经存在了该账号那只能以下几种可能:
|
||||
// 1、同一客户端发送了重复登录的请求数据。
|
||||
// 2、客户端经历的断线然后又重新连接到这个服务器上了(断线重连)。
|
||||
// 3、多个客户端同时登录了这个账号(顶号)。
|
||||
|
||||
if (gateRouteId == account.SessionRunTimeId)
|
||||
{
|
||||
// 如果执行到这里,说明是客户端发送了多次登录的请求,这样的情况下,直接返回就可以了,不需要做任何操作。
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
account.Statistics.LoginTime = TimeHelper.Now;
|
||||
|
||||
|
||||
if (account.GetComponent<MailComponent>() == null)
|
||||
{
|
||||
var mailComponent = await scene.World.DataBase.Query<MailComponent>(account.Id, true);
|
||||
if (mailComponent == null)
|
||||
{
|
||||
//如果没有邮件组件
|
||||
account.AddComponent<MailComponent>();
|
||||
}
|
||||
else
|
||||
{
|
||||
account.AddComponent(mailComponent);
|
||||
}
|
||||
}
|
||||
|
||||
await account.Save();
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 获取&移除
|
||||
|
||||
public static void Add(this PlayerManageComponent self, Player account)
|
||||
{
|
||||
self.Players.Add(account.Id, account);
|
||||
}
|
||||
|
||||
public static Player Get(this PlayerManageComponent self, long accountId)
|
||||
{
|
||||
return self.Players.GetValueOrDefault(accountId);
|
||||
}
|
||||
|
||||
public static bool TryGet(this PlayerManageComponent self, long accountId, out Player account)
|
||||
{
|
||||
return self.Players.TryGetValue(accountId, out account);
|
||||
}
|
||||
|
||||
public static void Remove(this PlayerManageComponent self, long accountId, bool isDispose = true)
|
||||
{
|
||||
if (!self.Players.Remove(accountId, out var account))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
account.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 自动保存
|
||||
|
||||
public static async FTask CheckAutoSave(this PlayerManageComponent self)
|
||||
{
|
||||
foreach (var (_, player) in self.Players)
|
||||
{
|
||||
if (player.NeedSave)
|
||||
{
|
||||
await player.SaveImmediately();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
35
Hotfix/Game/Player/Entity/PlayerSystem.cs
Normal file
35
Hotfix/Game/Player/Entity/PlayerSystem.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Fantasy;
|
||||
using Fantasy.Async;
|
||||
using Fantasy.Entitas;
|
||||
using Fantasy.Entitas.Interface;
|
||||
using Fantasy.Helper;
|
||||
|
||||
#pragma warning disable CS8625 // 无法将 null 字面量转换为非 null 的引用类型。
|
||||
|
||||
namespace NB.Game;
|
||||
|
||||
public sealed class PlayerDestroySystem : DestroySystem<Player>
|
||||
{
|
||||
protected override void Destroy(Player self)
|
||||
{
|
||||
self.Basic.Dispose();
|
||||
self.Basic = null;
|
||||
self.ItemContainer.Dispose();
|
||||
self.ItemContainer = null;
|
||||
self.FishContainer.Dispose();
|
||||
self.FishContainer = null;
|
||||
self.Wallet.Dispose();
|
||||
self.Wallet = null;
|
||||
self.Vip.Dispose();
|
||||
self.Vip = null;
|
||||
self.Statistics.Dispose();
|
||||
self.Statistics = null;
|
||||
|
||||
self.SessionRunTimeId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlayerSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -15,17 +15,10 @@ public static class PlayerFactory
|
||||
/// <param name="aId">ToKen令牌传递过来的aId</param>
|
||||
/// <param name="isSaveDataBase">是否在创建的过程中保存到数据库</param>
|
||||
/// <returns></returns>
|
||||
public static async FTask<Player> Create(Scene scene, long aId, bool isSaveDataBase = true)
|
||||
public static Player Create(Scene scene, long aId)
|
||||
{
|
||||
var gameAccount = Entity.Create<Player>(scene, aId, false, false);
|
||||
gameAccount.LoginTime = gameAccount.CreateTime = TimeHelper.Now;
|
||||
|
||||
|
||||
if (isSaveDataBase)
|
||||
{
|
||||
await gameAccount.SaveDataBase();
|
||||
}
|
||||
|
||||
return gameAccount;
|
||||
var player = Entity.Create<Player>(scene, aId, true, true);
|
||||
player.InitializeChildEntity();
|
||||
return player;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,63 @@
|
||||
using Fantasy;
|
||||
using Fantasy.Async;
|
||||
using Fantasy.Entitas;
|
||||
using Fantasy.Helper;
|
||||
using Fantasy.Network;
|
||||
|
||||
namespace NB.Game;
|
||||
|
||||
public static class PlayerHelper
|
||||
{
|
||||
public static void InitializeChildEntity(this Player self)
|
||||
{
|
||||
if (self.Basic == null)
|
||||
{
|
||||
self.Basic = Entity.Create<PlayerBasic>(self.Scene, true, true);
|
||||
}
|
||||
|
||||
if (self.ItemContainer == null)
|
||||
{
|
||||
self.ItemContainer = Entity.Create<ItemContainer>(self.Scene, true, true);
|
||||
}
|
||||
|
||||
if (self.FishContainer == null)
|
||||
{
|
||||
self.FishContainer = Entity.Create<FishContainer>(self.Scene, true, true);
|
||||
}
|
||||
|
||||
if (self.Wallet == null)
|
||||
{
|
||||
self.Wallet = Entity.Create<PlayerWallet>(self.Scene, true, true);
|
||||
}
|
||||
|
||||
if (self.Vip == null)
|
||||
{
|
||||
self.Vip = Entity.Create<PlayerVip>(self.Scene, true, true);
|
||||
}
|
||||
|
||||
if (self.Statistics == null)
|
||||
{
|
||||
self.Statistics = Entity.Create<PlayerStatistics>(self.Scene, true, true);
|
||||
self.Statistics.LoginTime = self.Statistics.CreateTime = TimeHelper.Now;
|
||||
}
|
||||
}
|
||||
|
||||
public static async FTask SaveImmediately(this Player self)
|
||||
{
|
||||
await self.Scene.World.DataBase.Save(self);
|
||||
self.NeedSave = false;
|
||||
Log.Info($"player id:{self.Id} save data to dataBase");
|
||||
}
|
||||
|
||||
public static async FTask Save(this Player self)
|
||||
{
|
||||
self.NeedSave = true;
|
||||
self.NeedSaveTime = TimeHelper.Now + PlayerManageComponent.AutoSaveTime;
|
||||
//先立马保存,后续做缓存
|
||||
await self.Scene.World.DataBase.Save(self);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 从数据库中读取GameAccount
|
||||
/// </summary>
|
||||
@@ -24,14 +76,6 @@ public static class PlayerHelper
|
||||
return account;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存账号到数据库中
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
public static async FTask SaveDataBase(this Player self)
|
||||
{
|
||||
await self.Scene.World.DataBase.Save(self);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行该账号的断开逻辑,不要非必要不要使用这个接口,这个接口是内部使用。
|
||||
@@ -40,7 +84,7 @@ public static class PlayerHelper
|
||||
public static async FTask Disconnect(this Player self)
|
||||
{
|
||||
// 保存该账号信息到数据库中。
|
||||
await SaveDataBase(self);
|
||||
await SaveImmediately(self);
|
||||
// 在缓存中移除自己,并且执行自己的Dispose方法。
|
||||
self.Scene.GetComponent<PlayerManageComponent>().Remove(self.Id);
|
||||
}
|
||||
@@ -59,7 +103,7 @@ public static class PlayerHelper
|
||||
// 如果是心跳检测断开的Session,我怎么能拿到当前的这个账号来进行下线处理呢?
|
||||
// 通过给当前的Session挂载一个组件,当销毁这个Session时候呢,也会销毁这个组件。
|
||||
// 这样的话,是不是可以在登录的时候,给这个组件把AccountId存到这个组件呢?
|
||||
|
||||
|
||||
// 要检查当前缓存中是否存在该账号的数据
|
||||
var gameAccountManageComponent = scene.GetComponent<PlayerManageComponent>();
|
||||
if (!gameAccountManageComponent.TryGet(accountId, out var account))
|
||||
@@ -68,19 +112,23 @@ public static class PlayerHelper
|
||||
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");
|
||||
Log.Warning(
|
||||
$"GameAccountHelper Disconnect accountId : {accountId} SessionRunTimeId : {account.SessionRunTimeId} not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果不存在定时任务的组件,那就添加并设置定时任务
|
||||
if (account.IsTimeOutComponent())
|
||||
{
|
||||
// 如果已经存在了,那就表示当然已经有一个延时断开的任务了,那就不需要重复添加了
|
||||
return;
|
||||
}
|
||||
|
||||
// 立即下线处理
|
||||
if (timeOut <= 0)
|
||||
{
|
||||
@@ -88,6 +136,7 @@ public static class PlayerHelper
|
||||
await account.Disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置延迟下线
|
||||
account.SetTimeout(timeOut, account.Disconnect);
|
||||
}
|
||||
@@ -102,11 +151,11 @@ public static class PlayerHelper
|
||||
// 其实可以不用每次都NEW一个新的GameAccountInfo
|
||||
// 可以在当前账号下创建一个GameAccountInfo,每次变动会提前通知这个GameAccountInfo
|
||||
// 又或者每次调用该方法的时候,把值重新赋值一下。
|
||||
|
||||
|
||||
return new GameAccountInfo()
|
||||
{
|
||||
CreateTime = self.CreateTime,
|
||||
LoginTime = self.LoginTime
|
||||
CreateTime = self.Statistics.CreateTime,
|
||||
LoginTime = self.Statistics.LoginTime
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace NB.Game.System;
|
||||
|
||||
public class ItemManagerComponentSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace NB.Game;
|
||||
|
||||
public class PlayerAutoSaveComponentSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using Fantasy.Entitas.Interface;
|
||||
|
||||
namespace NB.Game;
|
||||
|
||||
public sealed class PlayerDestroySystem : DestroySystem<Player>
|
||||
{
|
||||
protected override void Destroy(Player self)
|
||||
{
|
||||
self.CreateTime = 0;
|
||||
self.LoginTime = 0;
|
||||
self.SessionRunTimeId = 0;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using Fantasy.Entitas.Interface;
|
||||
|
||||
namespace NB.Game;
|
||||
|
||||
public sealed class PlayerManageComponentDestroySystem : DestroySystem<PlayerManageComponent>
|
||||
{
|
||||
protected override void Destroy(PlayerManageComponent self)
|
||||
{
|
||||
foreach (var (_, gameAccount) in self.Players)
|
||||
{
|
||||
gameAccount.Dispose();
|
||||
}
|
||||
|
||||
self.Players.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlayerManageComponentSystem
|
||||
{
|
||||
public static void Add(this PlayerManageComponent self, Player account)
|
||||
{
|
||||
self.Players.Add(account.Id, account);
|
||||
}
|
||||
|
||||
public static Player Get(this PlayerManageComponent self, long accountId)
|
||||
{
|
||||
return self.Players.GetValueOrDefault(accountId);
|
||||
}
|
||||
|
||||
public static bool TryGet(this PlayerManageComponent self, long accountId, out Player account)
|
||||
{
|
||||
return self.Players.TryGetValue(accountId, out account);
|
||||
}
|
||||
|
||||
public static void Remove(this PlayerManageComponent self, long accountId, bool isDispose = true)
|
||||
{
|
||||
if (!self.Players.Remove(accountId, out var account))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
account.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,4 @@
|
||||
<ProjectReference Include="..\Fantasy\Fantasy.Net\Fantasy.Net\Fantasy.Net.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Game\Mail\Handler\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArraySegment_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bc9cdb23bc146bcaaae0bb9e45e5d46d9dc00_003Fa7_003F744b85f8_003FArraySegment_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAssembly_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bc9cdb23bc146bcaaae0bb9e45e5d46d9dc00_003F81_003F68739b38_003FAssembly_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAttributes_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F7c3673bb3c794e71b1f094110cbaaa7bbcf5ba03630ccc4e84be1dba9dc586f_003FAttributes_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABsonClassMap_002Ecs_002Fl_003AC_0021_003FUsers_003Fbob_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F2eb023375b7879b26d41bf37f27d5d84cf356cf593d71659a9d432322829914c_003FBsonClassMap_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABsonElementAttribute_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3be33698cf8a4c6ea48ef124c9ddacad81800_003Ff5_003F5d71aba0_003FBsonElementAttribute_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABsonIgnoreAttribute_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F39988e239fb94b73b8bdb00ab8b87d1f82400_003F63_003Fca262714_003FBsonIgnoreAttribute_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AControllerBase_002Ecs_002Fl_003AC_0021_003FUsers_003Fbob_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F0207e94f3a3e4dca931382ccf94981471de930_003Fd2_003Fe3b49283_003FControllerBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
|
||||
Reference in New Issue
Block a user