diff --git a/src/main/java/gregtech/api/GTValues.java b/src/main/java/gregtech/api/GTValues.java index 7a1b5d34f4f..0d57fc6d5f9 100644 --- a/src/main/java/gregtech/api/GTValues.java +++ b/src/main/java/gregtech/api/GTValues.java @@ -158,7 +158,8 @@ public class GTValues { MODID_TCON = "tconstruct", MODID_PROJRED_CORE = "projectred-core", MODID_RC = "railcraft", - MODID_CHISEL = "chisel"; + MODID_CHISEL = "chisel", + MODID_RS = "refinedstorage"; private static Boolean isClient; diff --git a/src/main/java/gregtech/api/util/ModCompatibility.java b/src/main/java/gregtech/api/util/ModCompatibility.java index 9881948147d..d6b627d3b6a 100644 --- a/src/main/java/gregtech/api/util/ModCompatibility.java +++ b/src/main/java/gregtech/api/util/ModCompatibility.java @@ -1,66 +1,22 @@ package gregtech.api.util; -import gregtech.api.util.world.DummyWorld; +import gregtech.client.utils.ItemRenderCompat; import net.minecraft.item.ItemStack; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; -import java.lang.reflect.Method; -import java.util.List; -import java.util.Objects; +import org.jetbrains.annotations.ApiStatus; @SideOnly(Side.CLIENT) public class ModCompatibility { - private static RefinedStorage refinedStorage; - - public static void initCompat() { - try { - Class itemClass = Class.forName("com.raoulvdberge.refinedstorage.item.ItemPattern"); - refinedStorage = new RefinedStorage(itemClass); - GTLog.logger.info("RefinedStorage found; enabling integration."); - } catch (ClassNotFoundException ignored) { - GTLog.logger.info("RefinedStorage not found; skipping integration."); - } catch (Throwable exception) { - GTLog.logger.error("Failed to enable RefinedStorage integration", exception); - } - } - + /** + * @deprecated Use {@link ItemRenderCompat#getRepresentedStack(ItemStack)} + */ + @ApiStatus.ScheduledForRemoval(inVersion = "2.10") + @Deprecated public static ItemStack getRealItemStack(ItemStack itemStack) { - if (refinedStorage != null && RefinedStorage.canHandleItemStack(itemStack)) { - return refinedStorage.getRealItemStack(itemStack); - } - return itemStack; - } - - private static class RefinedStorage { - - private final Method getPatternFromCacheMethod; - private final Method getOutputsMethod; - - public RefinedStorage(Class itemPatternClass) throws ReflectiveOperationException { - this.getPatternFromCacheMethod = itemPatternClass.getMethod("getPatternFromCache", World.class, - ItemStack.class); - this.getOutputsMethod = getPatternFromCacheMethod.getReturnType().getMethod("getOutputs"); - } - - public static boolean canHandleItemStack(ItemStack itemStack) { - ResourceLocation registryName = Objects.requireNonNull(itemStack.getItem().getRegistryName()); - return registryName.getNamespace().equals("refinedstorage") && - registryName.getPath().equals("pattern"); - } - - public ItemStack getRealItemStack(ItemStack itemStack) { - try { - Object craftingPattern = getPatternFromCacheMethod.invoke(null, DummyWorld.INSTANCE, itemStack); - List outputs = (List) getOutputsMethod.invoke(craftingPattern); - return outputs.isEmpty() ? itemStack : outputs.get(0); - } catch (ReflectiveOperationException ex) { - throw new RuntimeException("Failed to obtain item from ItemPattern", ex); - } - } + return ItemRenderCompat.getRepresentedStack(itemStack); } } diff --git a/src/main/java/gregtech/client/ClientProxy.java b/src/main/java/gregtech/client/ClientProxy.java index 0210563b18c..2b7207a232d 100644 --- a/src/main/java/gregtech/client/ClientProxy.java +++ b/src/main/java/gregtech/client/ClientProxy.java @@ -10,12 +10,17 @@ import gregtech.api.unification.stack.UnificationEntry; import gregtech.api.util.FluidTooltipUtil; import gregtech.api.util.IBlockOre; -import gregtech.api.util.ModCompatibility; import gregtech.client.model.customtexture.CustomTextureModelHandler; import gregtech.client.model.customtexture.MetadataSectionCTM; import gregtech.client.renderer.handler.FacadeRenderer; import gregtech.client.renderer.handler.MetaTileEntityRenderer; -import gregtech.client.renderer.pipe.*; +import gregtech.client.renderer.pipe.CableRenderer; +import gregtech.client.renderer.pipe.FluidPipeRenderer; +import gregtech.client.renderer.pipe.ItemPipeRenderer; +import gregtech.client.renderer.pipe.LaserPipeRenderer; +import gregtech.client.renderer.pipe.OpticalPipeRenderer; +import gregtech.client.renderer.pipe.PipeRenderer; +import gregtech.client.utils.ItemRenderCompat; import gregtech.client.utils.TooltipHelper; import gregtech.common.CommonProxy; import gregtech.common.ConfigHolder; @@ -101,7 +106,7 @@ public void onLoad() { public void onPostLoad() { super.onPostLoad(); TerminalRegistry.initTerminalFiles(); - ModCompatibility.initCompat(); + ItemRenderCompat.init(); FacadeRenderer.init(); } diff --git a/src/main/java/gregtech/client/renderer/handler/CCLBlockRenderer.java b/src/main/java/gregtech/client/renderer/handler/CCLBlockRenderer.java index 5dabff41d6a..e2046685f86 100644 --- a/src/main/java/gregtech/client/renderer/handler/CCLBlockRenderer.java +++ b/src/main/java/gregtech/client/renderer/handler/CCLBlockRenderer.java @@ -2,9 +2,9 @@ import gregtech.api.util.GTLog; import gregtech.api.util.GTUtility; -import gregtech.api.util.ModCompatibility; import gregtech.client.renderer.ICCLBlockRenderer; import gregtech.client.renderer.texture.Textures; +import gregtech.client.utils.ItemRenderCompat; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; @@ -60,10 +60,10 @@ public void onModelsBake(ModelBakeEvent event) { @Override public void renderItem(ItemStack rawStack, ItemCameraTransforms.TransformType transformType) { - ItemStack stack = ModCompatibility.getRealItemStack(rawStack); - if (stack.getItem() instanceof ItemBlock && - ((ItemBlock) stack.getItem()).getBlock() instanceof ICCLBlockRenderer) { - ((ICCLBlockRenderer) ((ItemBlock) stack.getItem()).getBlock()).renderItem(stack, transformType); + ItemStack stack = ItemRenderCompat.getRepresentedStack(rawStack); + if (stack.getItem() instanceof ItemBlock itemBlock && + itemBlock.getBlock() instanceof ICCLBlockRenderer renderer) { + renderer.renderItem(stack, transformType); } } diff --git a/src/main/java/gregtech/client/renderer/handler/FacadeRenderer.java b/src/main/java/gregtech/client/renderer/handler/FacadeRenderer.java index fcd0c3ba523..6db1848659a 100644 --- a/src/main/java/gregtech/client/renderer/handler/FacadeRenderer.java +++ b/src/main/java/gregtech/client/renderer/handler/FacadeRenderer.java @@ -2,11 +2,11 @@ import gregtech.api.cover.CoverUtil; import gregtech.api.items.metaitem.MetaItem; -import gregtech.api.util.ModCompatibility; import gregtech.client.model.pipeline.VertexLighterFlatSpecial; import gregtech.client.model.pipeline.VertexLighterSmoothAoSpecial; import gregtech.client.utils.AdvCCRSConsumer; import gregtech.client.utils.FacadeBlockAccess; +import gregtech.client.utils.ItemRenderCompat; import gregtech.common.covers.facade.FacadeHelper; import gregtech.common.items.behaviors.FacadeItem; @@ -82,7 +82,7 @@ public static void init() { @Override public void renderItem(ItemStack rawStack, ItemCameraTransforms.TransformType transformType) { - ItemStack itemStack = ModCompatibility.getRealItemStack(rawStack); + ItemStack itemStack = ItemRenderCompat.getRepresentedStack(rawStack); if (!(itemStack.getItem() instanceof MetaItem)) { return; } diff --git a/src/main/java/gregtech/client/renderer/handler/MetaTileEntityRenderer.java b/src/main/java/gregtech/client/renderer/handler/MetaTileEntityRenderer.java index 9436d30dc8d..846bcc08860 100644 --- a/src/main/java/gregtech/client/renderer/handler/MetaTileEntityRenderer.java +++ b/src/main/java/gregtech/client/renderer/handler/MetaTileEntityRenderer.java @@ -4,9 +4,9 @@ import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.util.GTLog; import gregtech.api.util.GTUtility; -import gregtech.api.util.ModCompatibility; import gregtech.client.renderer.CubeRendererState; import gregtech.client.renderer.texture.Textures; +import gregtech.client.utils.ItemRenderCompat; import net.minecraft.block.state.IBlockState; import net.minecraft.client.renderer.BufferBuilder; @@ -72,7 +72,7 @@ public void onModelsBake(ModelBakeEvent event) { @Override public void renderItem(ItemStack rawStack, TransformType transformType) { - ItemStack stack = ModCompatibility.getRealItemStack(rawStack); + ItemStack stack = ItemRenderCompat.getRepresentedStack(rawStack); MetaTileEntity metaTileEntity = GTUtility.getMetaTileEntity(stack); if (metaTileEntity == null) { return; diff --git a/src/main/java/gregtech/client/renderer/pipe/PipeRenderer.java b/src/main/java/gregtech/client/renderer/pipe/PipeRenderer.java index d16ca9ace07..e3195dba22c 100644 --- a/src/main/java/gregtech/client/renderer/pipe/PipeRenderer.java +++ b/src/main/java/gregtech/client/renderer/pipe/PipeRenderer.java @@ -11,9 +11,9 @@ import gregtech.api.unification.material.Material; import gregtech.api.unification.material.info.MaterialIconType; import gregtech.api.util.GTUtility; -import gregtech.api.util.ModCompatibility; import gregtech.client.renderer.CubeRendererState; import gregtech.client.renderer.texture.Textures; +import gregtech.client.utils.ItemRenderCompat; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; @@ -150,7 +150,7 @@ public abstract void buildRenderer(PipeRenderContext renderContext, BlockPipe + * Primarily used to retrieve the output stack from AE2 or RS Patterns. + * + * @param stack the stack to retrieve from + * @return the actual represented ItemStack + */ + public static @NotNull ItemStack getRepresentedStack(@NotNull ItemStack stack) { + if (ae2Handler != null && ae2Handler.canHandleStack(stack)) { + return ae2Handler.getActualStack(stack); + } + if (rsHandler != null && rsHandler.canHandleStack(stack)) { + return rsHandler.getActualStack(stack); + } + return stack; + } + + /** + * An extractor to retrieve a represented stack from an ItemStack + */ + public interface RepresentativeStackExtractor { + + /** + * @param stack the stack to test + * @return if the extractor can handle the stack + */ + boolean canHandleStack(@NotNull ItemStack stack); + + /** + * @param stack the stack to retrieve from + * @return the represented stack + */ + @NotNull + ItemStack getActualStack(@NotNull ItemStack stack); + } + + /** + * Extracts the output stack from AE2 Patterns + */ + private static final class AE2StackExtractor implements RepresentativeStackExtractor { + + public static @Nullable ItemRenderCompat.AE2StackExtractor create() { + if (!Loader.isModLoaded(GTValues.MODID_APPENG)) return null; + GTLog.logger.info("AppliedEnergistics2 found; enabling render integration."); + return new AE2StackExtractor(); + } + + @Override + public boolean canHandleStack(@NotNull ItemStack stack) { + return stack.getItem() instanceof ItemEncodedPattern; + } + + @Override + public @NotNull ItemStack getActualStack(@NotNull ItemStack stack) { + if (stack.isEmpty()) return ItemStack.EMPTY; + if (stack.getItem() instanceof ItemEncodedPattern encodedPattern) { + return encodedPattern.getOutput(stack); + } + return stack; + } + } + + /** + * Extracts the output stack from RS Patterns + */ + @SuppressWarnings("ClassCanBeRecord") + private static final class RSStackExtractor implements RepresentativeStackExtractor { + + private final MethodHandle getPatternFromCacheHandle; + private final MethodHandle getOutputsHandle; + private final Class itemPatternClass; + + private RSStackExtractor(MethodHandle getPatternFromCacheHandle, MethodHandle getOutputsHandle, + Class itemPatternClass) { + this.getPatternFromCacheHandle = getPatternFromCacheHandle; + this.getOutputsHandle = getOutputsHandle; + this.itemPatternClass = itemPatternClass; + } + + public static @Nullable ItemRenderCompat.RSStackExtractor create() { + if (!Loader.isModLoaded(GTValues.MODID_RS)) return null; + + Class clazz; + try { + clazz = Class.forName("com.raoulvdberge.refinedstorage.item.ItemPattern"); + GTLog.logger.info("RefinedStorage found; enabling render integration."); + } catch (ClassNotFoundException ignored) { + GTLog.logger.error("RefinedStorage classes not found; skipping render integration."); + return null; + } + + try { + Method method = clazz.getMethod("getPatternFromCache", World.class, ItemStack.class); + + MethodHandles.Lookup lookup = MethodHandles.publicLookup(); + + MethodHandle getPatternFromCacheHandle = lookup.unreflect(method); + + method = method.getReturnType().getMethod("getOutputs"); + MethodHandle getOutputsHandle = lookup.unreflect(method); + + return new RSStackExtractor(getPatternFromCacheHandle, getOutputsHandle, clazz); + } catch (NoSuchMethodException | IllegalAccessException e) { + GTLog.logger.error("Failed to enable RefinedStorage integration", e); + return null; + } + } + + @Override + public boolean canHandleStack(@NotNull ItemStack stack) { + return itemPatternClass.isAssignableFrom(stack.getItem().getClass()); + } + + @SuppressWarnings("unchecked") + @Override + public @NotNull ItemStack getActualStack(@NotNull ItemStack stack) { + if (stack.isEmpty()) return ItemStack.EMPTY; + + List outputs; + try { + // ItemPattern.getPatternFromCache: (World, ItemStack) -> CraftingPattern + Object craftingPattern = getPatternFromCacheHandle.invoke(DummyWorld.INSTANCE, stack); + // CraftingPattern#getOutputs: () -> List + outputs = (List) getOutputsHandle.invoke(craftingPattern); + } catch (Throwable e) { + GTLog.logger.error("Failed to obtain item from ItemPattern", e); + return stack; + } + + if (outputs.isEmpty()) { + return stack; + } + return outputs.get(0); + } + } +}