From dab24a6f08c49708bcbf08b9d6b87ace89c180e0 Mon Sep 17 00:00:00 2001 From: Denver Jin Date: Fri, 2 Oct 2026 22:21:37 +0800 Subject: [PATCH] =?UTF-8?q?fix(protocol):=20=E8=A1=A5=E9=BD=90=E4=B8=8A?= =?UTF-8?q?=E6=B8=B8=E5=AE=98=E6=9C=8D=E5=AF=BC=E5=85=A5=E5=85=B1=E4=BA=AB?= =?UTF-8?q?=E5=A5=91=E7=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现场:Dashboard 与 Server 最新主分支已使用增量导入策略、目标账号和缓存状态字段,但共享协议主分支缺少定义,阻断鉴权改造的构建验证。 改动:补齐兼容 JSON 字段及策略标准化,未传策略保留默认增量语义;不修改 MasterMemory 或游戏消息模型。补英中文档与契约测试。 验证:8 项协议测试通过;Dashboard 构建零警告零错误;Server Release 构建通过(37 项既有警告)。 Co-authored-by: Codex --- README-CN.md | 6 +++ README.md | 6 +++ .../PlatformAccountApiContracts.cs | 15 +++++++ .../OfficialImportContractTests.cs | 44 +++++++++++++++++++ .../Sirius.Protocol.Tests.csproj | 15 +++++++ 5 files changed, 86 insertions(+) create mode 100644 tests/Sirius.Protocol.Tests/OfficialImportContractTests.cs create mode 100644 tests/Sirius.Protocol.Tests/Sirius.Protocol.Tests.csproj diff --git a/README-CN.md b/README-CN.md index 821e9ee..383e35e 100644 --- a/README-CN.md +++ b/README-CN.md @@ -518,3 +518,9 @@ MessagePack、MasterMemory、MagicOnion 等第三方依赖分别受其自身许 SiriusData 是非官方的社区互操作与基础设施项目。 *World Dai Star*、*World Dai Star: Yume no Stellarium*、ユメステ,以及相关名称、数据、资源和商标均归各自权利人所有。本仓库不是官方 SDK,与游戏开发商、发行商和运营方不存在官方隶属或授权关系。 + +## 内部导入 API 兼容性 + +共享官服导入契约包含 Dashboard 与 Server 使用的 `conflictPolicy`、`targetAccountId`、`source`、`hasReusableRawData`。缺少或未知策略按 `incremental` 处理,`new_account` 显式新建独立账号;目标未传为 0。这些新增 JSON 字段不改变 MasterMemory 表或游戏协议模型。 + +使用 .NET 10 SDK 执行 `dotnet test tests/Sirius.Protocol.Tests` 检查策略与 JSON 兼容性。 diff --git a/README.md b/README.md index d7beee0..f03bc59 100644 --- a/README.md +++ b/README.md @@ -495,3 +495,9 @@ Third-party dependencies such as MessagePack, MasterMemory, and MagicOnion remai SiriusData is an unofficial community interoperability and infrastructure project. *World Dai Star*, *World Dai Star: Yume no Stellarium*, ユメステ, and related names, data, assets, and trademarks belong to their respective rights holders. This repository is not an official SDK and is not affiliated with or endorsed by the game's developers, publishers, or operators. + +## Internal import API compatibility + +The shared official-import contracts include `conflictPolicy`, `targetAccountId`, `source`, and `hasReusableRawData` consumed by Dashboard and Server. Missing/unknown policy normalizes to `incremental`; `new_account` explicitly creates a separate account. Missing target remains zero. These additive JSON fields do not change MasterMemory tables or game wire models. + +Run `dotnet test tests/Sirius.Protocol.Tests` for policy and JSON compatibility checks (.NET 10 SDK). diff --git a/src/Sirius.Protocol/Models/InternalApi/PlatformAccountApiContracts.cs b/src/Sirius.Protocol/Models/InternalApi/PlatformAccountApiContracts.cs index a8d0ca2..3bd2faf 100644 --- a/src/Sirius.Protocol/Models/InternalApi/PlatformAccountApiContracts.cs +++ b/src/Sirius.Protocol/Models/InternalApi/PlatformAccountApiContracts.cs @@ -55,10 +55,22 @@ public sealed class PlatformIssueTemporaryTakeOverReply public string ErrorCode { get; set; } = string.Empty; } +public static class OfficialUserImportConflictPolicies +{ + public const string Incremental = "incremental"; + public const string NewAccount = "new_account"; + + public static string Normalize(string? value) => + string.Equals(value?.Trim(), NewAccount, StringComparison.OrdinalIgnoreCase) ? NewAccount : Incremental; +} + public sealed class PlatformCreateOfficialUserImportRequest { public string LinkageId { get; set; } = string.Empty; public string Password { get; set; } = string.Empty; + // Empty preserves legacy requests; the server normalizes it to incremental. + public string ConflictPolicy { get; set; } = string.Empty; + public long TargetAccountId { get; set; } } public sealed class PlatformCreateOfficialUserImportReply @@ -91,6 +103,9 @@ public sealed class PlatformOfficialUserImportStatusReply public long CreatedAt { get; set; } public long? StartedAt { get; set; } public long? CompletedAt { get; set; } + public string ConflictPolicy { get; set; } = string.Empty; + public string Source { get; set; } = string.Empty; + public bool HasReusableRawData { get; set; } } public sealed class PlatformOfficialUserImportJobPage diff --git a/tests/Sirius.Protocol.Tests/OfficialImportContractTests.cs b/tests/Sirius.Protocol.Tests/OfficialImportContractTests.cs new file mode 100644 index 0000000..a25d117 --- /dev/null +++ b/tests/Sirius.Protocol.Tests/OfficialImportContractTests.cs @@ -0,0 +1,44 @@ +using System.Text.Json; +using Sirius.Protocol.InternalApi; +using Xunit; + +namespace Sirius.Protocol.Tests; + +public sealed class OfficialImportContractTests +{ + private static readonly JsonSerializerOptions Json = new(JsonSerializerDefaults.Web); + + [Theory] + [InlineData(null, "incremental")] + [InlineData("", "incremental")] + [InlineData("unknown", "incremental")] + [InlineData("incremental", "incremental")] + [InlineData("new_account", "new_account")] + [InlineData(" NEW_ACCOUNT ", "new_account")] + public void Policy_matches_the_server_documented_fallback(string? input, string expected) => + Assert.Equal(expected, OfficialUserImportConflictPolicies.Normalize(input)); + + [Fact] + public void Legacy_request_keeps_unspecified_policy_and_no_target() + { + var request = JsonSerializer.Deserialize("""{"linkageId":"123","password":"fake"}""", Json)!; + Assert.Equal("", request.ConflictPolicy); + Assert.Equal(0, request.TargetAccountId); + Assert.Equal("incremental", OfficialUserImportConflictPolicies.Normalize(request.ConflictPolicy)); + } + + [Fact] + public void Incremental_request_and_reusable_job_status_round_trip() + { + var request = JsonSerializer.Deserialize("""{"linkageId":"123","password":"fake","conflictPolicy":"incremental","targetAccountId":81}""", Json)!; + Assert.Equal(81, request.TargetAccountId); + Assert.Equal("incremental", request.ConflictPolicy); + var status = new PlatformOfficialUserImportStatusReply { ConflictPolicy = "new_account", Source = "uploaded_file", HasReusableRawData = true }; + var encoded = JsonSerializer.Serialize(status, Json); + Assert.Contains("\"hasReusableRawData\":true", encoded); + var decoded = JsonSerializer.Deserialize(encoded, Json)!; + Assert.Equal(status.ConflictPolicy, decoded.ConflictPolicy); + Assert.Equal(status.Source, decoded.Source); + Assert.True(decoded.HasReusableRawData); + } +} diff --git a/tests/Sirius.Protocol.Tests/Sirius.Protocol.Tests.csproj b/tests/Sirius.Protocol.Tests/Sirius.Protocol.Tests.csproj new file mode 100644 index 0000000..802a788 --- /dev/null +++ b/tests/Sirius.Protocol.Tests/Sirius.Protocol.Tests.csproj @@ -0,0 +1,15 @@ + + + net10.0 + enable + enable + true + false + + + + + + + +