From e60e20bbd9b945d3ae6015494ebb7e12e0142738 Mon Sep 17 00:00:00 2001 From: CreepyCre Date: Fri, 18 Jun 2021 18:16:09 +0200 Subject: [PATCH] fix detached rifts being placed after door gets destroyed change ExplosionMixin Overwrite to Inject --- .../dimdoors/api/block/CustomBreakBlock.java | 43 +++++ .../api/block/ExplosionConvertibleBlock.java | 11 ++ .../block/door/DimensionalDoorBlock.java | 41 ++++- .../dimdev/dimdoors/mixin/ExplosionMixin.java | 151 +++--------------- .../org/dimdev/dimdoors/mixin/WorldMixin.java | 85 ++++++++++ src/main/resources/dimdoors.accesswidener | 5 + src/main/resources/dimdoors.mixins.json | 5 +- 7 files changed, 205 insertions(+), 136 deletions(-) create mode 100644 src/main/java/org/dimdev/dimdoors/api/block/CustomBreakBlock.java create mode 100644 src/main/java/org/dimdev/dimdoors/api/block/ExplosionConvertibleBlock.java create mode 100644 src/main/java/org/dimdev/dimdoors/mixin/WorldMixin.java diff --git a/src/main/java/org/dimdev/dimdoors/api/block/CustomBreakBlock.java b/src/main/java/org/dimdev/dimdoors/api/block/CustomBreakBlock.java new file mode 100644 index 00000000..960f7c33 --- /dev/null +++ b/src/main/java/org/dimdev/dimdoors/api/block/CustomBreakBlock.java @@ -0,0 +1,43 @@ +package org.dimdev.dimdoors.api.block; + +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.entity.Entity; +import net.minecraft.fluid.FluidState; +import net.minecraft.util.Pair; +import net.minecraft.util.TypedActionResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import java.util.function.Consumer; + +/** + * Only works in cases where {@link net.minecraft.block.AbstractBlock#getStateForNeighborUpdate AbstractBlock#getStateForNeighborUpdate} returns an air {@link BlockState} + */ +public interface CustomBreakBlock { + TypedActionResult>> customBreakBlock(World world, BlockPos pos, BlockState blockState, Entity breakingEntity); + + /* + If this causes any issue mixin into FluidState instead. + Also remember to remove the access wideners. + */ + class HackyFluidState extends FluidState { + private final BlockState blockState; + private final Consumer blockEntityConsumer; + + public HackyFluidState(BlockState blockState, Consumer blockEntityConsumer) { + super(blockState.getFluidState().getFluid(), blockState.getFluidState().getEntries(), blockState.getFluidState().codec); + this.blockState = blockState; + this.blockEntityConsumer = blockEntityConsumer; + } + + @Override + public BlockState getBlockState() { + return blockState; + } + + public Consumer getBlockEntityConsumer() { + return blockEntityConsumer; + } + } +} diff --git a/src/main/java/org/dimdev/dimdoors/api/block/ExplosionConvertibleBlock.java b/src/main/java/org/dimdev/dimdoors/api/block/ExplosionConvertibleBlock.java new file mode 100644 index 00000000..7ba930fa --- /dev/null +++ b/src/main/java/org/dimdev/dimdoors/api/block/ExplosionConvertibleBlock.java @@ -0,0 +1,11 @@ +package org.dimdev.dimdoors.api.block; + +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.ActionResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public interface ExplosionConvertibleBlock { + ActionResult explode(World world, BlockPos pos, BlockState state, BlockEntity blockEntity); +} diff --git a/src/main/java/org/dimdev/dimdoors/block/door/DimensionalDoorBlock.java b/src/main/java/org/dimdev/dimdoors/block/door/DimensionalDoorBlock.java index d07c16c0..636040d3 100644 --- a/src/main/java/org/dimdev/dimdoors/block/door/DimensionalDoorBlock.java +++ b/src/main/java/org/dimdev/dimdoors/block/door/DimensionalDoorBlock.java @@ -2,16 +2,23 @@ package org.dimdev.dimdoors.block.door; import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.minecraft.block.*; +import net.minecraft.block.piston.PistonBehavior; +import net.minecraft.util.Pair; +import net.minecraft.util.TypedActionResult; import net.minecraft.world.explosion.Explosion; import org.apache.logging.log4j.Level; import org.dimdev.dimdoors.DimensionalDoorsInitializer; +import org.dimdev.dimdoors.api.block.CustomBreakBlock; +import org.dimdev.dimdoors.api.block.ExplosionConvertibleBlock; import org.dimdev.dimdoors.api.util.math.MathUtil; import org.dimdev.dimdoors.api.util.math.TransformationMatrix3d; import org.dimdev.dimdoors.block.CoordinateTransformerBlock; +import org.dimdev.dimdoors.block.DetachedRiftBlock; import org.dimdev.dimdoors.block.ModBlocks; import org.dimdev.dimdoors.block.RiftProvider; import org.dimdev.dimdoors.block.entity.DetachedRiftBlockEntity; import org.dimdev.dimdoors.block.entity.EntranceRiftBlockEntity; +import org.dimdev.dimdoors.block.entity.RiftData; import org.jetbrains.annotations.Nullable; import net.minecraft.block.entity.BlockEntity; @@ -35,7 +42,9 @@ import net.minecraft.world.event.GameEvent; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -public class DimensionalDoorBlock extends WaterLoggableDoorBlock implements RiftProvider, CoordinateTransformerBlock { +import java.util.function.Consumer; + +public class DimensionalDoorBlock extends WaterLoggableDoorBlock implements RiftProvider, CoordinateTransformerBlock, ExplosionConvertibleBlock, CustomBreakBlock { public DimensionalDoorBlock(Settings settings) { super(settings); } @@ -99,6 +108,11 @@ public class DimensionalDoorBlock extends WaterLoggableDoorBlock implements Rift createDetachedRift(world, pos, world.getBlockState(pos)); } + /* + TODO: rewrite so it can only be used from the lower door block. + I fear this method may be called twice otherwise. + ~CreepyCre + */ public void createDetachedRift(World world, BlockPos pos, BlockState state) { DoubleBlockHalf doubleBlockHalf = state.get(HALF); BlockPos blockPos = pos; @@ -224,4 +238,29 @@ public class DimensionalDoorBlock extends WaterLoggableDoorBlock implements Rift } }); } + + @Override + public ActionResult explode(World world, BlockPos pos, BlockState state, BlockEntity blockEntity) { + if (blockEntity == null) { + return ActionResult.PASS; + } + createDetachedRift(world, pos, state); + return ActionResult.SUCCESS; + } + + @Override + public PistonBehavior getPistonBehavior(BlockState state) { + return state.get(HALF) == DoubleBlockHalf.LOWER ? PistonBehavior.BLOCK : super.getPistonBehavior(state); + } + + @Override + public TypedActionResult>> customBreakBlock(World world, BlockPos pos, BlockState blockState, Entity breakingEntity) { + if (blockState.get(HALF) != DoubleBlockHalf.LOWER) { + return TypedActionResult.pass(null); + } + RiftData data = ((EntranceRiftBlockEntity) world.getBlockEntity(pos)).getData(); + return TypedActionResult.success(new Pair<>(ModBlocks.DETACHED_RIFT.getDefaultState().with(WATERLOGGED, blockState.get(WATERLOGGED)), blockEntity -> { + ((EntranceRiftBlockEntity) blockEntity).setData(data); + })); + } } diff --git a/src/main/java/org/dimdev/dimdoors/mixin/ExplosionMixin.java b/src/main/java/org/dimdev/dimdoors/mixin/ExplosionMixin.java index c6874ffe..1e9d7eaa 100644 --- a/src/main/java/org/dimdev/dimdoors/mixin/ExplosionMixin.java +++ b/src/main/java/org/dimdev/dimdoors/mixin/ExplosionMixin.java @@ -1,154 +1,39 @@ package org.dimdev.dimdoors.mixin; -import com.mojang.datafixers.util.Pair; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectListIterator; -import net.minecraft.block.AbstractFireBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.entity.Entity; -import net.minecraft.entity.damage.DamageSource; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.loot.context.LootContext; -import net.minecraft.loot.context.LootContextParameters; -import net.minecraft.particle.ParticleTypes; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.sound.SoundCategory; -import net.minecraft.sound.SoundEvents; +import net.minecraft.util.ActionResult; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraft.world.explosion.Explosion; -import net.minecraft.world.explosion.ExplosionBehavior; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.dimdev.dimdoors.block.RiftProvider; -import org.dimdev.dimdoors.block.door.DimensionalDoorBlock; -import org.jetbrains.annotations.Nullable; +import org.dimdev.dimdoors.api.block.ExplosionConvertibleBlock; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Mutable; 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.CallbackInfo; import java.util.*; - -import static org.dimdev.dimdoors.block.ModBlocks.DETACHED_RIFT; +import java.util.stream.Collectors; @Mixin(Explosion.class) public class ExplosionMixin { - private static final Logger LOGGER = LogManager.getLogger(); - @Shadow - private static final int field_30960 = 16; - @Shadow - private boolean createFire; - @Shadow - private Explosion.DestructionType destructionType; - @Shadow - private Random random; @Shadow private World world; - @Shadow - private double x; - @Shadow - private double y; - @Shadow - private double z; - @Shadow - @Nullable - private Entity entity; - @Shadow - private float power; - @Shadow - private DamageSource damageSource; - @Shadow - private ExplosionBehavior behavior; - @Shadow + @Mutable @Shadow private List affectedBlocks; - @Shadow - private Map affectedPlayers; - @Shadow - private static void tryMergeStack(ObjectArrayList> stacks, ItemStack stack, BlockPos pos) { - - } - /** - * @author - MalekiRe - */ - @Overwrite - public void affectWorld(boolean particles) { - if (this.world.isClient) { - this.world.playSound(this.x, this.y, this.z, SoundEvents.ENTITY_GENERIC_EXPLODE, SoundCategory.BLOCKS, 4.0F, (1.0F + (this.world.random.nextFloat() - this.world.random.nextFloat()) * 0.2F) * 0.7F, false); - } - - boolean bl = this.destructionType != Explosion.DestructionType.NONE; - if (particles) { - if (!(this.power < 2.0F) && bl) { - this.world.addParticle(ParticleTypes.EXPLOSION_EMITTER, this.x, this.y, this.z, 1.0D, 0.0D, 0.0D); - } else { - this.world.addParticle(ParticleTypes.EXPLOSION, this.x, this.y, this.z, 1.0D, 0.0D, 0.0D); + @Inject(method = "affectWorld", at = @At(value = "INVOKE", target = "Ljava/util/Collections;shuffle(Ljava/util/List;Ljava/util/Random;)V", ordinal = 0, shift = At.Shift.AFTER)) + private void handleExplosionConvertibleBlocks(boolean b1, CallbackInfo ci) { + this.affectedBlocks = this.affectedBlocks.stream().filter(blockPos -> { + BlockState state = this.world.getBlockState(blockPos); + Block block = state.getBlock(); + if (!(block instanceof ExplosionConvertibleBlock)) { + return true; } - } - - if (bl) { - ObjectArrayList> objectArrayList = new ObjectArrayList(); - Collections.shuffle(this.affectedBlocks, this.world.random); - - for (BlockPos blockPos : this.affectedBlocks) { - BlockState blockState = this.world.getBlockState(blockPos); - Block block = blockState.getBlock(); - if (!blockState.isAir()) { - BlockPos blockPos2 = blockPos.toImmutable(); - this.world.getProfiler().push("explosion_blocks"); - if (block.shouldDropItemsOnExplosion((Explosion) (Object) this) && this.world instanceof ServerWorld) { - //TODO: Change this to work with trapdoors as well, when we implement trapdoors. - //This is the only changed code - if (block instanceof DimensionalDoorBlock) { - LOGGER.log(Level.INFO, "Creating Detached Rift From Explosion of Door"); - ((DimensionalDoorBlock) block).createDetachedRift(this.world, blockPos2); - continue; - } - else if(world.getBlockState(blockPos2).getBlock() == DETACHED_RIFT) { - continue; - } - //Normal code below - BlockEntity blockEntity = blockState.hasBlockEntity() ? this.world.getBlockEntity(blockPos) : null; - LootContext.Builder builder = (new LootContext.Builder((ServerWorld) this.world)).random(this.world.random).parameter(LootContextParameters.ORIGIN, Vec3d.ofCenter(blockPos)).parameter(LootContextParameters.TOOL, ItemStack.EMPTY).optionalParameter(LootContextParameters.BLOCK_ENTITY, blockEntity).optionalParameter(LootContextParameters.THIS_ENTITY, this.entity); - if (this.destructionType == Explosion.DestructionType.DESTROY) { - builder.parameter(LootContextParameters.EXPLOSION_RADIUS, this.power); - } - - blockState.getDroppedStacks(builder).forEach((stack) -> { - tryMergeStack(objectArrayList, stack, blockPos2); - }); - } - - this.world.setBlockState(blockPos, Blocks.AIR.getDefaultState(), 3); - block.onDestroyedByExplosion(this.world, blockPos, (Explosion) (Object) this); - this.world.getProfiler().pop(); - } - } - - ObjectListIterator var12 = objectArrayList.iterator(); - - while(var12.hasNext()) { - Pair pair = (Pair)var12.next(); - Block.dropStack(this.world, (BlockPos)pair.getSecond(), (ItemStack)pair.getFirst()); - } - } - - if (this.createFire) { - Iterator var11 = this.affectedBlocks.iterator(); - - while(var11.hasNext()) { - BlockPos blockPos3 = (BlockPos)var11.next(); - if (this.random.nextInt(3) == 0 && this.world.getBlockState(blockPos3).isAir() && this.world.getBlockState(blockPos3.down()).isOpaqueFullCube(this.world, blockPos3.down())) { - this.world.setBlockState(blockPos3, AbstractFireBlock.getState(this.world, blockPos3)); - } - } - } - + ActionResult result = ((ExplosionConvertibleBlock) block).explode(this.world, blockPos, state, state.hasBlockEntity() ? this.world.getBlockEntity(blockPos) : null); + return result == ActionResult.PASS; + }).collect(Collectors.toList()); } } diff --git a/src/main/java/org/dimdev/dimdoors/mixin/WorldMixin.java b/src/main/java/org/dimdev/dimdoors/mixin/WorldMixin.java new file mode 100644 index 00000000..9a2d44f0 --- /dev/null +++ b/src/main/java/org/dimdev/dimdoors/mixin/WorldMixin.java @@ -0,0 +1,85 @@ +package org.dimdev.dimdoors.mixin; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.entity.Entity; +import net.minecraft.fluid.FluidState; +import net.minecraft.util.Pair; +import net.minecraft.util.TypedActionResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.dimdev.dimdoors.api.block.CustomBreakBlock; +import org.jetbrains.annotations.Nullable; +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 org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.function.Consumer; + +@Mixin(World.class) +public abstract class WorldMixin { + + /* + I thought about redirecting the entire break method to be handled by the block itself, + but I am not quite sure what that would mean for compatibility with other mixins, + since then a large part of the method would need to be canceled. This is rather hacky, but it should fulfill the purpose best + ~CreepyCre + */ + @ModifyVariable(method = "Lnet/minecraft/world/World;breakBlock(Lnet/minecraft/util/math/BlockPos;ZLnet/minecraft/entity/Entity;I)Z", + at = @At(value = "INVOKE_ASSIGN", + target = "Lnet/minecraft/world/World;getFluidState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/fluid/FluidState;", + ordinal = 0)) + private FluidState replaceFluidStateWithCustomHackyFluidState(FluidState original, BlockPos pos, boolean drop, @Nullable Entity breakingEntity, int maxUpdateDepth) { + World world = (World) (Object) this; + BlockState blockState = world.getBlockState(pos); + Block block = blockState.getBlock(); + if (!(block instanceof CustomBreakBlock)) { + return original; + } + TypedActionResult>> result = ((CustomBreakBlock) block).customBreakBlock(world, pos, blockState, breakingEntity); + if (!result.getResult().isAccepted()) { + return original; + } + Pair> pair = result.getValue(); + return new CustomBreakBlock.HackyFluidState(pair.getLeft(), pair.getRight()); + } + + @Inject(method = "Lnet/minecraft/world/World;breakBlock(Lnet/minecraft/util/math/BlockPos;ZLnet/minecraft/entity/Entity;I)Z", + locals = LocalCapture.CAPTURE_FAILHARD, + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;II)Z", + ordinal = 0)) + private void applyBlockEntityModification(BlockPos pos, boolean drop, Entity breakingEntity, int maxUpdateDepth, CallbackInfoReturnable cir, BlockState blockState, FluidState fluidState) { + if (!(fluidState instanceof CustomBreakBlock.HackyFluidState)) { + return; + } + Consumer blockEntityConsumer = ((CustomBreakBlock.HackyFluidState) fluidState).getBlockEntityConsumer(); + if (blockEntityConsumer == null) { + return; + } + BlockEntity blockEntity = ((World) (Object) this).getBlockEntity(pos); + if (blockEntity != null) { + blockEntityConsumer.accept(blockEntity); + } + } + + /* + This is where I'd inject if it turns out the method used above does actually have an issue + */ +// @Inject(method = "Lnet/minecraft/world/World;breakBlock(Lnet/minecraft/util/math/BlockPos;ZLnet/minecraft/entity/Entity;I)Z", +// cancellable = true, +// at = @At( +// value = "INVOKE", +// target = "Lnet/minecraft/world/WorldAccess;syncWorldEvent(ILnet/minecraft/util/math/BlockPos;I)V", +// ordinal = 0, +// shift = At.Shift.BY, +// by = 2 +// ) +// ) + + +} diff --git a/src/main/resources/dimdoors.accesswidener b/src/main/resources/dimdoors.accesswidener index c652ebc6..7fa17f89 100644 --- a/src/main/resources/dimdoors.accesswidener +++ b/src/main/resources/dimdoors.accesswidener @@ -10,3 +10,8 @@ accessible method net/minecraft/entity/Entity setRotation (FF)V # for MutableBlockEntityType extendable class net/minecraft/block/entity/BlockEntityType$BlockEntityFactory + +# for HackyFluidState +extendable class net/minecraft/fluid/FluidState +accessible field net/minecraft/state/State codec Lcom/mojang/serialization/MapCodec; +#Lnet/minecraft/state/State;codec:Lcom/mojang/serialization/MapCodec; diff --git a/src/main/resources/dimdoors.mixins.json b/src/main/resources/dimdoors.mixins.json index 75207ffc..7cdbfc23 100644 --- a/src/main/resources/dimdoors.mixins.json +++ b/src/main/resources/dimdoors.mixins.json @@ -15,6 +15,7 @@ "ServerPlayerEntityMixin", "ServerPlayerInteractionManagerMixin", "StructurePoolMixin", + "WorldMixin", "accessor.BuiltinBiomesAccessor", "accessor.ChunkGeneratorAccessor", "accessor.DefaultParticleTypeAccessor", @@ -33,9 +34,9 @@ "client.ExtendedClientPlayNetworkHandlerMixin", "client.GameRendererMixin", "client.InGameHudMixin", + "client.PostProcessShaderMixin", "client.WorldRendererMixin", - "client.accessor.RenderLayerAccessor", - "client.PostProcessShaderMixin" + "client.accessor.RenderLayerAccessor" ], "injectors": { "defaultRequire": 1