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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@
* TcpConnectionProvider 单条 TCP 长连接改为按 endpoint 连接池(P0-A#3 / C239):每 endpoint 独立成池(默认上限 4 条,构造参数可调),并发需求驱动懒建(顺序调用保持单连接复用,并发调用按需扩展),轮询分发;连接级故障仅精准移除该连接(`IConnectionProvider.Invalidate(Stream)`/`ITransportProtocolAdapter.Invalidate(Stream)` 增量接口),不再全量断连。顺带修复旧实现隐藏缺陷:全进程单连接槽位在多 endpoint 交替调用时每次断连重连
* 连接死亡快速失败(P0-A#2#3 / C239):接收泵退出时经新增 `IRequestResponseMatcher.TryFail` 立即以 IOException 失败该连接上全部挂起请求(等待方收到可重试的 ConnectionFailed 而非干等超时),连接级异常(IO/Socket)仅失效当前连接、其余异常保守全量失效;新增本地化键 `RemoteMessaging.Client.ConnectionClosed`
* 新增 `RemoteMessageClientConcurrencyTests`(3 用例):屏障式假服务端证明并发请求不再全局串行(旧实现必然全体超时)且扇出多连接;连接读后即断场景验证快速失败 + 池仅移除死亡连接后重建成功;双服务交替调用验证按 endpoint 连接复用(各 1 条连接,旧实现每次断连重连)

### Documentation

* 全仓禁用 `<inheritdoc />`(C301,GFX-1013):55 个 `.cs` 文件 151 处 `/// <inheritdoc />`(含 2 处 `cref` 形态)替换为描述实现自身行为的完整双语 XML 文档注释(`<summary>` 中文、`<remarks>` 英文,`<param>` / `<returns>` / `<value>` / `<exception>` 用 `中文 / English`),覆盖 Capability 能力模块与 Options、Analyzers、Launcher 宿主适配、Templates 模板与测试替身;comment-hygiene 规范新增禁用条款。仅改 `///` 注释行,代码与行为零变更
## [1.13.7] - 2026-08-05

### Bug Fixes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,16 @@ internal static AdminApiErrorCode ToErrorCode(OnlineErrorCode code)
/// </summary>
private sealed class ScopeAdapter : IAdminApiScopeAdapter
{
/// <inheritdoc />
/// <summary>
/// 从请求中读取声明的租户、应用、服务器三元素作用域;三者均可读取时组装为机制管道作用域并返回 true,否则输出 null 并返回 false。
/// </summary>
/// <remarks>
/// Reads the claimed tenant / app / server triple from the request; when all three are readable it builds an
/// <see cref="AdminApiClaimedScope"/> and returns true, otherwise outputs null and returns false.
/// </remarks>
/// <param name="request">管理 API 请求 / Admin API request</param>
/// <param name="claimedScope">读取成功时为声明作用域,失败时为 null / Claimed scope on success, null on failure</param>
/// <returns>是否成功读取声明作用域 / Whether the claimed scope was read successfully</returns>
public bool TryReadClaimedScope(AdminApiRequest request, out AdminApiClaimedScope claimedScope)
{
if (request.TryReadClaimedScope(out var tenantId, out var appId, out var serverId))
Expand All @@ -143,7 +152,16 @@ public bool TryReadClaimedScope(AdminApiRequest request, out AdminApiClaimedScop
return false;
}

/// <inheritdoc />
/// <summary>
/// 将声明作用域与授权作用域转换为 Online 作用域后交由 <see cref="OnlineScopeGuard"/> 比对;越权时返回映射后的机制管道错误码,通过时返回 null。
/// </summary>
/// <remarks>
/// Converts both scopes to Online scopes and compares them via <see cref="OnlineScopeGuard"/>; returns the mapped
/// pipeline error code when access is denied, or null when the claimed scope is within the authorized scope.
/// </remarks>
/// <param name="claimed">请求声明的作用域 / Scope claimed by the request</param>
/// <param name="authorized">调用方被授权的作用域 / Scope the caller is authorized for</param>
/// <returns>拒绝时的错误码;通过时为 null / Error code when denied; null when allowed</returns>
public AdminApiErrorCode ValidateClaimed(AdminApiClaimedScope claimed, AdminApiClaimedScope authorized)
{
var denied = OnlineScopeGuard.Validate(ToOnlineScope(claimed), ToOnlineScope(authorized));
Expand All @@ -170,21 +188,51 @@ public IdempotencyAdapter(OnlineIdempotencyService idempotency)
_idempotency = idempotency ?? throw new ArgumentNullException(nameof(idempotency));
}

/// <inheritdoc />
/// <summary>
/// 将作用域转换为 Online 作用域后委托 Online 幂等服务开始幂等登记,并把结果种类、可执行标志、错误码(无错误时为 null)与首次响应副本映射为机制管道幂等结果。
/// </summary>
/// <remarks>
/// Delegates to the Online idempotency service after converting the scope, then maps the outcome kind, can-execute
/// flag, error code (null when none) and a copy of the first response into the pipeline idempotency outcome.
/// </remarks>
/// <param name="scope">幂等记录所属作用域 / Scope owning the idempotency record</param>
/// <param name="idempotencyKey">幂等键 / Idempotency key</param>
/// <param name="canonicalRequestText">规范化请求文本,用于比对重放请求是否一致 / Canonical request text used to detect mismatched replays</param>
/// <param name="bindPlayer">是否将幂等记录绑定到玩家维度 / Whether the record is bound to the player dimension</param>
/// <returns>机制管道幂等登记结果 / Pipeline idempotency outcome</returns>
public async Task<AdminIdempotencyOutcome> BeginAsync(AdminApiClaimedScope scope, string idempotencyKey, string canonicalRequestText, bool bindPlayer)
{
var outcome = await _idempotency.BeginAsync(ToOnlineScope(scope), idempotencyKey, canonicalRequestText, bindPlayer).ConfigureAwait(false);
var kind = (AdminIdempotencyOutcomeKind)(int)outcome.Kind;
return new AdminIdempotencyOutcome(kind, outcome.CanExecute, outcome.ErrorCode == OnlineErrorCode.None ? null : ToErrorCode(outcome.ErrorCode), outcome.FirstResponse.ToArray());
}

/// <inheritdoc />
/// <summary>
/// 将作用域转换为 Online 作用域后委托 Online 幂等服务把幂等记录标记为完成并保存首次响应。
/// </summary>
/// <remarks>
/// Converts the scope and delegates to the Online idempotency service to mark the record completed and store the first response.
/// </remarks>
/// <param name="scope">幂等记录所属作用域 / Scope owning the idempotency record</param>
/// <param name="idempotencyKey">幂等键 / Idempotency key</param>
/// <param name="firstResponse">首次执行的响应内容 / Response produced by the first execution</param>
/// <param name="bindPlayer">是否将幂等记录绑定到玩家维度 / Whether the record is bound to the player dimension</param>
/// <returns>表示异步操作的任务 / Task representing the asynchronous operation</returns>
public Task CompleteAsync(AdminApiClaimedScope scope, string idempotencyKey, byte[] firstResponse, bool bindPlayer)
{
return _idempotency.CompleteAsync(ToOnlineScope(scope), idempotencyKey, firstResponse, bindPlayer);
}

/// <inheritdoc />
/// <summary>
/// 将作用域转换为 Online 作用域后委托 Online 幂等服务把幂等记录标记为失败,以便后续请求重新执行。
/// </summary>
/// <remarks>
/// Converts the scope and delegates to the Online idempotency service to mark the record failed so later requests can execute again.
/// </remarks>
/// <param name="scope">幂等记录所属作用域 / Scope owning the idempotency record</param>
/// <param name="idempotencyKey">幂等键 / Idempotency key</param>
/// <param name="bindPlayer">是否将幂等记录绑定到玩家维度 / Whether the record is bound to the player dimension</param>
/// <returns>表示异步操作的任务 / Task representing the asynchronous operation</returns>
public Task FailAsync(AdminApiClaimedScope scope, string idempotencyKey, bool bindPlayer)
{
return _idempotency.FailAsync(ToOnlineScope(scope), idempotencyKey, bindPlayer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,27 @@ public sealed class OnlineAssetsCapabilityModule : ICapabilityModule, IAdminActi
/// </summary>
private IOnlineAuditReader _audit;

/// <inheritdoc />
/// <summary>
/// 获取资产能力清单,固定返回静态实例 <see cref="ManifestInstance"/>。
/// </summary>
/// <remarks>
/// Gets the assets capability manifest; always returns the static <see cref="ManifestInstance"/>.
/// </remarks>
/// <value>资产能力清单 / Assets capability manifest</value>
public CapabilityManifest Manifest
{
get { return ManifestInstance; }
}

/// <inheritdoc />
/// <summary>
/// 懒默认注册内存版资产存储与资产交易存储并解析实际生效实例,解析审计读面、幂等服务与事件发布器,构造资产查询服务与统一发放服务并注册回共享服务面。
/// </summary>
/// <remarks>
/// Registers in-memory asset and asset-transaction stores as lazy defaults (overridable by the host), resolves the
/// effective stores plus the audit reader, idempotency service and event publisher, then builds the
/// <see cref="OnlineAssetQueryService"/> and <see cref="OnlineGrantService"/> and registers them back into the shared services.
/// </remarks>
/// <param name="context">能力注册上下文 / Capability registration context</param>
public void Register(CapabilityRegistrationContext context)
{
// 两 store:懒默认注册后经共享面取用(族 / 宿主 GameDb Override 自动胜出)。
Expand All @@ -120,19 +134,42 @@ public void Register(CapabilityRegistrationContext context)
context.AddService(_grantService);
}

/// <inheritdoc />
/// <summary>
/// 激活阶段无需处理:所有服务已在注册阶段构造完毕,本能力不持有需启动的后台任务或桥接,直接返回已完成任务。
/// </summary>
/// <remarks>
/// Nothing to do on activation: every service is built during registration and the capability owns no background work
/// or bridges to start, so a completed task is returned.
/// </remarks>
/// <param name="context">能力激活上下文 / Capability activation context</param>
/// <param name="cancellationToken">取消令牌 / Cancellation token</param>
/// <returns>已完成的任务 / Completed task</returns>
public Task ActivateAsync(CapabilityActivationContext context, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}

/// <inheritdoc />
/// <summary>
/// 停用阶段无需处理:激活时未启动任何资源,因此没有需要释放或撤销的内容,直接返回已完成任务。
/// </summary>
/// <remarks>
/// Nothing to do on deactivation: activation starts no resources, so there is nothing to release or revoke.
/// </remarks>
/// <param name="cancellationToken">取消令牌 / Cancellation token</param>
/// <returns>已完成的任务 / Completed task</returns>
public Task DeactivateAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}

/// <inheritdoc />
/// <summary>
/// 使用注册阶段得到的发放服务、查询服务、审计读面与交易存储构造资产管理处理器,并登记发放、撤销、钱包账本、资产审计与交易详情等管理动作。
/// </summary>
/// <remarks>
/// Builds <see cref="OnlineAdminAssetHandlers"/> from the grant service, query service, audit reader and transaction store
/// resolved during registration, and registers the grant / revoke / wallet-ledger / asset-audit / transaction-detail admin actions.
/// </remarks>
/// <param name="registry">管理动作注册表 / Admin action registry</param>
public void RegisterAdminActions(AdminActionRegistry registry)
{
new OnlineAdminAssetHandlers(_grantService, _assetQuery, _audit, _assetTransactionStore).Register(registry);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,30 @@ public sealed class OnlineCompetitionCapabilityModule : ICapabilityModule, IAdmi
/// </summary>
private string _projectionBoardId;

/// <inheritdoc />
/// <summary>
/// 获取竞技能力清单,固定返回静态实例 <see cref="ManifestInstance"/>。
/// </summary>
/// <remarks>
/// Gets the competition capability manifest; always returns the static <see cref="ManifestInstance"/>.
/// </remarks>
/// <value>竞技能力清单 / Competition capability manifest</value>
public CapabilityManifest Manifest
{
get { return ManifestInstance; }
}

/// <inheritdoc />
/// <summary>
/// 读取竞技能力选项,懒默认注册内存版排行榜、赛季、锦标赛存储并解析生效实例,构造并注册排行榜服务与活动运营注册表;发放服务在场时再注册赛季服务与锦标赛服务;投影榜单标识非空白且对局结果存储在场时,注册 Match.Settled 排行榜投影器、订阅对局结算事件并添加投影重试的 tick 任务。
/// </summary>
/// <remarks>
/// Reads <see cref="OnlineCompetitionOptions"/>, registers in-memory leaderboard / season / tournament stores as lazy
/// defaults and resolves the effective instances, then builds and registers the leaderboard service and the live-ops
/// registry. Season and tournament services are registered only when the optional grant service is present. When the
/// projection board id is not blank and the match result store is present, it registers the Match.Settled leaderboard
/// projector, subscribes it to the match-settled event and adds the <c>leaderboard-projection-retry</c> tick job;
/// otherwise the projection is silently skipped.
/// </remarks>
/// <param name="context">能力注册上下文 / Capability registration context</param>
public void Register(CapabilityRegistrationContext context)
{
var options = context.GetOptions<OnlineCompetitionOptions>();
Expand Down Expand Up @@ -147,7 +164,18 @@ public void Register(CapabilityRegistrationContext context)
}
}

/// <inheritdoc />
/// <summary>
/// 投影榜单标识为空白时直接返回;否则以 Top 1 查询探测目标榜单,已存在则返回,返回资源不存在时按累加计分、降序排序创建该榜单。
/// </summary>
/// <remarks>
/// Returns immediately when the projection board id is blank. Otherwise probes the target board with a top-1 query;
/// if it already exists nothing else happens, and if the probe reports resource-not-found the board is created with
/// the sum score-update policy and descending sort order. Any other failure aborts activation so the host can roll back.
/// </remarks>
/// <param name="context">能力激活上下文,用于解析 Online 作用域 / Capability activation context used to resolve the Online scope</param>
/// <param name="cancellationToken">取消令牌 / Cancellation token</param>
/// <returns>表示异步激活操作的任务 / Task representing the asynchronous activation</returns>
/// <exception cref="InvalidOperationException">探测返回资源不存在以外的失败码,或创建榜单失败 / The probe fails with a code other than resource-not-found, or creating the board fails</exception>
public async Task ActivateAsync(CapabilityActivationContext context, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(_projectionBoardId))
Expand Down Expand Up @@ -182,13 +210,28 @@ public async Task ActivateAsync(CapabilityActivationContext context, Cancellatio
}
}

/// <inheritdoc />
/// <summary>
/// 停用阶段无需处理:事件订阅与 tick 任务由宿主机制在注册时托管,激活仅预建榜单而不持有运行资源,直接返回已完成任务。
/// </summary>
/// <remarks>
/// Nothing to do on deactivation: the event subscription and tick job are declared to the host during registration,
/// and activation only pre-creates the board without holding runtime resources, so a completed task is returned.
/// </remarks>
/// <param name="cancellationToken">取消令牌 / Cancellation token</param>
/// <returns>已完成的任务 / Completed task</returns>
public Task DeactivateAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}

/// <inheritdoc />
/// <summary>
/// 基于注册阶段构造的活动运营注册表登记活动运营管理动作,并基于排行榜服务登记排行榜查询管理动作。
/// </summary>
/// <remarks>
/// Registers the live-ops admin actions backed by the live-ops registry and the leaderboard query admin actions backed
/// by the leaderboard service, both built during registration.
/// </remarks>
/// <param name="registry">管理动作注册表 / Admin action registry</param>
public void RegisterAdminActions(AdminActionRegistry registry)
{
OnlineAdminLiveOpsHandlers.RegisterLiveOps(registry, _liveOps);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,15 @@ public sealed class OnlineCompetitionOptions : ICapabilityOptions
/// </summary>
public OnlineLeaderboardProjectionOptions LeaderboardProjection { get; set; } = new OnlineLeaderboardProjectionOptions();

/// <inheritdoc />
/// <summary>
/// 校验排行榜组件选项:单次提交最大分数、限流最大提交次数、限流窗口秒数、缓存有效期秒数与最大分页大小均须为正数;投影配置不做校验(空白榜单标识表示不投影)。
/// </summary>
/// <remarks>
/// Validates the leaderboard options: default maximum score per submission, rate-limit maximum submissions, rate-limit window
/// seconds, cache time-to-live seconds and maximum page size must all be positive. The projection settings are not validated because
/// a blank board id simply disables the projection. Failures are recorded on the validation collector.
/// </remarks>
/// <param name="validation">选项校验结果收集器 / Options validation collector</param>
public void Validate(CapabilityOptionsValidation validation)
{
validation.RequirePositive("Leaderboard.DefaultMaxScorePerSubmission", Leaderboard.DefaultMaxScorePerSubmission);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,13 @@ public OnlineAuthorizedScopeProvider(OnlineScope authorizedScope)
_authorizedScope = authorizedScope ?? throw new ArgumentNullException(nameof(authorizedScope));
}

/// <inheritdoc />
/// <summary>
/// 获取构造时注入的族基建预置授权作用域,只透出不构造。
/// </summary>
/// <remarks>
/// Gets the family-provisioned authorized scope injected at construction; the provider only exposes it and never builds a default.
/// </remarks>
/// <value>授权作用域,恒不为 null / Authorized scope, never null</value>
public OnlineScope AuthorizedScope
{
get { return _authorizedScope; }
Expand Down
Loading
Loading