diff --git a/common/src/main/java/dev/architectury/core/block/ArchitecturyLiquidBlock.java b/common/src/main/java/dev/architectury/core/block/ArchitecturyLiquidBlock.java new file mode 100644 index 00000000..96722352 --- /dev/null +++ b/common/src/main/java/dev/architectury/core/block/ArchitecturyLiquidBlock.java @@ -0,0 +1,40 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.block; + +import dev.architectury.platform.Platform; +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.material.FlowingFluid; + +import java.util.function.Supplier; + +public class ArchitecturyLiquidBlock extends LiquidBlock { + public ArchitecturyLiquidBlock(Supplier fluid, Properties properties) { + super(checkPlatform(fluid).get(), properties); + } + + private static T checkPlatform(T obj) { + if (Platform.isForge()) { + throw new IllegalStateException("This class should've been replaced on Forge!"); + } + + return obj; + } +} diff --git a/common/src/main/java/dev/architectury/core/fluid/ArchitecturyFlowingFluid.java b/common/src/main/java/dev/architectury/core/fluid/ArchitecturyFlowingFluid.java new file mode 100644 index 00000000..b36c075b --- /dev/null +++ b/common/src/main/java/dev/architectury/core/fluid/ArchitecturyFlowingFluid.java @@ -0,0 +1,179 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.fluid; + +import dev.architectury.injectables.annotations.ExpectPlatform; +import dev.architectury.platform.Platform; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.material.FlowingFluid; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public abstract class ArchitecturyFlowingFluid extends FlowingFluid { + private final ArchitecturyFluidAttributes attributes; + + ArchitecturyFlowingFluid(ArchitecturyFluidAttributes attributes) { + checkPlatform(null); + this.attributes = attributes; + if (Platform.isFabric()) { + addFabricFluidAttributes(this, attributes); + } + } + + private static T checkPlatform(T obj) { + if (Platform.isForge()) { + throw new IllegalStateException("This class should've been replaced on Forge!"); + } + + return obj; + } + + @ExpectPlatform + private static void addFabricFluidAttributes(FlowingFluid fluid, ArchitecturyFluidAttributes properties) { + throw new AssertionError(); + } + + @Override + public Fluid getFlowing() { + return attributes.getFlowingFluid(); + } + + @Override + public Fluid getSource() { + return attributes.getSourceFluid(); + } + + @Override + protected boolean canConvertToSource() { + return attributes.canConvertToSource(); + } + + @Override + protected void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state) { + // Same implementation as in WaterFluid. + BlockEntity blockEntity = state.hasBlockEntity() ? level.getBlockEntity(pos) : null; + Block.dropResources(state, level, pos, blockEntity); + } + + @Override + protected int getSlopeFindDistance(LevelReader level) { + return attributes.getSlopeFindDistance(level); + } + + @Override + protected int getDropOff(LevelReader level) { + return attributes.getDropOff(level); + } + + @Override + public Item getBucket() { + Item item = attributes.getBucketItem(); + return item == null ? Items.AIR : item; + } + + @Override + protected boolean canBeReplacedWith(FluidState state, BlockGetter level, BlockPos pos, Fluid fluid, Direction direction) { + // Same implementation as in WaterFluid. + return direction == Direction.DOWN && !this.isSame(fluid); + } + + @Override + public int getTickDelay(LevelReader level) { + return attributes.getTickDelay(level); + } + + @Override + protected float getExplosionResistance() { + return attributes.getExplosionResistance(); + } + + @Override + protected BlockState createLegacyBlock(FluidState state) { + LiquidBlock block = attributes.getBlock(); + if (block == null) return Blocks.AIR.defaultBlockState(); + return block.defaultBlockState().setValue(LiquidBlock.LEVEL, getLegacyLevel(state)); + } + + @NotNull + @Override + public Optional getPickupSound() { + return Optional.ofNullable(attributes.getFillSound()); + } + + @Override + public boolean isSame(Fluid fluid) { + return fluid == getSource() || fluid == getFlowing(); + } + + public static class Source extends ArchitecturyFlowingFluid { + public Source(ArchitecturyFluidAttributes attributes) { + super(attributes); + } + + @Override + public int getAmount(FluidState state) { + return 8; + } + + @Override + public boolean isSource(FluidState state) { + return true; + } + } + + public static class Flowing extends ArchitecturyFlowingFluid { + public Flowing(ArchitecturyFluidAttributes attributes) { + super(attributes); + this.registerDefaultState(this.getStateDefinition().any().setValue(LEVEL, 7)); + } + + @Override + protected void createFluidStateDefinition(StateDefinition.Builder builder) { + super.createFluidStateDefinition(builder); + builder.add(LEVEL); + } + + @Override + public int getAmount(FluidState state) { + return state.getValue(LEVEL); + } + + @Override + public boolean isSource(FluidState state) { + return false; + } + } +} diff --git a/common/src/main/java/dev/architectury/core/fluid/ArchitecturyFluidAttributes.java b/common/src/main/java/dev/architectury/core/fluid/ArchitecturyFluidAttributes.java new file mode 100644 index 00000000..af9ca0cf --- /dev/null +++ b/common/src/main/java/dev/architectury/core/fluid/ArchitecturyFluidAttributes.java @@ -0,0 +1,549 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.fluid; + +import dev.architectury.fluid.FluidStack; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Rarity; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.material.Fluid; +import org.jetbrains.annotations.Nullable; + +/** + * Attributes of a fluid. + * + * @see SimpleArchitecturyFluidAttributes + */ +public interface ArchitecturyFluidAttributes { + /** + * Returns the translation key of the name of this fluid. + * + * @param stack the fluid stack, can be {@code null} + * @return the translation key + */ + @Nullable + String getTranslationKey(@Nullable FluidStack stack); + + /** + * Returns the translation key of the name of this fluid. + * + * @return the translation key + */ + @Nullable + default String getTranslationKey() { + return getTranslationKey(null); + } + + /** + * Returns the name of this fluid. + * + * @param stack the fluid stack, can be {@code null} + * @return the name + */ + default Component getName(@Nullable FluidStack stack) { + return new TranslatableComponent(getTranslationKey(stack)); + } + + /** + * Returns the name of this fluid. + * + * @return the name + */ + default Component getName() { + return getName(null); + } + + /** + * Returns the flowing fluid. + * + * @return the flowing fluid + */ + Fluid getFlowingFluid(); + + /** + * Returns the still fluid. + * + * @return the still fluid + */ + Fluid getSourceFluid(); + + /** + * Returns whether this fluid can be converted to a source block when a flowing fluid is adjacent to two source blocks. + * A fluid that can be converted to a source block means that the fluid can be multiplied infinitely. + * + * @return whether this fluid can be converted to a source block + */ + boolean canConvertToSource(); + + /** + * Returns the maximum distance this fluid will consider as a flowable hole candidate. + * + * @param level the level, can be {@code null} + * @return the maximum distance + * @see net.minecraft.world.level.material.WaterFluid#getSlopeFindDistance(LevelReader) + * @see net.minecraft.world.level.material.LavaFluid#getSlopeFindDistance(LevelReader) + */ + int getSlopeFindDistance(@Nullable LevelReader level); + + /** + * Returns the maximum distance this fluid will consider as a flowable hole candidate. + * + * @return the maximum distance + * @see net.minecraft.world.level.material.WaterFluid#getSlopeFindDistance(LevelReader) + * @see net.minecraft.world.level.material.LavaFluid#getSlopeFindDistance(LevelReader) + */ + default int getSlopeFindDistance() { + return getSlopeFindDistance(null); + } + + /** + * Returns the drop in fluid level per block travelled. + * + * @param level the level, can be {@code null} + * @return the drop in fluid level per block travelled + * @see net.minecraft.world.level.material.WaterFluid#getDropOff(LevelReader) + * @see net.minecraft.world.level.material.LavaFluid#getDropOff(LevelReader) + */ + int getDropOff(@Nullable LevelReader level); + + /** + * Returns the drop in fluid level per block travelled. + * + * @return the drop in fluid level per block travelled + * @see net.minecraft.world.level.material.WaterFluid#getDropOff(LevelReader) + * @see net.minecraft.world.level.material.LavaFluid#getDropOff(LevelReader) + */ + default int getDropOff() { + return getDropOff(null); + } + + /** + * Returns the filled bucket item for this fluid. + * + * @return the filled bucket item + */ + @Nullable + Item getBucketItem(); + + /** + * Returns the tick delay between each flow update. + * + * @param level the level, can be {@code null} + * @return the tick delay + * @see net.minecraft.world.level.material.WaterFluid#getTickDelay(LevelReader) + * @see net.minecraft.world.level.material.LavaFluid#getTickDelay(LevelReader) + */ + int getTickDelay(@Nullable LevelReader level); + + /** + * Returns the tick delay between each flow update. + * + * @return the tick delay + * @see net.minecraft.world.level.material.WaterFluid#getTickDelay(LevelReader) + * @see net.minecraft.world.level.material.LavaFluid#getTickDelay(LevelReader) + */ + default int getTickDelay() { + return getTickDelay(null); + } + + /** + * Returns the explosion resistance of this fluid. + * + * @return the explosion resistance + * @see net.minecraft.world.level.material.WaterFluid#getExplosionResistance() + * @see net.minecraft.world.level.material.LavaFluid#getExplosionResistance() + */ + float getExplosionResistance(); + + /** + * Returns the block form of this fluid. + * + * @return the block form + * @see net.minecraft.world.level.block.Blocks#WATER + * @see net.minecraft.world.level.block.Blocks#LAVA + */ + @Nullable + LiquidBlock getBlock(); + + /** + * Returns the texture location of this fluid in its source form. + *

+ * The vanilla water location is {@code "block/water_still"}. + * + * @param stack the fluid stack, can be {@code null} + * @param level the level, can be {@code null} + * @param pos the position, can be {@code null} + * @return the texture location + */ + ResourceLocation getSourceTexture(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos); + + /** + * Returns the texture location of this fluid in its source form. + *

+ * The vanilla water location is {@code "block/water_still"}. + * + * @param stack the fluid stack, can be {@code null} + * @return the texture location + */ + default ResourceLocation getSourceTexture(@Nullable FluidStack stack) { + return getSourceTexture(stack, null, null); + } + + /** + * Returns the texture location of this fluid in its source form. + *

+ * The vanilla water location is {@code "block/water_still"}. + * + * @return the texture location + */ + default ResourceLocation getSourceTexture() { + return getSourceTexture(null); + } + + /** + * Returns the texture location of this fluid in its flowing form. + *

+ * The vanilla water location is {@code "block/water_flow"}. + * + * @param stack the fluid stack, can be {@code null} + * @param level the level, can be {@code null} + * @param pos the position, can be {@code null} + * @return the texture location + */ + ResourceLocation getFlowingTexture(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos); + + /** + * Returns the texture location of this fluid in its flowing form. + *

+ * The vanilla water location is {@code "block/water_flow"}. + * + * @param stack the fluid stack, can be {@code null} + * @return the texture location + */ + default ResourceLocation getFlowingTexture(@Nullable FluidStack stack) { + return getFlowingTexture(stack, null, null); + } + + /** + * Returns the texture location of this fluid in its flowing form. + *

+ * The vanilla water location is {@code "block/water_flow"}. + * + * @return the texture location + */ + default ResourceLocation getFlowingTexture() { + return getFlowingTexture(null); + } + + /** + * Returns the color of the fluid. + * + * @param stack the fluid stack, can be {@code null} + * @param level the level, can be {@code null} + * @param pos the position, can be {@code null} + * @return the color + */ + int getColor(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos); + + /** + * Returns the color of the fluid. + * + * @param stack the fluid stack, can be {@code null} + * @return the color + */ + default int getColor(@Nullable FluidStack stack) { + return getColor(stack, null, null); + } + + /** + * Returns the color of the fluid. + * + * @return the color + */ + default int getColor() { + return getColor(null); + } + + /** + * Returns the luminosity of the fluid, this is between 0 and 15. + * + * @param stack the fluid stack, can be {@code null} + * @param level the level, can be {@code null} + * @param pos the position, can be {@code null} + * @return the luminosity + */ + int getLuminosity(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos); + + /** + * Returns the luminosity of the fluid, this is between 0 and 15. + * + * @param stack the fluid stack, can be {@code null} + * @return the luminosity + */ + default int getLuminosity(@Nullable FluidStack stack) { + return getLuminosity(stack, null, null); + } + + /** + * Returns the luminosity of the fluid, this is between 0 and 15. + * + * @return the luminosity + */ + default int getLuminosity() { + return getLuminosity(null); + } + + /** + * Returns the density of the fluid, this is 1000 for water and 3000 for lava on forge. + * + * @param stack the fluid stack, can be {@code null} + * @param level the level, can be {@code null} + * @param pos the position, can be {@code null} + * @return the density + */ + int getDensity(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos); + + /** + * Returns the density of the fluid, this is 1000 for water and 3000 for lava on forge. + * + * @param stack the fluid stack, can be {@code null} + * @return the density + */ + default int getDensity(@Nullable FluidStack stack) { + return getDensity(stack, null, null); + } + + /** + * Returns the density of the fluid, this is 1000 for water and 3000 for lava on forge. + * + * @return the density + */ + default int getDensity() { + return getDensity(null); + } + + /** + * Returns the temperature of the fluid. + * The temperature is in kelvin, for example, 300 kelvin is equal to room temperature. + * + * @param stack the fluid stack, can be {@code null} + * @param level the level, can be {@code null} + * @param pos the position, can be {@code null} + * @return the temperature + */ + int getTemperature(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos); + + /** + * Returns the temperature of the fluid. + * The temperature is in kelvin, for example, 300 kelvin is equal to room temperature. + * + * @param stack the fluid stack, can be {@code null} + * @return the temperature + */ + default int getTemperature(@Nullable FluidStack stack) { + return getTemperature(stack, null, null); + } + + /** + * Returns the temperature of the fluid. + * The temperature is in kelvin, for example, 300 kelvin is equal to room temperature. + * + * @return the temperature + */ + default int getTemperature() { + return getTemperature(null); + } + + /** + * Returns the viscosity of the fluid. A lower viscosity means that the fluid will flow faster. + * The default value is 1000 for water. + * + * @param stack the fluid stack, can be {@code null} + * @param level the level, can be {@code null} + * @param pos the position, can be {@code null} + * @return the viscosity + */ + int getViscosity(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos); + + /** + * Returns the viscosity of the fluid. A lower viscosity means that the fluid will flow faster. + * The default value is 1000 for water. + * + * @param stack the fluid stack, can be {@code null} + * @return the viscosity + */ + default int getViscosity(@Nullable FluidStack stack) { + return getViscosity(stack, null, null); + } + + /** + * Returns the viscosity of the fluid. A lower viscosity means that the fluid will flow faster. + * The default value is 1000 for water. + * + * @return the viscosity + */ + default int getViscosity() { + return getViscosity(null); + } + + /** + * Returns whether this fluid is lighter than air. This is used to determine whether the fluid should be rendered + * upside down like gas. + * + * @param stack the fluid stack, can be {@code null} + * @param level the level, can be {@code null} + * @param pos the position, can be {@code null} + * @return {@code true} if the fluid is lighter than air + */ + boolean isLighterThanAir(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos); + + /** + * Returns whether this fluid is lighter than air. This is used to determine whether the fluid should be rendered + * upside down like gas. + * + * @param stack the fluid stack, can be {@code null} + * @return {@code true} if the fluid is lighter than air + */ + default boolean isLighterThanAir(@Nullable FluidStack stack) { + return isLighterThanAir(stack, null, null); + } + + /** + * Returns whether this fluid is lighter than air. This is used to determine whether the fluid should be rendered + * upside down like gas. + * + * @return {@code true} if the fluid is lighter than air + */ + default boolean isLighterThanAir() { + return isLighterThanAir(null); + } + + /** + * Returns the rarity of the fluid. + * + * @param stack the fluid stack, can be {@code null} + * @param level the level, can be {@code null} + * @param pos the position, can be {@code null} + * @return the rarity + */ + Rarity getRarity(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos); + + /** + * Returns the rarity of the fluid. + * + * @param stack the fluid stack, can be {@code null} + * @return the rarity + */ + default Rarity getRarity(@Nullable FluidStack stack) { + return getRarity(stack, null, null); + } + + /** + * Returns the rarity of the fluid. + * + * @return the rarity + */ + default Rarity getRarity() { + return getRarity(null); + } + + /** + * Returns the fill sound of the fluid. + * + * @param stack the fluid stack, can be {@code null} + * @param level the level, can be {@code null} + * @param pos the position, can be {@code null} + * @return the fill sound + * @see net.minecraft.sounds.SoundEvents#BUCKET_FILL + * @see net.minecraft.sounds.SoundEvents#BUCKET_FILL_LAVA + */ + @Nullable + SoundEvent getFillSound(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos); + + /** + * Returns the fill sound of the fluid. + * + * @param stack the fluid stack, can be {@code null} + * @return the fill sound + * @see net.minecraft.sounds.SoundEvents#BUCKET_FILL + * @see net.minecraft.sounds.SoundEvents#BUCKET_FILL_LAVA + */ + @Nullable + default SoundEvent getFillSound(@Nullable FluidStack stack) { + return getFillSound(stack, null, null); + } + + /** + * Returns the fill sound of the fluid. + * + * @return the fill sound + * @see net.minecraft.sounds.SoundEvents#BUCKET_FILL + * @see net.minecraft.sounds.SoundEvents#BUCKET_FILL_LAVA + */ + @Nullable + default SoundEvent getFillSound() { + return getFillSound(null); + } + + /** + * Returns the empty sound of the fluid. + * + * @param stack the fluid stack, can be {@code null} + * @param level the level, can be {@code null} + * @param pos the position, can be {@code null} + * @return the empty sound + * @see net.minecraft.sounds.SoundEvents#BUCKET_EMPTY + * @see net.minecraft.sounds.SoundEvents#BUCKET_EMPTY_LAVA + */ + @Nullable + SoundEvent getEmptySound(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos); + + /** + * Returns the empty sound of the fluid. + * + * @param stack the fluid stack, can be {@code null} + * @return the empty sound + * @see net.minecraft.sounds.SoundEvents#BUCKET_EMPTY + * @see net.minecraft.sounds.SoundEvents#BUCKET_EMPTY_LAVA + */ + @Nullable + default SoundEvent getEmptySound(@Nullable FluidStack stack) { + return getEmptySound(stack, null, null); + } + + /** + * Returns the empty sound of the fluid. + * + * @return the empty sound + * @see net.minecraft.sounds.SoundEvents#BUCKET_EMPTY + * @see net.minecraft.sounds.SoundEvents#BUCKET_EMPTY_LAVA + */ + @Nullable + default SoundEvent getEmptySound() { + return getEmptySound(null); + } +} diff --git a/common/src/main/java/dev/architectury/core/fluid/SimpleArchitecturyFluidAttributes.java b/common/src/main/java/dev/architectury/core/fluid/SimpleArchitecturyFluidAttributes.java new file mode 100644 index 00000000..cd85638e --- /dev/null +++ b/common/src/main/java/dev/architectury/core/fluid/SimpleArchitecturyFluidAttributes.java @@ -0,0 +1,366 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.fluid; + +import com.google.common.base.Suppliers; +import dev.architectury.fluid.FluidStack; +import dev.architectury.registry.registries.Registries; +import dev.architectury.registry.registries.RegistrySupplier; +import net.minecraft.Util; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Rarity; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.material.Fluid; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; + +public class SimpleArchitecturyFluidAttributes implements ArchitecturyFluidAttributes { + private final Supplier flowingFluid; + private final Supplier sourceFluid; + private boolean canConvertToSource = false; + private int slopeFindDistance = 4; + private int dropOff = 1; + private Supplier> bucketItem = Optional::empty; + private int tickDelay = 5; + private float explosionResistance = 100.0F; + private Supplier> block = Optional::empty; + @Nullable + private ResourceLocation sourceTexture; + @Nullable + private ResourceLocation flowingTexture; + private int color = 0xffffff; + private int luminosity = 0; + private int density = 1000; + private int temperature = 300; + private int viscosity = 1000; + private boolean lighterThanAir = false; + private Rarity rarity = Rarity.COMMON; + @Nullable + private SoundEvent fillSound = SoundEvents.BUCKET_FILL; + @Nullable + private SoundEvent emptySound = SoundEvents.BUCKET_EMPTY; + private final Supplier defaultTranslationKey = Suppliers.memoize(() -> Util.makeDescriptionId("fluid", Registries.getId(getSourceFluid(), Registry.FLUID_REGISTRY))); + + public static SimpleArchitecturyFluidAttributes ofSupplier(Supplier> flowingFluid, Supplier> sourceFluid) { + return of(() -> flowingFluid.get().get(), () -> sourceFluid.get().get()); + } + + + public static SimpleArchitecturyFluidAttributes of(Supplier flowingFluid, Supplier sourceFluid) { + return new SimpleArchitecturyFluidAttributes(flowingFluid, sourceFluid); + } + + protected SimpleArchitecturyFluidAttributes(Supplier flowingFluid, Supplier sourceFluid) { + this.flowingFluid = flowingFluid; + this.sourceFluid = sourceFluid; + } + + /** + * @see ArchitecturyFluidAttributes#canConvertToSource() + */ + public SimpleArchitecturyFluidAttributes convertToSource(boolean canConvertToSource) { + this.canConvertToSource = canConvertToSource; + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getSlopeFindDistance(LevelReader) + */ + public SimpleArchitecturyFluidAttributes slopeFindDistance(int slopeFindDistance) { + this.slopeFindDistance = slopeFindDistance; + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getDropOff(LevelReader) + */ + public SimpleArchitecturyFluidAttributes dropOff(int dropOff) { + this.dropOff = dropOff; + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getBucketItem() + */ + public SimpleArchitecturyFluidAttributes bucketItemSupplier(Supplier> bucketItem) { + return bucketItem(() -> bucketItem.get().toOptional()); + } + + /** + * @see ArchitecturyFluidAttributes#getBucketItem() + */ + public SimpleArchitecturyFluidAttributes bucketItem(RegistrySupplier bucketItem) { + return bucketItem(bucketItem::toOptional); + } + + /** + * @see ArchitecturyFluidAttributes#getBucketItem() + */ + public SimpleArchitecturyFluidAttributes bucketItem(Supplier> bucketItem) { + this.bucketItem = Objects.requireNonNull(bucketItem); + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getTickDelay(LevelReader) + */ + public SimpleArchitecturyFluidAttributes tickDelay(int tickDelay) { + this.tickDelay = tickDelay; + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getExplosionResistance() + */ + public SimpleArchitecturyFluidAttributes explosionResistance(float explosionResistance) { + this.explosionResistance = explosionResistance; + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getBlock() + */ + public SimpleArchitecturyFluidAttributes blockSupplier(Supplier> block) { + return block(() -> block.get().toOptional()); + } + + /** + * @see ArchitecturyFluidAttributes#getBlock() + */ + public SimpleArchitecturyFluidAttributes block(RegistrySupplier block) { + return block(block::toOptional); + } + + /** + * @see ArchitecturyFluidAttributes#getBlock() + */ + public SimpleArchitecturyFluidAttributes block(Supplier> block) { + this.block = Objects.requireNonNull(block); + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getSourceTexture(FluidStack, BlockAndTintGetter, BlockPos) + */ + public SimpleArchitecturyFluidAttributes sourceTexture(ResourceLocation sourceTexture) { + this.sourceTexture = sourceTexture; + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getFlowingTexture(FluidStack, BlockAndTintGetter, BlockPos) + */ + public SimpleArchitecturyFluidAttributes flowingTexture(ResourceLocation flowingTexture) { + this.flowingTexture = flowingTexture; + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getColor(FluidStack, BlockAndTintGetter, BlockPos) + */ + public SimpleArchitecturyFluidAttributes color(int color) { + this.color = color; + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getLuminosity(FluidStack, BlockAndTintGetter, BlockPos) + */ + public SimpleArchitecturyFluidAttributes luminosity(int luminosity) { + this.luminosity = luminosity; + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getDensity(FluidStack, BlockAndTintGetter, BlockPos) + */ + public SimpleArchitecturyFluidAttributes density(int density) { + this.density = density; + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getTemperature(FluidStack, BlockAndTintGetter, BlockPos) + */ + public SimpleArchitecturyFluidAttributes temperature(int temperature) { + this.temperature = temperature; + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getViscosity(FluidStack, BlockAndTintGetter, BlockPos) + */ + public SimpleArchitecturyFluidAttributes viscosity(int viscosity) { + this.viscosity = viscosity; + return this; + } + + /** + * @see ArchitecturyFluidAttributes#isLighterThanAir(FluidStack, BlockAndTintGetter, BlockPos) + */ + public SimpleArchitecturyFluidAttributes lighterThanAir(boolean lighterThanAir) { + this.lighterThanAir = lighterThanAir; + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getRarity(FluidStack, BlockAndTintGetter, BlockPos) + */ + public SimpleArchitecturyFluidAttributes rarity(Rarity rarity) { + this.rarity = rarity; + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getFillSound(FluidStack, BlockAndTintGetter, BlockPos) + */ + public SimpleArchitecturyFluidAttributes fillSound(SoundEvent fillSound) { + this.fillSound = fillSound; + return this; + } + + /** + * @see ArchitecturyFluidAttributes#getEmptySound(FluidStack, BlockAndTintGetter, BlockPos) + */ + public SimpleArchitecturyFluidAttributes emptySound(SoundEvent emptySound) { + this.emptySound = emptySound; + return this; + } + + @Override + @Nullable + public String getTranslationKey(@Nullable FluidStack stack) { + return defaultTranslationKey.get(); + } + + @Override + public final Fluid getFlowingFluid() { + return flowingFluid.get(); + } + + @Override + public final Fluid getSourceFluid() { + return sourceFluid.get(); + } + + @Override + public boolean canConvertToSource() { + return canConvertToSource; + } + + @Override + public int getSlopeFindDistance(@Nullable LevelReader level) { + return slopeFindDistance; + } + + @Override + public int getDropOff(@Nullable LevelReader level) { + return dropOff; + } + + @Override + @Nullable + public Item getBucketItem() { + return bucketItem.get().orElse(null); + } + + @Override + public int getTickDelay(@Nullable LevelReader level) { + return tickDelay; + } + + @Override + public float getExplosionResistance() { + return explosionResistance; + } + + @Override + @Nullable + public LiquidBlock getBlock() { + return block.get().orElse(null); + } + + @Override + public ResourceLocation getSourceTexture(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos) { + return sourceTexture; + } + + @Override + public ResourceLocation getFlowingTexture(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos) { + return flowingTexture; + } + + @Override + public int getColor(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos) { + return color; + } + + @Override + public int getLuminosity(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos) { + return luminosity; + } + + @Override + public int getDensity(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos) { + return density; + } + + @Override + public int getTemperature(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos) { + return temperature; + } + + @Override + public int getViscosity(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos) { + return viscosity; + } + + @Override + public boolean isLighterThanAir(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos) { + return lighterThanAir; + } + + @Override + public Rarity getRarity(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos) { + return rarity; + } + + @Override + @Nullable + public SoundEvent getFillSound(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos) { + return fillSound; + } + + @Override + @Nullable + public SoundEvent getEmptySound(@Nullable FluidStack stack, @Nullable BlockAndTintGetter level, @Nullable BlockPos pos) { + return emptySound; + } +} diff --git a/common/src/main/java/dev/architectury/core/item/ArchitecturyBucketItem.java b/common/src/main/java/dev/architectury/core/item/ArchitecturyBucketItem.java new file mode 100644 index 00000000..6b03521e --- /dev/null +++ b/common/src/main/java/dev/architectury/core/item/ArchitecturyBucketItem.java @@ -0,0 +1,45 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.item; + +import dev.architectury.hooks.fluid.FluidBucketHooks; +import dev.architectury.platform.Platform; +import net.minecraft.world.item.BucketItem; +import net.minecraft.world.level.material.Fluid; + +import java.util.function.Supplier; + +public class ArchitecturyBucketItem extends BucketItem { + public ArchitecturyBucketItem(Supplier fluid, Properties properties) { + super(checkPlatform(fluid).get(), properties); + } + + private static T checkPlatform(T obj) { + if (Platform.isForge()) { + throw new IllegalStateException("This class should've been replaced on Forge!"); + } + + return obj; + } + + public final Fluid getContainedFluid() { + return FluidBucketHooks.getFluid(this); + } +} diff --git a/common/src/main/java/dev/architectury/core/item/ArchitecturyMobBucketItem.java b/common/src/main/java/dev/architectury/core/item/ArchitecturyMobBucketItem.java new file mode 100644 index 00000000..b3d3a5b7 --- /dev/null +++ b/common/src/main/java/dev/architectury/core/item/ArchitecturyMobBucketItem.java @@ -0,0 +1,42 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.item; + +import dev.architectury.platform.Platform; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.MobBucketItem; +import net.minecraft.world.level.material.Fluid; + +import java.util.function.Supplier; + +public class ArchitecturyMobBucketItem extends MobBucketItem { + public ArchitecturyMobBucketItem(Supplier> entity, Supplier fluid, Supplier sound, Properties properties) { + super(checkPlatform(entity).get(), fluid.get(), sound.get(), properties); + } + + private static T checkPlatform(T obj) { + if (Platform.isForge()) { + throw new IllegalStateException("This class should've been replaced on Forge!"); + } + + return obj; + } +} diff --git a/common/src/main/java/dev/architectury/core/item/ArchitecturyRecordItem.java b/common/src/main/java/dev/architectury/core/item/ArchitecturyRecordItem.java new file mode 100644 index 00000000..314494d6 --- /dev/null +++ b/common/src/main/java/dev/architectury/core/item/ArchitecturyRecordItem.java @@ -0,0 +1,46 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.item; + +import dev.architectury.registry.registries.RegistrySupplier; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.item.RecordItem; + +public class ArchitecturyRecordItem extends RecordItem { + private final RegistrySupplier sound; + + public ArchitecturyRecordItem(int analogOutput, RegistrySupplier sound, Properties properties) { + super(analogOutput, sound.orElse(null), properties); + this.sound = sound; + + if (!sound.isPresent()) { + RecordItem.BY_NAME.remove(null); + + sound.listen(soundEvent -> { + RecordItem.BY_NAME.put(soundEvent, this); + }); + } + } + + @Override + public SoundEvent getSound() { + return sound.get(); + } +} diff --git a/common/src/main/java/dev/architectury/fluid/FluidStack.java b/common/src/main/java/dev/architectury/fluid/FluidStack.java index b3b4fa4b..81b583f8 100644 --- a/common/src/main/java/dev/architectury/fluid/FluidStack.java +++ b/common/src/main/java/dev/architectury/fluid/FluidStack.java @@ -20,29 +20,62 @@ package dev.architectury.fluid; import dev.architectury.hooks.fluid.FluidStackHooks; -import dev.architectury.utils.NbtType; +import dev.architectury.injectables.annotations.ExpectPlatform; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.Fluids; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import java.util.Objects; +import java.util.function.Function; import java.util.function.Supplier; public final class FluidStack { + private static final FluidStackAdapter ADAPTER = adapt(FluidStack::getValue, FluidStack::new); private static final FluidStack EMPTY = create(Fluids.EMPTY, 0); - private long amount; - @Nullable - private CompoundTag tag; - private Supplier fluid; + + private Object value; private FluidStack(Supplier fluid, long amount, CompoundTag tag) { - this.fluid = Objects.requireNonNull(fluid); - this.amount = amount; - this.tag = tag == null ? null : tag.copy(); + this(ADAPTER.create(fluid, amount, tag)); + } + + private FluidStack(Object value) { + this.value = Objects.requireNonNull(value); + } + + private Object getValue() { + return value; + } + + @ExpectPlatform + private static FluidStackAdapter adapt(Function toValue, Function fromValue) { + throw new AssertionError(); + } + + @ApiStatus.Internal + public interface FluidStackAdapter { + T create(Supplier fluid, long amount, CompoundTag tag); + + Supplier getRawFluidSupplier(T object); + + Fluid getFluid(T object); + + long getAmount(T object); + + void setAmount(T object, long amount); + + CompoundTag getTag(T value); + + void setTag(T value, CompoundTag tag); + + T copy(T value); + + int hashCode(T value); } public static FluidStack empty() { @@ -73,67 +106,72 @@ public final class FluidStack { return FluidStackHooks.bucketAmount(); } - public final Fluid getFluid() { + public Fluid getFluid() { return isEmpty() ? Fluids.EMPTY : getRawFluid(); } @Nullable - public final Fluid getRawFluid() { - return fluid.get(); + public Fluid getRawFluid() { + return ADAPTER.getFluid(value); } - public final Supplier getRawFluidSupplier() { - return fluid; + public Supplier getRawFluidSupplier() { + return ADAPTER.getRawFluidSupplier(value); } public boolean isEmpty() { - return getRawFluid() == Fluids.EMPTY || amount <= 0; + return getRawFluid() == Fluids.EMPTY || ADAPTER.getAmount(value) <= 0; } public long getAmount() { - return isEmpty() ? 0 : amount; + return isEmpty() ? 0 : ADAPTER.getAmount(value); } public void setAmount(long amount) { - this.amount = amount; + ADAPTER.setAmount(value, amount); } public void grow(long amount) { - setAmount(this.amount + amount); + setAmount(getAmount() + amount); } public void shrink(long amount) { - setAmount(this.amount - amount); + setAmount(getAmount() - amount); } public boolean hasTag() { - return tag != null; + return getTag() != null; } @Nullable public CompoundTag getTag() { - return tag; + return ADAPTER.getTag(value); } public void setTag(@Nullable CompoundTag tag) { - this.tag = tag; + ADAPTER.setTag(value, tag); } public CompoundTag getOrCreateTag() { - if (tag == null) - setTag(new CompoundTag()); + CompoundTag tag = getTag(); + if (tag == null) { + tag = new CompoundTag(); + setTag(tag); + return tag; + } return tag; } @Nullable public CompoundTag getChildTag(String childName) { + CompoundTag tag = getTag(); if (tag == null) return null; return tag.getCompound(childName); } public CompoundTag getOrCreateChildTag(String childName) { - getOrCreateTag(); + CompoundTag tag = getOrCreateTag(); var child = tag.getCompound(childName); if (!tag.contains(childName, Tag.TAG_COMPOUND)) { tag.put(childName, child); @@ -142,6 +180,7 @@ public final class FluidStack { } public void removeChildTag(String childName) { + CompoundTag tag = getTag(); if (tag != null) tag.remove(childName); } @@ -155,21 +194,16 @@ public final class FluidStack { } public FluidStack copy() { - return new FluidStack(fluid, amount, tag); + return new FluidStack(ADAPTER.copy(value)); } @Override - public final int hashCode() { - var code = 1; - code = 31 * code + getFluid().hashCode(); - code = 31 * code + Long.hashCode(amount); - if (tag != null) - code = 31 * code + tag.hashCode(); - return code; + public int hashCode() { + return ADAPTER.hashCode(value); } @Override - public final boolean equals(Object o) { + public boolean equals(Object o) { if (!(o instanceof FluidStack)) { return false; } @@ -180,8 +214,14 @@ public final class FluidStack { return getFluid() == other.getFluid() && getAmount() == other.getAmount() && isTagEqual(other); } - private boolean isTagEqual(FluidStack other) { - return tag == null ? other.tag == null : other.tag != null && tag.equals(other.tag); + public boolean isFluidEqual(FluidStack other) { + return getFluid() == other.getFluid(); + } + + public boolean isTagEqual(FluidStack other) { + var tag = getTag(); + var otherTag = other.getTag(); + return Objects.equals(tag, otherTag); } public static FluidStack read(FriendlyByteBuf buf) { @@ -199,4 +239,15 @@ public final class FluidStack { public CompoundTag write(CompoundTag tag) { return FluidStackHooks.write(this, tag); } + + public FluidStack copyWithAmount(long amount) { + if (isEmpty()) return this; + return new FluidStack(getRawFluidSupplier(), amount, getTag()); + } + + @ApiStatus.Internal + public static void init() { + // classloading my beloved 😍 + // please don't use this by the way + } } diff --git a/common/src/main/java/dev/architectury/hooks/item/tool/HoeItemHooks.java b/common/src/main/java/dev/architectury/hooks/item/tool/HoeItemHooks.java index 4f2c716f..78617c36 100644 --- a/common/src/main/java/dev/architectury/hooks/item/tool/HoeItemHooks.java +++ b/common/src/main/java/dev/architectury/hooks/item/tool/HoeItemHooks.java @@ -21,12 +21,16 @@ package dev.architectury.hooks.item.tool; import com.google.common.collect.ImmutableMap; import com.mojang.datafixers.util.Pair; +import dev.architectury.injectables.annotations.ExpectPlatform; import net.minecraft.world.item.HoeItem; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.ApiStatus; import java.util.HashMap; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; public final class HoeItemHooks { @@ -48,10 +52,25 @@ public final class HoeItemHooks { * @param predicate context predicate * @param action action to run */ + @Deprecated(forRemoval = true) + @ApiStatus.ScheduledForRemoval public static void addTillable(Block input, Predicate predicate, Consumer action) { if (HoeItem.TILLABLES instanceof ImmutableMap) { HoeItem.TILLABLES = new HashMap<>(HoeItem.TILLABLES); } HoeItem.TILLABLES.put(input, new Pair<>(predicate, action)); } + + /** + * Adds a new tilling action. + * + * @param input input block + * @param predicate context predicate + * @param action action to run + * @param newState the new block state + */ + @ExpectPlatform + public static void addTillable(Block input, Predicate predicate, Consumer action, Function newState) { + throw new AssertionError(); + } } diff --git a/common/src/main/java/dev/architectury/utils/Amount.java b/common/src/main/java/dev/architectury/utils/Amount.java new file mode 100644 index 00000000..1ea955e9 --- /dev/null +++ b/common/src/main/java/dev/architectury/utils/Amount.java @@ -0,0 +1,38 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.utils; + +public class Amount { + /** + * Converts a long to an int while dropping overflowed values. + * + * @param amount the long to convert + * @return the int value + */ + public static int toInt(long amount) { + if (amount >= Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } else if (amount <= Integer.MIN_VALUE) { + return Integer.MIN_VALUE; + } + + return (int) amount; + } +} diff --git a/common/src/main/resources/architectury.accessWidener b/common/src/main/resources/architectury.accessWidener index 4940f155..658a715b 100644 --- a/common/src/main/resources/architectury.accessWidener +++ b/common/src/main/resources/architectury.accessWidener @@ -120,6 +120,7 @@ transitive-accessible class net/minecraft/client/renderer/RenderType$OutlineProp accessible field net/minecraft/world/item/SpawnEggItem BY_ID Ljava/util/Map; accessible field net/minecraft/world/item/SpawnEggItem defaultType Lnet/minecraft/world/entity/EntityType; mutable field net/minecraft/world/item/SpawnEggItem defaultType Lnet/minecraft/world/entity/EntityType; +accessible field net/minecraft/world/item/RecordItem BY_NAME Ljava/util/Map; accessible field net/minecraft/client/particle/ParticleEngine textureAtlas Lnet/minecraft/client/renderer/texture/TextureAtlas; accessible class net/minecraft/client/particle/ParticleEngine$MutableSpriteSet accessible field net/minecraft/client/particle/ParticleEngine$MutableSpriteSet sprites Ljava/util/List; diff --git a/fabric/build.gradle b/fabric/build.gradle index 7c1e8bb4..a491093f 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -102,7 +102,7 @@ curseforge { if (project.hasProperty("CURSE_API_KEY") || System.getenv("CURSE_API_KEY") != null) { apiKey = project.hasProperty("CURSE_API_KEY") ? project.property("CURSE_API_KEY") : System.getenv("CURSE_API_KEY") project { - id = "419697" + id = "419699" releaseType = "$rootProject.cf_type" changelogType = "html" changelog = releaseChangelog() diff --git a/fabric/src/main/java/dev/architectury/core/fluid/fabric/ArchitecturyFlowingFluidImpl.java b/fabric/src/main/java/dev/architectury/core/fluid/fabric/ArchitecturyFlowingFluidImpl.java new file mode 100644 index 00000000..23d51759 --- /dev/null +++ b/fabric/src/main/java/dev/architectury/core/fluid/fabric/ArchitecturyFlowingFluidImpl.java @@ -0,0 +1,42 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.fluid.fabric; + +import dev.architectury.core.fluid.ArchitecturyFluidAttributes; +import dev.architectury.utils.Env; +import dev.architectury.utils.EnvExecutor; +import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry; +import net.fabricmc.fabric.api.transfer.v1.client.fluid.FluidVariantRendering; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariantAttributes; +import net.minecraft.world.level.material.FlowingFluid; + +public class ArchitecturyFlowingFluidImpl { + public static void addFabricFluidAttributes(FlowingFluid fluid, ArchitecturyFluidAttributes attributes) { + FluidVariantAttributes.register(fluid, new ArchitecturyFluidAttributesFabric(attributes)); + EnvExecutor.runInEnv(Env.CLIENT, () -> () -> Client.run(fluid, attributes)); + } + + private static class Client { + private static void run(FlowingFluid fluid, ArchitecturyFluidAttributes attributes) { + FluidVariantRendering.register(fluid, new ArchitecturyFluidRenderingFabric(attributes)); + FluidRenderHandlerRegistry.INSTANCE.register(fluid, new ArchitecturyFluidRenderingFabric(attributes)); + } + } +} diff --git a/fabric/src/main/java/dev/architectury/core/fluid/fabric/ArchitecturyFluidAttributesFabric.java b/fabric/src/main/java/dev/architectury/core/fluid/fabric/ArchitecturyFluidAttributesFabric.java new file mode 100644 index 00000000..9f6b0ad5 --- /dev/null +++ b/fabric/src/main/java/dev/architectury/core/fluid/fabric/ArchitecturyFluidAttributesFabric.java @@ -0,0 +1,76 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.fluid.fabric; + +import dev.architectury.core.fluid.ArchitecturyFluidAttributes; +import dev.architectury.fluid.FluidStack; +import dev.architectury.hooks.fluid.fabric.FluidStackHooksFabric; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariantAttributeHandler; +import net.minecraft.network.chat.Component; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +@SuppressWarnings("UnstableApiUsage") +class ArchitecturyFluidAttributesFabric implements FluidVariantAttributeHandler { + private final ArchitecturyFluidAttributes attributes; + + public ArchitecturyFluidAttributesFabric(ArchitecturyFluidAttributes attributes) { + this.attributes = attributes; + } + + @Override + public Component getName(FluidVariant variant) { + return attributes.getName(FluidStackHooksFabric.fromFabric(variant, FluidStack.bucketAmount())); + } + + @Override + public Optional getFillSound(FluidVariant variant) { + return Optional.ofNullable(attributes.getFillSound(FluidStackHooksFabric.fromFabric(variant, FluidStack.bucketAmount()))); + } + + @Override + public Optional getEmptySound(FluidVariant variant) { + return Optional.ofNullable(attributes.getEmptySound(FluidStackHooksFabric.fromFabric(variant, FluidStack.bucketAmount()))); + } + + @Override + public int getLuminance(FluidVariant variant) { + return attributes.getLuminosity(FluidStackHooksFabric.fromFabric(variant, FluidStack.bucketAmount())); + } + + @Override + public int getTemperature(FluidVariant variant) { + return attributes.getTemperature(FluidStackHooksFabric.fromFabric(variant, FluidStack.bucketAmount())); + } + + @Override + public int getViscosity(FluidVariant variant, @Nullable Level world) { + return attributes.getViscosity(FluidStackHooksFabric.fromFabric(variant, FluidStack.bucketAmount()), world, null); + } + + @Override + public boolean isLighterThanAir(FluidVariant variant) { + return attributes.isLighterThanAir(FluidStackHooksFabric.fromFabric(variant, FluidStack.bucketAmount())); + } +} diff --git a/fabric/src/main/java/dev/architectury/core/fluid/fabric/ArchitecturyFluidRenderingFabric.java b/fabric/src/main/java/dev/architectury/core/fluid/fabric/ArchitecturyFluidRenderingFabric.java new file mode 100644 index 00000000..16923ee0 --- /dev/null +++ b/fabric/src/main/java/dev/architectury/core/fluid/fabric/ArchitecturyFluidRenderingFabric.java @@ -0,0 +1,80 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.fluid.fabric; + +import dev.architectury.core.fluid.ArchitecturyFluidAttributes; +import dev.architectury.fluid.FluidStack; +import dev.architectury.hooks.fluid.fabric.FluidStackHooksFabric; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler; +import net.fabricmc.fabric.api.transfer.v1.client.fluid.FluidVariantRenderHandler; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.material.FluidState; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Function; + +@SuppressWarnings("UnstableApiUsage") +@Environment(EnvType.CLIENT) +class ArchitecturyFluidRenderingFabric implements FluidVariantRenderHandler, FluidRenderHandler { + private final ArchitecturyFluidAttributes attributes; + private final TextureAtlasSprite[] sprites = new TextureAtlasSprite[2]; + private final TextureAtlasSprite[] spritesOther = new TextureAtlasSprite[2]; + + public ArchitecturyFluidRenderingFabric(ArchitecturyFluidAttributes attributes) { + this.attributes = attributes; + } + + @Override + @Nullable + public TextureAtlasSprite[] getSprites(FluidVariant variant) { + FluidStack stack = FluidStackHooksFabric.fromFabric(variant, FluidStack.bucketAmount()); + Function atlas = Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS); + sprites[0] = atlas.apply(attributes.getSourceTexture(stack)); + sprites[1] = atlas.apply(attributes.getFlowingTexture(stack)); + return sprites; + } + + @Override + public int getColor(FluidVariant variant, @Nullable BlockAndTintGetter view, @Nullable BlockPos pos) { + return attributes.getColor(FluidStackHooksFabric.fromFabric(variant, FluidStack.bucketAmount()), view, pos); + } + + @Override + public TextureAtlasSprite[] getFluidSprites(@Nullable BlockAndTintGetter view, @Nullable BlockPos pos, FluidState state) { + FluidStack stack = FluidStack.create(state.getType(), FluidStack.bucketAmount()); + Function atlas = Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS); + spritesOther[0] = atlas.apply(attributes.getSourceTexture(stack, view, pos)); + spritesOther[1] = atlas.apply(attributes.getFlowingTexture(stack, view, pos)); + return spritesOther; + } + + @Override + public int getFluidColor(@Nullable BlockAndTintGetter view, @Nullable BlockPos pos, FluidState state) { + return attributes.getColor(FluidStack.create(state.getType(), FluidStack.bucketAmount()), view, pos); + } +} diff --git a/fabric/src/main/java/dev/architectury/fluid/fabric/FluidStackImpl.java b/fabric/src/main/java/dev/architectury/fluid/fabric/FluidStackImpl.java new file mode 100644 index 00000000..9d90cf97 --- /dev/null +++ b/fabric/src/main/java/dev/architectury/fluid/fabric/FluidStackImpl.java @@ -0,0 +1,116 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.fluid.fabric; + +import dev.architectury.fluid.FluidStack; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.material.FlowingFluid; +import net.minecraft.world.level.material.Fluid; +import org.jetbrains.annotations.ApiStatus; + +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; + +@ApiStatus.Internal +@SuppressWarnings("UnstableApiUsage") +public enum FluidStackImpl implements FluidStack.FluidStackAdapter { + INSTANCE; + + static { + dev.architectury.fluid.FluidStack.init(); + } + + public static Function toValue; + public static Function fromValue; + + public static FluidStack.FluidStackAdapter adapt(Function toValue, Function fromValue) { + FluidStackImpl.toValue = toValue; + FluidStackImpl.fromValue = fromValue; + return (FluidStack.FluidStackAdapter) (FluidStack.FluidStackAdapter) INSTANCE; + } + + public static class Pair { + public FluidVariant variant; + public long amount; + + public Pair(FluidVariant variant, long amount) { + this.variant = variant; + this.amount = amount; + } + } + + @Override + public FluidStackImpl.Pair create(Supplier fluid, long amount, CompoundTag tag) { + Fluid fluidType = Objects.requireNonNull(fluid).get(); + if (fluidType instanceof FlowingFluid flowingFluid) { + fluidType = flowingFluid.getSource(); + } + return new Pair(FluidVariant.of(fluidType, tag == null ? null : tag.copy()), amount); + } + + @Override + public Supplier getRawFluidSupplier(FluidStackImpl.Pair object) { + return object.variant::getFluid; + } + + @Override + public Fluid getFluid(FluidStackImpl.Pair object) { + return object.variant.getFluid(); + } + + @Override + public long getAmount(FluidStackImpl.Pair object) { + return object.amount; + } + + @Override + public void setAmount(FluidStackImpl.Pair object, long amount) { + object.amount = amount; + } + + @Override + public CompoundTag getTag(FluidStackImpl.Pair value) { + return value.variant.getNbt(); + } + + @Override + public void setTag(FluidStackImpl.Pair value, CompoundTag tag) { + value.variant = FluidVariant.of(value.variant.getFluid(), tag); + } + + @Override + public FluidStackImpl.Pair copy(FluidStackImpl.Pair value) { + return new Pair(FluidVariant.of(value.variant.getFluid(), value.variant.copyNbt()), value.amount); + } + + @Override + public int hashCode(FluidStackImpl.Pair value) { + var pair = (Pair) value; + var code = 1; + code = 31 * code + pair.variant.getFluid().hashCode(); + code = 31 * code + Long.hashCode(pair.amount); + var tag = pair.variant.getNbt(); + if (tag != null) + code = 31 * code + tag.hashCode(); + return code; + } +} diff --git a/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksFabric.java b/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksFabric.java new file mode 100644 index 00000000..83cf94aa --- /dev/null +++ b/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksFabric.java @@ -0,0 +1,43 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.fluid.fabric; + +import dev.architectury.fluid.FluidStack; +import dev.architectury.fluid.fabric.FluidStackImpl; +import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.fabricmc.fabric.api.transfer.v1.storage.StorageView; + +@SuppressWarnings("UnstableApiUsage") +public final class FluidStackHooksFabric { + private FluidStackHooksFabric() { + } + + public static FluidStack fromFabric(StorageView storageView) { + return fromFabric(storageView.getResource(), storageView.getAmount()); + } + + public static FluidStack fromFabric(FluidVariant variant, long amount) { + return FluidStackImpl.fromValue.apply(new FluidStackImpl.Pair(variant, amount)); + } + + public static FluidVariant toFabric(FluidStack stack) { + return ((FluidStackImpl.Pair) FluidStackImpl.toValue.apply(stack)).variant; + } +} \ No newline at end of file diff --git a/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksImpl.java b/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksImpl.java index 53aded69..f11a6804 100644 --- a/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksImpl.java +++ b/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksImpl.java @@ -44,7 +44,7 @@ import java.util.Objects; public class FluidStackHooksImpl { public static Component getName(FluidStack stack) { - return FluidVariantAttributes.getName(FluidVariant.of(stack.getFluid(), stack.getTag())); + return FluidVariantAttributes.getName(FluidStackHooksFabric.toFabric(stack)); } public static String getTranslationKey(FluidStack stack) { @@ -188,7 +188,7 @@ public class FluidStackHooksImpl { } public static int getLuminosity(FluidStack fluid, @Nullable Level level, @Nullable BlockPos pos) { - return FluidVariantAttributes.getLuminance(FluidVariant.of(fluid.getFluid(), fluid.getTag())); + return FluidVariantAttributes.getLuminance(FluidStackHooksFabric.toFabric(fluid)); } public static int getLuminosity(Fluid fluid, @Nullable Level level, @Nullable BlockPos pos) { @@ -196,7 +196,7 @@ public class FluidStackHooksImpl { } public static int getTemperature(FluidStack fluid, @Nullable Level level, @Nullable BlockPos pos) { - return FluidVariantAttributes.getTemperature(FluidVariant.of(fluid.getFluid(), fluid.getTag())); + return FluidVariantAttributes.getTemperature(FluidStackHooksFabric.toFabric(fluid)); } public static int getTemperature(Fluid fluid, @Nullable Level level, @Nullable BlockPos pos) { @@ -204,7 +204,7 @@ public class FluidStackHooksImpl { } public static int getViscosity(FluidStack fluid, @Nullable Level level, @Nullable BlockPos pos) { - return FluidVariantAttributes.getViscosity(FluidVariant.of(fluid.getFluid(), fluid.getTag()), level); + return FluidVariantAttributes.getViscosity(FluidStackHooksFabric.toFabric(fluid), level); } public static int getViscosity(Fluid fluid, @Nullable Level level, @Nullable BlockPos pos) { diff --git a/fabric/src/main/java/dev/architectury/hooks/item/tool/fabric/HoeItemHooksImpl.java b/fabric/src/main/java/dev/architectury/hooks/item/tool/fabric/HoeItemHooksImpl.java new file mode 100644 index 00000000..b1562249 --- /dev/null +++ b/fabric/src/main/java/dev/architectury/hooks/item/tool/fabric/HoeItemHooksImpl.java @@ -0,0 +1,26 @@ +package dev.architectury.hooks.item.tool.fabric; + +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.util.Pair; +import net.minecraft.world.item.HoeItem; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.HashMap; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +public class HoeItemHooksImpl { + public static void addTillable(Block input, Predicate predicate, Consumer action, Function newState) { + if (HoeItem.TILLABLES instanceof ImmutableMap) { + HoeItem.TILLABLES = new HashMap<>(HoeItem.TILLABLES); + } + HoeItem.TILLABLES.put(input, new Pair<>(predicate, useOnContext -> { + action.accept(useOnContext); + BlockState state = newState.apply(useOnContext); + useOnContext.getLevel().setBlock(useOnContext.getClickedPos(), state, 11); + })); + } +} diff --git a/fabric/src/main/java/dev/architectury/registry/registries/fabric/RegistriesImpl.java b/fabric/src/main/java/dev/architectury/registry/registries/fabric/RegistriesImpl.java index 0f4e9d6f..2110ee75 100644 --- a/fabric/src/main/java/dev/architectury/registry/registries/fabric/RegistriesImpl.java +++ b/fabric/src/main/java/dev/architectury/registry/registries/fabric/RegistriesImpl.java @@ -19,6 +19,7 @@ package dev.architectury.registry.registries.fabric; +import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.base.Suppliers; import com.google.common.collect.HashMultimap; @@ -34,6 +35,7 @@ import net.fabricmc.fabric.api.event.registry.RegistryAttribute; import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback; import net.minecraft.core.MappedRegistry; import net.minecraft.core.Registry; +import net.minecraft.data.BuiltinRegistries; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.Nullable; @@ -48,7 +50,8 @@ public class RegistriesImpl { private static void listen(ResourceKey resourceKey, ResourceLocation id, Consumer listener) { if (LISTENED_REGISTRIES.add(resourceKey)) { - RegistryEntryAddedCallback.event(Registry.REGISTRY.get(resourceKey.location())).register((rawId, entryId, object) -> { + Registry registry = MoreObjects.firstNonNull(Registry.REGISTRY.get(resourceKey.location()), BuiltinRegistries.REGISTRY.get(resourceKey.location())); + RegistryEntryAddedCallback.event(registry).register((rawId, entryId, object) -> { RegistryEntryId registryEntryId = new RegistryEntryId<>(resourceKey, entryId); for (Consumer consumer : LISTENERS.get(registryEntryId)) { ((Consumer) consumer).accept(object); @@ -67,7 +70,7 @@ public class RegistriesImpl { public static ResourceLocation getId(T object, ResourceKey> fallback) { if (fallback == null) return null; - return getId(object, (Registry) Registry.REGISTRY.get(fallback.location())); + return getId(object, (Registry) MoreObjects.firstNonNull(Registry.REGISTRY.get(fallback.location()), BuiltinRegistries.REGISTRY.get(fallback.location()))); } public static ResourceLocation getId(T object, Registry fallback) { @@ -85,7 +88,7 @@ public class RegistriesImpl { @Override public Registrar get(ResourceKey> key) { - return new RegistrarImpl<>(modId, (Registry) Registry.REGISTRY.get(key.location())); + return new RegistrarImpl<>(modId, (Registry) MoreObjects.firstNonNull(Registry.REGISTRY.get(key.location()), BuiltinRegistries.REGISTRY.get(key.location()))); } @Override diff --git a/forge/build.gradle b/forge/build.gradle index bb7005a6..d91b4213 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -49,6 +49,17 @@ shadowJar { configurations = [project.configurations.shadowCommon] classifier "dev-shadow" + + // Replace classes with forge's version + exclude "dev/architectury/core/block/ArchitecturyLiquidBlock.class" + exclude "dev/architectury/core/fluid/ArchitecturyFlowingFluid.class" + exclude 'dev/architectury/core/fluid/ArchitecturyFlowingFluid$Source.class' + exclude 'dev/architectury/core/fluid/ArchitecturyFlowingFluid$Flowing.class' + exclude 'dev/architectury/core/item/ArchitecturyBucketItem.class' + exclude 'dev/architectury/core/item/ArchitecturyMobBucketItem.class' + relocate "dev.architectury.core.block.forge.imitator", "dev.architectury.core.block" + relocate "dev.architectury.core.fluid.forge.imitator", "dev.architectury.core.fluid" + relocate "dev.architectury.core.item.forge.imitator", "dev.architectury.core.item" } remapJar { diff --git a/forge/src/main/java/dev/architectury/core/block/forge/imitator/ArchitecturyLiquidBlock.java b/forge/src/main/java/dev/architectury/core/block/forge/imitator/ArchitecturyLiquidBlock.java new file mode 100644 index 00000000..45daccce --- /dev/null +++ b/forge/src/main/java/dev/architectury/core/block/forge/imitator/ArchitecturyLiquidBlock.java @@ -0,0 +1,31 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.block.forge.imitator; + +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.material.FlowingFluid; + +import java.util.function.Supplier; + +public class ArchitecturyLiquidBlock extends LiquidBlock { + public ArchitecturyLiquidBlock(Supplier fluid, Properties properties) { + super(fluid, properties); + } +} \ No newline at end of file diff --git a/forge/src/main/java/dev/architectury/core/fluid/forge/imitator/ArchitecturyFlowingFluid.java b/forge/src/main/java/dev/architectury/core/fluid/forge/imitator/ArchitecturyFlowingFluid.java new file mode 100644 index 00000000..7b3440d5 --- /dev/null +++ b/forge/src/main/java/dev/architectury/core/fluid/forge/imitator/ArchitecturyFlowingFluid.java @@ -0,0 +1,179 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.fluid.forge.imitator; + +import com.google.common.base.MoreObjects; +import dev.architectury.core.fluid.ArchitecturyFluidAttributes; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraftforge.fluids.FluidAttributes; +import net.minecraftforge.fluids.ForgeFlowingFluid; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public abstract class ArchitecturyFlowingFluid extends ForgeFlowingFluid { + private final ArchitecturyFluidAttributes attributes; + + ArchitecturyFlowingFluid(ArchitecturyFluidAttributes attributes) { + super(toForgeProperties(attributes)); + this.attributes = attributes; + } + + private static Properties toForgeProperties(ArchitecturyFluidAttributes attributes) { + FluidAttributes.Builder forgeAttributes = new FluidAttributes.Builder(attributes.getSourceTexture(), attributes.getFlowingTexture(), (builder, fluid) -> + new ArchitecturyFluidAttributesForge(builder, fluid, attributes)) { + }; + Properties forge = new Properties(attributes::getSourceFluid, attributes::getFlowingFluid, forgeAttributes); + if (attributes.canConvertToSource()) forge.canMultiply(); + forge.slopeFindDistance(attributes.getSlopeFindDistance()); + forge.levelDecreasePerBlock(attributes.getDropOff()); + forge.bucket(() -> MoreObjects.firstNonNull(attributes.getBucketItem(), Items.AIR)); + forge.tickRate(attributes.getTickDelay()); + forge.explosionResistance(attributes.getExplosionResistance()); + forge.block(() -> MoreObjects.firstNonNull(attributes.getBlock(), (LiquidBlock) Blocks.WATER)); + return forge; + } + + @Override + public Fluid getFlowing() { + return attributes.getFlowingFluid(); + } + + @Override + public Fluid getSource() { + return attributes.getSourceFluid(); + } + + @Override + protected boolean canConvertToSource() { + return attributes.canConvertToSource(); + } + + @Override + protected void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state) { + // Same implementation as in WaterFluid. + BlockEntity blockEntity = state.hasBlockEntity() ? level.getBlockEntity(pos) : null; + Block.dropResources(state, level, pos, blockEntity); + } + + @Override + protected int getSlopeFindDistance(LevelReader level) { + return attributes.getSlopeFindDistance(level); + } + + @Override + protected int getDropOff(LevelReader level) { + return attributes.getDropOff(level); + } + + @Override + public Item getBucket() { + Item item = attributes.getBucketItem(); + return item == null ? Items.AIR : item; + } + + @Override + protected boolean canBeReplacedWith(FluidState state, BlockGetter level, BlockPos pos, Fluid fluid, Direction direction) { + // Same implementation as in WaterFluid. + return direction == Direction.DOWN && !this.isSame(fluid); + } + + @Override + public int getTickDelay(LevelReader level) { + return attributes.getTickDelay(level); + } + + @Override + protected float getExplosionResistance() { + return attributes.getExplosionResistance(); + } + + @Override + protected BlockState createLegacyBlock(FluidState state) { + LiquidBlock block = attributes.getBlock(); + if (block == null) return Blocks.AIR.defaultBlockState(); + return block.defaultBlockState().setValue(LiquidBlock.LEVEL, getLegacyLevel(state)); + } + + @NotNull + @Override + public Optional getPickupSound() { + return Optional.ofNullable(attributes.getFillSound()); + } + + @Override + public boolean isSame(Fluid fluid) { + return fluid == getSource() || fluid == getFlowing(); + } + + public static class Source extends ArchitecturyFlowingFluid { + public Source(ArchitecturyFluidAttributes attributes) { + super(attributes); + } + + @Override + public int getAmount(FluidState state) { + return 8; + } + + @Override + public boolean isSource(FluidState state) { + return true; + } + } + + public static class Flowing extends ArchitecturyFlowingFluid { + public Flowing(ArchitecturyFluidAttributes attributes) { + super(attributes); + this.registerDefaultState(this.getStateDefinition().any().setValue(LEVEL, 7)); + } + + @Override + protected void createFluidStateDefinition(StateDefinition.Builder builder) { + super.createFluidStateDefinition(builder); + builder.add(LEVEL); + } + + @Override + public int getAmount(FluidState state) { + return state.getValue(LEVEL); + } + + @Override + public boolean isSource(FluidState state) { + return false; + } + } +} diff --git a/forge/src/main/java/dev/architectury/core/fluid/forge/imitator/ArchitecturyFluidAttributesForge.java b/forge/src/main/java/dev/architectury/core/fluid/forge/imitator/ArchitecturyFluidAttributesForge.java new file mode 100644 index 00000000..0a2af9de --- /dev/null +++ b/forge/src/main/java/dev/architectury/core/fluid/forge/imitator/ArchitecturyFluidAttributesForge.java @@ -0,0 +1,209 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.fluid.forge.imitator; + +import com.google.common.base.MoreObjects; +import dev.architectury.core.fluid.ArchitecturyFluidAttributes; +import dev.architectury.hooks.fluid.forge.FluidStackHooksForge; +import net.minecraft.Util; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.item.Rarity; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.material.Fluid; +import net.minecraftforge.fluids.FluidAttributes; +import net.minecraftforge.fluids.FluidStack; + +class ArchitecturyFluidAttributesForge extends FluidAttributes { + private final ArchitecturyFluidAttributes attributes; + private final String defaultTranslationKey; + + public ArchitecturyFluidAttributesForge(Builder builder, Fluid fluid, ArchitecturyFluidAttributes attributes) { + super(addArchIntoBuilder(builder, attributes), fluid); + this.attributes = attributes; + this.defaultTranslationKey = Util.makeDescriptionId("fluid", fluid.getRegistryName()); + } + + private static Builder addArchIntoBuilder(Builder builder, ArchitecturyFluidAttributes attributes) { + builder.luminosity(attributes.getLuminosity()) + .density(attributes.getDensity()) + .temperature(attributes.getTemperature()) + .viscosity(attributes.getViscosity()); + if (attributes.isLighterThanAir()) builder.gaseous(); + return builder; + } + + @Override + public ResourceLocation getStillTexture() { + return attributes.getSourceTexture(); + } + + @Override + public ResourceLocation getStillTexture(FluidStack stack) { + return attributes.getSourceTexture(stack == null ? null : FluidStackHooksForge.fromForge(stack)); + } + + @Override + public ResourceLocation getStillTexture(BlockAndTintGetter level, BlockPos pos) { + return attributes.getSourceTexture(null, level, pos); + } + + @Override + public ResourceLocation getFlowingTexture() { + return attributes.getFlowingTexture(); + } + + @Override + public ResourceLocation getFlowingTexture(FluidStack stack) { + return attributes.getFlowingTexture(stack == null ? null : FluidStackHooksForge.fromForge(stack)); + } + + @Override + public ResourceLocation getFlowingTexture(BlockAndTintGetter level, BlockPos pos) { + return attributes.getFlowingTexture(null, level, pos); + } + + @Override + public int getColor() { + return attributes.getColor(); + } + + @Override + public int getColor(FluidStack stack) { + return attributes.getColor(stack == null ? null : FluidStackHooksForge.fromForge(stack)); + } + + @Override + public int getColor(BlockAndTintGetter level, BlockPos pos) { + return attributes.getColor(null, level, pos); + } + + @Override + public int getLuminosity(FluidStack stack) { + return attributes.getLuminosity(stack == null ? null : FluidStackHooksForge.fromForge(stack)); + } + + @Override + public int getLuminosity(BlockAndTintGetter level, BlockPos pos) { + return attributes.getLuminosity(null, level, pos); + } + + @Override + public int getDensity(FluidStack stack) { + return attributes.getDensity(stack == null ? null : FluidStackHooksForge.fromForge(stack)); + } + + @Override + public int getDensity(BlockAndTintGetter level, BlockPos pos) { + return attributes.getDensity(null, level, pos); + } + + @Override + public int getTemperature(FluidStack stack) { + return attributes.getTemperature(stack == null ? null : FluidStackHooksForge.fromForge(stack)); + } + + @Override + public int getTemperature(BlockAndTintGetter level, BlockPos pos) { + return attributes.getTemperature(null, level, pos); + } + + @Override + public int getViscosity(FluidStack stack) { + return attributes.getViscosity(stack == null ? null : FluidStackHooksForge.fromForge(stack)); + } + + @Override + public int getViscosity(BlockAndTintGetter level, BlockPos pos) { + return attributes.getViscosity(null, level, pos); + } + + @Override + public boolean isGaseous(FluidStack stack) { + return attributes.isLighterThanAir(stack == null ? null : FluidStackHooksForge.fromForge(stack)); + } + + @Override + public boolean isGaseous(BlockAndTintGetter level, BlockPos pos) { + return attributes.isLighterThanAir(null, level, pos); + } + + @Override + public Rarity getRarity() { + return attributes.getRarity(); + } + + @Override + public Rarity getRarity(FluidStack stack) { + return attributes.getRarity(stack == null ? null : FluidStackHooksForge.fromForge(stack)); + } + + @Override + public Rarity getRarity(BlockAndTintGetter level, BlockPos pos) { + return attributes.getRarity(null, level, pos); + } + + @Override + public Component getDisplayName(FluidStack stack) { + return attributes.getName(stack == null ? null : FluidStackHooksForge.fromForge(stack)); + } + + @Override + public String getTranslationKey() { + return MoreObjects.firstNonNull(attributes.getTranslationKey(), defaultTranslationKey); + } + + @Override + public String getTranslationKey(FluidStack stack) { + return MoreObjects.firstNonNull(attributes.getTranslationKey(stack == null ? null : FluidStackHooksForge.fromForge(stack)), defaultTranslationKey); + } + + @Override + public SoundEvent getFillSound() { + return attributes.getFillSound(); + } + + @Override + public SoundEvent getFillSound(FluidStack stack) { + return attributes.getFillSound(stack == null ? null : FluidStackHooksForge.fromForge(stack)); + } + + @Override + public SoundEvent getFillSound(BlockAndTintGetter level, BlockPos pos) { + return attributes.getFillSound(null, level, pos); + } + + @Override + public SoundEvent getEmptySound() { + return attributes.getEmptySound(); + } + + @Override + public SoundEvent getEmptySound(FluidStack stack) { + return attributes.getEmptySound(stack == null ? null : FluidStackHooksForge.fromForge(stack)); + } + + @Override + public SoundEvent getEmptySound(BlockAndTintGetter level, BlockPos pos) { + return attributes.getEmptySound(null, level, pos); + } +} diff --git a/forge/src/main/java/dev/architectury/core/item/forge/imitator/ArchitecturyBucketItem.java b/forge/src/main/java/dev/architectury/core/item/forge/imitator/ArchitecturyBucketItem.java new file mode 100644 index 00000000..c295d77c --- /dev/null +++ b/forge/src/main/java/dev/architectury/core/item/forge/imitator/ArchitecturyBucketItem.java @@ -0,0 +1,45 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.item.forge.imitator; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.BucketItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.material.Fluid; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.fluids.capability.wrappers.FluidBucketWrapper; + +import javax.annotation.Nullable; +import java.util.function.Supplier; + +public class ArchitecturyBucketItem extends BucketItem { + public ArchitecturyBucketItem(Supplier fluid, Properties properties) { + super(fluid, properties); + } + + public final Fluid getContainedFluid() { + return getFluid(); + } + + @Override + public ICapabilityProvider initCapabilities(ItemStack stack, @Nullable CompoundTag nbt) { + return this.getClass() == ArchitecturyBucketItem.class ? new FluidBucketWrapper(stack) : super.initCapabilities(stack, nbt); + } +} diff --git a/forge/src/main/java/dev/architectury/core/item/forge/imitator/ArchitecturyMobBucketItem.java b/forge/src/main/java/dev/architectury/core/item/forge/imitator/ArchitecturyMobBucketItem.java new file mode 100644 index 00000000..3a484a2a --- /dev/null +++ b/forge/src/main/java/dev/architectury/core/item/forge/imitator/ArchitecturyMobBucketItem.java @@ -0,0 +1,33 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.item.forge.imitator; + +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.MobBucketItem; +import net.minecraft.world.level.material.Fluid; + +import java.util.function.Supplier; + +public class ArchitecturyMobBucketItem extends MobBucketItem { + public ArchitecturyMobBucketItem(Supplier> entity, Supplier fluid, Supplier sound, Properties properties) { + super(entity, fluid, sound, properties); + } +} diff --git a/forge/src/main/java/dev/architectury/fluid/forge/FluidStackImpl.java b/forge/src/main/java/dev/architectury/fluid/forge/FluidStackImpl.java new file mode 100644 index 00000000..666de7e2 --- /dev/null +++ b/forge/src/main/java/dev/architectury/fluid/forge/FluidStackImpl.java @@ -0,0 +1,99 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.fluid.forge; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.material.Fluid; +import net.minecraftforge.fluids.FluidStack; +import org.jetbrains.annotations.ApiStatus; + +import java.util.function.Function; +import java.util.function.Supplier; + +import static dev.architectury.utils.Amount.toInt; + +@ApiStatus.Internal +public enum FluidStackImpl implements dev.architectury.fluid.FluidStack.FluidStackAdapter { + INSTANCE; + + static { + dev.architectury.fluid.FluidStack.init(); + } + + public static Function toValue; + public static Function fromValue; + + public static dev.architectury.fluid.FluidStack.FluidStackAdapter adapt(Function toValue, Function fromValue) { + FluidStackImpl.toValue = toValue; + FluidStackImpl.fromValue = fromValue; + return (dev.architectury.fluid.FluidStack.FluidStackAdapter) (dev.architectury.fluid.FluidStack.FluidStackAdapter) INSTANCE; + } + + @Override + public FluidStack create(Supplier fluid, long amount, CompoundTag tag) { + return new FluidStack(fluid.get(), toInt(amount), tag); + } + + @Override + public Supplier getRawFluidSupplier(FluidStack object) { + return object.getRawFluid().delegate; + } + + @Override + public Fluid getFluid(FluidStack object) { + return object.getFluid(); + } + + @Override + public long getAmount(FluidStack object) { + return object.getAmount(); + } + + @Override + public void setAmount(FluidStack object, long amount) { + object.setAmount(toInt(amount)); + } + + @Override + public CompoundTag getTag(FluidStack value) { + return value.getTag(); + } + + @Override + public void setTag(FluidStack value, CompoundTag tag) { + value.setTag(tag); + } + + @Override + public FluidStack copy(FluidStack value) { + return value.copy(); + } + + @Override + public int hashCode(FluidStack value) { + var code = 1; + code = 31 * code + value.getFluid().hashCode(); + code = 31 * code + value.getAmount(); + var tag = value.getTag(); + if (tag != null) + code = 31 * code + tag.hashCode(); + return code; + } +} diff --git a/forge/src/main/java/dev/architectury/hooks/fluid/forge/FluidStackHooksForge.java b/forge/src/main/java/dev/architectury/hooks/fluid/forge/FluidStackHooksForge.java index c1963e6e..f9e01595 100644 --- a/forge/src/main/java/dev/architectury/hooks/fluid/forge/FluidStackHooksForge.java +++ b/forge/src/main/java/dev/architectury/hooks/fluid/forge/FluidStackHooksForge.java @@ -20,16 +20,17 @@ package dev.architectury.hooks.fluid.forge; import dev.architectury.fluid.FluidStack; +import dev.architectury.fluid.forge.FluidStackImpl; public final class FluidStackHooksForge { private FluidStackHooksForge() { } public static FluidStack fromForge(net.minecraftforge.fluids.FluidStack stack) { - return FluidStack.create(stack.getFluid().delegate, stack.getAmount(), stack.getTag()); + return FluidStackImpl.fromValue.apply(stack); } public static net.minecraftforge.fluids.FluidStack toForge(FluidStack stack) { - return new net.minecraftforge.fluids.FluidStack(stack.getRawFluid(), (int) stack.getAmount(), stack.getTag()); + return (net.minecraftforge.fluids.FluidStack) FluidStackImpl.toValue.apply(stack); } } diff --git a/forge/src/main/java/dev/architectury/hooks/item/tool/forge/HoeItemHooksImpl.java b/forge/src/main/java/dev/architectury/hooks/item/tool/forge/HoeItemHooksImpl.java new file mode 100644 index 00000000..7b25ca60 --- /dev/null +++ b/forge/src/main/java/dev/architectury/hooks/item/tool/forge/HoeItemHooksImpl.java @@ -0,0 +1,28 @@ +package dev.architectury.hooks.item.tool.forge; + +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.ToolActions; +import net.minecraftforge.event.world.BlockEvent; + +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +public class HoeItemHooksImpl { + public static void addTillable(Block input, Predicate predicate, Consumer action, Function function) { + MinecraftForge.EVENT_BUS.addListener(event -> { + UseOnContext context = event.getContext(); + if (ToolActions.HOE_TILL == event.getToolAction() && context.getItemInHand().canPerformAction(ToolActions.HOE_TILL) + && event.getState().is(input) && predicate.test(context)) { + if (!event.isSimulated()) { + action.accept(context); + } + + event.setFinalState(function.apply(context)); + } + }); + } +} diff --git a/forge/src/main/java/dev/architectury/registry/registries/forge/RegistriesImpl.java b/forge/src/main/java/dev/architectury/registry/registries/forge/RegistriesImpl.java index ec72c755..493dcf9c 100644 --- a/forge/src/main/java/dev/architectury/registry/registries/forge/RegistriesImpl.java +++ b/forge/src/main/java/dev/architectury/registry/registries/forge/RegistriesImpl.java @@ -31,6 +31,7 @@ import dev.architectury.registry.registries.RegistrySupplier; import dev.architectury.registry.registries.options.RegistrarOption; import dev.architectury.registry.registries.options.StandardRegistrarOption; import net.minecraft.core.Registry; +import net.minecraft.data.BuiltinRegistries; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; @@ -42,8 +43,9 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.registries.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; import java.util.*; import java.util.function.Consumer; import java.util.function.Supplier; @@ -134,11 +136,18 @@ public class RegistriesImpl { } public static class RegistryProviderImpl implements Registries.RegistryProvider { + private static final Map>, Registrar> CUSTOM_REGS = new HashMap<>(); private final String modId; private final Supplier eventBus; private final Map>, Data> registry = new HashMap<>(); private final Multimap>, Consumer>> listeners = HashMultimap.create(); + record RegistryBuilderEntry(RegistryBuilder builder, Consumer> forgeRegistry) { + } + + @Nullable + private List builders = new ArrayList<>(); + public RegistryProviderImpl(String modId) { this.modId = modId; this.eventBus = Suppliers.memoize(() -> { @@ -161,11 +170,13 @@ public class RegistriesImpl { ForgeRegistry registry = RegistryManager.ACTIVE.getRegistry(registryKey.location()); if (registry == null) { Registry ts = (Registry) Registry.REGISTRY.get(registryKey.location()); - if (ts == null) { - throw new IllegalArgumentException("Registry " + registryKey + " does not exist!"); - } else { + if (ts == null) ts = (Registry) BuiltinRegistries.REGISTRY.get(registryKey.location()); + if (ts != null) { return get(ts); } + Registrar customReg = RegistryProviderImpl.CUSTOM_REGS.get(registryKey); + if (customReg != null) return (Registrar) customReg; + throw new IllegalArgumentException("Registry " + registryKey + " does not exist!"); } return get(registry); } @@ -191,7 +202,7 @@ public class RegistriesImpl { public RegistrarBuilder builder(Class type, ResourceLocation registryId) { return new RegistryBuilderWrapper<>(this, new net.minecraftforge.registries.RegistryBuilder<>() .setName(registryId) - .setType((Class) type)); + .setType((Class) type), registryId); } public class EventListener { @@ -325,25 +336,48 @@ public class RegistriesImpl { LISTENERS.removeAll(id); } } + + @SubscribeEvent + public void handleEvent(NewRegistryEvent event) { + if (builders != null) { + for (RegistryBuilderEntry builder : builders) { + event.create((RegistryBuilder) builder.builder(), (Consumer) builder.forgeRegistry()); + } + builders = null; + } + } } } public static class RegistryBuilderWrapper implements RegistrarBuilder { private final RegistryProviderImpl provider; private final net.minecraftforge.registries.RegistryBuilder builder; + private final ResourceLocation registryId; private boolean saveToDisk = false; private boolean syncToClients = false; - public RegistryBuilderWrapper(RegistryProviderImpl provider, net.minecraftforge.registries.RegistryBuilder builder) { + public RegistryBuilderWrapper(RegistryProviderImpl provider, RegistryBuilder builder, ResourceLocation registryId) { this.provider = provider; this.builder = builder; + this.registryId = registryId; } @Override public Registrar build() { if (!syncToClients) builder.disableSync(); if (!saveToDisk) builder.disableSaving(); - return provider.get(builder.create()); + if (provider.builders == null) { + throw new IllegalStateException("Cannot create registries when registries are already aggregated!"); + } + final var registrarRef = new Registrar[1]; + var registrar = new DelegatedRegistrar(provider.modId, () -> java.util.Objects.requireNonNull(registrarRef[0], "Registry not yet initialized!"), registryId); + var entry = new RegistryProviderImpl.RegistryBuilderEntry(builder, forgeRegistry -> { + registrarRef[0] = provider.get(forgeRegistry); + registrar.onRegister(); + }); + provider.builders.add(entry); + RegistryProviderImpl.CUSTOM_REGS.put(registrar.key(), registrar); + return registrar; } @Override @@ -565,7 +599,7 @@ public class RegistriesImpl { @Override public RegistrySupplier register(ResourceLocation id, Supplier supplier) { - RegistryObject registryObject = RegistryObject.of(id, delegate); + RegistryObject registryObject = RegistryObject.create(id, delegate); registry.computeIfAbsent(key(), type -> new Data()) .register(delegate, registryObject, () -> supplier.get().setRegistryName(id)); Registrar registrar = this; @@ -686,4 +720,141 @@ public class RegistriesImpl { } } } + + public static class DelegatedRegistrar implements Registrar { + private final String modId; + private final Supplier> delegate; + private final ResourceLocation registryId; + private List onRegister = new ArrayList<>(); + + public DelegatedRegistrar(String modId, Supplier> delegate, ResourceLocation registryId) { + this.modId = modId; + this.delegate = delegate; + this.registryId = registryId; + } + + public void onRegister() { + if (onRegister != null) { + for (Runnable runnable : onRegister) { + runnable.run(); + } + } + onRegister = null; + } + + public boolean isReady() { + return onRegister == null; + } + + @Override + public RegistrySupplier delegate(ResourceLocation id) { + if (isReady()) return delegate.get().delegate(id); + return new RegistrySupplier() { + @Override + public Registries getRegistries() { + return Registries.get(modId); + } + + @Override + public Registrar getRegistrar() { + return DelegatedRegistrar.this; + } + + @Override + public ResourceLocation getRegistryId() { + return DelegatedRegistrar.this.key().location(); + } + + @Override + public ResourceLocation getId() { + return id; + } + + @Override + public boolean isPresent() { + return isReady() && delegate.get().contains(id); + } + + @Override + public T get() { + return isReady() ? delegate.get().get(id) : null; + } + }; + } + + @Override + public RegistrySupplier register(ResourceLocation id, Supplier supplier) { + if (isReady()) return delegate.get().register(id, supplier); + onRegister.add(() -> delegate.get().register(id, supplier)); + return (RegistrySupplier) delegate(id); + } + + @Override + @Nullable + public ResourceLocation getId(T obj) { + return !isReady() ? null : delegate.get().getId(obj); + } + + @Override + public int getRawId(T obj) { + return !isReady() ? -1 : delegate.get().getRawId(obj); + } + + @Override + public Optional> getKey(T obj) { + return !isReady() ? Optional.empty() : delegate.get().getKey(obj); + } + + @Override + @Nullable + public T get(ResourceLocation id) { + return !isReady() ? null : delegate.get().get(id); + } + + @Override + @Nullable + public T byRawId(int rawId) { + return !isReady() ? null : delegate.get().byRawId(rawId); + } + + @Override + public boolean contains(ResourceLocation id) { + return isReady() && delegate.get().contains(id); + } + + @Override + public boolean containsValue(T obj) { + return isReady() && delegate.get().containsValue(obj); + } + + @Override + public Set getIds() { + return isReady() ? delegate.get().getIds() : Collections.emptySet(); + } + + @Override + public Set, T>> entrySet() { + return isReady() ? delegate.get().entrySet() : Collections.emptySet(); + } + + @Override + public ResourceKey> key() { + return isReady() ? delegate.get().key() : ResourceKey.createRegistryKey(registryId); + } + + @Override + public void listen(ResourceLocation id, Consumer callback) { + if (isReady()) { + delegate.get().listen(id, callback); + } else { + onRegister.add(() -> delegate.get().listen(id, callback)); + } + } + + @NotNull + @Override + public Iterator iterator() { + return isReady() ? delegate.get().iterator() : Collections.emptyIterator(); + } + } } diff --git a/gradle.properties b/gradle.properties index 26121b85..c3d02840 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,11 +10,11 @@ cf_type=beta archives_base_name=architectury archives_base_name_snapshot=architectury-snapshot -base_version=5.3 +base_version=5.4 maven_group=dev.architectury fabric_loader_version=0.14.3 fabric_api_version=0.52.1+1.19 mod_menu_version=3.1.0 -forge_version=40.0.1 +forge_version=40.1.14 diff --git a/testmod-common/src/main/java/dev/architectury/test/item/TestBlockInteractions.java b/testmod-common/src/main/java/dev/architectury/test/item/TestBlockInteractions.java index d0d19180..d10caa48 100644 --- a/testmod-common/src/main/java/dev/architectury/test/item/TestBlockInteractions.java +++ b/testmod-common/src/main/java/dev/architectury/test/item/TestBlockInteractions.java @@ -30,28 +30,21 @@ import net.minecraft.world.level.block.Blocks; public final class TestBlockInteractions { private TestBlockInteractions() { } - + public static void init() { AxeItemHooks.addStrippable(Blocks.QUARTZ_PILLAR, Blocks.OAK_LOG); ShovelItemHooks.addFlattenable(Blocks.IRON_ORE, Blocks.DIAMOND_BLOCK.defaultBlockState()); HoeItemHooks.addTillable(Blocks.COAL_BLOCK, ctx -> { - if (!ctx.getLevel().isNight()) { - if (!ctx.getLevel().isClientSide) { - Player player = ctx.getPlayer(); - if (player != null) - player.sendSystemMessage(Component.literal("These dark arts can only be done at night!")); - } - return false; - } - return true; + return ctx.getLevel().isNight(); }, ctx -> { BlockPos pos = ctx.getClickedPos(); - ctx.getLevel().setBlock(pos, Blocks.DIAMOND_BLOCK.defaultBlockState(), 3); if (!ctx.getLevel().isClientSide) { Player player = ctx.getPlayer(); if (player != null) player.sendSystemMessage(Component.literal("Thou has successfully committed the dark arts of alchemy!!")); } + }, ctx -> { + return Blocks.DIAMOND_BLOCK.defaultBlockState(); }); } } diff --git a/testmod-common/src/main/java/dev/architectury/test/registry/TestRegistries.java b/testmod-common/src/main/java/dev/architectury/test/registry/TestRegistries.java index d1418a8d..ec8ce33b 100644 --- a/testmod-common/src/main/java/dev/architectury/test/registry/TestRegistries.java +++ b/testmod-common/src/main/java/dev/architectury/test/registry/TestRegistries.java @@ -19,12 +19,17 @@ package dev.architectury.test.registry; +import dev.architectury.core.RegistryEntry; +import dev.architectury.core.fluid.ArchitecturyFluidAttributes; +import dev.architectury.core.fluid.SimpleArchitecturyFluidAttributes; import dev.architectury.core.item.ArchitecturySpawnEggItem; import dev.architectury.hooks.item.food.FoodPropertiesHooks; import dev.architectury.hooks.level.entity.EntityHooks; -import dev.architectury.registry.block.BlockProperties; +import dev.architectury.platform.Platform; import dev.architectury.registry.level.entity.EntityAttributeRegistry; import dev.architectury.registry.registries.DeferredRegister; +import dev.architectury.registry.registries.Registrar; +import dev.architectury.registry.registries.Registries; import dev.architectury.registry.registries.RegistrySupplier; import dev.architectury.test.TestMod; import dev.architectury.test.entity.TestEntity; @@ -33,6 +38,7 @@ import dev.architectury.test.registry.objects.EquippableTickingItem; import dev.architectury.test.tab.TestCreativeTabs; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffectCategory; import net.minecraft.world.effect.MobEffectInstance; @@ -46,20 +52,50 @@ import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FlowingFluid; +import net.minecraft.world.level.material.Fluid; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; +import java.lang.reflect.InvocationTargetException; +import java.util.function.Supplier; + import static dev.architectury.test.TestMod.SINK; public class TestRegistries { + public static final class TestInt extends RegistryEntry { + public final int value; + + public TestInt(int value) { + this.value = value; + } + } + + public static final Registrar INTS = Registries.get(TestMod.MOD_ID).builder(new ResourceLocation(TestMod.MOD_ID, "ints")) + .syncToClients() + .build(); public static final DeferredRegister ITEMS = DeferredRegister.create(TestMod.MOD_ID, Registry.ITEM_REGISTRY); public static final DeferredRegister BLOCKS = DeferredRegister.create(TestMod.MOD_ID, Registry.BLOCK_REGISTRY); + public static final DeferredRegister FLUIDS = DeferredRegister.create(TestMod.MOD_ID, Registry.FLUID_REGISTRY); public static final DeferredRegister> ENTITY_TYPES = DeferredRegister.create(TestMod.MOD_ID, Registry.ENTITY_TYPE_REGISTRY); public static final DeferredRegister MOB_EFFECTS = DeferredRegister.create(TestMod.MOD_ID, Registry.MOB_EFFECT_REGISTRY); public static final DeferredRegister> RECIPE_SERIALIZERS = DeferredRegister.create(TestMod.MOD_ID, Registry.RECIPE_SERIALIZER_REGISTRY); public static final DeferredRegister> RECIPE_TYPES = DeferredRegister.create(TestMod.MOD_ID, Registry.RECIPE_TYPE_REGISTRY); + public static final ArchitecturyFluidAttributes TEST_FLUID_ATTRIBUTES = SimpleArchitecturyFluidAttributes.ofSupplier(() -> TestRegistries.TEST_FLUID_FLOWING, () -> TestRegistries.TEST_FLUID) + .convertToSource(true) + .flowingTexture(new ResourceLocation("block/water_flow")) + .sourceTexture(new ResourceLocation("block/water_still")) + .blockSupplier(() -> TestRegistries.TEST_FLUID_BLOCK) + .bucketItemSupplier(() -> TestRegistries.TEST_FLUID_BUCKET) + .color(0xFF0000); + + public static final RegistrySupplier TEST_INT = INTS.register(new ResourceLocation(TestMod.MOD_ID, "test_int"), () -> new TestInt(1)); + public static final RegistrySupplier TEST_INT_2 = INTS.register(new ResourceLocation(TestMod.MOD_ID, "test_int_2"), () -> new TestInt(2)); + public static final RegistrySupplier TEST_EFFECT = MOB_EFFECTS.register("test_effect", () -> new MobEffect(MobEffectCategory.NEUTRAL, 0x123456) { }); @@ -80,10 +116,21 @@ public class TestRegistries { new ArchitecturySpawnEggItem(TestRegistries.TEST_ENTITY_2, 0xFFFFFFFF, 0xFF000000, new Item.Properties().tab(TestCreativeTabs.TEST_TAB))); + public static final RegistrySupplier TEST_FLUID_BUCKET = ITEMS.register("test_fluid_bucket", () -> { + try { + // In example mod the forge class isn't being replaced, this is not required in mods depending on architectury + return (Item) Class.forName(!Platform.isForge() ? "dev.architectury.core.item.ArchitecturyBucketItem" : "dev.architectury.core.item.forge.imitator.ArchitecturyBucketItem") + .getDeclaredConstructor(Supplier.class, Item.Properties.class) + .newInstance(TestRegistries.TEST_FLUID, new Item.Properties().tab(TestCreativeTabs.TEST_TAB)); + } catch (InstantiationException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + public static final RegistrySupplier TEST_BLOCK = BLOCKS.register("test_block", () -> - new Block(BlockProperties.copy(Blocks.STONE))); + new Block(BlockBehaviour.Properties.copy(Blocks.STONE))); public static final RegistrySupplier COLLISION_BLOCK = BLOCKS.register("collision_block", () -> - new Block(BlockProperties.copy(Blocks.STONE)) { + new Block(BlockBehaviour.Properties.copy(Blocks.STONE)) { @Override public VoxelShape getCollisionShape(BlockState state, BlockGetter bg, BlockPos pos, CollisionContext ctx) { SINK.accept(EntityHooks.fromCollision(ctx) + " is colliding with " + state); @@ -91,6 +138,39 @@ public class TestRegistries { } }); + public static final RegistrySupplier TEST_FLUID_BLOCK = BLOCKS.register("test_fluid", () -> { + try { + // In example mod the forge class isn't being replaced, this is not required in mods depending on architectury + return (LiquidBlock) Class.forName(!Platform.isForge() ? "dev.architectury.core.block.ArchitecturyLiquidBlock" : "dev.architectury.core.block.forge.imitator.ArchitecturyLiquidBlock") + .getDeclaredConstructor(Supplier.class, BlockBehaviour.Properties.class) + .newInstance(TestRegistries.TEST_FLUID, BlockBehaviour.Properties.copy(Blocks.WATER).noCollission().strength(100.0F).noDrops()); + } catch (InstantiationException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + + public static final RegistrySupplier TEST_FLUID = FLUIDS.register("test_fluid", () -> { + try { + // In example mod the forge class isn't being replaced, this is not required in mods depending on architectury + return (FlowingFluid) Class.forName(!Platform.isForge() ? "dev.architectury.core.fluid.ArchitecturyFlowingFluid$Source" : "dev.architectury.core.fluid.forge.imitator.ArchitecturyFlowingFluid$Source") + .getDeclaredConstructor(ArchitecturyFluidAttributes.class) + .newInstance(TestRegistries.TEST_FLUID_ATTRIBUTES); + } catch (InstantiationException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + + public static final RegistrySupplier TEST_FLUID_FLOWING = FLUIDS.register("test_fluid_flowing", () -> { + try { + // In example mod the forge class isn't being replaced, this is not required in mods depending on architectury + return (FlowingFluid) Class.forName(!Platform.isForge() ? "dev.architectury.core.fluid.ArchitecturyFlowingFluid$Flowing" : "dev.architectury.core.fluid.forge.imitator.ArchitecturyFlowingFluid$Flowing") + .getDeclaredConstructor(ArchitecturyFluidAttributes.class) + .newInstance(TestRegistries.TEST_FLUID_ATTRIBUTES); + } catch (InstantiationException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + public static final RegistrySupplier TEST_BLOCK_ITEM = ITEMS.register("test_block", () -> new BlockItem(TEST_BLOCK.get(), new Item.Properties().tab(TestCreativeTabs.TEST_TAB))); public static final RegistrySupplier COLLISION_BLOCK_ITEM = ITEMS.register("collision_block", () -> @@ -110,6 +190,7 @@ public class TestRegistries { public static void initialize() { MOB_EFFECTS.register(); + FLUIDS.register(); BLOCKS.register(); ITEMS.register(); ENTITY_TYPES.register();