自动定时数据落地

This commit is contained in:
bob
2025-08-01 18:05:25 +08:00
parent 96f22a3e48
commit c97bd0ab55
30 changed files with 477 additions and 310 deletions

View File

@@ -29,7 +29,6 @@
<ItemGroup>
<Folder Include="Game\Activity\" />
<Folder Include="Game\Skill\" />
<Folder Include="Map\" />
</ItemGroup>

View File

@@ -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
{
}

View File

@@ -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;
}

View 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>();
}

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -0,0 +1,10 @@
using Fantasy.Entitas;
namespace NB.Game;
/// <summary>
/// 角色任务
/// </summary>
public class Mission : Entity
{
}

View 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;
}

View 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;
}

View File

@@ -1,4 +1,5 @@
using Fantasy.Entitas;
using MongoDB.Bson.Serialization.Attributes;
namespace NB;

View File

@@ -3,8 +3,9 @@
namespace NB;
/// <summary>
/// 角色状态标志量
/// 玩家钓组
/// </summary>
public class PlayerDayFlags
public class PlayerTackle : Entity
{
}

View File

@@ -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

View 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();
}

View File

@@ -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;
}

View File

@@ -1,8 +0,0 @@
using Fantasy.Entitas;
namespace NB.Game;
public class PlayerAutoSaveComponent : Entity
{
}

View File

@@ -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();
}

View File

@@ -0,0 +1,11 @@
using Fantasy.Entitas;
namespace NB.Game;
/// <summary>
/// 角色技能
/// </summary>
public class Skill : Entity
{
}

View File

@@ -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;
}
}

View File

@@ -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))
{

View 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
}

View 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
{
}

View File

@@ -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;
}
}

View File

@@ -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
};
}
}

View File

@@ -1,6 +0,0 @@
namespace NB.Game.System;
public class ItemManagerComponentSystem
{
}

View File

@@ -1,6 +0,0 @@
namespace NB.Game;
public class PlayerAutoSaveComponentSystem
{
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -12,8 +12,4 @@
<ProjectReference Include="..\Fantasy\Fantasy.Net\Fantasy.Net\Fantasy.Net.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Game\Mail\Handler\" />
</ItemGroup>
</Project>

View File

@@ -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>