Fix legacy client crashes from unsupported 26.2 particles#7
Conversation
Filter 26.2-only particle registry entries and rewrite level_particles payloads to safe fallbacks so 26.1 clients no longer disconnect on unknown particle ids, then refresh the prebuilt jar artifact. Made-with: Cursor
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughAdds a constant listing 26.2-only particle IDs; strips those IDs from particle-type registry sync for legacy clients; and implements outbound ClientboundLevelParticlesPacket remapping with option rewrites and fallback substitution plus startup validation of fallback coverage. Changes
Sequence DiagramsequenceDiagram
participant Client as Legacy Client
participant Handler as LegacyPacketHandler
participant Registry as Registry Sync
participant Producer as Particle Producer
rect rgba(0, 150, 200, 0.5)
note over Handler,Registry: Registry Filtering Phase
Handler->>Registry: filterRegistryData()
Handler->>Handler: compute strip26_2OnlyParticle from LEGACY_UNSUPPORTED_PARTICLE_IDS
Handler->>Registry: remove unsupported particle entries
Registry-->>Client: Filtered registry (no 26.2 particles)
end
rect rgba(200, 150, 0, 0.5)
note over Producer,Handler: Packet Remapping Phase
Producer->>Handler: ClientboundLevelParticlesPacket (26.2 particle)
Handler->>Handler: routePlayPacket() detects particle packet
Handler->>Handler: remapLevelParticles()
Handler->>Handler: remapParticleOptionsForLegacy() (block/item state rewrites)
Handler->>Handler: check particle type against LEGACY_UNSUPPORTED_PARTICLE_IDS
alt Unsupported
Handler->>Handler: substitute with LEGACY_PARTICLE_FALLBACKS
Handler->>Client: rewritten ClientboundLevelParticlesPacket via copyParticlePacket()
else Supported
Handler-->>Client: original or option-rewritten packet
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Remap block-state ids and item templates inside level_particles options before fallback handling so common impact particles (such as deepslate crumble) cannot desync legacy decoding. Made-with: Cursor
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/main/java/dev/ohno/legacylink/handler/LegacyPacketHandler.java (1)
102-107: Consider adding a coverage guard between unsupported IDs and fallback mappings.This prevents silent drift if a new unsupported particle ID is added later without an explicit fallback choice.
Suggested guard
private static final Map<String, SimpleParticleType> LEGACY_PARTICLE_FALLBACKS = Map.of( "minecraft:noxious_gas", ParticleTypes.SMOKE, "minecraft:noxious_gas_cloud", ParticleTypes.CLOUD, "minecraft:sulfur_bubbles", ParticleTypes.BUBBLE, "minecraft:sulfur_cube_goo", ParticleTypes.ITEM_SLIME ); + +static { + Set<String> missing = new HashSet<>(LegacyLinkConstants.LEGACY_UNSUPPORTED_PARTICLE_IDS); + missing.removeAll(LEGACY_PARTICLE_FALLBACKS.keySet()); + if (!missing.isEmpty()) { + LegacyLinkMod.LOGGER.warn( + "[LegacyLink] Missing explicit legacy particle fallback(s): {}. Defaulting to smoke.", + missing + ); + } +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/dev/ohno/legacylink/handler/LegacyPacketHandler.java` around lines 102 - 107, Add a coverage guard to ensure the LEGACY_PARTICLE_FALLBACKS map covers exactly the known unsupported particle IDs: implement a static validation (in the LegacyPacketHandler class static initializer or the method that accesses LEGACY_PARTICLE_FALLBACKS) that computes the set difference between the authoritative unsupported ID set and LEGACY_PARTICLE_FALLBACKS.keySet() and fails fast (throw IllegalStateException or log+exit) if any unsupported IDs are missing or if there are unexpected keys; reference LEGACY_PARTICLE_FALLBACKS and the authoritative unsupported ID collection (create one if missing) so future additions cannot silently drift without an explicit fallback.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/main/java/dev/ohno/legacylink/handler/LegacyPacketHandler.java`:
- Around line 102-107: Add a coverage guard to ensure the
LEGACY_PARTICLE_FALLBACKS map covers exactly the known unsupported particle IDs:
implement a static validation (in the LegacyPacketHandler class static
initializer or the method that accesses LEGACY_PARTICLE_FALLBACKS) that computes
the set difference between the authoritative unsupported ID set and
LEGACY_PARTICLE_FALLBACKS.keySet() and fails fast (throw IllegalStateException
or log+exit) if any unsupported IDs are missing or if there are unexpected keys;
reference LEGACY_PARTICLE_FALLBACKS and the authoritative unsupported ID
collection (create one if missing) so future additions cannot silently drift
without an explicit fallback.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 703aaf97-e253-47cb-aeff-1aa0ba16a8cd
⛔ Files ignored due to path filters (1)
prebuilt/legacylink-0.1.1.jaris excluded by!**/*.jar
📒 Files selected for processing (2)
src/main/java/dev/ohno/legacylink/LegacyLinkConstants.javasrc/main/java/dev/ohno/legacylink/handler/LegacyPacketHandler.java
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/main/java/dev/ohno/legacylink/handler/LegacyPacketHandler.java (1)
107-112: Add startup validation so unsupported-particle coverage cannot silently drift.Line 632 defaults missing mappings to
SMOKE; that is safe, but it can hide newly added unsupported IDs. Consider validating that every ID inLEGACY_UNSUPPORTED_PARTICLE_IDShas an explicit fallback.♻️ Proposed hardening
private static final Map<String, SimpleParticleType> LEGACY_PARTICLE_FALLBACKS = Map.of( "minecraft:noxious_gas", ParticleTypes.SMOKE, "minecraft:noxious_gas_cloud", ParticleTypes.CLOUD, "minecraft:sulfur_bubbles", ParticleTypes.BUBBLE, "minecraft:sulfur_cube_goo", ParticleTypes.ITEM_SLIME ); + +static { + if (!LEGACY_PARTICLE_FALLBACKS.keySet().containsAll(LegacyLinkConstants.LEGACY_UNSUPPORTED_PARTICLE_IDS)) { + Set<String> missing = new HashSet<>(LegacyLinkConstants.LEGACY_UNSUPPORTED_PARTICLE_IDS); + missing.removeAll(LEGACY_PARTICLE_FALLBACKS.keySet()); + throw new ExceptionInInitializerError("[LegacyLink] Missing particle fallback mappings: " + missing); + } +}Also applies to: 632-632
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/dev/ohno/legacylink/handler/LegacyPacketHandler.java` around lines 107 - 112, Add a startup validation that ensures every ID in LEGACY_UNSUPPORTED_PARTICLE_IDS has an explicit entry in LEGACY_PARTICLE_FALLBACKS so new unsupported IDs cannot silently default to SMOKE; implement this check in a static initializer or a startup method in LegacyPacketHandler (e.g., validateFallbacksOnStartup) and have it log and/or throw an error listing any missing keys (referencing LEGACY_PARTICLE_FALLBACKS and LEGACY_UNSUPPORTED_PARTICLE_IDS) so the build fails or operator is alerted when coverage drifts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/main/java/dev/ohno/legacylink/handler/LegacyPacketHandler.java`:
- Around line 107-112: Add a startup validation that ensures every ID in
LEGACY_UNSUPPORTED_PARTICLE_IDS has an explicit entry in
LEGACY_PARTICLE_FALLBACKS so new unsupported IDs cannot silently default to
SMOKE; implement this check in a static initializer or a startup method in
LegacyPacketHandler (e.g., validateFallbacksOnStartup) and have it log and/or
throw an error listing any missing keys (referencing LEGACY_PARTICLE_FALLBACKS
and LEGACY_UNSUPPORTED_PARTICLE_IDS) so the build fails or operator is alerted
when coverage drifts.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5223d559-493f-4cac-afc4-081e63eb891b
⛔ Files ignored due to path filters (1)
prebuilt/legacylink-0.1.1.jaris excluded by!**/*.jar
📒 Files selected for processing (1)
src/main/java/dev/ohno/legacylink/handler/LegacyPacketHandler.java
Fail fast when unsupported particle IDs and fallback mappings drift, and require explicit fallback entries instead of silent SMOKE defaults. Made-with: Cursor
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/main/java/dev/ohno/legacylink/handler/LegacyPacketHandler.java (1)
571-600: Keep particle filtering tied to the explicit unsupported-id set.
stripSulfurOrModEntrystill applies toRegistries.PARTICLE_TYPE, so particle filtering can drift away fromLEGACY_UNSUPPORTED_PARTICLE_IDS/LEGACY_PARTICLE_FALLBACKSagain. A future sulfur-named particle would be removed from registry sync without trippingvalidateParticleFallbackCoverage()or getting a runtime fallback here.Possible simplification
- boolean stripSulfurOrModEntry = !entityTypeRegistry + boolean stripSulfurOrModEntry = !entityTypeRegistry + && !particleRegistry && (LegacyLinkConstants.SULFUR_BLOCK_IDS.contains(entryId) || LegacyLinkConstants.SULFUR_ITEM_IDS.contains(entryId) || entryId.equals(LegacyLinkConstants.SULFUR_CAVES_BIOME_ID) || entryId.equals(LegacyLinkConstants.SULFUR_CUBE_ENTITY_ID) || entryId.contains("sulfur"));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/dev/ohno/legacylink/handler/LegacyPacketHandler.java` around lines 571 - 600, The code allows stripSulfurOrModEntry to affect particle registries (particleRegistry / Registries.PARTICLE_TYPE), which can remove sulfur-named particles without using the explicit unsupported set (LegacyLinkConstants.LEGACY_UNSUPPORTED_PARTICLE_IDS / LEGACY_PARTICLE_FALLBACKS) and bypass validateParticleFallbackCoverage(); fix by preventing stripSulfurOrModEntry from applying when particleRegistry is true (e.g. change the stripSulfurOrModEntry boolean or the enclosing if to exclude particleRegistry), ensuring particle removal decisions rely only on strip26_2OnlyParticle and the LEGACY_UNSUPPORTED_PARTICLE_IDS/fallback logic so validateParticleFallbackCoverage() and runtime fallbacks remain effective.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/java/dev/ohno/legacylink/handler/LegacyPacketHandler.java`:
- Around line 662-672: In remapParticleOptionsForLegacy, calling
RegistryRemapper.remapBlockState(currentStateId) can throw and currently aborts
translation; wrap that call (and the subsequent lookup via
Block.BLOCK_STATE_REGISTRY.byId) in a try/catch that handles any runtime
exception by returning the original BlockParticleOption (or null/marker that
triggers the existing unsupported-particle fallback) so a single bad/missing
state does not fail the entire outbound translation; update the method around
the RegistryRemapper.remapBlockState and Block.BLOCK_STATE_REGISTRY.byId usage
to catch exceptions and degrade gracefully.
---
Nitpick comments:
In `@src/main/java/dev/ohno/legacylink/handler/LegacyPacketHandler.java`:
- Around line 571-600: The code allows stripSulfurOrModEntry to affect particle
registries (particleRegistry / Registries.PARTICLE_TYPE), which can remove
sulfur-named particles without using the explicit unsupported set
(LegacyLinkConstants.LEGACY_UNSUPPORTED_PARTICLE_IDS /
LEGACY_PARTICLE_FALLBACKS) and bypass validateParticleFallbackCoverage(); fix by
preventing stripSulfurOrModEntry from applying when particleRegistry is true
(e.g. change the stripSulfurOrModEntry boolean or the enclosing if to exclude
particleRegistry), ensuring particle removal decisions rely only on
strip26_2OnlyParticle and the LEGACY_UNSUPPORTED_PARTICLE_IDS/fallback logic so
validateParticleFallbackCoverage() and runtime fallbacks remain effective.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b7ea1c7a-11a3-4e29-94fb-1423309d37fd
⛔ Files ignored due to path filters (1)
prebuilt/legacylink-0.1.1.jaris excluded by!**/*.jar
📒 Files selected for processing (1)
src/main/java/dev/ohno/legacylink/handler/LegacyPacketHandler.java
Avoid aborting outbound translation when block particle state remaps fail and ensure sulfur keyword filtering does not apply to particle registries so explicit unsupported particle handling stays authoritative. Made-with: Cursor
Summary
clientbound level_particlespayloads that carry unsupported particle types to safe legacy fallback particles, preventing decode disconnects on 26.1 clients.prebuilt/legacylink-0.1.1.jarfrom the updated build output.Test plan
cd legacylink && ./gradlew buildcd legacylink-client-tests && ./.venv/bin/python run_e2e.py --skip-stability --skip-server-verifyclientbound/minecraft:level_particlesduring smoke run.Made with Cursor
Summary by CodeRabbit
Bug Fixes
New Features