summary refs log tree commit diff
path: root/src/main/java/wtf/kity/uncrackable/mixin
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/wtf/kity/uncrackable/mixin')
-rw-r--r--src/main/java/wtf/kity/uncrackable/mixin/BlockBehaviourMixin.java35
-rw-r--r--src/main/java/wtf/kity/uncrackable/mixin/DragonEggBlockMixin.java64
-rw-r--r--src/main/java/wtf/kity/uncrackable/mixin/FallingBlockEntityMixin.java112
3 files changed, 211 insertions, 0 deletions
diff --git a/src/main/java/wtf/kity/uncrackable/mixin/BlockBehaviourMixin.java b/src/main/java/wtf/kity/uncrackable/mixin/BlockBehaviourMixin.java
new file mode 100644
index 0000000..506b0c9
--- /dev/null
+++ b/src/main/java/wtf/kity/uncrackable/mixin/BlockBehaviourMixin.java
@@ -0,0 +1,35 @@
+package wtf.kity.uncrackable.mixin;
+
+import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
+import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.entity.item.FallingBlockEntity;
+import net.minecraft.world.level.BlockGetter;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.state.BlockBehaviour;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.material.PushReaction;
+import net.minecraft.world.phys.shapes.CollisionContext;
+import net.minecraft.world.phys.shapes.EntityCollisionContext;
+import net.minecraft.world.phys.shapes.Shapes;
+import net.minecraft.world.phys.shapes.VoxelShape;
+import org.spongepowered.asm.mixin.Mixin;
+
+@Mixin(BlockBehaviour.class)
+public class BlockBehaviourMixin {
+    /// If checking collision for a dragon egg, non-piston-destructible blocks should act
+    /// as full blocks so the egg lands on top of them.
+    @WrapMethod(method = "getCollisionShape")
+    VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context, Operation<VoxelShape> original) {
+        VoxelShape shape = original.call(state, level, pos, context);
+        if (!shape.isEmpty()
+                && context instanceof EntityCollisionContext entityContext
+                && entityContext.getEntity() instanceof FallingBlockEntity fallingBlock
+                && fallingBlock.getBlockState().is(Blocks.DRAGON_EGG)
+                && state.getPistonPushReaction() != PushReaction.DESTROY
+        ) {
+            return Shapes.block();
+        }
+        return shape;
+    }
+}
diff --git a/src/main/java/wtf/kity/uncrackable/mixin/DragonEggBlockMixin.java b/src/main/java/wtf/kity/uncrackable/mixin/DragonEggBlockMixin.java
new file mode 100644
index 0000000..7690985
--- /dev/null
+++ b/src/main/java/wtf/kity/uncrackable/mixin/DragonEggBlockMixin.java
@@ -0,0 +1,64 @@
+package wtf.kity.uncrackable.mixin;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.particles.ParticleTypes;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.sounds.SoundEvents;
+import net.minecraft.sounds.SoundSource;
+import net.minecraft.util.Mth;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.DragonEggBlock;
+import net.minecraft.world.level.block.state.BlockBehaviour;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.border.WorldBorder;
+import net.minecraft.world.level.material.PushReaction;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Overwrite;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
+
+@Mixin(DragonEggBlock.class)
+public class DragonEggBlockMixin {
+    @ModifyVariable(method = "<init>", at = @At("HEAD"), ordinal = 0, argsOnly = true)
+    private static BlockBehaviour.Properties init(BlockBehaviour.Properties properties) {
+        return properties
+                .pushReaction(PushReaction.NORMAL)
+                .strength(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
+    }
+
+    /**
+     * @author ashkitten
+     * @reason fixing MC-2157
+     */
+    @Overwrite
+    private void teleport(BlockState state, Level level, BlockPos pos) {
+        if (level instanceof ServerLevel serverLevel) {
+            WorldBorder worldborder = level.getWorldBorder();
+
+            for (int i = 0; i < 1000; i++) {
+                BlockPos blockpos = pos.offset(
+                    level.random.nextInt(16) - level.random.nextInt(16),
+                    level.random.nextInt(8) - level.random.nextInt(8),
+                    level.random.nextInt(16) - level.random.nextInt(16)
+                );
+                if (level.getBlockState(blockpos).isAir() && worldborder.isWithinBounds(blockpos)) {
+                    // slightly different behavior than vanilla, since we send 32x4 particles evenly distributed
+                    // instead of 1x128 randomly distributed, but it actually points in the right fucking direction lol
+                    for (int j = 0; j < 32; j++) {
+                        double d0 = j / 32.0;
+                        double d1 = Mth.lerp(d0, blockpos.getX(), pos.getX()) + (level.random.nextDouble() - 0.5) + 0.5;
+                        double d2 = Mth.lerp(d0, blockpos.getY(), pos.getY()) + level.random.nextDouble() - 0.5;
+                        double d3 = Mth.lerp(d0, blockpos.getZ(), pos.getZ()) + (level.random.nextDouble() - 0.5) + 0.5;
+                        serverLevel.sendParticles(ParticleTypes.PORTAL, d1, d2, d3, 4,  0.1, 0.1, 0.1, 0.0);
+                    }
+
+                    level.setBlock(blockpos, state, 2);
+                    level.removeBlock(pos, false);
+                    level.playSound(null, pos, SoundEvents.ENDERMAN_TELEPORT, SoundSource.BLOCKS);
+
+                    return;
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/wtf/kity/uncrackable/mixin/FallingBlockEntityMixin.java b/src/main/java/wtf/kity/uncrackable/mixin/FallingBlockEntityMixin.java
new file mode 100644
index 0000000..500f970
--- /dev/null
+++ b/src/main/java/wtf/kity/uncrackable/mixin/FallingBlockEntityMixin.java
@@ -0,0 +1,112 @@
+package wtf.kity.uncrackable.mixin;
+
+import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
+import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
+import com.llamalad7.mixinextras.sugar.Local;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.EntityType;
+import net.minecraft.world.entity.item.FallingBlockEntity;
+import net.minecraft.world.item.context.BlockPlaceContext;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.entity.BlockEntity;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.phys.Vec3;
+import org.objectweb.asm.Opcodes;
+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.callback.CallbackInfo;
+
+@Mixin(FallingBlockEntity.class)
+public abstract class FallingBlockEntityMixin extends Entity {
+    public FallingBlockEntityMixin(EntityType<?> entityType, Level level) {
+        super(entityType, level);
+    }
+
+    /// If the egg falls into the void, teleport back up to build limit.
+    @Inject(method = "tick", at = @At("HEAD"))
+    private void beforeTick(CallbackInfo ci) {
+        Vec3 pos = this.position();
+        if (pos.y < this.level().getMinBuildHeight()) {
+            this.teleportTo(pos.x, this.level().getMaxBuildHeight(), pos.z);
+        }
+    }
+
+    /// Egg should never time out. Skip add-assign operation entirely for the egg.
+    @WrapOperation(
+            method = "tick",
+            at = @At(
+                value = "FIELD",
+                target = "Lnet/minecraft/world/entity/item/FallingBlockEntity;time:I",
+                opcode = Opcodes.PUTFIELD
+            )
+    )
+    private void setTime(FallingBlockEntity instance, int value, Operation<Void> original) {
+        if (!instance.getBlockState().is(Blocks.DRAGON_EGG)) {
+            original.call(instance, value);
+        }
+    }
+
+    /// Egg should not drop as an item if it lands above build height.
+    @WrapOperation(
+            method = "tick",
+            at = @At(
+                    value = "FIELD",
+                    target = "Lnet/minecraft/world/entity/item/FallingBlockEntity;dropItem:Z",
+                    opcode = Opcodes.GETFIELD
+            )
+    )
+    private boolean getDropItem(FallingBlockEntity instance, Operation<Boolean> original) {
+        if (instance.getBlockState().is(Blocks.DRAGON_EGG)) {
+            return false;
+        }
+        return original.call(instance);
+    }
+
+    /// If it lands inside a block, destroy the block as if by piston push.
+    /// We override block shapes for non-piston-destructible blocks in`BlockBehaviorMixin`
+    /// so normally this only applies to piston-destructible blocks, unless a block is pushed
+    /// into the egg while it's an entity.
+    @WrapOperation(
+            method = "tick",
+            at = @At(
+                    value = "INVOKE",
+                    target = "Lnet/minecraft/world/level/block/state/BlockState;canBeReplaced(Lnet/minecraft/world/item/context/BlockPlaceContext;)Z"
+            )
+    )
+    private boolean canBeReplaced(
+            BlockState instance,
+            BlockPlaceContext blockPlaceContext,
+            Operation<Boolean> original,
+            @Local BlockPos blockPos
+    ) {
+        if (original.call(instance, blockPlaceContext)) {
+            return true;
+        }
+
+        if (instance.is(Blocks.DRAGON_EGG)) {
+            BlockEntity blockentity = instance.hasBlockEntity() ? this.level().getBlockEntity(blockPos) : null;
+            Block.dropResources(instance, this.level(), blockPos, blockentity);
+            instance.onDestroyedByPushReaction(this.level(), blockPos, Direction.DOWN, this.level().getFluidState(blockPos));
+            return true;
+        }
+
+        return false;
+    }
+
+    /// The egg should never drop as an item, so we always pretend that the ground beneath it is landable.
+    @WrapOperation(
+            method = "tick",
+            at = @At(
+                    value = "INVOKE",
+                    target = "Lnet/minecraft/world/level/block/FallingBlock;isFree(Lnet/minecraft/world/level/block/state/BlockState;)Z"
+            )
+    )
+    private boolean isFree(BlockState state, Operation<Boolean> original, @Local Block block) {
+        return original.call(state) && block != Blocks.DRAGON_EGG;
+    }
+}