Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .github/workflows/topology-equivalence.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Topology Equivalence Gate

# C143c D9:跨 Role 消息链路语义等价 CI 门禁。
# 4 条代表性链路 × 2 拓扑(All-in-One 单进程 / 三进程)在同一套固定预期下运行,
# 任一用例失败即阻塞合并。这是 DynamicPhase-0 核心契约(AC-2)的守护门。

on:
pull_request:
branches:
- main
workflow_dispatch:

jobs:
topology-equivalence:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3

- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 10.0.x

- name: Restore
run: dotnet restore Server.slnx

- name: Build
run: dotnet build Server.slnx --no-restore

- name: Run Topology Equivalence Suite
run: >
dotnet test Tests/GameFrameX.Tests/GameFrameX.Tests.csproj --no-build
--filter "FullyQualifiedName~GameFrameX.Tests.Topology.Equivalence"
--logger GitHubActions
10 changes: 10 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>

<!--
显式落默认值:不经解决方案直接对 csproj 求值(如 dotnet test 的 no-build 模式)时,
本文件先于 SDK 默认值求值,$(Platform) 为空会导致下方 OutputPath 条件组被跳过,
测试容器路径与实际构建输出(../bin/app_debug)不一致。
-->
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>$(MSBuildProjectDirectory)/../bin/app_debug</OutputPath>
</PropertyGroup>
Expand Down
3 changes: 2 additions & 1 deletion GameFrameX.NetWork.RemoteMessaging/GlobalUsings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@
global using GameFrameX.NetWork.RemoteMessaging.Resilience;
global using GameFrameX.NetWork.RemoteMessaging.Transport;
global using GameFrameX.NetWork.RemoteMessaging.Versioning;
global using GameFrameX.Utility;
global using GameFrameX.Utility;
global using GameFrameX.NetWork.RemoteMessaging.Routing;
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// ==========================================================================================
// GameFrameX 组织及其衍生项目的版权、商标、专利及其他相关权利
// GameFrameX organization and its derivative projects' copyrights, trademarks, patents, and related rights
// 均受中华人民共和国及相关国际法律法规保护。
// are protected by the laws of the People's Republic of China and related international regulations.
// 使用本项目须严格遵守相应法律法规与开源许可证之规定。
// Usage of this project must strictly comply with applicable laws, regulations, and open-source licenses.
// 本项目采用 Apache License 2.0 单协议分发,
// This project is licensed solely under the Apache License 2.0,
// 完整许可证文本请参见源代码根目录下的 LICENSE 文件。
// please see the LICENSE file in the root directory of the source code for the full license text.
// 禁止利用本项目实施任何危害国家安全、破坏社会秩序、
// It is prohibited to use this project to engage in any activities that endanger national security, disrupt social order,
// 侵犯他人合法权益等法律法规所禁止的行为!
// or infringe upon the legitimate rights and interests of others as prohibited by laws and regulations!
// 因基于本项目二次开发所产生的一切法律纠纷与责任,
// Any legal disputes or liabilities arising from secondary development based on this project
// 本组织与贡献者概不承担。
// shall be borne solely by the developer; the project organization and contributors assume no responsibility.
// GitHub 仓库:https://github.com/GameFrameX
// GitHub Repository: https://github.com/GameFrameX
// Gitee 仓库:https://gitee.com/GameFrameX
// Gitee Repository: https://gitee.com/GameFrameX
// CNB 仓库:https://cnb.cool/GameFrameX
// CNB Repository: https://cnb.cool/GameFrameX
// 官方文档:https://gameframex.doc.alianblank.com/
// Official Documentation: https://gameframex.doc.alianblank.com/
// ==========================================================================================


namespace GameFrameX.NetWork.RemoteMessaging.Routing;

/// <summary>
/// 本地 Role 消息投递缝(C143c D3 case 1)。
/// </summary>
/// <remarks>
/// The local in-process delivery seam behind D3 case 1.
/// Production delivery goes through the actor pipeline (Actor.Tell/SendAsync semantics);
/// this interface is the explicit seam so the routing decision itself stays free of
/// actor-world dependencies (GameFrameX.Core is outside this assembly's reference closure).
/// The production actor-backed dispatcher arrives with C143e; until then the router is
/// wired without one and a case 1 hit fails loudly with <see cref="RouteNotFoundException"/>.
/// </remarks>
public interface ILocalRoleMessageDispatcher
{
/// <summary>
/// 将信封投递给本进程目标 Role 的消息处理队列。
/// </summary>
/// <remarks>
/// Delivers the envelope to the target role's in-process message handling queue.
/// The returned task completes when the message has been handed to (or processed by)
/// the target queue, preserving per-role FIFO ordering for messages routed in order.
/// </remarks>
/// <param name="envelope">路由信封(目标 Role 必属于本进程角色集)/ The routing envelope (target role is hosted by this process)</param>
/// <param name="cancellationToken">取消操作的令牌 / The cancellation token</param>
Task DispatchAsync(MessageEnvelope envelope, CancellationToken cancellationToken = default);
}
58 changes: 58 additions & 0 deletions GameFrameX.NetWork.RemoteMessaging/Routing/IRemoteRoleRouter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// ==========================================================================================
// GameFrameX 组织及其衍生项目的版权、商标、专利及其他相关权利
// GameFrameX organization and its derivative projects' copyrights, trademarks, patents, and related rights
// 均受中华人民共和国及相关国际法律法规保护。
// are protected by the laws of the People's Republic of China and related international regulations.
// 使用本项目须严格遵守相应法律法规与开源许可证之规定。
// Usage of this project must strictly comply with applicable laws, regulations, and open-source licenses.
// 本项目采用 Apache License 2.0 单协议分发,
// This project is licensed solely under the Apache License 2.0,
// 完整许可证文本请参见源代码根目录下的 LICENSE 文件。
// please see the LICENSE file in the root directory of the source code for the full license text.
// 禁止利用本项目实施任何危害国家安全、破坏社会秩序、
// It is prohibited to use this project to engage in any activities that endanger national security, disrupt social order,
// 侵犯他人合法权益等法律法规所禁止的行为!
// or infringe upon the legitimate rights and interests of others as prohibited by laws and regulations!
// 因基于本项目二次开发所产生的一切法律纠纷与责任,
// Any legal disputes or liabilities arising from secondary development based on this project
// 本组织与贡献者概不承担。
// shall be borne solely by the developer; the project organization and contributors assume no responsibility.
// GitHub 仓库:https://github.com/GameFrameX
// GitHub Repository: https://github.com/GameFrameX
// Gitee 仓库:https://gitee.com/GameFrameX
// Gitee Repository: https://gitee.com/GameFrameX
// CNB 仓库:https://cnb.cool/GameFrameX
// CNB Repository: https://cnb.cool/GameFrameX
// 官方文档:https://gameframex.doc.alianblank.com/
// Official Documentation: https://gameframex.doc.alianblank.com/
// ==========================================================================================


namespace GameFrameX.NetWork.RemoteMessaging.Routing;

/// <summary>
/// 跨进程 Role 消息转发缝(C143c D3 case 2/3)。
/// </summary>
/// <remarks>
/// The remote forwarding seam behind D3 case 2/3.
/// Case 2 forwards to a known target instance id; case 3 picks any active instance of
/// the target role from the reachability table. The real implementation (endpoint
/// reachability table + ForwardToRemoteServerAsync) is delivered by C143d; until then
/// <see cref="RemoteRoleRouter"/> is the placeholder and throws
/// <see cref="NotImplementedException"/>. Topology equivalence tests substitute a
/// loopback forwarder to simulate the remote hop inside a single test process.
/// </remarks>
public interface IRemoteRoleRouter
{
/// <summary>
/// 将信封转发给目标 Role 所在的远端进程。
/// </summary>
/// <remarks>
/// Forwards the envelope to the remote process hosting the target role
/// (a known instance for D3 case 2, or any active instance for case 3).
/// </remarks>
/// <param name="envelope">路由信封(目标 Role 不属于本进程角色集)/ The routing envelope (target role is hosted by another process)</param>
/// <param name="cancellationToken">取消操作的令牌 / The cancellation token</param>
/// <returns>投递分支恒为 <see cref="RoleRouteDelivery.RemoteForwarded"/> / Always returns <see cref="RoleRouteDelivery.RemoteForwarded"/></returns>
Task<RoleRouteDelivery> ForwardAsync(MessageEnvelope envelope, CancellationToken cancellationToken = default);
}
57 changes: 57 additions & 0 deletions GameFrameX.NetWork.RemoteMessaging/Routing/IRoleRouter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// ==========================================================================================
// GameFrameX 组织及其衍生项目的版权、商标、专利及其他相关权利
// GameFrameX organization and its derivative projects' copyrights, trademarks, patents, and related rights
// 均受中华人民共和国及相关国际法律法规保护。
// are protected by the laws of the People's Republic of China and related international regulations.
// 使用本项目须严格遵守相应法律法规与开源许可证之规定。
// Usage of this project must strictly comply with applicable laws, regulations, and open-source licenses.
// 本项目采用 Apache License 2.0 单协议分发,
// This project is licensed solely under the Apache License 2.0,
// 完整许可证文本请参见源代码根目录下的 LICENSE 文件。
// please see the LICENSE file in the root directory of the source code for the full license text.
// 禁止利用本项目实施任何危害国家安全、破坏社会秩序、
// It is prohibited to use this project to engage in any activities that endanger national security, disrupt social order,
// 侵犯他人合法权益等法律法规所禁止的行为!
// or infringe upon the legitimate rights and interests of others as prohibited by laws and regulations!
// 因基于本项目二次开发所产生的一切法律纠纷与责任,
// Any legal disputes or liabilities arising from secondary development based on this project
// 本组织与贡献者概不承担。
// shall be borne solely by the developer; the project organization and contributors assume no responsibility.
// GitHub 仓库:https://github.com/GameFrameX
// GitHub Repository: https://github.com/GameFrameX
// Gitee 仓库:https://gitee.com/GameFrameX
// Gitee Repository: https://gitee.com/GameFrameX
// CNB 仓库:https://cnb.cool/GameFrameX
// CNB Repository: https://cnb.cool/GameFrameX
// 官方文档:https://gameframex.doc.alianblank.com/
// Official Documentation: https://gameframex.doc.alianblank.com/
// ==========================================================================================


namespace GameFrameX.NetWork.RemoteMessaging.Routing;

/// <summary>
/// 跨 Role 消息路由缝(C143c D3)。
/// </summary>
/// <remarks>
/// The cross-role message routing seam (C143c D3).
/// Implementations apply the three-step decision to every envelope:
/// target role hosted by this process goes to local in-process delivery (case 1);
/// everything else is handed to the remote forwarding seam (case 2/3, delivered by C143d).
/// Business code reaches the process-wide instance through <see cref="RoleRouterHolder"/>.
/// </remarks>
public interface IRoleRouter
{
/// <summary>
/// 路由一封跨 Role 消息信封。
/// </summary>
/// <remarks>
/// Routes one cross-role message envelope.
/// </remarks>
/// <param name="envelope">路由信封 / The routing envelope</param>
/// <param name="cancellationToken">取消操作的令牌 / The cancellation token</param>
/// <returns>实际命中的投递分支 / The delivery branch that was actually hit</returns>
/// <exception cref="ArgumentNullException">当 <paramref name="envelope"/> 为 null 时抛出 / Thrown when <paramref name="envelope"/> is null</exception>
/// <exception cref="RouteNotFoundException">当路由决策失败(空目标 Role / 缺本地投递器 / 缺远程转发器)时抛出 / Thrown when routing cannot decide a route</exception>
Task<RoleRouteDelivery> RouteAsync(MessageEnvelope envelope, CancellationToken cancellationToken = default);
}
Loading
Loading