Skip to content

fix: Maimoller 手台在游戏加载阶段/卡顿不更新灯光阶段 手台回落到固件待机灯 - #155

Merged
clansty merged 2 commits into
MuNET-OSS:mainfrom
ZCM-X:maimoller-keepalive
Oct 7, 2026
Merged

clansty merged 2 commits into
MuNET-OSS:mainfrom
ZCM-X:maimoller-keepalive

Conversation

@ZCM-X

@ZCM-X ZCM-X commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

进游戏之前 PreGameMainUpdate 被 isInitialize 挡着,主循环一发 LED 报告都不发,
手台的 LED 通道静默久了就切回固件自带的待机灯,一直亮到加载结束才恢复。
游戏卡住或者掉帧的时候主循环不跑,一样会断包,现象相同。

加了个后台保活线程,25ms 检查一次,距上次发包超过 100ms 就补发一发当前状态,
这两种情况都能盖住。主线程那边用 Monitor.TryEnter,抢不到锁直接跳过,不阻塞游戏线程。
(一开始用的是阻塞 lock,每帧都会撞上后台那次 HID 写,帧耗时能到 17ms,所以改成不等待的)

View guided diff

Sourcery 摘要

在游戏循环未更新期间保持 Maimoller LED 输出处于活动状态,同时不阻塞帧执行。

新功能:

  • 在游戏初始化、加载停顿和帧率下降期间,通过定期刷新当前 LED 状态,保持 Maimoller 控制器灯光处于活动状态。

错误修复:

  • 当主循环暂时未发送输出报告时,防止控制器恢复为固件待机灯光。

增强功能:

  • 当输出锁不可用时跳过并发写入,避免游戏线程因后台 HID 输出而阻塞。
Original summary in English

Sourcery 摘要

在游戏循环更新停滞期间保持 Maimoller LED 输出,且不会阻塞帧执行。

新功能:

  • 在游戏初始化、加载暂停、掉帧以及其他没有常规更新的期间,保持 Maimoller 控制器的 LED 输出处于活动状态。
  • 设备重新连接后恢复最新的 LED 状态。

错误修复:

  • 防止游戏循环暂时停止发送输出报告时,Maimoller 控制器恢复为固件待机灯光。

增强:

  • 将 HID 输出移至专用的后台写入器,以便及时发送状态变化,并定期刷新空闲输出,同时不会阻塞游戏线程。
Original summary in English

Summary by Sourcery

Keep Maimoller LED output alive during stalled game-loop updates without blocking frame execution.

New Features:

  • Keep Maimoller controller LED output active during game initialization, loading pauses, frame drops, and other periods without regular updates.
  • Restore the latest LED state after device reconnection.

Bug Fixes:

  • Prevent Maimoller controllers from reverting to firmware standby lighting when the game loop temporarily stops sending output reports.

Enhancements:

  • Move HID output to a dedicated background writer that sends state changes promptly and periodically refreshes idle output without blocking the game thread.

@sourcery-ai

sourcery-ai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

审查者指南

引入后台 LED 输出保活机制,在静默 100 毫秒后重新发送当前状态,防止 Maimoller 设备在初始化或帧停滞期间恢复为固件待机灯效,同时使用非阻塞锁获取,避免增加游戏线程的延迟。

Maimoller LED 输出保活时序图

sequenceDiagram
    participant GameThread
    participant MaimollerDeviceNative
    participant KeepAliveThread
    participant HIDDevice

    GameThread->>MaimollerDeviceNative: Update()
    MaimollerDeviceNative->>MaimollerDeviceNative: Monitor.TryEnter(_outputLock)
    alt lock acquired
        MaimollerDeviceNative->>HIDDevice: HidRawIO.Write(_reportBuffer)
    else lock unavailable
        MaimollerDeviceNative-->>GameThread: Skip output without blocking
    end

    loop every 25ms
        KeepAliveThread->>MaimollerDeviceNative: KeepAliveThread()
        alt silence exceeds 100ms
            MaimollerDeviceNative->>MaimollerDeviceNative: WriteOutputReport(force: true)
            MaimollerDeviceNative->>HIDDevice: HidRawIO.Write(_reportBuffer)
        end
    end
Loading

文件级变更

变更 详细信息 文件
添加后台保活循环:当设备近期未收到报告时,定期强制发送当前 LED 输出报告。
  • 设备打开时启动后台线程,并每 25 毫秒检查一次。
  • 自上次输出报告起经过 100 毫秒后强制重新发送,以覆盖初始化、加载停滞和丢帧情况。
AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs
在保持游戏线程路径非阻塞的同时,串行化 HID 输出写入。
  • 使用专用监视器锁保护报告序列化和 HID I/O。
  • 使用 Monitor.TryEnter,使常规更新在保活线程写入时跳过,而不是等待。
  • 允许强制保活写入绕过未改变报告的抑制逻辑。
AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs

可能关联的问题

  • #未知:PR 通过后台线程每 100 毫秒补发 LED 状态,直接解决问题描述中的 LED 信号中断和默认灯效回落。

提示和命令

与 Sourcery 交互

  • 触发新的审查: 在拉取请求中评论 @sourcery-ai review。
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub issue: 回复审查评论,请 Sourcery 根据该评论创建 issue。你也可以使用 @sourcery-ai issue 回复审查评论,以根据该评论创建 issue。
  • 生成拉取请求标题: 在拉取请求标题的任意位置写入 @sourcery-ai,即可随时生成标题。你也可以在拉取请求中评论 @sourcery-ai title,以随时(重新)生成标题。
  • 生成拉取请求摘要: 在拉取请求正文的任意位置写入 @sourcery-ai summary,即可在你指定的位置随时生成 PR 摘要。你也可以在拉取请求中评论 @sourcery-ai summary,以随时(重新)生成摘要。
  • 生成审查者指南: 在拉取请求中评论 @sourcery-ai guide,即可随时(重新)生成审查者指南。
  • 解决所有 Sourcery 评论: 在拉取请求中评论 @sourcery-ai resolve,以解决所有 Sourcery 评论。如果你已经处理完所有评论且不想再看到它们,这会很有用。
  • 忽略所有 Sourcery 审查: 在拉取请求中评论 @sourcery-ai dismiss,以忽略所有现有的 Sourcery 审查。如果你想从新的审查开始,这尤其有用——别忘了评论 @sourcery-ai review 以触发新的审查!

自定义你的使用体验

访问你的控制面板以:

  • 启用或禁用审查功能,例如 Sourcery 生成的拉取请求摘要、审查者指南等。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查说明。
  • 调整其他审查设置。

获取帮助

Original review guide in English

Reviewer's Guide

Introduces a background LED output keep-alive that resends the current state after 100 ms of silence, preventing Maimoller devices from reverting to firmware standby lighting during initialization or stalled frames, while using non-blocking lock acquisition to avoid adding latency to the game thread.

Sequence diagram for Maimoller LED output keep-alive

sequenceDiagram
    participant GameThread
    participant MaimollerDeviceNative
    participant KeepAliveThread
    participant HIDDevice

    GameThread->>MaimollerDeviceNative: Update()
    MaimollerDeviceNative->>MaimollerDeviceNative: Monitor.TryEnter(_outputLock)
    alt lock acquired
        MaimollerDeviceNative->>HIDDevice: HidRawIO.Write(_reportBuffer)
    else lock unavailable
        MaimollerDeviceNative-->>GameThread: Skip output without blocking
    end

    loop every 25ms
        KeepAliveThread->>MaimollerDeviceNative: KeepAliveThread()
        alt silence exceeds 100ms
            MaimollerDeviceNative->>MaimollerDeviceNative: WriteOutputReport(force: true)
            MaimollerDeviceNative->>HIDDevice: HidRawIO.Write(_reportBuffer)
        end
    end
Loading

File-Level Changes

Change Details Files
Add a background keep-alive loop that periodically forces the current LED output report when the device has not received a report recently.
  • Start a background thread when the device is opened and check every 25 ms.
  • Force a resend after 100 ms since the previous output report to cover initialization, loading stalls, and frame drops.
AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs
Serialize HID output writes while keeping the game-thread path non-blocking.
  • Protect report serialization and HID I/O with a dedicated monitor lock.
  • Use Monitor.TryEnter so regular updates skip rather than wait when the keep-alive thread is writing.
  • Allow forced keep-alive writes to bypass the unchanged-report suppression logic.
AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs

Possibly linked issues

  • #未知: PR通过后台线程每100毫秒补发LED状态,直接解决问题描述中的LED信号中断和默认灯效回落。

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

您好——我发现了 1 个问题

给 AI 代理的提示
请处理此次代码审查中的评论:

## 单条评论

### 评论 1
<location path="AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs" line_range="41" />
<code_context>
     private readonly InputLatch _inputLatch = new();
     private readonly MaimollerOutputReport _output = new();
     private readonly MaimollerLedManager _ledManager;
+    private readonly object _outputLock = new();


</code_context>
<issue_to_address>
**发送了混合的 LED 状态**

当游戏线程中的 LED 更新与报告序列化重叠时,`MaimollerLedManager` 的 setter 会在未使用 `_outputLock` 的情况下修改 `_output`,而 `KeepAliveThread` 同时对其进行序列化,因此报告会混合新旧 LED 值,导致控制器短暂显示错误的颜色或亮度。

请使用序列化期间所用的同一个锁来保护 LED 的修改,或者从一致的快照进行序列化。

此外,还涉及 `AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs:68`、`AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs:206`、`AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs:214` 和 `AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs:230`。
</issue_to_address>

Sourcery 评估

等待批准。 请先处理 1 个发现的问题。

阻塞性发现:AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs:41


Sourcery 对开源项目免费——如果您喜欢我们的审查结果,请考虑分享它们 ✨
Original comment in English

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs" line_range="41" />
<code_context>
     private readonly InputLatch _inputLatch = new();
     private readonly MaimollerOutputReport _output = new();
     private readonly MaimollerLedManager _ledManager;
+    private readonly object _outputLock = new();


</code_context>
<issue_to_address>
**Mixed LED states are sent**

When a game-thread LED update overlaps report serialization, `MaimollerLedManager` setters mutate `_output` without `_outputLock`, while `KeepAliveThread` serializes it, so the report combines old and new LED values and the controller briefly shows incorrect colors or brightness.

Protect LED mutations with the same lock used during serialization, or serialize from a consistent snapshot.

Also at `AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs:68`, `AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs:206`, `AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs:214`, `AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs:230`.
</issue_to_address>

Sourcery assessment

Approval pending. 1 finding to address first.

Blocking findings: AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs:41


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs Outdated
@clansty

clansty commented Oct 7, 2026

Copy link
Copy Markdown
Member

我觉得有没有必要,就是主线程就不用发了,发送灯光就直接放在那个单开的线程里面好了,单开的那个线程,看灯光0.5秒没发过或者变化了就发?

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 1 file

Reply to a comment to ask cubic a question or push back. It learns from your replies.

View guided diff | Re-trigger cubic

Comment thread AquaMai.Mods/GameSystem/MaimollerIO/Libs/MaimollerDeviceNative.cs Outdated
@ZCM-X

ZCM-X commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

那主线程就不发了。改成主线程只管序列化 + 标脏,后台线程统一发包,脏了或者距上次发超过 500ms 就发。
我一块把 _output 被两个线程同时读的问题也解决了

- 移除主线程直接写 HID 的逻辑,Update 只发布 64 字节状态快照

- 新增输出线程,Monitor.Wait 等待状态变化或保活超时,是唯一的 HID 写入方

- HID 写入不再持有锁,避免主线程被后台写入阻塞

- 保活间隔改为实测的 500ms,重连后立即补发一次

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sourcery assessment

Approved.

@clansty
clansty merged commit 6aaf11c into MuNET-OSS:main Oct 7, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants