diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capabilities.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capabilities.java new file mode 100644 index 0000000..8aa5dd5 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/Capabilities.java @@ -0,0 +1,111 @@ +package com.gtnewhorizon.gtnhlib.capability; + +import net.minecraft.entity.Entity; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +// spotless:off +/** + * Utility class for retrieving capabilities from various Minecraft objects. This class provides convenience methods + * that handle both capability-based and direct interface implementations, ensuring backward compatibility with + * traditional interface implementations. + *

+ * Example usage: + *

{@code
+ *     // Retrieve capability from a TileEntity
+ *     MyInterface impl = Capabilities.getCapability(tileEntity, MyCapability.class, ForgeDirection.NORTH);
+ *
+ *     // Retrieve capability from an ItemStack
+ *     MyInterface impl = Capabilities.getCapability(itemStack, MyCapability.class);
+ *
+ *     // Retrieve capability from an Entity
+ *     MyInterface impl = Capabilities.getCapability(entity, MyCapability.class);
+ * }
+ * + * @see CapabilityProvider + */ +// spotless:on +@SuppressWarnings("unused") +public final class Capabilities { + + /** + * Retrieves a capability from the given TileEntity. + * + * @param tileEntity The TileEntity to query. + * @param capability The capability being requested. + * @param side The side of the TileEntity being queried. + * @param The type of the capability interface. + * @return The capability implementation, or null if not available. + */ + public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Class capability, + @NotNull ForgeDirection side) { + return getCapability((Object) tileEntity, capability, side); + } + + /** + * Retrieves a capability from the given TileEntity without specifying a side. + * + * @param tileEntity The TileEntity to query. + * @param capability The capability being requested. + * @param The type of the capability interface. + * @return The capability implementation, or null if not available. + */ + public static T getCapability(@Nullable TileEntity tileEntity, @NotNull Class capability) { + return getCapability((Object) tileEntity, capability, ForgeDirection.UNKNOWN); + } + + /** + * Retrieves a capability from the given ItemStack's Item. + * + * @param itemStack The ItemStack to query. + * @param capability The capability being requested. + * @param The type of the capability interface. + * @return The capability implementation, or null if not available. + */ + public static T getCapability(@Nullable ItemStack itemStack, @NotNull Class capability) { + if (itemStack == null) { + return null; + } + return getCapability(itemStack.getItem(), capability, ForgeDirection.UNKNOWN); + } + + /** + * Retrieves a capability from the given Entity. + * + * @param entity The Entity to query. + * @param capability The capability being requested. + * @param The type of the capability interface. + * @return The capability implementation, or null if not available. + */ + public static T getCapability(@Nullable Entity entity, @NotNull Class capability) { + return getCapability(entity, capability, ForgeDirection.UNKNOWN); + } + + /** + * Internal utility method that tries a direct cast if the object matches the capability's interface, then falls + * back to querying {@link CapabilityProvider} if available. + * + * @param object The object to query. + * @param capability The capability being requested. + * @param side The side from which the capability is being requested. + * @param The type of the capability interface. + * @return The capability implementation, or null if not available. + */ + private static T getCapability(@Nullable Object object, @NotNull Class capability, + @NotNull ForgeDirection side) { + if (object == null) { + return null; + } + if (capability.isAssignableFrom(object.getClass())) { + return capability.cast(object); + } + if (object instanceof CapabilityProvider provider) { + return provider.getCapability(capability, side); + } + return null; + } +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityProvider.java b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityProvider.java new file mode 100644 index 0000000..15e6542 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/capability/CapabilityProvider.java @@ -0,0 +1,62 @@ +package com.gtnewhorizon.gtnhlib.capability; + +import net.minecraftforge.common.util.ForgeDirection; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +// spotless:off +/** + * Capability represents the ability of an object to expose a specific interface without directly implementing it, + * allowing for a more flexible and decoupled approach compared to the traditional approach. + * It is particularly useful when dealing with complex systems where objects may need to dynamically provide + * different functionalities or when working with complex patterns, like delegating responsibilities to another class. + *

+ * By implementing this interface, other systems are able to query and retrieve capabilities + * without relying on direct interface implementations. + *

+ * Note that it is required to replace all the {@code instanceof} checks with capability checks in order to + * ensure compatibility when migrating from direct interface implementation to capability. + * Avoid using capabilities for widely-used interfaces like {@code IFluidHandler} to prevent compatibility issues + * with other mods. + *

+ * Example usage: + *

{@code
+ *     public class MyTileEntity implements CapabilityProvider {
+ *         private MyCapability myCapability = new MyCapabilityImplementation();
+ *
+ *         @Override
+ *         public  T getCapability(@NotNull Class capability, @NotNull ForgeDirection side) {
+ *             if (capability == MyCapability.class) {
+ *                 return capability.cast(myCapability);
+ *             }
+ *             return null;
+ *         }
+ *     }
+ * }
+ * + * @see Capabilities + */ +// spotless:on +public interface CapabilityProvider { + + /** + * Queries this provider for a capability implementation. + *

+ * This method should: + *

    + *
  • Compare the requested capability with known capabilities using {@code ==} or do HashMap dispatch for a larger + * quantity of supported capability types.
  • + *
  • If matched, use {@link Class#cast} to return the implementation.
  • + *
  • Return null if the capability is not supported.
  • + *
+ * + * @param capability The capability being requested. + * @param side The {@link ForgeDirection} from which the capability is requested. Can be + * {@link ForgeDirection#UNKNOWN UNKNOWN} if the direction is not relevant. + * @param The type of the capability interface. + * @return The capability implementation, or null if not available. + */ + @Nullable + T getCapability(@NotNull Class capability, @NotNull ForgeDirection side); +}