diff options
Diffstat (limited to 'src/main/java')
4 files changed, 219 insertions, 0 deletions
diff --git a/src/main/java/wtf/kity/uncrackable/Uncrackable.java b/src/main/java/wtf/kity/uncrackable/Uncrackable.java new file mode 100644 index 0000000..8735efa --- /dev/null +++ b/src/main/java/wtf/kity/uncrackable/Uncrackable.java @@ -0,0 +1,8 @@ +package wtf.kity.uncrackable; + +import net.neoforged.fml.common.Mod; + +@Mod(Uncrackable.MODID) +public class Uncrackable { + public static final String MODID = "uncrackable"; +} 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; + } +} |