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 { protected override void Destroy(MailComponent self) { foreach (var (_, mail) in self.Mails) { mail.Dispose(); } self.Mails.Clear(); self.Timer.Clear(); } } public class MailComponentDeserializeSystem : DeserializeSystem { 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().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 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().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.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 mailSimplifyInfos) { foreach (var (_, mail) in self.Mails) { mailSimplifyInfos.Add(mail.ToMailSimplifyInfo()); } } public static void GetMailSimplifyInfos(this MailComponent self, ICollection 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 Receive(this MailComponent self, long mailId, bool money, List 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.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; } }