babalae/better-genshin-impact · error · InvalidOperationException

计算缺失树脂 {missingResin} 超过树脂上限 {resinLimit}

Error message

计算缺失树脂 {missingResin} 超过树脂上限 {resinLimit}

What it means

在 RecognizeOriginalResinInfoFromBigMap 中,缺失树脂数 missingResin = ⌈全部恢复秒数 / 480s⌉ 与 OCR 读到的树脂上限 resinLimit 做一致性校验。正常情况下 missingResin 永远 ≤ resinLimit(满上限约 160×8min≈21.3h),一旦超出说明两者之一被读错,任务以 InvalidOperationException 中止以免用错误的树脂数驱动后续补充逻辑。

Source

Thrown at BetterGenshinImpact/GameTask/AutoBoss/AutoBossTask.cs:329

        if (resinIconRegion.IsEmpty())
        {
            throw new InvalidOperationException("未找到原粹树脂图标");
        }

        var iconLeft = resinIconSearchRegion.X + resinIconRegion.Left;
        var iconRight = resinIconSearchRegion.X + resinIconRegion.Right;
        var iconBottom = resinIconSearchRegion.Y + resinIconRegion.Bottom;

        resinIconRegion.Click();
        await Delay(500, _ct);

        using var clickedCapture = CaptureToRectArea();
        var resinLimit = RecognizeOriginalResinLimit(clickedCapture, iconRight);
        var fullRecoveryTime = RecognizeFullRecoveryTime(clickedCapture, iconLeft, iconBottom);
        var missingResin = (int)Math.Ceiling(fullRecoveryTime.TotalSeconds / OriginalResinRecoveryInterval.TotalSeconds);
        if (missingResin > resinLimit)
        {
            throw new InvalidOperationException($"计算缺失树脂 {missingResin} 超过树脂上限 {resinLimit}");
        }

        var originalResin = resinLimit - missingResin;
        _logger.LogInformation("{Name}:剩余树脂 {Count}", Name, originalResin);
        return new OriginalResinInfo(originalResin, resinLimit);
    }

    /// <summary>
    /// 按原右上角 OCR 区域读取当前/上限文本,并取数字串最后三位作为树脂上限。
    /// </summary>
    private int RecognizeOriginalResinLimit(ImageRegion capture, int resinIconRight)
    {
        var countRect = new Rect(resinIconRight + ScaleX(25), ScaleY(37), ScaleX(120), ScaleY(24));
        using var countRegion = capture.DeriveCrop(countRect);
        var countText = OcrFactory.Paddle.OcrWithoutDetector(countRegion.SrcMat);
        var digits = Regex.Replace(StringUtils.ConvertFullWidthNumToHalfWidth(countText), @"\D", "");
        if (digits.Length < 3)
        {

View on GitHub (pinned to a7cb36712d)

Solutions

  1. 在 RecognizeOriginalResinInfoFromBigMap 外层已有 catch(行 282)做兜底,确认该兜底生效;将本次失败计入日志后让领奖界面逻辑接管,不要直接中止整个任务。
  2. 增加树脂上限识别的鲁棒性:当 digits 同时含 '/' 时优先按 '/' 拆分取右段作为 limit,而非盲目取最后三位。
  3. 对 fullRecoveryTime 做合理性校验(如 > 24h 则判定 OCR 异常并重试一次截图)。
  4. 校准 countRect 偏移(resinIconRight + ScaleX(25))与图标模板,确保区域覆盖 '当前/上限' 整段文本。

Example fix

// before
var missingResin = (int)Math.Ceiling(fullRecoveryTime.TotalSeconds / OriginalResinRecoveryInterval.TotalSeconds);
if (missingResin > resinLimit)
{
    throw new InvalidOperationException($"计算缺失树脂 {missingResin} 超过树脂上限 {resinLimit}");
}

// after
var missingResin = (int)Math.Ceiling(fullRecoveryTime.TotalSeconds / OriginalResinRecoveryInterval.TotalSeconds);
if (missingResin > resinLimit)
{
    _logger.LogWarning("{Name}:缺失树脂 {Missing} 超过上限 {Limit},判定为 OCR 异常并跳过预检", Name, missingResin, resinLimit);
    throw new InvalidOperationException($"计算缺失树脂 {missingResin} 超过树脂上限 {resinLimit}");
}
Defensive patterns

Strategy: validation

Validate before calling

// 在 RecognizeOriginalResinInfoFromBigMap 内、抛出前校验恢复时间合理性
var maxPlausibleRecovery = TimeSpan.FromHours(24);
if (fullRecoveryTime > maxPlausibleRecovery)
{
    throw new FormatException($"恢复时间 {fullRecoveryTime} 超出合理范围,疑似 OCR 异常");
}
// resinLimit 来自 RecognizeOriginalResinLimit,已保证 >0;这里只兜底上限合理性
if (resinLimit is < 60 or > 200)
{
    _logger.LogWarning("{Name}:树脂上限 {Limit} 异常,跳过预检", Name, resinLimit);
    throw new FormatException($"树脂上限 {resinLimit} 超出常见范围");
}

Try / catch

// 调用方(PreCheckResin 等)已用 catch (Exception e) when (e is not OperationCanceledException) 兜底,
// 将树脂预检失败降级为返回 null,由领奖界面逻辑接管。保持该兜底即可。

Prevention

When it happens

Trigger: 全部恢复时间被 OCR 读得过大(例如把其他界面文本里的时间误识别为恢复时间),或树脂上限被读得过小(例如 '120/160' 只识别到 '120',limitText 取最后三位得到 120 而非 160),导致 ceil(recovery/480) 超过 resinLimit。

Common situations: 大地图右上角树脂弹窗未完全展开就截图;OCR 区域偏移导致 limit 段丢失;游戏 UI 文案/字号变化使 'XXX/160' 被截断;图标模板定位漂移使 iconRight 偏移、countRect 落在错误位置。

Related errors


AI-assisted analysis of babalae/better-genshin-impact@a7cb36712d (2026-08-13). Data as JSON: /api/errors/6eb33c52b4cb575e. Report an issue: GitHub.