服务合并

This commit is contained in:
Bob.Song
2026-03-11 10:15:34 +08:00
parent 945ffe9861
commit c2ec7226c0
68 changed files with 866 additions and 1032 deletions

View File

@@ -6,10 +6,10 @@ using Fantasy.Network.Interface;
namespace NB.Game;
public class C2Map_CreateRoomRequestHandler : AddressRPC<Player, C2Map_CreateRoomRequest, Map2C_CreateRoomResponse>
public class C2Map_CreateRoomRequestHandler : AddressRPC<Player, C2Game_CreateRoomRequest, Game2C_CreateRoomResponse>
{
protected override async FTask Run(Player entity, C2Map_CreateRoomRequest request,
Map2C_CreateRoomResponse response,
protected override async FTask Run(Player entity, C2Game_CreateRoomRequest request,
Game2C_CreateRoomResponse response,
Action reply)
{
var mapUnit = entity.GetComponent<MapUnitComponent>();

View File

@@ -4,9 +4,9 @@ using Fantasy.Network.Interface;
namespace NB.Game;
public class C2Map_LookHandler : Address<Player, C2Map_Look>
public class C2Map_LookHandler : Address<Player, C2Game_Look>
{
protected override async FTask Run(Player entity, C2Map_Look message)
protected override async FTask Run(Player entity, C2Game_Look message)
{
var mapUnit = entity.GetComponent<MapUnitComponent>();
if (mapUnit == null)
@@ -23,7 +23,7 @@ public class C2Map_LookHandler : Address<Player, C2Map_Look>
return;
}
var notifyMessage = new Map2C_LookeNotify()
var notifyMessage = new Game2C_LookeNotify()
{
Id = entity.Id,
Rotation = message.Rotation,

View File

@@ -5,9 +5,9 @@ using Fantasy.Network.Interface;
namespace NB.Game;
public class C2Map_MoveHandler : Address<Player, C2Map_Move>
public class C2Map_MoveHandler : Address<Player, C2Game_Move>
{
protected override async FTask Run(Player entity, C2Map_Move message)
protected override async FTask Run(Player entity, C2Game_Move message)
{
var mapUnit = entity.GetComponent<MapUnitComponent>();
if (mapUnit == null)
@@ -24,7 +24,7 @@ public class C2Map_MoveHandler : Address<Player, C2Map_Move>
return;
}
// var mapUnitManageComponent = entity.Scene.GetComponent<MapUnitManageComponent>();
var notifyMessage = new Map2C_MoveNotify()
var notifyMessage = new Game2C_MoveNotify()
{
Id = entity.Id,
Position = message.Position,

View File

@@ -4,9 +4,9 @@ using Fantasy.Network.Interface;
namespace NB.Game;
public class C2Map_RolePropertyChangeHandler : Address<Player, C2Map_RolePropertyChange>
public class C2Map_RolePropertyChangeHandler : Address<Player, C2Game_RolePropertyChange>
{
protected override async FTask Run(Player entity, C2Map_RolePropertyChange message)
protected override async FTask Run(Player entity, C2Game_RolePropertyChange message)
{
var mapUnit = entity.GetComponent<MapUnitComponent>();
if (mapUnit == null)
@@ -23,7 +23,7 @@ public class C2Map_RolePropertyChangeHandler : Address<Player, C2Map_RolePropert
return;
}
var notifyMessage = new Map2C_RolePropertyChangeNotify()
var notifyMessage = new Game2C_RolePropertyChangeNotify()
{
Id = entity.Id,
Propertys = message.Propertys,

View File

@@ -5,9 +5,9 @@ using Fantasy.Network.Interface;
namespace NB.Game;
public class C2Map_TakeItemRequestHandler : AddressRPC<Player, C2Map_TakeItemRequest, Map2C_TakeItemResponse>
public class C2Map_TakeItemRequestHandler : AddressRPC<Player, C2Game_TakeItemRequest, Game2C_TakeItemResponse>
{
protected override async FTask Run(Player entity, C2Map_TakeItemRequest request, Map2C_TakeItemResponse response,
protected override async FTask Run(Player entity, C2Game_TakeItemRequest request, Game2C_TakeItemResponse response,
Action reply)
{
var itemContainer = entity.GetComponent<PlayerItemContainerComponent>();
@@ -48,7 +48,7 @@ public class C2Map_TakeItemRequestHandler : AddressRPC<Player, C2Map_TakeItemReq
var room = roomManageComponent.Get(roomId);
if (room != null)
{
var notifyMessage = new Map2C_RoleGearChangeNotify();
var notifyMessage = new Game2C_RoleGearChangeNotify();
// notifyMessage
// return;
foreach (var (_, unit) in room.Units)

View File

@@ -0,0 +1,21 @@
// using Fantasy;
// using Fantasy.Async;
// using Fantasy.Entitas;
// using Fantasy.Network.Interface;
//
// namespace NB.Chat;
//
// public sealed class G2Chat_LoginRequestHandler : AddressRPC<Scene, G2Chat_LoginRequest, Chat2G_LoginResponse>
// {
// protected override async FTask Run(Scene scene, G2Chat_LoginRequest request, Chat2G_LoginResponse response, Action reply)
// {
// var chatUnit = scene.GetComponent<ChatUnitManageComponent>().Add(request.UnitId, request.UserName, request.GateRouteId);
// response.ChatRouteId = chatUnit.RunTimeId;
// // 这里模拟创建一个频道用于测试用
// var chatChannelCenterComponent = scene.GetComponent<ChatChannelCenterComponent>();
// var chatChannelComponent = chatChannelCenterComponent.Apply(1);
// // 加入到聊天频道
// chatChannelComponent.JoinChannel(request.UnitId);
// await FTask.CompletedTask;
// }
// }

View File

@@ -0,0 +1,13 @@
// using Fantasy.Async;
// using Fantasy.Network.Interface;
//
// namespace NB.Chat;
//
// public sealed class G2Chat_OfflineRequestHandler : AddressRPC<ChatUnit, G2Chat_OfflineRequest, Chat2G_OfflineResponse>
// {
// protected override async FTask Run(ChatUnit chatUnit, G2Chat_OfflineRequest request, Chat2G_OfflineResponse response, Action reply)
// {
// await FTask.CompletedTask;
// chatUnit.Scene.GetComponent<ChatUnitManageComponent>().Remove(chatUnit.Id);
// }
// }

View File

@@ -0,0 +1,20 @@
// using Fantasy;
// using Fantasy.Async;
// using Fantasy.Network.Interface;
//
// namespace NB.Chat;
//
// public sealed class Other2Chat_ChatMessageHandler : Address<SocialUnit, Other2Chat_ChatMessage>
// {
// protected override async FTask Run(SocialUnit chatUnit, Other2Chat_ChatMessage message)
// {
// var result = ChatSceneHelper.Distribution(chatUnit, message.ChatInfoTree, false);
//
// if (result != 0)
// {
// Log.Warning($"Other2Chat_ChatMessageHandler: Distribution failed, result: {result}");
// }
//
// await FTask.CompletedTask;
// }
// }

View File

@@ -0,0 +1,15 @@
using Fantasy;
using Fantasy.Async;
using Fantasy.Network.Interface;
using NB.Game;
namespace NB.Chat;
public sealed class C2Chat_SendMessageRequestHandler : AddressRPC<Player, C2Game_SendMessageRequest, Game2C_SendMessageResponse>
{
protected override async FTask Run(Player chatUnit, C2Game_SendMessageRequest request, Game2C_SendMessageResponse response, Action reply)
{
response.ErrorCode = ChatSceneHelper.Distribution(chatUnit, request.ChatInfoTree);
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,39 @@
using Fantasy;
namespace NB.Chat;
public static class ChatChannelCenterHelper
{
/// <summary>
/// 申请一个频道
/// </summary>
/// <param name="scene"></param>
/// <param name="channelId"></param>
/// <returns></returns>
public static ChatChannelComponent Apply(Scene scene, long channelId)
{
return scene.GetComponent<ChatChannelCenterComponent>().Apply(channelId);
}
/// <summary>
/// 尝试获取一个频道
/// </summary>
/// <param name="scene"></param>
/// <param name="channelId"></param>
/// <param name="channel"></param>
/// <returns></returns>
public static bool TryGet(Scene scene, long channelId, out ChatChannelComponent channel)
{
return scene.GetComponent<ChatChannelCenterComponent>().TryGet(channelId, out channel);
}
/// <summary>
/// 解散一个频道
/// </summary>
/// <param name="scene"></param>
/// <param name="channelId"></param>
public static void Disband(Scene scene, long channelId)
{
scene.GetComponent<ChatChannelCenterComponent>().Disband(channelId);
}
}

View File

@@ -0,0 +1,30 @@
using Fantasy;
using Fantasy.Platform.Net;
namespace NB.Chat;
public static class ChatHelper
{
/// <summary>
/// 发送一个聊天消息给ChatScene不能在ChatScene中调用
/// </summary>
/// <param name="scene"></param>
/// <param name="chatUnitRouteId"></param>
/// <param name="tree"></param>
public static void SendChatMessage(Scene scene, long chatUnitRouteId, ChatInfoTree tree)
{
// if (scene.SceneType == SceneType.Social)
// {
// Log.Warning("ChatHelper.SendChatMessage: scene is not a chat scene.");
// return;
// }
var other2ChatChatMessage = new Other2Chat_ChatMessage()
{
ChatInfoTree = tree
};
// scene.NetworkMessagingComponent.SendInnerRoute(chatUnitRouteId, other2ChatChatMessage);
scene.NetworkMessagingComponent.Send(chatUnitRouteId, other2ChatChatMessage);
}
}

View File

@@ -0,0 +1,128 @@
// using Fantasy;
//
// namespace NB.Chat
// {
// /// <summary>
// /// 聊天信息节点
// /// </summary>
// public static class ChatNodeFactory
// {
// /// <summary>
// /// 添加文本节点
// /// </summary>
// /// <param name="chatInfoTree"></param>
// /// <param name="content"></param>
// /// <returns></returns>
// public static ChatInfoTree AddendTextNode(this ChatInfoTree chatInfoTree, string content)
// {
// var chatInfoNode = new ChatInfoNode()
// {
// ChatNodeType = (int)ChatNodeType.Text,
// Content = content
// };
// chatInfoTree.Node.Add(chatInfoNode);
// return chatInfoTree;
// }
//
// /// <summary>
// /// 添加链接节点
// /// </summary>
// /// <param name="chatInfoTree"></param>
// /// <param name="content"></param>
// /// <param name="link"></param>
// /// <returns></returns>
// public static ChatInfoTree AddendLinkNode(this ChatInfoTree chatInfoTree, string content, string link)
// {
// var chatLinkNode = new ChatLinkNode()
// {
// Link = link
// };
// var serializerComponent = chatInfoTree.Scene.GetComponent<SerializerComponent>();
// var chatInfoNode = new ChatInfoNode()
// {
// ChatNodeType = (int)ChatNodeType.Link,
// ChatNodeEvent = (int)ChatNodeEvent.OpenLink,
// Content = content,
// Data = serializerComponent.Serialize(chatLinkNode)
// };
// chatInfoTree.Node.Add(chatInfoNode);
// return chatInfoTree;
// }
//
// /// <summary>
// /// 添加图片节点
// /// </summary>
// /// <param name="chatInfoTree"></param>
// /// <param name="content"></param>
// /// <returns></returns>
// public static ChatInfoTree AddendImageNode(this ChatInfoTree chatInfoTree, string content)
// {
// var chatInfoNode = new ChatInfoNode()
// {
// ChatNodeType = (int)ChatNodeType.Image,
// Content = content
// };
// chatInfoTree.Node.Add(chatInfoNode);
// return chatInfoTree;
// }
//
// /// <summary>
// /// 添加打开UI节点
// /// </summary>
// /// <param name="chatInfoTree"></param>
// /// <param name="content"></param>
// /// <param name="uiName"></param>
// /// <returns></returns>
// public static ChatInfoTree AddendOpenUINode(this ChatInfoTree chatInfoTree, string content, string uiName)
// {
// var chatOpenUINode = new ChatOpenUINode()
// {
// UIName = uiName
// };
// var serializerComponent = chatInfoTree.Scene.GetComponent<SerializerComponent>();
// var chatInfoNode = new ChatInfoNode()
// {
// ChatNodeType = (int)ChatNodeType.OpenUI,
// ChatNodeEvent = (int)ChatNodeEvent.OpenUI,
// Content = content,
// Data = serializerComponent.Serialize(chatOpenUINode)
// };
// chatInfoTree.Node.Add(chatInfoNode);
// return chatInfoTree;
// }
//
// /// <summary>
// /// 添加位置节点
// /// </summary>
// /// <param name="chatInfoTree"></param>
// /// <param name="content"></param>
// /// <param name="mapName"></param>
// /// <param name="mapX"></param>
// /// <param name="mapY"></param>
// /// <param name="mapZ"></param>
// /// <returns></returns>
// public static ChatInfoTree AddendPositionNode(this ChatInfoTree chatInfoTree, string content, string mapName,
// float mapX, float mapY, float mapZ)
// {
// var chatPositionNode = new ChatPositionNode()
// {
// MapName = mapName,
// PosX = mapX,
// PosY = mapY,
// PosZ = mapZ,
// };
//
// var serializerComponent = chatInfoTree.Scene.GetComponent<SerializerComponent>();
// var chatInfoNode = new ChatInfoNode()
// {
// ChatNodeType = (int)ChatNodeType.Position,
// ChatNodeEvent = (int)ChatNodeEvent.Position,
// Content = content,
// Data = serializerComponent.Serialize(chatPositionNode)
// };
//
// chatInfoTree.Node.Add(chatInfoNode);
// return chatInfoTree;
// }
// }
// }

View File

@@ -0,0 +1,258 @@
using System.Threading.Channels;
using Fantasy;
using Fantasy.Helper;
using Fantasy.Platform.Net;
using NB.Game;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
namespace NB.Chat;
public static class ChatSceneHelper
{
private const int ChatCD = 1000;
private const int MaxTextLength = 10;
private const int MaxShowItemCount = 2;
/// <summary>
/// 聊天消息分发入口
/// </summary>
/// <param name="chatUnit"></param>
/// <param name="tree"></param>
/// <param name="isCheckSendTime"></param>
/// <returns></returns>
public static uint Distribution(Player chatUnit, ChatInfoTree tree, bool isCheckSendTime = true)
{
var result = Condition(chatUnit, tree, isCheckSendTime);
if (result != 0)
{
return result;
}
switch ((ChatChannelType)tree.ChatChannelType)
{
case ChatChannelType.Broadcast:
{
Broadcast(chatUnit.Scene, tree);
return 0;
}
case ChatChannelType.Team:
{
return Channel(chatUnit, tree);
}
case ChatChannelType.Private:
{
return Private(chatUnit, tree);
}
default:
{
// 这个1代表当前频道不存在。
return 1;
}
}
}
/// <summary>
/// 聊天消息条件判断
/// </summary>
/// <param name="chatUnit"></param>
/// <param name="tree"></param>
/// <param name="isCheckSendTime"></param>
/// <returns></returns>
private static uint Condition(Player chatUnit, ChatInfoTree tree, bool isCheckSendTime = true)
{
// 每个频道可能聊天的间隔都不一样。
// 这里的条件判断,是根据频道的类型,来判断是否到达了聊天的间隔。
var now = TimeHelper.Now;
if (isCheckSendTime)
{
// 这里的间隔时间,是根据频道的类型,来获取的。
chatUnit.SendTime.TryGetValue(tree.ChatChannelType, out var sendTime);
// 判定聊天间隔是否到达
// 其实的话这个ChatCD应该是根据频道的类型来获取的。
// 一般的话都是做一个配置表,通过配置表来获取不同频道的时间间隔。
if (now - sendTime < ChatCD)
{
// 这个1代表当前频道聊天的间隔过短
return 1;
}
}
// 判定聊天内容是否超长
var itemCount = 0;
var chatTextSize = 0;
foreach (var chatInfoNode in tree.Node)
{
switch ((ChatNodeType)chatInfoNode.ChatNodeType)
{
case ChatNodeType.Text:
{
chatTextSize += chatInfoNode.Content.Length;
break;
}
case ChatNodeType.Image:
{
// 规定图片占聊天消息的5个字符长度
chatTextSize += 5;
break;
}
case ChatNodeType.OpenUI:
{
// 规定OpenUI占聊天消息的10个字符长度
chatTextSize += 10;
break;
}
case ChatNodeType.Item:
{
itemCount++;
break;
}
}
}
if (chatTextSize > MaxTextLength)
{
// 这个2代表当前频道聊天内容超长
return 2;
}
if (itemCount > MaxShowItemCount)
{
// 这个3代表当前频道聊天里道具数量过多
return 3;
}
if (isCheckSendTime)
{
// 更新当前频道的发送时间
chatUnit.SendTime[tree.ChatChannelType] = now;
}
return 0;
}
#region
/// <summary>
/// 广播消息
/// </summary>
/// <param name="scene"></param>
/// <param name="tree"></param>
private static void Broadcast(Scene scene, ChatInfoTree tree)
{
var networkMessagingComponent = scene.NetworkMessagingComponent;
var chatMessage = new Chat2G_ChatMessage()
{
ChatInfoTree = tree
};
if (tree.Target.Count > 0)
{
var chatUnitManageComponent = scene.GetComponent<PlayerManageComponent>();
// 给一部分人广播消息
foreach (var chatUnitId in tree.Target)
{
if (!chatUnitManageComponent.TryGet(chatUnitId, out var chatUnit))
{
continue;
}
// networkMessagingComponent.SendInnerRoute(chatUnit.GateRouteId, chatMessage);
networkMessagingComponent.Send(chatUnit.SessionRunTimeId, chatMessage);
}
return;
}
// 发送给所有Gate服务器让Gate服务器转发给其他客户端
var gateConfigs = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Gate);
foreach (var gateSceneConfig in gateConfigs)
{
// 这里是要发送一个消息给Gate服务器Gate服务器再转发给其他客户端
// networkMessagingComponent.SendInnerRoute(gateSceneConfig.RouteId, chatMessage);
networkMessagingComponent.Send(gateSceneConfig.Address, chatMessage);
}
}
/// <summary>
/// 发送频道消息
/// </summary>
/// <param name="chatUnit"></param>
/// <param name="tree"></param>
private static uint Channel(Player chatUnit, ChatInfoTree tree)
{
// 那组队,公会、地图、等这个的聊天,如何使用频道呢?
// 这里的频道,是指一个频道,比如一个公会的频道,一个队伍的频道,一个地图的频道。
// 1、一般组队工会、地图等都是有一个创建的一个逻辑咱们个在这个创建的逻辑中根据队伍、公会、地图的ID
// 把这些ID当做频道ID,然后发送到Chat服务器先申请一个频道把这个频道ID返回给创建公会队伍的逻辑。
// 这时候队伍公会、发送聊天消息的时候就会根据这个ID来进行发送。
// 2、地图同样道理创建地图的时候也会有一个创建的逻辑这个逻辑会返回一个地图的ID这个ID就是地图的频道ID。
// 3、这这些ID根据频道类型发送给客户端客户端发送的时候根据频道不同拿不同的ID来发送。
// 课外:
// 客户端创建一个频道、拿到这个频道号告诉其他人其他人通过这个频道ID加入到这个频道。
// 客户端创建一个频道,邀请其他人加入到这个频道,其他人可能客户端会接收一个协议,就是邀请你加入到这个频道,如果同意,
// 你就加入到这个频道(你点同意后,会发送一个消息给聊天服务器,聊天服务器会把你加入到这个频道)。
if (!chatUnit.Channels.TryGetValue(tree.ChatChannelId, out var channel))
{
// 这个1代表当前频道不存在。
return 1;
}
channel.Send(tree);
return 0;
}
/// <summary>
/// 发送私聊消息
/// </summary>
/// <param name="chatUnit"></param>
/// <param name="tree"></param>
/// <returns></returns>
private static uint Private(Player chatUnit, ChatInfoTree tree)
{
// 私聊,就是两个玩家之间,直接聊天。
// 1、首先客户端需要知道对方的ID这个ID是通过什么方式获取的呢
// 2、客户端需要发送一个私聊消息给聊天服务器聊天服务器需要把这个消息转发给对方。
// 3、对方收到消息后需要显示出来。
// 4、聊天服务器需要记录这个私聊消息并把这个消息转发给两个玩家。
// 5、两个玩家收到消息后需要显示出来。
if (tree.Target == null || tree.Target.Count <= 0)
{
// 这个1代表对方ID不的合法的。
return 1;
}
var targetChatUnitId = tree.Target[0];
var scene = chatUnit.Scene;
if (!scene.GetComponent<PlayerManageComponent>().TryGet(targetChatUnitId, out var targetChatUnit))
{
// 这个2代表对方不在线。
return 2;
}
var networkMessagingComponent = scene.NetworkMessagingComponent;
var chatMessage = new Game2C_Message()
{
ChatInfoTree = tree
};
// // 先给自己发送一个聊天消息。
// networkMessagingComponent.SendInnerRoute(chatUnit.GateRouteId, chatMessage);
// // 然后再给对方发送一个聊天消息。
// networkMessagingComponent.SendInnerRoute(targetChatUnit.GateRouteId, chatMessage);
// 先给自己发送一个聊天消息。
networkMessagingComponent.Send(chatUnit.SessionRunTimeId, chatMessage);
// 然后再给对方发送一个聊天消息。
networkMessagingComponent.Send(targetChatUnit.SessionRunTimeId, chatMessage);
return 0;
}
#endregion
}

View File

@@ -0,0 +1,121 @@
using Fantasy;
namespace NB.Chat;
/// <summary>
/// 创建聊天树的总入口
/// </summary>
public static class ChatTreeFactory
{
/// <summary>
/// 创建世界聊天树
/// </summary>
/// <param name="scene"></param>
/// <returns></returns>
public static ChatInfoTree World(Scene scene)
{
return new ChatInfoTree()
{
Scene = scene,
ChatChannelType = (int)ChatChannelType.World,
};
}
/// <summary>
/// 创建私聊聊天树
/// </summary>
/// <param name="scene"></param>
/// <returns></returns>
public static ChatInfoTree Private(Scene scene)
{
return new ChatInfoTree()
{
Scene = scene,
ChatChannelType = (int)ChatChannelType.Private,
};
}
/// <summary>
/// 创建系统聊天树
/// </summary>
/// <param name="scene"></param>
/// <returns></returns>
public static ChatInfoTree System(Scene scene)
{
return new ChatInfoTree()
{
Scene = scene,
ChatChannelType = (int)ChatChannelType.System,
};
}
/// <summary>
/// 创建公广播聊天树
/// </summary>
/// <param name="scene"></param>
/// <returns></returns>
public static ChatInfoTree Broadcast(Scene scene)
{
return new ChatInfoTree()
{
Scene = scene,
ChatChannelType = (int)ChatChannelType.Broadcast,
};
}
/// <summary>
/// 创建公告聊天树
/// </summary>
/// <param name="scene"></param>
/// <returns></returns>
public static ChatInfoTree Notice(Scene scene)
{
return new ChatInfoTree()
{
Scene = scene,
ChatChannelType = (int)ChatChannelType.Notice,
};
}
/// <summary>
/// 创建队伍聊天树
/// </summary>
/// <param name="scene"></param>
/// <returns></returns>
public static ChatInfoTree Team(Scene scene)
{
return new ChatInfoTree()
{
Scene = scene,
ChatChannelType = (int)ChatChannelType.Team,
};
}
/// <summary>
/// 创建附近人聊天树
/// </summary>
/// <param name="scene"></param>
/// <returns></returns>
public static ChatInfoTree Near(Scene scene)
{
return new ChatInfoTree()
{
Scene = scene,
ChatChannelType = (int)ChatChannelType.Near,
};
}
/// <summary>
/// 创建当前地图聊天树
/// </summary>
/// <param name="scene"></param>
/// <returns></returns>
public static ChatInfoTree CurrentMap(Scene scene)
{
return new ChatInfoTree()
{
Scene = scene,
ChatChannelType = (int)ChatChannelType.CurrentMap,
};
}
}

View File

@@ -0,0 +1,49 @@
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8601 // Possible null reference assignment.
namespace NB.Chat;
public sealed class ChatChannelCenterComponentDestroySystem : DestroySystem<ChatChannelCenterComponent>
{
protected override void Destroy(ChatChannelCenterComponent self)
{
foreach (var chatChannelComponent in self.Channels.Values.ToArray())
{
chatChannelComponent.Dispose();
}
self.Channels.Clear();
}
}
public static class ChatChannelCenterComponentSystem
{
public static ChatChannelComponent Apply(this ChatChannelCenterComponent self, long channelId)
{
if (self.Channels.TryGetValue(channelId, out var channel))
{
return channel;
}
channel = Entity.Create<ChatChannelComponent>(self.Scene, channelId, true, true);
self.Channels.Add(channelId, channel);
return channel;
}
public static bool TryGet(this ChatChannelCenterComponent self, long channelId, out ChatChannelComponent channel)
{
return self.Channels.TryGetValue(channelId, out channel);
}
public static void Disband(this ChatChannelCenterComponent self, long channelId)
{
if (self.Channels.Remove(channelId, out var channel))
{
return;
}
channel.Dispose();
}
}

View File

@@ -0,0 +1,88 @@
using Fantasy;
using Fantasy.Entitas;
using NB.Game;
namespace NB.Chat;
public class MemoryEntity : Entity
{
}
public static class ChatChannelComponentSystem
{
public static void Send(this ChatChannelComponent self, ChatInfoTree tree)
{
var chatUnitManageComponent = self.Scene.GetComponent<PlayerManageComponent>();
var networkMessagingComponent = self.Scene.NetworkMessagingComponent;
var chatMessage = new Game2C_Message()
{
ChatInfoTree = tree
};
foreach (var unitId in self.Units)
{
if (!chatUnitManageComponent.TryGet(unitId, out var chatUnit))
{
continue;
}
// networkMessagingComponent.SendInnerRoute(chatUnit.GateRouteId, chatMessage);
networkMessagingComponent.Send(chatUnit.SessionRunTimeId, chatMessage);
}
}
public static bool JoinChannel(this ChatChannelComponent self, long chatUnitId)
{
var chatUnitManageComponent = self.Scene.GetComponent<PlayerManageComponent>();
if (!chatUnitManageComponent.TryGet(chatUnitId, out var chatUnit))
{
return false;
}
// 将当前频道中加入该用户。
self.Units.Add(chatUnitId);
// 给用户添加频道。
if (!chatUnit.Channels.ContainsKey(self.Id))
{
chatUnit.Channels.Add(self.Id, self);
}
// 可以在这里给客户端发送一个加入频道成功的消息。
return true;
}
public static bool IsJoinedChannel(this ChatChannelComponent self, long chatUnitId)
{
return self.Units.Contains(chatUnitId);
}
public static void ExitChannel(this ChatChannelComponent self, long chatUnitId, bool isRemoveUnitChannel = true)
{
if (!self.Units.Contains(chatUnitId))
{
return;
}
var chatUnitManageComponent = self.Scene.GetComponent<PlayerManageComponent>();
if (!chatUnitManageComponent.TryGet(chatUnitId, out var chatUnit))
{
return;
}
if (isRemoveUnitChannel)
{
// 给用户移除频道。
chatUnit.Channels.Remove(self.Id);
}
// 在当前频道中移除该用户。
self.Units.Remove(chatUnitId);
// 如果当前频道中没有用户了,则销毁该频道。
if (self.Units.Count == 0)
{
self.Dispose();
}
}
}

View File

@@ -0,0 +1,70 @@
// using Fantasy;
// using Fantasy.Entitas;
// using Fantasy.Entitas.Interface;
//
// #pragma warning disable CS8601 // Possible null reference assignment.
//
// namespace NB.Chat;
//
// public sealed class ChatUnitManageComponentDestroySystem : DestroySystem<ChatUnitManageComponent>
// {
// protected override void Destroy(ChatUnitManageComponent self)
// {
// foreach (var chatUnit in self.Units.Values.ToArray())
// {
// chatUnit.Dispose();
// }
//
// self.Units.Clear();
// }
// }
// public static class ChatUnitManageComponentSystem
// {
// public static ChatUnit Add(this ChatUnitManageComponent self, long unitId, string userName, long gateRouteId)
// {
// if (!self.Units.TryGetValue(unitId, out var chatUnit))
// {
// chatUnit = Entity.Create<ChatUnit>(self.Scene, unitId, true, true);
// self.Units.Add(unitId, chatUnit);
// Log.Debug(
// $"Add ChatUnit Count: {self.Units.Count} UnitId: {unitId} UserName: {userName} GateRouteId: {gateRouteId}");
// }
// else
// {
// Log.Debug($"ChatUnit: {chatUnit.UserName}({chatUnit.GateRouteId})");
// }
//
// chatUnit.UserName = userName;
// chatUnit.GateRouteId = gateRouteId;
// return chatUnit;
// }
//
// public static ChatUnit? Get(this ChatUnitManageComponent self, long unitId)
// {
// return self.Units.GetValueOrDefault(unitId);
// }
//
// public static bool TryGet(this ChatUnitManageComponent self, long unitId, out ChatUnit chatUnit)
// {
// return self.Units.TryGetValue(unitId, out chatUnit);
// }
//
// public static void Remove(this ChatUnitManageComponent self, long unitId, bool isDispose = true)
// {
// // 由于退出频道的时候也会检查该玩家是否在ChatUnitManageComponent中所以这里不做移除操作。
// if (!self.Units.TryGetValue(unitId, out var chatUnit))
// {
// return;
// }
//
// if (isDispose)
// {
// chatUnit.Dispose();
// }
//
// // 因为玩家已经执行了退出频道的操作了,所以要清除一下这个数据。
// self.Units.Remove(unitId);
// Log.Debug($"Remove ChatUnit: {chatUnit.UserName}({chatUnit.GateRouteId}) Count: {self.Units.Count}");
// }
// }

View File

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

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

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

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

@@ -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

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

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

@@ -0,0 +1,22 @@
using Fantasy.Entitas.Interface;
namespace NB.Chat;
public class MailBoxDestroySystem : DestroySystem<MailBox>
{
protected override void Destroy(MailBox self)
{
if (self.Mail != null)
{
self.Mail.Dispose();
self.Mail = null;
}
self.BoxType = MailBoxType.None;
self.CreateTime = 0;
self.ExpireTime = 0;
self.SendAccountId = 0;
self.AccountId.Clear();
self.Received.Clear();
}
}

View File

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

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

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

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

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

@@ -0,0 +1,22 @@
// 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();
// }
// }