diff --git a/src/main/java/com/comphenix/protocol/events/AbstractStructure.java b/src/main/java/com/comphenix/protocol/events/AbstractStructure.java index c0207398..87506987 100644 --- a/src/main/java/com/comphenix/protocol/events/AbstractStructure.java +++ b/src/main/java/com/comphenix/protocol/events/AbstractStructure.java @@ -29,6 +29,7 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.MerchantRecipe; import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; public abstract class AbstractStructure { protected transient Object handle; @@ -308,6 +309,31 @@ public abstract class AbstractStructure { BlockPosition.getConverter()); } + /** + * Retrieves a read/write structure for registrable objects. + * + * @param registrableClass The registrable object's class. + * @return A modifier for a registrable objects. + * @see MinecraftReflection#getBlockEntityTypeClass() + */ + public StructureModifier getRegistrableModifier( + @NotNull final Class registrableClass + ) { + // Convert to and from the Bukkit wrapper + return structureModifier.withType( + registrableClass, + BukkitConverters.getWrappedRegistrable(registrableClass)); + } + + /** + * Retrieves a read/write structure for BlockEntityType. + * @return A modifier for a BlockEntityType. + */ + public StructureModifier getBlockEntityTypeModifier() { + // Convert to and from the Bukkit wrapper + return getRegistrableModifier(MinecraftReflection.getBlockEntityTypeClass()); + } + /** * Retrieves a read/write structure for wrapped ChunkCoordIntPairs. * @return A modifier for ChunkCoordIntPair. diff --git a/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java b/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java index d7d53495..af5cf1ac 100644 --- a/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java +++ b/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java @@ -77,6 +77,7 @@ import org.bukkit.inventory.MerchantRecipe; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; import static com.comphenix.protocol.utility.MinecraftReflection.getCraftBukkitClass; import static com.comphenix.protocol.utility.MinecraftReflection.getMinecraftClass; @@ -587,7 +588,23 @@ public class BukkitConverters { public static EquivalentConverter getWrappedBlockDataConverter() { return ignoreNull(handle(WrappedBlockData::getHandle, WrappedBlockData::fromHandle, WrappedBlockData.class)); } - + + /** + * Retrieve a converter for wrapped block entity type. + * @return Wrapped block entity type. + */ + public static EquivalentConverter getWrappedRegistrable( + @NotNull final Class registrableClass + ) { + return ignoreNull( + handle( + WrappedRegistrable::getHandle, + handle -> WrappedRegistrable.fromHandle(registrableClass, handle), + WrappedRegistrable.class + ) + ); + } + /** * Retrieve a converter for wrapped attribute snapshots. * @return Wrapped attribute snapshot converter. diff --git a/src/main/java/com/comphenix/protocol/wrappers/WrappedRegistrable.java b/src/main/java/com/comphenix/protocol/wrappers/WrappedRegistrable.java new file mode 100644 index 00000000..e113857b --- /dev/null +++ b/src/main/java/com/comphenix/protocol/wrappers/WrappedRegistrable.java @@ -0,0 +1,185 @@ +/** + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2015 dmulloy2 + *

+ * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + *

+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ +package com.comphenix.protocol.wrappers; + +import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.reflect.accessors.Accessors; +import com.comphenix.protocol.reflect.accessors.FieldAccessor; +import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract; +import com.comphenix.protocol.utility.MinecraftReflection; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Represents a wrapper around registrable objects. + * + * @author portlek + */ +public final class WrappedRegistrable extends AbstractWrapper implements ClonableWrapper { + + @NotNull + private final Factory factory; + + private WrappedRegistrable( + @NotNull final Factory factory, + @NotNull final Object handle + ) { + super(factory.registrableClass); + this.factory = factory; + setHandle(handle); + } + + @NotNull + public static WrappedRegistrable fromHandle( + @NotNull final Factory factory, + @NotNull final Object handle + ) { + return new WrappedRegistrable(factory, handle); + } + + @NotNull + public static WrappedRegistrable fromHandle( + @NotNull final Class registrableClass, + @NotNull final Object handle + ) { + return fromHandle(Factory.getOrCreate(registrableClass), handle); + } + + @NotNull + public static WrappedRegistrable fromClassAndKey( + @NotNull final Class registrableClass, + @NotNull final MinecraftKey key + ) { + final Factory factory = Factory.getOrCreate(registrableClass); + return fromHandle(factory, factory.getHandle(key)); + } + + @NotNull + public static WrappedRegistrable fromClassAndKey( + @NotNull final Class registrableClass, + @NotNull final String key + ) { + return fromClassAndKey(registrableClass, new MinecraftKey(key)); + } + + @NotNull + public static WrappedRegistrable blockEntityType( + @NotNull final MinecraftKey key + ) { + return fromClassAndKey(MinecraftReflection.getBlockEntityTypeClass(), key); + } + + @NotNull + public static WrappedRegistrable blockEntityType( + @NotNull final String key + ) { + return blockEntityType(new MinecraftKey(key)); + } + + /** + * Gets this registrable object's Minecraft key + * + * @return The Minecraft key + */ + public MinecraftKey getKey() { + return factory.getKey(handle); + } + + /** + * Sets this registrable object's Minecraft key + * + * @param key Minecraft key + */ + public void setKey(MinecraftKey key) { + setHandle(factory.getHandle(key)); + } + + public WrappedRegistrable deepClone() { + return fromHandle(factory, handle); + } + + @Override + public String toString() { + return "WrappedRegistrable[handle=" + handle + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + getKey().hashCode(); + return result; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof WrappedRegistrable)) { + return false; + } + final WrappedRegistrable that = (WrappedRegistrable) o; + return handle.equals(that.handle) || (getKey() == that.getKey()); + } + + private static final class Factory { + + private static final Map, Factory> CACHE = new ConcurrentHashMap<>(); + + @NotNull + private final Class registrableClass; + + @NotNull + private final WrappedRegistry registry; + + @NotNull + private final FieldAccessor fieldAccessor; + + private Factory(@NotNull final Class registrableClass) { + this.registrableClass = registrableClass; + this.registry = WrappedRegistry.getRegistry(registrableClass); + this.fieldAccessor = Accessors.getFieldAccessor( + FuzzyReflection.fromClass(registrableClass, true) + .getField( + FuzzyFieldContract.newBuilder() + .typeExact(registrableClass) + .build() + ) + ); + } + + @NotNull + public static Factory getOrCreate( + @NotNull final Class registrableClass + ) { + return CACHE.computeIfAbsent(registrableClass, Factory::new); + } + + @NotNull + public MinecraftKey getKey(@NotNull final Object handle) { + return registry.getKey(fieldAccessor.get(handle)); + } + + @NotNull + public Object getHandle(MinecraftKey key) { + return registry.get(key); + } + } +}