提交修改

This commit is contained in:
Bob.Song
2026-03-19 16:14:33 +08:00
parent c2ec7226c0
commit 34070d0769
90 changed files with 3016 additions and 1765 deletions

View File

@@ -0,0 +1,74 @@
namespace NB.Game;
public static class ItemUseHelper
{
// public static uint UseBagItem(Account account, long itemId, int count)
// {
// return UseItem(account, ContainerType.Bag, itemId, count);
// }
//
// public static uint CanUseBagItem(Account account, long itemId, int count, out Container container, out ItemConfig itemConfig, out ItemUseComponent itemUseComponent)
// {
// return CanUseItem(account, ContainerType.Bag, itemId, count, out container, out itemConfig, out itemUseComponent);
// }
//
// public static uint UseItem(Account account, ContainerType containerType, long itemId, int count)
// {
// var errorCode = CanUseItem(account, containerType, itemId, count, out var container, out var itemConfig, out var itemUseComponent);
//
// if (errorCode != 0)
// {
// return errorCode;
// }
//
// errorCode = container.RemoveItem(itemId, count, ItemReason.ItemUse, true);
//
// if (errorCode != 0)
// {
// return errorCode;
// }
//
// itemUseComponent.Use(account, itemConfig, ref count);
// return 0;
// }
//
// public static uint CanUseItem(Player account, long itemId, int count, out cfg.Item itemConfig,
// out ItemUseComponent itemUseComponent)
// {
// itemConfig = null;
// itemUseComponent = null;
// var errorCode = ContainerHelper.TryGetContainer(account, containerType, out container);
//
// if (errorCode != 0)
// {
// return errorCode;
// }
//
// if (!container.GetItemById(itemId, out var item))
// {
// // 这里是找不到该物品的错误码。
// return 1;
// }
//
// if (item.Count < count)
// {
// // 这里是物品数量不足的错误码。
// return 2;
// }
//
// itemConfig = item.Config;
//
// if (itemConfig.Params.Length <= 0)
// {
// // 这里是物品没有配置参数的错误码。
// return 3;
// }
//
// // 这里还可以增加一些其他的判定,比如物品是否过期,物品是否被锁定等。
// // 甚至还有物品使用的CD。
// // 使用物品效果来判定
// itemUseComponent = account.Scene.GetComponent<ItemUseComponent>();
// return itemUseComponent.CanUse(account, item.Config, ref count);
// }
}

View File

@@ -0,0 +1,51 @@
using Fantasy;
namespace NB.Game;
[ItemUse(ItemUseEffect.AddAttr)]
public class ItemUse_Drug_AddAttr : IItemUse
{
// public uint CanUse(Account account, ItemConfig config, ref int count)
// {
// if (config.Params.Length < 2)
// {
// Log.Error($"configId:{config.Id} config.Params.Length({config.Params.Length}) < 2");
// return 1;
// }
//
// Log.Debug($"CanUse 使用了药品增加属性 configId:{config.Id} count:{count}");
// return 0;
// }
//
// public void Use(Account account, ItemConfig config, ref int count)
// {
// for (int i = 0; i < config.Params.Length; i += 2)
// {
// var attrKey = config.Params[i];
// var attrValue = config.Params[i + 1];
// Log.Debug($"Use 使用了药品增加属性 configId:{config.Id} attrKey:{attrKey} attrValue:{attrValue}");
// }
// }
public uint CanUse(Player player, cfg.Item config, ref int count)
{
// if (config.Params.Length < 2)
// {
// Log.Error($"configId:{config.Id} config.Params.Length({config.Params.Length}) < 2");
// return 1;
// }
//
Log.Debug($"CanUse 使用了药品增加属性 configId:{config.Id} count:{count}");
return 0;
}
public void Use(Player player, cfg.Item config, ref int count)
{
// for (int i = 0; i < config.Params.Length; i += 2)
// {
// var attrKey = config.Params[i];
// var attrValue = config.Params[i + 1];
// Log.Debug($"Use 使用了药品增加属性 configId:{config.Id} attrKey:{attrKey} attrValue:{attrValue}");
// }
Log.Debug($"Use 使用了药品增加属性 configId:{config.Id} count:{count}");
}
}

View File

@@ -0,0 +1,18 @@
using Fantasy;
namespace NB.Game;
[ItemUse(ItemUseEffect.Equip)]
public class ItemUse_Equip_Equip : IItemUse
{
public uint CanUse(Player player, cfg.Item config, ref int count)
{
Log.Debug($"CanUse 使用了装备装备到身上 configId:{config.Id} count:{count}");
return 0;
}
public void Use(Player player, cfg.Item config, ref int count)
{
Log.Debug($"Use 使用了装备装备到身上 configId:{config.Id} count:{count}");
}
}

View File

@@ -0,0 +1,14 @@
using Fantasy;
using Fantasy.Async;
using Fantasy.Network.Interface;
using NBF;
namespace NB;
public class Other2Mail_SendRequestHandler : AddressRPC<Scene, Other2Mail_SendMailRequest, Mail2Other_SendMailResponse>
{
protected override async FTask Run(Scene scene, Other2Mail_SendMailRequest request, Mail2Other_SendMailResponse response, Action reply)
{
await MailHelper.Send(scene, request.MailBox);
}
}

View File

@@ -0,0 +1,29 @@
using Fantasy;
using Fantasy.Async;
using Fantasy.Network.Interface;
using NB.Game;
using NBF;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
namespace NB;
public sealed class C2Mail_GetHaveMailRequestHandler : AddressRPC<Player, C2Mail_GetHaveMailRequest, Mail2C_GetHaveMailResposne>
{
protected override async FTask Run(Player mailUnit, C2Mail_GetHaveMailRequest request, Mail2C_GetHaveMailResposne response, Action reply)
{
var mailComponent = mailUnit.GetComponent<MailComponent>();
// 这个mailComponent是不是可能会为空?答案是可能的。
// 那如果是空的怎么办呢,这样情况只能是别人恶意发包了。
if (mailComponent == null)
{
return;
}
// 检查是否有超时的邮件。如果有那就清楚掉
await mailComponent.CheckTimeOut();
// 领取当前的邮件
mailComponent.GetMailSimplifyInfos(response.Mails);
}
}

View File

@@ -0,0 +1,35 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
using NB;
using NB.Game;
using NBF;
namespace Fantasy;
public class C2Mail_OpenMailRequestHandler : AddressRPC<Player, C2Mail_OpenMailRequest, Mail2C_OpenMailResposne>
{
protected override async FTask Run(Player mailUnit, C2Mail_OpenMailRequest request, Mail2C_OpenMailResposne response, Action reply)
{
if (request.MailId <= 0)
{
response.ErrorCode = 100;
return;
}
// 根据这个MailId来拿到邮件的详细信息
var (errorCode, mail) = await mailUnit.GetComponent<MailComponent>().OpenMail(request.MailId);
if (errorCode != 0)
{
response.ErrorCode = errorCode;
return;
}
if (!request.ReturnMailInfo)
{
return;
}
response.MailInfo = mail.ToMailInfo();
}
}

View File

@@ -0,0 +1,22 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
using NB;
using NB.Game;
using NBF;
namespace Fantasy;
public class C2Mail_ReceiveMailRequestHandler : AddressRPC<Player, C2Mail_ReceiveMailRequest, Mail2C_ReceiveMailResponse>
{
protected override async FTask Run(Player mailUnit, C2Mail_ReceiveMailRequest request, Mail2C_ReceiveMailResponse response, Action reply)
{
if (request.MailId <= 0)
{
response.ErrorCode = 100;
return;
}
response.ErrorCode = await mailUnit.GetComponent<MailComponent>()
.Receive(request.MailId, request.Money, request.ItemId, true);
}
}

View File

@@ -0,0 +1,24 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
using NB;
using NB.Game;
using NBF;
namespace Fantasy;
public class C2Mail_RemoveMailRequestHandler : AddressRPC<Player,C2Mail_RemoveMailRequest, Mail2C_RemoveMailResponse>
{
protected override async FTask Run(Player mailUnit, C2Mail_RemoveMailRequest request, Mail2C_RemoveMailResponse response, Action reply)
{
if (request.MailId <= 0)
{
// 这里的1代表MailId不正确。
response.ErrorCode = 1;
return;
}
response.ErrorCode = await mailUnit.GetComponent<MailComponent>()
.Remove(request.MailId, MailRemoveActionType.Remove, true);
}
}

View File

@@ -0,0 +1,73 @@
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.Network.Interface;
using NB;
using NB.Game;
using NBF;
namespace Fantasy;
public class C2Mail_SendMailRequestHandler : AddressRPC<Player, C2Mail_SendMailRequest, Mail2C_SendMailResponse>
{
protected override async FTask Run(Player mailUnit, C2Mail_SendMailRequest request,
Mail2C_SendMailResponse response, Action reply)
{
if (request.UserId < 1)
{
// 这里的1代表的是发送的接收玩家名字不正确。
response.ErrorCode = 1;
return;
}
if (string.IsNullOrEmpty(request.Title) || string.IsNullOrEmpty(request.Content))
{
// 这里的2代表的是发送的邮件标题或者内容不正确。
response.ErrorCode = 2;
return;
}
if (request.ItemId.Count > 10)
{
// 这里的3代表的是发送的邮件附件超出了最大范围。
response.ErrorCode = 3;
return;
}
if (!mailUnit.Scene.GetComponent<PlayerManageComponent>().TryGet(request.UserId, out var receiveMailUnit))
{
// 这里的4代表的是没有该玩家。
response.ErrorCode = 4;
return;
}
if (request.Money > 0)
{
// 如果大于0就要调用某一个接口去货币所在的服务器上面去扣除玩家的钱。
// var moneyResposne = await MoneyHelper.Cost(mailUnit.Scene, request.Money);
// if (moneyResposne.ErrorCode != 0)
// {
// response.ErrorCode = moneyResposne.ErrorCode;
// return;
// }
}
using var mailItems = ListPool<Item>.Create();
if (request.ItemId.Count > 0)
{
// var itemResposne = await BagHelper.Get(mailUnit.Scene, request.ItemId);
// if (itemResposne.ErrorCode != 0)
// {
// response.ErrorCode = itemResposne.ErrorCode;
// return;
// }
// mailItems.AddRange(itemResposne.Items);
}
var accountId = ListPool<long>.Create(receiveMailUnit.Id);
var mail = MailFactory.Create(mailUnit.Scene, MailType.User, request.Title, request.Content, request.Money,
mailItems);
var mailBox = MailBoxFactory.Create(mailUnit.Scene, MailBoxType.Specify, mailUnit.Id, mail,
1000 * 60 * 60, accountId);
await MailHelper.Send(mailUnit.Scene, mailBox);
}
}

View File

@@ -0,0 +1,22 @@
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.Network.Interface;
using NB;
using NB.Game;
using NBF;
namespace Fantasy;
public class C2Mail_TestRequestHandler : AddressRPC<Player, C2Mail_TestRequest, Mail2C_TestResponse>
{
protected override async FTask Run(Player mailUnit, C2Mail_TestRequest request, Mail2C_TestResponse response, Action reply)
{
Log.Debug($"这是一个测试的自定义消息协议 Tag:{mailUnit.Id}");
response.Tag = "666";
// using var accountId = ListPool<long>.Create(65491190472245249);
var mail = MailFactory.Create(mailUnit.Scene, MailType.System, "测试所有人指定日期玩家邮件", "测试所有人指定日期玩家邮件内容", 9991);
var mailBox = MailBoxFactory.Create(mailUnit.Scene, MailBoxType.AllToDate, mailUnit.Id, mail,
1000 * 60 * 60);
await MailHelper.Send(mailUnit.Scene, mailBox);
}
}

View File

@@ -0,0 +1,65 @@
using Fantasy;
using Fantasy.Entitas;
using Fantasy.Helper;
using NB;
using NB.Game;
namespace NBF;
public static class MailBoxFactory
{
/// <summary>
/// 创建一个邮件箱
/// </summary>
/// <param name="scene"></param>
/// <param name="mailBoxType"></param>
/// <param name="sendAccountId"></param>
/// <param name="mail"></param>
/// <param name="expireTime"></param>
/// <param name="accountId"></param>
/// <returns></returns>
public static MailBox Create(Scene scene, MailBoxType mailBoxType, long sendAccountId, Mail mail, int expireTime,
List<long> accountId = null)
{
var mailBox = Entity.Create<MailBox>(scene, true, true);
mailBox.SendAccountId = sendAccountId;
mailBox.Mail = mail;
mailBox.MailBoxType = mailBoxType;
mailBox.CreateTime = TimeHelper.Now;
mailBox.ExpireTime = mailBox.CreateTime + expireTime;
if (accountId == null || accountId.Count <= 0)
{
return mailBox;
}
foreach (var raId in accountId)
{
mailBox.AccountId.Add(raId);
}
return mailBox;
}
/// <summary>
/// 创建一个邮件箱
/// </summary>
/// <param name="scene"></param>
/// <param name="mailType"></param>
/// <param name="mailBoxType"></param>
/// <param name="title"></param>
/// <param name="content"></param>
/// <param name="money"></param>
/// <param name="items"></param>
/// <param name="sendAccountId"></param>
/// <param name="expireTime"></param>
/// <param name="accountId"></param>
/// <returns></returns>
public static MailBox Create(Scene scene, MailType mailType, MailBoxType mailBoxType, string title, string content,
int money, List<Item> items,
long sendAccountId, int expireTime, List<long> accountId = null)
{
var mail = MailFactory.Create(scene, mailType, title, content, money, items);
return Create(scene, mailBoxType, sendAccountId, mail, expireTime, accountId);
}
}

View File

@@ -0,0 +1,45 @@
using Fantasy;
using Fantasy.Entitas;
using Fantasy.Serialize;
using NB;
using NB.Game;
namespace NBF;
public static class MailFactory
{
/// <summary>
/// 创建一个基础的邮件
/// </summary>
/// <param name="scene"></param>
/// <param name="title"></param>
/// <param name="content"></param>
/// <param name="money"></param>
/// <param name="items"></param>
/// <param name="mailType"></param>
/// <returns></returns>
public static Mail Create(Scene scene, MailType mailType, string title, string content, int money = 0,
List<Item> items = null)
{
var mail = Entity.Create<Mail>(scene, true, true);
mail.Title = title;
mail.Content = content;
mail.Money = money;
mail.MailType = mailType;
mail.MailState = MailState.Unread;
if (items != null && items.Count > 0)
{
foreach (var item in items)
{
// 最好的是要个这个Item给克隆出一份来。
// 这样就可以保证,无论外面怎么改变也不会影响这个邮件的东西了。
var cloneItem = SerializerManager.BsonPack.CloneEntity(item);
mail.Items.Add(cloneItem);
}
}
return mail;
}
}

View File

@@ -0,0 +1,205 @@
using Fantasy;
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.Platform.Net;
using Fantasy.Serialize;
using NB;
using NB.Game;
namespace NBF;
/// <summary>
/// 发送邮件的唯一接口
/// 如果不是通过这个接口发送的邮件、出现任何问题,后果自负
/// </summary>
public static class MailHelper
{
/// <summary>
/// 发送邮件
/// </summary>
/// <param name="scene"></param>
/// <param name="mailBox">发送完成后记着一定要销毁这个MailBox不然会有GC。</param>
public static async FTask Send(Scene scene, MailBox mailBox)
{
if (scene.SceneType == SceneType.Game)
{
await InnerSend(scene, mailBox);
return;
}
// 如果不在同一个Scene下就需要发送内部的网络消息给这个Scene了
var mailSceneConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Game)[0];
// await scene.NetworkMessagingComponent.CallInnerRoute(mailSceneConfig.RouteId, new Other2Mail_SendMailRequest()
// {
// MailBox = mailBox
// });
scene.NetworkMessagingComponent.Send(mailSceneConfig.Address, new Other2Mail_SendMailRequest()
{
MailBox = mailBox
});
}
/// <summary>
/// 发送邮件
/// </summary>
/// <param name="scene"></param>
/// <param name="mailBox"></param>
/// <returns></returns>
private static async FTask InnerSend(Scene scene, MailBox mailBox)
{
// 现在的情况这个接口只能是在Mail这个服务器下可以操作
// 但是真实的情况,其他同时开发的逻辑,调用这个接口一般都是在其他的服务器。
// 这个问题其实很好解决只需要判定当前Scene不是Mail、那就做一个协议自动发送到这个MailScene就可以了。
if (mailBox.MailBoxType == MailBoxType.None)
{
Log.Error("MailBoxType MailBoxType.None not support!");
return;
}
var playerManageComponent = scene.GetComponent<PlayerManageComponent>();
var mailBoxManageComponent = scene.GetComponent<MailBoxManageComponent>();
mailBoxManageComponent.MailBoxes.Add(mailBox.Id, mailBox);
switch (mailBox.MailBoxType)
{
case MailBoxType.Specify:
{
if (mailBox.AccountId.Count <= 0)
{
Log.Error($"{mailBox.Id} AccountId is 0!");
return;
}
// 这里可能有几种情况
// 1、AccountId里面可能只有一个人。
// 2、AccountId里面有多个人。
using var sendAccountIds = ListPool<long>.Create();
foreach (var accountId in mailBox.AccountId)
{
if (!playerManageComponent.TryGet(accountId, out var mailUnit))
{
// 如果没有的话,代表这个用户根本不存在。
// 那这样的情况就不需要做任何处理。
continue;
}
// 如果有的话,那么就给这个用户发送邮件。
// 这个玩家是否在线?
var mailComponent = mailUnit.GetComponent<MailComponent>();
// 在线的话,就直接发送邮件给这个玩家就可以了。
if (mailComponent != null)
{
await mailComponent.Add(SerializerManager.BsonPack.CloneEntity(mailBox.Mail), true);
sendAccountIds.Add(accountId);
}
else
{
// 不在线
// 首先
// 1、如果玩家不在线那就把这个邮件在数据库中拿出来然后把邮件插入到玩家的邮件列表里。然后再保存
// 这样的做法是不推荐的,因为咱们游戏所有的东西都是有状态的,但是你拿出来的邮件是没有状态的。这样可能会导致一些问题的出现。
// 正确的做做法:
// 把这个邮件方法一个地方,比如是一个处理中心,当玩家上线的时候,主动去这个中心领取这个邮件。
// 快递驿站、菜鸟、
mailBoxManageComponent.MailsByAccount.Add(accountId, mailBox);
Log.Debug("发送离线邮件成功,用户上线第一时间会领取这个邮件。");
}
}
// 移除掉发送成功的账号。
foreach (var sendAccountId in sendAccountIds)
{
mailBox.AccountId.Remove(sendAccountId);
}
// 如果没有任何收件人了、就可以把这个邮箱给删除了。
if (mailBox.AccountId.Count <= 0)
{
mailBox.Dispose();
}
else
{
// 当这个邮件箱还有没有接收的玩家时候,要保存到数据库中,方便下次停服维护再重新启动的时候,加载这个邮件箱。
await scene.World.Database.Save(mailBox);
Log.Debug("保存离线邮件成功");
}
break;
}
case MailBoxType.Online:
{
// // 这里有个问题,如何知道在线的人呢?
// foreach (var (_, mailUnit) in mailUnitManageComponent.UnitByAccountId)
// {
// var mailComponent = mailUnit.GetComponent<MailComponent>();
// if (mailComponent == null)
// {
// continue;
// }
// // 能指定到这里的都是在线的玩家。
// await mailComponent.Add(MailFactory.Serializer.Clone(mailBox.Mail), true);
// }
try
{
foreach (var (_, mailUnit) in playerManageComponent.Players)
{
await mailUnit.GetComponent<MailComponent>()
.Add(SerializerManager.BsonPack.CloneEntity(mailBox.Mail), true);
}
}
finally
{
mailBox.Dispose();
}
break;
}
case MailBoxType.All:
{
// 要保证这个邮件一定要有一个生命周期。并且这个周期一定要短如果是想要实现永久的可以比如30天发送一次。
mailBoxManageComponent.MailsByMailBoxType.Add((int)MailBoxType.All, mailBox);
// 首先给所有在线的玩家发送。
foreach (var (_, mailUnit) in playerManageComponent.Players)
{
await mailUnit.GetComponent<MailComponent>()
.Add(SerializerManager.BsonPack.CloneEntity(mailBox.Mail), true);
// 在邮件盒子中记录下玩家领取的记录,避免重复领取。
mailBox.Received.Add(mailUnit.Id);
}
// 保存邮件箱到数据库。
await scene.World.Database.Save(mailBox);
break;
}
case MailBoxType.AllToDate:
{
mailBoxManageComponent.MailsByMailBoxType.Add((int)MailBoxType.AllToDate, mailBox);
foreach (var (_, player) in playerManageComponent.Players)
{
if (player.CreateTime > mailBox.CreateTime)
{
// 如果执行到这里,表示这里用户是这个邮件创建之后的用户。这个就不要发送了
continue;
}
// 所以这个邮件类型的逻辑就是,给当前邮件创建时间之前的玩家发送。
await player.GetComponent<MailComponent>()
.Add(SerializerManager.BsonPack.CloneEntity(mailBox.Mail), true);
// 在邮件盒子中记录下玩家领取的记录,避免重复领取。
mailBox.Received.Add(player.Id);
}
// 保存邮件箱到数据库。
await scene.World.Database.Save(mailBox);
break;
}
// 根据玩家等级、等等这样的邮件箱类型,都可以自行扩展了
// 课下作业、自己实现一个起来类型的邮箱。
}
}
}

View File

@@ -0,0 +1,230 @@
using Fantasy;
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
using Fantasy.Serialize;
using NB;
using NB.Game;
namespace NBF;
public class MailBoxManageComponentDestroySystem : DestroySystem<MailBoxManageComponent>
{
protected override void Destroy(MailBoxManageComponent self)
{
if (self.TimerId != 0)
{
self.Scene.TimerComponent.Net.Remove(ref self.TimerId);
}
foreach (var (_, mailBox) in self.MailBoxes)
{
mailBox.Dispose();
}
self.MinTime = 0;
self.MailsByAccount.Clear();
self.MailsByMailBoxType.Clear();
self.Timers.Clear();
self.TimeOutQueue.Clear();
}
}
public static class MailBoxManageComponentSystem
{
public static async FTask Init(this MailBoxManageComponent self)
{
// 获取数据库中所有的没有处理完成的MailBox
var mailBoxes = await self.Scene.World.Database.Query<MailBox>(d => true);
foreach (var mailBox in mailBoxes)
{
self.MailBoxes.Add(mailBox.Id, mailBox);
switch (mailBox.MailBoxType)
{
case MailBoxType.Specify:
{
foreach (var accountId in mailBox.AccountId)
{
self.MailsByAccount.Add(accountId, mailBox);
}
break;
}
case MailBoxType.All:
case MailBoxType.AllToDate:
{
self.MailsByMailBoxType.Add((int)mailBox.MailBoxType, mailBox);
break;
}
}
}
self.TimerId = self.Scene.TimerComponent.Net.RepeatedTimer(MailBoxManageComponent.MailCheckTIme, self.Timeout);
}
private static void Timeout(this MailBoxManageComponent self)
{
var currentTime = TimeHelper.Now;
if (currentTime < self.MinTime)
{
return;
}
// 检查当然时候有过期的邮件箱需要处理
foreach (var (timeKey, _) in self.Timers)
{
if (timeKey > currentTime)
{
self.MinTime = timeKey;
break;
}
self.TimeOutQueue.Enqueue(timeKey);
}
while (self.TimeOutQueue.TryDequeue(out var timeKey))
{
foreach (var mailBoxId in self.Timers[timeKey])
{
Log.Info($"MailBox:{mailBoxId} 过期了!");
self.Remove(mailBoxId).Coroutine();
}
self.Timers.RemoveKey(timeKey);
}
}
public static async FTask Remove(this MailBoxManageComponent self, long mailBoxId)
{
if (!self.MailBoxes.Remove(mailBoxId, out var mailBox))
{
return;
}
// 先删除按照分类存储的邮箱
self.MailsByMailBoxType.RemoveValue((int)mailBox.MailBoxType, mailBox);
// 删除个人邮件的邮件箱
self.MailsByAccount.RemoveValue(mailBoxId, mailBox);
// 在数据库中删除这个邮件
await self.Scene.World.Database.Remove<MailBox>(mailBoxId);
// 销毁这个MailBox
mailBox.Dispose();
}
public static async FTask GetHaveMail(this MailBoxManageComponent self, Player mailUnit, bool sync)
{
var now = TimeHelper.Now;
var worldDataBase = self.Scene.World.Database;
var mailComponent = mailUnit.GetComponent<MailComponent>();
// 玩家领取范围邮件的逻辑处理
foreach (var (mailBoxType, mailBoxList) in self.MailsByMailBoxType)
{
using var removeMailBox = ListPool<MailBox>.Create();
switch ((MailBoxType)mailBoxType)
{
// 发送给所有人的邮件
case MailBoxType.All:
{
foreach (var mailBox in mailBoxList)
{
if (!mailBox.Received.Contains(mailUnit.Id))
{
// 如果是没有领取过这个邮件,首先要把自己添加到领取过的名单中。
mailBox.Received.Add(mailUnit.Id);
// 发送给自己的邮件列表里添加邮件。
await mailUnit.GetComponent<MailComponent>().Add(SerializerManager.BsonPack.CloneEntity(mailBox.Mail), true);
}
if (mailBox.ExpireTime <= now)
{
// 邮件已经过期了,要进行清理这个邮件的操作了
removeMailBox.Add(mailBox);
continue;
}
// 保存邮件箱状态到数据库中。
await worldDataBase.Save(mailBox);
}
foreach (var mailBox in removeMailBox)
{
await self.Remove(mailBox.Id);
}
// 这里有一个小的细节要处理,这里大家课下自己实现一下。
break;
}
case MailBoxType.AllToDate:
{
foreach (var mailBox in mailBoxList)
{
Log.Debug($"mailBox.CreateTime >= mailUnit.CreateTime {mailBox.CreateTime} >= {mailUnit.CreateTime}");
if (mailBox.CreateTime >= mailUnit.CreateTime && !mailBox.Received.Contains(mailUnit.Id))
{
// 如果是没有领取过这个邮件,首先要把自己添加到领取过的名单中。
mailBox.Received.Add(mailUnit.Id);
// 发送给自己的邮件列表里添加邮件。
await mailUnit.GetComponent<MailComponent>().Add(SerializerManager.BsonPack.CloneEntity(mailBox.Mail), true);
}
if (mailBox.ExpireTime <= now)
{
// 邮件已经过期了,要进行清理这个邮件的操作了
removeMailBox.Add(mailBox);
continue;
}
// 保存邮件箱状态到数据库中。
await worldDataBase.Save(mailBox);
}
foreach (var mailBox in removeMailBox)
{
await self.Remove(mailBox.Id);
}
break;
}
}
}
if (self.MailsByAccount.TryGetValue(mailUnit.Id, out var mailBoxes))
{
using var removeMailBox = ListPool<MailBox>.Create();
foreach (var mailBox in mailBoxes)
{
var cloneMail = SerializerManager.BsonPack.CloneEntity(mailBox.Mail);
await mailComponent.Add(cloneMail, sync);
mailBox.AccountId.Remove(mailUnit.Id);
Log.Debug($"领取了一个离线邮件 MailId:{cloneMail.Id}");
if (mailBox.AccountId.Count <= 0)
{
// 当邮件箱里没有要发送的玩家了,就表示这个邮件箱已经没有用处,可以删除了。
removeMailBox.Add(mailBox);
}
}
foreach (var mailBox in removeMailBox)
{
await self.Remove(mailBox.Id);
}
if (mailBoxes.Count <= 0)
{
// 如果MailsByAccount里的邮件箱已经没有邮件了就删除这个邮件箱的缓存。
self.MailsByAccount.Remove(mailUnit.Id);
}
}
}
}

View File

@@ -1,6 +1,8 @@
using Fantasy.Entitas.Interface;
using Fantasy;
using Fantasy.Entitas.Interface;
using NB;
namespace NB.Chat;
namespace NBF;
public class MailBoxDestroySystem : DestroySystem<MailBox>
{
@@ -12,7 +14,7 @@ public class MailBoxDestroySystem : DestroySystem<MailBox>
self.Mail = null;
}
self.BoxType = MailBoxType.None;
self.MailBoxType = MailBoxType.None;
self.CreateTime = 0;
self.ExpireTime = 0;
self.SendAccountId = 0;

View File

@@ -0,0 +1,234 @@
using Fantasy;
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
using NB;
using NB.Game;
namespace NBF;
public class MailComponentDestroySystem : DestroySystem<MailComponent>
{
protected override void Destroy(MailComponent self)
{
foreach (var (_, mail) in self.Mails)
{
mail.Dispose();
}
self.Mails.Clear();
self.Timer.Clear();
}
}
public class MailComponentDeserializeSystem : DeserializeSystem<MailComponent>
{
protected override void Deserialize(MailComponent self)
{
self.Timer.Clear();
foreach (var (_, mail) in self.Mails)
{
self.Timer.Add(mail.ExpireTime, mail.Id);
}
}
}
public static class MailComponentSystem
{
public static async FTask Add(this MailComponent self, Mail mail, bool sync)
{
mail.CreateTime = TimeHelper.Now;
mail.ExpireTime = mail.CreateTime + MailComponent.MailExpireTime;
// 如果身上的邮件数量,大于了设置的最大数量怎么办?
// 先不用考虑,稍后咱们再解决问题。
if (self.Mails.Count >= MailComponent.MaxMailCount)
{
// 删除最老的邮件。
var (_, value) = self.Timer.First();
var removeId = value[0];
await self.Remove(removeId, MailRemoveActionType.Overtop, sync);
}
self.Mails.Add(mail.Id, mail);
self.Timer.Add(mail.ExpireTime, mail.Id);
if (sync)
{
// 同步邮件状态给客户端。
self.Scene.NetworkMessagingComponent.Send(self.GetParent<Player>().SessionRunTimeId,
new Mail2C_HaveMail()
{
Mail = mail.ToMailSimplifyInfo()
});
}
// 这里的保存,也可以不用,这里看情况而定。
await self.Scene.World.Database.Save(self);
Log.Info($"MailComponentSystem Add {mail.Id} Count:{self.Mails.Count}");
}
public static async FTask<uint> Remove(this MailComponent self, long mailId,
MailRemoveActionType mailRemoveActionType, bool sync)
{
if (!self.Mails.Remove(mailId, out var mail))
{
// 这里的1代表的没有找到对应邮件。
return 1;
}
self.Timer.RemoveValue(mail.ExpireTime, mail.Id);
mail.Dispose();
if (sync)
{
// 同步给客户端,邮件删除的消息。
self.Scene.NetworkMessagingComponent.Send(self.GetParent<Player>().SessionRunTimeId,
new Mail2C_MailState()
{
MailState = (int)mailRemoveActionType, MailId = mailId
});
}
// 保存下数据库。
await self.Scene.World.Database.Save(self);
Log.Info(
$"MailComponentSystem Remove {mailId} mailRemoveActionType:{mailRemoveActionType} Count:{self.Mails.Count}");
return 0;
}
public static async FTask CheckTimeOut(this MailComponent self)
{
var now = TimeHelper.Now;
using var listPool = ListPool<long>.Create();
foreach (var (_, mail) in self.Mails)
{
if (mail.ExpireTime > now)
{
continue;
}
listPool.Add(mail.Id);
}
foreach (var mailId in listPool)
{
await self.Remove(mailId, MailRemoveActionType.ExpireTimeRemove, false);
}
Log.Info($"MailComponentSystem CheckTimeOut Count:{listPool.Count}");
}
public static async FTask<(uint ErrorCode, Mail mail)> OpenMail(this MailComponent self, long mailId)
{
if (!self.Mails.TryGetValue(mailId, out var mail))
{
// 这个1代表的是没有找到对应邮件
return (1, null);
}
if (mail.ExpireTime < TimeHelper.Now)
{
// 如果邮件已经过期了,需要清楚这个邮件。
await self.Remove(mailId, MailRemoveActionType.ExpireTimeRemove, true);
// 这里2代表的是邮件已经过期。
return (2, null);
}
mail.MailState = MailState.HaveRead;
// 这个保存数据库不是必须要保存的,因为一个邮件的查看状态并不能影响游戏的逻辑。
// 这里的话,大家看自己的需求而定。
await self.Scene.World.Database.Save(self);
return (0, mail);
}
public static void GetMailSimplifyInfos(this MailComponent self, ICollection<MailSimplifyInfo> mailSimplifyInfos)
{
foreach (var (_, mail) in self.Mails)
{
mailSimplifyInfos.Add(mail.ToMailSimplifyInfo());
}
}
public static void GetMailSimplifyInfos(this MailComponent self, ICollection<MailSimplifyInfo> mailSimplifyInfos,
int pageIndex, int pageSize)
{
foreach (var (_, mail) in self.Mails.Skip((pageIndex - 1) * pageSize).Take(pageSize))
{
mailSimplifyInfos.Add(mail.ToMailSimplifyInfo());
}
}
public static async FTask<uint> Receive(this MailComponent self, long mailId, bool money, List<long> item,
bool sync)
{
if (!self.Mails.TryGetValue(mailId, out var mail))
{
// 这里的1代表是没有找到该邮件。
return 1;
}
var needSave = false;
if (money && mail.Money > 0)
{
// 领取金钱一般都是在玩家身上但现在咱们所在的服务器是Mail服务器玩家并不在这个服务器.
// 所以一般金钱的操作会有一个专用的接口来操作。这个接口无论在什么服务器上,都会正确的发送到用户所在的服务器上进行金钱操作。
// 假设下面的增加金钱的一个异步接口。
// var resposne = await MoneyHelper.Add(self.Scene, mail.Money, sync);
// if (resposne.ErrorCode != 0)
// {
// // 这里的2代表的是金钱添加失败。
// return 2;
// }
// 再假设增加金钱是成功的,那么咱们就要处理邮件相关信息了。
mail.Money = 0;
needSave = true;
}
if (item != null && item.Count > 0)
{
using var listItem = ListPool<Item>.Create();
foreach (var itemId in item)
{
var rItem = mail.Items.FirstOrDefault(d => d.Id == itemId);
if (rItem == null)
{
continue;
}
listItem.Add(rItem);
}
// 假设背包在其他服务器,需要调用某一个接口来添加物品到目标服务器的背包身上。
// var response = await BagHelper.Add(self.Scene, listItem, sync);
// if (response.ErrorCode != 0)
// {
// return 2;
// }
// 还有一种情况就是背包里的空间只有一个空余位置了也就是只能放进一个物品了但是邮件领取的是2个物品.
// 会有下面2中情况,当然这个情况是要策划来决定怎么处理:
// 1、只领取一个物品到背包中另外一个不领取然后提示用户背包已满。
// 2、一个都不领取直接提示用户背包已满。
// 如果是是第一种情况下把BagHelper.Add接口就需要返回添加成功的物品ID方便邮件来处理。
// 假设全部物品都领取成功了,就可以执行下面的逻辑了。
foreach (var item1 in listItem)
{
mail.Items.Remove(item1);
item1.Dispose();
}
needSave = true;
}
if (needSave)
{
await self.Scene.World.Database.Save(self);
}
return 0;
}
}

View File

@@ -0,0 +1,77 @@
using Fantasy;
using Fantasy.Entitas.Interface;
using NB;
namespace NBF;
public sealed class MailDestroySystem : DestroySystem<Mail>
{
protected override void Destroy(Mail self)
{
self.OwnerId = 0;
self.Title = null;
self.Content = null;
self.CreateTime = 0;
self.ExpireTime = 0;
self.Money = 0;
self.MailState = MailState.None;
self.MailType = MailType.None;
foreach (var selfItem in self.Items)
{
selfItem.Dispose();
}
self.Items.Clear();
}
}
public static class MailSystem
{
public static MailSimplifyInfo ToMailSimplifyInfo(this Mail self)
{
return new MailSimplifyInfo()
{
MailId = self.Id,
OwnerId = self.OwnerId,
Title = self.Title,
Content = self.Content,
CreateTime = self.CreateTime,
ExpireTime = self.ExpireTime,
MailState = (int)self.MailState,
MailType = (int)self.MailType
};
}
public static MailInfo ToMailInfo(this Mail self)
{
var mailInfo = new MailInfo()
{
MailId = self.Id,
OwnerId = self.OwnerId,
Title = self.Title,
Content = self.Content,
CreateTime = self.CreateTime,
ExpireTime = self.ExpireTime,
Money = self.Money,
MailState = (int)self.MailState,
MailType = (int)self.MailType
};
foreach (var selfItem in self.Items)
{
mailInfo.Items.Add(new ItemInfo()
{
Id = selfItem.Id,
ConfigId = selfItem.ConfigId,
Count = selfItem.Count,
ExpirationTime = selfItem.ExpirationTime,
GetTime = selfItem.GetTime,
Abrasion = selfItem.Abrasion
});
}
return mailInfo;
}
}

View File

@@ -42,7 +42,6 @@ public static class RoomManageComponentSystem
return null;
}
// roomManageComponent.on
var room = Entity.Create<MapRoom>(self.Scene, true, true);
room.Owner = ownerId;
room.RoomId = roomId;

View File

@@ -91,7 +91,7 @@ public static class PlayerManageComponentSystem
return null;
}
}
// account.Statistics.LoginTime = TimeHelper.Now;
// account.SetTestData();
@@ -157,6 +157,7 @@ public static class PlayerManageComponentSystem
player.Country = "cn";
player.Exp = 999;
player.Head = "xxx.png";
player.CreateTime = TimeHelper.Now;
// var list = InitConfig.GetList();
var list = Configs.Tables.TbInitItemConfig.DataList;

View File

@@ -5,6 +5,7 @@ using Fantasy.Async;
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
using NB.Chat;
#pragma warning disable CS8625 // 无法将 null 字面量转换为非 null 的引用类型。
@@ -31,6 +32,16 @@ public sealed class PlayerDestroySystem : DestroySystem<Player>
// self.Statistics.Dispose();
// self.Statistics = null;
// 退出当前ChatUnit拥有的所有频道
foreach (var (_, chatChannelComponent) in self.Channels)
{
chatChannelComponent.ExitChannel(self.Id, false);
}
// 理论情况下这个self.Channels不会存在因为数据的因为上面已经给清空掉了。
// 但是self.Channels.Clear();还是加上吧,防止以后忘记了。
self.Channels.Clear();
self.SessionRunTimeId = 0;
}
}
@@ -47,7 +58,6 @@ public static class PlayerSystem
/// <param name="items"></param>
public static async FTask AddItems(this Player self, Dictionary<int, int> items)
{
var itemContainer = self.GetComponent<PlayerItemContainerComponent>();
HashSet<ItemBasicType> addType = new HashSet<ItemBasicType>();
foreach (var (key, value) in items)
@@ -64,7 +74,7 @@ public static class PlayerSystem
}
#endregion
public static MapUnitInfo ToMapUnitInfo(this Player self)
{
var ret = new MapUnitInfo()
@@ -79,11 +89,11 @@ public static class PlayerSystem
ret.Position.x = mapUnit.Position.x;
ret.Position.y = mapUnit.Position.y;
ret.Position.z = mapUnit.Position.z;
ret.Rotation.x = mapUnit.Rotation.x;
ret.Rotation.y = mapUnit.Rotation.y;
ret.Rotation.z = mapUnit.Rotation.z;
// ret.Gears
return ret;

View File

@@ -1,46 +0,0 @@
using System;
using Fantasy;
using Fantasy.Async;
using Fantasy.Network.Interface;
using NB.Game;
namespace NB.Chat;
public class C2Game_DeleteMailRequestHandler : AddressRPC<Player, C2Game_DeleteMailRequest, Game2C_DeleteMailResponse>
{
protected override async FTask Run(Player entity, C2Game_DeleteMailRequest request,
Game2C_DeleteMailResponse response, Action reply)
{
if (request.Id < 1)
{
response.ErrorCode = ErrorCode.ErrArgs;
return;
}
var mailManageComponent = entity.Scene.GetComponent<MailManageComponent>();
if (mailManageComponent == null)
{
Log.Error("组件不存在 MailManageComponent");
response.ErrorCode = ErrorCode.ErrServer;
return;
}
var conversation = await mailManageComponent.GetConversation(entity.Id, request.Id);
if (conversation == null)
{
response.ErrorCode = ErrorCode.ErrArgs;
return;
}
conversation.RemoveId.Add(entity.Id);
if (conversation.RemoveId.Count > 1)
{
//都删除了,则吧会话直接移除掉
await mailManageComponent.DeleteConversation(entity.Id, request.Id);
}
else
{
await conversation.Save();
}
}
}

View File

@@ -1,24 +0,0 @@
using System;
using System.Diagnostics;
using Fantasy;
using Fantasy.Async;
using Fantasy.Network.Interface;
using NB.Game;
namespace NB.Chat;
public class
C2Game_GetConversationsRequestHandler : AddressRPC<Player, C2Game_GetConversationsRequest, Game2C_GetConversationsResponse>
{
protected override async FTask Run(Player entity, C2Game_GetConversationsRequest request,
Game2C_GetConversationsResponse response, Action reply)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
var mailManageComponent = entity.Scene.GetComponent<MailManageComponent>();
var list = await mailManageComponent.GetConversations(entity.Id);
response.List = await list.ToInfo(entity.Scene, entity.Id);
stopwatch.Stop();
Log.Info($"查询会话列表耗时={stopwatch.ElapsedMilliseconds}");
}
}

View File

@@ -1,70 +0,0 @@
using System;
using Fantasy;
using Fantasy.Async;
using Fantasy.Network.Interface;
using NB.Game;
namespace NB.Chat;
public class C2Game_SendMailRequestHandler : AddressRPC<Player, C2Game_SendMailRequest, Game2C_SendMailResponse>
{
protected override async FTask Run(Player entity, C2Game_SendMailRequest request, Game2C_SendMailResponse response,
Action reply)
{
if (request.Target < 1)
{
response.ErrorCode = ErrorCode.ErrArgs;
return;
}
var mailManageComponent = entity.Scene.GetComponent<MailManageComponent>();
if (mailManageComponent == null)
{
Log.Error("组件不存在 MailManageComponent");
response.ErrorCode = ErrorCode.ErrServer;
return;
}
var chatUnitManage = entity.Scene.GetComponent<PlayerManageComponent>();
if (chatUnitManage == null)
{
Log.Error("组件不存在 SocialUnitManageComponent");
response.ErrorCode = ErrorCode.ErrServer;
return;
}
var conversation = await mailManageComponent.GetConversation(entity.Id, request.Target);
if (conversation == null)
{
response.ErrorCode = ErrorCode.ErrArgs;
return;
}
//检查是否可以发消息,如果会话只有一句,则不允许再发
if (!conversation.CanSend(entity.Id))
{
response.ErrorCode = ErrorCode.MailNotReply;
return;
}
var mail = MailFactory.Create(entity.Scene, request.Content);
mail.Sender = entity.Id;
mail.OwnerId = request.Target;
await conversation.Add(mail);
var res = new Game2C_HaveMail()
{
Mail = mail.ToMailInfo(),
Key = conversation.Key
};
//同步客户端
entity.Scene.NetworkMessagingComponent.Send(entity.SessionRunTimeId, res);
// var chatUnit = chatUnitManage.Get(request.Target);
//
// if (chatUnit != null)
// {
// //对方在线
// entity.Scene.NetworkMessagingComponent.Send(chatUnit.GateRouteId, res);
// }
}
}

View File

@@ -1,56 +0,0 @@
using System.Collections.Generic;
using Fantasy;
using Fantasy.Entitas;
using Fantasy.Helper;
using NB.Game;
namespace NB.Chat;
public static class MailBoxFactory
{
/// <summary>
/// 创建一个邮件箱
/// </summary>
/// <param name="scene"></param>
/// <param name="sendAccountId"></param>
/// <param name="mail"></param>
/// <param name="expireTime"></param>
/// <param name="accountIds"></param>
/// <returns></returns>
public static MailBox Create(Scene scene, long sendAccountId, Mail mail, int expireTime, List<long> accountIds)
{
var mailBox = Entity.Create<MailBox>(scene, true, true);
mailBox.SendAccountId = sendAccountId;
mailBox.Mail = mail;
mailBox.ExpireTime = TimeHelper.Now + expireTime;
mailBox.CreateTime = TimeHelper.Now;
if (accountIds == null || accountIds.Count <= 0)
{
return mailBox;
}
foreach (var accountId in accountIds)
{
mailBox.AccountId.Add(accountId);
}
return mailBox;
}
/// <summary>
/// 创建一个邮件箱
/// </summary>
/// <param name="scene"></param>
/// <param name="sendAccountId"></param>
/// <param name="expireTime"></param>
/// <param name="accountIds"></param>
/// <param name="content"></param>
/// <param name="items"></param>
/// <returns></returns>
public static MailBox Create(Scene scene, long sendAccountId, int expireTime, List<long> accountIds,
string content, List<AwardItem> items = null)
{
var mail = MailFactory.Create(scene, content, items);
return Create(scene, sendAccountId, mail, expireTime, accountIds);
}
}

View File

@@ -1,38 +0,0 @@
using Fantasy;
using Fantasy.Async;
using NB.Game;
namespace NB.Chat;
public static class MailConversationHelper
{
/// <summary>
/// 从数据库中读取GameAccount
/// </summary>
/// <param name="scene"></param>
/// <param name="firstId"></param>
/// <param name="secondId"></param>
/// <returns></returns>
public static async FTask<MailConversation> LoadDataBase(Scene scene, long firstId, long secondId)
{
var conversation =
await scene.World.Database.First<MailConversation>(d => d.FirstId == firstId && d.SecondId == secondId);
if (conversation == null)
{
return null;
}
conversation.Deserialize(scene);
return conversation;
}
/// <summary>
/// 从数据库中移除
/// </summary>
/// <param name="scene"></param>
/// <param name="id"></param>
public static async FTask DeleteDataBase(Scene scene,long id)
{
await scene.World.Database.Remove<MailConversation>(id);
}
}

View File

@@ -1,32 +0,0 @@
using System.Collections.Generic;
using Fantasy;
using Fantasy.Entitas;
using Fantasy.Helper;
using Fantasy.Serialize;
using NB.Game;
namespace NB.Chat;
public static class MailFactory
{
private static readonly ISerialize _serializer = SerializerManager.BsonPack;
public static Mail Create(Scene scene, string content, List<AwardItem> items = null)
{
var mail = Entity.Create<Mail>(scene, true, true);
mail.Content = content;
mail.State = MailState.Unread;
mail.CreateTime = TimeHelper.Now;
if (items != null && items.Count > 0)
{
foreach (var item in items)
{
// mail.Items.Add(_serializer.Clone(item));
}
}
return mail;
}
}

View File

@@ -1,32 +0,0 @@
using Fantasy;
using Fantasy.Async;
namespace NB.Chat;
/// <summary>
/// 发送邮件接口
/// </summary>
public static class MailHelper
{
public static async FTask Send(Scene scene, MailBox mailBox)
{
if (mailBox.BoxType == MailBoxType.None)
{
Log.Error("不支持的邮件类型");
return;
}
switch (mailBox.BoxType)
{
case MailBoxType.All:
{
break;
}
case MailBoxType.Specify:
{
break;
}
}
}
}

View File

@@ -1,80 +0,0 @@
using System.Linq;
using Fantasy;
using Fantasy.Async;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
namespace NB.Chat;
public class MailComponentDestroySystem : DestroySystem<MailComponent>
{
protected override void Destroy(MailComponent self)
{
foreach (var (_, mail) in self.Mails)
{
mail.Dispose();
}
self.Mails.Clear();
self.Timer.Clear();
}
}
public static class MailComponentSystem
{
public static async FTask Add(this MailComponent self, Mail mail, bool sync)
{
mail.CreateTime = TimeHelper.Now;
mail.ExpireTime = TimeHelper.Now + MailComponent.MaxExpireTime;
if (self.Mails.Count >= MailComponent.MaxMailCount)
{
//删除最老的邮件
var (_, value) = self.Timer.First();
foreach (var removeId in value)
{
await self.Remove(removeId, sync);
}
}
self.Mails.Add(mail.Id, mail);
self.Timer.Add(mail.ExpireTime, mail.Id);
if (sync)
{
//同步客户端
self.Scene.NetworkMessagingComponent.Send(0,new Game2C_HaveMail()
{
Mail = mail.ToMailInfo(),
});
}
await self.Scene.World.Database.Save(self);
Log.Info($"MailComponent Add id:{self.Id} mailId:{mail.Id} count:{self.Mails.Count}");
}
public static async FTask<uint> Remove(this MailComponent self, long mailId, bool sync)
{
if (!self.Mails.Remove(mailId, out var mail))
{
return 1;
}
self.Timer.RemoveValue(mail.ExpireTime, mail.Id);
mail.Dispose();
if (sync)
{
//同步客户端
self.Scene.NetworkMessagingComponent.Send(0,new Game2C_MailState()
{
MailState = (int)MailState.Deleted,
MailId = mailId,
});
}
await self.Scene.World.Database.Save(self);
Log.Info($"MailComponent Remove id:{self.Id} mailId:{mail.Id} count:{self.Mails.Count}");
return 0;
}
}

View File

@@ -1,128 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Fantasy;
using Fantasy.Async;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
using NB.Game;
namespace NB.Chat;
public class MailConversationDeserializeSystem : DeserializeSystem<MailConversation>
{
protected override void Deserialize(MailConversation self)
{
self.Key = $"{self.FirstId}-{self.SecondId}";
}
}
public class MailConversationDestroySystem : DestroySystem<MailConversation>
{
protected override void Destroy(MailConversation self)
{
foreach (var mail in self.Mails)
{
mail.Dispose();
}
self.Mails.Clear();
self.FirstId = 0;
self.SecondId = 0;
self.Key = string.Empty;
}
}
public static class MailConversationSystem
{
public static async FTask Add(this MailConversation self, Mail mail)
{
self.Mails.Add(mail);
if (self.Mails.Count > AppConfig.MaxConversationCount)
{
self.Mails.RemoveAt(0);
}
if (mail.Items != null && mail.Items.Count > 0)
{
//有附件需要立马保存
await self.Save(true);
}
else
{
await self.Save(false);
}
}
public static async FTask Save(this MailConversation self, bool forceSave = true)
{
// self.NeedSave = true;
// self.NeedSaveTime = TimeHelper.Now + AppConfig.PlayerDataAutoSaveTime;
if (forceSave)
{
self.UpdateTime = TimeHelper.Now;
await self.Scene.World.Database.Save(self);
}
else
{
self.NeedSave = true;
self.NeedSaveTime = TimeHelper.Now + AppConfig.ChatDataAutoSaveTime;
}
}
/// <summary>
/// 是否可以发送邮件
/// </summary>
/// <param name="self"></param>
/// <param name="sendId"></param>
/// <returns></returns>
public static bool CanSend(this MailConversation self, long sendId)
{
if (self.Mails.Count < 1) return true;
foreach (var mail in self.Mails)
{
if (mail.Sender != sendId)
{
return true;
}
}
return false;
}
#region
public static async FTask<List<ConversationInfo>> ToInfo(this List<MailConversation> self, Scene scene, long selfId)
{
List<ConversationInfo> ret = new List<ConversationInfo>();
HashSet<long> ids = new HashSet<long>();
foreach (var conversation in self)
{
if (conversation.RemoveId.Contains(selfId)) continue;
ids.Add(conversation.FirstId);
ids.Add(conversation.SecondId);
}
ids.Remove(selfId);
var infos = await CacheHandler.GetPlayerBasicCacheInfos(scene, ids.ToList());
foreach (var conversation in self)
{
if (conversation.RemoveId.Contains(selfId)) continue;
var item = new ConversationInfo();
item.List = conversation.Mails.ToMailInfo();
var otherId = conversation.FirstId == selfId ? conversation.SecondId : conversation.FirstId;
var info = infos.Find(t => t.RoleId == otherId);
if (info != null)
{
item.RoleInfo = info;
}
ret.Add(item);
}
return ret;
}
#endregion
}

View File

@@ -1,94 +0,0 @@
using System.Collections.Generic;
using Fantasy;
using Fantasy.Async;
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
using NB.Game;
namespace NB.Chat;
public class MailManageComponentDestroySystem : DestroySystem<MailManageComponent>
{
protected override void Destroy(MailManageComponent self)
{
foreach (var (_, conversation) in self.Conversations)
{
conversation.Dispose();
}
self.Conversations.Clear();
}
}
public static class MailManageComponentSystem
{
public static async FTask DeleteConversation(this MailManageComponent self, long selfId, long otherId)
{
var firstId = selfId;
var secondId = otherId;
if (otherId < selfId)
{
firstId = otherId;
secondId = selfId;
}
var key = $"{firstId}-{secondId}";
if (self.Conversations.Remove(key, out var conversation))
{
await MailConversationHelper.DeleteDataBase(conversation.Scene, conversation.Id);
conversation.Dispose();
}
}
public static async FTask<MailConversation?> GetConversation(this MailManageComponent self, long selfId,
long otherId)
{
var firstId = selfId;
var secondId = otherId;
if (otherId < selfId)
{
firstId = otherId;
secondId = selfId;
}
var key = $"{firstId}-{secondId}";
if (!self.Conversations.TryGetValue(key, out var conversation))
{
//检查数据库中是否存在
conversation = await MailConversationHelper.LoadDataBase(self.Scene, firstId, secondId);
if (conversation == null)
{
//检查id是否合法
var roleInfo = await CacheHandler.GetPlayerBasicCacheInfo(self.Scene, otherId);
if (roleInfo == null)
{
return null;
}
conversation = Entity.Create<MailConversation>(self.Scene, true, true);
conversation.FirstId = firstId;
conversation.SecondId = secondId;
conversation.Key = key;
await conversation.Save(false);
}
self.Conversations.Add(key, conversation);
}
return conversation;
}
public static async FTask<List<MailConversation>> GetConversations(this MailManageComponent self, long id)
{
List<MailConversation> players =
await self.Scene.World.Database.QueryByPageOrderBy<MailConversation>(
d => d.FirstId == id || d.SecondId == id, 1, 50,
d => d.UpdateTime);
return players;
}
}

View File

@@ -1,53 +0,0 @@
using System.Collections.Generic;
using Fantasy;
using Fantasy.Entitas.Interface;
using NB.Game;
namespace NB.Chat;
public class MailDestroySystem : DestroySystem<Mail>
{
protected override void Destroy(Mail self)
{
self.OwnerId = 0;
self.Sender = 0;
self.Content = string.Empty;
self.ExpireTime = 0;
self.CreateTime = 0;
self.State = MailState.None;
self.MailType = MailType.None;
foreach (var item in self.Items)
{
item.Dispose();
}
self.Items.Clear();
}
}
public static class MailSystem
{
public static MailInfo ToMailInfo(this Mail mail)
{
return new MailInfo()
{
Id = mail.Id,
Content = mail.Content,
CreateTime = mail.CreateTime,
MailState = (int)mail.State,
MailType = (int)mail.MailType,
Items = mail.Items.ToAwardInfo()
};
}
public static List<MailInfo> ToMailInfo(this List<Mail> mails)
{
var list = new List<MailInfo>();
foreach (var mail in mails)
{
list.Add(mail.ToMailInfo());
}
return list;
}
}

View File

@@ -1,146 +0,0 @@
// using Fantasy;
// using Fantasy.Async;
// using Fantasy.Entitas;
// using Fantasy.Entitas.Interface;
// using NB.Chat;
//
// namespace NBF.Social;
//
// public sealed class ChatUnitManageComponentDestroySystem : DestroySystem<SocialUnitManageComponent>
// {
// protected override void Destroy(SocialUnitManageComponent self)
// {
// foreach (var chatUnit in self.Units.Values.ToArray())
// {
// chatUnit.Dispose();
// }
//
// self.Units.Clear();
// }
// }
//
// public static class SocialUnitManageComponentSystem
// {
// #region 消息缓存
//
// /// <summary>
// /// 离线消息,进入待领取队列
// /// </summary>
// /// <param name="self"></param>
// /// <param name="targetId"></param>
// /// <param name="message"></param>
// public static void SaveOfflineMessage(this SocialUnitManageComponent self, long targetId, ChatMessageInfo message)
// {
// // self.NotSendMessage.Add(targetId, message);
// }
//
// #endregion
//
// #region 上线下线
//
// /// <summary>
// /// 玩家上线
// /// </summary>
// /// <param name="self"></param>
// /// <param name="scene"></param>
// /// <param name="accountId"></param>
// /// <param name="gateRouteId"></param>
// public static async FTask<SocialUnit?> Online(this SocialUnitManageComponent self, Scene scene,
// long accountId,
// long gateRouteId)
// {
// // var accountId = roleSimpleInfo.RoleId;
// if (!self.TryGet(accountId, out var account))
// {
// account = Entity.Create<SocialUnit>(scene, accountId, true, true);
// self.Add(account);
// }
//
// if (account != null)
// {
// await account.TryComponent<MailComponent>();
// account.GateRouteId = gateRouteId;
// // account.Role = roleSimpleInfo;
// }
//
// await FTask.CompletedTask;
// return account;
// }
//
// public static async FTask Offline(this SocialUnitManageComponent self, Scene scene, long accountId,
// long gateRouteId)
// {
// if (self.TryGet(accountId, out var unit) && unit != null)
// {
// if (unit.GateRouteId == gateRouteId)
// {
// Log.Info("退出当前聊天服==");
// self.Remove(accountId); //如果当前网关和下线的网关一致
// }
// }
//
// await FTask.CompletedTask;
// }
//
// #endregion
//
// #region 获取&移除
//
// public static void Add(this SocialUnitManageComponent self, SocialUnit account)
// {
// self.Units.Add(account.Id, account);
// }
//
// public static SocialUnit? Get(this SocialUnitManageComponent self, long accountId)
// {
// return self.Units.GetValueOrDefault(accountId);
// }
//
// public static bool TryGet(this SocialUnitManageComponent self, long accountId, out SocialUnit? account)
// {
// return self.Units.TryGetValue(accountId, out account);
// }
//
// public static void Remove(this SocialUnitManageComponent self, long accountId, bool isDispose = true)
// {
// if (!self.Units.Remove(accountId, out var account))
// {
// return;
// }
//
// if (!isDispose)
// {
// return;
// }
//
// account.Dispose();
// }
//
// #endregion
//
// #region 组件
//
// /// <summary>
// /// 尝试给增加相关组件
// /// </summary>
// /// <param name="socialUnit"></param>
// /// <typeparam name="T"></typeparam>
// public static async FTask TryComponent<T>(this SocialUnit socialUnit) where T : Entity, new()
// {
// if (socialUnit.GetComponent<T>() == null)
// {
// var mailComponent = await socialUnit.Scene.World.Database.Query<T>(socialUnit.Id, true);
// if (mailComponent == null)
// {
// //如果没有邮件组件
// socialUnit.AddComponent<T>();
// }
// else
// {
// socialUnit.AddComponent(mailComponent);
// }
// }
// }
//
// #endregion
// }

View File

@@ -1,22 +0,0 @@
// using Fantasy.Entitas.Interface;
// #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
//
// namespace NB.Chat;
//
// public sealed class SocialUnitDestroySystem : DestroySystem<SocialUnit>
// {
// protected override void Destroy(SocialUnit self)
// {
// self.Role?.Return();
// self.Role = null;
// self.GateRouteId = 0;
// // 退出当前ChatUnit拥有的所有频道
// foreach (var (_,chatChannelComponent) in self.Channels)
// {
// chatChannelComponent.ExitChannel(self.Id, false);
// }
// // 理论情况下这个self.Channels不会存在因为数据的因为上面已经给清空掉了。
// // 但是self.Channels.Clear();还是加上吧,防止以后忘记了。
// self.Channels.Clear();
// }
// }

View File

@@ -12,4 +12,9 @@
<ProjectReference Include="..\Entity\Entity.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Game\Condition\" />
<Folder Include="Game\Mail\Handler\" />
</ItemGroup>
</Project>

View File

@@ -138,10 +138,9 @@ public sealed class OnCreateSceneEvent : AsyncEventSystem<OnCreateScene>
private async FTask InitializeSocialScene(Scene scene)
{
Log.Info($"初始化 Social 场景: {scene.Id}");
//用于管理玩家的组件
scene.AddComponent<MailManageComponent>();
//聊天
// Log.Info($"初始化 Social 场景: {scene.Id}");
// 离线邮件箱管理组件
await scene.AddComponent<MailBoxManageComponent>().Init();
// 聊天频道中控中心组件。
scene.AddComponent<ChatChannelCenterComponent>();
await FTask.CompletedTask;
@@ -154,7 +153,7 @@ public sealed class OnCreateSceneEvent : AsyncEventSystem<OnCreateScene>
//用于管理玩家的组件
scene.AddComponent<PlayerManageComponent>();
scene.AddComponent<PlayerBasicCacheManageComponent>();
await scene.AddComponent<ItemUseComponent>().Initialize();
Log.Info("创建地图场景===");
var roomManageComponent = scene.AddComponent<RoomManageComponent>();