Last active
April 15, 2026 10:39
-
-
Save Ensamisten/862e8253abcc2b93f7990e18f1cb52d4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package io.github.ensamisten.util; | |
| import net.minecraft.world.item.component.FireworkExplosion; | |
| public class CustomFireworkShapes { | |
| public static FireworkExplosion.Shape RAINBOW; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "item.minecraft.firework_star.shape.rainbow": "Rainbow" | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package io.github.ensamisten.mixin; | |
| import com.mojang.serialization.Codec; | |
| import io.github.ensamisten.util.CustomFireworkShapes; | |
| import io.netty.buffer.ByteBuf; | |
| import net.minecraft.network.codec.ByteBufCodecs; | |
| import net.minecraft.network.codec.StreamCodec; | |
| import net.minecraft.util.ByIdMap; | |
| import net.minecraft.util.StringRepresentable; | |
| import net.minecraft.world.item.component.FireworkExplosion; | |
| import org.spongepowered.asm.mixin.Final; | |
| import org.spongepowered.asm.mixin.Mixin; | |
| import org.spongepowered.asm.mixin.Mutable; | |
| import org.spongepowered.asm.mixin.Shadow; | |
| import org.spongepowered.asm.mixin.gen.Invoker; | |
| import org.spongepowered.asm.mixin.injection.At; | |
| import org.spongepowered.asm.mixin.injection.Inject; | |
| import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; | |
| import java.util.Arrays; | |
| import java.util.function.IntFunction; | |
| @Mixin(FireworkExplosion.Shape.class) | |
| public abstract class FireworkExplosionMixin { | |
| @Mutable @Final @Shadow | |
| private static FireworkExplosion.Shape[] $VALUES; | |
| // Rebuild these after appending | |
| @Mutable @Final @Shadow | |
| private static IntFunction<FireworkExplosion.Shape> BY_ID; | |
| @Mutable @Final @Shadow | |
| public static Codec<FireworkExplosion.Shape> CODEC; | |
| @Mutable @Final @Shadow | |
| public static StreamCodec<ByteBuf, FireworkExplosion.Shape> STREAM_CODEC; | |
| @Invoker("<init>") | |
| private static FireworkExplosion.Shape invokeInit( | |
| String internalName, int ordinal, int id, String name) { | |
| throw new AssertionError(); | |
| } | |
| @Inject(method = "<clinit>", at = @At("TAIL")) | |
| private static void addCustomShapes(CallbackInfo ci) { | |
| FireworkExplosion.Shape rainbow = invokeInit( | |
| "RAINBOW", | |
| $VALUES.length, | |
| 5, | |
| "enderman_face" | |
| ); | |
| FireworkExplosion.Shape[] newValues = Arrays.copyOf($VALUES, $VALUES.length + 1); | |
| newValues[$VALUES.length] = rainbow; | |
| $VALUES = newValues; | |
| // Rebuild BY_ID so network deserialization works | |
| BY_ID = ByIdMap.continuous( | |
| FireworkExplosion.Shape::getId, | |
| FireworkExplosion.Shape.values(), | |
| ByIdMap.OutOfBoundsStrategy.ZERO | |
| ); | |
| // Rebuild CODEC so string deserialization works — fixes "Unknown element name" | |
| CODEC = StringRepresentable.fromValues(FireworkExplosion.Shape::values); | |
| CustomFireworkShapes.RAINBOW = rainbow; | |
| STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, FireworkExplosion.Shape::getId); | |
| System.out.println("[YourMod] Registered shape, total shapes: " | |
| + FireworkExplosion.Shape.values().length); // should print 6 | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package io.github.ensamisten.client.mixin; | |
| import io.github.ensamisten.util.CustomFireworkShapes; | |
| import it.unimi.dsi.fastutil.ints.IntList; | |
| import net.minecraft.client.particle.FireworkParticles; | |
| import net.minecraft.world.item.component.FireworkExplosion; | |
| import org.spongepowered.asm.mixin.Final; | |
| import org.spongepowered.asm.mixin.Mixin; | |
| import org.spongepowered.asm.mixin.Shadow; | |
| import org.spongepowered.asm.mixin.injection.At; | |
| import org.spongepowered.asm.mixin.injection.Inject; | |
| import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; | |
| import java.util.List; | |
| @Mixin(FireworkParticles.Starter.class) | |
| public abstract class FireworkParticlesMixin { | |
| @Shadow private int life; | |
| @Final | |
| @Shadow private List<FireworkExplosion> explosions; | |
| @Shadow | |
| protected abstract void createParticle( | |
| double x, double y, double z, | |
| double xa, double ya, double za, | |
| IntList rgbColors, IntList fadeColors, | |
| boolean trail, boolean twinkle | |
| ); | |
| @Inject(method = "tick", at = @At("HEAD")) | |
| private void injectRainbow(CallbackInfo ci) { | |
| if (this.life % 2 != 0) return; | |
| int eIndex = this.life / 2; | |
| if (eIndex >= this.explosions.size()) return; | |
| FireworkExplosion explosion = this.explosions.get(eIndex); | |
| if (explosion.shape().getId() != 5) return; | |
| // Cast this to the accessor to get x/y/z from Particle superclass | |
| ParticleAccessor self = (ParticleAccessor)(Object) this; | |
| double px = self.getX(); | |
| double py = self.getY(); | |
| double pz = self.getZ(); | |
| boolean trail = explosion.hasTrail(); | |
| boolean twinkle = explosion.hasTwinkle(); | |
| IntList noFade = IntList.of(); | |
| int[] colors = { 0xFF0000, 0xFF7F00, 0xFFFF00, 0x00FF00, 0x0000FF, 0x4B0082, 0x9400D3 }; | |
| double[] radii = { 0.90, 0.78, 0.66, 0.54, 0.42, 0.30, 0.18 }; | |
| for (int b = 0; b < 7; b++) { | |
| IntList color = IntList.of(colors[b]); | |
| double r = radii[b]; | |
| int steps = 32; | |
| for (int i = 0; i <= steps; i++) { | |
| double angle = Math.PI * i / steps; | |
| double xa = -Math.cos(angle) * r; | |
| double ya = Math.sin(angle) * r; | |
| double za = 0.0; | |
| createParticle(px, py, pz, xa, ya, za, color, noFade, trail, twinkle); | |
| } | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package io.github.ensamisten.client.mixin; | |
| import net.minecraft.client.particle.Particle; | |
| import org.spongepowered.asm.mixin.Mixin; | |
| import org.spongepowered.asm.mixin.gen.Accessor; | |
| @Mixin(Particle.class) | |
| public interface ParticleAccessor { | |
| @Accessor("x") double getX(); | |
| @Accessor("y") double getY(); | |
| @Accessor("z") double getZ(); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package io.github.ensamisten.client.particle; | |
| public class Rainbow { | |
| // Arc from left to right across the top — semicircle points | |
| // createParticleShape will lerp between these points | |
| public static final double[][] RAINBOW_ARC = { | |
| {-1.0, 0.0}, // far left | |
| {-0.87, 0.5}, // left curve | |
| {-0.5, 0.87}, // upper left | |
| { 0.0, 1.0}, // top center | |
| { 0.5, 0.87}, // upper right | |
| { 0.87, 0.5}, // right curve | |
| { 1.0, 0.0}, // far right | |
| }; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment