Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ bin/
# fabric

run/
# temp files

DEVNODE/
gradlew
8 changes: 8 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ repositories {



sourceSets {
main {
java {
exclude("**/temp/**")
}
}
}

tasks {
processResources {
val propertyMap = mapOf(
Expand Down
Empty file modified gradlew
100644 → 100755
Empty file.
4 changes: 3 additions & 1 deletion src/main/java/com/nnpg/glazed/GlazedAddon.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,10 @@ public void onInitialize() {
Modules.get().add(new PremiumTunnelBaseFinder());
Modules.get().add(new AdminList());
Modules.get().add(new AutoTreeFarmer());
Modules.get().add(new CrystalTweaks());
Modules.get().add(new CrystalDeathLock());
}

@EventHandler
private void onGameJoined(GameJoinedEvent event) {
MyScreen.checkVersionOnServerJoin();
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/com/nnpg/glazed/mixins/CrystalTweaksMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.nnpg.glazed.mixins;

import com.nnpg.glazed.modules.pvp.CrystalTweaks;
import meteordevelopment.meteorclient.systems.modules.Modules;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.network.ClientPlayerInteractionManager;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
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;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(ClientPlayerInteractionManager.class)
public class CrystalTweaksMixin {

@Inject(
method = "clickSlot",
at = @At("HEAD"),
cancellable = true
)
private void glazed$onClickSlot(
int syncId,
int slot,
int button,
SlotActionType actionType,
PlayerEntity player,
CallbackInfo ci
) {
CrystalTweaks module = Modules.get().get(CrystalTweaks.class);
if (module != null && module.isActive() && module.shouldBlockSlotClick(syncId, slot, button, actionType)) {
ci.cancel();
}
}

@Inject(
method = "interactBlock",
at = @At("HEAD"),
cancellable = true
)
private void glazed$onInteractBlock(
ClientPlayerEntity player,
Hand hand,
BlockHitResult hitResult,
CallbackInfoReturnable<ActionResult> cir
) {
CrystalTweaks module = Modules.get().get(CrystalTweaks.class);
if (module != null && module.isActive() && module.shouldBlockInteractBlock(player, hand, hitResult)) {
cir.setReturnValue(ActionResult.FAIL);
}
}
}
149 changes: 149 additions & 0 deletions src/main/java/com/nnpg/glazed/modules/pvp/CrystalDeathLock.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package com.nnpg.glazed.modules.pvp;

import com.nnpg.glazed.GlazedAddon;
import meteordevelopment.meteorclient.events.entity.player.AttackEntityEvent;
import meteordevelopment.meteorclient.events.packets.PacketEvent;
import meteordevelopment.meteorclient.settings.*;
import meteordevelopment.meteorclient.systems.modules.Module;
import meteordevelopment.meteorclient.utils.player.ChatUtils;
import meteordevelopment.orbit.EventHandler;
import meteordevelopment.orbit.EventPriority;
import net.minecraft.block.Blocks;
import net.minecraft.entity.decoration.EndCrystalEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.Entity;
import net.minecraft.item.Items;
import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket;
import net.minecraft.network.packet.s2c.play.EntityStatusS2CPacket;
import net.minecraft.util.math.BlockPos;

public class CrystalDeathLock extends Module {

private final SettingGroup sgGeneral = settings.getDefaultGroup();

private final Setting<Double> detectionRange = sgGeneral.add(new DoubleSetting.Builder()
.name("detection-range")
.description("Radius around your position in which a player death triggers the lock.")
.defaultValue(15.0)
.min(1.0)
.sliderMax(30.0)
.build()
);

private final Setting<Integer> lockDurationMs = sgGeneral.add(new IntSetting.Builder()
.name("lock-duration-ms")
.description("How long (milliseconds) inputs are blocked after the death. Default: 1000 = 1 second.")
.defaultValue(1000)
.min(100)
.sliderRange(100, 5000)
.build()
);

private final Setting<Boolean> blockCrystalPlace = sgGeneral.add(new BoolSetting.Builder()
.name("block-crystal-place")
.description("Prevent placing end crystals during the lock window.")
.defaultValue(true)
.build()
);

private final Setting<Boolean> blockCrystalAttack = sgGeneral.add(new BoolSetting.Builder()
.name("block-crystal-attack")
.description("Prevent left-clicking (attacking) end crystals during the lock window.")
.defaultValue(true)
.build()
);

private final Setting<Boolean> blockAnchorInteract = sgGeneral.add(new BoolSetting.Builder()
.name("block-anchor-interact")
.description("Prevent right-clicking respawn anchors during the lock window.")
.defaultValue(true)
.build()
);

private final Setting<Boolean> notifications = sgGeneral.add(new BoolSetting.Builder()
.name("notifications")
.description("Show a chat message when the lock activates and expires.")
.defaultValue(true)
.build()
);

private volatile long lockUntilNano = 0L;

public CrystalDeathLock() {
super(GlazedAddon.pvp, "crystal-death-lock",
"Blocks end crystal and respawn anchor inputs for a configurable window after a nearby player dies.");
}

@Override
public void onDeactivate() {
lockUntilNano = 0L;
}

@EventHandler(priority = EventPriority.HIGHEST)
private void onPacketReceive(PacketEvent.Receive event) {
if (!(event.packet instanceof EntityStatusS2CPacket statusPacket)) return;
if (statusPacket.getStatus() != 3) return;

if (mc.player == null || mc.world == null) return;
Entity entity = statusPacket.getEntity(mc.world);
if (!(entity instanceof PlayerEntity dead)) return;
if (dead == mc.player) return;
double dist = mc.player.getPos().distanceTo(dead.getPos());
if (dist > detectionRange.get()) return;
lockUntilNano = System.nanoTime() + (lockDurationMs.get() * 1_000_000L);

if (notifications.get()) {
String playerName = dead.getName().getString();
mc.execute(() -> ChatUtils.info(
String.format("[CrystalDeathLock] §cLocked §r— %s died (%.1f blocks away). "
+ "Blocking for §e%dms§r.",
playerName, dist, lockDurationMs.get())));
}
}

@EventHandler(priority = EventPriority.HIGHEST)
private void onPacketSend(PacketEvent.Send event) {
if (!isLocked()) return;
if (mc.player == null || mc.world == null) return;
if (!(event.packet instanceof PlayerInteractBlockC2SPacket packet)) return;

BlockPos pos = packet.getBlockHitResult().getBlockPos();
var block = mc.world.getBlockState(pos).getBlock();
if (blockCrystalPlace.get()) {
boolean holdsCrystal =
mc.player.getMainHandStack().getItem() == Items.END_CRYSTAL
|| mc.player.getOffHandStack().getItem() == Items.END_CRYSTAL;

if (holdsCrystal) {
event.cancel();
return;
}
}
if (blockAnchorInteract.get() && block == Blocks.RESPAWN_ANCHOR) {
event.cancel();
}
}

@EventHandler(priority = EventPriority.HIGHEST)
private void onAttackEntity(AttackEntityEvent event) {
if (!blockCrystalAttack.get()) return;
if (!isLocked()) return;
if (event.entity instanceof EndCrystalEntity) {
event.cancel();
}
}

private boolean isLocked() {
long now = System.nanoTime();
if (lockUntilNano == 0L) return false;

if (now < lockUntilNano) return true;
if (lockUntilNano != 0L) {
lockUntilNano = 0L;
if (notifications.get()) {
ChatUtils.info("[CrystalDeathLock] §aUnlocked §r— inputs restored.");
}
}
return false;
}
}
Loading