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
16 changes: 15 additions & 1 deletion .github/workflows/topology-equivalence.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ name: Topology Equivalence Gate
# C143c D9:跨 Role 消息链路语义等价 CI 门禁。
# 4 条代表性链路 × 2 拓扑(All-in-One 单进程 / 三进程)在同一套固定预期下运行,
# 任一用例失败即阻塞合并。这是 DynamicPhase-0 核心契约(AC-2)的守护门。
# C143d:套件扩入 Discovery(地址格式 / 双视图路由 / Mongo 心跳集成用例),
# sidecar Mongo 让 GAMEFRAMEX_TEST_MONGODB_CONNECTION_STRING 门控的集成用例真跑。

on:
pull_request:
Expand All @@ -13,6 +15,18 @@ on:
jobs:
topology-equivalence:
runs-on: ubuntu-latest
services:
mongo:
image: mongo:7
ports:
- 27017:27017
options: >-
--health-cmd "mongosh --quiet --eval \"db.adminCommand('ping')\""
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
GAMEFRAMEX_TEST_MONGODB_CONNECTION_STRING: mongodb://localhost:27017
steps:
- name: Checkout Repository
uses: actions/checkout@v3
Expand All @@ -31,5 +45,5 @@ jobs:
- name: Run Topology Equivalence Suite
run: >
dotnet test Tests/GameFrameX.Tests/GameFrameX.Tests.csproj --no-build
--filter "FullyQualifiedName~GameFrameX.Tests.Topology.Equivalence"
--filter "FullyQualifiedName~GameFrameX.Tests.Topology.Equivalence|FullyQualifiedName~GameFrameX.Tests.Discovery"
--logger GitHubActions
8 changes: 8 additions & 0 deletions GameFrameX.Launcher/StartUp/AppStartUpGame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

using GameFrameX.DataBase;
using GameFrameX.DataBase.Abstractions;
using GameFrameX.NetWork.RemoteMessaging.Discovery;
using GameFrameX.Foundation.Utility;
using GameFrameX.Foundation.Localization.Core;
using GameFrameX.Online.Runtime;
Expand Down Expand Up @@ -82,6 +83,10 @@ public override async Task StartAsync()
}
}

// C143d D11-D15:控制库就绪后激活 Mongo 发现层——读侧 watcher + 写侧心跳(未配置广播端口时自动跳过)
// 并以真实 case 2/3 转发器重装跨 Role 路由缝(替换 C143c 占位)。幂等:多 Role 进程首个调用生效。
MongoDiscoveryRuntime.Activate(((MongoDbService)MultiDbRegistry.Get(MultiDbRegistry.ControlDatabaseName)).CurrentDatabase, RoleSet.Current);

var initResult = await GameDb.Init<MongoDbService>(Setting.DataBaseUrl, new DbOptions { Name = Setting.DataBaseName, IsUseTimeZone = Setting.IsUseTimeZone, });
if (initResult == false)
{
Expand Down Expand Up @@ -120,6 +125,9 @@ public override async Task StartAsync()
LogHelper.Info(LocalizationService.GetString(Localization.Keys.Launcher.ServerStartEnd, Setting.ServerType));
// C143b D7:启动阶段完成(DB/组件/热-fix/在线管理均已就绪),放行下一个 Role 的启动屏障
MarkStartUpReady();
// C143d D15:启动阶段真正完成(DB/组件/热-fix/在线管理均已就绪)后才把心跳从 Booting 切到 Active,
// 避免其他进程在服务就绪前发现本实例并投递流量;未激活发现层或无广播身份时为无害 no-op。
MongoDiscoveryRuntime.MarkActive();
exitMessage = await AppExitToken;
}
catch (Exception e)
Expand Down
8 changes: 8 additions & 0 deletions GameFrameX.Launcher/StartUp/Social/AppStartUpSocial.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
using GameFrameX.Core.Components;
using GameFrameX.DataBase;
using GameFrameX.DataBase.Abstractions;
using GameFrameX.NetWork.RemoteMessaging.Discovery;
using GameFrameX.NetWork.Abstractions;
using GameFrameX.NetWork.HTTP;
using GameFrameX.NetWork.Message;
Expand Down Expand Up @@ -68,6 +69,10 @@ public override async Task StartAsync()
}
}

// C143d D11-D15:控制库就绪后激活 Mongo 发现层——读侧 watcher + 写侧心跳(未配置广播端口时自动跳过)
// 并以真实 case 2/3 转发器重装跨 Role 路由缝(替换 C143c 占位)。幂等:多 Role 进程首个调用生效。
MongoDiscoveryRuntime.Activate(((MongoDbService)MultiDbRegistry.Get(MultiDbRegistry.ControlDatabaseName)).CurrentDatabase, RoleSet.Current);

var initResult = await GameDb.Init<MongoDbService>(Setting.DataBaseUrl, new DbOptions { Name = Setting.DataBaseName, IsUseTimeZone = Setting.IsUseTimeZone, });
if (initResult == false)
{
Expand All @@ -81,6 +86,9 @@ public override async Task StartAsync()

// C143b D7:启动阶段完成(DB/组件/网络监听均已就绪),放行下一个 Role 的启动屏障
MarkStartUpReady();
// C143d D15:启动阶段真正完成(DB/组件/网络监听均已就绪)后才把心跳从 Booting 切到 Active,
// 避免其他进程在 Social TCP listener 就绪前发现本实例并投递流量。
MongoDiscoveryRuntime.MarkActive();

await AppExitToken;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// ==========================================================================================
// 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 relevant 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 refer to 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 and 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.Discovery;

/// <summary>
/// 端点主机形态(C143d D15 addressKind 字段)。
/// </summary>
/// <remarks>
/// The host address kind carried by heartbeat documents (C143d D15 addressKind field).
/// The DNS name is the first-class default per D15: advertised addresses prefer
/// stable DNS names (container name / Kubernetes Service name / domain) over
/// ephemeral IP literals so restarts keep a routable identity.
/// </remarks>
public enum EndpointAddressKind
{
/// <summary>
/// 域名/容器名/Kubernetes Service 名(一等缺省形态)。
/// </summary>
/// <remarks>
/// Domain, container name, or Kubernetes Service name (the first-class default).
/// </remarks>
DnsName = 1,

/// <summary>
/// IPv4 字面量。
/// </summary>
/// <remarks>
/// An IPv4 literal.
/// </remarks>
IPv4 = 2,

/// <summary>
/// IPv6 字面量(方括号形式)。
/// </summary>
/// <remarks>
/// An IPv6 literal (bracketed form).
/// </remarks>
IPv6 = 3,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// ==========================================================================================
// 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 relevant 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 refer to 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 and 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.Discovery;

/// <summary>
/// 端点地址格式异常(C143d D15)。
/// </summary>
/// <remarks>
/// Thrown by <see cref="EndpointParser"/> when an endpoint string violates the
/// unified <c>scheme://host:port</c> format: missing scheme, missing or out-of-range
/// port, or an unparsable host. Failing loudly at parse time (instead of at connect
/// time with an opaque socket error) is the AC-4a contract: every endpoint shape is
/// validated by the same single parser.
/// </remarks>
public sealed class EndpointFormatException : FormatException
{
/// <summary>
/// 初始化端点格式异常。
/// </summary>
/// <remarks>
/// Initializes the exception with a descriptive message.
/// </remarks>
/// <param name="message">描述违规原因的消息 / The message describing the violation</param>
public EndpointFormatException(string message)
: base(message)
{
}
}
Loading
Loading