Skip to content

迁移至CsTrees - #3423

Merged
huiyadanli merged 3 commits into
babalae:mainfrom
FishmanTheMurloc:refactor/behavior-tree
Aug 10, 2026
Merged

迁移至CsTrees#3423
huiyadanli merged 3 commits into
babalae:mainfrom
FishmanTheMurloc:refactor/behavior-tree

Conversation

@FishmanTheMurloc

Copy link
Copy Markdown
Contributor

旧框架有许多不满意的地方

  • 没有内置黑板
  • 没有Tick钩子,只能自己糊个行为基类
  • 预置节点不够用

且无法满足未来的需要

  • 需要将行为树进行渲染
  • 需要异步支持

但是它也有可圈可点的地方(流式构建),于是以它和py_trees为素材,要上了、CsTrees!成为Agent编程的Key吧!

FishmanTheMurloc and others added 3 commits August 10, 2026 02:09
- 迁移AutoFishing
- 迁移到完整的黑板系统
- 截屏本身改为行为而非上下文
- 关键帧截图保存功能改用Visitor实现
- 迁移相应单元测试
@greptile-apps

greptile-apps Bot commented Aug 9, 2026

Copy link
Copy Markdown

Greptile Summary

此 PR 将自动钓鱼行为树从 BehaviourTree 迁移到 CsTrees,并引入共享黑板、统一截图节点和异步 Tick。

  • 重构独立自动钓鱼任务与实时触发器的树结构
  • 将各钓鱼行为迁移到 CsTrees Behaviour 和 BlackboardKey
  • 增加 ScreenshotVisitor,并同步迁移单元测试
  • 实时触发器当前未串行等待异步 Tick,可能并发操作同一行为树

Confidence Score: 4/5

合并前应修复实时自动钓鱼触发器中未等待 TickOnce 的问题,避免同一行为树并发执行和异步异常丢失。

AutoFishingTrigger 会在连续截图回调中启动多个未等待的 TickOnce,它们复用同一个行为树和黑板,可能重叠修改节点状态并重复发送游戏输入。

Files Needing Attention: BetterGenshinImpact/GameTask/AutoFishing/AutoFishingTrigger.cs

Important Files Changed

Filename Overview
BetterGenshinImpact/GameTask/AutoFishing/AutoFishingTrigger.cs 实时钓鱼树已迁移到 CsTrees,但同步 OnCapture 丢弃异步 TickOnce,允许同一树发生重叠 Tick。
BetterGenshinImpact/GameTask/AutoFishing/AutoFishingTask.cs 独立任务改为异步 Tick,并通过 CsTrees 黑板和组合节点重建完整钓鱼流程。
BetterGenshinImpact/GameTask/AutoFishing/Behaviours.PartII.cs 新增超时、退出、流程检查和截图等 CsTrees 行为节点,截图资源由 TakeScreenshot 跨 Tick 轮换释放。
BetterGenshinImpact/GameTask/AutoFishing/Blackboard.cs 原有专用黑板状态迁移为 CsTrees BlackboardKey 定义及访问方式。
BetterGenshinImpact/GameTask/AutoFishing/ScreenshotVisitor.cs 新增基于节点终止事件保存关键帧的访问器。
BetterGenshinImpact/BetterGenshinImpact.csproj 移除 BehaviourTree 依赖并引入 CsTrees 1.0.1。

Sequence Diagram

sequenceDiagram
    participant D as Capture Dispatcher
    participant T as AutoFishingTrigger
    participant B as BehaviourTreeLaTiao
    D->>T: OnCapture(frame N)
    T->>B: TickOnce()
    Note over T,B: Task 未等待,OnCapture 立即返回
    D->>T: OnCapture(frame N+1)
    T->>B: TickOnce()
    Note over B: 两次 Tick 可能重叠并共享节点状态与 Blackboard
Loading
Prompt To Fix All With AI
### Issue 1
BetterGenshinImpact/GameTask/AutoFishing/AutoFishingTrigger.cs:100
**异步 Tick 未被串行化**`IsExclusive` 为 true 且连续截图回调间隔超过 67ms 时,`OnCapture` 会丢弃 `TickOnce()` 返回的 `Task`,使下一次回调能在前一次 Tick 完成前再次操作同一个行为树和 Blackboard,导致节点状态并发修改、重复发送游戏输入,并使异步异常无法沿调用链处理。

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "清理 BehaviourTree" | Re-trigger Greptile

}
else
{
BehaviourTreeLaTiao.TickOnce();

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 异步 Tick 未被串行化

IsExclusive 为 true 且连续截图回调间隔超过 67ms 时,OnCapture 会丢弃 TickOnce() 返回的 Task,使下一次回调能在前一次 Tick 完成前再次操作同一个行为树和 Blackboard,导致节点状态并发修改、重复发送游戏输入,并使异步异常无法沿调用链处理。

Context Used: AGENTS.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: BetterGenshinImpact/GameTask/AutoFishing/AutoFishingTrigger.cs
Line: 100

Comment:
**异步 Tick 未被串行化**`IsExclusive` 为 true 且连续截图回调间隔超过 67ms 时,`OnCapture` 会丢弃 `TickOnce()` 返回的 `Task`,使下一次回调能在前一次 Tick 完成前再次操作同一个行为树和 Blackboard,导致节点状态并发修改、重复发送游戏输入,并使异步异常无法沿调用链处理。

**Context Used:** AGENTS.md ([source](https://github.com/babalae/better-genshin-impact/blob/main/AGENTS.md))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 681f205317

ℹ️ 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".

}
else
{
BehaviourTreeLaTiao.TickOnce();

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 Badge 等待 TickOnce 完成避免并发执行

严重程度:P1;问题位置:AutoFishingTrigger.OnCapture 的独占分支;问题原因:TickOnce() 在迁移后的 CsTrees 用法中返回 Task<Status>,这里直接丢弃返回值,导致每 67ms 的截图回调都可能在上一轮行为树还没完成时再次启动同一棵 BehaviourTreeLaTiao。可能造成的影响:半自动钓鱼会在同一个 blackboard、截图和游戏输入上并发 tick,出现重复提竿/鼠标按键、状态互相覆盖以及未观察异常。推荐修复方案:在 OnCapture 中串行等待该 tick 完成,或用正在运行的 _tickTask/锁做重入保护并观察异常,保证同一棵树不会并发执行。

Useful? React with 👍 / 👎.

.FindFishTimeout("找到鱼", 20, _logger)
.End()
.EnterFishingMode("进入钓鱼模式", _logger, input, session, prototypes, cultureInfo: param.GameCultureInfo, stringLocalizer: param.StringLocalizer)
.SuccessIsRunning(@"\")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 保证终止分支继续退出钓鱼模式

严重程度:P2;问题位置:全自动钓鱼循环装饰器;问题原因:这里用 SuccessIsRunning 代替原来的“直到失败”循环后,FindFishTimeout 设置 AbortBubbleAbortCheck 返回 Failure 的场景会把失败冒泡到外层 Parallel,外层 Sequence 因此在到达 .QuitFishingMode(...) 之前结束。可能造成的影响:进入钓鱼界面后鱼群耗尽、找鱼超时或主动终止时,任务会记录结束但不再按 ESC/确认退出,玩家会被留在钓鱼界面。推荐修复方案:使用 CsTrees 中等价于 UntilFailed 且失败时让父序列继续的装饰器,或在该层把终止失败转换为成功后再执行 QuitFishingMode

Useful? React with 👍 / 👎.


async Task tickARound()
{
blackboard.Clear();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 不要清除一次性注册的 Sleep 键

严重程度:P2;问题位置:tickARound() 每轮开始清空黑板;问题原因:Sleep 只在树开头的 OneShot(SetSleep) 中写入一次,但第二次进入 tickARound()(例如昼夜策略会依次跑 7 点和 19 点)时 blackboard.Clear() 会把这个键删掉,而 OneShot 不会再次注册。可能造成的影响:第二轮开始后 MoveViewpointDownTurnAroundChooseBait 等节点调用 Sleep.Get() 时会因为黑板键缺失而中断任务。推荐修复方案:不要全量清空黑板,改为只重置钓鱼状态键,或在每次 Clear() 后重新写入 Sleep/不要把 SetSleep 包在一次性节点中。

Useful? React with 👍 / 👎.

selectedBaitAccess.Set(BaitType.FakeFlyBait);
var fishpondAccess = blackboard.GrantWrite<Fishpond>(null!, "Fishpond");

var sut = new ThrowRod("-", new FakeLogger(), new FakeInputSimulator(), Predictor, blackboard, new FakeTimeProvider(), drawContent: new FakeDrawContent());

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 Badge 修正测试构造函数参数以恢复编译

严重程度:P1;问题位置:迁移后的自动钓鱼单测;问题原因:ThrowRod 的构造函数现在是 (string, ILogger, IInputSimulator, BgiYoloPredictor, TimeProvider?, DrawContent?),但这里仍按旧模式把 blackboard 作为第五个参数传入,同类残留还出现在 BehavioursTests.FishBite.csFishBiteTimeout 构造调用。可能造成的影响:包含 BetterGenshinImpact.UnitTest 的解决方案编译会直接失败,单测无法运行。推荐修复方案:移除多余的 blackboard 参数,并把 FakeTimeProvider 传到新的 timeProvider 位置。

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

baka AI无法穿透SG来分析……

if (_bitmap == null)
{
_logger.LogWarning("截图失败");
return Status.Failure;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 截图失败时不要结束整棵钓鱼树

严重程度:P2;问题位置:TakeScreenshot.Update();问题原因:迁移前 CaptureGameImageNoRetry 偶发返回 null 时只记录“截图失败”并 continue 到下一帧,现在截图节点直接返回 Status.Failure,位于根 Sequence 前置步骤会让全自动钓鱼任务在一次瞬时截图失败后结束。可能造成的影响:游戏捕获短暂丢帧、窗口切换瞬间或采集后端偶发失败时,钓鱼流程会提前退出,甚至绕过后续正常钓鱼动作。推荐修复方案:让截图节点在失败时保持 Status.Running 并等待下一帧,或在调用方对截图失败做重试/跳过当前 tick 的处理。

Useful? React with 👍 / 👎.

Comment on lines +431 to +432
private Mat? _bitmap;
private CaptureContent? _content;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 结束时释放最后一帧截图资源

严重程度:P2;问题位置:TakeScreenshot 持有的截图字段;问题原因:该行为每次 tick 只释放上一帧 _bitmap/_content,但行为树结束后最后一次成功捕获的 MatCaptureContent 没有任何清理路径;迁移前这些对象在每轮循环中由 using 立即释放。可能造成的影响:每次全自动钓鱼任务结束都会遗留一张截图及其派生缓存的本机内存,重复运行会累积内存占用。推荐修复方案:在任务结束的 finally 中释放截图行为,或让 TakeScreenshot 实现受行为树调用的清理逻辑来释放 _content_bitmap

Useful? React with 👍 / 👎.

@huiyadanli
huiyadanli merged commit 92d1f9e into babalae:main Aug 10, 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