diff --git a/src/main/java/net/shadowfacts/extrahoppers/mixin/MixinBaseFluid.java b/src/main/java/net/shadowfacts/extrahoppers/mixin/MixinBaseFluid.java new file mode 100644 index 0000000..5489b9b --- /dev/null +++ b/src/main/java/net/shadowfacts/extrahoppers/mixin/MixinBaseFluid.java @@ -0,0 +1,58 @@ +package net.shadowfacts.extrahoppers.mixin; + +import net.minecraft.block.BlockState; +import net.minecraft.fluid.BaseFluid; +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.WorldView; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import net.shadowfacts.extrahoppers.util.FluidFlowControllable; +import net.shadowfacts.extrahoppers.util.PositionedFluidStateProvider; + +/** + * @author shadowfacts + */ +@Mixin(BaseFluid.class) +public abstract class MixinBaseFluid { + + @ModifyVariable( + method = "getUpdatedState(Lnet/minecraft/world/WorldView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)Lnet/minecraft/fluid/FluidState;", + at = @At(value = "INVOKE", target = "Lnet/minecraft/fluid/FluidState;isEmpty()Z", shift = At.Shift.BEFORE), + index = 8, + ordinal = 0, + require = 1 + ) + private FluidState getUpdatedState(FluidState initial, WorldView world, BlockPos pos, BlockState state) { + BlockPos up = pos.up(); + BlockState upState = world.getBlockState(up); + if (upState.getBlock() instanceof PositionedFluidStateProvider) { + return ((PositionedFluidStateProvider)upState.getBlock()).getFluidState(upState, world, up); + } + return initial; + } + + @Inject( + method = "receivesFlow(Lnet/minecraft/util/math/Direction;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)Z", + at = @At("HEAD"), + cancellable = true + ) + private void receivesFlow(Direction direction, BlockView world, BlockPos pos, BlockState state, BlockPos fromPos, BlockState fromState, CallbackInfoReturnable cir) { + FluidFlowControllable.Result result = FluidFlowControllable.Result.DEFAULT; + if (state.getBlock() instanceof FluidFlowControllable) { + result = ((FluidFlowControllable)state.getBlock()).allowsFlow(direction, state, world, pos); + } + if (result != FluidFlowControllable.Result.DENY && fromState.getBlock() instanceof FluidFlowControllable) { + result = ((FluidFlowControllable)fromState.getBlock()).allowsFlow(direction, fromState, world, fromPos); + } + if (result != FluidFlowControllable.Result.DEFAULT) { + cir.setReturnValue(result == FluidFlowControllable.Result.ALLOW); + } + } + +} diff --git a/src/main/java/net/shadowfacts/extrahoppers/mixin/MixinWorldChunk.java b/src/main/java/net/shadowfacts/extrahoppers/mixin/MixinWorldChunk.java new file mode 100644 index 0000000..7f9c007 --- /dev/null +++ b/src/main/java/net/shadowfacts/extrahoppers/mixin/MixinWorldChunk.java @@ -0,0 +1,36 @@ +package net.shadowfacts.extrahoppers.mixin; + +import net.minecraft.block.BlockState; +import net.minecraft.fluid.FluidState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.WorldChunk; +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.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import net.shadowfacts.extrahoppers.util.PositionedFluidStateProvider; + +/** + * @author shadowfacts + */ +@Mixin(WorldChunk.class) +public abstract class MixinWorldChunk implements Chunk { + + @Shadow + private World world; + + @Inject(method = "getFluidState(III)Lnet/minecraft/fluid/FluidState;", at = @At("HEAD"), cancellable = true) + public void getFluidState(int x, int y, int z, CallbackInfoReturnable cir) { + BlockPos pos = new BlockPos(x, y, z); + BlockState state = getBlockState(pos); + if (state.getBlock() instanceof PositionedFluidStateProvider) { + FluidState fluidState = ((PositionedFluidStateProvider)state.getBlock()).getFluidState(state, world, pos); + cir.setReturnValue(fluidState); + cir.cancel(); + } + } + +} diff --git a/src/main/java/net/shadowfacts/extrahoppers/util/FluidFlowControllable.java b/src/main/java/net/shadowfacts/extrahoppers/util/FluidFlowControllable.java new file mode 100644 index 0000000..b8ecec0 --- /dev/null +++ b/src/main/java/net/shadowfacts/extrahoppers/util/FluidFlowControllable.java @@ -0,0 +1,23 @@ +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 + } + +} diff --git a/src/main/java/net/shadowfacts/extrahoppers/util/PositionedFluidStateProvider.java b/src/main/java/net/shadowfacts/extrahoppers/util/PositionedFluidStateProvider.java new file mode 100644 index 0000000..c1d5678 --- /dev/null +++ b/src/main/java/net/shadowfacts/extrahoppers/util/PositionedFluidStateProvider.java @@ -0,0 +1,14 @@ +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); +} diff --git a/src/main/kotlin/net/shadowfacts/extrahoppers/block/grate/GrateBlock.kt b/src/main/kotlin/net/shadowfacts/extrahoppers/block/grate/GrateBlock.kt new file mode 100644 index 0000000..96a9dfe --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/extrahoppers/block/grate/GrateBlock.kt @@ -0,0 +1,121 @@ +package net.shadowfacts.extrahoppers.block.grate + +import net.fabricmc.fabric.api.block.FabricBlockSettings +import net.minecraft.block.* +import net.minecraft.block.enums.BlockHalf +import net.minecraft.entity.EntityContext +import net.minecraft.fluid.Fluid +import net.minecraft.fluid.FluidState +import net.minecraft.fluid.Fluids +import net.minecraft.item.ItemPlacementContext +import net.minecraft.sound.BlockSoundGroup +import net.minecraft.state.StateManager +import net.minecraft.state.property.Properties +import net.minecraft.util.Identifier +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction +import net.minecraft.util.shape.VoxelShape +import net.minecraft.world.BlockView +import net.minecraft.world.IWorld +import net.shadowfacts.extrahoppers.block.base.BlockWithEntity +import net.shadowfacts.extrahoppers.util.FluidFlowControllable +import net.shadowfacts.extrahoppers.util.PositionedFluidStateProvider + +/** + * @author shadowfacts + */ +class GrateBlock: BlockWithEntity( + FabricBlockSettings.of(Material.METAL, MaterialColor.AIR) + .strength(5f, 6f) + .sounds(BlockSoundGroup.METAL) + .nonOpaque() + .build() +), PositionedFluidStateProvider, FluidFlowControllable, FluidFillable, FluidDrainable { + + companion object { + val ID = Identifier("extrahoppers", "grate") + + val HALF = Properties.BLOCK_HALF + + val TOP_SHAPE = createCuboidShape(0.0, 15.0, 0.0, 16.0, 16.0, 16.0) + val BOTTOM_SHAPE = createCuboidShape(0.0, 0.0, 0.0, 16.0, 1.0, 16.0) + } + + init { + defaultState = stateManager.defaultState.with(HALF, BlockHalf.BOTTOM) + } + + override fun appendProperties(builder: StateManager.Builder) { + builder.add(HALF) + } + + + override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, entityContext: EntityContext): VoxelShape { + return when (state.get(HALF)) { + BlockHalf.TOP -> TOP_SHAPE + BlockHalf.BOTTOM -> BOTTOM_SHAPE + } + } + + override fun getRayTraceShape(state: BlockState, world: BlockView, pos: BlockPos): VoxelShape { + return when (state.get(HALF)) { + BlockHalf.TOP -> TOP_SHAPE + BlockHalf.BOTTOM -> BOTTOM_SHAPE + } + } + + override fun getPlacementState(context: ItemPlacementContext): BlockState { + val half = when (context.side) { + Direction.DOWN -> BlockHalf.TOP + Direction.UP -> BlockHalf.BOTTOM + else -> if (context.hitPos.y - context.blockPos.y > 0.5) BlockHalf.TOP else BlockHalf.BOTTOM + } + return defaultState.with(HALF, half) + } + + override fun createBlockEntity(world: BlockView) = GrateBlockEntity() + + override fun getFluidState(state: BlockState, world: BlockView, pos: BlockPos): FluidState { + return getBlockEntity(world, pos)?.fluidState ?: Fluids.EMPTY.defaultState + } + + override fun allowsFlow(toSide: Direction, state: BlockState, world: BlockView, pos: BlockPos): FluidFlowControllable.Result { + return FluidFlowControllable.Result.ALLOW + } + + + // Fluid Fillable + + override fun canFillWithFluid(world: BlockView, pos: BlockPos, state: BlockState, fluid: Fluid): Boolean { + val be = getBlockEntity(world, pos) + return be != null && be.fluidState == null + } + + override fun tryFillWithFluid(world: IWorld, pos: BlockPos, state: BlockState, fluidState: FluidState): Boolean { + val be = getBlockEntity(world, pos) + return if (be != null) { + if (!world.isClient) { + be.fluidState = fluidState + world.fluidTickScheduler.schedule(pos, fluidState.fluid, fluidState.fluid.getTickRate(world)) + be.sync() + } + true + } else { + false + } + } + + override fun tryDrainFluid(world: IWorld, pos: BlockPos, state: BlockState): Fluid { + val be = getBlockEntity(world, pos) ?: return Fluids.EMPTY + val fluidState = be.fluidState ?: return Fluids.EMPTY + return if (fluidState.isStill) { + if (!world.isClient) { + be.fluidState = null + be.sync() + } + fluidState.fluid + } else { + Fluids.EMPTY + } + } +} diff --git a/src/main/kotlin/net/shadowfacts/extrahoppers/block/grate/GrateBlockEntity.kt b/src/main/kotlin/net/shadowfacts/extrahoppers/block/grate/GrateBlockEntity.kt new file mode 100644 index 0000000..5b70d6f --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/extrahoppers/block/grate/GrateBlockEntity.kt @@ -0,0 +1,45 @@ +package net.shadowfacts.extrahoppers.block.grate + +import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable +import net.fabricmc.fabric.api.util.NbtType +import net.minecraft.block.entity.BlockEntity +import net.minecraft.fluid.FluidState +import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.NbtHelper +import net.shadowfacts.extrahoppers.init.EHBlockEntities + +/** + * @author shadowfacts + */ +class GrateBlockEntity: BlockEntity(EHBlockEntities.GRATE), BlockEntityClientSerializable { + + var fluidState: FluidState? = null + + override fun toTag(tag: CompoundTag): CompoundTag { + fluidState?.also { + tag.put("fluid", NbtHelper.fromBlockState(it.blockState)) + } + return super.toTag(tag) + } + + override fun fromTag(tag: CompoundTag) { + super.fromTag(tag) + if (tag.contains("fluid", NbtType.COMPOUND)) { + fluidState = NbtHelper.toBlockState(tag.getCompound("fluid")).fluidState + } + } + + override fun toClientTag(tag: CompoundTag): CompoundTag { + fluidState?.also { + tag.put("fluid", NbtHelper.fromBlockState(it.blockState)) + } + return tag + } + + override fun fromClientTag(tag: CompoundTag) { + if (tag.contains("fluid", NbtType.COMPOUND)) { + fluidState = NbtHelper.toBlockState(tag.getCompound("fluid")).fluidState + } + } + +} diff --git a/src/main/kotlin/net/shadowfacts/extrahoppers/init/EHBlockEntities.kt b/src/main/kotlin/net/shadowfacts/extrahoppers/init/EHBlockEntities.kt index c129524..a5d8fb6 100644 --- a/src/main/kotlin/net/shadowfacts/extrahoppers/init/EHBlockEntities.kt +++ b/src/main/kotlin/net/shadowfacts/extrahoppers/init/EHBlockEntities.kt @@ -8,6 +8,8 @@ import net.minecraft.util.registry.Registry import net.shadowfacts.extrahoppers.block.gold.GoldHopperBlock import net.shadowfacts.extrahoppers.block.gold.GoldHopperBlockEntity import net.shadowfacts.extrahoppers.block.gold.InvertedGoldHopperBlock +import net.shadowfacts.extrahoppers.block.grate.GrateBlock +import net.shadowfacts.extrahoppers.block.grate.GrateBlockEntity import net.shadowfacts.extrahoppers.block.wood.WoodHopperBlock import net.shadowfacts.extrahoppers.block.wood.WoodHopperBlockEntity @@ -16,12 +18,13 @@ object EHBlockEntities { val WOOD_HOPPER = create(::WoodHopperBlockEntity, EHBlocks.WOOD_HOPPER) val GOLD_HOPPER = create({ GoldHopperBlockEntity(false) }, EHBlocks.GOLD_HOPPER) val INVERTED_GOLD_HOPPER = create({ GoldHopperBlockEntity(true) }, EHBlocks.INVERTED_GOLD_HOPPER) -// val INVERTED_GOLD_HOPPER = create(::InvertedGoldHopperBlockEntity, EHBlocks.INVERTED_GOLD_HOPPER) + val GRATE = create(::GrateBlockEntity, EHBlocks.GRATE) fun init() { register(WoodHopperBlock.ID, WOOD_HOPPER) register(GoldHopperBlock.ID, GOLD_HOPPER) register(InvertedGoldHopperBlock.ID, INVERTED_GOLD_HOPPER) + register(GrateBlock.ID, GRATE) } private fun create(builder: () -> T, block: Block): BlockEntityType { diff --git a/src/main/kotlin/net/shadowfacts/extrahoppers/init/EHBlocks.kt b/src/main/kotlin/net/shadowfacts/extrahoppers/init/EHBlocks.kt index 38715a1..5026e7a 100644 --- a/src/main/kotlin/net/shadowfacts/extrahoppers/init/EHBlocks.kt +++ b/src/main/kotlin/net/shadowfacts/extrahoppers/init/EHBlocks.kt @@ -5,6 +5,7 @@ import net.minecraft.util.Identifier import net.minecraft.util.registry.Registry import net.shadowfacts.extrahoppers.block.gold.GoldHopperBlock import net.shadowfacts.extrahoppers.block.gold.InvertedGoldHopperBlock +import net.shadowfacts.extrahoppers.block.grate.GrateBlock import net.shadowfacts.extrahoppers.block.wood.WoodHopperBlock object EHBlocks { @@ -12,11 +13,13 @@ object EHBlocks { val WOOD_HOPPER = WoodHopperBlock() val GOLD_HOPPER = GoldHopperBlock() val INVERTED_GOLD_HOPPER = InvertedGoldHopperBlock() + val GRATE = GrateBlock() fun init() { register(WoodHopperBlock.ID, WOOD_HOPPER) register(GoldHopperBlock.ID, GOLD_HOPPER) register(InvertedGoldHopperBlock.ID, INVERTED_GOLD_HOPPER) + register(GrateBlock.ID, GRATE) } private fun register(id: Identifier, block: Block) { diff --git a/src/main/kotlin/net/shadowfacts/extrahoppers/init/EHItems.kt b/src/main/kotlin/net/shadowfacts/extrahoppers/init/EHItems.kt index fe08142..3b4b44e 100644 --- a/src/main/kotlin/net/shadowfacts/extrahoppers/init/EHItems.kt +++ b/src/main/kotlin/net/shadowfacts/extrahoppers/init/EHItems.kt @@ -6,6 +6,7 @@ import net.minecraft.util.Identifier import net.minecraft.util.registry.Registry import net.shadowfacts.extrahoppers.block.gold.GoldHopperBlock import net.shadowfacts.extrahoppers.block.gold.InvertedGoldHopperBlock +import net.shadowfacts.extrahoppers.block.grate.GrateBlock import net.shadowfacts.extrahoppers.block.wood.WoodHopperBlock object EHItems { @@ -13,11 +14,13 @@ object EHItems { val WOOD_HOPPER = BlockItem(EHBlocks.WOOD_HOPPER, Item.Settings()) val GOLD_HOPPER = BlockItem(EHBlocks.GOLD_HOPPER, Item.Settings()) val INVERTED_GOLD_HOPPER = BlockItem(EHBlocks.INVERTED_GOLD_HOPPER, Item.Settings()) + val GRATE = BlockItem(EHBlocks.GRATE, Item.Settings()) fun init() { register(WoodHopperBlock.ID, WOOD_HOPPER) register(GoldHopperBlock.ID, GOLD_HOPPER) register(InvertedGoldHopperBlock.ID, INVERTED_GOLD_HOPPER) + register(GrateBlock.ID, GRATE) } private fun register(id: Identifier, item: Item) { diff --git a/src/main/resources/assets/extrahoppers/blockstates/grate.json b/src/main/resources/assets/extrahoppers/blockstates/grate.json new file mode 100644 index 0000000..f286550 --- /dev/null +++ b/src/main/resources/assets/extrahoppers/blockstates/grate.json @@ -0,0 +1,6 @@ +{ + "variants": { + "half=top": { "model": "extrahoppers:block/grate" }, + "half=bottom": { "model": "extrahoppers:block/grate", "x": 180 } + } +} diff --git a/src/main/resources/assets/extrahoppers/lang/en_us.json b/src/main/resources/assets/extrahoppers/lang/en_us.json index 67817e7..619a0dc 100644 --- a/src/main/resources/assets/extrahoppers/lang/en_us.json +++ b/src/main/resources/assets/extrahoppers/lang/en_us.json @@ -1,5 +1,6 @@ { "block.extrahoppers.wood_hopper": "Wooden Hopper", "block.extrahoppers.gold_hopper": "Golden Hopper", - "block.extrahoppers.inverted_gold_hopper": "Inverted Golden Hopper" + "block.extrahoppers.inverted_gold_hopper": "Inverted Golden Hopper", + "block.extrahoppers.grate": "Grate" } diff --git a/src/main/resources/assets/extrahoppers/models/block/grate.json b/src/main/resources/assets/extrahoppers/models/block/grate.json new file mode 100644 index 0000000..c1596c1 --- /dev/null +++ b/src/main/resources/assets/extrahoppers/models/block/grate.json @@ -0,0 +1,53 @@ +{ + "ambientocclusion": false, + "textures": { + "face": "extrahoppers:block/grate/face", + "edge": "extrahoppers:block/grate/edge" + }, + "elements": [ + { + "from": [0, 15, 0], + "to": [16, 16, 16], + "faces": { + "down": {"texture": "#face"}, + "up": {"texture": "#face", "cullface": "up"}, + "north": {"texture": "#edge", "cullface": "north"}, + "south": {"texture": "#edge", "cullface": "south"}, + "west": {"texture": "#edge", "cullface": "west"}, + "east": {"texture": "#edge", "cullface": "east"} + } + } + ], + "display": { + "gui": { + "rotation": [ 30, 225, 0 ], + "translation": [ 0, 0, 0], + "scale":[ 0.625, 0.625, 0.625 ] + }, + "ground": { + "rotation": [ 0, 0, 0 ], + "translation": [ 0, 3, 0], + "scale":[ 0.25, 0.25, 0.25 ] + }, + "fixed": { + "rotation": [ 0, 0, 0 ], + "translation": [ 0, 0, 0], + "scale":[ 0.5, 0.5, 0.5 ] + }, + "thirdperson_righthand": { + "rotation": [ 75, 45, 0 ], + "translation": [ 0, 2.5, 0], + "scale": [ 0.375, 0.375, 0.375 ] + }, + "firstperson_righthand": { + "rotation": [ 0, 45, 0 ], + "translation": [ 0, 0, 0 ], + "scale": [ 0.40, 0.40, 0.40 ] + }, + "firstperson_lefthand": { + "rotation": [ 0, 225, 0 ], + "translation": [ 0, 0, 0 ], + "scale": [ 0.40, 0.40, 0.40 ] + } + } +} diff --git a/src/main/resources/assets/extrahoppers/models/item/grate.json b/src/main/resources/assets/extrahoppers/models/item/grate.json new file mode 100644 index 0000000..33155d7 --- /dev/null +++ b/src/main/resources/assets/extrahoppers/models/item/grate.json @@ -0,0 +1,3 @@ +{ + "parent": "extrahoppers:block/grate" +} diff --git a/src/main/resources/extrahoppers.mixins.json b/src/main/resources/extrahoppers.mixins.json new file mode 100644 index 0000000..2ee09a2 --- /dev/null +++ b/src/main/resources/extrahoppers.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "package": "net.shadowfacts.extrahoppers.mixin", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "MixinWorldChunk", + "MixinBaseFluid" + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 74efd05..a5fb77b 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -17,6 +17,9 @@ } ] }, + "mixins": [ + "extrahoppers.mixins.json" + ], "depends": { }