This commit is contained in:
retuci0 committed 2026-02-10 16:06:38 +01:00
1 parent a9fd8da7f5
commit c993d4fdac
11 files changed
+482 -33

No files matched your search

+4 -10
View File
@@ -1,23 +1,17 @@
# v3.1
# v3.2
## novedades
- aviso para actualizar
### adiciones
#### módulos nuevos:
- resaltado de entidades: wallhack para entidades, modo de caja y de brillo
- hitboxes: cambia el color de las hitboxes de f3+b
- dupe de bundle: robado de .gg/dupes, usando un bundle y un libro de bookbot, dupea 63 del ítem deseado
- j. k. rowling: rellena libros con caca de manera automática
### arreglos / mejoras
- más opciones para amortiguar caídas con tobillos fuertes
- selección de minado ahora solo renderizará el contorno si su ajuste está activado
- prioridad de selección de minado, cuantos más clics tenga un bloque, mayor será su prioridad. también la opción de deseleccionar un bloque.
- opción para minar bloques en orden forzosamente en selección de minado (no funciona del todo)
- evitar romper la caña si está a punto de romperse en auto pesca
- ahora el aviso de actualizaciones ya funciona bien
## problemas reconocidos
+13 -5
View File
@@ -1,5 +1,6 @@
package me.retucio.sputnik;
import com.ibm.icu.text.CaseMap;
import me.retucio.sputnik.cape.CapeManager;
import me.retucio.sputnik.command.CommandManager;
import me.retucio.sputnik.command.commands.BindCommand;
@@ -9,6 +10,7 @@ import me.retucio.sputnik.config.ConfigManager;
import me.retucio.sputnik.event.EventBus;
import me.retucio.sputnik.event.SubscribeEvent;
import me.retucio.sputnik.event.events.ShutdownEvent;
import me.retucio.sputnik.event.events.interact.OpenScreenEvent;
import me.retucio.sputnik.event.events.sputnik.LoadCapeManagerEvent;
import me.retucio.sputnik.event.events.sputnik.LoadClickGUIEvent;
import me.retucio.sputnik.event.events.sputnik.LoadCommandManagerEvent;
@@ -42,6 +44,7 @@ import net.minecraft.SharedConstants;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ChatScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.TitleScreen;
import net.minecraft.client.gui.screen.ingame.AbstractCommandBlockScreen;
import net.minecraft.client.gui.screen.ingame.AbstractSignEditScreen;
import net.minecraft.client.gui.screen.ingame.AnvilScreen;
@@ -133,11 +136,6 @@ public class Sputnik implements ClientModInitializer {
settingsApplied = true;
}
if (VersionChecker.shouldShowScreen && !(mc.currentScreen instanceof UpdateScreen)) {
VersionChecker.shouldShowScreen = false;
CompletableFuture.delayedExecutor(5, TimeUnit.SECONDS).execute(() -> mc.execute(() -> mc.setScreen(new UpdateScreen())));
}
ModuleManager.INSTANCE.getEnabledModules().forEach(Module::onTick);
mc.getWindow().setTitle(ClientSettingsFrame.guiSettings.windowTitle.getValue());
}
@@ -244,6 +242,16 @@ public class Sputnik implements ClientModInitializer {
|| mc.currentScreen instanceof AbstractCommandBlockScreen;
}
// notificar al usuario de que hay una actualización disponible, si la hay
@SubscribeEvent
public void onSetScreen(OpenScreenEvent event) {
if (VersionChecker.shouldShowScreen && event.getScreen() instanceof TitleScreen) {
event.cancel();
VersionChecker.shouldShowScreen = false;
mc.execute(() -> mc.currentScreen = new UpdateScreen());
}
}
@SubscribeEvent
public void onStop(ShutdownEvent event) {
ConfigManager.save();
@@ -9,11 +9,14 @@ import me.retucio.sputnik.event.events.TickEvent;
import me.retucio.sputnik.event.events.interact.UseItemEvent;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.player.FastUse;
import me.retucio.sputnik.ui.screen.UpdateScreen;
import me.retucio.sputnik.util.VersionChecker;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand;
import org.jspecify.annotations.Nullable;
import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@@ -32,6 +35,10 @@ public abstract class MinecraftClientMixin {
// eventos
@Shadow
@Nullable
public Screen currentScreen;
@Inject(method = "tick", at = @At("HEAD"))
private void onTickPre(CallbackInfo ci) {
Sputnik.INSTANCE.onTick();
@@ -67,7 +74,6 @@ public abstract class MinecraftClientMixin {
if (event.isCancelled()) ci.cancel();
}
// telemetría
@Inject(method = "isTelemetryEnabledByApi", at = @At("RETURN"), cancellable = true)
@@ -84,7 +84,9 @@ public class ModuleManager {
private void addMisc() {
add(new AntiInvis());
add(new AnvilFont());
add(new BookBot());
add(new BossbarStack());
add(new BundleDupe());
add(new ChatPlus());
add(new CreativeInventoryHotbarKeybinds());
add(new FakePlayer());
@@ -0,0 +1,255 @@
package me.retucio.sputnik.module.modules.misc;
import me.retucio.sputnik.event.SubscribeEvent;
import me.retucio.sputnik.event.events.network.DisconnectEvent;
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.BooleanSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting;
import me.retucio.sputnik.module.setting.settings.StringSetting;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.InventoryUtil;
import net.minecraft.client.font.TextHandler;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.component.type.WritableBookContentComponent;
import net.minecraft.component.type.WrittenBookContentComponent;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.network.packet.c2s.play.BookUpdateC2SPacket;
import net.minecraft.text.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.PrimitiveIterator;
import java.util.Random;
import java.util.function.Predicate;
public class BookBot extends Module {
private final SettingGroup sgBook = addSg(new SettingGroup("libro", true));
private final NumberSetting delay = sgGeneral.add(new NumberSetting(
"delay",
"delay entre la escritura de libros",
20,
1,
200,
1
));
private final BooleanSetting toggleOnDisconnect = sgGeneral.add(new BooleanSetting(
"desactivar al desconectarse",
".",
true
));
private final StringSetting name = sgBook.add(new StringSetting(
"nombre",
"nombre a usar para los libros",
"mortadela y filetón",
100
));
private final BooleanSetting count = sgBook.add(new BooleanSetting(
"contar",
"contar los libros y añadir el número al final del nombre",
true
));
private final NumberSetting pages = sgBook.add(new NumberSetting(
"páginas",
"número de páginas a llenar con mierda",
50,
1,
100,
1
));
private final BooleanSetting sign = sgBook.add(new BooleanSetting(
"firmar",
"firmar el libro",
true
));
private final BooleanSetting onlyAscii = sgBook.add(new BooleanSetting(
"solo ascii",
"usar solamente caracteres ascii",
false
));
private int delayTimer, bookCount;
private Random random;
public BookBot() {
super("j. k. rowling", "rellena libros con caca", Category.MISC);
}
@Override
public void onEnable() {
random = new Random();
delayTimer = delay.getIntValue();
bookCount = 0;
super.onEnable();
}
@Override
public void onTick() {
if (mc.player == null) return;
Predicate<ItemStack> bookPredicate = stack -> {
WritableBookContentComponent component = stack.get(DataComponentTypes.WRITABLE_BOOK_CONTENT);
return stack.getItem() == Items.WRITABLE_BOOK && (component == null || component.pages().isEmpty());
};
ItemStack book = InventoryUtil.find(bookPredicate);
if (book == null) {
ChatUtil.warn("no se encontró ningún libro");
toggle();
return;
}
if (mc.player.getMainHandStack() != book) {
InventoryUtil.swapWithHotbar(
mc.player.getInventory().getSlotWithStack(book),
mc.player.getInventory().getSelectedSlot()
);
}
if (delayTimer > 0) {
delayTimer--;
return;
}
delayTimer = delay.getIntValue();
int origin = onlyAscii.getValue() ? 0x21 : 0x0800;
int bound = onlyAscii.getValue() ? 0x7E : 0x10FFFF;
writeBook(random.ints(origin, bound)
.filter(i -> !Character.isWhitespace(i)
&& i != '\r' && i != '\n')
.iterator()
);
}
@SubscribeEvent
private void onDisconnect(DisconnectEvent event) {
if (toggleOnDisconnect.getValue()) {
toggle();
}
}
private void writeBook(PrimitiveIterator.OfInt chars) {
ArrayList<String> pages = new ArrayList<>();
ArrayList<RawFilteredPair<Text>> filteredPages = new ArrayList<>();
int maxPages = this.pages.getIntValue();
TextHandler.WidthRetriever widthRetriever = mc.textRenderer.getTextHandler().widthRetriever;
int pageIndex = 0;
int lineIndex = 0;
final StringBuilder page = new StringBuilder();
float lineWidth = 0;
while (chars.hasNext() && pageIndex < maxPages) {
int c = chars.nextInt();
if (c == '\r' || c == '\n') {
page.append('\n');
lineWidth = 0;
lineIndex++;
} else {
float charWidth = widthRetriever.getWidth(c, Style.EMPTY);
if (lineWidth + charWidth > 114f) {
page.append('\n');
lineWidth = charWidth;
lineIndex++;
if (lineIndex != 14) page.appendCodePoint(c);
} else if (lineWidth == 0f && c == ' ') {
continue;
} else {
lineWidth += charWidth;
page.appendCodePoint(c);
}
}
if (lineIndex == 14) {
filteredPages.add(RawFilteredPair.of(Text.of(page.toString())));
pages.add(page.toString());
page.setLength(0);
pageIndex++;
lineIndex = 0;
lineWidth = 0;
}
}
if (!page.isEmpty() && pageIndex < maxPages) {
filteredPages.add(RawFilteredPair.of(Text.of(page.toString())));
pages.add(page.toString());
}
if (bookCount > 0) {
ChatUtil.info("creado libro n. " + bookCount);
}
createBook(pages, filteredPages);
}
private void processLinesToPages(List<StringVisitable> lines, ArrayList<String> pages, ArrayList<RawFilteredPair<Text>> filteredPages, int maxPages) {
int pageIndex = 0;
int lineIndex = 0;
StringBuilder currentPage = new StringBuilder();
for (StringVisitable line : lines) {
String lineText = line.getString();
if (!currentPage.isEmpty()) {
currentPage.append('\n');
}
currentPage.append(lineText);
lineIndex++;
if (lineIndex == 14) {
filteredPages.add(RawFilteredPair.of(Text.of(currentPage.toString())));
pages.add(currentPage.toString());
currentPage.setLength(0);
pageIndex++;
lineIndex = 0;
if (pageIndex == maxPages) break;
}
}
if (!currentPage.isEmpty() && pageIndex < maxPages) {
filteredPages.add(RawFilteredPair.of(Text.of(currentPage.toString())));
pages.add(currentPage.toString());
}
}
private void createBook(ArrayList<String> pages, ArrayList<RawFilteredPair<Text>> filteredPages) {
String title = name.getValue();
if (count.getValue() && bookCount != 0) title += " #" + bookCount;
mc.player.getMainHandStack().set(DataComponentTypes.WRITTEN_BOOK_CONTENT, new WrittenBookContentComponent(
RawFilteredPair.of(title),
mc.player.getGameProfile().name(),
0,
filteredPages,
true
));
mc.player.networkHandler.sendPacket(new BookUpdateC2SPacket(
mc.player.getInventory().getSelectedSlot(),
pages,
sign.getValue()
? Optional.of(title)
: Optional.empty()
));
bookCount++;
}
}
@@ -0,0 +1,132 @@
package me.retucio.sputnik.module.modules.misc;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import me.retucio.sputnik.event.SubscribeEvent;
import me.retucio.sputnik.event.events.network.DisconnectEvent;
import me.retucio.sputnik.event.events.network.PacketEvent;
import me.retucio.sputnik.module.Category;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.NumberSetting;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.network.packet.c2s.common.KeepAliveC2SPacket;
import net.minecraft.network.packet.c2s.play.ClickSlotC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerInteractItemC2SPacket;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.screen.sync.ItemStackHash;
import net.minecraft.util.Hand;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
// versión simplificada del módulo de Numberz y Nummernuts, del mod de DupersUnited (https://discord.gg/dupes)
public class BundleDupe extends Module {
private final NumberSetting timeoutSeconds = sgGeneral.add(new NumberSetting(
"tiempo de espera",
"tiempo a esperar en segundos antes de ser kickeado",
30,
1,
120,
1
));
private final NumberSetting clickslotPackets = sgGeneral.add(new NumberSetting(
"paquetes",
"paquetes a enviar",
200,
1,
1000,
1
));
private boolean cancelKeepAlive = false;
private boolean dupeActivated = false;
private boolean waitingForKeepAlive = false;
private final ScheduledExecutorService delayedExecutor = new ScheduledThreadPoolExecutor(2);
public BundleDupe() {
super("dupe de bundle",
"dupea items usando un bundle y un libro",
Category.MISC
);
}
@Override
public void onEnable() {
dupeActivated = true;
dupe();
}
@Override
public void onDisable() {
this.cancelKeepAlive = false;
this.dupeActivated = false;
this.waitingForKeepAlive = false;
}
@SubscribeEvent
private void onDisconnect(DisconnectEvent event) {
this.toggle();
}
@SubscribeEvent
private void onPacketSend(PacketEvent.Send event) {
if (this.dupeActivated && event.getPacket() instanceof PlayerActionC2SPacket) {
event.cancel();
}
if (event.getPacket() instanceof KeepAliveC2SPacket) {
if (waitingForKeepAlive) {
waitingForKeepAlive = false;
cancelKeepAlive = true;
ChatUtil.info("KeepAlive enviado, esperando " + this.timeoutSeconds.getValue() + " segundos...");
delayedExecutor.schedule(() -> {
if (isEnabled()) {
sendInteractItem();
sendClickSlots();
toggle();
}
}, timeoutSeconds.getIntValue() * 1000L, TimeUnit.MILLISECONDS);
} else if (cancelKeepAlive) {
event.cancel();
}
}
}
private void dupe() {
if (mc.player == null || mc.getNetworkHandler() == null) return;
sendClickSlots();
sendInteractItem();
mc.getNetworkHandler().sendPacket(PlayerInteractEntityC2SPacket.attack(mc.player, false));
sendClickSlots();
delayedExecutor.schedule(() -> {
if (isEnabled()) {
this.toggle();
}
}, 100L, TimeUnit.MILLISECONDS);
}
private void sendInteractItem() {
if (mc.player == null || mc.getNetworkHandler() == null) return;
PlayerInteractItemC2SPacket packet = new PlayerInteractItemC2SPacket(Hand.MAIN_HAND, 0, mc.player.getYaw(), this.mc.player.getPitch());
mc.getNetworkHandler().sendPacket(packet);
}
private void sendClickSlots() {
if (mc.player == null || mc.getNetworkHandler() == null) return;
for (int i = 0; i < this.clickslotPackets.getIntValue(); i++) {
ClickSlotC2SPacket packet = new ClickSlotC2SPacket(0, 0, (short) 0, (byte) 0, SlotActionType.PICKUP, new Int2ObjectArrayMap<>(), ItemStackHash.EMPTY);
this.mc.getNetworkHandler().sendPacket(packet);
}
ChatUtil.info(clickslotPackets.getIntValue() + " paquetes ClickSlot enviados");
}
}
@@ -25,7 +25,7 @@ import net.minecraft.util.hit.HitResult;
import java.util.List;
// todo: implementar otros tipos de MLGs (cama, miel, slime, heno, ...)
// todo: arreglar MLGs con bloques
public class NoFall extends Module {
private boolean placed;
@@ -4,6 +4,7 @@ import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.util.VersionChecker;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.Click;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.DirectionalLayoutWidget;
@@ -21,8 +22,6 @@ public class UpdateScreen extends Screen {
private final MinecraftClient mc = MinecraftClient.getInstance();
private final DirectionalLayoutWidget grid = DirectionalLayoutWidget.vertical();
private final Text outdatedText = Text.of("version desactualizada: " + Sputnik.MOD_VERSION);
private final Text updateText = Text.of("descarga la última versión (" + VersionChecker.getLatestVersion() + ") desde aquí:");
private final URI updateLink = URI.create(FabricLoader.getInstance()
.getModContainer(Sputnik.MOD_ID)
.orElseThrow()
@@ -33,6 +32,20 @@ public class UpdateScreen extends Screen {
+ "/releases/latest"
);
private final Text outdatedText = Text.of("version desactualizada: " + Sputnik.MOD_VERSION);
private final Text updateText = Text.of("descarga la última versión (" + VersionChecker.getLatestVersion() + ") desde aquí:");
private final ButtonWidget idgafButton = ButtonWidget.builder(
Text.of("me la pela"),
button -> this.close()
).build();
private final ButtonWidget updateButton = ButtonWidget.builder(
Text.of("CLIC AQUÍ"),
button -> handleOpenUri(mc, this, updateLink)
).build();
public UpdateScreen() {
super(Text.literal(Formatting.BOLD + "actualizar"));
}
@@ -48,18 +61,7 @@ public class UpdateScreen extends Screen {
this.grid.getMainPositioner().margin(2);
ButtonWidget updateButton = ButtonWidget.builder(
Text.of("CLIC AQUÍ"),
button -> {
handleOpenUri(mc, this, updateLink);
}
).build();
this.grid.add(updateButton);
ButtonWidget idgafButton = ButtonWidget.builder(
Text.of("me la pela"),
button -> this.close()
).build();
this.grid.add(idgafButton);
this.grid.refreshPositions();
@@ -76,4 +78,9 @@ public class UpdateScreen extends Screen {
public boolean shouldCloseOnEsc() {
return false;
}
@Override
public boolean canInterruptOtherScreen() {
return true;
}
}
@@ -21,6 +21,7 @@ import net.minecraft.text.Text;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import static me.retucio.sputnik.Sputnik.mc;
@@ -36,6 +37,17 @@ public class InventoryUtil {
private static Inventory echestInv;
public static ItemStack find(Predicate<ItemStack> predicate) {
if (mc.player == null) return null;
for (int i = 0; i <= mc.player.getInventory().size(); i++) {
ItemStack stack = mc.player.getInventory().getStack(i);
if (predicate.test(stack)) {
return stack;
}
}
return null;
}
public static ItemStack getStackOf(Item item) {
if (mc.player == null) return null;
for (ItemStack stack : mc.player.getInventory())
@@ -82,6 +94,10 @@ public class InventoryUtil {
mc.interactionManager.clickSlot(syncId, containerSlot1, 0, SlotActionType.PICKUP, mc.player);
}
public static void swapWithHotbar(int containerSlot, int hotbarSlot) {
swapWithHotbar(containerSlot, hotbarSlot, mc.player.playerScreenHandler);
}
public static void swapWithHotbar(int containerSlot, int hotbarSlot, HandledScreen<?> screen) {
swapWithHotbar(containerSlot, hotbarSlot, screen.getScreenHandler());
}
@@ -34,16 +34,18 @@ public class VersionChecker {
latestRemoteVersion = extract(gradleProperties);
if (latestRemoteVersion != null) {
String remoteVersion = (latestRemoteVersion);
String remoteVersion = cleanVersion(latestRemoteVersion);
String localVersion = cleanVersion(currentVersion);
if (!remoteVersion.equals(localVersion)) {
if (compareVersions(localVersion, remoteVersion) == -1) {
updateAvailable = true;
Sputnik.LOGGER.info("actualización disponible. local: {}, más reciente: {}",
currentVersion, latestRemoteVersion);
shouldShowScreen = true;
} else {
} else if (compareVersions(localVersion, remoteVersion) == 0){
Sputnik.LOGGER.info("el mod está al día: v{}", currentVersion);
} else {
Sputnik.LOGGER.info("mod en estado de desarrollo: v{}", currentVersion);
}
}
} else {
@@ -84,6 +86,32 @@ public class VersionChecker {
return version.trim();
}
private static int compareVersions(String version1, String version2) {
if (version1 == null || version2 == null) return 0;
String[] parts1 = version1.split("\\.");
String[] parts2 = version2.split("\\.");
int maxLength = Math.max(parts1.length, parts2.length);
for (int i = 0; i < maxLength; i++) {
int v1 = (i < parts1.length) ? parseVersionPart(parts1[i]) : 0;
int v2 = (i < parts2.length) ? parseVersionPart(parts2[i]) : 0;
if (v1 < v2) return -1;
if (v1 > v2) return 1;
}
return 0;
}
private static int parseVersionPart(String part) {
try {
return Integer.parseInt(part);
} catch (NumberFormatException e) {
return 0;
}
}
private static URI getFileURI() {
try {
+1
View File
@@ -14,6 +14,7 @@ accessible class net/minecraft/client/network/ClientCommonNetworkHandler$Confirm
# texto
accessible method net/minecraft/client/font/FontManager getStorageInternal (Lnet/minecraft/util/Identifier;)Lnet/minecraft/client/font/FontStorage;
accessible class net/minecraft/client/font/TextRenderer$Drawer
accessible field net/minecraft/client/font/TextHandler widthRetriever Lnet/minecraft/client/font/TextHandler$WidthRetriever;
# entidad
accessible field net/minecraft/entity/LivingEntity jumpingCooldown I