修改为luban

This commit is contained in:
Bob.Song
2026-03-05 15:03:45 +08:00
parent 13e4315a70
commit 36067705f2
122 changed files with 10497 additions and 2216 deletions

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 NBF.Social;
// 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(SocialUnit 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(SocialUnit 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<SocialUnitManageComponent>();
// 给一部分人广播消息
foreach (var chatUnitId in tree.Target)
{
if (!chatUnitManageComponent.TryGet(chatUnitId, out var chatUnit))
{
continue;
}
// networkMessagingComponent.SendInnerRoute(chatUnit.GateRouteId, chatMessage);
networkMessagingComponent.Send(chatUnit.GateRouteId, 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(SocialUnit 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(SocialUnit 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<SocialUnitManageComponent>().TryGet(targetChatUnitId, out var targetChatUnit))
{
// 这个2代表对方不在线。
return 2;
}
var networkMessagingComponent = scene.NetworkMessagingComponent;
var chatMessage = new Chat2C_Message()
{
ChatInfoTree = tree
};
// // 先给自己发送一个聊天消息。
// networkMessagingComponent.SendInnerRoute(chatUnit.GateRouteId, chatMessage);
// // 然后再给对方发送一个聊天消息。
// networkMessagingComponent.SendInnerRoute(targetChatUnit.GateRouteId, chatMessage);
// 先给自己发送一个聊天消息。
networkMessagingComponent.Send(chatUnit.GateRouteId, chatMessage);
// 然后再给对方发送一个聊天消息。
networkMessagingComponent.Send(targetChatUnit.GateRouteId, 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

@@ -1,20 +0,0 @@
using Fantasy;
using Fantasy.Entitas;
namespace NB.Chat;
public static class ChatUnitFactory
{
/// <summary>
/// 创建一个新的Player
/// </summary>
/// <param name="scene"></param>
/// <param name="aId">ToKen令牌传递过来的aId</param>
/// <param name="isSaveDataBase">是否在创建的过程中保存到数据库</param>
/// <returns></returns>
public static SocialUnit Create(Scene scene, long aId)
{
var player = Entity.Create<SocialUnit>(scene, aId, true, true);
return player;
}
}

View File

@@ -1,173 +0,0 @@
using System.Collections.Generic;
using Fantasy;
using Fantasy.Async;
using Fantasy.Platform.Net;
namespace NB.Chat;
public static class SocialSceneHelper
{
#region
/// <summary>
/// 发送一条私聊
/// </summary>
/// <param name="scene"></param>
/// <param name="selfId"></param>
/// <param name="targetId"></param>
/// <param name="message"></param>
public static void PrivateMessage(Scene scene, long selfId, long targetId, ChatMessageInfo message)
{
var chatUnitManage = scene.GetComponent<SocialUnitManageComponent>();
if (chatUnitManage == null) return;
var chatUnit = chatUnitManage.Get(targetId);
message.Type = 1;
message.Source = selfId;
if (chatUnit != null)
{
//对方在线
BroadcastAll(scene, message, [targetId]);
}
else
{
//对方不在线,存入待领取列表,等待玩家下次登录领取
chatUnitManage.SaveOfflineMessage(targetId, message);
}
}
/// <summary>
/// 发送一条地图消息
/// </summary>
/// <param name="scene"></param>
/// <param name="mapId"></param>
/// <param name="message"></param>
public static void BroadcastMap(Scene scene, long mapId, ChatMessageInfo message)
{
var chatUnitManage = scene.GetComponent<SocialUnitManageComponent>();
if (chatUnitManage == null) return;
HashSet<long> targets = new HashSet<long>();
foreach (var (_, chatUnit) in chatUnitManage.Units)
{
if (chatUnit.MapId == mapId)
{
targets.Add(chatUnit.Id);
}
}
if (targets.Count < 1)
{
Log.Info("地图在线人数为0群发取消");
return;
}
BroadcastAll(scene, message, targets);
}
/// <summary>
/// 发送一条频道消息
/// </summary>
/// <param name="scene"></param>
/// <param name="channelId"></param>
/// <param name="message"></param>
public static void BroadcastChannel(Scene scene, long channelId, ChatMessageInfo message)
{
var chatUnitManage = scene.GetComponent<SocialUnitManageComponent>();
if (chatUnitManage == null) return;
HashSet<long> targets = new HashSet<long>();
foreach (var (_, chatUnit) in chatUnitManage.Units)
{
if (chatUnit.CurrentChannel == channelId)
{
targets.Add(chatUnit.Id);
}
}
if (targets.Count < 1)
{
Log.Info("频道在线人数为0群发取消");
return;
}
BroadcastAll(scene, message, targets);
}
/// <summary>
/// 广播消息给所有人
/// </summary>
/// <param name="scene"></param>
/// <param name="message"></param>
/// <param name="targets"></param>
public static void BroadcastAll(Scene scene, ChatMessageInfo message, HashSet<long>? targets = null)
{
//发送给所有Gate服务器让Gate转发给其他客户端
var gateConfigs = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Gate);
var sendMessage = new S2G_ChatMessage()
{
Message = message,
};
if (targets != null && targets.Count > 0)
{
sendMessage.IdList.AddRange(targets);
}
var networkMessagingComponent = scene.NetworkMessagingComponent;
foreach (var config in gateConfigs)
{
//发送给Gate服务器转发消息
networkMessagingComponent.Send(config.Address, sendMessage);
}
}
#endregion
#region 线线
// public static async FTask<(long, long)> Online(Scene scene, RoleSimpleInfo roleSimple, long gateRuntimeId)
// {
// var gameSceneConfig = GetSceneConfig();
// var gameRouteId = gameSceneConfig.RouteId;
// //连接到游戏中心服
// var gameResponse = (S2G_EnterResponse)await scene.NetworkMessagingComponent.CallInnerRoute(
// gameRouteId, new G2S_EnterRequest()
// {
// Role = roleSimple,
// GateRouteId = gateRuntimeId
// });
//
// if (gameResponse.ErrorCode != 0)
// {
// return (0, 0);
// }
//
// return (gameResponse.RoleRouteId, gameRouteId);
// }
//
// public static async FTask Offline(Scene scene, long accountId, long gateRuntimeId, long sceneRouteId)
// {
// for (int i = 0; i < 10; i++)
// {
// var gameResponse = (S2G_ExitResponse)await scene.NetworkMessagingComponent.CallInnerRoute(
// sceneRouteId, new G2S_ExitRequest()
// {
// AccountId = accountId,
// GateRouteId = gateRuntimeId
// });
// if (gameResponse.ErrorCode == 0)
// {
// return;
// }
// }
//
// Log.Error("重试多次还是退出失败,需检查");
// }
//
// private static SceneConfig GetSceneConfig()
// {
// var gameSceneConfigs = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Social);
//
// return gameSceneConfigs.First();
// }
#endregion
}