diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/util/data/BlockMeta.java b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/BlockMeta.java new file mode 100644 index 0000000..0b8a654 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/BlockMeta.java @@ -0,0 +1,83 @@ +package com.gtnewhorizon.gtnhlib.util.data; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +import net.minecraft.block.Block; + +/** + * A mutable implementation of {@link ImmutableBlockMeta}. If your API should return a mutable pair, return this + * instead. Must follow the same contracts as the immutable version if this is ever upcast to a + * {@link ImmutableBlockMeta} in your API. If this type is exposed instead of the immutable interface, assume that the + * contained values can change. + */ +public class BlockMeta implements ImmutableBlockMeta { + + @Nonnull + private Block block; + private int meta; + + public BlockMeta(@Nonnull Block block, int meta) { + this.block = block; + this.meta = meta; + } + + public BlockMeta(@Nonnull Block block) { + this(block, 0); + } + + @Override + @Nonnull + public Block getBlock() { + return block; + } + + @Override + public int getBlockMeta() { + return meta; + } + + /** + * Note: see the header comment in {@link ImmutableBlockMeta} for this method's contract. + */ + public BlockMeta setBlock(@Nonnull Block block) { + this.block = Objects.requireNonNull(block); + + return this; + } + + /** + * Note: see the header comment in {@link ImmutableBlockMeta} for this method's contract. + */ + public BlockMeta setBlockMeta(int meta) { + this.meta = meta; + + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + block.hashCode(); + result = prime * result + meta; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + BlockMeta other = (BlockMeta) obj; + if (!block.equals(other.block)) return false; + if (meta != other.meta) return false; + return true; + } + + @Override + public String toString() { + return "BlockMeta [block=" + block + ", meta=" + meta + "]"; + } +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/util/data/BlockSupplier.java b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/BlockSupplier.java new file mode 100644 index 0000000..3ae615a --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/BlockSupplier.java @@ -0,0 +1,15 @@ +package com.gtnewhorizon.gtnhlib.util.data; + +import java.util.function.Supplier; + +import net.minecraft.block.Block; + +/** + * A supplier that provides an Block. This is its own type because superclasses save their generics, allowing the JVM to + * differentiate between functional interfaces at runtime. Without this interface, two methods overloads that accept a + * Supplier with different generics but an otherwise identical method signature would cause a compilation error. + */ +@FunctionalInterface +public interface BlockSupplier extends Supplier { + +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/util/data/IMod.java b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/IMod.java new file mode 100644 index 0000000..2328872 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/IMod.java @@ -0,0 +1,15 @@ +package com.gtnewhorizon.gtnhlib.util.data; + +/** + * An interface for any mod enums. Represents a mod. + */ +public interface IMod { + + boolean isModLoaded(); + + /** Gets the mod id. */ + String getID(); + + /** Gets the mod's resource location prefix. */ + String getResourceLocation(); +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/util/data/ImmutableBlockMeta.java b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/ImmutableBlockMeta.java new file mode 100644 index 0000000..0d1ad04 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/ImmutableBlockMeta.java @@ -0,0 +1,51 @@ +package com.gtnewhorizon.gtnhlib.util.data; + +import javax.annotation.Nonnull; + +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraftforge.oredict.OreDictionary; + +/** + * An immutable block-meta pair. This must not be cast down to its mutable version unless you have a very good reason. + * It can be assumed that the values of {@link #getBlock()} and {@link #getBlockMeta()} will never change for this + * object if the object is exposed through an API. + */ +public interface ImmutableBlockMeta { + + /** + * The value of this must not change while this object is exposed via an API. + * + * @return The block stored in this pair. + */ + @Nonnull + public Block getBlock(); + + /** + * The value of this must not change while this object is exposed via an API. + * + * @return The block's metadata stored in this pair. May be {@link OreDictionary#WILDCARD_VALUE}. + */ + public int getBlockMeta(); + + /** + * Gets the corresponding item for this block. Subclasses may provide a faster implementation. + */ + public default Item getItem() { + return Item.getItemFromBlock(getBlock()); + } + + /** + * Checks if this pair matches the given block & meta. + * + * @param block The block. + * @param meta The meta. If this parameter or {@link #getBlockMeta()} equals {@link OreDictionary#WILDCARD_VALUE} + * then meta checks are ignored. + * @return Whether this pair matches or not. + */ + public default boolean matches(Block block, int meta) { + return getBlock() == block + && (meta == OreDictionary.WILDCARD_VALUE || getBlockMeta() == OreDictionary.WILDCARD_VALUE + || getBlockMeta() == meta); + } +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/util/data/ImmutableItemMeta.java b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/ImmutableItemMeta.java new file mode 100644 index 0000000..f6b6485 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/ImmutableItemMeta.java @@ -0,0 +1,69 @@ +package com.gtnewhorizon.gtnhlib.util.data; + +import javax.annotation.Nonnull; + +import net.minecraft.block.Block; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraftforge.oredict.OreDictionary; + +/** + * An immutable item-meta pair. This must not be cast down to its mutable version unless you have a very good reason. It + * can be assumed that the values of {@link #getItem()} and {@link #getItemMeta()} will never change for this object if + * the object is exposed through an API. + */ +public interface ImmutableItemMeta { + + /** + * The value of this must not change while this object is exposed via an API. + * + * @return The item stored in this pair. + */ + @Nonnull + public Item getItem(); + + /** + * The value of this must not change while this object is exposed via an API. + * + * @return The item's metadata stored in this pair. May be {@link OreDictionary#WILDCARD_VALUE}. + */ + public int getItemMeta(); + + /** + * Gets the corresponding block for this item. Subclasses may provide a faster implementation. + */ + public default Block getBlock() { + return Block.getBlockFromItem(getItem()); + } + + /** + * Checks if this pair matches the given ItemStack's item and metadata. + */ + public default boolean matches(ItemStack stack) { + if (stack == null) return false; + + return matches(stack.getItem(), Items.feather.getDamage(stack)); + } + + /** + * Checks if this pair matches the given item & meta. + * + * @param Item The item. + * @param meta The meta. If this parameter or {@link #getItemMeta()} equals {@link OreDictionary#WILDCARD_VALUE} + * then meta checks are ignored. + * @return Whether this pair matches or not. + */ + public default boolean matches(Item item, int meta) { + return getItem() == item + && (meta == OreDictionary.WILDCARD_VALUE || getItemMeta() == OreDictionary.WILDCARD_VALUE + || getItemMeta() == meta); + } + + /** Converts this pair to an ItemStack. */ + public default ItemStack toStack(int amount) { + int meta = getItemMeta(); + + return new ItemStack(getItem(), amount, meta == OreDictionary.WILDCARD_VALUE ? 0 : meta); + } +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/util/data/ItemMeta.java b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/ItemMeta.java new file mode 100644 index 0000000..9c0fff1 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/ItemMeta.java @@ -0,0 +1,82 @@ +package com.gtnewhorizon.gtnhlib.util.data; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +import net.minecraft.item.Item; + +/** + * A mutable implementation of {@link ImmutableItemMeta}. If your API should return a mutable pair, return this instead. + * Must follow the same contracts as the immutable version if this is ever upcast to a {@link ImmutableItemMeta} in your + * API. If this type is exposed instead of the immutable interface, assume that the contained values can change. + */ +public class ItemMeta implements ImmutableItemMeta { + + @Nonnull + private Item item; + private int meta; + + public ItemMeta(@Nonnull Item item, int meta) { + this.item = item; + this.meta = meta; + } + + public ItemMeta(@Nonnull Item item) { + this(item, 0); + } + + @Override + @Nonnull + public Item getItem() { + return item; + } + + @Override + public int getItemMeta() { + return meta; + } + + /** + * Note: see the header comment in {@link ImmutableItemMeta} for this method's contract. + */ + public ItemMeta setItem(@Nonnull Item item) { + this.item = Objects.requireNonNull(item); + + return this; + } + + /** + * Note: see the header comment in {@link ImmutableItemMeta} for this method's contract. + */ + public ItemMeta setItemMeta(int meta) { + this.meta = meta; + + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + item.hashCode(); + result = prime * result + meta; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + ItemMeta other = (ItemMeta) obj; + if (!item.equals(other.item)) return false; + if (meta != other.meta) return false; + return true; + } + + @Override + public String toString() { + return "ItemMeta [item=" + item + ", meta=" + meta + "]"; + } +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/util/data/ItemStackSupplier.java b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/ItemStackSupplier.java new file mode 100644 index 0000000..d7239c3 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/ItemStackSupplier.java @@ -0,0 +1,16 @@ +package com.gtnewhorizon.gtnhlib.util.data; + +import java.util.function.Supplier; + +import net.minecraft.item.ItemStack; + +/** + * A supplier that provides an ItemStack. This is its own type because superclasses save their generics, allowing the + * JVM to differentiate between functional interfaces at runtime. Without this interface, two methods overloads that + * accept a Supplier with different generics but an otherwise identical method signature would cause a compilation + * error. + */ +@FunctionalInterface +public interface ItemStackSupplier extends Supplier { + +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/util/data/ItemSupplier.java b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/ItemSupplier.java new file mode 100644 index 0000000..dc47357 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/ItemSupplier.java @@ -0,0 +1,15 @@ +package com.gtnewhorizon.gtnhlib.util.data; + +import java.util.function.Supplier; + +import net.minecraft.item.Item; + +/** + * A supplier that provides an Item. This is its own type because superclasses save their generics, allowing the JVM to + * differentiate between functional interfaces at runtime. Without this interface, two methods overloads that accept a + * Supplier with different generics but an otherwise identical method signature would cause a compilation error. + */ +@FunctionalInterface +public interface ItemSupplier extends Supplier { + +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/util/data/Lazy.java b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/Lazy.java new file mode 100644 index 0000000..7495f99 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/Lazy.java @@ -0,0 +1,49 @@ +package com.gtnewhorizon.gtnhlib.util.data; + +import java.util.function.Supplier; + +/** + * Just a container that will lazy-load a value. Can be used in the place of a Supplier. This is useful if you want a + * static final field that shouldn't be initialized when the class is loaded. + */ +public class Lazy implements Supplier { + + private boolean hasValue = false; + private T value; + + private Supplier getter; + + public Lazy(Supplier getter) { + this.getter = getter; + } + + /** + * Sets the value even if it's been initialized already. + */ + public synchronized void set(T value) { + hasValue = true; + this.value = value; + getter = null; + } + + /** + * Gets the value. Thread safe. + */ + @Override + public synchronized T get() { + if (!hasValue) { + value = getter.get(); + getter = null; + hasValue = true; + } + + return value; + } + + /** + * Initializes the value. {@link #get()}, but with a more readable method name. + */ + public void initialize() { + get(); + } +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/util/data/LazyBlock.java b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/LazyBlock.java new file mode 100644 index 0000000..394e2ad --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/LazyBlock.java @@ -0,0 +1,96 @@ +package com.gtnewhorizon.gtnhlib.util.data; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +import net.minecraft.block.Block; + +import cpw.mods.fml.common.registry.GameRegistry; + +/** + * A lazy-loaded block. Useful for making a static final reference to a block. Can also be used to simplify references + * to static final blocks in optional methods. + * + *
+ * {@code
+ * private static final LazyBlock SOME_OPTIONAL_BLOCK = new LazyBlock(Mods.SomeMod, "foo");
+ * 
+ * {@literal @}Optional.Method(modid = Names.SOME_MOD)
+ * public static void doSomething(World world, int x, int y, int z) {
+ *   if (world.getBlock(x, y, z) == SOME_OPTIONAL_BLOCK.getBlock()) {
+ *      ...
+ *   }
+ * }
+ * 
+ */ +public class LazyBlock extends Lazy implements ImmutableBlockMeta { + + private final IMod mod; + + public LazyBlock(IMod mod, String blockName, int meta) { + super(() -> { + if (!mod.isModLoaded()) throw new IllegalStateException( + "cannot get() LazyBlock " + mod.getID() + ":" + blockName + " because its mod is not loaded"); + + Block block = GameRegistry.findBlock(mod.getID(), blockName); + + Objects.requireNonNull(block, "could not find block: " + mod.getID() + ":" + blockName); + + return new BlockMeta(block, meta); + }); + + this.mod = mod; + } + + public LazyBlock(IMod mod, String blockName) { + this(mod, blockName, 0); + } + + public LazyBlock(IMod mod, BlockSupplier getter, int meta) { + super(() -> { + if (!mod.isModLoaded()) return null; + + Block block = getter.get(); + + if (block == null) return null; + + return new BlockMeta(block, meta); + }); + + this.mod = mod; + } + + public LazyBlock(IMod mod, BlockSupplier getter) { + this(mod, getter, 0); + } + + /** Checks if the parent mod is loaded. */ + public boolean isPresent() { + return mod.isModLoaded(); + } + + public IMod getMod() { + return mod; + } + + @Override + @Nonnull + public Block getBlock() { + return get().getBlock(); + } + + @Override + public int getBlockMeta() { + return get().getBlockMeta(); + } + + @Override + public boolean matches(Block other, int metaOther) { + if (!isPresent()) return false; + + ImmutableBlockMeta bm = get(); + + return bm == null ? false : bm.matches(other, metaOther); + } +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/util/data/LazyItem.java b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/LazyItem.java new file mode 100644 index 0000000..b58f0c6 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/util/data/LazyItem.java @@ -0,0 +1,138 @@ +package com.gtnewhorizon.gtnhlib.util.data; + +import java.util.Objects; + +import javax.annotation.Nonnull; + +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraftforge.oredict.OreDictionary; + +import cpw.mods.fml.common.registry.GameRegistry; + +/** + * A lazy-loaded item. Useful for making a static final reference to a item. Can also be used to simplify references to + * static final items in optional methods. + * + *
+ * {@code
+ * private static final LazyItem SOME_OPTIONAL_ITEM = new LazyItem(Mods.SomeMod, "foo");
+ * 
+ * {@literal @}Optional.Method(modid = Names.SOME_MOD)
+ * public static void doSomething(ItemStack stack) {
+ *   if (SOME_OPTIONAL_BLOCK.matches(stack)) {
+ *      ...
+ *   }
+ * }
+ * 
+ */ +public class LazyItem extends Lazy implements ImmutableItemMeta { + + private final IMod mod; + + public LazyItem(IMod mod, String itemName, int meta) { + super(() -> { + if (!mod.isModLoaded()) return null; + + Item item = GameRegistry.findItem(mod.getID(), itemName); + + Objects.requireNonNull(item, "could not find item: " + mod.getID() + ":" + itemName); + + return new ItemMeta(item, meta); + }); + + this.mod = mod; + } + + public LazyItem(IMod mod, String itemName) { + this(mod, itemName, 0); + } + + public LazyItem(IMod mod, ItemStackSupplier getter) { + super(() -> { + if (!mod.isModLoaded()) return null; + + ItemStack stack = getter.get(); + + if (stack == null || stack.getItem() == null) return null; + + return new ItemMeta(stack.getItem(), Items.feather.getDamage(stack)); + }); + + this.mod = mod; + } + + public LazyItem(IMod mod, ItemSupplier getter, int meta) { + super(() -> { + if (!mod.isModLoaded()) return null; + + Item item = getter.get(); + + if (item == null) return null; + + return new ItemMeta(item, meta); + }); + + this.mod = mod; + } + + public LazyItem(IMod mod, ItemSupplier getter) { + this(mod, getter, 0); + } + + /** Checks if the parent mod is loaded. */ + public boolean isLoaded() { + return mod.isModLoaded(); + } + + public IMod getMod() { + return mod; + } + + @Override + @Nonnull + public Item getItem() { + return get().getItem(); + } + + @Override + public int getItemMeta() { + return get().getItemMeta(); + } + + @Override + public boolean matches(Item other, int metaOther) { + if (!isLoaded()) return false; + + ImmutableItemMeta bm = get(); + + if (bm == null) return false; + + return bm.getItem() == other + && (bm.getItemMeta() == metaOther || bm.getItemMeta() == OreDictionary.WILDCARD_VALUE + || metaOther == OreDictionary.WILDCARD_VALUE); + } + + @Override + public boolean matches(ItemStack stack) { + if (stack == null) return false; + + return matches(stack.getItem(), Items.feather.getDamage(stack)); + } + + /** + * Converts this LazyItem to an ItemStack. Returns null if the parent mod isn't loaded, or if the contained + * ImmutableItemMeta is null. + */ + @Override + public ItemStack toStack(int amount) { + if (!isLoaded()) return null; + + ImmutableItemMeta bm = get(); + + if (bm == null) return null; + + return bm.toStack(amount); + } +}