babalae/better-genshin-impact · critical · Exception

灰度分层地图 {LayerId} 读取失败

Error message

灰度分层地图 {LayerId} 读取失败

What it means

Companion to error 406, thrown when reading the gray map image for a template-match layer. Same caveat: Bv.ImRead is non-nullable, so the '?? throw' is unreachable; the actual failure for a missing <LayerId>_gray.webp (or .png when IsOverSize) is a FileNotFoundException from File.OpenRead inside ImRead, not this custom exception.

Source

Thrown at BetterGenshinImpact/GameTask/Common/Map/Maps/Base/BaseMapLayerByTemplateMatch.cs:42

    public float Top { get; set; } = 0;
    public float Left { get; set; } = 0;
    public bool IsOverSize  { get; set; } = false;
    [JsonIgnore]
    public required FastSqDiffMatcher CoarseColorMatcher; // 小尺寸彩图
    [JsonIgnore]
    public Mat FineGrayMap = new Mat(); // 大尺寸灰度图
    
    public void LoadLayer(string layerDir)
    {
        SpeedTimer speedTimer = new($"加载 {LayerId} 地图图片");
        var colorMapFileName = LayerId + "_color" + ".webp";
        var colorMapPath = Path.Combine(layerDir, colorMapFileName);
        var coarseColorMap = Bv.ImRead(colorMapPath)?? throw new Exception($"彩色分层地图 {LayerId} 读取失败");
        speedTimer.Record("精确匹配用彩图");
        CoarseColorMatcher = new FastSqDiffMatcher(coarseColorMap, new Size(52, 52));
        var grayMapFileName = LayerId + "_gray" + (IsOverSize ? ".png" : ".webp");
        var grayMapPath = Path.Combine(layerDir, grayMapFileName);
        FineGrayMap = Bv.ImRead(grayMapPath, ImreadModes.Grayscale)?? throw new Exception($"灰度分层地图 {LayerId} 读取失败");
        speedTimer.Record("粗匹配用灰度图");
        speedTimer.DebugPrint();
    }

    public static List<BaseMapLayerByTemplateMatch> LoadLayers(SceneBaseMapByTemplateMatch sceneBaseMap)
    {
        var layers = new List<BaseMapLayerByTemplateMatch>();
        var layerDir = Path.Combine(Global.Absolute(@"Assets\Map\"), sceneBaseMap.Type.ToString());
        if (!Directory.Exists(layerDir))
        {
            return layers;
        }
        var jsonFiles = Directory.GetFiles(layerDir, "*.json", SearchOption.AllDirectories);
        foreach (var jsonFile in jsonFiles)
        {
            var json = File.ReadAllText(jsonFile);
            var tempLayers = JsonSerializer.Deserialize<List<BaseMapLayerByTemplateMatch>>(json) ?? throw new Exception("Failed to deserialize JSON.");
            layers.AddRange(tempLayers);

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Confirm <LayerId>_gray.webp (or <LayerId>_gray.png when IsOverSize=true) exists for each layer.
  2. Ensure the IsOverSize flag in the JSON matches which gray-image extension is shipped.
  3. Guard with File.Exists(grayMapPath) before ImRead so the intended error message is actually produced.

Example fix

// before
FineGrayMap = Bv.ImRead(grayMapPath, ImreadModes.Grayscale)?? throw new Exception($"灰度分层地图 {LayerId} 读取失败");

// after
if (!File.Exists(grayMapPath)) throw new Exception($"灰度分层地图 {LayerId} 读取失败: 缺少 {grayMapPath}");
FineGrayMap = Bv.ImRead(grayMapPath, ImreadModes.Grayscale);
Defensive patterns

Strategy: validation

Validate before calling

var grayMapPath = Path.Combine(layerDir, LayerId + "_gray" + (IsOverSize ? ".png" : ".webp"));
if (!File.Exists(grayMapPath)) throw new FileNotFoundException($"缺少灰度地图: {grayMapPath}", grayMapPath);

Prevention

When it happens

Trigger: LoadLayer computes grayMapPath = Path.Combine(layerDir, LayerId + "_gray" + (IsOverSize ? ".png" : ".webp")) and calls Bv.ImRead(grayMapPath, ImreadModes.Grayscale). Missing file → File.OpenRead throws before this message could ever be reached.

Common situations: IsOverSize flag in the JSON is true but only a .webp (not .png) gray map exists, or vice versa; LayerId mismatch; partial asset deployment.

Related errors


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