diff --git a/cambios.md b/cambios.md index d1a83e2..d7d9e6a 100644 --- a/cambios.md +++ b/cambios.md @@ -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 diff --git a/src/main/java/me/retucio/sputnik/Sputnik.java b/src/main/java/me/retucio/sputnik/Sputnik.java index 5e4375a..3b0c799 100644 --- a/src/main/java/me/retucio/sputnik/Sputnik.java +++ b/src/main/java/me/retucio/sputnik/Sputnik.java @@ -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(); diff --git a/src/main/java/me/retucio/sputnik/mixin/mixins/misc/MinecraftClientMixin.java b/src/main/java/me/retucio/sputnik/mixin/mixins/misc/MinecraftClientMixin.java index 79a4255..4cbc853 100644 --- a/src/main/java/me/retucio/sputnik/mixin/mixins/misc/MinecraftClientMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/mixins/misc/MinecraftClientMixin.java @@ -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) diff --git a/src/main/java/me/retucio/sputnik/module/ModuleManager.java b/src/main/java/me/retucio/sputnik/module/ModuleManager.java index 5aa848d..b0070d0 100644 --- a/src/main/java/me/retucio/sputnik/module/ModuleManager.java +++ b/src/main/java/me/retucio/sputnik/module/ModuleManager.java @@ -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()); diff --git a/src/main/java/me/retucio/sputnik/module/modules/misc/BookBot.java b/src/main/java/me/retucio/sputnik/module/modules/misc/BookBot.java new file mode 100644 index 0000000..f87a711 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/module/modules/misc/BookBot.java @@ -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 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 pages = new ArrayList<>(); + ArrayList> 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 lines, ArrayList pages, ArrayList> 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 pages, ArrayList> 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++; + } +} \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/module/modules/misc/BundleDupe.java b/src/main/java/me/retucio/sputnik/module/modules/misc/BundleDupe.java new file mode 100644 index 0000000..79b987a --- /dev/null +++ b/src/main/java/me/retucio/sputnik/module/modules/misc/BundleDupe.java @@ -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"); + } +} diff --git a/src/main/java/me/retucio/sputnik/module/modules/player/NoFall.java b/src/main/java/me/retucio/sputnik/module/modules/player/NoFall.java index f9eac0e..8bc0abf 100644 --- a/src/main/java/me/retucio/sputnik/module/modules/player/NoFall.java +++ b/src/main/java/me/retucio/sputnik/module/modules/player/NoFall.java @@ -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; diff --git a/src/main/java/me/retucio/sputnik/ui/screen/UpdateScreen.java b/src/main/java/me/retucio/sputnik/ui/screen/UpdateScreen.java index 7f176e4..f066e9d 100644 --- a/src/main/java/me/retucio/sputnik/ui/screen/UpdateScreen.java +++ b/src/main/java/me/retucio/sputnik/ui/screen/UpdateScreen.java @@ -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; + } } diff --git a/src/main/java/me/retucio/sputnik/util/InventoryUtil.java b/src/main/java/me/retucio/sputnik/util/InventoryUtil.java index a37a562..05acd0d 100644 --- a/src/main/java/me/retucio/sputnik/util/InventoryUtil.java +++ b/src/main/java/me/retucio/sputnik/util/InventoryUtil.java @@ -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 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()); } diff --git a/src/main/java/me/retucio/sputnik/util/VersionChecker.java b/src/main/java/me/retucio/sputnik/util/VersionChecker.java index 498598e..cf16864 100644 --- a/src/main/java/me/retucio/sputnik/util/VersionChecker.java +++ b/src/main/java/me/retucio/sputnik/util/VersionChecker.java @@ -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 { @@ -110,4 +138,4 @@ public class VersionChecker { public static String getLatestVersion() { return latestRemoteVersion; } -} +} \ No newline at end of file diff --git a/src/main/resources/sputnik.accesswidener b/src/main/resources/sputnik.accesswidener index 28f30df..98a3eba 100644 --- a/src/main/resources/sputnik.accesswidener +++ b/src/main/resources/sputnik.accesswidener @@ -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