饭太稀

This commit is contained in:
bob
2025-06-30 10:51:37 +08:00
commit 8e45469c83
753 changed files with 87652 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
namespace Fantasy;
// 这个是一个自定义系统类型,用于决定系统类型。
// 也可以用枚举,看个人怎么使用了。
public static class CustomSystemType
{
public const int RunSystem = 1;
}
// 这个是一个自定义系统,用于处理自定义事件。
public abstract class RunSystem<T> : CustomSystem<T> where T : Entity
{
/// <summary>
/// 自定义事件类型,用于决定事件的类型。
/// </summary>
public override int CustomEventType => CustomSystemType.RunSystem;
/// <summary>
/// 不知道为什么这样定义的,就照搬就可以了。
/// </summary>
/// <param name="self"></param>
protected abstract override void Custom(T self);
/// <summary>
/// 不知道为什么这样定义的,就照搬就可以了。
/// </summary>
/// <returns></returns>
public override Type EntitiesType() => typeof(T);
}
// 下面是一个测试自定义系统。
// 首先定义一个组件用来测试自定义系统。
public class TestCustomSystemComponent : Entity
{
}
// 现在给TestCustomSystemComponent组件添加一个自定义系统。
// 现在添加的就是上面定义的RunSystem自定义系统。
public class TestCustomSystemComponentRunSystem : RunSystem<TestCustomSystemComponent>
{
protected override void Custom(TestCustomSystemComponent self)
{
Log.Debug($"执行了TestCustomSystemComponentRunSystem");
}
}
// 执行方法
// 在任何实体下获得Scene下面的EntityComponent.CustomSystem方法。
// 比如下面这个
// 第一个参数是你要执行自定义系统的实体实例
// 第二个参数是自定义系统类型
// scene.EntityComponent.CustomSystem(testCustomSystemComponent, CustomSystemType.RunSystem);
// 你可以在OnCreateSceneEvent.cs的Handler方法里找到执行这个的例子。

View File

@@ -0,0 +1,31 @@
using System.Threading;
using Fantasy.Async;
using Fantasy.Entitas;
using Fantasy.Network.HTTP;
using Microsoft.AspNetCore.Mvc;
namespace Fantasy;
[ApiController]
[Route("api/[controller]")]
[ServiceFilter(typeof(SceneContextFilter))]
public class HelloController : ControllerBase
{
private readonly Scene _scene;
/// <summary>
/// 构造函数依赖注入
/// </summary>
/// <param name="scene"></param>
public HelloController(Scene scene)
{
_scene = scene;
}
[HttpGet("greet")]
public async FTask<IActionResult> Greet()
{
Log.Debug($"HelloController Thread.CurrentThread.ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}");
return Ok($"Hello from the Fantasy controller! _scene.SceneType:{_scene.SceneType} _scene.SceneType:{_scene.SceneConfigId}");
}
}

View File

@@ -0,0 +1,21 @@
using Microsoft.AspNetCore.Mvc;
namespace Fantasy;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpPost]
public IActionResult MiniGame ([FromBody] Product product)
{
// 假设已经保存产品数据
return Ok("Product created successfully");
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}

View File

@@ -0,0 +1,45 @@
using System.Threading;
using Microsoft.AspNetCore.Mvc;
namespace Fantasy;
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly Scene _scene;
/// <summary>
/// 构造函数依赖注入
/// </summary>
/// <param name="scene"></param>
public UsersController(Scene scene)
{
_scene = scene;
}
[HttpGet("{userId}")]
public IActionResult GetUser(int userId)
{
return Ok($"User ID: {userId}");
}
[HttpPost("register")]
public IActionResult RegisterUser([FromBody] User user)
{
return Ok("User registered successfully");
}
[HttpGet("greet")]
public IActionResult Greet()
{
Log.Debug($"HelloController Thread.CurrentThread.ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}");
return Ok($"Hello from the Fantasy controller! _scene.SceneType:{_scene.SceneType} _scene.SceneType:{_scene.SceneConfigId}");
}
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}

20
Hotfix/Hotfix.csproj Normal file
View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TargetFrameworks>net9.0;net8.0</TargetFrameworks>
<LangVersion>default</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Fantasy.Net\Fantasy.Net\Fantasy.Net.csproj" />
<ProjectReference Include="..\Entity\Entity.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Benchmark\" />
<Folder Include="Outer\NormalMessage\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,22 @@
using System;
using Fantasy.Async;
using Fantasy.Entitas;
using Fantasy.Network;
using Fantasy.Network.Interface;
using Fantasy.Network.Route;
namespace Fantasy;
public sealed class G2M_RequestAddressableIdHandler : RouteRPC<Scene, G2M_RequestAddressableId, M2G_ResponseAddressableId>
{
protected override async FTask Run(Scene scene, G2M_RequestAddressableId request, M2G_ResponseAddressableId response, Action reply)
{
// 1、因为是测试代码所以默认每次请求这个协议我都创建一个新的Unit来做Addressable。
var unit = Entity.Create<Unit>(scene, false, true);
// 2、给Unit添加AddressableMessageComponent组件并执行Register()向AddressableScene注册自己当前的位置。
await unit.AddComponent<AddressableMessageComponent>().Register();
// 3、返回给Gate服务器AddressableId
response.AddressableId = unit.Id;
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,15 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace Fantasy;
public class G2A_TestMessageHandler : Route<Scene,G2A_TestMessage>
{
protected override async FTask Run(Scene entity, G2A_TestMessage message)
{
Log.Debug($"G2A_TestMessageHandler :{message.Tag}");
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,19 @@
using System;
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace Fantasy;
public class G2A_TestRequestHandler : RouteRPC<Scene, G2A_TestRequest, G2A_TestResponse>
{
private static int Count;
protected override async FTask Run(Scene entity, G2A_TestRequest request, G2A_TestResponse response, Action reply)
{
if (++Count % 1000000 == 0)
{
Log.Debug($"count:1000000");
}
// Log.Debug($"{Count}");
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,23 @@
using System;
using Fantasy.Async;
using Fantasy.Entitas;
using Fantasy.Network.Interface;
namespace Fantasy;
public sealed class G2Chat_CreateRouteRequestHandler : RouteRPC<Scene, G2Chat_CreateRouteRequest, Chat2G_CreateRouteResponse>
{
protected override async FTask Run(Scene scene, G2Chat_CreateRouteRequest request, Chat2G_CreateRouteResponse response, Action reply)
{
// 接收到Gate消息后首先建立一个实体用来后面接收Route消息。
// 这里就拿ChatUnit来做这个。
var chatUnit = Entity.Create<ChatUnit>(scene, true, true);
// 把Gate传递过来的RouteId保存住以便后面可以直接给Gate发送消息。
// 例如断线等操作都可以通过这个GateRouteId发送到Gate的Session上。
chatUnit.GateRouteId = request.GateRouteId;
// 把chatUnit的RunTimeId发送给Gate。
// 正如之前所说任何实体的RunTimeId都可以当做RouteId使用。
response.ChatRouteId = chatUnit.RouteId;
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,17 @@
using Fantasy.Async;
using Fantasy.Entitas;
using Fantasy.Network.Interface;
namespace Fantasy;
public class G2M_CreateSubSceneRequestHandler : RouteRPC<Scene, G2M_CreateSubSceneRequest, M2G_CreateSubSceneResponse>
{
protected override async FTask Run(Scene scene, G2M_CreateSubSceneRequest request, M2G_CreateSubSceneResponse response, Action reply)
{
// 下面的SceneType传的是666其实并没有这个类型这个是我随便写的。
var subScene = Scene.CreateSubScene(scene, 6666);
// 返回subScene的运行时id
response.SubSceneRouteId = subScene.RouteId;
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,21 @@
using Fantasy.Async;
using Fantasy.Entitas;
using Fantasy.Network.Interface;
using Fantasy.Network.Route;
namespace Fantasy;
public sealed class G2SubScene_AddressableIdRequestHandler : RouteRPC<SubScene, G2SubScene_AddressableIdRequest, SubScene2G_AddressableIdResponse>
{
protected override async FTask Run(SubScene subScene, G2SubScene_AddressableIdRequest request, SubScene2G_AddressableIdResponse response, Action reply)
{
Log.Debug($"G2SubScene_AddressableIdRequestHandler {subScene.SceneType}");
// 1、因为是测试代码所以默认每次请求这个协议我都创建一个新的Unit来做Addressable。
var unit = Entity.Create<Unit>(subScene, false, true);
// 2、给Unit添加AddressableMessageComponent组件并执行Register()向AddressableScene注册自己当前的位置。
await unit.AddComponent<AddressableMessageComponent>().Register();
// 3、返回给Gate服务器AddressableId
response.AddressableId = unit.Id;
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,13 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace Fantasy;
public class G2SubScene_SentMessageHandler : Route<Scene, G2SubScene_SentMessage>
{
protected override async FTask Run(Scene scene, G2SubScene_SentMessage message)
{
Log.Debug($"接受到来自Gate的消息 SceneType:{scene.SceneType} Message:{message.Tag}");
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,108 @@
using Fantasy.Async;
using Fantasy.Entitas;
using Fantasy.Event;
using Fantasy.Helper;
using Fantasy.Serialize;
using ProtoBuf;
namespace Fantasy;
public sealed class SubSceneTestComponent : Entity
{
public override void Dispose()
{
Log.Debug("销毁SubScene下的SubSceneTestComponent");
base.Dispose();
}
}
public sealed class OnCreateSceneEvent : AsyncEventSystem<OnCreateScene>
{
private static long _addressableSceneRunTimeId;
/// <summary>
/// Handles the OnCreateScene event.
/// </summary>
/// <param name="self">The OnCreateScene object.</param>
/// <returns>A task representing the asynchronous operation.</returns>
protected override async FTask Handler(OnCreateScene self)
{
// var epoch1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks / 10000;
//
// {
// var now = TimeHelper.Transition(new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc));
// var epochThisYear = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks / 10000 - epoch1970;
// var time = (uint)((now - epochThisYear) / 1000);
// Log.Debug($"time = {time} now = {now} epochThisYear = {epochThisYear}");
// }
//
// {
// var now = TimeHelper.Transition(new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc));
// var epochThisYear = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks / 10000 - epoch1970;
// var time = (uint)((now - epochThisYear) / 1000);
// Log.Debug($"time = {time} now = {now} epochThisYear = {epochThisYear}");
// }
var scene = self.Scene;
switch (scene.SceneType)
{
case 6666:
{
var subSceneTestComponent = scene.AddComponent<SubSceneTestComponent>();
Log.Debug("增加了SubSceneTestComponent");
scene.EntityComponent.CustomSystem(subSceneTestComponent,CustomSystemType.RunSystem);
break;
}
case SceneType.Addressable:
{
// scene.AddComponent<AddressableManageComponent>();
_addressableSceneRunTimeId = scene.RuntimeId;
break;
}
case SceneType.Map:
{
Log.Debug($"Map Scene SceneRuntimeId:{scene.RuntimeId}");
break;
}
case SceneType.Chat:
{
break;
}
case SceneType.Gate:
{
// var tasks = new List<FTask>(2000);
// var session = scene.GetSession(_addressableSceneRunTimeId);
// var sceneNetworkMessagingComponent = scene.NetworkMessagingComponent;
// var g2ATestRequest = new G2A_TestRequest();
//
// async FTask Call()
// {
// await sceneNetworkMessagingComponent.CallInnerRouteBySession(session,_addressableSceneRunTimeId,g2ATestRequest);
// }
//
// for (int i = 0; i < 100000000000; i++)
// {
// tasks.Clear();
// for (int j = 0; j < tasks.Capacity; ++j)
// {
// tasks.Add(Call());
// }
// await FTask.WaitAll(tasks);
// }
// 执行自定义系统
var testCustomSystemComponent = scene.AddComponent<TestCustomSystemComponent>();
scene.EntityComponent.CustomSystem(testCustomSystemComponent, CustomSystemType.RunSystem);
// // 测试配置表
// var instanceList = UnitConfigData.Instance.List;
// var unitConfig = instanceList[0];
// Log.Debug(instanceList[0].Dic[1]);
break;
}
}
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,29 @@
using System;
using Fantasy.Async;
using Fantasy.Network;
using Fantasy.Network.Interface;
using Fantasy.Network.Route;
using Fantasy.Platform.Net;
namespace Fantasy;
public sealed class C2G_CreateAddressableRequestHandler : MessageRPC<C2G_CreateAddressableRequest, G2C_CreateAddressableResponse>
{
protected override async FTask Run(Session session, C2G_CreateAddressableRequest request, G2C_CreateAddressableResponse response, Action reply)
{
var scene = session.Scene;
// 1、首先要通过SceneConfig配置文件拿到进行注册Addressable协议的服务器
// 实际开发的时候可能会根据一些规则来选择不同的Map服务器。
// 演示的例子里只有一个MapScene所以我就拿第一个Map服务器进行通讯了。
// 我这里仅是演示功能不是一定要这样拿Map
var sceneConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Map)[0];
// 2、使用Scene.NetworkMessagingComponent.CallInnerRoute方法跟Gate服务器进行通讯。
// CallInnerRoute方法跟Gate服务器进行通讯需要提供一个runTimeId这个Id在sceneConfig.RouteId可以获取到。
// 第二个参数是需要发送网络协议这个协议在Fantasy/Examples/Config/ProtoBuf里的InnerBson或Inner文件定义。
var responseAddressableId = (M2G_ResponseAddressableId)await scene.NetworkMessagingComponent.CallInnerRoute(sceneConfig.RouteId, new G2M_RequestAddressableId());
// 3、给session添加一个AddressableRouteComponent组件这个组件很重要、能否转发Addressable协议主要是通过这个。
var addressableRouteComponent = session.AddComponent<AddressableRouteComponent>();
// 4、拿到MapScene返回的AddressableId赋值给addressableRouteComponent.AddressableId。
addressableRouteComponent.AddressableId = responseAddressableId.AddressableId;
}
}

View File

@@ -0,0 +1,28 @@
using Fantasy.Async;
using Fantasy.Network;
using Fantasy.Network.Interface;
using Fantasy.Network.Route;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
namespace Fantasy;
public sealed class C2G_SendAddressableToMapHandler : Message<C2G_SendAddressableToMap>
{
protected override async FTask Run(Session session, C2G_SendAddressableToMap message)
{
var addressableRouteComponent = session.GetComponent<AddressableRouteComponent>();
if (addressableRouteComponent == null)
{
return;
}
// Gate发送一个Addressable消息给MAP
await session.Scene.NetworkMessagingComponent.SendAddressable(addressableRouteComponent.AddressableId,
new G2M_SendAddressableMessage()
{
Tag = message.Tag
});
}
}

View File

@@ -0,0 +1,40 @@
using System;
using Fantasy.Async;
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
using Fantasy.Network.Interface;
using Fantasy.Network.Route;
using Fantasy.Platform.Net;
using Fantasy.Serialize;
namespace Fantasy;
public class C2M_MoveToMapRequestHandler : AddressableRPC<Unit, C2M_MoveToMapRequest, M2C_MoveToMapResponse>
{
protected override async FTask Run(Unit unit, C2M_MoveToMapRequest request, M2C_MoveToMapResponse response, Action reply)
{
// 1、首先要通过SceneConfig配置文件拿到MapScene的配置文件。
// 这里Map[1]就是要发送的服务器,因为Map[0]是当前的Scene。
var scene = unit.Scene;
var mapSceneConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Map)[1];
// 2、锁定Addressable防止在转移期间有消息发送过来。
// LockAndRelease方法是先锁定Addressable消息后再销毁这个组件。
// 注意:只有这个方法的销毁组件不会去Addressable里删除自己的位置信息。
// 其他的任何销毁这个AddressableMessageComponent方法都会去Addressable里删除自己的位置信息。
await unit.GetComponent<AddressableMessageComponent>().LockAndRelease();
// 3、通过NetworkMessagingComponent发送内部消息给Map的Scene。
var sendResponse = await scene.NetworkMessagingComponent.CallInnerRoute(mapSceneConfig.RouteId,
new M2M_SendUnitRequest()
{
Unit = unit
});
if (sendResponse.ErrorCode != 0)
{
Log.Error($"转移Unit到目标Map服务器失败 ErrorCode={sendResponse.ErrorCode}");
return;
}
// 这个Unit已经转移到另外的Map服务器了所以就不需要这个组件了。
unit.Dispose();
Log.Debug("转移Unit到目标Map服务器成功");
}
}

View File

@@ -0,0 +1,13 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace Fantasy;
public sealed class C2M_TestMessageHandler : Addressable<Unit, C2M_TestMessage>
{
protected override async FTask Run(Unit unit, C2M_TestMessage message)
{
Log.Debug($"C2M_TestMessageHandler = {message.Tag} Scene:{unit.Scene.Scene.SceneConfigId}");
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,15 @@
using System;
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace Fantasy;
public sealed class C2M_TestRequestHandler : AddressableRPC<Unit, C2M_TestRequest, M2C_TestResponse>
{
protected override async FTask Run(Unit unit, C2M_TestRequest request, M2C_TestResponse response, Action reply)
{
Log.Debug($"Receive C2M_TestRequest Tag = {request.Tag}");
response.Tag = "Hello M2C_TestResponse";
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,13 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace Fantasy;
public sealed class G2M_SendAddressableMessageHandler : Addressable<Unit, G2M_SendAddressableMessage>
{
protected override async FTask Run(Unit unit, G2M_SendAddressableMessage message)
{
Log.Debug($"收到Gate发送来的Addressable消息 message:{message.Tag}");
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,21 @@
using System;
using Fantasy.Async;
using Fantasy.Network.Interface;
using Fantasy.Network.Route;
namespace Fantasy;
public class M2M_SendUnitRequestHandler : RouteRPC<Scene, M2M_SendUnitRequest, M2M_SendUnitResponse>
{
protected override async FTask Run(Scene scene, M2M_SendUnitRequest request, M2M_SendUnitResponse response, Action reply)
{
var requestUnit = request.Unit;
// 反序列化Unit把Unit注册到框架中
requestUnit.Deserialize(scene);
// 解锁这个Unit的Addressable消息解锁后Gate上缓存的消息会发送到这里。
// 由于AddressableMessageComponent不支持存数据库所以在发送Unit的时候会自动把这个给忽略掉。
// 所以需要再次手动的添加下才可以。
await requestUnit.AddComponent<AddressableMessageComponent>().UnLock("M2M_SendUnitRequestHandler");
Log.Debug($"传送完成 {scene.SceneConfigId}");
}
}

View File

@@ -0,0 +1,16 @@
using Fantasy.Async;
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
using Fantasy.Network;
using Fantasy.Network.Interface;
namespace Fantasy.Gate;
public sealed class C2G_TestMessageHandler : Message<C2G_TestMessage>
{
protected override async FTask Run(Session session, C2G_TestMessage message)
{
Log.Debug($"Receive C2G_TestMessage Tag={message.Tag}");
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,16 @@
using System;
using Fantasy.Async;
using Fantasy.Network;
using Fantasy.Network.Interface;
namespace Fantasy.Gate;
public sealed class C2G_TestRequestHandler : MessageRPC<C2G_TestRequest, G2C_TestResponse>
{
protected override async FTask Run(Session session, C2G_TestRequest request, G2C_TestResponse response, Action reply)
{
Log.Debug($"Receive C2G_TestRequest Tag = {request.Tag}");
response.Tag = "Hello G2C_TestResponse";
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,20 @@
using Fantasy.Async;
using Fantasy.Network;
using Fantasy.Network.Interface;
namespace Fantasy.Gate;
public sealed class C2G_TestRequestPushMessageHandler : Message<C2G_TestRequestPushMessage>
{
protected override async FTask Run(Session session, C2G_TestRequestPushMessage message)
{
// 因为没有服务器的相关的逻辑,所以制作了一个协议来触发服务器发送消息给客户端的环境。
// 使用当前会话的Session.Send发送消息给客户端。
// 如果需要群发你可以用一个容器保存起来发送的时候遍历这个容器调用Send方法就可以了。
session.Send(new G2C_PushMessage()
{
Tag = "Hi G2C_PushMessage"
});
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,15 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
using Fantasy.Network.Roaming;
using Fantasy.Roaming;
namespace Fantasy;
public class C2Chat_TestRPCRoamingRequestHandler : RoamingRPC<Terminus, C2Chat_TestRPCRoamingRequest, Chat2C_TestRPCRoamingResponse>
{
protected override async FTask Run(Terminus terminus, C2Chat_TestRPCRoamingRequest request, Chat2C_TestRPCRoamingResponse response, Action reply)
{
Log.Debug($"C2Chat_TestRPCRoamingRequestHandler message:{request.Tag} SceneType:{terminus.Scene.SceneType} SceneId:{terminus.Scene.RuntimeId}");
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,15 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
using Fantasy.Network.Roaming;
using Fantasy.Roaming;
namespace Fantasy;
public sealed class C2Chat_TestRoamingMessageHandler : Roaming<Terminus, C2Chat_TestRoamingMessage>
{
protected override async FTask Run(Terminus terminus, C2Chat_TestRoamingMessage message)
{
Log.Debug($"C2Chat_TestRoamingMessageHandler message:{message.Tag} SceneType:{terminus.Scene.SceneType} SceneId:{terminus.Scene.RuntimeId}");
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,18 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
using Fantasy.Network.Roaming;
using Fantasy.Roaming;
namespace Fantasy;
public sealed class C2Chat_TestSendMapMessageHandler : Roaming<Terminus, C2Chat_TestSendMapMessage>
{
protected override async FTask Run(Terminus terminus, C2Chat_TestSendMapMessage message)
{
terminus.Send(RoamingType.MapRoamingType, new Chat2M_TestMessage()
{
Tag = "Hi Inner Roaming Message!"
});
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,62 @@
using Fantasy.Async;
using Fantasy.Network;
using Fantasy.Network.Interface;
using Fantasy.Network.Roaming;
using Fantasy.Platform.Net;
using Fantasy.Roaming;
namespace Fantasy;
public sealed class C2G_ConnectRoamingRequestHandler : MessageRPC<C2G_ConnectRoamingRequest, G2C_ConnectRoamingResponse>
{
protected override async FTask Run(Session session, C2G_ConnectRoamingRequest request, G2C_ConnectRoamingResponse response, Action reply)
{
// 给session创建一个漫游功能。
// 这个功能很重要这个组件是整个Roaming系统最核心的组件这个组件会处理Roaming协议
// 这个功能会处理Roaming协议所以创建这个是必须的。
// CreateRoaming需要支持三个参数:
// roamingId:这个参数是RoamingIdRoamingId是Roaming的唯一标识不能重复。
// 指定了这个RoamingId后服务器其他漫游终端的Id会是你设置的RoamingId。
// 这样操作方便统一管理漫游协议。
// 一般这个RoamingId是一个角色的Id这样方便管理。
// isAutoDispose:是否在Session断开的时候自动断开漫游功能。
// delayRemove:如果开启了自定断开漫游功能需要设置一个延迟多久执行断开。
// 这里没有角色的Id所以这里使用1来代替。
// isAutoDispose我选择自动断开这个断开的时机是Session断开后执行。
// delayRemove断开漫游功能后Session会自动断开所以这里设置延迟1000毫秒执行断开。
// 这里创建的漫游功能会自动处理Roaming协议所以不需要手动处理Roaming协议。
var roaming = session.CreateRoaming(1,true,1000);
// 通过SceneConfigData.Instance.GetSceneBySceneType(SceneType.Map)[0]拿到Map场景的配置信息
// 如果需要协议漫游其他Scene可以在配置中查找要漫游的服务器。
// 可以同时漫游多个Scene但每个Scene的漫游都有一个固定的类型不能重复。
var mapConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Map)[0];
// 通过RoamingComponent.Link(session, mapConfig, 1, 1)链接Map场景
// 第一个参数是Session第二个参数是Map场景的配置信息第三个参数是Map场景的RouteId第四个参数是Map场景的RoamingType。
// 这个RoamingType是通过RoamingType.Config文件中定义的。
// RouteType.Config文件位置在你定义的网络文件协议文件夹下。如果找不到RoamingType.Config文件可以运行下导出协议工具导出一个协议后会自动创建。
// 该示例工程下文件位置在Config/NetworkProtocol/RoamingType.Config
// 执行完后漫游会自动把Session绑定到Map场景上。
// 后面发送该类型的消息到Session上会自动转发给Map场景。
var linkResponse = await roaming.Link(session, mapConfig, RoamingType.MapRoamingType);
if (linkResponse != 0)
{
response.ErrorCode = linkResponse;
return;
}
// 同样你可以创建多个漫游的场景但每个场景的RouteId和RoamingType不能重复。
// 这里创建Chat场景的漫游。
var chatConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Chat)[0];
linkResponse = await roaming.Link(session, chatConfig, RoamingType.ChatRoamingType);
if (linkResponse != 0)
{
response.ErrorCode = linkResponse;
return;
}
// 如果你觉的每次创建一个场景的漫游都麻烦你可以利用RoamingType.RoamingTypes遍历创建。
// 但这样的会把你在RoamingType.Config定义的都创建出来
foreach (var roamingType in RoamingType.RoamingTypes)
{
// 这里添加roaming.Link的方法进行创建。
}
}
}

View File

@@ -0,0 +1,17 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
using Fantasy.Network.Roaming;
namespace Fantasy;
public sealed class C2Map_PushMessageToClientHandler : Roaming<Terminus, C2Map_PushMessageToClient>
{
protected override async FTask Run(Terminus terminus, C2Map_PushMessageToClient message)
{
terminus.Send(new Map2C_PushMessageToClient()
{
Tag = message.Tag
});
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,15 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
using Fantasy.Network.Roaming;
using Fantasy.Roaming;
namespace Fantasy;
public class C2Map_TestRoamingMessageHandler : Roaming<Terminus, C2Map_TestRoamingMessage>
{
protected override async FTask Run(Terminus terminus, C2Map_TestRoamingMessage message)
{
Log.Debug($"C2Map_TestRoamingMessageHandler message:{message.Tag} SceneType:{terminus.Scene.SceneType} SceneId:{terminus.Scene.RuntimeId}");
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,17 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
using Fantasy.Network.Roaming;
using Fantasy.Platform.Net;
using Fantasy.Roaming;
namespace Fantasy;
public class C2Map_TestTransferRequestHandler : RoamingRPC<Terminus, C2Map_TestTransferRequest, Map2C_TestTransferResponse>
{
protected override async FTask Run(Terminus terminus, C2Map_TestTransferRequest request, Map2C_TestTransferResponse response, Action reply)
{
Log.Debug($"C2Map_TestTransferRequestHandler1 terminus:{terminus.RuntimeId}");
var mapConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Map)[1];
response.ErrorCode = await terminus.StartTransfer(mapConfig.RouteId);
}
}

View File

@@ -0,0 +1,15 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
using Fantasy.Network.Roaming;
using Fantasy.Roaming;
namespace Fantasy;
public sealed class Chat2M_TestMessageHandler : Roaming<Terminus, Chat2M_TestMessage>
{
protected override async FTask Run(Terminus terminus, Chat2M_TestMessage message)
{
Log.Debug($"Chat2M_TestMessageHandler message:{message.Tag} SceneType:{terminus.Scene.SceneType} SceneId:{terminus.Scene.RuntimeId}");
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,13 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace Fantasy;
public sealed class C2Chat_TestMessageHandler : Route<ChatUnit, C2Chat_TestMessage>
{
protected override async FTask Run(ChatUnit entity, C2Chat_TestMessage message)
{
Log.Debug($"C2Chat_TestMessageHandler.c2Chat_TestMessage: {message}");
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,15 @@
using System;
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace Fantasy;
public sealed class C2Chat_TestMessageRequestHandler : RouteRPC<ChatUnit, C2Chat_TestMessageRequest, Chat2C_TestMessageResponse>
{
protected override async FTask Run(ChatUnit entity, C2Chat_TestMessageRequest request, Chat2C_TestMessageResponse response, Action reply)
{
Log.Debug($"C2Chat_TestMessageRequestHandler request = {request}");
response.Tag = "Hello RouteRPC";
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,22 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace Fantasy;
public sealed class C2Chat_TestRequestPushMessageHandler : Route<ChatUnit, C2Chat_TestRequestPushMessage>
{
protected override async FTask Run(ChatUnit chatUnit, C2Chat_TestRequestPushMessage message)
{
// 因为没有服务器的相关的逻辑,所以制作了一个协议来触发服务器发送消息给客户端的环境。
// 使用当前Scene.NetworkMessagingComponent.SendInnerRoute发送消息给客户端。
// 只需要把消息发送给创建链接的Gate上就会自动转发消息到客户端上。
// 因为chatUnit.GateRouteId是在G2Chat_CreateRouteRequestHandler方法里记录的所以直接使用这个就可以了。
chatUnit.Scene.NetworkMessagingComponent.SendInnerRoute(chatUnit.GateRouteId, new Chat2C_PushMessage()
{
Tag = "Hi Route Chat2C_PushMessage"
});
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,46 @@
using System;
using Fantasy.Async;
using Fantasy.Network;
using Fantasy.Network.Interface;
using Fantasy.Platform.Net;
namespace Fantasy;
public sealed class C2G_CreateChatRouteRequestHandler : MessageRPC<C2G_CreateChatRouteRequest, G2C_CreateChatRouteResponse>
{
protected override async FTask Run(Session session, C2G_CreateChatRouteRequest request, G2C_CreateChatRouteResponse response, Action reply)
{
// 首先需要找到一个需要建立Route的Scene的SceneConfig。
// 例子演示的连接的ChatScene,所以这里我通过SceneConfigData拿到这个SceneConfig。
// 如果是其他Scene用法跟这个没有任何区别。
var chatSceneConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Chat)[0];
// 通过chatSceneConfig拿到这个Scene的RouteId
var chatRouteId = chatSceneConfig.RouteId;
// 通过Scene拿到当前Scene的NetworkMessagingComponent。
// NetworkMessagingComponent是服务器之间通讯的唯一手段。
var networkMessagingComponent = session.Scene.NetworkMessagingComponent;
// 通过CallInnerRoute方法发送一个RPC消息给ChatScene上。
// 任何一个实体的RunTimeId都可以做为RouteId使用。
// 所以这个传递了一个session.RunTimeId是方便Chat发送消息回Gate上。
var routeResponse = (Chat2G_CreateRouteResponse)await networkMessagingComponent.CallInnerRoute(chatRouteId,
new G2Chat_CreateRouteRequest()
{
GateRouteId = session.RouteId
});
if (routeResponse.ErrorCode != 0)
{
// 如果ErrorCode不是0表示请求的协议发生错误应该提示给客户端。
// 这里就不做这个了。
return;
}
// 要实现Route协议的转发需要给Session添加一个RouteComponent这个非常重要。
var routeComponent = session.AddComponent<RouteComponent>();
// 需要再Examples/Config/NetworkProtocol/RouteType.Config里添加一个ChatRoute
// 然后点击导表工具会自动生成一个RouteType.cs文件。
// 使用你定义的ChatRoute当routeType的参数传递进去。
// routeResponse会返回一个ChatRouteId这个就是Chat的RouteId。
routeComponent.AddAddress((int)RouteType.ChatRoute,routeResponse.ChatRouteId);
// 这些操作完成后就完成了Route消息的建立。
// 后面可以直接发送Route消息通过Gate自动中转给Chat了。
}
}

View File

@@ -0,0 +1,21 @@
using Fantasy.Async;
using Fantasy.Network;
using Fantasy.Network.Interface;
using Fantasy.Network.Route;
namespace Fantasy;
public sealed class C2G_CreateSubSceneAddressableRequestHandler : MessageRPC<C2G_CreateSubSceneAddressableRequest, G2C_CreateSubSceneAddressableResponse>
{
protected override async FTask Run(Session session, C2G_CreateSubSceneAddressableRequest request, G2C_CreateSubSceneAddressableResponse response, Action reply)
{
var scene = session.Scene;
var subSceneRouteId = session.GetComponent<GateSubSceneFlagComponent>().SubSceneRouteId;
// 1、向SubScene请求AddressableId
var responseAddressableId = (SubScene2G_AddressableIdResponse)await scene.NetworkMessagingComponent.CallInnerRoute(subSceneRouteId, new G2SubScene_AddressableIdRequest());
// 2、给session添加一个AddressableRouteComponent组件这个组件很重要、能否转发Addressable协议主要是通过这个。
var addressableRouteComponent = session.AddComponent<AddressableRouteComponent>();
// 3、拿到SubScene返回的AddressableId赋值给addressableRouteComponent.AddressableId。
addressableRouteComponent.AddressableId = responseAddressableId.AddressableId;
}
}

View File

@@ -0,0 +1,26 @@
using Fantasy.Async;
using Fantasy.Network;
using Fantasy.Network.Interface;
using Fantasy.Platform.Net;
namespace Fantasy;
public sealed class C2G_CreateSubSceneRequestHandler : MessageRPC<C2G_CreateSubSceneRequest, G2C_CreateSubSceneResponse>
{
protected override async FTask Run(Session session, C2G_CreateSubSceneRequest request, G2C_CreateSubSceneResponse response, Action reply)
{
var scene = session.Scene;
var sceneConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Map)[0];
var createSubSceneResponse = (M2G_CreateSubSceneResponse)await scene.NetworkMessagingComponent.CallInnerRoute(sceneConfig.RouteId, new G2M_CreateSubSceneRequest());
if (createSubSceneResponse.ErrorCode != 0)
{
// 创建SubScene失败。
response.ErrorCode = createSubSceneResponse.ErrorCode;
return;
}
// 记录下这个RouteId以便后续的消息转发。
session.AddComponent<GateSubSceneFlagComponent>().SubSceneRouteId = createSubSceneResponse.SubSceneRouteId;
}
}

View File

@@ -0,0 +1,18 @@
using Fantasy.Async;
using Fantasy.Network;
using Fantasy.Network.Interface;
namespace Fantasy;
public class C2G_SendToSubSceneMessageHandler : Message<C2G_SendToSubSceneMessage>
{
protected override async FTask Run(Session session, C2G_SendToSubSceneMessage message)
{
var subSceneRouteId = session.GetComponent<GateSubSceneFlagComponent>().SubSceneRouteId;
session.Scene.NetworkMessagingComponent.SendInnerRoute(subSceneRouteId, new G2SubScene_SentMessage()
{
Tag = "Hi SubScene",
});
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,16 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace Fantasy;
public class C2SubScene_TestDisposeMessageHandler : Addressable<Unit, C2SubScene_TestDisposeMessage>
{
protected override async FTask Run(Unit unit, C2SubScene_TestDisposeMessage message)
{
var unitScene = unit.Scene;
var unitSceneSceneType = unitScene.SceneType;
unitScene.Dispose();
Log.Debug($"{unitSceneSceneType} {unitScene.RuntimeId} is Dispose!");
await FTask.CompletedTask;
}
}

View File

@@ -0,0 +1,14 @@
using Fantasy.Async;
using Fantasy.Network.Interface;
namespace Fantasy;
public class C2SubScene_TestMessageHandler : Addressable<Unit, C2SubScene_TestMessage>
{
protected override async FTask Run(Unit unit, C2SubScene_TestMessage message)
{
Log.Debug($"C2M_TestMessageHandler = {message.Tag} SceneType:{unit.Scene.SceneType} {unit.Scene.GetComponent<SubSceneTestComponent>() == null}");
await FTask.CompletedTask;
}
}