Repository navigation
feat(tools): 成就图片生成器改为本地渲染并支持任意语言 [Deepseek-V4.1-Flash] - #3655
meixuan1234 wants to merge 6 commits into
Conversation
审查者指南该 PR 移除成就图片生成对 minecraft-api.com 的依赖,改用 WPF 本地渲染实现任意语言、自适应排版和可选字体,并通过本地资源、在线服务与占位图标提供多级图标回退;同时修复原生目录清理及头像尺寸切换问题。 本地成就图片渲染时序图sequenceDiagram
participant User
participant PageToolsTest
participant IconLoader
participant Renderer
participant AchievementImage
User->>PageToolsTest: BtnAchievementPreview_Click
PageToolsTest->>IconLoader: _LoadAchievementIconAsync(itemId)
IconLoader->>IconLoader: _TryLoadVanillaIcon(itemId)
alt local icon unavailable
IconLoader->>IconLoader: _TryLoadOnlineIcon(itemId)
alt online icon unavailable
IconLoader->>IconLoader: _TryLoadBuiltInBarrier()
alt built-in barrier unavailable
IconLoader->>IconLoader: _LoadOnlineBarrier()
end
end
end
PageToolsTest->>Renderer: _RenderAchievementImage(icon)
Renderer->>Renderer: _WrapTextByWidth(text, typeface, fontSize, maxWidth, pixelsPerDip)
Renderer->>AchievementImage: set Source
成就图标回退流程图flowchart TD
A[Item ID] --> B[_TryLoadVanillaIcon]
B -->|found| E[Render achievement image]
B -->|not found| C[_TryLoadOnlineIcon]
C -->|found| E
C -->|not found| D[_TryLoadBuiltInBarrier]
D -->|found| E
D -->|not found| F[_LoadOnlineBarrier]
F -->|found| E
F -->|not found| G[Draw fallback cube]
G --> E
文件级变更
对照关联 issue 的评估
可能关联的 issue
提示与命令与 Sourcery 交互
自定义使用体验访问你的控制面板以:
获取帮助Original review guide in EnglishReviewer's Guide该 PR 移除成就图片生成对 minecraft-api.com 的依赖,改用 WPF 本地渲染实现任意语言、自适应排版和可选字体,并通过本地资源、在线服务与占位图标提供多级图标回退;同时修复原生目录清理及头像尺寸切换问题。 Sequence diagram for local achievement image renderingsequenceDiagram
participant User
participant PageToolsTest
participant IconLoader
participant Renderer
participant AchievementImage
User->>PageToolsTest: BtnAchievementPreview_Click
PageToolsTest->>IconLoader: _LoadAchievementIconAsync(itemId)
IconLoader->>IconLoader: _TryLoadVanillaIcon(itemId)
alt local icon unavailable
IconLoader->>IconLoader: _TryLoadOnlineIcon(itemId)
alt online icon unavailable
IconLoader->>IconLoader: _TryLoadBuiltInBarrier()
alt built-in barrier unavailable
IconLoader->>IconLoader: _LoadOnlineBarrier()
end
end
end
PageToolsTest->>Renderer: _RenderAchievementImage(icon)
Renderer->>Renderer: _WrapTextByWidth(text, typeface, fontSize, maxWidth, pixelsPerDip)
Renderer->>AchievementImage: set Source
Flow diagram for achievement icon fallbackflowchart TD
A[Item ID] --> B[_TryLoadVanillaIcon]
B -->|found| E[Render achievement image]
B -->|not found| C[_TryLoadOnlineIcon]
C -->|found| E
C -->|not found| D[_TryLoadBuiltInBarrier]
D -->|found| E
D -->|not found| F[_LoadOnlineBarrier]
F -->|found| E
F -->|not found| G[Draw fallback cube]
G --> E
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
嘿——我发现了 3 个问题
面向 AI Agent 的提示
请处理这次代码审查中的评论:
## 单独评论
### 评论 1
<location path="Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.cs" line_range="741" />
<code_context>
+ Media.FormattedText MakeText(string text, Media.Color color) => new(
+ _WrapTextByWidth(text, typeface, fontSize, maxTextWidth, superSample),
+ CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, typeface, fontSize,
+ new Media.SolidColorBrush(color), superSample)
+ {
+ MaxTextWidth = maxTextWidth,
</code_context>
<issue_to_address>
**问题 (bug_risk):**RTL 成就文本始终按从左到右的方向进行格式化,因此即使该功能声称支持任意语言,阿拉伯语和希伯来语文本仍会以错误的双向文本顺序和方向显示。
**触发条件:**标题或描述包含从右到左的文本时。
**建议修复:**根据文本或输入区域性选择流方向,或者仅对 LTR 内容使用 `FlowDirection.LeftToRight`,对 RTL 内容使用 `FlowDirection.RightToLeft`。
```suggestion
CultureInfo.CurrentUICulture, CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft ? FlowDirection.RightToLeft : FlowDirection.LeftToRight, typeface, fontSize,
```
</issue_to_address>
### 评论 2
<location path="Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.cs" line_range="601-606" />
<code_context>
+ try
+ {
+ await Task.Yield(); // 先把加载指示绘制出来
+ var itemId = AchievementBlockTextBox.Text.Trim();
+ var (icon, notFound) = await _LoadAchievementIconAsync(itemId);
+ if (notFound && !string.IsNullOrEmpty(itemId))
+ HintService.Hint(Lang.Text("Tools.Test.Achievement.IconNotFound", itemId), HintType.Info);
+
+ AchievementImage.Source = _RenderAchievementImage(icon);
+ AchievementImage.Visibility = Visibility.Visible;
+ }
</code_context>
<issue_to_address>
**问题 (bug_risk):**项目 ID 在异步加载图标之前被捕获,但 `_RenderAchievementImage` 在等待完成后又重新读取文本框。因此,在网络请求进行期间编辑项目 ID 或成就文本,会将旧项目的图标与新文本组合在一起,从而生成内容不一致的预览或保存图片。
**触发条件:**在线图标或屏障请求进行期间,用户编辑成就字段时。
**建议修复:**在等待异步操作之前快照保存所有成就输入,并将该不可变快照传递给图标加载和渲染逻辑;或者在操作期间禁用输入控件。
</issue_to_address>
### 评论 3
<location path="Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.cs" line_range="826-844" />
<code_context>
- ModBase.CopyFile(savePath, path);
- File.Delete(savePath);
- HintService.Hint(Lang.Text("Tools.Test.Achievement.Saved"), HintType.Success);
+ foreach (var ch in text)
+ {
+ if (ch == '\n')
+ {
+ result.Append(line).Append('\n');
+ line.Clear();
+ continue;
}
- // 下载成功,返回 True
- else if (response.StatusCode == HttpStatusCode.NotFound)
</code_context>
<issue_to_address>
**问题 (bug_risk):**换行逻辑遍历的是 UTF-16 `char` 值,可能会将代理项对中的一半移动到下一行。这样生成的字符串会在代理项对之间插入换行符,导致表情符号和其他补充平面字符在换行边界处显示为损坏字符或替换字符。
**触发条件:**补充 Unicode 字符恰好位于行宽边界时。
**建议修复:**按 Unicode 标量值或字素簇进行换行,在测量和移动文本时确保每个代理项对保持完整。
</issue_to_address>Original comment in English
Hey - I've found 3 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.cs" line_range="741" />
<code_context>
+ Media.FormattedText MakeText(string text, Media.Color color) => new(
+ _WrapTextByWidth(text, typeface, fontSize, maxTextWidth, superSample),
+ CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, typeface, fontSize,
+ new Media.SolidColorBrush(color), superSample)
+ {
+ MaxTextWidth = maxTextWidth,
</code_context>
<issue_to_address>
**issue (bug_risk):** RTL achievement text is always formatted as left-to-right, so Arabic and Hebrew text is displayed with incorrect bidi ordering and direction despite the feature claiming support for arbitrary languages.
**Triggers:** When the title or description contains right-to-left text.
**Suggested fix:** Choose the flow direction from the text or input culture, or use `FlowDirection.LeftToRight` only for LTR content and `FlowDirection.RightToLeft` for RTL content.
```suggestion
CultureInfo.CurrentUICulture, CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft ? FlowDirection.RightToLeft : FlowDirection.LeftToRight, typeface, fontSize,
```
</issue_to_address>
### Comment 2
<location path="Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.cs" line_range="601-606" />
<code_context>
+ try
+ {
+ await Task.Yield(); // 先把加载指示绘制出来
+ var itemId = AchievementBlockTextBox.Text.Trim();
+ var (icon, notFound) = await _LoadAchievementIconAsync(itemId);
+ if (notFound && !string.IsNullOrEmpty(itemId))
+ HintService.Hint(Lang.Text("Tools.Test.Achievement.IconNotFound", itemId), HintType.Info);
+
+ AchievementImage.Source = _RenderAchievementImage(icon);
+ AchievementImage.Visibility = Visibility.Visible;
+ }
</code_context>
<issue_to_address>
**issue (bug_risk):** The item ID is captured before the asynchronous icon load, but `_RenderAchievementImage` reads the text boxes again after the await. Editing the item ID or achievement text while the network request is running therefore combines the old item's icon with the new text, producing an internally inconsistent preview or saved image.
**Triggers:** When the user edits an achievement field while an online icon or barrier request is in progress.
**Suggested fix:** Snapshot all achievement inputs before awaiting and pass that immutable snapshot through icon loading and rendering, or disable the input controls for the duration of the operation.
</issue_to_address>
### Comment 3
<location path="Plain Craft Launcher 2/Pages/PageTools/PageToolsTest.xaml.cs" line_range="826-844" />
<code_context>
- ModBase.CopyFile(savePath, path);
- File.Delete(savePath);
- HintService.Hint(Lang.Text("Tools.Test.Achievement.Saved"), HintType.Success);
+ foreach (var ch in text)
+ {
+ if (ch == '\n')
+ {
+ result.Append(line).Append('\n');
+ line.Clear();
+ continue;
}
- // 下载成功,返回 True
- else if (response.StatusCode == HttpStatusCode.NotFound)
</code_context>
<issue_to_address>
**issue (bug_risk):** Wrapping iterates UTF-16 `char` values and can move only one half of a surrogate pair to the next line. The resulting string contains a newline between the pair, so emoji and other supplementary-plane characters render as broken or replacement glyphs at a wrap boundary.
**Triggers:** When a supplementary Unicode character falls exactly at a line-width boundary.
**Suggested fix:** Wrap by Unicode scalar values or grapheme clusters, keeping each surrogate pair together while measuring and moving text.
</issue_to_address>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 26aea5c2e4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| var instance = ModInstanceList.McMcInstanceSelected; | ||
| if (instance is null) return null; | ||
|
|
||
| var objects = ModAssets.McAssetsGetIndex(instance)?["objects"]; |
There was a problem hiding this comment.
Read local icons from the installed resource data
When a normal selected instance is available, ModAssets.McAssetsGetIndex returns only the instance JSON's assetIndex descriptor (Plain Craft Launcher 2/Modules/Minecraft/ModAssets.cs:20-39), whose fields are metadata such as id and url, rather than the downloaded index's objects map. This lookup is therefore null and _TryLoadVanillaIcon always falls through to the external services, so offline users get a barrier for valid installed items such as stone; read from the actual texture-bearing installed resource (for modern clients, the resolved client JAR) before taking the network fallback.
Useful? React with 👍 / 👎.
| { | ||
| const int maxLength = 67; | ||
| if (string.IsNullOrEmpty(text) || text.Length <= maxLength) return text; | ||
| return text[..maxLength] + "..."; |
There was a problem hiding this comment.
Truncate text without splitting surrogate pairs
When a title or description longer than 67 UTF-16 code units has a supplementary character at the cutoff (for example, 66 ASCII characters followed by an emoji), this slice retains only the high surrogate and FormattedText renders a replacement glyph. The wrapping path already enumerates Runes, but truncation must also stop at a Unicode scalar or grapheme boundary to preserve the advertised arbitrary-language rendering.
Useful? React with 👍 / 👎.
| <Image x:Name="AchievementImage" Width="320" Height="64" Stretch="Uniform" Visibility="Collapsed" /> | ||
| <local:MyLoading x:Name="LoadAchievement" Margin="0,5,0,10" HorizontalAlignment="Center" | ||
| Text="{DynamicResource Tools.Test.Achievement.Loading}" | ||
| Visibility="Collapsed" /> |
There was a problem hiding this comment.
Stop the collapsed loading control's animation
Whenever this page is open and no achievement render is running, Collapsed does not unload this element. MyLoading.InitState defaults its state to Run (Plain Craft Launcher 2/Controls/MyLoading.xaml.cs:188-195), and AniLoop checks only IsLoaded and recursively schedules itself (MyLoading.xaml.cs:260-295), while these handlers only change Visibility; the invisible pickaxe animation therefore consumes animation and UI-thread work for the page's lifetime. Disable AutoRun and explicitly run and stop the loading state around each operation.
Useful? React with 👍 / 👎.
bb737c6 to
26aea5c
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bb737c665f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| var jar = current.PathInstance + current.Name + ".jar"; | ||
| if (File.Exists(jar)) return jar; | ||
|
|
||
| if (string.IsNullOrEmpty(current.InheritInstanceName)) break; |
There was a problem hiding this comment.
Honor client JAR overrides before following inheritance
For a selected profile that uses the supported version-JSON jar override, this resolver either stops when inheritsFrom is empty or follows the inherited ID, so it never opens the archive the launcher actually uses. The fresh evidence in this revision is the newly added resolver contrasted with ModLibrary.cs:159-181, where launches honor mcInstance.JsonObject["jar"]; with such a profile and no network, a locally available icon is replaced by the barrier, and with both fields it can come from the wrong version. Resolve the jar override before falling back to inheritance.
Useful? React with 👍 / 👎.
| foreach (var rune in text.EnumerateRunes()) | ||
| if (rune.Value is >= 0x0590 and <= 0x08FF) | ||
| return FlowDirection.RightToLeft; |
There was a problem hiding this comment.
Derive mixed-text direction from the first strong character
When a field starts with LTR text but contains an Arabic or Hebrew run (for example, Version 2 — إصدار), returning RTL merely because any RTL rune appears gives the whole FormattedText the wrong paragraph base direction, which reorders runs and neutral punctuation; joining the two description lines also lets one RTL line force the other line's direction. Determine direction from the first strong directional character, ideally per paragraph, rather than from any matching character.
Useful? React with 👍 / 👎.
26aea5c to
980f1c4
Compare
- RubbishClear 中 natives 目录的判断条件恒为 false,该清理分支从未生效 - CmbHeadSize_SelectionChanged 中 skinPath 判空无效,其初始值是空字符串而非 null
原先调用 minecraft-api.com 生成图片,该接口不支持非 ASCII 字符,输入中文返回 404, 界面因此只能标注「仅支持英文」。改为完全本地渲染后不再依赖第三方接口。 - 用 WPF 本地绘制成就卡片,支持任意语言 - 文字按像素宽度自动换行(含连续 ASCII 串),卡片高度随内容自适应 - 单行超过 67 字符时截取并追加省略号 - 物品图标按实例资源、在线接口、屏障占位依次回退 - 字体可切换为本机已安装的字体 - 在线图标缓存使用 ConcurrentDictionary,生成期间禁用预览与保存按钮 BREAKING CHANGE: 移除对 minecraft-api.com 的依赖
- 书写方向固定为 LeftToRight,阿拉伯文、希伯来文等 RTL 文本会显示反掉; 改为按文本内容检测(U+0590–U+08FF 区段)决定方向 - 图标异步加载完成后才读取文本框,若用户在此期间编辑,会得到 「旧图标配新文字」的不一致结果;改为等待前快照全部输入 - 按 char 遍历换行会从中间拆开代理项对,emoji 等补充平面字符落在 换行边界时会显示为乱码;改为按 Unicode 标量值(Rune)遍历
- 从实例客户端 JAR 读取物品贴图:assetIndex 只有元数据、objects 表不含贴图, 原实现恒为 null 导致离线时始终回退到在线源或屏障图标;现沿继承链定位 JAR - 文本截断改为按 Rune 遍历,避免在代理项对中间截断使 emoji 显示为替换符 - 成就加载指示关闭 AutoRun 并在预览/保存前后显式启停,避免折叠后动画持续空转
- 保存文件名改用快照标题并过滤 Windows 非法字符,标题含 : ? 等字符时不再保存失败 - 客户端 JAR 的读取与解码拆开:定位 JAR 留在 UI 线程,开包拷贝字节放到后台 - 在线请求超时改用 CancellationToken,超时后底层连接会被真正取消 - 图标缩放按原始尺寸选择插值:本地 16px 用最近邻,256px 在线渲染图用高质量 - 第一行为空时不再拼出多余空行 - 内置屏障图标加载失败后不再每次重试 - 统一在线下载与解码逻辑,去掉两处重复实现
- 实例 JSON 的 jar 字段指向其他实例时(OptiFine、LiteLoader 等), 沿该字段定位真正被加载的 JAR,避免取到错误版本或退回屏障图标 - 书写方向改由第一个强方向字符决定,不再因文本中任意位置出现 RTL 字符 就把整段判为 RTL,防止中性标点错位
980f1c4 to
b84e6be
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b84e6be579
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| <sys:String x:Key="Tools.Test.Achievement.Title">Custom achievement image generator</sys:String> | ||
| <sys:String x:Key="Tools.Test.Achievement.Font">Font</sys:String> | ||
| <sys:String x:Key="Tools.Test.Achievement.Loading">Fetching item icon…</sys:String> |
There was a problem hiding this comment.
Update complete locale overrides for the new achievement UI
When the UI language is en-GB or zh-TW, LocalizationService loads that locale after the zh-CN base dictionary (PCL.Core/App/Localization/LocalizationService.cs:267-277), but those two complete dictionaries were not updated: they still override the title with “English only” at en-GB.xaml:3437 / zh-TW.xaml:3437 and omit the new Font, Loading, and IconNotFound keys. Consequently these users see a contradictory title plus Simplified Chinese fallback labels/hints; add the corresponding entries to both locale dictionaries.
Useful? React with 👍 / 👎.
背景
成就图片生成器原先调用
minecraft-api.com,该接口不支持非 ASCII 字符,输入中文返回 404,界面因此只能标注「仅支持英文」。改为 WPF 本地渲染后不再依赖第三方接口。改动
...RubbishClear:dirInfo2.Name == dirInfo2.Name + "-natives"恒为 false,该清理分支从未生效CmbHeadSize_SelectionChanged:skinPath is not null无效(其初始值为空字符串)AI 使用与状态
使用 Deepseek-V4.1-Flash 辅助实现、整理和验证。已使用 GitHub 签名。人工已核查每行代码并测试功能。
Fixes #1566
Sourcery 摘要
将成就图片生成迁移至本地渲染,提升多语言支持并修复相关工具缺陷。
新功能:
错误修复:
Original summary in English
Sourcery 摘要
将成就图片生成改为支持多语言的本地渲染,并修复工具清理和头像处理缺陷。
新功能:
问题修复:
改进:
Original summary in English
Sourcery 摘要
将成就图片生成迁移至本地渲染,以支持多语言内容,并修复工具清理和头像处理方面的问题。
新功能:
错误修复:
改进:
Original summary in English
Summary by Sourcery
将成就图片生成功能改为在本地渲染,以支持多种语言,并修复工具清理和头像处理方面的问题。
New Features:
Bug Fixes:
Enhancements:
Original summary in English
Summary by Sourcery
将成就图片生成改为支持多语言的本地渲染,并修复工具清理和头像处理缺陷。
New Features:
Bug Fixes:
Enhancements:
Original summary in English
Original summary in English