Skip to content
Open
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
159 changes: 159 additions & 0 deletions AquaMai.Mods/Tweaks/BlockAmDaemonError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
using System.Collections.Generic;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Nopping out AMDaemon.Error.Set(int) without also removing/consuming its argument can leave the evaluation stack unbalanced and produce invalid IL.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At AquaMai.Mods/Tweaks/BlockAmDaemonError.cs, line 107:

<comment>Nopping out `AMDaemon.Error.Set(int)` without also removing/consuming its argument can leave the evaluation stack unbalanced and produce invalid IL.</comment>

<file context>
@@ -0,0 +1,155 @@
+        );
+        if (!matcher.IsValid)
+            return instructions;
+        matcher.SetOpcodeAndAdvance(OpCodes.Nop);
+
+        return matcher.Instructions();
</file context>

using System.Diagnostics;
using System.Reflection.Emit;
using AquaMai.Config.Attributes;
using AquaMai.Core.Attributes;
using HarmonyLib;
using IO;
using Manager;
using PartyLink;
using Process;

namespace AquaMai.Mods.Tweaks;
[ConfigSection(
"拦截AMDaemon错误",
zh: """
阻止游戏在不使用Dummy时向AMDaemon发送错误状态,避免AMDaemon锁点数以及忽略Coin Signal等问题
""",
en: """
Prevents the game from sending error states to AMDaemon when Dummy is not in use, thereby avoiding issues such as AMDaemon locking points and ignoring Coin Signals.
""")]

public class BlockAmDaemonError
{
[ConfigEntry(
"拦截触摸错误",
"Do not send touch panel errors to AMDaemon.",
"不向AMDaemon发送触摸错误"
)]
public static readonly bool TouchPanel = false;

[ConfigEntry(
"拦截相机管理器错误",
"Do not send camera manager errors to AMDaemon.",
Comment on lines +24 to +33

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): 把配置项定义为 static readonly 字段,可能会阻止配置系统在运行时更新这些值。

由于这些字段是 static readonly,它们的值会在类型初始化时就被固定下来。如果 ConfigEntry 是在加载时通过反射来更新字段,那么这些标志位将始终保持为 false,相关 tweak 将永远不会被启用。请确认配置框架是如何应用配置值的;如果它是直接修改这些字段的值,那么请去掉 readonly,以便它们能在运行时被更新。

Original comment in English

suggestion (bug_risk): Using static readonly fields for config entries may prevent the configuration system from updating them at runtime.

Because these fields are static readonly, their values are fixed at type initialization. If ConfigEntry updates fields via reflection at load time, these flags will remain false and the tweaks will never enable. Please confirm how the config framework applies values; if it mutates these fields directly, drop readonly so they can be updated at runtime.

"不向AMDaemon发送相机管理器错误"
)]
public static readonly bool CameraManager = false;

[ConfigEntry(
"拦截QrCamera错误",
"No error reported from the QR camera to AMDaemon.",
"不向AMDaemon发送二维码相机的错误"
)]
public static readonly bool QrCamera = false;

[ConfigEntry(
"拦截PhotoCamera错误",
"Do not send photo camera errors to AMDaemon.",
"不向AMDaemon发送照片相机错误"
)]
public static readonly bool PhotoCamera = false;

[ConfigEntry(
Comment on lines +39 to +52
"拦截基准机错误",
"Do not send error 3201 related to the benchmark machine to AMDaemon.",
"不向AMDaemon发送基准机相关的错误(3201)"
)]
public static readonly bool Error3201 = false;

[EnableIf(nameof(TouchPanel))]
[HarmonyTranspiler]
[HarmonyPatch(typeof(StartupProcess), "OnUpdate")]
public static IEnumerable<CodeInstruction> TouchPanelTranspiler(IEnumerable<CodeInstruction> instructions)
{
var codeMatcher = new CodeMatcher(instructions);

codeMatcher.MatchStartForward(
new CodeMatch(i => i.Calls(AccessTools.Method(
typeof(NewTouchPanel),
Comment on lines +59 to +68

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): 在这里使用 ThrowIfInvalid 比较脆弱,而且与其他 transpiler 使用的更防御性的模式不一致。

一旦 IL 模式发生变化,这里就会抛异常,而其他 transpiler 则是优雅地回退到原始指令。由于不同版本/构建之间的 IL 可能会不同,这会把本来只是模式不匹配的问题变成一次崩溃。除非你在这里就是需要强制性的硬失败,否则建议让它的行为与其他 transpiler 保持一致(例如:直接返回原始指令,或者记录日志)。

Original comment in English

issue (bug_risk): Using ThrowIfInvalid here is brittle and inconsistent with the more defensive pattern used in the other transpilers.

This will throw if the IL pattern changes, while the other transpilers gracefully fall back to the original instructions. Since IL can differ between versions/builds, that turns a benign mismatch into a crash. Please consider matching the other transpilers’ behavior (e.g., return original instructions or log) unless a hard failure is intentionally required here.

nameof(NewTouchPanel.GetLastErrorPs)))),
new CodeMatch(i => i.Calls(AccessTools.Method(
typeof(AMDaemon.Error), "Set", new[] { typeof(int) })))
);
if (!codeMatcher.IsValid)
return instructions;

codeMatcher.ThrowIfInvalid("Could not find call to TouchPanel AMDaemon.Error.Set")
.SetOpcodeAndAdvance(OpCodes.Nop)
.SetOpcodeAndAdvance(OpCodes.Nop);

return codeMatcher.Instructions();
}

[EnableIf(nameof(CameraManager))]
[HarmonyTranspiler]
[HarmonyPatch(typeof(StartupProcess), "OnUpdate")]
public static IEnumerable<CodeInstruction> CameraManagerTranspiler(IEnumerable<CodeInstruction> instructions)
{
var errorSet = AccessTools.Method(
typeof(AMDaemon.Error),
"Set",
new[] { typeof(int) });

var startMethod = AccessTools.Method(
typeof(Stopwatch),
nameof(Stopwatch.Start));

var matcher = new CodeMatcher(instructions);

matcher.MatchStartForward(
new CodeMatch(i => i.Calls(startMethod))
);
if (!matcher.IsValid)
return instructions;
matcher.Advance(1);
matcher.MatchStartForward(
new CodeMatch(i => i.Calls(errorSet))
);
if (!matcher.IsValid)
return instructions;
matcher.SetOpcodeAndAdvance(OpCodes.Nop);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Replacing a call to a method that takes arguments (like AMDaemon.Error.Set(int)) with a Nop instruction will leave the arguments on the evaluation stack. This causes a stack imbalance which typically leads to an InvalidProgramException at runtime. Since you are only replacing the call instruction itself, you should use OpCodes.Pop to consume the integer argument from the stack.

        matcher.SetOpcodeAndAdvance(OpCodes.Pop);


return matcher.Instructions();
Comment on lines +105 to +112
}

[EnableIf(nameof(Error3201))]
[HarmonyTranspiler]
[HarmonyPatch(typeof(StartupProcess), "OnUpdate")]
[HarmonyPatch(typeof(AdvertiseProcess), "UpdateSetting")]
public static IEnumerable<CodeInstruction> Error3201Transpiler(IEnumerable<CodeInstruction> instructions)
{
var errorSet = AccessTools.Method(
typeof(AMDaemon.Error),
"Set",
new[] { typeof(int) });

var startMethod = AccessTools.Method(
typeof(Setting.IManager),
nameof(Setting.IManager.terminate));

var matcher = new CodeMatcher(instructions);

matcher.MatchStartForward(
new CodeMatch(i => i.Calls(startMethod))
);
if (!matcher.IsValid)
return instructions;
matcher.Advance(1);
matcher.MatchStartForward(
new CodeMatch(i => i.Calls(errorSet))
);
if (!matcher.IsValid)
return instructions;
matcher.SetOpcodeAndAdvance(OpCodes.Nop);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Similar to the CameraManagerTranspiler, replacing the call to AMDaemon.Error.Set(int) with Nop leaves the argument on the stack. Use OpCodes.Pop instead to maintain stack balance.

        matcher.SetOpcodeAndAdvance(OpCodes.Pop);


return matcher.Instructions();
}

[HarmonyPrefix]
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
[HarmonyPatch(typeof(AMDaemon.Error), "Set", new[] { typeof(int) })]
public static bool CameraPrefix(int __0)
{
if (__0 == 3101 || QrCamera)
return false;
if (__0 == 3102 || PhotoCamera)
return false;
return true;
}
Comment on lines +148 to +157

}
2 changes: 2 additions & 0 deletions AquaMai/configSort.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
- Utils.ShowErrorLog
- GameSettings.ForceAsServer
- GameSystem.RemoveEncryption
- Tweaks.BlockAmDaemonError
Comment on lines 140 to +143

社区功能:
- GameSystem.VolumeSync
Expand Down Expand Up @@ -177,3 +178,4 @@
- Fancy.ResourcesOverride
- GameSystem.OldCabLightBoardSupport
- GameSystem.VirtualCoin
- Tweaks.BlockAmDaemonError
Loading