#1279: Back Particle by a minecraft registry

This commit is contained in:
DerFrZocker 2023-10-21 13:42:09 +11:00 committed by md_5
parent f4d977e794
commit 67a52a6485
No known key found for this signature in database
GPG Key ID: E8E901AC7C617C11
6 changed files with 456 additions and 230 deletions

View File

@ -1,11 +1,10 @@
package org.bukkit.craftbukkit;
import com.google.common.base.Preconditions;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.core.BlockPosition;
import java.util.function.BiFunction;
import net.minecraft.core.IRegistry;
import net.minecraft.core.particles.DustColorTransitionOptions;
import net.minecraft.core.particles.ParticleParam;
import net.minecraft.core.particles.ParticleParamBlock;
@ -15,15 +14,17 @@ import net.minecraft.core.particles.ParticleType;
import net.minecraft.core.particles.SculkChargeParticleOptions;
import net.minecraft.core.particles.ShriekParticleOption;
import net.minecraft.core.particles.VibrationParticleOption;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.MinecraftKey;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.gameevent.BlockPositionSource;
import net.minecraft.world.level.gameevent.EntityPositionSource;
import net.minecraft.world.level.gameevent.PositionSource;
import org.bukkit.Color;
import org.bukkit.Keyed;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.Particle;
import org.bukkit.Registry;
import org.bukkit.Vibration;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
@ -31,205 +32,184 @@ import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.util.CraftLocation;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
import org.joml.Vector3f;
public enum CraftParticle {
public abstract class CraftParticle<D> implements Keyed {
EXPLOSION_NORMAL("poof"),
EXPLOSION_LARGE("explosion"),
EXPLOSION_HUGE("explosion_emitter"),
FIREWORKS_SPARK("firework"),
WATER_BUBBLE("bubble"),
WATER_SPLASH("splash"),
WATER_WAKE("fishing"),
SUSPENDED("underwater"),
SUSPENDED_DEPTH("underwater"),
CRIT("crit"),
CRIT_MAGIC("enchanted_hit"),
SMOKE_NORMAL("smoke"),
SMOKE_LARGE("large_smoke"),
SPELL("effect"),
SPELL_INSTANT("instant_effect"),
SPELL_MOB("entity_effect"),
SPELL_MOB_AMBIENT("ambient_entity_effect"),
SPELL_WITCH("witch"),
DRIP_WATER("dripping_water"),
DRIP_LAVA("dripping_lava"),
VILLAGER_ANGRY("angry_villager"),
VILLAGER_HAPPY("happy_villager"),
TOWN_AURA("mycelium"),
NOTE("note"),
PORTAL("portal"),
ENCHANTMENT_TABLE("enchant"),
FLAME("flame"),
LAVA("lava"),
CLOUD("cloud"),
REDSTONE("dust"),
SNOWBALL("item_snowball"),
SNOW_SHOVEL("item_snowball"),
SLIME("item_slime"),
HEART("heart"),
ITEM_CRACK("item"),
BLOCK_CRACK("block"),
BLOCK_DUST("block"),
WATER_DROP("rain"),
MOB_APPEARANCE("elder_guardian"),
DRAGON_BREATH("dragon_breath"),
END_ROD("end_rod"),
DAMAGE_INDICATOR("damage_indicator"),
SWEEP_ATTACK("sweep_attack"),
FALLING_DUST("falling_dust"),
TOTEM("totem_of_undying"),
SPIT("spit"),
SQUID_INK("squid_ink"),
BUBBLE_POP("bubble_pop"),
CURRENT_DOWN("current_down"),
BUBBLE_COLUMN_UP("bubble_column_up"),
NAUTILUS("nautilus"),
DOLPHIN("dolphin"),
SNEEZE("sneeze"),
CAMPFIRE_COSY_SMOKE("campfire_cosy_smoke"),
CAMPFIRE_SIGNAL_SMOKE("campfire_signal_smoke"),
COMPOSTER("composter"),
FLASH("flash"),
FALLING_LAVA("falling_lava"),
LANDING_LAVA("landing_lava"),
FALLING_WATER("falling_water"),
DRIPPING_HONEY("dripping_honey"),
FALLING_HONEY("falling_honey"),
LANDING_HONEY("landing_honey"),
FALLING_NECTAR("falling_nectar"),
SOUL_FIRE_FLAME("soul_fire_flame"),
ASH("ash"),
CRIMSON_SPORE("crimson_spore"),
WARPED_SPORE("warped_spore"),
SOUL("soul"),
DRIPPING_OBSIDIAN_TEAR("dripping_obsidian_tear"),
FALLING_OBSIDIAN_TEAR("falling_obsidian_tear"),
LANDING_OBSIDIAN_TEAR("landing_obsidian_tear"),
REVERSE_PORTAL("reverse_portal"),
WHITE_ASH("white_ash"),
DUST_COLOR_TRANSITION("dust_color_transition"),
VIBRATION("vibration"),
FALLING_SPORE_BLOSSOM("falling_spore_blossom"),
SPORE_BLOSSOM_AIR("spore_blossom_air"),
SMALL_FLAME("small_flame"),
SNOWFLAKE("snowflake"),
DRIPPING_DRIPSTONE_LAVA("dripping_dripstone_lava"),
FALLING_DRIPSTONE_LAVA("falling_dripstone_lava"),
DRIPPING_DRIPSTONE_WATER("dripping_dripstone_water"),
FALLING_DRIPSTONE_WATER("falling_dripstone_water"),
GLOW_SQUID_INK("glow_squid_ink"),
GLOW("glow"),
WAX_ON("wax_on"),
WAX_OFF("wax_off"),
ELECTRIC_SPARK("electric_spark"),
SCRAPE("scrape"),
BLOCK_MARKER("block_marker"),
SONIC_BOOM("sonic_boom"),
SCULK_SOUL("sculk_soul"),
SCULK_CHARGE("sculk_charge"),
SCULK_CHARGE_POP("sculk_charge_pop"),
SHRIEK("shriek"),
CHERRY_LEAVES("cherry_leaves"),
EGG_CRACK("egg_crack"),
// ----- Legacy Separator -----
LEGACY_BLOCK_CRACK("block"),
LEGACY_BLOCK_DUST("block"),
LEGACY_FALLING_DUST("falling_dust");
private final MinecraftKey minecraftKey;
private final Particle bukkit;
private static final BiMap<Particle, MinecraftKey> particles;
private static final Map<Particle, Particle> aliases;
private static final Registry<CraftParticle<?>> CRAFT_PARTICLE_REGISTRY = new CraftParticleRegistry(CraftRegistry.getMinecraftRegistry(Registries.PARTICLE_TYPE));
public static Particle minecraftToBukkit(net.minecraft.core.particles.Particle<?> minecraft) {
Preconditions.checkArgument(minecraft != null);
IRegistry<net.minecraft.core.particles.Particle<?>> registry = CraftRegistry.getMinecraftRegistry(Registries.PARTICLE_TYPE);
Particle bukkit = Registry.PARTICLE_TYPE.get(CraftNamespacedKey.fromMinecraft(registry.getResourceKey(minecraft).orElseThrow().location()));
Preconditions.checkArgument(bukkit != null);
return bukkit;
}
public static net.minecraft.core.particles.Particle<?> bukkitToMinecraft(Particle bukkit) {
Preconditions.checkArgument(bukkit != null);
return CraftRegistry.getMinecraftRegistry(Registries.PARTICLE_TYPE)
.getOptional(CraftNamespacedKey.toMinecraft(bukkit.getKey())).orElseThrow();
}
public static <D> ParticleParam createParticleParam(Particle particle, D data) {
Preconditions.checkArgument(particle != null);
CraftParticle<D> craftParticle = (CraftParticle<D>) CRAFT_PARTICLE_REGISTRY.get(particle.getKey());
Preconditions.checkArgument(craftParticle != null);
return craftParticle.createParticleParam(data);
}
public static <T> T convertLegacy(T object) {
if (object instanceof MaterialData mat) {
return (T) CraftBlockData.fromData(CraftMagicNumbers.getBlock(mat));
}
return object;
}
public static Particle convertLegacy(Particle particle) {
return switch (particle) {
case LEGACY_BLOCK_DUST -> Particle.BLOCK_DUST;
case LEGACY_FALLING_DUST -> Particle.FALLING_DUST;
case LEGACY_BLOCK_CRACK -> Particle.BLOCK_CRACK;
default -> particle;
};
}
private final NamespacedKey key;
private final net.minecraft.core.particles.Particle<?> particle;
private final Class<D> clazz;
public CraftParticle(NamespacedKey key, net.minecraft.core.particles.Particle<?> particle, Class<D> clazz) {
this.key = key;
this.particle = particle;
this.clazz = clazz;
}
public net.minecraft.core.particles.Particle<?> getHandle() {
return particle;
}
public abstract ParticleParam createParticleParam(D data);
@Override
public NamespacedKey getKey() {
return key;
}
public static class CraftParticleRegistry extends CraftRegistry<CraftParticle<?>, net.minecraft.core.particles.Particle<?>> {
private static final Map<NamespacedKey, BiFunction<NamespacedKey, net.minecraft.core.particles.Particle<?>, CraftParticle<?>>> PARTICLE_MAP = new HashMap<>();
private static final BiFunction<NamespacedKey, net.minecraft.core.particles.Particle<?>, CraftParticle<?>> VOID_FUNCTION = (name, particle) -> new CraftParticle<>(name, particle, Void.class) {
@Override
public ParticleParam createParticleParam(Void data) {
return (ParticleType) getHandle();
}
};
static {
particles = HashBiMap.create();
aliases = new HashMap<>();
for (CraftParticle particle : CraftParticle.values()) {
if (particles.containsValue(particle.minecraftKey)) {
aliases.put(particle.bukkit, particles.inverse().get(particle.minecraftKey));
} else {
particles.put(particle.bukkit, particle.minecraftKey);
}
}
}
private CraftParticle(String minecraftKey) {
this.minecraftKey = new MinecraftKey(minecraftKey);
this.bukkit = Particle.valueOf(this.name());
Preconditions.checkState(bukkit != null, "Bukkit particle %s does not exist", this.name());
}
public static ParticleParam toNMS(Particle bukkit) {
return toNMS(bukkit, null);
}
public static <T> ParticleParam toNMS(Particle particle, T obj) {
Particle canonical = particle;
if (aliases.containsKey(particle)) {
canonical = aliases.get(particle);
}
net.minecraft.core.particles.Particle nms = BuiltInRegistries.PARTICLE_TYPE.get(particles.get(canonical));
Preconditions.checkArgument(nms != null, "No NMS particle %s", particle);
if (particle.getDataType().equals(Void.class)) {
return (ParticleType) nms;
}
Preconditions.checkArgument(obj != null, "Particle %s requires data, null provided", particle);
if (particle.getDataType().equals(ItemStack.class)) {
ItemStack itemStack = (ItemStack) obj;
return new ParticleParamItem((net.minecraft.core.particles.Particle<ParticleParamItem>) nms, CraftItemStack.asNMSCopy(itemStack));
}
if (particle.getDataType() == MaterialData.class) {
MaterialData data = (MaterialData) obj;
return new ParticleParamBlock((net.minecraft.core.particles.Particle<ParticleParamBlock>) nms, CraftMagicNumbers.getBlock(data));
}
if (particle.getDataType() == BlockData.class) {
BlockData data = (BlockData) obj;
return new ParticleParamBlock((net.minecraft.core.particles.Particle<ParticleParamBlock>) nms, ((CraftBlockData) data).getState());
}
if (particle.getDataType() == Particle.DustOptions.class) {
Particle.DustOptions data = (Particle.DustOptions) obj;
BiFunction<NamespacedKey, net.minecraft.core.particles.Particle<?>, CraftParticle<?>> dustOptionsFunction = (name, particle) -> new CraftParticle<>(name, particle, Particle.DustOptions.class) {
@Override
public ParticleParam createParticleParam(Particle.DustOptions data) {
Color color = data.getColor();
return new ParticleParamRedstone(new Vector3f(color.getRed() / 255.0f, color.getGreen() / 255.0f, color.getBlue() / 255.0f), data.getSize());
}
if (particle.getDataType() == Particle.DustTransition.class) {
Particle.DustTransition data = (Particle.DustTransition) obj;
};
BiFunction<NamespacedKey, net.minecraft.core.particles.Particle<?>, CraftParticle<?>> itemStackFunction = (name, particle) -> new CraftParticle<>(name, particle, ItemStack.class) {
@Override
public ParticleParam createParticleParam(ItemStack data) {
return new ParticleParamItem((net.minecraft.core.particles.Particle<ParticleParamItem>) getHandle(), CraftItemStack.asNMSCopy(data));
}
};
BiFunction<NamespacedKey, net.minecraft.core.particles.Particle<?>, CraftParticle<?>> blockDataFunction = (name, particle) -> new CraftParticle<>(name, particle, BlockData.class) {
@Override
public ParticleParam createParticleParam(BlockData data) {
return new ParticleParamBlock((net.minecraft.core.particles.Particle<ParticleParamBlock>) getHandle(), ((CraftBlockData) data).getState());
}
};
BiFunction<NamespacedKey, net.minecraft.core.particles.Particle<?>, CraftParticle<?>> dustTransitionFunction = (name, particle) -> new CraftParticle<>(name, particle, Particle.DustTransition.class) {
@Override
public ParticleParam createParticleParam(Particle.DustTransition data) {
Color from = data.getColor();
Color to = data.getToColor();
return new DustColorTransitionOptions(new Vector3f(from.getRed() / 255.0f, from.getGreen() / 255.0f, from.getBlue() / 255.0f), new Vector3f(to.getRed() / 255.0f, to.getGreen() / 255.0f, to.getBlue() / 255.0f), data.getSize());
}
if (particle.getDataType() == Vibration.class) {
Vibration vibration = (Vibration) obj;
};
BiFunction<NamespacedKey, net.minecraft.core.particles.Particle<?>, CraftParticle<?>> vibrationFunction = (name, particle) -> new CraftParticle<>(name, particle, Vibration.class) {
@Override
public ParticleParam createParticleParam(Vibration data) {
PositionSource source;
if (vibration.getDestination() instanceof Vibration.Destination.BlockDestination) {
Location destination = ((Vibration.Destination.BlockDestination) vibration.getDestination()).getLocation();
if (data.getDestination() instanceof Vibration.Destination.BlockDestination) {
Location destination = ((Vibration.Destination.BlockDestination) data.getDestination()).getLocation();
source = new BlockPositionSource(CraftLocation.toBlockPosition(destination));
} else if (vibration.getDestination() instanceof Vibration.Destination.EntityDestination) {
Entity destination = ((CraftEntity) ((Vibration.Destination.EntityDestination) vibration.getDestination()).getEntity()).getHandle();
} else if (data.getDestination() instanceof Vibration.Destination.EntityDestination) {
Entity destination = ((CraftEntity) ((Vibration.Destination.EntityDestination) data.getDestination()).getEntity()).getHandle();
source = new EntityPositionSource(destination, destination.getEyeHeight());
} else {
throw new IllegalArgumentException("Unknown vibration destination " + vibration.getDestination());
throw new IllegalArgumentException("Unknown vibration destination " + data.getDestination());
}
return new VibrationParticleOption(source, vibration.getArrivalTime());
return new VibrationParticleOption(source, data.getArrivalTime());
}
if (particle.getDataType() == Float.class) {
return new SculkChargeParticleOptions((Float) obj);
};
BiFunction<NamespacedKey, net.minecraft.core.particles.Particle<?>, CraftParticle<?>> floatFunction = (name, particle) -> new CraftParticle<>(name, particle, Float.class) {
@Override
public ParticleParam createParticleParam(Float data) {
return new SculkChargeParticleOptions(data);
}
if (particle.getDataType() == Integer.class) {
return new ShriekParticleOption((Integer) obj);
};
BiFunction<NamespacedKey, net.minecraft.core.particles.Particle<?>, CraftParticle<?>> integerFunction = (name, particle) -> new CraftParticle<>(name, particle, Integer.class) {
@Override
public ParticleParam createParticleParam(Integer data) {
return new ShriekParticleOption(data);
}
throw new IllegalArgumentException(particle.getDataType().toString());
};
add("dust", dustOptionsFunction);
add("item", itemStackFunction);
add("block", blockDataFunction);
add("falling_dust", blockDataFunction);
add("dust_color_transition", dustTransitionFunction);
add("vibration", vibrationFunction);
add("sculk_charge", floatFunction);
add("shriek", integerFunction);
add("block_marker", blockDataFunction);
}
public static Particle minecraftToBukkit(net.minecraft.core.particles.Particle<?> minecraft) {
return particles.inverse().get(BuiltInRegistries.PARTICLE_TYPE.getKey(minecraft));
private static void add(String name, BiFunction<NamespacedKey, net.minecraft.core.particles.Particle<?>, CraftParticle<?>> function) {
PARTICLE_MAP.put(NamespacedKey.fromString(name), function);
}
public CraftParticleRegistry(IRegistry<net.minecraft.core.particles.Particle<?>> minecraftRegistry) {
super(CraftParticle.class, minecraftRegistry, null);
}
@Override
public CraftParticle<?> createBukkit(NamespacedKey namespacedKey, net.minecraft.core.particles.Particle<?> particle) {
if (particle == null) {
return null;
}
BiFunction<NamespacedKey, net.minecraft.core.particles.Particle<?>, CraftParticle<?>> function = PARTICLE_MAP.getOrDefault(namespacedKey, VOID_FUNCTION);
return function.apply(namespacedKey, particle);
}
}
}

View File

@ -67,13 +67,13 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
return null;
}
private final Class<B> bukkitClass;
private final Class<? super B> bukkitClass;
private final Map<NamespacedKey, B> cache = new HashMap<>();
private final IRegistry<M> minecraftRegistry;
private final BiFunction<NamespacedKey, M, B> minecraftToBukkit;
private boolean init;
public CraftRegistry(Class<B> bukkitClass, IRegistry<M> minecraftRegistry, BiFunction<NamespacedKey, M, B> minecraftToBukkit) {
public CraftRegistry(Class<? super B> bukkitClass, IRegistry<M> minecraftRegistry, BiFunction<NamespacedKey, M, B> minecraftToBukkit) {
this.bukkitClass = bukkitClass;
this.minecraftRegistry = minecraftRegistry;
this.minecraftToBukkit = minecraftToBukkit;

View File

@ -1788,12 +1788,14 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public <T> void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, T data, boolean force) {
particle = CraftParticle.convertLegacy(particle);
data = CraftParticle.convertLegacy(data);
if (data != null) {
Preconditions.checkArgument(particle.getDataType().isInstance(data), "data (%s) should be %s", data.getClass(), particle.getDataType());
}
getHandle().sendParticles(
null, // Sender
CraftParticle.toNMS(particle, data), // Particle
CraftParticle.createParticleParam(particle, data), // Particle
x, y, z, // Position
count, // Count
offsetX, offsetY, offsetZ, // Random offset

View File

@ -121,7 +121,12 @@ public class CraftAreaEffectCloud extends CraftEntity implements AreaEffectCloud
@Override
public <T> void setParticle(Particle particle, T data) {
getHandle().setParticle(CraftParticle.toNMS(particle, data));
particle = CraftParticle.convertLegacy(particle);
data = CraftParticle.convertLegacy(data);
if (data != null) {
Preconditions.checkArgument(particle.getDataType().isInstance(data), "data (%s) should be %s", data.getClass(), particle.getDataType());
}
getHandle().setParticle(CraftParticle.createParticleParam(particle, data));
}
@Override

View File

@ -2080,10 +2080,12 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public <T> void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, T data) {
particle = CraftParticle.convertLegacy(particle);
data = CraftParticle.convertLegacy(data);
if (data != null) {
Preconditions.checkArgument(particle.getDataType().isInstance(data), "data (%s) should be %s", data.getClass(), particle.getDataType());
}
PacketPlayOutWorldParticles packetplayoutworldparticles = new PacketPlayOutWorldParticles(CraftParticle.toNMS(particle, data), true, (float) x, (float) y, (float) z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count);
PacketPlayOutWorldParticles packetplayoutworldparticles = new PacketPlayOutWorldParticles(CraftParticle.createParticleParam(particle, data), true, (float) x, (float) y, (float) z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count);
getHandle().connection.send(packetplayoutworldparticles);
}

View File

@ -1,43 +1,280 @@
package org.bukkit;
import static org.junit.jupiter.api.Assertions.*;
import net.minecraft.core.registries.BuiltInRegistries;
import com.mojang.serialization.DataResult;
import java.util.Optional;
import java.util.stream.Stream;
import net.minecraft.core.particles.DustColorTransitionOptions;
import net.minecraft.core.particles.ParticleParam;
import net.minecraft.core.particles.ParticleParamBlock;
import net.minecraft.core.particles.ParticleParamItem;
import net.minecraft.core.particles.ParticleParamRedstone;
import net.minecraft.core.particles.ParticleType;
import net.minecraft.core.particles.SculkChargeParticleOptions;
import net.minecraft.core.particles.ShriekParticleOption;
import net.minecraft.core.particles.VibrationParticleOption;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.DynamicOpsNBT;
import net.minecraft.nbt.NBTBase;
import net.minecraft.resources.MinecraftKey;
import net.minecraft.world.phys.Vec3D;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.CraftParticle;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
import org.bukkit.support.AbstractTestingBase;
import org.junit.jupiter.api.Test;
import org.joml.Vector3f;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.MethodSource;
public class ParticleTest extends AbstractTestingBase {
@Test
public void verifyMapping() {
for (Particle bukkit : Particle.values()) {
Object data = null;
if (bukkit.getDataType().equals(ItemStack.class)) {
data = new ItemStack(Material.STONE);
} else if (bukkit.getDataType() == MaterialData.class) {
data = new MaterialData(Material.LEGACY_STONE);
} else if (bukkit.getDataType() == Particle.DustOptions.class) {
data = new Particle.DustOptions(Color.BLACK, 0);
} else if (bukkit.getDataType() == Particle.DustTransition.class) {
data = new Particle.DustTransition(Color.BLACK, Color.WHITE, 0);
} else if (bukkit.getDataType() == Vibration.class) {
data = new Vibration(new Location(null, 0, 0, 0), new Vibration.Destination.BlockDestination(new Location(null, 0, 0, 0)), 0);
} else if (bukkit.getDataType() == BlockData.class) {
data = CraftBlockData.newData(Material.STONE, "");
} else if (bukkit.getDataType() == Float.class) {
data = 1.0F;
} else if (bukkit.getDataType() == Integer.class) {
data = 0;
public static Stream<Arguments> data() {
return CraftRegistry.getMinecraftRegistry(Registries.PARTICLE_TYPE).keySet().stream().map(Arguments::of);
}
assertNotNull(CraftParticle.toNMS(bukkit, data), "Missing Bukkit->NMS particle mapping for " + bukkit);
}
for (net.minecraft.core.particles.Particle nms : BuiltInRegistries.PARTICLE_TYPE) {
assertNotNull(CraftParticle.minecraftToBukkit(nms), "Missing NMS->Bukkit particle mapping for " + BuiltInRegistries.PARTICLE_TYPE.getKey(nms));
@ParameterizedTest
@MethodSource("data")
public void testBukkitValuesPresent(MinecraftKey minecraft) {
// TODO: 10/19/23 Remove with enum PR, it is then no longer needed, since the enum PR has a extra test for this
assertNotNull(Registry.PARTICLE_TYPE.get(CraftNamespacedKey.fromMinecraft(minecraft)), String.format("""
No bukkit particle found for minecraft particle %s.
Please add the particle to bukkit.
""", minecraft));
}
@ParameterizedTest
@EnumSource(Particle.class)
public void testMinecraftValuesPresent(Particle bukkit) {
// TODO: 10/19/23 Remove with enum PR, it is then no longer needed, since the enum PR has a extra test for this
bukkit = CraftParticle.convertLegacy(bukkit);
Particle finalBukkit = bukkit;
assertDoesNotThrow(() -> CraftParticle.bukkitToMinecraft(finalBukkit), String.format("""
No minecraft particle found for bukkit particle %s.
Please map the bukkit particle to a minecraft particle.
""", bukkit.getKey()));
}
@ParameterizedTest
@EnumSource(Particle.class)
public void testRightParticleParamCreation(Particle bukkit) {
bukkit = CraftParticle.convertLegacy(bukkit);
net.minecraft.core.particles.Particle<?> minecraft = CraftParticle.bukkitToMinecraft(bukkit);
if (bukkit.getDataType().equals(Void.class)) {
testEmptyData(bukkit, minecraft);
return;
}
if (bukkit.getDataType().equals(Particle.DustOptions.class)) {
testDustOption(bukkit, minecraft);
return;
}
if (bukkit.getDataType().equals(ItemStack.class)) {
testItemStack(bukkit, minecraft);
return;
}
if (bukkit.getDataType().equals(BlockData.class)) {
testBlockData(bukkit, minecraft);
return;
}
if (bukkit.getDataType().equals(Particle.DustTransition.class)) {
testDustTransition(bukkit, minecraft);
return;
}
if (bukkit.getDataType().equals(Vibration.class)) {
testVibration(bukkit, minecraft);
return;
}
if (bukkit.getDataType().equals(Float.class)) {
testFloat(bukkit, minecraft);
return;
}
if (bukkit.getDataType().equals(Integer.class)) {
testInteger(bukkit, minecraft);
return;
}
fail(String.format("""
No test found for particle %s.
Please add a test case for it here.
""", bukkit.getKey()));
}
private <T extends ParticleParam> void testEmptyData(Particle bukkit, net.minecraft.core.particles.Particle<T> minecraft) {
createAndTest(bukkit, minecraft, null, ParticleType.class);
}
private <T extends ParticleParam> void testDustOption(Particle bukkit, net.minecraft.core.particles.Particle<T> minecraft) {
Particle.DustOptions dustOptions = new Particle.DustOptions(Color.fromRGB(236, 28, 36), 0.1205f);
ParticleParamRedstone param = createAndTest(bukkit, minecraft, dustOptions, ParticleParamRedstone.class);
assertEquals(0.1205f, param.getScale(), 0.001, String.format("""
Dust option scale for particle %s do not match.
Did something change in the implementation or minecraft?
""", bukkit.getKey()));
Vector3f expectedColor = new Vector3f(0.92549f, 0.1098f, 0.14117647f);
assertTrue(expectedColor.equals(param.getColor(), 0.001f), String.format("""
Dust option color for particle %s do not match.
Did something change in the implementation or minecraft?
Expected: %s.
Got: %s.
""", bukkit.getKey(), expectedColor, param.getColor())); // Print expected and got since we use assert true
}
private <T extends ParticleParam> void testItemStack(Particle bukkit, net.minecraft.core.particles.Particle<T> minecraft) {
ItemStack itemStack = new ItemStack(Material.STONE);
ParticleParamItem param = createAndTest(bukkit, minecraft, itemStack, ParticleParamItem.class);
assertEquals(itemStack, CraftItemStack.asBukkitCopy(param.getItem()), String.format("""
ItemStack for particle %s do not match.
Did something change in the implementation or minecraft?
""", bukkit.getKey()));
}
private <T extends ParticleParam> void testBlockData(Particle bukkit, net.minecraft.core.particles.Particle<T> minecraft) {
BlockData blockData = Bukkit.createBlockData(Material.STONE);
ParticleParamBlock param = createAndTest(bukkit, minecraft, blockData, ParticleParamBlock.class);
assertEquals(blockData, CraftBlockData.fromData(param.getState()), String.format("""
Block data for particle %s do not match.
Did something change in the implementation or minecraft?
""", bukkit.getKey()));
}
private <T extends ParticleParam> void testDustTransition(Particle bukkit, net.minecraft.core.particles.Particle<T> minecraft) {
Particle.DustTransition dustTransition = new Particle.DustTransition(Color.fromRGB(236, 28, 36), Color.fromRGB(107, 159, 181), 0.1205f);
DustColorTransitionOptions param = createAndTest(bukkit, minecraft, dustTransition, DustColorTransitionOptions.class);
assertEquals(0.1205f, param.getScale(), 0.001, String.format("""
Dust transition scale for particle %s do not match.
Did something change in the implementation or minecraft?
""", bukkit.getKey()));
Vector3f expectedFrom = new Vector3f(0.92549f, 0.1098f, 0.14117647f);
assertTrue(expectedFrom.equals(param.getFromColor(), 0.001f), String.format("""
Dust transition from color for particle %s do not match.
Did something change in the implementation or minecraft?
Expected: %s.
Got: %s.
""", bukkit.getKey(), expectedFrom, param.getColor())); // Print expected and got since we use assert true
Vector3f expectedTo = new Vector3f(0.4196f, 0.6235294f, 0.7098f);
assertTrue(expectedTo.equals(param.getToColor(), 0.001f), String.format("""
Dust transition to color for particle %s do not match.
Did something change in the implementation or minecraft?
Expected: %s.
Got: %s.
""", bukkit.getKey(), expectedTo, param.getColor())); // Print expected and got since we use assert true
}
private <T extends ParticleParam> void testVibration(Particle bukkit, net.minecraft.core.particles.Particle<T> minecraft) {
Vibration vibration = new Vibration(new Location(null, 3, 1, 4), new Vibration.Destination.BlockDestination(new Location(null, 1, 5, 9)), 265);
VibrationParticleOption param = createAndTest(bukkit, minecraft, vibration, VibrationParticleOption.class);
assertEquals(265, param.getArrivalInTicks(), String.format("""
Vibration ticks for particle %s do not match.
Did something change in the implementation or minecraft?
""", bukkit.getKey()));
Optional<Vec3D> pos = param.getDestination().getPosition(null);
assertTrue(pos.isPresent(), String.format("""
Vibration position for particle %s is not present.
Did something change in the implementation or minecraft?
""", bukkit.getKey()));
// Add 0.5 since it gets centered to the block
assertEquals(new Vec3D(1.5, 5.5, 9.5), pos.get(), String.format("""
Vibration position for particle %s do not match.
Did something change in the implementation or minecraft?
""", bukkit.getKey()));
}
private <T extends ParticleParam> void testFloat(Particle bukkit, net.minecraft.core.particles.Particle<T> minecraft) {
float role = 0.1205f;
SculkChargeParticleOptions param = createAndTest(bukkit, minecraft, role, SculkChargeParticleOptions.class);
assertEquals(role, param.roll(), 0.001, String.format("""
Float role for particle %s do not match.
Did something change in the implementation or minecraft?
""", bukkit.getKey()));
}
private <T extends ParticleParam> void testInteger(Particle bukkit, net.minecraft.core.particles.Particle<T> minecraft) {
int delay = 1205;
ShriekParticleOption param = createAndTest(bukkit, minecraft, delay, ShriekParticleOption.class);
assertEquals(delay, param.getDelay(), String.format("""
Integer delay for particle %s do not match.
Did something change in the implementation or minecraft?
""", bukkit.getKey()));
}
private <D extends ParticleParam, T extends ParticleParam> D createAndTest(Particle bukkit, net.minecraft.core.particles.Particle<T> minecraft, Object data, Class<D> paramClass) {
@SuppressWarnings("unchecked")
T particleParam = (T) assertDoesNotThrow(() -> CraftParticle.createParticleParam(bukkit, data), String.format("""
Could not create particle param for particle %s.
This can indicated, that the default particle param is used, but the particle requires extra data.
Or that something is wrong with the logic which creates the particle param with extra data.
Check in CraftParticle if the conversion is still correct.
""", bukkit.getKey()));
DataResult<NBTBase> encoded = assertDoesNotThrow(() -> minecraft.codec().encodeStart(DynamicOpsNBT.INSTANCE, particleParam),
String.format("""
Could not encoded particle param for particle %s.
This can indicated, that the wrong particle param is created in CraftParticle.
Particle param is of type %s.
""", bukkit.getKey(), particleParam.getClass()));
Optional<DataResult.PartialResult<NBTBase>> encodeError = encoded.error();
assertTrue(encodeError.isEmpty(), () -> String.format("""
Could not encoded particle param for particle %s.
This is possible because the wrong particle param is created in CraftParticle.
Particle param is of type %s.
Error message: %s.
""", bukkit.getKey(), particleParam.getClass(), encoded.error().get().message()));
Optional<NBTBase> encodeResult = encoded.result();
assertTrue(encodeResult.isPresent(), String.format("""
Result is not present for particle %s.
Even though there is also no error, this should not happen.
Particle param is of type %s.
""", bukkit.getKey(), particleParam.getClass()));
DataResult<T> decoded = minecraft.codec().parse(DynamicOpsNBT.INSTANCE, encodeResult.get());
Optional<DataResult.PartialResult<T>> decodeError = decoded.error();
assertTrue(decodeError.isEmpty(), () -> String.format("""
Could not decoded particle param for particle %s.
This is possible because the wrong particle param is created in CraftParticle.
Particle param is of type %s.
Error message: %s.
NBT data: %s.
""", bukkit.getKey(), particleParam.getClass(), decodeError.get().message(), encodeResult.get()));
Optional<T> decodeResult = decoded.result();
assertTrue(decodeResult.isPresent(), String.format("""
Result is not present for particle %s.
Even though there is also no error, this should not happen.
Particle param is of type %s.
""", bukkit.getKey(), particleParam.getClass()));
return assertInstanceOf(paramClass, decodeResult.get(), String.format("""
Result is not of the right type for particle %s.
""", bukkit.getKey()));
}
}