Files
ServerStudy/邮件系统课程完整代码/Server/Hotfix/Mail/Components/MailComponentSystem.cs
2026-03-05 11:39:06 +08:00

226 lines
8.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type.
namespace Fantasy;
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.SendInnerRoute(self.GetParent<MailUnit>().GateRouteId, 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.SendInnerRoute(self.GetParent<MailUnit>().GateRouteId,
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;
}
}