diff --git a/src/main/java/gregtech/api/capability/IFilter.java b/src/main/java/gregtech/api/capability/IFilter.java new file mode 100644 index 00000000000..9d7463b9a3a --- /dev/null +++ b/src/main/java/gregtech/api/capability/IFilter.java @@ -0,0 +1,163 @@ +package gregtech.api.capability; + +import javax.annotation.Nonnull; +import java.util.Comparator; +import java.util.function.Predicate; + +/** + * Base type for generic filters. In addition to the predicate method, this interface provides priority primarily used + * in insertion logic. + *

+ * Although this class can be used as lambda interface, it is strongly encouraged to define priority according to + * criteria specified in javadocs on {@link #getPriority()}. + * + * @param type of the instance for filtering + */ +@FunctionalInterface +public interface IFilter extends Predicate { + + /** + * Compare logic for filter instances. + */ + Comparator> FILTER_COMPARATOR = Comparator + .>comparingInt(IFilter::getPriority) + .reversed(); + + /** + * Test if this filter accepts the instance. If the parameter is {@code null}, then the behavior of this method is + * undefined. + * + * @param t instance to be tested + * @return if this filter accepts the instance + */ + @Override + boolean test(@Nonnull T t); + + /** + * Return insertion priority for this filter. The priority is applied on some insertion logics, to prioritize + * certain filters from others. The priority system uses reverse ordering; higher priority values have + * precedence over lower ones. + *

+ * Although the priority is not a strict requirement, it is strongly encouraged to specify priority according to + * these criteria. + * + * @return insertion priority + */ + default int getPriority() { + return 0; + } + + /** + * Return the reverse of this filter. The resulting filter returns the opposite of what the original filter would, + * has priority of inverse of the original. + * + * @return reverse of this filter + */ + @Override + @Nonnull + default IFilter negate() { + return new IFilter<>() { + @Override + public boolean test(@Nonnull T t) { + return !IFilter.this.test(t); + } + + @Override + public int getPriority() { + return -IFilter.this.getPriority(); + } + + @Override + @Nonnull + public IFilter negate() { + return IFilter.this; + } + }; + } + + /** + * Default priority logic for all whitelist filters. + *

+ * Whitelist filters have {@code Integer.MAX_VALUE - whitelistSize} as their default priority. The highest possible + * number for whitelist priority is {@code Integer.MAX_VALUE - 1}, where only one entry is whitelisted. + * + * @param whitelistSize the size of whitelist entries + * @return default priority logic for all whitelist filters + */ + static int whitelistPriority(int whitelistSize) { + return Integer.MAX_VALUE - whitelistSize; + } + + /** + * Default priority logic for all blacklist filters. + *

+ * Blacklist filters have {@code Integer.MIN_VALUE + 1 + blacklistSize} as their default priority. The lowest + * possible number for blacklist priority is {@code Integer.MIN_VALUE + 2}, where only one entry is blacklisted. + * + * @param blacklistSize the size of whitelist entries + * @return default priority logic for all blacklist filters + */ + static int blacklistPriority(int blacklistSize) { + return Integer.MIN_VALUE + 1 + blacklistSize; + } + + /** + * Recommended priority for 'whitelist-like' filters; can be adjusted. + * + * @return recommended priority for 'whitelist-like' filters + */ + static int whitelistLikePriority() { + return 1000; + } + + /** + * Recommended priority for 'blacklist-like' filters; can be adjusted. + * + * @return recommended priority for 'blacklist-like' filters + */ + static int blacklistLikePriority() { + return -1000; + } + + /** + * Highest possible priority for filters. + * + * @return highest possible priority + */ + static int firstPriority() { + return Integer.MAX_VALUE; + } + + /** + * Lowest possible priority for filters. + * + * @return lowest possible priority + */ + static int lastPriority() { + return Integer.MIN_VALUE + 1; + } + + /** + * Special priority for 'no-priority' filters; applicable to no-op filters and its reverse (everything filter). + * + * @return special priority for 'no-priority' filters + */ + static int noPriority() { + return Integer.MIN_VALUE; + } +} diff --git a/src/main/java/gregtech/api/capability/IFilteredFluidContainer.java b/src/main/java/gregtech/api/capability/IFilteredFluidContainer.java new file mode 100644 index 00000000000..ca35dc0b32b --- /dev/null +++ b/src/main/java/gregtech/api/capability/IFilteredFluidContainer.java @@ -0,0 +1,27 @@ +package gregtech.api.capability; + +import net.minecraftforge.fluids.FluidStack; + +import javax.annotation.Nullable; +import java.util.Comparator; + +/** + * Interface for fluid containers ({@link net.minecraftforge.fluids.IFluidTank IFluidTank} or + * {@link net.minecraftforge.fluids.capability.IFluidHandler IFluidHandler}) associated with {@link IFilter}. + */ +public interface IFilteredFluidContainer { + + /** + * Compare logic for filtered instances. + */ + Comparator COMPARATOR = Comparator.nullsLast( + Comparator.comparing(IFilteredFluidContainer::getFilter, IFilter.FILTER_COMPARATOR) + ); + + /** + * @return instance of {@link IFilter} associated to this object, or {@code null} if there's no filter + * associated. + */ + @Nullable + IFilter getFilter(); +} diff --git a/src/main/java/gregtech/api/capability/IMultipleTankHandler.java b/src/main/java/gregtech/api/capability/IMultipleTankHandler.java index eae6890271b..eaf5ae04cf4 100644 --- a/src/main/java/gregtech/api/capability/IMultipleTankHandler.java +++ b/src/main/java/gregtech/api/capability/IMultipleTankHandler.java @@ -1,21 +1,248 @@ package gregtech.api.capability; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.INBTSerializable; import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTank; +import net.minecraftforge.fluids.FluidTankInfo; import net.minecraftforge.fluids.IFluidTank; import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.IFluidTankProperties; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Comparator; +import java.util.Iterator; import java.util.List; -public interface IMultipleTankHandler extends IFluidHandler, Iterable { +/** + * Base class for multi-tank fluid handlers. Handles insertion logic, along with other standard + * {@link IFluidHandler} functionalities. + * + * @see gregtech.api.capability.impl.FluidTankList FluidTankList + */ +public interface IMultipleTankHandler extends IFluidHandler, Iterable { - List getFluidTanks(); + /** + * Comparator for entries that can be used in insertion logic + */ + Comparator ENTRY_COMPARATOR = (o1, o2) -> { + // #1: non-empty tank first + boolean empty1 = o1.getFluidAmount() <= 0; + boolean empty2 = o2.getFluidAmount() <= 0; + if (empty1 != empty2) return empty1 ? 1 : -1; - int getTanks(); + // #2: filter priority + IFilter filter1 = o1.getFilter(); + IFilter filter2 = o2.getFilter(); + if (filter1 == null) return filter2 == null ? 0 : 1; + if (filter2 == null) return -1; + return IFilter.FILTER_COMPARATOR.compare(filter1, filter2); + }; + + /** + * @return unmodifiable view of {@code MultiFluidTankEntry}s. Note that it's still possible to access + * and modify inner contents of the tanks. + */ + @Nonnull + List getFluidTanks(); - IFluidTank getTankAt(int index); + /** + * @return Number of tanks in this tank handler + */ + int getTanks(); - int getIndexOfFluid(FluidStack other); + @Nonnull + MultiFluidTankEntry getTankAt(int index); + /** + * @return {@code false} if insertion to this fluid handler enforces input to be + * filled in one slot at max. {@code true} if it bypasses the rule. + */ boolean allowSameFluidFill(); + + /** + * Tries to search tank with contents equal to {@code fluidStack}. If {@code fluidStack} is + * {@code null}, an empty tank is searched instead. + * + * @param fluidStack Fluid stack to search index + * @return Index corresponding to tank at {@link #getFluidTanks()} with matching + */ + default int getIndexOfFluid(@Nullable FluidStack fluidStack) { + List fluidTanks = getFluidTanks(); + for (int i = 0; i < fluidTanks.size(); i++) { + FluidStack tankStack = fluidTanks.get(i).getFluid(); + if (fluidStack == tankStack || tankStack != null && tankStack.isFluidEqual(fluidStack)) { + return i; + } + } + return -1; + } + + @Override + default Iterator iterator() { + return getFluidTanks().iterator(); + } + + /** + * Entry of multi fluid tanks. Retains reference to original {@link IMultipleTankHandler} for accessing + * information such as {@link IMultipleTankHandler#allowSameFluidFill()}. + */ + final class MultiFluidTankEntry implements IFluidTank, IFluidHandler, IFilteredFluidContainer { + + private final IMultipleTankHandler tank; + private final IFluidTank delegate; + + public MultiFluidTankEntry(@Nonnull IMultipleTankHandler tank, @Nonnull IFluidTank delegate) { + this.tank = tank; + this.delegate = delegate; + } + + @Nonnull + public IMultipleTankHandler getTank() { + return tank; + } + + @Nonnull + public IFluidTank getDelegate() { + return delegate; + } + + public boolean allowSameFluidFill() { + return tank.allowSameFluidFill(); + } + + @Nullable + @Override + public IFilter getFilter() { + return this.delegate instanceof IFilteredFluidContainer filtered ? filtered.getFilter() : null; + } + + @Nonnull + public IFluidTankProperties[] getTankProperties() { + return delegate instanceof IFluidHandler fluidHandler ? + fluidHandler.getTankProperties() : + new IFluidTankProperties[]{new FallbackTankProperty()}; + } + + public NBTTagCompound trySerialize() { + if (delegate instanceof FluidTank fluidTank) { + return fluidTank.writeToNBT(new NBTTagCompound()); + } else if (delegate instanceof INBTSerializable serializable) { + try { + return (NBTTagCompound) serializable.serializeNBT(); + } catch (ClassCastException ignored) {} + } + return new NBTTagCompound(); + } + + @SuppressWarnings({"unchecked"}) + public void tryDeserialize(NBTTagCompound tag) { + if (delegate instanceof FluidTank fluidTank) { + fluidTank.readFromNBT(tag); + } else if (delegate instanceof INBTSerializable serializable) { + try { + serializable.deserializeNBT(tag); + } catch (ClassCastException ignored) {} + } + } + + @Nullable + @Override + public FluidStack getFluid() { + return delegate.getFluid(); + } + + @Override + public int getFluidAmount() { + return delegate.getFluidAmount(); + } + + @Override + public int getCapacity() { + return delegate.getCapacity(); + } + + @Override + public FluidTankInfo getInfo() { + return delegate.getInfo(); + } + + @Override + public int fill(FluidStack resource, boolean doFill) { + return delegate.fill(resource, doFill); + } + + @Nullable + @Override + public FluidStack drain(FluidStack resource, boolean doDrain) { + if (resource == null || resource.amount <= 0) { + return null; + } + if (delegate instanceof IFluidHandler fluidHandler) { + return fluidHandler.drain(resource, doDrain); + } + // just imitate the logic + FluidStack fluid = delegate.getFluid(); + return fluid != null && fluid.isFluidEqual(resource) ? drain(resource.amount, doDrain) : null; + } + + @Nullable + @Override + public FluidStack drain(int maxDrain, boolean doDrain) { + return delegate.drain(maxDrain, doDrain); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") + public boolean equals(Object obj) { + return this == obj || delegate.equals(obj); + } + + @Override + public String toString() { + return delegate.toString(); + } + + private final class FallbackTankProperty implements IFluidTankProperties { + + @Nullable + @Override + public FluidStack getContents() { + return delegate.getFluid(); + } + + @Override + public int getCapacity() { + return delegate.getCapacity(); + } + + @Override + public boolean canFill() { + return true; + } + + @Override + public boolean canDrain() { + return true; + } + + @Override + public boolean canFillFluidType(FluidStack fluidStack) { + IFilter filter = getFilter(); + return filter == null || filter.test(fluidStack); + } + + @Override + public boolean canDrainFluidType(FluidStack fluidStack) { + return true; + } + } + } } diff --git a/src/main/java/gregtech/api/capability/IPropertyFluidFilter.java b/src/main/java/gregtech/api/capability/IPropertyFluidFilter.java new file mode 100644 index 00000000000..52a1e3d5627 --- /dev/null +++ b/src/main/java/gregtech/api/capability/IPropertyFluidFilter.java @@ -0,0 +1,80 @@ +package gregtech.api.capability; + +import gregtech.api.fluids.MaterialFluid; +import gregtech.api.fluids.fluidType.FluidType; +import gregtech.api.fluids.fluidType.FluidTypes; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import javax.annotation.Nonnull; + +/** + * Fluid filter based on fluid properties; i.e. temperature, fluid state, and various material flags such as acid + * and plasma. + * + * @see FluidType + * @see FluidTypes + * @see MaterialFluid + */ +public interface IPropertyFluidFilter extends IFilter { + + /** + * Minimum temperature of the fluid in kelvin before it starts being considered 'cryogenic'; if a fluid has lower + * temperature than this, it's considered cryogenic. + */ + int CRYOGENIC_TEMPERATURE_THRESHOLD = 120; + + @Override + default boolean test(@Nonnull FluidStack stack) { + Fluid fluid = stack.getFluid(); + if (fluid.getTemperature() < CRYOGENIC_TEMPERATURE_THRESHOLD && !isCryoProof()) return false; + if (fluid.isGaseous() && !isGasProof()) return false; + + if (fluid instanceof MaterialFluid materialFluid) { + FluidType fluidType = materialFluid.getFluidType(); + if (fluidType == FluidTypes.ACID) { + if (!isAcidProof()) return false; + } else if (fluidType == FluidTypes.PLASMA) { + return isPlasmaProof(); // bypass max temperature check below + } + } + return fluid.getTemperature() <= getMaxFluidTemperature(); + } + + @Override + default int getPriority() { + return IFilter.blacklistLikePriority(); + } + + /** + * This is always checked, regardless of the contained fluid being a {@link MaterialFluid} or not + * + * @return the maximum allowed temperature for a fluid + */ + int getMaxFluidTemperature(); + + /** + * This is always checked, regardless of the contained fluid being a {@link MaterialFluid} or not + * + * @return whether this filter allows gases + */ + boolean isGasProof(); + + /** + * @return whether this filter allows acids + * @see FluidTypes + */ + boolean isAcidProof(); + + /** + * @return whether this filter allows cryogenic fluids + * @see FluidTypes + */ + boolean isCryoProof(); + + /** + * @return whether this filter allows plasmas + * @see FluidTypes + */ + boolean isPlasmaProof(); +} diff --git a/src/main/java/gregtech/api/capability/IThermalFluidHandlerItemStack.java b/src/main/java/gregtech/api/capability/IThermalFluidHandlerItemStack.java index d3e005d592f..95eec218209 100644 --- a/src/main/java/gregtech/api/capability/IThermalFluidHandlerItemStack.java +++ b/src/main/java/gregtech/api/capability/IThermalFluidHandlerItemStack.java @@ -6,20 +6,24 @@ import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; +import javax.annotation.Nullable; + /** * Interface for FluidHandlerItemStacks which handle GT's unique fluid mechanics + * * @see FluidType * @see FluidTypes * @see MaterialFluid + * @deprecated use {@link IPropertyFluidFilter} */ +@Deprecated public interface IThermalFluidHandlerItemStack { /** - * * @param stack the {@link FluidStack} to check * @return whether the FluidStack can be used to fill this fluid container */ - default boolean canFillFluidType(FluidStack stack) { + default boolean canFillFluidType(@Nullable FluidStack stack) { if (stack == null || stack.getFluid() == null) return false; Fluid fluid = stack.getFluid(); @@ -51,23 +55,20 @@ default boolean canFillFluidType(FluidStack stack) { boolean isGasProof(); /** - * @see FluidTypes - * * @return true if this fluid container allows acids, otherwise false + * @see FluidTypes */ boolean isAcidProof(); /** - * @see FluidTypes - * * @return true if this fluid container allows cryogenics, otherwise false + * @see FluidTypes */ boolean isCryoProof(); /** - * @see FluidTypes - * * @return true if this fluid container allows plasmas, otherwise false + * @see FluidTypes */ boolean isPlasmaProof(); } diff --git a/src/main/java/gregtech/api/capability/impl/BoilerRecipeLogic.java b/src/main/java/gregtech/api/capability/impl/BoilerRecipeLogic.java index 3f99bdc1f8f..e799e5e913c 100644 --- a/src/main/java/gregtech/api/capability/impl/BoilerRecipeLogic.java +++ b/src/main/java/gregtech/api/capability/impl/BoilerRecipeLogic.java @@ -3,7 +3,6 @@ import gregtech.api.GTValues; import gregtech.api.capability.IMultiblockController; import gregtech.api.capability.IMultipleTankHandler; -import gregtech.api.recipes.ModHandler; import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeMaps; import gregtech.api.unification.material.Materials; @@ -13,6 +12,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.PacketBuffer; +import net.minecraft.tileentity.TileEntityFurnace; import net.minecraft.util.NonNullList; import net.minecraftforge.fluids.*; import net.minecraftforge.fluids.capability.IFluidHandler; @@ -72,7 +72,7 @@ protected void trySearchNewRecipe() { for (IFluidTank fluidTank : importFluids.getFluidTanks()) { FluidStack fuelStack = fluidTank.drain(Integer.MAX_VALUE, false); - if (fuelStack == null || ModHandler.isWater(fuelStack)) continue; + if (fuelStack == null || CommonFluidFilters.BOILER_FLUID.test(fuelStack)) continue; Recipe dieselRecipe = RecipeMaps.COMBUSTION_GENERATOR_FUELS.findRecipe( GTValues.V[GTValues.MAX], dummyList, Collections.singletonList(fuelStack)); @@ -101,7 +101,7 @@ protected void trySearchNewRecipe() { IItemHandlerModifiable importItems = boiler.getImportItems(); for (int i = 0; i < importItems.getSlots(); i++) { ItemStack stack = importItems.getStackInSlot(i); - int fuelBurnTime = (int) Math.ceil(ModHandler.getFuelValue(stack)); + int fuelBurnTime = (int) Math.ceil(TileEntityFurnace.getItemBurnTime(stack)); if (fuelBurnTime / 80 > 0) { // try to ensure this fuel can burn for at least 1 tick if (FluidUtil.getFluidHandler(stack) != null) continue; this.excessFuel += fuelBurnTime % 80; @@ -143,7 +143,7 @@ protected void updateRecipeProgress() { getMetaTileEntity().explodeMultiblock((currentHeat / getMaximumHeat()) * 8); } else { setLastTickSteam(generatedSteam); - getOutputTank().fill(ModHandler.getSteam(generatedSteam), true); + getOutputTank().fill(Materials.Steam.getFluid(generatedSteam), true); } } if (currentHeat < getMaximumHeat()) { diff --git a/src/main/java/gregtech/api/capability/impl/CommonFluidFilters.java b/src/main/java/gregtech/api/capability/impl/CommonFluidFilters.java new file mode 100644 index 00000000000..8a2b633df16 --- /dev/null +++ b/src/main/java/gregtech/api/capability/impl/CommonFluidFilters.java @@ -0,0 +1,114 @@ +package gregtech.api.capability.impl; + +import gregtech.api.capability.IFilter; +import gregtech.api.unification.material.Material; +import gregtech.api.unification.material.Materials; +import gregtech.common.ConfigHolder; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; + +import javax.annotation.Nonnull; + +/** + * Common fluid filter implementations. + */ +public enum CommonFluidFilters implements IFilter { + ALLOW_ALL { + @Override + public boolean test(@Nonnull FluidStack fluid) { + return true; + } + + @Override + public int getPriority() { + return IFilter.noPriority(); + } + + @Override + public IFilter negate() { + return DISALLOW_ALL; + } + }, + DISALLOW_ALL { + @Override + public boolean test(@Nonnull FluidStack fluid) { + return false; + } + + @Override + public int getPriority() { + return IFilter.noPriority(); + } + + @Override + public IFilter negate() { + return ALLOW_ALL; + } + }, + BOILER_FLUID { + @Override + public boolean test(@Nonnull FluidStack fluid) { + if (matchesFluid(fluid, FluidRegistry.WATER) || matchesFluid(fluid, Materials.DistilledWater)) { + return true; + } + + for (String fluidName : ConfigHolder.machines.boilerFluids) { + Fluid boilerFluid = FluidRegistry.getFluid(fluidName); + if (boilerFluid != null && matchesFluid(fluid, boilerFluid)) { + return true; + } + } + return false; + } + + @Override + public int getPriority() { + return IFilter.whitelistLikePriority(); + } + }, + STEAM { + @Override + public boolean test(@Nonnull FluidStack fluid) { + return matchesFluid(fluid, Materials.Steam); + } + + @Override + public int getPriority() { + return IFilter.whitelistPriority(1); + } + }, + LIGHTER_FUEL { + @Override + public boolean test(@Nonnull FluidStack fluidStack) { + return matchesFluid(fluidStack, Materials.Butane) || matchesFluid(fluidStack, Materials.Propane); + } + + @Override + public int getPriority() { + return IFilter.whitelistPriority(2); + } + }; + + /** + * Comparison logic identical to {@link FluidStack#isFluidEqual}, without instantiation of FluidStack instance + * + * @param fluidStack fluid stack + * @param fluidMaterial material with fluid + * @return whether the fluid in fluid stack and fluid associated with the material are equal + */ + public static boolean matchesFluid(@Nonnull FluidStack fluidStack, @Nonnull Material fluidMaterial) { + return fluidStack.tag == null && fluidStack.getFluid() == fluidMaterial.getFluid(); + } + + /** + * Comparison logic identical to {@link FluidStack#isFluidEqual}, without instantiation of FluidStack instance + * + * @param fluidStack fluid stack + * @param fluid fluid + * @return whether the fluid in fluid stack and fluid parameter are equal + */ + public static boolean matchesFluid(@Nonnull FluidStack fluidStack, @Nonnull Fluid fluid) { + return fluidStack.tag == null && fluidStack.getFluid() == fluid; + } +} diff --git a/src/main/java/gregtech/api/capability/impl/FilteredFluidHandler.java b/src/main/java/gregtech/api/capability/impl/FilteredFluidHandler.java index ab87ac7dcba..702cf794578 100644 --- a/src/main/java/gregtech/api/capability/impl/FilteredFluidHandler.java +++ b/src/main/java/gregtech/api/capability/impl/FilteredFluidHandler.java @@ -1,15 +1,19 @@ package gregtech.api.capability.impl; +import gregtech.api.capability.IFilter; +import gregtech.api.capability.IFilteredFluidContainer; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidTank; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.function.Predicate; -public class FilteredFluidHandler extends FluidTank { +public class FilteredFluidHandler extends FluidTank implements IFilteredFluidContainer { - private Predicate fillPredicate; + @Nullable + private IFilter filter; public FilteredFluidHandler(int capacity) { super(capacity); @@ -23,13 +27,34 @@ public FilteredFluidHandler(Fluid fluid, int amount, int capacity) { super(fluid, amount, capacity); } - public FilteredFluidHandler setFillPredicate(Predicate predicate) { - this.fillPredicate = predicate; + @Nullable + @Override + public IFilter getFilter() { + return this.filter; + } + + /** + * Set filter instance. If {@code null} is given, then the filter is set to be + * + * @param filter new filter instance + * @return this + */ + @Nonnull + public FilteredFluidHandler setFilter(@Nullable IFilter filter) { + this.filter = filter; return this; } + /** + * @deprecated Use {@link #setFilter(IFilter)} with new filter API. + */ + @Deprecated + public FilteredFluidHandler setFillPredicate(Predicate predicate) { + return setFilter(predicate::test); + } + @Override public boolean canFillFluidType(FluidStack fluid) { - return canFill() && (fillPredicate == null || fillPredicate.test(fluid)); + return canFill() && (this.filter == null || this.filter.test(fluid)); } } diff --git a/src/main/java/gregtech/api/capability/impl/FluidTankList.java b/src/main/java/gregtech/api/capability/impl/FluidTankList.java index d8eeff3d058..79425f9731a 100644 --- a/src/main/java/gregtech/api/capability/impl/FluidTankList.java +++ b/src/main/java/gregtech/api/capability/impl/FluidTankList.java @@ -1,130 +1,149 @@ package gregtech.api.capability.impl; import gregtech.api.capability.IMultipleTankHandler; -import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.INBTSerializable; import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.FluidTank; import net.minecraftforge.fluids.IFluidTank; -import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fluids.capability.IFluidTankProperties; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; -/** - * Recommended to use this with {@link NotifiableFluidTankFromList} to ensure - * proper behavior of the "allowSameFluidFill" setting, but not required. - */ -public class FluidTankList implements IFluidHandler, IMultipleTankHandler, INBTSerializable { +public class FluidTankList implements IMultipleTankHandler, INBTSerializable { - protected final List fluidTanks; - protected IFluidTankProperties[] properties; + private final MultiFluidTankEntry[] fluidTanks; private final boolean allowSameFluidFill; + private IFluidTankProperties[] fluidTankProperties; - private final int hashCode; public FluidTankList(boolean allowSameFluidFill, IFluidTank... fluidTanks) { - this.fluidTanks = Arrays.asList(fluidTanks); + ArrayList list = new ArrayList<>(); + for (IFluidTank tank : fluidTanks) list.add(wrapIntoEntry(tank)); + this.fluidTanks = list.toArray(new MultiFluidTankEntry[0]); this.allowSameFluidFill = allowSameFluidFill; - this.hashCode = Arrays.hashCode(fluidTanks); } - public FluidTankList(boolean allowSameFluidFill, List fluidTanks) { - this.fluidTanks = new ArrayList<>(fluidTanks); + public FluidTankList(boolean allowSameFluidFill, @Nonnull List fluidTanks) { + ArrayList list = new ArrayList<>(); + for (IFluidTank tank : fluidTanks) list.add(wrapIntoEntry(tank)); + this.fluidTanks = list.toArray(new MultiFluidTankEntry[0]); this.allowSameFluidFill = allowSameFluidFill; - this.hashCode = Arrays.hashCode(fluidTanks.toArray()); } - public FluidTankList(boolean allowSameFluidFill, FluidTankList parent, IFluidTank... additionalTanks) { - this.fluidTanks = new ArrayList<>(); - this.fluidTanks.addAll(parent.fluidTanks); - this.fluidTanks.addAll(Arrays.asList(additionalTanks)); + public FluidTankList(boolean allowSameFluidFill, @Nonnull IMultipleTankHandler parent, IFluidTank... additionalTanks) { + ArrayList list = new ArrayList<>(parent.getFluidTanks()); + for (IFluidTank tank : additionalTanks) list.add(wrapIntoEntry(tank)); + this.fluidTanks = list.toArray(new MultiFluidTankEntry[0]); this.allowSameFluidFill = allowSameFluidFill; - int hash = Objects.hash(parent); - hash = 31 * hash + Arrays.hashCode(additionalTanks); - this.hashCode = hash; } - @Override - public int hashCode() { - return hashCode; - } - - public List getFluidTanks() { - return Collections.unmodifiableList(fluidTanks); + private MultiFluidTankEntry wrapIntoEntry(IFluidTank tank) { + return tank instanceof MultiFluidTankEntry entry ? entry : new MultiFluidTankEntry(this, tank); } @Nonnull @Override - public Iterator iterator() { - return getFluidTanks().iterator(); + public List getFluidTanks() { + return Collections.unmodifiableList(Arrays.asList(fluidTanks)); } @Override public int getTanks() { - return fluidTanks.size(); + return fluidTanks.length; } + @Nonnull @Override - public IFluidTank getTankAt(int index) { - return fluidTanks.get(index); + public MultiFluidTankEntry getTankAt(int index) { + return fluidTanks[index]; } + @Nonnull @Override public IFluidTankProperties[] getTankProperties() { if (fluidTankProperties == null) { ArrayList propertiesList = new ArrayList<>(); - for (IFluidTank fluidTank : fluidTanks) { - if (fluidTank instanceof IFluidHandler) { - IFluidHandler fluidHandler = (IFluidHandler) fluidTank; - propertiesList.addAll(Arrays.asList(fluidHandler.getTankProperties())); - } + for (MultiFluidTankEntry fluidTank : fluidTanks) { + Collections.addAll(propertiesList, fluidTank.getTankProperties()); } this.fluidTankProperties = propertiesList.toArray(new IFluidTankProperties[0]); } return fluidTankProperties; } + @Override + public boolean allowSameFluidFill() { + return allowSameFluidFill; + } + @Override public int fill(FluidStack resource, boolean doFill) { if (resource == null || resource.amount <= 0) { return 0; } - return fillTanksImpl(resource.copy(), doFill); - } - - //fills exactly one tank if multi-filling is not allowed - //and as much tanks as possible otherwise - //note that it will always try to fill tanks with same fluid first - private int fillTanksImpl(FluidStack resource, boolean doFill) { - int totalFilled = 0; - //first, try to fill tanks that already have same fluid type - for (IFluidTank handler : fluidTanks) { - if (resource.isFluidEqual(handler.getFluid())) { - int filledAmount = handler.fill(resource, doFill); - totalFilled += filledAmount; - resource.amount -= filledAmount; - //if filling multiple tanks is not allowed, or resource is empty, return now - if (!allowSameFluidFill() || resource.amount == 0) - return totalFilled; + int totalInserted = 0; + boolean inputFluidCopied = false; + // flag value indicating whether the fluid was stored in 'distinct' slot at least once + boolean distinctSlotVisited = false; + + MultiFluidTankEntry[] fluidTanks = this.fluidTanks.clone(); + Arrays.sort(fluidTanks, IMultipleTankHandler.ENTRY_COMPARATOR); + + // search for tanks with same fluid type first + for (MultiFluidTankEntry tank : fluidTanks) { + // if the fluid to insert matches the tank, insert the fluid + if (resource.isFluidEqual(tank.getFluid())) { + int inserted = tank.fill(resource, doFill); + if (inserted > 0) { + totalInserted += inserted; + if (resource.amount - inserted <= 0) { + return totalInserted; + } + if (!inputFluidCopied) { + inputFluidCopied = true; + resource = resource.copy(); + } + resource.amount -= inserted; + } + // regardless of whether the insertion succeeded, presence of identical fluid in + // a slot prevents distinct fill to other slots + if (!tank.allowSameFluidFill()) { + distinctSlotVisited = true; + } } } - //otherwise, try to fill empty tanks - for (IFluidTank handler : fluidTanks) { - if (handler.getFluidAmount() == 0) { - int filledAmount = handler.fill(resource, doFill); - totalFilled += filledAmount; - resource.amount -= filledAmount; - if (!allowSameFluidFill() || resource.amount == 0) - return totalFilled; + // if we still have fluid to insert, loop through empty tanks until we find one that can accept the fluid + for (MultiFluidTankEntry tank : fluidTanks) { + // if the tank uses distinct fluid fill (allowSameFluidFill disabled) and another distinct tank had + // received the fluid, skip this tank + boolean usesDistinctFluidFill = tank.allowSameFluidFill(); + if ((usesDistinctFluidFill || !distinctSlotVisited) && tank.getFluidAmount() == 0) { + int inserted = tank.fill(resource, doFill); + if (inserted > 0) { + totalInserted += inserted; + if (resource.amount - inserted <= 0) { + return totalInserted; + } + if (!inputFluidCopied) { + inputFluidCopied = true; + resource = resource.copy(); + } + resource.amount -= inserted; + if (!usesDistinctFluidFill) { + distinctSlotVisited = true; + } + } } } - return totalFilled; + // return the amount of fluid that was inserted + return totalInserted; } @Nullable @@ -133,22 +152,24 @@ public FluidStack drain(FluidStack resource, boolean doDrain) { if (resource == null || resource.amount <= 0) { return null; } - resource = resource.copy(); + int amountLeft = resource.amount; FluidStack totalDrained = null; for (IFluidTank handler : fluidTanks) { if (!resource.isFluidEqual(handler.getFluid())) { continue; } - FluidStack drain = handler.drain(resource.amount, doDrain); - if (drain == null) { - continue; + FluidStack drain = handler.drain(amountLeft, doDrain); + if (drain != null) { + if (totalDrained == null) { + totalDrained = drain; + } else { + totalDrained.amount += drain.amount; + } + amountLeft -= drain.amount; + if (amountLeft <= 0) { + return totalDrained; + } } - if (totalDrained == null) { - totalDrained = drain; - } else totalDrained.amount += drain.amount; - - resource.amount -= drain.amount; - if (resource.amount == 0) break; } return totalDrained; } @@ -156,26 +177,29 @@ public FluidStack drain(FluidStack resource, boolean doDrain) { @Nullable @Override public FluidStack drain(int maxDrain, boolean doDrain) { - if (maxDrain == 0) { + if (maxDrain <= 0) { return null; } FluidStack totalDrained = null; for (IFluidTank handler : fluidTanks) { if (totalDrained == null) { totalDrained = handler.drain(maxDrain, doDrain); - if (totalDrained != null) + if (totalDrained != null) { maxDrain -= totalDrained.amount; + } } else { - FluidStack copy = totalDrained.copy(); - copy.amount = maxDrain; - if (!copy.isFluidEqual(handler.getFluid())) continue; - FluidStack drain = handler.drain(copy.amount, doDrain); + if (!totalDrained.isFluidEqual(handler.getFluid())) { + continue; + } + FluidStack drain = handler.drain(maxDrain, doDrain); if (drain != null) { totalDrained.amount += drain.amount; maxDrain -= drain.amount; } } - if (maxDrain <= 0) break; + if (maxDrain <= 0) { + return totalDrained; + } } return totalDrained; } @@ -183,19 +207,9 @@ public FluidStack drain(int maxDrain, boolean doDrain) { @Override public NBTTagCompound serializeNBT() { NBTTagCompound fluidInventory = new NBTTagCompound(); - fluidInventory.setInteger("TankAmount", this.getTanks()); - NBTTagList tanks = new NBTTagList(); for (int i = 0; i < this.getTanks(); i++) { - NBTBase writeTag; - IFluidTank fluidTank = fluidTanks.get(i); - if (fluidTank instanceof FluidTank) { - writeTag = ((FluidTank) fluidTank).writeToNBT(new NBTTagCompound()); - } else if (fluidTank instanceof INBTSerializable) { - writeTag = ((INBTSerializable) fluidTank).serializeNBT(); - } else writeTag = new NBTTagCompound(); - - tanks.appendTag(writeTag); + tanks.appendTag(this.fluidTanks[i].trySerialize()); } fluidInventory.setTag("Tanks", tanks); return fluidInventory; @@ -204,35 +218,31 @@ public NBTTagCompound serializeNBT() { @Override public void deserializeNBT(NBTTagCompound nbt) { NBTTagList tanks = nbt.getTagList("Tanks", Constants.NBT.TAG_COMPOUND); - for (int i = 0; i < Math.min(fluidTanks.size(), nbt.getInteger("TankAmount")); i++) { - NBTBase nbtTag = tanks.get(i); - IFluidTank fluidTank = fluidTanks.get(i); - if (fluidTank instanceof FluidTank) { - ((FluidTank) fluidTank).readFromNBT((NBTTagCompound) nbtTag); - } else if (fluidTank instanceof INBTSerializable) { - ((INBTSerializable) fluidTank).deserializeNBT(nbtTag); - } + for (int i = 0; i < Math.min(fluidTanks.length, tanks.tagCount()); i++) { + this.fluidTanks[i].tryDeserialize(tanks.getCompoundTagAt(i)); } } - protected void validateTankIndex(int tank) { - if (tank < 0 || tank >= fluidTanks.size()) - throw new RuntimeException("Tank " + tank + " not in valid range - (0," + fluidTanks.size() + "]"); + @Override + public String toString() { + return toString(false); } - @Override - public int getIndexOfFluid(FluidStack fluidStack) { - for (int i = 0; i < fluidTanks.size(); i++) { - FluidStack tankStack = fluidTanks.get(i).getFluid(); - if (tankStack != null && tankStack.isFluidEqual(fluidStack)) { - return i; + public String toString(boolean lineBreak) { + StringBuilder stb = new StringBuilder("FluidTankList[").append(this.fluidTanks.length).append(";"); + for (int i = 0; i < this.fluidTanks.length; i++) { + if (i != 0) stb.append(','); + stb.append(lineBreak ? "\n " : " "); + + FluidStack fluid = this.fluidTanks[i].getFluid(); + if (fluid == null || fluid.amount == 0) { + stb.append("None 0 / ").append(this.fluidTanks[i].getCapacity()); + } else { + stb.append(fluid.getFluid().getName()).append(' ').append(fluid.amount) + .append(" / ").append(this.fluidTanks[i].getCapacity()); } } - return -1; - } - - @Override - public boolean allowSameFluidFill() { - return allowSameFluidFill; + if (lineBreak) stb.append('\n'); + return stb.append(']').toString(); } } diff --git a/src/main/java/gregtech/api/capability/impl/GTFluidHandlerItemStack.java b/src/main/java/gregtech/api/capability/impl/GTFluidHandlerItemStack.java index 2562acabc2c..dabb85647cb 100644 --- a/src/main/java/gregtech/api/capability/impl/GTFluidHandlerItemStack.java +++ b/src/main/java/gregtech/api/capability/impl/GTFluidHandlerItemStack.java @@ -1,12 +1,25 @@ package gregtech.api.capability.impl; +import gregtech.api.capability.IFilter; +import gregtech.api.capability.IFilteredFluidContainer; import net.minecraft.item.ItemStack; import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.IFluidTankProperties; import net.minecraftforge.fluids.capability.templates.FluidHandlerItemStack; import javax.annotation.Nonnull; +import javax.annotation.Nullable; -public class GTFluidHandlerItemStack extends FluidHandlerItemStack { +public class GTFluidHandlerItemStack extends FluidHandlerItemStack implements IFilteredFluidContainer { + + @Nullable + private IFilter filter; + + private boolean canFill = true; + private boolean canDrain = true; + + @Nullable + private IFluidTankProperties[] properties; /** * @param container The container itemStack, data is stored on it directly as NBT. @@ -16,6 +29,44 @@ public GTFluidHandlerItemStack(@Nonnull ItemStack container, int capacity) { super(container, capacity); } + @Nullable + @Override + public IFilter getFilter() { + return this.filter; + } + + @Nonnull + public GTFluidHandlerItemStack setFilter(@Nullable IFilter filter) { + this.filter = filter; + return this; + } + + public boolean canFill() { + return canFill; + } + + public GTFluidHandlerItemStack setCanFill(boolean canFill) { + this.canFill = canFill; + return this; + } + + public boolean canDrain() { + return canDrain; + } + + public GTFluidHandlerItemStack setCanDrain(boolean canDrain) { + this.canDrain = canDrain; + return this; + } + + @Override + public IFluidTankProperties[] getTankProperties() { + if (properties == null) { + return properties = new IFluidTankProperties[]{new TankProperties()}; + } + return properties; + } + @Override public FluidStack drain(FluidStack resource, boolean doDrain) { FluidStack drained = super.drain(resource, doDrain); @@ -35,4 +86,48 @@ private void removeTagWhenEmpty(boolean doDrain) { this.container.setTagCompound(null); } } + + @Override + public boolean canFillFluidType(FluidStack fluid) { + return canFill() && (this.filter == null || this.filter.test(fluid)); + } + + @Override + public boolean canDrainFluidType(FluidStack fluid) { + return canDrain(); + } + + private final class TankProperties implements IFluidTankProperties { + + @Nullable + @Override + public FluidStack getContents() { + return GTFluidHandlerItemStack.this.getFluid(); + } + + @Override + public int getCapacity() { + return GTFluidHandlerItemStack.this.capacity; + } + + @Override + public boolean canFill() { + return GTFluidHandlerItemStack.this.canFill(); + } + + @Override + public boolean canDrain() { + return GTFluidHandlerItemStack.this.canDrain(); + } + + @Override + public boolean canFillFluidType(FluidStack fluidStack) { + return GTFluidHandlerItemStack.this.canFillFluidType(fluidStack); + } + + @Override + public boolean canDrainFluidType(FluidStack fluidStack) { + return GTFluidHandlerItemStack.this.canDrainFluidType(fluidStack); + } + } } diff --git a/src/main/java/gregtech/api/capability/impl/GTSimpleFluidHandlerItemStack.java b/src/main/java/gregtech/api/capability/impl/GTSimpleFluidHandlerItemStack.java index eeab6c9694d..86bb0457652 100644 --- a/src/main/java/gregtech/api/capability/impl/GTSimpleFluidHandlerItemStack.java +++ b/src/main/java/gregtech/api/capability/impl/GTSimpleFluidHandlerItemStack.java @@ -1,12 +1,26 @@ package gregtech.api.capability.impl; +import gregtech.api.capability.IFilter; +import gregtech.api.capability.IFilteredFluidContainer; import net.minecraft.item.ItemStack; import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.FluidTankProperties; +import net.minecraftforge.fluids.capability.IFluidTankProperties; import net.minecraftforge.fluids.capability.templates.FluidHandlerItemStackSimple; import javax.annotation.Nonnull; +import javax.annotation.Nullable; -public class GTSimpleFluidHandlerItemStack extends FluidHandlerItemStackSimple { +public class GTSimpleFluidHandlerItemStack extends FluidHandlerItemStackSimple implements IFilteredFluidContainer { + + @Nullable + private IFilter filter; + + private boolean canFill = true; + private boolean canDrain = true; + + @Nullable + private IFluidTankProperties[] properties; /** * @param container The container itemStack, data is stored on it directly as NBT. @@ -16,6 +30,44 @@ public GTSimpleFluidHandlerItemStack(@Nonnull ItemStack container, int capacity) super(container, capacity); } + @Nullable + @Override + public IFilter getFilter() { + return this.filter; + } + + @Nonnull + public GTSimpleFluidHandlerItemStack setFilter(@Nullable IFilter filter) { + this.filter = filter; + return this; + } + + public boolean canFill() { + return canFill; + } + + public GTSimpleFluidHandlerItemStack setCanFill(boolean canFill) { + this.canFill = canFill; + return this; + } + + public boolean canDrain() { + return canDrain; + } + + public GTSimpleFluidHandlerItemStack setCanDrain(boolean canDrain) { + this.canDrain = canDrain; + return this; + } + + @Override + public IFluidTankProperties[] getTankProperties() { + if (properties == null) { + return properties = new IFluidTankProperties[]{new GTSimpleFluidHandlerItemStack.TankProperties()}; + } + return properties; + } + @Override public FluidStack drain(FluidStack resource, boolean doDrain) { FluidStack drained = super.drain(resource, doDrain); @@ -35,4 +87,48 @@ private void removeTagWhenEmpty(boolean doDrain) { this.container.setTagCompound(null); } } + + @Override + public boolean canFillFluidType(FluidStack fluid) { + return canFill() && (this.filter == null || this.filter.test(fluid)); + } + + @Override + public boolean canDrainFluidType(FluidStack fluid) { + return canDrain(); + } + + private final class TankProperties implements IFluidTankProperties { + + @Nullable + @Override + public FluidStack getContents() { + return GTSimpleFluidHandlerItemStack.this.getFluid(); + } + + @Override + public int getCapacity() { + return GTSimpleFluidHandlerItemStack.this.capacity; + } + + @Override + public boolean canFill() { + return GTSimpleFluidHandlerItemStack.this.canFill(); + } + + @Override + public boolean canDrain() { + return GTSimpleFluidHandlerItemStack.this.canDrain(); + } + + @Override + public boolean canFillFluidType(FluidStack fluidStack) { + return GTSimpleFluidHandlerItemStack.this.canFillFluidType(fluidStack); + } + + @Override + public boolean canDrainFluidType(FluidStack fluidStack) { + return GTSimpleFluidHandlerItemStack.this.canDrainFluidType(fluidStack); + } + } } diff --git a/src/main/java/gregtech/api/capability/impl/MultiFluidFilter.java b/src/main/java/gregtech/api/capability/impl/MultiFluidFilter.java new file mode 100644 index 00000000000..0e0c4d599e0 --- /dev/null +++ b/src/main/java/gregtech/api/capability/impl/MultiFluidFilter.java @@ -0,0 +1,71 @@ +package gregtech.api.capability.impl; + +import com.google.common.collect.Iterables; +import gregtech.api.capability.IFilter; +import net.minecraftforge.fluids.FluidStack; + +import javax.annotation.Nonnull; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Basic filter with multiple fluid templates. Can be either whitelist or blacklist. + */ +public final class MultiFluidFilter implements IFilter { + + private final boolean blacklist; + private final FluidStack[] fluids; + + public MultiFluidFilter(boolean blacklist, @Nonnull FluidStack... fluids) { + this.blacklist = blacklist; + this.fluids = fluids; + } + + public MultiFluidFilter(boolean blacklist, Iterable fluids) { + this.blacklist = blacklist; + this.fluids = Iterables.toArray(fluids, FluidStack.class); + } + + @Nonnull + public List getFluids() { + return Collections.unmodifiableList(Arrays.asList(fluids)); + } + + public boolean isWhitelist() { + return !blacklist; + } + + public boolean isBlacklist() { + return blacklist; + } + + @Override + public boolean test(@Nonnull FluidStack fluidStack) { + for (FluidStack fluid : this.fluids) { + if (fluid.isFluidEqual(fluid)) return true; + } + return false; + } + + @Override + public int getPriority() { + return this.fluids.length == 0 ? IFilter.noPriority() : + this.blacklist ? IFilter.blacklistPriority(this.fluids.length) : + IFilter.whitelistPriority(this.fluids.length); + } + + @Nonnull + @Override + public IFilter negate() { + return new MultiFluidFilter(!this.blacklist, this.fluids); + } + + @Override + public String toString() { + return "MultiFluidFilter{" + + "blacklist=" + blacklist + + ", fluids=" + Arrays.toString(fluids) + + '}'; + } +} diff --git a/src/main/java/gregtech/api/capability/impl/NotifiableFluidTankFromList.java b/src/main/java/gregtech/api/capability/impl/NotifiableFluidTankFromList.java index 6ca99a05c21..19cb8c1ea5f 100644 --- a/src/main/java/gregtech/api/capability/impl/NotifiableFluidTankFromList.java +++ b/src/main/java/gregtech/api/capability/impl/NotifiableFluidTankFromList.java @@ -6,6 +6,11 @@ import java.util.function.Supplier; +/** + * @deprecated Distinct insertion logic should be handled by {@link FluidTankList}s. + * Use other fluid tank implementations. + */ +@Deprecated public abstract class NotifiableFluidTankFromList extends NotifiableFluidTank { private final int index; diff --git a/src/main/java/gregtech/api/capability/impl/PropertyFluidFilter.java b/src/main/java/gregtech/api/capability/impl/PropertyFluidFilter.java new file mode 100644 index 00000000000..4309cbfc793 --- /dev/null +++ b/src/main/java/gregtech/api/capability/impl/PropertyFluidFilter.java @@ -0,0 +1,60 @@ +package gregtech.api.capability.impl; + +import gregtech.api.capability.IPropertyFluidFilter; + +public class PropertyFluidFilter implements IPropertyFluidFilter { + + private final int maxFluidTemperature; + private final boolean gasProof; + private final boolean acidProof; + private final boolean cryoProof; + private final boolean plasmaProof; + + public PropertyFluidFilter(int maxFluidTemperature, + boolean gasProof, + boolean acidProof, + boolean cryoProof, + boolean plasmaProof) { + this.maxFluidTemperature = maxFluidTemperature; + this.gasProof = gasProof; + this.acidProof = acidProof; + this.cryoProof = cryoProof; + this.plasmaProof = plasmaProof; + } + + @Override + public int getMaxFluidTemperature() { + return this.maxFluidTemperature; + } + + @Override + public boolean isGasProof() { + return this.gasProof; + } + + @Override + public boolean isAcidProof() { + return this.acidProof; + } + + @Override + public boolean isCryoProof() { + return this.cryoProof; + } + + @Override + public boolean isPlasmaProof() { + return this.plasmaProof; + } + + @Override + public String toString() { + return "SimplePropertyFluidFilter{" + + "maxFluidTemperature=" + maxFluidTemperature + + ", gasProof=" + gasProof + + ", acidProof=" + acidProof + + ", cryoProof=" + cryoProof + + ", plasmaProof=" + plasmaProof + + '}'; + } +} diff --git a/src/main/java/gregtech/api/capability/impl/SimpleThermalFluidHandlerItemStack.java b/src/main/java/gregtech/api/capability/impl/SimpleThermalFluidHandlerItemStack.java index a0f6bbc073e..aed9d5a5e04 100644 --- a/src/main/java/gregtech/api/capability/impl/SimpleThermalFluidHandlerItemStack.java +++ b/src/main/java/gregtech/api/capability/impl/SimpleThermalFluidHandlerItemStack.java @@ -6,6 +6,10 @@ import javax.annotation.Nonnull; +/** + * @deprecated use {@link gregtech.api.capability.IFilter} API + */ +@Deprecated public class SimpleThermalFluidHandlerItemStack extends GTSimpleFluidHandlerItemStack implements IThermalFluidHandlerItemStack { public final int maxFluidTemperature; diff --git a/src/main/java/gregtech/api/capability/impl/SingleFluidFilter.java b/src/main/java/gregtech/api/capability/impl/SingleFluidFilter.java new file mode 100644 index 00000000000..f1d3a4622f8 --- /dev/null +++ b/src/main/java/gregtech/api/capability/impl/SingleFluidFilter.java @@ -0,0 +1,56 @@ +package gregtech.api.capability.impl; + +import gregtech.api.capability.IFilter; +import net.minecraftforge.fluids.FluidStack; + +import javax.annotation.Nonnull; + +/** + * Basic filter with one fluid template. Can be either whitelist or blacklist. + */ +public final class SingleFluidFilter implements IFilter { + + private final FluidStack fluid; + private final boolean blacklist; + + public SingleFluidFilter(@Nonnull FluidStack fluid, boolean blacklist) { + this.fluid = fluid; + this.blacklist = blacklist; + } + + @Nonnull + public FluidStack getFluid() { + return fluid; + } + + public boolean isWhitelist() { + return !blacklist; + } + + public boolean isBlacklist() { + return blacklist; + } + + @Override + public boolean test(@Nonnull FluidStack fluid) { + return this.fluid.isFluidEqual(fluid) != this.blacklist; + } + + @Override + public int getPriority() { + return this.blacklist ? IFilter.blacklistPriority(1) : IFilter.whitelistPriority(1); + } + + @Override + public IFilter negate() { + return new SingleFluidFilter(this.fluid, !this.blacklist); + } + + @Override + public String toString() { + return "SingleFluidFilter{" + + "fluid=" + fluid + + ", blacklist=" + blacklist + + '}'; + } +} diff --git a/src/main/java/gregtech/api/capability/impl/ThermalFluidHandlerItemStack.java b/src/main/java/gregtech/api/capability/impl/ThermalFluidHandlerItemStack.java index a812a95d558..07a85e14ef8 100644 --- a/src/main/java/gregtech/api/capability/impl/ThermalFluidHandlerItemStack.java +++ b/src/main/java/gregtech/api/capability/impl/ThermalFluidHandlerItemStack.java @@ -6,6 +6,10 @@ import javax.annotation.Nonnull; +/** + * @deprecated use {@link gregtech.api.capability.IFilter} API + */ +@Deprecated public class ThermalFluidHandlerItemStack extends GTFluidHandlerItemStack implements IThermalFluidHandlerItemStack { private final int maxFluidTemperature; diff --git a/src/main/java/gregtech/api/cover/CoverBehavior.java b/src/main/java/gregtech/api/cover/CoverBehavior.java index b934cb93240..ca2cbe365ba 100644 --- a/src/main/java/gregtech/api/cover/CoverBehavior.java +++ b/src/main/java/gregtech/api/cover/CoverBehavior.java @@ -25,6 +25,8 @@ import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.List; import java.util.function.Consumer; @@ -181,7 +183,8 @@ public EnumActionResult onSoftMalletClick(EntityPlayer playerIn, EnumHand hand, * @param defaultValue value of the capability from meta tile entity itself * @return result capability value external caller will receive */ - public T getCapability(Capability capability, T defaultValue) { + @Nullable + public T getCapability(@Nonnull Capability capability, @Nullable T defaultValue) { return defaultValue; } diff --git a/src/main/java/gregtech/api/items/metaitem/FilteredFluidStats.java b/src/main/java/gregtech/api/items/metaitem/FilteredFluidStats.java index 1f09ea0bbab..50bfeec74be 100644 --- a/src/main/java/gregtech/api/items/metaitem/FilteredFluidStats.java +++ b/src/main/java/gregtech/api/items/metaitem/FilteredFluidStats.java @@ -1,42 +1,47 @@ package gregtech.api.items.metaitem; +import gregtech.api.capability.IFilter; import gregtech.api.capability.impl.GTFluidHandlerItemStack; import gregtech.api.capability.impl.GTSimpleFluidHandlerItemStack; +import gregtech.api.capability.impl.PropertyFluidFilter; import gregtech.api.items.metaitem.stats.IItemCapabilityProvider; import gregtech.api.items.metaitem.stats.IItemComponent; import net.minecraft.item.ItemStack; import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.fluids.FluidStack; +import javax.annotation.Nullable; import java.util.function.Function; public class FilteredFluidStats implements IItemComponent, IItemCapabilityProvider { public final int capacity; public final boolean allowPartialFill; - private final Function fillPredicate; + @Nullable + public final IFilter filter; + @Deprecated public FilteredFluidStats(int capacity, boolean allowPartialFill, Function fillPredicate) { this.capacity = capacity; this.allowPartialFill = allowPartialFill; - this.fillPredicate = fillPredicate; + this.filter = fillPredicate::apply; + } + + public FilteredFluidStats(int capacity, boolean allowPartialFill, @Nullable IFilter filter) { + this.capacity = capacity; + this.allowPartialFill = allowPartialFill; + this.filter = filter; + } + + public FilteredFluidStats(int capacity, int maxFluidTemperature, boolean gasProof, boolean acidProof, + boolean cryoProof, boolean plasmaProof, boolean allowPartialFill) { + this(capacity, allowPartialFill, new PropertyFluidFilter(maxFluidTemperature, gasProof, acidProof, cryoProof, plasmaProof)); } @Override public ICapabilityProvider createProvider(ItemStack itemStack) { - if (allowPartialFill) { - return new GTFluidHandlerItemStack(itemStack, capacity) { - @Override - public boolean canFillFluidType(FluidStack fluid) { - return super.canFillFluidType(fluid) && fillPredicate.apply(fluid); - } - }; - } - return new GTSimpleFluidHandlerItemStack(itemStack, capacity) { - @Override - public boolean canFillFluidType(FluidStack fluid) { - return super.canFillFluidType(fluid) && fillPredicate.apply(fluid); - } - }; + return allowPartialFill ? + new GTFluidHandlerItemStack(itemStack, this.capacity).setFilter(this.filter) : + new GTSimpleFluidHandlerItemStack(itemStack, this.capacity).setFilter(this.filter); } } diff --git a/src/main/java/gregtech/api/items/metaitem/MetaItem.java b/src/main/java/gregtech/api/items/metaitem/MetaItem.java index e66c77ca12f..11b2c0c4e69 100644 --- a/src/main/java/gregtech/api/items/metaitem/MetaItem.java +++ b/src/main/java/gregtech/api/items/metaitem/MetaItem.java @@ -8,7 +8,8 @@ import gregtech.api.GregTechAPI; import gregtech.api.capability.GregtechCapabilities; import gregtech.api.capability.IElectricItem; -import gregtech.api.capability.IThermalFluidHandlerItemStack; +import gregtech.api.capability.IFilteredFluidContainer; +import gregtech.api.capability.IPropertyFluidFilter; import gregtech.api.capability.impl.CombinedCapabilityProvider; import gregtech.api.capability.impl.ElectricItem; import gregtech.api.gui.ModularUI; @@ -257,8 +258,7 @@ public ICapabilityProvider initCapabilities(@Nonnull ItemStack stack, @Nullable } ArrayList providers = new ArrayList<>(); for (IItemComponent itemComponent : metaValueItem.getAllStats()) { - if (itemComponent instanceof IItemCapabilityProvider) { - IItemCapabilityProvider provider = (IItemCapabilityProvider) itemComponent; + if (itemComponent instanceof IItemCapabilityProvider provider) { providers.add(provider.createProvider(stack)); } } @@ -569,15 +569,15 @@ public void addInformation(@Nonnull ItemStack itemStack, @Nullable World worldIn fluidTankProperties.getCapacity(), fluid == null ? "" : fluid.getLocalizedName())); - if (fluidHandler instanceof IThermalFluidHandlerItemStack) { - IThermalFluidHandlerItemStack thermalFluidHandler = (IThermalFluidHandlerItemStack) fluidHandler; + if (fluidHandler instanceof IFilteredFluidContainer filtered && + filtered.getFilter() instanceof IPropertyFluidFilter propertyFilter) { if (TooltipHelper.isShiftDown()) { - lines.add(I18n.format("gregtech.fluid_pipe.max_temperature", thermalFluidHandler.getMaxFluidTemperature())); - if (thermalFluidHandler.isGasProof()) lines.add(I18n.format("gregtech.fluid_pipe.gas_proof")); - if (thermalFluidHandler.isAcidProof()) lines.add(I18n.format("gregtech.fluid_pipe.acid_proof")); - if (thermalFluidHandler.isCryoProof()) lines.add(I18n.format("gregtech.fluid_pipe.cryo_proof")); - if (thermalFluidHandler.isPlasmaProof()) lines.add(I18n.format("gregtech.fluid_pipe.plasma_proof")); - } else if (thermalFluidHandler.isGasProof() || thermalFluidHandler.isAcidProof() || thermalFluidHandler.isCryoProof() || thermalFluidHandler.isPlasmaProof()) { + lines.add(I18n.format("gregtech.fluid_pipe.max_temperature", propertyFilter.getMaxFluidTemperature())); + if (propertyFilter.isGasProof()) lines.add(I18n.format("gregtech.fluid_pipe.gas_proof")); + if (propertyFilter.isAcidProof()) lines.add(I18n.format("gregtech.fluid_pipe.acid_proof")); + if (propertyFilter.isCryoProof()) lines.add(I18n.format("gregtech.fluid_pipe.cryo_proof")); + if (propertyFilter.isPlasmaProof()) lines.add(I18n.format("gregtech.fluid_pipe.plasma_proof")); + } else if (propertyFilter.isGasProof() || propertyFilter.isAcidProof() || propertyFilter.isCryoProof() || propertyFilter.isPlasmaProof()) { lines.add(I18n.format("gregtech.tooltip.fluid_pipe_hold_shift")); } } diff --git a/src/main/java/gregtech/api/items/metaitem/ThermalFluidStats.java b/src/main/java/gregtech/api/items/metaitem/ThermalFluidStats.java index 5c018d71d2e..a2ee753e777 100644 --- a/src/main/java/gregtech/api/items/metaitem/ThermalFluidStats.java +++ b/src/main/java/gregtech/api/items/metaitem/ThermalFluidStats.java @@ -1,37 +1,11 @@ package gregtech.api.items.metaitem; -import gregtech.api.capability.impl.SimpleThermalFluidHandlerItemStack; -import gregtech.api.capability.impl.ThermalFluidHandlerItemStack; -import gregtech.api.items.metaitem.stats.IItemCapabilityProvider; -import gregtech.api.items.metaitem.stats.IItemComponent; -import net.minecraft.item.ItemStack; -import net.minecraftforge.common.capabilities.ICapabilityProvider; - -public class ThermalFluidStats implements IItemComponent, IItemCapabilityProvider { - - public final int capacity; - public final int maxFluidTemperature; - private final boolean gasProof; - private final boolean acidProof; - private final boolean cryoProof; - private final boolean plasmaProof; - public final boolean allowPartialFill; - +/** + * @deprecated use {@link FilteredFluidStats#FilteredFluidStats(int, int, boolean, boolean, boolean, boolean, boolean)} + */ +@Deprecated +public class ThermalFluidStats extends FilteredFluidStats { public ThermalFluidStats(int capacity, int maxFluidTemperature, boolean gasProof, boolean acidProof, boolean cryoProof, boolean plasmaProof, boolean allowPartialFill) { - this.capacity = capacity; - this.maxFluidTemperature = maxFluidTemperature; - this.gasProof = gasProof; - this.acidProof = acidProof; - this.cryoProof = cryoProof; - this.plasmaProof = plasmaProof; - this.allowPartialFill = allowPartialFill; - } - - @Override - public ICapabilityProvider createProvider(ItemStack itemStack) { - if (allowPartialFill) { - return new ThermalFluidHandlerItemStack(itemStack, capacity, maxFluidTemperature, gasProof, acidProof, cryoProof, plasmaProof); - } - return new SimpleThermalFluidHandlerItemStack(itemStack, capacity, maxFluidTemperature, gasProof, acidProof, cryoProof, plasmaProof); + super(capacity, maxFluidTemperature, gasProof, acidProof, cryoProof, plasmaProof, allowPartialFill); } } diff --git a/src/main/java/gregtech/api/metatileentity/SteamMetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/SteamMetaTileEntity.java index 746f68aec5a..59b9d1ba1b7 100644 --- a/src/main/java/gregtech/api/metatileentity/SteamMetaTileEntity.java +++ b/src/main/java/gregtech/api/metatileentity/SteamMetaTileEntity.java @@ -6,13 +6,13 @@ import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; import gregtech.api.GTValues; +import gregtech.api.capability.impl.CommonFluidFilters; import gregtech.api.capability.impl.FilteredFluidHandler; import gregtech.api.capability.impl.FluidTankList; import gregtech.api.capability.impl.RecipeLogicSteam; import gregtech.api.gui.GuiTextures; import gregtech.api.gui.ModularUI; import gregtech.api.gui.widgets.ImageWidget; -import gregtech.api.recipes.ModHandler; import gregtech.api.recipes.RecipeMap; import gregtech.api.util.GTUtility; import gregtech.client.renderer.ICubeRenderer; @@ -23,8 +23,8 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.I18n; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.ItemStack; import net.minecraft.init.SoundEvents; +import net.minecraft.item.ItemStack; import net.minecraft.util.*; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; @@ -36,6 +36,7 @@ import javax.annotation.Nullable; import java.util.List; +import java.util.Objects; public abstract class SteamMetaTileEntity extends MetaTileEntity { @@ -113,8 +114,7 @@ protected boolean isBrickedCasing() { @Override public FluidTankList createImportFluidHandler() { - this.steamFluidTank = new FilteredFluidHandler(STEAM_CAPACITY) - .setFillPredicate(ModHandler::isSteam); + this.steamFluidTank = new FilteredFluidHandler(STEAM_CAPACITY).setFilter(CommonFluidFilters.STEAM); return new FluidTankList(false, steamFluidTank); } @@ -128,7 +128,7 @@ public ModularUI.Builder createUITemplate(EntityPlayer player) { @Override public SoundEvent getSound() { - return workableHandler.getRecipeMap().getSound(); + return Objects.requireNonNull(workableHandler.getRecipeMap()).getSound(); } @SideOnly(Side.CLIENT) diff --git a/src/main/java/gregtech/api/pipenet/tile/TileEntityPipeBase.java b/src/main/java/gregtech/api/pipenet/tile/TileEntityPipeBase.java index 23ffdfcfc9d..5113badee97 100644 --- a/src/main/java/gregtech/api/pipenet/tile/TileEntityPipeBase.java +++ b/src/main/java/gregtech/api/pipenet/tile/TileEntityPipeBase.java @@ -58,8 +58,8 @@ public void transferDataFrom(IPipeTile tileEntity) { this.pipeType = tileEntity.getPipeType(); this.paintingColor = tileEntity.getPaintingColor(); this.connections = tileEntity.getConnections(); - if (tileEntity instanceof TileEntityPipeBase) { - this.updates.putAll(((TileEntityPipeBase) tileEntity).updates); + if (tileEntity instanceof TileEntityPipeBase pipeBase) { + this.updates.putAll(pipeBase.updates); } tileEntity.getCoverableImplementation().transferDataTo(coverableImplementation); setFrameMaterial(tileEntity.getFrameMaterial()); @@ -111,9 +111,9 @@ public IPipeTile setSupportsTicking() { return this; } //create new tickable tile entity, transfer data, and replace it - IPipeTile newTile = getPipeBlock().createNewTileEntity(true); + TileEntityPipeBase newTile = getPipeBlock().createNewTileEntity(true); newTile.transferDataFrom(this); - getWorld().setTileEntity(getPos(), (TileEntity) newTile); + getWorld().setTileEntity(getPos(), newTile); return newTile; } @@ -122,7 +122,7 @@ public IPipeTile setSupportsTicking() { if (pipeBlock == null) { Block block = getBlockState().getBlock(); //noinspection unchecked - this.pipeBlock = block instanceof BlockPipe ? (BlockPipe) block : null; + this.pipeBlock = block instanceof BlockPipe blockPipe ? blockPipe : null; } return pipeBlock; } @@ -176,7 +176,9 @@ public void setConnection(EnumFacing side, boolean connected, boolean fromNeighb } TileEntity tile = getWorld().getTileEntity(getPos().offset(side)); // block connections if Pipe Types do not match - if (connected && tile instanceof IPipeTile && ((IPipeTile) tile).getPipeType().getClass() != this.getPipeType().getClass()) { + if (connected && + tile instanceof IPipeTile pipeTile && + pipeTile.getPipeType().getClass() != this.getPipeType().getClass()) { return; } connections = withSideConnection(connections, side, connected); @@ -187,8 +189,8 @@ public void setConnection(EnumFacing side, boolean connected, boolean fromNeighb }); markDirty(); - if (!fromNeighbor && tile instanceof IPipeTile) { - syncPipeConnections(side, (IPipeTile) tile); + if (!fromNeighbor && tile instanceof IPipeTile pipeTile) { + syncPipeConnections(side, pipeTile); } } } @@ -266,12 +268,10 @@ public int getVisualConnections() { float selfThickness = getPipeType().getThickness(); for (EnumFacing facing : EnumFacing.values()) { if (isConnected(facing)) { - TileEntity neighbourTile = world.getTileEntity(pos.offset(facing)); - if (neighbourTile instanceof IPipeTile) { - IPipeTile pipeTile = (IPipeTile) neighbourTile; - if (pipeTile.isConnected(facing.getOpposite()) && pipeTile.getPipeType().getThickness() < selfThickness) { - connections |= 1 << (facing.getIndex() + 6); - } + if (world.getTileEntity(pos.offset(facing)) instanceof IPipeTile pipeTile && + pipeTile.isConnected(facing.getOpposite()) && + pipeTile.getPipeType().getThickness() < selfThickness) { + connections |= 1 << (facing.getIndex() + 6); } if (getCoverableImplementation().getCoverAtSide(facing) != null) { connections |= 1 << (facing.getIndex() + 12); @@ -342,7 +342,7 @@ public void readFromNBT(@Nonnull NBTTagCompound compound) { if (compound.hasKey("PipeBlock", NBT.TAG_STRING)) { Block block = Block.REGISTRY.getObject(new ResourceLocation(compound.getString("PipeBlock"))); //noinspection unchecked - this.pipeBlock = block instanceof BlockPipe ? (BlockPipe) block : null; + this.pipeBlock = block instanceof BlockPipe blockPipe ? blockPipe : null; } this.pipeType = getPipeTypeClass().getEnumConstants()[compound.getInteger("PipeType")]; diff --git a/src/main/java/gregtech/api/recipes/ModHandler.java b/src/main/java/gregtech/api/recipes/ModHandler.java index 951fbd9a66b..34acaa150f1 100644 --- a/src/main/java/gregtech/api/recipes/ModHandler.java +++ b/src/main/java/gregtech/api/recipes/ModHandler.java @@ -3,6 +3,7 @@ import crafttweaker.mc1120.actions.ActionAddFurnaceRecipe; import crafttweaker.mc1120.furnace.MCFurnaceManager; import gregtech.api.GTValues; +import gregtech.api.capability.impl.CommonFluidFilters; import gregtech.api.items.metaitem.MetaItem; import gregtech.api.items.toolitem.IGTTool; import gregtech.api.items.toolitem.ToolHelper; @@ -37,11 +38,8 @@ import net.minecraft.item.crafting.FurnaceRecipes; import net.minecraft.item.crafting.IRecipe; import net.minecraft.item.crafting.Ingredient; -import net.minecraft.tileentity.TileEntityFurnace; import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; -import net.minecraftforge.fluids.Fluid; -import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.ModContainer; @@ -62,19 +60,8 @@ public final class ModHandler { public static final boolean ERROR_ON_INVALID_RECIPE = GTValues.isDeobfEnvironment() || !ConfigHolder.misc.ignoreErrorOrInvalidRecipes; public static boolean hasInvalidRecipe = false; - private static FluidStack WATER; - private static FluidStack DISTILLED_WATER; - private static FluidStack LAVA; - private static FluidStack STEAM; - private ModHandler() {/**/} - - public static void init() { - WATER = new FluidStack(FluidRegistry.WATER, 1); - DISTILLED_WATER = Materials.DistilledWater.getFluid(1); - LAVA = new FluidStack(FluidRegistry.LAVA, 0); - STEAM = Materials.Steam.getFluid(1); - } + private ModHandler() {} public static void postInit() { if (ERROR_ON_INVALID_RECIPE && hasInvalidRecipe) { @@ -87,52 +74,29 @@ public static void postInit() { /** * @param stack the fluid to check * @return if the fluid is a valid water fluid + * @deprecated use {@link CommonFluidFilters#BOILER_FLUID} */ + @Deprecated public static boolean isWater(@Nullable FluidStack stack) { - if (stack == null) return false; - if (WATER.isFluidEqual(stack)) return true; - if (DISTILLED_WATER.isFluidEqual(stack)) return true; - - for (String fluidName : ConfigHolder.machines.boilerFluids) { - Fluid fluid = FluidRegistry.getFluid(fluidName); - if (fluid == null) continue; - if (stack.isFluidEqual(new FluidStack(fluid, 1))) { - return true; - } - } - return false; - } - - /** - * @param stack the fluid to check - * @return if the fluid is a valid lava fluid - */ - @SuppressWarnings("unused") - public static boolean isLava(FluidStack stack) { - return LAVA.isFluidEqual(stack); - } - - /** - * @param amount the amount of fluid - * @return a FluidStack of lava - */ - @SuppressWarnings("unused") - @Nonnull - public static FluidStack getLava(int amount) { - return new FluidStack(FluidRegistry.LAVA, amount); + return stack != null && CommonFluidFilters.BOILER_FLUID.test(stack); } /** * @param stack the fluid to check * @return if the fluid is a valid steam fluid + * @deprecated use {@link CommonFluidFilters#STEAM} */ + @Deprecated public static boolean isSteam(FluidStack stack) { - return STEAM.isFluidEqual(stack); + return CommonFluidFilters.STEAM.test(stack); } /** - * Returns a Liquid Stack with given amount of Steam. + * @param amount amount of steam in mb + * @return a Liquid Stack with given amount of Steam. + * @deprecated make it yourself */ + @Deprecated public static FluidStack getSteam(int amount) { return Materials.Steam.getFluid(amount); } @@ -147,14 +111,6 @@ public static boolean isMaterialWood(@Nullable Material material) { // Furnace Smelting - /** - * @param stack the stack to check - * @return the furnace fuel value for the stack - */ - public static int getFuelValue(@Nonnull ItemStack stack) { - return TileEntityFurnace.getItemBurnTime(stack); - } - /** * @param fuelStack the stack to check * @return the remainder item for a burnt fuel in a boiler @@ -453,14 +409,13 @@ public static Object[] finalizeShapedRecipeInput(Object... recipe) { */ @Nonnull public static Object finalizeIngredient(@Nonnull Object ingredient) { - if (ingredient instanceof MetaItem.MetaValueItem) { - ingredient = ((MetaItem.MetaValueItem) ingredient).getStackForm(); - } else if (ingredient instanceof Enum) { - ingredient = ((Enum) ingredient).name(); - } else if (ingredient instanceof OrePrefix) { - ingredient = ((OrePrefix) ingredient).name(); - } else if (ingredient instanceof UnificationEntry) { - UnificationEntry entry = (UnificationEntry) ingredient; + if (ingredient instanceof MetaItem.MetaValueItem metaValueItem) { + ingredient = metaValueItem.getStackForm(); + } else if (ingredient instanceof Enum anEnum) { + ingredient = anEnum.name(); + } else if (ingredient instanceof OrePrefix orePrefix) { + ingredient = orePrefix.name(); + } else if (ingredient instanceof UnificationEntry entry) { if (ConfigHolder.misc.debug && entry.material != null && !entry.orePrefix.isIgnored(entry.material) && !entry.orePrefix.doGenerateItem(entry.material)) { logInvalidRecipe("Attempted to create recipe for invalid/missing Unification Entry " + ingredient); @@ -489,8 +444,7 @@ public static ItemMaterialInfo getRecyclingIngredients(int outputCount, @Nonnull Object2LongMap materialStacksExploded = new Object2LongOpenHashMap<>(); int itr = 0; - while (recipe[itr] instanceof String) { - String s = (String) recipe[itr]; + while (recipe[itr] instanceof String s) { for (char c : s.toCharArray()) { if (ToolHelper.getToolFromSymbol(c) != null) continue; // skip tools int count = inputCountMap.getOrDefault(c, 0); diff --git a/src/main/java/gregtech/api/recipes/logic/ParallelLogic.java b/src/main/java/gregtech/api/recipes/logic/ParallelLogic.java index 27361c08abf..23312592384 100644 --- a/src/main/java/gregtech/api/recipes/logic/ParallelLogic.java +++ b/src/main/java/gregtech/api/recipes/logic/ParallelLogic.java @@ -240,21 +240,20 @@ public static int limitParallelByFluids(@Nonnull Recipe recipe, @Nonnull Overlay int minMultiplier = 0; int maxMultiplier = multiplier; - Map recipeFluidOutputs = GTHashMaps.fromFluidCollection(recipe.getFluidOutputs()); - while (minMultiplier != maxMultiplier) { overlayedFluidHandler.reset(); int amountLeft = 0; - for (Map.Entry entry : recipeFluidOutputs.entrySet()) { + for (FluidStack fluidStack : recipe.getFluidOutputs()) { + if (fluidStack.amount <= 0) continue; // Since multiplier starts at Int.MAX, check here for integer overflow - if (entry.getValue() != 0 && multiplier > Integer.MAX_VALUE / entry.getValue()) { + if (multiplier > Integer.MAX_VALUE / fluidStack.amount) { amountLeft = Integer.MAX_VALUE; } else { - amountLeft = entry.getValue() * multiplier; + amountLeft = fluidStack.amount * multiplier; } - int inserted = overlayedFluidHandler.insertStackedFluidKey(entry.getKey(), amountLeft); + int inserted = overlayedFluidHandler.insertFluid(fluidStack, amountLeft); if (inserted > 0) { amountLeft -= inserted; } diff --git a/src/main/java/gregtech/api/unification/material/properties/FluidPipeProperties.java b/src/main/java/gregtech/api/unification/material/properties/FluidPipeProperties.java index 56aed87e0c6..3eb852dc427 100644 --- a/src/main/java/gregtech/api/unification/material/properties/FluidPipeProperties.java +++ b/src/main/java/gregtech/api/unification/material/properties/FluidPipeProperties.java @@ -1,8 +1,10 @@ package gregtech.api.unification.material.properties; +import gregtech.api.capability.IPropertyFluidFilter; + import java.util.Objects; -public class FluidPipeProperties implements IMaterialProperty { +public class FluidPipeProperties implements IMaterialProperty, IPropertyFluidFilter { private int throughput; private final int tanks; @@ -62,6 +64,7 @@ public void setThroughput(int throughput) { this.throughput = throughput; } + @Override public int getMaxFluidTemperature() { return maxFluidTemperature; } @@ -70,6 +73,7 @@ public void setMaxFluidTemperature(int maxFluidTemperature) { this.maxFluidTemperature = maxFluidTemperature; } + @Override public boolean isGasProof() { return gasProof; } @@ -78,6 +82,7 @@ public void setGasProof(boolean gasProof) { this.gasProof = gasProof; } + @Override public boolean isAcidProof() { return acidProof; } @@ -86,6 +91,7 @@ public void setAcidProof(boolean acidProof) { this.acidProof = acidProof; } + @Override public boolean isCryoProof() { return cryoProof; } @@ -94,6 +100,7 @@ public void setCryoProof(boolean cryoProof) { this.cryoProof = cryoProof; } + @Override public boolean isPlasmaProof() { return plasmaProof; } @@ -105,27 +112,31 @@ public void setPlasmaProof(boolean plasmaProof) { @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof FluidPipeProperties)) return false; - FluidPipeProperties that = (FluidPipeProperties) o; - return maxFluidTemperature == that.maxFluidTemperature && - throughput == that.throughput && gasProof == that.gasProof && tanks == that.tanks; + if (!(o instanceof FluidPipeProperties that)) return false; + return getThroughput() == that.getThroughput() && + getTanks() == that.getTanks() && + getMaxFluidTemperature() == that.getMaxFluidTemperature() && + isGasProof() == that.isGasProof() && + isAcidProof() == that.isAcidProof() && + isCryoProof() == that.isCryoProof() && + isPlasmaProof() == that.isPlasmaProof(); } @Override public int hashCode() { - return Objects.hash(maxFluidTemperature, throughput, gasProof, tanks); + return Objects.hash(getThroughput(), getTanks(), getMaxFluidTemperature(), isGasProof(), isAcidProof(), isCryoProof(), isPlasmaProof()); } @Override public String toString() { return "FluidPipeProperties{" + - "maxFluidTemperature=" + maxFluidTemperature + - ", throughput=" + throughput + + "throughput=" + throughput + + ", tanks=" + tanks + + ", maxFluidTemperature=" + maxFluidTemperature + ", gasProof=" + gasProof + ", acidProof=" + acidProof + ", cryoProof=" + cryoProof + ", plasmaProof=" + plasmaProof + - ", tanks=" + tanks + '}'; } } diff --git a/src/main/java/gregtech/api/util/GTTransferUtils.java b/src/main/java/gregtech/api/util/GTTransferUtils.java index c1b4f24d992..eec4eefdeda 100644 --- a/src/main/java/gregtech/api/util/GTTransferUtils.java +++ b/src/main/java/gregtech/api/util/GTTransferUtils.java @@ -1,7 +1,6 @@ package gregtech.api.util; import gregtech.api.capability.IMultipleTankHandler; -import gregtech.api.recipes.FluidKey; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.objects.Object2IntMap; @@ -17,7 +16,6 @@ import javax.annotation.Nonnull; import java.util.List; -import java.util.Map; import java.util.function.Predicate; public class GTTransferUtils { @@ -148,18 +146,18 @@ public static boolean addFluidsToFluidHandler(IMultipleTankHandler fluidHandler, List fluidStacks) { if (simulate) { OverlayedFluidHandler overlayedFluidHandler = new OverlayedFluidHandler(fluidHandler); - Map fluidKeyMap = GTHashMaps.fromFluidCollection(fluidStacks); - for (Map.Entry entry : fluidKeyMap.entrySet()) { - int amountToInsert = entry.getValue(); - int inserted = overlayedFluidHandler.insertStackedFluidKey(entry.getKey(), amountToInsert); - if (inserted != amountToInsert) { + for (FluidStack fluidStack : fluidStacks) { + int inserted = overlayedFluidHandler.insertFluid(fluidStack, fluidStack.amount); + if (inserted != fluidStack.amount) { return false; } } return true; } - fluidStacks.forEach(fluidStack -> fluidHandler.fill(fluidStack, true)); + for (FluidStack fluidStack : fluidStacks) { + fluidHandler.fill(fluidStack, true); + } return true; } diff --git a/src/main/java/gregtech/api/util/GTUtility.java b/src/main/java/gregtech/api/util/GTUtility.java index 4d21aee5643..337ffe5ff58 100644 --- a/src/main/java/gregtech/api/util/GTUtility.java +++ b/src/main/java/gregtech/api/util/GTUtility.java @@ -356,15 +356,15 @@ public int size() { * modifications in list will reflect on fluid handler and wise-versa */ public static List fluidHandlerToList(IMultipleTankHandler fluidInputs) { - List backedList = fluidInputs.getFluidTanks(); + List backedList = fluidInputs.getFluidTanks(); return new AbstractList() { @Override public FluidStack set(int index, FluidStack element) { - IFluidTank fluidTank = backedList.get(index); + IFluidTank fluidTank = backedList.get(index).getDelegate(); FluidStack oldStack = fluidTank.getFluid(); - if (!(fluidTank instanceof FluidTank)) - return oldStack; - ((FluidTank) backedList.get(index)).setFluid(element); + if (fluidTank instanceof FluidTank) { + ((FluidTank) fluidTank).setFluid(element); + } return oldStack; } diff --git a/src/main/java/gregtech/api/util/OverlayedFluidHandler.java b/src/main/java/gregtech/api/util/OverlayedFluidHandler.java index 6bb0e9b2046..309371a0286 100644 --- a/src/main/java/gregtech/api/util/OverlayedFluidHandler.java +++ b/src/main/java/gregtech/api/util/OverlayedFluidHandler.java @@ -1,185 +1,171 @@ package gregtech.api.util; import gregtech.api.capability.IMultipleTankHandler; -import gregtech.api.capability.impl.NotifiableFluidTankFromList; -import gregtech.api.recipes.FluidKey; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import gregtech.api.capability.IMultipleTankHandler.MultiFluidTankEntry; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidTankProperties; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.Map; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +/** + * Simulates consecutive fills to {@link IMultipleTankHandler} instance. + */ public class OverlayedFluidHandler { - private final OverlayedTank[] overlayedTanks; - private final OverlayedTank[] originalTanks; - private final IMultipleTankHandler overlayed; + private final List overlayedTanks; - private final ObjectOpenCustomHashSet tankDeniesSameFluidFill = new ObjectOpenCustomHashSet<>(IFluidTankPropertiesHashStrategy.create()); - private final Map> uniqueFluidMap = new Object2ObjectOpenHashMap<>(); - - public OverlayedFluidHandler(IMultipleTankHandler toOverlay) { - this.overlayedTanks = new OverlayedTank[toOverlay.getTankProperties().length]; - this.originalTanks = new OverlayedTank[toOverlay.getTankProperties().length]; - this.overlayed = toOverlay; + public OverlayedFluidHandler(@Nonnull IMultipleTankHandler tank) { + this.overlayedTanks = new ArrayList<>(); + MultiFluidTankEntry[] entries = tank.getFluidTanks().toArray(new MultiFluidTankEntry[0]); + Arrays.sort(entries, IMultipleTankHandler.ENTRY_COMPARATOR); + for (MultiFluidTankEntry fluidTank : entries) { + for (IFluidTankProperties property : fluidTank.getTankProperties()) { + this.overlayedTanks.add(new OverlayedTank(property, fluidTank.allowSameFluidFill())); + } + } } /** - * Resets the {slots} array to the state when the handler was - * first mirrored + * Resets the internal state back to the state when the handler was + * first mirrored. */ public void reset() { - for (int i = 0; i < this.originalTanks.length; i++) { - if (this.originalTanks[i] != null) { - this.overlayedTanks[i] = this.originalTanks[i].copy(); - } - } - uniqueFluidMap.forEach((k, v) -> v.clear()); - } - - public IFluidTankProperties[] getTankProperties() { - return overlayed.getTankProperties(); - } - - private void initTank(int tank) { - if (this.overlayedTanks[tank] == null) { - IFluidTankProperties fluidTankProperties = overlayed.getTankProperties()[tank]; - this.originalTanks[tank] = new OverlayedTank(fluidTankProperties); - this.overlayedTanks[tank] = new OverlayedTank(fluidTankProperties); - - if (overlayed.getTankAt(tank) instanceof NotifiableFluidTankFromList) { - NotifiableFluidTankFromList nftfl = (NotifiableFluidTankFromList) overlayed.getTankAt(tank); - if (!nftfl.getFluidTankList().get().allowSameFluidFill()) { - this.tankDeniesSameFluidFill.add(overlayed.getTankProperties()[tank]); - } - } + for (OverlayedTank overlayedTank : this.overlayedTanks) { + overlayedTank.reset(); } } - public int insertStackedFluidKey(@Nonnull FluidKey toInsert, int amountToInsert) { + /** + * Simulate fluid insertion to the fluid tanks. + * + * @param fluid Fluid + * @param amountToInsert Amount of the fluid to insert + * @return Amount of fluid inserted into tanks + */ + public int insertFluid(@Nonnull FluidStack fluid, int amountToInsert) { if (amountToInsert <= 0) { return 0; } - int insertedAmount = 0; + int totalInserted = 0; + // flag value indicating whether the fluid was stored in 'distinct' slot at least once + boolean distinctFillPerformed = false; + // search for tanks with same fluid type first - for (int i = 0; i < this.overlayedTanks.length; i++) { - // initialize the tanks if they are not already populated - initTank(i); - OverlayedTank overlayedTank = this.overlayedTanks[i]; - // if the fluid key matches the tank, insert the fluid - if (toInsert.equals(overlayedTank.fluidKey)) { - if (!markFluidAsUnique(toInsert, i)) continue; - int canInsertUpTo = overlayedTank.tryInsert(toInsert, amountToInsert); - if (canInsertUpTo > 0) { - insertedAmount += canInsertUpTo; - amountToInsert -= canInsertUpTo; + for (OverlayedTank overlayedTank : this.overlayedTanks) { + // if the fluid to insert matches the tank, insert the fluid + if (fluid.isFluidEqual(overlayedTank.fluid)) { + int inserted = overlayedTank.tryInsert(fluid, amountToInsert); + if (inserted > 0) { + totalInserted += inserted; + amountToInsert -= inserted; if (amountToInsert <= 0) { - return insertedAmount; + return totalInserted; } } + // regardless of whether the insertion succeeded, presence of identical fluid in + // a slot prevents distinct fill to other slots + if (!overlayedTank.allowSameFluidFill) { + distinctFillPerformed = true; + } } } // if we still have fluid to insert, loop through empty tanks until we find one that can accept the fluid - for (int i = 0; i < this.overlayedTanks.length; i++) { - OverlayedTank overlayedTank = this.overlayedTanks[i]; - // if the tank is empty - if (overlayedTank.fluidKey == null) { - if (!markFluidAsUnique(toInsert, i)) continue; - //check if this tanks accepts the fluid we're simulating - if (overlayed.getTankProperties()[i].canFillFluidType(new FluidStack(toInsert.getFluid(), amountToInsert))) { - int inserted = overlayedTank.tryInsert(toInsert, amountToInsert); - if (inserted > 0) { - insertedAmount += inserted; - amountToInsert -= inserted; - if (amountToInsert <= 0) { - return insertedAmount; - } + for (OverlayedTank overlayedTank : this.overlayedTanks) { + // if the tank uses distinct fluid fill (allowSameFluidFill disabled) and another distinct tank had + // received the fluid, skip this tank + if ((!distinctFillPerformed || overlayedTank.allowSameFluidFill) && + overlayedTank.isEmpty() && + overlayedTank.property.canFillFluidType(fluid)) { + int inserted = overlayedTank.tryInsert(fluid, amountToInsert); + if (inserted > 0) { + totalInserted += inserted; + amountToInsert -= inserted; + if (amountToInsert <= 0) { + return totalInserted; + } + if (!overlayedTank.allowSameFluidFill) { + distinctFillPerformed = true; } } } } // return the amount of fluid that was inserted - return insertedAmount; + return totalInserted; } - /** - * Marks the fluid as unique, to prevent further insertions in other tank slots. - * If the fluid is already marked as unique in previous calls to this method, - * this method fails and returns {@code false}. - *

- * If {@link IMultipleTankHandler#allowSameFluidFill()} is {@code false} for - * {@link #overlayed} field or tank provider returned by {@link - * NotifiableFluidTankFromList#getFluidTankList()}, this method gets bypassed - * and {@code true} is returned without modifying any state. - * - * @param fluid the fluid to mark - * @param tankIndex the index of the tank - * @return {@code true} if the fluid is not marked as unique in previous calls, - * or the tank allows same fluids to be filled in multiple slots - */ - private boolean markFluidAsUnique(@Nonnull FluidKey fluid, int tankIndex) { - if (overlayed.getTankAt(tankIndex) instanceof NotifiableFluidTankFromList) { - if (!overlayed.allowSameFluidFill() || tankDeniesSameFluidFill.contains(overlayed.getTankProperties()[tankIndex])) { - NotifiableFluidTankFromList nftfl = (NotifiableFluidTankFromList) overlayed.getTankAt(tankIndex); - return this.uniqueFluidMap - .computeIfAbsent(nftfl.getFluidTankList().get(), t -> new ObjectOpenHashSet<>()) - .add(fluid); + @Override + public String toString() { + return toString(false); + } + + public String toString(boolean lineBreak) { + StringBuilder stb = new StringBuilder("OverlayedFluidHandler[").append(this.overlayedTanks.size()).append(";"); + if (lineBreak) stb.append("\n "); + for (int i = 0; i < this.overlayedTanks.size(); i++) { + if (i != 0) stb.append(','); + if (lineBreak) stb.append("\n "); + + OverlayedTank overlayedTank = this.overlayedTanks.get(i); + FluidStack fluid = overlayedTank.fluid; + if (fluid == null || fluid.amount == 0) { + stb.append("None 0 / ").append(overlayedTank.property.getCapacity()); + } else { + stb.append(fluid.getFluid().getName()).append(' ').append(fluid.amount) + .append(" / ").append(overlayedTank.property.getCapacity()); } - } else if (!overlayed.allowSameFluidFill()) { - return this.uniqueFluidMap - .computeIfAbsent(overlayed, t -> new ObjectOpenHashSet<>()) - .add(fluid); } - return true; + if (lineBreak) stb.append('\n'); + return stb.append(']').toString(); } private static class OverlayedTank { - private final int capacity; + private final IFluidTankProperties property; + private final boolean allowSameFluidFill; @Nullable - private FluidKey fluidKey = null; - private int fluidAmount = 0; - - OverlayedTank(IFluidTankProperties property) { - FluidStack stackToMirror = property.getContents(); - if (stackToMirror != null) { - this.fluidKey = new FluidKey(stackToMirror); - this.fluidAmount = stackToMirror.amount; - } - this.capacity = property.getCapacity(); + private FluidStack fluid; + + OverlayedTank(@Nonnull IFluidTankProperties property, boolean allowSameFluidFill) { + this.property = property; + this.allowSameFluidFill = allowSameFluidFill; + reset(); } - OverlayedTank(@Nullable FluidKey fluidKey, int fluidAmount, int capacity) { - this.fluidKey = fluidKey; - this.fluidAmount = fluidAmount; - this.capacity = capacity; + public boolean isEmpty() { + return fluid == null || fluid.amount <= 0; } /** * Tries to insert set amount of fluid into this tank. If operation succeeds, - * the fluid key of this tank will be set to {@code fluid} and the {@code - * fluidAmount} will be changed to reflect the state after insertion. + * the content of this tank will be updated. + * + * Note that this method does not check preexisting fluids for insertion. * - * @param fluid Fluid key - * @param amountToInsert Amount of the fluid to insert + * @param fluid Fluid + * @param amount Amount of the fluid to insert * @return Amount of fluid inserted into this tank */ - public int tryInsert(@Nonnull FluidKey fluid, int amountToInsert) { - int maxInsert = Math.min(this.capacity - this.fluidAmount, amountToInsert); - if (maxInsert > 0) { - this.fluidKey = fluid; - this.fluidAmount += maxInsert; - return maxInsert; - } else return 0; + public int tryInsert(@Nonnull FluidStack fluid, int amount) { + if (this.fluid == null) { + this.fluid = fluid.copy(); + return this.fluid.amount = Math.min(this.property.getCapacity(), amount); + } else { + int maxInsert = Math.min(this.property.getCapacity() - this.fluid.amount, amount); + if (maxInsert > 0) { + this.fluid.amount += maxInsert; + return maxInsert; + } else return 0; + } } - public OverlayedTank copy() { - return new OverlayedTank(this.fluidKey, this.fluidAmount, this.capacity); + public void reset() { + FluidStack fluid = this.property.getContents(); + this.fluid = fluid != null ? fluid.copy() : null; } } } diff --git a/src/main/java/gregtech/common/items/MetaItem1.java b/src/main/java/gregtech/common/items/MetaItem1.java index 290a2d40d2f..aac893968e7 100644 --- a/src/main/java/gregtech/common/items/MetaItem1.java +++ b/src/main/java/gregtech/common/items/MetaItem1.java @@ -2,6 +2,7 @@ import gregtech.api.GTValues; import gregtech.api.GregTechAPI; +import gregtech.api.capability.impl.CommonFluidFilters; import gregtech.api.items.metaitem.*; import gregtech.api.items.metaitem.stats.IItemComponent; import gregtech.api.items.metaitem.stats.IItemContainerItemProvider; @@ -121,41 +122,41 @@ public void registerSubItems() { // Fluid Cells: ID 78-88 FLUID_CELL = addItem(78, "fluid_cell") - .addComponents(new ThermalFluidStats(1000, 1800, true, false, false, false, false), new ItemFluidContainer()) + .addComponents(new FilteredFluidStats(1000, 1800, true, false, false, false, false), new ItemFluidContainer()) .setCreativeTabs(GregTechAPI.TAB_GREGTECH_TOOLS); FLUID_CELL_UNIVERSAL = addItem(79, "fluid_cell.universal") - .addComponents(new ThermalFluidStats(1000, 1800, true, false, false, false, true), new ItemFluidContainer()) + .addComponents(new FilteredFluidStats(1000, 1800, true, false, false, false, true), new ItemFluidContainer()) .setCreativeTabs(GregTechAPI.TAB_GREGTECH_TOOLS); FLUID_CELL_LARGE_STEEL = addItem(80, "large_fluid_cell.steel") - .addComponents(new ThermalFluidStats(8000, Materials.Steel.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, false, false, false, true), new ItemFluidContainer()) + .addComponents(new FilteredFluidStats(8000, Materials.Steel.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, false, false, false, true), new ItemFluidContainer()) .setMaterialInfo(new ItemMaterialInfo(new MaterialStack(Materials.Steel, M * 4))) // ingot * 4 .setCreativeTabs(GregTechAPI.TAB_GREGTECH_TOOLS); FLUID_CELL_LARGE_ALUMINIUM = addItem(81, "large_fluid_cell.aluminium") - .addComponents(new ThermalFluidStats(32000, Materials.Aluminium.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, false, false, false, true), new ItemFluidContainer()) + .addComponents(new FilteredFluidStats(32000, Materials.Aluminium.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, false, false, false, true), new ItemFluidContainer()) .setMaterialInfo(new ItemMaterialInfo(new MaterialStack(Materials.Aluminium, M * 4))) // ingot * 4 .setCreativeTabs(GregTechAPI.TAB_GREGTECH_TOOLS); FLUID_CELL_LARGE_STAINLESS_STEEL = addItem(82, "large_fluid_cell.stainless_steel") - .addComponents(new ThermalFluidStats(64000, Materials.StainlessSteel.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, true, true, false, true), new ItemFluidContainer()) + .addComponents(new FilteredFluidStats(64000, Materials.StainlessSteel.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, true, true, false, true), new ItemFluidContainer()) .setMaterialInfo(new ItemMaterialInfo(new MaterialStack(Materials.StainlessSteel, M * 6))) // ingot * 6 .setCreativeTabs(GregTechAPI.TAB_GREGTECH_TOOLS); FLUID_CELL_LARGE_TITANIUM = addItem(83, "large_fluid_cell.titanium") - .addComponents(new ThermalFluidStats(128000, Materials.Titanium.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, false, false, false, true), new ItemFluidContainer()) + .addComponents(new FilteredFluidStats(128000, Materials.Titanium.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, false, false, false, true), new ItemFluidContainer()) .setMaterialInfo(new ItemMaterialInfo(new MaterialStack(Materials.Titanium, M * 6))) // ingot * 6 .setCreativeTabs(GregTechAPI.TAB_GREGTECH_TOOLS); FLUID_CELL_LARGE_TUNGSTEN_STEEL = addItem(84, "large_fluid_cell.tungstensteel") - .addComponents(new ThermalFluidStats(512000, Materials.TungstenSteel.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, false, false, false, true), new ItemFluidContainer()) + .addComponents(new FilteredFluidStats(512000, Materials.TungstenSteel.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, false, false, false, true), new ItemFluidContainer()) .setMaxStackSize(32) .setMaterialInfo(new ItemMaterialInfo(new MaterialStack(Materials.TungstenSteel, M * 8))) // ingot * 8 .setCreativeTabs(GregTechAPI.TAB_GREGTECH_TOOLS); FLUID_CELL_GLASS_VIAL = addItem(85, "fluid_cell.glass_vial") - .addComponents(new ThermalFluidStats(1000, 1200, false, true, false, false, true), new ItemFluidContainer()) + .addComponents(new FilteredFluidStats(1000, 1200, false, true, false, false, true), new ItemFluidContainer()) .setMaterialInfo(new ItemMaterialInfo(new MaterialStack(Materials.Glass, M / 4))) // small dust .setCreativeTabs(GregTechAPI.TAB_GREGTECH_TOOLS); @@ -171,13 +172,13 @@ public void registerSubItems() { TOOL_LIGHTER_INVAR = addItem(91, "tool.lighter.invar") .setMaterialInfo(new ItemMaterialInfo(new MaterialStack(Materials.Invar, M * 2))) .addComponents(new LighterBehaviour(new ResourceLocation(GTValues.MODID, "lighter_open"), true, true, true)) - .addComponents(new FilteredFluidStats(100, true, fs -> fs.getFluid().equals(Materials.Butane.getFluid()) || fs.getFluid().equals(Materials.Propane.getFluid()))) + .addComponents(new FilteredFluidStats(100, true, CommonFluidFilters.LIGHTER_FUEL)) .setMaxStackSize(1) .setCreativeTabs(GregTechAPI.TAB_GREGTECH_TOOLS); TOOL_LIGHTER_PLATINUM = addItem(92, "tool.lighter.platinum") .setMaterialInfo(new ItemMaterialInfo(new MaterialStack(Materials.Platinum, M * 2))) .addComponents(new LighterBehaviour(new ResourceLocation(GTValues.MODID, "lighter_open"), true, true, true)) - .addComponents(new FilteredFluidStats(1000, true, fs -> fs.getFluid().equals(Materials.Butane.getFluid()) || fs.getFluid().equals(Materials.Propane.getFluid()))) + .addComponents(new FilteredFluidStats(1000, true, CommonFluidFilters.LIGHTER_FUEL)) .setMaxStackSize(1) .setRarity(EnumRarity.UNCOMMON) .setCreativeTabs(GregTechAPI.TAB_GREGTECH_TOOLS); diff --git a/src/main/java/gregtech/common/items/armor/PowerlessJetpack.java b/src/main/java/gregtech/common/items/armor/PowerlessJetpack.java index 908d4f376e7..8af4658cc18 100644 --- a/src/main/java/gregtech/common/items/armor/PowerlessJetpack.java +++ b/src/main/java/gregtech/common/items/armor/PowerlessJetpack.java @@ -1,5 +1,7 @@ package gregtech.common.items.armor; +import gregtech.api.capability.IFilter; +import gregtech.api.capability.impl.GTFluidHandlerItemStack; import gregtech.api.items.armor.ArmorMetaItem; import gregtech.api.items.armor.ArmorMetaItem.ArmorMetaValueItem; import gregtech.api.items.armor.ArmorUtils; @@ -26,10 +28,8 @@ import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; -import net.minecraftforge.fluids.capability.FluidTankProperties; import net.minecraftforge.fluids.capability.IFluidHandlerItem; import net.minecraftforge.fluids.capability.IFluidTankProperties; -import net.minecraftforge.fluids.capability.templates.FluidHandlerItemStack; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import org.apache.commons.lang3.tuple.Pair; @@ -192,7 +192,7 @@ public void findNewRecipe(@Nonnull ItemStack stack) { return; } else if (fluidStack != null) { Recipe recipe = RecipeMaps.COMBUSTION_GENERATOR_FUELS.find(Collections.emptyList(), Collections.singletonList(fluidStack), (Objects::nonNull)); - if (recipe != null) { + if (recipe != null) { previousRecipe = recipe; currentRecipe = previousRecipe; return; @@ -237,6 +237,18 @@ public int getArmorDisplay(EntityPlayer player, @Nonnull ItemStack armor, int sl public class Behaviour implements IItemDurabilityManager, IItemCapabilityProvider, IItemBehaviour, ISubItemHandler { + private static final IFilter JETPACK_FUEL_FILTER = new IFilter<>() { + @Override + public boolean test(@Nonnull FluidStack fluidStack) { + return RecipeMaps.COMBUSTION_GENERATOR_FUELS.find(Collections.emptyList(), Collections.singletonList(fluidStack), (Objects::nonNull)) != null; + } + + @Override + public int getPriority() { + return IFilter.whitelistLikePriority(); + } + }; + public final int maxCapacity; private final Pair durabilityBarColors; @@ -262,17 +274,9 @@ public Pair getDurabilityColorsForDisplay(ItemStack itemStack) { @Override public ICapabilityProvider createProvider(ItemStack itemStack) { - return new FluidHandlerItemStack(itemStack, maxCapacity) { - @Override - public boolean canFillFluidType(FluidStack fluidStack) { - return RecipeMaps.COMBUSTION_GENERATOR_FUELS.find(Collections.emptyList(), Collections.singletonList(fluidStack), (Objects::nonNull)) != null; - } - - @Override - public IFluidTankProperties[] getTankProperties() { - return new FluidTankProperties[]{new FluidTankProperties(getFluid(), capacity, true, false)}; - } - }; + return new GTFluidHandlerItemStack(itemStack, maxCapacity) + .setFilter(JETPACK_FUEL_FILTER) + .setCanDrain(false); } @Override diff --git a/src/main/java/gregtech/common/items/behaviors/FoamSprayerBehavior.java b/src/main/java/gregtech/common/items/behaviors/FoamSprayerBehavior.java index 5596c606a41..83304faf8d6 100644 --- a/src/main/java/gregtech/common/items/behaviors/FoamSprayerBehavior.java +++ b/src/main/java/gregtech/common/items/behaviors/FoamSprayerBehavior.java @@ -1,14 +1,17 @@ package gregtech.common.items.behaviors; +import gregtech.api.capability.impl.GTFluidHandlerItemStack; +import gregtech.api.capability.impl.SingleFluidFilter; import gregtech.api.items.metaitem.stats.IItemBehaviour; import gregtech.api.items.metaitem.stats.IItemCapabilityProvider; import gregtech.api.items.metaitem.stats.IItemDurabilityManager; import gregtech.api.items.metaitem.stats.ISubItemHandler; +import gregtech.api.recipes.ModHandler; import gregtech.api.unification.material.Materials; import gregtech.api.util.GradientUtil; import gregtech.common.blocks.BlockFrame; import gregtech.common.blocks.MetaBlocks; -import net.minecraft.block.material.Material; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import net.minecraft.block.state.IBlockState; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.player.EntityPlayer; @@ -22,13 +25,14 @@ import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandlerItem; import net.minecraftforge.fluids.capability.IFluidTankProperties; -import net.minecraftforge.fluids.capability.templates.FluidHandlerItemStack; import org.apache.commons.lang3.tuple.Pair; import javax.annotation.Nullable; import java.awt.*; -import java.util.*; +import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.Set; public class FoamSprayerBehavior implements IItemCapabilityProvider, IItemDurabilityManager, IItemBehaviour, ISubItemHandler { @@ -44,6 +48,10 @@ public FoamSprayerBehavior() { public ActionResult onItemUse(EntityPlayer player, World world, BlockPos pos, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) { ItemStack itemStack = player.getHeldItem(hand); IFluidHandlerItem fluidHandlerItem = itemStack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null); + if (fluidHandlerItem == null) { + return ActionResult.newResult(EnumActionResult.FAIL, itemStack); + } + FluidStack fluidStack = fluidHandlerItem.drain(Integer.MAX_VALUE, false); if (fluidStack != null && fluidStack.amount >= FLUID_PER_BLOCK) { BlockPos offsetPos = pos.offset(facing); @@ -87,12 +95,8 @@ public Pair getDurabilityColorsForDisplay(ItemStack itemStack) { @Override public ICapabilityProvider createProvider(ItemStack itemStack) { - return new FluidHandlerItemStack(itemStack, 10000) { - @Override - public boolean canFillFluidType(FluidStack fluid) { - return fluid != null && fluid.isFluidEqual(Materials.ConstructionFoam.getFluid(1)); - } - }; + return new GTFluidHandlerItemStack(itemStack, 10000) + .setFilter(new SingleFluidFilter(Materials.ConstructionFoam.getFluid(1), false)); } private static int foamAllFrameBlocks(World world, BlockPos pos, int maxBlocksToFoam, boolean isSneaking) { @@ -102,7 +106,8 @@ private static int foamAllFrameBlocks(World world, BlockPos pos, int maxBlocksTo //replace blocks without updating physics for (BlockPos framePos : frameBlocks) { IBlockState blockState = world.getBlockState(framePos); - boolean isNormalFrame = blockState.getBlock().getMaterial(blockState) == Material.WOOD || isSneaking; + boolean isNormalFrame = isSneaking || + ModHandler.isMaterialWood(((BlockFrame) blockState.getBlock()).getGtMaterial(blockState)); if (isNormalFrame) { blockState.getBlock().dropBlockAsItem(world, framePos, blockState, 0); } @@ -135,49 +140,49 @@ private static int foamReplacableBlocks(World world, BlockPos pos, int maxBlocks return replacableBlocks.size(); } - private static List gatherReplacableBlocks(World worldIn, BlockPos centerPos, int maxRadiusSq) { - HashSet observedSet = new HashSet<>(); + private static List gatherReplacableBlocks(World world, BlockPos centerPos, int maxRadiusSq) { + Set observedSet = new ObjectOpenHashSet<>(); ArrayList resultAirBlocks = new ArrayList<>(); observedSet.add(centerPos); resultAirBlocks.add(centerPos); - Deque moveStack = new ArrayDeque<>(); + List moveStack = new ArrayList<>(); MutableBlockPos currentPos = new MutableBlockPos(centerPos); main: while (true) { for (EnumFacing facing : EnumFacing.VALUES) { currentPos.move(facing); - IBlockState blockStateHere = worldIn.getBlockState(currentPos); + IBlockState blockStateHere = world.getBlockState(currentPos); //if there is node, and it can connect with previous node, add it to list, and set previous node as current - if (blockStateHere.getBlock().isReplaceable(worldIn, currentPos) && + if (blockStateHere.getBlock().isReplaceable(world, currentPos) && currentPos.distanceSq(centerPos) <= maxRadiusSq && !observedSet.contains(currentPos)) { BlockPos immutablePos = currentPos.toImmutable(); observedSet.add(immutablePos); resultAirBlocks.add(immutablePos); - moveStack.push(facing.getOpposite()); + moveStack.add(facing.getOpposite()); continue main; } else currentPos.move(facing.getOpposite()); } if (!moveStack.isEmpty()) { - currentPos.move(moveStack.pop()); + currentPos.move(moveStack.remove(moveStack.size() - 1)); } else break; } resultAirBlocks.sort(Comparator.comparing(it -> it.distanceSq(centerPos))); return resultAirBlocks; } - private static List gatherFrameBlocks(World worldIn, BlockPos centerPos, int maxRadiusSq) { - HashSet observedSet = new HashSet<>(); + private static List gatherFrameBlocks(World world, BlockPos centerPos, int maxRadiusSq) { + Set observedSet = new ObjectOpenHashSet<>(); ArrayList resultFrameBlocks = new ArrayList<>(); observedSet.add(centerPos); resultFrameBlocks.add(centerPos); IBlockState frameState = null; - Deque moveStack = new ArrayDeque<>(); + List moveStack = new ArrayList<>(); MutableBlockPos currentPos = new MutableBlockPos(centerPos); main: while (true) { for (EnumFacing facing : EnumFacing.VALUES) { currentPos.move(facing); - IBlockState blockStateHere = worldIn.getBlockState(currentPos); + IBlockState blockStateHere = world.getBlockState(currentPos); //if there is node, and it can connect with previous node, add it to list, and set previous node as current if (blockStateHere.getBlock() instanceof BlockFrame && currentPos.distanceSq(centerPos) <= maxRadiusSq && @@ -185,13 +190,13 @@ private static List gatherFrameBlocks(World worldIn, BlockPos centerPo BlockPos immutablePos = currentPos.toImmutable(); observedSet.add(immutablePos); resultFrameBlocks.add(immutablePos); - moveStack.push(facing.getOpposite()); + moveStack.add(facing.getOpposite()); frameState = blockStateHere; continue main; } else currentPos.move(facing.getOpposite()); } if (!moveStack.isEmpty()) { - currentPos.move(moveStack.pop()); + currentPos.move(moveStack.remove(moveStack.size() - 1)); } else break; } resultFrameBlocks.sort(Comparator.comparing(it -> it.distanceSq(centerPos))); diff --git a/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityMultiblockTank.java b/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityMultiblockTank.java index 6bb5b68c02f..efed08b53be 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityMultiblockTank.java +++ b/src/main/java/gregtech/common/metatileentities/multi/MetaTileEntityMultiblockTank.java @@ -6,6 +6,7 @@ import codechicken.lib.vec.Matrix4; import gregtech.api.capability.impl.FilteredFluidHandler; import gregtech.api.capability.impl.FluidTankList; +import gregtech.api.capability.impl.PropertyFluidFilter; import gregtech.api.gui.GuiTextures; import gregtech.api.gui.ModularUI; import gregtech.api.gui.widgets.LabelWidget; @@ -30,7 +31,6 @@ import net.minecraft.util.EnumHand; import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; -import net.minecraftforge.fluids.FluidTank; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -52,12 +52,12 @@ public MetaTileEntityMultiblockTank(ResourceLocation metaTileEntityId, boolean i protected void initializeInventory() { super.initializeInventory(); - FluidTank tank = new FilteredFluidHandler(capacity).setFillPredicate( - fluidStack -> isMetal || (!fluidStack.getFluid().isGaseous() && fluidStack.getFluid().getTemperature() <= 325)); - FluidTankList tankList = new FluidTankList(true, tank); + FilteredFluidHandler tank = new FilteredFluidHandler(capacity); + if (!isMetal) { + tank.setFilter(new PropertyFluidFilter(340, false, false, false, false)); + } - this.importFluids = tankList; - this.exportFluids = tankList; + this.exportFluids = this.importFluids = new FluidTankList(true, tank); this.fluidInventory = tank; } diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityMultiFluidHatch.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityMultiFluidHatch.java index a4292ab0832..7e6edc67df9 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityMultiFluidHatch.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityMultiFluidHatch.java @@ -6,9 +6,9 @@ import gregtech.api.capability.GregtechDataCodes; import gregtech.api.capability.GregtechTileCapabilities; import gregtech.api.capability.IControllable; -import gregtech.api.capability.IMultipleTankHandler; +import gregtech.api.capability.IMultipleTankHandler.MultiFluidTankEntry; import gregtech.api.capability.impl.FluidTankList; -import gregtech.api.capability.impl.NotifiableFluidTankFromList; +import gregtech.api.capability.impl.NotifiableFluidTank; import gregtech.api.gui.GuiTextures; import gregtech.api.gui.ModularUI; import gregtech.api.gui.widgets.TankWidget; @@ -34,18 +34,23 @@ import javax.annotation.Nullable; import java.util.List; -import java.util.function.Supplier; public class MetaTileEntityMultiFluidHatch extends MetaTileEntityMultiblockNotifiablePart implements IMultiblockAbilityPart, IControllable { private static final int TANK_SIZE = 16000; - protected FluidTankList fluidTanks; + // only holding this for convenience + private final FluidTankList fluidTankList; private boolean workingEnabled; public MetaTileEntityMultiFluidHatch(ResourceLocation metaTileEntityId, int tier, boolean isExportHatch) { super(metaTileEntityId, tier, isExportHatch); this.workingEnabled = true; + FluidTank[] fluidsHandlers = new FluidTank[getTier() * getTier()]; + for (int i = 0; i < fluidsHandlers.length; i++) { + fluidsHandlers[i] = new NotifiableFluidTank(TANK_SIZE, this, isExportHatch); + } + this.fluidTankList = new FluidTankList(false, fluidsHandlers); initializeInventory(); } @@ -56,17 +61,7 @@ public MetaTileEntity createMetaTileEntity(IGregTechTileEntity metaTileEntityHol @Override protected void initializeInventory() { - FluidTank[] fluidsHandlers = new FluidTank[(int) Math.pow(this.getTier(), 2)]; - for (int i = 0; i < fluidsHandlers.length; i++) { - fluidsHandlers[i] = new NotifiableFluidTankFromList(TANK_SIZE, this, isExportHatch, i) { - @Override - public Supplier getFluidTankList() { - return () -> MetaTileEntityMultiFluidHatch.this.fluidTanks; - } - }; - } - this.fluidTanks = new FluidTankList(false, fluidsHandlers); - this.fluidInventory = fluidTanks; + if (this.fluidTankList == null) return; super.initializeInventory(); } @@ -173,12 +168,12 @@ public void addToolUsages(ItemStack stack, @Nullable World world, List t @Override protected FluidTankList createImportFluidHandler() { - return isExportHatch ? new FluidTankList(false) : new FluidTankList(false, fluidTanks); + return isExportHatch ? new FluidTankList(false) : fluidTankList; } @Override protected FluidTankList createExportFluidHandler() { - return isExportHatch ? new FluidTankList(false, fluidTanks) : new FluidTankList(false); + return isExportHatch ? fluidTankList : new FluidTankList(false); } @Override @@ -188,7 +183,9 @@ public MultiblockAbility getAbility() { @Override public void registerAbilities(List abilityList) { - abilityList.addAll(fluidTanks.getFluidTanks()); + for (IFluidTank fluidTank : fluidTankList.getFluidTanks()) { + abilityList.add(new MultiFluidTankEntry(this.fluidTankList, fluidTank)); + } } @Override @@ -201,7 +198,7 @@ protected ModularUI createUI(EntityPlayer entityPlayer) { for (int y = 0; y < rowSize; y++) { for (int x = 0; x < rowSize; x++) { int index = y * rowSize + x; - builder.widget(new TankWidget(fluidTanks.getTankAt(index), 89 - rowSize * 9 + x * 18, 18 + y * 18, 18, 18) + builder.widget(new TankWidget(fluidTankList.getTankAt(index), 89 - rowSize * 9 + x * 18, 18 + y * 18, 18, 18) .setBackgroundTexture(GuiTextures.FLUID_SLOT) .setContainerClicking(true, !isExportHatch) .setAlwaysShowFull(true)); diff --git a/src/main/java/gregtech/common/metatileentities/steam/SteamMiner.java b/src/main/java/gregtech/common/metatileentities/steam/SteamMiner.java index ef670b22dd2..98962ee4b71 100644 --- a/src/main/java/gregtech/common/metatileentities/steam/SteamMiner.java +++ b/src/main/java/gregtech/common/metatileentities/steam/SteamMiner.java @@ -5,6 +5,7 @@ import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; import gregtech.api.capability.*; +import gregtech.api.capability.impl.CommonFluidFilters; import gregtech.api.capability.impl.FilteredFluidHandler; import gregtech.api.capability.impl.FluidTankList; import gregtech.api.capability.impl.NotifiableItemStackHandler; @@ -18,7 +19,6 @@ import gregtech.api.metatileentity.IDataInfoProvider; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; -import gregtech.api.recipes.ModHandler; import gregtech.api.util.GTUtility; import gregtech.client.renderer.texture.Textures; import gregtech.client.renderer.texture.cube.SimpleSidedCubeRenderer; @@ -80,8 +80,7 @@ public MetaTileEntity createMetaTileEntity(IGregTechTileEntity tileEntity) { @Override public FluidTankList createImportFluidHandler() { - return new FluidTankList(false, new FilteredFluidHandler(16000) - .setFillPredicate(ModHandler::isSteam)); + return new FluidTankList(false, new FilteredFluidHandler(16000).setFilter(CommonFluidFilters.STEAM)); } protected IItemHandlerModifiable createImportItemHandler() { @@ -318,7 +317,7 @@ public void tryDoVenting() { double posY = (double) machinePos.getY() + 0.5D + (double) ventingSide.getYOffset() * 0.6D; double posZ = (double) machinePos.getZ() + 0.5D + (double) ventingSide.getZOffset() * 0.6D; world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, posX, posY, posZ, 7 + world.rand.nextInt(3), (double) ventingSide.getXOffset() / 2.0D, (double) ventingSide.getYOffset() / 2.0D, (double) ventingSide.getZOffset() / 2.0D, 0.1D); - if (ConfigHolder.machines.machineSounds && !this.isMuffled()){ + if (ConfigHolder.machines.machineSounds && !this.isMuffled()) { world.playSound(null, posX, posY, posZ, SoundEvents.BLOCK_LAVA_EXTINGUISH, SoundCategory.BLOCKS, 1.0F, 1.0F); } this.setNeedsVenting(false); diff --git a/src/main/java/gregtech/common/metatileentities/steam/boiler/SteamBoiler.java b/src/main/java/gregtech/common/metatileentities/steam/boiler/SteamBoiler.java index f9c681a1dd0..9cbe34c96c1 100644 --- a/src/main/java/gregtech/common/metatileentities/steam/boiler/SteamBoiler.java +++ b/src/main/java/gregtech/common/metatileentities/steam/boiler/SteamBoiler.java @@ -5,6 +5,7 @@ import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; import gregtech.api.GTValues; +import gregtech.api.capability.impl.CommonFluidFilters; import gregtech.api.capability.impl.FilteredFluidHandler; import gregtech.api.capability.impl.FluidTankList; import gregtech.api.gui.GuiTextures; @@ -16,7 +17,6 @@ import gregtech.api.gui.widgets.TankWidget; import gregtech.api.metatileentity.IDataInfoProvider; import gregtech.api.metatileentity.MetaTileEntity; -import gregtech.api.recipes.ModHandler; import gregtech.api.unification.material.Materials; import gregtech.api.util.GTTransferUtils; import gregtech.api.util.GTUtility; @@ -232,7 +232,7 @@ private void generateSteam() { boolean hasDrainedWater = waterFluidTank.drain(1, true) != null; int filledSteam = 0; if (hasDrainedWater) { - filledSteam = steamFluidTank.fill(ModHandler.getSteam(fillAmount), true); + filledSteam = steamFluidTank.fill(Materials.Steam.getFluid(fillAmount), true); } if (this.hasNoWater && hasDrainedWater) { doExplosion(2.0f); @@ -292,7 +292,7 @@ public double getFuelLeftPercent() { @Override protected FluidTankList createImportFluidHandler() { - this.waterFluidTank = new FilteredFluidHandler(16000).setFillPredicate(ModHandler::isWater); + this.waterFluidTank = new FilteredFluidHandler(16000).setFilter(CommonFluidFilters.BOILER_FLUID); return new FluidTankList(false, waterFluidTank); } diff --git a/src/main/java/gregtech/common/metatileentities/steam/boiler/SteamLavaBoiler.java b/src/main/java/gregtech/common/metatileentities/steam/boiler/SteamLavaBoiler.java index 59371dd8649..5ab8832caed 100755 --- a/src/main/java/gregtech/common/metatileentities/steam/boiler/SteamLavaBoiler.java +++ b/src/main/java/gregtech/common/metatileentities/steam/boiler/SteamLavaBoiler.java @@ -2,8 +2,10 @@ import gregtech.api.GTValues; import gregtech.api.capability.GregtechCapabilities; +import gregtech.api.capability.IFilter; import gregtech.api.capability.IFuelInfo; import gregtech.api.capability.IFuelable; +import gregtech.api.capability.impl.CommonFluidFilters; import gregtech.api.capability.impl.FilteredFluidHandler; import gregtech.api.capability.impl.FluidFuelInfo; import gregtech.api.capability.impl.FluidTankList; @@ -14,6 +16,9 @@ import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.unification.material.Materials; import gregtech.client.renderer.texture.Textures; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntMaps; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; @@ -25,20 +30,55 @@ import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import javax.annotation.Nonnull; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.Objects; public class SteamLavaBoiler extends SteamBoiler implements IFuelable { - private FluidTank fuelFluidTank; + private static final Object2IntMap BOILER_FUEL_TO_CONSUMPTION = new Object2IntOpenHashMap<>(); + private static boolean initialized; + + private static final IFilter FUEL_FILTER = new IFilter<>() { + @Override + public boolean test(@Nonnull FluidStack fluidStack) { + for (Fluid fluid : getBoilerFuelToConsumption().keySet()) { + if (CommonFluidFilters.matchesFluid(fluidStack, fluid)) return true; + } + return false; + } + + @Override + public int getPriority() { + return IFilter.whitelistPriority(getBoilerFuelToConsumption().size()); + } + }; + + private static void init() { + setBoilerFuelToConsumption(Materials.Lava.getFluid(), 100); + setBoilerFuelToConsumption(Materials.Creosote.getFluid(), 250); + } + + @Nonnull + public static Object2IntMap getBoilerFuelToConsumption() { + if (!initialized) { + initialized = true; + init(); + } + return Object2IntMaps.unmodifiable(BOILER_FUEL_TO_CONSUMPTION); + } - private final Map boilerFuels; + public static void setBoilerFuelToConsumption(@Nonnull Fluid fluid, int amount) { + Objects.requireNonNull(fluid, "fluid == null"); + if (amount <= 0) throw new IllegalArgumentException("amount <= 0"); + BOILER_FUEL_TO_CONSUMPTION.put(fluid, amount); + } + + private FluidTank fuelFluidTank; public SteamLavaBoiler(ResourceLocation metaTileEntityId, boolean isHighPressure) { super(metaTileEntityId, isHighPressure, Textures.LAVA_BOILER_OVERLAY); - this.boilerFuels = getBoilerFuels(); } @Override @@ -51,30 +91,23 @@ protected int getBaseSteamOutput() { return isHighPressure ? 600 : 240; } - private Map getBoilerFuels() { - Map fuels = new HashMap<>(); - fuels.put(Materials.Lava.getFluid(), 100); - fuels.put(Materials.Creosote.getFluid(), 250); - - return fuels; - } - @Override protected FluidTankList createImportFluidHandler() { FluidTankList superHandler = super.createImportFluidHandler(); - this.fuelFluidTank = new FilteredFluidHandler(16000) - .setFillPredicate(fs -> boilerFuels.containsKey(fs.getFluid())); + this.fuelFluidTank = new FilteredFluidHandler(16000).setFilter(FUEL_FILTER); return new FluidTankList(false, superHandler, fuelFluidTank); - } @Override protected void tryConsumeNewFuel() { - for(Map.Entry fuels : boilerFuels.entrySet()) { - if(fuelFluidTank.getFluid() != null && fuelFluidTank.getFluid().isFluidEqual(new FluidStack(fuels.getKey(), fuels.getValue())) && fuelFluidTank.getFluidAmount() >= fuels.getValue()) { - fuelFluidTank.drain(fuels.getValue(), true); - setFuelMaxBurnTime(100); - } + FluidStack fluid = fuelFluidTank.getFluid(); + if (fluid == null || fluid.tag != null) { // fluid with nbt tag cannot match normal fluids + return; + } + int consumption = getBoilerFuelToConsumption().getInt(fluid.getFluid()); + if (consumption > 0 && fuelFluidTank.getFluidAmount() >= consumption) { + fuelFluidTank.drain(consumption, true); + setFuelMaxBurnTime(100); } } @@ -106,7 +139,7 @@ public Collection getFuels() { final int fuelRemaining = fuel.amount; final int fuelCapacity = fuelFluidTank.getCapacity(); final long burnTime = (long) fuelRemaining * (this.isHighPressure ? 6 : 12); // 100 mb lasts 600 or 1200 ticks - return Collections.singleton(new FluidFuelInfo(fuel, fuelRemaining, fuelCapacity, boilerFuels.get(fuel.getFluid()), burnTime)); + return Collections.singleton(new FluidFuelInfo(fuel, fuelRemaining, fuelCapacity, getBoilerFuelToConsumption().get(fuel.getFluid()), burnTime)); } @Override diff --git a/src/main/java/gregtech/common/metatileentities/steam/multiblockpart/MetaTileEntitySteamHatch.java b/src/main/java/gregtech/common/metatileentities/steam/multiblockpart/MetaTileEntitySteamHatch.java index f36ec653292..9f6ce0a38f8 100644 --- a/src/main/java/gregtech/common/metatileentities/steam/multiblockpart/MetaTileEntitySteamHatch.java +++ b/src/main/java/gregtech/common/metatileentities/steam/multiblockpart/MetaTileEntitySteamHatch.java @@ -17,7 +17,7 @@ import gregtech.api.metatileentity.multiblock.IMultiblockAbilityPart; import gregtech.api.metatileentity.multiblock.MultiblockAbility; import gregtech.api.metatileentity.multiblock.MultiblockControllerBase; -import gregtech.api.recipes.ModHandler; +import gregtech.api.capability.impl.CommonFluidFilters; import gregtech.client.renderer.ICubeRenderer; import gregtech.client.renderer.texture.Textures; import gregtech.client.renderer.texture.cube.SimpleOverlayRenderer; @@ -94,7 +94,8 @@ public int getDefaultPaintingColor() { @Override protected FluidTankList createImportFluidHandler() { - return new FluidTankList(false, new FilteredFluidHandler(INVENTORY_SIZE).setFillPredicate(ModHandler::isSteam)); + return new FluidTankList(false, new FilteredFluidHandler(INVENTORY_SIZE) + .setFilter(CommonFluidFilters.STEAM)); } @Override diff --git a/src/main/java/gregtech/common/metatileentities/storage/MetaTileEntityDrum.java b/src/main/java/gregtech/common/metatileentities/storage/MetaTileEntityDrum.java index 15a8fff425b..78f68758bf5 100644 --- a/src/main/java/gregtech/common/metatileentities/storage/MetaTileEntityDrum.java +++ b/src/main/java/gregtech/common/metatileentities/storage/MetaTileEntityDrum.java @@ -6,11 +6,9 @@ import codechicken.lib.render.pipeline.ColourMultiplier; import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; +import gregtech.api.capability.IPropertyFluidFilter; import gregtech.api.capability.impl.FilteredFluidHandler; -import gregtech.api.capability.impl.ThermalFluidHandlerItemStack; -import gregtech.api.fluids.MaterialFluid; -import gregtech.api.fluids.fluidType.FluidType; -import gregtech.api.fluids.fluidType.FluidTypes; +import gregtech.api.capability.impl.GTFluidHandlerItemStack; import gregtech.api.gui.ModularUI; import gregtech.api.items.toolitem.ToolClasses; import gregtech.api.metatileentity.MetaTileEntity; @@ -36,7 +34,6 @@ import net.minecraft.world.World; import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.common.util.Constants; -import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidTank; import net.minecraftforge.fluids.FluidUtil; @@ -64,9 +61,6 @@ public MetaTileEntityDrum(ResourceLocation metaTileEntityId, Material material, super(metaTileEntityId); this.tankSize = tankSize; this.material = material; - if (!this.material.hasProperty(PropertyKey.FLUID_PIPE)) { - throw new IllegalArgumentException(String.format("Material %s requires FluidPipePropety for Drums", material)); - } initializeInventory(); } @@ -106,26 +100,13 @@ public boolean hasFrontFacing() { @Override protected void initializeInventory() { + if (this.material == null) return; // call before field initialization, should be called later with fields set super.initializeInventory(); - this.fluidTank = new FilteredFluidHandler(tankSize) - .setFillPredicate(stack -> { - if (stack == null || stack.getFluid() == null) return false; - - Fluid fluid = stack.getFluid(); - FluidPipeProperties pipeProperties = material.getProperty(PropertyKey.FLUID_PIPE); - if (fluid.getTemperature() > pipeProperties.getMaxFluidTemperature()) return false; - // fluids less than 120K are cryogenic - if (fluid.getTemperature() < 120 && !pipeProperties.isCryoProof()) return false; - if (fluid.isGaseous() && !pipeProperties.isGasProof()) return false; - - if (fluid instanceof MaterialFluid) { - FluidType fluidType = ((MaterialFluid) fluid).getFluidType(); - if (fluidType == FluidTypes.ACID && !pipeProperties.isAcidProof()) return false; - return fluidType != FluidTypes.PLASMA || pipeProperties.isPlasmaProof(); - } - return true; - }); - this.fluidInventory = fluidTank; + IPropertyFluidFilter filter = this.material.getProperty(PropertyKey.FLUID_PIPE); + if (filter == null) { + throw new IllegalArgumentException(String.format("Material %s requires FluidPipePropety for Drums", material)); + } + this.fluidInventory = this.fluidTank = new FilteredFluidHandler(tankSize).setFilter(filter); } @Override @@ -150,13 +131,7 @@ public void writeItemStackData(NBTTagCompound itemStack) { @Override public ICapabilityProvider initItemStackCapabilities(ItemStack itemStack) { - FluidPipeProperties pipeProperties = material.getProperty(PropertyKey.FLUID_PIPE); - return new ThermalFluidHandlerItemStack(itemStack, tankSize, - pipeProperties.getMaxFluidTemperature(), - pipeProperties.isGasProof(), - pipeProperties.isAcidProof(), - pipeProperties.isCryoProof(), - pipeProperties.isPlasmaProof()); + return new GTFluidHandlerItemStack(itemStack, tankSize).setFilter(this.fluidTank.getFilter()); } @Override @@ -240,7 +215,7 @@ private void toggleOutput() { @Override @SideOnly(Side.CLIENT) public Pair getParticleTexture() { - if(ModHandler.isMaterialWood(material)) { + if (ModHandler.isMaterialWood(material)) { return Pair.of(Textures.WOODEN_DRUM.getParticleTexture(), getPaintingColorForRendering()); } else { int color = ColourRGBA.multiply( @@ -253,7 +228,7 @@ public Pair getParticleTexture() { @Override public void renderMetaTileEntity(CCRenderState renderState, Matrix4 translation, IVertexOperation[] pipeline) { - if(ModHandler.isMaterialWood(material)) { + if (ModHandler.isMaterialWood(material)) { ColourMultiplier multiplier = new ColourMultiplier(GTUtility.convertRGBtoOpaqueRGBA_CL(getPaintingColorForRendering())); Textures.WOODEN_DRUM.render(renderState, translation, ArrayUtils.add(pipeline, multiplier), getFrontFacing()); } else { @@ -332,5 +307,4 @@ public void readFromNBT(NBTTagCompound data) { protected boolean shouldSerializeInventories() { return false; } - } diff --git a/src/main/java/gregtech/common/metatileentities/storage/MetaTileEntityQuantumTank.java b/src/main/java/gregtech/common/metatileentities/storage/MetaTileEntityQuantumTank.java index f980e8e99c1..5a12b5ffbd9 100644 --- a/src/main/java/gregtech/common/metatileentities/storage/MetaTileEntityQuantumTank.java +++ b/src/main/java/gregtech/common/metatileentities/storage/MetaTileEntityQuantumTank.java @@ -7,10 +7,12 @@ import codechicken.lib.vec.Matrix4; import gregtech.api.capability.GregtechTileCapabilities; import gregtech.api.capability.IActiveOutputSide; +import gregtech.api.capability.IFilter; +import gregtech.api.capability.IFilteredFluidContainer; import gregtech.api.capability.impl.FilteredItemHandler; import gregtech.api.capability.impl.FluidHandlerProxy; import gregtech.api.capability.impl.FluidTankList; -import gregtech.api.capability.impl.ThermalFluidHandlerItemStack; +import gregtech.api.capability.impl.GTFluidHandlerItemStack; import gregtech.api.cover.ICoverable; import gregtech.api.gui.GuiTextures; import gregtech.api.gui.ModularUI; @@ -51,6 +53,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.tuple.Pair; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; import java.util.List; @@ -65,10 +68,12 @@ public class MetaTileEntityQuantumTank extends MetaTileEntity implements ITiered private final int maxFluidCapacity; protected FluidTank fluidTank; private boolean autoOutputFluids; + @Nullable private EnumFacing outputFacing; private boolean allowInputFromOutputSide = false; protected IFluidHandler outputFluidInventory; + @Nullable private FluidStack previousFluid; private boolean locked; private boolean voiding; @@ -335,8 +340,7 @@ private Consumer> getFluidNameText(TankWidget tankWidget) { if (this.lockedFluid != null) { fluidName = this.lockedFluid.getLocalizedName(); } - } - else { + } else { fluidName = tankWidget.getFluidLocalizedName(); } @@ -357,8 +361,7 @@ private Consumer> getFluidAmountText(TankWidget tankWidget) if (this.lockedFluid != null) { fluidAmount = "0"; } - } - else { + } else { fluidAmount = tankWidget.getFormattedFluidAmount(); } if (!fluidAmount.isEmpty()) { @@ -491,7 +494,7 @@ public T getCapability(Capability capability, EnumFacing side) { @Override public ICapabilityProvider initItemStackCapabilities(ItemStack itemStack) { - return new ThermalFluidHandlerItemStack(itemStack, maxFluidCapacity, Integer.MAX_VALUE, true, true, true, true); + return new GTFluidHandlerItemStack(itemStack, maxFluidCapacity); } @Override @@ -577,7 +580,22 @@ public boolean needsSneakToRotate() { return true; } - private class QuantumFluidTank extends FluidTank { + @Override + public AxisAlignedBB getRenderBoundingBox() { + return new AxisAlignedBB(getPos()); + } + + @Override + public boolean isOpaqueCube() { + return false; + } + + @Override + public int getLightOpacity() { + return 0; + } + + private class QuantumFluidTank extends FluidTank implements IFilteredFluidContainer, IFilter { public QuantumFluidTank(int capacity) { super(capacity); @@ -595,22 +613,22 @@ public int fillInternal(FluidStack resource, boolean doFill) { @Override public boolean canFillFluidType(FluidStack fluid) { - return !locked || lockedFluid == null || fluid.isFluidEqual(lockedFluid); + return test(fluid); } - } - @Override - public AxisAlignedBB getRenderBoundingBox() { - return new AxisAlignedBB(getPos()); - } + @Override + public IFilter getFilter() { + return this; + } - @Override - public boolean isOpaqueCube() { - return false; - } + @Override + public boolean test(@Nonnull FluidStack fluidStack) { + return !locked || lockedFluid == null || fluidStack.isFluidEqual(lockedFluid); + } - @Override - public int getLightOpacity() { - return 0; + @Override + public int getPriority() { + return !locked || lockedFluid == null ? IFilter.noPriority() : IFilter.whitelistPriority(1); + } } } diff --git a/src/main/java/gregtech/common/pipelike/fluidpipe/tile/TileEntityFluidPipeTickable.java b/src/main/java/gregtech/common/pipelike/fluidpipe/tile/TileEntityFluidPipeTickable.java index be6d6f1e3c5..427525fc3fd 100644 --- a/src/main/java/gregtech/common/pipelike/fluidpipe/tile/TileEntityFluidPipeTickable.java +++ b/src/main/java/gregtech/common/pipelike/fluidpipe/tile/TileEntityFluidPipeTickable.java @@ -1,12 +1,13 @@ package gregtech.common.pipelike.fluidpipe.tile; import gregtech.api.GTValues; -import gregtech.api.capability.GregtechCapabilities; +import gregtech.api.capability.IPropertyFluidFilter; import gregtech.api.cover.CoverBehavior; import gregtech.api.fluids.MaterialFluid; import gregtech.api.fluids.fluidType.FluidTypes; import gregtech.api.metatileentity.IDataInfoProvider; import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.unification.material.properties.FluidPipeProperties; import gregtech.api.util.EntityDamageUtil; import gregtech.api.util.GTUtility; import gregtech.common.covers.CoverPump; @@ -111,15 +112,16 @@ private void distributeFluid(int channel, FluidTank tank, FluidStack fluid) { FluidStack maxFluid = fluid.copy(); double availableCapacity = 0; - for (byte side, i = 0, j = (byte) GTValues.RNG.nextInt(6); i < 6; i++) { + for (byte i = 0, j = (byte) GTValues.RNG.nextInt(6); i < 6; i++) { // Get a list of tanks accepting fluids, and what side they're on - side = (byte) ((i + j) % 6); + byte side = (byte) ((i + j) % 6); EnumFacing facing = EnumFacing.VALUES[side]; - if (!isConnected(facing) || (lastReceivedFrom & (1 << side)) != 0) + + if (!isConnected(facing) || (lastReceivedFrom & (1 << side)) != 0) { continue; - EnumFacing oppositeSide = facing.getOpposite(); + } - IFluidHandler fluidHandler = getFluidHandlerAt(facing, oppositeSide); + IFluidHandler fluidHandler = getFluidHandlerAt(facing, facing.getOpposite()); if (fluidHandler == null) continue; @@ -127,31 +129,25 @@ private void distributeFluid(int channel, FluidTank tank, FluidStack fluid) { CoverBehavior cover = getCoverableImplementation().getCoverAtSide(facing); // pipeTank should only be determined by the cover attached to the actual pipe - if (cover != null){ - IFluidHandler capability = cover.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, pipeTank); + if (cover != null) { + pipeTank = cover.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, pipeTank); // Shutter covers return null capability when active, so check here to prevent NPE - if (capability == null) { - continue; - } - pipeTank = capability; + if (pipeTank == null) continue; } else { - cover = getOtherCoverAt(facing, oppositeSide); + MetaTileEntity tile = GTUtility.getMetaTileEntity(world, pos.offset(facing)); + if (tile != null) cover = tile.getCoverAtSide(facing.getOpposite()); } - if (cover instanceof CoverPump) { - + if (cover instanceof CoverPump coverPump) { int pipeThroughput = getNodeData().getThroughput() * 20; - if (((CoverPump) cover).getTransferRate() > pipeThroughput) { - ((CoverPump) cover).setTransferRate(pipeThroughput); + if (coverPump.getTransferRate() > pipeThroughput) { + coverPump.setTransferRate(pipeThroughput); } - - ManualImportExportMode mode = ((CoverPump) cover).getManualImportExportMode(); - if (mode == ManualImportExportMode.DISABLED) { + if (coverPump.getManualImportExportMode() == ManualImportExportMode.DISABLED) { continue; } } - FluidStack drainable = pipeTank.drain(maxFluid, false); if (drainable == null || drainable.amount <= 0) { continue; @@ -178,7 +174,8 @@ private void distributeFluid(int channel, FluidTank tank, FluidStack fluid) { triple.setRight((int) Math.floor(triple.getRight() * maxAmount / availableCapacity)); // Distribute fluids based on percentage available space at destination } if (triple.getRight() == 0) { - if (tank.getFluidAmount() <= 0) break; // If there is no more stored fluid, stop transferring to prevent dupes + if (tank.getFluidAmount() <= 0) + break; // If there is no more stored fluid, stop transferring to prevent dupes triple.setRight(1); // If the percent is not enough to give at least 1L, try to give 1L } else if (triple.getRight() < 0) { continue; @@ -196,18 +193,20 @@ private void distributeFluid(int channel, FluidTank tank, FluidStack fluid) { public void checkAndDestroy(@Nonnull FluidStack stack) { Fluid fluid = stack.getFluid(); - boolean burning = getNodeData().getMaxFluidTemperature() < fluid.getTemperature(stack); - boolean leaking = !getNodeData().isGasProof() && fluid.isGaseous(stack); - boolean shattering = !getNodeData().isCryoProof() && fluid.getTemperature() < 120; // fluids less than 120K are cryogenic + FluidPipeProperties prop = getNodeData(); + + boolean burning = prop.getMaxFluidTemperature() < fluid.getTemperature(stack); + boolean leaking = !prop.isGasProof() && fluid.isGaseous(stack); + boolean shattering = !prop.isCryoProof() && fluid.getTemperature() < IPropertyFluidFilter.CRYOGENIC_TEMPERATURE_THRESHOLD; boolean corroding = false; boolean melting = false; - if (fluid instanceof MaterialFluid) { - MaterialFluid materialFluid = (MaterialFluid) fluid; - corroding = !getNodeData().isAcidProof() && materialFluid.getFluidType().equals(FluidTypes.ACID); - melting = !getNodeData().isPlasmaProof() && materialFluid.getFluidType().equals(FluidTypes.PLASMA); + + if (fluid instanceof MaterialFluid materialFluid) { + corroding = !prop.isAcidProof() && materialFluid.getFluidType().equals(FluidTypes.ACID); + melting = !prop.isPlasmaProof() && materialFluid.getFluidType().equals(FluidTypes.PLASMA); // carrying plasmas which are too hot when plasma proof does not burn pipes - if (burning && getNodeData().isPlasmaProof() && materialFluid.getFluidType().equals(FluidTypes.PLASMA)) + if (burning && prop.isPlasmaProof() && materialFluid.getFluidType().equals(FluidTypes.PLASMA)) burning = false; } @@ -319,14 +318,6 @@ private IFluidHandler getFluidHandlerAt(EnumFacing facing, EnumFacing oppositeSi return tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, oppositeSide); } - private CoverBehavior getOtherCoverAt(EnumFacing facing, EnumFacing oppositeSide) { - MetaTileEntity tile = GTUtility.getMetaTileEntity(world, pos.offset(facing)); - if (tile == null) { - return null; - } - return tile.getCoverAtSide(oppositeSide); - } - public void receivedFrom(EnumFacing facing) { if (facing != null) { lastReceivedFrom |= (1 << facing.getIndex()); diff --git a/src/main/java/gregtech/core/CoreModule.java b/src/main/java/gregtech/core/CoreModule.java index eb92cfe9ac9..57f12dbc2a9 100644 --- a/src/main/java/gregtech/core/CoreModule.java +++ b/src/main/java/gregtech/core/CoreModule.java @@ -135,7 +135,6 @@ public void preInit(FMLPreInitializationEvent event) { MetaItems.init(); ToolItems.init(); MetaFluids.init(); - ModHandler.init(); /* Start MetaTileEntity Registration */ MTE_REGISTRY.unfreeze(); diff --git a/src/test/java/gregtech/Bootstrap.java b/src/test/java/gregtech/Bootstrap.java index edbecea3587..e450bceae47 100644 --- a/src/test/java/gregtech/Bootstrap.java +++ b/src/test/java/gregtech/Bootstrap.java @@ -3,7 +3,6 @@ import gregtech.api.GTValues; import gregtech.api.GregTechAPI; import gregtech.api.fluids.MetaFluids; -import gregtech.api.recipes.ModHandler; import gregtech.api.unification.material.Materials; import gregtech.api.unification.ore.OrePrefix; import gregtech.common.items.MetaItems; @@ -67,7 +66,6 @@ public static void perform() { OrePrefix.runMaterialHandlers(); MetaFluids.init(); MetaItems.init(); - ModHandler.init(); bootstrapped = true; } diff --git a/src/test/java/gregtech/api/capability/impl/FluidTankListTest.java b/src/test/java/gregtech/api/capability/impl/FluidTankListTest.java new file mode 100644 index 00000000000..ed636cfa186 --- /dev/null +++ b/src/test/java/gregtech/api/capability/impl/FluidTankListTest.java @@ -0,0 +1,435 @@ +package gregtech.api.capability.impl; + +import gregtech.Bootstrap; +import gregtech.api.capability.IMultipleTankHandler; +import gregtech.api.unification.material.Materials; +import gregtech.api.util.OverlayedFluidHandler; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTank; +import net.minecraftforge.fluids.IFluidTank; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.stream.Collectors; + +import static net.minecraftforge.fluids.FluidRegistry.LAVA; +import static net.minecraftforge.fluids.FluidRegistry.WATER; + +public class FluidTankListTest { + + @BeforeAll + public static void bootstrap() { + Bootstrap.perform(); + } + + @Test + public void testSimpleFills() { + new FluidHandlerTester(false, + new FluidTank(1000)) + .beginSimulation() + .fill(WATER, 1000) + .expectContents(new FluidStack(WATER, 1000)); + + new FluidHandlerTester(false, + new FluidTank(1000)) + .beginSimulation() + .fill(WATER, 333) + .expectContents(new FluidStack(WATER, 333)) + .fill(WATER, 333) + .expectContents(new FluidStack(WATER, 666)) + .fill(WATER, 333) + .expectContents(new FluidStack(WATER, 999)) + .fill(WATER, 333) + .expectContents(new FluidStack(WATER, 1000)); + + new FluidHandlerTester(false, + new FluidTank(1000), + new FluidTank(1000)) + .beginSimulation() + .fill(WATER, 333) + .expectContents(new FluidStack(WATER, 333), null) + .fill(WATER, 333) + .expectContents(new FluidStack(WATER, 666), null) + .fill(WATER, 333) + .expectContents(new FluidStack(WATER, 999), null) + .fill(WATER, 333) + .expectContents(new FluidStack(WATER, 1000), null); + + new FluidHandlerTester(true, + new FluidTank(1000), + new FluidTank(1000)) + .beginSimulation() + .fill(WATER, 333) + .expectContents(new FluidStack(WATER, 333), null) + .fill(WATER, 333) + .expectContents(new FluidStack(WATER, 666), null) + .fill(WATER, 333) + .expectContents(new FluidStack(WATER, 999), null) + .fill(WATER, 333) + .expectContents(new FluidStack(WATER, 1000), new FluidStack(WATER, 332)); + + new FluidHandlerTester(false, + new FluidTank(1000), + new FluidTank(1000)) + .beginSimulation() + .fill(WATER, 1500) + .expectContents(new FluidStack(WATER, 1000), null); + + new FluidHandlerTester(true, + new FluidTank(1000), + new FluidTank(1000)) + .beginSimulation() + .fill(WATER, 1500) + .expectContents(new FluidStack(WATER, 1000), new FluidStack(WATER, 500)); + } + + @Test + public void testMultipleFluidFills() { + new FluidHandlerTester(false, + new FluidTank(1000), + new FluidTank(1000), + new FluidTank(1000)) + .fill(WATER, 800) + .fill(WATER, 800) + .fill(LAVA, 800) + .expectContents( + new FluidStack(WATER, 1000), + new FluidStack(LAVA, 800), + null) + .drain(WATER, 1000) + .expectContents( + null, + new FluidStack(LAVA, 800), + null) + .fill(LAVA, 800) + .expectContents( + null, + new FluidStack(LAVA, 1000), + null) + .fill(LAVA, 800) + .expectContents( + null, + new FluidStack(LAVA, 1000), + null); + + new FluidHandlerTester(true, + new FluidTank(1000), + new FluidTank(1000), + new FluidTank(1000)) + .fill(WATER, 800) + .fill(WATER, 800) + .fill(LAVA, 800) + .expectContents( + new FluidStack(WATER, 1000), + new FluidStack(WATER, 600), + new FluidStack(LAVA, 800)) + .drain(WATER, 1600) + .expectContents( + null, + null, + new FluidStack(LAVA, 800)) + .fill(LAVA, 800) + .expectContents( + new FluidStack(LAVA, 600), + null, + new FluidStack(LAVA, 1000)) + .fill(LAVA, 800) + .expectContents( + new FluidStack(LAVA, 1000), + new FluidStack(LAVA, 400), + new FluidStack(LAVA, 1000)) + .fill(WATER, 69420) + .expectContents( + new FluidStack(LAVA, 1000), + new FluidStack(LAVA, 400), + new FluidStack(LAVA, 1000)); + } + + @Test + public void testMixedSameFluidFill() { + new FluidHandlerTester(new FluidTankList(true, + new FluidTankList(false, + new FluidTank(1000), + new FluidTank(1000) + ), + new FluidTank(1000), + new FluidTank(1000))) // distinct slots first + .beginSimulation() + .fill(WATER, 800) + .fill(WATER, 800) + .fill(WATER, 800) + .expectContents( + new FluidStack(WATER, 1000), + null, + new FluidStack(WATER, 1000), + new FluidStack(WATER, 400)); + + new FluidHandlerTester(new FluidTankList(false, + new FluidTankList(true, + new FluidTank(1000), + new FluidTank(1000) + ), + new FluidTank(1000), + new FluidTank(1000))) // non-distinct slots first + .beginSimulation() + .fill(WATER, 800) + .fill(WATER, 800) + .fill(WATER, 800) + .expectContents( + new FluidStack(WATER, 1000), + new FluidStack(WATER, 1000), + new FluidStack(WATER, 400), + null); + } + + @Test + public void testDrain() { + new FluidHandlerTester(true, + new FluidTank(1000), + new FluidTank(1000), + new FluidTank(1000)) + .fill(WATER, 1500) + .fill(LAVA, 500) + .expectContents( + new FluidStack(WATER, 1000), + new FluidStack(WATER, 500), + new FluidStack(LAVA, 500)) + .drain(1000) + .expectContents( + null, + new FluidStack(WATER, 500), + new FluidStack(LAVA, 500)) + .drain(1000) + .expectContents( + null, + null, + new FluidStack(LAVA, 500)) + .drain(1000) + .expectContents( + null, + null, + null) + .fill(LAVA, 500) + .fill(WATER, 1500) + .expectContents( + new FluidStack(LAVA, 500), + new FluidStack(WATER, 1000), + new FluidStack(WATER, 500)) + .drain(1000) + .expectContents( + null, + new FluidStack(WATER, 1000), + new FluidStack(WATER, 500)) + .drain(500) + .expectContents( + null, + new FluidStack(WATER, 500), + new FluidStack(WATER, 500)) + .drain(1000) + .expectContents( + null, + null, + null); + } + + @Test + public void testFilterOrdering() { + SingleFluidFilter waterFilter = new SingleFluidFilter(new FluidStack(WATER, 1), false); + SingleFluidFilter lavaFilter = new SingleFluidFilter(new FluidStack(LAVA, 1), false); + SingleFluidFilter creosoteFilter = new SingleFluidFilter(new FluidStack(Materials.Creosote.getFluid(), 1), false); + + new FluidHandlerTester(false, + new FilteredFluidHandler(1000).setFilter(waterFilter), + new FilteredFluidHandler(1000).setFilter(lavaFilter), + new FilteredFluidHandler(1000).setFilter(creosoteFilter)) + .beginSimulation() + .fill(WATER, 800) + .fill(LAVA, 800) + .fill(WATER, 800) + .expectContents( + new FluidStack(WATER, 1000), + new FluidStack(LAVA, 800), + null); + + new FluidHandlerTester(true, + new FilteredFluidHandler(1000).setFilter(waterFilter), + new FilteredFluidHandler(1000).setFilter(lavaFilter), + new FilteredFluidHandler(1000).setFilter(creosoteFilter)) + .beginSimulation() + .fill(WATER, 800) + .fill(LAVA, 800) + .fill(WATER, 800) + .expectContents( + new FluidStack(WATER, 1000), + new FluidStack(LAVA, 800), + null); + + new FluidHandlerTester(true, + new FilteredFluidHandler(1000).setFilter(waterFilter), + new FluidTank(1000)) + .beginSimulation() + .fill(WATER, 800) + .fill(LAVA, 800) + .expectContents( + new FluidStack(WATER, 800), + new FluidStack(LAVA, 800)); + + new FluidHandlerTester(true, + new FilteredFluidHandler(1000).setFilter(waterFilter), + new FluidTank(1000)) + .beginSimulation() + .fill(LAVA, 800) + .fill(WATER, 800) + .expectContents( + new FluidStack(WATER, 800), + new FluidStack(LAVA, 800)); + + new FluidHandlerTester(true, + new FluidTank(1000), + new FilteredFluidHandler(1000).setFilter(waterFilter)) + .beginSimulation() + .fill(WATER, 800) + .fill(LAVA, 800) + .expectContents( + new FluidStack(LAVA, 800), + new FluidStack(WATER, 800)); + + new FluidHandlerTester(true, + new FluidTank(1000), + new FilteredFluidHandler(1000).setFilter(waterFilter)) + .beginSimulation() + .fill(LAVA, 800) + .fill(WATER, 800) + .expectContents( + new FluidStack(LAVA, 800), + new FluidStack(WATER, 800)); + } + + private static final class FluidHandlerTester { + + private final FluidTankList tank; + + @Nullable + private OverlayedFluidHandler overlayedFluidHandler; + + FluidHandlerTester(FluidTankList tank) { + this.tank = tank; + } + + FluidHandlerTester(boolean allowSameFluidFill, IFluidTank... tanks) { + this(new FluidTankList(allowSameFluidFill, tanks)); + } + + FluidHandlerTester fill(Fluid fluid, int amount) { + return fill(new FluidStack(fluid, amount)); + } + + FluidHandlerTester fill(FluidStack fluidStack) { + // make string representation before modifying the state, to produce better error message + String tankString = this.tank.toString(true); + + int tankFillSim = this.tank.fill(fluidStack, false); + + if (this.overlayedFluidHandler != null) { + String overlayString = this.overlayedFluidHandler.toString(true); + int ofhSim = this.overlayedFluidHandler.insertFluid(fluidStack, fluidStack.amount); + + if (tankFillSim != ofhSim) { + throw new AssertionError("Result of simulation fill from tank and OFH differ.\n" + + "Tank Simulation: " + tankFillSim + ", OFH simulation: " + ofhSim + "\n" + + "Tank: " + tankString + "\n" + + "OFH: " + overlayString); + } + } + int actualFill = this.tank.fill(fluidStack, true); + if (tankFillSim != actualFill) { + throw new AssertionError("Simulation fill to tank and actual fill differ.\n" + + "Simulated Fill: " + tankFillSim + ", Actual Fill: " + actualFill + "\n" + + "Tank: " + tankString); + } + return this; + } + + FluidHandlerTester drain(Fluid fluid, int amount) { + return drain(new FluidStack(fluid, amount)); + } + + FluidHandlerTester drain(FluidStack fluidStack) { + if (this.overlayedFluidHandler != null) { + throw new IllegalStateException("Cannot drain stuff in simulation"); + } + // make string representation before modifying the state, to produce better error message + String tankString = this.tank.toString(true); + + FluidStack drainSim = this.tank.drain(fluidStack, false); + FluidStack actualDrain = this.tank.drain(fluidStack, true); + + if (!eq(drainSim, actualDrain)) { + throw new AssertionError("Simulation drain from tank and actual drain differ.\n" + + "Simulated Drain: " + ftos(drainSim) + ", Actual Drain: " + ftos(actualDrain) + "\n" + + "Tank: " + tankString); + } + return this; + } + + FluidHandlerTester drain(int amount) { + if (this.overlayedFluidHandler != null) { + throw new IllegalStateException("Cannot drain stuff in simulation"); + } + // make string representation before modifying the state, to produce better error message + String tankString = this.tank.toString(true); + + FluidStack drainSim = this.tank.drain(amount, false); + FluidStack actualDrain = this.tank.drain(amount, true); + + if (!eq(drainSim, actualDrain)) { + throw new AssertionError("Simulation drain from tank and actual drain differ.\n" + + "Simulated Drain: " + ftos(drainSim) + ", Actual Drain: " + ftos(actualDrain) + "\n" + + "Tank: " + tankString); + } + return this; + } + + FluidHandlerTester beginSimulation() { + if (this.overlayedFluidHandler != null) { + throw new IllegalStateException("Simulation already begun"); + } + this.overlayedFluidHandler = new OverlayedFluidHandler(this.tank); + return this; + } + + FluidHandlerTester expectContents(@Nonnull FluidStack... optionalFluidStacks) { + if (optionalFluidStacks.length != this.tank.getTanks()) { + throw new IllegalArgumentException("Wrong number of fluids to compare; " + + "expected: " + this.tank.getTanks() + ", provided: " + optionalFluidStacks.length); + } + for (int i = 0; i < optionalFluidStacks.length; i++) { + IMultipleTankHandler.MultiFluidTankEntry tank = this.tank.getTankAt(i); + if (!eq(tank.getFluid(), optionalFluidStacks[i])) { + throw new AssertionError("Contents of the tank don't match expected state.\n" + + "Expected: [\n " + Arrays.stream(optionalFluidStacks) + .map(FluidHandlerTester::ftos) + .collect(Collectors.joining(",\n ")) + "\n]\n" + + "Tank: " + this.tank.toString(true)); + } + } + return this; + } + + static boolean eq(@Nullable FluidStack fluid1, @Nullable FluidStack fluid2) { + if (fluid1 == null || fluid1.amount <= 0) { + return fluid2 == null || fluid2.amount <= 0; + } else { + return fluid1.isFluidEqual(fluid2); + } + } + + static String ftos(@Nullable FluidStack fluid) { + return fluid == null ? "Empty" : fluid.getFluid().getName() + " / " + fluid.amount; + } + } +}