Skip to content

Commit

Permalink
Finish implementing dispenser support for fluid tanks in bucket mode …
Browse files Browse the repository at this point in the history
…so that we can have "smarter" logic than the forge one uses once it is eventually fixed
  • Loading branch information
pupnewfster committed Oct 29, 2020
1 parent 7dfb437 commit 78e9416
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import net.minecraft.block.DispenserBlock;
import net.minecraft.dispenser.DefaultDispenseItemBehavior;
import net.minecraft.dispenser.IBlockSource;
import net.minecraft.dispenser.IDispenseItemBehavior;
import net.minecraft.entity.EntityClassification;
import net.minecraft.entity.EntitySpawnPlacementRegistry;
import net.minecraft.entity.EntityType;
Expand Down Expand Up @@ -75,7 +76,7 @@ private static void registerSpawnControls(EntityTypeRegistryObject<? extends Mon

@SafeVarargs
private static void registerDelayedDispenserBehavior(ItemRegistryObject<AdditionsSpawnEggItem>... spawnEggs) {
DefaultDispenseItemBehavior dispenseBehavior = new DefaultDispenseItemBehavior() {
IDispenseItemBehavior dispenseBehavior = new DefaultDispenseItemBehavior() {
@Nonnull
@Override
public ItemStack dispenseStack(IBlockSource source, ItemStack stack) {
Expand Down
23 changes: 3 additions & 20 deletions src/main/java/mekanism/common/Mekanism.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nonnull;
import mekanism.api.Coord4D;
import mekanism.api.MekanismAPI;
import mekanism.api.NBTConstants;
Expand Down Expand Up @@ -51,7 +50,7 @@
import mekanism.common.entity.EntityRobit;
import mekanism.common.integration.MekanismHooks;
import mekanism.common.inventory.container.sync.dynamic.SyncMapper;
import mekanism.common.item.block.machine.ItemBlockFluidTank;
import mekanism.common.item.block.machine.ItemBlockFluidTank.FluidTankItemDispenseBehavior;
import mekanism.common.lib.Version;
import mekanism.common.lib.frequency.FrequencyManager;
import mekanism.common.lib.frequency.FrequencyType;
Expand Down Expand Up @@ -81,11 +80,8 @@
import mekanism.common.registries.MekanismTileEntityTypes;
import mekanism.common.world.GenHandler;
import net.minecraft.block.DispenserBlock;
import net.minecraft.dispenser.DefaultDispenseItemBehavior;
import net.minecraft.dispenser.IBlockSource;
import net.minecraft.dispenser.IDispenseItemBehavior;
import net.minecraft.entity.ai.attributes.GlobalEntityTypeAttributes;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.ResourceLocation;
Expand Down Expand Up @@ -314,21 +310,8 @@ private void commonSetup(FMLCommonSetupEvent event) {
GlobalEntityTypeAttributes.put(MekanismEntityTypes.ROBIT.get(), EntityRobit.getDefaultAttributes().create());
//Register dispenser behaviors
MekanismFluids.FLUIDS.registerBucketDispenserBehavior();
registerDispenseBehavior(new DefaultDispenseItemBehavior() {
@Nonnull
@Override
public ItemStack dispenseStack(@Nonnull IBlockSource source, @Nonnull ItemStack stack) {
if (stack.getItem() instanceof ItemBlockFluidTank && ((ItemBlockFluidTank) stack.getItem()).getBucketMode(stack)) {
//If the fluid tank is in bucket mode allow for it to act as a bucket
//TODO: Once https://github.com/MinecraftForge/MinecraftForge/pull/7422 gets merged uncomment the line below
//return DispenseFluidContainer.getInstance().dispenseStack(source, stack);
}
//Otherwise eject it as a normal item
return super.dispenseStack(source, stack);
}
},
MekanismBlocks.BASIC_FLUID_TANK, MekanismBlocks.ADVANCED_FLUID_TANK, MekanismBlocks.ELITE_FLUID_TANK, MekanismBlocks.ULTIMATE_FLUID_TANK,
MekanismBlocks.CREATIVE_FLUID_TANK);
registerDispenseBehavior(FluidTankItemDispenseBehavior.INSTANCE, MekanismBlocks.BASIC_FLUID_TANK, MekanismBlocks.ADVANCED_FLUID_TANK,
MekanismBlocks.ELITE_FLUID_TANK, MekanismBlocks.ULTIMATE_FLUID_TANK, MekanismBlocks.CREATIVE_FLUID_TANK);
});

//Register player tracker
Expand Down
124 changes: 100 additions & 24 deletions src/main/java/mekanism/common/item/block/machine/ItemBlockFluidTank.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,21 @@
import mekanism.common.util.text.BooleanStateDisplay.YesNo;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.DispenserBlock;
import net.minecraft.block.IBucketPickupHandler;
import net.minecraft.dispenser.DefaultDispenseItemBehavior;
import net.minecraft.dispenser.IBlockSource;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.FluidState;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tags.FluidTags;
import net.minecraft.util.ActionResult;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand;
import net.minecraft.util.NonNullList;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.Util;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
Expand Down Expand Up @@ -151,24 +150,15 @@ public ActionResult<ItemStack> onItemRightClick(@Nonnull World world, PlayerEnti
}
return new ActionResult<>(ActionResultType.SUCCESS, stack);
} else if (SecurityUtils.canAccess(player, stack)) {
//TODO: At some point maybe try to reduce the duplicate code between this and the dispense behavior
BlockRayTraceResult result = rayTrace(world, player, !player.isSneaking() ? FluidMode.SOURCE_ONLY : FluidMode.NONE);
//It can be null if there is nothing in range
if (result.getType() == Type.BLOCK) {
BlockPos pos = result.getPos();
if (!world.isBlockModifiable(player, pos)) {
return new ActionResult<>(ActionResultType.FAIL, stack);
}
Optional<IFluidHandlerItem> capability = FluidUtil.getFluidHandler(stack).resolve();
if (!capability.isPresent()) {
//If something went wrong and we don't have a fluid handler on our tank, then fail
return new ActionResult<>(ActionResultType.FAIL, stack);
}
IFluidHandlerItem fluidHandlerItem = capability.get();
if (!(fluidHandlerItem instanceof IMekanismFluidHandler)) {
//If it isn't one of our fluid handlers fail
return new ActionResult<>(ActionResultType.FAIL, stack);
}
IExtendedFluidTank fluidTank = ((IMekanismFluidHandler) fluidHandlerItem).getFluidTank(0, null);
IExtendedFluidTank fluidTank = getExtendedFluidTank(stack);
if (fluidTank == null) {
//If something went wrong and we don't have a fluid tank fail
return new ActionResult<>(ActionResultType.FAIL, stack);
Expand Down Expand Up @@ -215,22 +205,17 @@ public ActionResult<ItemStack> onItemRightClick(@Nonnull World world, PlayerEnti
MekanismUtils.logMismatchedStackSize(fluidTank.growStack(fluidStack.getAmount(), Action.EXECUTE), fluidStack.getAmount());
}
//Play the bucket fill sound
SoundEvent soundevent = fluidStack.getFluid().getAttributes().getFillSound(world, pos);
if (soundevent == null) {
soundevent = fluidStack.getFluid().isIn(FluidTags.LAVA) ? SoundEvents.ITEM_BUCKET_FILL_LAVA : SoundEvents.ITEM_BUCKET_FILL;
}
world.playSound(player, pos, soundevent, SoundCategory.BLOCKS, 1.0F, 1.0F);
WorldUtils.playFillSound(player, world, pos, fluidStack);
return new ActionResult<>(ActionResultType.SUCCESS, stack);
} else {
return new ActionResult<>(ActionResultType.FAIL, stack);
}
return new ActionResult<>(ActionResultType.FAIL, stack);
}
} else {
if (fluidTank.extract(FluidAttributes.BUCKET_VOLUME, Action.SIMULATE, AutomationType.MANUAL).getAmount() < FluidAttributes.BUCKET_VOLUME
|| !player.canPlayerEdit(pos.offset(result.getFace()), result.getFace(), stack)) {
return new ActionResult<>(ActionResultType.FAIL, stack);
}
if (WorldUtils.tryPlaceContainedLiquid(player, world, pos, fluidHandlerItem.getFluidInTank(0), result.getFace())) {
if (WorldUtils.tryPlaceContainedLiquid(player, world, pos, fluidTank.getFluid(), result.getFace())) {
if (!player.isCreative()) {
MekanismUtils.logMismatchedStackSize(fluidTank.shrinkStack(FluidAttributes.BUCKET_VOLUME, Action.EXECUTE), FluidAttributes.BUCKET_VOLUME);
}
Expand All @@ -248,10 +233,21 @@ public ActionResult<ItemStack> onItemRightClick(@Nonnull World world, PlayerEnti
return new ActionResult<>(ActionResultType.PASS, stack);
}

private boolean validFluid(@Nonnull IExtendedFluidTank fluidTank, @Nonnull FluidStack fluidStack) {
private static boolean validFluid(@Nonnull IExtendedFluidTank fluidTank, @Nonnull FluidStack fluidStack) {
return !fluidStack.isEmpty() && fluidTank.insert(fluidStack, Action.SIMULATE, AutomationType.MANUAL).isEmpty();
}

private static IExtendedFluidTank getExtendedFluidTank(@Nonnull ItemStack stack) {
Optional<IFluidHandlerItem> capability = FluidUtil.getFluidHandler(stack).resolve();
if (capability.isPresent()) {
IFluidHandlerItem fluidHandlerItem = capability.get();
if (fluidHandlerItem instanceof IMekanismFluidHandler) {
return ((IMekanismFluidHandler) fluidHandlerItem).getFluidTank(0, null);
}
}
return null;
}

public void setBucketMode(ItemStack itemStack, boolean bucketMode) {
ItemDataUtils.setBoolean(itemStack, NBTConstants.BUCKET_MODE, bucketMode);
}
Expand Down Expand Up @@ -283,4 +279,84 @@ public void changeMode(@Nonnull PlayerEntity player, @Nonnull ItemStack stack, i
public ITextComponent getScrollTextComponent(@Nonnull ItemStack stack) {
return MekanismLang.BUCKET_MODE.translateColored(EnumColor.GRAY, OnOff.of(getBucketMode(stack), true));
}

public static class FluidTankItemDispenseBehavior extends DefaultDispenseItemBehavior {

public static final FluidTankItemDispenseBehavior INSTANCE = new FluidTankItemDispenseBehavior();

private FluidTankItemDispenseBehavior() {
}

@Nonnull
@Override
public ItemStack dispenseStack(@Nonnull IBlockSource source, @Nonnull ItemStack stack) {
if (stack.getItem() instanceof ItemBlockFluidTank && ((ItemBlockFluidTank) stack.getItem()).getBucketMode(stack)) {
//If the fluid tank is in bucket mode allow for it to act as a bucket
//Note: We don't use DispenseFluidContainer as we have more specific logic for determining if we want it to
// act as a bucket that is emptying its contents or one that is picking up contents
IExtendedFluidTank fluidTank = getExtendedFluidTank(stack);
//Get the fluid tank for the stack
if (fluidTank == null) {
//If there isn't one then there is something wrong with the stack, treat it as a normal stack and just eject it
return super.dispenseStack(source, stack);
}
World world = source.getWorld();
BlockPos pos = source.getBlockPos().offset(source.getBlockState().get(DispenserBlock.FACING));
//Note: we get the block state from the world so that we can get the proper block in case it is fluid logged
BlockState blockState = world.getBlockState(pos);
FluidState fluidState = blockState.getFluidState();
//If the fluid state in the world isn't empty and is a source try to pick it up otherwise try to dispense the stored fluid
if (!fluidState.isEmpty() && fluidState.isSource()) {
//Just in case someone does weird things and has a fluid state that is empty and a source
// only allow collecting from non empty sources
Fluid fluid = fluidState.getFluid();
FluidStack fluidStack = new FluidStack(fluid, FluidAttributes.BUCKET_VOLUME);
Block block = blockState.getBlock();
if (block instanceof IFluidBlock) {
fluidStack = ((IFluidBlock) block).drain(world, pos, FluidAction.SIMULATE);
if (!validFluid(fluidTank, fluidStack)) {
//If the fluid is not valid, then eject the stack similar to how vanilla does for buckets
return super.dispenseStack(source, stack);
}
//Actually drain it
fluidStack = ((IFluidBlock) block).drain(world, pos, FluidAction.EXECUTE);
} else if (block instanceof IBucketPickupHandler && validFluid(fluidTank, fluidStack)) {
//If it can be picked up by a bucket and we actually want to pick it up, do so to update the fluid type we are doing
// otherwise we assume the type from the fluid state is correct
fluid = ((IBucketPickupHandler) block).pickupFluid(world, pos, blockState);
//Update the fluid stack in case something somehow changed about the type
// making sure that we replace to heavy water if we got heavy water
fluidStack = new FluidStack(fluid, FluidAttributes.BUCKET_VOLUME);
if (!validFluid(fluidTank, fluidStack)) {
Mekanism.logger.warn("Fluid removed without successfully picking up. Fluid {} at {} in {} was valid, but after picking up was {}.",
fluidState.getFluid().getRegistryName(), pos, world.getDimensionKey().getLocation(), fluid.getRegistryName());
//If we can't insert or extract it, then eject the stack similar to how vanilla does for buckets
return super.dispenseStack(source, stack);
}
}
if (validFluid(fluidTank, fluidStack)) {
if (fluidTank.isEmpty()) {
fluidTank.setStack(fluidStack);
} else {
//Grow the stack
MekanismUtils.logMismatchedStackSize(fluidTank.growStack(fluidStack.getAmount(), Action.EXECUTE), fluidStack.getAmount());
}
//Play the bucket fill sound
WorldUtils.playFillSound(null, world, pos, fluidStack);
//Success, don't dispense anything just return our resulting stack
return stack;
}
} else if (fluidTank.extract(FluidAttributes.BUCKET_VOLUME, Action.SIMULATE, AutomationType.MANUAL).getAmount() >= FluidAttributes.BUCKET_VOLUME) {
if (WorldUtils.tryPlaceContainedLiquid(null, world, pos, fluidTank.getFluid(), null)) {
MekanismUtils.logMismatchedStackSize(fluidTank.shrinkStack(FluidAttributes.BUCKET_VOLUME, Action.EXECUTE), FluidAttributes.BUCKET_VOLUME);
//Success, don't dispense anything just return our resulting stack
return stack;
}
}
//If we can't insert or extract it, then eject the stack similar to how vanilla does for buckets
}
//Otherwise eject it as a normal item
return super.dispenseStack(source, stack);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@
public class FluidDeferredRegister {

private static final ResourceLocation OVERLAY = new ResourceLocation("minecraft", "block/water_overlay");
//Based off of vanilla's lava/water bucket dispense behavior
@Deprecated
private static final IDispenseItemBehavior bucketDispenseBehavior = new DefaultDispenseItemBehavior() {
//Copy of/based off of vanilla's lava/water bucket dispense behavior
private static final IDispenseItemBehavior BUCKET_DISPENSE_BEHAVIOR = new DefaultDispenseItemBehavior() {
@Nonnull
@Override
public ItemStack dispenseStack(@Nonnull IBlockSource source, @Nonnull ItemStack stack) {
Expand Down Expand Up @@ -115,8 +114,7 @@ public void register(IEventBus bus) {

public void registerBucketDispenserBehavior() {
for (FluidRegistryObject<?, ?, ?, ?> fluidRO : getAllFluids()) {
//TODO: Use DispenseFluidContainer.getInstance() once https://github.com/MinecraftForge/MinecraftForge/pull/7422 gets merged
DispenserBlock.registerDispenseBehavior(fluidRO.getBucket(), bucketDispenseBehavior);
DispenserBlock.registerDispenseBehavior(fluidRO.getBucket(), BUCKET_DISPENSE_BEHAVIOR);
}
}
}
8 changes: 8 additions & 0 deletions src/main/java/mekanism/common/util/WorldUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,14 @@ private static void playEmptySound(@Nullable PlayerEntity player, IWorld world,
world.playSound(player, pos, soundevent, SoundCategory.BLOCKS, 1.0F, 1.0F);
}

public static void playFillSound(@Nullable PlayerEntity player, IWorld world, BlockPos pos, @Nonnull FluidStack fluidStack) {
SoundEvent soundevent = fluidStack.getFluid().getAttributes().getFillSound(world, pos);
if (soundevent == null) {
soundevent = fluidStack.getFluid().isIn(FluidTags.LAVA) ? SoundEvents.ITEM_BUCKET_FILL_LAVA : SoundEvents.ITEM_BUCKET_FILL;
}
world.playSound(player, pos, soundevent, SoundCategory.BLOCKS, 1.0F, 1.0F);
}

/**
* Better version of the World.getRedstonePowerFromNeighbors() method that doesn't load chunks.
*
Expand Down

0 comments on commit 78e9416

Please sign in to comment.