Repository navigation
Conversation
|
The matching clean asset-only resource branch is submitted as Alban1911/LeagueSkins#158. It is based on the current LeagueSkins upstream
Both trees expose the same 1,737 relative package paths. Regular |
fcfc584 to
274f459
Compare
Resolve the active Rift Classic carrier from the intersection of the live carousel and pickable-skin catalogs. Prefer a unique native Skin301/Skin302 slot, retain a verified Skin0 slot when no native carrier exists, and refuse to write a guessed Skin0 ID when the catalogs do not identify a safe carrier. Keep the resolver independent from ClassicWheel. A standalone installation resolves the carrier at the injection write boundary, including when LCU already reports the generated 600xx000 Skin0 ID, and restores the cached native carrier after an LCU rollback. When ClassicWheel is installed, the same boundary reuses its already validated carrier. Use the verified carrier for regular history and chroma default-skin comparisons without changing their ordinary-mode behavior. Document the 63-champion reference, the matching LeagueSkins package layout, and the missing-package Katarina case. Validation: - 17 Classic injection and carrier tests pass. - Standalone regressions cover an initial 600xx000 selection and a later rollback from the cached native carrier. - The wheel-first merge is validated separately before publication. - Regular mode keeps the requested Skin0 ID unchanged.
Alban1911
left a comment
There was a problem hiding this comment.
Thanks for the detailed write-up and the Katarina evidence. The carrier problem is real, and resolving it from the live carousel and pickable-skin catalogs is a clean way to find it. I tested bb514ad4: the 17 tests pass and it merges into main without conflicts. Two things block it for now.
1. An unresolved carrier cancels the whole injection. When carrier_skin_id_for_force can't get a carrier (an LCU catalog call that fails or times out, or a catalog we don't expect), it raises inside _force_base_skin. _inject_unowned_skin catches that, and the injection thread never starts. I simulated it with lcu.get returning None on a JADE champion:
[INJECT] injection error: Classic native carrier is unavailable; refusing Skin0 fallback
injection thread started: False
skip-base-skin broadcast sent: True
So the player gets no skin at all, and the UI has already been told to skip the base skin. Today the same case still injects on Skin0. When the carrier can't be resolved, please fall back to the current Skin0 behavior and log it, rather than refusing the injection.
2. The library change can't ship without breaking someone. This PR needs LeagueSkins#158, which rebuilds the Classic packages of the Skin301/Skin302 champions on those slots. But every Rose version downloads the same library:
- If #158 lands first, Rose 1.4.8 and older still select
600xx000, so packages built for Skin301 don't apply, and those champions' Classic skins break for everyone who hasn't updated. - If this PR lands first, Rose selects Skin301 while the packages still target Skin0, which breaks the same champions.
Before either can merge, I need a transition plan. For example, keep both package sets side by side, have this version of Rose pick the Skin301 ones, and drop the Skin0 ones once older versions are gone.
Also, a question: the PR shows the failure when a target package is missing. With the package present, do the current Skin0 packages show anything wrong in-game for the Skin301/Skin302 champions? If they don't, the gain is the fallback when a package is missing, and I'd like to weigh that against a coordinated change across both repos.
Preserve the original reviewed commit bb514ad while integrating the current upstream baseline. The merge tree matches the already validated 1.4.9 carrier root before the follow-up fixes.
Replace the fail-closed carrier path with one automatic decision: use a verified native carrier only when the matching classic-native archive resolves, otherwise keep the generated Skin0 carrier and the legacy classic package. Resolve the decision before broadcasting skip-base-skin, pass the package family explicitly through the injection thread, and keep party archive resolution on the same family. Download and clean classic-native independently so the existing classic library remains compatible with released Rose versions. Validation: 66 focused carrier, injection, downloader, and party tests pass. The full macOS suite still has six pre-existing Windows-only failures involving ctypes.windll, mbcs, and patcher process return codes.
Prefer the classic-native package family for every verified live default carrier, including champions whose native carrier is Skin0. This keeps the package source aligned with the champion's real default slot instead of treating every Skin0 carrier as a reason to use the compatibility library. Preflight both candidate archives before changing the LCU selection. If the default-slot package is missing or invalid, fall back atomically to the existing classic Skin0 package; native Skin0 carriers keep the same slot and only change package source. Abort without an LCU write when neither package is usable. Cache ZIP validation by path, size, and modification time so repeated carousel events do not reread unchanged packages. Invalid archives, CRC failures, and damaged compressed streams are treated as unavailable. Extraction and LTK failures after a valid preflight remain on the existing injection failure path. Validation: 77 focused Classic, library, party, and custom-mod tests pass; Python compile checks pass.
Use the same feature-detected injection boundary as the wheel PR. The carrier branch still supplies native discovery, package validation, and dual-library support, while the wheel remains functional on upstream Skin0 when this module is absent. Pass the native package option only when the automatic plan selects it. This keeps the two PRs independently usable and avoids a second carrier implementation inside the carousel. Validation: Python compilation and git diff checks pass for the injection trigger.
Expose Classic champion detection, ID conversion, catalog normalization, and state-aware default-slot helpers from injection.classic. Keep classic_carrier focused on live carrier discovery and automatic package policy, and import shared predicates directly at the injection boundary. The shared module is intentionally identical to the wheel PR so the optional carrier and carousel changes remain independently usable and merge without competing implementations. Validation: Python compilation and git diff checks pass for the changed modules.
Return the regular champion's Skin0 ID before importing the optional carrier policy or probing either Classic package family. This keeps the compatibility boundary inert outside Rift Classic and prevents ordinary injection from touching classic/ or classic-native/. Validation: 78 focused carrier, library, party, and custom-mod tests pass. The regression test verifies that regular mode performs no Classic package or LCU catalog lookup.
Resolve and cache the live Rift Classic default slot before probing either package family. If the carrier cannot be identified, stop before package preflight or any LCU write instead of treating an unknown slot as permission to use generated Skin0. After a carrier is known, keep the automatic package order unchanged: use the matching classic-native archive first, switch carrier and package together to the legacy classic Skin0 archive when necessary, and abort when neither archive is usable. Remove carrier substitution from the generic base-skin force path because it has no selected package context and is also used by custom mods. Official Classic skin injection continues through the validated automatic plan. Validation: 81 focused carrier, library, party, custom-mod, chroma, and mode tests pass. The unresolved-carrier regression verifies that package probes, LCU writes, and injection startup do not occur.
|
Thanks for the detailed review. The main result first: the two-path compatibility mode is now complete and working. Rose now supports both Rift Classic carrier layouts automatically:
Rose prefers the native-default path. If its package is unavailable, Rose switches the carrier and package library together to the Skin0 path. The two package families are never mixed, and there is no manual carrier setting. The Skin0 solution is technically valid. I rebuilt and tested the Skin0 packages, including Katarina's weapons, daggers, effects, materials, animations, and linked objects. When those dependencies are fully retargeted, Skin0 injects correctly in game. The earlier weapon and effect problems were caused by an incomplete Skin0 object graph, not by Skin0 itself being unusable. Additional log evidenceI also found new log evidence that explains why I still prefer the native carrier when it is available. Katarina's live Classic default is Rose runtime log:
League client log:
Game log:
This does not prove that Skin0 is rejected, detected by Vanguard, or causes a ban. It does show that the recorded identity differs from the live Classic default. That difference may create anomalous telemetry and could carry additional account or enforcement risk. For that reason, I prefer the native path while keeping Skin0 as a tested compatibility fallback. A two-client test is still needed to confirm what an unmodified teammate sees. Responses to the review points1. Unresolved carrier and partial injection You were right that the previous ordering was unsafe: Rose could send Carrier discovery and package checks now finish before Rose changes LCU, sends 2. Safe library transition LeagueSkins#158 now keeps both complete package families side by side with the same 1,737 relative paths:
Older Rose versions continue reading 3. Does Skin0 work when the target package is present? Yes. After rebuilding the complete dependency graph, the Skin0 path works in game. The native path is preferred because it requires less retargeting and keeps the LCU default, package root, and no-overlay fallback aligned; it is not preferred because Skin0 is technically impossible. 4. Remaining damaged-package boundary Missing and structurally invalid archives are rejected before the LCU write. A ZIP-valid package with a semantically broken internal WAD can still fail later in Both paths have been verified in live games, and 81 focused carrier, injection, library, party, custom-mod, chroma, and mode tests pass. Regular modes remain on the existing injection path, and party injection uses the same package family selected for the local player. The original reviewed commit Since Rift Classic has also been announced for removal, I kept this as a small automatic compatibility layer rather than adding a permanent settings switch. The Classic-specific code and resources remain isolated so they can be disabled or removed later without changing regular injection. |
What this changes
This PR fixes the base carrier used by Rift Classic skin injection.
Skin0 means slot
0;600xx000is the full Rift Classic LCU skin ID for thatslot. Some Classic champions use Skin0 as their real default, while others use
Skin301, and Kayle currently uses Skin302. Instead of generating Skin0 for
every champion, Rose now resolves the live default slot and selects a matching
package family before changing LCU.
Regular game modes are unchanged.
Two automatically matched paths
This PR supports two complete injection paths and selects between them
automatically for each requested Classic skin:
default carrier together with the matching package from
classic-native/.600xx000together with thefully retargeted Skin0 package from
classic/.Rose first resolves the real default carrier from the intersection of the live
carousel and pickable-skin catalogs, then validates the requested archive in
both package libraries. It prefers the native-default path. If that package is
missing or structurally invalid, Rose switches both the carrier and package
library to the Skin0 compatibility path. It never combines a native carrier
with a Skin0 package, or Skin0 with a native-carrier package.
If the carrier cannot be resolved, or neither package is usable, Rose stops
before sending
skip-base-skin, changing the UI, or writing the LCU selection.The requested target skin remains separate from this carrier choice and still
uses the existing LTK injection path. Party injection follows the same selected
package family. There is no manual slot setting.
Why keep the native carrier
For Katarina:
55000;60055000;60055301;60055029.When the target overlay is unavailable after Rose has selected
60055000, thegame can still launch on the hidden slot, but the visible result is regular base
Katarina rather than the mode-native Classic default at
60055301.Figure 1: the missing target leaves the loading card on the hidden Classic
Skin0 selection,
60055000.Figure 2: the expected mode-native Classic fallback at
60055301.Figure 3: the regular base model reached through
60055000when no overlayreplaces it.
Skin0 injection is still supported. A fully adapted Skin0 package works when
its model, animation, weapon, material, effect, and linked-object dependencies
are retargeted correctly. The native path is preferred because the LCU default,
failure fallback, and package object graph remain aligned.
Compatible library transition
The matching resource PR is
Alban1911/LeagueSkins#158:
classic/is the complete Skin0-compatible library used by released Roseversions and by the automatic compatibility path;
classic-native/is the parallel native-default library rooted in eachchampion's live Skin0/Skin301/Skin302 carrier.
Both trees expose the same 1,737 package paths. Older Rose releases continue to
use
classic/; this PR can choose either family without requiring an atomicupgrade across the two repositories.
Scope
This PR contains carrier discovery, package preflight, dual-library download
and injection wiring, party-path consistency, focused tests, and the carrier
reference. It does not include ClassicWheel, history navigation, random mode,
the Classic chroma picker, badges, carousel projection, or background syncing.
The lookup method, 63-champion reference, package layout, and repair procedure
are documented in
docs/rift-classic-native-carriers.md.The table is an audit reference; runtime selection comes from live LCU data.
Validation
tests pass.
live games, including Katarina's weapon and effect dependencies.
existing late injection failure path; it cannot be safely rolled back after
game startup begins.
limited to existing Windows-only APIs (
ctypes.windll,mbcs, and patcherprocess return-code behavior); 7 tests were skipped.
The original reviewed commit
bb514ad4remains intact in this PR. The latercommits update it to Rose 1.4.9 and address the review findings incrementally.
Local logs confirm the LCU, Team Builder, and local game-spawn values described
above. They do not establish what an unmodified second client renders, and they
do not establish any Vanguard behavior.