package com.comphenix.protocol.wrappers; import java.lang.reflect.Modifier; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.MethodAccessor; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftVersion; import com.mojang.math.Vector3fa; import org.bukkit.Color; import org.bukkit.Particle; import org.bukkit.inventory.ItemStack; /** * Represents an immutable wrapped ParticleParam in 1.13 */ public class WrappedParticle { private static MethodAccessor toBukkit; private static MethodAccessor toNMS; private static MethodAccessor toCraftData; private static void ensureMethods() { if (toBukkit != null && toNMS != null) { return; } FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("CraftParticle")); FuzzyMethodContract contract = FuzzyMethodContract .newBuilder() .requireModifier(Modifier.STATIC) .returnTypeExact(Particle.class) .parameterExactType(MinecraftReflection.getParticleParam()) .build(); toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract)); contract = FuzzyMethodContract .newBuilder() .requireModifier(Modifier.STATIC) .returnTypeExact(MinecraftReflection.getParticleParam()) .parameterCount(2) .build(); toNMS = Accessors.getMethodAccessor(fuzzy.getMethod(contract)); Class cbData = MinecraftReflection.getCraftBukkitClass("block.data.CraftBlockData"); fuzzy = FuzzyReflection.fromClass(cbData); contract = FuzzyMethodContract .newBuilder() .requireModifier(Modifier.STATIC) .returnTypeExact(cbData) .parameterExactArray(MinecraftReflection.getIBlockDataClass()) .build(); toCraftData = Accessors.getMethodAccessor(fuzzy.getMethod(contract)); } private final Particle particle; private final T data; private final Object handle; private WrappedParticle(Object handle, Particle particle, T data) { this.handle = handle; this.particle = particle; this.data = data; } /** * @return This particle's Bukkit type */ public Particle getParticle() { return particle; } /** * Gets this Particle's Bukkit/ProtocolLib data. The type of this data depends on the * {@link #getParticle() Particle type}. For Block particles it will be {@link WrappedBlockData}, * for Item crack particles, it will be an {@link ItemStack}, and for redstone particles it will * be {@link Particle.DustOptions} * * @return The particle data */ public T getData() { return data; } /** * @return NMS handle */ public Object getHandle() { return handle; } public static WrappedParticle fromHandle(Object handle) { ensureMethods(); Particle bukkit = (Particle) toBukkit.invoke(null, handle); Object data = null; switch (bukkit) { case BLOCK_CRACK: case BLOCK_DUST: case FALLING_DUST: data = getBlockData(handle); break; case ITEM_CRACK: data = getItem(handle); break; case REDSTONE: data = getRedstone(handle); break; default: break; } return new WrappedParticle<>(handle, bukkit, data); } private static WrappedBlockData getBlockData(Object handle) { return new StructureModifier<>(handle.getClass()) .withTarget(handle) .withType(MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter()) .read(0); } private static Object getItem(Object handle) { return new StructureModifier<>(handle.getClass()) .withTarget(handle) .withType(MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter()) .read(0); } private static Object getRedstone(Object handle) { int r, g, b; float alpha; if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) { StructureModifier modifier = new StructureModifier<>(handle.getClass()).withTarget(handle); Vector3fa rgb = (Vector3fa) modifier.withType(Vector3fa.class).read(0); r = (int) (rgb.a() * 255); g = (int) (rgb.b() * 255); b = (int) (rgb.c() * 255); alpha = (float) modifier.withType(float.class).read(0); } else { StructureModifier modifier = new StructureModifier<>(handle.getClass()).withTarget(handle).withType(float.class); r = (int) (modifier.read(0) * 255); g = (int) (modifier.read(1) * 255); b = (int) (modifier.read(2) * 255); alpha = modifier.read(3); } return new Particle.DustOptions(Color.fromRGB(r, g, b), alpha); } public static WrappedParticle create(Particle particle, T data) { ensureMethods(); Object bukkitData = data; if (data instanceof WrappedBlockData) { WrappedBlockData blockData = (WrappedBlockData) data; bukkitData = toCraftData.invoke(null, blockData.getHandle()); } Object handle = toNMS.invoke(null, particle, bukkitData); return new WrappedParticle<>(handle, particle, data); } }