提交示例代码
This commit is contained in:
62
邮件系统课程完整代码/Server/Hotfix/Mail/Helper/MailBoxFactory.cs
Normal file
62
邮件系统课程完整代码/Server/Hotfix/Mail/Helper/MailBoxFactory.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Fantasy.Entitas;
|
||||
using Fantasy.Helper;
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
|
||||
namespace Fantasy;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
47
邮件系统课程完整代码/Server/Hotfix/Mail/Helper/MailFactory.cs
Normal file
47
邮件系统课程完整代码/Server/Hotfix/Mail/Helper/MailFactory.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Fantasy.Entitas;
|
||||
using Fantasy.Serialize;
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
|
||||
namespace Fantasy;
|
||||
|
||||
public static class MailFactory
|
||||
{
|
||||
public static readonly ISerialize Serializer = SerializerManager.GetSerializer(FantasySerializerType.Bson);
|
||||
|
||||
/// <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 is not { Count: > 0 })
|
||||
// {
|
||||
//
|
||||
// }
|
||||
if (items != null && items.Count > 0)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
// 最好的是要个这个Item给克隆出一份来。
|
||||
// 这样就可以保证,无论外面怎么改变也不会影响这个邮件的东西了。
|
||||
var cloneItem = Serializer.Clone(item);
|
||||
mail.Items.Add(cloneItem);
|
||||
}
|
||||
}
|
||||
|
||||
return mail;
|
||||
}
|
||||
}
|
||||
193
邮件系统课程完整代码/Server/Hotfix/Mail/Helper/MailHelper.cs
Normal file
193
邮件系统课程完整代码/Server/Hotfix/Mail/Helper/MailHelper.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
using Fantasy.Async;
|
||||
using Fantasy.DataStructure.Collection;
|
||||
using Fantasy.Platform.Net;
|
||||
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
|
||||
namespace Fantasy;
|
||||
|
||||
/// <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.Mail)
|
||||
{
|
||||
await InnerSend(scene, mailBox);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果不在同一个Scene下,就需要发送内部的网络消息给这个Scene了
|
||||
var mailSceneConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Mail)[0];
|
||||
await scene.NetworkMessagingComponent.CallInnerRoute(mailSceneConfig.RouteId, 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 mailUnitManageComponent = scene.GetComponent<MailUnitManageComponent>();
|
||||
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 (!mailUnitManageComponent.TryGet(accountId, out var mailUnit))
|
||||
{
|
||||
// 如果没有的话,代表这个用户根本不存在。
|
||||
// 那这样的情况就不需要做任何处理。
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果有的话,那么就给这个用户发送邮件。
|
||||
// 这个玩家是否在线?
|
||||
var mailComponent = mailUnit.GetComponent<MailComponent>();
|
||||
// 在线的话,就直接发送邮件给这个玩家就可以了。
|
||||
if (mailComponent != null)
|
||||
{
|
||||
await mailComponent.Add(MailFactory.Serializer.Clone(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 mailUnitManageComponent.Online)
|
||||
{
|
||||
await mailUnit.GetComponent<MailComponent>().Add(MailFactory.Serializer.Clone(mailBox.Mail), true);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mailBox.Dispose();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MailBoxType.All:
|
||||
{
|
||||
// 要保证这个邮件一定要有一个生命周期。并且这个周期一定要短,如果是想要实现永久的,可以比如30天发送一次。
|
||||
mailBoxManageComponent.MailsByMailBoxType.Add((int)MailBoxType.All, mailBox);
|
||||
// 首先给所有在线的玩家发送。
|
||||
foreach (var (_, mailUnit) in mailUnitManageComponent.Online)
|
||||
{
|
||||
await mailUnit.GetComponent<MailComponent>().Add(MailFactory.Serializer.Clone(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 (_, mailUnit) in mailUnitManageComponent.Online)
|
||||
{
|
||||
if (mailUnit.CreateTime > mailBox.CreateTime)
|
||||
{
|
||||
// 如果执行到这里,表示这里用户是这个邮件创建之后的用户。这个就不要发送了
|
||||
continue;
|
||||
}
|
||||
|
||||
// 所以这个邮件类型的逻辑就是,给当前邮件创建时间之前的玩家发送。
|
||||
await mailUnit.GetComponent<MailComponent>().Add(MailFactory.Serializer.Clone(mailBox.Mail), true);
|
||||
// 在邮件盒子中记录下玩家领取的记录,避免重复领取。
|
||||
mailBox.Received.Add(mailUnit.Id);
|
||||
}
|
||||
// 保存邮件箱到数据库。
|
||||
await scene.World.DataBase.Save(mailBox);
|
||||
break;
|
||||
}
|
||||
// 根据玩家等级、等等这样的邮件箱类型,都可以自行扩展了
|
||||
// 课下作业、自己实现一个起来类型的邮箱。
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user