From 52b847653296f6e80fd68678b2a88ee9f560eaff Mon Sep 17 00:00:00 2001 From: ash lea Date: Mon, 1 Jun 2026 14:25:40 -0400 Subject: initial commit --- .../java/wtf/kity/uncrackable/Uncrackable.java | 8 ++ .../uncrackable/mixin/BlockBehaviourMixin.java | 35 +++++++ .../uncrackable/mixin/DragonEggBlockMixin.java | 64 ++++++++++++ .../uncrackable/mixin/FallingBlockEntityMixin.java | 112 +++++++++++++++++++++ src/main/resources/uncrackable.mixins.json | 16 +++ src/main/templates/META-INF/neoforge.mods.toml | 110 ++++++++++++++++++++ 6 files changed, 345 insertions(+) create mode 100644 src/main/java/wtf/kity/uncrackable/Uncrackable.java create mode 100644 src/main/java/wtf/kity/uncrackable/mixin/BlockBehaviourMixin.java create mode 100644 src/main/java/wtf/kity/uncrackable/mixin/DragonEggBlockMixin.java create mode 100644 src/main/java/wtf/kity/uncrackable/mixin/FallingBlockEntityMixin.java create mode 100644 src/main/resources/uncrackable.mixins.json create mode 100644 src/main/templates/META-INF/neoforge.mods.toml (limited to 'src') 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 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 = "", 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 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 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 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 original, @Local Block block) { + return original.call(state) && block != Blocks.DRAGON_EGG; + } +} diff --git a/src/main/resources/uncrackable.mixins.json b/src/main/resources/uncrackable.mixins.json new file mode 100644 index 0000000..d31eb1b --- /dev/null +++ b/src/main/resources/uncrackable.mixins.json @@ -0,0 +1,16 @@ +{ + "required": true, + "package": "wtf.kity.uncrackable.mixin", + "compatibilityLevel": "JAVA_21", + "mixins": [ + "BlockBehaviourMixin", + "DragonEggBlockMixin", + "FallingBlockEntityMixin" + ], + "injectors": { + "defaultRequire": 1 + }, + "overwrites": { + "requireAnnotations": true + } +} \ No newline at end of file diff --git a/src/main/templates/META-INF/neoforge.mods.toml b/src/main/templates/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..d2b603e --- /dev/null +++ b/src/main/templates/META-INF/neoforge.mods.toml @@ -0,0 +1,110 @@ +# This is an example neoforge.mods.toml file. It contains the data relating to the loading mods. +# There are several mandatory fields (#mandatory), and many more that are optional (#optional). +# The overall format is standard TOML format, v0.5.0. +# Note that there are a couple of TOML lists in this file. +# Find more information on toml format here: https://github.com/toml-lang/toml + +# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml +modLoader="javafml" #mandatory + +# A version range to match for said mod loader - for regular FML @Mod it will be the FML version. This is currently 2. +loaderVersion="${loader_version_range}" #mandatory + +# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. +# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. +license="${mod_license}" + +# A URL to refer people to when problems occur with this mod +issueTrackerURL="https://github.com/ashkitten/Uncrackable/issues" #optional + +# A list of mods - how many allowed here is determined by the individual mod loader +[[mods]] #mandatory + +# The modid of the mod +modId="${mod_id}" #mandatory + +# The version number of the mod +version="${mod_version}" #mandatory + +# A display name for the mod +displayName="${mod_name}" #mandatory + +# A URL to query for updates for this mod. See the JSON update specification https://docs.neoforged.net/docs/misc/updatechecker/ +#updateJSONURL="https://change.me.example.invalid/updates.json" #optional + +# A URL for the "homepage" for this mod, displayed in the mod UI +displayURL="https://github.com/ashkitten/Uncrackable/" #optional + +# A file name (in the root of the mod JAR) containing a logo for display +#logoFile="examplemod.png" #optional + +# A text field displayed in the mod UI +#credits="" #optional + +# The authors of the mod, displayed in the mod UI (optional) +authors="ashkitten" + +# The description text for the mod (multi line!) (#mandatory) +description=''' +Makes the dragon egg indestructible by any means, and adds minor feedback improvements to the teleportation mechanic. + +Functional changes: +- Pushable by pistons +- Indestructible by mining or explosions +- Lands on top of slabs and other pushable non-full blocks +- Destroys blocks when landing inside (for non-full blocks like torches, or if a block gets pushed inside while falling) +- Doesn't time out and drop as an item after 30 seconds like other falling blocks +- Remains a falling block if landing above build height +- Wraps around to build height when falling into the void + +Feedback improvements: +- Teleportation particles correctly point to the new location +- Teleportation sound effect added + +This mod is only required on the server side, though the client will generate its own teleportation trail without it. +''' + +# The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded. +[[mixins]] +config="${mod_id}.mixins.json" + +# The [[accessTransformers]] block allows you to declare where your AT file is. +# If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg +#[[accessTransformers]] +#file="META-INF/accesstransformer.cfg" + +# The coremods config file path is not configurable and is always loaded from META-INF/coremods.json + +# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. +[[dependencies.${mod_id}]] #optional + # the modid of the dependency + modId="neoforge" #mandatory + # The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive). + # 'required' requires the mod to exist, 'optional' does not + # 'incompatible' will prevent the game from loading when the mod exists, and 'discouraged' will show a warning + type="required" #mandatory + # Optional field describing why the dependency is required or why it is incompatible + # reason="..." + # The version range of the dependency + versionRange="[${neo_version},)" #mandatory + # An ordering relationship for the dependency. + # BEFORE - This mod is loaded BEFORE the dependency + # AFTER - This mod is loaded AFTER the dependency + ordering="NONE" + # Side this dependency is applied on - BOTH, CLIENT, or SERVER + side="BOTH" + +# Here's another dependency +[[dependencies.${mod_id}]] + modId="minecraft" + type="required" + # This version range declares a minimum of the current minecraft version up to but not including the next major version + versionRange="${minecraft_version_range}" + ordering="NONE" + side="BOTH" + +# Features are specific properties of the game environment, that you may want to declare you require. This example declares +# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't +# stop your mod loading on the server for example. +#[features.${mod_id}] +#openGLVersion="[3.2,)" -- cgit 1.4.1