Add fluid block and flowing fluid (#251)

[ci skip]

Squash of the following commits:

* Add way to register fluids and fluid attributes, WIP UNTESTED
* Move to correct package
* Update forge/build.gradle
* Add bucket item wrapper and add test mod
* Make it easier to declare attributes by suppliers
* Fix fabric support
* Change SimpleArchitecturyFluidAttributes to accept Supplier<Optional<T>>
* Make ArchitecturyLiquidBlock and ArchitecturyBucketItem accept Supplier
* Update testmod
* Link javadocs in the builder to make it easier to check
* Add ArchitecturyMobBucketItem and fix caps on ArchitecturyBucketItem
* Make SimpleArchitecturyFluidAttributes accept wildcard fluids
* getContainingFluid -> getContainedFluid
* Add supplier variant of the methods

Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com>
Co-authored-by: Max <maxh2709@gmail.com>
This commit is contained in:
shedaniel
2022-05-06 19:51:25 +08:00
committed by GitHub
parent 3532d24577
commit bd9b3e73e4
23 changed files with 2368 additions and 44 deletions

View File

@@ -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<? extends FlowingFluid> fluid, Properties properties) {
super(checkPlatform(fluid).get(), properties);
}
private static <T> T checkPlatform(T obj) {
if (Platform.isForge()) {
throw new IllegalStateException("This class should've been replaced on Forge!");
}
return obj;
}
}

View File

@@ -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> 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<SoundEvent> 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<Fluid, FluidState> 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;
}
}
}

View File

@@ -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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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);
}
}

View File

@@ -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<? extends Fluid> flowingFluid;
private final Supplier<? extends Fluid> sourceFluid;
private boolean canConvertToSource = false;
private int slopeFindDistance = 4;
private int dropOff = 1;
private Supplier<? extends Optional<Item>> bucketItem = Optional::empty;
private int tickDelay = 5;
private float explosionResistance = 100.0F;
private Supplier<? extends Optional<? extends LiquidBlock>> 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<String> defaultTranslationKey = Suppliers.memoize(() -> Util.makeDescriptionId("fluid", Registries.getId(getSourceFluid(), Registry.FLUID_REGISTRY)));
public static SimpleArchitecturyFluidAttributes ofSupplier(Supplier<? extends Supplier<? extends Fluid>> flowingFluid, Supplier<? extends Supplier<? extends Fluid>> sourceFluid) {
return of(() -> flowingFluid.get().get(), () -> sourceFluid.get().get());
}
public static SimpleArchitecturyFluidAttributes of(Supplier<? extends Fluid> flowingFluid, Supplier<? extends Fluid> sourceFluid) {
return new SimpleArchitecturyFluidAttributes(flowingFluid, sourceFluid);
}
protected SimpleArchitecturyFluidAttributes(Supplier<? extends Fluid> flowingFluid, Supplier<? extends Fluid> 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<RegistrySupplier<Item>> bucketItem) {
return bucketItem(() -> bucketItem.get().toOptional());
}
/**
* @see ArchitecturyFluidAttributes#getBucketItem()
*/
public SimpleArchitecturyFluidAttributes bucketItem(RegistrySupplier<Item> bucketItem) {
return bucketItem(bucketItem::toOptional);
}
/**
* @see ArchitecturyFluidAttributes#getBucketItem()
*/
public SimpleArchitecturyFluidAttributes bucketItem(Supplier<? extends Optional<Item>> 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<RegistrySupplier<? extends LiquidBlock>> block) {
return block(() -> block.get().toOptional());
}
/**
* @see ArchitecturyFluidAttributes#getBlock()
*/
public SimpleArchitecturyFluidAttributes block(RegistrySupplier<? extends LiquidBlock> block) {
return block(block::toOptional);
}
/**
* @see ArchitecturyFluidAttributes#getBlock()
*/
public SimpleArchitecturyFluidAttributes block(Supplier<? extends Optional<? extends LiquidBlock>> 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;
}
}

View File

@@ -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<? extends Fluid> fluid, Properties properties) {
super(checkPlatform(fluid).get(), properties);
}
private static <T> 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);
}
}

View File

@@ -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<? extends EntityType<?>> entity, Supplier<? extends Fluid> fluid, Supplier<? extends SoundEvent> sound, Properties properties) {
super(checkPlatform(entity).get(), fluid.get(), sound.get(), properties);
}
private static <T> T checkPlatform(T obj) {
if (Platform.isForge()) {
throw new IllegalStateException("This class should've been replaced on Forge!");
}
return obj;
}
}

View File

@@ -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<Object> 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> fluid;
private Object value;
private FluidStack(Supplier<Fluid> 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<Object> adapt(Function<FluidStack, Object> toValue, Function<Object, FluidStack> fromValue) {
throw new AssertionError();
}
@ApiStatus.Internal
public interface FluidStackAdapter<T> {
T create(Supplier<Fluid> fluid, long amount, CompoundTag tag);
Supplier<Fluid> 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<Fluid> getRawFluidSupplier() {
return fluid;
public Supplier<Fluid> 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,9 @@ 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());
}
}

View File

@@ -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;
}
}