提交示例代码
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
117
聊天系统课程代码/Server/Hotfix/Gate/Helper/GateLoginHelper.cs
Normal file
117
聊天系统课程代码/Server/Hotfix/Gate/Helper/GateLoginHelper.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
105
聊天系统课程代码/Server/Hotfix/Gate/Helper/GateUnitHelper.cs
Normal file
105
聊天系统课程代码/Server/Hotfix/Gate/Helper/GateUnitHelper.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
聊天系统课程代码/Server/Hotfix/Gate/System/GateUnitSystem.cs
Normal file
14
聊天系统课程代码/Server/Hotfix/Gate/System/GateUnitSystem.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user