From 412fe93a89503dca3f695ee5768d5e00cc600e57 Mon Sep 17 00:00:00 2001 From: retuci0 Date: Sun, 22 Feb 2026 12:01:00 +0100 Subject: [PATCH] v3.6 --- cambios.md | 12 +- gradle.properties | 2 +- .../sputnik/command/CommandManager.java | 1 + .../command/commands/RepeatWaitCommand.java | 46 +++ .../sputnik/event/input/SneakEvent.java | 5 + .../mixin/mixins/player/PlayerInputMixin.java | 19 + .../world/RenderTickCounterDynamicMixin.java | 23 ++ .../retucio/sputnik/module/ModuleManager.java | 2 + .../module/modules/movement/Scaffold.java | 339 ++++++++++++++++++ .../module/modules/network/PacketMine.java | 16 +- .../module/modules/player/AutoFish.java | 2 +- .../module/modules/world/AutoTool.java | 2 +- .../sputnik/module/modules/world/Timer.java | 21 ++ .../retucio/sputnik/util/InventoryUtil.java | 33 +- .../me/retucio/sputnik/util/NetworkUtil.java | 82 +++++ src/main/resources/sputnik.mixins.json | 4 +- 16 files changed, 582 insertions(+), 27 deletions(-) create mode 100644 src/main/java/me/retucio/sputnik/command/commands/RepeatWaitCommand.java create mode 100644 src/main/java/me/retucio/sputnik/event/input/SneakEvent.java create mode 100644 src/main/java/me/retucio/sputnik/mixin/mixins/player/PlayerInputMixin.java create mode 100644 src/main/java/me/retucio/sputnik/mixin/mixins/world/RenderTickCounterDynamicMixin.java create mode 100644 src/main/java/me/retucio/sputnik/module/modules/movement/Scaffold.java create mode 100644 src/main/java/me/retucio/sputnik/module/modules/world/Timer.java diff --git a/cambios.md b/cambios.md index 448fb37..54139cd 100644 --- a/cambios.md +++ b/cambios.md @@ -4,15 +4,19 @@ ### adiciones +#### comandos nuevos: + +- repesperar: combinación de \$repetir y \$esperar #### módulos nuevos: +- andamios: coloca bloques a tus pies a medida que caminas +- cocaína: hace que todo vaya más rápido o más lento ### arreglos / mejoras -- arreglado el crash por no incluir el sistema de eventos en el mod (soy gilipollas) -- mejoras varias al módulo tobillos fuertes -- ahora el módulo de interfaces en portales funciona correctamente -- reponer ya no está repe +- opción para no romper el pico en selección de minado ## problemas reconocidos + +- lo de siempre \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index aa078ef..849fc88 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,6 +10,6 @@ fabric_api_version=0.140.2+1.21.11 neutrino_version=1.1.0 -mod_version=3.5 +mod_version=3.6 maven_group=me.retucio archives_base_name=sputnik \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/command/CommandManager.java b/src/main/java/me/retucio/sputnik/command/CommandManager.java index 7cd6b88..a4cfb65 100644 --- a/src/main/java/me/retucio/sputnik/command/CommandManager.java +++ b/src/main/java/me/retucio/sputnik/command/CommandManager.java @@ -42,6 +42,7 @@ public class CommandManager { addCommand(new PrefixCommand()); addCommand(new PurgeCommand()); addCommand(new RepeatCommand()); + addCommand(new RepeatWaitCommand()); addCommand(new SaveScreenshotCommand()); addCommand(new SendCommand()); addCommand(new SpectateCommand()); diff --git a/src/main/java/me/retucio/sputnik/command/commands/RepeatWaitCommand.java b/src/main/java/me/retucio/sputnik/command/commands/RepeatWaitCommand.java new file mode 100644 index 0000000..6ee6c94 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/command/commands/RepeatWaitCommand.java @@ -0,0 +1,46 @@ +package me.retucio.sputnik.command.commands; + +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import me.retucio.sputnik.command.Command; +import me.retucio.sputnik.util.ChatUtil; +import net.minecraft.command.CommandSource; + +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class RepeatWaitCommand extends Command { + + public RepeatWaitCommand() { + super("repesperar", "combinación de $repetir y $esperar"); + } + + @Override + public void build(LiteralArgumentBuilder builder) { + builder.then(argument("ms", IntegerArgumentType.integer(1)) + .then(argument("veces", IntegerArgumentType.integer(1)) + .then(argument("comando", StringArgumentType.greedyString()) + .executes((context) -> { + int ms = context.getArgument("ms", Integer.class); + int times = context.getArgument("veces", Integer.class); + String cmd = context.getArgument("comando", String.class); + + for (int i = 0; i < times; i++) { + int delay = i * ms; + int finalI = i; + new ScheduledThreadPoolExecutor(2).schedule( + () -> ChatUtil.info( + cmd.replaceAll("(?i)%index%", + String.valueOf(finalI))), + delay, + TimeUnit.MILLISECONDS + ); + } + return SUCCESS; + }) + ) + ) + ); + } +} diff --git a/src/main/java/me/retucio/sputnik/event/input/SneakEvent.java b/src/main/java/me/retucio/sputnik/event/input/SneakEvent.java new file mode 100644 index 0000000..a2d01f2 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/event/input/SneakEvent.java @@ -0,0 +1,5 @@ +package me.retucio.sputnik.event.input; + +import com.github.retucio.neutrino.Event; + +public class SneakEvent extends Event {} diff --git a/src/main/java/me/retucio/sputnik/mixin/mixins/player/PlayerInputMixin.java b/src/main/java/me/retucio/sputnik/mixin/mixins/player/PlayerInputMixin.java new file mode 100644 index 0000000..fe33583 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/mixin/mixins/player/PlayerInputMixin.java @@ -0,0 +1,19 @@ +package me.retucio.sputnik.mixin.mixins.player; + +import me.retucio.sputnik.Sputnik; +import me.retucio.sputnik.event.input.SneakEvent; +import net.minecraft.util.PlayerInput; +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.CallbackInfoReturnable; + +@Mixin(PlayerInput.class) +public abstract class PlayerInputMixin { + + @Inject(method = "sneak", at = @At("HEAD"), cancellable = true) + private void onSneak(CallbackInfoReturnable cir) { + SneakEvent event = Sputnik.EVENT_BUS.post(new SneakEvent()); + if (event.isCancelled()) cir.cancel(); + } +} diff --git a/src/main/java/me/retucio/sputnik/mixin/mixins/world/RenderTickCounterDynamicMixin.java b/src/main/java/me/retucio/sputnik/mixin/mixins/world/RenderTickCounterDynamicMixin.java new file mode 100644 index 0000000..71a2fab --- /dev/null +++ b/src/main/java/me/retucio/sputnik/mixin/mixins/world/RenderTickCounterDynamicMixin.java @@ -0,0 +1,23 @@ +package me.retucio.sputnik.mixin.mixins.world; + +import me.retucio.sputnik.module.ModuleManager; +import me.retucio.sputnik.module.modules.world.Timer; +import net.minecraft.client.render.RenderTickCounter; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +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.CallbackInfoReturnable; + +@Mixin(RenderTickCounter.Dynamic.class) +public abstract class RenderTickCounterDynamicMixin { + + @Shadow + private float dynamicDeltaTicks; + + @Inject(method = "beginRenderTick(J)I", at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/RenderTickCounter$Dynamic;lastTimeMillis:J", opcode = Opcodes.PUTFIELD)) + private void onBeingRenderTick(long timeMillis, CallbackInfoReturnable cir) { + dynamicDeltaTicks *= ModuleManager.INSTANCE.getModuleByClass(Timer.class).multiplier.getFloatValue(); + } +} diff --git a/src/main/java/me/retucio/sputnik/module/ModuleManager.java b/src/main/java/me/retucio/sputnik/module/ModuleManager.java index 4644899..70f9790 100644 --- a/src/main/java/me/retucio/sputnik/module/ModuleManager.java +++ b/src/main/java/me/retucio/sputnik/module/ModuleManager.java @@ -116,6 +116,7 @@ public class ModuleManager { add(new Jesus()); add(new ReverseStep()); add(new SafeWalk()); + add(new Scaffold()); add(new Slippy()); add(new Step()); add(new TridentBoost()); @@ -171,6 +172,7 @@ public class ModuleManager { add(new Racist()); add(new StrongholdTriangulator()); add(new TimeChanger()); + add(new Timer()); } diff --git a/src/main/java/me/retucio/sputnik/module/modules/movement/Scaffold.java b/src/main/java/me/retucio/sputnik/module/modules/movement/Scaffold.java new file mode 100644 index 0000000..5d0dd06 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/module/modules/movement/Scaffold.java @@ -0,0 +1,339 @@ +package me.retucio.sputnik.module.modules.movement; + +import com.github.retucio.neutrino.EventListener; +import com.google.common.collect.Streams; +import me.retucio.sputnik.event.input.SneakEvent; +import me.retucio.sputnik.event.interact.PlaceBlockEvent; +import me.retucio.sputnik.event.render.Render3DEvent; +import me.retucio.sputnik.module.Category; +import me.retucio.sputnik.module.Module; +import me.retucio.sputnik.module.setting.SettingGroup; +import me.retucio.sputnik.module.setting.settings.*; + +import me.retucio.sputnik.util.Colors; +import me.retucio.sputnik.util.InventoryUtil; +import me.retucio.sputnik.util.Lists; +import me.retucio.sputnik.util.NetworkUtil; +import me.retucio.sputnik.util.render.RenderUtil; +import net.minecraft.block.Block; +import net.minecraft.block.FallingBlock; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.BlockItem; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Hand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.Vec3d; + +import java.util.List; + +public class Scaffold extends Module { + + private final SettingGroup sgBlocks = addSg(new SettingGroup("bloques", false)); + private final SettingGroup sgBridge = addSg(new SettingGroup("puente", true)); + private final SettingGroup sgRender = addSg(new SettingGroup("render", false)); + + // todo: arreglar esta puta mierda + private final BooleanSetting rotate = sgGeneral.add(new BooleanSetting( + "rotar", + "mover la cabeza", + false + )); + + + /* ajustes de selección de bloques */ + + private final ListSetting blocks = sgBlocks.add(new ListSetting<>( + "bloques", + "bloques a aplicar al filtro", + Lists.blockList, + Lists.allFalse(Lists.blockList), + Lists.blockNames + )); + + private final EnumSetting blocksFilter = sgBlocks.add(new EnumSetting<>( + "filtro", + "tipo de filtro a usar", + ListMode.class, ListMode.BLACKLIST + )); + + private final BooleanSetting autoSelectItem = sgBlocks.add(new BooleanSetting( + "autoselect", + "seleccionar slot más cercano con bloques automáticamente", + true + )); + + + /* extender puente */ + + private final BooleanSetting extend = sgBridge.add(new BooleanSetting( + "extender", + "poner bloques en frente al caminar", + false + )); + + private final NumberSetting extendDistance = sgBridge.add(new NumberSetting( + "distancia a extender", + "cuánto extender", + 2, + 0, + 4, + 0.1 + )); + + + /* torre */ + + private final BooleanSetting tower = sgBridge.add(new BooleanSetting( + "torre", + "hacer torres verticales chulas", + false + )); + + private final NumberSetting towerSpeed = sgBridge.add(new NumberSetting( + "velocidad de torre", + "a qué velocidad hacer la torre", + 0.5, + 0, + 1, + 0.01 + )); + + private final BooleanSetting towerWhileMoving = sgBridge.add(new BooleanSetting( + "torre moviéndose", + "te permite hacer una torre mientras te mueves", + false + )); + + + /* otros */ + + private final BooleanSetting mjBridge = sgBridge.add(new BooleanSetting( + "mj bridge", + "hacer que saltar no eleve el nivel y del puente", + false + )); + + private final BooleanSetting sneakForDown = sgBridge.add(new BooleanSetting( + "descender", + "agacharse para descender", + true + )); + + private final BooleanSetting headhitter = sgBridge.add(new BooleanSetting( + "headhitter", + "poner bloques sobre tu cabeza", + false + )); + + + /* ajustes de renderizado */ + + private final BooleanSetting outlines = sgRender.add(new BooleanSetting( + "contorno", + "renderizar contorno del bloque colocado", + true + )); + + private final ColorSetting outlineColor = sgRender.add(new ColorSetting( + "color del contorno", + "color a usar al renderizar el contorno", + Colors.mainColor, + false + )); + + private final NumberSetting lineWidth = sgRender.add(new NumberSetting( + "grosor de línea", + "grosor de línea del contorno", + 1.5, + 0.1, + 10, + 0.1 + )); + + private final BooleanSetting fillings = sgRender.add(new BooleanSetting( + "relleno", + "renderizar relleno del bloque colocado", + false + )); + + private final ColorSetting fillingColor = sgRender.add(new ColorSetting( + "color del relleno", + "color a uisar al renderizar el relleno", + Colors.withAlpha(Colors.mainColor, 60), + false + )); + + + private final BlockPos.Mutable bp = new BlockPos.Mutable(); + private Integer mjY = null; + + + public Scaffold() { + super("andamios", "pone bloques bajo tus pies de manera automática", Category.MOVEMENT); + + extend.onUpdate(extendDistance::setVisible); + + tower.onUpdate(v -> { + towerSpeed.setVisible(v); + towerWhileMoving.setVisible(v); + }); + + outlines.onUpdate(v -> { + outlineColor.setVisible(v); + lineWidth.setVisible(v); + }); + + fillings.onUpdate(fillingColor::setVisible); + } + + + @Override + public void onDisable() { + mjY = null; + } + + @Override + public void onTick() { + if (mc.player == null || mc.world == null) return; + + Vec3d pos = mc.player.getEntityPos(); + + // extender puente si se está moviendo hacia adelante + if (extend.getValue() && isMoving() && mc.player.forwardSpeed > 0) { + Vec3d dir = Vec3d.fromPolar(0, mc.player.getYaw()).normalize().multiply(extendDistance.getValue()); + pos = pos.add(dir.x, 0, dir.z); + } + + // seleccionar nivel Y + double y; + if (mjBridge.getValue() && mjY != null && isMoving()) { + y = mjY; + } else { + y = pos.y - 0.5; + } + + // descender con la tecla de agacharse + if (sneakForDown.getValue() && mc.options.sneakKey.isPressed()) { + if (mc.world.isAir(bp.withY(bp.getY() - 1))) { + y -= 1; + } + } + + // colocar bloque + bp.set(pos.x, y, pos.z); + place(bp); + + // colocar bloque encima de la cabeza + if (headhitter.getValue() && mc.options.jumpKey.isPressed() && !mc.options.sneakKey.isPressed()) { + place(mc.player.getBlockPos().up(2)); + } + + // poner bloques a los pies del jugador mientras asciende para hacer la torre + if (tower.getValue() + && mc.options.jumpKey.isPressed() + && !mc.options.sneakKey.isPressed() + && !mc.player.getInventory().getSelectedStack().isEmpty() + && !mc.world.isAir(mc.player.getBlockPos().down())) { + Vec3d velocity = mc.player.getVelocity(); + Box playerBox = mc.player.getBoundingBox(); + + if (Streams.stream(mc.world.getBlockCollisions(mc.player, playerBox.offset(0, 1, 0))).toList().isEmpty()) { + if (towerWhileMoving.getValue() || !isMoving()) { + velocity = new Vec3d(velocity.x, towerSpeed.getValue(), velocity.z); + } + mc.player.setVelocity(velocity); + } else { + mc.player.setVelocity(velocity.x, Math.ceil(mc.player.getY()) - mc.player.getY(), velocity.z); + mc.player.setOnGround(true); + } + } + } + + @EventListener + private void onRenderWorld(Render3DEvent event) { + if (outlines.getValue()) RenderUtil.drawBlockOutline(event.getMatrices(), bp, outlineColor.getValue(), lineWidth.getFloatValue(), false); + if (fillings.getValue()) RenderUtil.drawBlockFilled(event.getMatrices(), bp, fillingColor.getValue(), false); + } + + @EventListener + private void onPlaceBlock(PlaceBlockEvent event) { + // guardar último valor Y para el mj bridge + if (mjBridge.getValue()) { + mjY = event.getResult().getBlockPos().getY(); + } + } + + @EventListener + private void onSneak(SneakEvent event) { + // no agacharse + if (sneakForDown.getValue()) { + event.cancel(); + } + } + + private void place(BlockPos pos) { + int slot = -1; + // seleccionar bloque + if (autoSelectItem.getValue()) { + List slots = InventoryUtil.findAllSlots(this::isValidBlock); + for (int s : slots) { + if (PlayerInventory.isValidHotbarIndex(s)) { + slot = s; + break; + } + } + if (slot == -1) return; + } else { + slot = mc.player.getInventory().getSelectedSlot(); + } + + if (mc.player.getInventory().getSelectedSlot() != slot) { + mc.player.getInventory().setSelectedSlot(slot); + } + + // colocar bloque + NetworkUtil.placeBlock( + pos, Hand.MAIN_HAND, + mc.player.getInventory().getSelectedSlot(), + rotate.getValue(), + false, + true, + true + ); + } + + // método helper para ver si un bloque cumple con el filtro + private boolean isValidBlock(ItemStack stack) { + if (!(stack.getItem() instanceof BlockItem)) return false; + Block block = ((BlockItem) stack.getItem()).getBlock(); + + boolean contains = blocks.isEnabled(block); + if (blocksFilter.is(ListMode.BLACKLIST) && contains) return false; + if (blocksFilter.is(ListMode.WHITELIST) && !contains) return false; + + if (!Block.isShapeFullCube(block.getDefaultState().getCollisionShape(mc.world, BlockPos.ORIGIN))) { + return false; + } + + if (block instanceof FallingBlock) { + return !FallingBlock.canFallThrough(mc.world.getBlockState(mc.player.getBlockPos().down())); + } + + return true; + } + + private boolean isMoving() { + return mc.player.forwardSpeed != 0 + || mc.player.sidewaysSpeed != 0; + } + + public enum ListMode { + WHITELIST("lista blanca (incluir)"), + BLACKLIST("lista negra (excluir"); + + private final String name; + ListMode(String name) { this.name = name; } + @Override public String toString() { return name; } + } +} diff --git a/src/main/java/me/retucio/sputnik/module/modules/network/PacketMine.java b/src/main/java/me/retucio/sputnik/module/modules/network/PacketMine.java index d2cae78..c7a2ca0 100644 --- a/src/main/java/me/retucio/sputnik/module/modules/network/PacketMine.java +++ b/src/main/java/me/retucio/sputnik/module/modules/network/PacketMine.java @@ -88,6 +88,12 @@ public class PacketMine extends Module { 0.1 )); + private final BooleanSetting dontBreak = sgGeneral.add(new BooleanSetting( + "no romper", + "evita romper el pico", + true + )); + public PacketMine() { super("selección de minado", "selecciona bloques a minar con paquetes", @@ -150,7 +156,9 @@ public class PacketMine extends Module { currentOrderIndex = (currentOrderIndex + 1) % miningBlocks.size(); break; } - currentOrderIndex = (currentOrderIndex + 1) % miningBlocks.size(); + if (!miningBlocks.isEmpty()) { + currentOrderIndex = (currentOrderIndex + 1) % miningBlocks.size(); + } } while (currentOrderIndex != startIndex); if (!processed) { @@ -252,6 +260,12 @@ public class PacketMine extends Module { return false; } + if (dontBreak.getValue() && mc.player.getMainHandStack().getMaxDamage() - mc.player.getMainHandStack().getDamage() <= 1) { + ChatUtil.warn("pico por romperse"); + toggle(); + return false; + } + mc.interactionManager.sendSequencedPacket(mc.world, sequence -> new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, pos, dir, sequence)); mining = true; diff --git a/src/main/java/me/retucio/sputnik/module/modules/player/AutoFish.java b/src/main/java/me/retucio/sputnik/module/modules/player/AutoFish.java index 3d9e8a7..240d8c0 100644 --- a/src/main/java/me/retucio/sputnik/module/modules/player/AutoFish.java +++ b/src/main/java/me/retucio/sputnik/module/modules/player/AutoFish.java @@ -143,7 +143,7 @@ public class AutoFish extends Module { private int switchToFishingRod() { if (mc.player.getActiveItem().isOf(Items.FISHING_ROD)) return mc.player.getInventory().getSelectedSlot(); - List slots = InventoryUtil.findItem(Items.FISHING_ROD); + List slots = InventoryUtil.findAllSlots(stack -> stack.isOf(Items.FISHING_ROD)); for (int slot : slots) { if (PlayerInventory.isValidHotbarIndex(slot)) { mc.player.getInventory().setSelectedSlot(slot); diff --git a/src/main/java/me/retucio/sputnik/module/modules/world/AutoTool.java b/src/main/java/me/retucio/sputnik/module/modules/world/AutoTool.java index a04a617..b736aaa 100644 --- a/src/main/java/me/retucio/sputnik/module/modules/world/AutoTool.java +++ b/src/main/java/me/retucio/sputnik/module/modules/world/AutoTool.java @@ -163,7 +163,7 @@ public class AutoTool extends Module { if (result.getType() == HitResult.Type.MISS || !(result instanceof BlockHitResult bhr)) return; BlockState state = mc.world.getBlockState(bhr.getBlockPos()); - List tools = InventoryUtil.getStacksOfComponentType(DataComponentTypes.TOOL); + List tools = InventoryUtil.find(DataComponentTypes.TOOL); if (tools.isEmpty()) return; Map speeds = new HashMap<>(); diff --git a/src/main/java/me/retucio/sputnik/module/modules/world/Timer.java b/src/main/java/me/retucio/sputnik/module/modules/world/Timer.java new file mode 100644 index 0000000..20d868e --- /dev/null +++ b/src/main/java/me/retucio/sputnik/module/modules/world/Timer.java @@ -0,0 +1,21 @@ +package me.retucio.sputnik.module.modules.world; + +import me.retucio.sputnik.module.Category; +import me.retucio.sputnik.module.Module; +import me.retucio.sputnik.module.setting.settings.NumberSetting; + +public class Timer extends Module { + + public NumberSetting multiplier = sgGeneral.add(new NumberSetting( + "multi", + "como en el Balatro", + 1, + 0.1, + 10, + 0.1 + )); + + public Timer() { + super("cocaína", "cambia la velocidad de todo el en juego", Category.WORLD); + } +} diff --git a/src/main/java/me/retucio/sputnik/util/InventoryUtil.java b/src/main/java/me/retucio/sputnik/util/InventoryUtil.java index 7bdd157..b2ebfc9 100644 --- a/src/main/java/me/retucio/sputnik/util/InventoryUtil.java +++ b/src/main/java/me/retucio/sputnik/util/InventoryUtil.java @@ -70,7 +70,20 @@ public class InventoryUtil { return stacks; } - public static List getStacksOfTag(TagKey tag) { + public static List findAllSlots(Predicate predicate) { + List slots = new ArrayList<>(); + if (mc.player == null) return slots; + for (int i = 0; i < mc.player.getInventory().size(); i++) { + ItemStack stack = mc.player.getInventory().getStack(i); + if (predicate.test(stack)) { + slots.add(i); + } + } + return slots; + } + + + public static List find(TagKey tag) { if (mc.player == null) return null; List stacks = new ArrayList<>(); for (ItemStack stack : mc.player.getInventory()) { @@ -83,7 +96,7 @@ public class InventoryUtil { return stacks; } - public static List getStacksOfComponentType(ComponentType componentType) { + public static List find(ComponentType componentType) { List stacks = new ArrayList<>(); if (mc.player == null) return stacks; for (ItemStack stack : mc.player.getInventory()) { @@ -256,20 +269,6 @@ public class InventoryUtil { return matchingSlots; } - public static List findItem(Item item) { - List matchingSlots = new ArrayList<>(); - PlayerInventory inv = mc.player.getInventory(); - - for (int i = 0; i < inv.size(); i++) { - ItemStack stack = inv.getStack(i); - if (!stack.isEmpty() && stack.isOf(item)) { - matchingSlots.add(i); - } - } - - return matchingSlots; - } - public static void quickMoveAll(HandledScreen screen, ItemStack reference) { if (mc.player == null || mc.interactionManager == null) return; @@ -349,6 +348,4 @@ public class InventoryUtil { public static Inventory getEchestInv() { return echestInv; } - - } diff --git a/src/main/java/me/retucio/sputnik/util/NetworkUtil.java b/src/main/java/me/retucio/sputnik/util/NetworkUtil.java index 524ed2c..56a4658 100644 --- a/src/main/java/me/retucio/sputnik/util/NetworkUtil.java +++ b/src/main/java/me/retucio/sputnik/util/NetworkUtil.java @@ -2,15 +2,27 @@ package me.retucio.sputnik.util; import me.retucio.sputnik.event.network.PacketEvent; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.ShapeContext; import net.minecraft.client.MinecraftClient; +import net.minecraft.command.argument.EntityAnchorArgumentType; +import net.minecraft.item.BlockItem; +import net.minecraft.item.ItemStack; import net.minecraft.network.ClientConnection; import net.minecraft.network.listener.PacketListener; import net.minecraft.network.packet.Packet; import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket; +import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket; import net.minecraft.network.packet.s2c.play.WorldTimeUpdateS2CPacket; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; import java.util.ArrayList; import java.util.List; @@ -87,4 +99,74 @@ public class NetworkUtil { mc.player.setSneaking(wasSneaking); } + + public static boolean placeBlock(BlockPos blockPos, Hand hand, int slot, boolean rotate, boolean swingHand, boolean checkEntities, boolean swapBack) { + if (slot < 0 || slot > 8) return false; + + Block toPlace = Blocks.OBSIDIAN; + ItemStack i = hand == Hand.MAIN_HAND ? mc.player.getInventory().getStack(slot) : mc.player.getOffHandStack(); + if (i.getItem() instanceof BlockItem blockItem) toPlace = blockItem.getBlock(); + if (!canPlaceBlock(blockPos, checkEntities, toPlace)) return false; + + Vec3d hitPos = Vec3d.ofCenter(blockPos); + + BlockPos neighbour; + Direction side = getClosestSide(blockPos); + + if (side == null) { + side = Direction.UP; + neighbour = blockPos; + } else { + neighbour = blockPos.offset(side); + hitPos = hitPos.add(side.getOffsetX() * 0.5, side.getOffsetY() * 0.5, side.getOffsetZ() * 0.5); + } + + BlockHitResult bhr = new BlockHitResult(hitPos, side.getOpposite(), neighbour, false); + + int prevSlot = mc.player.getInventory().getSelectedSlot(); + + if (rotate) { + mc.player.setYaw((float) EntityUtil.getYaw(hitPos)); + mc.player.setPitch((float) EntityUtil.getPitch(hitPos)); + } + + InventoryUtil.swapWithHotbar(mc.player.getInventory().getSelectedSlot(), slot); + interactBlock(bhr, hand, swingHand); + + if (swapBack) { + InventoryUtil.swapWithHotbar(mc.player.getInventory().getSelectedSlot(), prevSlot); + } + + return true; + } + + public static boolean canPlaceBlock(BlockPos blockPos, boolean checkEntities, Block block) { + if (blockPos == null) return false; + if (!World.isValid(blockPos)) return false; + if (!mc.world.getBlockState(blockPos).isReplaceable()) return false; + return !checkEntities || mc.world.canPlace(block.getDefaultState(), blockPos, ShapeContext.absent()); + } + + public static Direction getClosestSide(BlockPos blockPos) { + Vec3d lookVec = blockPos.toCenterPos().subtract(mc.player.getEyePos()); + double bestRelevancy = -Double.MAX_VALUE; + Direction bestSide = null; + + for (Direction side : Direction.values()) { + BlockPos neighbor = blockPos.offset(side); + BlockState state = mc.world.getBlockState(neighbor); + + if (state.isAir()) continue; + + if (!state.getFluidState().isEmpty()) continue; + + double relevancy = side.getAxis().choose(lookVec.getX(), lookVec.getY(), lookVec.getZ()) * side.getDirection().offset(); + if (relevancy > bestRelevancy) { + bestRelevancy = relevancy; + bestSide = side; + } + } + + return bestSide; + } } \ No newline at end of file diff --git a/src/main/resources/sputnik.mixins.json b/src/main/resources/sputnik.mixins.json index 3ace205..f8d6267 100644 --- a/src/main/resources/sputnik.mixins.json +++ b/src/main/resources/sputnik.mixins.json @@ -23,6 +23,7 @@ "mixins.network.ServerConnectorMixin", "mixins.network.YggdrasilUserApiServiceMixin", "mixins.player.PlayerEntityMixin", + "mixins.player.PlayerInputMixin", "mixins.player.PlayerInventoryMixin", "mixins.world.BlockCollisionSpliteratorMixin" ], @@ -96,6 +97,7 @@ "mixins.world.DamageParticleFactoryMixin", "mixins.world.DamageParticleMixin", "mixins.world.EmitterParticleMixin", - "mixins.world.ParticleManagerMixin" + "mixins.world.ParticleManagerMixin", + "mixins.world.RenderTickCounterDynamicMixin" ] } \ No newline at end of file