fix: missing-dep [add] button respects MOD_ID_ALIASES on build mismatch
When user is on B42 and a mod requires the B41 mod_id (e.g. truemusic_mixtape_megapack require=truemusic), the canonical wsid (2613146550, B41-only) is rightly filtered out by the build guard — but we were silently dropping the suggestion. Now we fall back to MOD_ID_ALIASES: truemusic → TrueMoozic, suggest wsid 3632610172. - _lookup_wsids_for_missing returns Dict[str, Tuple[wsid, suggested_mod_id]] - build_warnings unpacks tuple, label clarifies rename when alias used - adapters.build_response signature updated for new shape
This commit is contained in:
@@ -84,17 +84,20 @@ def _resolve_satisfied_via_alias(input_modids: Set[str]) -> Set[str]:
|
||||
|
||||
def build_warnings(
|
||||
mlos_warnings: Dict[str, Any],
|
||||
wsid_lookup: Dict[str, str] | None = None,
|
||||
wsid_lookup: Dict[str, Tuple[str, str]] | None = None,
|
||||
source_wsids: Dict[str, str] | None = None,
|
||||
input_modids: Set[str] | None = None,
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Translate mlos_sort warnings to the SORTOF_DATA WARNINGS shape.
|
||||
|
||||
wsid_lookup: optional {mod_id -> workshop_id} map of *deps* we already
|
||||
have cached. When provided, each 'missing' warning is augmented with
|
||||
`actions: [{type: "add-wsid", wsid, label}]` so the frontend can render
|
||||
a click-to-add button. Unknown deps fall back to a Steam Workshop search
|
||||
link the user follows manually.
|
||||
wsid_lookup: optional {missing_mod_id -> (workshop_id, suggested_mod_id)}
|
||||
map produced by app._lookup_wsids_for_missing. The `suggested_mod_id`
|
||||
differs from the missing mod_id when a MOD_ID_ALIASES alternative was
|
||||
used because the canonical mod_id's only cached wsid is wrong-build
|
||||
(e.g. user on B42 needs `truemusic` → suggest `TrueMoozic`'s wsid).
|
||||
When provided, each 'missing' warning is augmented with `actions:
|
||||
[{type: "add-wsid", wsid, modId, label}]`. Unknown deps fall back to a
|
||||
Steam Workshop search link the user follows manually.
|
||||
|
||||
source_wsids: optional {mod_id -> workshop_id} map of *every cached mod
|
||||
in the current sort*. Used to attach the source mod's wsid onto
|
||||
@@ -120,13 +123,19 @@ def build_warnings(
|
||||
continue
|
||||
actions: List[Dict[str, str]] = []
|
||||
for dep in deps:
|
||||
wsid = lookup.get(dep)
|
||||
if wsid:
|
||||
entry = lookup.get(dep)
|
||||
if entry:
|
||||
wsid, suggested = entry
|
||||
# If MOD_ID_ALIASES rewrote the suggestion (B42 user needing
|
||||
# B41 mod_id `truemusic` gets `TrueMoozic` instead), surface
|
||||
# both names so the user understands what's being added.
|
||||
label = (f"add {suggested}" if suggested == dep
|
||||
else f"add {suggested} (renamed from {dep})")
|
||||
actions.append({
|
||||
"type": "add-wsid",
|
||||
"wsid": wsid,
|
||||
"modId": dep,
|
||||
"label": f"add {dep}",
|
||||
"modId": suggested,
|
||||
"label": label,
|
||||
})
|
||||
else:
|
||||
# No cache hit -> link to Steam Workshop search so the user
|
||||
@@ -558,7 +567,7 @@ def build_response(
|
||||
mods: List[ModInfo],
|
||||
sort_result: Dict[str, Any],
|
||||
status: str,
|
||||
wsid_lookup: Dict[str, str] | None = None,
|
||||
wsid_lookup: Dict[str, Tuple[str, str]] | None = None,
|
||||
pz_build: str = "B42",
|
||||
is_resort: bool = False,
|
||||
selected_modids: Optional[Set[str]] = None,
|
||||
|
||||
69
api/app.py
69
api/app.py
@@ -228,20 +228,29 @@ async def _lookup_wsids_for_missing(
|
||||
conn,
|
||||
mlos_warnings: Dict[str, Any],
|
||||
pz_build: Optional[str] = None,
|
||||
) -> Dict[str, str]:
|
||||
) -> Dict[str, Tuple[str, str]]:
|
||||
"""Resolve missing-requirement mod_ids to wsids via mod_parsed cache.
|
||||
|
||||
Returns {mod_id -> workshop_id} for any missing dep we've previously
|
||||
cached. Used to render an [add to list] action button next to the
|
||||
'missing' warning. Unknown deps just get no button.
|
||||
Returns {missing_mod_id -> (workshop_id, suggested_mod_id)} for any
|
||||
missing dep we've previously cached. `suggested_mod_id` matches the
|
||||
missing mod_id by default, but when the canonical wsid is wrong-build
|
||||
AND `MOD_ID_ALIASES` (adapters.py) has an alternative whose cached
|
||||
wsid IS build-correct, we redirect to the alias. Example: user on B42
|
||||
has `truemusic_mixtape_megapack` (require=truemusic). truemusic only
|
||||
exists as a B41 wsid; the alias `TrueMoozic` exists as a B42 wsid.
|
||||
Result: {"truemusic": ("3632610172", "TrueMoozic")} — frontend renders
|
||||
[add TrueMoozic] pointing at the correct build's wsid.
|
||||
|
||||
pz_build filters out wrong-build resolutions: if the user is on B42 and
|
||||
the only cached wsid for `dep` is tagged `Build 41` only, we DROP the
|
||||
suggestion. Adding it would just trigger a build-mismatch warning;
|
||||
worse, in cases like `TacHold requires modoptions` where the author's
|
||||
`require=` is over-declared, the suggestion misleads the user toward
|
||||
a B41 mod that doesn't actually need to be in the load order.
|
||||
the only cached wsid for `dep` is tagged `Build 41` only, the canonical
|
||||
suggestion is DROPPED (and the alias path may rescue it). Adding the
|
||||
wrong-build dep would just trigger a build-mismatch warning; worse, in
|
||||
cases like `TacHold requires modoptions` where `require=` is over-
|
||||
declared, the suggestion misleads the user toward a B41 mod that
|
||||
doesn't actually need to be in the load order.
|
||||
"""
|
||||
from adapters import MOD_ID_ALIASES
|
||||
|
||||
missing = mlos_warnings.get("missing_requirements") or {}
|
||||
if not missing:
|
||||
return {}
|
||||
@@ -252,6 +261,11 @@ async def _lookup_wsids_for_missing(
|
||||
wanted.add(d)
|
||||
if not wanted:
|
||||
return {}
|
||||
# Expand the fetch set with alias targets so we can resolve in one round.
|
||||
for dep in list(wanted):
|
||||
for alias_target in MOD_ID_ALIASES.get(dep, []):
|
||||
wanted.add(alias_target)
|
||||
|
||||
rows = await conn.fetch(
|
||||
"""
|
||||
SELECT DISTINCT ON (mp.mod_id) mp.mod_id, mp.workshop_id, wm.tags
|
||||
@@ -268,15 +282,36 @@ async def _lookup_wsids_for_missing(
|
||||
other_tag = "Build 41" if target_tag == "Build 42" else (
|
||||
"Build 42" if target_tag == "Build 41" else None
|
||||
)
|
||||
out: Dict[str, str] = {}
|
||||
|
||||
def _is_build_correct(tags: List[str]) -> bool:
|
||||
if not target_tag or not other_tag:
|
||||
return True
|
||||
return not (other_tag in tags and target_tag not in tags)
|
||||
|
||||
# candidates: mod_id -> (wsid, build_correct)
|
||||
candidates: Dict[str, Tuple[str, bool]] = {}
|
||||
for r in rows:
|
||||
if target_tag and other_tag:
|
||||
tags = list(r["tags"] or [])
|
||||
if other_tag in tags and target_tag not in tags:
|
||||
# Wrong-build only — adding it would just trigger a
|
||||
# build-mismatch. Skip the suggestion.
|
||||
continue
|
||||
out[r["mod_id"]] = r["workshop_id"]
|
||||
tags = list(r["tags"] or [])
|
||||
candidates[r["mod_id"]] = (r["workshop_id"], _is_build_correct(tags))
|
||||
|
||||
deps_seen: set = set()
|
||||
for deps in missing.values():
|
||||
for d in deps:
|
||||
if d:
|
||||
deps_seen.add(d)
|
||||
|
||||
out: Dict[str, Tuple[str, str]] = {}
|
||||
for dep in deps_seen:
|
||||
cand = candidates.get(dep)
|
||||
if cand and cand[1]:
|
||||
out[dep] = (cand[0], dep)
|
||||
continue
|
||||
# Canonical missing or wrong-build; try aliases in declaration order.
|
||||
for alias_target in MOD_ID_ALIASES.get(dep, []):
|
||||
alt = candidates.get(alias_target)
|
||||
if alt and alt[1]:
|
||||
out[dep] = (alt[0], alias_target)
|
||||
break
|
||||
return out
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user