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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@

### Breaking

* 统一消息发送器本服身份改为构造注入(C286,GFX-988):`UnifiedMessageSender` 构造器新增必填 `localServerType`(空白即抛 `ArgumentException`)/ `localServerId`(置于 `instanceSelector` 之后、可选 `metrics` 之前),`UnifiedMessageSenderHolder.InitializeWithDefaults` 签名同步并透传;类内删除构造期读取 `GlobalSettings.CurrentSetting` 与 `GameServerConst.Game` 常量回落——发送端本服判定(`SendToPlayerAsync` Step 3)与指标服务名兜底不再依赖「桥接构造前把 CurrentSetting 钉回首启动 Role」的隐式时序,`--AllInOne` 多 Role 进程下身份取自热更桥接 `OnLoadSuccess` 收到的首个装载 Role 的 `ServerType` / `ServerId`,现网所有流程行为等价。对外破坏性:公开构造器与 `InitializeWithDefaults` 签名变化(RemoteMessaging 为可打包程序集),旧签名不再编译;仓内调用方(热更桥接装配点 + 测试构造)已同批迁移。


* Online 叶子域命名空间迁移映射(C231 P3 拆包;无 NuGet 包消费者,破坏面纯源码级,仓内消费者 = 单体剩余 9 目录 / Runtime 壳 / Tests,已全量随迁):`GameFrameX.Online.{Assets,Social,Matchmaking,Party} → GameFrameX.Capability.Online.*`(143 域文件,分归 Assets 34 / Social 67 / Matchmaking 29 / Party 13,另 handler 与助手文件随包);`GameFrameX.Online.Runtime.AdminApi.{OnlineAdminApiContract,OnlineAdminApiAdapters} → GameFrameX.Capability.Online.Contracts`;`GameFrameX.Online.Audit.{OnlineAuditQuery,OnlineAuditRecord,OnlineAuditPage,OnlineAuditDomain} → GameFrameX.Capability.Online.Contracts`;`OnlineRuntimeHost` 叶子域 typed 属性(AssetStore/AssetTransactionStore/AssetQuery/GrantService/ChatStore/FriendStore/GroupStore/NotificationStore/ReportStore/SocialGraphStore/MatchListingStore/MatchTicketStore/PartyStore/SocialDecisions/Punishments/Notifications/MatchTickets/Matchmaker/MatchQueueObserver/ChatAudit)退役——产物经能力模块自装配与 `CapabilityHost.Services` 取用(P4 域属性保留:Timeline/Overview/Seasons/Tournaments 在叶子能力被禁用配置下可为 null,其 handler/job 随模块禁用不可达)
* P4 上层域命名空间与组合根类型迁移映射(C232 P4;无 NuGet 包消费者,破坏面纯源码级,仓内消费者 = Launcher / Tests,已全量随迁):`GameFrameX.Online.{Match,Leaderboard,Season,Tournament,Audit,HotfixRollback,GameEvents,Timeline,Overview} → GameFrameX.Capability.Online.{Match,Competition.Leaderboard,Competition.Season,Competition.Tournament,Operations.Audit,Operations.HotfixRollback,Operations.GameEvents,Operations.Overview,Operations.Timeline}`;`GameFrameX.Online.Runtime`(LiveOps 域类型)`→ GameFrameX.Capability.Online.Competition.LiveOps`;`GameFrameX.Online.Runtime` 命名空间下的组合根类型(OnlineCapabilityCompositionOptions / OnlineCapabilityComposition / OnlineAdminApiComposition)`→ GameFrameX.Launcher.Online`;`OnlineRuntimeHost` 退役——`OnlineCapabilityComposition.Runtime` 改为机制公开面 `CapabilityHost`(StartAsync/StopAsync 同签名同幂等语义,AppStartUpGame 调用形态零变化)
Expand Down
13 changes: 9 additions & 4 deletions GameFrameX.Hotfix/Lifecycle/HotfixModuleLifecycleBridge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,17 @@ public async Task<bool> OnLoadSuccess(AppSetting setting, bool reload)
// 多 Role 进程(--AllInOne)中钉住 CurrentSetting(C285 自 AppStartUpBase.Init 显式化):
// Launcher 的创建循环先对全部 Role 逐个 Init,SetCurrentSetting 对 Role 级字段接受最新值,
// 循环结束时 CurrentSetting 指向最后创建的 Role;热更首次装载由首个启动的 Role 触发,
// 这里把 CurrentSetting 钉回该首启动 Role,且必须早于 UnifiedMessageSender 构造——
// 其构造器读取 CurrentSetting 固化本服身份(另有路由/会话等调用期读取方)。
// 单 Role 形态无差异;直接删除会让上述消费方的服务器身份漂移。
// 这里把 CurrentSetting 钉回该首启动 Role,供路由/会话等调用期读取方取到稳定身份
// (发送端身份自 C286 起改为构造注入,不再依赖本钉住的时序与值)。
// 单 Role 形态无差异;直接删除会让调用期读取方的服务器身份漂移。

GlobalSettings.SetCurrentSetting(setting);
// 初始化统一消息发送器:resolver 按发现层可用性选择——
// 启动流程先 Activate 发现层(含 player_route 读侧),后加载 Hotfix,故此处
// Bootstrap.Resolver 必然已就绪;未激活发现层的形态回落 DefaultPlayerRouteResolver。
// localSender 单实例同时供 UnifiedMessageSenderHolder 与 LocalEnvelopeDispatcher,保证一条本地复投语义。
// 发送端本服身份(C286)构造注入自本回调收到的 setting,不依赖 CurrentSetting 的钉住时序
// (钉住仍服务 PlayerSessionManager 等调用期读取方)。
if (!UnifiedMessageSenderHolder.IsInitialized)
{
var remoteClient = RemoteMessagingBuilder.BuildFromEnvironment();
Expand All @@ -105,7 +108,9 @@ public async Task<bool> OnLoadSuccess(AppSetting setting, bool reload)
UnifiedMessageSenderHolder.InitializeWithDefaults(
routeResolver,
localSender,
remoteClient);
remoteClient,
setting.ServerType,
setting.ServerId);

// holder 初始化后补装路由缝 case 1 的本地投递槽(幂等;时序前提见 AttachLocalDispatcher 注释)。
// 本地投递槽装配统一走组合侧 DiscoveryRoutingWire(两 Provider 共用,不再按实现分支)。
Expand Down
29 changes: 24 additions & 5 deletions GameFrameX.Network.RemoteMessaging/Unified/UnifiedMessageSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
using System.Collections.Concurrent;
using GameFrameX.Network.Abstractions;
using GameFrameX.Network.RemoteMessaging.Contracts;
using GameFrameX.Utility.Setting;

namespace GameFrameX.Network.RemoteMessaging.Unified;

Expand All @@ -58,34 +57,44 @@
private readonly int _currentServerId;

/// <summary>
/// 初始化统一消息发送器。
/// 初始化统一消息发送器。本服身份(本服判定与指标服务名兜底的基准)由调用方在构造时显式注入,
/// 不读取全局配置——多 Role 进程中应传入触发装配的 Role 的 <c>ServerType</c> / <c>ServerId</c>。
/// </summary>
/// <remarks>
/// Initializes the unified message sender.
/// Initializes the unified message sender. The local-server identity (the basis for the
/// local-server check and the metrics service-name fallback) is explicitly injected by the caller
/// at construction time instead of reading global settings — in a multi-role process, pass the
/// <c>ServerType</c> / <c>ServerId</c> of the role that triggers the wiring.
/// </remarks>
/// <param name="remoteClient">远程消息客户端 / Remote message client</param>
/// <param name="routeResolver">玩家路由解析器 / Player route resolver</param>
/// <param name="localSender">本服玩家发送器 / Local player sender</param>
/// <param name="instanceSelector">服务实例选择器 / Server instance selector</param>
/// <param name="localServerType">本服服务器类型(本服判定基准,忽略大小写) / Local server type (basis of the local-server check, case-insensitive)</param>
/// <param name="localServerId">本服服务器编号(本服判定基准) / Local server id (basis of the local-server check)</param>
/// <param name="metrics">发送指标聚合器;为空时自动创建 / Metrics recorder; auto-created when null</param>
/// <exception cref="ArgumentNullException">当 <paramref name="remoteClient"/> 为 null 时抛出 / Thrown when <paramref name="remoteClient"/> is null</exception>
/// <exception cref="ArgumentNullException">当 <paramref name="routeResolver"/> 为 null 时抛出 / Thrown when <paramref name="routeResolver"/> is null</exception>
/// <exception cref="ArgumentNullException">当 <paramref name="localSender"/> 为 null 时抛出 / Thrown when <paramref name="localSender"/> is null</exception>
/// <exception cref="ArgumentNullException">当 <paramref name="instanceSelector"/> 为 null 时抛出 / Thrown when <paramref name="instanceSelector"/> is null</exception>
/// <exception cref="ArgumentException">当 <paramref name="localServerType"/> 为 null、空串或空白时抛出 / Thrown when <paramref name="localServerType"/> is null, empty, or whitespace</exception>
public UnifiedMessageSender(
IRemoteMessageClient remoteClient,
IPlayerRouteResolver routeResolver,
IPlayerLocalSender localSender,
IServerInstanceSelector instanceSelector,
string localServerType,
int localServerId,
MessageSendMetrics metrics = null)
{
_remoteClient = remoteClient ?? throw new ArgumentNullException(nameof(remoteClient));
_routeResolver = routeResolver ?? throw new ArgumentNullException(nameof(routeResolver));
_localSender = localSender ?? throw new ArgumentNullException(nameof(localSender));
_instanceSelector = instanceSelector ?? throw new ArgumentNullException(nameof(instanceSelector));
ArgumentException.ThrowIfNullOrWhiteSpace(localServerType, nameof(localServerType));

Check warning on line 94 in GameFrameX.Network.RemoteMessaging/Unified/UnifiedMessageSender.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this argument from the method call; it hides the caller information.

See more on https://sonarcloud.io/project/issues?id=GameFrameX.Server.Source&issues=AaFh_6KXOwdcpAly39nc&open=AaFh_6KXOwdcpAly39nc&pullRequest=617
_metrics = metrics ?? new MessageSendMetrics();
_currentServerType = GlobalSettings.CurrentSetting?.ServerType ?? GameServerConst.Game.Name;
_currentServerId = GlobalSettings.CurrentSetting?.ServerId ?? GameServerConst.Game.Id;
_currentServerType = localServerType;
_currentServerId = localServerId;
}

/// <summary>
Expand Down Expand Up @@ -315,6 +324,16 @@
}
}

/// <summary>
/// 判定路由是否指向本服:路由 <c>ServerType</c>(忽略大小写)与 <c>ServerId</c> 同时等于
/// 构造时注入的本服身份。多 Role 进程中「本服」为单一身份(触发装配的 Role)。
/// </summary>
/// <remarks>
/// Determines whether the route points to the local server: both the route's
/// <c>ServerType</c> (case-insensitive) and <c>ServerId</c> equal the identity injected at
/// construction. In a multi-role process the "local server" is a single identity
/// (the role that triggered the wiring).
/// </remarks>
private bool IsCurrentServer(PlayerRouteInfo routeInfo)
{
if (routeInfo == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,19 +121,27 @@ public static void Initialize(IUnifiedMessageSender sender)
}

/// <summary>
/// 使用默认组件初始化。适用于标准启动流程。
/// 使用默认组件初始化。适用于标准启动流程。本服身份由调用方显式传入并透传给发送器,
/// 不读取全局配置。
/// </summary>
/// <remarks>
/// Initializes with default components. Suitable for standard startup flow.
/// The local-server identity is passed in by the caller and forwarded to the sender
/// instead of being read from global settings.
/// </remarks>
/// <param name="routeResolver">玩家路由解析器 / Player route resolver</param>
/// <param name="localSender">本服玩家发送器 / Local player sender</param>
/// <param name="remoteClient">远程消息客户端 / Remote message client</param>
/// <param name="localServerType">本服服务器类型(透传给发送器的本服判定基准) / Local server type forwarded to the sender as the local-server identity</param>
/// <param name="localServerId">本服服务器编号(透传给发送器的本服判定基准) / Local server id forwarded to the sender as the local-server identity</param>
/// <exception cref="ArgumentNullException">当 <paramref name="remoteClient"/> 为 null 时抛出 / Thrown when <paramref name="remoteClient"/> is null</exception>
/// <exception cref="ArgumentException">当 <paramref name="localServerType"/> 为 null、空串或空白时抛出(发送器构造器校验) / Thrown when <paramref name="localServerType"/> is null, empty, or whitespace (validated by the sender constructor)</exception>
public static void InitializeWithDefaults(
IPlayerRouteResolver routeResolver,
IPlayerLocalSender localSender,
IRemoteMessageClient remoteClient)
IRemoteMessageClient remoteClient,
string localServerType,
int localServerId)
{
if (remoteClient == null)
{
Expand All @@ -142,7 +150,7 @@ public static void InitializeWithDefaults(

var instanceSelector = new ConsistentHashServerInstanceSelector();
var metrics = new MessageSendMetrics();
var sender = new UnifiedMessageSender(remoteClient, routeResolver, localSender, instanceSelector, metrics);
var sender = new UnifiedMessageSender(remoteClient, routeResolver, localSender, instanceSelector, localServerType, localServerId, metrics);
Initialize(sender);
}
}
Loading
Loading