在选曲分类界面添加了定数分类的效果#133
Conversation
审阅者指南在乐曲选择界面中新增一个“常数难度”排序标签页,并将其接入配置、排序/标签导航、分类分组和 UI,从而可以按谱面常数浏览乐曲,并可选使用自定义标签精灵。 乐曲选择中常数难度排序标签的时序图sequenceDiagram
actor Player
participant MusicSelectMonitor
participant MusicSelectProcess
participant SongConstantSort as SongConstantSort_CategoryTabPatches
participant ConstNameStore
participant NotesListManager as NotesListManager_Singleton
participant DataManager as DataManager_Singleton
participant ConstSpriteCache
Player->>MusicSelectProcess: AddSort(root=Tab)
MusicSelectProcess-->>MusicSelectProcess: _beforeCategorySortSetting++ (wrap to 0-6)
Player->>MusicSelectProcess: CategoryTabSort(player)
MusicSelectProcess->>SongConstantSort: CategoryTabSort_Prefix(player)
SongConstantSort-->>MusicSelectProcess: return DoCategoryTabConstant(player)
Note over SongConstantSort,NotesListManager: Build constant-based categories
SongConstantSort->>NotesListManager_Singleton: GetNotesList()
NotesListManager_Singleton-->>SongConstantSort: notes dict
SongConstantSort-->>ConstNameStore: NameMap[constKey] = "Lv.x.y"
Player->>MusicSelectMonitor: getTabString(data)
MusicSelectMonitor->>MusicSelectMonitor: GetCategorySortSetting()
MusicSelectMonitor->>ConstNameStore: NameMap.TryGetValue(data.categoryID)
ConstNameStore-->>MusicSelectMonitor: name
MusicSelectMonitor-->>Player: tab label (e.g. Lv.13.7)
Player->>MusicSelectMonitor: getTabColor(data)
MusicSelectMonitor->>MusicSelectMonitor: GetCategorySortSetting()
MusicSelectMonitor->>DataManager_Singleton: GetMusicLevel(levelEnum)
DataManager_Singleton-->>MusicSelectMonitor: levelData
MusicSelectMonitor-->>Player: tab Color
Player->>MusicSelectMonitor: GetTabSprite(data)
MusicSelectMonitor->>MusicSelectMonitor: GetCategorySortSetting()
MusicSelectMonitor->>ConstSpriteCache: GetSprite(data.categoryID)
ConstSpriteCache-->>MusicSelectMonitor: custom Sprite or null
MusicSelectMonitor-->>Player: tab Sprite (custom or default)
文件级变更
提示与命令与 Sourcery 交互
自定义你的使用体验访问你的 控制面板 以:
获取帮助
Original review guide in EnglishReviewer's GuideAdds a new "constant difficulty" sort tab to the music selection screen, wiring it into config, sort/tab navigation, category grouping, and UI so songs can be browsed by chart constant with optional custom tab sprites. Sequence diagram for constant difficulty sort tab on music selectionsequenceDiagram
actor Player
participant MusicSelectMonitor
participant MusicSelectProcess
participant SongConstantSort as SongConstantSort_CategoryTabPatches
participant ConstNameStore
participant NotesListManager as NotesListManager_Singleton
participant DataManager as DataManager_Singleton
participant ConstSpriteCache
Player->>MusicSelectProcess: AddSort(root=Tab)
MusicSelectProcess-->>MusicSelectProcess: _beforeCategorySortSetting++ (wrap to 0-6)
Player->>MusicSelectProcess: CategoryTabSort(player)
MusicSelectProcess->>SongConstantSort: CategoryTabSort_Prefix(player)
SongConstantSort-->>MusicSelectProcess: return DoCategoryTabConstant(player)
Note over SongConstantSort,NotesListManager: Build constant-based categories
SongConstantSort->>NotesListManager_Singleton: GetNotesList()
NotesListManager_Singleton-->>SongConstantSort: notes dict
SongConstantSort-->>ConstNameStore: NameMap[constKey] = "Lv.x.y"
Player->>MusicSelectMonitor: getTabString(data)
MusicSelectMonitor->>MusicSelectMonitor: GetCategorySortSetting()
MusicSelectMonitor->>ConstNameStore: NameMap.TryGetValue(data.categoryID)
ConstNameStore-->>MusicSelectMonitor: name
MusicSelectMonitor-->>Player: tab label (e.g. Lv.13.7)
Player->>MusicSelectMonitor: getTabColor(data)
MusicSelectMonitor->>MusicSelectMonitor: GetCategorySortSetting()
MusicSelectMonitor->>DataManager_Singleton: GetMusicLevel(levelEnum)
DataManager_Singleton-->>MusicSelectMonitor: levelData
MusicSelectMonitor-->>Player: tab Color
Player->>MusicSelectMonitor: GetTabSprite(data)
MusicSelectMonitor->>MusicSelectMonitor: GetCategorySortSetting()
MusicSelectMonitor->>ConstSpriteCache: GetSprite(data.categoryID)
ConstSpriteCache-->>MusicSelectMonitor: custom Sprite or null
MusicSelectMonitor-->>Player: tab Sprite (custom or default)
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - 我发现了两个问题,并留下了一些整体性的反馈:
- 新增标签页 ID 和边界相关的地方有不少硬编码的魔法数字(例如在
SortTabPatches、NavigationPatches以及检查CategorySortSetting时用到的6、7)。这会导致行为在枚举或标签数量变化时变得非常脆弱;建议基于DB.SortTabID把这些值集中到一个常量或辅助方法中,以减少未来改动时出错的风险。 - 在
DoCategoryTabConstant中,你对cd.msDetailData做了原地修改(设置musicId和difficultyId),然后把它传入新的CombineMusicSelectData实例。如果msDetailData在不同分类或不同调用之间是共享的,这种就地修改可能会引入很隐蔽的 bug。更安全的做法是在每个条目上克隆或新建一个 detail 对象,而不是复用并修改现有的对象。 - 在
ConstSpriteCache.GetSprite中加载 AssetBundle 的逻辑,只在foreach期间保持每个 bundle 打开,但在第一次加载之后没有对_sprites做空值检查,而且吞掉了所有类型的异常。如果能收紧异常处理范围,并为 bundle 使用using/try-finally模式,会让它在面对部分失败或资源损坏时更加健壮。
给 AI Agents 的提示
请根据这次代码审查中的评论进行修改:
## 整体性评论
- 新增标签页 ID 和边界相关的地方有不少硬编码的魔法数字(例如在 `SortTabPatches`、`NavigationPatches` 以及检查 `CategorySortSetting` 时用到的 `6`、`7`)。这会导致行为在枚举或标签数量变化时变得非常脆弱;建议基于 `DB.SortTabID` 把这些值集中到一个常量或辅助方法中,以减少未来改动时出错的风险。
- 在 `DoCategoryTabConstant` 中,你对 `cd.msDetailData` 做了原地修改(设置 `musicId` 和 `difficultyId`),然后把它传入新的 `CombineMusicSelectData` 实例。如果 `msDetailData` 在不同分类或不同调用之间是共享的,这种就地修改可能会引入很隐蔽的 bug。更安全的做法是在每个条目上克隆或新建一个 detail 对象,而不是复用并修改现有的对象。
- 在 `ConstSpriteCache.GetSprite` 中加载 AssetBundle 的逻辑,只在 `foreach` 期间保持每个 bundle 打开,但在第一次加载之后没有对 `_sprites` 做空值检查,而且吞掉了所有类型的异常。如果能收紧异常处理范围,并为 bundle 使用 `using` / `try-finally` 模式,会让它在面对部分失败或资源损坏时更加健壮。
## 逐条评论
### 评论 1
<location path="AquaMai.Mods/GameSystem/SongConstantSort.cs" line_range="90" />
<code_context>
+ [HarmonyPatch]
+ public static class SortTabPatches
+ {
+ [HarmonyPatch(typeof(DB.SortTabIDEnum), "GetEnd", new System.Type[0])]
+ [HarmonyPrefix]
+ public static bool GetEnd_Static(ref int __result) { __result = 7; return false; }
</code_context>
<issue_to_address>
**suggestion (bug_risk):** 对标签索引 `6`/`7` 的硬编码让补丁与当前枚举布局高度耦合。
依赖 `6`/`7` 等于假设枚举的顺序和数量永远不会变化;如果未来 `DB.SortTabID` 被重新排序或扩展,标签文本和导航可能会以不明显的方式出现问题。更建议从枚举中推导这些值(例如使用 `Enum.GetValues`,或引入专门的 `SortTabID.Constant`),或者把它们集中到一个常量里,这样未来枚举变更时只需要改一个地方。
建议实现:
```csharp
[HarmonyPatch]
public static class SortTabPatches
{
/// <summary>
/// Maximum underlying value of DB.SortTabID. Keeps patches resilient to enum reordering/extensions.
/// </summary>
private static readonly int MaxSortTabIndex = GetMaxSortTabIndex();
private static int GetMaxSortTabIndex()
{
var values = (DB.SortTabID[])Enum.GetValues(typeof(DB.SortTabID));
var max = int.MinValue;
foreach (var value in values)
{
var intValue = (int)value;
if (intValue > max)
max = intValue;
}
return max;
}
```
```csharp
[HarmonyPatch(typeof(DB.SortTabIDEnum), "GetEnd", new System.Type[0])]
[HarmonyPrefix]
public static bool GetEnd_Static(ref int __result)
{
__result = MaxSortTabIndex + 1;
return false;
}
```
```csharp
[HarmonyPatch(typeof(DB.SortTabIDEnum), "GetEnd", new System.Type[] { typeof(DB.SortTabID) })]
[HarmonyPrefix]
public static bool GetEnd_Instance(ref int __result)
{
__result = MaxSortTabIndex + 1;
return false;
}
```
```csharp
[HarmonyPatch(typeof(DB.SortTabIDEnum), "IsValid")]
[HarmonyPrefix]
public static bool IsValid(DB.SortTabID self, ref bool __result)
{
__result = self >= DB.SortTabID.Genre && (int)self <= MaxSortTabIndex;
```
如果 `DB.SortTabID` 中包含不打算作为用户可见标签的值(例如内部用或哨兵值),你可能需要调整 `GetMaxSortTabIndex`,只在特定区间(例如从 `Genre` 到某个特定最后标签)内筛选出真正需要的枚举值,而不是直接采用枚举的最大值。
</issue_to_address>
### 评论 2
<location path="AquaMai.Mods/GameSystem/SongConstantSort.cs" line_range="281" />
<code_context>
+ return result;
+ }
+
+ private static void InitReflection()
+ {
+ if (_combineMusicDataListField == null)
</code_context>
<issue_to_address>
**suggestion (bug_risk):** 反射初始化没有处理成员缺失的情况,在游戏更新后可能会静默损坏。
这些反射调用假设 `_combineMusicDataList`、`CategoryNameList`、`SetSortList` 和 `AddRandomData` 一定存在。如果未来某次游戏更新重命名或删除了其中任意一个,字段会保持为 `null`,并在之后某处抛出 `NullReferenceException`,距离根因很远。建议在某个统一入口(例如 `OnBeforePatch` 或 `InitReflection`)一次性校验所有反射查找,并在失败时打印清晰的错误日志,使版本不匹配问题更明显,同时避免难以排查的崩溃。
建议实现:
```csharp
using System.Reflection;
using UnityEngine;
```
```csharp
private static void InitReflection()
{
// 预先解析所有反射目标,以便统一校验
var type = typeof(Process.MusicSelectProcess);
_combineMusicDataListField = type.GetField(
"_combineMusicDataList", BindingFlags.NonPublic | BindingFlags.Instance);
_categoryNameListProp = type.GetProperty(
"CategoryNameList", BindingFlags.Public | BindingFlags.Instance);
_setSortListMethod = type.GetMethod(
"SetSortList", BindingFlags.NonPublic | BindingFlags.Instance);
_addRandomDataMethod = type.GetMethod(
"AddRandomData", BindingFlags.NonPublic | BindingFlags.Instance);
// 校验所有成员是否成功获取;如果没有,记录日志并用清晰错误快速失败
var missingMembers = new List<string>();
if (_combineMusicDataListField == null)
missingMembers.Add("_combineMusicDataList (Field, NonPublic Instance)");
if (_categoryNameListProp == null)
missingMembers.Add("CategoryNameList (Property, Public Instance)");
if (_setSortListMethod == null)
missingMembers.Add("SetSortList (Method, NonPublic Instance)");
if (_addRandomDataMethod == null)
missingMembers.Add("AddRandomData (Method, NonPublic Instance)");
if (missingMembers.Count > 0)
{
var message =
"[SongConstantSort] Failed to initialize reflection bindings for Process.MusicSelectProcess. " +
"Missing members: " + string.Join(", ", missingMembers) +
". The game may have been updated; SongConstantSort needs to be updated for this game version.";
Debug.LogError(message);
throw new MissingMemberException(message);
}
```
</issue_to_address>帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进今后的审查。
Original comment in English
Hey - I've found 2 issues, and left some high level feedback:
- There are a lot of hardcoded magic numbers around the new tab ID and bounds (e.g.,
6,7inSortTabPatches,NavigationPatches, and checks forCategorySortSetting), which makes the behavior brittle if the enum or tab count changes; consider centralizing these values in a single constant or helper based onDB.SortTabIDto avoid future breakage. - In
DoCategoryTabConstant, you mutatecd.msDetailData(settingmusicIdanddifficultyId) and then pass it into newCombineMusicSelectDatainstances; ifmsDetailDatais shared across categories or calls, this in-place mutation could cause subtle bugs, so it would be safer to clone or create a fresh detail object per entry instead of reusing and modifying the existing one. - The asset bundle loading in
ConstSpriteCache.GetSpritekeeps each bundle open only for the duration of the foreach but performs no null checks on_spritesafter the first load and swallows all exception types; tightening the exception handling and using ausing/try-finallypattern for bundles would make this more robust against partial failures or corrupted assets.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- There are a lot of hardcoded magic numbers around the new tab ID and bounds (e.g., `6`, `7` in `SortTabPatches`, `NavigationPatches`, and checks for `CategorySortSetting`), which makes the behavior brittle if the enum or tab count changes; consider centralizing these values in a single constant or helper based on `DB.SortTabID` to avoid future breakage.
- In `DoCategoryTabConstant`, you mutate `cd.msDetailData` (setting `musicId` and `difficultyId`) and then pass it into new `CombineMusicSelectData` instances; if `msDetailData` is shared across categories or calls, this in-place mutation could cause subtle bugs, so it would be safer to clone or create a fresh detail object per entry instead of reusing and modifying the existing one.
- The asset bundle loading in `ConstSpriteCache.GetSprite` keeps each bundle open only for the duration of the foreach but performs no null checks on `_sprites` after the first load and swallows all exception types; tightening the exception handling and using a `using`/`try-finally` pattern for bundles would make this more robust against partial failures or corrupted assets.
## Individual Comments
### Comment 1
<location path="AquaMai.Mods/GameSystem/SongConstantSort.cs" line_range="90" />
<code_context>
+ [HarmonyPatch]
+ public static class SortTabPatches
+ {
+ [HarmonyPatch(typeof(DB.SortTabIDEnum), "GetEnd", new System.Type[0])]
+ [HarmonyPrefix]
+ public static bool GetEnd_Static(ref int __result) { __result = 7; return false; }
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Hard-coded tab indices `6`/`7` tightly couple the patch to current enum layout.
Relying on `6`/`7` assumes the enum order and count never change; if `DB.SortTabID` is reordered or extended, tab labels and navigation could break in non-obvious ways. Prefer deriving these values from the enum (e.g., `Enum.GetValues` or a dedicated `SortTabID.Constant`) or centralizing them in a single constant so future enum changes only require updating one place.
Suggested implementation:
```csharp
[HarmonyPatch]
public static class SortTabPatches
{
/// <summary>
/// Maximum underlying value of DB.SortTabID. Keeps patches resilient to enum reordering/extensions.
/// </summary>
private static readonly int MaxSortTabIndex = GetMaxSortTabIndex();
private static int GetMaxSortTabIndex()
{
var values = (DB.SortTabID[])Enum.GetValues(typeof(DB.SortTabID));
var max = int.MinValue;
foreach (var value in values)
{
var intValue = (int)value;
if (intValue > max)
max = intValue;
}
return max;
}
```
```csharp
[HarmonyPatch(typeof(DB.SortTabIDEnum), "GetEnd", new System.Type[0])]
[HarmonyPrefix]
public static bool GetEnd_Static(ref int __result)
{
__result = MaxSortTabIndex + 1;
return false;
}
```
```csharp
[HarmonyPatch(typeof(DB.SortTabIDEnum), "GetEnd", new System.Type[] { typeof(DB.SortTabID) })]
[HarmonyPrefix]
public static bool GetEnd_Instance(ref int __result)
{
__result = MaxSortTabIndex + 1;
return false;
}
```
```csharp
[HarmonyPatch(typeof(DB.SortTabIDEnum), "IsValid")]
[HarmonyPrefix]
public static bool IsValid(DB.SortTabID self, ref bool __result)
{
__result = self >= DB.SortTabID.Genre && (int)self <= MaxSortTabIndex;
```
If `DB.SortTabID` contains values that are not intended to be user-visible tabs (e.g., internal or sentinel values), you may want to adjust `GetMaxSortTabIndex` to filter the enum values to only the relevant subset (for example, between `Genre` and a specific final tab), instead of blindly using the maximum enum value.
</issue_to_address>
### Comment 2
<location path="AquaMai.Mods/GameSystem/SongConstantSort.cs" line_range="281" />
<code_context>
+ return result;
+ }
+
+ private static void InitReflection()
+ {
+ if (_combineMusicDataListField == null)
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Reflection setup doesn’t handle missing members, which may break silently on game updates.
These reflection calls assume `_combineMusicDataList`, `CategoryNameList`, `SetSortList`, and `AddRandomData` always exist. If a future game update renames or removes any of them, the fields will stay `null` and trigger `NullReferenceException`s later, far from the root cause. Consider validating all reflection lookups once (e.g., in `OnBeforePatch` or `InitReflection`) and logging a clear error on failure to make version mismatches obvious and avoid hard-to-debug crashes.
Suggested implementation:
```csharp
using System.Reflection;
using UnityEngine;
```
```csharp
private static void InitReflection()
{
// Resolve all reflection targets eagerly so we can validate them together
var type = typeof(Process.MusicSelectProcess);
_combineMusicDataListField = type.GetField(
"_combineMusicDataList", BindingFlags.NonPublic | BindingFlags.Instance);
_categoryNameListProp = type.GetProperty(
"CategoryNameList", BindingFlags.Public | BindingFlags.Instance);
_setSortListMethod = type.GetMethod(
"SetSortList", BindingFlags.NonPublic | BindingFlags.Instance);
_addRandomDataMethod = type.GetMethod(
"AddRandomData", BindingFlags.NonPublic | BindingFlags.Instance);
// Validate that all members were found; if not, log and fail fast with a clear error
var missingMembers = new List<string>();
if (_combineMusicDataListField == null)
missingMembers.Add("_combineMusicDataList (Field, NonPublic Instance)");
if (_categoryNameListProp == null)
missingMembers.Add("CategoryNameList (Property, Public Instance)");
if (_setSortListMethod == null)
missingMembers.Add("SetSortList (Method, NonPublic Instance)");
if (_addRandomDataMethod == null)
missingMembers.Add("AddRandomData (Method, NonPublic Instance)");
if (missingMembers.Count > 0)
{
var message =
"[SongConstantSort] Failed to initialize reflection bindings for Process.MusicSelectProcess. " +
"Missing members: " + string.Join(", ", missingMembers) +
". The game may have been updated; SongConstantSort needs to be updated for this game version.";
Debug.LogError(message);
throw new MissingMemberException(message);
}
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Code Review
This pull request introduces a new mod, SongConstantSort, which adds a constant difficulty sorting tab to the music selection screen, and registers it in the configuration file. The review feedback highlights a critical bug where modifying cd.msDetailData directly mutates the shared reference, causing data corruption across other categories; it suggests cloning the object first. Additionally, a performance bottleneck was identified due to retrieving the NotesListManager singleton inside the innermost loop, which should be hoisted outside the loops.
There was a problem hiding this comment.
5 issues found across 2 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="AquaMai.Mods/GameSystem/SongConstantSort.cs">
<violation number="1" location="AquaMai.Mods/GameSystem/SongConstantSort.cs:92">
P2: The magic numbers `6` and `7` are scattered across multiple patch classes (`SortTabPatches`, `NavigationPatches`, `UIPatches`) without a single source of truth. If `DB.SortTabID` is extended or reordered, every occurrence must be updated manually. Consider deriving these from the enum (e.g., `Enum.GetValues`) or centralizing them into named constants.</violation>
<violation number="2" location="AquaMai.Mods/GameSystem/SongConstantSort.cs:135">
P1: Unchecked reflection results can cause hard runtime crashes when private member names or signatures change. Add null checks after `GetField`/`GetMethod`/`GetProperty` calls and return `true` (fallback to original behavior) when reflection fails.</violation>
<violation number="3" location="AquaMai.Mods/GameSystem/SongConstantSort.cs:222">
P2: Repeated dictionary retrieval inside the innermost nested loop causes unnecessary overhead during category sorting</violation>
<violation number="4" location="AquaMai.Mods/GameSystem/SongConstantSort.cs:281">
P2: `InitReflection` does not validate that any of the reflection lookups succeeded. If a game update renames or removes any target member, these fields stay `null` and later cause a `NullReferenceException` far from the root cause. Add null-checks after the lookups and log/throw a descriptive error to make version mismatches immediately obvious.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| int musicId = cd.GetID(sk); | ||
| if (musicId >= 100000) continue; | ||
|
|
||
| var notesDict = Singleton<NotesListManager>.Instance.GetNotesList(); |
There was a problem hiding this comment.
P2: Repeated dictionary retrieval inside the innermost nested loop causes unnecessary overhead during category sorting
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At AquaMai.Mods/GameSystem/SongConstantSort.cs, line 222:
<comment>Repeated dictionary retrieval inside the innermost nested loop causes unnecessary overhead during category sorting</comment>
<file context>
@@ -0,0 +1,381 @@
+ int musicId = cd.GetID(sk);
+ if (musicId >= 100000) continue;
+
+ var notesDict = Singleton<NotesListManager>.Instance.GetNotesList();
+ if (!notesDict.TryGetValue(musicId, out var nw)) continue;
+
</file context>
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="AquaMai.Mods/GameSystem/SongConstantSort.cs">
<violation number="1" location="AquaMai.Mods/GameSystem/SongConstantSort.cs:66">
P1: AssetBundle loaded from stream is never unloaded, causing a persistent native memory leak.</violation>
<violation number="2" location="AquaMai.Mods/GameSystem/SongConstantSort.cs:222">
P2: Repeated dictionary retrieval inside the innermost nested loop causes unnecessary overhead during category sorting</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="AquaMai.Mods/GameSystem/SongConstantSort.cs">
<violation number="1" location="AquaMai.Mods/GameSystem/SongConstantSort.cs:222">
P2: Repeated dictionary retrieval inside the innermost nested loop causes unnecessary overhead during category sorting</violation>
<violation number="2" location="AquaMai.Mods/GameSystem/SongConstantSort.cs:289">
P1: Unsafe nested list indexing outside try/catch in IsForceMusicBack prefix creates a crash path during back-navigation state transitions.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| var musicData = __instance.CombineMusicDataList[__instance.CurrentCategorySelect] | ||
| [__instance.CurrentMusicSelect].musicSelectData[(int)__instance.ScoreType]; | ||
| int musicId = musicData.MusicData.name.id; | ||
|
|
||
| // 用 _levelCategoryPositionList 跳转到新难度下的位置 | ||
| try | ||
| { | ||
| var pos = __instance.GetLevelToListPositoin(musicId, newDiff); | ||
| __instance.CurrentCategorySelect = pos.Category; | ||
| __instance.CurrentMusicSelect = pos.Index; | ||
| __instance.CurrentDifficulty[__instance.SortDecidePlayer] = (Manager.MusicDifficultyID)newDiff; | ||
| } |
There was a problem hiding this comment.
P1: Unsafe nested list indexing outside try/catch in IsForceMusicBack prefix creates a crash path during back-navigation state transitions.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At AquaMai.Mods/GameSystem/SongConstantSort.cs, line 289:
<comment>Unsafe nested list indexing outside try/catch in IsForceMusicBack prefix creates a crash path during back-navigation state transitions.</comment>
<file context>
@@ -154,46 +183,127 @@ public static bool SubSort_Prefix(Process.MusicSelectProcess __instance, DB.Sort
+ if (newDiff < 0 || newDiff > 4) return;
+
+ // 获取当前歌曲 ID
+ var musicData = __instance.CombineMusicDataList[__instance.CurrentCategorySelect]
+ [__instance.CurrentMusicSelect].musicSelectData[(int)__instance.ScoreType];
+ int musicId = musicData.MusicData.name.id;
</file context>
| var musicData = __instance.CombineMusicDataList[__instance.CurrentCategorySelect] | |
| [__instance.CurrentMusicSelect].musicSelectData[(int)__instance.ScoreType]; | |
| int musicId = musicData.MusicData.name.id; | |
| // 用 _levelCategoryPositionList 跳转到新难度下的位置 | |
| try | |
| { | |
| var pos = __instance.GetLevelToListPositoin(musicId, newDiff); | |
| __instance.CurrentCategorySelect = pos.Category; | |
| __instance.CurrentMusicSelect = pos.Index; | |
| __instance.CurrentDifficulty[__instance.SortDecidePlayer] = (Manager.MusicDifficultyID)newDiff; | |
| } | |
| try | |
| { | |
| var musicData = __instance.CombineMusicDataList[__instance.CurrentCategorySelect] | |
| [__instance.CurrentMusicSelect].musicSelectData[(int)__instance.ScoreType]; | |
| int musicId = musicData.MusicData.name.id; | |
| var pos = __instance.GetLevelToListPositoin(musicId, newDiff); | |
| __instance.CurrentCategorySelect = pos.Category; | |
| __instance.CurrentMusicSelect = pos.Index; | |
| __instance.CurrentDifficulty[__instance.SortDecidePlayer] = (Manager.MusicDifficultyID)newDiff; | |
| } | |
| catch { } |
具体而言,当AddSort尝试切换超出范围时,官方游戏是会Clamp固定在最边上、不会循环回开头;但这个patch下,会使得行为变成循环。 然后,修复上述问题、改为和原版游戏行为一致后,会发现:`SubSort_Prefix`函数实际上没有作用、patch后的版本代码和原版游戏完全相同。因此直接删除即可。
AddSort中所引用的变量,在小于1.65的版本中为`_categorySortSetting`,没有`_before`前缀。
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="AquaMai.Mods/GameSystem/SongConstantSort.cs">
<violation number="1" location="AquaMai.Mods/GameSystem/SongConstantSort.cs:222">
P2: Repeated dictionary retrieval inside the innermost nested loop causes unnecessary overhead during category sorting</violation>
<violation number="2" location="AquaMai.Mods/GameSystem/SongConstantSort.cs:289">
P1: Unsafe nested list indexing outside try/catch in IsForceMusicBack prefix creates a crash path during back-navigation state transitions.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
在原先的选曲分类界面添加了定数分类这一档,位置在全曲分类后,用来排序各个详细定数包含的歌曲。可能会方便选对应定数的歌曲。
Summary by Sourcery
在乐曲选择界面中新增基于常数难度的排序标签页,并将其集成进现有的排序和 UI 流程中。
新功能:
增强内容:
Original summary in English
Summary by Sourcery
Add a new constant-based difficulty sort tab to the music selection screen and integrate it into existing sorting and UI flows.
New Features:
Enhancements: