Grate fluid interaction improvements

This commit is contained in:
Shadowfacts 2020-03-29 12:17:29 -04:00
parent 22891a9692
commit dd92515c2f
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
9 changed files with 143 additions and 64 deletions

View File

@ -6,14 +6,17 @@ import net.minecraft.fluid.FluidState;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraft.world.WorldView; import net.minecraft.world.WorldView;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.shadowfacts.extrahoppers.util.FluidFlowControllable; import net.shadowfacts.extrahoppers.util.DynamicFluidStateProvider;
import net.shadowfacts.extrahoppers.util.PositionedFluidStateProvider;
/** /**
* @author shadowfacts * @author shadowfacts
@ -31,8 +34,8 @@ public abstract class MixinBaseFluid {
private FluidState getUpdatedState(FluidState initial, WorldView world, BlockPos pos, BlockState state) { private FluidState getUpdatedState(FluidState initial, WorldView world, BlockPos pos, BlockState state) {
BlockPos up = pos.up(); BlockPos up = pos.up();
BlockState upState = world.getBlockState(up); BlockState upState = world.getBlockState(up);
if (upState.getBlock() instanceof PositionedFluidStateProvider) { if (upState.getBlock() instanceof DynamicFluidStateProvider) {
return ((PositionedFluidStateProvider)upState.getBlock()).getFluidState(upState, world, up); return ((DynamicFluidStateProvider)upState.getBlock()).getFluidState(upState, world, up);
} }
return initial; return initial;
} }
@ -43,16 +46,33 @@ public abstract class MixinBaseFluid {
cancellable = true cancellable = true
) )
private void receivesFlow(Direction direction, BlockView world, BlockPos pos, BlockState state, BlockPos fromPos, BlockState fromState, CallbackInfoReturnable<Boolean> cir) { private void receivesFlow(Direction direction, BlockView world, BlockPos pos, BlockState state, BlockPos fromPos, BlockState fromState, CallbackInfoReturnable<Boolean> cir) {
FluidFlowControllable.Result result = FluidFlowControllable.Result.DEFAULT; DynamicFluidStateProvider.FlowResult result = DynamicFluidStateProvider.FlowResult.DEFAULT;
if (state.getBlock() instanceof FluidFlowControllable) { if (state.getBlock() instanceof DynamicFluidStateProvider) {
result = ((FluidFlowControllable)state.getBlock()).allowsFlow(direction, state, world, pos); result = ((DynamicFluidStateProvider)state.getBlock()).allowsFlow(direction, state, world, pos);
} }
if (result != FluidFlowControllable.Result.DENY && fromState.getBlock() instanceof FluidFlowControllable) { if (result != DynamicFluidStateProvider.FlowResult.DENY && fromState.getBlock() instanceof DynamicFluidStateProvider) {
result = ((FluidFlowControllable)fromState.getBlock()).allowsFlow(direction, fromState, world, fromPos); result = ((DynamicFluidStateProvider)fromState.getBlock()).allowsFlow(direction, fromState, world, fromPos);
} }
if (result != FluidFlowControllable.Result.DEFAULT) { if (result != DynamicFluidStateProvider.FlowResult.DEFAULT) {
cir.setReturnValue(result == FluidFlowControllable.Result.ALLOW); cir.setReturnValue(result == DynamicFluidStateProvider.FlowResult.ALLOW);
} }
} }
@Inject(
method = "onScheduledTick(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/fluid/FluidState;)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;I)Z"),
cancellable = true
)
private void onScheduledTick(World world, BlockPos pos, FluidState fluidState, CallbackInfo ci) {
BlockState state = world.getBlockState(pos);
if (state.getBlock() instanceof DynamicFluidStateProvider) {
((DynamicFluidStateProvider)state.getBlock()).setEmptyFluid(world, pos);
this.tryFlow(world, pos, fluidState);
ci.cancel();
}
}
@Shadow
protected abstract void tryFlow(IWorld world, BlockPos pos, FluidState state);
} }

View File

@ -0,0 +1,37 @@
package net.shadowfacts.extrahoppers.mixin;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.BucketItem;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.shadowfacts.extrahoppers.util.DynamicFluidStateProvider;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/**
* @author shadowfacts
*/
@Mixin(BucketItem.class)
public abstract class MixinBucketItem {
@Shadow
private Fluid fluid;
@Redirect(
method = "placeFluid(Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/hit/BlockHitResult;)Z",
at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/item/BucketItem;fluid:Lnet/minecraft/fluid/Fluid;", ordinal = 4)
)
private Fluid getFluid(BucketItem item, PlayerEntity player, World world, BlockPos pos, BlockHitResult result) {
if (world.getBlockState(pos).getBlock() instanceof DynamicFluidStateProvider) {
return Fluids.WATER;
} else {
return this.fluid;
}
}
}

View File

@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.shadowfacts.extrahoppers.util.PositionedFluidStateProvider; import net.shadowfacts.extrahoppers.util.DynamicFluidStateProvider;
/** /**
* @author shadowfacts * @author shadowfacts
@ -26,8 +26,8 @@ public abstract class MixinWorldChunk implements Chunk {
public void getFluidState(int x, int y, int z, CallbackInfoReturnable<FluidState> cir) { public void getFluidState(int x, int y, int z, CallbackInfoReturnable<FluidState> cir) {
BlockPos pos = new BlockPos(x, y, z); BlockPos pos = new BlockPos(x, y, z);
BlockState state = getBlockState(pos); BlockState state = getBlockState(pos);
if (state.getBlock() instanceof PositionedFluidStateProvider) { if (state.getBlock() instanceof DynamicFluidStateProvider) {
FluidState fluidState = ((PositionedFluidStateProvider)state.getBlock()).getFluidState(state, world, pos); FluidState fluidState = ((DynamicFluidStateProvider)state.getBlock()).getFluidState(state, world, pos);
cir.setReturnValue(fluidState); cir.setReturnValue(fluidState);
cir.cancel(); cir.cancel();
} }

View File

@ -0,0 +1,27 @@
package net.shadowfacts.extrahoppers.util;
import net.minecraft.block.BlockState;
import net.minecraft.fluid.FluidState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import org.jetbrains.annotations.NotNull;
/**
* @author shadowfacts
*/
public interface DynamicFluidStateProvider {
FluidState getFluidState(@NotNull BlockState state, @NotNull BlockView world, @NotNull BlockPos pos);
@NotNull
FlowResult allowsFlow(@NotNull Direction toSide, @NotNull BlockState state, @NotNull BlockView world, @NotNull BlockPos pos);
void setEmptyFluid(@NotNull World world, @NotNull BlockPos pos);
enum FlowResult {
ALLOW,
DENY,
DEFAULT
}
}

View File

@ -1,23 +0,0 @@
package net.shadowfacts.extrahoppers.util;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView;
import org.jetbrains.annotations.NotNull;
/**
* @author shadowfacts
*/
public interface FluidFlowControllable {
@NotNull
Result allowsFlow(@NotNull Direction toSide, @NotNull BlockState state, @NotNull BlockView world, @NotNull BlockPos pos);
enum Result {
ALLOW,
DENY,
DEFAULT
}
}

View File

@ -1,14 +0,0 @@
package net.shadowfacts.extrahoppers.util;
import net.minecraft.block.BlockState;
import net.minecraft.fluid.FluidState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockView;
import org.jetbrains.annotations.NotNull;
/**
* @author shadowfacts
*/
public interface PositionedFluidStateProvider {
FluidState getFluidState(@NotNull BlockState state, @NotNull BlockView world, @NotNull BlockPos pos);
}

View File

@ -17,9 +17,9 @@ import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape import net.minecraft.util.shape.VoxelShape
import net.minecraft.world.BlockView import net.minecraft.world.BlockView
import net.minecraft.world.IWorld import net.minecraft.world.IWorld
import net.minecraft.world.World
import net.shadowfacts.extrahoppers.block.base.BlockWithEntity import net.shadowfacts.extrahoppers.block.base.BlockWithEntity
import net.shadowfacts.extrahoppers.util.FluidFlowControllable import net.shadowfacts.extrahoppers.util.DynamicFluidStateProvider
import net.shadowfacts.extrahoppers.util.PositionedFluidStateProvider
/** /**
* @author shadowfacts * @author shadowfacts
@ -30,7 +30,7 @@ class GrateBlock: BlockWithEntity<GrateBlockEntity>(
.sounds(BlockSoundGroup.METAL) .sounds(BlockSoundGroup.METAL)
.nonOpaque() .nonOpaque()
.build() .build()
), PositionedFluidStateProvider, FluidFlowControllable, FluidFillable, FluidDrainable { ), DynamicFluidStateProvider, FluidFillable, FluidDrainable {
companion object { companion object {
val ID = Identifier("extrahoppers", "grate") val ID = Identifier("extrahoppers", "grate")
@ -79,10 +79,30 @@ class GrateBlock: BlockWithEntity<GrateBlockEntity>(
return getBlockEntity(world, pos)?.fluidState ?: Fluids.EMPTY.defaultState return getBlockEntity(world, pos)?.fluidState ?: Fluids.EMPTY.defaultState
} }
override fun allowsFlow(toSide: Direction, state: BlockState, world: BlockView, pos: BlockPos): FluidFlowControllable.Result { override fun setEmptyFluid(world: World, pos: BlockPos) {
return FluidFlowControllable.Result.ALLOW val be = getBlockEntity(world, pos) ?: return
be.fluidState = null
if (!world.isClient) {
be.sync()
}
triggerFluidUpdate(world, pos, world.getBlockState(pos))
} }
override fun allowsFlow(toSide: Direction, state: BlockState, world: BlockView, pos: BlockPos): DynamicFluidStateProvider.FlowResult {
return DynamicFluidStateProvider.FlowResult.ALLOW
}
override fun neighborUpdate(state: BlockState, world: World, pos: BlockPos, block: Block, neighborPos: BlockPos, bl: Boolean) {
val fluidState = getFluidState(state, world, pos)
if (!fluidState.isEmpty) {
world.fluidTickScheduler.schedule(pos, fluidState.fluid, fluidState.fluid.getTickRate(world))
}
}
private fun triggerFluidUpdate(world: World, pos: BlockPos, state: BlockState) {
world.updateListeners(pos, state, state, 3)
world.updateNeighbors(pos, state.block)
}
// Fluid Fillable // Fluid Fillable
@ -94,11 +114,14 @@ class GrateBlock: BlockWithEntity<GrateBlockEntity>(
override fun tryFillWithFluid(world: IWorld, pos: BlockPos, state: BlockState, fluidState: FluidState): Boolean { override fun tryFillWithFluid(world: IWorld, pos: BlockPos, state: BlockState, fluidState: FluidState): Boolean {
val be = getBlockEntity(world, pos) val be = getBlockEntity(world, pos)
return if (be != null) { return if (be != null) {
if (!world.isClient) {
be.fluidState = fluidState be.fluidState = fluidState
if (!world.isClient) {
world.fluidTickScheduler.schedule(pos, fluidState.fluid, fluidState.fluid.getTickRate(world)) world.fluidTickScheduler.schedule(pos, fluidState.fluid, fluidState.fluid.getTickRate(world))
be.sync() be.sync()
} }
if (world is World) {
triggerFluidUpdate(world, pos, state)
}
true true
} else { } else {
false false
@ -109,10 +132,13 @@ class GrateBlock: BlockWithEntity<GrateBlockEntity>(
val be = getBlockEntity(world, pos) ?: return Fluids.EMPTY val be = getBlockEntity(world, pos) ?: return Fluids.EMPTY
val fluidState = be.fluidState ?: return Fluids.EMPTY val fluidState = be.fluidState ?: return Fluids.EMPTY
return if (fluidState.isStill) { return if (fluidState.isStill) {
if (!world.isClient) {
be.fluidState = null be.fluidState = null
if (!world.isClient) {
be.sync() be.sync()
} }
if (world is World) {
triggerFluidUpdate(world, pos, state)
}
fluidState.fluid fluidState.fluid
} else { } else {
Fluids.EMPTY Fluids.EMPTY

View File

@ -24,8 +24,10 @@ class GrateBlockEntity: BlockEntity(EHBlockEntities.GRATE), BlockEntityClientSer
override fun fromTag(tag: CompoundTag) { override fun fromTag(tag: CompoundTag) {
super.fromTag(tag) super.fromTag(tag)
if (tag.contains("fluid", NbtType.COMPOUND)) { fluidState = if (tag.contains("fluid", NbtType.COMPOUND)) {
fluidState = NbtHelper.toBlockState(tag.getCompound("fluid")).fluidState NbtHelper.toBlockState(tag.getCompound("fluid")).fluidState
} else {
null
} }
} }
@ -37,9 +39,12 @@ class GrateBlockEntity: BlockEntity(EHBlockEntities.GRATE), BlockEntityClientSer
} }
override fun fromClientTag(tag: CompoundTag) { override fun fromClientTag(tag: CompoundTag) {
if (tag.contains("fluid", NbtType.COMPOUND)) { fluidState = if (tag.contains("fluid", NbtType.COMPOUND)) {
fluidState = NbtHelper.toBlockState(tag.getCompound("fluid")).fluidState NbtHelper.toBlockState(tag.getCompound("fluid")).fluidState
} else {
null
} }
world?.updateListeners(pos, cachedState, cachedState, 3)
} }
} }

View File

@ -3,8 +3,9 @@
"package": "net.shadowfacts.extrahoppers.mixin", "package": "net.shadowfacts.extrahoppers.mixin",
"compatibilityLevel": "JAVA_8", "compatibilityLevel": "JAVA_8",
"mixins": [ "mixins": [
"MixinWorldChunk", "MixinBaseFluid",
"MixinBaseFluid" "MixinBucketItem",
"MixinWorldChunk"
], ],
"client": [ "client": [
], ],