From 2a8eb84c8bd28bf3a1795f3ea12f4268dea437b2 Mon Sep 17 00:00:00 2001 From: Sara Freimer Date: Sat, 27 Apr 2024 20:58:05 -0500 Subject: [PATCH] Refactor how containers are implemented and exposed as capabilities on itemstacks to properly use the data component system --- .../mekanism/api/chemical/ChemicalStack.java | 10 + .../mekanism/api/energy/IEnergyContainer.java | 2 +- .../api/fluid/IExtendedFluidTank.java | 5 + .../api/inventory/IInventorySlot.java | 7 +- .../loot/table/BaseBlockLootTables.java | 14 +- .../common/registries/GeneratorsBlocks.java | 64 +- .../common/registries/GeneratorsItems.java | 11 +- .../common/slot/FluidFuelInventorySlot.java | 2 +- .../QIOCraftingTransferHandler.java | 3 +- .../client/recipe_viewer/emi/MekanismEmi.java | 8 +- .../client/recipe_viewer/jei/MekanismJEI.java | 8 +- .../item/block/RenderFluidTankItem.java | 4 +- .../mekanism/common/attachments/LockData.java | 39 + .../containers/AttachedChemicalTanks.java | 89 --- .../containers/AttachedContainers.java | 49 -- .../containers/AttachedEnergyContainers.java | 27 - .../containers/AttachedFluidTanks.java | 45 -- .../containers/AttachedHeatCapacitors.java | 27 - .../containers/AttachedInventorySlots.java | 27 - .../containers/ComponentBackedContainer.java | 59 ++ .../containers/ComponentBackedHandler.java | 75 ++ .../attachments/containers/ContainerType.java | 252 ++++--- .../containers/ContainsRecipe.java | 11 + .../containers/IAttachedContainers.java | 50 ++ .../chemical/ChemicalTanksBuilder.java | 52 ++ .../ComponentBackedChemicalHandler.java | 66 ++ .../chemical/ComponentBackedChemicalTank.java | 227 ++++++ .../chemical/gas/AttachedGases.java | 39 + .../ComponentBackedChemicalTankGasTank.java | 52 ++ .../gas/ComponentBackedGasHandler.java | 23 + .../chemical/gas/ComponentBackedGasTank.java | 31 + .../chemical/gas/GasTanksBuilder.java | 58 ++ .../chemical/infuse/AttachedInfuseTypes.java | 39 + ...mponentBackedChemicalTankInfusionTank.java | 52 ++ .../ComponentBackedInfusionHandler.java | 23 + .../infuse/ComponentBackedInfusionTank.java | 31 + .../chemical/infuse/InfusionTanksBuilder.java | 58 ++ .../chemical/merged/MergedTankCreator.java | 84 +++ .../chemical/pigment/AttachedPigments.java | 39 + ...omponentBackedChemicalTankPigmentTank.java | 52 ++ .../ComponentBackedPigmentHandler.java | 23 + .../pigment/ComponentBackedPigmentTank.java | 31 + .../chemical/pigment/PigmentTanksBuilder.java | 58 ++ .../chemical/slurry/AttachedSlurries.java | 39 + ...ComponentBackedChemicalTankSlurryTank.java | 52 ++ .../slurry/ComponentBackedSlurryHandler.java | 23 + .../slurry/ComponentBackedSlurryTank.java | 31 + .../chemical/slurry/SlurryTanksBuilder.java | 58 ++ .../creator/BaseContainerCreator.java | 34 + .../creator/IBasicContainerCreator.java | 12 + .../containers/creator/IContainerCreator.java | 14 + .../containers/energy/AttachedEnergy.java | 40 + .../ComponentBackedEnergyContainer.java | 150 ++++ .../ComponentBackedEnergyCubeContainer.java | 39 + .../energy/ComponentBackedEnergyHandler.java | 62 ++ ...ComponentBackedNoClampEnergyContainer.java | 24 + ...ponentBackedResistiveEnergyContainer.java} | 23 +- .../energy/EnergyContainersBuilder.java | 54 ++ .../containers/fluid/AttachedFluids.java | 67 ++ .../fluid/ComponentBackedFluidHandler.java | 74 ++ .../fluid/ComponentBackedFluidTank.java | 241 ++++++ .../ComponentBackedFluidTankFluidTank.java | 50 ++ .../containers/fluid/FluidTanksBuilder.java | 77 ++ .../containers/heat/AttachedHeat.java | 38 + .../heat/ComponentBackedHeatCapacitor.java | 135 ++++ .../heat/ComponentBackedHeatHandler.java | 40 + .../containers/heat/HeatCapacitorData.java | 21 + .../heat/HeatCapacitorsBuilder.java | 60 ++ .../containers/item/AttachedItems.java | 52 ++ .../item/ComponentBackedBinInventorySlot.java | 165 +++++ .../item/ComponentBackedInventorySlot.java | 235 ++++++ .../item/ComponentBackedItemHandler.java | 62 ++ .../containers/item/ItemSlotsBuilder.java | 689 ++++++++++++++++++ .../qio/PortableQIODashboardInventory.java | 3 +- .../ChemicalTankRateLimitChemicalTank.java | 91 --- .../chemical/item/ChemicalTankSpec.java | 27 + .../variable/RateLimitChemicalTank.java | 33 - .../chemical/variable/RateLimitGasTank.java | 56 -- .../variable/RateLimitInfusionTank.java | 51 -- .../variable/RateLimitPigmentTank.java | 51 -- .../variable/RateLimitSlurryTank.java | 51 -- .../EnergyCubeRateLimitEnergyContainer.java | 37 - .../item/NoClampRateLimitEnergyContainer.java | 43 -- .../energy/item/RateLimitEnergyContainer.java | 48 -- .../capabilities/fluid/BasicFluidTank.java | 4 +- .../item/FluidTankRateLimitFluidTank.java | 48 -- .../fluid/item/FluidTankSpec.java | 15 + .../fluid/item/RateLimitFluidTank.java | 55 -- .../capabilities/heat/BasicHeatCapacitor.java | 4 - .../common/content/qio/QIOCraftingWindow.java | 31 +- .../qio/QIOServerCraftingTransferHandler.java | 12 +- .../container/QIOItemViewerContainer.java | 6 +- .../inventory/slot/BasicInventorySlot.java | 14 +- .../inventory/slot/BinInventorySlot.java | 20 +- .../inventory/slot/EnergyInventorySlot.java | 8 +- .../inventory/slot/FluidInventorySlot.java | 4 +- .../inventory/slot/ItemSlotsBuilder.java | 182 ----- .../inventory/slot/SecurityInventorySlot.java | 11 +- .../slot/chemical/GasInventorySlot.java | 5 +- .../slot/chemical/InfusionInventorySlot.java | 2 +- .../chemical/MergedChemicalInventorySlot.java | 18 +- .../mekanism/common/item/ItemEnergized.java | 10 +- .../common/item/ItemGaugeDropper.java | 22 + .../common/item/block/ItemBlockBin.java | 5 +- .../item/block/ItemBlockEnergyCube.java | 8 +- .../common/item/block/ItemBlockTooltip.java | 25 +- .../machine/ItemBlockResistiveHeater.java | 11 +- .../common/item/gear/ItemFreeRunners.java | 9 +- .../common/item/gear/ItemGasArmor.java | 10 +- .../common/item/gear/ItemMekaSuitArmor.java | 38 +- .../common/item/gear/ItemMekaTool.java | 15 +- .../item/loot/CopyContainersLootFunction.java | 2 +- .../recipe/ClearConfigurationRecipe.java | 2 +- .../common/recipe/bin/BinExtractRecipe.java | 4 +- .../common/recipe/bin/BinInsertRecipe.java | 12 +- .../mekanism/common/recipe/bin/BinRecipe.java | 5 +- .../SingleChemicalStackIngredient.java | 2 +- .../creator/ItemStackIngredientCreator.java | 1 + .../recipe/upgrade/EnergyRecipeData.java | 2 +- .../recipe/upgrade/FluidRecipeData.java | 2 +- .../common/recipe/upgrade/ItemRecipeData.java | 2 +- .../common/recipe/upgrade/LockRecipeData.java | 6 +- .../recipe/upgrade/RecipeUpgradeData.java | 3 +- .../upgrade/chemical/ChemicalRecipeData.java | 4 +- .../upgrade/chemical/GasRecipeData.java | 6 +- .../upgrade/chemical/InfusionRecipeData.java | 6 +- .../upgrade/chemical/PigmentRecipeData.java | 6 +- .../upgrade/chemical/SlurryRecipeData.java | 6 +- .../impl/DataComponentDeferredRegister.java | 17 - .../registration/impl/ItemRegistryObject.java | 73 +- .../common/registries/MekanismBlocks.java | 620 ++++++++-------- .../registries/MekanismDataComponents.java | 147 ++-- .../common/registries/MekanismItems.java | 52 +- .../common/tile/base/TileEntityMekanism.java | 6 +- .../mekanism/common/util/ChemicalUtil.java | 6 +- .../java/mekanism/common/util/FluidUtils.java | 4 +- .../mekanism/common/util/StorageUtils.java | 14 +- 137 files changed, 4761 insertions(+), 1919 deletions(-) create mode 100644 src/main/java/mekanism/common/attachments/LockData.java delete mode 100644 src/main/java/mekanism/common/attachments/containers/AttachedChemicalTanks.java delete mode 100644 src/main/java/mekanism/common/attachments/containers/AttachedContainers.java delete mode 100644 src/main/java/mekanism/common/attachments/containers/AttachedEnergyContainers.java delete mode 100644 src/main/java/mekanism/common/attachments/containers/AttachedFluidTanks.java delete mode 100644 src/main/java/mekanism/common/attachments/containers/AttachedHeatCapacitors.java delete mode 100644 src/main/java/mekanism/common/attachments/containers/AttachedInventorySlots.java create mode 100644 src/main/java/mekanism/common/attachments/containers/ComponentBackedContainer.java create mode 100644 src/main/java/mekanism/common/attachments/containers/ComponentBackedHandler.java create mode 100644 src/main/java/mekanism/common/attachments/containers/ContainsRecipe.java create mode 100644 src/main/java/mekanism/common/attachments/containers/IAttachedContainers.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/ChemicalTanksBuilder.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/ComponentBackedChemicalHandler.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/ComponentBackedChemicalTank.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/gas/AttachedGases.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/gas/ComponentBackedChemicalTankGasTank.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/gas/ComponentBackedGasHandler.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/gas/ComponentBackedGasTank.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/gas/GasTanksBuilder.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/infuse/AttachedInfuseTypes.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/infuse/ComponentBackedChemicalTankInfusionTank.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/infuse/ComponentBackedInfusionHandler.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/infuse/ComponentBackedInfusionTank.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/infuse/InfusionTanksBuilder.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/merged/MergedTankCreator.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/pigment/AttachedPigments.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/pigment/ComponentBackedChemicalTankPigmentTank.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/pigment/ComponentBackedPigmentHandler.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/pigment/ComponentBackedPigmentTank.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/pigment/PigmentTanksBuilder.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/slurry/AttachedSlurries.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/slurry/ComponentBackedChemicalTankSlurryTank.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/slurry/ComponentBackedSlurryHandler.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/slurry/ComponentBackedSlurryTank.java create mode 100644 src/main/java/mekanism/common/attachments/containers/chemical/slurry/SlurryTanksBuilder.java create mode 100644 src/main/java/mekanism/common/attachments/containers/creator/BaseContainerCreator.java create mode 100644 src/main/java/mekanism/common/attachments/containers/creator/IBasicContainerCreator.java create mode 100644 src/main/java/mekanism/common/attachments/containers/creator/IContainerCreator.java create mode 100644 src/main/java/mekanism/common/attachments/containers/energy/AttachedEnergy.java create mode 100644 src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedEnergyContainer.java create mode 100644 src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedEnergyCubeContainer.java create mode 100644 src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedEnergyHandler.java create mode 100644 src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedNoClampEnergyContainer.java rename src/main/java/mekanism/common/{capabilities/energy/item/ResistiveHeaterItemEnergyContainer.java => attachments/containers/energy/ComponentBackedResistiveEnergyContainer.java} (58%) create mode 100644 src/main/java/mekanism/common/attachments/containers/energy/EnergyContainersBuilder.java create mode 100644 src/main/java/mekanism/common/attachments/containers/fluid/AttachedFluids.java create mode 100644 src/main/java/mekanism/common/attachments/containers/fluid/ComponentBackedFluidHandler.java create mode 100644 src/main/java/mekanism/common/attachments/containers/fluid/ComponentBackedFluidTank.java create mode 100644 src/main/java/mekanism/common/attachments/containers/fluid/ComponentBackedFluidTankFluidTank.java create mode 100644 src/main/java/mekanism/common/attachments/containers/fluid/FluidTanksBuilder.java create mode 100644 src/main/java/mekanism/common/attachments/containers/heat/AttachedHeat.java create mode 100644 src/main/java/mekanism/common/attachments/containers/heat/ComponentBackedHeatCapacitor.java create mode 100644 src/main/java/mekanism/common/attachments/containers/heat/ComponentBackedHeatHandler.java create mode 100644 src/main/java/mekanism/common/attachments/containers/heat/HeatCapacitorData.java create mode 100644 src/main/java/mekanism/common/attachments/containers/heat/HeatCapacitorsBuilder.java create mode 100644 src/main/java/mekanism/common/attachments/containers/item/AttachedItems.java create mode 100644 src/main/java/mekanism/common/attachments/containers/item/ComponentBackedBinInventorySlot.java create mode 100644 src/main/java/mekanism/common/attachments/containers/item/ComponentBackedInventorySlot.java create mode 100644 src/main/java/mekanism/common/attachments/containers/item/ComponentBackedItemHandler.java create mode 100644 src/main/java/mekanism/common/attachments/containers/item/ItemSlotsBuilder.java delete mode 100644 src/main/java/mekanism/common/capabilities/chemical/item/ChemicalTankRateLimitChemicalTank.java delete mode 100644 src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitChemicalTank.java delete mode 100644 src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitGasTank.java delete mode 100644 src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitInfusionTank.java delete mode 100644 src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitPigmentTank.java delete mode 100644 src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitSlurryTank.java delete mode 100644 src/main/java/mekanism/common/capabilities/energy/item/EnergyCubeRateLimitEnergyContainer.java delete mode 100644 src/main/java/mekanism/common/capabilities/energy/item/NoClampRateLimitEnergyContainer.java delete mode 100644 src/main/java/mekanism/common/capabilities/energy/item/RateLimitEnergyContainer.java delete mode 100644 src/main/java/mekanism/common/capabilities/fluid/item/FluidTankRateLimitFluidTank.java delete mode 100644 src/main/java/mekanism/common/capabilities/fluid/item/RateLimitFluidTank.java delete mode 100644 src/main/java/mekanism/common/inventory/slot/ItemSlotsBuilder.java diff --git a/src/api/java/mekanism/api/chemical/ChemicalStack.java b/src/api/java/mekanism/api/chemical/ChemicalStack.java index c92bc04fe2e..8db0f658b15 100644 --- a/src/api/java/mekanism/api/chemical/ChemicalStack.java +++ b/src/api/java/mekanism/api/chemical/ChemicalStack.java @@ -163,6 +163,16 @@ public void encode(RegistryFriendlyByteBuf buf, STACK stack) { case PIGMENT -> PigmentStack.MAP_CODEC; case SLURRY -> SlurryStack.MAP_CODEC; }); + /** + * Codec to get any kind of chemical stack, based on a "chemicalType" field. + * + * @see ChemicalType + * @see mekanism.api.chemical.merged.BoxedChemicalStack + * @since 10.6.0 + */ + //TODO - 1.20.5: Re-evaluate if we wan this defaulting to an empty gas stack or to try and get the same stack type as it was? + public static final Codec> BOXED_OPTIONAL_CODEC = ExtraCodecs.optionalEmptyMap(BOXED_CODEC).xmap(optional -> optional.orElse(GasStack.EMPTY), + stack -> stack.isEmpty() ? Optional.empty() : Optional.of(stack)); /** * StreamCodec to get any kind of chemical stack (that does not accept empty stacks), based on a "chemicalType" field. * diff --git a/src/api/java/mekanism/api/energy/IEnergyContainer.java b/src/api/java/mekanism/api/energy/IEnergyContainer.java index 770e9d518e4..d4906b2b80e 100644 --- a/src/api/java/mekanism/api/energy/IEnergyContainer.java +++ b/src/api/java/mekanism/api/energy/IEnergyContainer.java @@ -154,7 +154,7 @@ default void setEmpty() { * @return Amount of energy needed */ default FloatingLong getNeeded() { - return FloatingLong.ZERO.max(getMaxEnergy().subtract(getEnergy())); + return getMaxEnergy().subtract(getEnergy()); } @Override diff --git a/src/api/java/mekanism/api/fluid/IExtendedFluidTank.java b/src/api/java/mekanism/api/fluid/IExtendedFluidTank.java index bcd6c1e96da..530fb9a7660 100644 --- a/src/api/java/mekanism/api/fluid/IExtendedFluidTank.java +++ b/src/api/java/mekanism/api/fluid/IExtendedFluidTank.java @@ -233,6 +233,11 @@ default int getNeeded() { return Math.max(0, getCapacity() - getFluidAmount()); } + @Override + default int getFluidAmount() { + return getFluid().getAmount(); + } + @Override default CompoundTag serializeNBT(HolderLookup.Provider provider) { CompoundTag nbt = new CompoundTag(); diff --git a/src/api/java/mekanism/api/inventory/IInventorySlot.java b/src/api/java/mekanism/api/inventory/IInventorySlot.java index 1e59c9590aa..ad94d0ff754 100644 --- a/src/api/java/mekanism/api/inventory/IInventorySlot.java +++ b/src/api/java/mekanism/api/inventory/IInventorySlot.java @@ -125,7 +125,7 @@ default ItemStack extractItem(int amount, Action action, AutomationType automati ItemStack current = getStack(); //Ensure that if this slot allows going past the max stack size of an item, that when extracting we don't act as if we have more than // the max stack size, as the JavaDoc for IItemHandler requires that the returned stack is not larger than its stack size - int currentAmount = Math.min(getCount(), current.getMaxStackSize()); + int currentAmount = Math.min(current.getCount(), current.getMaxStackSize()); if (currentAmount < amount) { //If we are trying to extract more than we have, just change it so that we are extracting it all amount = currentAmount; @@ -179,7 +179,9 @@ default ItemStack extractItem(int amount, Action action, AutomationType automati * @return A slot for use in a container that represents this {@link IInventorySlot}, or null if this slot should not be added. */ @Nullable - Slot createContainerSlot(); + default Slot createContainerSlot() { + return null; + } /** * Convenience method for modifying the size of the stored stack. @@ -312,6 +314,7 @@ default CompoundTag serializeNBT(HolderLookup.Provider provider) { * @since 10.5.0 */ default boolean isCompatible(IInventorySlot other) { + //TODO - 1.20.5: Remove this? I don't think it is necessary anymore return getClass() == other.getClass() && ItemStack.matches(getStack(), other.getStack()); } } \ No newline at end of file diff --git a/src/datagen/main/java/mekanism/common/loot/table/BaseBlockLootTables.java b/src/datagen/main/java/mekanism/common/loot/table/BaseBlockLootTables.java index 7c84ad0d2c6..490e4edbf55 100644 --- a/src/datagen/main/java/mekanism/common/loot/table/BaseBlockLootTables.java +++ b/src/datagen/main/java/mekanism/common/loot/table/BaseBlockLootTables.java @@ -11,7 +11,6 @@ import mekanism.api.annotations.NothingNullByDefault; import mekanism.api.providers.IBlockProvider; import mekanism.common.Mekanism; -import mekanism.common.attachments.containers.AttachedContainers; import mekanism.common.attachments.containers.ContainerType; import mekanism.common.block.BlockRadioactiveWasteBarrel; import mekanism.common.block.attribute.Attribute; @@ -183,10 +182,9 @@ protected void dropSelfWithContents(Collection> blockPro boolean hasContainers = false; CopyContainersLootFunction.Builder containerBuilder = CopyContainersLootFunction.builder(); for (ContainerType type : ContainerType.TYPES) { - AttachedContainers attachment = type.getAttachment(stack); List containers = tileEntity.persists(type) ? type.getContainers(tileEntity) : Collections.emptyList(); - List attachmentContainers = attachment == null ? Collections.emptyList() : attachment.getContainers(); - if (containers.size() == attachmentContainers.size()) { + int attachmentContainers = type.getContainerCount(stack); + if (containers.size() == attachmentContainers) { if (!containers.isEmpty()) { containerBuilder.copy(type); hasContainers = true; @@ -194,18 +192,18 @@ protected void dropSelfWithContents(Collection> blockPro hasContents = true; } } - } else if (attachmentContainers.isEmpty()) { + } else if (attachmentContainers == 0) { //TODO: Improve how we handle skipping warnings for known missing types if (type == ContainerType.ITEM && block.asItem() instanceof ItemBlockPersonalStorage) { //We don't want explosions causing personal storage items to be directly destroyed. It is also known that the attachment is missing hasContents = true; } else if (type != ContainerType.GAS || !(block instanceof BlockRadioactiveWasteBarrel)) { - Mekanism.logger.warn("Container type: {}, item missing attachments: {}", type.getAttachmentName(), RegistryUtils.getName(block)); + Mekanism.logger.warn("Container type: {}, item missing attachments: {}", type.getComponentName(), RegistryUtils.getName(block)); } } else if (containers.isEmpty()) { - Mekanism.logger.warn("Container type: {}, item has attachments but block doesn't have containers: {}", type.getAttachmentName(), RegistryUtils.getName(block)); + Mekanism.logger.warn("Container type: {}, item has attachments but block doesn't have containers: {}", type.getComponentName(), RegistryUtils.getName(block)); } else { - Mekanism.logger.warn("Container type: {}, has {} item attachments and block has {} containers: {}", type.getAttachmentName(), attachmentContainers.size(), + Mekanism.logger.warn("Container type: {}, has {} item attachments and block has {} containers: {}", type.getComponentName(), attachmentContainers, containers.size(), RegistryUtils.getName(block)); } } diff --git a/src/generators/java/mekanism/generators/common/registries/GeneratorsBlocks.java b/src/generators/java/mekanism/generators/common/registries/GeneratorsBlocks.java index ecbda366124..6d1302cc37d 100644 --- a/src/generators/java/mekanism/generators/common/registries/GeneratorsBlocks.java +++ b/src/generators/java/mekanism/generators/common/registries/GeneratorsBlocks.java @@ -1,21 +1,18 @@ package mekanism.generators.common.registries; import java.util.function.Supplier; -import mekanism.api.chemical.ChemicalTankBuilder; import mekanism.api.chemical.gas.attribute.GasAttributes.Fuel; import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.chemical.gas.GasTanksBuilder; +import mekanism.common.attachments.containers.fluid.FluidTanksBuilder; +import mekanism.common.attachments.containers.heat.HeatCapacitorsBuilder; +import mekanism.common.attachments.containers.item.ItemSlotsBuilder; import mekanism.common.block.basic.BlockStructuralGlass; import mekanism.common.block.interfaces.IHasDescription; import mekanism.common.block.prefab.BlockBasicMultiblock; import mekanism.common.block.prefab.BlockTile; import mekanism.common.block.prefab.BlockTile.BlockTileModel; -import mekanism.common.capabilities.chemical.variable.RateLimitGasTank; -import mekanism.common.capabilities.fluid.BasicFluidTank; -import mekanism.common.capabilities.fluid.item.RateLimitFluidTank; -import mekanism.common.capabilities.heat.BasicHeatCapacitor; import mekanism.common.content.blocktype.BlockTypeTile; -import mekanism.common.inventory.slot.ItemSlotsBuilder; -import mekanism.common.inventory.slot.chemical.GasInventorySlot; import mekanism.common.item.block.ItemBlockTooltip; import mekanism.common.registration.impl.BlockDeferredRegister; import mekanism.common.registration.impl.BlockRegistryObject; @@ -30,7 +27,6 @@ import mekanism.generators.common.item.ItemBlockFissionLogicAdapter; import mekanism.generators.common.item.ItemBlockFusionLogicAdapter; import mekanism.generators.common.item.generator.ItemBlockWindGenerator; -import mekanism.generators.common.slot.FluidFuelInventorySlot; import mekanism.generators.common.tile.TileEntityAdvancedSolarGenerator; import mekanism.generators.common.tile.TileEntityBioGenerator; import mekanism.generators.common.tile.TileEntityGasGenerator; @@ -55,9 +51,7 @@ import mekanism.generators.common.tile.turbine.TileEntityTurbineVent; import net.minecraft.tags.FluidTags; import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.material.Fluids; import net.minecraft.world.level.material.MapColor; -import net.neoforged.neoforge.fluids.FluidStack; public class GeneratorsBlocks { @@ -69,31 +63,29 @@ private GeneratorsBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> HEAT_GENERATOR = BLOCKS.register("heat_generator", () -> new BlockTileModel<>(GeneratorsBlockTypes.HEAT_GENERATOR, properties -> properties.mapColor(MapColor.METAL)), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.FLUID, stack -> RateLimitFluidTank.createBasicItem(MekanismGeneratorsConfig.generators.heatTankCapacity, - BasicFluidTank.manualOnly, BasicFluidTank.alwaysTrueBi, - fluidStack -> fluidStack.is(FluidTags.LAVA) - )).addAttachmentOnlyContainer(ContainerType.HEAT, stack -> BasicHeatCapacitor.createBasicItem(TileEntityHeatGenerator.HEAT_CAPACITY, - TileEntityHeatGenerator.INVERSE_CONDUCTION_COEFFICIENT, TileEntityHeatGenerator.INVERSE_INSULATION_COEFFICIENT - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addFluidSlot(0, (tank, listener, x, y) -> FluidFuelInventorySlot.forFuel(tank, - s -> s.getBurnTime(null) / 20, - size -> new FluidStack(Fluids.LAVA, size), - listener, x, y) - ).addEnergy() + .addAttachmentOnlyContainers(ContainerType.FLUID, () -> FluidTanksBuilder.builder() + .addBasic(MekanismGeneratorsConfig.generators.heatTankCapacity, fluid -> fluid.is(FluidTags.LAVA)) + .build() + ).addAttachmentOnlyContainers(ContainerType.HEAT, () -> HeatCapacitorsBuilder.builder() + .addBasic(TileEntityHeatGenerator.HEAT_CAPACITY, TileEntityHeatGenerator.INVERSE_CONDUCTION_COEFFICIENT, TileEntityHeatGenerator.INVERSE_INSULATION_COEFFICIENT) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addFluidFuelSlot(0, s -> s.getBurnTime(null) > 0) + .addEnergy() .build() ) ); public static final BlockRegistryObject>, ItemBlockTooltip>>> SOLAR_GENERATOR = BLOCKS.register("solar_generator", () -> new BlockTileModel<>(GeneratorsBlockTypes.SOLAR_GENERATOR, properties -> properties.mapColor(MapColor.COLOR_BLUE)), ItemBlockTooltip::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack).addEnergy().build())); + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder().addEnergy().build())); public static final BlockRegistryObject>, ItemBlockTooltip>>> GAS_BURNING_GENERATOR = BLOCKS.register("gas_burning_generator", () -> new BlockTileModel<>(GeneratorsBlockTypes.GAS_BURNING_GENERATOR, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.GAS, stack -> RateLimitGasTank.createBasicItem(MekanismGeneratorsConfig.generators.gbgTankCapacity, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, - gas -> gas.has(Fuel.class) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addGasSlot(0, GasInventorySlot::fill) + .addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addBasic(MekanismGeneratorsConfig.generators.gbgTankCapacity, gas -> gas.has(Fuel.class)) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addGasFillSlot(0) .addEnergy() .build() ) @@ -101,22 +93,20 @@ private GeneratorsBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> BIO_GENERATOR = BLOCKS.register("bio_generator", () -> new BlockTileModel<>(GeneratorsBlockTypes.BIO_GENERATOR, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.FLUID, stack -> RateLimitFluidTank.createBasicItem(MekanismGeneratorsConfig.generators.bioTankCapacity, - BasicFluidTank.manualOnly, BasicFluidTank.alwaysTrueBi, - fluidStack -> fluidStack.is(GeneratorTags.Fluids.BIOETHANOL) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addFluidSlot(0, (tank, listener, x, y) -> FluidFuelInventorySlot.forFuel(tank, s -> s.is(MekanismTags.Items.FUELS_BIO) ? 200 : s.is(MekanismTags.Items.FUELS_BLOCK_BIO) ? 200 * 9 : 0, - GeneratorsFluids.BIOETHANOL::getFluidStack, - listener, x, y) - ).addEnergy() + .addAttachmentOnlyContainers(ContainerType.FLUID, () -> FluidTanksBuilder.builder() + .addBasic(MekanismGeneratorsConfig.generators.bioTankCapacity, fluid -> fluid.is(GeneratorTags.Fluids.BIOETHANOL)) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addFluidFuelSlot(0, s -> s.is(MekanismTags.Items.FUELS_BIO) || s.is(MekanismTags.Items.FUELS_BLOCK_BIO)) + .addEnergy() .build() ) ); public static final BlockRegistryObject>, ItemBlockTooltip>>> ADVANCED_SOLAR_GENERATOR = BLOCKS.register("advanced_solar_generator", () -> new BlockTileModel<>(GeneratorsBlockTypes.ADVANCED_SOLAR_GENERATOR, properties -> properties.mapColor(MapColor.COLOR_BLUE)), ItemBlockTooltip::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack).addEnergy().build())); + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder().addEnergy().build())); public static final BlockRegistryObject>, ItemBlockWindGenerator> WIND_GENERATOR = BLOCKS.register("wind_generator", () -> new BlockTileModel<>(GeneratorsBlockTypes.WIND_GENERATOR, properties -> properties.mapColor(MapColor.METAL)), ItemBlockWindGenerator::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack).addEnergy().build())); + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder().addEnergy().build())); public static final BlockRegistryObject> TURBINE_ROTOR = registerTooltipBlock("turbine_rotor", BlockTurbineRotor::new); public static final BlockRegistryObject>, ItemBlockTooltip>>> ROTATIONAL_COMPLEX = registerTooltipBlock("rotational_complex", () -> new BlockTile<>(GeneratorsBlockTypes.ROTATIONAL_COMPLEX, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor()))); diff --git a/src/generators/java/mekanism/generators/common/registries/GeneratorsItems.java b/src/generators/java/mekanism/generators/common/registries/GeneratorsItems.java index e2ed55ad4b3..8ab52cb5a92 100644 --- a/src/generators/java/mekanism/generators/common/registries/GeneratorsItems.java +++ b/src/generators/java/mekanism/generators/common/registries/GeneratorsItems.java @@ -1,7 +1,7 @@ package mekanism.generators.common.registries; import mekanism.common.attachments.containers.ContainerType; -import mekanism.common.capabilities.chemical.variable.RateLimitGasTank; +import mekanism.common.attachments.containers.chemical.gas.GasTanksBuilder; import mekanism.common.item.ItemModule; import mekanism.common.registration.impl.ItemDeferredRegister; import mekanism.common.registration.impl.ItemRegistryObject; @@ -22,11 +22,10 @@ private GeneratorsItems() { public static final ItemRegistryObject SOLAR_PANEL = ITEMS.register("solar_panel"); public static final ItemRegistryObject HOHLRAUM = ITEMS.registerItem("hohlraum", ItemHohlraum::new) - .addAttachedContainerCapability(ContainerType.GAS, stack -> RateLimitGasTank.createInternalStorage( - MekanismGeneratorsConfig.generators.hohlraumFillRate, - MekanismGeneratorsConfig.generators.hohlraumMaxGas, - gas -> gas.is(GeneratorTags.Gases.FUSION_FUEL) - ), MekanismGeneratorsConfig.generators); + .addAttachedContainerCapabilities(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addInternalStorage(MekanismGeneratorsConfig.generators.hohlraumFillRate, MekanismGeneratorsConfig.generators.hohlraumMaxGas, + gas -> gas.is(GeneratorTags.Gases.FUSION_FUEL) + ).build(), MekanismGeneratorsConfig.generators); public static final ItemRegistryObject TURBINE_BLADE = ITEMS.registerItem("turbine_blade", ItemTurbineBlade::new); public static final ItemRegistryObject MODULE_SOLAR_RECHARGING = ITEMS.registerModule(GeneratorsModules.SOLAR_RECHARGING_UNIT, Rarity.RARE); diff --git a/src/generators/java/mekanism/generators/common/slot/FluidFuelInventorySlot.java b/src/generators/java/mekanism/generators/common/slot/FluidFuelInventorySlot.java index df40cbafa95..8149bdf0abf 100644 --- a/src/generators/java/mekanism/generators/common/slot/FluidFuelInventorySlot.java +++ b/src/generators/java/mekanism/generators/common/slot/FluidFuelInventorySlot.java @@ -44,7 +44,7 @@ public static FluidFuelInventorySlot forFuel(IExtendedFluidTank fluidTank, ToInt //Always allow extraction if something went horribly wrong, and we are not a fluid item AND we can't provide a valid type of chemical // This might happen after a reload for example return fuelValue.applyAsInt(stack) == 0; - }, fillPredicate.or(stack -> fuelValue.applyAsInt(stack) > 0), listener, x, y); + }, stack -> fuelValue.applyAsInt(stack) > 0 || fillPredicate.test(stack), listener, x, y); } private final IntFunction<@NotNull FluidStack> fuelCreator; diff --git a/src/main/java/mekanism/client/recipe_viewer/QIOCraftingTransferHandler.java b/src/main/java/mekanism/client/recipe_viewer/QIOCraftingTransferHandler.java index e013b3d5ab7..44368dae500 100644 --- a/src/main/java/mekanism/client/recipe_viewer/QIOCraftingTransferHandler.java +++ b/src/main/java/mekanism/client/recipe_viewer/QIOCraftingTransferHandler.java @@ -33,7 +33,6 @@ import mekanism.common.inventory.container.QIOItemViewerContainer; import mekanism.common.inventory.container.slot.HotBarSlot; import mekanism.common.inventory.container.slot.MainInventorySlot; -import mekanism.common.inventory.slot.CraftingWindowInventorySlot; import mekanism.common.lib.inventory.HashedItem; import mekanism.common.network.PacketUtils; import mekanism.common.network.to_server.qio.PacketQIOFillCraftingWindow; @@ -106,7 +105,7 @@ public static RESULT transferReci if (action.simulate()) { CraftingContainer dummy = MekanismUtils.getDummyCraftingInv(); for (int slot = 0; slot < 9; slot++) { - CraftingWindowInventorySlot inputSlot = craftingWindow.getInputSlot(slot); + IInventorySlot inputSlot = craftingWindow.getInputSlot(slot); if (!inputSlot.isEmpty()) { //Copy it in case any recipe does weird things and tries to mutate the stack dummy.setItem(slot, inputSlot.getStack().copyWithCount(1)); diff --git a/src/main/java/mekanism/client/recipe_viewer/emi/MekanismEmi.java b/src/main/java/mekanism/client/recipe_viewer/emi/MekanismEmi.java index 62bb0bf6251..e78b50f3e2a 100644 --- a/src/main/java/mekanism/client/recipe_viewer/emi/MekanismEmi.java +++ b/src/main/java/mekanism/client/recipe_viewer/emi/MekanismEmi.java @@ -124,9 +124,9 @@ public class MekanismEmi implements EmiPlugin { return null; }); - private static void addChemicalComponent(Set representation, ItemStack stack, ContainerType, ?> containerType, + private static void addChemicalComponent(Set representation, ItemStack stack, ContainerType> containerType, ItemCapability, Void> capability) { - IChemicalHandler handler = containerType.getAttachmentIfPresent(stack); + IChemicalHandler handler = containerType.createHandlerIfData(stack); if (handler == null) { handler = stack.getCapability(capability); } @@ -148,7 +148,7 @@ private static void addChemicalComponent(Set representation, ItemStack s } private static void addFluidComponent(Set representation, ItemStack stack) { - IFluidHandler handler = ContainerType.FLUID.getAttachmentIfPresent(stack); + IFluidHandler handler = ContainerType.FLUID.createHandlerIfData(stack); if (handler == null) { handler = Capabilities.FLUID.getCapability(stack); } @@ -172,7 +172,7 @@ private static void addFluidComponent(Set representation, ItemStack stac } private static void addEnergyComponent(Set representation, ItemStack stack) { - IStrictEnergyHandler energyHandlerItem = ContainerType.ENERGY.getAttachmentIfPresent(stack); + IStrictEnergyHandler energyHandlerItem = ContainerType.ENERGY.createHandlerIfData(stack); if (energyHandlerItem == null) { energyHandlerItem = Capabilities.STRICT_ENERGY.getCapability(stack); } diff --git a/src/main/java/mekanism/client/recipe_viewer/jei/MekanismJEI.java b/src/main/java/mekanism/client/recipe_viewer/jei/MekanismJEI.java index 71065500da2..dd2bb4215ca 100644 --- a/src/main/java/mekanism/client/recipe_viewer/jei/MekanismJEI.java +++ b/src/main/java/mekanism/client/recipe_viewer/jei/MekanismJEI.java @@ -169,9 +169,9 @@ private static String addInterpretation(String nbtRepresentation, String compone return nbtRepresentation.isEmpty() ? component : nbtRepresentation + ":" + component; } - private static String getChemicalComponent(ItemStack stack, ContainerType, ?> containerType, + private static String getChemicalComponent(ItemStack stack, ContainerType> containerType, ItemCapability, Void> capability) { - IChemicalHandler handler = containerType.getAttachmentIfPresent(stack); + IChemicalHandler handler = containerType.createHandlerIfData(stack); if (handler == null) { handler = stack.getCapability(capability); } @@ -191,7 +191,7 @@ private static String getChemicalComponent(ItemStack stack, ContainerType CODEC = RecordCodecBuilder.create(instance -> instance.group( + ItemStack.CODEC.fieldOf(JsonConstants.OUTPUT).forGetter(LockData::lock) + ).apply(instance, LockData::new)); + public static final StreamCodec STREAM_CODEC = ItemStack.STREAM_CODEC.map(LockData::new, LockData::lock); + + public LockData { + lock = lock.copy(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o == null || getClass() != o.getClass()) { + return false; + } + return ItemStack.matches(lock, ((LockData) o).lock); + } + + @Override + public int hashCode() { + return ItemStack.hashItemAndComponents(lock); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/AttachedChemicalTanks.java b/src/main/java/mekanism/common/attachments/containers/AttachedChemicalTanks.java deleted file mode 100644 index b3dee4aadbf..00000000000 --- a/src/main/java/mekanism/common/attachments/containers/AttachedChemicalTanks.java +++ /dev/null @@ -1,89 +0,0 @@ -package mekanism.common.attachments.containers; - -import java.util.List; -import mekanism.api.IContentsListener; -import mekanism.api.annotations.NothingNullByDefault; -import mekanism.api.chemical.Chemical; -import mekanism.api.chemical.ChemicalStack; -import mekanism.api.chemical.IChemicalTank; -import mekanism.api.chemical.IMekanismChemicalHandler; -import mekanism.api.chemical.gas.Gas; -import mekanism.api.chemical.gas.GasStack; -import mekanism.api.chemical.gas.IGasHandler.IMekanismGasHandler; -import mekanism.api.chemical.gas.IGasTank; -import mekanism.api.chemical.infuse.IInfusionHandler.IMekanismInfusionHandler; -import mekanism.api.chemical.infuse.IInfusionTank; -import mekanism.api.chemical.infuse.InfuseType; -import mekanism.api.chemical.infuse.InfusionStack; -import mekanism.api.chemical.pigment.IPigmentHandler.IMekanismPigmentHandler; -import mekanism.api.chemical.pigment.IPigmentTank; -import mekanism.api.chemical.pigment.Pigment; -import mekanism.api.chemical.pigment.PigmentStack; -import mekanism.api.chemical.slurry.ISlurryHandler.IMekanismSlurryHandler; -import mekanism.api.chemical.slurry.ISlurryTank; -import mekanism.api.chemical.slurry.Slurry; -import mekanism.api.chemical.slurry.SlurryStack; -import net.minecraft.core.Direction; -import org.jetbrains.annotations.Nullable; - -@NothingNullByDefault -public abstract class AttachedChemicalTanks, STACK extends ChemicalStack, TANK extends IChemicalTank> - extends AttachedContainers implements IMekanismChemicalHandler { - - AttachedChemicalTanks(List tanks, @Nullable IContentsListener listener) { - super(tanks, listener); - } - - @Override - public List getChemicalTanks(@Nullable Direction side) { - return containers; - } - - public static class AttachedGasTanks extends AttachedChemicalTanks implements IMekanismGasHandler { - - AttachedGasTanks(List tanks, @Nullable IContentsListener listener) { - super(tanks, listener); - } - - @Override - protected ContainerType getContainerType() { - return ContainerType.GAS; - } - } - - public static class AttachedInfusionTanks extends AttachedChemicalTanks implements IMekanismInfusionHandler { - - AttachedInfusionTanks(List tanks, @Nullable IContentsListener listener) { - super(tanks, listener); - } - - @Override - protected ContainerType getContainerType() { - return ContainerType.INFUSION; - } - } - - public static class AttachedPigmentTanks extends AttachedChemicalTanks implements IMekanismPigmentHandler { - - AttachedPigmentTanks(List tanks, @Nullable IContentsListener listener) { - super(tanks, listener); - } - - @Override - protected ContainerType getContainerType() { - return ContainerType.PIGMENT; - } - } - - public static class AttachedSlurryTanks extends AttachedChemicalTanks implements IMekanismSlurryHandler { - - AttachedSlurryTanks(List tanks, @Nullable IContentsListener listener) { - super(tanks, listener); - } - - @Override - protected ContainerType getContainerType() { - return ContainerType.SLURRY; - } - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/AttachedContainers.java b/src/main/java/mekanism/common/attachments/containers/AttachedContainers.java deleted file mode 100644 index 82801691030..00000000000 --- a/src/main/java/mekanism/common/attachments/containers/AttachedContainers.java +++ /dev/null @@ -1,49 +0,0 @@ -package mekanism.common.attachments.containers; - -import java.util.List; -import mekanism.api.IContentsListener; -import mekanism.api.annotations.NothingNullByDefault; -import net.minecraft.core.HolderLookup; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.neoforged.neoforge.common.util.INBTSerializable; -import org.jetbrains.annotations.Nullable; - -@NothingNullByDefault -public abstract class AttachedContainers> implements INBTSerializable, IContentsListener { - - @Nullable - private final IContentsListener listener; - protected final List containers; - - AttachedContainers(List containers, @Nullable IContentsListener listener) { - //Ensure that the list the attachment receives is immutable. In general, we will already have an immutable type and this will not cause any copying to occur - this.containers = List.copyOf(containers); - this.listener = listener; - } - - protected abstract ContainerType getContainerType(); - - @Nullable - @Override - public ListTag serializeNBT(HolderLookup.Provider provider) { - ListTag serialized = getContainerType().save(provider, this.containers); - return serialized.isEmpty() ? null : serialized; - } - - @Override - public void deserializeNBT(HolderLookup.Provider provider, ListTag nbt) { - getContainerType().read(provider, this.containers, nbt); - } - - @Override - public void onContentsChanged() { - if (this.listener != null) { - this.listener.onContentsChanged(); - } - } - - public List getContainers() { - return containers; - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/AttachedEnergyContainers.java b/src/main/java/mekanism/common/attachments/containers/AttachedEnergyContainers.java deleted file mode 100644 index 489f1e43ff4..00000000000 --- a/src/main/java/mekanism/common/attachments/containers/AttachedEnergyContainers.java +++ /dev/null @@ -1,27 +0,0 @@ -package mekanism.common.attachments.containers; - -import java.util.List; -import mekanism.api.IContentsListener; -import mekanism.api.annotations.NothingNullByDefault; -import mekanism.api.energy.IEnergyContainer; -import mekanism.api.energy.IMekanismStrictEnergyHandler; -import net.minecraft.core.Direction; -import org.jetbrains.annotations.Nullable; - -@NothingNullByDefault -public class AttachedEnergyContainers extends AttachedContainers implements IMekanismStrictEnergyHandler { - - AttachedEnergyContainers(List containers, @Nullable IContentsListener listener) { - super(containers, listener); - } - - @Override - protected ContainerType getContainerType() { - return ContainerType.ENERGY; - } - - @Override - public List getEnergyContainers(@Nullable Direction side) { - return containers; - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/AttachedFluidTanks.java b/src/main/java/mekanism/common/attachments/containers/AttachedFluidTanks.java deleted file mode 100644 index 1ed4e6efc8f..00000000000 --- a/src/main/java/mekanism/common/attachments/containers/AttachedFluidTanks.java +++ /dev/null @@ -1,45 +0,0 @@ -package mekanism.common.attachments.containers; - -import java.util.List; -import mekanism.api.IContentsListener; -import mekanism.api.annotations.NothingNullByDefault; -import mekanism.api.fluid.IExtendedFluidTank; -import mekanism.api.fluid.IMekanismFluidHandler; -import net.minecraft.core.Direction; -import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.fluids.capability.IFluidHandlerItem; -import org.jetbrains.annotations.Nullable; - -@NothingNullByDefault -public class AttachedFluidTanks extends AttachedContainers implements IMekanismFluidHandler { - - AttachedFluidTanks(List tanks, @Nullable IContentsListener listener) { - super(tanks, listener); - } - - @Override - protected ContainerType getContainerType() { - return ContainerType.FLUID; - } - - @Override - public List getFluidTanks(@Nullable Direction side) { - return containers; - } - - public static class AttachedItemFluidTanks extends AttachedFluidTanks implements IFluidHandlerItem { - - private final ItemStack stack; - - AttachedItemFluidTanks(ItemStack stack, List tanks) { - //Attachments on ItemStacks auto save, so we don't need to bother taking a listener as a parameter - super(tanks, null); - this.stack = stack; - } - - @Override - public ItemStack getContainer() { - return stack; - } - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/AttachedHeatCapacitors.java b/src/main/java/mekanism/common/attachments/containers/AttachedHeatCapacitors.java deleted file mode 100644 index 39b0d8569b3..00000000000 --- a/src/main/java/mekanism/common/attachments/containers/AttachedHeatCapacitors.java +++ /dev/null @@ -1,27 +0,0 @@ -package mekanism.common.attachments.containers; - -import java.util.List; -import mekanism.api.IContentsListener; -import mekanism.api.annotations.NothingNullByDefault; -import mekanism.api.heat.IHeatCapacitor; -import mekanism.api.heat.IMekanismHeatHandler; -import net.minecraft.core.Direction; -import org.jetbrains.annotations.Nullable; - -@NothingNullByDefault -public class AttachedHeatCapacitors extends AttachedContainers implements IMekanismHeatHandler { - - AttachedHeatCapacitors(List capacitors, @Nullable IContentsListener listener) { - super(capacitors, listener); - } - - @Override - protected ContainerType getContainerType() { - return ContainerType.HEAT; - } - - @Override - public List getHeatCapacitors(@Nullable Direction side) { - return containers; - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/AttachedInventorySlots.java b/src/main/java/mekanism/common/attachments/containers/AttachedInventorySlots.java deleted file mode 100644 index a5bfc5e555f..00000000000 --- a/src/main/java/mekanism/common/attachments/containers/AttachedInventorySlots.java +++ /dev/null @@ -1,27 +0,0 @@ -package mekanism.common.attachments.containers; - -import java.util.List; -import mekanism.api.IContentsListener; -import mekanism.api.annotations.NothingNullByDefault; -import mekanism.api.inventory.IInventorySlot; -import mekanism.api.inventory.IMekanismInventory; -import net.minecraft.core.Direction; -import org.jetbrains.annotations.Nullable; - -@NothingNullByDefault -public class AttachedInventorySlots extends AttachedContainers implements IMekanismInventory { - - AttachedInventorySlots(List slots, @Nullable IContentsListener listener) { - super(slots, listener); - } - - @Override - protected ContainerType getContainerType() { - return ContainerType.ITEM; - } - - @Override - public List getInventorySlots(@Nullable Direction side) { - return containers; - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/ComponentBackedContainer.java b/src/main/java/mekanism/common/attachments/containers/ComponentBackedContainer.java new file mode 100644 index 00000000000..c7dd0ae12a4 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/ComponentBackedContainer.java @@ -0,0 +1,59 @@ +package mekanism.common.attachments.containers; + +import java.util.function.Supplier; +import mekanism.api.IContentsListener; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +public abstract class ComponentBackedContainer> implements IContentsListener { + + protected final ItemStack attachedTo; + protected final int containerIndex; + + protected ComponentBackedContainer(ItemStack attachedTo, int containerIndex) { + this.attachedTo = attachedTo; + this.containerIndex = containerIndex; + } + + protected abstract TYPE copy(TYPE toCopy); + + protected abstract boolean isEmpty(TYPE value); + + protected abstract Supplier> dataComponentType(); + + @Nullable + protected ATTACHED getAttached() { + return attachedTo.get(dataComponentType()); + } + + protected TYPE getContents(ATTACHED attached) { + return attached.get(containerIndex); + } + + protected void setContents(TYPE value) { + //TODO - 1.20.5: Comment about why we have the overload that accepts attachedItems + ATTACHED attached = getAttached(); + if (attached != null) { + setContents(attached, value); + } + //TODO - 1.20.5: Else initialize to whatever the default size is meant to be? + // I think we might always end up actually doing so when accessing this from the container type? + // Though maybe not if it isn't going through capability systems + } + + protected void setContents(ATTACHED attached, TYPE value) { + //If both stacks are empty we don't do anything + if (!isEmpty(value) || !isEmpty(getContents(attached))) { + //TODO - 1.20.5: Do we want to do a matches check instead of just seeing if both are empty + // Or maybe only do that in the non overloaded setStack so as a way to potentially avoid the extra lookup here when we know + // we only call this method if something has changed + attachedTo.set(dataComponentType(), attached.with(containerIndex, copy(value))); + onContentsChanged(); + } + } + + @Override + public void onContentsChanged() { + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/ComponentBackedHandler.java b/src/main/java/mekanism/common/attachments/containers/ComponentBackedHandler.java new file mode 100644 index 00000000000..188c228490c --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/ComponentBackedHandler.java @@ -0,0 +1,75 @@ +package mekanism.common.attachments.containers; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import mekanism.api.IContentsListener; +import mekanism.api.annotations.NothingNullByDefault; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.common.util.INBTSerializable; +import org.jetbrains.annotations.Nullable; + +@NothingNullByDefault +public abstract class ComponentBackedHandler, ATTACHED extends IAttachedContainers> + implements IContentsListener { + + private final List containers; + protected final ItemStack attachedTo; + private int numNotInitialized; + + //TODO - 1.20.5: Do we want to validate slot indices are within range? + protected ComponentBackedHandler(ItemStack attachedTo) { + this.attachedTo = attachedTo; + ATTACHED attached = getAttached(); + if (attached == null || attached.isEmpty()) { + //TODO - 1.20.5: Is this meant to be zero if there is no attachment, or is it meant to be what the default is? + numNotInitialized = 0; + containers = Collections.emptyList(); + } else { + numNotInitialized = attached.size(); + //Note: Use an Arrays#asList to allow for null elements and force it to be the size we want it to be + containers = Arrays.asList((CONTAINER[]) new INBTSerializable[numNotInitialized]); + } + } + + protected abstract ContainerType containerType(); + + @Nullable + protected ATTACHED getAttached() { + return attachedTo.get(containerType().getComponentType()); + } + + public List getContainers() { + //Ensure all our containers are initialized. This short circuits if they are, and if they aren't it initializes any ones that haven't been initialized yet + for (int i = 0, size = containers.size(); numNotInitialized > 0 && i < size; i++) { + if (containers.get(i) == null) { + initializeContainer(i); + } + } + return containers; + } + + private CONTAINER initializeContainer(int index) { + //TODO - 1.20.5: ?? + CONTAINER container = containerType().createContainer(attachedTo, index); + containers.set(index, container); + numNotInitialized--; + return container; + } + + protected CONTAINER getContainer(int index) { + CONTAINER container = containers.get(index); + //Lazily initialize the containers + return container == null ? initializeContainer(index) : container; + } + + protected int containerCount() { + ATTACHED attached = getAttached(); + return attached == null ? 0 : attached.size(); + } + + @Override + public void onContentsChanged() { + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/ContainerType.java b/src/main/java/mekanism/common/attachments/containers/ContainerType.java index 9b1244a8fac..a8425f5cc16 100644 --- a/src/main/java/mekanism/common/attachments/containers/ContainerType.java +++ b/src/main/java/mekanism/common/attachments/containers/ContainerType.java @@ -10,147 +10,153 @@ import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.Supplier; import mekanism.api.DataHandlerUtils; -import mekanism.api.IContentsListener; import mekanism.api.NBTConstants; import mekanism.api.annotations.NothingNullByDefault; -import mekanism.api.chemical.gas.IGasHandler; import mekanism.api.chemical.gas.IGasTank; -import mekanism.api.chemical.infuse.IInfusionHandler; import mekanism.api.chemical.infuse.IInfusionTank; -import mekanism.api.chemical.pigment.IPigmentHandler; import mekanism.api.chemical.pigment.IPigmentTank; -import mekanism.api.chemical.slurry.ISlurryHandler; import mekanism.api.chemical.slurry.ISlurryTank; import mekanism.api.energy.IEnergyContainer; import mekanism.api.energy.IStrictEnergyHandler; import mekanism.api.fluid.IExtendedFluidTank; import mekanism.api.heat.IHeatCapacitor; -import mekanism.api.heat.IHeatHandler; import mekanism.api.inventory.IInventorySlot; -import mekanism.common.Mekanism; -import mekanism.common.attachments.containers.AttachedChemicalTanks.AttachedGasTanks; -import mekanism.common.attachments.containers.AttachedChemicalTanks.AttachedInfusionTanks; -import mekanism.common.attachments.containers.AttachedChemicalTanks.AttachedPigmentTanks; -import mekanism.common.attachments.containers.AttachedChemicalTanks.AttachedSlurryTanks; -import mekanism.common.attachments.containers.AttachedFluidTanks.AttachedItemFluidTanks; +import mekanism.common.attachments.containers.chemical.gas.AttachedGases; +import mekanism.common.attachments.containers.chemical.gas.ComponentBackedGasHandler; +import mekanism.common.attachments.containers.chemical.infuse.AttachedInfuseTypes; +import mekanism.common.attachments.containers.chemical.infuse.ComponentBackedInfusionHandler; +import mekanism.common.attachments.containers.chemical.pigment.AttachedPigments; +import mekanism.common.attachments.containers.chemical.pigment.ComponentBackedPigmentHandler; +import mekanism.common.attachments.containers.chemical.slurry.AttachedSlurries; +import mekanism.common.attachments.containers.chemical.slurry.ComponentBackedSlurryHandler; +import mekanism.common.attachments.containers.creator.IContainerCreator; +import mekanism.common.attachments.containers.energy.AttachedEnergy; +import mekanism.common.attachments.containers.energy.ComponentBackedEnergyHandler; +import mekanism.common.attachments.containers.fluid.AttachedFluids; +import mekanism.common.attachments.containers.fluid.ComponentBackedFluidHandler; +import mekanism.common.attachments.containers.heat.AttachedHeat; +import mekanism.common.attachments.containers.heat.ComponentBackedHeatHandler; +import mekanism.common.attachments.containers.item.AttachedItems; +import mekanism.common.attachments.containers.item.ComponentBackedItemHandler; import mekanism.common.capabilities.Capabilities; import mekanism.common.capabilities.IMultiTypeCapability; import mekanism.common.config.IMekanismConfig; import mekanism.common.integration.energy.EnergyCompatUtils; import mekanism.common.registries.MekanismDataComponents; import mekanism.common.tile.base.TileEntityMekanism; -import mekanism.common.util.RegistryUtils; import net.minecraft.core.Direction; import net.minecraft.core.HolderLookup; +import net.minecraft.core.component.DataComponentMap; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; import net.neoforged.bus.api.IEventBus; import net.neoforged.neoforge.capabilities.ICapabilityProvider; +import net.neoforged.neoforge.capabilities.ItemCapability; import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; import net.neoforged.neoforge.common.util.INBTSerializable; -import net.neoforged.neoforge.fluids.capability.IFluidHandler; -import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.common.util.Lazy; import net.neoforged.neoforge.registries.DeferredHolder; import org.jetbrains.annotations.Nullable; @NothingNullByDefault -public class ContainerType, ATTACHMENT extends AttachedContainers, HANDLER> { +public class ContainerType, ATTACHED extends IAttachedContainers, + HANDLER extends ComponentBackedHandler> { private static final List> TYPES_INTERNAL = new ArrayList<>(); public static final List> TYPES = Collections.unmodifiableList(TYPES_INTERNAL); - public static final ContainerType ENERGY = - new ContainerType<>(MekanismDataComponents.ENERGY_CONTAINERS, NBTConstants.ENERGY_CONTAINERS, NBTConstants.CONTAINER, AttachedEnergyContainers::new, Capabilities.STRICT_ENERGY, TileEntityMekanism::getEnergyContainers, - TileEntityMekanism::canHandleEnergy) { - @Override - public void registerItemCapabilities(RegisterCapabilitiesEvent event, Item item, boolean exposeWhenStacked, IMekanismConfig... requiredConfigs) { - EnergyCompatUtils.registerItemCapabilities(event, item, getCapabilityProvider(exposeWhenStacked, requiredConfigs)); - } - }; - public static final ContainerType ITEM = new ContainerType<>(MekanismDataComponents.INVENTORY_SLOTS, NBTConstants.ITEMS, NBTConstants.SLOT, AttachedInventorySlots::new, Capabilities.ITEM, TileEntityMekanism::getInventorySlots, TileEntityMekanism::hasInventory); - public static final ContainerType FLUID = new ContainerType<>(MekanismDataComponents.FLUID_TANKS, NBTConstants.FLUID_TANKS, NBTConstants.TANK, AttachedFluidTanks::new, AttachedItemFluidTanks::new, Capabilities.FLUID, TileEntityMekanism::getFluidTanks, TileEntityMekanism::canHandleFluid); - public static final ContainerType GAS = new ContainerType<>(MekanismDataComponents.GAS_TANKS, NBTConstants.GAS_TANKS, NBTConstants.TANK, AttachedGasTanks::new, Capabilities.GAS, TileEntityMekanism::getGasTanks, TileEntityMekanism::canHandleGas); - public static final ContainerType INFUSION = new ContainerType<>(MekanismDataComponents.INFUSION_TANKS, NBTConstants.INFUSION_TANKS, NBTConstants.TANK, AttachedInfusionTanks::new, Capabilities.INFUSION, TileEntityMekanism::getInfusionTanks, TileEntityMekanism::canHandleInfusion); - public static final ContainerType PIGMENT = new ContainerType<>(MekanismDataComponents.PIGMENT_TANKS, NBTConstants.PIGMENT_TANKS, NBTConstants.TANK, AttachedPigmentTanks::new, Capabilities.PIGMENT, TileEntityMekanism::getPigmentTanks, TileEntityMekanism::canHandlePigment); - public static final ContainerType SLURRY = new ContainerType<>(MekanismDataComponents.SLURRY_TANKS, NBTConstants.SLURRY_TANKS, NBTConstants.TANK, AttachedSlurryTanks::new, Capabilities.SLURRY, TileEntityMekanism::getSlurryTanks, TileEntityMekanism::canHandleSlurry); - public static final ContainerType HEAT = new ContainerType<>(MekanismDataComponents.HEAT_CAPACITORS, NBTConstants.HEAT_CAPACITORS, NBTConstants.CONTAINER, AttachedHeatCapacitors::new, null, TileEntityMekanism::getHeatCapacitors, TileEntityMekanism::canHandleHeat); - + public static final ContainerType ENERGY = new ContainerType<>(MekanismDataComponents.ATTACHED_ENERGY, + NBTConstants.ENERGY_CONTAINERS, NBTConstants.CONTAINER, ComponentBackedEnergyHandler::new, Capabilities.STRICT_ENERGY, + TileEntityMekanism::getEnergyContainers, TileEntityMekanism::canHandleEnergy) { + @Override + @SuppressWarnings("unchecked") + public void registerItemCapabilities(RegisterCapabilitiesEvent event, Item item, boolean exposeWhenStacked, IMekanismConfig... requiredConfigs) { + EnergyCompatUtils.registerItemCapabilities(event, item, (ICapabilityProvider) getCapabilityProvider(exposeWhenStacked, requiredConfigs)); + } + }; + public static final ContainerType ITEM = new ContainerType<>(MekanismDataComponents.ATTACHED_ITEMS, + NBTConstants.ITEMS, NBTConstants.SLOT, ComponentBackedItemHandler::new, Capabilities.ITEM, TileEntityMekanism::getInventorySlots, + TileEntityMekanism::hasInventory); + public static final ContainerType FLUID = new ContainerType<>(MekanismDataComponents.ATTACHED_FLUIDS, + NBTConstants.FLUID_TANKS, NBTConstants.TANK, ComponentBackedFluidHandler::new, Capabilities.FLUID, TileEntityMekanism::getFluidTanks, + TileEntityMekanism::canHandleFluid); + public static final ContainerType GAS = new ContainerType<>(MekanismDataComponents.ATTACHED_GASES, + NBTConstants.GAS_TANKS, NBTConstants.TANK, ComponentBackedGasHandler::new, Capabilities.GAS, TileEntityMekanism::getGasTanks, + TileEntityMekanism::canHandleGas); + public static final ContainerType INFUSION = new ContainerType<>( + MekanismDataComponents.ATTACHED_INFUSE_TYPES, NBTConstants.INFUSION_TANKS, NBTConstants.TANK, ComponentBackedInfusionHandler::new, Capabilities.INFUSION, + TileEntityMekanism::getInfusionTanks, TileEntityMekanism::canHandleInfusion); + public static final ContainerType PIGMENT = new ContainerType<>(MekanismDataComponents.ATTACHED_PIGMENTS, + NBTConstants.PIGMENT_TANKS, NBTConstants.TANK, ComponentBackedPigmentHandler::new, Capabilities.PIGMENT, + TileEntityMekanism::getPigmentTanks, TileEntityMekanism::canHandlePigment); + public static final ContainerType SLURRY = new ContainerType<>(MekanismDataComponents.ATTACHED_SLURRIES, + NBTConstants.SLURRY_TANKS, NBTConstants.TANK, ComponentBackedSlurryHandler::new, Capabilities.SLURRY, TileEntityMekanism::getSlurryTanks, + TileEntityMekanism::canHandleSlurry); + public static final ContainerType HEAT = new ContainerType<>(MekanismDataComponents.ATTACHED_HEAT, + NBTConstants.HEAT_CAPACITORS, NBTConstants.CONTAINER, ComponentBackedHeatHandler::new, null, TileEntityMekanism::getHeatCapacitors, + TileEntityMekanism::canHandleHeat); + + //TODO - 1.20.5: Re-evaluate this codec implementation public static final Codec> CODEC = BuiltInRegistries.DATA_COMPONENT_TYPE.byNameCodec().comapFlatMap(componentType -> { for (ContainerType type : TYPES) { - if (type.attachment.value() == componentType) { + if (type.component.value() == componentType) { return DataResult.success(type); } } - return DataResult.error(() -> "Attachment type " + BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(componentType) + " does not have a corresponding container type"); - }, containerType -> containerType.attachment.get()); + return DataResult.error(() -> "Data Component type " + BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(componentType) + " does not have a corresponding container type"); + }, containerType -> containerType.component.get()); - private final Map>> knownDefaultItemContainers = new Reference2ObjectOpenHashMap<>(); - private final Map, Function>> knownDefaultEntityContainers = new Reference2ObjectOpenHashMap<>(); - private final BiFunction, @Nullable IContentsListener, ATTACHMENT> attachmentConstructor; + private final Map>> knownDefaultCreators = new Reference2ObjectOpenHashMap<>(); + private final Function handlerConstructor; private final BiFunction> containersFromTile; + private final DeferredHolder, DataComponentType> component; @Nullable - private final BiFunction, ATTACHMENT> itemAttachmentConstructor; - private final DeferredHolder, DataComponentType> attachment; - @Nullable - private final IMultiTypeCapability capability; + private final IMultiTypeCapability capability; private final Predicate canHandle; private final String containerTag; private final String containerKey; - private ContainerType(DeferredHolder, DataComponentType> attachment, String containerTag, String containerKey, - BiFunction, @Nullable IContentsListener, ATTACHMENT> attachmentConstructor, @Nullable IMultiTypeCapability capability, + private ContainerType(DeferredHolder, DataComponentType> component, String containerTag, String containerKey, + Function handlerConstructor, @Nullable IMultiTypeCapability capability, BiFunction> containersFromTile, Predicate canHandle) { - this(attachment, containerTag, containerKey, attachmentConstructor, null, capability, containersFromTile, canHandle); - } - - private ContainerType(DeferredHolder, DataComponentType> attachment, String containerTag, String containerKey, - BiFunction, @Nullable IContentsListener, ATTACHMENT> attachmentConstructor, @Nullable BiFunction, ATTACHMENT> itemAttachmentConstructor, - @Nullable IMultiTypeCapability capability, BiFunction> containersFromTile, Predicate canHandle) { TYPES_INTERNAL.add(this); - this.attachment = attachment; + this.component = component; this.containerTag = containerTag; this.containerKey = containerKey; - this.attachmentConstructor = attachmentConstructor; - this.itemAttachmentConstructor = itemAttachmentConstructor; + this.handlerConstructor = handlerConstructor; this.containersFromTile = containersFromTile; this.capability = capability; this.canHandle = canHandle; } - public DeferredHolder, DataComponentType> getDataComponentTypeHolder() { - return attachment; + public DeferredHolder, DataComponentType> getComponentType() { + return component; } @Nullable - public ResourceLocation getAttachmentName() { - return BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(attachment.get()); + public ResourceLocation getComponentName() { + return BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(component.get()); } public String getTag() { return containerTag; } - /** - * Adds a container as default and exposes it as a capability that requires the given configs if the specified bus is present. - */ - public void addDefaultContainer(@Nullable IEventBus eventBus, Item item, Function defaultCreator, IMekanismConfig... requiredConfigs) { - addDefaultContainers(eventBus, item, defaultCreator.andThen(List::of), requiredConfigs); - } - /** * Adds some containers as default and exposes it as a capability that requires the given configs if the specified bus is present. */ - public void addDefaultContainers(@Nullable IEventBus eventBus, Item item, Function> defaultCreators, IMekanismConfig... requiredConfigs) { - knownDefaultItemContainers.put(item, defaultCreators); + public void addDefaultCreators(@Nullable IEventBus eventBus, Item item, Supplier> defaultCreator, + IMekanismConfig... requiredConfigs) { + knownDefaultCreators.put(item, Lazy.of(defaultCreator)); if (eventBus != null && capability != null) { eventBus.addListener(RegisterCapabilitiesEvent.class, event -> registerItemCapabilities(event, item, false, requiredConfigs)); } @@ -158,73 +164,79 @@ public void addDefaultContainers(@Nullable IEventBus eventBus, Item item, Functi public void registerItemCapabilities(RegisterCapabilitiesEvent event, Item item, boolean exposeWhenStacked, IMekanismConfig... requiredConfigs) { if (capability != null) { - event.registerItem(capability.item(), getCapabilityProvider(exposeWhenStacked, requiredConfigs), item); + //TODO - 1.20.5: Re-evaluate this + event.registerItem((ItemCapability) capability.item(), getCapabilityProvider(exposeWhenStacked, requiredConfigs), item); } } + //TODO - 1.20.5: Do we want to have create in the name instead of get public List getAttachmentContainersIfPresent(ItemStack stack) { - ATTACHMENT attachment = getAttachmentIfPresent(stack); - return attachment == null ? Collections.emptyList() : attachment.getContainers(); + HANDLER handler = createHandlerIfData(stack); + return handler == null ? Collections.emptyList() : handler.getContainers(); } - @Nullable - public ATTACHMENT getAttachmentIfPresent(ItemStack stack) { - return stack.get(attachment); - } - - @Nullable - public ATTACHMENT getAttachment(ItemStack stack) { - ATTACHMENT existingData = stack.get(attachment); - if (existingData == null && knownDefaultItemContainers.containsKey(stack.getItem())) { - stack.set(attachment, existingData = getDefaultInternal(stack)); + public int getContainerCount(ItemStack stack) { + ATTACHED attached = stack.get(component); + if (attached == null) { + Lazy> containerCreator = knownDefaultCreators.get(stack.getItem()); + return containerCreator == null ? 0 : containerCreator.get().totalContainers(); } - return existingData; + return attached.size(); } - public ATTACHMENT getDefault(ItemStack stack) { - ATTACHMENT attachment = getDefaultInternal(stack); - if (attachment == null) { - throw new IllegalArgumentException("Attempted to attach a " + getAttachmentName() + " container to an object (" + RegistryUtils.getName(stack.getItem()) + - ") that doesn't have containers of that type."); - } - return attachment; + @Nullable//TODO - 1.20.5: Re-evaluate + public HANDLER createHandlerIfData(ItemStack stack) { + ATTACHED attached = stack.get(component); + return attached == null ? null : handlerConstructor.apply(stack); } @Nullable - private ATTACHMENT getDefaultInternal(ItemStack stack) { - List defaultContainers = Collections.emptyList(); - if (!stack.isEmpty()) { - defaultContainers = knownDefaultItemContainers.getOrDefault(stack.getItem(), s -> Collections.emptyList()).apply(stack); - if (!defaultContainers.isEmpty() && itemAttachmentConstructor != null) { - return itemAttachmentConstructor.apply(stack, defaultContainers); + public HANDLER createHandler(ItemStack stack) { + //TODO - 1.20.5: Do we want local callers to just directly access the handler constructor as we wouldn't be exposing the cap + // if we didn't have any creators? + ATTACHED attached = stack.get(component); + if (attached == null) { + Lazy> lazy = knownDefaultCreators.get(stack.getItem()); + if (lazy == null) { + return null; + } + IContainerCreator containerCreator = lazy.get(); + int count = containerCreator.totalContainers(); + if (count == 0) { + return null; } + attached = containerCreator.initStorage(count); + stack.set(component, attached); } - if (defaultContainers.isEmpty()) { - return null; + return handlerConstructor.apply(stack); + } + + public CONTAINER createContainer(ItemStack attachedTo, int containerIndex) { + Lazy> creator = knownDefaultCreators.get(attachedTo.getItem()); + if (creator != null) { + return creator.get().create(this, attachedTo, containerIndex); } - //TODO: If we end up supporting other types of attachment holders than stacks and entities we will want to make sure to pass a contents listener to them - // we don't need to for items or entities as attachments on them are always saved - return attachmentConstructor.apply(defaultContainers, null); + //TODO - 1.20.5: Do we want to throw something if there is an error in creating the slot? Or do we want to mark this as nullable + // and then fail further up the line? + throw new IllegalArgumentException("No known containers for item " + attachedTo.getItem()); } - @SuppressWarnings("unchecked") - protected ICapabilityProvider getCapabilityProvider(boolean exposeWhenStacked, IMekanismConfig... requiredConfigs) { + protected ICapabilityProvider getCapabilityProvider(boolean exposeWhenStacked, IMekanismConfig... requiredConfigs) { if (exposeWhenStacked) { return getCapabilityProvider(requiredConfigs); } else if (requiredConfigs.length == 0) { - return (stack, context) -> stack.getCount() == 1 ? (H) getAttachment(stack) : null; + return (stack, context) -> stack.getCount() == 1 ? createHandler(stack) : null; } //Only expose the capabilities if the required configs are loaded - return (stack, context) -> stack.getCount() == 1 && hasRequiredConfigs(requiredConfigs) ? (H) getAttachment(stack) : null; + return (stack, context) -> stack.getCount() == 1 && hasRequiredConfigs(requiredConfigs) ? createHandler(stack) : null; } - @SuppressWarnings("unchecked") - protected ICapabilityProvider getCapabilityProvider(IMekanismConfig... requiredConfigs) { + protected ICapabilityProvider getCapabilityProvider(IMekanismConfig... requiredConfigs) { if (requiredConfigs.length == 0) { - return (stack, context) -> (H) getAttachment(stack); + return (stack, context) -> createHandler(stack); } //Only expose the capabilities if the required configs are loaded - return (stack, context) -> hasRequiredConfigs(requiredConfigs) ? (H) getAttachment(stack) : null; + return (stack, context) -> hasRequiredConfigs(requiredConfigs) ? createHandler(stack) : null; } private static boolean hasRequiredConfigs(IMekanismConfig... requiredConfigs) { @@ -237,7 +249,7 @@ private static boolean hasRequiredConfigs(IMekanismConfig... requiredConfigs) { } public boolean supports(ItemStack stack) { - return stack.has(attachment) || knownDefaultItemContainers.containsKey(stack.getItem()); + return stack.has(component) || knownDefaultCreators.containsKey(stack.getItem()); } ListTag save(HolderLookup.Provider provider, List containers) { @@ -269,29 +281,33 @@ public void readFrom(HolderLookup.Provider provider, CompoundTag tag, List containers, ItemStack stack) { - ATTACHMENT attachment = getAttachment(stack); + //TODO - 1.20.5: Figure out container copying + /*ATTACHMENT attachment = getAttachment(stack); if (attachment != null) { attachment.deserializeNBT(provider, save(provider, containers)); if (stack.getCount() > 1) { - Mekanism.logger.error("Copied {} to a stack ({}) of {}. This might lead to duplication of data.", getAttachmentName(), stack.getCount(), RegistryUtils.getName(stack.getItem())); + Mekanism.logger.error("Copied {} to a stack ({}). This might lead to duplication of data.", getComponentName(), stack); } - } + }*/ } - public void copyFrom(HolderLookup.Provider provider, ItemStack stack, TileEntityMekanism tile) { - copyFrom(provider, stack, getContainers(tile)); + public void copyFrom(TileEntityMekanism tile, BlockEntity.DataComponentInput input) { + //TODO - 1.20.5: Figure out container copying + //copyFrom(provider, stack, getContainers(tile)); } public void copyFrom(HolderLookup.Provider provider, ItemStack stack, List containers) { - ATTACHMENT attachment = getAttachment(stack); + //TODO - 1.20.5: Figure out container copying + /*ATTACHMENT attachment = getAttachment(stack); if (attachment != null) { read(provider, containers, attachment.serializeNBT(provider)); - } + }*/ } public boolean canHandle(TileEntityMekanism tile) { diff --git a/src/main/java/mekanism/common/attachments/containers/ContainsRecipe.java b/src/main/java/mekanism/common/attachments/containers/ContainsRecipe.java new file mode 100644 index 00000000000..7bde0631771 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/ContainsRecipe.java @@ -0,0 +1,11 @@ +package mekanism.common.attachments.containers; + +import mekanism.common.recipe.lookup.cache.IInputRecipeCache; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; + +@FunctionalInterface +public interface ContainsRecipe { + + boolean check(INPUT_CACHE cache, @Nullable Level level, TYPE value); +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/IAttachedContainers.java b/src/main/java/mekanism/common/attachments/containers/IAttachedContainers.java new file mode 100644 index 00000000000..3b1a6805c1e --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/IAttachedContainers.java @@ -0,0 +1,50 @@ +package mekanism.common.attachments.containers; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Spliterator; +import java.util.function.Consumer; +import mekanism.api.annotations.NothingNullByDefault; + +@NothingNullByDefault +public interface IAttachedContainers> extends Iterable { + + List containers(); + + default boolean isEmpty() { + return containers().isEmpty(); + } + + default int size() { + return containers().size(); + } + + default TYPE get(int index) { + return containers().get(index); + } + + ATTACHED create(List containers); + + //TODO - 1.20.5: add javadocs that specify it is assumed data will be copied BEFORE calling this + default ATTACHED with(int index, TYPE data) { + List copy = new ArrayList<>(containers()); + copy.set(index, data); + return create(copy); + } + + @Override + default Iterator iterator() { + return containers().iterator(); + } + + @Override + default void forEach(Consumer action) { + containers().forEach(action); + } + + @Override + default Spliterator spliterator() { + return containers().spliterator(); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/ChemicalTanksBuilder.java b/src/main/java/mekanism/common/attachments/containers/chemical/ChemicalTanksBuilder.java new file mode 100644 index 00000000000..7c41b2be3b3 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/ChemicalTanksBuilder.java @@ -0,0 +1,52 @@ +package mekanism.common.attachments.containers.chemical; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.LongSupplier; +import java.util.function.Predicate; +import mekanism.api.chemical.Chemical; +import mekanism.api.chemical.ChemicalStack; +import mekanism.api.recipes.MekanismRecipe; +import mekanism.common.attachments.containers.ContainsRecipe; +import mekanism.common.attachments.containers.creator.BaseContainerCreator; +import mekanism.common.attachments.containers.creator.IBasicContainerCreator; +import mekanism.common.recipe.IMekanismRecipeTypeProvider; +import mekanism.common.recipe.lookup.cache.IInputRecipeCache; +import org.jetbrains.annotations.NotNull; + +public abstract class ChemicalTanksBuilder, STACK extends ChemicalStack, + TANK extends ComponentBackedChemicalTank, BUILDER extends ChemicalTanksBuilder> { + + protected final List> tankCreators = new ArrayList<>(); + + protected ChemicalTanksBuilder() { + } + + public abstract BaseContainerCreator build(); + + @SuppressWarnings("unchecked") + public BUILDER addBasic(long capacity, + IMekanismRecipeTypeProvider recipeType, ContainsRecipe containsRecipe) { + return addBasic(capacity, chemical -> containsRecipe.check(recipeType.getInputCache(), null, (STACK) chemical.getStack(1))); + } + + public BUILDER addBasic(long capacity, Predicate<@NotNull CHEMICAL> isValid) { + return addBasic(() -> capacity, isValid); + } + + public abstract BUILDER addBasic(LongSupplier capacity, Predicate<@NotNull CHEMICAL> isValid); + + public BUILDER addBasic(long capacity) { + return addBasic(() -> capacity); + } + + public abstract BUILDER addBasic(LongSupplier capacity); + + public abstract BUILDER addInternalStorage(LongSupplier rate, LongSupplier capacity, Predicate<@NotNull CHEMICAL> isValid); + + @SuppressWarnings("unchecked") + public BUILDER addTank(IBasicContainerCreator tank) { + tankCreators.add(tank); + return (BUILDER) this; + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/ComponentBackedChemicalHandler.java b/src/main/java/mekanism/common/attachments/containers/chemical/ComponentBackedChemicalHandler.java new file mode 100644 index 00000000000..055acc72c6d --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/ComponentBackedChemicalHandler.java @@ -0,0 +1,66 @@ +package mekanism.common.attachments.containers.chemical; + +import java.util.List; +import mekanism.api.Action; +import mekanism.api.AutomationType; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.chemical.Chemical; +import mekanism.api.chemical.ChemicalStack; +import mekanism.api.chemical.ChemicalUtils; +import mekanism.api.chemical.IChemicalTank; +import mekanism.api.chemical.IMekanismChemicalHandler; +import mekanism.common.attachments.containers.ComponentBackedHandler; +import mekanism.common.attachments.containers.IAttachedContainers; +import net.minecraft.core.Direction; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +@NothingNullByDefault +public abstract class ComponentBackedChemicalHandler, STACK extends ChemicalStack, + TANK extends IChemicalTank, ATTACHED extends IAttachedContainers> + extends ComponentBackedHandler implements IMekanismChemicalHandler { + + public ComponentBackedChemicalHandler(ItemStack attachedTo) { + super(attachedTo); + } + + @Override + public List getChemicalTanks(@Nullable Direction side) { + return getContainers(); + } + + @Nullable + @Override + public TANK getChemicalTank(int tank, @Nullable Direction side) { + return getContainer(tank); + } + + @Override + public int getTanks(@Nullable Direction side) { + return containerCount(); + } + + @Override + public STACK getChemicalInTank(int tank, @Nullable Direction side) { + ATTACHED attachedChemicals = getAttached(); + return attachedChemicals == null ? getEmptyStack() : attachedChemicals.get(tank); + } + + @Override + public STACK insertChemical(STACK stack, @Nullable Direction side, Action action) { + //TODO - 1.20.5: Can we optimize this any further? Maybe by somehow only initializing the chemical tanks as necessary/we actually get to iterating against them? + return ChemicalUtils.insert(stack, side, this::getChemicalTanks, action, AutomationType.handler(side), getEmptyStack()); + } + + @Override + public STACK extractChemical(long amount, @Nullable Direction side, Action action) { + //TODO - 1.20.5: Can we optimize this any further? Maybe by somehow only initializing the chemical tanks as necessary/we actually get to iterating against them? + return ChemicalUtils.extract(amount, side, this::getChemicalTanks, action, AutomationType.handler(side), getEmptyStack()); + } + + @Override + public STACK extractChemical(STACK stack, @Nullable Direction side, Action action) { + //TODO - 1.20.5: Can we optimize this any further? Maybe by somehow only initializing the chemical tanks as necessary/we actually get to iterating against them? + return ChemicalUtils.extract(stack, side, this::getChemicalTanks, action, AutomationType.handler(side), getEmptyStack()); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/ComponentBackedChemicalTank.java b/src/main/java/mekanism/common/attachments/containers/chemical/ComponentBackedChemicalTank.java new file mode 100644 index 00000000000..512142dd1d9 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/ComponentBackedChemicalTank.java @@ -0,0 +1,227 @@ +package mekanism.common.attachments.containers.chemical; + +import java.util.function.BiPredicate; +import java.util.function.LongSupplier; +import java.util.function.Predicate; +import mekanism.api.Action; +import mekanism.api.AutomationType; +import mekanism.api.NBTConstants; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.chemical.Chemical; +import mekanism.api.chemical.ChemicalStack; +import mekanism.api.chemical.IChemicalTank; +import mekanism.api.chemical.attribute.ChemicalAttributeValidator; +import mekanism.common.attachments.containers.ComponentBackedContainer; +import mekanism.common.attachments.containers.IAttachedContainers; +import mekanism.common.util.ChemicalUtil; +import net.minecraft.core.HolderLookup.Provider; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@NothingNullByDefault//TODO - 1.20.5: BasicChemicalTank also implement IChemicalHandler. Do we want to be doing that here? +public abstract class ComponentBackedChemicalTank, STACK extends ChemicalStack, + ATTACHED extends IAttachedContainers> extends ComponentBackedContainer implements IChemicalTank { + + private final BiPredicate<@NotNull CHEMICAL, @NotNull AutomationType> canExtract; + private final BiPredicate<@NotNull CHEMICAL, @NotNull AutomationType> canInsert; + private final Predicate<@NotNull CHEMICAL> validator; + @Nullable + private final ChemicalAttributeValidator attributeValidator; + private final LongSupplier capacity; + private final LongSupplier rate; + + protected ComponentBackedChemicalTank(ItemStack attachedTo, int tankIndex, BiPredicate<@NotNull CHEMICAL, @NotNull AutomationType> canExtract, + BiPredicate<@NotNull CHEMICAL, @NotNull AutomationType> canInsert, Predicate<@NotNull CHEMICAL> validator, LongSupplier rate, LongSupplier capacity, + @Nullable ChemicalAttributeValidator attributeValidator) { + super(attachedTo, tankIndex); + this.canExtract = canExtract; + this.canInsert = canInsert; + this.validator = validator; + this.capacity = capacity; + this.rate = rate; + this.attributeValidator = attributeValidator; + } + + @Override + protected STACK copy(STACK toCopy) { + return ChemicalUtil.copy(toCopy); + } + + @Override + protected boolean isEmpty(STACK value) { + return value.isEmpty(); + } + + @Override + public STACK getStack() { + //TODO - 1.20.5: Similar to getBasicInventorySlot do we want to reduce calls to this? Probably (We mostly do so, but we probably want to add a note here) + ATTACHED attachedChemicals = getAttached(); + return attachedChemicals == null ? getEmptyStack() : getContents(attachedChemicals); + } + + @Override + public void setStack(STACK stack) { + //TODO - 1.20.5: I think just always setting it as unchecked is fine as we are doing that for items regardless + // but we may want to validate it? + setStackUnchecked(stack); + } + + @Override + public void setStackUnchecked(STACK stack) { + setContents(stack); + } + + @Override + public ChemicalAttributeValidator getAttributeValidator() { + return attributeValidator == null ? IChemicalTank.super.getAttributeValidator() : attributeValidator; + } + + protected long getRate(@Nullable AutomationType automationType) { + //Allow unknown or manual interaction to bypass rate limit for the item + return automationType == null || automationType == AutomationType.MANUAL ? Long.MAX_VALUE : rate.getAsLong(); + } + + @Override + public long getCapacity() { + return capacity.getAsLong(); + } + + @Override + public boolean isValid(STACK stack) { + return getAttributeValidator().process(stack) && validator.test(stack.getChemical()); + } + + @Override + public STACK insert(STACK stack, Action action, AutomationType automationType) { + //TODO - 1.20.5: Items do the is valid and canInsert check after checking the needed amount. Should we do the same for fluids + // or should items have the order flipped? In general calculating the needed amount is likely cheaper which is likely why items do it first + if (stack.isEmpty() || !isValid(stack) || !canInsert.test(stack.getChemical(), automationType)) { + //"Fail quick" if the given stack is empty, or we can never insert the fluid or currently are unable to insert it + return stack; + } + ATTACHED attachedChemicals = getAttached(); + if (attachedChemicals == null) { + //"Fail quick" if we can't have a stack + return stack; + } + STACK stored = getContents(attachedChemicals); + long needed = Math.min(getRate(automationType), getNeeded(stored)); + if (needed <= 0) { + //Fail if we are a full tank or our rate is zero + return stack; + } else if (stored.isEmpty() || stored.isTypeEqual(stack)) { + long toAdd = Math.min(stack.getAmount(), needed); + if (action.execute()) { + //Note: We let setStack handle updating the backing holding stack + // We use stored.getAmount + toAdd so that if we are empty we end up at toAdd + // but if we aren't then we grow by the given amount + setContents(attachedChemicals, createStack(stack, stored.getAmount() + toAdd)); + } + return createStack(stack, stack.getAmount() - toAdd); + } + //If we didn't accept this fluid, then just return the given stack + return stack; + } + + @Override + public final STACK extract(long amount, Action action, AutomationType automationType) { + if (amount < 1) { + //"Fail quick" if the amount being requested is less than one + return getEmptyStack(); + } + ATTACHED attachedChemicals = getAttached(); + if (attachedChemicals == null) { + //"Fail quick" if we can't have a stack + return getEmptyStack(); + } + return extract(attachedChemicals, getContents(attachedChemicals), amount, action, automationType); + } + + protected STACK extract(ATTACHED attachedChemicals, STACK stored, long amount, Action action, AutomationType automationType) { + if (amount < 1 || stored.isEmpty() || !canExtract.test(stored.getChemical(), automationType)) { + //"Fail quick" if we don't can never extract from this tank, have a fluid stored, or the amount being requested is less than one + return getEmptyStack(); + } + //Note: While we technically could just return the stack itself if we are removing all that we have, it would require a lot more checks + // We also are limiting it by the rate this tank has + long size = Math.min(Math.min(getRate(automationType), stored.getAmount()), amount); + if (size == 0) { + return getEmptyStack(); + } + STACK ret = createStack(stored, size); + if (!ret.isEmpty() && action.execute()) { + //Note: We let setStack handle updating the backing holding stack + setContents(attachedChemicals, createStack(stored, stored.getAmount() - ret.getAmount())); + } + return ret; + } + + @Override + public final long setStackSize(long amount, Action action) { + ATTACHED attachedChemicals = getAttached(); + if (attachedChemicals == null) { + //"Fail quick" if we can't have a stack + return 0; + } + return setStackSize(attachedChemicals, getContents(attachedChemicals), amount, action); + } + + protected long setStackSize(ATTACHED attachedChemicals, STACK stored, long amount, Action action) { + if (stored.isEmpty()) { + return 0; + } else if (amount <= 0) { + if (action.execute()) { + setContents(attachedChemicals, getEmptyStack()); + } + return 0; + } + long maxStackSize = getCapacity(); + if (amount > maxStackSize) { + amount = maxStackSize; + } + if (stored.getAmount() == amount || action.simulate()) { + //If our size is not changing, or we are only simulating the change, don't do anything + return amount; + } + setContents(attachedChemicals, createStack(stored, amount)); + return amount; + } + + @Override + public long growStack(long amount, Action action) { + ATTACHED attachedChemicals = getAttached(); + if (attachedChemicals == null) { + //"Fail quick" if we can't have a stack + return 0; + } + STACK stored = getContents(attachedChemicals); + long current = stored.getAmount(); + if (amount > 0) { + //Cap adding amount at how much we need, so that we don't risk integer overflow + amount = Math.min(Math.min(amount, getNeeded(stored)), getRate(null)); + } else if (amount < 0) { + amount = Math.max(amount, -getRate(null)); + } + long newSize = setStackSize(attachedChemicals, stored, current + amount, action); + return newSize - current; + } + + protected long getNeeded(STACK stored) { + //Skip the stack lookup for getNeeded + return Math.max(0, getCapacity() - stored.getAmount()); + } + + @Override + public CompoundTag serializeNBT(Provider provider) { + //TODO - 1.20.5: This is a copy of BasicChemicalTank#serializeNBT. We might need to also grab the specific overrides of + // that method as special component backed inventory slots, that then access and put that other data as a different component? + CompoundTag nbt = new CompoundTag(); + STACK stored = getStack(); + if (!stored.isEmpty()) { + nbt.put(NBTConstants.STORED, stored.save(provider)); + } + return nbt; + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/gas/AttachedGases.java b/src/main/java/mekanism/common/attachments/containers/chemical/gas/AttachedGases.java new file mode 100644 index 00000000000..d93e23cc366 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/gas/AttachedGases.java @@ -0,0 +1,39 @@ +package mekanism.common.attachments.containers.chemical.gas; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.Collections; +import java.util.List; +import mekanism.api.NBTConstants; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.chemical.gas.GasStack; +import mekanism.common.attachments.containers.IAttachedContainers; +import net.minecraft.core.NonNullList; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; + +@NothingNullByDefault +public record AttachedGases(List containers) implements IAttachedContainers { + + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + GasStack.OPTIONAL_CODEC.listOf().fieldOf(NBTConstants.GAS_TANKS).forGetter(AttachedGases::containers) + ).apply(instance, AttachedGases::new)); + public static final StreamCodec STREAM_CODEC = + GasStack.OPTIONAL_STREAM_CODEC.>apply(ByteBufCodecs.collection(NonNullList::createWithCapacity)) + .map(AttachedGases::new, AttachedGases::containers); + + public AttachedGases { + //Make the list unmodifiable to ensure we don't accidentally mutate it + containers = Collections.unmodifiableList(containers); + } + + public AttachedGases(int containers) { + this(NonNullList.withSize(containers, GasStack.EMPTY)); + } + + @Override + public AttachedGases create(List containers) { + return new AttachedGases(containers); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/gas/ComponentBackedChemicalTankGasTank.java b/src/main/java/mekanism/common/attachments/containers/chemical/gas/ComponentBackedChemicalTankGasTank.java new file mode 100644 index 00000000000..7715a699f4a --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/gas/ComponentBackedChemicalTankGasTank.java @@ -0,0 +1,52 @@ +package mekanism.common.attachments.containers.chemical.gas; + +import mekanism.api.Action; +import mekanism.api.AutomationType; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.chemical.ChemicalTankBuilder; +import mekanism.api.chemical.attribute.ChemicalAttributeValidator; +import mekanism.api.chemical.gas.GasStack; +import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.item.block.ItemBlockChemicalTank; +import mekanism.common.tier.ChemicalTankTier; +import net.minecraft.world.item.ItemStack; + +@NothingNullByDefault +public class ComponentBackedChemicalTankGasTank extends ComponentBackedGasTank { + + private final boolean isCreative; + + public static ComponentBackedChemicalTankGasTank create(ContainerType ignored, ItemStack attachedTo, int tankIndex) { + if (!(attachedTo.getItem() instanceof ItemBlockChemicalTank item)) { + throw new IllegalStateException("Attached to should always be a chemical tank item"); + } + return new ComponentBackedChemicalTankGasTank(attachedTo, tankIndex, item.getTier()); + } + + private ComponentBackedChemicalTankGasTank(ItemStack attachedTo, int tankIndex, ChemicalTankTier tier) { + super(attachedTo, tankIndex, ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrue, + tier::getOutput, tier::getStorage, tier == ChemicalTankTier.CREATIVE ? ChemicalAttributeValidator.ALWAYS_ALLOW : null); + isCreative = tier == ChemicalTankTier.CREATIVE; + } + + @Override + public GasStack insert(GasStack stack, Action action, AutomationType automationType) { + return super.insert(stack, action.combine(!isCreative), automationType); + } + + @Override + public GasStack extract(AttachedGases attachedGases, GasStack stored, long amount, Action action, AutomationType automationType) { + return super.extract(attachedGases, stored, amount, action.combine(!isCreative), automationType); + } + + /** + * {@inheritDoc} + * + * Note: We are only patching {@link #setStackSize(AttachedGases, GasStack, long, Action)}, as both {@link #growStack(long, Action)} and + * {@link #shrinkStack(long, Action)} are wrapped through this method. + */ + @Override + public long setStackSize(AttachedGases attachedGases, GasStack stored, long amount, Action action) { + return super.setStackSize(attachedGases, stored, amount, action.combine(!isCreative)); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/gas/ComponentBackedGasHandler.java b/src/main/java/mekanism/common/attachments/containers/chemical/gas/ComponentBackedGasHandler.java new file mode 100644 index 00000000000..2449b9c51a6 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/gas/ComponentBackedGasHandler.java @@ -0,0 +1,23 @@ +package mekanism.common.attachments.containers.chemical.gas; + +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.chemical.gas.Gas; +import mekanism.api.chemical.gas.GasStack; +import mekanism.api.chemical.gas.IGasHandler.IMekanismGasHandler; +import mekanism.api.chemical.gas.IGasTank; +import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.chemical.ComponentBackedChemicalHandler; +import net.minecraft.world.item.ItemStack; + +@NothingNullByDefault +public class ComponentBackedGasHandler extends ComponentBackedChemicalHandler implements IMekanismGasHandler { + + public ComponentBackedGasHandler(ItemStack attachedTo) { + super(attachedTo); + } + + @Override + protected ContainerType containerType() { + return ContainerType.GAS; + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/gas/ComponentBackedGasTank.java b/src/main/java/mekanism/common/attachments/containers/chemical/gas/ComponentBackedGasTank.java new file mode 100644 index 00000000000..8bf4446d276 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/gas/ComponentBackedGasTank.java @@ -0,0 +1,31 @@ +package mekanism.common.attachments.containers.chemical.gas; + +import java.util.function.BiPredicate; +import java.util.function.LongSupplier; +import java.util.function.Predicate; +import java.util.function.Supplier; +import mekanism.api.AutomationType; +import mekanism.api.chemical.attribute.ChemicalAttributeValidator; +import mekanism.api.chemical.gas.Gas; +import mekanism.api.chemical.gas.GasStack; +import mekanism.api.chemical.gas.IGasTank; +import mekanism.common.attachments.containers.chemical.ComponentBackedChemicalTank; +import mekanism.common.registries.MekanismDataComponents; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ComponentBackedGasTank extends ComponentBackedChemicalTank implements IGasTank { + + public ComponentBackedGasTank(ItemStack attachedTo, int tankIndex, BiPredicate<@NotNull Gas, @NotNull AutomationType> canExtract, + BiPredicate<@NotNull Gas, @NotNull AutomationType> canInsert, Predicate<@NotNull Gas> validator, LongSupplier rate, LongSupplier capacity, + @Nullable ChemicalAttributeValidator attributeValidator) { + super(attachedTo, tankIndex, canExtract, canInsert, validator, rate, capacity, attributeValidator); + } + + @Override + protected Supplier> dataComponentType() { + return MekanismDataComponents.ATTACHED_GASES; + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/gas/GasTanksBuilder.java b/src/main/java/mekanism/common/attachments/containers/chemical/gas/GasTanksBuilder.java new file mode 100644 index 00000000000..8feb9963816 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/gas/GasTanksBuilder.java @@ -0,0 +1,58 @@ +package mekanism.common.attachments.containers.chemical.gas; + +import java.util.List; +import java.util.function.LongSupplier; +import java.util.function.Predicate; +import mekanism.api.chemical.ChemicalTankBuilder; +import mekanism.api.chemical.gas.Gas; +import mekanism.api.chemical.gas.GasStack; +import mekanism.common.attachments.containers.chemical.ChemicalTanksBuilder; +import mekanism.common.attachments.containers.creator.BaseContainerCreator; +import mekanism.common.attachments.containers.creator.IBasicContainerCreator; +import mekanism.common.config.MekanismConfig; +import org.jetbrains.annotations.NotNull; + +public class GasTanksBuilder extends ChemicalTanksBuilder { + + public static GasTanksBuilder builder() { + return new GasTanksBuilder(); + } + + private GasTanksBuilder() { + } + + @Override + public BaseContainerCreator build() { + return new BaseGasTankBuilder(tankCreators); + } + + @Override + public GasTanksBuilder addBasic(LongSupplier capacity, Predicate<@NotNull Gas> isValid) { + return addTank((type, attachedTo, containerIndex) -> new ComponentBackedGasTank(attachedTo, containerIndex, ChemicalTankBuilder.GAS.manualOnly, + ChemicalTankBuilder.GAS.alwaysTrueBi, isValid, MekanismConfig.general.chemicalItemFillRate, capacity, null)); + } + + @Override + public GasTanksBuilder addBasic(LongSupplier capacity) { + return addTank((type, attachedTo, containerIndex) -> new ComponentBackedGasTank(attachedTo, containerIndex, ChemicalTankBuilder.GAS.manualOnly, + ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrue, MekanismConfig.general.chemicalItemFillRate, capacity, null)); + } + + @Override + public GasTanksBuilder addInternalStorage(LongSupplier rate, LongSupplier capacity, Predicate<@NotNull Gas> isValid) { + return addTank((type, attachedTo, containerIndex) -> new ComponentBackedGasTank(attachedTo, containerIndex, ChemicalTankBuilder.GAS.notExternal, + ChemicalTankBuilder.GAS.alwaysTrueBi, isValid, rate, capacity, null)); + } + + private static class BaseGasTankBuilder extends BaseContainerCreator { + + public BaseGasTankBuilder(List> creators) { + super(creators); + } + + @Override + public AttachedGases initStorage(int containers) { + return new AttachedGases(containers); + } + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/infuse/AttachedInfuseTypes.java b/src/main/java/mekanism/common/attachments/containers/chemical/infuse/AttachedInfuseTypes.java new file mode 100644 index 00000000000..40e7cb52fe1 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/infuse/AttachedInfuseTypes.java @@ -0,0 +1,39 @@ +package mekanism.common.attachments.containers.chemical.infuse; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.Collections; +import java.util.List; +import mekanism.api.NBTConstants; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.chemical.infuse.InfusionStack; +import mekanism.common.attachments.containers.IAttachedContainers; +import net.minecraft.core.NonNullList; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; + +@NothingNullByDefault +public record AttachedInfuseTypes(List containers) implements IAttachedContainers { + + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + InfusionStack.OPTIONAL_CODEC.listOf().fieldOf(NBTConstants.INFUSION_TANKS).forGetter(AttachedInfuseTypes::containers) + ).apply(instance, AttachedInfuseTypes::new)); + public static final StreamCodec STREAM_CODEC = + InfusionStack.OPTIONAL_STREAM_CODEC.>apply(ByteBufCodecs.collection(NonNullList::createWithCapacity)) + .map(AttachedInfuseTypes::new, AttachedInfuseTypes::containers); + + public AttachedInfuseTypes { + //Make the list unmodifiable to ensure we don't accidentally mutate it + containers = Collections.unmodifiableList(containers); + } + + public AttachedInfuseTypes(int containers) { + this(NonNullList.withSize(containers, InfusionStack.EMPTY)); + } + + @Override + public AttachedInfuseTypes create(List containers) { + return new AttachedInfuseTypes(containers); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/infuse/ComponentBackedChemicalTankInfusionTank.java b/src/main/java/mekanism/common/attachments/containers/chemical/infuse/ComponentBackedChemicalTankInfusionTank.java new file mode 100644 index 00000000000..a212017fa2f --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/infuse/ComponentBackedChemicalTankInfusionTank.java @@ -0,0 +1,52 @@ +package mekanism.common.attachments.containers.chemical.infuse; + +import mekanism.api.Action; +import mekanism.api.AutomationType; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.chemical.ChemicalTankBuilder; +import mekanism.api.chemical.attribute.ChemicalAttributeValidator; +import mekanism.api.chemical.infuse.InfusionStack; +import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.item.block.ItemBlockChemicalTank; +import mekanism.common.tier.ChemicalTankTier; +import net.minecraft.world.item.ItemStack; + +@NothingNullByDefault +public class ComponentBackedChemicalTankInfusionTank extends ComponentBackedInfusionTank { + + private final boolean isCreative; + + public static ComponentBackedChemicalTankInfusionTank create(ContainerType ignored, ItemStack attachedTo, int tankIndex) { + if (!(attachedTo.getItem() instanceof ItemBlockChemicalTank item)) { + throw new IllegalStateException("Attached to should always be a chemical tank item"); + } + return new ComponentBackedChemicalTankInfusionTank(attachedTo, tankIndex, item.getTier()); + } + + private ComponentBackedChemicalTankInfusionTank(ItemStack attachedTo, int tankIndex, ChemicalTankTier tier) { + super(attachedTo, tankIndex, ChemicalTankBuilder.INFUSION.alwaysTrueBi, ChemicalTankBuilder.INFUSION.alwaysTrueBi, ChemicalTankBuilder.INFUSION.alwaysTrue, + tier::getOutput, tier::getStorage, tier == ChemicalTankTier.CREATIVE ? ChemicalAttributeValidator.ALWAYS_ALLOW : null); + isCreative = tier == ChemicalTankTier.CREATIVE; + } + + @Override + public InfusionStack insert(InfusionStack stack, Action action, AutomationType automationType) { + return super.insert(stack, action.combine(!isCreative), automationType); + } + + @Override + public InfusionStack extract(AttachedInfuseTypes attachedInfuseTypes, InfusionStack stored, long amount, Action action, AutomationType automationType) { + return super.extract(attachedInfuseTypes, stored, amount, action.combine(!isCreative), automationType); + } + + /** + * {@inheritDoc} + * + * Note: We are only patching {@link #setStackSize(AttachedInfuseTypes, InfusionStack, long, Action)}, as both {@link #growStack(long, Action)} and + * {@link #shrinkStack(long, Action)} are wrapped through this method. + */ + @Override + public long setStackSize(AttachedInfuseTypes attachedInfuseTypes, InfusionStack stored, long amount, Action action) { + return super.setStackSize(attachedInfuseTypes, stored, amount, action.combine(!isCreative)); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/infuse/ComponentBackedInfusionHandler.java b/src/main/java/mekanism/common/attachments/containers/chemical/infuse/ComponentBackedInfusionHandler.java new file mode 100644 index 00000000000..c1ebfbf9e73 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/infuse/ComponentBackedInfusionHandler.java @@ -0,0 +1,23 @@ +package mekanism.common.attachments.containers.chemical.infuse; + +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.chemical.infuse.IInfusionHandler.IMekanismInfusionHandler; +import mekanism.api.chemical.infuse.IInfusionTank; +import mekanism.api.chemical.infuse.InfuseType; +import mekanism.api.chemical.infuse.InfusionStack; +import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.chemical.ComponentBackedChemicalHandler; +import net.minecraft.world.item.ItemStack; + +@NothingNullByDefault +public class ComponentBackedInfusionHandler extends ComponentBackedChemicalHandler implements IMekanismInfusionHandler { + + public ComponentBackedInfusionHandler(ItemStack attachedTo) { + super(attachedTo); + } + + @Override + protected ContainerType containerType() { + return ContainerType.INFUSION; + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/infuse/ComponentBackedInfusionTank.java b/src/main/java/mekanism/common/attachments/containers/chemical/infuse/ComponentBackedInfusionTank.java new file mode 100644 index 00000000000..9415a9d1893 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/infuse/ComponentBackedInfusionTank.java @@ -0,0 +1,31 @@ +package mekanism.common.attachments.containers.chemical.infuse; + +import java.util.function.BiPredicate; +import java.util.function.LongSupplier; +import java.util.function.Predicate; +import java.util.function.Supplier; +import mekanism.api.AutomationType; +import mekanism.api.chemical.attribute.ChemicalAttributeValidator; +import mekanism.api.chemical.infuse.IInfusionTank; +import mekanism.api.chemical.infuse.InfuseType; +import mekanism.api.chemical.infuse.InfusionStack; +import mekanism.common.attachments.containers.chemical.ComponentBackedChemicalTank; +import mekanism.common.registries.MekanismDataComponents; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ComponentBackedInfusionTank extends ComponentBackedChemicalTank implements IInfusionTank { + + public ComponentBackedInfusionTank(ItemStack attachedTo, int tankIndex, BiPredicate<@NotNull InfuseType, @NotNull AutomationType> canExtract, + BiPredicate<@NotNull InfuseType, @NotNull AutomationType> canInsert, Predicate<@NotNull InfuseType> validator, LongSupplier rate, LongSupplier capacity, + @Nullable ChemicalAttributeValidator attributeValidator) { + super(attachedTo, tankIndex, canExtract, canInsert, validator, rate, capacity, attributeValidator); + } + + @Override + protected Supplier> dataComponentType() { + return MekanismDataComponents.ATTACHED_INFUSE_TYPES; + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/infuse/InfusionTanksBuilder.java b/src/main/java/mekanism/common/attachments/containers/chemical/infuse/InfusionTanksBuilder.java new file mode 100644 index 00000000000..599f6300089 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/infuse/InfusionTanksBuilder.java @@ -0,0 +1,58 @@ +package mekanism.common.attachments.containers.chemical.infuse; + +import java.util.List; +import java.util.function.LongSupplier; +import java.util.function.Predicate; +import mekanism.api.chemical.ChemicalTankBuilder; +import mekanism.api.chemical.infuse.InfuseType; +import mekanism.api.chemical.infuse.InfusionStack; +import mekanism.common.attachments.containers.chemical.ChemicalTanksBuilder; +import mekanism.common.attachments.containers.creator.BaseContainerCreator; +import mekanism.common.attachments.containers.creator.IBasicContainerCreator; +import mekanism.common.config.MekanismConfig; +import org.jetbrains.annotations.NotNull; + +public class InfusionTanksBuilder extends ChemicalTanksBuilder { + + public static InfusionTanksBuilder builder() { + return new InfusionTanksBuilder(); + } + + private InfusionTanksBuilder() { + } + + @Override + public BaseContainerCreator build() { + return new BaseInfusionTankBuilder(tankCreators); + } + + @Override + public InfusionTanksBuilder addBasic(LongSupplier capacity, Predicate<@NotNull InfuseType> isValid) { + return addTank((type, attachedTo, containerIndex) -> new ComponentBackedInfusionTank(attachedTo, containerIndex, ChemicalTankBuilder.INFUSION.manualOnly, + ChemicalTankBuilder.INFUSION.alwaysTrueBi, isValid, MekanismConfig.general.chemicalItemFillRate, capacity, null)); + } + + @Override + public InfusionTanksBuilder addBasic(LongSupplier capacity) { + return addTank((type, attachedTo, containerIndex) -> new ComponentBackedInfusionTank(attachedTo, containerIndex, ChemicalTankBuilder.INFUSION.manualOnly, + ChemicalTankBuilder.INFUSION.alwaysTrueBi, ChemicalTankBuilder.INFUSION.alwaysTrue, MekanismConfig.general.chemicalItemFillRate, capacity, null)); + } + + @Override + public InfusionTanksBuilder addInternalStorage(LongSupplier rate, LongSupplier capacity, Predicate<@NotNull InfuseType> isValid) { + return addTank((type, attachedTo, containerIndex) -> new ComponentBackedInfusionTank(attachedTo, containerIndex, ChemicalTankBuilder.INFUSION.notExternal, + ChemicalTankBuilder.INFUSION.alwaysTrueBi, isValid, rate, capacity, null)); + } + + private static class BaseInfusionTankBuilder extends BaseContainerCreator { + + public BaseInfusionTankBuilder(List> creators) { + super(creators); + } + + @Override + public AttachedInfuseTypes initStorage(int containers) { + return new AttachedInfuseTypes(containers); + } + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/merged/MergedTankCreator.java b/src/main/java/mekanism/common/attachments/containers/chemical/merged/MergedTankCreator.java new file mode 100644 index 00000000000..bcbfd3a1c87 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/merged/MergedTankCreator.java @@ -0,0 +1,84 @@ +package mekanism.common.attachments.containers.chemical.merged; + +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.chemical.merged.MergedChemicalTank; +import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.chemical.gas.ComponentBackedGasTank; +import mekanism.common.attachments.containers.chemical.infuse.ComponentBackedInfusionTank; +import mekanism.common.attachments.containers.chemical.pigment.ComponentBackedPigmentTank; +import mekanism.common.attachments.containers.chemical.slurry.ComponentBackedSlurryTank; +import mekanism.common.attachments.containers.creator.IBasicContainerCreator; +import mekanism.common.attachments.containers.fluid.ComponentBackedFluidTank; +import mekanism.common.capabilities.merged.MergedTank; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.common.util.INBTSerializable; +import org.jetbrains.annotations.Nullable; + +//TODO - 1.20.5: Re-evaluate/rethink this as using rawtypes to get around things like this is very cursed +@NothingNullByDefault +@SuppressWarnings({"rawtypes", "unchecked"}) +public class MergedTankCreator implements IBasicContainerCreator { + + private final IBasicContainerCreator gasCreator; + private final IBasicContainerCreator infusionCreator; + private final IBasicContainerCreator pigmentCreator; + private final IBasicContainerCreator slurryCreator; + @Nullable + private final IBasicContainerCreator fluidCreator; + + public MergedTankCreator(IBasicContainerCreator gasCreator, + IBasicContainerCreator infusionCreator, + IBasicContainerCreator pigmentCreator, + IBasicContainerCreator slurryCreator) { + this(gasCreator, infusionCreator, pigmentCreator, slurryCreator, null); + } + + public MergedTankCreator(IBasicContainerCreator gasCreator, + IBasicContainerCreator infusionCreator, + IBasicContainerCreator pigmentCreator, + IBasicContainerCreator slurryCreator, + @Nullable IBasicContainerCreator fluidCreator) { + this.gasCreator = gasCreator; + this.infusionCreator = infusionCreator; + this.pigmentCreator = pigmentCreator; + this.slurryCreator = slurryCreator; + this.fluidCreator = fluidCreator; + } + + private MergedChemicalTank createMergedTank(ContainerType containerType, ItemStack attachedTo, int containerIndex) { + if (fluidCreator == null) { + return MergedChemicalTank.create( + gasCreator.create(containerType, attachedTo, containerIndex), + infusionCreator.create(containerType, attachedTo, containerIndex), + pigmentCreator.create(containerType, attachedTo, containerIndex), + slurryCreator.create(containerType, attachedTo, containerIndex) + ); + } + return MergedTank.create( + fluidCreator.create(containerType, attachedTo, containerIndex), + gasCreator.create(containerType, attachedTo, containerIndex), + infusionCreator.create(containerType, attachedTo, containerIndex), + pigmentCreator.create(containerType, attachedTo, containerIndex), + slurryCreator.create(containerType, attachedTo, containerIndex) + ); + } + + @Override + public INBTSerializable create(ContainerType containerType, ItemStack attachedTo, int containerIndex) { + if (containerType == ContainerType.FLUID) { + if (fluidCreator != null) { + return ((MergedTank) createMergedTank(containerType, attachedTo, containerIndex)).getFluidTank(); + } + } else if (containerType == ContainerType.GAS) { + return createMergedTank(containerType, attachedTo, containerIndex).getGasTank(); + } else if (containerType == ContainerType.INFUSION) { + return createMergedTank(containerType, attachedTo, containerIndex).getInfusionTank(); + } else if (containerType == ContainerType.PIGMENT) { + return createMergedTank(containerType, attachedTo, containerIndex).getPigmentTank(); + } else if (containerType == ContainerType.SLURRY) { + return createMergedTank(containerType, attachedTo, containerIndex).getSlurryTank(); + } + throw new IllegalStateException("Unexpected container type " + containerType.getComponentName() + " for merged tank creation"); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/pigment/AttachedPigments.java b/src/main/java/mekanism/common/attachments/containers/chemical/pigment/AttachedPigments.java new file mode 100644 index 00000000000..9353d682644 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/pigment/AttachedPigments.java @@ -0,0 +1,39 @@ +package mekanism.common.attachments.containers.chemical.pigment; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.Collections; +import java.util.List; +import mekanism.api.NBTConstants; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.chemical.pigment.PigmentStack; +import mekanism.common.attachments.containers.IAttachedContainers; +import net.minecraft.core.NonNullList; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; + +@NothingNullByDefault +public record AttachedPigments(List containers) implements IAttachedContainers { + + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + PigmentStack.OPTIONAL_CODEC.listOf().fieldOf(NBTConstants.PIGMENT_TANKS).forGetter(AttachedPigments::containers) + ).apply(instance, AttachedPigments::new)); + public static final StreamCodec STREAM_CODEC = + PigmentStack.OPTIONAL_STREAM_CODEC.>apply(ByteBufCodecs.collection(NonNullList::createWithCapacity)) + .map(AttachedPigments::new, AttachedPigments::containers); + + public AttachedPigments { + //Make the list unmodifiable to ensure we don't accidentally mutate it + containers = Collections.unmodifiableList(containers); + } + + public AttachedPigments(int containers) { + this(NonNullList.withSize(containers, PigmentStack.EMPTY)); + } + + @Override + public AttachedPigments create(List containers) { + return new AttachedPigments(containers); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/pigment/ComponentBackedChemicalTankPigmentTank.java b/src/main/java/mekanism/common/attachments/containers/chemical/pigment/ComponentBackedChemicalTankPigmentTank.java new file mode 100644 index 00000000000..93564ebed72 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/pigment/ComponentBackedChemicalTankPigmentTank.java @@ -0,0 +1,52 @@ +package mekanism.common.attachments.containers.chemical.pigment; + +import mekanism.api.Action; +import mekanism.api.AutomationType; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.chemical.ChemicalTankBuilder; +import mekanism.api.chemical.attribute.ChemicalAttributeValidator; +import mekanism.api.chemical.pigment.PigmentStack; +import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.item.block.ItemBlockChemicalTank; +import mekanism.common.tier.ChemicalTankTier; +import net.minecraft.world.item.ItemStack; + +@NothingNullByDefault +public class ComponentBackedChemicalTankPigmentTank extends ComponentBackedPigmentTank { + + private final boolean isCreative; + + public static ComponentBackedChemicalTankPigmentTank create(ContainerType ignored, ItemStack attachedTo, int tankIndex) { + if (!(attachedTo.getItem() instanceof ItemBlockChemicalTank item)) { + throw new IllegalStateException("Attached to should always be a chemical tank item"); + } + return new ComponentBackedChemicalTankPigmentTank(attachedTo, tankIndex, item.getTier()); + } + + private ComponentBackedChemicalTankPigmentTank(ItemStack attachedTo, int tankIndex, ChemicalTankTier tier) { + super(attachedTo, tankIndex, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, ChemicalTankBuilder.PIGMENT.alwaysTrue, + tier::getOutput, tier::getStorage, tier == ChemicalTankTier.CREATIVE ? ChemicalAttributeValidator.ALWAYS_ALLOW : null); + isCreative = tier == ChemicalTankTier.CREATIVE; + } + + @Override + public PigmentStack insert(PigmentStack stack, Action action, AutomationType automationType) { + return super.insert(stack, action.combine(!isCreative), automationType); + } + + @Override + public PigmentStack extract(AttachedPigments attachedPigments, PigmentStack stored, long amount, Action action, AutomationType automationType) { + return super.extract(attachedPigments, stored, amount, action.combine(!isCreative), automationType); + } + + /** + * {@inheritDoc} + * + * Note: We are only patching {@link #setStackSize(AttachedPigments, PigmentStack, long, Action)}, as both {@link #growStack(long, Action)} and + * {@link #shrinkStack(long, Action)} are wrapped through this method. + */ + @Override + public long setStackSize(AttachedPigments attachedPigments, PigmentStack stored, long amount, Action action) { + return super.setStackSize(attachedPigments, stored, amount, action.combine(!isCreative)); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/pigment/ComponentBackedPigmentHandler.java b/src/main/java/mekanism/common/attachments/containers/chemical/pigment/ComponentBackedPigmentHandler.java new file mode 100644 index 00000000000..f5419fb9863 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/pigment/ComponentBackedPigmentHandler.java @@ -0,0 +1,23 @@ +package mekanism.common.attachments.containers.chemical.pigment; + +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.chemical.pigment.IPigmentHandler.IMekanismPigmentHandler; +import mekanism.api.chemical.pigment.IPigmentTank; +import mekanism.api.chemical.pigment.Pigment; +import mekanism.api.chemical.pigment.PigmentStack; +import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.chemical.ComponentBackedChemicalHandler; +import net.minecraft.world.item.ItemStack; + +@NothingNullByDefault +public class ComponentBackedPigmentHandler extends ComponentBackedChemicalHandler implements IMekanismPigmentHandler { + + public ComponentBackedPigmentHandler(ItemStack attachedTo) { + super(attachedTo); + } + + @Override + protected ContainerType containerType() { + return ContainerType.PIGMENT; + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/pigment/ComponentBackedPigmentTank.java b/src/main/java/mekanism/common/attachments/containers/chemical/pigment/ComponentBackedPigmentTank.java new file mode 100644 index 00000000000..d602d349645 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/pigment/ComponentBackedPigmentTank.java @@ -0,0 +1,31 @@ +package mekanism.common.attachments.containers.chemical.pigment; + +import java.util.function.BiPredicate; +import java.util.function.LongSupplier; +import java.util.function.Predicate; +import java.util.function.Supplier; +import mekanism.api.AutomationType; +import mekanism.api.chemical.attribute.ChemicalAttributeValidator; +import mekanism.api.chemical.pigment.IPigmentTank; +import mekanism.api.chemical.pigment.Pigment; +import mekanism.api.chemical.pigment.PigmentStack; +import mekanism.common.attachments.containers.chemical.ComponentBackedChemicalTank; +import mekanism.common.registries.MekanismDataComponents; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ComponentBackedPigmentTank extends ComponentBackedChemicalTank implements IPigmentTank { + + public ComponentBackedPigmentTank(ItemStack attachedTo, int tankIndex, BiPredicate<@NotNull Pigment, @NotNull AutomationType> canExtract, + BiPredicate<@NotNull Pigment, @NotNull AutomationType> canInsert, Predicate<@NotNull Pigment> validator, LongSupplier rate, LongSupplier capacity, + @Nullable ChemicalAttributeValidator attributeValidator) { + super(attachedTo, tankIndex, canExtract, canInsert, validator, rate, capacity, attributeValidator); + } + + @Override + protected Supplier> dataComponentType() { + return MekanismDataComponents.ATTACHED_PIGMENTS; + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/pigment/PigmentTanksBuilder.java b/src/main/java/mekanism/common/attachments/containers/chemical/pigment/PigmentTanksBuilder.java new file mode 100644 index 00000000000..5293a219297 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/pigment/PigmentTanksBuilder.java @@ -0,0 +1,58 @@ +package mekanism.common.attachments.containers.chemical.pigment; + +import java.util.List; +import java.util.function.LongSupplier; +import java.util.function.Predicate; +import mekanism.api.chemical.ChemicalTankBuilder; +import mekanism.api.chemical.pigment.Pigment; +import mekanism.api.chemical.pigment.PigmentStack; +import mekanism.common.attachments.containers.chemical.ChemicalTanksBuilder; +import mekanism.common.attachments.containers.creator.BaseContainerCreator; +import mekanism.common.attachments.containers.creator.IBasicContainerCreator; +import mekanism.common.config.MekanismConfig; +import org.jetbrains.annotations.NotNull; + +public class PigmentTanksBuilder extends ChemicalTanksBuilder { + + public static PigmentTanksBuilder builder() { + return new PigmentTanksBuilder(); + } + + private PigmentTanksBuilder() { + } + + @Override + public BaseContainerCreator build() { + return new BasePigmentTankBuilder(tankCreators); + } + + @Override + public PigmentTanksBuilder addBasic(LongSupplier capacity, Predicate<@NotNull Pigment> isValid) { + return addTank((type, attachedTo, containerIndex) -> new ComponentBackedPigmentTank(attachedTo, containerIndex, ChemicalTankBuilder.PIGMENT.manualOnly, + ChemicalTankBuilder.PIGMENT.alwaysTrueBi, isValid, MekanismConfig.general.chemicalItemFillRate, capacity, null)); + } + + @Override + public PigmentTanksBuilder addBasic(LongSupplier capacity) { + return addTank((type, attachedTo, containerIndex) -> new ComponentBackedPigmentTank(attachedTo, containerIndex, ChemicalTankBuilder.PIGMENT.manualOnly, + ChemicalTankBuilder.PIGMENT.alwaysTrueBi, ChemicalTankBuilder.PIGMENT.alwaysTrue, MekanismConfig.general.chemicalItemFillRate, capacity, null)); + } + + @Override + public PigmentTanksBuilder addInternalStorage(LongSupplier rate, LongSupplier capacity, Predicate<@NotNull Pigment> isValid) { + return addTank((type, attachedTo, containerIndex) -> new ComponentBackedPigmentTank(attachedTo, containerIndex, ChemicalTankBuilder.PIGMENT.notExternal, + ChemicalTankBuilder.PIGMENT.alwaysTrueBi, isValid, rate, capacity, null)); + } + + private static class BasePigmentTankBuilder extends BaseContainerCreator { + + public BasePigmentTankBuilder(List> creators) { + super(creators); + } + + @Override + public AttachedPigments initStorage(int containers) { + return new AttachedPigments(containers); + } + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/slurry/AttachedSlurries.java b/src/main/java/mekanism/common/attachments/containers/chemical/slurry/AttachedSlurries.java new file mode 100644 index 00000000000..0dec42b4690 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/slurry/AttachedSlurries.java @@ -0,0 +1,39 @@ +package mekanism.common.attachments.containers.chemical.slurry; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.Collections; +import java.util.List; +import mekanism.api.NBTConstants; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.chemical.slurry.SlurryStack; +import mekanism.common.attachments.containers.IAttachedContainers; +import net.minecraft.core.NonNullList; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; + +@NothingNullByDefault +public record AttachedSlurries(List containers) implements IAttachedContainers { + + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + SlurryStack.OPTIONAL_CODEC.listOf().fieldOf(NBTConstants.SLURRY_TANKS).forGetter(AttachedSlurries::containers) + ).apply(instance, AttachedSlurries::new)); + public static final StreamCodec STREAM_CODEC = + SlurryStack.OPTIONAL_STREAM_CODEC.>apply(ByteBufCodecs.collection(NonNullList::createWithCapacity)) + .map(AttachedSlurries::new, AttachedSlurries::containers); + + public AttachedSlurries { + //Make the list unmodifiable to ensure we don't accidentally mutate it + containers = Collections.unmodifiableList(containers); + } + + public AttachedSlurries(int containers) { + this(NonNullList.withSize(containers, SlurryStack.EMPTY)); + } + + @Override + public AttachedSlurries create(List containers) { + return new AttachedSlurries(containers); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/slurry/ComponentBackedChemicalTankSlurryTank.java b/src/main/java/mekanism/common/attachments/containers/chemical/slurry/ComponentBackedChemicalTankSlurryTank.java new file mode 100644 index 00000000000..645c8bec1d7 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/slurry/ComponentBackedChemicalTankSlurryTank.java @@ -0,0 +1,52 @@ +package mekanism.common.attachments.containers.chemical.slurry; + +import mekanism.api.Action; +import mekanism.api.AutomationType; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.chemical.ChemicalTankBuilder; +import mekanism.api.chemical.attribute.ChemicalAttributeValidator; +import mekanism.api.chemical.slurry.SlurryStack; +import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.item.block.ItemBlockChemicalTank; +import mekanism.common.tier.ChemicalTankTier; +import net.minecraft.world.item.ItemStack; + +@NothingNullByDefault +public class ComponentBackedChemicalTankSlurryTank extends ComponentBackedSlurryTank { + + private final boolean isCreative; + + public static ComponentBackedChemicalTankSlurryTank create(ContainerType ignored, ItemStack attachedTo, int tankIndex) { + if (!(attachedTo.getItem() instanceof ItemBlockChemicalTank item)) { + throw new IllegalStateException("Attached to should always be a chemical tank item"); + } + return new ComponentBackedChemicalTankSlurryTank(attachedTo, tankIndex, item.getTier()); + } + + private ComponentBackedChemicalTankSlurryTank(ItemStack attachedTo, int tankIndex, ChemicalTankTier tier) { + super(attachedTo, tankIndex, ChemicalTankBuilder.SLURRY.alwaysTrueBi, ChemicalTankBuilder.SLURRY.alwaysTrueBi, ChemicalTankBuilder.SLURRY.alwaysTrue, + tier::getOutput, tier::getStorage, tier == ChemicalTankTier.CREATIVE ? ChemicalAttributeValidator.ALWAYS_ALLOW : null); + isCreative = tier == ChemicalTankTier.CREATIVE; + } + + @Override + public SlurryStack insert(SlurryStack stack, Action action, AutomationType automationType) { + return super.insert(stack, action.combine(!isCreative), automationType); + } + + @Override + public SlurryStack extract(AttachedSlurries attachedSlurries, SlurryStack stored, long amount, Action action, AutomationType automationType) { + return super.extract(attachedSlurries, stored, amount, action.combine(!isCreative), automationType); + } + + /** + * {@inheritDoc} + * + * Note: We are only patching {@link #setStackSize(AttachedSlurries, SlurryStack, long, Action)}, as both {@link #growStack(long, Action)} and + * {@link #shrinkStack(long, Action)} are wrapped through this method. + */ + @Override + public long setStackSize(AttachedSlurries attachedSlurries, SlurryStack stored, long amount, Action action) { + return super.setStackSize(attachedSlurries, stored, amount, action.combine(!isCreative)); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/slurry/ComponentBackedSlurryHandler.java b/src/main/java/mekanism/common/attachments/containers/chemical/slurry/ComponentBackedSlurryHandler.java new file mode 100644 index 00000000000..76124ce7394 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/slurry/ComponentBackedSlurryHandler.java @@ -0,0 +1,23 @@ +package mekanism.common.attachments.containers.chemical.slurry; + +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.chemical.slurry.ISlurryHandler.IMekanismSlurryHandler; +import mekanism.api.chemical.slurry.ISlurryTank; +import mekanism.api.chemical.slurry.Slurry; +import mekanism.api.chemical.slurry.SlurryStack; +import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.chemical.ComponentBackedChemicalHandler; +import net.minecraft.world.item.ItemStack; + +@NothingNullByDefault +public class ComponentBackedSlurryHandler extends ComponentBackedChemicalHandler implements IMekanismSlurryHandler { + + public ComponentBackedSlurryHandler(ItemStack attachedTo) { + super(attachedTo); + } + + @Override + protected ContainerType containerType() { + return ContainerType.SLURRY; + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/slurry/ComponentBackedSlurryTank.java b/src/main/java/mekanism/common/attachments/containers/chemical/slurry/ComponentBackedSlurryTank.java new file mode 100644 index 00000000000..52521ea3696 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/slurry/ComponentBackedSlurryTank.java @@ -0,0 +1,31 @@ +package mekanism.common.attachments.containers.chemical.slurry; + +import java.util.function.BiPredicate; +import java.util.function.LongSupplier; +import java.util.function.Predicate; +import java.util.function.Supplier; +import mekanism.api.AutomationType; +import mekanism.api.chemical.attribute.ChemicalAttributeValidator; +import mekanism.api.chemical.slurry.ISlurryTank; +import mekanism.api.chemical.slurry.Slurry; +import mekanism.api.chemical.slurry.SlurryStack; +import mekanism.common.attachments.containers.chemical.ComponentBackedChemicalTank; +import mekanism.common.registries.MekanismDataComponents; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ComponentBackedSlurryTank extends ComponentBackedChemicalTank implements ISlurryTank { + + public ComponentBackedSlurryTank(ItemStack attachedTo, int tankIndex, BiPredicate<@NotNull Slurry, @NotNull AutomationType> canExtract, + BiPredicate<@NotNull Slurry, @NotNull AutomationType> canInsert, Predicate<@NotNull Slurry> validator, LongSupplier rate, LongSupplier capacity, + @Nullable ChemicalAttributeValidator attributeValidator) { + super(attachedTo, tankIndex, canExtract, canInsert, validator, rate, capacity, attributeValidator); + } + + @Override + protected Supplier> dataComponentType() { + return MekanismDataComponents.ATTACHED_SLURRIES; + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/chemical/slurry/SlurryTanksBuilder.java b/src/main/java/mekanism/common/attachments/containers/chemical/slurry/SlurryTanksBuilder.java new file mode 100644 index 00000000000..1c4f70a7bff --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/chemical/slurry/SlurryTanksBuilder.java @@ -0,0 +1,58 @@ +package mekanism.common.attachments.containers.chemical.slurry; + +import java.util.List; +import java.util.function.LongSupplier; +import java.util.function.Predicate; +import mekanism.api.chemical.ChemicalTankBuilder; +import mekanism.api.chemical.slurry.Slurry; +import mekanism.api.chemical.slurry.SlurryStack; +import mekanism.common.attachments.containers.chemical.ChemicalTanksBuilder; +import mekanism.common.attachments.containers.creator.BaseContainerCreator; +import mekanism.common.attachments.containers.creator.IBasicContainerCreator; +import mekanism.common.config.MekanismConfig; +import org.jetbrains.annotations.NotNull; + +public class SlurryTanksBuilder extends ChemicalTanksBuilder { + + public static SlurryTanksBuilder builder() { + return new SlurryTanksBuilder(); + } + + private SlurryTanksBuilder() { + } + + @Override + public BaseContainerCreator build() { + return new BaseSlurryTankBuilder(tankCreators); + } + + @Override + public SlurryTanksBuilder addBasic(LongSupplier capacity, Predicate<@NotNull Slurry> isValid) { + return addTank((type, attachedTo, containerIndex) -> new ComponentBackedSlurryTank(attachedTo, containerIndex, ChemicalTankBuilder.SLURRY.manualOnly, + ChemicalTankBuilder.SLURRY.alwaysTrueBi, isValid, MekanismConfig.general.chemicalItemFillRate, capacity, null)); + } + + @Override + public SlurryTanksBuilder addBasic(LongSupplier capacity) { + return addTank((type, attachedTo, containerIndex) -> new ComponentBackedSlurryTank(attachedTo, containerIndex, ChemicalTankBuilder.SLURRY.manualOnly, + ChemicalTankBuilder.SLURRY.alwaysTrueBi, ChemicalTankBuilder.SLURRY.alwaysTrue, MekanismConfig.general.chemicalItemFillRate, capacity, null)); + } + + @Override + public SlurryTanksBuilder addInternalStorage(LongSupplier rate, LongSupplier capacity, Predicate<@NotNull Slurry> isValid) { + return addTank((type, attachedTo, containerIndex) -> new ComponentBackedSlurryTank(attachedTo, containerIndex, ChemicalTankBuilder.SLURRY.notExternal, + ChemicalTankBuilder.SLURRY.alwaysTrueBi, isValid, rate, capacity, null)); + } + + private static class BaseSlurryTankBuilder extends BaseContainerCreator { + + public BaseSlurryTankBuilder(List> creators) { + super(creators); + } + + @Override + public AttachedSlurries initStorage(int containers) { + return new AttachedSlurries(containers); + } + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/creator/BaseContainerCreator.java b/src/main/java/mekanism/common/attachments/containers/creator/BaseContainerCreator.java new file mode 100644 index 00000000000..06004c67083 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/creator/BaseContainerCreator.java @@ -0,0 +1,34 @@ +package mekanism.common.attachments.containers.creator; + +import java.util.List; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.IAttachedContainers; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.common.util.INBTSerializable; + +@NothingNullByDefault +public abstract class BaseContainerCreator, CONTAINER extends INBTSerializable> implements IContainerCreator { + + private final List> creators; + + public BaseContainerCreator(List> creators) { + //TODO - 1.20.5: Is this copy necessary? We probably want it to be immutable so yes? + this.creators = List.copyOf(creators); + } + + @Override + public int totalContainers() { + return creators.size(); + } + + @Override + public CONTAINER create(ContainerType containerType, ItemStack attachedTo, int containerIndex) { + //TODO - 1.20.5: Figure out how to handle this and if we want to validate the index + /*if (containerIndex < 0 || containerIndex >= creators.size()) { + return null; + }*/ + return creators.get(containerIndex).create(containerType, attachedTo, containerIndex); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/creator/IBasicContainerCreator.java b/src/main/java/mekanism/common/attachments/containers/creator/IBasicContainerCreator.java new file mode 100644 index 00000000000..4b3eeb89b99 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/creator/IBasicContainerCreator.java @@ -0,0 +1,12 @@ +package mekanism.common.attachments.containers.creator; + +import mekanism.common.attachments.containers.ContainerType; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.common.util.INBTSerializable; + +@FunctionalInterface +public interface IBasicContainerCreator> { + + CONTAINER create(ContainerType containerType, ItemStack attachedTo, int containerIndex); +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/creator/IContainerCreator.java b/src/main/java/mekanism/common/attachments/containers/creator/IContainerCreator.java new file mode 100644 index 00000000000..0d38865b54a --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/creator/IContainerCreator.java @@ -0,0 +1,14 @@ +package mekanism.common.attachments.containers.creator; + +import mekanism.common.attachments.containers.IAttachedContainers; +import net.minecraft.nbt.CompoundTag; +import net.neoforged.neoforge.common.util.INBTSerializable; + +//TODO - 1.20.5: Do we want to require the nbt serializable bound on the container? +//TODO - 1.20.5: Better generic names once we figure out the methods and stuff we need +public interface IContainerCreator, ATTACHED extends IAttachedContainers> extends IBasicContainerCreator { + + int totalContainers(); + + ATTACHED initStorage(int containers); +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/energy/AttachedEnergy.java b/src/main/java/mekanism/common/attachments/containers/energy/AttachedEnergy.java new file mode 100644 index 00000000000..5b6557489d0 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/energy/AttachedEnergy.java @@ -0,0 +1,40 @@ + +package mekanism.common.attachments.containers.energy; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import io.netty.buffer.ByteBuf; +import java.util.Collections; +import java.util.List; +import mekanism.api.NBTConstants; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.math.FloatingLong; +import mekanism.common.attachments.containers.IAttachedContainers; +import net.minecraft.core.NonNullList; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; + +@NothingNullByDefault +public record AttachedEnergy(List containers) implements IAttachedContainers { + + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + FloatingLong.CODEC.listOf().fieldOf(NBTConstants.ENERGY_CONTAINERS).forGetter(AttachedEnergy::containers) + ).apply(instance, AttachedEnergy::new)); + public static final StreamCodec STREAM_CODEC = + FloatingLong.STREAM_CODEC.>apply(ByteBufCodecs.collection(NonNullList::createWithCapacity)) + .map(AttachedEnergy::new, AttachedEnergy::containers); + + public AttachedEnergy { + //Make the list unmodifiable to ensure we don't accidentally mutate it + containers = Collections.unmodifiableList(containers); + } + + public AttachedEnergy(int containers) { + this(NonNullList.withSize(containers, FloatingLong.ZERO)); + } + + @Override + public AttachedEnergy create(List containers) { + return new AttachedEnergy(containers); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedEnergyContainer.java b/src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedEnergyContainer.java new file mode 100644 index 00000000000..d28bb6eb935 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedEnergyContainer.java @@ -0,0 +1,150 @@ +package mekanism.common.attachments.containers.energy; + +import java.util.function.Predicate; +import java.util.function.Supplier; +import mekanism.api.Action; +import mekanism.api.AutomationType; +import mekanism.api.NBTConstants; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.energy.IEnergyContainer; +import mekanism.api.math.FloatingLong; +import mekanism.api.math.FloatingLongSupplier; +import mekanism.common.attachments.containers.ComponentBackedContainer; +import mekanism.common.registries.MekanismDataComponents; +import mekanism.common.util.NBTUtils; +import net.minecraft.core.HolderLookup.Provider; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@NothingNullByDefault +public class ComponentBackedEnergyContainer extends ComponentBackedContainer implements IEnergyContainer { + + private final Predicate<@NotNull AutomationType> canExtract; + private final Predicate<@NotNull AutomationType> canInsert; + private final FloatingLongSupplier maxEnergy; + private final FloatingLongSupplier rate; + + public ComponentBackedEnergyContainer(ItemStack attachedTo, int containerIndex, Predicate<@NotNull AutomationType> canExtract, + Predicate<@NotNull AutomationType> canInsert, FloatingLongSupplier rate, FloatingLongSupplier maxEnergy) { + super(attachedTo, containerIndex); + this.canExtract = canExtract; + this.canInsert = canInsert; + this.maxEnergy = maxEnergy; + this.rate = rate; + } + + @Override + protected Supplier> dataComponentType() { + return MekanismDataComponents.ATTACHED_ENERGY; + } + + @Override + protected FloatingLong copy(FloatingLong toCopy) { + return toCopy.copyAsConst(); + } + + @Override + protected boolean isEmpty(FloatingLong value) { + return value.isZero(); + } + + @Override + public FloatingLong getEnergy() { + //TODO - 1.20.5: Similar to getBasicInventorySlot do we want to reduce calls to this? Probably (We mostly do so, but we probably want to add a note here) + AttachedEnergy attachedEnergy = getAttached(); + return attachedEnergy == null ? FloatingLong.ZERO : getContents(attachedEnergy); + } + + @Override + public void setEnergy(FloatingLong energy) { + setContents(energy); + } + + protected FloatingLong clampEnergy(FloatingLong energy) { + return energy.min(getMaxEnergy()); + } + + @Override + protected void setContents(AttachedEnergy attachedEnergy, FloatingLong energy) { + super.setContents(attachedEnergy, clampEnergy(energy)); + } + + protected FloatingLong getRate(@Nullable AutomationType automationType) { + //Allow unknown or manual interaction to bypass rate limit for the item + return automationType == null || automationType == AutomationType.MANUAL ? FloatingLong.MAX_VALUE : rate.get(); + } + + @Override + public FloatingLong insert(FloatingLong amount, Action action, AutomationType automationType) { + if (amount.isZero() || !canInsert.test(automationType)) { + return amount; + } + AttachedEnergy attachedEnergy = getAttached(); + if (attachedEnergy == null) { + return amount; + } + FloatingLong stored = getContents(attachedEnergy); + FloatingLong needed = getRate(automationType).min(getNeeded(stored)); + if (needed.isZero()) { + //Fail if we are a full container or our rate is zero + return amount; + } + FloatingLong toAdd = amount.min(needed); + if (!toAdd.isZero() && action.execute()) { + //If we want to actually insert the energy, then update the current energy + // Note: this also will mark that the contents changed + setContents(attachedEnergy, stored.add(toAdd)); + } + return amount.subtract(toAdd); + } + + @Override + public FloatingLong extract(FloatingLong amount, Action action, AutomationType automationType) { + if (amount.isZero()) { + return FloatingLong.ZERO; + } + AttachedEnergy attachedEnergy = getAttached(); + if (attachedEnergy == null) { + return FloatingLong.ZERO; + } + FloatingLong stored = getContents(attachedEnergy); + if (stored.isZero() || !canExtract.test(automationType)) { + return FloatingLong.ZERO; + } + FloatingLong ret = getRate(automationType).min(stored).min(amount).copy(); + if (!ret.isZero() && action.execute()) { + //Note: this also will mark that the contents changed + setContents(attachedEnergy, stored.subtract(ret)); + } + return ret; + } + + protected FloatingLong getNeeded(FloatingLong stored) { + return getMaxEnergy().subtract(stored); + } + + @Override + public FloatingLong getMaxEnergy() { + return maxEnergy.get(); + } + + @Override + public CompoundTag serializeNBT(Provider provider) { + //TODO - 1.20.5: This is a copy of BasicEnergyContainer#serializeNBT. We might need to also grab the specific overrides of + // that method as special component backed inventory slots, that then access and put that other data as a different component? + CompoundTag nbt = new CompoundTag(); + FloatingLong stored = getEnergy(); + if (!stored.isZero()) { + nbt.putString(NBTConstants.STORED, stored.toString()); + } + return nbt; + } + + @Override + public void deserializeNBT(Provider provider, CompoundTag nbt) { + NBTUtils.setFloatingLongIfPresent(nbt, NBTConstants.STORED, this::setEnergy); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedEnergyCubeContainer.java b/src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedEnergyCubeContainer.java new file mode 100644 index 00000000000..ff1790e18b7 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedEnergyCubeContainer.java @@ -0,0 +1,39 @@ +package mekanism.common.attachments.containers.energy; + +import mekanism.api.Action; +import mekanism.api.AutomationType; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.math.FloatingLong; +import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.capabilities.energy.BasicEnergyContainer; +import mekanism.common.item.block.ItemBlockEnergyCube; +import mekanism.common.tier.EnergyCubeTier; +import net.minecraft.world.item.ItemStack; + +@NothingNullByDefault +public class ComponentBackedEnergyCubeContainer extends ComponentBackedEnergyContainer { + + public static ComponentBackedEnergyCubeContainer create(ContainerType ignored, ItemStack attachedTo, int containerIndex) { + if (!(attachedTo.getItem() instanceof ItemBlockEnergyCube item)) { + throw new IllegalStateException("Attached to should always be an energy cube item"); + } + return new ComponentBackedEnergyCubeContainer(attachedTo, containerIndex, item.getTier()); + } + + private final boolean isCreative; + + private ComponentBackedEnergyCubeContainer(ItemStack attachedTo, int containerIndex, EnergyCubeTier tier) { + super(attachedTo, containerIndex, BasicEnergyContainer.alwaysTrue, BasicEnergyContainer.alwaysTrue, tier::getOutput, tier::getMaxEnergy); + isCreative = tier == EnergyCubeTier.CREATIVE; + } + + @Override + public FloatingLong insert(FloatingLong amount, Action action, AutomationType automationType) { + return super.insert(amount, action.combine(!isCreative), automationType); + } + + @Override + public FloatingLong extract(FloatingLong amount, Action action, AutomationType automationType) { + return super.extract(amount, action.combine(!isCreative), automationType); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedEnergyHandler.java b/src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedEnergyHandler.java new file mode 100644 index 00000000000..e7db553d45c --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedEnergyHandler.java @@ -0,0 +1,62 @@ +package mekanism.common.attachments.containers.energy; + +import java.util.List; +import mekanism.api.Action; +import mekanism.api.AutomationType; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.energy.IEnergyContainer; +import mekanism.api.energy.IMekanismStrictEnergyHandler; +import mekanism.api.math.FloatingLong; +import mekanism.api.math.FloatingLongTransferUtils; +import mekanism.common.attachments.containers.ComponentBackedHandler; +import mekanism.common.attachments.containers.ContainerType; +import net.minecraft.core.Direction; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +@NothingNullByDefault +public class ComponentBackedEnergyHandler extends ComponentBackedHandler implements IMekanismStrictEnergyHandler { + + public ComponentBackedEnergyHandler(ItemStack attachedTo) { + super(attachedTo); + } + + @Override + protected ContainerType containerType() { + return ContainerType.ENERGY; + } + + @Override + public List getEnergyContainers(@Nullable Direction side) { + return getContainers(); + } + + @Nullable + @Override + public IEnergyContainer getEnergyContainer(int container, @Nullable Direction side) { + return getContainer(container); + } + + @Override + public int getEnergyContainerCount(@Nullable Direction side) { + return containerCount(); + } + + @Override + public FloatingLong getEnergy(int container, @Nullable Direction side) { + AttachedEnergy attachedEnergy = getAttached(); + return attachedEnergy == null ? FloatingLong.ZERO : attachedEnergy.get(container); + } + + @Override + public FloatingLong insertEnergy(FloatingLong amount, @Nullable Direction side, Action action) { + //TODO - 1.20.5: Can we optimize this any further? Maybe by somehow only initializing the energy containers as necessary/we actually get to iterating against them? + return FloatingLongTransferUtils.insert(amount, side, this::getEnergyContainers, action, AutomationType.handler(side)); + } + + @Override + public FloatingLong extractEnergy(FloatingLong amount, @Nullable Direction side, Action action) { + //TODO - 1.20.5: Can we optimize this any further? Maybe by somehow only initializing the energy containers as necessary/we actually get to iterating against them? + return FloatingLongTransferUtils.extract(amount, side, this::getEnergyContainers, action, AutomationType.handler(side)); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedNoClampEnergyContainer.java b/src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedNoClampEnergyContainer.java new file mode 100644 index 00000000000..5694a31acf4 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedNoClampEnergyContainer.java @@ -0,0 +1,24 @@ +package mekanism.common.attachments.containers.energy; + +import java.util.function.Predicate; +import mekanism.api.AutomationType; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.math.FloatingLong; +import mekanism.api.math.FloatingLongSupplier; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +@NothingNullByDefault +public class ComponentBackedNoClampEnergyContainer extends ComponentBackedEnergyContainer { + + public ComponentBackedNoClampEnergyContainer(ItemStack attachedTo, int containerIndex, Predicate<@NotNull AutomationType> canExtract, + Predicate<@NotNull AutomationType> canInsert, FloatingLongSupplier rate, FloatingLongSupplier maxEnergy) { + super(attachedTo, containerIndex, canExtract, canInsert, rate, maxEnergy); + } + + @Override + protected FloatingLong clampEnergy(FloatingLong energy) { + //Don't clamp the energy + return energy; + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/capabilities/energy/item/ResistiveHeaterItemEnergyContainer.java b/src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedResistiveEnergyContainer.java similarity index 58% rename from src/main/java/mekanism/common/capabilities/energy/item/ResistiveHeaterItemEnergyContainer.java rename to src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedResistiveEnergyContainer.java index e3d9eeb54f2..a249de271ff 100644 --- a/src/main/java/mekanism/common/capabilities/energy/item/ResistiveHeaterItemEnergyContainer.java +++ b/src/main/java/mekanism/common/attachments/containers/energy/ComponentBackedResistiveEnergyContainer.java @@ -1,4 +1,4 @@ -package mekanism.common.capabilities.energy.item; +package mekanism.common.attachments.containers.energy; import java.util.Objects; import mekanism.api.NBTConstants; @@ -6,26 +6,31 @@ import mekanism.api.energy.IEnergyContainer; import mekanism.api.math.FloatingLong; import mekanism.api.math.FloatingLongSupplier; +import mekanism.common.attachments.containers.ContainerType; import mekanism.common.block.attribute.AttributeEnergy; import mekanism.common.capabilities.energy.BasicEnergyContainer; import mekanism.common.capabilities.energy.ResistiveHeaterEnergyContainer; +import mekanism.common.registries.MekanismBlockTypes; import mekanism.common.util.NBTUtils; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; -@NothingNullByDefault -public class ResistiveHeaterItemEnergyContainer extends RateLimitEnergyContainer { +@NothingNullByDefault//TODO - 1.20.5: Figure out how to implement this properly?? +public class ComponentBackedResistiveEnergyContainer extends ComponentBackedEnergyContainer { - public static ResistiveHeaterItemEnergyContainer create(AttributeEnergy attributeEnergy) { - Objects.requireNonNull(attributeEnergy); - return new ResistiveHeaterItemEnergyContainer(() -> attributeEnergy.getStorage().multiply(0.005), attributeEnergy::getStorage, attributeEnergy.getUsage()); + public static ComponentBackedResistiveEnergyContainer create(ContainerType ignored, ItemStack attachedTo, int containerIndex) { + AttributeEnergy attributeEnergy = Objects.requireNonNull(MekanismBlockTypes.RESISTIVE_HEATER.get(AttributeEnergy.class)); + return new ComponentBackedResistiveEnergyContainer(attachedTo, containerIndex, () -> attributeEnergy.getStorage().multiply(0.005), + attributeEnergy::getStorage, attributeEnergy.getUsage()); } private FloatingLong currentMaxEnergy; private FloatingLong energyPerTick; - private ResistiveHeaterItemEnergyContainer(FloatingLongSupplier rate, FloatingLongSupplier capacity, FloatingLong baseEnergyPerTick) { - super(rate, capacity, BasicEnergyContainer.manualOnly, BasicEnergyContainer.alwaysTrue, null); + private ComponentBackedResistiveEnergyContainer(ItemStack attachedTo, int containerIndex, FloatingLongSupplier rate, FloatingLongSupplier capacity, + FloatingLong baseEnergyPerTick) { + super(attachedTo, containerIndex, BasicEnergyContainer.manualOnly, BasicEnergyContainer.alwaysTrue, rate, capacity); this.currentMaxEnergy = super.getMaxEnergy(); this.energyPerTick = baseEnergyPerTick.copyAsConst(); } @@ -51,7 +56,7 @@ public CompoundTag serializeNBT(HolderLookup.Provider provider) { @Override public boolean isCompatible(IEnergyContainer other) { - return super.isCompatible(other) && energyPerTick.equals(((ResistiveHeaterItemEnergyContainer) other).energyPerTick); + return super.isCompatible(other) && energyPerTick.equals(((ComponentBackedResistiveEnergyContainer) other).energyPerTick); } @Override diff --git a/src/main/java/mekanism/common/attachments/containers/energy/EnergyContainersBuilder.java b/src/main/java/mekanism/common/attachments/containers/energy/EnergyContainersBuilder.java new file mode 100644 index 00000000000..1e223e949f7 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/energy/EnergyContainersBuilder.java @@ -0,0 +1,54 @@ +package mekanism.common.attachments.containers.energy; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import mekanism.api.AutomationType; +import mekanism.api.math.FloatingLongSupplier; +import mekanism.common.attachments.containers.creator.BaseContainerCreator; +import mekanism.common.attachments.containers.creator.IBasicContainerCreator; +import mekanism.common.capabilities.energy.BasicEnergyContainer; +import org.jetbrains.annotations.NotNull; + +public class EnergyContainersBuilder { + + public static EnergyContainersBuilder builder() { + return new EnergyContainersBuilder(); + } + + private final List> containerCreators = new ArrayList<>(); + + private EnergyContainersBuilder() { + } + + public BaseContainerCreator build() { + return new BaseEnergyContainerCreator(containerCreators); + } + + public EnergyContainersBuilder addBasic(FloatingLongSupplier rate, FloatingLongSupplier maxEnergy) { + return addContainer((type, attachedTo, containerIndex) -> new ComponentBackedEnergyContainer(attachedTo, containerIndex, BasicEnergyContainer.manualOnly, + BasicEnergyContainer.alwaysTrue, rate, maxEnergy)); + } + + public EnergyContainersBuilder addBasic(Predicate<@NotNull AutomationType> canExtract, Predicate<@NotNull AutomationType> canInsert, FloatingLongSupplier rate, + FloatingLongSupplier maxEnergy) { + return addContainer((type, attachedTo, containerIndex) -> new ComponentBackedEnergyContainer(attachedTo, containerIndex, canExtract, canInsert, rate, maxEnergy)); + } + + public EnergyContainersBuilder addContainer(IBasicContainerCreator capacitor) { + containerCreators.add(capacitor); + return this; + } + + private static class BaseEnergyContainerCreator extends BaseContainerCreator { + + public BaseEnergyContainerCreator(List> creators) { + super(creators); + } + + @Override + public AttachedEnergy initStorage(int containers) { + return new AttachedEnergy(containers); + } + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/fluid/AttachedFluids.java b/src/main/java/mekanism/common/attachments/containers/fluid/AttachedFluids.java new file mode 100644 index 00000000000..222c384cc9f --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/fluid/AttachedFluids.java @@ -0,0 +1,67 @@ +package mekanism.common.attachments.containers.fluid; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.Collections; +import java.util.List; +import mekanism.api.NBTConstants; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.common.attachments.containers.IAttachedContainers; +import net.minecraft.core.NonNullList; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.neoforged.neoforge.fluids.FluidStack; + +@NothingNullByDefault +public record AttachedFluids(List containers) implements IAttachedContainers { + + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + FluidStack.OPTIONAL_CODEC.listOf().fieldOf(NBTConstants.FLUID_TANKS).forGetter(AttachedFluids::containers) + ).apply(instance, AttachedFluids::new)); + public static final StreamCodec STREAM_CODEC = + FluidStack.OPTIONAL_STREAM_CODEC.>apply(ByteBufCodecs.collection(NonNullList::createWithCapacity)) + .map(AttachedFluids::new, AttachedFluids::containers); + + public AttachedFluids { + //Make the list unmodifiable to ensure we don't accidentally mutate it + containers = Collections.unmodifiableList(containers); + } + + public AttachedFluids(int containers) { + this(NonNullList.withSize(containers, FluidStack.EMPTY)); + } + + @Override + public AttachedFluids create(List containers) { + return new AttachedFluids(containers); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o == null || getClass() != o.getClass()) { + return false; + } + List otherContainers = ((AttachedFluids) o).containers; + if (containers.size() != otherContainers.size()) { + return false; + } + for (int i = 0; i < containers.size(); i++) { + if (!FluidStack.matches(containers.get(i), otherContainers.get(i))) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + int hash = 0; + for (FluidStack stack : containers) { + hash = hash * 31 + FluidStack.hashFluidAndComponents(stack); + } + return hash; + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/fluid/ComponentBackedFluidHandler.java b/src/main/java/mekanism/common/attachments/containers/fluid/ComponentBackedFluidHandler.java new file mode 100644 index 00000000000..6f29455c6b9 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/fluid/ComponentBackedFluidHandler.java @@ -0,0 +1,74 @@ +package mekanism.common.attachments.containers.fluid; + +import java.util.List; +import mekanism.api.Action; +import mekanism.api.AutomationType; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.fluid.ExtendedFluidHandlerUtils; +import mekanism.api.fluid.IExtendedFluidTank; +import mekanism.api.fluid.IMekanismFluidHandler; +import mekanism.common.attachments.containers.ComponentBackedHandler; +import mekanism.common.attachments.containers.ContainerType; +import net.minecraft.core.Direction; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.capability.IFluidHandlerItem; +import org.jetbrains.annotations.Nullable; + +@NothingNullByDefault +public class ComponentBackedFluidHandler extends ComponentBackedHandler implements IMekanismFluidHandler, IFluidHandlerItem { + + public ComponentBackedFluidHandler(ItemStack attachedTo) { + super(attachedTo); + } + + @Override + protected ContainerType containerType() { + return ContainerType.FLUID; + } + + @Override + public List getFluidTanks(@Nullable Direction side) { + return getContainers(); + } + + @Nullable + @Override + public IExtendedFluidTank getFluidTank(int tank, @Nullable Direction side) { + return getContainer(tank); + } + + @Override + public int getTanks(@Nullable Direction side) { + return containerCount(); + } + + @Override + public FluidStack getFluidInTank(int tank, @Nullable Direction side) { + AttachedFluids attachedFluids = getAttached(); + return attachedFluids == null ? FluidStack.EMPTY : attachedFluids.get(tank); + } + + @Override + public FluidStack insertFluid(FluidStack stack, @Nullable Direction side, Action action) { + //TODO - 1.20.5: Can we optimize this any further? Maybe by somehow only initializing the fluid tanks as necessary/we actually get to iterating against them? + return ExtendedFluidHandlerUtils.insert(stack, side, this::getFluidTanks, action, AutomationType.handler(side)); + } + + @Override + public FluidStack extractFluid(int amount, @Nullable Direction side, Action action) { + //TODO - 1.20.5: Can we optimize this any further? Maybe by somehow only initializing the fluid tanks as necessary/we actually get to iterating against them? + return ExtendedFluidHandlerUtils.extract(amount, side, this::getFluidTanks, action, AutomationType.handler(side)); + } + + @Override + public FluidStack extractFluid(FluidStack stack, @Nullable Direction side, Action action) { + //TODO - 1.20.5: Can we optimize this any further? Maybe by somehow only initializing the fluid tanks as necessary/we actually get to iterating against them? + return ExtendedFluidHandlerUtils.extract(stack, side, this::getFluidTanks, action, AutomationType.handler(side)); + } + + @Override + public ItemStack getContainer() { + return attachedTo; + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/fluid/ComponentBackedFluidTank.java b/src/main/java/mekanism/common/attachments/containers/fluid/ComponentBackedFluidTank.java new file mode 100644 index 00000000000..95b828d9336 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/fluid/ComponentBackedFluidTank.java @@ -0,0 +1,241 @@ +package mekanism.common.attachments.containers.fluid; + +import java.util.function.BiPredicate; +import java.util.function.IntSupplier; +import java.util.function.Predicate; +import java.util.function.Supplier; +import mekanism.api.Action; +import mekanism.api.AutomationType; +import mekanism.api.NBTConstants; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.fluid.IExtendedFluidTank; +import mekanism.common.attachments.containers.ComponentBackedContainer; +import mekanism.common.registries.MekanismDataComponents; +import mekanism.common.util.NBTUtils; +import net.minecraft.core.HolderLookup.Provider; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.capability.IFluidHandler.FluidAction; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@NothingNullByDefault +public class ComponentBackedFluidTank extends ComponentBackedContainer implements IExtendedFluidTank { + + private final BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canExtract; + private final BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canInsert; + private final Predicate<@NotNull FluidStack> validator; + private final IntSupplier capacity; + private final IntSupplier rate; + + public ComponentBackedFluidTank(ItemStack attachedTo, int tankIndex, BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canExtract, + BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canInsert, Predicate<@NotNull FluidStack> validator, IntSupplier rate, IntSupplier capacity) { + super(attachedTo, tankIndex); + this.canExtract = canExtract; + this.canInsert = canInsert; + this.validator = validator; + this.capacity = capacity; + this.rate = rate; + } + + @Override + protected Supplier> dataComponentType() { + return MekanismDataComponents.ATTACHED_FLUIDS; + } + + @Override + protected FluidStack copy(FluidStack toCopy) { + return toCopy.copy(); + } + + @Override + protected boolean isEmpty(FluidStack value) { + return value.isEmpty(); + } + + @Override + public FluidStack getFluid() { + //TODO - 1.20.5: Similar to getBasicInventorySlot do we want to reduce calls to this? Probably (We mostly do so, but we probably want to add a note here) + AttachedFluids attachedFluids = getAttached(); + return attachedFluids == null ? FluidStack.EMPTY : getContents(attachedFluids); + } + + @Override + public void setStack(FluidStack stack) { + //TODO - 1.20.5: I think just always setting it as unchecked is fine as we are doing that for items regardless + // but we may want to validate it? + setStackUnchecked(stack); + } + + @Override + public void setStackUnchecked(FluidStack stack) { + setContents(stack); + } + + @Override + public int getCapacity() { + return capacity.getAsInt(); + } + + protected int getRate(@Nullable AutomationType automationType) { + //Allow unknown or manual interaction to bypass rate limit for the item + return automationType == null || automationType == AutomationType.MANUAL ? Integer.MAX_VALUE : rate.getAsInt(); + } + + @Override + public FluidStack insert(FluidStack stack, Action action, AutomationType automationType) { + //TODO - 1.20.5: Items do the is valid and canInsert check after checking the needed amount. Should we do the same for fluids + // or should items have the order flipped? In general calculating the needed amount is likely cheaper which is likely why items do it first + if (stack.isEmpty() || !isFluidValid(stack) || !canInsert.test(stack, automationType)) { + //"Fail quick" if the given stack is empty, or we can never insert the fluid or currently are unable to insert it + return stack; + } + AttachedFluids attachedFluids = getAttached(); + if (attachedFluids == null) { + //"Fail quick" if we can't have a stack + return stack; + } + FluidStack stored = getContents(attachedFluids); + int needed = Math.min(getRate(automationType), getNeeded(stored)); + if (needed <= 0) { + //Fail if we are a full tank or our rate is zero + return stack; + } else if (stored.isEmpty() || FluidStack.isSameFluidSameComponents(stored, stack)) { + int toAdd = Math.min(stack.getAmount(), needed); + if (action.execute()) { + //Note: We let setStack handle updating the backing holding stack + // We use stored.getAmount + toAdd so that if we are empty we end up at toAdd + // but if we aren't then we grow by the given amount + setContents(attachedFluids, stack.copyWithAmount(stored.getAmount() + toAdd)); + } + return stack.copyWithAmount(stack.getAmount() - toAdd); + } + //If we didn't accept this fluid, then just return the given stack + return stack; + } + + @Override + public final FluidStack extract(int amount, Action action, AutomationType automationType) { + if (amount < 1) { + //"Fail quick" if the amount being requested is less than one + return FluidStack.EMPTY; + } + AttachedFluids attachedFluids = getAttached(); + if (attachedFluids == null) { + //"Fail quick" if we can't have a stack + return FluidStack.EMPTY; + } + return extract(attachedFluids, getContents(attachedFluids), amount, action, automationType); + } + + protected FluidStack extract(AttachedFluids attachedFluids, FluidStack stored, int amount, Action action, AutomationType automationType) { + if (amount < 1 || stored.isEmpty() || !canExtract.test(stored, automationType)) { + //"Fail quick" if we don't can never extract from this tank, have a fluid stored, or the amount being requested is less than one + return FluidStack.EMPTY; + } + //Note: While we technically could just return the stack itself if we are removing all that we have, it would require a lot more checks + // We also are limiting it by the rate this tank has + int size = Math.min(Math.min(getRate(automationType), stored.getAmount()), amount); + FluidStack ret = stored.copyWithAmount(size); + if (!ret.isEmpty() && action.execute()) { + //Note: We let setStack handle updating the backing holding stack + setContents(attachedFluids, stored.copyWithAmount(stored.getAmount() - ret.getAmount())); + } + return ret; + } + + @Override + public final int setStackSize(int amount, Action action) { + AttachedFluids attachedFluids = getAttached(); + if (attachedFluids == null) { + //"Fail quick" if we can't have a stack + return 0; + } + return setStackSize(attachedFluids, getContents(attachedFluids), amount, action); + } + + protected int setStackSize(AttachedFluids attachedFluids, FluidStack stored, int amount, Action action) { + if (stored.isEmpty()) { + return 0; + } else if (amount <= 0) { + if (action.execute()) { + setContents(attachedFluids, FluidStack.EMPTY); + } + return 0; + } + int maxStackSize = getCapacity(); + if (amount > maxStackSize) { + amount = maxStackSize; + } + if (stored.getAmount() == amount || action.simulate()) { + //If our size is not changing, or we are only simulating the change, don't do anything + return amount; + } + setContents(attachedFluids, stored.copyWithAmount(amount)); + return amount; + } + + @Override + public int growStack(int amount, Action action) { + AttachedFluids attachedFluids = getAttached(); + if (attachedFluids == null) { + //"Fail quick" if we can't have a stack + return 0; + } + FluidStack stored = getContents(attachedFluids); + int current = stored.getAmount(); + if (amount > 0) { + //Cap adding amount at how much we need, so that we don't risk integer overflow + amount = Math.min(Math.min(amount, getNeeded(stored)), getRate(null)); + } else if (amount < 0) { + amount = Math.max(amount, -getRate(null)); + } + int newSize = setStackSize(attachedFluids, stored,current + amount, action); + return newSize - current; + } + + protected int getNeeded(FluidStack stored) { + //Skip the stack lookup for getNeeded + return Math.max(0, getCapacity() - stored.getAmount()); + } + + @Override + public boolean isFluidValid(FluidStack stack) { + return validator.test(stack); + } + + @Override + public CompoundTag serializeNBT(Provider provider) { + //TODO - 1.20.5: This is a copy of BasicFluidTank#serializeNBT. We might need to also grab the specific overrides of + // that method as special component backed inventory slots, that then access and put that other data as a different component? + CompoundTag nbt = new CompoundTag(); + FluidStack stored = getFluid(); + if (!stored.isEmpty()) { + nbt.put(NBTConstants.STORED, stored.save(provider)); + } + return nbt; + } + + @Override + public void deserializeNBT(Provider provider, CompoundTag nbt) { + NBTUtils.setFluidStackIfPresent(provider, nbt, NBTConstants.STORED, this::setStackUnchecked); + } + + @Override + @Deprecated + public FluidStack drain(FluidStack stack, FluidAction action) { + //Override to only look up the stack once + AttachedFluids attachedFluids = getAttached(); + if (attachedFluids == null) { + //"Fail quick" if we can't have a stack + return FluidStack.EMPTY; + } + FluidStack stored = getContents(attachedFluids); + if (!stored.isEmpty() && FluidStack.isSameFluidSameComponents(stored, stack)) { + return extract(attachedFluids, stored, stack.getAmount(), Action.fromFluidAction(action), AutomationType.EXTERNAL); + } + return FluidStack.EMPTY; + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/fluid/ComponentBackedFluidTankFluidTank.java b/src/main/java/mekanism/common/attachments/containers/fluid/ComponentBackedFluidTankFluidTank.java new file mode 100644 index 00000000000..091fa3c31f7 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/fluid/ComponentBackedFluidTankFluidTank.java @@ -0,0 +1,50 @@ +package mekanism.common.attachments.containers.fluid; + +import mekanism.api.Action; +import mekanism.api.AutomationType; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.capabilities.fluid.BasicFluidTank; +import mekanism.common.item.block.machine.ItemBlockFluidTank; +import mekanism.common.tier.FluidTankTier; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.fluids.FluidStack; + +@NothingNullByDefault +public class ComponentBackedFluidTankFluidTank extends ComponentBackedFluidTank { + + private final boolean isCreative; + + public static ComponentBackedFluidTankFluidTank create(ContainerType ignored, ItemStack attachedTo, int tankIndex) { + if (!(attachedTo.getItem() instanceof ItemBlockFluidTank item)) { + throw new IllegalStateException("Attached to should always be a fluid tank item"); + } + return new ComponentBackedFluidTankFluidTank(attachedTo, tankIndex, item.getTier()); + } + + private ComponentBackedFluidTankFluidTank(ItemStack attachedTo, int tankIndex, FluidTankTier tier) { + super(attachedTo, tankIndex, BasicFluidTank.alwaysTrueBi, BasicFluidTank.alwaysTrueBi, BasicFluidTank.alwaysTrue, tier::getOutput, tier::getStorage); + isCreative = tier == FluidTankTier.CREATIVE; + } + + @Override + public FluidStack insert(FluidStack stack, Action action, AutomationType automationType) { + return super.insert(stack, action.combine(!isCreative), automationType); + } + + @Override + public FluidStack extract(AttachedFluids attachedFluids, FluidStack stored, int amount, Action action, AutomationType automationType) { + return super.extract(attachedFluids, stored, amount, action.combine(!isCreative), automationType); + } + + /** + * {@inheritDoc} + * + * Note: We are only patching {@link #setStackSize(AttachedFluids, FluidStack, int, Action)}, as both {@link #growStack(int, Action)} and + * {@link #shrinkStack(int, Action)} are wrapped through this method. + */ + @Override + public int setStackSize(AttachedFluids attachedFluids, FluidStack stored, int amount, Action action) { + return super.setStackSize(attachedFluids, stored, amount, action.combine(!isCreative)); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/fluid/FluidTanksBuilder.java b/src/main/java/mekanism/common/attachments/containers/fluid/FluidTanksBuilder.java new file mode 100644 index 00000000000..9ef36458927 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/fluid/FluidTanksBuilder.java @@ -0,0 +1,77 @@ +package mekanism.common.attachments.containers.fluid; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.IntSupplier; +import java.util.function.Predicate; +import mekanism.api.recipes.MekanismRecipe; +import mekanism.common.attachments.containers.ContainsRecipe; +import mekanism.common.attachments.containers.creator.BaseContainerCreator; +import mekanism.common.attachments.containers.creator.IBasicContainerCreator; +import mekanism.common.capabilities.fluid.BasicFluidTank; +import mekanism.common.config.MekanismConfig; +import mekanism.common.recipe.IMekanismRecipeTypeProvider; +import mekanism.common.recipe.lookup.cache.IInputRecipeCache; +import net.neoforged.neoforge.fluids.FluidStack; +import org.jetbrains.annotations.NotNull; + +public class FluidTanksBuilder { + + public static FluidTanksBuilder builder() { + return new FluidTanksBuilder(); + } + + private final List> tankCreators = new ArrayList<>(); + + private FluidTanksBuilder() { + } + + public BaseContainerCreator build() { + return new BaseFluidTankCreator(tankCreators); + } + + public FluidTanksBuilder addBasic(int capacity, + IMekanismRecipeTypeProvider recipeType, ContainsRecipe containsRecipe) { + return addBasic(capacity, fluid -> containsRecipe.check(recipeType.getInputCache(), null, fluid)); + } + + public FluidTanksBuilder addBasic(int capacity, Predicate<@NotNull FluidStack> isValid) { + return addBasic(() -> capacity, isValid); + } + + public FluidTanksBuilder addBasic(IntSupplier capacity, Predicate<@NotNull FluidStack> isValid) { + return addTank((type, attachedTo, containerIndex) -> new ComponentBackedFluidTank(attachedTo, containerIndex, BasicFluidTank.manualOnly, BasicFluidTank.alwaysTrueBi, + isValid, MekanismConfig.general.fluidItemFillRate, capacity)); + } + + public FluidTanksBuilder addBasic(int capacity) { + return addBasic(() -> capacity); + } + + public FluidTanksBuilder addBasic(IntSupplier capacity) { + return addTank((type, attachedTo, containerIndex) -> new ComponentBackedFluidTank(attachedTo, containerIndex, BasicFluidTank.manualOnly, BasicFluidTank.alwaysTrueBi, + BasicFluidTank.alwaysTrue, MekanismConfig.general.fluidItemFillRate, capacity)); + } + + public FluidTanksBuilder addBasicExtractable(IntSupplier rate, IntSupplier capacity, Predicate<@NotNull FluidStack> isValid) { + return addTank((type, attachedTo, containerIndex) -> new ComponentBackedFluidTank(attachedTo, containerIndex, BasicFluidTank.alwaysTrueBi, BasicFluidTank.alwaysTrueBi, + isValid, rate, capacity)); + } + + public FluidTanksBuilder addTank(IBasicContainerCreator tank) { + tankCreators.add(tank); + return this; + } + + private static class BaseFluidTankCreator extends BaseContainerCreator { + + public BaseFluidTankCreator(List> creators) { + super(creators); + } + + @Override + public AttachedFluids initStorage(int containers) { + return new AttachedFluids(containers); + } + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/heat/AttachedHeat.java b/src/main/java/mekanism/common/attachments/containers/heat/AttachedHeat.java new file mode 100644 index 00000000000..0755b53c12b --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/heat/AttachedHeat.java @@ -0,0 +1,38 @@ +package mekanism.common.attachments.containers.heat; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import io.netty.buffer.ByteBuf; +import java.util.Collections; +import java.util.List; +import mekanism.api.NBTConstants; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.math.FloatingLong; +import mekanism.common.attachments.containers.IAttachedContainers; +import mekanism.common.attachments.containers.creator.BaseContainerCreator; +import mekanism.common.attachments.containers.creator.IBasicContainerCreator; +import mekanism.common.attachments.containers.fluid.AttachedFluids; +import mekanism.common.attachments.containers.fluid.ComponentBackedFluidTank; +import net.minecraft.core.NonNullList; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; + +@NothingNullByDefault +public record AttachedHeat(List containers) implements IAttachedContainers { + + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + HeatCapacitorData.CODEC.listOf().fieldOf(NBTConstants.HEAT_CAPACITORS).forGetter(AttachedHeat::containers) + ).apply(instance, AttachedHeat::new)); + public static final StreamCodec STREAM_CODEC = HeatCapacitorData.STREAM_CODEC. + >apply(ByteBufCodecs.collection(NonNullList::createWithCapacity)).map(AttachedHeat::new, AttachedHeat::containers); + + public AttachedHeat { + //Make the list unmodifiable to ensure we don't accidentally mutate it + containers = Collections.unmodifiableList(containers); + } + + @Override + public AttachedHeat create(List containers) { + return new AttachedHeat(containers); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/heat/ComponentBackedHeatCapacitor.java b/src/main/java/mekanism/common/attachments/containers/heat/ComponentBackedHeatCapacitor.java new file mode 100644 index 00000000000..747f63a67da --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/heat/ComponentBackedHeatCapacitor.java @@ -0,0 +1,135 @@ +package mekanism.common.attachments.containers.heat; + +import java.util.function.Supplier; +import mekanism.api.NBTConstants; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.heat.HeatAPI; +import mekanism.api.heat.IHeatCapacitor; +import mekanism.common.attachments.containers.ComponentBackedContainer; +import mekanism.common.registries.MekanismDataComponents; +import net.minecraft.core.HolderLookup.Provider; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.item.ItemStack; + +@NothingNullByDefault +public class ComponentBackedHeatCapacitor extends ComponentBackedContainer implements IHeatCapacitor { + + private final double inverseConductionCoefficient; + private final double inverseInsulationCoefficient; + private final HeatCapacitorData defaultData; + + public ComponentBackedHeatCapacitor(ItemStack attachedTo, int slotIndex, double inverseConductionCoefficient, double inverseInsulationCoefficient) { + this(attachedTo, slotIndex, inverseConductionCoefficient, inverseInsulationCoefficient, HeatAPI.DEFAULT_HEAT_CAPACITY); + } + + public ComponentBackedHeatCapacitor(ItemStack attachedTo, int slotIndex, double inverseConductionCoefficient, double inverseInsulationCoefficient, + double defaultHeatCapacity) { + super(attachedTo, slotIndex); + this.inverseConductionCoefficient = inverseConductionCoefficient; + this.inverseInsulationCoefficient = inverseInsulationCoefficient; + this.defaultData = new HeatCapacitorData(0.0, defaultHeatCapacity); + } + + @Override + protected Supplier> dataComponentType() { + return MekanismDataComponents.ATTACHED_HEAT; + } + + @Override + protected HeatCapacitorData copy(HeatCapacitorData toCopy) { + //HeatCapacitorData is already immutable, so we don't need to copy it + return toCopy; + } + + @Override + protected boolean isEmpty(HeatCapacitorData value) { + //TODO - 1.20.5: Re-evaluate + return value.equals(defaultData); + } + + protected HeatCapacitorData getData() { + //TODO - 1.20.5: Similar to getBasicInventorySlot do we want to reduce calls to this? Probably (We mostly do so, but we probably want to add a note here) + AttachedHeat attachedHeat = getAttached(); + return attachedHeat == null ? defaultData : getContents(attachedHeat); + } + + @Override + public double getTemperature() { + HeatCapacitorData data = getData(); + return data.heat() / data.capacity(); + } + + @Override + public double getInverseConduction() { + return inverseConductionCoefficient; + } + + @Override + public double getInverseInsulation() { + return inverseInsulationCoefficient; + } + + @Override + public double getHeatCapacity() { + return getData().capacity(); + } + + @Override + public double getHeat() { + return getData().heat(); + } + + @Override + public void setHeat(double heat) { + AttachedHeat attachedHeat = getAttached(); + if (attachedHeat != null) { + HeatCapacitorData stored = getContents(attachedHeat); + setContents(attachedHeat, new HeatCapacitorData(heat, stored.capacity())); + } + //TODO - 1.20.5: Else initialize to whatever the default size is meant to be? + } + + @Override//TODO - 1.20.5: Re-evaluate this override + protected void setContents(AttachedHeat attached, HeatCapacitorData value) { + HeatCapacitorData stored = getContents(attached); + if (!stored.equals(value)) { + attachedTo.set(dataComponentType(), attached.with(containerIndex, value)); + onContentsChanged(); + } + } + + @Override + public void handleHeat(double transfer) { + if (transfer != 0 && Math.abs(transfer) > HeatAPI.EPSILON) { + AttachedHeat attachedHeat = getAttached(); + if (attachedHeat != null) { + HeatCapacitorData stored = getContents(attachedHeat); + setContents(attachedHeat, new HeatCapacitorData(stored.heat() + transfer, stored.capacity())); + } + } + } + + @Override + public CompoundTag serializeNBT(Provider provider) { + //TODO - 1.20.5: This is a copy of BasicHeatCapacitor#serializeNBT. We might need to also grab the specific overrides of + // that method as special component backed inventory slots, that then access and put that other data as a different component? + CompoundTag nbt = new CompoundTag(); + HeatCapacitorData data = getData(); + nbt.putDouble(NBTConstants.STORED, data.heat()); + nbt.putDouble(NBTConstants.HEAT_CAPACITY, data.capacity()); + return nbt; + } + + @Override + public void deserializeNBT(Provider provider, CompoundTag nbt) { + double capacity; + if (nbt.contains(NBTConstants.HEAT_CAPACITY, Tag.TAG_DOUBLE)) { + capacity = nbt.getDouble(NBTConstants.HEAT_CAPACITY); + } else { + capacity = defaultData.capacity(); + } + setContents(new HeatCapacitorData(nbt.getDouble(NBTConstants.STORED), capacity)); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/heat/ComponentBackedHeatHandler.java b/src/main/java/mekanism/common/attachments/containers/heat/ComponentBackedHeatHandler.java new file mode 100644 index 00000000000..26af4c326f8 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/heat/ComponentBackedHeatHandler.java @@ -0,0 +1,40 @@ +package mekanism.common.attachments.containers.heat; + +import java.util.List; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.heat.IHeatCapacitor; +import mekanism.api.heat.IMekanismHeatHandler; +import mekanism.common.attachments.containers.ComponentBackedHandler; +import mekanism.common.attachments.containers.ContainerType; +import net.minecraft.core.Direction; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +@NothingNullByDefault +public class ComponentBackedHeatHandler extends ComponentBackedHandler implements IMekanismHeatHandler { + + public ComponentBackedHeatHandler(ItemStack attachedTo) { + super(attachedTo); + } + + @Override + protected ContainerType containerType() { + return ContainerType.HEAT; + } + + @Override + public int getHeatCapacitorCount(@Nullable Direction side) { + return containerCount(); + } + + @Override + public List getHeatCapacitors(@Nullable Direction side) { + return getContainers(); + } + + @Nullable + @Override + public IHeatCapacitor getHeatCapacitor(int capacitor, @Nullable Direction side) { + return getContainer(capacitor); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/heat/HeatCapacitorData.java b/src/main/java/mekanism/common/attachments/containers/heat/HeatCapacitorData.java new file mode 100644 index 00000000000..387983e7067 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/heat/HeatCapacitorData.java @@ -0,0 +1,21 @@ +package mekanism.common.attachments.containers.heat; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import io.netty.buffer.ByteBuf; +import mekanism.api.NBTConstants; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; + +public record HeatCapacitorData(double heat, double capacity) { + + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.DOUBLE.fieldOf(NBTConstants.STORED).forGetter(HeatCapacitorData::heat), + Codec.DOUBLE.fieldOf(NBTConstants.HEAT_CAPACITY).forGetter(HeatCapacitorData::capacity) + ).apply(instance, HeatCapacitorData::new)); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.DOUBLE, HeatCapacitorData::heat, + ByteBufCodecs.DOUBLE, HeatCapacitorData::capacity, + HeatCapacitorData::new + ); +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/heat/HeatCapacitorsBuilder.java b/src/main/java/mekanism/common/attachments/containers/heat/HeatCapacitorsBuilder.java new file mode 100644 index 00000000000..e8bd0911ed9 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/heat/HeatCapacitorsBuilder.java @@ -0,0 +1,60 @@ +package mekanism.common.attachments.containers.heat; + +import it.unimi.dsi.fastutil.doubles.DoubleArrayList; +import it.unimi.dsi.fastutil.doubles.DoubleList; +import java.util.ArrayList; +import java.util.List; +import mekanism.api.heat.HeatAPI; +import mekanism.common.attachments.containers.creator.BaseContainerCreator; +import mekanism.common.attachments.containers.creator.IBasicContainerCreator; + +public class HeatCapacitorsBuilder { + + public static HeatCapacitorsBuilder builder() { + return new HeatCapacitorsBuilder(); + } + + private final List> capacitorCreators = new ArrayList<>(); + private final DoubleList defaultHeatCapacities = new DoubleArrayList(); + + private HeatCapacitorsBuilder() { + } + + public BaseContainerCreator build() { + return new BaseHeatCapacitorCreator(capacitorCreators, defaultHeatCapacities); + } + + public HeatCapacitorsBuilder addBasic(double heatCapacity, double inverseConductionCoefficient, double inverseInsulationCoefficient) { + return addCapacitor(heatCapacity, (type, attachedTo, containerIndex) -> new ComponentBackedHeatCapacitor(attachedTo, containerIndex, inverseConductionCoefficient, + inverseInsulationCoefficient, heatCapacity)); + } + + public HeatCapacitorsBuilder addCapacitor(IBasicContainerCreator capacitor) { + return addCapacitor(HeatAPI.DEFAULT_HEAT_CAPACITY, capacitor); + } + + public HeatCapacitorsBuilder addCapacitor(double defaultHeatCapacity, IBasicContainerCreator capacitor) { + defaultHeatCapacities.add(defaultHeatCapacity); + capacitorCreators.add(capacitor); + return this; + } + + private static class BaseHeatCapacitorCreator extends BaseContainerCreator { + + private final DoubleList defaultHeatCapacities; + + public BaseHeatCapacitorCreator(List> creators, DoubleList defaultHeatCapacities) { + super(creators); + this.defaultHeatCapacities = defaultHeatCapacities; + } + + @Override + public AttachedHeat initStorage(int containers) { + List capacitors = new ArrayList<>(containers); + for (int capacitor = 0; capacitor < containers; capacitor++) { + capacitors.add(new HeatCapacitorData(0.0, defaultHeatCapacities.getDouble(capacitor))); + } + return new AttachedHeat(capacitors); + } + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/item/AttachedItems.java b/src/main/java/mekanism/common/attachments/containers/item/AttachedItems.java new file mode 100644 index 00000000000..9ebbb6b4f12 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/item/AttachedItems.java @@ -0,0 +1,52 @@ +package mekanism.common.attachments.containers.item; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.Collections; +import java.util.List; +import mekanism.api.NBTConstants; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.common.attachments.containers.IAttachedContainers; +import net.minecraft.core.NonNullList; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.ItemStack; + +@NothingNullByDefault +public record AttachedItems(List containers) implements IAttachedContainers { + + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + ItemStack.OPTIONAL_CODEC.listOf().fieldOf(NBTConstants.ITEMS).forGetter(AttachedItems::containers) + ).apply(instance, AttachedItems::new)); + public static final StreamCodec STREAM_CODEC = ItemStack.OPTIONAL_LIST_STREAM_CODEC + .map(AttachedItems::new, AttachedItems::containers); + + public AttachedItems { + //Make the list unmodifiable to ensure we don't accidentally mutate it + containers = Collections.unmodifiableList(containers); + } + + public AttachedItems(int containers) { + this(NonNullList.withSize(containers, ItemStack.EMPTY)); + } + + @Override + public AttachedItems create(List containers) { + return new AttachedItems(containers); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o == null || getClass() != o.getClass()) { + return false; + } + return ItemStack.listMatches(containers, ((AttachedItems) o).containers); + } + + @Override + public int hashCode() { + return ItemStack.hashStackList(containers); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/item/ComponentBackedBinInventorySlot.java b/src/main/java/mekanism/common/attachments/containers/item/ComponentBackedBinInventorySlot.java new file mode 100644 index 00000000000..6c353a16de8 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/item/ComponentBackedBinInventorySlot.java @@ -0,0 +1,165 @@ + +package mekanism.common.attachments.containers.item; + +import mekanism.api.Action; +import mekanism.api.AutomationType; +import mekanism.api.NBTConstants; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.inventory.IInventorySlot; +import mekanism.common.attachments.LockData; +import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.inventory.slot.BasicInventorySlot; +import mekanism.common.inventory.slot.BinInventorySlot; +import mekanism.common.item.block.ItemBlockBin; +import mekanism.common.registries.MekanismDataComponents; +import mekanism.common.tier.BinTier; +import mekanism.common.util.NBTUtils; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +@NothingNullByDefault +public class ComponentBackedBinInventorySlot extends ComponentBackedInventorySlot { + + private final boolean isCreative; + + public static ComponentBackedBinInventorySlot create(ContainerType ignored, ItemStack attachedTo, int tankIndex) { + if (!(attachedTo.getItem() instanceof ItemBlockBin item)) { + throw new IllegalStateException("Attached to should always be a bin item"); + } + return new ComponentBackedBinInventorySlot(attachedTo, tankIndex, item.getTier()); + } + + private ComponentBackedBinInventorySlot(ItemStack attachedTo, int slotIndex, BinTier tier) { + super(attachedTo, slotIndex, BasicInventorySlot.alwaysTrueBi, BasicInventorySlot.alwaysTrueBi, BinInventorySlot.validator); + isCreative = tier == BinTier.CREATIVE; + } + + @Override + public ItemStack insertItem(AttachedItems attachedItems, ItemStack current, ItemStack stack, Action action, AutomationType automationType) { + if (current.isEmpty()) { + ItemStack lockStack = getLockStack(); + if (!lockStack.isEmpty() && !ItemStack.isSameItemSameComponents(lockStack, stack)) { + // When locked, we need to make sure the correct item type is being inserted + return stack; + } else if (isCreative && action.execute() && automationType != AutomationType.EXTERNAL) { + //If a player manually inserts into a creative bin, that is empty we need to allow setting the type, + // Note: We check that it is not external insertion because an empty creative bin acts as a "void" for automation + ItemStack simulatedRemainder = super.insertItem(attachedItems, current, stack, Action.SIMULATE, automationType); + if (simulatedRemainder.isEmpty()) { + //If we are able to insert it then set perform the action of setting it to full + setContents(attachedItems, stack.copyWithCount(getLimit(stack))); + } + return simulatedRemainder; + } + } + return super.insertItem(attachedItems, current, stack, action.combine(!isCreative), automationType); + } + + @Override + public ItemStack extractItem(int amount, Action action, AutomationType automationType) { + return super.extractItem(amount, action.combine(!isCreative), automationType); + } + + /** + * {@inheritDoc} + * + * Note: We are only patching {@link #setStackSize(AttachedItems, ItemStack, int, Action)}, as both {@link #growStack(int, Action)} and + * {@link #shrinkStack(int, Action)} are wrapped through this method. + */ + @Override + protected int setStackSize(AttachedItems attachedItems, ItemStack current, int amount, Action action) { + return super.setStackSize(attachedItems, current, amount, action.combine(!isCreative)); + } + + /** + * Gets the "bottom" stack for the bin, this is the stack that can be extracted/interacted with directly. + * + * @return The "bottom" stack for the bin + * + * @apiNote The returned stack can be safely modified. + */ + public ItemStack getBottomStack() { + //TODO - 1.20.5: ?? + ItemStack current = getStack(); + if (current.isEmpty()) { + return ItemStack.EMPTY; + } + return current.copyWithCount(Math.min(current.getCount(), current.getMaxStackSize())); + } + + /** + * Modifies the lock state of the slot. + * + * @param lock if the slot should be locked + * + * @return if the lock state was modified + */ + public boolean setLocked(boolean lock) {//TODO - 1.20.5: ?? + // Don't lock if: + // - We are a creative bin + // - We already have the same state as the one we're supposed to switch to + // - We were asked to lock, but we're empty + if (isCreative || isLocked() == lock) { + return false; + } else if (lock) { + ItemStack current = getStack(); + if (current.isEmpty()) { + return false; + } + setStack(current); + } else { + setLockStack(ItemStack.EMPTY); + } + return true; + } + + /** + * For use by upgrade recipes, do not use this in place of {@link #setLocked(boolean)} + */ + public void setLockStack(@NotNull ItemStack stack) {//TODO - 1.20.5: ?? + //Note: This doesn't support the case where one backing stack may have multiple bin slots. If we ever support that case + // we will need to adjust how this works + if (stack.isEmpty()) { + attachedTo.remove(MekanismDataComponents.LOCK); + } else { + attachedTo.set(MekanismDataComponents.LOCK, new LockData(stack.copyWithCount(1))); + } + } + + public boolean isLocked() {//TODO - 1.20.5: ?? + return !getLockStack().isEmpty(); + } + + public ItemStack getRenderStack() {//TODO - 1.20.5: ?? + ItemStack lockStack = getLockStack(); + return lockStack.isEmpty() ? getStack() : lockStack; + } + + public ItemStack getLockStack() {//TODO - 1.20.5: ?? + LockData lockData = attachedTo.get(MekanismDataComponents.LOCK); + return lockData == null ? ItemStack.EMPTY : lockData.lock(); + } + + @Override + public CompoundTag serializeNBT(HolderLookup.Provider provider) { + CompoundTag nbt = super.serializeNBT(provider); + ItemStack lockStack = getLockStack(); + if (!lockStack.isEmpty()) { + nbt.put(NBTConstants.LOCK_STACK, lockStack.save(provider)); + } + return nbt; + } + + @Override + public void deserializeNBT(HolderLookup.Provider provider, CompoundTag nbt) { + NBTUtils.setItemStackOrEmpty(provider, nbt, NBTConstants.LOCK_STACK, this::setLockStack); + super.deserializeNBT(provider, nbt); + } + + @Override + public boolean isCompatible(IInventorySlot other) { + return super.isCompatible(other) && ItemStack.isSameItemSameComponents(getLockStack(), ((ComponentBackedBinInventorySlot) other).getLockStack()); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/item/ComponentBackedInventorySlot.java b/src/main/java/mekanism/common/attachments/containers/item/ComponentBackedInventorySlot.java new file mode 100644 index 00000000000..c0a6d4fa561 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/item/ComponentBackedInventorySlot.java @@ -0,0 +1,235 @@ +package mekanism.common.attachments.containers.item; + +import java.util.function.BiPredicate; +import java.util.function.Predicate; +import java.util.function.Supplier; +import mekanism.api.Action; +import mekanism.api.AutomationType; +import mekanism.api.NBTConstants; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.inventory.IInventorySlot; +import mekanism.common.attachments.containers.ComponentBackedContainer; +import mekanism.common.inventory.slot.BasicInventorySlot; +import mekanism.common.registries.MekanismDataComponents; +import mekanism.common.util.NBTUtils; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.HolderLookup.Provider; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +@NothingNullByDefault +public class ComponentBackedInventorySlot extends ComponentBackedContainer implements IInventorySlot { + + private final BiPredicate<@NotNull ItemStack, @NotNull AutomationType> canExtract; + private final BiPredicate<@NotNull ItemStack, @NotNull AutomationType> canInsert; + private final Predicate<@NotNull ItemStack> validator; + private final boolean obeyStackLimit; + private final int limit; + + public ComponentBackedInventorySlot(ItemStack attachedTo, int slotIndex, BiPredicate<@NotNull ItemStack, @NotNull AutomationType> canExtract, + BiPredicate<@NotNull ItemStack, @NotNull AutomationType> canInsert, Predicate<@NotNull ItemStack> validator) { + this(attachedTo, slotIndex, canExtract, canInsert, validator, true, BasicInventorySlot.DEFAULT_LIMIT); + } + + public ComponentBackedInventorySlot(ItemStack attachedTo, int slotIndex, BiPredicate<@NotNull ItemStack, @NotNull AutomationType> canExtract, + BiPredicate<@NotNull ItemStack, @NotNull AutomationType> canInsert, Predicate<@NotNull ItemStack> validator, boolean obeyStackLimit, int limit) { + super(attachedTo, slotIndex); + this.canExtract = canExtract; + this.canInsert = canInsert; + this.validator = validator; + this.obeyStackLimit = obeyStackLimit; + this.limit = limit; + } + + @Override + protected Supplier> dataComponentType() { + return MekanismDataComponents.ATTACHED_ITEMS; + } + + @Override + protected ItemStack copy(ItemStack toCopy) { + return toCopy.copy(); + } + + @Override + protected boolean isEmpty(ItemStack value) { + return value.isEmpty(); + } + + @Override + public ItemStack getStack() { + //TODO - 1.20.5: Similar to getBasicInventorySlot do we want to reduce calls to this? Probably (We mostly do so, but we probably want to add a note here) + AttachedItems attachedItems = getAttached(); + return attachedItems == null ? ItemStack.EMPTY : getContents(attachedItems); + } + + @Override + public final void setStack(ItemStack stack) { + setContents(stack); + } + + /** + * Ignores current contents + */ + private boolean isItemValidForInsertion(ItemStack stack, AutomationType automationType) { + return validator.test(stack) && canInsert.test(stack, automationType); + } + + @Override + public final ItemStack insertItem(ItemStack stack, Action action, AutomationType automationType) { + if (stack.isEmpty()) { + //"Fail quick" if the given stack is empty + return ItemStack.EMPTY; + } + AttachedItems attachedItems = getAttached(); + if (attachedItems == null) { + //"Fail quick" if we can't have a stack + return stack; + } + return insertItem(attachedItems, getContents(attachedItems), stack, action, automationType); + } + + public ItemStack insertItem(AttachedItems attachedItems, ItemStack current, ItemStack stack, Action action, AutomationType automationType) { + if (stack.isEmpty()) { + //"Fail quick" if the given stack is empty + return ItemStack.EMPTY; + } + //Validate that we aren't at max stack size before we try to see if we can insert the item, as on average this will be a cheaper check + int needed = getLimit(stack) - current.getCount(); + if (needed <= 0 || !isItemValidForInsertion(stack, automationType)) { + //Fail if we are a full slot, or we can never insert the item or currently are unable to insert it + return stack; + } else if (current.isEmpty() || ItemStack.isSameItemSameComponents(current, stack)) { + int toAdd = Math.min(stack.getCount(), needed); + if (action.execute()) { + //Note: We let setStack handle updating the backing holding stack + // We use current.getCount + toAdd so that if we are empty we end up at toAdd + // but if we aren't then we grow by the given amount + //TODO - 1.20.5: FIXME?? If same type we want to add it by x + setContents(attachedItems, stack.copyWithCount(current.getCount() + toAdd)); + } + return stack.copyWithCount(stack.getCount() - toAdd); + } + //If we didn't accept this item, then just return the given stack + return stack; + } + + @Override + public ItemStack extractItem(int amount, Action action, AutomationType automationType) { + if (amount < 1) { + //"Fail quick" if we don't can never extract from this slot, have an item stored, or the amount being requested is less than one + return ItemStack.EMPTY; + } + AttachedItems attachedItems = getAttached(); + if (attachedItems == null) { + //"Fail quick" if we can't have a stack + return ItemStack.EMPTY; + } + ItemStack current = getContents(attachedItems); + if (current.isEmpty() || !canExtract.test(current, automationType)) { + return ItemStack.EMPTY; + } + //Ensure that if this slot allows going past the max stack size of an item, that when extracting we don't act as if we have more than + // the max stack size, as the JavaDoc for IItemHandler requires that the returned stack is not larger than its stack size + int currentAmount = Math.min(current.getCount(), current.getMaxStackSize()); + if (currentAmount < amount) { + //If we are trying to extract more than we have, just change it so that we are extracting it all + amount = currentAmount; + } + //Note: While we technically could just return the stack itself if we are removing all that we have, it would require a lot more checks + // especially for supporting the fact of limiting by the max stack size. + ItemStack toReturn = current.copyWithCount(amount); + if (action.execute()) { + //Note: We let setStack handle updating the backing holding stack + setContents(attachedItems, current.copyWithCount(current.getCount() - amount)); + } + return toReturn; + } + + @Override + public int getLimit(ItemStack stack) { + return obeyStackLimit && !stack.isEmpty() ? Math.min(limit, stack.getMaxStackSize()) : limit; + } + + @Override + public boolean isItemValid(ItemStack stack) { + return validator.test(stack); + } + + @Override + public final int setStackSize(int amount, Action action) { + AttachedItems attachedItems = getAttached(); + if (attachedItems == null) { + //"Fail quick" if we can't have a stack + return 0; + } + return setStackSize(attachedItems, getContents(attachedItems), amount, action); + } + + protected int setStackSize(AttachedItems attachedItems, ItemStack current, int amount, Action action) { + if (current.isEmpty()) { + return 0; + } else if (amount <= 0) { + if (action.execute()) { + setContents(attachedItems, ItemStack.EMPTY); + } + return 0; + } + int maxStackSize = getLimit(current); + if (amount > maxStackSize) { + amount = maxStackSize; + } + if (current.getCount() == amount || action.simulate()) { + //If our size is not changing, or we are only simulating the change, don't do anything + return amount; + } + setContents(attachedItems, current.copyWithCount(amount)); + return amount; + } + + @Override + public int growStack(int amount, Action action) { + AttachedItems attachedItems = getAttached(); + if (attachedItems == null) { + //"Fail quick" if we can't have a stack + return 0; + } + //Avoid extra getStack lookup calls + ItemStack stack = getContents(attachedItems); + int current = stack.getCount(); + if (amount > 0) { + //Cap adding amount at how much we need, so that we don't risk integer overflow + amount = Math.min(amount, getLimit(stack)); + } + int newSize = setStackSize(attachedItems, stack, current + amount, action); + return newSize - current; + } + + @Override + public CompoundTag serializeNBT(HolderLookup.Provider provider) { + //TODO - 1.20.5: This is a copy of BasicInventorySlot#serializeNBT. We might need to also grab the specific overrides of + // that method as special component backed inventory slots, that then access and put that other data as a different component? + CompoundTag nbt = new CompoundTag(); + ItemStack current = getStack(); + if (!current.isEmpty()) { + nbt.put(NBTConstants.ITEM, current.save(provider)); + if (getCount() > current.getMaxStackSize()) { + nbt.putInt(NBTConstants.SIZE_OVERRIDE, getCount()); + } + } + return nbt; + } + + @Override + public void deserializeNBT(Provider provider, CompoundTag nbt) { + ItemStack stack = ItemStack.EMPTY; + if (nbt.contains(NBTConstants.ITEM, Tag.TAG_COMPOUND)) { + stack = ItemStack.parseOptional(provider, nbt.getCompound(NBTConstants.ITEM)); + NBTUtils.setIntIfPresent(nbt, NBTConstants.SIZE_OVERRIDE, stack::setCount); + } + setStack(stack); + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/item/ComponentBackedItemHandler.java b/src/main/java/mekanism/common/attachments/containers/item/ComponentBackedItemHandler.java new file mode 100644 index 00000000000..1f8d3e15cda --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/item/ComponentBackedItemHandler.java @@ -0,0 +1,62 @@ +package mekanism.common.attachments.containers.item; + +import java.util.List; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.inventory.IInventorySlot; +import mekanism.api.inventory.IMekanismInventory; +import mekanism.common.attachments.containers.ComponentBackedHandler; +import mekanism.common.attachments.containers.ContainerType; +import net.minecraft.core.Direction; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +//TODO - 1.20.5: Can we make this in of itself a non serializable non syncable component or something? +// Unfortunately I don't think so because the attachedTo stack might change instances +@NothingNullByDefault +public class ComponentBackedItemHandler extends ComponentBackedHandler implements IMekanismInventory { + + public ComponentBackedItemHandler(ItemStack attachedTo) { + super(attachedTo); + } + + @Override + protected ContainerType containerType() { + return ContainerType.ITEM; + } + + @Override + public List getInventorySlots(@Nullable Direction side) { + return getContainers(); + } + + @Nullable + @Override + public IInventorySlot getInventorySlot(int slot, @Nullable Direction side) { + return getContainer(slot); + } + + @Override + public int getSlots(@Nullable Direction side) { + return containerCount(); + } + + @Override + public ItemStack getStackInSlot(int slot, @Nullable Direction side) { + AttachedItems attachedItems = getAttached(); + return attachedItems == null ? ItemStack.EMPTY : attachedItems.get(slot); + } + + @Override + public boolean isInventoryEmpty(@Nullable Direction side) { + AttachedItems attachedItems = getAttached(); + if (attachedItems == null) { + return true; + } + for (ItemStack item : attachedItems) { + if (!item.isEmpty()) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/containers/item/ItemSlotsBuilder.java b/src/main/java/mekanism/common/attachments/containers/item/ItemSlotsBuilder.java new file mode 100644 index 00000000000..98ea1043326 --- /dev/null +++ b/src/main/java/mekanism/common/attachments/containers/item/ItemSlotsBuilder.java @@ -0,0 +1,689 @@ +package mekanism.common.attachments.containers.item; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.function.BiPredicate; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import mekanism.api.Action; +import mekanism.api.AutomationType; +import mekanism.api.chemical.Chemical; +import mekanism.api.chemical.ChemicalStack; +import mekanism.api.chemical.IChemicalHandler; +import mekanism.api.chemical.IChemicalTank; +import mekanism.api.chemical.gas.GasStack; +import mekanism.api.chemical.infuse.InfusionStack; +import mekanism.api.chemical.merged.MergedChemicalTank; +import mekanism.api.energy.IStrictEnergyHandler; +import mekanism.api.fluid.IExtendedFluidTank; +import mekanism.api.math.FloatingLong; +import mekanism.api.recipes.MekanismRecipe; +import mekanism.api.security.IItemSecurityUtils; +import mekanism.common.attachments.FilterAware; +import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.ContainsRecipe; +import mekanism.common.attachments.containers.IAttachedContainers; +import mekanism.common.attachments.containers.creator.BaseContainerCreator; +import mekanism.common.attachments.containers.creator.IBasicContainerCreator; +import mekanism.common.attachments.containers.fluid.AttachedFluids; +import mekanism.common.capabilities.Capabilities; +import mekanism.common.capabilities.MultiTypeCapability; +import mekanism.common.content.oredictionificator.OredictionificatorItemFilter; +import mekanism.common.integration.energy.EnergyCompatUtils; +import mekanism.common.inventory.slot.BasicInventorySlot; +import mekanism.common.inventory.slot.EnergyInventorySlot; +import mekanism.common.inventory.slot.FluidInventorySlot; +import mekanism.common.inventory.slot.QIODriveSlot; +import mekanism.common.inventory.slot.SecurityInventorySlot; +import mekanism.common.inventory.slot.chemical.ChemicalInventorySlot; +import mekanism.common.inventory.slot.chemical.GasInventorySlot; +import mekanism.common.inventory.slot.chemical.InfusionInventorySlot; +import mekanism.common.recipe.IMekanismRecipeTypeProvider; +import mekanism.common.recipe.lookup.cache.IInputRecipeCache; +import mekanism.common.registries.MekanismDataComponents; +import mekanism.common.tile.machine.TileEntityDigitalMiner; +import mekanism.common.tile.machine.TileEntityFormulaicAssemblicator; +import mekanism.common.tile.machine.TileEntityOredictionificator; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.FluidType; +import net.neoforged.neoforge.fluids.capability.IFluidHandler.FluidAction; +import net.neoforged.neoforge.fluids.capability.IFluidHandlerItem; +import org.jetbrains.annotations.NotNull; + +public class ItemSlotsBuilder { + + //Note: For a lot of slots with specific helper methods we can simply use a ComponentBackedInventorySlot as we don't have any overrides or desire to call those methods while on an itemstack + private static final IBasicContainerCreator BASIC_SLOT_CREATOR = (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, + containerIndex, BasicInventorySlot.alwaysTrueBi, BasicInventorySlot.alwaysTrueBi, BasicInventorySlot.alwaysTrue); + private static final IBasicContainerCreator BASIC_INPUT_SLOT_CREATOR = (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, + containerIndex, BasicInventorySlot.notExternal, BasicInventorySlot.alwaysTrueBi, BasicInventorySlot.alwaysTrue); + private static final IBasicContainerCreator OUTPUT_SLOT_CREATOR = (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, + containerIndex, BasicInventorySlot.alwaysTrueBi, BasicInventorySlot.internalOnly, BasicInventorySlot.alwaysTrue); + + //Copy of predicates from FuelInventorySlot + private static final BiPredicate<@NotNull ItemStack, @NotNull AutomationType> FUEL_CAN_EXTRACT = (stack, automationType) -> automationType == AutomationType.MANUAL || stack.getBurnTime(null) == 0; + private static final BiPredicate<@NotNull ItemStack, @NotNull AutomationType> FUEL_CAN_INSERT = (stack, automationType) -> stack.getBurnTime(null) > 0; + private static final IBasicContainerCreator FUEL_SLOT_CREATOR = (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, + containerIndex, FUEL_CAN_EXTRACT, FUEL_CAN_INSERT, BasicInventorySlot.alwaysTrue); + + //Security Inventory Slot + private static final BiPredicate<@NotNull ItemStack, @NotNull AutomationType> SECURITY_LOCK_CAN_EXTRACT = (stack, automationType) -> automationType == AutomationType.MANUAL || SecurityInventorySlot.LOCK_EXTRACT_PREDICATE.test(stack); + private static final BiPredicate<@NotNull ItemStack, @NotNull AutomationType> SECURITY_LOCK_CAN_INSERT = (stack, automationType) -> SecurityInventorySlot.LOCK_INSERT_PREDICATE.test(stack); + private static final IBasicContainerCreator SECURITY_LOCK_SLOT_CREATOR = (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, + containerIndex, SECURITY_LOCK_CAN_EXTRACT, SECURITY_LOCK_CAN_INSERT, SecurityInventorySlot.VALIDATOR); + + //FormulaInventorySlot + //Note: We skip making the extra checks based on the formula and just allow all items + private static final IBasicContainerCreator FORMULA_SLOT_CREATOR = (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, + containerIndex, BasicInventorySlot.alwaysTrueBi, BasicInventorySlot.alwaysTrueBi, TileEntityFormulaicAssemblicator.FORMULA_SLOT_VALIDATOR); + + //QOP drive slot + //Note: As we don't have to update the presence of a drive or remove it from the frequency we can make do with just using a basic slot + //TODO - 1.20.4: Evaluate if copy the notExternal is correct or do we want this to have some other checks + private static final IBasicContainerCreator QIO_DRIVE_SLOT_CREATOR = (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, + containerIndex, BasicInventorySlot.notExternal, BasicInventorySlot.notExternal, QIODriveSlot.IS_QIO_ITEM); + + //EnergyInventorySlot + //Note: As energy is untyped we don't have to do extra checks about what is currently stored or not on the attached stack + private static final BiPredicate<@NotNull ItemStack, @NotNull AutomationType> FILL_CONVERT_ENERGY_SLOT_CAN_EXTRACT = (stack, automationType) -> + //Allow extraction if something went horribly wrong, and we are not an energy container item or no longer have any energy left to give, + // or we are no longer a valid conversion, this might happen after a reload for example + automationType == AutomationType.MANUAL || !EnergyInventorySlot.fillInsertCheck(stack) && EnergyInventorySlot.getPotentialConversion(null, stack).isZero(); + private static final BiPredicate<@NotNull ItemStack, @NotNull AutomationType> FILL_CONVERT_ENERGY_SLOT_CAN_INSERT = (stack, automationType) -> { + if (EnergyInventorySlot.fillInsertCheck(stack)) { + return true; + } + //Note: We recheck about this being empty and that it is still valid as the conversion list might have changed, such as after a reload + // Unlike with the chemical conversions, we don't check if the type is "valid" as we only have one "type" of energy. + return !EnergyInventorySlot.getPotentialConversion(null, stack).isZero(); + }; + //Note: we mark all energy handler items as valid and have a more restrictive insert check so that we allow full containers when they are done being filled + // We also allow energy conversion of items that can be converted + private static final Predicate FILL_CONVERT_ENERGY_SLOT_VALIDATOR = stack -> EnergyCompatUtils.hasStrictEnergyHandler(stack) || !EnergyInventorySlot.getPotentialConversion(null, stack).isZero(); + private static final IBasicContainerCreator FILL_CONVERT_ENERGY_SLOT_CREATOR = (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, + containerIndex, FILL_CONVERT_ENERGY_SLOT_CAN_EXTRACT, FILL_CONVERT_ENERGY_SLOT_CAN_INSERT, FILL_CONVERT_ENERGY_SLOT_VALIDATOR); + + private static final BiPredicate<@NotNull ItemStack, @NotNull AutomationType> DRAIN_ENERGY_SLOT_CAN_EXTRACT = (stack, automationType) -> { + if (automationType == AutomationType.MANUAL) { + return true; + } + //Inversion of the insert check + IStrictEnergyHandler itemEnergyHandler = EnergyCompatUtils.getStrictEnergyHandler(stack); + return itemEnergyHandler == null || itemEnergyHandler.insertEnergy(FloatingLong.MAX_VALUE, Action.SIMULATE).equals(FloatingLong.MAX_VALUE); + }; + private static final BiPredicate<@NotNull ItemStack, @NotNull AutomationType> DRAIN_ENERGY_SLOT_CAN_INSERT = (stack, automationType) -> { + IStrictEnergyHandler itemEnergyHandler = EnergyCompatUtils.getStrictEnergyHandler(stack); + //if we can accept any energy that is currently stored in the container, then we allow inserting the item + return itemEnergyHandler != null && itemEnergyHandler.insertEnergy(FloatingLong.MAX_VALUE, Action.SIMULATE).smallerThan(FloatingLong.MAX_VALUE); + }; + private static final IBasicContainerCreator DRAIN_ENERGY_SLOT_CREATOR = (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, + containerIndex, DRAIN_ENERGY_SLOT_CAN_EXTRACT, DRAIN_ENERGY_SLOT_CAN_INSERT, EnergyInventorySlot.DRAIN_VALIDATOR); + + //Chemical conversions + private static final Function GAS_STACK_CONVERSION = stack -> GasInventorySlot.getPotentialConversion(null, stack); + private static final Function INFUSION_STACK_CONVERSION = stack -> InfusionInventorySlot.getPotentialConversion(null, stack); + + public static ItemSlotsBuilder builder() { + return new ItemSlotsBuilder(); + } + + private final List> slotCreators = new ArrayList<>(); + + private ItemSlotsBuilder() { + } + + public BaseContainerCreator build() { + return new BaseInventorySlotCreator(slotCreators); + } + + public ItemSlotsBuilder addBasicFactorySlots(int process, Predicate recipeInputPredicate) { + return addBasicFactorySlots(process, recipeInputPredicate, false); + } + + public ItemSlotsBuilder addBasicFactorySlots(int process, Predicate recipeInputPredicate, boolean secondaryOutput) { + IBasicContainerCreator inputSlotCreator = (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, + BasicInventorySlot.notExternal, BasicInventorySlot.alwaysTrueBi, recipeInputPredicate); + for (int i = 0; i < process; i++) { + //Note: We can just get away with using a simple input instead of a factory input slot and skip checking insert based on producing output + addSlot(inputSlotCreator) + .addOutput(); + if (secondaryOutput) { + addOutput(); + } + } + return this; + } + + public ItemSlotsBuilder addSlots(int count, IBasicContainerCreator creator) { + for (int i = 0; i < count; i++) { + addSlot(creator); + } + return this; + } + + public ItemSlotsBuilder addQIODriveSlots(int count) { + return addSlots(count, QIO_DRIVE_SLOT_CREATOR); + } + + public ItemSlotsBuilder addQIODashboardSlots() { + //TODO - 1.20.5: IMPLEMENT Figure out how to do this + //new PortableQIODashboardInventory(null, stack).getSlots() + return this; + } + + public ItemSlotsBuilder addMinerSlots(int count) { + return addSlots(count, (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, + //Allow extraction if it is manual or for internal usage, or if it is not a replace stack + //Note: We don't currently use internal for extraction anywhere here as we just shrink replace stacks directly + (stack, automationType) -> automationType != AutomationType.EXTERNAL || !TileEntityDigitalMiner.isSavedReplaceTarget(attachedTo, stack.getItem()), + (stack, automationType) -> automationType != AutomationType.EXTERNAL || TileEntityDigitalMiner.isSavedReplaceTarget(attachedTo, stack.getItem()), + BasicInventorySlot.alwaysTrue)); + } + + public ItemSlotsBuilder addFormulaSlot() { + return addSlot(FORMULA_SLOT_CREATOR); + } + + public ItemSlotsBuilder addFormulaCraftingSlot(int count) { + return addSlots(count, (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, BasicInventorySlot.alwaysTrueBi, + (stack, automationType) -> automationType == AutomationType.INTERNAL || !attachedTo.getOrDefault(MekanismDataComponents.AUTO, false), BasicInventorySlot.alwaysFalse)); + } + + public ItemSlotsBuilder addLockSlot() { + return addSlot(SECURITY_LOCK_SLOT_CREATOR); + } + + public ItemSlotsBuilder addUnlockSlot() { + return addSlot((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, SECURITY_LOCK_CAN_INSERT, (stack, automationType) -> { + UUID ownerUUID = IItemSecurityUtils.INSTANCE.getOwnerUUID(stack); + return ownerUUID != null && ownerUUID.equals(IItemSecurityUtils.INSTANCE.getOwnerUUID(attachedTo)); + }, SecurityInventorySlot.VALIDATOR)); + } + + public ItemSlotsBuilder addSlot(IBasicContainerCreator slot) { + slotCreators.add(slot); + return this; + } + + public ItemSlotsBuilder addFuelSlot() { + return addSlot(FUEL_SLOT_CREATOR); + } + + public ItemSlotsBuilder addOredictionificatorInput() { + return addSlot((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, BasicInventorySlot.notExternal, BasicInventorySlot.alwaysTrueBi, + stack -> TileEntityOredictionificator.hasResult(attachedTo.getOrDefault(MekanismDataComponents.FILTER_AWARE, FilterAware.EMPTY).getEnabled(OredictionificatorItemFilter.class), stack))); + } + + public ItemSlotsBuilder addOutput() { + return addSlot(OUTPUT_SLOT_CREATOR); + } + + public ItemSlotsBuilder addOutput(int count) { + return addSlots(count, OUTPUT_SLOT_CREATOR); + } + + public ItemSlotsBuilder addBasic(int count) { + return addSlots(count, BASIC_SLOT_CREATOR); + } + + public ItemSlotsBuilder addInput(int count) { + return addSlots(count, BASIC_INPUT_SLOT_CREATOR); + } + + //TODO - 1.20.5: Docs that the predicate shouldn't access the attached stack? More likely we just want to not be passing it via the addAttachment stuff + public ItemSlotsBuilder addInput(Predicate<@NotNull ItemStack> isItemValid) { + return addSlot((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, BasicInventorySlot.notExternal, BasicInventorySlot.alwaysTrueBi, isItemValid)); + } + + public ItemSlotsBuilder addInput(IMekanismRecipeTypeProvider recipeType, + ContainsRecipe containsRecipe) { + return addInput(stack -> containsRecipe.check(recipeType.getInputCache(), null, stack)); + } + + public ItemSlotsBuilder addEnergy() { + return addSlot(FILL_CONVERT_ENERGY_SLOT_CREATOR); + } + + public ItemSlotsBuilder addDrainEnergy() { + return addSlot(DRAIN_ENERGY_SLOT_CREATOR); + } + + private boolean canFluidFill(ItemStack attachedTo, int tankIndex, ItemStack stack) { + //Copy of FluidInventorySlot#getFillPredicate + IFluidHandlerItem fluidHandlerItem = Capabilities.FLUID.getCapability(stack); + if (fluidHandlerItem != null) { + //TODO - 1.20.5: We cache the tank as a component backed one on the capability, can we access it via that instead?? + IExtendedFluidTank fluidTank = ContainerType.FLUID.createContainer(attachedTo, tankIndex); + for (int tank = 0, tanks = fluidHandlerItem.getTanks(); tank < tanks; tank++) { + FluidStack fluidInTank = fluidHandlerItem.getFluidInTank(tank); + if (!fluidInTank.isEmpty() && fluidTank.insert(fluidInTank, Action.SIMULATE, AutomationType.INTERNAL).getAmount() < fluidInTank.getAmount()) { + //True if we can fill the tank with any of our contents + // Note: We need to recheck the fact the fluid is not empty and that it is valid, + // in case the item has multiple tanks and only some of the fluids are valid + return true; + } + } + } + return false; + } + + public ItemSlotsBuilder addFluidFillSlot(int tankIndex) { + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, BasicInventorySlot.manualOnly, + (stack, automationType) -> canFluidFill(attachedTo, tankIndex, stack), BasicInventorySlot.alwaysTrue))); + } + + public ItemSlotsBuilder addFluidDrainSlot(int tankIndex) { + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, BasicInventorySlot.manualOnly, (stack, automationType) -> { + //Copy of FluidInventorySlot's drain insert predicate + IFluidHandlerItem itemFluidHandler = FluidInventorySlot.tryGetFluidHandlerUnstacked(stack); + if (itemFluidHandler != null) { + //Note: We don't need to create a fake tank using the container type, as we only care about the stored type + AttachedFluids attachedFluids = attachedTo.get(MekanismDataComponents.ATTACHED_FLUIDS); + FluidStack fluidInTank = attachedFluids == null ? FluidStack.EMPTY : attachedFluids.get(tankIndex); + //True if the tanks contents are valid, and we can fill the item with any of the contents + if (fluidInTank.isEmpty()) { + return FluidInventorySlot.isNonFullFluidContainer(itemFluidHandler); + } + return itemFluidHandler.fill(fluidInTank, FluidAction.SIMULATE) > 0; + } + return false; + }, BasicInventorySlot.alwaysTrue))); + } + + public ItemSlotsBuilder addFluidInputSlot(int tankIndex) { + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, BasicInventorySlot.manualOnly, (stack, automationType) -> { + //Copy of FluidInventorySlot#getInputPredicate + IFluidHandlerItem fluidHandlerItem = FluidInventorySlot.tryGetFluidHandlerUnstacked(stack); + if (fluidHandlerItem != null) { + //TODO - 1.20.5: We cache the tank as a component backed one on the capability, can we access it via that instead?? + IExtendedFluidTank fluidTank = ContainerType.FLUID.createContainer(attachedTo, tankIndex); + boolean hasEmpty = false; + for (int tank = 0, tanks = fluidHandlerItem.getTanks(); tank < tanks; tank++) { + FluidStack fluidInTank = fluidHandlerItem.getFluidInTank(tank); + if (fluidInTank.isEmpty()) { + hasEmpty = true; + } else if (fluidTank.insert(fluidInTank, Action.SIMULATE, AutomationType.INTERNAL).getAmount() < fluidInTank.getAmount()) { + //True if the items contents are valid, and we can fill the tank with any of our contents + return true; + } + } + //If we have no valid fluids/can't fill the tank with it + if (fluidTank.isEmpty()) { + //we return if there is at least one empty tank in the item so that we can then drain into it + return hasEmpty; + } + FluidStack fluid = fluidTank.getFluid(); + if (fluid.getAmount() < FluidType.BUCKET_VOLUME) { + //Workaround for buckets not being able to be filled until we have enough of our volume + fluid = fluid.copyWithAmount(FluidType.BUCKET_VOLUME); + } + return fluidHandlerItem.fill(fluid, FluidAction.SIMULATE) > 0; + } + return false; + }, BasicInventorySlot.alwaysTrue))); + } + + public ItemSlotsBuilder addFluidRotarySlot(int tankIndex) { + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, BasicInventorySlot.manualOnly, (stack, automationType) -> { + //Copy of FluidInventorySlot's rotary insert predicate + IFluidHandlerItem fluidHandlerItem = Capabilities.FLUID.getCapability(stack); + if (fluidHandlerItem != null) { + boolean mode = attachedTo.getOrDefault(MekanismDataComponents.ROTARY_MODE, false); + //Mode == true if fluid to gas + boolean allEmpty = true; + IExtendedFluidTank fluidTank = null; + for (int tank = 0, tanks = fluidHandlerItem.getTanks(); tank < tanks; tank++) { + FluidStack fluidInTank = fluidHandlerItem.getFluidInTank(tank); + if (!fluidInTank.isEmpty()) { + if (fluidTank == null) { + //Lazily initialize the tank + //TODO - 1.20.5: We cache the tank as a component backed one on the capability, can we access it via that instead?? + fluidTank = ContainerType.FLUID.createContainer(attachedTo, tankIndex); + } + if (fluidTank.insert(fluidInTank, Action.SIMULATE, AutomationType.INTERNAL).getAmount() < fluidInTank.getAmount()) { + //True if we are the input tank and the items contents are valid and can fill the tank with any of our contents + return mode; + } + allEmpty = false; + } + } + //We want to try and drain the tank AND we are not the input tank + return allEmpty && !mode; + } + return false; + }, BasicInventorySlot.alwaysTrue))); + } + + public ItemSlotsBuilder addFluidFuelSlot(int tankIndex, Predicate<@NotNull ItemStack> hasFuelValue) { + //Copy of FluidFuelInventorySlot's forFuel insert and extract predicates + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, (stack, automationType) -> { + IFluidHandlerItem fluidHandlerItem = Capabilities.FLUID.getCapability(stack); + if (fluidHandlerItem != null) { + int tanks = fluidHandlerItem.getTanks(); + if (tanks > 0) { + //TODO - 1.20.5: We cache the tank as a component backed one on the capability, can we access it via that instead?? + IExtendedFluidTank fluidTank = ContainerType.FLUID.createContainer(attachedTo, tankIndex); + for (int tank = 0; tank < tanks; tank++) { + if (fluidTank.isFluidValid(fluidHandlerItem.getFluidInTank(tank))) { + //False if the items contents are still valid + return false; + } + } + } + //Only allow extraction if our item is out of fluid, but also verify there is no conversion for it + } + //Always allow extraction if something went horribly wrong, and we are not a fluid item AND we can't provide a valid type of chemical + // This might happen after a reload for example + return !hasFuelValue.test(stack); + }, (stack, automationType) -> hasFuelValue.test(stack) || canFluidFill(attachedTo, tankIndex, stack), BasicInventorySlot.alwaysTrue))); + } + + private , STACK extends ChemicalStack> boolean canChemicalDrainInsert(ItemStack attachedTo, int tankIndex, + ItemStack stack, Supplier>> componentType, + MultiTypeCapability> chemicalCapability) { + //Copy of logic from ChemicalInventorySlot#getDrainInsertPredicate + IChemicalHandler handler = chemicalCapability.getCapability(stack); + if (handler != null) { + //Note: We don't need to create a fake tank using the container type, as we only care about the stored type + IAttachedContainers containers = attachedTo.get(componentType); + STACK chemicalInTank = containers == null ? null : containers.get(tankIndex); + if (chemicalInTank == null || chemicalInTank.isEmpty()) { + //If the chemical tank is empty, accept the chemical item as long as it is not full + for (int tank = 0; tank < handler.getTanks(); tank++) { + if (handler.getChemicalInTank(tank).getAmount() < handler.getTankCapacity(tank)) { + //True if we have any space in this tank + return true; + } + } + return false; + } + //Otherwise, if we can accept any of the chemical that is currently stored in the tank, then we allow inserting the item + return handler.insertChemical(chemicalInTank, Action.SIMULATE).getAmount() < chemicalInTank.getAmount(); + } + return false; + } + + private , STACK extends ChemicalStack, TANK extends IChemicalTank> boolean canChemicalFillExtract( + ItemStack attachedTo, int tankIndex, ItemStack stack, ContainerType containerType, + MultiTypeCapability> chemicalCapability) { + //Copy of logic from ChemicalInventorySlot#getFillExtractPredicate + IChemicalHandler handler = chemicalCapability.getCapability(stack); + if (handler != null) { + IChemicalTank chemicalTank = null; + for (int tank = 0; tank < handler.getTanks(); tank++) { + STACK storedChemical = handler.getChemicalInTank(tank); + if (!storedChemical.isEmpty()) { + if (chemicalTank == null) { + //TODO - 1.20.5: We cache the tank as a component backed one on the capability, can we access it via that instead?? + chemicalTank = containerType.createContainer(attachedTo, tankIndex); + } + if (chemicalTank.isValid(storedChemical)) { + //False if the item isn't empty and the contents are still valid + return false; + } + } + } + //If we have no contents that are still valid, allow extraction + } + //Always allow it if we are not a chemical item (For example this may be true for hybrid inventory slots) + return true; + } + + private , STACK extends ChemicalStack, TANK extends IChemicalTank> boolean canChemicalFillInsert( + ItemStack attachedTo, int tankIndex, ItemStack stack, ContainerType containerType, + MultiTypeCapability> chemicalCapability) { + //Copy of logic from ChemicalInventorySlot#fillInsertCheck + IChemicalHandler handler = chemicalCapability.getCapability(stack); + if (handler != null) { + TANK chemicalTank = null; + for (int tank = 0; tank < handler.getTanks(); tank++) { + STACK chemicalInTank = handler.getChemicalInTank(tank); + if (!chemicalInTank.isEmpty()) { + if (chemicalTank == null) { + //TODO - 1.20.5: We cache the tank as a component backed one on the capability, can we access it via that instead?? + chemicalTank = containerType.createContainer(attachedTo, tankIndex); + } + if (chemicalTank.insert(chemicalInTank, Action.SIMULATE, AutomationType.INTERNAL).getAmount() < chemicalInTank.getAmount()) { + //True if we can fill the tank with any of our contents + // Note: We need to recheck the fact the chemical is not empty in case the item has multiple tanks and only some of the chemicals are valid + return true; + } + } + } + } + return false; + } + + private , STACK extends ChemicalStack, TANK extends IChemicalTank> boolean canChemicalFillOrConvertExtract( + ItemStack attachedTo, int tankIndex, ItemStack stack, ContainerType containerType, + MultiTypeCapability> chemicalCapability, Function potentialConversionSupplier) { + //Copy of logic from ChemicalInventorySlot#getFillOrConvertExtractPredicate + IChemicalHandler handler = chemicalCapability.getCapability(stack); + TANK chemicalTank = null; + if (handler != null) { + int tanks = handler.getTanks(); + if (tanks > 0) { + //TODO - 1.20.5: We cache the tank as a component backed one on the capability, can we access it via that instead?? + chemicalTank = containerType.createContainer(attachedTo, tankIndex); + for (int tank = 0; tank < tanks; tank++) { + if (chemicalTank.isValid(handler.getChemicalInTank(tank))) { + //False if the items contents are still valid + return false; + } + } + } + //Only allow extraction if our item is out of chemical, and doesn't have a valid conversion for it + } + //Always allow extraction if something went horribly wrong, and we are not a chemical item AND we can't provide a valid type of chemical + // This might happen after a reload for example + STACK conversion = potentialConversionSupplier.apply(stack); + if (conversion.isEmpty()) { + return true; + } else if (chemicalTank == null) { + //If we haven't resolved the tank yet, we need to do it now + //TODO - 1.20.5: We cache the tank as a component backed one on the capability, can we access it via that instead?? + chemicalTank = containerType.createContainer(attachedTo, tankIndex); + } + return !chemicalTank.isValid(conversion); + } + + private , STACK extends ChemicalStack, TANK extends IChemicalTank> boolean canChemicalFillOrConvertInsert( + ItemStack attachedTo, int tankIndex, ItemStack stack, ContainerType containerType, + MultiTypeCapability> chemicalCapability, Function potentialConversionSupplier) { + //Copy of logic from ChemicalInventorySlot#getFillOrConvertInsertPredicate + TANK chemicalTank = null; + {//Fill insert check logic, we want to avoid resolving the tank as long as possible + IChemicalHandler handler = chemicalCapability.getCapability(stack); + if (handler != null) { + for (int tank = 0; tank < handler.getTanks(); tank++) { + STACK chemicalInTank = handler.getChemicalInTank(tank); + if (!chemicalInTank.isEmpty()) { + if (chemicalTank == null) { + //TODO - 1.20.5: We cache the tank as a component backed one on the capability, can we access it via that instead?? + chemicalTank = containerType.createContainer(attachedTo, tankIndex); + } + if (chemicalTank.insert(chemicalInTank, Action.SIMULATE, AutomationType.INTERNAL).getAmount() < chemicalInTank.getAmount()) { + //True if we can fill the tank with any of our contents + // Note: We need to recheck the fact the chemical is not empty in case the item has multiple tanks and only some of the chemicals are valid + return true; + } + } + } + } + } + STACK conversion = potentialConversionSupplier.apply(stack); + //Note: We recheck about this being empty and that it is still valid as the conversion list might have changed, such as after a reload + if (conversion.isEmpty()) { + return false; + } else if (chemicalTank == null) { + //If we haven't resolved the tank yet, we need to do it now + //TODO - 1.20.5: We cache the tank as a component backed one on the capability, can we access it via that instead?? + chemicalTank = containerType.createContainer(attachedTo, tankIndex); + } + if (chemicalTank.insert(conversion, Action.SIMULATE, AutomationType.INTERNAL).getAmount() < conversion.getAmount()) { + //If we can insert the converted substance into the tank allow insertion + return true; + } + //If we can't because the tank is full, we do a slightly less accurate check and validate that the type matches the stored type + // and that it is still actually valid for the tank, as a reload could theoretically make it no longer be valid while there is still some stored + return chemicalTank.getNeeded() == 0 && chemicalTank.isTypeEqual(conversion) && chemicalTank.isValid(conversion); + } + + public ItemSlotsBuilder addGasFillSlot(int tankIndex) { + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, + (stack, automationType) -> automationType == AutomationType.MANUAL || canChemicalFillExtract(attachedTo, tankIndex, stack, ContainerType.GAS, Capabilities.GAS), + (stack, automationType) -> canChemicalFillInsert(attachedTo, tankIndex, stack, ContainerType.GAS, Capabilities.GAS), BasicInventorySlot.alwaysTrue))); + } + + public ItemSlotsBuilder addGasFillOrConvertSlot(int tankIndex) { + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, + (stack, automationType) -> automationType == AutomationType.MANUAL || canChemicalFillOrConvertExtract(attachedTo, tankIndex, stack, ContainerType.GAS, Capabilities.GAS, GAS_STACK_CONVERSION), + (stack, automationType) -> canChemicalFillOrConvertInsert(attachedTo, tankIndex, stack, ContainerType.GAS, Capabilities.GAS, GAS_STACK_CONVERSION), BasicInventorySlot.alwaysTrue))); + } + + public ItemSlotsBuilder addGasDrainSlot(int tankIndex) { + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, + (stack, automationType) -> automationType == AutomationType.MANUAL || !canChemicalDrainInsert(attachedTo, tankIndex, stack, MekanismDataComponents.ATTACHED_GASES, Capabilities.GAS), + (stack, automationType) -> canChemicalDrainInsert(attachedTo, tankIndex, stack, MekanismDataComponents.ATTACHED_GASES, Capabilities.GAS), BasicInventorySlot.alwaysTrue))); + } + + public ItemSlotsBuilder addGasRotaryDrainSlot(int tankIndex) { + //Copy of logic from GasInventorySlot#rotaryDrain + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, + (stack, automationType) -> { + if (automationType == AutomationType.MANUAL) { + return true; + } + //Copy of the insert check but inverted + return !attachedTo.getOrDefault(MekanismDataComponents.ROTARY_MODE, false) || + !canChemicalDrainInsert(attachedTo, tankIndex, stack, MekanismDataComponents.ATTACHED_GASES, Capabilities.GAS); + }, + (stack, automationType) -> attachedTo.getOrDefault(MekanismDataComponents.ROTARY_MODE, false) && + canChemicalDrainInsert(attachedTo, tankIndex, stack, MekanismDataComponents.ATTACHED_GASES, Capabilities.GAS), + BasicInventorySlot.alwaysTrue))); + } + + public ItemSlotsBuilder addGasRotaryFillSlot(int tankIndex) { + //Copy of logic from GasInventorySlot#rotaryFill + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, + (stack, automationType) -> automationType == AutomationType.MANUAL || canChemicalFillExtract(attachedTo, tankIndex, stack, ContainerType.GAS, Capabilities.GAS), + (stack, automationType) -> !attachedTo.getOrDefault(MekanismDataComponents.ROTARY_MODE, false) && + canChemicalFillInsert(attachedTo, tankIndex, stack, ContainerType.GAS, Capabilities.GAS), + BasicInventorySlot.alwaysTrue))); + } + + public ItemSlotsBuilder addInfusionFillOrConvertSlot(int tankIndex) { + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, + (stack, automationType) -> automationType == AutomationType.MANUAL || canChemicalFillOrConvertExtract(attachedTo, tankIndex, stack, ContainerType.INFUSION, Capabilities.INFUSION, INFUSION_STACK_CONVERSION), + (stack, automationType) -> canChemicalFillOrConvertInsert(attachedTo, tankIndex, stack, ContainerType.INFUSION, Capabilities.INFUSION, INFUSION_STACK_CONVERSION), BasicInventorySlot.alwaysTrue))); + } + + public ItemSlotsBuilder addPigmentFillSlot(int tankIndex) { + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, + (stack, automationType) -> automationType == AutomationType.MANUAL || canChemicalFillExtract(attachedTo, tankIndex, stack, ContainerType.PIGMENT, Capabilities.PIGMENT), + (stack, automationType) -> canChemicalFillInsert(attachedTo, tankIndex, stack, ContainerType.PIGMENT, Capabilities.PIGMENT), BasicInventorySlot.alwaysTrue))); + } + + public ItemSlotsBuilder addPigmentDrainSlot(int tankIndex) { + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, + (stack, automationType) -> automationType == AutomationType.MANUAL || !canChemicalDrainInsert(attachedTo, tankIndex, stack, MekanismDataComponents.ATTACHED_PIGMENTS, Capabilities.PIGMENT), + (stack, automationType) -> canChemicalDrainInsert(attachedTo, tankIndex, stack, MekanismDataComponents.ATTACHED_PIGMENTS, Capabilities.PIGMENT), BasicInventorySlot.alwaysTrue))); + } + + public ItemSlotsBuilder addSlurryDrainSlot(int tankIndex) { + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, + (stack, automationType) -> automationType == AutomationType.MANUAL || !canChemicalDrainInsert(attachedTo, tankIndex, stack, MekanismDataComponents.ATTACHED_SLURRIES, Capabilities.SLURRY), + (stack, automationType) -> canChemicalDrainInsert(attachedTo, tankIndex, stack, MekanismDataComponents.ATTACHED_SLURRIES, Capabilities.SLURRY), BasicInventorySlot.alwaysTrue))); + } + + //TODO - 1.20.5: Test this + public ItemSlotsBuilder addMergedChemicalFillSlot(int gasIndex, int infusionIndex, int pigmentIndex, int slurryIndex) { + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, (stack, automationType) -> { + if (automationType == AutomationType.MANUAL) { + //Always allow the player to manually extract + return true; + } + MergedChemicalTank chemicalTank = createMergedTank(attachedTo, gasIndex, infusionIndex, pigmentIndex, slurryIndex); + Predicate<@NotNull ItemStack> gasExtractPredicate = ChemicalInventorySlot.getFillExtractPredicate(chemicalTank.getGasTank(), Capabilities.GAS); + Predicate<@NotNull ItemStack> infusionExtractPredicate = ChemicalInventorySlot.getFillExtractPredicate(chemicalTank.getInfusionTank(), Capabilities.INFUSION); + Predicate<@NotNull ItemStack> pigmentExtractPredicate = ChemicalInventorySlot.getFillExtractPredicate(chemicalTank.getPigmentTank(), Capabilities.PIGMENT); + Predicate<@NotNull ItemStack> slurryExtractPredicate = ChemicalInventorySlot.getFillExtractPredicate(chemicalTank.getSlurryTank(), Capabilities.SLURRY); + return switch (chemicalTank.getCurrent()) { + case GAS -> gasExtractPredicate.test(stack); + case INFUSION -> infusionExtractPredicate.test(stack); + case PIGMENT -> pigmentExtractPredicate.test(stack); + case SLURRY -> slurryExtractPredicate.test(stack); + //Tank is empty, check all our extraction predicates + case EMPTY -> gasExtractPredicate.test(stack) && infusionExtractPredicate.test(stack) && pigmentExtractPredicate.test(stack) && + slurryExtractPredicate.test(stack); + }; + }, (stack, automationType) -> { + MergedChemicalTank chemicalTank = createMergedTank(attachedTo, gasIndex, infusionIndex, pigmentIndex, slurryIndex); + return switch (chemicalTank.getCurrent()) { + case GAS -> ChemicalInventorySlot.fillInsertCheck(chemicalTank.getGasTank(), Capabilities.GAS, stack); + case INFUSION -> ChemicalInventorySlot.fillInsertCheck(chemicalTank.getInfusionTank(), Capabilities.INFUSION, stack); + case PIGMENT -> ChemicalInventorySlot.fillInsertCheck(chemicalTank.getPigmentTank(), Capabilities.PIGMENT, stack); + case SLURRY -> ChemicalInventorySlot.fillInsertCheck(chemicalTank.getSlurryTank(), Capabilities.SLURRY, stack); + //Tank is empty, only allow it if one of the chemical insert predicates matches + case EMPTY -> ChemicalInventorySlot.fillInsertCheck(chemicalTank.getGasTank(), Capabilities.GAS, stack) || + ChemicalInventorySlot.fillInsertCheck(chemicalTank.getInfusionTank(), Capabilities.INFUSION, stack) || + ChemicalInventorySlot.fillInsertCheck(chemicalTank.getPigmentTank(), Capabilities.PIGMENT, stack) || + ChemicalInventorySlot.fillInsertCheck(chemicalTank.getSlurryTank(), Capabilities.SLURRY, stack); + }; + }, BasicInventorySlot.alwaysTrue))); + } + + private static MergedChemicalTank createMergedTank(ItemStack attachedTo, int gasIndex, int infusionIndex, int pigmentIndex, int slurryIndex) { + return MergedChemicalTank.create( + ContainerType.GAS.createContainer(attachedTo, gasIndex), + ContainerType.INFUSION.createContainer(attachedTo, infusionIndex), + ContainerType.PIGMENT.createContainer(attachedTo, pigmentIndex), + ContainerType.SLURRY.createContainer(attachedTo, slurryIndex) + ); + } + + private boolean canInsertMerged(ItemStack attachedTo, int gasIndex, int infusionIndex, int pigmentIndex, int slurryIndex, ItemStack stack) { + //TODO - 1.20.5: Figure out the merged tanks + MergedChemicalTank chemicalTank = createMergedTank(attachedTo, gasIndex, infusionIndex, pigmentIndex, slurryIndex); + //TODO - 1.20.5: Improve this so we aren't looking up tanks multiple times? + Predicate<@NotNull ItemStack> gasInsertPredicate = ChemicalInventorySlot.getDrainInsertPredicate(chemicalTank.getGasTank(), Capabilities.GAS); + Predicate<@NotNull ItemStack> infusionInsertPredicate = ChemicalInventorySlot.getDrainInsertPredicate(chemicalTank.getInfusionTank(), Capabilities.INFUSION); + Predicate<@NotNull ItemStack> pigmentInsertPredicate = ChemicalInventorySlot.getDrainInsertPredicate(chemicalTank.getPigmentTank(), Capabilities.PIGMENT); + Predicate<@NotNull ItemStack> slurryInsertPredicate = ChemicalInventorySlot.getDrainInsertPredicate(chemicalTank.getSlurryTank(), Capabilities.SLURRY); + return switch (chemicalTank.getCurrent()) { + case GAS -> gasInsertPredicate.test(stack); + case INFUSION -> infusionInsertPredicate.test(stack); + case PIGMENT -> pigmentInsertPredicate.test(stack); + case SLURRY -> slurryInsertPredicate.test(stack); + //Tank is empty, check if any insert predicate is valid + case EMPTY -> gasInsertPredicate.test(stack) || infusionInsertPredicate.test(stack) || pigmentInsertPredicate.test(stack) || + slurryInsertPredicate.test(stack); + }; + } + + //TODO - 1.20.5: Test this + public ItemSlotsBuilder addMergedChemicalDrainSlot(int gasIndex, int infusionIndex, int pigmentIndex, int slurryIndex) { + return addSlot(((type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo, containerIndex, (stack, automationType) -> { + if (automationType == AutomationType.MANUAL) { + return true; + } + return !canInsertMerged(attachedTo, gasIndex, infusionIndex, pigmentIndex, slurryIndex, stack); + }, (stack, automationType) -> canInsertMerged(attachedTo, gasIndex, infusionIndex, pigmentIndex, slurryIndex, stack), BasicInventorySlot.alwaysTrue))); + } + + private static class BaseInventorySlotCreator extends BaseContainerCreator { + + public BaseInventorySlotCreator(List> creators) { + super(creators); + } + + @Override + public AttachedItems initStorage(int containers) { + return new AttachedItems(containers); + } + } +} \ No newline at end of file diff --git a/src/main/java/mekanism/common/attachments/qio/PortableQIODashboardInventory.java b/src/main/java/mekanism/common/attachments/qio/PortableQIODashboardInventory.java index 08d8ad87696..2120a83c090 100644 --- a/src/main/java/mekanism/common/attachments/qio/PortableQIODashboardInventory.java +++ b/src/main/java/mekanism/common/attachments/qio/PortableQIODashboardInventory.java @@ -7,7 +7,6 @@ import mekanism.common.content.qio.IQIOCraftingWindowHolder; import mekanism.common.content.qio.QIOCraftingWindow; import mekanism.common.content.qio.QIOFrequency; -import mekanism.common.inventory.slot.CraftingWindowInventorySlot; import mekanism.common.registries.MekanismDataComponents; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; @@ -32,7 +31,7 @@ public PortableQIODashboardInventory(@Nullable Level level, ItemStack stack) { QIOCraftingWindow craftingWindow = new QIOCraftingWindow(this, (byte) tableIndex); craftingWindows[tableIndex] = craftingWindow; for (int slot = 0; slot < 9; slot++) { - CraftingWindowInventorySlot inputSlot = craftingWindow.getInputSlot(slot); + IInventorySlot inputSlot = craftingWindow.getInputSlot(slot); slots.add(inputSlot); if (contents != null) { //Note: setStack will ensure the stack is copied diff --git a/src/main/java/mekanism/common/capabilities/chemical/item/ChemicalTankRateLimitChemicalTank.java b/src/main/java/mekanism/common/capabilities/chemical/item/ChemicalTankRateLimitChemicalTank.java deleted file mode 100644 index 86fd689e01b..00000000000 --- a/src/main/java/mekanism/common/capabilities/chemical/item/ChemicalTankRateLimitChemicalTank.java +++ /dev/null @@ -1,91 +0,0 @@ -package mekanism.common.capabilities.chemical.item; - -import mekanism.api.Action; -import mekanism.api.AutomationType; -import mekanism.api.IContentsListener; -import mekanism.api.annotations.NothingNullByDefault; -import mekanism.api.chemical.Chemical; -import mekanism.api.chemical.ChemicalStack; -import mekanism.api.chemical.ChemicalTankBuilder; -import mekanism.api.chemical.attribute.ChemicalAttributeValidator; -import mekanism.api.chemical.gas.Gas; -import mekanism.api.chemical.gas.GasStack; -import mekanism.api.chemical.gas.IGasHandler; -import mekanism.api.chemical.gas.IGasTank; -import mekanism.api.chemical.infuse.IInfusionHandler; -import mekanism.api.chemical.infuse.IInfusionTank; -import mekanism.api.chemical.infuse.InfuseType; -import mekanism.api.chemical.infuse.InfusionStack; -import mekanism.api.chemical.pigment.IPigmentHandler; -import mekanism.api.chemical.pigment.IPigmentTank; -import mekanism.api.chemical.pigment.Pigment; -import mekanism.api.chemical.pigment.PigmentStack; -import mekanism.api.chemical.slurry.ISlurryHandler; -import mekanism.api.chemical.slurry.ISlurryTank; -import mekanism.api.chemical.slurry.Slurry; -import mekanism.api.chemical.slurry.SlurryStack; -import mekanism.common.capabilities.chemical.variable.RateLimitChemicalTank; -import mekanism.common.tier.ChemicalTankTier; -import org.jetbrains.annotations.Nullable; - -@NothingNullByDefault -public abstract class ChemicalTankRateLimitChemicalTank, STACK extends ChemicalStack> - extends RateLimitChemicalTank { - - private final boolean isCreative; - - private ChemicalTankRateLimitChemicalTank(ChemicalTankTier tier, ChemicalTankBuilder tankBuilder, @Nullable IContentsListener listener) { - super(tier::getOutput, tier::getStorage, tankBuilder.alwaysTrueBi, tankBuilder.alwaysTrueBi, tankBuilder.alwaysTrue, - tier == ChemicalTankTier.CREATIVE ? ChemicalAttributeValidator.ALWAYS_ALLOW : null, listener); - isCreative = tier == ChemicalTankTier.CREATIVE; - } - - @Override - public STACK insert(STACK stack, Action action, AutomationType automationType) { - return super.insert(stack, action.combine(!isCreative), automationType); - } - - @Override - public STACK extract(long amount, Action action, AutomationType automationType) { - return super.extract(amount, action.combine(!isCreative), automationType); - } - - /** - * {@inheritDoc} - * - * Note: We are only patching {@link #setStackSize(long, Action)}, as both {@link #growStack(long, Action)} and {@link #shrinkStack(long, Action)} are wrapped through - * this method. - */ - @Override - public long setStackSize(long amount, Action action) { - return super.setStackSize(amount, action.combine(!isCreative)); - } - - public static class GasTankRateLimitChemicalTank extends ChemicalTankRateLimitChemicalTank implements IGasHandler, IGasTank { - - public GasTankRateLimitChemicalTank(ChemicalTankTier tier, @Nullable IContentsListener listener) { - super(tier, ChemicalTankBuilder.GAS, listener); - } - } - - public static class InfusionTankRateLimitChemicalTank extends ChemicalTankRateLimitChemicalTank implements IInfusionHandler, IInfusionTank { - - public InfusionTankRateLimitChemicalTank(ChemicalTankTier tier, @Nullable IContentsListener listener) { - super(tier, ChemicalTankBuilder.INFUSION, listener); - } - } - - public static class PigmentTankRateLimitChemicalTank extends ChemicalTankRateLimitChemicalTank implements IPigmentHandler, IPigmentTank { - - public PigmentTankRateLimitChemicalTank(ChemicalTankTier tier, @Nullable IContentsListener listener) { - super(tier, ChemicalTankBuilder.PIGMENT, listener); - } - } - - public static class SlurryTankRateLimitChemicalTank extends ChemicalTankRateLimitChemicalTank implements ISlurryHandler, ISlurryTank { - - public SlurryTankRateLimitChemicalTank(ChemicalTankTier tier, @Nullable IContentsListener listener) { - super(tier, ChemicalTankBuilder.SLURRY, listener); - } - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/capabilities/chemical/item/ChemicalTankSpec.java b/src/main/java/mekanism/common/capabilities/chemical/item/ChemicalTankSpec.java index b008443491a..799f67164b9 100644 --- a/src/main/java/mekanism/common/capabilities/chemical/item/ChemicalTankSpec.java +++ b/src/main/java/mekanism/common/capabilities/chemical/item/ChemicalTankSpec.java @@ -11,6 +11,8 @@ import mekanism.api.chemical.IChemicalTank; import mekanism.api.chemical.attribute.ChemicalAttributeValidator; import mekanism.api.functions.ConstantPredicates; +import mekanism.common.attachments.containers.chemical.ChemicalTanksBuilder; +import mekanism.common.attachments.containers.chemical.ComponentBackedChemicalTank; import mekanism.common.capabilities.GenericTankSpec; import net.minecraft.world.item.ItemStack; import net.neoforged.neoforge.common.util.TriPredicate; @@ -56,6 +58,18 @@ public , TANK extends IChemicalTank canInsert.test(chemical, automationType, stack), isValid, validator, null); } + //TODO - 1.20.5: Re-evaluate this + public , TANK extends ComponentBackedChemicalTank> void addTank( + ChemicalTanksBuilder builder, ComponentTankFromSpecCreator tankCreator) { + if (stackBasedCapacity == null) { + builder.addTank(((type, attachedTo, containerIndex) -> tankCreator.create(attachedTo, containerIndex, canExtract, + (chemical, automationType) -> canInsert.test(chemical, automationType, attachedTo), isValid, rate, capacity, validator))); + } else { + builder.addTank(((type, attachedTo, containerIndex) -> tankCreator.create(attachedTo, containerIndex, canExtract, + (chemical, automationType) -> canInsert.test(chemical, automationType, attachedTo), isValid, rate, () -> stackBasedCapacity.applyAsLong(attachedTo), validator))); + } + } + @SuppressWarnings("Convert2Diamond") public static > ChemicalTankSpec create(LongSupplier rate, LongSupplier capacity) { return new ChemicalTankSpec(rate, capacity, ConstantPredicates.alwaysTrueBi(), ConstantPredicates.alwaysTrueTri(), ConstantPredicates.alwaysTrue(), @@ -78,6 +92,19 @@ public static > ChemicalTankSpec c (chemical, automation, stack) -> supportsStack.test(stack), isValid, null, supportsStack); } + @FunctionalInterface + public interface ComponentTankFromSpecCreator, STACK extends ChemicalStack, TANK extends ComponentBackedChemicalTank> { + + TANK create(ItemStack attachedTo, int tankIndex, BiPredicate<@NotNull CHEMICAL, @NotNull AutomationType> canExtract, + BiPredicate<@NotNull CHEMICAL, @NotNull AutomationType> canInsert, Predicate<@NotNull CHEMICAL> isValid, LongSupplier rate, LongSupplier capacity, + @Nullable ChemicalAttributeValidator validator); + + default TANK create(ItemStack attachedTo, int tankIndex, LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull CHEMICAL, @NotNull AutomationType> canExtract, + BiPredicate<@NotNull CHEMICAL, @NotNull AutomationType> canInsert, Predicate<@NotNull CHEMICAL> isValid) { + return create(attachedTo, tankIndex, canExtract, canInsert, isValid, rate, capacity, null); + } + } + @FunctionalInterface public interface TankFromSpecCreator, STACK extends ChemicalStack, TANK extends IChemicalTank> { diff --git a/src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitChemicalTank.java b/src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitChemicalTank.java deleted file mode 100644 index 09ccfbe7c5f..00000000000 --- a/src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitChemicalTank.java +++ /dev/null @@ -1,33 +0,0 @@ -package mekanism.common.capabilities.chemical.variable; - -import java.util.function.BiPredicate; -import java.util.function.LongSupplier; -import java.util.function.Predicate; -import mekanism.api.AutomationType; -import mekanism.api.IContentsListener; -import mekanism.api.annotations.NothingNullByDefault; -import mekanism.api.chemical.Chemical; -import mekanism.api.chemical.ChemicalStack; -import mekanism.api.chemical.attribute.ChemicalAttributeValidator; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@NothingNullByDefault -public abstract class RateLimitChemicalTank, STACK extends ChemicalStack> extends - VariableCapacityChemicalTank { - - private final LongSupplier rate; - - public RateLimitChemicalTank(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull CHEMICAL, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull CHEMICAL, @NotNull AutomationType> canInsert, Predicate<@NotNull CHEMICAL> isValid, - @Nullable ChemicalAttributeValidator attributeValidator, @Nullable IContentsListener listener) { - super(capacity, canExtract, canInsert, isValid, attributeValidator, listener); - this.rate = rate; - } - - @Override - protected long getRate(@Nullable AutomationType automationType) { - //Allow unknown or manual interaction to bypass rate limit for the item - return automationType == null || automationType == AutomationType.MANUAL ? super.getRate(automationType) : rate.getAsLong(); - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitGasTank.java b/src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitGasTank.java deleted file mode 100644 index 45ceee7577f..00000000000 --- a/src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitGasTank.java +++ /dev/null @@ -1,56 +0,0 @@ -package mekanism.common.capabilities.chemical.variable; - -import java.util.Objects; -import java.util.function.BiPredicate; -import java.util.function.LongSupplier; -import java.util.function.Predicate; -import mekanism.api.AutomationType; -import mekanism.api.IContentsListener; -import mekanism.api.chemical.ChemicalTankBuilder; -import mekanism.api.chemical.attribute.ChemicalAttributeValidator; -import mekanism.api.chemical.gas.Gas; -import mekanism.api.chemical.gas.GasStack; -import mekanism.api.chemical.gas.IGasHandler; -import mekanism.api.chemical.gas.IGasTank; -import mekanism.common.config.MekanismConfig; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class RateLimitGasTank extends RateLimitChemicalTank implements IGasHandler, IGasTank { - - public static RateLimitGasTank createBasicItem(long capacity, BiPredicate<@NotNull Gas, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull Gas, @NotNull AutomationType> canInsert, Predicate<@NotNull Gas> isValid) { - return createBasicItem(() -> capacity, canExtract, canInsert, isValid); - } - - public static RateLimitGasTank createBasicItem(LongSupplier capacity, BiPredicate<@NotNull Gas, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull Gas, @NotNull AutomationType> canInsert, Predicate<@NotNull Gas> isValid) { - return create(MekanismConfig.general.chemicalItemFillRate, capacity, canExtract, canInsert, isValid); - } - - public static RateLimitGasTank createInternalStorage(LongSupplier rate, LongSupplier capacity, Predicate<@NotNull Gas> isValid) { - return create(rate, capacity, ChemicalTankBuilder.GAS.notExternal, ChemicalTankBuilder.GAS.alwaysTrueBi, isValid); - } - - public static RateLimitGasTank create(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull Gas, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull Gas, @NotNull AutomationType> canInsert, Predicate<@NotNull Gas> isValid) { - return create(rate, capacity, canExtract, canInsert, isValid, null, null); - } - - public static RateLimitGasTank create(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull Gas, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull Gas, @NotNull AutomationType> canInsert, Predicate<@NotNull Gas> isValid, @Nullable ChemicalAttributeValidator attributeValidator, - @Nullable IContentsListener listener) { - Objects.requireNonNull(rate, "Rate supplier cannot be null"); - Objects.requireNonNull(capacity, "Capacity supplier cannot be null"); - Objects.requireNonNull(canExtract, "Extraction validity check cannot be null"); - Objects.requireNonNull(canInsert, "Insertion validity check cannot be null"); - Objects.requireNonNull(isValid, "Gas validity check cannot be null"); - return new RateLimitGasTank(rate, capacity, canExtract, canInsert, isValid, attributeValidator, listener); - } - - private RateLimitGasTank(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull Gas, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull Gas, @NotNull AutomationType> canInsert, Predicate<@NotNull Gas> isValid, @Nullable ChemicalAttributeValidator attributeValidator, - @Nullable IContentsListener listener) { - super(rate, capacity, canExtract, canInsert, isValid, attributeValidator, listener); - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitInfusionTank.java b/src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitInfusionTank.java deleted file mode 100644 index c596bfaca2b..00000000000 --- a/src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitInfusionTank.java +++ /dev/null @@ -1,51 +0,0 @@ -package mekanism.common.capabilities.chemical.variable; - -import java.util.Objects; -import java.util.function.BiPredicate; -import java.util.function.LongSupplier; -import java.util.function.Predicate; -import mekanism.api.AutomationType; -import mekanism.api.IContentsListener; -import mekanism.api.chemical.attribute.ChemicalAttributeValidator; -import mekanism.api.chemical.infuse.IInfusionHandler; -import mekanism.api.chemical.infuse.IInfusionTank; -import mekanism.api.chemical.infuse.InfuseType; -import mekanism.api.chemical.infuse.InfusionStack; -import mekanism.common.config.MekanismConfig; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class RateLimitInfusionTank extends RateLimitChemicalTank implements IInfusionHandler, IInfusionTank { - - public static RateLimitInfusionTank createBasicItem(long capacity, BiPredicate<@NotNull InfuseType, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull InfuseType, @NotNull AutomationType> canInsert, Predicate<@NotNull InfuseType> isValid) { - return create(MekanismConfig.general.chemicalItemFillRate, () -> capacity, canExtract, canInsert, isValid); - } - - public static RateLimitInfusionTank create(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull InfuseType, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull InfuseType, @NotNull AutomationType> canInsert, Predicate<@NotNull InfuseType> isValid) { - return create(rate, capacity, canExtract, canInsert, isValid, null); - } - - public static RateLimitInfusionTank create(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull InfuseType, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull InfuseType, @NotNull AutomationType> canInsert, Predicate<@NotNull InfuseType> isValid, @Nullable IContentsListener listener) { - return create(rate, capacity, canExtract, canInsert, isValid, null, listener); - } - - public static RateLimitInfusionTank create(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull InfuseType, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull InfuseType, @NotNull AutomationType> canInsert, Predicate<@NotNull InfuseType> isValid, - @Nullable ChemicalAttributeValidator attributeValidator, @Nullable IContentsListener listener) { - Objects.requireNonNull(rate, "Rate supplier cannot be null"); - Objects.requireNonNull(capacity, "Capacity supplier cannot be null"); - Objects.requireNonNull(canExtract, "Extraction validity check cannot be null"); - Objects.requireNonNull(canInsert, "Insertion validity check cannot be null"); - Objects.requireNonNull(isValid, "Infuse Type validity check cannot be null"); - return new RateLimitInfusionTank(rate, capacity, canExtract, canInsert, isValid, attributeValidator, listener); - } - - private RateLimitInfusionTank(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull InfuseType, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull InfuseType, @NotNull AutomationType> canInsert, Predicate<@NotNull InfuseType> isValid, - @Nullable ChemicalAttributeValidator attributeValidator, @Nullable IContentsListener listener) { - super(rate, capacity, canExtract, canInsert, isValid, attributeValidator, listener); - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitPigmentTank.java b/src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitPigmentTank.java deleted file mode 100644 index 7a108e90097..00000000000 --- a/src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitPigmentTank.java +++ /dev/null @@ -1,51 +0,0 @@ -package mekanism.common.capabilities.chemical.variable; - -import java.util.Objects; -import java.util.function.BiPredicate; -import java.util.function.LongSupplier; -import java.util.function.Predicate; -import mekanism.api.AutomationType; -import mekanism.api.IContentsListener; -import mekanism.api.chemical.attribute.ChemicalAttributeValidator; -import mekanism.api.chemical.pigment.IPigmentHandler; -import mekanism.api.chemical.pigment.IPigmentTank; -import mekanism.api.chemical.pigment.Pigment; -import mekanism.api.chemical.pigment.PigmentStack; -import mekanism.common.config.MekanismConfig; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class RateLimitPigmentTank extends RateLimitChemicalTank implements IPigmentHandler, IPigmentTank { - - public static RateLimitPigmentTank createBasicItem(long capacity, BiPredicate<@NotNull Pigment, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull Pigment, @NotNull AutomationType> canInsert, Predicate<@NotNull Pigment> isValid) { - return create(MekanismConfig.general.chemicalItemFillRate, () -> capacity, canExtract, canInsert, isValid); - } - - public static RateLimitPigmentTank create(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull Pigment, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull Pigment, @NotNull AutomationType> canInsert, Predicate<@NotNull Pigment> isValid) { - return create(rate, capacity, canExtract, canInsert, isValid, null); - } - - public static RateLimitPigmentTank create(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull Pigment, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull Pigment, @NotNull AutomationType> canInsert, Predicate<@NotNull Pigment> isValid, @Nullable IContentsListener listener) { - return create(rate, capacity, canExtract, canInsert, isValid, null, listener); - } - - public static RateLimitPigmentTank create(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull Pigment, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull Pigment, @NotNull AutomationType> canInsert, Predicate<@NotNull Pigment> isValid, @Nullable ChemicalAttributeValidator attributeValidator, - @Nullable IContentsListener listener) { - Objects.requireNonNull(rate, "Rate supplier cannot be null"); - Objects.requireNonNull(capacity, "Capacity supplier cannot be null"); - Objects.requireNonNull(canExtract, "Extraction validity check cannot be null"); - Objects.requireNonNull(canInsert, "Insertion validity check cannot be null"); - Objects.requireNonNull(isValid, "Pigment validity check cannot be null"); - return new RateLimitPigmentTank(rate, capacity, canExtract, canInsert, isValid, attributeValidator, listener); - } - - private RateLimitPigmentTank(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull Pigment, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull Pigment, @NotNull AutomationType> canInsert, Predicate<@NotNull Pigment> isValid, - @Nullable ChemicalAttributeValidator attributeValidator, @Nullable IContentsListener listener) { - super(rate, capacity, canExtract, canInsert, isValid, attributeValidator, listener); - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitSlurryTank.java b/src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitSlurryTank.java deleted file mode 100644 index 9a54b1bf063..00000000000 --- a/src/main/java/mekanism/common/capabilities/chemical/variable/RateLimitSlurryTank.java +++ /dev/null @@ -1,51 +0,0 @@ -package mekanism.common.capabilities.chemical.variable; - -import java.util.Objects; -import java.util.function.BiPredicate; -import java.util.function.LongSupplier; -import java.util.function.Predicate; -import mekanism.api.AutomationType; -import mekanism.api.IContentsListener; -import mekanism.api.chemical.attribute.ChemicalAttributeValidator; -import mekanism.api.chemical.slurry.ISlurryHandler; -import mekanism.api.chemical.slurry.ISlurryTank; -import mekanism.api.chemical.slurry.Slurry; -import mekanism.api.chemical.slurry.SlurryStack; -import mekanism.common.config.MekanismConfig; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class RateLimitSlurryTank extends RateLimitChemicalTank implements ISlurryHandler, ISlurryTank { - - public static RateLimitSlurryTank createBasicItem(long capacity, BiPredicate<@NotNull Slurry, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull Slurry, @NotNull AutomationType> canInsert, Predicate<@NotNull Slurry> isValid) { - return create(MekanismConfig.general.chemicalItemFillRate, () -> capacity, canExtract, canInsert, isValid); - } - - public static RateLimitSlurryTank create(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull Slurry, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull Slurry, @NotNull AutomationType> canInsert, Predicate<@NotNull Slurry> isValid) { - return create(rate, capacity, canExtract, canInsert, isValid, null); - } - - public static RateLimitSlurryTank create(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull Slurry, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull Slurry, @NotNull AutomationType> canInsert, Predicate<@NotNull Slurry> isValid, @Nullable IContentsListener listener) { - return create(rate, capacity, canExtract, canInsert, isValid, null, listener); - } - - public static RateLimitSlurryTank create(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull Slurry, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull Slurry, @NotNull AutomationType> canInsert, Predicate<@NotNull Slurry> isValid, @Nullable ChemicalAttributeValidator attributeValidator, - @Nullable IContentsListener listener) { - Objects.requireNonNull(rate, "Rate supplier cannot be null"); - Objects.requireNonNull(capacity, "Capacity supplier cannot be null"); - Objects.requireNonNull(canExtract, "Extraction validity check cannot be null"); - Objects.requireNonNull(canInsert, "Insertion validity check cannot be null"); - Objects.requireNonNull(isValid, "Slurry validity check cannot be null"); - return new RateLimitSlurryTank(rate, capacity, canExtract, canInsert, isValid, attributeValidator, listener); - } - - private RateLimitSlurryTank(LongSupplier rate, LongSupplier capacity, BiPredicate<@NotNull Slurry, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull Slurry, @NotNull AutomationType> canInsert, Predicate<@NotNull Slurry> isValid, - @Nullable ChemicalAttributeValidator attributeValidator, @Nullable IContentsListener listener) { - super(rate, capacity, canExtract, canInsert, isValid, attributeValidator, listener); - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/capabilities/energy/item/EnergyCubeRateLimitEnergyContainer.java b/src/main/java/mekanism/common/capabilities/energy/item/EnergyCubeRateLimitEnergyContainer.java deleted file mode 100644 index 08a6212bbd2..00000000000 --- a/src/main/java/mekanism/common/capabilities/energy/item/EnergyCubeRateLimitEnergyContainer.java +++ /dev/null @@ -1,37 +0,0 @@ -package mekanism.common.capabilities.energy.item; - -import java.util.Objects; -import mekanism.api.Action; -import mekanism.api.AutomationType; -import mekanism.api.IContentsListener; -import mekanism.api.annotations.NothingNullByDefault; -import mekanism.api.math.FloatingLong; -import mekanism.common.capabilities.energy.BasicEnergyContainer; -import mekanism.common.tier.EnergyCubeTier; -import org.jetbrains.annotations.Nullable; - -@NothingNullByDefault -public class EnergyCubeRateLimitEnergyContainer extends RateLimitEnergyContainer { - - public static EnergyCubeRateLimitEnergyContainer create(EnergyCubeTier tier) { - Objects.requireNonNull(tier, "Energy cube tier cannot be null"); - return new EnergyCubeRateLimitEnergyContainer(tier, null); - } - - private final boolean isCreative; - - private EnergyCubeRateLimitEnergyContainer(EnergyCubeTier tier, @Nullable IContentsListener listener) { - super(tier::getOutput, tier::getMaxEnergy, BasicEnergyContainer.alwaysTrue, BasicEnergyContainer.alwaysTrue, listener); - isCreative = tier == EnergyCubeTier.CREATIVE; - } - - @Override - public FloatingLong insert(FloatingLong amount, Action action, AutomationType automationType) { - return super.insert(amount, action.combine(!isCreative), automationType); - } - - @Override - public FloatingLong extract(FloatingLong amount, Action action, AutomationType automationType) { - return super.extract(amount, action.combine(!isCreative), automationType); - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/capabilities/energy/item/NoClampRateLimitEnergyContainer.java b/src/main/java/mekanism/common/capabilities/energy/item/NoClampRateLimitEnergyContainer.java deleted file mode 100644 index 08d58b54131..00000000000 --- a/src/main/java/mekanism/common/capabilities/energy/item/NoClampRateLimitEnergyContainer.java +++ /dev/null @@ -1,43 +0,0 @@ -package mekanism.common.capabilities.energy.item; - -import java.util.Objects; -import java.util.function.Predicate; -import mekanism.api.AutomationType; -import mekanism.api.IContentsListener; -import mekanism.api.annotations.NothingNullByDefault; -import mekanism.api.math.FloatingLong; -import mekanism.api.math.FloatingLongSupplier; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@NothingNullByDefault -public class NoClampRateLimitEnergyContainer extends RateLimitEnergyContainer { - - public static NoClampRateLimitEnergyContainer create(FloatingLongSupplier rate, FloatingLongSupplier capacity) { - return create(rate, capacity, manualOnly, alwaysTrue); - } - - public static NoClampRateLimitEnergyContainer create(FloatingLongSupplier capacity, Predicate<@NotNull AutomationType> canExtract, Predicate<@NotNull AutomationType> canInsert) { - return create(() -> capacity.get().multiply(0.005), capacity, canExtract, canInsert); - } - - public static NoClampRateLimitEnergyContainer create(FloatingLongSupplier rate, FloatingLongSupplier capacity, Predicate<@NotNull AutomationType> canExtract, - Predicate<@NotNull AutomationType> canInsert) { - Objects.requireNonNull(rate, "Rate supplier cannot be null"); - Objects.requireNonNull(capacity, "Capacity supplier cannot be null"); - Objects.requireNonNull(canExtract, "Extraction validity check cannot be null"); - Objects.requireNonNull(canInsert, "Insertion validity check cannot be null"); - return new NoClampRateLimitEnergyContainer(rate, capacity, canExtract, canInsert, null); - } - - protected NoClampRateLimitEnergyContainer(FloatingLongSupplier rate, FloatingLongSupplier capacity, Predicate<@NotNull AutomationType> canExtract, Predicate<@NotNull AutomationType> canInsert, - @Nullable IContentsListener listener) { - super(rate, capacity, canExtract, canInsert, listener); - } - - @Override - protected FloatingLong clampEnergy(FloatingLong energy) { - //Don't clamp the energy - return energy; - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/capabilities/energy/item/RateLimitEnergyContainer.java b/src/main/java/mekanism/common/capabilities/energy/item/RateLimitEnergyContainer.java deleted file mode 100644 index 57d4cd261da..00000000000 --- a/src/main/java/mekanism/common/capabilities/energy/item/RateLimitEnergyContainer.java +++ /dev/null @@ -1,48 +0,0 @@ -package mekanism.common.capabilities.energy.item; - -import java.util.Objects; -import java.util.function.Predicate; -import mekanism.api.AutomationType; -import mekanism.api.IContentsListener; -import mekanism.api.annotations.NothingNullByDefault; -import mekanism.api.math.FloatingLong; -import mekanism.api.math.FloatingLongSupplier; -import mekanism.common.capabilities.energy.VariableCapacityEnergyContainer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@NothingNullByDefault -public class RateLimitEnergyContainer extends VariableCapacityEnergyContainer { - - public static RateLimitEnergyContainer create(FloatingLongSupplier rate, FloatingLongSupplier capacity) { - return create(rate, capacity, manualOnly, alwaysTrue); - } - - public static RateLimitEnergyContainer create(FloatingLongSupplier capacity, Predicate<@NotNull AutomationType> canExtract, Predicate<@NotNull AutomationType> canInsert) { - return create(() -> capacity.get().multiply(0.005), capacity, canExtract, canInsert); - } - - public static RateLimitEnergyContainer create(FloatingLongSupplier rate, FloatingLongSupplier capacity, Predicate<@NotNull AutomationType> canExtract, - Predicate<@NotNull AutomationType> canInsert) { - Objects.requireNonNull(rate, "Rate supplier cannot be null"); - Objects.requireNonNull(capacity, "Capacity supplier cannot be null"); - Objects.requireNonNull(canExtract, "Extraction validity check cannot be null"); - Objects.requireNonNull(canInsert, "Insertion validity check cannot be null"); - return new RateLimitEnergyContainer(rate, capacity, canExtract, canInsert, null); - } - - private final FloatingLongSupplier rate; - - protected RateLimitEnergyContainer(FloatingLongSupplier rate, FloatingLongSupplier capacity, Predicate<@NotNull AutomationType> canExtract, - Predicate<@NotNull AutomationType> canInsert, @Nullable IContentsListener listener) { - super(capacity, canExtract, canInsert, listener); - this.rate = rate; - } - - @Override - protected FloatingLong getRate(@Nullable AutomationType automationType) { - //TODO: Do we want to move this up a package and somehow specify this as a parameter or something so that this container isn't limited to items - //Allow unknown or manual interaction to bypass rate limit for the item - return automationType == null || automationType == AutomationType.MANUAL ? super.getRate(automationType) : rate.get(); - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/capabilities/fluid/BasicFluidTank.java b/src/main/java/mekanism/common/capabilities/fluid/BasicFluidTank.java index 97267b7c46b..538838c9078 100644 --- a/src/main/java/mekanism/common/capabilities/fluid/BasicFluidTank.java +++ b/src/main/java/mekanism/common/capabilities/fluid/BasicFluidTank.java @@ -150,6 +150,8 @@ public void setStack(FluidStack stack) { * stack/stack size */ protected int getRate(@Nullable AutomationType automationType) { + //TODO - 1.20.5: As we don't actually use this anymore in subclasses decide if we want to remove this + // That or we might want to actually start making use of it //TODO: Decide if we want to split this into a rate for inserting and a rate for extracting. return Integer.MAX_VALUE; } @@ -188,7 +190,7 @@ public FluidStack insert(@NotNull FluidStack stack, Action action, AutomationTyp return stack; } boolean sameType = false; - if (isEmpty() || (sameType = FluidStack.isSameFluidSameComponents(stored, stack))) { + if (isEmpty() || (sameType = isFluidEqual(stack))) { int toAdd = Math.min(stack.getAmount(), needed); if (action.execute()) { //If we want to actually insert the fluid, then update the current fluid diff --git a/src/main/java/mekanism/common/capabilities/fluid/item/FluidTankRateLimitFluidTank.java b/src/main/java/mekanism/common/capabilities/fluid/item/FluidTankRateLimitFluidTank.java deleted file mode 100644 index 1b5b9d7a8c2..00000000000 --- a/src/main/java/mekanism/common/capabilities/fluid/item/FluidTankRateLimitFluidTank.java +++ /dev/null @@ -1,48 +0,0 @@ -package mekanism.common.capabilities.fluid.item; - -import java.util.Objects; -import mekanism.api.Action; -import mekanism.api.AutomationType; -import mekanism.api.IContentsListener; -import mekanism.api.annotations.NothingNullByDefault; -import mekanism.common.capabilities.fluid.BasicFluidTank; -import mekanism.common.tier.FluidTankTier; -import net.neoforged.neoforge.fluids.FluidStack; -import org.jetbrains.annotations.Nullable; - -@NothingNullByDefault -public class FluidTankRateLimitFluidTank extends RateLimitFluidTank { - - public static FluidTankRateLimitFluidTank create(FluidTankTier tier) { - Objects.requireNonNull(tier, "Fluid tank tier cannot be null"); - return new FluidTankRateLimitFluidTank(tier, null); - } - - private final boolean isCreative; - - private FluidTankRateLimitFluidTank(FluidTankTier tier, @Nullable IContentsListener listener) { - super(tier::getOutput, tier::getStorage, BasicFluidTank.alwaysTrueBi, BasicFluidTank.alwaysTrueBi, BasicFluidTank.alwaysTrue, listener); - isCreative = tier == FluidTankTier.CREATIVE; - } - - @Override - public FluidStack insert(FluidStack stack, Action action, AutomationType automationType) { - return super.insert(stack, action.combine(!isCreative), automationType); - } - - @Override - public FluidStack extract(int amount, Action action, AutomationType automationType) { - return super.extract(amount, action.combine(!isCreative), automationType); - } - - /** - * {@inheritDoc} - * - * Note: We are only patching {@link #setStackSize(int, Action)}, as both {@link #growStack(int, Action)} and {@link #shrinkStack(int, Action)} are wrapped through - * this method. - */ - @Override - public int setStackSize(int amount, Action action) { - return super.setStackSize(amount, action.combine(!isCreative)); - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/capabilities/fluid/item/FluidTankSpec.java b/src/main/java/mekanism/common/capabilities/fluid/item/FluidTankSpec.java index 2a46519886f..5fff8d015d4 100644 --- a/src/main/java/mekanism/common/capabilities/fluid/item/FluidTankSpec.java +++ b/src/main/java/mekanism/common/capabilities/fluid/item/FluidTankSpec.java @@ -7,6 +7,8 @@ import mekanism.api.IContentsListener; import mekanism.api.fluid.IExtendedFluidTank; import mekanism.api.functions.ConstantPredicates; +import mekanism.common.attachments.containers.fluid.ComponentBackedFluidTank; +import mekanism.common.attachments.containers.fluid.FluidTanksBuilder; import mekanism.common.capabilities.GenericTankSpec; import net.minecraft.world.item.ItemStack; import net.neoforged.neoforge.common.util.TriPredicate; @@ -31,6 +33,12 @@ public TANK createTank(TankFromSpecCreator canInsert.test(fluid, automationType, stack), isValid, null); } + //TODO - 1.20.5: Re-evaluate this + public void addTank(FluidTanksBuilder builder, ComponentTankFromSpecCreator tankCreator) { + builder.addTank(((type, attachedTo, containerIndex) -> tankCreator.create(attachedTo, containerIndex, canExtract, + (chemical, automationType) -> canInsert.test(chemical, automationType, attachedTo), isValid, rate, capacity))); + } + public static FluidTankSpec create(IntSupplier rate, IntSupplier capacity) { return new FluidTankSpec(rate, capacity, ConstantPredicates.alwaysTrueBi(), ConstantPredicates.alwaysTrueTri(), ConstantPredicates.alwaysTrue(), ConstantPredicates.alwaysTrue()); @@ -45,6 +53,13 @@ public static FluidTankSpec createFillOnly(IntSupplier rate, IntSupplier capacit return new FluidTankSpec(rate, capacity, ConstantPredicates.notExternal(), (chemical, automation, stack) -> supportsStack.test(stack), isValid, supportsStack); } + @FunctionalInterface + public interface ComponentTankFromSpecCreator { + + ComponentBackedFluidTank create(ItemStack attachedTo, int tankIndex, BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canExtract, + BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canInsert, Predicate<@NotNull FluidStack> isValid, IntSupplier rate, IntSupplier capacity); + } + @FunctionalInterface public interface TankFromSpecCreator { diff --git a/src/main/java/mekanism/common/capabilities/fluid/item/RateLimitFluidTank.java b/src/main/java/mekanism/common/capabilities/fluid/item/RateLimitFluidTank.java deleted file mode 100644 index cb9848afc33..00000000000 --- a/src/main/java/mekanism/common/capabilities/fluid/item/RateLimitFluidTank.java +++ /dev/null @@ -1,55 +0,0 @@ -package mekanism.common.capabilities.fluid.item; - -import java.util.Objects; -import java.util.function.BiPredicate; -import java.util.function.IntSupplier; -import java.util.function.Predicate; -import mekanism.api.AutomationType; -import mekanism.api.IContentsListener; -import mekanism.common.capabilities.fluid.VariableCapacityFluidTank; -import mekanism.common.config.MekanismConfig; -import net.neoforged.neoforge.fluids.FluidStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class RateLimitFluidTank extends VariableCapacityFluidTank { - - public static RateLimitFluidTank createBasicItem(int capacity, BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canInsert, Predicate<@NotNull FluidStack> isValid) { - return createBasicItem(() -> capacity, canExtract, canInsert, isValid); - } - - public static RateLimitFluidTank createBasicItem(IntSupplier capacity, BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canInsert, Predicate<@NotNull FluidStack> isValid) { - return create(MekanismConfig.general.fluidItemFillRate, capacity, canExtract, canInsert, isValid); - } - - public static RateLimitFluidTank create(IntSupplier rate, IntSupplier capacity, BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canInsert, Predicate<@NotNull FluidStack> isValid) { - return create(rate, capacity, canExtract, canInsert, isValid, null); - } - - public static RateLimitFluidTank create(IntSupplier rate, IntSupplier capacity, BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canInsert, Predicate<@NotNull FluidStack> isValid, @Nullable IContentsListener listener) { - Objects.requireNonNull(rate, "Rate supplier cannot be null"); - Objects.requireNonNull(capacity, "Capacity supplier cannot be null"); - Objects.requireNonNull(canExtract, "Extraction validity check cannot be null"); - Objects.requireNonNull(canInsert, "Insertion validity check cannot be null"); - Objects.requireNonNull(isValid, "Gas validity check cannot be null"); - return new RateLimitFluidTank(rate, capacity, canExtract, canInsert, isValid, listener); - } - - private final IntSupplier rate; - - protected RateLimitFluidTank(IntSupplier rate, IntSupplier capacity, BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canExtract, - BiPredicate<@NotNull FluidStack, @NotNull AutomationType> canInsert, Predicate<@NotNull FluidStack> isValid, @Nullable IContentsListener listener) { - super(capacity, canExtract, canInsert, isValid, listener); - this.rate = rate; - } - - @Override - protected int getRate(@Nullable AutomationType automationType) { - //Allow unknown or manual interaction to bypass rate limit for the item - return automationType == null || automationType == AutomationType.MANUAL ? super.getRate(automationType) : rate.getAsInt(); - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/capabilities/heat/BasicHeatCapacitor.java b/src/main/java/mekanism/common/capabilities/heat/BasicHeatCapacitor.java index 5090590e92b..cc81f41d69d 100644 --- a/src/main/java/mekanism/common/capabilities/heat/BasicHeatCapacitor.java +++ b/src/main/java/mekanism/common/capabilities/heat/BasicHeatCapacitor.java @@ -32,10 +32,6 @@ public static BasicHeatCapacitor create(double heatCapacity, @Nullable DoubleSup return create(heatCapacity, HeatAPI.DEFAULT_INVERSE_CONDUCTION, HeatAPI.DEFAULT_INVERSE_INSULATION, ambientTempSupplier, listener); } - public static BasicHeatCapacitor createBasicItem(double heatCapacity, double inverseConductionCoefficient, double inverseInsulationCoefficient) { - return create(heatCapacity, inverseConductionCoefficient, inverseInsulationCoefficient, () -> HeatAPI.AMBIENT_TEMP, null); - } - public static BasicHeatCapacitor create(double heatCapacity, double inverseConductionCoefficient, double inverseInsulationCoefficient, @Nullable DoubleSupplier ambientTempSupplier, @Nullable IContentsListener listener) { if (heatCapacity < 1) { diff --git a/src/main/java/mekanism/common/content/qio/QIOCraftingWindow.java b/src/main/java/mekanism/common/content/qio/QIOCraftingWindow.java index 883b8a95c51..d6ea364733e 100644 --- a/src/main/java/mekanism/common/content/qio/QIOCraftingWindow.java +++ b/src/main/java/mekanism/common/content/qio/QIOCraftingWindow.java @@ -22,7 +22,6 @@ import mekanism.common.inventory.container.slot.HotBarSlot; import mekanism.common.inventory.container.slot.IInsertableSlot; import mekanism.common.inventory.container.slot.MainInventorySlot; -import mekanism.common.inventory.slot.BasicInventorySlot; import mekanism.common.inventory.slot.CraftingWindowInventorySlot; import mekanism.common.inventory.slot.CraftingWindowOutputInventorySlot; import mekanism.common.lib.inventory.HashedItem; @@ -62,10 +61,10 @@ public class QIOCraftingWindow implements IContentsListener { } } - private final CraftingWindowInventorySlot[] inputSlots = new CraftingWindowInventorySlot[9]; + private final IInventorySlot[] inputSlots = new CraftingWindowInventorySlot[9]; private final ReplacementHelper replacementHelper = new ReplacementHelper(); private final RemainderHelper remainderHelper = new RemainderHelper(); - private final CraftingWindowOutputInventorySlot outputSlot; + private final IInventorySlot outputSlot; private final QIOCraftingInventory craftingInventory; private final IQIOCraftingWindowHolder holder; private final SelectedWindowData windowData; @@ -94,14 +93,14 @@ public byte getWindowIndex() { return windowIndex; } - public CraftingWindowInventorySlot getInputSlot(int slot) { + public IInventorySlot getInputSlot(int slot) { if (slot < 0 || slot >= 9) { throw new IllegalArgumentException("Input slot out of range"); } return inputSlots[slot]; } - public CraftingWindowOutputInventorySlot getOutputSlot() { + public IInventorySlot getOutputSlot() { return outputSlot; } @@ -246,7 +245,7 @@ private void craftingFinished(@NotNull Level world) { private int calculateMaxCraftAmount(@NotNull ItemStack stack, @Nullable QIOFrequency frequency) { int outputSize = stack.getCount(); int inputSize = 64; - for (CraftingWindowInventorySlot inputSlot : inputSlots) { + for (IInventorySlot inputSlot : inputSlots) { int count = inputSlot.getCount(); if (count > 0 && count < inputSize) { inputSize = count; @@ -286,7 +285,7 @@ private void useInput(IInventorySlot inputSlot) { */ public void emptyTo(boolean toPlayerInv, List hotBarSlots, List mainInventorySlots) { if (toPlayerInv) { - for (CraftingWindowInventorySlot inputSlot : inputSlots) { + for (IInventorySlot inputSlot : inputSlots) { if (!inputSlot.isEmpty()) { ItemStack toTransfer = inputSlot.extractItem(inputSlot.getCount(), Action.SIMULATE, AutomationType.INTERNAL); if (!toTransfer.isEmpty()) { @@ -302,7 +301,7 @@ public void emptyTo(boolean toPlayerInv, List hotBarSlots, List
hotBarSlots, L //Update slots with remaining contents for (int index = 0; index < remaining.size(); index++) { ItemStack remainder = remaining.get(index); - CraftingWindowInventorySlot inputSlot = inputSlots[index]; + IInventorySlot inputSlot = inputSlots[index]; if (inputSlot.getCount() > 1) { //If the input slot contains an item that is stacked, reduce the size of it by one //Note: We "ignore" the fact that the container item may still be valid for the recipe, if the input is stacked @@ -497,7 +496,7 @@ public ItemStack performCraft(@NotNull Player player, @NotNull ItemStack result, //Update slots with remaining contents for (int index = 0; index < remaining.size(); index++) { ItemStack remainder = remaining.get(index); - CraftingWindowInventorySlot inputSlot = inputSlots[index]; + IInventorySlot inputSlot = inputSlots[index]; if (inputSlot.getCount() > 1) { //If the input slot contains an item that is stacked, reduce the size of it by one //Note: We "ignore" the fact that the container item may still be valid for the recipe, if the input is stacked @@ -635,7 +634,7 @@ public int getContainerSize() { @Override public boolean isEmpty() { - return Arrays.stream(inputSlots).allMatch(BasicInventorySlot::isEmpty); + return Arrays.stream(inputSlots).allMatch(IInventorySlot::isEmpty); } @NotNull @@ -656,7 +655,7 @@ public ItemStack getItem(int index) { @Override public List getItems() { List items = new ArrayList<>(getWidth() * getHeight()); - for (CraftingWindowInventorySlot inputSlot : inputSlots) { + for (IInventorySlot inputSlot : inputSlots) { //Note: We copy this as we don't want to allow someone trying to interact with the stack directly // to change the size of it. We also add it regardless of it is empty as that is what the method expects items.add(inputSlot.getStack().copy()); @@ -707,7 +706,7 @@ public boolean stillValid(@NotNull Player player) { @Override public void clearContent() { - for (CraftingWindowInventorySlot inputSlot : inputSlots) { + for (IInventorySlot inputSlot : inputSlots) { inputSlot.setEmpty(); } } @@ -727,7 +726,7 @@ public void fillStackedContents(@NotNull StackedContents helper) { //Note: We don't copy it as it seems to be read only // Don't trust custom implementations though to be read only boolean copyNeeded = helper.getClass() != StackedContents.class; - for (CraftingWindowInventorySlot inputSlot : inputSlots) { + for (IInventorySlot inputSlot : inputSlots) { ItemStack stack = inputSlot.getStack(); helper.accountSimpleStack(copyNeeded ? stack.copy() : stack); } @@ -805,7 +804,7 @@ public void reset() { } } - public void findEquivalentItem(Level world, @NotNull QIOFrequency frequency, CraftingWindowInventorySlot slot, int index, ItemStack used) { + public void findEquivalentItem(Level world, @NotNull QIOFrequency frequency, IInventorySlot slot, int index, ItemStack used) { mapRecipe(index, used); if (invalid) { //If something about mapping the recipe went wrong, we can't find any equivalents @@ -842,7 +841,7 @@ public void findEquivalentItem(Level world, @NotNull QIOFrequency frequency, Cra } } - private boolean testEquivalentItem(Level world, @NotNull QIOFrequency frequency, CraftingWindowInventorySlot slot, int index, Ingredient usedIngredient, + private boolean testEquivalentItem(Level world, @NotNull QIOFrequency frequency, IInventorySlot slot, int index, Ingredient usedIngredient, HashedItem replacementType) { if (!frequency.isStoring(replacementType) || !usedIngredient.test(replacementType.getInternalStack())) { //Our frequency doesn't actually have the item stored we are trying to use or the type we are trying diff --git a/src/main/java/mekanism/common/content/qio/QIOServerCraftingTransferHandler.java b/src/main/java/mekanism/common/content/qio/QIOServerCraftingTransferHandler.java index c108cbcecfd..8e5b0da8f88 100644 --- a/src/main/java/mekanism/common/content/qio/QIOServerCraftingTransferHandler.java +++ b/src/main/java/mekanism/common/content/qio/QIOServerCraftingTransferHandler.java @@ -19,6 +19,7 @@ import java.util.UUID; import mekanism.api.Action; import mekanism.api.AutomationType; +import mekanism.api.inventory.IInventorySlot; import mekanism.api.math.MathUtils; import mekanism.common.Mekanism; import mekanism.common.content.qio.QIOCraftingTransferHelper.BaseSimulatedInventory; @@ -29,7 +30,6 @@ import mekanism.common.inventory.container.slot.HotBarSlot; import mekanism.common.inventory.container.slot.InsertableSlot; import mekanism.common.inventory.container.slot.MainInventorySlot; -import mekanism.common.inventory.slot.CraftingWindowInventorySlot; import mekanism.common.lib.inventory.HashedItem; import mekanism.common.network.to_server.qio.PacketQIOFillCraftingWindow; import mekanism.common.util.MekanismUtils; @@ -80,7 +80,7 @@ private void tryTransfer(CraftingRecipe recipe, Byte2ObjectMap> source //Extract what items are still in the window Byte2ObjectMap remainingCraftingGridContents = new Byte2ObjectArrayMap<>(9); for (byte slot = 0; slot < 9; slot++) { - CraftingWindowInventorySlot inputSlot = craftingWindow.getInputSlot(slot); + IInventorySlot inputSlot = craftingWindow.getInputSlot(slot); if (!inputSlot.isEmpty()) { ItemStack stack = inputSlot.extractItem(inputSlot.getCount(), Action.EXECUTE, AutomationType.MANUAL); if (!stack.isEmpty()) { @@ -512,7 +512,7 @@ private void transferItems(Byte2ObjectMap> source for (ObjectIterator> iter = targetContents.byte2ObjectEntrySet().iterator(); iter.hasNext(); ) { Byte2ObjectMap.Entry entry = iter.next(); byte targetSlot = entry.getByteKey(); - CraftingWindowInventorySlot inputSlot = craftingWindow.getInputSlot(targetSlot); + IInventorySlot inputSlot = craftingWindow.getInputSlot(targetSlot); ItemStack remainder = inputSlot.insertItem(entry.getValue(), Action.EXECUTE, AutomationType.MANUAL); if (remainder.isEmpty()) { //If it was fully inserted, remove the entry from what we have left to deal with @@ -537,7 +537,7 @@ private void transferItems(Byte2ObjectMap> source if (!stack.isEmpty()) { //If we couldn't insert it all, try recombining with the slots they were in the crafting window // (only if the type matches though) - CraftingWindowInventorySlot inputSlot = craftingWindow.getInputSlot(entry.getByteKey()); + IInventorySlot inputSlot = craftingWindow.getInputSlot(entry.getByteKey()); if (ItemStack.isSameItemSameComponents(inputSlot.getStack(), stack)) { stack = inputSlot.insertItem(stack, Action.EXECUTE, AutomationType.MANUAL); } @@ -595,7 +595,7 @@ private void bail(Byte2ObjectMap targetContents, Byte2ObjectMap entry : remainingCraftingGridContents.byte2ObjectEntrySet()) { ItemStack stack = entry.getValue(); - CraftingWindowInventorySlot inputSlot = craftingWindow.getInputSlot(entry.getByteKey()); + IInventorySlot inputSlot = craftingWindow.getInputSlot(entry.getByteKey()); if (ItemStack.isSameItemSameComponents(inputSlot.getStack(), stack)) { stack = inputSlot.insertItem(stack, Action.EXECUTE, AutomationType.MANUAL); if (stack.isEmpty()) { diff --git a/src/main/java/mekanism/common/inventory/container/QIOItemViewerContainer.java b/src/main/java/mekanism/common/inventory/container/QIOItemViewerContainer.java index 46e2a1da776..4459f615913 100644 --- a/src/main/java/mekanism/common/inventory/container/QIOItemViewerContainer.java +++ b/src/main/java/mekanism/common/inventory/container/QIOItemViewerContainer.java @@ -11,6 +11,7 @@ import java.util.UUID; import java.util.function.Supplier; import mekanism.api.Action; +import mekanism.api.inventory.IInventorySlot; import mekanism.api.math.MathUtils; import mekanism.api.text.ILangEntry; import mekanism.common.Mekanism; @@ -30,7 +31,6 @@ import mekanism.common.inventory.container.slot.InventoryContainerSlot; import mekanism.common.inventory.container.slot.VirtualCraftingOutputSlot; import mekanism.common.inventory.container.slot.VirtualInventoryContainerSlot; -import mekanism.common.inventory.slot.CraftingWindowInventorySlot; import mekanism.common.lib.inventory.HashedItem; import mekanism.common.lib.inventory.HashedItem.UUIDAwareHashedItem; import mekanism.common.network.PacketUtils; @@ -167,8 +167,8 @@ protected void addSlots() { } } - private void addCraftingSlot(CraftingWindowInventorySlot slot, byte tableIndex, int slotIndex) { - VirtualInventoryContainerSlot containerSlot = slot.createContainerSlot(); + private void addCraftingSlot(IInventorySlot slot, byte tableIndex, int slotIndex) { + VirtualInventoryContainerSlot containerSlot = (VirtualInventoryContainerSlot) slot.createContainerSlot(); craftingSlots[tableIndex][slotIndex] = containerSlot; addSlot(containerSlot); } diff --git a/src/main/java/mekanism/common/inventory/slot/BasicInventorySlot.java b/src/main/java/mekanism/common/inventory/slot/BasicInventorySlot.java index dfd5f14c3d3..211cda21a90 100644 --- a/src/main/java/mekanism/common/inventory/slot/BasicInventorySlot.java +++ b/src/main/java/mekanism/common/inventory/slot/BasicInventorySlot.java @@ -150,13 +150,13 @@ public ItemStack insertItem(ItemStack stack, Action action, AutomationType autom return ItemStack.EMPTY; } //Validate that we aren't at max stack size before we try to see if we can insert the item, as on average this will be a cheaper check - int needed = getLimit(stack) - getCount(); + int needed = getLimit(stack) - current.getCount(); if (needed <= 0 || !isItemValidForInsertion(stack, automationType)) { //Fail if we are a full slot, or we can never insert the item or currently are unable to insert it return stack; } boolean sameType = false; - if (isEmpty() || (sameType = ItemStack.isSameItemSameComponents(current, stack))) { + if (current.isEmpty() || (sameType = ItemStack.isSameItemSameComponents(current, stack))) { int toAdd = Math.min(stack.getCount(), needed); if (action.execute()) { //If we want to actually insert the item, then update the current item @@ -179,13 +179,13 @@ public ItemStack insertItem(ItemStack stack, Action action, AutomationType autom @Override public ItemStack extractItem(int amount, Action action, AutomationType automationType) { - if (isEmpty() || amount < 1 || !canExtract.test(current, automationType)) { + if (current.isEmpty() || amount < 1 || !canExtract.test(current, automationType)) { //"Fail quick" if we don't can never extract from this slot, have an item stored, or the amount being requested is less than one return ItemStack.EMPTY; } //Ensure that if this slot allows going past the max stack size of an item, that when extracting we don't act as if we have more than // the max stack size, as the JavaDoc for IItemHandler requires that the returned stack is not larger than its stack size - int currentAmount = Math.min(getCount(), current.getMaxStackSize()); + int currentAmount = Math.min(current.getCount(), current.getMaxStackSize()); if (currentAmount < amount) { //If we are trying to extract more than we have, just change it so that we are extracting it all amount = currentAmount; @@ -264,7 +264,7 @@ protected final ContainerSlotType getSlotType() { */ @Override public int setStackSize(int amount, Action action) { - if (isEmpty()) { + if (current.isEmpty()) { return 0; } else if (amount <= 0) { if (action.execute()) { @@ -276,7 +276,7 @@ public int setStackSize(int amount, Action action) { if (amount > maxStackSize) { amount = maxStackSize; } - if (getCount() == amount || action.simulate()) { + if (current.getCount() == amount || action.simulate()) { //If our size is not changing, or we are only simulating the change, don't do anything return amount; } @@ -292,7 +292,7 @@ public int setStackSize(int amount, Action action) { */ @Override public int growStack(int amount, Action action) { - int current = getCount(); + int current = this.current.getCount(); if (amount > 0) { //Cap adding amount at how much we need, so that we don't risk integer overflow amount = Math.min(amount, getLimit(this.current)); diff --git a/src/main/java/mekanism/common/inventory/slot/BinInventorySlot.java b/src/main/java/mekanism/common/inventory/slot/BinInventorySlot.java index 1c08edb3cb5..5471cbfad82 100644 --- a/src/main/java/mekanism/common/inventory/slot/BinInventorySlot.java +++ b/src/main/java/mekanism/common/inventory/slot/BinInventorySlot.java @@ -9,34 +9,33 @@ import mekanism.api.NBTConstants; import mekanism.api.annotations.NothingNullByDefault; import mekanism.api.inventory.IInventorySlot; -import mekanism.common.attachments.containers.AttachedInventorySlots; +import mekanism.api.inventory.IMekanismInventory; import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.item.ComponentBackedBinInventorySlot; import mekanism.common.inventory.container.slot.InventoryContainerSlot; import mekanism.common.item.block.ItemBlockBin; import mekanism.common.tier.BinTier; import mekanism.common.util.NBTUtils; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.Tag; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.items.ItemHandlerHelper; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @NothingNullByDefault public class BinInventorySlot extends BasicInventorySlot { - private static final Predicate<@NotNull ItemStack> validator = stack -> !(stack.getItem() instanceof ItemBlockBin); + public static final Predicate<@NotNull ItemStack> validator = stack -> !(stack.getItem() instanceof ItemBlockBin); @Nullable - public static BinInventorySlot getForStack(@NotNull ItemStack stack) { + public static ComponentBackedBinInventorySlot getForStack(@NotNull ItemStack stack) { if (!stack.isEmpty() && stack.getItem() instanceof ItemBlockBin) { - AttachedInventorySlots attachment = ContainerType.ITEM.getAttachment(stack); + IMekanismInventory attachment = ContainerType.ITEM.createHandler(stack); if (attachment != null) { List slots = attachment.getInventorySlots(null); if (slots.size() == 1) { - IInventorySlot slot = slots.get(0); - if (slot instanceof BinInventorySlot binSlot) { + IInventorySlot slot = slots.getFirst(); + if (slot instanceof ComponentBackedBinInventorySlot binSlot) { return binSlot; } } @@ -157,15 +156,14 @@ public ItemStack getLockStack() { public CompoundTag serializeNBT(HolderLookup.Provider provider) { CompoundTag nbt = super.serializeNBT(provider); if (isLocked()) { - Tag stackTag = lockStack.save(provider); - nbt.put(NBTConstants.LOCK_STACK, stackTag); + nbt.put(NBTConstants.LOCK_STACK, lockStack.save(provider)); } return nbt; } @Override public boolean isCompatible(IInventorySlot other) { - return super.isCompatible(other) && isLocked() == ((BinInventorySlot) other).isLocked(); + return super.isCompatible(other) && ItemStack.isSameItemSameComponents(getLockStack(), ((BinInventorySlot) other).getLockStack()); } @Override diff --git a/src/main/java/mekanism/common/inventory/slot/EnergyInventorySlot.java b/src/main/java/mekanism/common/inventory/slot/EnergyInventorySlot.java index 7628023c194..ccf28d2f81f 100644 --- a/src/main/java/mekanism/common/inventory/slot/EnergyInventorySlot.java +++ b/src/main/java/mekanism/common/inventory/slot/EnergyInventorySlot.java @@ -24,10 +24,12 @@ @NothingNullByDefault public class EnergyInventorySlot extends BasicInventorySlot { + public static final Predicate DRAIN_VALIDATOR = EnergyCompatUtils::hasStrictEnergyHandler; + /** * Gets the energy from ItemStack conversion, ignoring the size of the item stack. */ - private static FloatingLong getPotentialConversion(@Nullable Level world, ItemStack itemStack) { + public static FloatingLong getPotentialConversion(@Nullable Level world, ItemStack itemStack) { ItemStackToEnergyRecipe foundRecipe = MekanismRecipeType.ENERGY_CONVERSION.getInputCache().findTypeBasedRecipe(world, itemStack); return foundRecipe == null ? FloatingLong.ZERO : foundRecipe.getOutput(itemStack); } @@ -91,10 +93,10 @@ public static EnergyInventorySlot drain(IEnergyContainer energyContainer, @Nulla //Otherwise, if we can accept any energy that is currently stored in the container, then we allow inserting the item return itemEnergyHandler.insertEnergy(storedEnergy, Action.SIMULATE).smallerThan(storedEnergy); }; - return new EnergyInventorySlot(energyContainer, insertPredicate.negate(), insertPredicate, EnergyCompatUtils::hasStrictEnergyHandler, listener, x, y); + return new EnergyInventorySlot(energyContainer, insertPredicate.negate(), insertPredicate, EnergyInventorySlot.DRAIN_VALIDATOR, listener, x, y); } - private static boolean fillInsertCheck(ItemStack stack) { + public static boolean fillInsertCheck(ItemStack stack) { IStrictEnergyHandler itemEnergyHandler = EnergyCompatUtils.getStrictEnergyHandler(stack); //If we can extract any energy we are valid. Note: We can't just use FloatingLong.ONE as depending on conversion rates // that might be less than a single unit and thus can't be extracted diff --git a/src/main/java/mekanism/common/inventory/slot/FluidInventorySlot.java b/src/main/java/mekanism/common/inventory/slot/FluidInventorySlot.java index 709d61e603d..28e39b32cbc 100644 --- a/src/main/java/mekanism/common/inventory/slot/FluidInventorySlot.java +++ b/src/main/java/mekanism/common/inventory/slot/FluidInventorySlot.java @@ -143,7 +143,7 @@ public static FluidInventorySlot drain(IExtendedFluidTank fluidTank, @Nullable I } @Nullable - static IFluidHandlerItem tryGetFluidHandlerUnstacked(ItemStack stack) { + public static IFluidHandlerItem tryGetFluidHandlerUnstacked(ItemStack stack) { //If we have more than one item in the input, check if we can fill a single item of it // The fluid handler for buckets returns false about being able to accept fluids if they are stacked // though we have special handling to only move one item at a time anyway @@ -156,7 +156,7 @@ static IFluidHandlerItem tryGetFluidHandlerUnstacked(ItemStack stack) { } //TODO: Should we make this also have the fluid type have to match a desired type??? - private static boolean isNonFullFluidContainer(@Nullable IFluidHandlerItem fluidHandler) { + public static boolean isNonFullFluidContainer(@Nullable IFluidHandlerItem fluidHandler) { if (fluidHandler != null) { for (int tank = 0, tanks = fluidHandler.getTanks(); tank < tanks; tank++) { if (fluidHandler.getFluidInTank(tank).getAmount() < fluidHandler.getTankCapacity(tank)) { diff --git a/src/main/java/mekanism/common/inventory/slot/ItemSlotsBuilder.java b/src/main/java/mekanism/common/inventory/slot/ItemSlotsBuilder.java deleted file mode 100644 index 86e95486b19..00000000000 --- a/src/main/java/mekanism/common/inventory/slot/ItemSlotsBuilder.java +++ /dev/null @@ -1,182 +0,0 @@ -package mekanism.common.inventory.slot; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Predicate; -import java.util.function.ToIntFunction; -import mekanism.api.IContentsListener; -import mekanism.api.chemical.gas.IGasTank; -import mekanism.api.chemical.infuse.IInfusionTank; -import mekanism.api.chemical.pigment.IPigmentTank; -import mekanism.api.chemical.slurry.ISlurryTank; -import mekanism.api.energy.IEnergyContainer; -import mekanism.api.fluid.IExtendedFluidTank; -import mekanism.api.inventory.IInventorySlot; -import mekanism.api.recipes.MekanismRecipe; -import mekanism.common.attachments.containers.AttachedContainers; -import mekanism.common.attachments.containers.ContainerType; -import mekanism.common.inventory.slot.chemical.GasInventorySlot; -import mekanism.common.inventory.slot.chemical.InfusionInventorySlot; -import mekanism.common.recipe.IMekanismRecipeTypeProvider; -import mekanism.common.recipe.lookup.cache.IInputRecipeCache; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.neoforged.neoforge.common.CommonHooks; -import net.neoforged.neoforge.common.util.INBTSerializable; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class ItemSlotsBuilder { - - //Note: We don't render these in the GUI, so they don't need to be the actual values, and attachments are automatically saved so we don't need a listener - @Nullable - private static final IContentsListener LISTENER = null; - private static final int X = 0; - private static final int Y = 0; - - public static ItemSlotsBuilder builder(ItemStack backingStack) { - return new ItemSlotsBuilder(backingStack); - } - - private final List slots = new ArrayList<>(); - private final ItemStack backingStack; - - private ItemSlotsBuilder(ItemStack backingStack) { - this.backingStack = backingStack; - } - - public List build() { - return List.copyOf(slots); - } - - public ItemSlotsBuilder addBasicFactorySlots(int process, Predicate recipeInputPredicate) { - return addBasicFactorySlots(process, recipeInputPredicate, false); - } - - public ItemSlotsBuilder addBasicFactorySlots(int process, Predicate recipeInputPredicate, boolean secondaryOutput) { - for (int i = 0; i < process; i++) { - //Note: We can just get away with using a simple input instead of a factory input slot and skip checking insert based on producing output - addInput(recipeInputPredicate) - .addOutput(); - if (secondaryOutput) { - addOutput(); - } - } - return this; - } - - public ItemSlotsBuilder addSlots(int count, SlotCreator creator) { - for (int i = 0; i < count; i++) { - addSlot(creator); - } - return this; - } - - public ItemSlotsBuilder addSlot(SlotCreator creator) { - return addSlot(creator.create(LISTENER, X, Y)); - } - - public ItemSlotsBuilder addSlot(IInventorySlot slot) { - slots.add(slot); - return this; - } - - public ItemSlotsBuilder addFuelSlot() { - return addFuelSlot(stack -> stack.getBurnTime(null)); - } - - public ItemSlotsBuilder addFuelSlot(ToIntFunction<@NotNull ItemStack> fuelValue) { - return addSlot(FuelInventorySlot.forFuel(fuelValue, LISTENER, X, Y)); - } - - public ItemSlotsBuilder addOutput() { - return addSlot(OutputInventorySlot.at(LISTENER, X, Y)); - } - - public ItemSlotsBuilder addInput(Predicate<@NotNull ItemStack> isItemValid) { - return addSlot(InputInventorySlot.at(isItemValid, LISTENER, X, Y)); - } - - public ItemSlotsBuilder addInput(IMekanismRecipeTypeProvider recipeType, - ContainsRecipe containsRecipe) { - return addInput(stack -> containsRecipe.check(recipeType.getInputCache(), null, stack)); - } - - public ItemSlotsBuilder addEnergy() { - return addEnergy(0); - } - - public ItemSlotsBuilder addEnergy(int containerIndex) { - return addEnergySlot(containerIndex, (container, listener, x, y) -> EnergyInventorySlot.fillOrConvert(container, () -> null, listener, x, y)); - } - - public ItemSlotsBuilder addEnergySlot(int containerIndex, ContainerBasedSlotCreator slotCreator) { - return addContainerInput(ContainerType.ENERGY, containerIndex, slotCreator); - } - - public ItemSlotsBuilder addFluidSlot(int tankIndex, ContainerBasedSlotCreator slotCreator) { - return addContainerInput(ContainerType.FLUID, tankIndex, slotCreator); - } - - public ItemSlotsBuilder addGasSlot(int tankIndex, ContainerBasedSlotCreator slotCreator) { - return addContainerInput(ContainerType.GAS, tankIndex, slotCreator); - } - - public ItemSlotsBuilder addGasSlotWithConversion(int tankIndex) { - return addGasSlot(tankIndex, (tank, listener, x, y) -> GasInventorySlot.fillOrConvert(tank, () -> null, listener, x, y)); - } - - public ItemSlotsBuilder addInfusionSlot(int tankIndex, ContainerBasedSlotCreator slotCreator) { - return addContainerInput(ContainerType.INFUSION, tankIndex, slotCreator); - } - - public ItemSlotsBuilder addInfusionSlotWithConversion(int tankIndex) { - return addInfusionSlot(tankIndex, (tank, listener, x, y) -> InfusionInventorySlot.fillOrConvert(tank, () -> null, listener, x, y)); - } - - public ItemSlotsBuilder addPigmentSlot(int tankIndex, ContainerBasedSlotCreator slotCreator) { - return addContainerInput(ContainerType.PIGMENT, tankIndex, slotCreator); - } - - public ItemSlotsBuilder addSlurrySlot(int tankIndex, ContainerBasedSlotCreator slotCreator) { - return addContainerInput(ContainerType.SLURRY, tankIndex, slotCreator); - } - - private > ItemSlotsBuilder addContainerInput(ContainerType, ?> containerType, - int containerIndex, ContainerBasedSlotCreator slotCreator) { - //Note: In theory this shouldn't cause problems as we don't allow interacting with the actual slots, and we don't expose an ItemHandler - // cap (currently ever), for stacked stacks - AttachedContainers attachment = containerType.getAttachment(backingStack); - if (attachment == null) { - throw new IllegalStateException("Expected stack to have attached " + containerType.getAttachmentName() + " containers."); - } - List containers = attachment.getContainers(); - if (containerIndex >= containers.size()) { - throw new IllegalStateException("Expected stack to have an attached " + containerType.getAttachmentName() + " container with index " + containerIndex); - } - return addContainerSlot(containers.get(containerIndex), slotCreator); - } - - public ItemSlotsBuilder addContainerSlot(CONTAINER container, ContainerBasedSlotCreator slotCreator) { - return addSlot(slotCreator.create(container, LISTENER, X, Y)); - } - - @FunctionalInterface - public interface ContainsRecipe { - - boolean check(INPUT_CACHE cache, @Nullable Level level, ItemStack stack); - } - - @FunctionalInterface - public interface SlotCreator { - - IInventorySlot create(@Nullable IContentsListener listener, int x, int y); - } - - @FunctionalInterface - public interface ContainerBasedSlotCreator { - - IInventorySlot create(CONTAINER container, @Nullable IContentsListener listener, int x, int y); - } -} \ No newline at end of file diff --git a/src/main/java/mekanism/common/inventory/slot/SecurityInventorySlot.java b/src/main/java/mekanism/common/inventory/slot/SecurityInventorySlot.java index f05c29c13b3..732b30cedb2 100644 --- a/src/main/java/mekanism/common/inventory/slot/SecurityInventorySlot.java +++ b/src/main/java/mekanism/common/inventory/slot/SecurityInventorySlot.java @@ -18,23 +18,24 @@ @NothingNullByDefault public class SecurityInventorySlot extends BasicInventorySlot { - private static final Predicate<@NotNull ItemStack> validator = stack -> IItemSecurityUtils.INSTANCE.ownerCapability(stack) != null; + public static final Predicate<@NotNull ItemStack> VALIDATOR = stack -> IItemSecurityUtils.INSTANCE.ownerCapability(stack) != null; + public static final Predicate<@NotNull ItemStack> LOCK_EXTRACT_PREDICATE = stack -> IItemSecurityUtils.INSTANCE.getOwnerUUID(stack) != null; + public static final Predicate<@NotNull ItemStack> LOCK_INSERT_PREDICATE = stack -> IItemSecurityUtils.INSTANCE.getOwnerUUID(stack) == null; public static SecurityInventorySlot unlock(Supplier ownerSupplier, @Nullable IContentsListener listener, int x, int y) { Objects.requireNonNull(ownerSupplier, "Owner supplier cannot be null"); - return new SecurityInventorySlot(stack -> IItemSecurityUtils.INSTANCE.getOwnerUUID(stack) == null, stack -> { + return new SecurityInventorySlot(LOCK_INSERT_PREDICATE, stack -> { UUID ownerUUID = IItemSecurityUtils.INSTANCE.getOwnerUUID(stack); return ownerUUID != null && ownerUUID.equals(ownerSupplier.get()); }, listener, x, y); } public static SecurityInventorySlot lock(@Nullable IContentsListener listener, int x, int y) { - Predicate<@NotNull ItemStack> insertPredicate = stack -> IItemSecurityUtils.INSTANCE.getOwnerUUID(stack) == null; - return new SecurityInventorySlot(insertPredicate.negate(), insertPredicate, listener, x, y); + return new SecurityInventorySlot(LOCK_EXTRACT_PREDICATE, LOCK_INSERT_PREDICATE, listener, x, y); } private SecurityInventorySlot(Predicate<@NotNull ItemStack> canExtract, Predicate<@NotNull ItemStack> canInsert, @Nullable IContentsListener listener, int x, int y) { - super(canExtract, canInsert, validator, listener, x, y); + super(canExtract, canInsert, VALIDATOR, listener, x, y); } public void unlock(UUID ownerUUID) { diff --git a/src/main/java/mekanism/common/inventory/slot/chemical/GasInventorySlot.java b/src/main/java/mekanism/common/inventory/slot/chemical/GasInventorySlot.java index af3ddfd4f05..e9488f701ad 100644 --- a/src/main/java/mekanism/common/inventory/slot/chemical/GasInventorySlot.java +++ b/src/main/java/mekanism/common/inventory/slot/chemical/GasInventorySlot.java @@ -26,7 +26,7 @@ public class GasInventorySlot extends ChemicalInventorySlot { /** * Gets the GasStack from ItemStack conversion, ignoring the size of the item stack. */ - private static GasStack getPotentialConversion(@Nullable Level world, ItemStack itemStack) { + public static GasStack getPotentialConversion(@Nullable Level world, ItemStack itemStack) { return getPotentialConversion(MekanismRecipeType.GAS_CONVERSION, world, itemStack, GasStack.EMPTY); } @@ -36,7 +36,8 @@ private static GasStack getPotentialConversion(@Nullable Level world, ItemStack public static GasInventorySlot rotaryDrain(IGasTank gasTank, BooleanSupplier modeSupplier, @Nullable IContentsListener listener, int x, int y) { Objects.requireNonNull(gasTank, "Gas tank cannot be null"); Objects.requireNonNull(modeSupplier, "Mode supplier cannot be null"); - Predicate<@NotNull ItemStack> insertPredicate = getDrainInsertPredicate(gasTank, Capabilities.GAS).and(stack -> modeSupplier.getAsBoolean()); + Predicate<@NotNull ItemStack> drainInsertPredicate = getDrainInsertPredicate(gasTank, Capabilities.GAS); + Predicate<@NotNull ItemStack> insertPredicate = stack -> modeSupplier.getAsBoolean() && drainInsertPredicate.test(stack); return new GasInventorySlot(gasTank, insertPredicate.negate(), insertPredicate, listener, x, y); } diff --git a/src/main/java/mekanism/common/inventory/slot/chemical/InfusionInventorySlot.java b/src/main/java/mekanism/common/inventory/slot/chemical/InfusionInventorySlot.java index e65a005f117..1e5f7f3532e 100644 --- a/src/main/java/mekanism/common/inventory/slot/chemical/InfusionInventorySlot.java +++ b/src/main/java/mekanism/common/inventory/slot/chemical/InfusionInventorySlot.java @@ -25,7 +25,7 @@ public class InfusionInventorySlot extends ChemicalInventorySlot fill(MergedChemica Predicate<@NotNull ItemStack> infusionExtractPredicate = ChemicalInventorySlot.getFillExtractPredicate(chemicalTank.getInfusionTank(), Capabilities.INFUSION); Predicate<@NotNull ItemStack> pigmentExtractPredicate = ChemicalInventorySlot.getFillExtractPredicate(chemicalTank.getPigmentTank(), Capabilities.PIGMENT); Predicate<@NotNull ItemStack> slurryExtractPredicate = ChemicalInventorySlot.getFillExtractPredicate(chemicalTank.getSlurryTank(), Capabilities.SLURRY); - Predicate<@NotNull ItemStack> gasInsertPredicate = stack -> ChemicalInventorySlot.fillInsertCheck(chemicalTank.getGasTank(), Capabilities.GAS, stack); - Predicate<@NotNull ItemStack> infusionInsertPredicate = stack -> ChemicalInventorySlot.fillInsertCheck(chemicalTank.getInfusionTank(), Capabilities.INFUSION, stack); - Predicate<@NotNull ItemStack> pigmentInsertPredicate = stack -> ChemicalInventorySlot.fillInsertCheck(chemicalTank.getPigmentTank(), Capabilities.PIGMENT, stack); - Predicate<@NotNull ItemStack> slurryInsertPredicate = stack -> ChemicalInventorySlot.fillInsertCheck(chemicalTank.getSlurryTank(), Capabilities.SLURRY, stack); return new MergedChemicalInventorySlot<>(chemicalTank, (stack, automationType) -> { if (automationType == AutomationType.MANUAL) { //Always allow the player to manually extract @@ -61,13 +57,15 @@ public static MergedChemicalInventorySlot fill(MergedChemica slurryExtractPredicate.test(stack); }; }, (stack, automationType) -> switch (chemicalTank.getCurrent()) { - case GAS -> gasInsertPredicate.test(stack); - case INFUSION -> infusionInsertPredicate.test(stack); - case PIGMENT -> pigmentInsertPredicate.test(stack); - case SLURRY -> slurryInsertPredicate.test(stack); + case GAS -> ChemicalInventorySlot.fillInsertCheck(chemicalTank.getGasTank(), Capabilities.GAS, stack); + case INFUSION -> ChemicalInventorySlot.fillInsertCheck(chemicalTank.getInfusionTank(), Capabilities.INFUSION, stack); + case PIGMENT -> ChemicalInventorySlot.fillInsertCheck(chemicalTank.getPigmentTank(), Capabilities.PIGMENT, stack); + case SLURRY -> ChemicalInventorySlot.fillInsertCheck(chemicalTank.getSlurryTank(), Capabilities.SLURRY, stack); //Tank is empty, only allow it if one of the chemical insert predicates matches - case EMPTY -> gasInsertPredicate.test(stack) || infusionInsertPredicate.test(stack) || pigmentInsertPredicate.test(stack) || - slurryInsertPredicate.test(stack); + case EMPTY -> ChemicalInventorySlot.fillInsertCheck(chemicalTank.getGasTank(), Capabilities.GAS, stack) || + ChemicalInventorySlot.fillInsertCheck(chemicalTank.getInfusionTank(), Capabilities.INFUSION, stack) || + ChemicalInventorySlot.fillInsertCheck(chemicalTank.getPigmentTank(), Capabilities.PIGMENT, stack) || + ChemicalInventorySlot.fillInsertCheck(chemicalTank.getSlurryTank(), Capabilities.SLURRY, stack); }, listener, x, y); } diff --git a/src/main/java/mekanism/common/item/ItemEnergized.java b/src/main/java/mekanism/common/item/ItemEnergized.java index 60689773279..f746b16633e 100644 --- a/src/main/java/mekanism/common/item/ItemEnergized.java +++ b/src/main/java/mekanism/common/item/ItemEnergized.java @@ -3,12 +3,11 @@ import java.util.List; import java.util.function.Predicate; import mekanism.api.AutomationType; -import mekanism.api.energy.IEnergyContainer; import mekanism.api.math.FloatingLongSupplier; import mekanism.common.attachments.IAttachmentAware; import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.energy.EnergyContainersBuilder; import mekanism.common.capabilities.energy.BasicEnergyContainer; -import mekanism.common.capabilities.energy.item.RateLimitEnergyContainer; import mekanism.common.config.MekanismConfig; import mekanism.common.registration.impl.CreativeTabDeferredRegister.ICustomCreativeTabContents; import mekanism.common.util.StorageUtils; @@ -17,7 +16,6 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; -import net.minecraft.world.level.Level; import net.neoforged.bus.api.IEventBus; import org.jetbrains.annotations.NotNull; @@ -67,15 +65,15 @@ public void addItems(CreativeModeTab.Output tabOutput) { tabOutput.accept(StorageUtils.getFilledEnergyVariant(new ItemStack(this))); } - protected IEnergyContainer getDefaultEnergyContainer(ItemStack stack) { - return RateLimitEnergyContainer.create(chargeRateSupplier, maxEnergySupplier, canExtract, canInsert); + protected EnergyContainersBuilder addDefaultEnergyContainers(EnergyContainersBuilder builder) { + return builder.addBasic(canExtract, canInsert, chargeRateSupplier, maxEnergySupplier); } @Override public void attachAttachments(IEventBus eventBus) { //Note: We interact with this capability using "manual" as the automation type, to ensure we can properly bypass the energy limit for extracting // Internal is used by the "null" side, which is what will get used for most items - ContainerType.ENERGY.addDefaultContainer(eventBus, this, this::getDefaultEnergyContainer, MekanismConfig.gear); + ContainerType.ENERGY.addDefaultCreators(eventBus, this, () -> addDefaultEnergyContainers(EnergyContainersBuilder.builder()).build(), MekanismConfig.gear); } @Override diff --git a/src/main/java/mekanism/common/item/ItemGaugeDropper.java b/src/main/java/mekanism/common/item/ItemGaugeDropper.java index 84f47cf1c5f..854a816b005 100644 --- a/src/main/java/mekanism/common/item/ItemGaugeDropper.java +++ b/src/main/java/mekanism/common/item/ItemGaugeDropper.java @@ -4,13 +4,22 @@ import java.util.OptionalInt; import mekanism.api.chemical.Chemical; import mekanism.api.chemical.ChemicalStack; +import mekanism.api.chemical.ChemicalTankBuilder; import mekanism.api.chemical.IChemicalHandler; import mekanism.api.chemical.gas.GasStack; import mekanism.api.chemical.infuse.InfusionStack; import mekanism.api.chemical.pigment.PigmentStack; import mekanism.api.chemical.slurry.SlurryStack; import mekanism.api.fluid.IExtendedFluidHandler; +import mekanism.common.attachments.containers.chemical.gas.ComponentBackedGasTank; +import mekanism.common.attachments.containers.chemical.infuse.ComponentBackedInfusionTank; +import mekanism.common.attachments.containers.chemical.merged.MergedTankCreator; +import mekanism.common.attachments.containers.chemical.pigment.ComponentBackedPigmentTank; +import mekanism.common.attachments.containers.chemical.slurry.ComponentBackedSlurryTank; +import mekanism.common.attachments.containers.fluid.ComponentBackedFluidTank; import mekanism.common.capabilities.Capabilities; +import mekanism.common.capabilities.fluid.BasicFluidTank; +import mekanism.common.config.MekanismConfig; import mekanism.common.util.ChemicalUtil; import mekanism.common.util.FluidUtils; import mekanism.common.util.StorageUtils; @@ -29,6 +38,19 @@ public class ItemGaugeDropper extends Item { + public static final MergedTankCreator MERGED_TANK_CREATOR = new MergedTankCreator( + (type, attachedTo, containerIndex) -> new ComponentBackedGasTank(attachedTo, containerIndex, ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrueBi, + ChemicalTankBuilder.GAS.alwaysTrue, MekanismConfig.gear.gaugeDroppedTransferRate, MekanismConfig.gear.gaugeDropperCapacity, null), + (type, attachedTo, containerIndex) -> new ComponentBackedInfusionTank(attachedTo, containerIndex, ChemicalTankBuilder.INFUSION.alwaysTrueBi, ChemicalTankBuilder.INFUSION.alwaysTrueBi, + ChemicalTankBuilder.INFUSION.alwaysTrue, MekanismConfig.gear.gaugeDroppedTransferRate, MekanismConfig.gear.gaugeDropperCapacity, null), + (type, attachedTo, containerIndex) -> new ComponentBackedPigmentTank(attachedTo, containerIndex, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, + ChemicalTankBuilder.PIGMENT.alwaysTrue, MekanismConfig.gear.gaugeDroppedTransferRate, MekanismConfig.gear.gaugeDropperCapacity, null), + (type, attachedTo, containerIndex) -> new ComponentBackedSlurryTank(attachedTo, containerIndex, ChemicalTankBuilder.SLURRY.alwaysTrueBi, ChemicalTankBuilder.SLURRY.alwaysTrueBi, + ChemicalTankBuilder.SLURRY.alwaysTrue, MekanismConfig.gear.gaugeDroppedTransferRate, MekanismConfig.gear.gaugeDropperCapacity, null), + (type, attachedTo, containerIndex) -> new ComponentBackedFluidTank(attachedTo, containerIndex, BasicFluidTank.alwaysTrueBi, BasicFluidTank.alwaysTrueBi, + BasicFluidTank.alwaysTrue, MekanismConfig.gear.gaugeDroppedTransferRate, MekanismConfig.gear.gaugeDropperCapacity) + ); + public ItemGaugeDropper(Properties properties) { super(properties.stacksTo(1).rarity(Rarity.UNCOMMON)); } diff --git a/src/main/java/mekanism/common/item/block/ItemBlockBin.java b/src/main/java/mekanism/common/item/block/ItemBlockBin.java index cfc7d65175b..3bf86212561 100644 --- a/src/main/java/mekanism/common/item/block/ItemBlockBin.java +++ b/src/main/java/mekanism/common/item/block/ItemBlockBin.java @@ -3,6 +3,7 @@ import java.util.List; import mekanism.api.text.EnumColor; import mekanism.common.MekanismLang; +import mekanism.common.attachments.containers.item.ComponentBackedBinInventorySlot; import mekanism.common.block.attribute.Attribute; import mekanism.common.block.basic.BlockBin; import mekanism.common.inventory.slot.BinInventorySlot; @@ -13,9 +14,7 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; -import net.minecraft.world.level.Level; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; public class ItemBlockBin extends ItemBlockTooltip implements IDroppableAttachmentContents { @@ -30,7 +29,7 @@ public BinTier getTier() { @Override protected void addStats(@NotNull ItemStack stack, @NotNull Item.TooltipContext context, @NotNull List tooltip, @NotNull TooltipFlag flag) { - BinInventorySlot slot = BinInventorySlot.getForStack(stack); + ComponentBackedBinInventorySlot slot = BinInventorySlot.getForStack(stack); BinTier tier = getTier(); if (slot != null && tier != null) { if (slot.isEmpty()) { diff --git a/src/main/java/mekanism/common/item/block/ItemBlockEnergyCube.java b/src/main/java/mekanism/common/item/block/ItemBlockEnergyCube.java index e84cbf98232..ae1851db68e 100644 --- a/src/main/java/mekanism/common/item/block/ItemBlockEnergyCube.java +++ b/src/main/java/mekanism/common/item/block/ItemBlockEnergyCube.java @@ -5,15 +5,15 @@ import java.util.Map; import java.util.function.Consumer; import mekanism.api.RelativeSide; -import mekanism.api.energy.IEnergyContainer; import mekanism.api.text.EnumColor; import mekanism.client.render.RenderPropertiesProvider; import mekanism.common.MekanismLang; import mekanism.common.attachments.component.AttachedSideConfig; import mekanism.common.attachments.component.AttachedSideConfig.LightConfigInfo; +import mekanism.common.attachments.containers.energy.ComponentBackedEnergyCubeContainer; +import mekanism.common.attachments.containers.energy.EnergyContainersBuilder; import mekanism.common.block.BlockEnergyCube; import mekanism.common.block.attribute.Attribute; -import mekanism.common.capabilities.energy.item.EnergyCubeRateLimitEnergyContainer; import mekanism.common.config.MekanismConfig; import mekanism.common.lib.transmitter.TransmissionType; import mekanism.common.registration.impl.CreativeTabDeferredRegister.ICustomCreativeTabContents; @@ -116,7 +116,7 @@ private ItemStack withEnergyCubeSideConfig(AttachedSideConfig config) { } @Override - protected IEnergyContainer getDefaultEnergyContainer(ItemStack stack) { - return EnergyCubeRateLimitEnergyContainer.create(getTier()); + protected EnergyContainersBuilder addDefaultEnergyContainers(EnergyContainersBuilder builder) { + return builder.addContainer(ComponentBackedEnergyCubeContainer::create); } } \ No newline at end of file diff --git a/src/main/java/mekanism/common/item/block/ItemBlockTooltip.java b/src/main/java/mekanism/common/item/block/ItemBlockTooltip.java index fc17552595d..e203a6721bc 100644 --- a/src/main/java/mekanism/common/item/block/ItemBlockTooltip.java +++ b/src/main/java/mekanism/common/item/block/ItemBlockTooltip.java @@ -5,7 +5,6 @@ import java.util.function.Predicate; import mekanism.api.AutomationType; import mekanism.api.Upgrade; -import mekanism.api.energy.IEnergyContainer; import mekanism.api.math.FloatingLong; import mekanism.api.math.FloatingLongSupplier; import mekanism.api.security.IItemSecurityUtils; @@ -16,7 +15,8 @@ import mekanism.common.attachments.IAttachmentAware; import mekanism.common.attachments.component.UpgradeAware; import mekanism.common.attachments.containers.ContainerType; -import mekanism.common.capabilities.security.SecurityObject; +import mekanism.common.attachments.containers.energy.ComponentBackedNoClampEnergyContainer; +import mekanism.common.attachments.containers.energy.EnergyContainersBuilder; import mekanism.common.block.attribute.Attribute; import mekanism.common.block.attribute.AttributeEnergy; import mekanism.common.block.attribute.AttributeHasBounding; @@ -26,8 +26,7 @@ import mekanism.common.block.interfaces.IHasDescription; import mekanism.common.capabilities.ICapabilityAware; import mekanism.common.capabilities.energy.BasicEnergyContainer; -import mekanism.common.capabilities.energy.item.NoClampRateLimitEnergyContainer; -import mekanism.common.capabilities.energy.item.RateLimitEnergyContainer; +import mekanism.common.capabilities.security.SecurityObject; import mekanism.common.config.MekanismConfig; import mekanism.common.registries.MekanismDataComponents; import mekanism.common.util.InventoryUtils; @@ -51,7 +50,6 @@ import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; import net.neoforged.neoforge.fluids.FluidStack; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; public class ItemBlockTooltip extends ItemBlockMekanism implements ICapabilityAware, IAttachmentAware { @@ -161,8 +159,7 @@ protected boolean exposesEnergyCap() { return Attribute.has(getBlock(), AttributeEnergy.class); } - @Nullable - protected IEnergyContainer getDefaultEnergyContainer(ItemStack stack) { + protected EnergyContainersBuilder addDefaultEnergyContainers(EnergyContainersBuilder builder) { BLOCK block = getBlock(); AttributeEnergy attributeEnergy = Attribute.get(block, AttributeEnergy.class); if (attributeEnergy == null) { @@ -170,12 +167,15 @@ protected IEnergyContainer getDefaultEnergyContainer(ItemStack stack) { } FloatingLongSupplier maxEnergy = attributeEnergy::getStorage; if (Attribute.matches(block, AttributeUpgradeSupport.class, attribute -> attribute.supportedUpgrades().contains(Upgrade.ENERGY))) { - //If our block supports energy upgrades, make a more dynamically updating cache for our item's max energy - maxEnergy = new UpgradeBasedFloatingLongCache(stack, maxEnergy); - return NoClampRateLimitEnergyContainer.create(maxEnergy, BasicEnergyContainer.manualOnly, getEnergyCapInsertPredicate()); + return builder.addContainer((type, attachedTo, containerIndex) -> { + //If our block supports energy upgrades, make a more dynamically updating cache for our item's max energy + FloatingLongSupplier capacity = new UpgradeBasedFloatingLongCache(attachedTo, maxEnergy); + return new ComponentBackedNoClampEnergyContainer(attachedTo, containerIndex, BasicEnergyContainer.manualOnly, getEnergyCapInsertPredicate(), + () -> capacity.get().multiply(0.005), capacity); + }); } //If we don't support energy upgrades, our max energy isn't dependent on another attachment, we can safely clamp to the config values - return RateLimitEnergyContainer.create(maxEnergy, BasicEnergyContainer.manualOnly, getEnergyCapInsertPredicate()); + return builder.addBasic(BasicEnergyContainer.manualOnly, getEnergyCapInsertPredicate(), () -> maxEnergy.get().multiply(0.005), maxEnergy); } @Override @@ -191,7 +191,8 @@ public void attachAttachments(IEventBus eventBus) { if (Attribute.has(getBlock(), AttributeEnergy.class)) { //Only expose the capability the required configs are loaded and the item wants to IEventBus energyEventBus = exposesEnergyCap() ? eventBus : null; - ContainerType.ENERGY.addDefaultContainer(energyEventBus, this, this::getDefaultEnergyContainer, MekanismConfig.storage, MekanismConfig.usage); + ContainerType.ENERGY.addDefaultCreators(energyEventBus, this, () -> addDefaultEnergyContainers(EnergyContainersBuilder.builder()).build(), + MekanismConfig.storage, MekanismConfig.usage); } } diff --git a/src/main/java/mekanism/common/item/block/machine/ItemBlockResistiveHeater.java b/src/main/java/mekanism/common/item/block/machine/ItemBlockResistiveHeater.java index 847c4604c82..3dfd2029899 100644 --- a/src/main/java/mekanism/common/item/block/machine/ItemBlockResistiveHeater.java +++ b/src/main/java/mekanism/common/item/block/machine/ItemBlockResistiveHeater.java @@ -1,12 +1,9 @@ package mekanism.common.item.block.machine; -import mekanism.api.energy.IEnergyContainer; -import mekanism.common.block.attribute.Attribute; -import mekanism.common.block.attribute.AttributeEnergy; +import mekanism.common.attachments.containers.energy.ComponentBackedResistiveEnergyContainer; +import mekanism.common.attachments.containers.energy.EnergyContainersBuilder; import mekanism.common.block.prefab.BlockTile; -import mekanism.common.capabilities.energy.item.ResistiveHeaterItemEnergyContainer; import mekanism.common.item.block.ItemBlockTooltip; -import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.Nullable; public class ItemBlockResistiveHeater extends ItemBlockTooltip> { @@ -17,7 +14,7 @@ public ItemBlockResistiveHeater(BlockTile block) { @Nullable @Override - protected IEnergyContainer getDefaultEnergyContainer(ItemStack stack) { - return ResistiveHeaterItemEnergyContainer.create(Attribute.get(getBlock(), AttributeEnergy.class)); + protected EnergyContainersBuilder addDefaultEnergyContainers(EnergyContainersBuilder builder) { + return builder.addContainer(ComponentBackedResistiveEnergyContainer::create); } } \ No newline at end of file diff --git a/src/main/java/mekanism/common/item/gear/ItemFreeRunners.java b/src/main/java/mekanism/common/item/gear/ItemFreeRunners.java index d48a4970325..0b2607f88f5 100644 --- a/src/main/java/mekanism/common/item/gear/ItemFreeRunners.java +++ b/src/main/java/mekanism/common/item/gear/ItemFreeRunners.java @@ -15,7 +15,7 @@ import mekanism.common.MekanismLang; import mekanism.common.attachments.IAttachmentAware; import mekanism.common.attachments.containers.ContainerType; -import mekanism.common.capabilities.energy.item.RateLimitEnergyContainer; +import mekanism.common.attachments.containers.energy.EnergyContainersBuilder; import mekanism.common.config.MekanismConfig; import mekanism.common.item.gear.ItemFreeRunners.FreeRunnerMode; import mekanism.common.item.interfaces.IItemHUDProvider; @@ -121,10 +121,9 @@ public ItemEnchantments getAllEnchantments(@NotNull ItemStack stack) { @Override public void attachAttachments(IEventBus eventBus) { - ContainerType.ENERGY.addDefaultContainer(eventBus, this, stack -> RateLimitEnergyContainer.create( - MekanismConfig.gear.freeRunnerChargeRate, - MekanismConfig.gear.freeRunnerMaxEnergy - ), MekanismConfig.gear); + ContainerType.ENERGY.addDefaultCreators(eventBus, this, () -> EnergyContainersBuilder.builder() + .addBasic(MekanismConfig.gear.freeRunnerChargeRate, MekanismConfig.gear.freeRunnerMaxEnergy) + .build(), MekanismConfig.gear); } @Override diff --git a/src/main/java/mekanism/common/item/gear/ItemGasArmor.java b/src/main/java/mekanism/common/item/gear/ItemGasArmor.java index bce134bff45..57051cc5e98 100644 --- a/src/main/java/mekanism/common/item/gear/ItemGasArmor.java +++ b/src/main/java/mekanism/common/item/gear/ItemGasArmor.java @@ -5,7 +5,7 @@ import mekanism.api.providers.IGasProvider; import mekanism.common.attachments.IAttachmentAware; import mekanism.common.attachments.containers.ContainerType; -import mekanism.common.capabilities.chemical.variable.RateLimitGasTank; +import mekanism.common.attachments.containers.chemical.gas.GasTanksBuilder; import mekanism.common.config.MekanismConfig; import mekanism.common.config.value.CachedLongValue; import mekanism.common.item.interfaces.IGasItem; @@ -63,10 +63,8 @@ public void addItems(CreativeModeTab.Output tabOutput) { @Override public void attachAttachments(IEventBus eventBus) { - ContainerType.GAS.addDefaultContainer(eventBus, this, stack -> RateLimitGasTank.createInternalStorage( - getFillRate(), - getMaxGas(), - gas -> gas == getGasType().getChemical() - ), MekanismConfig.gear); + ContainerType.GAS.addDefaultCreators(eventBus, this, () -> GasTanksBuilder.builder() + .addInternalStorage(getFillRate(), getMaxGas(), gas -> gas == getGasType().getChemical()) + .build(), MekanismConfig.gear); } } \ No newline at end of file diff --git a/src/main/java/mekanism/common/item/gear/ItemMekaSuitArmor.java b/src/main/java/mekanism/common/item/gear/ItemMekaSuitArmor.java index ff80861ab86..3616b730949 100644 --- a/src/main/java/mekanism/common/item/gear/ItemMekaSuitArmor.java +++ b/src/main/java/mekanism/common/item/gear/ItemMekaSuitArmor.java @@ -12,10 +12,8 @@ import mekanism.api.chemical.gas.Gas; import mekanism.api.chemical.gas.GasStack; import mekanism.api.chemical.gas.IGasHandler; -import mekanism.api.chemical.gas.IGasTank; import mekanism.api.datamaps.MekaSuitAbsorption; import mekanism.api.energy.IEnergyContainer; -import mekanism.api.fluid.IExtendedFluidTank; import mekanism.api.gear.ICustomModule; import mekanism.api.gear.ICustomModule.ModuleDamageAbsorbInfo; import mekanism.api.gear.IModule; @@ -32,12 +30,16 @@ import mekanism.common.MekanismLang; import mekanism.common.attachments.IAttachmentAware; import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.chemical.gas.ComponentBackedGasTank; +import mekanism.common.attachments.containers.chemical.gas.GasTanksBuilder; +import mekanism.common.attachments.containers.energy.ComponentBackedNoClampEnergyContainer; +import mekanism.common.attachments.containers.energy.EnergyContainersBuilder; +import mekanism.common.attachments.containers.fluid.ComponentBackedFluidTank; +import mekanism.common.attachments.containers.fluid.FluidTanksBuilder; import mekanism.common.capabilities.Capabilities; import mekanism.common.capabilities.chemical.item.ChemicalTankSpec; -import mekanism.common.capabilities.chemical.variable.RateLimitGasTank; -import mekanism.common.capabilities.energy.item.NoClampRateLimitEnergyContainer; +import mekanism.common.capabilities.energy.BasicEnergyContainer; import mekanism.common.capabilities.fluid.item.FluidTankSpec; -import mekanism.common.capabilities.fluid.item.RateLimitFluidTank; import mekanism.common.capabilities.laser.item.LaserDissipationHandler; import mekanism.common.capabilities.radiation.item.RadiationShieldingHandler; import mekanism.common.config.MekanismConfig; @@ -280,27 +282,29 @@ public void inventoryTick(@NotNull ItemStack stack, @NotNull Level level, @NotNu @Override public void attachAttachments(IEventBus eventBus) { - ContainerType.ENERGY.addDefaultContainer(eventBus, this, stack -> NoClampRateLimitEnergyContainer.create( - () -> ModuleEnergyUnit.getChargeRate(stack, MekanismConfig.gear.mekaSuitBaseChargeRate.get()), - () -> ModuleEnergyUnit.getEnergyCapacity(stack, MekanismConfig.gear.mekaSuitBaseEnergyCapacity.get()) - ), MekanismConfig.gear); + ContainerType.ENERGY.addDefaultCreators(eventBus, this, () -> EnergyContainersBuilder.builder() + .addContainer((type, attachedTo, containerIndex) -> new ComponentBackedNoClampEnergyContainer(attachedTo, containerIndex, BasicEnergyContainer.manualOnly, + BasicEnergyContainer.alwaysTrue, + () -> ModuleEnergyUnit.getChargeRate(attachedTo, MekanismConfig.gear.mekaSuitBaseChargeRate.get()), + () -> ModuleEnergyUnit.getEnergyCapacity(attachedTo, MekanismConfig.gear.mekaSuitBaseEnergyCapacity.get()) + )).build(), MekanismConfig.gear); if (!gasTankSpecs.isEmpty()) { - ContainerType.GAS.addDefaultContainers(eventBus, this, stack -> { - List list = new ArrayList<>(gasTankSpecs.size()); + ContainerType.GAS.addDefaultCreators(eventBus, this, () -> { + GasTanksBuilder builder = GasTanksBuilder.builder(); for (ChemicalTankSpec spec : gasTankSpecs) { - list.add(spec.createTank(RateLimitGasTank::create, stack)); + spec.addTank(builder, ComponentBackedGasTank::new); } - return list; + return builder.build(); }, MekanismConfig.gear); } if (!fluidTankSpecs.isEmpty()) { - ContainerType.FLUID.addDefaultContainers(eventBus, this, stack -> { - List list = new ArrayList<>(fluidTankSpecs.size()); + ContainerType.FLUID.addDefaultCreators(eventBus, this, () -> { + FluidTanksBuilder builder = FluidTanksBuilder.builder(); for (FluidTankSpec spec : fluidTankSpecs) { - list.add(spec.createTank(RateLimitFluidTank::create, stack)); + spec.addTank(builder, ComponentBackedFluidTank::new); } - return list; + return builder.build(); }, MekanismConfig.gear); } } diff --git a/src/main/java/mekanism/common/item/gear/ItemMekaTool.java b/src/main/java/mekanism/common/item/gear/ItemMekaTool.java index 2ce0b820367..52219d751a1 100644 --- a/src/main/java/mekanism/common/item/gear/ItemMekaTool.java +++ b/src/main/java/mekanism/common/item/gear/ItemMekaTool.java @@ -1,7 +1,6 @@ package mekanism.common.item.gear; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMultimap; import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2IntArrayMap; @@ -27,7 +26,8 @@ import mekanism.client.key.MekanismKeyHandler; import mekanism.common.Mekanism; import mekanism.common.MekanismLang; -import mekanism.common.capabilities.energy.item.NoClampRateLimitEnergyContainer; +import mekanism.common.attachments.containers.energy.ComponentBackedNoClampEnergyContainer; +import mekanism.common.attachments.containers.energy.EnergyContainersBuilder; import mekanism.common.config.MekanismConfig; import mekanism.common.content.gear.IBlastingItem; import mekanism.common.content.gear.IRadialModuleContainerItem; @@ -60,7 +60,6 @@ import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.EquipmentSlotGroup; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.ai.attributes.Attribute; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.AttributeModifier.Operation; import net.minecraft.world.entity.ai.attributes.Attributes; @@ -463,12 +462,10 @@ public boolean canApplyAtEnchantingTable(ItemStack stack, Enchantment enchantmen } @Override - protected IEnergyContainer getDefaultEnergyContainer(ItemStack stack) { - return NoClampRateLimitEnergyContainer.create( - () -> ModuleEnergyUnit.getChargeRate(stack, MekanismConfig.gear.mekaToolBaseChargeRate.get()), - () -> ModuleEnergyUnit.getEnergyCapacity(stack, MekanismConfig.gear.mekaToolBaseEnergyCapacity.get()), - canExtract, canInsert - ); + protected EnergyContainersBuilder addDefaultEnergyContainers(EnergyContainersBuilder builder) { + return builder.addContainer((type, attachedTo, containerIndex) -> new ComponentBackedNoClampEnergyContainer(attachedTo, containerIndex, canExtract, canInsert, + () -> ModuleEnergyUnit.getChargeRate(attachedTo, MekanismConfig.gear.mekaToolBaseChargeRate.get()), + () -> ModuleEnergyUnit.getEnergyCapacity(attachedTo, MekanismConfig.gear.mekaToolBaseEnergyCapacity.get()))); } @Override diff --git a/src/main/java/mekanism/common/item/loot/CopyContainersLootFunction.java b/src/main/java/mekanism/common/item/loot/CopyContainersLootFunction.java index f6eba11727f..dd0ce8405cc 100644 --- a/src/main/java/mekanism/common/item/loot/CopyContainersLootFunction.java +++ b/src/main/java/mekanism/common/item/loot/CopyContainersLootFunction.java @@ -104,7 +104,7 @@ public Builder copy(ContainerType containerType) { @Override public LootItemFunction build() { //Ensure the operations are always saved in the same order - containerTypes.sort(Comparator.comparing(ContainerType::getAttachmentName)); + containerTypes.sort(Comparator.comparing(ContainerType::getComponentName)); return new CopyContainersLootFunction(this.containerTypes); } } diff --git a/src/main/java/mekanism/common/recipe/ClearConfigurationRecipe.java b/src/main/java/mekanism/common/recipe/ClearConfigurationRecipe.java index e7927707e19..2fd7c9c6b9a 100644 --- a/src/main/java/mekanism/common/recipe/ClearConfigurationRecipe.java +++ b/src/main/java/mekanism/common/recipe/ClearConfigurationRecipe.java @@ -58,7 +58,7 @@ public class ClearConfigurationRecipe extends CustomRecipe { set.add(MekanismDataComponents.FILTER_AWARE); set.add(MekanismDataComponents.CONFIGURATION_DATA); - set.add(MekanismDataComponents.HEAT_CAPACITORS); + set.add(MekanismDataComponents.ATTACHED_HEAT); //TODO: Do we want to clear frequencies? //set.add(MekanismDataComponents.FREQUENCY_AWARE); //set.add(MekanismDataComponents.FREQUENCY_COMPONENT); diff --git a/src/main/java/mekanism/common/recipe/bin/BinExtractRecipe.java b/src/main/java/mekanism/common/recipe/bin/BinExtractRecipe.java index 2e7b0c0218b..e1babb1defa 100644 --- a/src/main/java/mekanism/common/recipe/bin/BinExtractRecipe.java +++ b/src/main/java/mekanism/common/recipe/bin/BinExtractRecipe.java @@ -2,7 +2,7 @@ import mekanism.api.Action; import mekanism.api.annotations.NothingNullByDefault; -import mekanism.common.inventory.slot.BinInventorySlot; +import mekanism.common.attachments.containers.item.ComponentBackedBinInventorySlot; import mekanism.common.item.block.ItemBlockBin; import mekanism.common.registries.MekanismRecipeSerializersInternal; import mekanism.common.util.MekanismUtils; @@ -72,7 +72,7 @@ public NonNullList getRemainingItems(CraftingContainer inv) { ItemStack stackInSlot = inv.getItem(i); if (stackInSlot.getItem() instanceof ItemBlockBin) { ItemStack binStack = stackInSlot.copy(); - BinInventorySlot slot = convertToSlot(binStack); + ComponentBackedBinInventorySlot slot = convertToSlot(binStack); ItemStack bottomStack = slot.getBottomStack(); if (!bottomStack.isEmpty()) { //Only attempt to do anything if there are items to try and remove diff --git a/src/main/java/mekanism/common/recipe/bin/BinInsertRecipe.java b/src/main/java/mekanism/common/recipe/bin/BinInsertRecipe.java index 6c83e89a052..eb424bea55e 100644 --- a/src/main/java/mekanism/common/recipe/bin/BinInsertRecipe.java +++ b/src/main/java/mekanism/common/recipe/bin/BinInsertRecipe.java @@ -7,13 +7,12 @@ import mekanism.api.Action; import mekanism.api.AutomationType; import mekanism.api.annotations.NothingNullByDefault; -import mekanism.common.inventory.slot.BinInventorySlot; +import mekanism.common.attachments.containers.item.ComponentBackedBinInventorySlot; import mekanism.common.item.block.ItemBlockBin; import mekanism.common.registries.MekanismDataComponents; import mekanism.common.registries.MekanismRecipeSerializersInternal; import net.minecraft.core.HolderLookup; import net.minecraft.core.NonNullList; -import net.minecraft.core.RegistryAccess; import net.minecraft.world.Container; import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.item.ItemStack; @@ -21,7 +20,6 @@ import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.level.Level; import net.neoforged.neoforge.event.entity.player.PlayerEvent.ItemCraftedEvent; -import net.neoforged.neoforge.items.ItemHandlerHelper; //TODO: Test this recipe in various modded crafting tables/auto crafters @NothingNullByDefault @@ -58,7 +56,7 @@ public boolean matches(CraftingContainer inv, Level world) { //If we didn't find a bin or an item to add it, we don't match the bin insertion recipe return false; } - BinInventorySlot slot = convertToSlot(binStack); + ComponentBackedBinInventorySlot slot = convertToSlot(binStack); ItemStack remaining = slot.insertItem(foundType, Action.SIMULATE, AutomationType.MANUAL); //Return that it doesn't match if our simulation claims we would not be able to accept any items into the bin return !ItemStack.matches(remaining, foundType); @@ -96,7 +94,7 @@ public ItemStack assemble(CraftingContainer inv, HolderLookup.Provider provider) } //Copy the stack binStack = binStack.copy(); - BinInventorySlot slot = convertToSlot(binStack); + ComponentBackedBinInventorySlot slot = convertToSlot(binStack); boolean hasInserted = false; for (ItemStack stack : foundItems) { //Try inserting a single item (as crafting grids only go one item at a time) @@ -154,7 +152,7 @@ public NonNullList getRemainingItems(CraftingContainer inv) { } //Copy the stack binStack = binStack.copy(); - BinInventorySlot slot = convertToSlot(binStack); + ComponentBackedBinInventorySlot slot = convertToSlot(binStack); for (Int2ObjectMap.Entry entry : foundSlots.int2ObjectEntrySet()) { ItemStack slotItem = entry.getValue(); //Only try inserting a single item into the bin. We execute on a copy of the bin stack so that we can mutate it and chain insertions @@ -187,7 +185,7 @@ public static void onCrafting(ItemCraftedEvent event) { Boolean fromRecipe = result.remove(MekanismDataComponents.FROM_RECIPE); if (fromRecipe != null && fromRecipe) { //And if it was try to move extra items from the container into it - BinInventorySlot slot = convertToSlot(result); + ComponentBackedBinInventorySlot slot = convertToSlot(result); ItemStack storedStack = slot.getStack(); if (!storedStack.isEmpty()) { Container craftingMatrix = event.getInventory(); diff --git a/src/main/java/mekanism/common/recipe/bin/BinRecipe.java b/src/main/java/mekanism/common/recipe/bin/BinRecipe.java index 943990b9326..41edf5dbd16 100644 --- a/src/main/java/mekanism/common/recipe/bin/BinRecipe.java +++ b/src/main/java/mekanism/common/recipe/bin/BinRecipe.java @@ -1,6 +1,7 @@ package mekanism.common.recipe.bin; import mekanism.api.annotations.NothingNullByDefault; +import mekanism.common.attachments.containers.item.ComponentBackedBinInventorySlot; import mekanism.common.inventory.slot.BinInventorySlot; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.CraftingBookCategory; @@ -15,8 +16,8 @@ protected BinRecipe(CraftingBookCategory category) { super(category); } - protected static BinInventorySlot convertToSlot(ItemStack binStack) { - BinInventorySlot slot = BinInventorySlot.getForStack(binStack); + protected static ComponentBackedBinInventorySlot convertToSlot(ItemStack binStack) { + ComponentBackedBinInventorySlot slot = BinInventorySlot.getForStack(binStack); if (slot == null) { throw new IllegalStateException("Expected bin stack to have an inventory"); } diff --git a/src/main/java/mekanism/common/recipe/ingredient/chemical/SingleChemicalStackIngredient.java b/src/main/java/mekanism/common/recipe/ingredient/chemical/SingleChemicalStackIngredient.java index a2e149f3152..df9c0a43c72 100644 --- a/src/main/java/mekanism/common/recipe/ingredient/chemical/SingleChemicalStackIngredient.java +++ b/src/main/java/mekanism/common/recipe/ingredient/chemical/SingleChemicalStackIngredient.java @@ -60,7 +60,7 @@ public boolean testType(CHEMICAL chemical) { public STACK getMatchingInstance(STACK chemicalStack) { if (test(chemicalStack)) { //Note: We manually "implement" the copy to ensure it returns the proper type as ChemicalStack#copy returns ChemicalStack instead of STACK - return (STACK) chemicalInstance.copy(); + return ChemicalUtil.copy(chemicalInstance); } return getEmptyStack(); } diff --git a/src/main/java/mekanism/common/recipe/ingredient/creator/ItemStackIngredientCreator.java b/src/main/java/mekanism/common/recipe/ingredient/creator/ItemStackIngredientCreator.java index 13fd79630c7..4006358cbd2 100644 --- a/src/main/java/mekanism/common/recipe/ingredient/creator/ItemStackIngredientCreator.java +++ b/src/main/java/mekanism/common/recipe/ingredient/creator/ItemStackIngredientCreator.java @@ -130,6 +130,7 @@ public static class SingleItemStackIngredient extends ItemStackIngredient { SingleItemStackIngredient::new )); + //TODO - 1.20.5: Evaluate if we want to use Neo's SizedIngredient private final Ingredient ingredient; private final int amount; diff --git a/src/main/java/mekanism/common/recipe/upgrade/EnergyRecipeData.java b/src/main/java/mekanism/common/recipe/upgrade/EnergyRecipeData.java index 5809a8560fe..77472606206 100644 --- a/src/main/java/mekanism/common/recipe/upgrade/EnergyRecipeData.java +++ b/src/main/java/mekanism/common/recipe/upgrade/EnergyRecipeData.java @@ -36,7 +36,7 @@ public boolean applyToStack(HolderLookup.Provider provider, ItemStack stack) { if (energyContainers.isEmpty()) { return true; } - IMekanismStrictEnergyHandler outputHandler = ContainerType.ENERGY.getAttachment(stack); + IMekanismStrictEnergyHandler outputHandler = ContainerType.ENERGY.createHandler(stack); if (outputHandler == null) { //Something went wrong, fail return false; diff --git a/src/main/java/mekanism/common/recipe/upgrade/FluidRecipeData.java b/src/main/java/mekanism/common/recipe/upgrade/FluidRecipeData.java index c24d2405199..661fa937130 100644 --- a/src/main/java/mekanism/common/recipe/upgrade/FluidRecipeData.java +++ b/src/main/java/mekanism/common/recipe/upgrade/FluidRecipeData.java @@ -38,7 +38,7 @@ public boolean applyToStack(HolderLookup.Provider provider, ItemStack stack) { } //TODO: Improve the logic used so that it tries to batch similar types of fluids together first // and maybe make it try multiple slot combinations?? - IMekanismFluidHandler outputHandler = ContainerType.FLUID.getAttachment(stack); + IMekanismFluidHandler outputHandler = ContainerType.FLUID.createHandler(stack); if (outputHandler == null) { //Something went wrong, fail return false; diff --git a/src/main/java/mekanism/common/recipe/upgrade/ItemRecipeData.java b/src/main/java/mekanism/common/recipe/upgrade/ItemRecipeData.java index 57e0daaf9f2..cd6bb701381 100644 --- a/src/main/java/mekanism/common/recipe/upgrade/ItemRecipeData.java +++ b/src/main/java/mekanism/common/recipe/upgrade/ItemRecipeData.java @@ -61,7 +61,7 @@ public void onContentsChanged() { } return false; } - IMekanismInventory outputHandler = ContainerType.ITEM.getAttachment(stack); + IMekanismInventory outputHandler = ContainerType.ITEM.createHandler(stack); //Something went wrong, fail return outputHandler != null && applyToStack(outputHandler, slots); } diff --git a/src/main/java/mekanism/common/recipe/upgrade/LockRecipeData.java b/src/main/java/mekanism/common/recipe/upgrade/LockRecipeData.java index b64224cbbca..52b85411ad9 100644 --- a/src/main/java/mekanism/common/recipe/upgrade/LockRecipeData.java +++ b/src/main/java/mekanism/common/recipe/upgrade/LockRecipeData.java @@ -1,10 +1,10 @@ package mekanism.common.recipe.upgrade; import mekanism.api.annotations.NothingNullByDefault; +import mekanism.common.attachments.containers.item.ComponentBackedBinInventorySlot; import mekanism.common.inventory.slot.BinInventorySlot; import net.minecraft.core.HolderLookup; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.items.ItemHandlerHelper; import org.jetbrains.annotations.Nullable; @NothingNullByDefault @@ -12,7 +12,7 @@ public class LockRecipeData implements RecipeUpgradeData { private final ItemStack lock; - LockRecipeData(BinInventorySlot slot) { + LockRecipeData(ComponentBackedBinInventorySlot slot) { this.lock = slot.getLockStack(); } @@ -24,7 +24,7 @@ public LockRecipeData merge(LockRecipeData other) { @Override public boolean applyToStack(HolderLookup.Provider provider, ItemStack stack) { - BinInventorySlot slot = BinInventorySlot.getForStack(stack); + ComponentBackedBinInventorySlot slot = BinInventorySlot.getForStack(stack); if (slot == null) { return false; } diff --git a/src/main/java/mekanism/common/recipe/upgrade/RecipeUpgradeData.java b/src/main/java/mekanism/common/recipe/upgrade/RecipeUpgradeData.java index 5db17d5ba70..fb02178a554 100644 --- a/src/main/java/mekanism/common/recipe/upgrade/RecipeUpgradeData.java +++ b/src/main/java/mekanism/common/recipe/upgrade/RecipeUpgradeData.java @@ -13,6 +13,7 @@ import mekanism.api.security.IItemSecurityUtils; import mekanism.api.security.ISecurityObject; import mekanism.api.security.SecurityMode; +import mekanism.common.attachments.containers.item.ComponentBackedBinInventorySlot; import mekanism.common.attachments.qio.DriveContents; import mekanism.common.attachments.component.UpgradeAware; import mekanism.common.attachments.containers.ContainerType; @@ -130,7 +131,7 @@ static RecipeUpgradeData getUpgradeData(@NotNull RecipeUpgradeType type, @Not yield slots.isEmpty() ? null : new ItemRecipeData(slots); } case LOCK -> { - BinInventorySlot slot = BinInventorySlot.getForStack(stack); + ComponentBackedBinInventorySlot slot = BinInventorySlot.getForStack(stack); //If there is no inventory, or it isn't locked just skip yield slot == null || !slot.isLocked() ? null : new LockRecipeData(slot); } diff --git a/src/main/java/mekanism/common/recipe/upgrade/chemical/ChemicalRecipeData.java b/src/main/java/mekanism/common/recipe/upgrade/chemical/ChemicalRecipeData.java index 3cd36b786c9..6ef186201d8 100644 --- a/src/main/java/mekanism/common/recipe/upgrade/chemical/ChemicalRecipeData.java +++ b/src/main/java/mekanism/common/recipe/upgrade/chemical/ChemicalRecipeData.java @@ -36,7 +36,7 @@ public ChemicalRecipeData merge(ChemicalRecipeData create(List tanks); - protected abstract ContainerType, ?> getContainerType(); + protected abstract ContainerType> getContainerType(); @Override public boolean applyToStack(HolderLookup.Provider provider, ItemStack stack) { @@ -45,7 +45,7 @@ public boolean applyToStack(HolderLookup.Provider provider, ItemStack stack) { } //TODO: Improve the logic used so that it tries to batch similar types of chemicals together first // and maybe make it try multiple slot combinations - IMekanismChemicalHandler outputHandler = getContainerType().getAttachment(stack); + IMekanismChemicalHandler outputHandler = getContainerType().createHandler(stack); if (outputHandler == null) { //Something went wrong, fail return false; diff --git a/src/main/java/mekanism/common/recipe/upgrade/chemical/GasRecipeData.java b/src/main/java/mekanism/common/recipe/upgrade/chemical/GasRecipeData.java index 0d6fb749e70..8ac1519bbbf 100644 --- a/src/main/java/mekanism/common/recipe/upgrade/chemical/GasRecipeData.java +++ b/src/main/java/mekanism/common/recipe/upgrade/chemical/GasRecipeData.java @@ -4,10 +4,10 @@ import mekanism.api.annotations.NothingNullByDefault; import mekanism.api.chemical.gas.Gas; import mekanism.api.chemical.gas.GasStack; -import mekanism.api.chemical.gas.IGasHandler; +import mekanism.api.chemical.gas.IGasHandler.IMekanismGasHandler; import mekanism.api.chemical.gas.IGasTank; -import mekanism.common.attachments.containers.AttachedChemicalTanks.AttachedGasTanks; import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.chemical.gas.AttachedGases; @NothingNullByDefault public class GasRecipeData extends ChemicalRecipeData { @@ -22,7 +22,7 @@ protected GasRecipeData create(List tanks) { } @Override - protected ContainerType getContainerType() { + protected ContainerType getContainerType() { return ContainerType.GAS; } } \ No newline at end of file diff --git a/src/main/java/mekanism/common/recipe/upgrade/chemical/InfusionRecipeData.java b/src/main/java/mekanism/common/recipe/upgrade/chemical/InfusionRecipeData.java index 84369da8d9d..18e765bd2b5 100644 --- a/src/main/java/mekanism/common/recipe/upgrade/chemical/InfusionRecipeData.java +++ b/src/main/java/mekanism/common/recipe/upgrade/chemical/InfusionRecipeData.java @@ -2,12 +2,12 @@ import java.util.List; import mekanism.api.annotations.NothingNullByDefault; -import mekanism.api.chemical.infuse.IInfusionHandler; +import mekanism.api.chemical.infuse.IInfusionHandler.IMekanismInfusionHandler; import mekanism.api.chemical.infuse.IInfusionTank; import mekanism.api.chemical.infuse.InfuseType; import mekanism.api.chemical.infuse.InfusionStack; -import mekanism.common.attachments.containers.AttachedChemicalTanks.AttachedInfusionTanks; import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.chemical.infuse.AttachedInfuseTypes; @NothingNullByDefault public class InfusionRecipeData extends ChemicalRecipeData { @@ -22,7 +22,7 @@ protected InfusionRecipeData create(List tanks) { } @Override - protected ContainerType getContainerType() { + protected ContainerType getContainerType() { return ContainerType.INFUSION; } } \ No newline at end of file diff --git a/src/main/java/mekanism/common/recipe/upgrade/chemical/PigmentRecipeData.java b/src/main/java/mekanism/common/recipe/upgrade/chemical/PigmentRecipeData.java index f9094a8726e..acfbc423f77 100644 --- a/src/main/java/mekanism/common/recipe/upgrade/chemical/PigmentRecipeData.java +++ b/src/main/java/mekanism/common/recipe/upgrade/chemical/PigmentRecipeData.java @@ -2,12 +2,12 @@ import java.util.List; import mekanism.api.annotations.NothingNullByDefault; -import mekanism.api.chemical.pigment.IPigmentHandler; +import mekanism.api.chemical.pigment.IPigmentHandler.IMekanismPigmentHandler; import mekanism.api.chemical.pigment.IPigmentTank; import mekanism.api.chemical.pigment.Pigment; import mekanism.api.chemical.pigment.PigmentStack; -import mekanism.common.attachments.containers.AttachedChemicalTanks.AttachedPigmentTanks; import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.chemical.pigment.AttachedPigments; @NothingNullByDefault public class PigmentRecipeData extends ChemicalRecipeData { @@ -22,7 +22,7 @@ protected PigmentRecipeData create(List tanks) { } @Override - protected ContainerType getContainerType() { + protected ContainerType getContainerType() { return ContainerType.PIGMENT; } } \ No newline at end of file diff --git a/src/main/java/mekanism/common/recipe/upgrade/chemical/SlurryRecipeData.java b/src/main/java/mekanism/common/recipe/upgrade/chemical/SlurryRecipeData.java index 492e4e32418..f857df0c1a2 100644 --- a/src/main/java/mekanism/common/recipe/upgrade/chemical/SlurryRecipeData.java +++ b/src/main/java/mekanism/common/recipe/upgrade/chemical/SlurryRecipeData.java @@ -2,12 +2,12 @@ import java.util.List; import mekanism.api.annotations.NothingNullByDefault; -import mekanism.api.chemical.slurry.ISlurryHandler; +import mekanism.api.chemical.slurry.ISlurryHandler.IMekanismSlurryHandler; import mekanism.api.chemical.slurry.ISlurryTank; import mekanism.api.chemical.slurry.Slurry; import mekanism.api.chemical.slurry.SlurryStack; -import mekanism.common.attachments.containers.AttachedChemicalTanks.AttachedSlurryTanks; import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.chemical.slurry.AttachedSlurries; @NothingNullByDefault public class SlurryRecipeData extends ChemicalRecipeData { @@ -22,7 +22,7 @@ protected SlurryRecipeData create(List tanks) { } @Override - protected ContainerType getContainerType() { + protected ContainerType getContainerType() { return ContainerType.SLURRY; } } \ No newline at end of file diff --git a/src/main/java/mekanism/common/registration/impl/DataComponentDeferredRegister.java b/src/main/java/mekanism/common/registration/impl/DataComponentDeferredRegister.java index 17ba74f5c13..2be6ecefbeb 100644 --- a/src/main/java/mekanism/common/registration/impl/DataComponentDeferredRegister.java +++ b/src/main/java/mekanism/common/registration/impl/DataComponentDeferredRegister.java @@ -10,8 +10,6 @@ import mekanism.api.annotations.NothingNullByDefault; import mekanism.api.math.FloatingLong; import mekanism.common.attachments.FrequencyAware; -import mekanism.common.attachments.containers.AttachedContainers; -import mekanism.common.attachments.containers.ContainerType; import mekanism.common.lib.frequency.Frequency; import mekanism.common.lib.frequency.FrequencyType; import mekanism.common.registration.MekanismDeferredHolder; @@ -20,14 +18,11 @@ import net.minecraft.core.UUIDUtil; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.resources.ResourceKey; import net.minecraft.util.ExtraCodecs; -import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.common.util.INBTSerializable; //TODO - 1.20.5: Should we be setting cacheEncoding on any of our builders? //TODO - 1.20.5: Figure out how to handle the default values @@ -42,18 +37,6 @@ public MekanismDeferredHolder, DataComponentType operator.apply(DataComponentType.builder()).build()); } - public , ATTACHMENT extends AttachedContainers> - MekanismDeferredHolder, DataComponentType> registerContainer(String name, Supplier> typeSupplier) { - return simple(name, builder -> { - ContainerType containerType = typeSupplier.get(); - //TODO - 1.20.5: Figure out how we want to setup containers - /*return AttachmentType.serializable(containerType::getDefault) - .copyHandler(containerType) - .build();*/ - return builder.persistent(Codec.unit(() -> containerType.getDefault(ItemStack.EMPTY))); - }); - } - public MekanismDeferredHolder, DataComponentType>> registerFrequencyAware(String name, Supplier> frequencyTypeSupplier) { return simple(name, builder -> { diff --git a/src/main/java/mekanism/common/registration/impl/ItemRegistryObject.java b/src/main/java/mekanism/common/registration/impl/ItemRegistryObject.java index f3d1995b6a8..e592e44bd65 100644 --- a/src/main/java/mekanism/common/registration/impl/ItemRegistryObject.java +++ b/src/main/java/mekanism/common/registration/impl/ItemRegistryObject.java @@ -5,21 +5,17 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; -import java.util.function.Function; import java.util.function.Supplier; -import mekanism.api.chemical.merged.MergedChemicalTank; import mekanism.api.providers.IItemProvider; import mekanism.common.attachments.IAttachmentAware; import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.creator.IContainerCreator; import mekanism.common.capabilities.ICapabilityAware; -import mekanism.common.capabilities.merged.MergedTank; import mekanism.common.config.IMekanismConfig; import mekanism.common.registration.MekanismDeferredHolder; -import net.minecraft.core.component.DataComponentType; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceKey; import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; import net.neoforged.bus.api.IEventBus; import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; import net.neoforged.neoforge.common.util.INBTSerializable; @@ -30,7 +26,7 @@ public class ItemRegistryObject extends MekanismDeferredHolder implements IItemProvider { @Nullable - private Map, Function>> defaultContainers; + private Map, Supplier>> defaultCreators; @Nullable private List> containerCapabilities; @@ -44,36 +40,25 @@ public ITEM asItem() { return value(); } - @Internal - public > ItemRegistryObject addAttachmentOnlyContainer(ContainerType containerType, - Function defaultCreator) { - return addAttachmentOnlyContainers(containerType, defaultCreator.andThen(List::of)); - } - @Internal public > ItemRegistryObject addAttachmentOnlyContainers(ContainerType containerType, - Function> defaultCreators) { - if (defaultContainers == null) { + Supplier> defaultCreator) { + //TODO - 1.20.5: Should we use Lazy instead of Supplier? As in theory the container creators are fine as is once made, they don't have to be reconstructed + if (defaultCreators == null) { //In case any containers have deps on others make this linked even though it really shouldn't matter // as nothing should be trying to construct the containers between register calls - defaultContainers = new LinkedHashMap<>(); + defaultCreators = new LinkedHashMap<>(); } - if (defaultContainers.put(containerType, defaultCreators) != null) { - throw new IllegalStateException("Duplicate attachments added for container type: " + containerType.getAttachmentName()); + if (defaultCreators.put(containerType, defaultCreator) != null) { + throw new IllegalStateException("Duplicate attachments added for container type: " + containerType.getComponentName()); } return this; } - @Internal - public > ItemRegistryObject addAttachedContainerCapability(ContainerType containerType, - Function defaultCreator, IMekanismConfig... requiredConfigs) { - return addAttachedContainerCapabilities(containerType, defaultCreator.andThen(List::of), requiredConfigs); - } - @Internal public > ItemRegistryObject addAttachedContainerCapabilities(ContainerType containerType, - Function> defaultCreators, IMekanismConfig... requiredConfigs) { - addAttachmentOnlyContainers(containerType, defaultCreators); + Supplier> defaultCreator, IMekanismConfig... requiredConfigs) { + addAttachmentOnlyContainers(containerType, defaultCreator); return addContainerCapability(containerType, requiredConfigs); } @@ -86,36 +71,6 @@ private ItemRegistryObject addContainerCapability(ContainerType c return this; } - @Internal - public ItemRegistryObject addMissingMergedTanks(Supplier> backingAttachment, boolean supportsFluid, - boolean exposeCapability) { - //TODO - 1.20.5: Fix all these stack.get(component) calls - int added = addMissingTankType(ContainerType.GAS, exposeCapability, stack -> stack.get(backingAttachment).getGasTank()); - added += addMissingTankType(ContainerType.INFUSION, exposeCapability, stack -> stack.get(backingAttachment).getInfusionTank()); - added += addMissingTankType(ContainerType.PIGMENT, exposeCapability, stack -> stack.get(backingAttachment).getPigmentTank()); - added += addMissingTankType(ContainerType.SLURRY, exposeCapability, stack -> stack.get(backingAttachment).getSlurryTank()); - if (supportsFluid) { - Supplier> attachment = (Supplier) backingAttachment; - added += addMissingTankType(ContainerType.FLUID, exposeCapability, stack -> stack.get(attachment).getFluidTank()); - } - if (added == 0) { - throw new IllegalStateException("Unnecessary addMissingMergedTanks call"); - } - return this; - } - - private > int addMissingTankType(ContainerType containerType, boolean exposeCapability, - Function defaultCreator) { - if (defaultContainers != null && defaultContainers.containsKey(containerType)) { - return 0; - } - addAttachmentOnlyContainer(containerType, defaultCreator); - if (exposeCapability) { - addContainerCapability(containerType); - } - return 1; - } - @Internal void registerCapabilities(RegisterCapabilitiesEvent event) { if (asItem() instanceof ICapabilityAware capabilityAware) { @@ -138,13 +93,13 @@ void attachDefaultContainers(IEventBus eventBus) { if (item instanceof IAttachmentAware attachmentAware) { attachmentAware.attachAttachments(eventBus); } - if (defaultContainers != null) { - for (Map.Entry, Function>> entry : defaultContainers.entrySet()) { + if (defaultCreators != null) { + for (Map.Entry, Supplier>> entry : defaultCreators.entrySet()) { //Note: We pass null for the event bus to not expose this attachment as a capability - entry.getKey().addDefaultContainers(null, item, (Function) entry.getValue()); + entry.getKey().addDefaultCreators(null, item, (Supplier) entry.getValue()); } //We only allow them being attached once - defaultContainers = null; + defaultCreators = null; } } } \ No newline at end of file diff --git a/src/main/java/mekanism/common/registries/MekanismBlocks.java b/src/main/java/mekanism/common/registries/MekanismBlocks.java index 9299fc42635..33e8dfb9329 100644 --- a/src/main/java/mekanism/common/registries/MekanismBlocks.java +++ b/src/main/java/mekanism/common/registries/MekanismBlocks.java @@ -3,25 +3,39 @@ import com.google.common.collect.HashBasedTable; import com.google.common.collect.Table; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; -import java.util.function.BiPredicate; -import java.util.function.BooleanSupplier; import java.util.function.Function; +import java.util.function.LongSupplier; import java.util.function.Predicate; import java.util.function.Supplier; -import mekanism.api.AutomationType; import mekanism.api.chemical.ChemicalTankBuilder; -import mekanism.api.chemical.gas.GasStack; -import mekanism.api.chemical.merged.MergedChemicalTank; +import mekanism.api.chemical.gas.Gas; +import mekanism.api.chemical.infuse.InfuseType; +import mekanism.api.chemical.pigment.Pigment; +import mekanism.api.chemical.slurry.Slurry; import mekanism.api.gear.IModuleHelper; -import mekanism.api.security.IItemSecurityUtils; import mekanism.api.tier.ITier; import mekanism.common.Mekanism; import mekanism.common.MekanismLang; -import mekanism.common.attachments.FilterAware; import mekanism.common.attachments.containers.ContainerType; -import mekanism.common.attachments.qio.PortableQIODashboardInventory; +import mekanism.common.attachments.containers.chemical.gas.ComponentBackedChemicalTankGasTank; +import mekanism.common.attachments.containers.chemical.gas.ComponentBackedGasTank; +import mekanism.common.attachments.containers.chemical.gas.GasTanksBuilder; +import mekanism.common.attachments.containers.chemical.infuse.ComponentBackedChemicalTankInfusionTank; +import mekanism.common.attachments.containers.chemical.infuse.ComponentBackedInfusionTank; +import mekanism.common.attachments.containers.chemical.infuse.InfusionTanksBuilder; +import mekanism.common.attachments.containers.chemical.merged.MergedTankCreator; +import mekanism.common.attachments.containers.chemical.pigment.ComponentBackedChemicalTankPigmentTank; +import mekanism.common.attachments.containers.chemical.pigment.ComponentBackedPigmentTank; +import mekanism.common.attachments.containers.chemical.pigment.PigmentTanksBuilder; +import mekanism.common.attachments.containers.chemical.slurry.ComponentBackedChemicalTankSlurryTank; +import mekanism.common.attachments.containers.chemical.slurry.ComponentBackedSlurryTank; +import mekanism.common.attachments.containers.chemical.slurry.SlurryTanksBuilder; +import mekanism.common.attachments.containers.fluid.ComponentBackedFluidTankFluidTank; +import mekanism.common.attachments.containers.fluid.FluidTanksBuilder; +import mekanism.common.attachments.containers.heat.HeatCapacitorsBuilder; +import mekanism.common.attachments.containers.item.ComponentBackedBinInventorySlot; +import mekanism.common.attachments.containers.item.ItemSlotsBuilder; import mekanism.common.block.BlockBounding; import mekanism.common.block.BlockCardboardBox; import mekanism.common.block.BlockEnergyCube; @@ -48,14 +62,7 @@ import mekanism.common.block.prefab.BlockTile.BlockTileModel; import mekanism.common.block.transmitter.BlockLargeTransmitter; import mekanism.common.block.transmitter.BlockSmallTransmitter; -import mekanism.common.capabilities.chemical.variable.RateLimitGasTank; -import mekanism.common.capabilities.chemical.variable.RateLimitInfusionTank; -import mekanism.common.capabilities.chemical.variable.RateLimitPigmentTank; -import mekanism.common.capabilities.chemical.variable.RateLimitSlurryTank; -import mekanism.common.capabilities.fluid.BasicFluidTank; -import mekanism.common.capabilities.fluid.item.FluidTankRateLimitFluidTank; -import mekanism.common.capabilities.fluid.item.RateLimitFluidTank; -import mekanism.common.capabilities.heat.BasicHeatCapacitor; +import mekanism.common.config.MekanismConfig; import mekanism.common.content.blocktype.BlockType; import mekanism.common.content.blocktype.BlockTypeTile; import mekanism.common.content.blocktype.Factory; @@ -63,21 +70,6 @@ import mekanism.common.content.blocktype.Machine; import mekanism.common.content.blocktype.Machine.FactoryMachine; import mekanism.common.content.gear.IModuleItem; -import mekanism.common.content.oredictionificator.OredictionificatorItemFilter; -import mekanism.common.inventory.slot.BasicInventorySlot; -import mekanism.common.inventory.slot.BinInventorySlot; -import mekanism.common.inventory.slot.EnergyInventorySlot; -import mekanism.common.inventory.slot.FluidInventorySlot; -import mekanism.common.inventory.slot.FormulaicCraftingSlot; -import mekanism.common.inventory.slot.InputInventorySlot; -import mekanism.common.inventory.slot.ItemSlotsBuilder; -import mekanism.common.inventory.slot.OutputInventorySlot; -import mekanism.common.inventory.slot.QIODriveSlot; -import mekanism.common.inventory.slot.SecurityInventorySlot; -import mekanism.common.inventory.slot.chemical.GasInventorySlot; -import mekanism.common.inventory.slot.chemical.MergedChemicalInventorySlot; -import mekanism.common.inventory.slot.chemical.PigmentInventorySlot; -import mekanism.common.inventory.slot.chemical.SlurryInventorySlot; import mekanism.common.item.block.ItemBlockBin; import mekanism.common.item.block.ItemBlockCardboardBox; import mekanism.common.item.block.ItemBlockChemicalTank; @@ -105,8 +97,13 @@ import mekanism.common.item.block.transmitter.ItemBlockUniversalCable; import mekanism.common.recipe.MekanismRecipeType; import mekanism.common.recipe.lookup.cache.InputRecipeCache.DoubleItem; +import mekanism.common.recipe.lookup.cache.InputRecipeCache.EitherSideChemical; +import mekanism.common.recipe.lookup.cache.InputRecipeCache.FluidChemical; import mekanism.common.recipe.lookup.cache.InputRecipeCache.ItemChemical; import mekanism.common.recipe.lookup.cache.InputRecipeCache.ItemFluidChemical; +import mekanism.common.recipe.lookup.cache.InputRecipeCache.SingleChemical; +import mekanism.common.recipe.lookup.cache.InputRecipeCache.SingleFluid; +import mekanism.common.recipe.lookup.cache.RotaryInputRecipeCache; import mekanism.common.recipe.lookup.cache.SingleInputRecipeCache; import mekanism.common.registration.impl.BlockDeferredRegister; import mekanism.common.registration.impl.BlockRegistryObject; @@ -115,9 +112,7 @@ import mekanism.common.resource.PrimaryResource; import mekanism.common.resource.ore.OreBlockType; import mekanism.common.resource.ore.OreType; -import mekanism.common.tier.BinTier; import mekanism.common.tier.FactoryTier; -import mekanism.common.tier.FluidTankTier; import mekanism.common.tile.TileEntityBin; import mekanism.common.tile.TileEntityChemicalTank; import mekanism.common.tile.TileEntityEnergyCube; @@ -279,9 +274,9 @@ private MekanismBlocks() { public static final BlockRegistryObject, ItemBlockTooltip>> BOILER_CASING = registerBlock("boiler_casing", () -> new BlockBasicMultiblock<>(MekanismBlockTypes.BOILER_CASING, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor()))); public static final BlockRegistryObject, ItemBlockTooltip>> BOILER_VALVE = registerBlock("boiler_valve", () -> new BlockBasicMultiblock<>(MekanismBlockTypes.BOILER_VALVE, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor()))); public static final BlockRegistryObject>, ItemBlockSecurityDesk> SECURITY_DESK = BLOCKS.register("security_desk", () -> new BlockTileModel<>(MekanismBlockTypes.SECURITY_DESK, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockSecurityDesk::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addSlot((listener, x, y) -> SecurityInventorySlot.unlock(() -> IItemSecurityUtils.INSTANCE.getOwnerUUID(stack), listener, x, y)) - .addSlot(SecurityInventorySlot::lock) + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addUnlockSlot() + .addLockSlot() .build() )); public static final BlockRegistryObject RADIOACTIVE_WASTE_BARREL = BLOCKS.registerDefaultProperties("radioactive_waste_barrel", BlockRadioactiveWasteBarrel::new, ItemBlockRadioactiveWasteBarrel::new); @@ -289,7 +284,7 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> ENRICHMENT_CHAMBER = BLOCKS.register("enrichment_chamber", () -> new BlockFactoryMachine<>(MekanismBlockTypes.ENRICHMENT_CHAMBER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addInput(MekanismRecipeType.ENRICHING, SingleInputRecipeCache::containsInput) .addOutput() .addEnergy() @@ -298,12 +293,12 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> OSMIUM_COMPRESSOR = BLOCKS.register("osmium_compressor", () -> new BlockFactoryMachine<>(MekanismBlockTypes.OSMIUM_COMPRESSOR, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.GAS, stack -> RateLimitGasTank.createBasicItem(TileEntityAdvancedElectricMachine.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, - gas -> MekanismRecipeType.COMPRESSING.getInputCache().containsInputB(null, gas.getStack(1)) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + .addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addBasic(TileEntityAdvancedElectricMachine.MAX_GAS, MekanismRecipeType.COMPRESSING, ItemChemical::containsInputB) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addInput(MekanismRecipeType.COMPRESSING, ItemChemical::containsInputA) - .addGasSlotWithConversion(0) + .addGasFillOrConvertSlot(0) .addOutput() .addEnergy() .build() @@ -311,7 +306,7 @@ private MekanismBlocks() { ); public static final BlockRegistryObject>, ItemBlockTooltip>>> COMBINER = BLOCKS.register("combiner", () -> new BlockFactoryMachine<>(MekanismBlockTypes.COMBINER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addInput(MekanismRecipeType.COMBINING, DoubleItem::containsInputA) .addInput(MekanismRecipeType.COMBINING, DoubleItem::containsInputB) .addOutput() @@ -320,7 +315,7 @@ private MekanismBlocks() { )); public static final BlockRegistryObject>, ItemBlockTooltip>>> CRUSHER = BLOCKS.register("crusher", () -> new BlockFactoryMachine<>(MekanismBlockTypes.CRUSHER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addInput(MekanismRecipeType.CRUSHING, SingleInputRecipeCache::containsInput) .addOutput() .addEnergy() @@ -328,25 +323,20 @@ private MekanismBlocks() { )); public static final BlockRegistryObject>, ItemBlockTooltip>>> DIGITAL_MINER = BLOCKS.register("digital_miner", () -> new BlockTileModel<>(MekanismBlockTypes.DIGITAL_MINER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> { - BiPredicate canInsert = (s, automationType) -> automationType != AutomationType.EXTERNAL || TileEntityDigitalMiner.isSavedReplaceTarget(stack, s.getItem()); - //Allow extraction if it is manual or for internal usage, or if it is not a replace stack - //Note: We don't currently use internal for extraction anywhere here as we just shrink replace stacks directly - BiPredicate canExtract = (s, automationType) -> automationType != AutomationType.EXTERNAL || !TileEntityDigitalMiner.isSavedReplaceTarget(stack, s.getItem()); - return ItemSlotsBuilder.builder(stack) - .addSlots(3 * 9, (listener, x, y) -> BasicInventorySlot.at(canExtract, canInsert, listener, x, y)) - .addEnergy() - .build(); - })); + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addMinerSlots(3 * 9) + .addEnergy() + .build()) + ); public static final BlockRegistryObject>, ItemBlockTooltip>>> METALLURGIC_INFUSER = BLOCKS.register("metallurgic_infuser", () -> new BlockFactoryMachineModel<>(MekanismBlockTypes.METALLURGIC_INFUSER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.INFUSION, stack -> RateLimitInfusionTank.createBasicItem(TileEntityMetallurgicInfuser.MAX_INFUSE, - ChemicalTankBuilder.INFUSION.manualOnly, ChemicalTankBuilder.INFUSION.alwaysTrueBi, - infuseType -> MekanismRecipeType.METALLURGIC_INFUSING.getInputCache().containsInputB(null, infuseType.getStack(1)) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addInfusionSlotWithConversion(0) + .addAttachmentOnlyContainers(ContainerType.INFUSION, () -> InfusionTanksBuilder.builder() + .addBasic(TileEntityMetallurgicInfuser.MAX_INFUSE, MekanismRecipeType.METALLURGIC_INFUSING, ItemChemical::containsInputB) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addInfusionFillOrConvertSlot(0) .addInput(MekanismRecipeType.METALLURGIC_INFUSING, ItemChemical::containsInputA) .addOutput() .addEnergy() @@ -356,12 +346,12 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> PURIFICATION_CHAMBER = BLOCKS.register("purification_chamber", () -> new BlockFactoryMachine<>(MekanismBlockTypes.PURIFICATION_CHAMBER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.GAS, stack -> RateLimitGasTank.createBasicItem(TileEntityAdvancedElectricMachine.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, - gas -> MekanismRecipeType.PURIFYING.getInputCache().containsInputB(null, gas.getStack(1)) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + .addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addBasic(TileEntityAdvancedElectricMachine.MAX_GAS, MekanismRecipeType.PURIFYING, ItemChemical::containsInputB) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addInput(MekanismRecipeType.PURIFYING, ItemChemical::containsInputA) - .addGasSlotWithConversion(0) + .addGasFillOrConvertSlot(0) .addOutput() .addEnergy() .build() @@ -369,21 +359,22 @@ private MekanismBlocks() { ); public static final BlockRegistryObject>, ItemBlockTooltip>>> ENERGIZED_SMELTER = BLOCKS.register("energized_smelter", () -> new BlockFactoryMachine<>(MekanismBlockTypes.ENERGIZED_SMELTER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addInput(MekanismRecipeType.SMELTING, SingleInputRecipeCache::containsInput) .addOutput() .addEnergy() .build() )); public static final BlockRegistryObject>, ItemBlockTeleporter> TELEPORTER = BLOCKS.register("teleporter", () -> new BlockTile<>(MekanismBlockTypes.TELEPORTER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTeleporter::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack).addEnergy().build())); + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder().addEnergy().build())); public static final BlockRegistryObject>, ItemBlockTooltip>>> ELECTRIC_PUMP = BLOCKS.register("electric_pump", () -> new BlockTileModel<>(MekanismBlockTypes.ELECTRIC_PUMP, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.FLUID, stack -> RateLimitFluidTank.createBasicItem(TileEntityElectricPump.MAX_FLUID, - BasicFluidTank.manualOnly, BasicFluidTank.alwaysTrueBi, BasicFluidTank.alwaysTrue - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addFluidSlot(0, FluidInventorySlot::drain) + .addAttachmentOnlyContainers(ContainerType.FLUID, () -> FluidTanksBuilder.builder() + .addBasic(TileEntityElectricPump.MAX_FLUID) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addFluidDrainSlot(0) .addOutput() .addEnergy() .build() @@ -396,31 +387,30 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> ROTARY_CONDENSENTRATOR = BLOCKS.register("rotary_condensentrator", () -> new BlockTileModel<>(MekanismBlockTypes.ROTARY_CONDENSENTRATOR, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.FLUID, stack -> RateLimitFluidTank.createBasicItem(TileEntityRotaryCondensentrator.CAPACITY, - BasicFluidTank.manualOnly, BasicFluidTank.alwaysTrueBi, - fluid -> MekanismRecipeType.ROTARY.getInputCache().containsInput(null, fluid) - )).addAttachmentOnlyContainer(ContainerType.GAS, stack -> RateLimitGasTank.createBasicItem(TileEntityRotaryCondensentrator.CAPACITY, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, - gas -> MekanismRecipeType.ROTARY.getInputCache().containsInput(null, gas.getStack(1)) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> { - BooleanSupplier modeSupplier = () -> stack.getOrDefault(MekanismDataComponents.ROTARY_MODE, false); - return ItemSlotsBuilder.builder(stack) - .addGasSlot(0, (tank, listener, x, y) -> GasInventorySlot.rotaryDrain(tank, modeSupplier, listener, x, y)) - .addGasSlot(0, (tank, listener, x, y) -> GasInventorySlot.rotaryDrain(tank, modeSupplier, listener, x, y)) - .addFluidSlot(0, (tank, listener, x, y) -> FluidInventorySlot.rotary(tank, modeSupplier, listener, x, y)) - .addOutput() - .addEnergy() - .build(); - }) + .addAttachmentOnlyContainers(ContainerType.FLUID, () -> FluidTanksBuilder.builder() + .addBasic(TileEntityRotaryCondensentrator.CAPACITY, MekanismRecipeType.ROTARY, RotaryInputRecipeCache::containsInput) + .build() + ).addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addBasic(TileEntityRotaryCondensentrator.CAPACITY, MekanismRecipeType.ROTARY, RotaryInputRecipeCache::containsInput) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addGasRotaryDrainSlot(0) + .addGasRotaryFillSlot(0) + .addFluidRotarySlot(0) + .addOutput() + .addEnergy() + .build() + ) ); public static final BlockRegistryObject>, ItemBlockTooltip>>> CHEMICAL_OXIDIZER = BLOCKS.register("chemical_oxidizer", () -> new BlockTileModel<>(MekanismBlockTypes.CHEMICAL_OXIDIZER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.GAS, stack -> RateLimitGasTank.createBasicItem(TileEntityChemicalOxidizer.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrue - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + .addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addBasic(TileEntityChemicalOxidizer.MAX_GAS) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addInput(MekanismRecipeType.OXIDIZING, SingleInputRecipeCache::containsInput) - .addGasSlot(0, GasInventorySlot::drain) + .addGasDrainSlot(0) .addEnergy() .build() ) @@ -428,22 +418,15 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> CHEMICAL_INFUSER = BLOCKS.register("chemical_infuser", () -> new BlockTileModel<>(MekanismBlockTypes.CHEMICAL_INFUSER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainers(ContainerType.GAS, stack -> List.of( - RateLimitGasTank.createBasicItem(TileEntityChemicalInfuser.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, - gas -> MekanismRecipeType.CHEMICAL_INFUSING.getInputCache().containsInput(null, gas.getStack(1)) - ), - RateLimitGasTank.createBasicItem(TileEntityChemicalInfuser.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, - gas -> MekanismRecipeType.CHEMICAL_INFUSING.getInputCache().containsInput(null, gas.getStack(1)) - ), - RateLimitGasTank.createBasicItem(TileEntityChemicalInfuser.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrue - ) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addGasSlot(0, GasInventorySlot::fill) - .addGasSlot(1, GasInventorySlot::fill) - .addGasSlot(2, GasInventorySlot::drain) + .addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addBasic(TileEntityChemicalInfuser.MAX_GAS, MekanismRecipeType.CHEMICAL_INFUSING, EitherSideChemical::containsInput) + .addBasic(TileEntityChemicalInfuser.MAX_GAS, MekanismRecipeType.CHEMICAL_INFUSING, EitherSideChemical::containsInput) + .addBasic(TileEntityChemicalInfuser.MAX_GAS) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addGasFillSlot(0) + .addGasFillSlot(1) + .addGasDrainSlot(2) .addEnergy() .build() ) @@ -451,12 +434,12 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> CHEMICAL_INJECTION_CHAMBER = BLOCKS.register("chemical_injection_chamber", () -> new BlockFactoryMachine<>(MekanismBlockTypes.CHEMICAL_INJECTION_CHAMBER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.GAS, stack -> RateLimitGasTank.createBasicItem(TileEntityAdvancedElectricMachine.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, - gas -> MekanismRecipeType.INJECTING.getInputCache().containsInputB(null, gas.getStack(1)) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + .addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addBasic(TileEntityAdvancedElectricMachine.MAX_GAS, MekanismRecipeType.INJECTING, ItemChemical::containsInputB) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addInput(MekanismRecipeType.INJECTING, ItemChemical::containsInputA) - .addGasSlotWithConversion(0) + .addGasFillOrConvertSlot(0) .addOutput() .addEnergy() .build() @@ -465,27 +448,24 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> ELECTROLYTIC_SEPARATOR = BLOCKS.register("electrolytic_separator", () -> new BlockTileModel<>(MekanismBlockTypes.ELECTROLYTIC_SEPARATOR, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.FLUID, stack -> RateLimitFluidTank.createBasicItem(TileEntityElectrolyticSeparator.MAX_FLUID, - BasicFluidTank.manualOnly, BasicFluidTank.alwaysTrueBi, - fluid -> MekanismRecipeType.SEPARATING.getInputCache().containsInput(null, fluid) - )).addAttachmentOnlyContainers(ContainerType.GAS, stack -> List.of( - RateLimitGasTank.createBasicItem(TileEntityElectrolyticSeparator.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrue - ), - RateLimitGasTank.createBasicItem(TileEntityElectrolyticSeparator.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrue - ) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addFluidSlot(0, FluidInventorySlot::fill) - .addGasSlot(0, GasInventorySlot::drain) - .addGasSlot(1, GasInventorySlot::drain) + .addAttachmentOnlyContainers(ContainerType.FLUID, () -> FluidTanksBuilder.builder() + .addBasic(TileEntityElectrolyticSeparator.MAX_FLUID, MekanismRecipeType.SEPARATING, SingleFluid::containsInput) + .build() + ).addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addBasic(TileEntityElectrolyticSeparator.MAX_GAS) + .addBasic(TileEntityElectrolyticSeparator.MAX_GAS) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addFluidFillSlot(0) + .addGasDrainSlot(0) + .addGasDrainSlot(1) .addEnergy() .build() ) ); public static final BlockRegistryObject>, ItemBlockTooltip>>> PRECISION_SAWMILL = BLOCKS.register("precision_sawmill", () -> new BlockFactoryMachine<>(MekanismBlockTypes.PRECISION_SAWMILL, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addInput(MekanismRecipeType.SAWING, SingleInputRecipeCache::containsInput) .addOutput() .addOutput()//Secondary output @@ -494,76 +474,96 @@ private MekanismBlocks() { )); public static final BlockRegistryObject>, ItemBlockTooltip>>> CHEMICAL_DISSOLUTION_CHAMBER = BLOCKS.register("chemical_dissolution_chamber", () -> new BlockTileModel<>(MekanismBlockTypes.CHEMICAL_DISSOLUTION_CHAMBER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) - .forItemHolder(holder -> holder - .addAttachmentOnlyContainers(ContainerType.GAS, stack -> List.of( - RateLimitGasTank.createBasicItem(TileEntityChemicalDissolutionChamber.MAX_CHEMICAL, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, - gas -> MekanismRecipeType.DISSOLUTION.getInputCache().containsInputB(null, gas.getStack(1)) - ), - //TODO - 1.20.5: Fix this get component call - stack.get(MekanismDataComponents.CDC_CONTENTS_HANDLER).getGasTank() - )).addMissingMergedTanks(MekanismDataComponents.CDC_CONTENTS_HANDLER, false, false) - .addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addGasSlotWithConversion(0) - .addInput(MekanismRecipeType.DISSOLUTION, ItemChemical::containsInputA) - //TODO - 1.20.5: Fix this get component call - .addContainerSlot(stack.get(MekanismDataComponents.CDC_CONTENTS_HANDLER), MergedChemicalInventorySlot::drain) - .addEnergy() - .build() - ) + .forItemHolder(holder -> { + final LongSupplier capacitySupplier = () -> TileEntityChemicalDissolutionChamber.MAX_CHEMICAL; + final MergedTankCreator mergedTankCreator = new MergedTankCreator( + (type, attachedTo, containerIndex) -> new ComponentBackedGasTank(attachedTo, containerIndex, ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrueBi, + ChemicalTankBuilder.GAS.alwaysTrue, MekanismConfig.general.chemicalItemFillRate, capacitySupplier, null), + (type, attachedTo, containerIndex) -> new ComponentBackedInfusionTank(attachedTo, containerIndex, ChemicalTankBuilder.INFUSION.alwaysTrueBi, ChemicalTankBuilder.INFUSION.alwaysTrueBi, + ChemicalTankBuilder.INFUSION.alwaysTrue, MekanismConfig.general.chemicalItemFillRate, capacitySupplier, null), + (type, attachedTo, containerIndex) -> new ComponentBackedPigmentTank(attachedTo, containerIndex, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, + ChemicalTankBuilder.PIGMENT.alwaysTrue, MekanismConfig.general.chemicalItemFillRate, capacitySupplier, null), + (type, attachedTo, containerIndex) -> new ComponentBackedSlurryTank(attachedTo, containerIndex, ChemicalTankBuilder.SLURRY.alwaysTrueBi, ChemicalTankBuilder.SLURRY.alwaysTrueBi, + ChemicalTankBuilder.SLURRY.alwaysTrue, MekanismConfig.general.chemicalItemFillRate, capacitySupplier, null) + ); + holder.addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addBasic(TileEntityChemicalDissolutionChamber.MAX_CHEMICAL, MekanismRecipeType.DISSOLUTION, ItemChemical::containsInputB) + .addTank(mergedTankCreator) + .build() + ).addAttachmentOnlyContainers(ContainerType.INFUSION, () -> InfusionTanksBuilder.builder().addTank(mergedTankCreator).build()) + .addAttachmentOnlyContainers(ContainerType.PIGMENT, () -> PigmentTanksBuilder.builder().addTank(mergedTankCreator).build()) + .addAttachmentOnlyContainers(ContainerType.SLURRY, () -> SlurryTanksBuilder.builder().addTank(mergedTankCreator).build()) + .addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addGasFillOrConvertSlot(0) + .addInput(MekanismRecipeType.DISSOLUTION, ItemChemical::containsInputA) + .addMergedChemicalDrainSlot(1, 0, 0, 0) + .addEnergy() + .build() + ); + } ); public static final BlockRegistryObject>, ItemBlockTooltip>>> CHEMICAL_WASHER = BLOCKS.register("chemical_washer", () -> new BlockTileModel<>(MekanismBlockTypes.CHEMICAL_WASHER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.FLUID, stack -> RateLimitFluidTank.createBasicItem(TileEntityChemicalWasher.MAX_FLUID, - BasicFluidTank.manualOnly, BasicFluidTank.alwaysTrueBi, - fluid -> MekanismRecipeType.WASHING.getInputCache().containsInputA(null, fluid) - )).addAttachmentOnlyContainers(ContainerType.SLURRY, stack -> List.of( - RateLimitSlurryTank.createBasicItem(TileEntityChemicalWasher.MAX_SLURRY, - ChemicalTankBuilder.SLURRY.manualOnly, ChemicalTankBuilder.SLURRY.alwaysTrueBi, - slurry -> MekanismRecipeType.WASHING.getInputCache().containsInputB(null, slurry.getStack(1)) - ), - RateLimitSlurryTank.createBasicItem(TileEntityChemicalWasher.MAX_SLURRY, - ChemicalTankBuilder.SLURRY.manualOnly, ChemicalTankBuilder.SLURRY.alwaysTrueBi, ChemicalTankBuilder.SLURRY.alwaysTrue - ) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addFluidSlot(0, FluidInventorySlot::fill) + .addAttachmentOnlyContainers(ContainerType.FLUID, () -> FluidTanksBuilder.builder() + .addBasic(TileEntityChemicalWasher.MAX_FLUID, MekanismRecipeType.WASHING, FluidChemical::containsInputA) + .build() + ).addAttachmentOnlyContainers(ContainerType.SLURRY, () -> SlurryTanksBuilder.builder() + .addBasic(TileEntityChemicalWasher.MAX_SLURRY, MekanismRecipeType.WASHING, FluidChemical::containsInputB) + .addBasic(TileEntityChemicalWasher.MAX_SLURRY) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addFluidFillSlot(0) .addOutput() - .addSlurrySlot(1, SlurryInventorySlot::drain) + .addSlurryDrainSlot(1) .addEnergy() .build() ) ); public static final BlockRegistryObject>, ItemBlockTooltip>>> CHEMICAL_CRYSTALLIZER = BLOCKS.register("chemical_crystallizer", () -> new BlockTileModel<>(MekanismBlockTypes.CHEMICAL_CRYSTALLIZER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) - .forItemHolder(holder -> holder - .addMissingMergedTanks(MekanismDataComponents.CRYSTALLIZER_CONTENTS_HANDLER, false, false) - .addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - //TODO - 1.20.5: Fix this get component call - .addContainerSlot(stack.get(MekanismDataComponents.CRYSTALLIZER_CONTENTS_HANDLER), MergedChemicalInventorySlot::fill) - .addOutput() - .addEnergy() - .build() - ) + .forItemHolder(holder -> { + final LongSupplier capacitySupplier = () -> TileEntityChemicalCrystallizer.MAX_CHEMICAL; + final Predicate gasPredicate = gas -> MekanismRecipeType.CRYSTALLIZING.getInputCache().containsInput(null, gas); + final Predicate infusionPredicate = infuseType -> MekanismRecipeType.CRYSTALLIZING.getInputCache().containsInput(null, infuseType); + final Predicate pigmentPredicate = pigment -> MekanismRecipeType.CRYSTALLIZING.getInputCache().containsInput(null, pigment); + final Predicate slurryPredicate = slurry -> MekanismRecipeType.CRYSTALLIZING.getInputCache().containsInput(null, slurry); + final MergedTankCreator mergedTankCreator = new MergedTankCreator( + (type, attachedTo, containerIndex) -> new ComponentBackedGasTank(attachedTo, containerIndex, ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrueBi, + gasPredicate, MekanismConfig.general.chemicalItemFillRate, capacitySupplier, null), + (type, attachedTo, containerIndex) -> new ComponentBackedInfusionTank(attachedTo, containerIndex, ChemicalTankBuilder.INFUSION.alwaysTrueBi, ChemicalTankBuilder.INFUSION.alwaysTrueBi, + infusionPredicate, MekanismConfig.general.chemicalItemFillRate, capacitySupplier, null), + (type, attachedTo, containerIndex) -> new ComponentBackedPigmentTank(attachedTo, containerIndex, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, + pigmentPredicate, MekanismConfig.general.chemicalItemFillRate, capacitySupplier, null), + (type, attachedTo, containerIndex) -> new ComponentBackedSlurryTank(attachedTo, containerIndex, ChemicalTankBuilder.SLURRY.alwaysTrueBi, ChemicalTankBuilder.SLURRY.alwaysTrueBi, + slurryPredicate, MekanismConfig.general.chemicalItemFillRate, capacitySupplier, null) + ); + holder.addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder().addTank(mergedTankCreator).build()) + .addAttachmentOnlyContainers(ContainerType.INFUSION, () -> InfusionTanksBuilder.builder().addTank(mergedTankCreator).build()) + .addAttachmentOnlyContainers(ContainerType.PIGMENT, () -> PigmentTanksBuilder.builder().addTank(mergedTankCreator).build()) + .addAttachmentOnlyContainers(ContainerType.SLURRY, () -> SlurryTanksBuilder.builder().addTank(mergedTankCreator).build()) + .addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addMergedChemicalFillSlot(0, 0, 0, 0) + .addOutput() + .addEnergy() + .build() + ); + } ); public static final BlockRegistryObject>, ItemBlockTooltip>>> SEISMIC_VIBRATOR = BLOCKS.register("seismic_vibrator", () -> new BlockTileModel<>(MekanismBlockTypes.SEISMIC_VIBRATOR, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack).addEnergy().build())); + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder().addEnergy().build())); public static final BlockRegistryObject>, ItemBlockTooltip>>> PRESSURIZED_REACTION_CHAMBER = BLOCKS.register("pressurized_reaction_chamber", () -> new BlockTileModel<>(MekanismBlockTypes.PRESSURIZED_REACTION_CHAMBER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.FLUID, stack -> RateLimitFluidTank.createBasicItem(TileEntityPressurizedReactionChamber.MAX_FLUID, - BasicFluidTank.manualOnly, BasicFluidTank.alwaysTrueBi, - fluid -> MekanismRecipeType.REACTION.getInputCache().containsInputB(null, fluid) - )).addAttachmentOnlyContainers(ContainerType.GAS, stack -> List.of( - RateLimitGasTank.createBasicItem(TileEntityPressurizedReactionChamber.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, - gas -> MekanismRecipeType.REACTION.getInputCache().containsInputC(null, gas.getStack(1)) - ), - RateLimitGasTank.createBasicItem(TileEntityPressurizedReactionChamber.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrue - ) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + .addAttachmentOnlyContainers(ContainerType.FLUID, () -> FluidTanksBuilder.builder() + .addBasic(TileEntityPressurizedReactionChamber.MAX_FLUID, MekanismRecipeType.REACTION, ItemFluidChemical::containsInputB) + .build() + ).addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addBasic(TileEntityPressurizedReactionChamber.MAX_GAS, MekanismRecipeType.REACTION, ItemFluidChemical::containsInputC) + .addBasic(TileEntityPressurizedReactionChamber.MAX_GAS) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addInput(MekanismRecipeType.REACTION, ItemFluidChemical::containsInputA) .addOutput() .addEnergy() @@ -573,17 +573,13 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> ISOTOPIC_CENTRIFUGE = BLOCKS.register("isotopic_centrifuge", () -> new BlockTileModel<>(MekanismBlockTypes.ISOTOPIC_CENTRIFUGE, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainers(ContainerType.GAS, stack -> List.of( - RateLimitGasTank.createBasicItem(TileEntityIsotopicCentrifuge.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, - gas -> MekanismRecipeType.CENTRIFUGING.getInputCache().containsInput(null, gas.getStack(1)) - ), - RateLimitGasTank.createBasicItem(TileEntityIsotopicCentrifuge.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrue - ) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addGasSlot(0, GasInventorySlot::fill) - .addGasSlot(1, GasInventorySlot::drain) + .addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addBasic(TileEntityIsotopicCentrifuge.MAX_GAS, MekanismRecipeType.CENTRIFUGING, SingleChemical::containsInput) + .addBasic(TileEntityIsotopicCentrifuge.MAX_GAS) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addGasFillSlot(0) + .addGasDrainSlot(1) .addEnergy() .build() ) @@ -591,11 +587,12 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> NUTRITIONAL_LIQUIFIER = BLOCKS.register("nutritional_liquifier", () -> new BlockTile<>(MekanismBlockTypes.NUTRITIONAL_LIQUIFIER, properties -> properties.noOcclusion().mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.FLUID, stack -> RateLimitFluidTank.createBasicItem(TileEntityNutritionalLiquifier.MAX_FLUID, - BasicFluidTank.manualOnly, BasicFluidTank.alwaysTrueBi, BasicFluidTank.alwaysTrue - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + .addAttachmentOnlyContainers(ContainerType.FLUID, () -> FluidTanksBuilder.builder() + .addBasic(TileEntityNutritionalLiquifier.MAX_FLUID) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addInput(TileEntityNutritionalLiquifier::isValidInput) - .addFluidSlot(0, FluidInventorySlot::drain) + .addFluidDrainSlot(0) .addOutput() .addEnergy() .build() @@ -611,10 +608,11 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> FLUIDIC_PLENISHER = BLOCKS.register("fluidic_plenisher", () -> new BlockTileModel<>(MekanismBlockTypes.FLUIDIC_PLENISHER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.FLUID, stack -> RateLimitFluidTank.createBasicItem(TileEntityFluidicPlenisher.MAX_FLUID, - BasicFluidTank.manualOnly, BasicFluidTank.alwaysTrueBi, BasicFluidTank.alwaysTrue - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addFluidSlot(0, FluidInventorySlot::fill) + .addAttachmentOnlyContainers(ContainerType.FLUID, () -> FluidTanksBuilder.builder() + .addBasic(TileEntityFluidicPlenisher.MAX_FLUID) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addFluidFillSlot(0) .addOutput() .addEnergy() .build() @@ -623,61 +621,56 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> LASER = BLOCKS.register("laser", () -> new BlockTileModel<>(MekanismBlockTypes.LASER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new); public static final BlockRegistryObject>, ItemBlockLaserAmplifier> LASER_AMPLIFIER = BLOCKS.register("laser_amplifier", () -> new BlockTileModel<>(MekanismBlockTypes.LASER_AMPLIFIER, properties -> properties.mapColor(MapColor.COLOR_GRAY)), ItemBlockLaserAmplifier::new); public static final BlockRegistryObject>, ItemBlockLaserTractorBeam> LASER_TRACTOR_BEAM = BLOCKS.register("laser_tractor_beam", () -> new BlockTileModel<>(MekanismBlockTypes.LASER_TRACTOR_BEAM, properties -> properties.mapColor(MapColor.COLOR_GRAY)), ItemBlockLaserTractorBeam::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack).addSlots(3 * 9, OutputInventorySlot::at).build())); + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder().addOutput(3 * 9).build())); public static final BlockRegistryObject>, ItemBlockQuantumEntangloporter> QUANTUM_ENTANGLOPORTER = BLOCKS.register("quantum_entangloporter", () -> new BlockTileModel<>(MekanismBlockTypes.QUANTUM_ENTANGLOPORTER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockQuantumEntangloporter::new); public static final BlockRegistryObject>, ItemBlockTooltip>>> SOLAR_NEUTRON_ACTIVATOR = BLOCKS.register("solar_neutron_activator", () -> new BlockTileModel<>(MekanismBlockTypes.SOLAR_NEUTRON_ACTIVATOR, properties -> properties.mapColor(MapColor.COLOR_BLUE)), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainers(ContainerType.GAS, stack -> List.of( - RateLimitGasTank.createBasicItem(TileEntitySolarNeutronActivator.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, - gas -> MekanismRecipeType.ACTIVATING.getInputCache().containsInput(null, gas.getStack(1)) - ), - RateLimitGasTank.createBasicItem(TileEntitySolarNeutronActivator.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrue - ) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addGasSlot(0, GasInventorySlot::fill) - .addGasSlot(1, GasInventorySlot::drain) + .addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addBasic(TileEntitySolarNeutronActivator.MAX_GAS, MekanismRecipeType.ACTIVATING, SingleChemical::containsInput) + .addBasic(TileEntitySolarNeutronActivator.MAX_GAS) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addGasFillSlot(0) + .addGasDrainSlot(1) .build() ) ); public static final BlockRegistryObject>, ItemBlockTooltip>>> OREDICTIONIFICATOR = BLOCKS.register("oredictionificator", () -> new BlockTile<>(MekanismBlockTypes.OREDICTIONIFICATOR, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addInput(s -> TileEntityOredictionificator.hasResult(stack.getOrDefault(MekanismDataComponents.FILTER_AWARE, FilterAware.EMPTY).getEnabled(OredictionificatorItemFilter.class), s)) + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addOredictionificatorInput() .addOutput() .build() )); public static final BlockRegistryObject>, ItemBlockResistiveHeater> RESISTIVE_HEATER = BLOCKS.register("resistive_heater", () -> new BlockTileModel<>(MekanismBlockTypes.RESISTIVE_HEATER, properties -> properties.mapColor(MapColor.METAL)), ItemBlockResistiveHeater::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.HEAT, stack -> BasicHeatCapacitor.createBasicItem(TileEntityResistiveHeater.HEAT_CAPACITY, - TileEntityResistiveHeater.INVERSE_CONDUCTION_COEFFICIENT, TileEntityResistiveHeater.INVERSE_INSULATION_COEFFICIENT - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack).addEnergy().build()) + .addAttachmentOnlyContainers(ContainerType.HEAT, () -> HeatCapacitorsBuilder.builder() + .addBasic(TileEntityResistiveHeater.HEAT_CAPACITY, TileEntityResistiveHeater.INVERSE_CONDUCTION_COEFFICIENT, TileEntityResistiveHeater.INVERSE_INSULATION_COEFFICIENT) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder().addEnergy().build()) ); public static final BlockRegistryObject>, ItemBlockTooltip>>> FORMULAIC_ASSEMBLICATOR = BLOCKS.register("formulaic_assemblicator", () -> new BlockTile<>(MekanismBlockTypes.FORMULAIC_ASSEMBLICATOR, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> { - BooleanSupplier autoMode = () -> stack.getOrDefault(MekanismDataComponents.AUTO, false); - return ItemSlotsBuilder.builder(stack) - .addSlot((listener, x, y) -> BasicInventorySlot.at(TileEntityFormulaicAssemblicator.FORMULA_SLOT_VALIDATOR, listener, x, y)) - //Note: We skip making the extra checks based on the formula and just allow all items - .addSlots(2 * 9, InputInventorySlot::at) - .addSlots(3 * 3, (listener, x, y) -> FormulaicCraftingSlot.at(autoMode, listener, x, y)) - .addSlots(3 * 2, OutputInventorySlot::at) - .addEnergy() - .build(); - })); + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addFormulaSlot() + .addInput(2 * 9) + .addFormulaCraftingSlot(3 * 3) + .addOutput(3 * 2) + .addEnergy() + .build()) + ); public static final BlockRegistryObject>, ItemBlockTooltip>>> FUELWOOD_HEATER = BLOCKS.register("fuelwood_heater", () -> new BlockTile<>(MekanismBlockTypes.FUELWOOD_HEATER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.HEAT, stack -> BasicHeatCapacitor.createBasicItem(TileEntityFuelwoodHeater.HEAT_CAPACITY, - TileEntityFuelwoodHeater.INVERSE_CONDUCTION_COEFFICIENT, TileEntityFuelwoodHeater.INVERSE_INSULATION_COEFFICIENT - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack).addFuelSlot().build()) + .addAttachmentOnlyContainers(ContainerType.HEAT, () -> HeatCapacitorsBuilder.builder() + .addBasic(TileEntityFuelwoodHeater.HEAT_CAPACITY, TileEntityFuelwoodHeater.INVERSE_CONDUCTION_COEFFICIENT, TileEntityFuelwoodHeater.INVERSE_INSULATION_COEFFICIENT) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder().addFuelSlot().build()) ); public static final BlockRegistryObject>, ItemBlockTooltip>>> MODIFICATION_STATION = BLOCKS.register("modification_station", () -> new BlockTileModel<>(MekanismBlockTypes.MODIFICATION_STATION, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addInput(s -> s.getItem() instanceof IModuleItem) .addInput(IModuleHelper.INSTANCE::isModuleContainer) .addEnergy() @@ -686,11 +679,11 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> ANTIPROTONIC_NUCLEOSYNTHESIZER = BLOCKS.register("antiprotonic_nucleosynthesizer", () -> new BlockTileModel<>(MekanismBlockTypes.ANTIPROTONIC_NUCLEOSYNTHESIZER, properties -> properties.mapColor(MapColor.METAL)), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.GAS, stack -> RateLimitGasTank.createBasicItem(TileEntityAntiprotonicNucleosynthesizer.MAX_GAS, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, - gas -> MekanismRecipeType.NUCLEOSYNTHESIZING.getInputCache().containsInputB(null, gas.getStack(1)) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addGasSlotWithConversion(0) + .addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addBasic(TileEntityAntiprotonicNucleosynthesizer.MAX_GAS, MekanismRecipeType.NUCLEOSYNTHESIZING, ItemChemical::containsInputB) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addGasFillOrConvertSlot(0) .addInput(MekanismRecipeType.NUCLEOSYNTHESIZING, ItemChemical::containsInputA) .addOutput() .addEnergy() @@ -700,11 +693,12 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> PIGMENT_EXTRACTOR = BLOCKS.register("pigment_extractor", () -> new BlockTile<>(MekanismBlockTypes.PIGMENT_EXTRACTOR, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.PIGMENT, stack -> RateLimitPigmentTank.createBasicItem(TileEntityPigmentExtractor.MAX_PIGMENT, - ChemicalTankBuilder.PIGMENT.manualOnly, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, ChemicalTankBuilder.PIGMENT.alwaysTrue - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + .addAttachmentOnlyContainers(ContainerType.PIGMENT, () -> PigmentTanksBuilder.builder() + .addBasic(TileEntityPigmentExtractor.MAX_PIGMENT) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addInput(MekanismRecipeType.PIGMENT_EXTRACTING, SingleInputRecipeCache::containsInput) - .addPigmentSlot(0, PigmentInventorySlot::drain) + .addPigmentDrainSlot(0) .addEnergy() .build() ) @@ -713,22 +707,15 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> PIGMENT_MIXER = BLOCKS.register("pigment_mixer", () -> new BlockTile<>(MekanismBlockTypes.PIGMENT_MIXER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainers(ContainerType.PIGMENT, stack -> List.of( - RateLimitPigmentTank.createBasicItem(TileEntityPigmentMixer.MAX_INPUT_PIGMENT, - ChemicalTankBuilder.PIGMENT.manualOnly, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, - pigment -> MekanismRecipeType.PIGMENT_MIXING.getInputCache().containsInput(null, pigment.getStack(1)) - ), - RateLimitPigmentTank.createBasicItem(TileEntityPigmentMixer.MAX_INPUT_PIGMENT, - ChemicalTankBuilder.PIGMENT.manualOnly, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, - pigment -> MekanismRecipeType.PIGMENT_MIXING.getInputCache().containsInput(null, pigment.getStack(1)) - ), - RateLimitPigmentTank.createBasicItem(TileEntityPigmentMixer.MAX_OUTPUT_PIGMENT, - ChemicalTankBuilder.PIGMENT.manualOnly, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, ChemicalTankBuilder.PIGMENT.alwaysTrue - ) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addPigmentSlot(0, PigmentInventorySlot::fill) - .addPigmentSlot(1, PigmentInventorySlot::fill) - .addPigmentSlot(2, PigmentInventorySlot::drain) + .addAttachmentOnlyContainers(ContainerType.PIGMENT, () -> PigmentTanksBuilder.builder() + .addBasic(TileEntityPigmentMixer.MAX_INPUT_PIGMENT, MekanismRecipeType.PIGMENT_MIXING, EitherSideChemical::containsInput) + .addBasic(TileEntityPigmentMixer.MAX_INPUT_PIGMENT, MekanismRecipeType.PIGMENT_MIXING, EitherSideChemical::containsInput) + .addBasic(TileEntityPigmentMixer.MAX_OUTPUT_PIGMENT) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addPigmentFillSlot(0) + .addPigmentFillSlot(1) + .addPigmentDrainSlot(2) .addEnergy() .build() ) @@ -736,11 +723,11 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> PAINTING_MACHINE = BLOCKS.register("painting_machine", () -> new BlockTile<>(MekanismBlockTypes.PAINTING_MACHINE, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) .forItemHolder(holder -> holder - .addAttachmentOnlyContainer(ContainerType.PIGMENT, stack -> RateLimitPigmentTank.createBasicItem(TileEntityPaintingMachine.MAX_PIGMENT, - ChemicalTankBuilder.PIGMENT.manualOnly, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, - pigment -> MekanismRecipeType.PAINTING.getInputCache().containsInputB(null, pigment.getStack(1)) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addPigmentSlot(0, PigmentInventorySlot::fill) + .addAttachmentOnlyContainers(ContainerType.PIGMENT, () -> PigmentTanksBuilder.builder() + .addBasic(TileEntityPaintingMachine.MAX_PIGMENT, MekanismRecipeType.PAINTING, ItemChemical::containsInputB) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addPigmentFillSlot(0) //TODO - 1.20.4: add this comment to more methods //Note: We don't bother with the insertion check based on what pigments are currently stored .addInput(MekanismRecipeType.PAINTING, ItemChemical::containsInputA) @@ -754,20 +741,17 @@ private MekanismBlocks() { public static final BlockRegistryObject>, ItemBlockTooltip>>> SUPERCHARGED_COIL = registerBlock("supercharged_coil", () -> new BlockTileModel<>(MekanismBlockTypes.SUPERCHARGED_COIL, properties -> properties.mapColor(MapColor.COLOR_ORANGE)), Rarity.EPIC); public static final BlockRegistryObject>, ItemBlockTooltip>>> DIMENSIONAL_STABILIZER = BLOCKS.register("dimensional_stabilizer", () -> new BlockTile<>(MekanismBlockTypes.DIMENSIONAL_STABILIZER, properties -> properties.mapColor(BlockResourceInfo.STEEL.getMapColor())), ItemBlockTooltip::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack).addEnergy().build())); + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder().addEnergy().build())); public static final BlockRegistryObject>, ItemBlockQIOComponent> QIO_DRIVE_ARRAY = BLOCKS.register("qio_drive_array", () -> new BlockQIOComponent<>(MekanismBlockTypes.QIO_DRIVE_ARRAY, properties -> properties.mapColor(MapColor.METAL)), ItemBlockQIOComponent::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - //Note: As we don't have to update the presence of a drive or remove it from the frequency we can make do with just using a basic slot - //TODO - 1.20.4: Evaluate if copy the notExternal is correct or do we want this to have some other checks - .addSlots(2 * 6, (listener, x, y) -> BasicInventorySlot.at(BasicInventorySlot.notExternal, BasicInventorySlot.notExternal, QIODriveSlot.IS_QIO_ITEM, listener, x, y)) + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addQIODriveSlots(2 * 6) .build() )); public static final BlockRegistryObject>, ItemBlockQIOComponent> QIO_DASHBOARD = BLOCKS.register("qio_dashboard", () -> new BlockQIOComponent<>(MekanismBlockTypes.QIO_DASHBOARD, properties -> properties.mapColor(MapColor.COLOR_GRAY)), ItemBlockQIOComponent::new) //Note: While the attachment is mainly used for the portable dashboard, it is a convenient way to also handle window construction // and setting up the proper predicates for the actual dashboard block - //TODO - 1.20.5: Fix how we get the slots?? - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> new PortableQIODashboardInventory(null, stack).getSlots())); + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder().addQIODashboardSlots().build())); public static final BlockRegistryObject>, ItemBlockQIOComponent> QIO_IMPORTER = BLOCKS.register("qio_importer", () -> new BlockQIOComponent<>(MekanismBlockTypes.QIO_IMPORTER, properties -> properties.mapColor(MapColor.COLOR_GRAY)), ItemBlockQIOComponent::new); public static final BlockRegistryObject>, ItemBlockQIOComponent> QIO_EXPORTER = BLOCKS.register("qio_exporter", () -> new BlockQIOComponent<>(MekanismBlockTypes.QIO_EXPORTER, properties -> properties.mapColor(MapColor.COLOR_GRAY)), ItemBlockQIOComponent::new); public static final BlockRegistryObject>, ItemBlockQIOComponent> QIO_REDSTONE_ADAPTER = BLOCKS.register("qio_redstone_adapter", () -> new BlockQIOComponent<>(MekanismBlockTypes.QIO_REDSTONE_ADAPTER, properties -> properties.mapColor(MapColor.COLOR_GRAY)), ItemBlockQIOComponent::new); @@ -828,9 +812,11 @@ private static BlockRegistryObject registerBin(BlockTypeTile type) { - BinTier tier = (BinTier) type.get(AttributeTier.class).tier(); - return registerTieredBlock(tier, "_bin", color -> new BlockBin(type, properties -> properties.mapColor(color)), ItemBlockBin::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> List.of(BinInventorySlot.create(null, tier)))); + return registerTieredBlock(type, "_bin", color -> new BlockBin(type, properties -> properties.mapColor(color)), ItemBlockBin::new) + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addSlot(ComponentBackedBinInventorySlot::create) + .build() + )); } private static BlockRegistryObject>, ItemBlockInductionCell> registerInductionCell(BlockTypeTile type) { @@ -842,12 +828,13 @@ private static BlockRegistryObject registerFluidTank(Machine type) { - FluidTankTier tier = (FluidTankTier) type.get(AttributeTier.class).tier(); - return registerTieredBlock(tier, "_fluid_tank", () -> new BlockFluidTank(type), ItemBlockFluidTank::new) + return registerTieredBlock(type, "_fluid_tank", () -> new BlockFluidTank(type), ItemBlockFluidTank::new) .forItemHolder(holder -> holder - .addAttachedContainerCapability(ContainerType.FLUID, stack -> FluidTankRateLimitFluidTank.create(tier)) - .addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addFluidSlot(0, FluidInventorySlot::input) + .addAttachedContainerCapabilities(ContainerType.FLUID, () -> FluidTanksBuilder.builder() + .addTank(ComponentBackedFluidTankFluidTank::create) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addFluidInputSlot(0) .addOutput() .build() ) @@ -856,9 +843,9 @@ private static BlockRegistryObject registerF private static BlockRegistryObject registerEnergyCube(Machine type) { return registerTieredBlock(type, "_energy_cube", () -> new BlockEnergyCube(type), ItemBlockEnergyCube::new) - .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + .forItemHolder(holder -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addEnergy() - .addEnergySlot(0, EnergyInventorySlot::drain) + .addDrainEnergy() .build() )); } @@ -891,16 +878,19 @@ private static BlockRegistryObject>, ItemBlockChemicalTank> registerChemicalTank( Machine type) { return registerTieredBlock(type, "_chemical_tank", color -> new BlockTileModel<>(type, properties -> properties.mapColor(color)), ItemBlockChemicalTank::new) - .forItemHolder(holder -> holder - .addMissingMergedTanks(MekanismDataComponents.CHEMICAL_TANK_CONTENTS_HANDLER, false, true) - .addAttachmentOnlyContainers(ContainerType.ITEM, stack -> { - //TODO - 1.20.5: Fix this get component call - MergedChemicalTank tank = stack.get(MekanismDataComponents.CHEMICAL_TANK_CONTENTS_HANDLER); - return ItemSlotsBuilder.builder(stack) - .addContainerSlot(tank, MergedChemicalInventorySlot::drain) - .addContainerSlot(tank, MergedChemicalInventorySlot::fill) - .build(); - }) + .forItemHolder(holder -> { + final MergedTankCreator mergedTankCreator = new MergedTankCreator(ComponentBackedChemicalTankGasTank::create, ComponentBackedChemicalTankInfusionTank::create, + ComponentBackedChemicalTankPigmentTank::create, ComponentBackedChemicalTankSlurryTank::create); + holder.addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder().build()) + .addAttachmentOnlyContainers(ContainerType.INFUSION, () -> InfusionTanksBuilder.builder().addTank(mergedTankCreator).build()) + .addAttachmentOnlyContainers(ContainerType.PIGMENT, () -> PigmentTanksBuilder.builder().addTank(mergedTankCreator).build()) + .addAttachmentOnlyContainers(ContainerType.SLURRY, () -> SlurryTanksBuilder.builder().addTank(mergedTankCreator).build()) + .addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addMergedChemicalDrainSlot(0, 0, 0, 0) + .addMergedChemicalFillSlot(0, 0, 0, 0) + .build() + ); + } ); } @@ -921,45 +911,43 @@ private static > BlockRegistryObject s -> MekanismRecipeType.SAWING.getInputCache().containsInput(null, s); }; switch (type.getFactoryType()) { - case SMELTING, ENRICHING, CRUSHING -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + case SMELTING, ENRICHING, CRUSHING -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addBasicFactorySlots(processes, recipeInputPredicate) .addEnergy() .build() ); - case COMPRESSING, INJECTING, PURIFYING -> { - Predicate secondaryInputPredicate = switch (type.getFactoryType()) { - case COMPRESSING -> gas -> MekanismRecipeType.COMPRESSING.getInputCache().containsInputB(null, gas); - case INJECTING -> gas -> MekanismRecipeType.INJECTING.getInputCache().containsInputB(null, gas); - case PURIFYING -> gas -> MekanismRecipeType.PURIFYING.getInputCache().containsInputB(null, gas); - default -> throw new IllegalStateException("Factory type doesn't have a known gas recipe"); - }; - holder.addAttachmentOnlyContainer(ContainerType.GAS, stack -> RateLimitGasTank.createBasicItem(TileEntityAdvancedElectricMachine.MAX_GAS * processes, - ChemicalTankBuilder.GAS.manualOnly, ChemicalTankBuilder.GAS.alwaysTrueBi, - gas -> secondaryInputPredicate.test(gas.getStack(1)) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addBasicFactorySlots(processes, recipeInputPredicate) - .addGasSlotWithConversion(0) - .addEnergy() - .build() - ); - } - case COMBINING -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + case COMPRESSING, INJECTING, PURIFYING -> holder + .addAttachmentOnlyContainers(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addBasic(TileEntityAdvancedElectricMachine.MAX_GAS * processes, switch (type.getFactoryType()) { + case COMPRESSING -> MekanismRecipeType.COMPRESSING; + case INJECTING -> MekanismRecipeType.INJECTING; + case PURIFYING -> MekanismRecipeType.PURIFYING; + default -> throw new IllegalStateException("Factory type doesn't have a known gas recipe"); + }, ItemChemical::containsInputB) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addBasicFactorySlots(processes, recipeInputPredicate) + .addGasFillOrConvertSlot(0) + .addEnergy() + .build() + ); + case COMBINING -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addBasicFactorySlots(processes, recipeInputPredicate) .addInput(MekanismRecipeType.COMBINING, DoubleItem::containsInputB) .addEnergy() .build() ); case INFUSING -> holder - .addAttachmentOnlyContainer(ContainerType.INFUSION, stack -> RateLimitInfusionTank.createBasicItem(TileEntityMetallurgicInfuser.MAX_INFUSE * processes, - ChemicalTankBuilder.INFUSION.manualOnly, ChemicalTankBuilder.INFUSION.alwaysTrueBi, - infuseType -> MekanismRecipeType.METALLURGIC_INFUSING.getInputCache().containsInputB(null, infuseType.getStack(1)) - )).addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + .addAttachmentOnlyContainers(ContainerType.INFUSION, () -> InfusionTanksBuilder.builder() + .addBasic(TileEntityMetallurgicInfuser.MAX_INFUSE * processes, MekanismRecipeType.METALLURGIC_INFUSING, ItemChemical::containsInputB) + .build() + ).addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addBasicFactorySlots(processes, recipeInputPredicate) - .addInfusionSlotWithConversion(0) + .addInfusionFillOrConvertSlot(0) .addEnergy() .build() ); - case SAWING -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) + case SAWING -> holder.addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() .addBasicFactorySlots(processes, recipeInputPredicate, true) .addEnergy() .build() @@ -972,11 +960,7 @@ private static > BlockRegistryObject BlockRegistryObject registerTieredBlock(BlockType type, String suffix, Function blockSupplier, Function itemCreator) { - return registerTieredBlock(type.get(AttributeTier.class).tier(), suffix, blockSupplier, itemCreator); - } - - private static BlockRegistryObject registerTieredBlock(ITier tier, String suffix, - Function blockSupplier, Function itemCreator) { + ITier tier = type.get(AttributeTier.class).tier(); return registerTieredBlock(tier, suffix, () -> blockSupplier.apply(tier.getBaseTier().getMapColor()), itemCreator); } diff --git a/src/main/java/mekanism/common/registries/MekanismDataComponents.java b/src/main/java/mekanism/common/registries/MekanismDataComponents.java index dd7e2fc1736..eb74f077e46 100644 --- a/src/main/java/mekanism/common/registries/MekanismDataComponents.java +++ b/src/main/java/mekanism/common/registries/MekanismDataComponents.java @@ -1,10 +1,8 @@ package mekanism.common.registries; -import com.mojang.serialization.Codec; import java.util.UUID; import mekanism.api.MekanismAPI; import mekanism.api.annotations.NothingNullByDefault; -import mekanism.api.chemical.merged.MergedChemicalTank; import mekanism.api.math.FloatingLong; import mekanism.api.robit.RobitSkin; import mekanism.api.security.SecurityMode; @@ -14,24 +12,23 @@ import mekanism.common.attachments.FilterAware; import mekanism.common.attachments.FormulaAttachment; import mekanism.common.attachments.FrequencyAware; +import mekanism.common.attachments.LockData; import mekanism.common.attachments.OverflowAware; import mekanism.common.attachments.StabilizedChunks; import mekanism.common.attachments.component.AttachedEjector; import mekanism.common.attachments.component.AttachedSideConfig; import mekanism.common.attachments.component.UpgradeAware; -import mekanism.common.attachments.containers.AttachedChemicalTanks.AttachedGasTanks; -import mekanism.common.attachments.containers.AttachedChemicalTanks.AttachedInfusionTanks; -import mekanism.common.attachments.containers.AttachedChemicalTanks.AttachedPigmentTanks; -import mekanism.common.attachments.containers.AttachedChemicalTanks.AttachedSlurryTanks; -import mekanism.common.attachments.containers.AttachedEnergyContainers; -import mekanism.common.attachments.containers.AttachedFluidTanks; -import mekanism.common.attachments.containers.AttachedHeatCapacitors; -import mekanism.common.attachments.containers.AttachedInventorySlots; -import mekanism.common.attachments.containers.ContainerType; +import mekanism.common.attachments.containers.chemical.gas.AttachedGases; +import mekanism.common.attachments.containers.chemical.infuse.AttachedInfuseTypes; +import mekanism.common.attachments.containers.chemical.pigment.AttachedPigments; +import mekanism.common.attachments.containers.chemical.slurry.AttachedSlurries; +import mekanism.common.attachments.containers.energy.AttachedEnergy; +import mekanism.common.attachments.containers.fluid.AttachedFluids; +import mekanism.common.attachments.containers.heat.AttachedHeat; +import mekanism.common.attachments.containers.item.AttachedItems; import mekanism.common.attachments.qio.DriveContents; import mekanism.common.attachments.qio.DriveMetadata; import mekanism.common.attachments.qio.PortableDashboardContents; -import mekanism.common.capabilities.merged.MergedTank; import mekanism.common.content.entangloporter.InventoryFrequency; import mekanism.common.content.gear.ModuleContainer; import mekanism.common.content.qio.QIOFrequency; @@ -147,14 +144,38 @@ private MekanismDataComponents() { public static final MekanismDeferredHolder, DataComponentType> PERSONAL_STORAGE_ID = DATA_COMPONENTS.registerUUID("storage_id"); - public static final MekanismDeferredHolder, DataComponentType> ENERGY_CONTAINERS = DATA_COMPONENTS.registerContainer("energy_containers", () -> ContainerType.ENERGY); - public static final MekanismDeferredHolder, DataComponentType> INVENTORY_SLOTS = DATA_COMPONENTS.registerContainer("inventory_slots", () -> ContainerType.ITEM); - public static final MekanismDeferredHolder, DataComponentType> FLUID_TANKS = DATA_COMPONENTS.registerContainer("fluid_tanks", () -> ContainerType.FLUID); - public static final MekanismDeferredHolder, DataComponentType> GAS_TANKS = DATA_COMPONENTS.registerContainer("gas_tanks", () -> ContainerType.GAS); - public static final MekanismDeferredHolder, DataComponentType> INFUSION_TANKS = DATA_COMPONENTS.registerContainer("infusion_tanks", () -> ContainerType.INFUSION); - public static final MekanismDeferredHolder, DataComponentType> PIGMENT_TANKS = DATA_COMPONENTS.registerContainer("pigment_tanks", () -> ContainerType.PIGMENT); - public static final MekanismDeferredHolder, DataComponentType> SLURRY_TANKS = DATA_COMPONENTS.registerContainer("slurry_tanks", () -> ContainerType.SLURRY); - public static final MekanismDeferredHolder, DataComponentType> HEAT_CAPACITORS = DATA_COMPONENTS.registerContainer("heat_capacitors", () -> ContainerType.HEAT); + public static final MekanismDeferredHolder, DataComponentType> ATTACHED_ENERGY = DATA_COMPONENTS.simple("energy", + builder -> builder.persistent(AttachedEnergy.CODEC) + .networkSynchronized(AttachedEnergy.STREAM_CODEC) + ); + public static final MekanismDeferredHolder, DataComponentType> ATTACHED_ITEMS = DATA_COMPONENTS.simple("items", + builder -> builder.persistent(AttachedItems.CODEC) + .networkSynchronized(AttachedItems.STREAM_CODEC) + ); + public static final MekanismDeferredHolder, DataComponentType> ATTACHED_FLUIDS = DATA_COMPONENTS.simple("fluids", + builder -> builder.persistent(AttachedFluids.CODEC) + .networkSynchronized(AttachedFluids.STREAM_CODEC) + ); + public static final MekanismDeferredHolder, DataComponentType> ATTACHED_GASES = DATA_COMPONENTS.simple("gases", + builder -> builder.persistent(AttachedGases.CODEC) + .networkSynchronized(AttachedGases.STREAM_CODEC) + ); + public static final MekanismDeferredHolder, DataComponentType> ATTACHED_INFUSE_TYPES = DATA_COMPONENTS.simple("infuse_types", + builder -> builder.persistent(AttachedInfuseTypes.CODEC) + .networkSynchronized(AttachedInfuseTypes.STREAM_CODEC) + ); + public static final MekanismDeferredHolder, DataComponentType> ATTACHED_PIGMENTS = DATA_COMPONENTS.simple("pigments", + builder -> builder.persistent(AttachedPigments.CODEC) + .networkSynchronized(AttachedPigments.STREAM_CODEC) + ); + public static final MekanismDeferredHolder, DataComponentType> ATTACHED_SLURRIES = DATA_COMPONENTS.simple("slurries", + builder -> builder.persistent(AttachedSlurries.CODEC) + .networkSynchronized(AttachedSlurries.STREAM_CODEC) + ); + public static final MekanismDeferredHolder, DataComponentType> ATTACHED_HEAT = DATA_COMPONENTS.simple("heat_data", + builder -> builder.persistent(AttachedHeat.CODEC) + .networkSynchronized(AttachedHeat.STREAM_CODEC) + ); public static final MekanismDeferredHolder, DataComponentType> OWNER = DATA_COMPONENTS.registerUUID("owner"); public static final MekanismDeferredHolder, DataComponentType> SECURITY = DATA_COMPONENTS.simple("security", @@ -252,93 +273,19 @@ public static DataComponentType> g .networkSynchronized(UpgradeAware.STREAM_CODEC) ); + public static final MekanismDeferredHolder, DataComponentType> LOCK = DATA_COMPONENTS.simple("lock", + builder -> builder.persistent(LockData.CODEC) + .networkSynchronized(LockData.STREAM_CODEC) + ); + public static final MekanismDeferredHolder, DataComponentType> FORMULA_HOLDER = DATA_COMPONENTS.simple("formula", builder -> builder.persistent(FormulaAttachment.CODEC) .networkSynchronized(FormulaAttachment.STREAM_CODEC) ); - //Non-serializable attachments for use in persisting a backing object between multiple capabilities + //TODO - 1.20.5: Re-evaluate this public static final MekanismDeferredHolder, DataComponentType> QIO_DASHBOARD = DATA_COMPONENTS.simple("qio_dashboard", builder -> builder.persistent(PortableDashboardContents.CODEC) .networkSynchronized(PortableDashboardContents.STREAM_CODEC) ); - public static final MekanismDeferredHolder, DataComponentType> CHEMICAL_TANK_CONTENTS_HANDLER = DATA_COMPONENTS.simple("chemical_tank_contents_handler", - builder -> { - //TODO - 1.20.5: Figure out how to implement containers - /*if (holder instanceof ItemStack stack && !stack.isEmpty() && stack.getItem() instanceof ItemBlockChemicalTank tank) { - ChemicalTankTier tier = Objects.requireNonNull(tank.getTier(), "Chemical tank tier cannot be null"); - return MergedChemicalTank.create( - new GasTankRateLimitChemicalTank(tier, null), - new InfusionTankRateLimitChemicalTank(tier, null), - new PigmentTankRateLimitChemicalTank(tier, null), - new SlurryTankRateLimitChemicalTank(tier, null) - ); - }*/ - return builder.persistent(Codec.unit(() -> null)); - }); - public static final MekanismDeferredHolder, DataComponentType> GAUGE_DROPPER_CONTENTS_HANDLER = DATA_COMPONENTS.simple("gauge_dropper_contents_handler", - builder -> { - //TODO - 1.20.5: Figure out how to implement containers - /*if (holder instanceof ItemStack stack && stack.is(MekanismItems.GAUGE_DROPPER)) { - return MergedTank.create( - RateLimitFluidTank.create(MekanismConfig.gear.gaugeDroppedTransferRate, MekanismConfig.gear.gaugeDropperCapacity, - BasicFluidTank.alwaysTrueBi, BasicFluidTank.alwaysTrueBi, BasicFluidTank.alwaysTrue), - RateLimitGasTank.create(MekanismConfig.gear.gaugeDroppedTransferRate, MekanismConfig.gear.gaugeDropperCapacity, - ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrue), - RateLimitInfusionTank.create(MekanismConfig.gear.gaugeDroppedTransferRate, MekanismConfig.gear.gaugeDropperCapacity, - ChemicalTankBuilder.INFUSION.alwaysTrueBi, ChemicalTankBuilder.INFUSION.alwaysTrueBi, ChemicalTankBuilder.INFUSION.alwaysTrue), - RateLimitPigmentTank.create(MekanismConfig.gear.gaugeDroppedTransferRate, MekanismConfig.gear.gaugeDropperCapacity, - ChemicalTankBuilder.PIGMENT.alwaysTrueBi, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, ChemicalTankBuilder.PIGMENT.alwaysTrue), - RateLimitSlurryTank.create(MekanismConfig.gear.gaugeDroppedTransferRate, MekanismConfig.gear.gaugeDropperCapacity, - ChemicalTankBuilder.SLURRY.alwaysTrueBi, ChemicalTankBuilder.SLURRY.alwaysTrueBi, ChemicalTankBuilder.SLURRY.alwaysTrue) - ); - }*/ - return builder.persistent(Codec.unit(() -> null)); - }); - public static final MekanismDeferredHolder, DataComponentType> CDC_CONTENTS_HANDLER = DATA_COMPONENTS.simple("cdc_contents_handler", - builder -> { - //TODO - 1.20.5: Figure out how to implement containers - /*if (holder instanceof ItemStack stack && stack.is(MekanismBlocks.CHEMICAL_DISSOLUTION_CHAMBER.asItem())) { - return MergedChemicalTank.create( - RateLimitGasTank.createBasicItem(TileEntityChemicalDissolutionChamber.MAX_CHEMICAL, - ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrue - ), - RateLimitInfusionTank.createBasicItem(TileEntityChemicalDissolutionChamber.MAX_CHEMICAL, - ChemicalTankBuilder.INFUSION.alwaysTrueBi, ChemicalTankBuilder.INFUSION.alwaysTrueBi, ChemicalTankBuilder.INFUSION.alwaysTrue - ), - RateLimitPigmentTank.createBasicItem(TileEntityChemicalDissolutionChamber.MAX_CHEMICAL, - ChemicalTankBuilder.PIGMENT.alwaysTrueBi, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, ChemicalTankBuilder.PIGMENT.alwaysTrue - ), - RateLimitSlurryTank.createBasicItem(TileEntityChemicalDissolutionChamber.MAX_CHEMICAL, - ChemicalTankBuilder.SLURRY.alwaysTrueBi, ChemicalTankBuilder.SLURRY.alwaysTrueBi, ChemicalTankBuilder.SLURRY.alwaysTrue - ) - ); - }*/ - return builder.persistent(Codec.unit(() -> null)); - }); - public static final MekanismDeferredHolder, DataComponentType> CRYSTALLIZER_CONTENTS_HANDLER = DATA_COMPONENTS.simple("crystallizer_contents_handler", - builder -> { - //TODO - 1.20.5: Figure out how to implement containers - /*if (holder instanceof ItemStack stack && stack.is(MekanismBlocks.CHEMICAL_CRYSTALLIZER.asItem())) { - return MergedChemicalTank.create( - RateLimitGasTank.createBasicItem(TileEntityChemicalCrystallizer.MAX_CHEMICAL, - ChemicalTankBuilder.GAS.alwaysTrueBi, ChemicalTankBuilder.GAS.alwaysTrueBi, - gas -> MekanismRecipeType.CRYSTALLIZING.getInputCache().containsInput(null, gas) - ), - RateLimitInfusionTank.createBasicItem(TileEntityChemicalCrystallizer.MAX_CHEMICAL, - ChemicalTankBuilder.INFUSION.alwaysTrueBi, ChemicalTankBuilder.INFUSION.alwaysTrueBi, - infuseType -> MekanismRecipeType.CRYSTALLIZING.getInputCache().containsInput(null, infuseType) - ), - RateLimitPigmentTank.createBasicItem(TileEntityChemicalCrystallizer.MAX_CHEMICAL, - ChemicalTankBuilder.PIGMENT.alwaysTrueBi, ChemicalTankBuilder.PIGMENT.alwaysTrueBi, - pigment -> MekanismRecipeType.CRYSTALLIZING.getInputCache().containsInput(null, pigment) - ), - RateLimitSlurryTank.createBasicItem(TileEntityChemicalCrystallizer.MAX_CHEMICAL, - ChemicalTankBuilder.SLURRY.alwaysTrueBi, ChemicalTankBuilder.SLURRY.alwaysTrueBi, - slurry -> MekanismRecipeType.CRYSTALLIZING.getInputCache().containsInput(null, slurry) - ) - ); - }*/ - return builder.persistent(Codec.unit(() -> null)); - }); } \ No newline at end of file diff --git a/src/main/java/mekanism/common/registries/MekanismItems.java b/src/main/java/mekanism/common/registries/MekanismItems.java index 494a13d13c4..4b53aa4c636 100644 --- a/src/main/java/mekanism/common/registries/MekanismItems.java +++ b/src/main/java/mekanism/common/registries/MekanismItems.java @@ -10,15 +10,15 @@ import mekanism.api.tier.BaseTier; import mekanism.common.Mekanism; import mekanism.common.attachments.containers.ContainerType; -import mekanism.common.attachments.qio.PortableQIODashboardInventory; -import mekanism.common.capabilities.chemical.variable.RateLimitGasTank; +import mekanism.common.attachments.containers.chemical.gas.GasTanksBuilder; +import mekanism.common.attachments.containers.chemical.infuse.InfusionTanksBuilder; +import mekanism.common.attachments.containers.chemical.pigment.PigmentTanksBuilder; +import mekanism.common.attachments.containers.chemical.slurry.SlurryTanksBuilder; +import mekanism.common.attachments.containers.energy.EnergyContainersBuilder; +import mekanism.common.attachments.containers.fluid.FluidTanksBuilder; +import mekanism.common.attachments.containers.item.ItemSlotsBuilder; import mekanism.common.capabilities.energy.BasicEnergyContainer; -import mekanism.common.capabilities.energy.item.RateLimitEnergyContainer; -import mekanism.common.capabilities.fluid.BasicFluidTank; -import mekanism.common.capabilities.fluid.item.RateLimitFluidTank; import mekanism.common.config.MekanismConfig; -import mekanism.common.inventory.slot.BasicInventorySlot; -import mekanism.common.inventory.slot.ItemSlotsBuilder; import mekanism.common.item.ItemAlloy; import mekanism.common.item.ItemConfigurationCard; import mekanism.common.item.ItemConfigurator; @@ -79,8 +79,8 @@ private MekanismItems() { public static final Table> PROCESSED_RESOURCES = HashBasedTable.create(); public static final ItemRegistryObject ROBIT = ITEMS.registerItem("robit", ItemRobit::new) - .addAttachmentOnlyContainers(ContainerType.ITEM, stack -> ItemSlotsBuilder.builder(stack) - .addSlots(3 * 9, BasicInventorySlot::at) + .addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder() + .addBasic(3 * 9) .addEnergy() .addInput(MekanismRecipeType.SMELTING, SingleInputRecipeCache::containsInput) .addOutput() @@ -95,19 +95,20 @@ private MekanismItems() { public static final ItemRegistryObject CRAFTING_FORMULA = ITEMS.registerItem("crafting_formula", ItemCraftingFormula::new); public static final ItemRegistryObject SEISMIC_READER = ITEMS.registerItem("seismic_reader", ItemSeismicReader::new); public static final ItemRegistryObject GAUGE_DROPPER = ITEMS.registerItem("gauge_dropper", ItemGaugeDropper::new) - .addMissingMergedTanks(MekanismDataComponents.GAUGE_DROPPER_CONTENTS_HANDLER, true, true); + .addAttachedContainerCapabilities(ContainerType.GAS, () -> GasTanksBuilder.builder().addTank(ItemGaugeDropper.MERGED_TANK_CREATOR).build(), MekanismConfig.gear) + .addAttachedContainerCapabilities(ContainerType.INFUSION, () -> InfusionTanksBuilder.builder().addTank(ItemGaugeDropper.MERGED_TANK_CREATOR).build(), MekanismConfig.gear) + .addAttachedContainerCapabilities(ContainerType.PIGMENT, () -> PigmentTanksBuilder.builder().addTank(ItemGaugeDropper.MERGED_TANK_CREATOR).build(), MekanismConfig.gear) + .addAttachedContainerCapabilities(ContainerType.SLURRY, () -> SlurryTanksBuilder.builder().addTank(ItemGaugeDropper.MERGED_TANK_CREATOR).build(), MekanismConfig.gear) + .addAttachedContainerCapabilities(ContainerType.FLUID, () -> FluidTanksBuilder.builder().addTank(ItemGaugeDropper.MERGED_TANK_CREATOR).build(), MekanismConfig.gear); public static final ItemRegistryObject GEIGER_COUNTER = ITEMS.registerItem("geiger_counter", ItemGeigerCounter::new); public static final ItemRegistryObject DOSIMETER = ITEMS.registerItem("dosimeter", ItemDosimeter::new); public static final ItemRegistryObject CANTEEN = ITEMS.registerItem("canteen", ItemCanteen::new) - .addAttachedContainerCapability(ContainerType.FLUID, stack -> RateLimitFluidTank.create( - MekanismConfig.gear.canteenTransferRate, - MekanismConfig.gear.canteenMaxStorage, - BasicFluidTank.alwaysTrueBi, BasicFluidTank.alwaysTrueBi, - fluid -> fluid.is(MekanismFluids.NUTRITIONAL_PASTE.getFluid()) - ), MekanismConfig.gear); + .addAttachedContainerCapabilities(ContainerType.FLUID, () -> FluidTanksBuilder.builder() + .addBasicExtractable(MekanismConfig.gear.canteenTransferRate, MekanismConfig.gear.canteenMaxStorage, + fluid -> fluid.is(MekanismFluids.NUTRITIONAL_PASTE.getFluid())) + .build(), MekanismConfig.gear); public static final ItemRegistryObject PORTABLE_QIO_DASHBOARD = ITEMS.registerItem("portable_qio_dashboard", ItemPortableQIODashboard::new) - //TODO - 1.20.5: Fix this get component call - .addAttachmentOnlyContainers(ContainerType.ITEM, stack -> new PortableQIODashboardInventory(null, stack).getSlots()); + .addAttachmentOnlyContainers(ContainerType.ITEM, () -> ItemSlotsBuilder.builder().addQIODashboardSlots().build()); // QIO Drives public static final ItemRegistryObject BASE_QIO_DRIVE = registerQIODrive(QIODriveTier.BASE); public static final ItemRegistryObject HYPER_DENSE_QIO_DRIVE = registerQIODrive(QIODriveTier.HYPER_DENSE); @@ -116,16 +117,13 @@ private MekanismItems() { // Tools public static final ItemRegistryObject ATOMIC_DISASSEMBLER = ITEMS.registerItem("atomic_disassembler", ItemAtomicDisassembler::new); public static final ItemRegistryObject ELECTRIC_BOW = ITEMS.registerItem("electric_bow", ItemElectricBow::new) - .addAttachedContainerCapability(ContainerType.ENERGY, stack -> RateLimitEnergyContainer.create( - MekanismConfig.gear.electricBowChargeRate, - MekanismConfig.gear.electricBowMaxEnergy - ), MekanismConfig.gear); + .addAttachedContainerCapabilities(ContainerType.ENERGY, () -> EnergyContainersBuilder.builder() + .addBasic(MekanismConfig.gear.electricBowChargeRate, MekanismConfig.gear.electricBowMaxEnergy) + .build(), MekanismConfig.gear); public static final ItemRegistryObject FLAMETHROWER = ITEMS.registerItem("flamethrower", ItemFlamethrower::new) - .addAttachedContainerCapability(ContainerType.GAS, stack -> RateLimitGasTank.createInternalStorage( - MekanismConfig.gear.flamethrowerFillRate, - MekanismConfig.gear.flamethrowerMaxGas, - gas -> gas == MekanismGases.HYDROGEN.getChemical() - ), MekanismConfig.gear); + .addAttachedContainerCapabilities(ContainerType.GAS, () -> GasTanksBuilder.builder() + .addInternalStorage(MekanismConfig.gear.flamethrowerFillRate, MekanismConfig.gear.flamethrowerMaxGas, gas -> gas == MekanismGases.HYDROGEN.getChemical() + ).build(), MekanismConfig.gear); public static final ItemRegistryObject MEKA_TOOL = ITEMS.registerUnburnable("meka_tool", ItemMekaTool::new); // Armor public static final ItemRegistryObject FREE_RUNNERS = ITEMS.registerItem("free_runners", ItemFreeRunners::new); diff --git a/src/main/java/mekanism/common/tile/base/TileEntityMekanism.java b/src/main/java/mekanism/common/tile/base/TileEntityMekanism.java index baf6e65c6eb..99ff4cb85fc 100644 --- a/src/main/java/mekanism/common/tile/base/TileEntityMekanism.java +++ b/src/main/java/mekanism/common/tile/base/TileEntityMekanism.java @@ -808,8 +808,7 @@ protected void applyImplicitComponents(@NotNull BlockEntity.DataComponentInput i // but there is a good chance a lot of this stuff has no real reason to need to be set on the client side at all for (ContainerType type : ContainerType.TYPES) { if (persists(type)) { - //TODO - 1.20.5: Figure out container copying - //type.copyFrom(stack, this); + type.copyFrom(this, input); } } if (this instanceof ITileFilterHolder filterHolder) { @@ -834,8 +833,7 @@ protected void collectImplicitComponents(@NotNull DataComponentMap.Builder build } for (ContainerType type : ContainerType.TYPES) { if (persists(type)) { - //TODO - 1.20.5: Figure out container copying - //type.copyTo(this, stack); + type.copyTo(this, builder); } } if (this instanceof ITileFilterHolder filterHolder) { diff --git a/src/main/java/mekanism/common/util/ChemicalUtil.java b/src/main/java/mekanism/common/util/ChemicalUtil.java index f475d43dbc5..b42835004bc 100644 --- a/src/main/java/mekanism/common/util/ChemicalUtil.java +++ b/src/main/java/mekanism/common/util/ChemicalUtil.java @@ -11,6 +11,7 @@ import mekanism.api.chemical.ChemicalType; import mekanism.api.chemical.IChemicalHandler; import mekanism.api.chemical.IChemicalTank; +import mekanism.api.chemical.IMekanismChemicalHandler; import mekanism.api.chemical.attribute.ChemicalAttribute; import mekanism.api.chemical.gas.Gas; import mekanism.api.chemical.gas.GasBuilder; @@ -34,7 +35,6 @@ import mekanism.api.text.EnumColor; import mekanism.api.text.TextComponentUtil; import mekanism.common.MekanismLang; -import mekanism.common.attachments.containers.AttachedChemicalTanks; import mekanism.common.attachments.containers.ContainerType; import mekanism.common.capabilities.Capabilities; import mekanism.common.capabilities.MultiTypeCapability; @@ -209,8 +209,8 @@ public static ItemStack getFilledVariant(ItemStack toFill, IChemicalProvider } private static , STACK extends ChemicalStack, TANK extends IChemicalTank> ItemStack getFilledVariant( - ItemStack toFill, IChemicalProvider provider, ContainerType, ?> containerType) { - AttachedChemicalTanks attachment = containerType.getAttachment(toFill); + ItemStack toFill, IChemicalProvider provider, ContainerType> containerType) { + IMekanismChemicalHandler attachment = containerType.createHandler(toFill); if (attachment != null) { for (TANK tank : attachment.getChemicalTanks(null)) { tank.setStack(withAmount(provider, tank.getCapacity())); diff --git a/src/main/java/mekanism/common/util/FluidUtils.java b/src/main/java/mekanism/common/util/FluidUtils.java index 41106f0602b..0f653ffa648 100644 --- a/src/main/java/mekanism/common/util/FluidUtils.java +++ b/src/main/java/mekanism/common/util/FluidUtils.java @@ -5,7 +5,7 @@ import mekanism.api.Action; import mekanism.api.AutomationType; import mekanism.api.fluid.IExtendedFluidTank; -import mekanism.common.attachments.containers.AttachedFluidTanks; +import mekanism.api.fluid.IMekanismFluidHandler; import mekanism.common.attachments.containers.ContainerType; import mekanism.common.capabilities.Capabilities; import mekanism.common.content.network.distribution.FluidHandlerTarget; @@ -31,7 +31,7 @@ private FluidUtils() { } public static ItemStack getFilledVariant(ItemStack toFill, Fluid fluid) { - AttachedFluidTanks attachment = ContainerType.FLUID.getAttachment(toFill); + IMekanismFluidHandler attachment = ContainerType.FLUID.createHandler(toFill); if (attachment != null) { for (IExtendedFluidTank fluidTank : attachment.getFluidTanks(null)) { fluidTank.setStack(new FluidStack(fluid, fluidTank.getCapacity())); diff --git a/src/main/java/mekanism/common/util/StorageUtils.java b/src/main/java/mekanism/common/util/StorageUtils.java index e7d22f3e003..a7dfb4eb348 100644 --- a/src/main/java/mekanism/common/util/StorageUtils.java +++ b/src/main/java/mekanism/common/util/StorageUtils.java @@ -25,8 +25,6 @@ import mekanism.api.text.ILangEntry; import mekanism.api.text.TextComponentUtil; import mekanism.common.MekanismLang; -import mekanism.common.attachments.containers.AttachedChemicalTanks; -import mekanism.common.attachments.containers.AttachedEnergyContainers; import mekanism.common.attachments.containers.ContainerType; import mekanism.common.capabilities.Capabilities; import mekanism.common.capabilities.heat.BasicHeatCapacitor; @@ -53,7 +51,7 @@ public static void addStoredEnergy(@NotNull ItemStack stack, @NotNull List, STACK extends ChemicalStack> void addStoredChemical(@NotNull ItemStack stack, @NotNull List tooltip, boolean showMissingCap, boolean showAttributes, ILangEntry emptyLangEntry, BiFunction storedFunction, ItemCapability, Void> capability, - ContainerType, ?> containerType) { + ContainerType> containerType) { IChemicalHandler handler = stack.getCapability(capability); if (handler == null) { //Fall back to trying to look up the stored chemical by the container type if the stack doesn't expose it - handler = containerType.getAttachmentIfPresent(stack); + handler = containerType.createHandlerIfData(stack); } if (handler != null) { for (int tank = 0, tanks = handler.getTanks(); tank < tanks; tank++) { @@ -121,7 +119,7 @@ public static void addStoredFluid(@NotNull ItemStack stack, @NotNull List, TANK extends IChemicalTank> STACK getStoredChemicalFromAttachment(ItemStack stack, STACK emptyStack, - ContainerType, ?> containerType) { + ContainerType> containerType) { STACK chemicalStack = emptyStack; for (TANK tank : containerType.getAttachmentContainersIfPresent(stack)) { if (tank.isEmpty()) { @@ -307,7 +305,7 @@ public static FloatingLong getStoredEnergyFromAttachment(ItemStack stack) { } public static ItemStack getFilledEnergyVariant(ItemStack toFill) { - AttachedEnergyContainers attachment = ContainerType.ENERGY.getAttachment(toFill); + IMekanismStrictEnergyHandler attachment = ContainerType.ENERGY.createHandler(toFill); if (attachment != null) { for (IEnergyContainer energyContainer : attachment.getEnergyContainers(null)) { energyContainer.setEnergy(energyContainer.getMaxEnergy());