-
Notifications
You must be signed in to change notification settings - Fork 58
feat: AmDaemonError拦截器 #130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| using System.Collections.Generic; | ||
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (bug_risk): 把配置项定义为 static readonly 字段,可能会阻止配置系统在运行时更新这些值。 由于这些字段是 Original comment in Englishsuggestion (bug_risk): Using static readonly fields for config entries may prevent the configuration system from updating them at runtime. Because these fields are |
||
| "不向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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (bug_risk): 在这里使用 ThrowIfInvalid 比较脆弱,而且与其他 transpiler 使用的更防御性的模式不一致。 一旦 IL 模式发生变化,这里就会抛异常,而其他 transpiler 则是优雅地回退到原始指令。由于不同版本/构建之间的 IL 可能会不同,这会把本来只是模式不匹配的问题变成一次崩溃。除非你在这里就是需要强制性的硬失败,否则建议让它的行为与其他 transpiler 保持一致(例如:直接返回原始指令,或者记录日志)。 Original comment in Englishissue (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); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| return matcher.Instructions(); | ||
| } | ||
|
|
||
| [HarmonyPrefix] | ||
|
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
|
||
|
|
||
| } | ||
There was a problem hiding this comment.
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