From b1471b739fbc3c65c838fa4446e249be32edd1d5 Mon Sep 17 00:00:00 2001 From: indifferentketchup Date: Wed, 6 May 2026 19:20:29 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20HellDrinx=20takeaways=20=E2=80=94=20con?= =?UTF-8?q?flict=20blocklist=20+=20vehicle=20path=20signal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two narrow additions adopted from a review of HellDrinx Mod Manager (/tmp/helldrinx-modmanager-PZ-main.zip), per docs/plans/ (planning conversation, no spec checked in). A. _IGNORED_FILENAMES in api/diagnostics.py — skip filenames that are intended merge points (PZ engine-concatenated or framework hooks) from /api/conflicts output. Multiple distinct sha1s for these files is by-design, not a conflict. Live cache had 33 providers shipping sandbox-options.txt with 31 distinct hashes — those would have been 31 false-positive conflict rows on any sort spanning them. B. Path-based Vehicles signal in worker.build_manifest_and_types — extended the existing scripts/vehicles[/] check with models_x/vehicles/ and models/vehicles/. Catches vehicle mods that ship 3D assets without scripts (rare but real); still requires the user-build's manifest to be rebuilt before mod_types reflects it. --- api/diagnostics.py | 32 +++++++++++++++++++++++++++++++- worker/worker.py | 8 +++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/api/diagnostics.py b/api/diagnostics.py index a6214ba..d9f1313 100644 --- a/api/diagnostics.py +++ b/api/diagnostics.py @@ -14,6 +14,29 @@ from typing import Dict, List, Tuple from mlos_sort import ModInfo +# Filenames that legitimately appear across multiple mods because they're +# intended merge points — either PZ engine-merged at runtime, or framework +# extension hooks where multiple client mods coexist by design. Multiple +# distinct sha1s here is by-design, not a conflict, so we skip them in +# scan_file_conflicts. Sourced from HellDrinx Mod Manager's known false- +# positive list (backend/services/workshop.cjs); add more here as we +# encounter them. +_IGNORED_FILENAMES = { + # PZ engine-merged at load time + "sandbox-options.txt", + "fileguidtable.xml", + # Framework extension hooks (designed for many mods to override) + "mf_ismoodle.lua", # MoodleFramework hook + "kp_extrabodylocations.lua", # KP body-locations framework hook + "registries.lua", # damnlib-style registry framework + # Specific mod-side merge points + "hat_gasmask.xml", + "hat_gasmask_nofilter.xml", + "sounds_tmrremovemumble.txt", + "null.wav", # silent placeholder, deliberately shared +} + + @dataclass class FileConflict: rel_path: str @@ -59,7 +82,14 @@ async def scan_file_conflicts(conn, mods: List[ModInfo]) -> List[FileConflict]: idx = order_index.get(mod_id) if idx is None: continue - by_path[r["rel_path"]].append((idx, mod_id, r["sha1"])) + rel_path = r["rel_path"] + # Skip known intended-merge-point filenames (engine-concatenated or + # framework hooks). These produce noisy false positives because + # multiple mods adding sandbox vars / framework hooks is by design. + basename = rel_path.rsplit("/", 1)[-1] + if basename in _IGNORED_FILENAMES: + continue + by_path[rel_path].append((idx, mod_id, r["sha1"])) conflicts: List[FileConflict] = [] for rel, entries in by_path.items(): diff --git a/worker/worker.py b/worker/worker.py index 24c3757..2d66dd8 100644 --- a/worker/worker.py +++ b/worker/worker.py @@ -431,7 +431,13 @@ def build_manifest_and_types( or suffix in {".png", ".dds"}): if path.name.lower() != "poster.png": tags.add("Textures") - if rel_below.startswith(("scripts/vehicles/", "scripts/vehicle")): + # Path-based vehicle detection. scripts/vehicles is the universal + # signal; models[_x]/vehicles/ catches mods that ship 3D assets + # without scripts (rarer, but real — borrowed from HellDrinx Mod + # Manager's heuristic). rel_below is already lowercased so the + # capital "X" in the original PZ "models_X" path lands as "models_x". + if rel_below.startswith(("scripts/vehicles/", "scripts/vehicle", + "models_x/vehicles/", "models/vehicles/")): tags.add("Vehicles") if rel_below.startswith(("clothing/", "scripts/clothing/")): tags.add("Clothing")