Created
April 14, 2026 12:56
-
-
Save Ensamisten/e5b155c30fbf49edd84d65350a248aef 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.command.brigadier; | |
| import com.mojang.brigadier.Command; | |
| import com.mojang.brigadier.CommandDispatcher; | |
| import com.mojang.brigadier.arguments.IntegerArgumentType; | |
| import com.mojang.serialization.Codec; | |
| import com.mojang.serialization.codecs.RecordCodecBuilder; | |
| import it.unimi.dsi.fastutil.ints.IntArrayList; | |
| import net.minecraft.commands.CommandSourceStack; | |
| import net.minecraft.commands.Commands; | |
| import net.minecraft.commands.arguments.EntityArgument; | |
| import net.minecraft.commands.arguments.coordinates.BlockPosArgument; | |
| import net.minecraft.core.BlockPos; | |
| import net.minecraft.core.component.DataComponents; | |
| import net.minecraft.world.entity.Entity; | |
| import net.minecraft.world.entity.projectile.FireworkRocketEntity; | |
| import net.minecraft.world.item.ItemStack; | |
| import net.minecraft.world.item.Items; | |
| import net.minecraft.world.item.component.FireworkExplosion; | |
| import net.minecraft.world.item.component.Fireworks; | |
| import net.minecraft.world.level.Level; | |
| import org.jspecify.annotations.NonNull; | |
| import java.util.List; | |
| import java.util.random.RandomGenerator; | |
| public class FireworkCommand { | |
| // ── Codec for FireworkParams (used for SavedData / DataAttachment) ── | |
| public record FireworkParams(int shape, int color, int flight) { | |
| public static final Codec<FireworkParams> CODEC = RecordCodecBuilder.create(instance -> instance.group( | |
| Codec.INT.fieldOf("shape").forGetter(FireworkParams::shape), | |
| Codec.INT.fieldOf("color").forGetter(FireworkParams::color), | |
| Codec.INT.fieldOf("flight").forGetter(FireworkParams::flight) | |
| ).apply(instance, FireworkParams::new)); | |
| } | |
| public static void register(CommandDispatcher<CommandSourceStack> dispatcher) { | |
| dispatcher.register(Commands.literal("firework") | |
| // ── /firework <pos> [count] [shape] [color] [flight] ── | |
| .then(Commands.argument("pos", BlockPosArgument.blockPos()) | |
| .executes(ctx -> execute(ctx.getSource().getLevel(), | |
| BlockPosArgument.getLoadedBlockPos(ctx, "pos"), | |
| 1, 0, 0xFFFFFF, 1)) | |
| .then(Commands.argument("count", IntegerArgumentType.integer(1)) | |
| .then(Commands.argument("shape", IntegerArgumentType.integer(0, 4)) | |
| .then(Commands.argument("color", IntegerArgumentType.integer(0, 0xFFFFFF)) | |
| .then(Commands.argument("flight", IntegerArgumentType.integer(1, 3)) | |
| .executes(ctx -> execute( | |
| ctx.getSource().getLevel(), | |
| BlockPosArgument.getLoadedBlockPos(ctx, "pos"), | |
| IntegerArgumentType.getInteger(ctx, "count"), | |
| IntegerArgumentType.getInteger(ctx, "shape"), | |
| IntegerArgumentType.getInteger(ctx, "color"), | |
| IntegerArgumentType.getInteger(ctx, "flight") | |
| )) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ) | |
| // ── /firework entity <targets> <count> <shape> <color> <flight> ── | |
| .then(Commands.literal("entity") | |
| .then(Commands.argument("targets", EntityArgument.entities()) | |
| .then(Commands.argument("count", IntegerArgumentType.integer(1)) | |
| .then(Commands.argument("shape", IntegerArgumentType.integer(0, 4)) | |
| .then(Commands.argument("color", IntegerArgumentType.integer(0, 0xFFFFFF)) | |
| .then(Commands.argument("flight", IntegerArgumentType.integer(1, 3)) | |
| .executes(ctx -> executeFromEntities( | |
| ctx.getSource().getLevel(), | |
| EntityArgument.getEntities(ctx, "targets"), | |
| IntegerArgumentType.getInteger(ctx, "count"), | |
| IntegerArgumentType.getInteger(ctx, "shape"), | |
| IntegerArgumentType.getInteger(ctx, "color"), | |
| IntegerArgumentType.getInteger(ctx, "flight") | |
| )) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ); | |
| } | |
| // ── Spawn at block pos ──────────────────────────────────────────────── | |
| private static int execute(Level level, BlockPos pos, | |
| int count, int shape, int color, int flight) { | |
| RandomGenerator rng = RandomGenerator.getDefault(); | |
| for (int i = 0; i < count; i++) { | |
| double ox = (rng.nextDouble() - 0.5) * 5.0; | |
| double oy = (rng.nextDouble() - 0.5) * 5.0; | |
| double oz = (rng.nextDouble() - 0.5) * 5.0; | |
| spawnFirework(level, | |
| pos.getX() + 0.5 + ox, | |
| pos.getY() + 0.5 + oy, | |
| pos.getZ() + 0.5 + oz, | |
| shape, color, flight); | |
| } | |
| return Command.SINGLE_SUCCESS; | |
| } | |
| // ── Spawn at entity positions ───────────────────────────────────────── | |
| private static int executeFromEntities(Level level, Iterable<? extends Entity> targets, | |
| int count, int shape, int color, int flight) { | |
| RandomGenerator rng = RandomGenerator.getDefault(); | |
| for (Entity target : targets) { | |
| for (int i = 0; i < count; i++) { | |
| double ox = (rng.nextDouble() - 0.5) * 5.0; | |
| double oy = (rng.nextDouble() - 0.5) * 5.0; | |
| double oz = (rng.nextDouble() - 0.5) * 5.0; | |
| spawnFirework(level, | |
| target.getX() + 0.5 + ox, | |
| target.getY() + target.getBbHeight() + oy, | |
| target.getZ() + 0.5 + oz, | |
| shape, color, flight); | |
| } | |
| } | |
| return Command.SINGLE_SUCCESS; | |
| } | |
| // ── Core spawn logic using modern DataComponents ────────────────────── | |
| private static void spawnFirework(Level level, double x, double y, double z, | |
| int shape, int color, int flight) { | |
| // ── Map int → FireworkExplosion.Shape ───────────────────── | |
| FireworkExplosion explosion = getFireworkExplosion(shape, color); | |
| // ── Build the rocket ItemStack with DataComponents ──────── | |
| ItemStack rocket = new ItemStack(Items.FIREWORK_ROCKET); | |
| rocket.set(DataComponents.FIREWORKS, new Fireworks(flight, List.of(explosion))); | |
| // ── Spawn entity ────────────────────────────────────────── | |
| FireworkRocketEntity entity = new FireworkRocketEntity(level, x, y, z, rocket); | |
| entity.setDeltaMovement(entity.getDeltaMovement().add(0, 0.05, 0)); | |
| level.addFreshEntity(entity); | |
| } | |
| private static @NonNull FireworkExplosion getFireworkExplosion(int shape, int color) { | |
| FireworkExplosion.Shape explosionShape = switch (shape) { | |
| case 1 -> FireworkExplosion.Shape.LARGE_BALL; | |
| case 2 -> FireworkExplosion.Shape.STAR; | |
| case 3 -> FireworkExplosion.Shape.CREEPER; | |
| case 4 -> FireworkExplosion.Shape.BURST; | |
| default -> FireworkExplosion.Shape.SMALL_BALL; | |
| }; | |
| // ── Build explosion via DataComponent (replaces CompoundTag) ── | |
| // ── Build explosion via DataComponent ───────────────────── | |
| return new FireworkExplosion( | |
| explosionShape, | |
| new IntArrayList(new int[]{color}), // colors — IntList not int[] | |
| new IntArrayList(new int[]{0xFFFFFF}), // fade — IntList not int[] | |
| true, // trail | |
| true // flicker | |
| ); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment