• v3.9.2 6b0440acc9

    v3.9.2
    All checks were successful
    CI / build-and-test (push) Successful in 1m53s
    Stable

    joeyr released this 2026-07-29 18:17:03 -05:00 | 28 commits to main since this release

    Fixed

    • Champion display names stopped being unique, and it silently cost 35% of the
      roster its stats.
      Patch 16.15 shipped the Classic Rift mode (map 453, queue
      4300) and with it 60 alternate champion entries in the client's
      champion-summary.json — alias Jade_<Base>, id 60000 + base id. Every one
      carries its base champion's display name, so "Garen" now names both id 86 and
      id 60086.

      ChampionNameResolver built its name→id maps last-write-wins, and the asset is
      listed id-ascending, so the classic id always landed last and won. All 60 shared
      names resolved to a 60xxx id — Ahri, Annie, Garen, Lux, Teemo, Wukong, Master
      Yi, Miss Fortune, Nunu & Willump and 51 more. Nothing in the published artifacts
      is keyed by those ids, so the in-game overlay, the scouting report, and both
      op.gg adapters quietly lost benchmarks, damage splits, and counters for a third
      of the roster, with no error surfaced.

      Champ select was never affected: it passes numeric championId straight
      through, and ids stayed unique throughout. Only name→id resolution was
      ambiguous.

    Changed

    • Name→id resolution now takes the roster it means, because breaking the tie
      the obvious way would have been worse.
      The Live Client Data API identifies
      participants by display name, so simply preferring the lowest id would have
      fixed normal games and started answering classic ones with live-roster stats.
      Jade Annie has 460 HP and 550 attack range against live Annie's 560 and 625, on
      a reworked kit — that is a confident wrong number where an unresolved id
      honestly renders "—", and showing nothing is the recoverable failure.

      So the lookups are split per roster and the in-game poller identifies the game
      before asking. rawChampionName is the primary signal, being per-participant
      and exact; mapName is the fallback. gameMode is deliberately ignored —
      ordinary Summoner's Rift reports the literal string "CLASSIC", so it
      identifies the wrong thing. Anything unidentified stays live, which is both
      the safe default and the overwhelmingly common case, and the verdict is logged
      once per game (live.roster_detected) with the raw strings beside it so a real
      classic game confirms the mapName spelling.

      op.gg adapters stay pinned to the live roster: op.gg publishes no classic pages,
      so a classic subject 404s to "no data" until it does.

    Downloads