From 8e43dfa7a626387556e642f21aeb632e8e96cdba Mon Sep 17 00:00:00 2001 From: tterrag Date: Sun, 27 Jan 2019 01:38:53 -0500 Subject: [PATCH] Refactor OptionalCapabilityInstance to LazyOptional; clean up - Move to util package, rename to LazyOptional - Clean up existing javadocs, add javadocs to everything missing them - Add more NonNull functional interfaces, and use them everywhere - Move orEmpty() to Capability, works better here anyways --- .../TileEntityRendererAnimation.java | 4 +- .../common/capabilities/Capability.java | 7 + .../capabilities/CapabilityDispatcher.java | 7 +- .../capabilities/CapabilityProvider.java | 5 +- .../capabilities/ICapabilityProvider.java | 5 +- .../OptionalCapabilityInstance.java | 241 -------------- .../model/animation/CapabilityAnimation.java | 10 +- .../common/util/LazyOptional.java | 298 ++++++++++++++++++ .../NonNullConsumer.java | 10 +- .../common/util/NonNullFunction.java | 36 +++ .../common/util/NonNullPredicate.java | 35 ++ .../NonNullSupplier.java | 10 +- .../net/minecraftforge/fluids/FluidUtil.java | 12 +- .../fluids/capability/TileFluidHandler.java | 6 +- .../templates/FluidHandlerItemStack.java | 8 +- .../FluidHandlerItemStackSimple.java | 8 +- .../wrappers/FluidBucketWrapper.java | 8 +- .../items/VanillaInventoryCodeHooks.java | 8 +- .../wrapper/EntityEquipmentInvWrapper.java | 12 +- .../items/wrapper/SidedInvWrapper.java | 8 +- 20 files changed, 444 insertions(+), 294 deletions(-) delete mode 100644 src/main/java/net/minecraftforge/common/capabilities/OptionalCapabilityInstance.java create mode 100644 src/main/java/net/minecraftforge/common/util/LazyOptional.java rename src/main/java/net/minecraftforge/common/{capabilities => util}/NonNullConsumer.java (83%) create mode 100644 src/main/java/net/minecraftforge/common/util/NonNullFunction.java create mode 100644 src/main/java/net/minecraftforge/common/util/NonNullPredicate.java rename src/main/java/net/minecraftforge/common/{capabilities => util}/NonNullSupplier.java (82%) diff --git a/src/main/java/net/minecraftforge/client/model/animation/TileEntityRendererAnimation.java b/src/main/java/net/minecraftforge/client/model/animation/TileEntityRendererAnimation.java index 21b69429e..b2161a6ea 100644 --- a/src/main/java/net/minecraftforge/client/model/animation/TileEntityRendererAnimation.java +++ b/src/main/java/net/minecraftforge/client/model/animation/TileEntityRendererAnimation.java @@ -31,11 +31,11 @@ import net.minecraft.world.IWorldReader; import net.minecraftforge.client.MinecraftForgeClient; import net.minecraftforge.common.animation.Event; import net.minecraftforge.common.animation.IEventHandler; -import net.minecraftforge.common.capabilities.OptionalCapabilityInstance; import net.minecraftforge.common.model.animation.CapabilityAnimation; import net.minecraftforge.common.model.animation.IAnimationStateMachine; import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.Properties; +import net.minecraftforge.common.util.LazyOptional; import java.util.Random; @@ -49,7 +49,7 @@ public class TileEntityRendererAnimation extends TileEntit @Override public void renderTileEntityFast(T te, double x, double y, double z, float partialTick, int breakStage, BufferBuilder renderer) { - OptionalCapabilityInstance cap = te.getCapability(CapabilityAnimation.ANIMATION_CAPABILITY); + LazyOptional cap = te.getCapability(CapabilityAnimation.ANIMATION_CAPABILITY); if(!cap.isPresent()) { return; diff --git a/src/main/java/net/minecraftforge/common/capabilities/Capability.java b/src/main/java/net/minecraftforge/common/capabilities/Capability.java index 086161d1a..6ee398a7b 100644 --- a/src/main/java/net/minecraftforge/common/capabilities/Capability.java +++ b/src/main/java/net/minecraftforge/common/capabilities/Capability.java @@ -25,7 +25,9 @@ import com.google.common.base.Throwables; import net.minecraft.nbt.INBTBase; import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.util.LazyOptional; +import javax.annotation.Nonnull; import javax.annotation.Nullable; /** @@ -134,6 +136,11 @@ public class Capability throw new RuntimeException(e); } } + + public @Nonnull LazyOptional orEmpty(Capability toCheck, LazyOptional inst) + { + return this == toCheck ? inst.cast() : LazyOptional.empty(); + } // INTERNAL private final String name; diff --git a/src/main/java/net/minecraftforge/common/capabilities/CapabilityDispatcher.java b/src/main/java/net/minecraftforge/common/capabilities/CapabilityDispatcher.java index 142d8af8c..14b881966 100644 --- a/src/main/java/net/minecraftforge/common/capabilities/CapabilityDispatcher.java +++ b/src/main/java/net/minecraftforge/common/capabilities/CapabilityDispatcher.java @@ -33,6 +33,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; import net.minecraftforge.common.util.INBTSerializable; +import net.minecraftforge.common.util.LazyOptional; /** * A high-speed implementation of a capability delegator. @@ -92,16 +93,16 @@ public final class CapabilityDispatcher implements INBTSerializable OptionalCapabilityInstance getCapability(Capability cap, @Nullable EnumFacing side) + public LazyOptional getCapability(Capability cap, @Nullable EnumFacing side) { for (ICapabilityProvider c : caps) { - OptionalCapabilityInstance ret = c.getCapability(cap, side); + LazyOptional ret = c.getCapability(cap, side); if (ret.isPresent()) { return ret; } } - return OptionalCapabilityInstance.empty(); + return LazyOptional.empty(); } @Override diff --git a/src/main/java/net/minecraftforge/common/capabilities/CapabilityProvider.java b/src/main/java/net/minecraftforge/common/capabilities/CapabilityProvider.java index bcb4215ec..b60d2b99f 100644 --- a/src/main/java/net/minecraftforge/common/capabilities/CapabilityProvider.java +++ b/src/main/java/net/minecraftforge/common/capabilities/CapabilityProvider.java @@ -26,6 +26,7 @@ import javax.annotation.ParametersAreNonnullByDefault; import mcp.MethodsReturnNonnullByDefault; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.event.ForgeEventFactory; @MethodsReturnNonnullByDefault @@ -98,9 +99,9 @@ public abstract class CapabilityProvider> implem @Override @Nonnull - public OptionalCapabilityInstance getCapability(@Nonnull Capability cap, @Nullable EnumFacing side) + public LazyOptional getCapability(@Nonnull Capability cap, @Nullable EnumFacing side) { final CapabilityDispatcher disp = getCapabilities(); - return disp == null ? OptionalCapabilityInstance.empty() : disp.getCapability(cap, side); + return disp == null ? LazyOptional.empty() : disp.getCapability(cap, side); } } diff --git a/src/main/java/net/minecraftforge/common/capabilities/ICapabilityProvider.java b/src/main/java/net/minecraftforge/common/capabilities/ICapabilityProvider.java index b95380fdc..1c894ede1 100644 --- a/src/main/java/net/minecraftforge/common/capabilities/ICapabilityProvider.java +++ b/src/main/java/net/minecraftforge/common/capabilities/ICapabilityProvider.java @@ -23,6 +23,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.util.LazyOptional; public interface ICapabilityProvider { @@ -37,13 +38,13 @@ public interface ICapabilityProvider * CAN BE NULL. Null is defined to represent 'internal' or 'self' * @return The requested an optional holding the requested capability. */ - @Nonnull OptionalCapabilityInstance getCapability(@Nonnull final Capability cap, final @Nullable EnumFacing side); + @Nonnull LazyOptional getCapability(@Nonnull final Capability cap, final @Nullable EnumFacing side); /* * Purely added as a bouncer to sided version, to make modders stop complaining about calling with a null value. * This should never be OVERRIDDEN, modders should only ever implement the sided version. */ - @Nonnull default OptionalCapabilityInstance getCapability(@Nonnull final Capability cap) { + @Nonnull default LazyOptional getCapability(@Nonnull final Capability cap) { return getCapability(cap, null); } } diff --git a/src/main/java/net/minecraftforge/common/capabilities/OptionalCapabilityInstance.java b/src/main/java/net/minecraftforge/common/capabilities/OptionalCapabilityInstance.java deleted file mode 100644 index acd9e4957..000000000 --- a/src/main/java/net/minecraftforge/common/capabilities/OptionalCapabilityInstance.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2018. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation version 2.1 - * of the License. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.common.capabilities; - -import java.util.HashSet; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.ParametersAreNonnullByDefault; - -import mcp.MethodsReturnNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public class OptionalCapabilityInstance -{ - private final NonNullSupplier supplier; - private AtomicReference resolved; - private Set>> listeners = new HashSet<>(); - private boolean isValid = true; - - private static final @Nonnull OptionalCapabilityInstance EMPTY = new OptionalCapabilityInstance<>(null); - - @SuppressWarnings("unchecked") - public static OptionalCapabilityInstance empty() - { - return (OptionalCapabilityInstance)EMPTY; - } - - @SuppressWarnings("unchecked") - public OptionalCapabilityInstance cast() - { - return (OptionalCapabilityInstance)this; - } - - private OptionalCapabilityInstance(@Nullable NonNullSupplier instanceSupplier) - { - this.supplier = instanceSupplier; - } - - public static OptionalCapabilityInstance of(final @Nullable NonNullSupplier instanceSupplier) - { - return instanceSupplier == null ? EMPTY.cast() : new OptionalCapabilityInstance<>(instanceSupplier); - } - - public static OptionalCapabilityInstance orEmpty(Capability cap, Capability toCheck, OptionalCapabilityInstance inst) - { - return cap == toCheck ? inst.cast() : EMPTY.cast(); - } - - private @Nullable T getValue() - { - if (!isValid) - return null; - if (resolved != null) - return resolved.get(); - - if (supplier != null) - { - resolved = new AtomicReference<>(null); - try - { - T temp = supplier.get(); - if (temp == null) - throw new IllegalStateException("Supplier must not return null value"); - resolved.set(temp); - return resolved.get(); - } - catch (Throwable e) - { - return null; - } - } - return null; - } - - /** - * Return {@code true} if there is a mod object present, otherwise {@code false}. - * - * @return {@code true} if there is a mod object present, otherwise {@code false} - */ - public boolean isPresent() - { - return supplier != null && isValid; - } - - /** - * If a mod object is present, invoke the specified consumer with the object, - * otherwise do nothing. - * - * @param consumer block to be executed if a mod object is present - * @throws NullPointerException if mod object is present and {@code consumer} is - * null - */ - public void ifPresent(NonNullConsumer consumer) - { - T val = getValue(); - if (isValid && val != null) - consumer.accept(val); - } - - /** - * If a mod object is present, and the mod object matches the given predicate, - * return an {@code OptionalMod} describing the value, otherwise return an - * empty {@code OptionalMod}. - * - * @param predicate a predicate to apply to the mod object, if present - * @return an {@code OptionalMod} describing the value of this {@code OptionalMod} - * if a mod object is present and the mod object matches the given predicate, - * otherwise an empty {@code OptionalMod} - * @throws NullPointerException if the predicate is null - */ - public OptionalCapabilityInstance filter(Predicate predicate) - { - Objects.requireNonNull(predicate); - final T value = getValue(); // To keep the non-null contract we have to evaluate right now. Should we allow this function at all? - return predicate.test(value) ? OptionalCapabilityInstance.of(()->value) : empty(); - } - - /** - * If a mod object is present, apply the provided mapping function to it, - * and if the result is non-null, return an {@code Optional} describing the - * result. Otherwise return an empty {@code Optional}. - * - * @apiNote This method supports post-processing on optional values, without - * the need to explicitly check for a return status. - * - * @param The type of the result of the mapping function - * @param mapper a mapping function to apply to the mod object, if present - * @return an {@code Optional} describing the result of applying a mapping - * function to the mod object of this {@code OptionalMod}, if a mod object is present, - * otherwise an empty {@code Optional} - * @throws NullPointerException if the mapping function is null - */ - public OptionalCapabilityInstance map(Function mapper) - { - Objects.requireNonNull(mapper); - return isPresent() ? OptionalCapabilityInstance.of(()->mapper.apply(getValue())) : empty(); - } - - /** - * Return the mod object if present, otherwise return {@code other}. - * - * @param other the mod object to be returned if there is no mod object present, may - * be null - * @return the mod object, if present, otherwise {@code other} - */ - public T orElse(T other) - { - T val = getValue(); - return val != null ? val : other; - } - - /** - * Return the mod object if present, otherwise invoke {@code other} and return - * the result of that invocation. - * - * @param other a {@code Supplier} whose result is returned if no mod object - * is present - * @return the mod object if present otherwise the result of {@code other.get()} - * @throws NullPointerException if mod object is not present and {@code other} is - * null - */ - public T orElseGet(NonNullSupplier other) - { - T val = getValue(); - return val != null ? val : other.get(); - } - - /** - * Return the contained mod object, if present, otherwise throw an exception - * to be created by the provided supplier. - * - * @apiNote A method reference to the exception constructor with an empty - * argument list can be used as the supplier. For example, - * {@code IllegalStateException::new} - * - * @param Type of the exception to be thrown - * @param exceptionSupplier The supplier which will return the exception to - * be thrown - * @return the present mod object - * @throws X if there is no mod object present - * @throws NullPointerException if no mod object is present and - * {@code exceptionSupplier} is null - */ - public T orElseThrow(Supplier exceptionSupplier) throws X - { - T val = getValue(); - if (val != null) - return val; - throw exceptionSupplier.get(); - } - - /** - * Registers a listener that will be called when this Optional becomes invalid. - */ - public void addListener(Consumer> listener) - { - if (!isPresent()) - listener.accept(this); // They are stupid so just directly call them. - else - this.listeners.add(listener); - } - - /* - * Should only be called by the 'Owner' of this capability, this is to notify any listerners that this has become invalid and they should update. - * For example, a TE would call this, if they are covered with a microblock panel, thus cutting off pipe connectivity to this side. - * Also should be called for all caps when a TE is invalidated, or a world/chunk unloads, or a entity dies, etc... - * This allows modders to keep a cache of Capabilities instead of re-checking them every tick. - */ - public void invalidate() - { - this.isValid = false; - this.listeners.forEach(e -> e.accept(this)); - } -} diff --git a/src/main/java/net/minecraftforge/common/model/animation/CapabilityAnimation.java b/src/main/java/net/minecraftforge/common/model/animation/CapabilityAnimation.java index 7e79a3f1c..3790e150c 100644 --- a/src/main/java/net/minecraftforge/common/model/animation/CapabilityAnimation.java +++ b/src/main/java/net/minecraftforge/common/model/animation/CapabilityAnimation.java @@ -25,7 +25,7 @@ import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.CapabilityInject; import net.minecraftforge.common.capabilities.CapabilityManager; import net.minecraftforge.common.capabilities.ICapabilityProvider; -import net.minecraftforge.common.capabilities.OptionalCapabilityInstance; +import net.minecraftforge.common.util.LazyOptional; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -53,18 +53,18 @@ public class CapabilityAnimation public static class DefaultItemAnimationCapabilityProvider implements ICapabilityProvider { @Nonnull - private final OptionalCapabilityInstance asm; + private final LazyOptional asm; - public DefaultItemAnimationCapabilityProvider(@Nonnull OptionalCapabilityInstance asm) + public DefaultItemAnimationCapabilityProvider(@Nonnull LazyOptional asm) { this.asm = asm; } @Override @Nonnull - public OptionalCapabilityInstance getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) + public LazyOptional getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { - return OptionalCapabilityInstance.orEmpty(capability, ANIMATION_CAPABILITY, asm); + return ANIMATION_CAPABILITY.orEmpty(capability, asm); } } } diff --git a/src/main/java/net/minecraftforge/common/util/LazyOptional.java b/src/main/java/net/minecraftforge/common/util/LazyOptional.java new file mode 100644 index 000000000..5cba70f7e --- /dev/null +++ b/src/main/java/net/minecraftforge/common/util/LazyOptional.java @@ -0,0 +1,298 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2018. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common.util; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import mcp.MethodsReturnNonnullByDefault; +import net.minecraftforge.common.capabilities.Capability; + +/** + * This object encapsulates a lazy value, with typical transformation operations + * (map/ifPresent) available, much like {@link Optional}. + *

+ * It also provides the ability to listen for invalidation, via + * {@link #addListener(Consumer)}. This method is invoked when the provider of + * this object calls {@link #invalidate()}. + *

+ * To create an instance of this class, use {@link #of(NonNullSupplier)}. Note + * that this accepts a {@link NonNullSupplier}, so the result of the supplier + * must never be null. + *

+ * The empty instance can be retrieved with {@link #empty()}. + * + * @param The type of the optional value. + */ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class LazyOptional +{ + private final NonNullSupplier supplier; + private AtomicReference resolved; + private Set>> listeners = new HashSet<>(); + private boolean isValid = true; + + private static final @Nonnull LazyOptional EMPTY = new LazyOptional<>(null); + + /** + * Construct a new {@link LazyOptional} that wraps the given + * {@link NonNullSupplier}. + * + * @param instanceSupplier The {@link NonNullSupplier} to wrap. Cannot return + * null, but can be null itself. If null, this method + * returns {@link #empty()}. + */ + public static LazyOptional of(final @Nullable NonNullSupplier instanceSupplier) + { + return instanceSupplier == null ? empty() : new LazyOptional<>(instanceSupplier); + } + + /** + * @return The singleton empty instance + */ + public static LazyOptional empty() + { + return EMPTY.cast(); + } + + /** + * This method hides an unchecked cast to the inferred type. Only use this if + * you are sure the type should match. For capabilities, generally + * {@link Capability#orEmpty(Capability, LazyOptional)} should be used. + * + * @return This {@link LazyOptional}, cast to the inferred generic type + */ + @SuppressWarnings("unchecked") + public LazyOptional cast() + { + return (LazyOptional)this; + } + + private LazyOptional(@Nullable NonNullSupplier instanceSupplier) + { + this.supplier = instanceSupplier; + } + + private @Nullable T getValue() + { + if (!isValid) + return null; + if (resolved != null) + return resolved.get(); + + if (supplier != null) + { + resolved = new AtomicReference<>(null); + try + { + T temp = supplier.get(); + if (temp == null) + throw new IllegalStateException("Supplier must not return null value"); + resolved.set(temp); + return resolved.get(); + } + catch (Throwable e) + { + return null; + } + } + return null; + } + + private T getValueUnsafe() + { + T ret = getValue(); + if (ret == null) + { + throw new IllegalStateException("LazyOptional is empty or otherwise returned null from getValue() unexpectedly"); + } + return ret; + } + + /** + * Check if this {@link LazyOptional} is non-empty. + * + * @return {@code true} if this {@link LazyOptional} is non-empty, i.e. holds a + * non-null supplier + */ + public boolean isPresent() + { + return supplier != null && isValid; + } + + /** + * If non-empty, invoke the specified {@link NonNullConsumer} with the object, + * otherwise do nothing. + * + * @param The {@link NonNullConsumer} to run if this optional is non-empty. + * @throws NullPointerException if {@code consumer} is null and this {@link LazyOptional} is non-empty + */ + public void ifPresent(NonNullConsumer consumer) + { + Objects.requireNonNull(consumer); + T val = getValue(); + if (isValid && val != null) + consumer.accept(val); + } + + /** + * If a this {@link LazyOptional} is non-empty, return a new + * {@link LazyOptional} encapsulating the mapping function. Otherwise, returns + * {@link #empty()}. + *

+ * The supplier inside this object is NOT resolved. + * + * @apiNote This method supports post-processing on optional values, without the + * need to explicitly check for a return status. + * + * @param The type of the result of the mapping function + * @param mapper A mapping function to apply to the mod object, if present + * @return A {@link LazyOptional} describing the result of applying a mapping + * function to the value of this {@link LazyOptional}, if a value is + * present, otherwise an empty {@link LazyOptional} + * @throws NullPointerException if {@code mapper} is null and this {@link LazyOptional} is non-empty + */ + public LazyOptional map(NonNullFunction mapper) + { + Objects.requireNonNull(mapper); + return isPresent() ? of(() -> mapper.apply(getValueUnsafe())) : empty(); + } + + /** + * Resolve the contained supplier if non-empty, and filter it by the given + * {@link NonNullPredicate}, returning empty if false. + *

+ * It is important to note that this method is not lazy, as + * it must resolve the value of the supplier to validate it with the + * predicate. + * + * @param predicate A {@link NonNullPredicate} to apply to the result of the + * contained supplier, if non-empty + * @return A {@link LazyOptional} containing the result of the contained + * supplier, if and only if the passed {@link NonNullPredicate} returns + * true, otherwise an empty {@link LazyOptional} + * @throws NullPointerException If {@code predicate} is null and this + * {@link LazyOptional} is non-empty + */ + public LazyOptional filter(NonNullPredicate predicate) + { + Objects.requireNonNull(predicate); + final T value = getValue(); // To keep the non-null contract we have to evaluate right now. Should we allow this function at all? + return value != null && predicate.test(value) ? of(() -> value) : empty(); + } + + /** + * Resolve the contained supplier if non-empty and return the result, otherwise return + * {@code other}. + * + * @param other the value to be returned if this {@link LazyOptional} is empty + * @return the result of the supplier, if non-empty, otherwise {@code other} + */ + public T orElse(T other) + { + T val = getValue(); + return val != null ? val : other; + } + + /** + * Resolve the contained supplier if non-empty and return the result, otherwise return the + * result of {@code other}. + * + * @param other A {@link NonNullSupplier} whose result is returned if this + * {@link LazyOptional} is empty + * @return The result of the supplier, if non-empty, otherwise the result of + * {@code other.get()} + * @throws NullPointerException If {@code other} is null and this + * {@link LazyOptional} is non-empty + */ + public T orElseGet(NonNullSupplier other) + { + T val = getValue(); + return val != null ? val : other.get(); + } + + /** + * Resolve the contained supplier if non-empty and return the result, otherwise throw the + * exception created by the provided {@link NonNullSupplier}. + * + * @apiNote A method reference to the exception constructor with an empty + * argument list can be used as the supplier. For example, + * {@code IllegalStateException::new} + * + * @param Type of the exception to be thrown + * @param exceptionSupplier The {@link NonNullSupplier} which will return the + * exception to be thrown + * @return The result of the supplier + * @throws X If this {@link LazyOptional} is empty + * @throws NullPointerException If {@code exceptionSupplier} is null and this + * {@link LazyOptional} is empty + */ + public T orElseThrow(NonNullSupplier exceptionSupplier) throws X + { + T val = getValue(); + if (val != null) + return val; + throw exceptionSupplier.get(); + } + + /** + * Register a {@link NonNullConsumer listener} that will be called when this {@link LazyOptional} becomes invalid (via {@link #invalidate()}). + *

+ * If this {@link LazyOptional} is empty, the listener will be called immediately. + */ + public void addListener(NonNullConsumer> listener) + { + if (isPresent()) + { + this.listeners.add(listener); + } + else + { + listener.accept(this); + } + } + + /** + * Invalidate this {@link LazyOptional}, making it unavailable for further use, + * and notifying any {@link #addListener(NonNullConsumer) listeners} that this + * has become invalid and they should update. + *

+ * This would typically be used with capability objects. For example, a TE would + * call this, if they are covered with a microblock panel, thus cutting off pipe + * connectivity to this side. + *

+ * Also should be called for all when a TE is invalidated, or a world/chunk + * unloads, or a entity dies, etc... This allows modders to keep a cache of + * capability objects instead of re-checking them every tick. + */ + public void invalidate() + { + this.isValid = false; + this.listeners.forEach(e -> e.accept(this)); + } +} diff --git a/src/main/java/net/minecraftforge/common/capabilities/NonNullConsumer.java b/src/main/java/net/minecraftforge/common/util/NonNullConsumer.java similarity index 83% rename from src/main/java/net/minecraftforge/common/capabilities/NonNullConsumer.java rename to src/main/java/net/minecraftforge/common/util/NonNullConsumer.java index 455149b9f..df730aa8b 100644 --- a/src/main/java/net/minecraftforge/common/capabilities/NonNullConsumer.java +++ b/src/main/java/net/minecraftforge/common/util/NonNullConsumer.java @@ -17,11 +17,17 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -package net.minecraftforge.common.capabilities; +package net.minecraftforge.common.util; + +import java.util.function.Consumer; import javax.annotation.Nonnull; -//Exactly like Consumer, except there IS a contract that the parameter must not be null. +/** + * Equivalent to {@link Consumer}, except with nonnull contract. + * + * @see Consumer + */ @FunctionalInterface public interface NonNullConsumer { diff --git a/src/main/java/net/minecraftforge/common/util/NonNullFunction.java b/src/main/java/net/minecraftforge/common/util/NonNullFunction.java new file mode 100644 index 000000000..50597a2d2 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/util/NonNullFunction.java @@ -0,0 +1,36 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2018. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common.util; + +import java.util.function.Function; + +import javax.annotation.Nonnull; + +/** + * Equivalent to {@link Function}, except with nonnull contract. + * + * @see Function + */ +@FunctionalInterface +public interface NonNullFunction +{ + @Nonnull + R apply(@Nonnull T t); +} diff --git a/src/main/java/net/minecraftforge/common/util/NonNullPredicate.java b/src/main/java/net/minecraftforge/common/util/NonNullPredicate.java new file mode 100644 index 000000000..7f4c77cb0 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/util/NonNullPredicate.java @@ -0,0 +1,35 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2018. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common.util; + +import java.util.function.Predicate; + +import javax.annotation.Nonnull; + +/** + * Equivalent to {@link Predicate}, except with nonnull contract. + * + * @see Predicate + */ +@FunctionalInterface +public interface NonNullPredicate +{ + boolean test(@Nonnull T t); +} diff --git a/src/main/java/net/minecraftforge/common/capabilities/NonNullSupplier.java b/src/main/java/net/minecraftforge/common/util/NonNullSupplier.java similarity index 82% rename from src/main/java/net/minecraftforge/common/capabilities/NonNullSupplier.java rename to src/main/java/net/minecraftforge/common/util/NonNullSupplier.java index f317cdac1..86881dae0 100644 --- a/src/main/java/net/minecraftforge/common/capabilities/NonNullSupplier.java +++ b/src/main/java/net/minecraftforge/common/util/NonNullSupplier.java @@ -17,11 +17,17 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -package net.minecraftforge.common.capabilities; +package net.minecraftforge.common.util; + +import java.util.function.Supplier; import javax.annotation.Nonnull; -//Exactly like Supplier, except there IS a contract that each invocation should return a new unique instance and it must not be null. +/** + * Equivalent to {@link Supplier}, except with nonnull contract. + * + * @see Supplier + */ @FunctionalInterface public interface NonNullSupplier { diff --git a/src/main/java/net/minecraftforge/fluids/FluidUtil.java b/src/main/java/net/minecraftforge/fluids/FluidUtil.java index 7463e078a..9d5c26d76 100644 --- a/src/main/java/net/minecraftforge/fluids/FluidUtil.java +++ b/src/main/java/net/minecraftforge/fluids/FluidUtil.java @@ -41,7 +41,7 @@ import net.minecraft.util.SoundEvent; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.common.ForgeMod; -import net.minecraftforge.common.capabilities.OptionalCapabilityInstance; +import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandlerItem; @@ -434,7 +434,7 @@ public class FluidUtil * * Vanilla buckets will be converted to universal buckets if they are enabled. */ - public static OptionalCapabilityInstance getFluidHandler(@Nonnull ItemStack itemStack) + public static LazyOptional getFluidHandler(@Nonnull ItemStack itemStack) { return itemStack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY); } @@ -442,7 +442,7 @@ public class FluidUtil /** * Helper method to get the fluid contained in an itemStack */ - public static OptionalCapabilityInstance getFluidContained(@Nonnull ItemStack container) + public static LazyOptional getFluidContained(@Nonnull ItemStack container) { if (!container.isEmpty()) { @@ -450,13 +450,13 @@ public class FluidUtil return getFluidHandler(container) .map(handler -> handler.drain(Integer.MAX_VALUE, false)); } - return OptionalCapabilityInstance.empty(); + return LazyOptional.empty(); } /** * Helper method to get an IFluidHandler for at a block position. */ - public static OptionalCapabilityInstance getFluidHandler(World world, BlockPos blockPos, @Nullable EnumFacing side) + public static LazyOptional getFluidHandler(World world, BlockPos blockPos, @Nullable EnumFacing side) { IBlockState state = world.getBlockState(blockPos); Block block = state.getBlock(); @@ -478,7 +478,7 @@ public class FluidUtil return OptionalCapabilityInstance.of(() -> new BlockLiquidWrapper((BlockLiquid) block, world, blockPos)); } */ - return OptionalCapabilityInstance.empty(); + return LazyOptional.empty(); } /** diff --git a/src/main/java/net/minecraftforge/fluids/capability/TileFluidHandler.java b/src/main/java/net/minecraftforge/fluids/capability/TileFluidHandler.java index 3c14b6b32..50cb4e1b4 100644 --- a/src/main/java/net/minecraftforge/fluids/capability/TileFluidHandler.java +++ b/src/main/java/net/minecraftforge/fluids/capability/TileFluidHandler.java @@ -24,7 +24,7 @@ import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityType; import net.minecraft.util.EnumFacing; import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.capabilities.OptionalCapabilityInstance; +import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidTank; @@ -35,7 +35,7 @@ public class TileFluidHandler extends TileEntity { protected FluidTank tank = new FluidTank(Fluid.BUCKET_VOLUME); - private final OptionalCapabilityInstance holder = OptionalCapabilityInstance.of(() -> tank); + private final LazyOptional holder = LazyOptional.of(() -> tank); public TileFluidHandler(@Nonnull TileEntityType tileEntityTypeIn) { @@ -59,7 +59,7 @@ public class TileFluidHandler extends TileEntity @Override @Nonnull - public OptionalCapabilityInstance getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) + public LazyOptional getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) return holder.cast(); diff --git a/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerItemStack.java b/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerItemStack.java index e8b49425d..2f18810bc 100644 --- a/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerItemStack.java +++ b/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerItemStack.java @@ -27,7 +27,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ICapabilityProvider; -import net.minecraftforge.common.capabilities.OptionalCapabilityInstance; +import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.*; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.FluidTankProperties; @@ -47,7 +47,7 @@ public class FluidHandlerItemStack implements IFluidHandlerItem, ICapabilityProv { public static final String FLUID_NBT_KEY = "Fluid"; - private final OptionalCapabilityInstance holder = OptionalCapabilityInstance.of(() -> this); + private final LazyOptional holder = LazyOptional.of(() -> this); @Nonnull protected ItemStack container; @@ -205,9 +205,9 @@ public class FluidHandlerItemStack implements IFluidHandlerItem, ICapabilityProv @Override @Nonnull - public OptionalCapabilityInstance getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) + public LazyOptional getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { - return OptionalCapabilityInstance.orEmpty(capability, CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, holder); + return CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY.orEmpty(capability, holder); } /** diff --git a/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerItemStackSimple.java b/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerItemStackSimple.java index 1f618907c..c662301f3 100644 --- a/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerItemStackSimple.java +++ b/src/main/java/net/minecraftforge/fluids/capability/templates/FluidHandlerItemStackSimple.java @@ -27,7 +27,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ICapabilityProvider; -import net.minecraftforge.common.capabilities.OptionalCapabilityInstance; +import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.FluidTankProperties; import net.minecraftforge.fluids.capability.IFluidHandlerItem; @@ -44,7 +44,7 @@ public class FluidHandlerItemStackSimple implements IFluidHandlerItem, ICapabili { public static final String FLUID_NBT_KEY = "Fluid"; - private final OptionalCapabilityInstance holder = OptionalCapabilityInstance.of(() -> this); + private final LazyOptional holder = LazyOptional.of(() -> this); @Nonnull protected ItemStack container; @@ -182,9 +182,9 @@ public class FluidHandlerItemStackSimple implements IFluidHandlerItem, ICapabili @Override @Nonnull - public OptionalCapabilityInstance getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) + public LazyOptional getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { - return OptionalCapabilityInstance.orEmpty(capability, CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, holder); + return CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY.orEmpty(capability, holder); } /** diff --git a/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBucketWrapper.java b/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBucketWrapper.java index 4aeaa4635..a74c2c457 100644 --- a/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBucketWrapper.java +++ b/src/main/java/net/minecraftforge/fluids/capability/wrappers/FluidBucketWrapper.java @@ -30,7 +30,7 @@ import net.minecraft.util.EnumFacing; import net.minecraftforge.common.ForgeMod; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ICapabilityProvider; -import net.minecraftforge.common.capabilities.OptionalCapabilityInstance; +import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fluids.FluidStack; @@ -46,7 +46,7 @@ import net.minecraftforge.fluids.capability.CapabilityFluidHandler; */ public class FluidBucketWrapper implements IFluidHandlerItem, ICapabilityProvider { - private final OptionalCapabilityInstance holder = OptionalCapabilityInstance.of(() -> this); + private final LazyOptional holder = LazyOptional.of(() -> this); @Nonnull protected ItemStack container; @@ -175,8 +175,8 @@ public class FluidBucketWrapper implements IFluidHandlerItem, ICapabilityProvide @Override @Nonnull - public OptionalCapabilityInstance getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) + public LazyOptional getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { - return OptionalCapabilityInstance.orEmpty(capability, CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, holder); + return CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY.orEmpty(capability, holder); } } diff --git a/src/main/java/net/minecraftforge/items/VanillaInventoryCodeHooks.java b/src/main/java/net/minecraftforge/items/VanillaInventoryCodeHooks.java index 38ead4c48..5139e8ace 100644 --- a/src/main/java/net/minecraftforge/items/VanillaInventoryCodeHooks.java +++ b/src/main/java/net/minecraftforge/items/VanillaInventoryCodeHooks.java @@ -31,7 +31,7 @@ import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; -import net.minecraftforge.common.capabilities.OptionalCapabilityInstance; +import net.minecraftforge.common.util.LazyOptional; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; @@ -212,7 +212,7 @@ public class VanillaInventoryCodeHooks return stack; } - private static OptionalCapabilityInstance> getItemHandler(IHopper hopper, EnumFacing hopperFacing) + private static LazyOptional> getItemHandler(IHopper hopper, EnumFacing hopperFacing) { double x = hopper.getXPos() + (double) hopperFacing.getXOffset(); double y = hopper.getYPos() + (double) hopperFacing.getYOffset(); @@ -246,7 +246,7 @@ public class VanillaInventoryCodeHooks return true; } - public static OptionalCapabilityInstance> getItemHandler(World worldIn, double x, double y, double z, final EnumFacing side) + public static LazyOptional> getItemHandler(World worldIn, double x, double y, double z, final EnumFacing side) { int i = MathHelper.floor(x); int j = MathHelper.floor(y); @@ -264,6 +264,6 @@ public class VanillaInventoryCodeHooks } } - return OptionalCapabilityInstance.empty(); + return LazyOptional.empty(); } } diff --git a/src/main/java/net/minecraftforge/items/wrapper/EntityEquipmentInvWrapper.java b/src/main/java/net/minecraftforge/items/wrapper/EntityEquipmentInvWrapper.java index 3c1598d58..8432cf042 100644 --- a/src/main/java/net/minecraftforge/items/wrapper/EntityEquipmentInvWrapper.java +++ b/src/main/java/net/minecraftforge/items/wrapper/EntityEquipmentInvWrapper.java @@ -23,7 +23,7 @@ import com.google.common.collect.ImmutableList; import net.minecraft.entity.EntityLivingBase; import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.item.ItemStack; -import net.minecraftforge.common.capabilities.OptionalCapabilityInstance; +import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.ItemHandlerHelper; @@ -194,13 +194,13 @@ public abstract class EntityEquipmentInvWrapper implements IItemHandlerModifiabl return slots.get(slot); } - public static OptionalCapabilityInstance[] create(EntityLivingBase entity) + public static LazyOptional[] create(EntityLivingBase entity) { @SuppressWarnings("unchecked") - OptionalCapabilityInstance[] ret = new OptionalCapabilityInstance[3]; - ret[0] = OptionalCapabilityInstance.of(() -> new EntityHandsInvWrapper(entity)); - ret[1] = OptionalCapabilityInstance.of(() -> new EntityArmorInvWrapper(entity)); - ret[2] = OptionalCapabilityInstance.of(() -> new CombinedInvWrapper(ret[0].orElse(null), ret[1].orElse(null))); + LazyOptional[] ret = new LazyOptional[3]; + ret[0] = LazyOptional.of(() -> new EntityHandsInvWrapper(entity)); + ret[1] = LazyOptional.of(() -> new EntityArmorInvWrapper(entity)); + ret[2] = LazyOptional.of(() -> new CombinedInvWrapper(ret[0].orElse(null), ret[1].orElse(null))); return ret; } } diff --git a/src/main/java/net/minecraftforge/items/wrapper/SidedInvWrapper.java b/src/main/java/net/minecraftforge/items/wrapper/SidedInvWrapper.java index 11add8d91..4dc01fa92 100644 --- a/src/main/java/net/minecraftforge/items/wrapper/SidedInvWrapper.java +++ b/src/main/java/net/minecraftforge/items/wrapper/SidedInvWrapper.java @@ -22,7 +22,7 @@ package net.minecraftforge.items.wrapper; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; -import net.minecraftforge.common.capabilities.OptionalCapabilityInstance; +import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.ItemHandlerHelper; @@ -34,11 +34,11 @@ public class SidedInvWrapper implements IItemHandlerModifiable protected final EnumFacing side; @SuppressWarnings("unchecked") - public static OptionalCapabilityInstance[] create(ISidedInventory inv, EnumFacing... sides) { - OptionalCapabilityInstance[] ret = new OptionalCapabilityInstance[sides.length]; + public static LazyOptional[] create(ISidedInventory inv, EnumFacing... sides) { + LazyOptional[] ret = new LazyOptional[sides.length]; for (int x = 0; x < sides.length; x++) { final EnumFacing side = sides[x]; - ret[x] = OptionalCapabilityInstance.of(() -> new SidedInvWrapper(inv, side)); + ret[x] = LazyOptional.of(() -> new SidedInvWrapper(inv, side)); } return ret; }