新增私聊相关

This commit is contained in:
2025-08-18 23:24:33 +08:00
parent 34b25273a7
commit 8122c577f6
68 changed files with 1442 additions and 467 deletions

View File

@@ -1,16 +0,0 @@
using Fantasy;
namespace NB.Game;
public static class CacheHandler
{
public static List<RoleSimpleInfo> GetPlayerBasicCacheInfos(List<long> id)
{
}
public static RoleSimpleInfo GetPlayerBasicCacheInfos(long id)
{
}
}

View File

@@ -0,0 +1,24 @@
using Fantasy;
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace NB.Game;
public class
S2G_GetPlayerBasicInfoRequestHandler : RouteRPC<Scene, S2G_GetPlayerBasicInfoRequest,
G2S_GetPlayerBasicInfoResponse>
{
protected override async FTask Run(Scene scene, S2G_GetPlayerBasicInfoRequest request,
G2S_GetPlayerBasicInfoResponse response, Action reply)
{
var playerBasicCacheManageComponent = scene.GetComponent<PlayerBasicCacheManageComponent>();
if (playerBasicCacheManageComponent != null)
{
var list = await playerBasicCacheManageComponent.GetPlayerBasicCacheInfos(request.IdList);
if (list != null && list.Count > 0)
{
response.RoleList.AddRange(list);
}
}
}
}

View File

@@ -0,0 +1,49 @@
using Fantasy;
using Fantasy.Async;
namespace NB.Game;
public static class CacheHandler
{
/// <summary>
/// 查询玩家基础信息
/// </summary>
/// <param name="scene"></param>
/// <param name="id"></param>
/// <returns></returns>
public static async FTask<List<RoleSimpleInfo>> GetPlayerBasicCacheInfos(Scene scene, List<long> id)
{
var gameSceneConfig = GameSceneHelper.GetSceneConfig();
var gameRouteId = gameSceneConfig.RouteId;
//连接到游戏中心服
var gameResponse = (G2S_GetPlayerBasicInfoResponse)await scene.NetworkMessagingComponent.CallInnerRoute(
gameRouteId, new S2G_GetPlayerBasicInfoRequest()
{
IdList = id
});
if (gameResponse.ErrorCode != 0)
{
return new List<RoleSimpleInfo>();
}
return gameResponse.RoleList;
}
/// <summary>
/// 获取玩家基础信息
/// </summary>
/// <param name="scene"></param>
/// <param name="id"></param>
/// <returns></returns>
public static async FTask<RoleSimpleInfo?> GetPlayerBasicCacheInfo(Scene scene, long id)
{
var list = await GetPlayerBasicCacheInfos(scene, [id]);
if (list.Count > 0)
{
return list[0];
}
return null;
}
}

View File

@@ -0,0 +1,13 @@
using Fantasy;
using Fantasy.Entitas;
namespace NB.Game;
public class PlayerBasicCacheFactory
{
public static PlayerBasicCache Create(Scene scene, long id)
{
var player = Entity.Create<PlayerBasicCache>(scene, id, true, true);
return player;
}
}

View File

@@ -0,0 +1,130 @@
using System.Diagnostics;
using Fantasy;
using Fantasy.Async;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
namespace NB.Game;
public class PlayerBasicCacheManageComponentAwakeSystem : AwakeSystem<PlayerBasicCacheManageComponent>
{
protected override void Awake(PlayerBasicCacheManageComponent self)
{
// var timerId = self.Scene.TimerComponent.Net.RepeatedTimer(1000 * 60, new TestEvent());
}
}
public class PlayerBasicCacheManageComponentDestroySystem : DestroySystem<PlayerBasicCacheManageComponent>
{
protected override void Destroy(PlayerBasicCacheManageComponent self)
{
foreach (var (_, player) in self.Players)
{
player.Dispose();
}
self.Players.Clear();
}
}
public static class PlayerBasicCacheManageComponentSystem
{
/// <summary>
/// 获取一组玩家基本信息
/// </summary>
/// <param name="manage"></param>
/// <param name="list"></param>
/// <returns></returns>
public static async FTask<List<RoleSimpleInfo>> GetPlayerBasicCacheInfos(
this PlayerBasicCacheManageComponent manage,
List<long> list)
{
var ret = new List<RoleSimpleInfo>();
var notCacheIdList = new List<long>();
foreach (var id in list)
{
var info = GetPlayerBasicCacheInfos(manage, id);
if (info != null)
{
ret.Add(info);
}
else
{
notCacheIdList.Add(id);
}
}
if (notCacheIdList.Count > 0)
{
//查数据库
var dbDataList = await LoadPlayerBasicCacheByDB(manage, notCacheIdList);
foreach (var playerBasicCache in dbDataList)
{
ret.Add(playerBasicCache.ToInfo());
}
}
return ret;
}
/// <summary>
/// 获取缓存的单个玩家基本信息
/// </summary>
/// <param name="manage"></param>
/// <param name="id"></param>
/// <returns></returns>
public static RoleSimpleInfo? GetPlayerBasicCacheInfos(this PlayerBasicCacheManageComponent manage,
long id)
{
if (manage.Players.TryGetValue(id, out var cache))
{
return cache.ToInfo();
}
return null;
}
/// <summary>
/// 从数据库中加载玩家基本信息
/// </summary>
/// <param name="manage"></param>
/// <param name="ids"></param>
/// <returns></returns>
public static async FTask<List<PlayerBasicCache>> LoadPlayerBasicCacheByDB(
this PlayerBasicCacheManageComponent manage,
List<long> ids)
{
// Stopwatch stopwatch = new Stopwatch();
// stopwatch.Start();
var ret = new List<PlayerBasicCache>();
//TODO:需要考虑如果数量过大是否需要分页
List<Player> players =
await manage.Scene.World.DataBase.QueryByPage<Player>(d => ids.Contains(d.Id), 1, ids.Count);
foreach (var player in players)
{
var cache = manage.UpdateCache(player);
ret.Add(cache);
}
return ret;
}
public static PlayerBasicCache UpdateCache(this PlayerBasicCacheManageComponent manage, Player player)
{
if (!manage.Players.TryGetValue(player.Id, out var cache))
{
cache = PlayerBasicCacheFactory.Create(manage.Scene, player.Id);
manage.Players.Add(player.Id, cache);
}
cache.NickName = player.NickName;
cache.Country = player.Country;
cache.Head = player.Head;
cache.Level = player.Level;
cache.IsVip = player.IsVip;
cache.ExpirationTime = TimeHelper.Now + TimeHelper.OneDay; //更新则过期时间增加一天
return cache;
}
}

View File

@@ -0,0 +1,33 @@
using Fantasy;
using Fantasy.Entitas.Interface;
namespace NB.Game;
public class PlayerBasicCacheDestroySystem : DestroySystem<PlayerBasicCache>
{
protected override void Destroy(PlayerBasicCache self)
{
self.Country = string.Empty;
self.NickName = string.Empty;
self.Head = string.Empty;
self.Level = 0;
self.IsVip = false;
self.ExpirationTime = 0;
}
}
public static class PlayerBasicCacheSystem
{
public static RoleSimpleInfo ToInfo(this PlayerBasicCache player)
{
var ret = new RoleSimpleInfo();
ret.NickName = player.NickName;
ret.Country = player.Country;
ret.Head = player.Head;
ret.Level = player.Level;
ret.Vip = player.IsVip;
ret.RoleId = player.Id;
return ret;
}
}

View File

@@ -8,13 +8,16 @@ namespace NB.Game;
public static class GameSceneHelper
{
private static SceneConfig GetSceneConfig()
#region 线 线
public static SceneConfig GetSceneConfig()
{
var gameSceneConfigs = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Game);
return gameSceneConfigs.First();
}
public static async FTask<(long, RoleSimpleInfo?)> Online(Scene scene, long accountID, long gateRuntimeId)
{
var gameSceneConfig = GetSceneConfig();
@@ -34,8 +37,12 @@ public static class GameSceneHelper
return (gameResponse.RoleRouteId, gameResponse.RoleInfo);
}
public static async FTask Offline(Scene scene, long accountId, long gateRuntimeId)
{
}
#endregion
}

View File

@@ -103,7 +103,7 @@ public static class PlayerManageComponentSystem
}
}
account.Statistics.LoginTime = TimeHelper.Now;
// account.Statistics.LoginTime = TimeHelper.Now;
await account.Save();

View File

@@ -19,16 +19,16 @@ public sealed class PlayerDestroySystem : DestroySystem<Player>
self.IsVip = false;
self.Head = string.Empty;
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.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;
}

View File

@@ -12,31 +12,31 @@ public static class PlayerHelper
public static void InitializeChildEntity(this Player self)
{
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;
}
// 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)
@@ -156,8 +156,8 @@ public static class PlayerHelper
return new GameAccountInfo()
{
CreateTime = self.Statistics.CreateTime,
LoginTime = self.Statistics.LoginTime
// CreateTime = self.Statistics.CreateTime,
// LoginTime = self.Statistics.LoginTime
};
}
@@ -194,8 +194,8 @@ public static class PlayerHelper
if (self.IsVip)
{
ret.VipInfo = new VipInfo();
ret.VipInfo.OpenTime = self.Vip.GetTime;
ret.VipInfo.ExpirationTime = self.Vip.ExpirationTime;
// ret.VipInfo.OpenTime = self.Vip.GetTime;
// ret.VipInfo.ExpirationTime = self.Vip.ExpirationTime;
}
return ret;

View File

@@ -13,6 +13,9 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Social\Chat\" />
<Folder Include="Social\Club\" />
<Folder Include="Social\Mail\Handler\Inner\" />
<Folder Include="Utils\" />
</ItemGroup>

View File

@@ -1,3 +1,4 @@
using System.Diagnostics;
using Fantasy;
using Fantasy.Async;
using Fantasy.Entitas;
@@ -73,6 +74,46 @@ public sealed class OnCreateSceneEvent : AsyncEventSystem<OnCreateScene>
}
case SceneType.Game:
{
// // Begins transaction
// using (var session = mongoClient.StartSession())
// {
// session.StartTransaction();
// try
// {
// // Creates sample data
// var book = new Book
// {
// Title = "Beloved",
// Author = "Toni Morrison",
// InStock = true
// };
// var film = new Film
// {
// Title = "Star Wars",
// Director = "George Lucas",
// InStock = true
// };
// // Inserts sample data
// books.InsertOne(session, book);
// films.InsertOne(session, film);
// // Commits our transaction
// session.CommitTransaction();
// }
// catch (Exception e)
// {
// Console.WriteLine("Error writing to MongoDB: " + e.Message);
// return;
// }
// // Prints a success message if no error thrown
// Console.WriteLine("Successfully committed transaction!");
// }
// var client = self.Scene.World.DataBase;
// List<FTask> tasks = new List<FTask>();
// Stopwatch stopwatch = new Stopwatch();
// stopwatch.Start();
// for (int i = 0; i < 100; i++)
// {
// var accountId = scene.EntityIdFactory.Create;
@@ -83,9 +124,15 @@ public sealed class OnCreateSceneEvent : AsyncEventSystem<OnCreateScene>
// account.Country = "cn";
// account.Exp = 999;
// account.Head = "xxx.png";
// await account.Save();
// tasks.Add(account.Save());
// }
// await FTask.WaitAll(tasks);
// // self.Scene.World.DataBase.InsertBatch()
//
// stopwatch.Stop();
// Log.Info($"创建100个号入库耗时={stopwatch.ElapsedMilliseconds}ms");
break;
}
case SceneType.Gate:

View File

@@ -36,6 +36,7 @@ public class OnSceneCreate_Init : AsyncEventSystem<OnCreateScene>
{
//用于管理玩家的组件
scene.AddComponent<PlayerManageComponent>();
scene.AddComponent<PlayerBasicCacheManageComponent>();
break;
}
case SceneType.Social:
@@ -43,6 +44,7 @@ public class OnSceneCreate_Init : AsyncEventSystem<OnCreateScene>
//用于管理玩家的组件
scene.AddComponent<SocialUnitManageComponent>();
scene.AddComponent<ChatChannelCenterComponent>();
scene.AddComponent<MailManageComponent>();
break;
}
}

View File

@@ -0,0 +1,24 @@
// using Fantasy;
// using Fantasy.Async;
// using Fantasy.Network.Interface;
//
// namespace NB.Chat;
//
// public class
// C2S_GetOfflineMessageRequestHandler : RouteRPC<SocialUnit, C2S_GetOfflineMessageRequest,
// S2C_GetOfflineMessageResponse>
// {
// protected override async FTask Run(SocialUnit entity, C2S_GetOfflineMessageRequest request,
// S2C_GetOfflineMessageResponse response,
// Action reply)
// {
// var chatUnitManage = entity.Scene.GetComponent<SocialUnitManageComponent>();
// if (chatUnitManage.NotSendMessage.TryGetValue(entity.Id, out var list))
// {
// response.Message.AddRange(list);
// chatUnitManage.NotSendMessage.RemoveByKey(entity.Id);
// }
//
// await FTask.CompletedTask;
// }
// }

View File

@@ -0,0 +1,155 @@
using System.Diagnostics;
using System.Text;
using Fantasy;
using Fantasy.Async;
using Fantasy.Helper;
using Fantasy.Network.Interface;
using NB.Game;
namespace NB.Chat;
public sealed class
C2S_SendMessageRequestHandler : RouteRPC<SocialUnit, C2S_SendMessageRequest, S2C_SendMessageResponse>
{
protected override async FTask Run(SocialUnit socialUnit, C2S_SendMessageRequest request,
S2C_SendMessageResponse response, Action reply)
{
if (request.Target < 1)
{
response.ErrorCode = ErrorCode.ErrArgs;
return;
}
SocialSceneHelper.BroadcastChannel(socialUnit.Scene, request.Target, new ChatMessageInfo()
{
SendTime = TimeHelper.Now,
Content = request.Message,
});
// if (request.Type == 0) //频道聊天
// {
// }
// else if (request.Type == 1) //私聊
// {
// //发送私聊
// SocialSceneHelper.PrivateMessage(socialUnit.Scene, socialUnit.Id, request.Target, new ChatMessageInfo()
// {
// SendTime = TimeHelper.Now,
// Content = request.Message,
// });
// }
// var list = new List<long>()
// {
// 337951357380329472,
// 337951357380329473,
// 337951357380329474,
// 337951357380329475,
// 337951357380329476,
// 337951357380329477,
// 337951357380329478,
// 337951357380329479,
// 337951357380329480,
// 337951357380329481,
// 337951357380329482,
// 337951357380329483,
// 337951357380329484,
// 337951357380329485,
// 337951357380329486,
// 337951357380329487,
// 337951357380329488,
// 337951357380329489,
// 337951357380329490,
// 337951357380329491,
// 337951357380329492,
// 337951357380329493,
// 337951357380329494,
// 337951357380329495,
// 337951357380329496,
// 337951357380329497,
// 337951357380329498,
// 337951357380329499,
// 337951357380329500,
// 337951357380329501,
// 337951357380329502,
// 337951357380329503,
// 337951357380329504,
// 337951357380329505,
// 337951357380329506,
// 337951357380329507,
// 337951357380329508,
// 337951357380329509,
// 337951357380329510,
// 337951357380329511,
// 337951357380329512,
// 337951357380329513,
// 337951357380329514,
// 337951357380329515,
// 337951357380329516,
// 337951357380329517,
// 337951357380329518,
// 337951357380329519,
// 337951357380329520,
// 337951357380329521,
// 337951357380329522,
// 337951357380329523,
// 337951357380329524,
// 337951357380329525,
// 337951357380329526,
// 337951357380329527,
// 337951357380329528,
// 337951357380329529,
// 337951357380329530,
// 337951357380329531,
// 337951357380329532,
// 337951357380329533,
// 337951357380329534,
// 337951357380329535,
// 337951357380329536,
// 337951357380329537,
// 337951357380329538,
// 337951357380329539,
// 337951357380329540,
// 337951357380329541,
// 337951357380329542,
// 337951357380329543,
// 337951357380329544,
// 337951357380329545,
// 337951357380329546,
// 337951357380329547,
// 337951357380329548,
// 337951357380329549,
// 337951357380329550,
// 337951357380329551,
// 337951357380329552,
// 337951357380329553,
// 337951357380329554,
// 337951357380329555,
// 337951357380329556,
// 337951357380329557,
// 337951357380329558,
// 337951357380329559,
// 337951357380329560,
// 337951357380329561,
// 337951357380329562,
// 337951357380329563,
// 337951357380329564,
// 337951357380329565,
// 337951357380329566,
// 337951357380329567,
// 337951357380329568,
// 337951357380329569,
// 337951357380329570,
// 337951357380329571
// };
//
// Stopwatch stopwatch = new Stopwatch();
// stopwatch.Start();
// var infoList = await CacheHandler.GetPlayerBasicCacheInfos(socialUnit.Scene,list);
// stopwatch.Stop();
// Log.Info($"查询数量={infoList.Count} 耗时={stopwatch.ElapsedMilliseconds}");
//
await FTask.CompletedTask;
}
}

View File

@@ -17,7 +17,7 @@ public static class ChatUnitManageComponentSystem
/// <param name="message"></param>
public static void SaveOfflineMessage(this SocialUnitManageComponent self, long targetId, ChatMessageInfo message)
{
self.NotSendMessage.Add(targetId, message);
// self.NotSendMessage.Add(targetId, message);
}
#endregion

View File

@@ -1,24 +0,0 @@
using Fantasy;
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace NB.Chat;
public class
C2S_GetOfflineMessageRequestHandler : RouteRPC<SocialUnit, C2S_GetOfflineMessageRequest,
S2C_GetOfflineMessageResponse>
{
protected override async FTask Run(SocialUnit entity, C2S_GetOfflineMessageRequest request,
S2C_GetOfflineMessageResponse response,
Action reply)
{
var chatUnitManage = entity.Scene.GetComponent<SocialUnitManageComponent>();
if (chatUnitManage.NotSendMessage.TryGetValue(entity.Id, out var list))
{
response.Message.AddRange(list);
chatUnitManage.NotSendMessage.RemoveByKey(entity.Id);
}
await FTask.CompletedTask;
}
}

View File

@@ -1,41 +0,0 @@
using System.Text;
using Fantasy;
using Fantasy.Async;
using Fantasy.Helper;
using Fantasy.Network.Interface;
namespace NB.Chat;
public sealed class
C2S_SendMessageRequestHandler : RouteRPC<SocialUnit, C2S_SendMessageRequest, S2C_SendMessageResponse>
{
protected override async FTask Run(SocialUnit socialUnit, C2S_SendMessageRequest request,
S2C_SendMessageResponse response, Action reply)
{
if (request.Target < 1)
{
response.ErrorCode = ErrorCode.ErrArgs;
return;
}
if (request.Type == 0) //频道聊天
{
SocialSceneHelper.BroadcastChannel(socialUnit.Scene, request.Target, new ChatMessageInfo()
{
SendTime = TimeHelper.Now,
Content = request.Message,
});
}
else if (request.Type == 1) //私聊
{
//发送私聊
SocialSceneHelper.PrivateMessage(socialUnit.Scene, socialUnit.Id, request.Target, new ChatMessageInfo()
{
SendTime = TimeHelper.Now,
Content = request.Message,
});
}
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,44 @@
using Fantasy;
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace NB.Chat;
public class C2S_DeleteMailRequestHandler : RouteRPC<SocialUnit, C2S_DeleteMailRequest, S2C_DeleteMailResponse>
{
protected override async FTask Run(SocialUnit entity, C2S_DeleteMailRequest request,
S2C_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

@@ -0,0 +1,22 @@
using System.Diagnostics;
using Fantasy;
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace NB.Chat;
public class
C2S_GetConversationsRequestHandler : RouteRPC<SocialUnit, C2S_GetConversationsRequest, S2C_GetConversationsResponse>
{
protected override async FTask Run(SocialUnit entity, C2S_GetConversationsRequest request,
S2C_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

@@ -0,0 +1,68 @@
using Fantasy;
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace NB.Chat;
public class C2S_SendMailRequestHandler : RouteRPC<SocialUnit, C2S_SendMailRequest, S2C_SendMailResponse>
{
protected override async FTask Run(SocialUnit entity, C2S_SendMailRequest request, S2C_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<SocialUnitManageComponent>();
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 S2C_HaveMail()
{
Mail = mail.ToMailInfo(),
Key = conversation.Key
};
//同步客户端
entity.Scene.NetworkMessagingComponent.SendInnerRoute(entity.GateRouteId, res);
var chatUnit = chatUnitManage.Get(request.Target);
if (chatUnit != null)
{
//对方在线
entity.Scene.NetworkMessagingComponent.SendInnerRoute(chatUnit.GateRouteId, res);
}
}
}

View File

@@ -1,8 +1,9 @@
using Fantasy;
using Fantasy.Entitas;
using Fantasy.Helper;
using NB.Game;
namespace NB.Game;
namespace NB.Chat;
public static class MailBoxFactory
{
@@ -42,14 +43,13 @@ public static class MailBoxFactory
/// <param name="sendAccountId"></param>
/// <param name="expireTime"></param>
/// <param name="accountIds"></param>
/// <param name="title"></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 title,
public static MailBox Create(Scene scene, long sendAccountId, int expireTime, List<long> accountIds,
string content, List<AwardItem> items = null)
{
var mail = MailFactory.Create(scene, title, content, items);
var mail = MailFactory.Create(scene, content, items);
return Create(scene, sendAccountId, mail, expireTime, accountIds);
}
}

View File

@@ -0,0 +1,38 @@
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

@@ -2,17 +2,17 @@
using Fantasy.Entitas;
using Fantasy.Helper;
using Fantasy.Serialize;
using NB.Game;
namespace NB.Game;
namespace NB.Chat;
public static class MailFactory
{
private static readonly ISerialize _serializer = SerializerManager.GetSerializer(FantasySerializerType.Bson);
public static Mail Create(Scene scene, string title, string content, List<AwardItem> items = null)
public static Mail Create(Scene scene, string content, List<AwardItem> items = null)
{
var mail = Entity.Create<Mail>(scene, true, true);
mail.Title = title;
mail.Content = content;
mail.State = MailState.Unread;
mail.CreateTime = TimeHelper.Now;

View File

@@ -1,13 +1,14 @@
using Fantasy;
using Fantasy.Async;
namespace NB.Game;
namespace NB.Chat;
/// <summary>
/// 发送邮件接口
/// </summary>
public static class MailHelper
{
public static async FTask Send(Scene scene, MailBox mailBox)
{
if (mailBox.BoxType == MailBoxType.None)

View File

@@ -1,6 +1,6 @@
using Fantasy.Entitas.Interface;
namespace NB.Game;
namespace NB.Chat;
public class MailBoxDestroySystem : DestroySystem<MailBox>
{

View File

@@ -3,7 +3,7 @@ using Fantasy.Async;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
namespace NB.Game;
namespace NB.Chat;
public class MailComponentDestroySystem : DestroySystem<MailComponent>
{
@@ -42,7 +42,7 @@ public static class MailComponentSystem
if (sync)
{
//同步客户端
self.Scene.NetworkMessagingComponent.SendInnerRoute(0,new Game2C_HaveMail()
self.Scene.NetworkMessagingComponent.SendInnerRoute(0,new S2C_HaveMail()
{
Mail = mail.ToMailInfo(),
});
@@ -65,7 +65,7 @@ public static class MailComponentSystem
if (sync)
{
//同步客户端
self.Scene.NetworkMessagingComponent.SendInnerRoute(0,new Game2C_MailState()
self.Scene.NetworkMessagingComponent.SendInnerRoute(0,new S2C_MailState()
{
MailState = (int)MailState.Deleted,
MailId = mailId,

View File

@@ -0,0 +1,126 @@
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

@@ -0,0 +1,93 @@
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,14 +1,15 @@
using Fantasy;
using Fantasy.Entitas.Interface;
using NB.Game;
namespace NB.Game;
namespace NB.Chat;
public class MailDestroySystem : DestroySystem<Mail>
{
protected override void Destroy(Mail self)
{
self.OwnerId = 0;
self.Title = string.Empty;
self.Sender = 0;
self.Content = string.Empty;
self.ExpireTime = 0;
self.CreateTime = 0;
@@ -30,9 +31,7 @@ public static class MailSystem
return new MailInfo()
{
Id = mail.Id,
Title = mail.Title,
Content = mail.Content,
ExpireTime = mail.ExpireTime,
CreateTime = mail.CreateTime,
MailState = (int)mail.State,
MailType = (int)mail.MailType,