提交示例代码

This commit is contained in:
Bob.Song
2026-03-05 11:39:06 +08:00
commit 25958f58c3
2534 changed files with 209593 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace Fantasy;
public sealed class Chat2G_ChatMessageHandler : Route<Scene,Chat2G_ChatMessage>
{
protected override async FTask Run(Scene scene, Chat2G_ChatMessage message)
{
var chatMessage = new Chat2C_Message()
{
ChatInfoTree = message.ChatInfoTree
};
foreach (var session in scene.GetComponent<GateUnitManageComponent>().ForEachUnitSession())
{
session.Send(chatMessage);
}
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,15 @@
using Fantasy.Async;
using Fantasy.Network;
using Fantasy.Network.Interface;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
namespace Fantasy;
public sealed class C2G_ExitRequestHandler : MessageRPC<C2G_ExitRequest, G2C_ExitResponse>
{
protected override async FTask Run(Session session, C2G_ExitRequest request, G2C_ExitResponse response, Action reply)
{
session.RemoveComponent<GateUnitFlagComponent>();
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,30 @@
using Fantasy.Async;
using Fantasy.Network;
using Fantasy.Network.Interface;
namespace Fantasy;
public sealed class C2G_LoginRequestHandler : MessageRPC<C2G_LoginRequest, G2C_LoginResponse>
{
protected override async FTask Run(Session session, C2G_LoginRequest request, G2C_LoginResponse response, Action reply)
{
if (string.IsNullOrEmpty(request.UserName))
{
// 这里返回的1代表账号信息是空的。
response.ErrorCode = 1;
return;
}
var scene = session.Scene;
// 添加一个GateUnitFlagComponent组件用来Session断开、或者传递数据时使用。
var gateUnitFlagComponent = session.GetOrAddComponent<GateUnitFlagComponent>();
// 上线到Gate
var gateUnit = GateUnitHelper.Online(scene, request.UserName, session);
// 设置GateUnitFlagComponent的GateUnitId
gateUnitFlagComponent.GateUnitId = gateUnit.Id;
// 登录到其他服务器
await GateLoginHelper.Online(session, gateUnit, session.RunTimeId);
Log.Debug($"gateUnit : {gateUnit.UserName}");
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,117 @@
using Fantasy.Async;
using Fantasy.Network;
using Fantasy.Platform.Net;
namespace Fantasy;
public static class GateLoginHelper
{
private static readonly List<Func<Scene, GateUnit, long, FTask<(uint, long, int)>>> Scenes =
new List<Func<Scene, GateUnit, long, FTask<(uint, long, int)>>>()
{
// 聊天服务器上线
{ OnlineChat },
// 游戏服务器上线
{ OnlineGame }
};
/// <summary>
/// 上线到其他服务器
/// </summary>
/// <param name="session"></param>
/// <param name="gateUnit"></param>
/// <param name="gateRouteId"></param>
public static async FTask<uint> Online(Session session, GateUnit gateUnit, long gateRouteId)
{
var scene = session.Scene;
// 要实现自动Route转发协议必须要给Session添加一个RouteComponent然后通过它来转发消息。
var routeComponent = session.GetOrAddComponent<RouteComponent>();
// 这里是登录的总入口,在这里会陆续的登录其他服务器,如:聊天服务器、游戏服务器等
foreach (var sceneHandler in Scenes)
{
var (errorCode, routeId, routeType) = await sceneHandler(scene, gateUnit, gateRouteId);
if (errorCode != 0)
{
return errorCode;
}
// 保存上线过的RouteId用于下线时通知其他服务器下线
gateUnit.Routes[routeType] = routeId;
// 添加到路由地址中只有添加了这个路由映射地址才会自动的从Gate转发到Chat
routeComponent.AddAddress(routeType, routeId);
}
return 0;
}
/// <summary>
/// 通知其他服务器下线
/// </summary>
/// <param name="gateUnit"></param>
public static async FTask<uint> Offline(GateUnit gateUnit)
{
var networkMessagingComponent = gateUnit.Scene.NetworkMessagingComponent;
foreach (var (routeType, routeId) in gateUnit.Routes)
{
switch (routeType)
{
case RouteType.ChatRoute:
{
var response =
(Chat2G_OfflineResponse)await networkMessagingComponent.CallInnerRoute(routeId,
new G2Chat_OfflineRequest());
if (response.ErrorCode != 0)
{
return response.ErrorCode;
}
continue;
}
}
}
gateUnit.Routes.Clear();
return 0;
}
/// <summary>
/// 聊天服务器上线
/// </summary>
/// <param name="scene"></param>
/// <param name="gateUnit"></param>
/// <param name="gateRouteId"></param>
/// <returns></returns>
private static async FTask<(uint errorCode, long routeId, int routeType)> OnlineChat(Scene scene, GateUnit gateUnit, long gateRouteId)
{
// 登录聊天服务器
var chatConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Chat)[0];
// 咱们框架中如果要Scene和Scene之间通讯必须要用到NetworkMessagingComponent通过它来发送没有其他办法。
var response = (Chat2G_LoginResponse)await scene.NetworkMessagingComponent.CallInnerRoute(chatConfig.RouteId,
new G2Chat_LoginRequest()
{
UserName = gateUnit.UserName,
UnitId = gateUnit.Id,
GateRouteId = gateRouteId
});
return (response.ErrorCode, response.ChatRouteId, RouteType.ChatRoute);
}
/// <summary>
/// 游戏服务器上线
/// </summary>
/// <param name="scene"></param>
/// <param name="gateUnit"></param>
/// <param name="gateRouteId"></param>
/// <returns></returns>
private static async FTask<(uint errorCode, long routeId, int routeType)> OnlineGame(Scene scene, GateUnit gateUnit, long gateRouteId)
{
// 登录聊天服务器
var mapConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Map)[0];
var response = (M2G_LoginResponse)await scene.NetworkMessagingComponent.CallInnerRoute(mapConfig.RouteId,
new G2M_LoginRequest()
{
ChatUnitRouteId = gateUnit.Routes[RouteType.ChatRoute]
});
return (response.ErrorCode, response.MapRouteId, RouteType.GateRoute);
}
}

View File

@@ -0,0 +1,105 @@
using Fantasy.Async;
using Fantasy.Network;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
#pragma warning disable CS8602 // Dereference of a possibly null reference.
namespace Fantasy;
public static class GateUnitHelper
{
/// <summary>
/// GateUnit上线
/// </summary>
/// <param name="scene"></param>
/// <param name="userName"></param>
/// <param name="session"></param>
/// <returns></returns>
public static GateUnit Online(Scene scene, string userName, Session session)
{
// 增加一个GateUnit到缓存中、如果缓存中已经存在直接返回缓存中的实体数据
return scene.GetComponent<GateUnitManageComponent>().Add(userName, session);
}
/// <summary>
/// GateUnit下线
/// </summary>
/// <param name="scene"></param>
/// <param name="gateUnitId"></param>
public static void Offline(Scene scene, long gateUnitId)
{
if (!scene.GetComponent<GateUnitManageComponent>().TryGet(gateUnitId, out var gateUnit))
{
return;
}
gateUnit.GetOrAddComponent<EntityTimeoutComponent>().SetTimeout(5000);
}
/// <summary>
/// 缓存中获取一个GateUnit
/// </summary>
/// <param name="scene"></param>
/// <param name="userName"></param>
/// <returns></returns>
public static GateUnit? Get(Scene scene, string userName)
{
return scene.GetComponent<GateUnitManageComponent>().Get(userName);
}
/// <summary>
/// 缓存中获取一个GateUnit
/// </summary>
/// <param name="scene"></param>
/// <param name="gateUnitId"></param>
/// <returns></returns>
public static GateUnit? Get(Scene scene, long gateUnitId)
{
return scene.GetComponent<GateUnitManageComponent>().Get(gateUnitId);
}
/// <summary>
/// 尝试获取一个GateUnit
/// </summary>
/// <param name="scene"></param>
/// <param name="userName"></param>
/// <param name="gateUnit"></param>
/// <returns></returns>
public static bool TryGet(Scene scene, string userName, out GateUnit? gateUnit)
{
return scene.GetComponent<GateUnitManageComponent>().TryGet(userName, out gateUnit);
}
/// <summary>
/// 尝试获取一个GateUnit
/// </summary>
/// <param name="scene"></param>
/// <param name="gateUnitId"></param>
/// <param name="gateUnit"></param>
/// <returns></returns>
public static bool TryGet(Scene scene, long gateUnitId, out GateUnit? gateUnit)
{
return scene.GetComponent<GateUnitManageComponent>().TryGet(gateUnitId, out gateUnit);
}
/// <summary>
/// 在缓存中移除一个GateUnit
/// </summary>
/// <param name="scene"></param>
/// <param name="userName"></param>
/// <param name="isDispose"></param>
public static FTask Remove(Scene scene, string userName, bool isDispose = true)
{
return scene.GetComponent<GateUnitManageComponent>().Remove(userName, isDispose);
}
/// <summary>
/// 在缓存中移除一个GateUnit
/// </summary>
/// <param name="scene"></param>
/// <param name="gateUnitId"></param>
/// <param name="isDispose"></param>
public static FTask Remove(Scene scene, long gateUnitId, bool isDispose = true)
{
return scene.GetComponent<GateUnitManageComponent>().Remove(gateUnitId, isDispose);
}
}

View File

@@ -0,0 +1,15 @@
using Fantasy.Entitas.Interface;
namespace Fantasy;
public sealed class GateUnitFlagComponentDestroySystem : DestroySystem<GateUnitFlagComponent>
{
protected override void Destroy(GateUnitFlagComponent self)
{
var selfGateUnitId = self.GateUnitId;
// 执行下线操作
GateUnitHelper.Offline(self.Scene, selfGateUnitId);
// 清理垃圾数据
self.GateUnitId = 0;
}
}

View File

@@ -0,0 +1,135 @@
using Fantasy.Async;
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
using Fantasy.Network;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
namespace Fantasy;
public sealed class GateUnitManageComponentDestroySystem : DestroySystem<GateUnitManageComponent>
{
protected override void Destroy(GateUnitManageComponent self)
{
foreach (var gateUnit in self.Units.Values.ToArray())
{
gateUnit.Dispose();
}
self.Units.Clear();
self.UnitsByUserName.Clear();
}
}
public static class GateUnitManageComponentSystem
{
public static GateUnit Add(this GateUnitManageComponent self, string userName, Session session)
{
if (self.UnitsByUserName.TryGetValue(userName, out var gateUnit))
{
gateUnit.Session = session;
// 如果缓存中已经存在了该名字,那就直接从缓存中返回就可以了
Log.Debug($"在缓存中获取的数据 userName:{userName}");
return gateUnit;
}
// 创建一个新的实体
gateUnit = Entity.Create<GateUnit>(self.Scene, true, true);
gateUnit.UserName = userName;
gateUnit.Session = session;
// 添加到缓存中
self.Units.Add(gateUnit.Id, gateUnit);
self.UnitsByUserName.Add(userName, gateUnit);
Log.Debug($"新创建的数据 userName:{userName}");
return gateUnit;
}
public static GateUnit? Get(this GateUnitManageComponent self, string userName)
{
return self.UnitsByUserName.GetValueOrDefault(userName);
}
public static GateUnit? Get(this GateUnitManageComponent self, long gateUnitId)
{
return self.Units.GetValueOrDefault(gateUnitId);
}
public static bool TryGet(this GateUnitManageComponent self, string userName, out GateUnit? gateUnit)
{
return self.UnitsByUserName.TryGetValue(userName, out gateUnit);
}
public static bool TryGet(this GateUnitManageComponent self, long gateUnitId, out GateUnit? gateUnit)
{
return self.Units.TryGetValue(gateUnitId, out gateUnit);
}
public static async FTask Remove(this GateUnitManageComponent self, string userName, bool isDispose = true)
{
if (!self.UnitsByUserName.TryGetValue(userName, out var gateUnit))
{
return;
}
// 通知其他服务器下线
var result = await GateLoginHelper.Offline(gateUnit);
if (result != 0)
{
Log.Error($"通知其他服务器下线失败,错误码:{result}");
return;
}
// 如果其他服务器都已经下线了,那就直接移除本地数据
self.Units.Remove(gateUnit.Id);
self.UnitsByUserName.Remove(userName);
if (isDispose)
{
gateUnit.Dispose();
}
gateUnit.Session = null;
gateUnit.UserName = null;
}
public static async FTask Remove(this GateUnitManageComponent self, long gateUnitId, bool isDispose = true)
{
if (!self.Units.TryGetValue(gateUnitId, out var gateUnit))
{
return;
}
// 通知其他服务器下线
var result = await GateLoginHelper.Offline(gateUnit);
if (result != 0)
{
Log.Error($"通知其他服务器下线失败,错误码:{result}");
return;
}
// 如果其他服务器都已经下线了,那就直接移除本地数据
self.Units.Remove(gateUnitId);
self.UnitsByUserName.Remove(gateUnit.UserName);
if (isDispose)
{
gateUnit.Dispose();
}
gateUnit.Session = null;
gateUnit.UserName = null;
}
public static IEnumerable<Session> ForEachUnitSession(this GateUnitManageComponent self)
{
foreach (var (_, gateUnit) in self.Units)
{
Session gateUnitSession = gateUnit.Session;
if (gateUnitSession == null)
{
continue;
}
yield return gateUnitSession;
}
}
}

View File

@@ -0,0 +1,14 @@
using Fantasy.Entitas.Interface;
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
namespace Fantasy;
public sealed class GateUnitDestroySystem : DestroySystem<GateUnit>
{
protected override void Destroy(GateUnit self)
{
// 移除缓存中的GateUnit
// 这里的销毁只能是通过EntityTimeoutComponent超时来触发的销毁不能通过直接调用Dispose来触发的销毁
GateUnitHelper.Remove(self.Scene, self.Id, false).Coroutine();
}
}