Compare commits

...
9 Commits
Author SHA1 Message Date
retucio 0bd31971a0 v6.0 2026-08-23 16:50:27 +02:00
retucio 3010b164ab v5.9 2026-08-23 16:03:32 +02:00
retucio 6cb4656365 v5.8 2026-08-22 18:20:52 +02:00
retucio 272c1059ef v5.7 2026-08-19 22:57:41 +02:00
retucio e0f8523809 v5.6 2026-08-18 13:35:24 +02:00
retucio 81fc25c33d v5.5 2026-07-20 22:17:44 +02:00
retucio 81b043270b v5.4.1 2026-07-19 23:20:48 +02:00
retucio 3913b31374 v5.4 2026-07-19 23:14:33 +02:00
retucio aeadfb28b1 v5.3 2026-07-15 21:50:51 +02:00
192 changed files with 8484 additions and 1092 deletions

No files matched your search

+2 -1
View File
@@ -43,4 +43,5 @@ replay_*.log
todo.txt
cambios.md
remappedSrc/
remappedSrc/
.claude/
+3 -3
View File
@@ -1,6 +1,6 @@
# jenny's mod 2 100% real
mod para Minecraft Fabric 26.1.2 que añade algunas cosas QOL, mayormente relacionadas a la cámara.
mod para Minecraft Fabric 26.2 que añade algunas cosas QOL, mayormente relacionadas a la cámara.
## instalación
@@ -10,7 +10,7 @@ mod para Minecraft Fabric 26.1.2 que añade algunas cosas QOL, mayormente relaci
3. descargar el .jar
4. moverlo a tu directorio de mods (probablemente `%appdata%/.minecraft/mods`)
asegúrate de tener una version de Minecraft y Fabric compatibles con el mod (MC `26.1.2`; Fabric `0.19.2`)
asegúrate de tener una version de Minecraft y Fabric compatibles con el mod (MC `26.2`; Fabric `0.19.3`)
es posible que haya problemas al usarlo junto a otros mods, especialmente junto a otros clientes, pues pueden conflictar entre ellos.
### para frikis de 200 de iq
@@ -56,7 +56,7 @@ algunos módulos tienen teclas asignadas por defecto, asígnales `ESC` para desa
<img src="screenshots/hudeditor.png" alt="editor de HUD">
<img src="screenshots/colorpicker.png" alt="selector de colores">
## contibuciones
## contribuciones
sí por favor
+8 -5
View File
@@ -1,4 +1,4 @@
# v5.2
# v6.0
## novedades
@@ -6,13 +6,16 @@
#### módulos nuevos:
#### comandos nuevos:
#### elementos del HUD nuevos:
- imagen custom: imagen custom.
### arreglos / mejoras
- `andamios` ya funciona correctamente
- autoguardado cada cierto intervalo de tiempo en vez de guardar inmediatamente tras cada cambio en los ajustes, por optimización
- el guardado se realiza desde otro hilo ahora, para evitar que el I/O bloque el hilo principal
- ahora se guardan correctamente los ajustes de archivo
## problemas reconocidos
- lo de siempre.
- el ítem del tótem no se renderiza en el editor del HUD si es abierto en la pantalla de inicio
+3 -1
View File
@@ -10,6 +10,8 @@ elytra-recast: https://github.com/InLieuOfLuna/elytra-recast/
SpearHax: https://github.com/etianl/spearHax/
ProjectileTrajectoryPreview: https://github.com/maDU59/ProjectilesTrajectoryPreview
DupersUnited public addon: https://discord.gg/dupes | https://dupers.wtf
Stardust: https://github.com/0xTas/stardust/
probablemente más pero como tengo demencia se me olvida
si quieres que te acredite o quite tu código de mi mod, mándame un mensaje en discord (username: retucio) y sin problemas :)
si quieres que te acredite o quite tu código de mi mod, mándame un mensaje en discord (username: retucio) y sin problemas :)
también es probable que haya créditos sueltos en forma de comentarios donde haya usado código ajeno
+5 -5
View File
@@ -1,15 +1,15 @@
org.gradle.jvmargs=-Xmx2G
org.gradle.parallel=true
minecraft_version=26.1.2
loader_version=0.19.2
loom_version=1.16-SNAPSHOT
minecraft_version=26.2
loader_version=0.19.3
loom_version=1.17-SNAPSHOT
fabric_api_version=0.146.0+26.1.2
fabric_api_version=0.154.0+26.2
discord_rpc_version=1.0.3
neutrino_version=1.1.0
jdk_version=25
mod_version=5.2
mod_version=6.0
maven_group=me.retucio
archives_base_name=sputnik
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
View File
Whitespace-only changes.
+53 -29
View File
@@ -7,6 +7,8 @@ import me.retucio.sputnik.command.CommandManager;
import me.retucio.sputnik.command.commands.BindCommand;
import me.retucio.sputnik.config.ConfigManager;
import me.retucio.sputnik.config.ProfileManager;
import me.retucio.sputnik.macro.MacroManager;
import me.retucio.sputnik.event.ShutdownEvent;
import me.retucio.sputnik.event.interact.OpenScreenEvent;
@@ -17,12 +19,14 @@ import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.client.Hud;
import me.retucio.sputnik.ui.hud.HudEditor;
import me.retucio.sputnik.ui.hud.HudRenderer;
import me.retucio.sputnik.ui.screen.ClickGui;
import me.retucio.sputnik.ui.hud.HudEditorScreen;
import me.retucio.sputnik.ui.screen.UpdateScreen;
import me.retucio.sputnik.ui.widgets.buttons.settings.BindButton;
import me.retucio.sputnik.ui.widgets.buttons.SettingButton;
import me.retucio.sputnik.ui.widgets.buttons.settings.FileButton;
import me.retucio.sputnik.ui.widgets.buttons.settings.TextButton;
import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel;
import me.retucio.sputnik.ui.widgets.panels.SettingsPanel;
@@ -47,6 +51,7 @@ import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.client.gui.screens.inventory.AbstractCommandBlockEditScreen;
import net.minecraft.client.gui.screens.inventory.AbstractSignEditScreen;
import net.minecraft.client.gui.screens.inventory.AnvilScreen;
import net.minecraft.network.chat.Component;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -98,12 +103,17 @@ public class Sputnik implements ClientModInitializer {
EVENT_BUS.subscribe(MiscUtil.class);
EVENT_BUS.subscribe(NetworkUtil.class);
EVENT_BUS.subscribe(RenderUtil.class);
EVENT_BUS.subscribe(RotationUtil.class);
EVENT_BUS.subscribe(BlockIterator.class);
Lists.init();
HudRenderer.INSTANCE = new HudRenderer();
HudEditor.INSTANCE = new HudEditor();
MacroManager.INSTANCE = new MacroManager();
ProfileManager.INSTANCE = new ProfileManager();
CapeManager.INSTANCE = new CapeManager();
EVENT_BUS.post(new LoadCapeManagerEvent());
@@ -134,11 +144,8 @@ public class Sputnik implements ClientModInitializer {
// se ejecuta cada tick, es decir, 20 veces por segundo
public void onTick() {
if (!settingsApplied
&& ConfigManager.INSTANCE.getConfig() != null
&& !ConfigManager.INSTANCE.hasLoaded()) {
ConfigManager.INSTANCE.applyConfig();
settingsApplied = true;
if (!settingsApplied && !ConfigManager.INSTANCE.hasLoaded()) {
settingsApplied = ConfigManager.INSTANCE.applyConfig();
}
ModuleManager.INSTANCE.getEnabledModules().forEach(Module::onTick);
@@ -153,14 +160,15 @@ public class Sputnik implements ClientModInitializer {
// prevenir interrumpir combos de F3
if (key == GLFW.GLFW_KEY_F3) return;
boolean anyFocused = isAnySettingButtonFocused() || ClickGui.INSTANCE.getSearchBar().isFocused();
boolean anyFocused = isAnySettingButtonFocused() || ClickGui.INSTANCE.isCapturingInput();
ClickGui.INSTANCE.setAnyFocused(anyFocused);
if (action != GLFW.GLFW_RELEASE) {
if (BindCommand.onKeyPress(key)) return;
if (action == GLFW.GLFW_PRESS && MacroManager.INSTANCE.onKeyPress(key)) return;
if (action == GLFW.GLFW_PRESS) {
handleModuleToggle(key, anyFocused);
handleClickGUIKey(key, anyFocused);
handleClickGuiKey(key, anyFocused);
handleHudEditorKey(key, anyFocused);
}
// solo reenviar teclas del teclado
@@ -174,16 +182,21 @@ public class Sputnik implements ClientModInitializer {
// verifica si algún botón de ajustes está escuchando
private boolean isAnySettingButtonFocused() {
if (ClickGui.INSTANCE == null) return false;
for (SettingsPanel sf : ClickGui.INSTANCE.getSettingsPanels())
for (Button sb : sf.getButtons())
if ((sb instanceof BindButton b && b.isFocused()) || (sb instanceof TextButton t && t.isFocused()))
if ((sb instanceof BindButton b && b.isFocused())
|| (sb instanceof TextButton t && t.isFocused())
|| (sb instanceof FileButton f && f.isFocused())
) {
return true;
}
return false;
}
// se ocupa de la lógica de encendido y apagado de los módulos
private void handleModuleToggle(int key, boolean anyFocused) {
if (mc.screen != null && mc.screen != ClickGui.INSTANCE) return;
if (mc.gui.screen() != null && mc.gui.screen() != ClickGui.INSTANCE) return;
for (Module module : ModuleManager.INSTANCE.getModules()) {
// evitar interrumpir combinaciones de teclas del F3
@@ -201,46 +214,47 @@ public class Sputnik implements ClientModInitializer {
for (SettingsPanel sf : ClickGui.INSTANCE.getSettingsPanels()) {
for (SettingButton<?> sb : sf.getButtons()) {
if (sb instanceof BindButton bb) bb.onKey(key, action);
if (sb instanceof FileButton fb) fb.onKey(key, action);
if (sb instanceof TextButton tb) tb.onKey(key, action);
}
}
}
// maneja la lógica de apertura de la interfaz
private void handleClickGUIKey(int key, boolean anyFocused) {
private void handleClickGuiKey(int key, boolean anyFocused) {
if (key != ClientSettingsPanel.clientSettings.getKey() || anyFocused || isOnTypingScreen())
return;
if (mc.screen != ClickGui.INSTANCE) {
prevScreen = mc.screen;
mc.setScreen(ClickGui.INSTANCE);
if (mc.gui.screen() != ClickGui.INSTANCE) {
prevScreen = mc.gui.screen();
mc.gui.setScreen(ClickGui.INSTANCE);
} else {
ClickGui.INSTANCE.onClose();
mc.setScreen(prevScreen);
mc.gui.setScreen(prevScreen);
}
}
// manejar la lógica de apertura del editor de elementos del hud, con la misma lógica que handleClickGUIKey()
// manejar la lógica de apertura del editor de elementos del hud, con la misma lógica que handleClickGuiKey()
private void handleHudEditorKey(int key, boolean anyFocused) {
Hud hud = ModuleManager.INSTANCE.getModuleByClass(Hud.class);
if (key != hud.editorKey.getValue() || anyFocused || isOnTypingScreen()) return;
if (mc.screen != HudEditorScreen.INSTANCE) {
if (mc.gui.screen() != HudEditorScreen.INSTANCE) {
if (!hud.isEnabled()) {
ChatUtil.warn("HUD está desactivado, lumbreras");
ChatUtil.warn(Component.translatable("sputnik.hud.disabled"));
return;
}
prevScreen = mc.screen;
mc.setScreen(HudEditorScreen.INSTANCE);
prevScreen = mc.gui.screen();
mc.gui.setScreen(HudEditorScreen.INSTANCE);
} else {
HudEditorScreen.INSTANCE.onClose();
mc.setScreen(prevScreen);
mc.gui.setScreen(prevScreen);
}
}
// se ocupa de apagar los módulos que tengan configurado hacerlo tras soltar su tecla
private void handleModuleRelease(int key) {
if (mc.screen != null && mc.screen != ClickGui.INSTANCE) return;
if (mc.gui.screen() != null && mc.gui.screen() != ClickGui.INSTANCE) return;
for (Module module : ModuleManager.INSTANCE.getEnabledModules())
if (module.shouldToggleOnBindRelease() && key == module.getKey())
@@ -248,19 +262,29 @@ public class Sputnik implements ClientModInitializer {
}
private boolean isOnTypingScreen() {
return mc.screen instanceof ChatScreen
|| mc.screen instanceof AnvilScreen
|| mc.screen instanceof AbstractSignEditScreen
|| mc.screen instanceof AbstractCommandBlockEditScreen;
return mc.gui.screen() instanceof ChatScreen
|| mc.gui.screen() instanceof AnvilScreen
|| mc.gui.screen() instanceof AbstractSignEditScreen
|| mc.gui.screen() instanceof AbstractCommandBlockEditScreen;
}
private boolean keysVerified = false;
@EventListener
// notificar al usuario de que hay una actualización disponible, si la hay
public void onSetScreen(OpenScreenEvent event) {
if (VersionChecker.INSTANCE.shouldShowScreen && event.getScreen() instanceof TitleScreen) {
event.cancel();
VersionChecker.INSTANCE.shouldShowScreen = false;
mc.execute(() -> mc.screen = new UpdateScreen());
if (event.getScreen() instanceof TitleScreen) {
// aquí el idioma ya está cargado, que en onInitializeClient todavía no
if (!keysVerified) {
keysVerified = true;
I18nUtil.verifyKeys();
}
if (VersionChecker.INSTANCE.shouldShowScreen) {
event.cancel();
VersionChecker.INSTANCE.shouldShowScreen = false;
mc.execute(() -> mc.gui.setScreen(new UpdateScreen()));
}
}
}
@@ -9,6 +9,7 @@ import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.Commands;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.data.registries.VanillaRegistries;
import net.minecraft.network.chat.Component;
import java.util.List;
@@ -20,12 +21,14 @@ public abstract class Command {
protected static final Minecraft mc = Minecraft.getInstance();
private final String name, description;
private final String name, descriptionKey;
private final List<String> aliases;
public Command(String name, String description, String... aliases) {
// la descripción se guarda como clave de traducción, no como texto ya resuelto: los comandos
// se construyen antes de que se cargue el idioma, así que hay que traducirla al mostrarla
public Command(String name, String descriptionKey, String... aliases) {
this.name = name;
this.description = description;
this.descriptionKey = descriptionKey;
this.aliases = List.of(aliases);
}
@@ -56,8 +59,12 @@ public abstract class Command {
return name;
}
public String getDescription() {
return description;
public String getDescriptionKey() {
return descriptionKey;
}
public Component getDescription() {
return Component.translatable(descriptionKey);
}
public List<String> getAliases() {
@@ -38,9 +38,13 @@ public class CommandManager {
addCommand(new EnderChestCommand());
addCommand(new FriendCommand());
addCommand(new GarbageCleanerCommand());
addCommand(new HelpCommand());
addCommand(new MacroCommand());
addCommand(new PanoramaCommand());
addCommand(new PeekCommand());
addCommand(new PluginsCommand());
addCommand(new PrefixCommand());
addCommand(new ProfileCommand());
addCommand(new PurgeCommand());
addCommand(new RepeatCommand());
addCommand(new RepeatWaitCommand());
@@ -25,7 +25,7 @@ public class EntityArgumentType implements ArgumentType<Entity> {
public static final EntityArgumentType INSTANCE = new EntityArgumentType();
private static final DynamicCommandExceptionType NO_SUCH_ENTITY = new DynamicCommandExceptionType(
name -> Component.literal("Entidad \"" + name + "\" no encontrada"));
name -> Component.translatable("sputnik.args.entity.notfound", name));
private static final Collection<String> EXAMPLES = List.of(
"AdlerHitdolf",
@@ -15,7 +15,8 @@ import java.util.concurrent.CompletableFuture;
public class EnumArgumentType<T extends Enum<T>> implements ArgumentType<T> {
private static final DynamicCommandExceptionType NO_SUCH_TYPE = new DynamicCommandExceptionType((value) -> Component.nullToEmpty(" argumento inválido: " + value));
private static final DynamicCommandExceptionType NO_SUCH_TYPE = new DynamicCommandExceptionType(
(value) -> Component.translatable("sputnik.args.enum.invalid", value));
private final T[] values;
@@ -33,8 +34,10 @@ public class EnumArgumentType<T extends Enum<T>> implements ArgumentType<T> {
public T parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString();
// ignorando mayúsculas: listSuggestions sugiere en minúsculas, así que comparar
// con equals() hacía que las propias sugerencias no se pudieran parsear
return Arrays.stream(this.values)
.filter((value) -> value.toString().equals(argument))
.filter((value) -> value.toString().equalsIgnoreCase(argument))
.findFirst()
.orElseThrow(() -> NO_SUCH_TYPE.create(argument)
);
@@ -22,15 +22,15 @@ public class ModuleArgumentType implements ArgumentType<Module> {
public static final ModuleArgumentType INSTANCE = new ModuleArgumentType();
private static final DynamicCommandExceptionType UNKNOWN_MODULE = new DynamicCommandExceptionType(
name -> Component.literal("módulo \"" + name + "\" no encontrado"));
name -> Component.translatable("sputnik.args.module.notfound", name));
private static final Collection<String> examples = ModuleManager.INSTANCE.getModules()
.stream().limit(3).map(module -> module.getName().replace(" ", "_")).toList();
.stream().limit(3).map(module -> module.getDisplayName().replace(" ", "_")).toList();
@Override
public Module parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString().replace("_", " ");
Module module = ModuleManager.INSTANCE.getModuleByName(argument);
Module module = ModuleManager.INSTANCE.getModuleByDisplayName(argument);
if (module == null) throw UNKNOWN_MODULE.create(argument);
return module;
}
@@ -38,7 +38,7 @@ public class ModuleArgumentType implements ArgumentType<Module> {
@Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return SharedSuggestionProvider.suggest(ModuleManager.INSTANCE.getModules()
.stream().map(module -> MiscUtil.removeAccentMarks(module.getName().replace(" ", "_"))), builder);
.stream().map(module -> MiscUtil.removeAccentMarks(module.getDisplayName().replace(" ", "_"))), builder);
}
@Override
@@ -21,7 +21,8 @@ import static me.retucio.sputnik.Sputnik.mc;
public class PlayerArgumentType implements ArgumentType<Player> {
public static final PlayerArgumentType INSTANCE = new PlayerArgumentType();
private static final DynamicCommandExceptionType NO_SUCH_PLAYER = new DynamicCommandExceptionType(name -> Component.literal("Player with name " + name + " doesn't exist."));
private static final DynamicCommandExceptionType NO_SUCH_PLAYER = new DynamicCommandExceptionType(
name -> Component.translatable("sputnik.args.player.notfound", name));
private static final Collection<String> EXAMPLES = List.of("retucio", "prepucio", "postpucio", "pucio", "adler hitdolf");
@@ -24,7 +24,7 @@ public class BindCommand extends Command {
private static Module listeningModule = null;
public BindCommand() {
super("bind", "asigna una tecla a un módulo", "keybind");
super("bind", "sputnik.commands.bind.desc", "keybind", "kb", "tecla");
}
@Override
@@ -34,14 +34,16 @@ public class BindCommand extends Command {
.executes(ctx -> {
Module module = ctx.getArgument("módulo", Module.class);
listeningModule = module;
ChatUtil.info("presiona una tecla para asignarla al módulo " + ChatFormatting.GREEN + module.getName());
ChatUtil.info(Component.translatable("sputnik.commands.bind.presskey", ChatFormatting.GREEN + module.getDisplayName()));
return SUCCESS;
})
.then(literal("reset").executes(ctx -> {
Module module = ctx.getArgument("módulo", Module.class);
module.getBind().reset();
ChatUtil.info("tecla para el módulo " + ChatFormatting.GREEN + module.getName()
+ " restablecida a " + ChatFormatting.AQUA + KeyUtil.getKeyName(module.getKey()));
ChatUtil.info(Component.translatable("sputnik.commands.bind.keyreset",
ChatFormatting.GREEN + module.getDisplayName(),
ChatFormatting.AQUA + KeyUtil.getKeyName(module.getKey())
));
return SUCCESS;
}))
);
@@ -51,17 +53,18 @@ public class BindCommand extends Command {
if (listeningModule == null) return false;
KeySetting bind = listeningModule.getBind();
if (ClientSettingsPanel.clientSettings.multipleKeybinds.getValue()) {
// solo hay que avisar de que la tecla está cogida cuando NO se permiten teclas repetidas.
// antes esto entraba con multipleKeybinds activado y luego comprobaba !allowMultiple con el
// mismo valor, así que nunca se llegaba a avisar
if (!ClientSettingsPanel.clientSettings.multipleKeybinds.getValue()) {
List<KeyMapping> keys = new ArrayList<>(List.of(mc.options.keyMappings));
keys.removeAll(List.of(mc.options.debugKeys));
for (KeyMapping kb : keys) {
boolean keyAlreadyBound = kb.matches(new KeyEvent(key, 0, 0));
boolean allowMultiple = ClientSettingsPanel.clientSettings.multipleKeybinds.getValue();
if (keyAlreadyBound && !allowMultiple) {
ChatUtil.warn("esa tecla ya está cogida por "
+ ChatFormatting.GREEN + "\"" + I18n.get(kb.getName() + "\""));
if (kb.matches(new KeyEvent(key, 0, 0))) {
// la comilla iba dentro del I18n.get, así que buscaba una clave inexistente
ChatUtil.warn(Component.translatable("sputnik.commands.bind.keytaken",
ChatFormatting.GREEN + "\"" + I18n.get(kb.getName()) + "\""));
listeningModule = null;
return true;
}
@@ -70,10 +73,10 @@ public class BindCommand extends Command {
if (bind != null) bind.setValue(key);
ChatUtil.info(
Component.nullToEmpty("la tecla " + ChatFormatting.AQUA + KeyUtil.getKeyName(key) + ChatFormatting.RESET +
" ha sido asignada al módulo " + ChatFormatting.GREEN + listeningModule.getName())
);
ChatUtil.info(Component.translatable("sputnik.commands.bind.keybound",
ChatFormatting.AQUA + KeyUtil.getKeyName(key) + ChatFormatting.RESET,
ChatFormatting.GREEN + listeningModule.getDisplayName()
));
listeningModule = null;
return true;
@@ -13,7 +13,7 @@ import net.minecraft.world.inventory.ContainerInput;
public class ClickSlotCommand extends Command {
public ClickSlotCommand() {
super("clickslot", "simula un clic a un slot");
super("clickslot", "sputnik.commands.clickslot.desc", "cs");
}
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
@@ -23,7 +23,7 @@ public class ClickSlotCommand extends Command {
.executes(context -> {
int slot = context.getArgument("slot", Integer.class), button = context.getArgument("botón", Integer.class);
ContainerInput action = context.getArgument("acción", ContainerInput.class);
Screen screen = mc.screen;
Screen screen = mc.gui.screen();
int syncId = (screen instanceof ContainerScreen cScreen) ? cScreen.getMenu().containerId : 0;
if (mc.gameMode == null) return 0;
mc.gameMode.handleContainerInput(syncId, slot, button, action, mc.player);
@@ -13,7 +13,7 @@ import me.retucio.sputnik.module.Module;
public class ConfigCommand extends Command {
public ConfigCommand() {
super("config", "abre el marco de ajustes de un módulo", "ajustes", "settings");
super("config", "sputnik.commands.config.desc", "ajustes", "settings");
}
@Override
@@ -40,7 +40,7 @@ public class ConfigCommand extends Command {
private void openSf(CommandContext<SharedSuggestionProvider> ctx) {
Module module = ctx.getArgument("módulo", Module.class);
mc.setScreen(ClickGui.INSTANCE);
mc.gui.setScreen(ClickGui.INSTANCE);
if (ClickGui.INSTANCE.isSettingsPanelOpen(module)) return;
@@ -53,7 +53,7 @@ public class ConfigCommand extends Command {
private void openSfWithPos(CommandContext<SharedSuggestionProvider> ctx, int x, int y) {
Module module = ctx.getArgument("módulo", Module.class);
mc.setScreen(ClickGui.INSTANCE);
mc.gui.setScreen(ClickGui.INSTANCE);
if (ClickGui.INSTANCE.isSettingsPanelOpen(module)) {
SettingsPanel sf = ClickGui.INSTANCE.getSettingsPanelOfModule(module);
@@ -10,7 +10,7 @@ import net.minecraft.commands.SharedSuggestionProvider;
public class CopyScreenshotCommand extends Command {
public CopyScreenshotCommand() {
super("copiarcaptura", "copia la captura más reciente al portapapeles");
super("copiarcaptura", "sputnik.commands.copyscreenshot.desc", "copyscreenshot");
}
@Override
@@ -8,6 +8,7 @@ import me.retucio.sputnik.ui.screen.PreviewScreen;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.InventoryUtil;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
// accede a los contenidos de tu enderchest
public class EnderChestCommand extends Command {
@@ -15,7 +16,7 @@ public class EnderChestCommand extends Command {
private boolean shouldOpenEchest = false;
public EnderChestCommand() {
super("echest", "muestra el contenido de tu enderchest (solo de ver)", "ec", "enderchest");
super("echest", "sputnik.commands.echest.desc", "ec", "enderchest");
}
@Override
@@ -24,7 +25,7 @@ public class EnderChestCommand extends Command {
mc.execute(() -> {
if (mc.player == null) return;
if (InventoryUtil.getEchestInv() == null) {
ChatUtil.warn("necesitas abrir un enderchest una vez primero");
ChatUtil.warn(Component.translatable( "sputnik.commands.echest.openfirst"));
return;
}
@@ -40,7 +41,7 @@ public class EnderChestCommand extends Command {
if (shouldOpenEchest) {
shouldOpenEchest = false;
if (InventoryUtil.getEchestInv() != null)
mc.setScreen(new PreviewScreen(InventoryUtil.getEchestInv(), null));
mc.gui.setScreen(new PreviewScreen(InventoryUtil.getEchestInv(), null));
}
}
}
@@ -8,12 +8,14 @@ import me.retucio.sputnik.friend.FriendManager;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Player;
public class FriendCommand extends Command {
public FriendCommand() {
super("friend", "gestión de amigos", "amigo", "f");
super("friend", "sputnik.commands.friend.desc", "amigo", "f");
}
@Override
@@ -24,7 +26,10 @@ public class FriendCommand extends Command {
.executes(ctx -> {
Player friend = ctx.getArgument("amigo", Player.class);
FriendManager.INSTANCE.add(friend.getUUID());
ChatUtil.info(ChatFormatting.GREEN + friend.getName().getString() + ChatFormatting.RESET + " ahora es tu amigo :)");
ChatUtil.info(Component.translatable(
"sputnik.commands.friend.added",
ChatFormatting.GREEN + friend.getName().getString() + ChatFormatting.RESET
));
return SUCCESS;
})
)
@@ -34,14 +39,17 @@ public class FriendCommand extends Command {
.executes(ctx -> {
Player friend = ctx.getArgument("amigo", Player.class);
FriendManager.INSTANCE.remove(friend.getUUID());
ChatUtil.info(ChatFormatting.GREEN + friend.getName().getString() + ChatFormatting.RESET + " ya no es tu amigo :(");
ChatUtil.info(Component.translatable(
"sputnik.commands.friend.removed",
ChatFormatting.GREEN + friend.getName().getString() + ChatFormatting.RESET
));
return SUCCESS;
})
)
)
.then(literal("list")
.executes(ctx -> {
ChatUtil.info(ChatFormatting.GOLD + "amigos:");
ChatUtil.info(ChatFormatting.GOLD + Component.translatable("sputnik.commands.friend.list").getString());
for (Friend friend : FriendManager.INSTANCE.getFriends()) {
ChatUtil.info("- " + friend.getName());
}
@@ -9,14 +9,14 @@ import net.minecraft.network.chat.Component;
public class GarbageCleanerCommand extends Command {
public GarbageCleanerCommand() {
super("gc", "limpia la ram mediante el recolector de basura (gc)", "garbagecollect");
super("gc", "sputnik.commands.gc.desc", "garbagecollect");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.executes(context -> {
builder.executes(ctx -> {
System.gc();
ChatUtil.info(Component.nullToEmpty("basura recolectada"));
ChatUtil.info(Component.translatable("sputnik.commands.gc.collected"));
return SUCCESS;
});
}
@@ -0,0 +1,28 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.command.CommandManager;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
public class HelpCommand extends Command {
public HelpCommand() {
super("help", Component.translatable("sputnik.commands.help.desc").getString(), "?", "ayuda", "h");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.executes(ctx -> {
for (Command command : CommandManager.commands) {
ChatUtil.info(CommandManager.INSTANCE.getPrefix()
+ ChatFormatting.BOLD + command.getName() + ": "
+ ChatFormatting.RESET + ChatFormatting.GRAY + command.getDescription().getString());
}
return SUCCESS;
});
}
}
@@ -0,0 +1,114 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.macro.Macro;
import me.retucio.sputnik.macro.MacroManager;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.KeyUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.util.List;
public class MacroCommand extends Command {
private static final SuggestionProvider<SharedSuggestionProvider> MACROS = (ctx, builder) ->
SharedSuggestionProvider.suggest(
MacroManager.INSTANCE.getMacros().stream().map(Macro::getName), builder);
public MacroCommand() {
super("macro", "sputnik.commands.macro.desc", "macros");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder
.executes(ctx -> {
list();
return SUCCESS;
})
.then(literal("list").executes(ctx -> {
list();
return SUCCESS;
}))
.then(literal("add")
.then(argument("nombre", StringArgumentType.word()).executes(ctx -> {
String name = ctx.getArgument("nombre", String.class);
if (MacroManager.INSTANCE.add(new Macro(name)))
ChatUtil.info(Component.translatable("sputnik.commands.macro.added",
ChatFormatting.AQUA + name));
else
ChatUtil.error(Component.translatable("sputnik.commands.macro.exists", name));
return SUCCESS;
})))
.then(literal("remove")
.then(argument("nombre", StringArgumentType.word()).suggests(MACROS).executes(ctx -> {
Macro macro = get(ctx.getArgument("nombre", String.class));
if (macro == null) return SUCCESS;
MacroManager.INSTANCE.remove(macro);
ChatUtil.info(Component.translatable("sputnik.commands.macro.removed",
ChatFormatting.AQUA + macro.getName()));
return SUCCESS;
})))
.then(literal("line")
.then(argument("nombre", StringArgumentType.word()).suggests(MACROS)
.then(argument("línea", StringArgumentType.greedyString()).executes(ctx -> {
Macro macro = get(ctx.getArgument("nombre", String.class));
if (macro == null) return SUCCESS;
String line = ctx.getArgument("línea", String.class);
macro.getLines().add(line);
MacroManager.INSTANCE.save();
ChatUtil.info(Component.translatable("sputnik.commands.macro.lineadded",
ChatFormatting.AQUA + macro.getName()));
return SUCCESS;
}))))
.then(literal("clear")
.then(argument("nombre", StringArgumentType.word()).suggests(MACROS).executes(ctx -> {
Macro macro = get(ctx.getArgument("nombre", String.class));
if (macro == null) return SUCCESS;
macro.getLines().clear();
MacroManager.INSTANCE.save();
ChatUtil.info(Component.translatable("sputnik.commands.macro.cleared",
ChatFormatting.AQUA + macro.getName()));
return SUCCESS;
})))
.then(literal("run")
.then(argument("nombre", StringArgumentType.word()).suggests(MACROS).executes(ctx -> {
Macro macro = get(ctx.getArgument("nombre", String.class));
if (macro != null) macro.run();
return SUCCESS;
})));
}
private static Macro get(String name) {
Macro macro = MacroManager.INSTANCE.getByName(name);
if (macro == null) ChatUtil.error(Component.translatable("sputnik.commands.macro.notfound", name));
return macro;
}
private static void list() {
List<Macro> macros = MacroManager.INSTANCE.getMacros();
if (macros.isEmpty()) {
ChatUtil.info(Component.translatable("sputnik.commands.macro.none"));
return;
}
ChatUtil.info(Component.translatable("sputnik.commands.macro.list", macros.size()));
for (Macro macro : macros)
ChatUtil.addMessage(ChatFormatting.GRAY + " - " + macro.getName()
+ ChatFormatting.DARK_GRAY + " [" + KeyUtil.getKeyName(macro.getKey()) + "] "
+ macro.getLines().size());
}
}
@@ -0,0 +1,265 @@
package me.retucio.sputnik.command.commands;
import com.github.retucio.neutrino.EventListener;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.event.TickEvent;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.MiscUtil;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.SharedConstants;
import net.minecraft.WorldVersion;
import net.minecraft.client.Screenshot;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.metadata.pack.PackFormat;
import net.minecraft.sounds.SoundEvents;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.arguments.StringArgumentType;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NonNull;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.ThreadLocalRandom;
/**
* literalmente robado de <a href="https://github.com/0xTas/stardust/blob/main/src/main/java/dev/stardust/commands/Panorama.java">aquí</a>
*/
public class PanoramaCommand extends Command {
private int i = -1;
int timer = 10;
boolean ready = false;
boolean warming = false;
private float prevYaw;
private float prevPitch;
private int prevWidth;
private int prevHeight;
private boolean prevHudHidden;
@Nullable Path dir = null;
public PanoramaCommand() {
super("panorama", "sputnik.commands.panorama.desc");
}
private void takeScreenshot() {
if (dir == null) return;
Screenshot.grab(
dir.toFile(),
"panorama_" + i + ".png",
mc.gameRenderer.mainRenderTarget(),
1, msg -> Sputnik.LOGGER.info(msg.getString())
);
i++;
warming = false;
}
private void begin(String name) {
if (mc.player == null) return;
dir = FabricLoader.getInstance().getGameDir().resolve(Sputnik.MOD_ID + "/panoramas/" + name);
dir.toFile().mkdirs();
i = 0;
warming = false;
prevYaw = mc.player.getYRot();
prevPitch = mc.player.getXRot();
prevWidth = mc.getWindow().getWidth();
prevHeight = mc.getWindow().getHeight();
prevHudHidden = mc.gui.hud.isHidden();
mc.getWindow().setWidth(4096);
mc.getWindow().setHeight(4096);
mc.gameRenderer.mainRenderTarget().resize(4096, 4096);
}
private void assembleResourcePack() {
if (dir == null) return;
Path resourcePacks = FabricLoader.getInstance().getGameDir().resolve("resourcepacks");
Path screenshotsFolder = FabricLoader.getInstance().getGameDir().resolve(Sputnik.MOD_ID + "/panoramas/" + dir.getFileName() + "/screenshots");
Path customBaseFolder = resourcePacks.resolve(dir.getFileName());
Path customPackFolder = customBaseFolder.resolve("assets/minecraft/textures/gui/title/background/");
customPackFolder.toFile().mkdirs();
String mcMeta =
"""
{
"pack":
{
"min_format": [65, 0],
"max_format": [%d, 0],
"description": "%s"
}
}
""".formatted(
getPackVersion(),
Component.translatable("sputnik.commands.panorama.packdesc").getString()
);
try {
int luckyNumber = ThreadLocalRandom.current().nextInt(6);
Files.walkFileTree(screenshotsFolder, new SimpleFileVisitor<>() {
public @NonNull FileVisitResult visitFile(@NonNull Path file, @NonNull BasicFileAttributes attrs) {
MiscUtil.copy(file.toFile(), customPackFolder.resolve(file.getFileName()).toFile());
// pack.png random de alguna de los screenshots
if (file.getFileName().toString().contains(String.valueOf(luckyNumber))) {
MiscUtil.copy(file.toFile(), customBaseFolder.resolve("pack.png").toFile());
}
return FileVisitResult.CONTINUE;
}
});
File packMeta = customBaseFolder.resolve("pack.mcmeta").toFile();
if (!packMeta.createNewFile()) {
ChatUtil.error(Component.translatable("sputnik.commands.panorama.errorassemble").getString());
return;
}
Files.write(packMeta.toPath(), mcMeta.getBytes());
// eliminar el efecto vignette feo de cojones que usa Minecraft por defecto
BufferedImage transparentOverlay = new BufferedImage(16, 128, BufferedImage.TYPE_INT_ARGB);
File overlayImage = customPackFolder.resolve("panorama_overlay.png").toFile();
ImageIO.write(transparentOverlay, "PNG", overlayImage);
} catch (Exception e) {
ChatUtil.error("error de i/o");
Sputnik.LOGGER.error(e);
}
ready = false;
if (mc.player != null) {
mc.player.playSound(SoundEvents.AMETHYST_BLOCK_CHIME, 1f, 1f);
ChatUtil.info(Component.translatable("sputnik.commands.panorama.ready").getString());
}
}
@EventListener
private void onTick(TickEvent.Post event) {
if (mc.player == null || dir == null) return;
if (timer > 0) {
--timer;
return;
} else {
timer = 10;
}
if (ready) {
assembleResourcePack();
}
if (i < 0) return;
switch (i) {
case 0 -> {
if (!warming) {
mc.gameRenderer.mainCamera().enablePanoramicMode();
mc.gameRenderer.setRenderBlockOutline(false);
mc.execute(() -> {
synchronized (mc.levelRenderer) {
mc.levelRenderer.notifyAll();
}
});
if (!mc.gui.hud.isHidden()) mc.gui.hud.toggle();
mc.player.setYRot(prevYaw);
mc.player.setXRot(0f);
warming = true;
} else takeScreenshot();
}
case 1 -> {
if (!warming) {
mc.player.setYRot((prevYaw + 90f) % 360f);
mc.player.setXRot(0f);
warming = true;
} else takeScreenshot();
}
case 2 -> {
if (!warming) {
mc.player.setYRot((prevYaw + 180f) % 360f);
mc.player.setXRot(0f);
warming = true;
} else takeScreenshot();
}
case 3 -> {
if (!warming) {
mc.player.setYRot((prevYaw - 90f) % 360f);
mc.player.setXRot(0f);
warming = true;
} else takeScreenshot();
}
case 4 -> {
if (!warming) {
mc.player.setYRot(prevYaw);
mc.player.setXRot(-90f);
warming = true;
} else takeScreenshot();
}
default -> {
if (!warming) {
mc.player.setYRot(prevYaw);
mc.player.setXRot(90f);
warming = true;
} else {
takeScreenshot();
i = -1;
mc.player.setYRot(prevYaw);
mc.player.setXRot(prevPitch);
mc.gameRenderer.mainCamera().disablePanoramicMode();
mc.gameRenderer.setRenderBlockOutline(true);
mc.getWindow().setWidth(prevWidth);
mc.getWindow().setHeight(prevHeight);
if (mc.gui.hud.isHidden() != prevHudHidden) mc.gui.hud.toggle();
mc.gameRenderer.mainRenderTarget().resize(prevWidth, prevHeight);
mc.execute(() -> {
synchronized (mc.levelRenderer) {
mc.levelRenderer.notifyAll();
}
});
timer = 100; // esperar a que las capturas se escriban
ready = true;
ChatUtil.info(Component.translatable("sputnik.commands.panorama.finalizing").getString());
}
}
}
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.then(
argument("name", StringArgumentType.word()).executes(ctx -> {
String name = ctx.getArgument("name", String.class);
begin(name);
return SUCCESS;
})
);
}
public static int getPackVersion() {
WorldVersion version = SharedConstants.getCurrentVersion();
PackFormat format = version.packVersion(PackType.CLIENT_RESOURCES);
return format.major();
}
}
@@ -7,11 +7,13 @@ import me.retucio.sputnik.util.MiscUtil;
import net.minecraft.client.gui.screens.inventory.InventoryScreen;
import net.minecraft.commands.SharedSuggestionProvider;
import java.awt.*;
// comando que debería de mostrar el inventario de un jugador, pero por cómo funcionan los servers, solo te deja ver el ítem que tenga en la mano
public class PeekCommand extends Command {
public PeekCommand() {
super("inventario", "cotillea el inventario de otro jugador", "invsee", "peek", "inv");
super("inventario", "sputnik.commands.peek.desc", "invsee", "peek", "inv");
}
@Override
@@ -12,6 +12,7 @@ import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.multiplayer.ClientSuggestionProvider;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundCommandsPacket;
import java.util.HashSet;
@@ -23,7 +24,7 @@ public class PluginsCommand extends Command {
private boolean scanning = false;
public PluginsCommand() {
super("plugins", "busca los plugins que tenga el server", "pl", "serverplugins");
super("plugins", "sputnik.commands.plugins.desc", "pl", "serverplugins");
Sputnik.EVENT_BUS.subscribe(this);
}
@@ -31,12 +32,12 @@ public class PluginsCommand extends Command {
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.executes(context -> {
if (mc.hasSingleplayerServer()) {
ChatUtil.error("estás en un solo jugador");
ChatUtil.error(Component.translatable("sputnik.commands.plugins.singleplayer"));
return SUCCESS;
}
if (scanning) {
ChatUtil.error("calmaaa");
ChatUtil.error(Component.translatable("sputnik.commands.plugins.calmdown"));
return SUCCESS;
}
@@ -60,7 +61,7 @@ public class PluginsCommand extends Command {
root.getChildren().forEach(this::checkNodeForPlugins);
if (!plugins.isEmpty()) {
ChatUtil.info(plugins.size() + " namespace(s) encontrado(s) de comandos:");
ChatUtil.info(Component.translatable("sputnik.commands.plugins.namespacesfound", plugins.size()));
plugins.stream()
.sorted()
.forEach(plugin -> ChatUtil.info(" - " + plugin));
@@ -69,7 +70,7 @@ public class PluginsCommand extends Command {
displayResults();
} catch (Exception e) {
ChatUtil.error("error analizando comandos: " + e.getMessage());
ChatUtil.error(Component.translatable("sputnik.commands.plugins.erroranalyzingcommands", e.getMessage()));
}
}
@@ -99,11 +100,11 @@ public class PluginsCommand extends Command {
private void displayResults() {
if (!plugins.isEmpty()) {
ChatUtil.info(plugins.size() + " plugin(s) encontrado(s)");
ChatUtil.info(Component.translatable("sputnik.commands.plugins.pluginsfound", plugins.size()));
plugins.stream().sorted()
.forEach(plugin -> ChatUtil.info(" - " + plugin));
} else {
ChatUtil.warn("sin plugins (probablemente)");
ChatUtil.warn(Component.translatable("sputnik.commands.plugins.noplugins"));
}
scanning = false;
@@ -13,17 +13,17 @@ import net.minecraft.network.chat.Component;
public class PrefixCommand extends Command {
public PrefixCommand() {
super("prefijo", "cambia el prefijo de los comandos", "prefix");
super("prefijo", "sputnik.commands.prefix.desc", "prefix");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder
.then(argument("prefijo", StringArgumentType.word()).executes(ctx -> {
String prefix = ctx.getArgument("prefijo", String.class);
ClientSettingsPanel.clientSettings.commandPrefix.setValue(prefix);
ChatUtil.info(Component.literal("prefijo cambiado a " + ChatFormatting.AQUA + prefix));
return SUCCESS;
}));
.then(argument("prefijo", StringArgumentType.word()).executes(ctx -> {
String prefix = ctx.getArgument("prefijo", String.class);
ClientSettingsPanel.clientSettings.commandPrefix.setValue(prefix);
ChatUtil.info(Component.translatable("sputnik.commands.prefix.changed", ChatFormatting.AQUA + prefix));
return SUCCESS;
}));
}
}
@@ -0,0 +1,92 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.config.ConfigManager;
import me.retucio.sputnik.config.ProfileManager;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.util.List;
public class ProfileCommand extends Command {
private static final SuggestionProvider<SharedSuggestionProvider> PROFILES = (ctx, builder) ->
SharedSuggestionProvider.suggest(ProfileManager.INSTANCE.getProfileNames(), builder);
public ProfileCommand() {
super("perfil", "sputnik.commands.profile.desc", "profile", "perfiles", "profiles");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder
.executes(ctx -> {
list();
return SUCCESS;
})
.then(literal("list").executes(ctx -> {
list();
return SUCCESS;
}))
.then(literal("save")
.then(argument("nombre", StringArgumentType.word()).executes(ctx -> {
String name = ctx.getArgument("nombre", String.class);
if (!ProfileManager.isValidName(name)) {
ChatUtil.error(Component.translatable("sputnik.commands.profile.invalidname", name));
return SUCCESS;
}
if (ProfileManager.INSTANCE.save(name)) {
ConfigManager.INSTANCE.save();
ChatUtil.info(Component.translatable("sputnik.commands.profile.saved",
ChatFormatting.AQUA + name));
} else {
ChatUtil.error(Component.translatable("sputnik.commands.profile.savefailed", name));
}
return SUCCESS;
})))
.then(literal("load")
.then(argument("nombre", StringArgumentType.word()).suggests(PROFILES).executes(ctx -> {
String name = ctx.getArgument("nombre", String.class);
if (ProfileManager.INSTANCE.load(name))
ChatUtil.info(Component.translatable("sputnik.commands.profile.loaded",
ChatFormatting.AQUA + name));
else
ChatUtil.error(Component.translatable("sputnik.commands.profile.notfound", name));
return SUCCESS;
})))
.then(literal("delete")
.then(argument("nombre", StringArgumentType.word()).suggests(PROFILES).executes(ctx -> {
String name = ctx.getArgument("nombre", String.class);
if (ProfileManager.INSTANCE.delete(name))
ChatUtil.info(Component.translatable("sputnik.commands.profile.deleted",
ChatFormatting.AQUA + name));
else
ChatUtil.error(Component.translatable("sputnik.commands.profile.notfound", name));
return SUCCESS;
})));
}
private static void list() {
List<String> profiles = ProfileManager.INSTANCE.getProfileNames();
if (profiles.isEmpty()) {
ChatUtil.info(Component.translatable("sputnik.commands.profile.none"));
return;
}
String current = ProfileManager.INSTANCE.getCurrent();
ChatUtil.info(Component.translatable("sputnik.commands.profile.list", profiles.size()));
for (String profile : profiles)
ChatUtil.addMessage((profile.equals(current) ? ChatFormatting.GREEN : ChatFormatting.GRAY) + " - " + profile);
}
}
@@ -8,12 +8,13 @@ import net.minecraft.client.gui.components.ChatComponent;
import net.minecraft.client.multiplayer.chat.GuiMessage;
import net.minecraft.commands.SharedSuggestionProvider;
import java.awt.*;
import java.util.List;
public class PurgeCommand extends Command {
public PurgeCommand() {
super("purgar", "elimina mensajes del chat", "purge");
super("purgar", "sputnik.commands.purge.desc", "purge");
}
@Override
@@ -28,7 +29,7 @@ public class PurgeCommand extends Command {
}
private void purgeMessages(int amount) {
ChatComponent chatHud = mc.gui.getChat();
ChatComponent chatHud = mc.gui.hud.getChat();
synchronized (chatHud) {
List<GuiMessage.Line> visibleMessages = ((IChatComponent) chatHud).sputnik$getVisibleMessages();
@@ -10,7 +10,7 @@ import net.minecraft.commands.SharedSuggestionProvider;
public class RepeatCommand extends Command {
public RepeatCommand() {
super("repetir", "repite un comando x veces", "repeat");
super("repetir", "sputnik.commands.repeat.desc", "repeat");
}
@Override
@@ -13,7 +13,7 @@ import java.util.concurrent.TimeUnit;
public class RepeatWaitCommand extends Command {
public RepeatWaitCommand() {
super("repesperar", "combinación de $repetir y $esperar");
super("repesperar", "sputnik.commands.repeatwait.desc", "repeatwait");
}
@Override
@@ -6,6 +6,7 @@ import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.misc.ScreenshotPlus;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.io.IOException;
@@ -13,7 +14,7 @@ import java.io.IOException;
public class SaveScreenshotCommand extends Command {
public SaveScreenshotCommand() {
super("guardarcaptura", "guarda localmente la captura más reciente");
super("guardarcaptura", "sputnik.commands.savescreenshot.desc", "savescreenshot");
}
@Override
@@ -26,7 +27,7 @@ public class SaveScreenshotCommand extends Command {
try {
screenshotPlus.saveScreenshot();
} catch (IOException e) {
ChatUtil.error("no se pudo guardar la captura");
ChatUtil.error(Component.translatable("sputnik.commands.savescreenshot.error"));
}
}
});
@@ -18,7 +18,7 @@ import java.time.Instant;
public class SendCommand extends Command {
public SendCommand() {
super("enviar", "manda un mensaje en el chat", "send");
super("enviar", "sputnik.commands.send.desc", "send");
}
@Override
@@ -37,7 +37,7 @@ public class SendCommand extends Command {
LastSeenMessagesTracker tracker = ((ClientPacketListenerAccessor) handler).getLastSeenMessagesTracker();
LastSeenMessagesTracker.Update lastSeenMessages = tracker.generateAndApplyUpdate();
// empacar firma para un chat seguro
// empacar firma
MessageSignature messageSignature = ((ClientPacketListenerAccessor) handler)
.getSignedMessageEncoder()
.pack(new SignedMessageBody(
@@ -10,16 +10,18 @@ import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.world.entity.Entity;
import org.lwjgl.glfw.GLFW;
import java.awt.*;
public class SpectateCommand extends Command {
public SpectateCommand() {
super("espectar", "te permite adquirir el punto de vista de una entidad", "spectate", "spec");
super("espectar", "sputnik.commands.spectate.desc", "spectate", "spec");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder
.executes(ctx -> { // If no argument, use the entity player is looking at
.executes(ctx -> {
mc.execute(() -> {
Entity target = EntityUtil.getEntityPlayerIsLookingAt();
if (target != null)
@@ -40,7 +42,7 @@ public class SpectateCommand extends Command {
@EventListener
public void onKey(KeyEvent event) {
if (mc.getCameraEntity() != mc.player && mc.screen == null
if (mc.getCameraEntity() != mc.player && mc.gui.screen() == null
&& event.getKey() == GLFW.GLFW_KEY_ESCAPE && event.getAction() != GLFW.GLFW_RELEASE ) {
event.cancel();
mc.setCameraEntity(mc.player);
@@ -7,11 +7,13 @@ import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.ModuleManager;
import net.minecraft.commands.SharedSuggestionProvider;
import java.awt.*;
// porque usar el ratón es más lento que escribir, a menos que tengas toque con el teclado como yo
public class ToggleCommand extends Command {
public ToggleCommand() {
super("alternar", "enciende / apaga módulos", "toggle");
super("alternar", "sputnik.commands.toggle.desc", "toggle");
}
@Override
@@ -16,7 +16,7 @@ public class WaitCommand extends Command {
private static final ScheduledExecutorService DELAYED_EXECUTOR = new ScheduledThreadPoolExecutor(2);
public WaitCommand() {
super("wait", "espera un tiempo determinado antes de ejecutar otro comando", "esperar", "sleep", "delay");
super("wait", "sputnik.commands.wait.desc", "esperar", "sleep", "delay");
}
@Override
@@ -1,5 +1,7 @@
package me.retucio.sputnik.config;
import me.retucio.sputnik.util.I18nUtil;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -12,7 +14,8 @@ import java.util.Map;
public class BinarySerializer {
private static final int MAGIC_NUMBER = 0x53505554; // "SPUT"
private static final int VERSION = 2;
private static final int VERSION = 3;
private static final int MIN_VERSION = 2;
private static final byte TYPE_NULL = 0;
private static final byte TYPE_BOOLEAN = 1;
@@ -70,6 +73,8 @@ public class BinarySerializer {
writeStringBooleanMap(out, config.hudVisibilities);
writeStringStringMap(out, config.hudImagePaths);
writeStringStringMap(out, config.friends);
writeMacros(out, config.macros);
writeString(out, config.currentProfile);
if (config.searchBarPosition != null) {
out.writeBoolean(true);
@@ -83,43 +88,63 @@ public class BinarySerializer {
public static ClientConfig readConfig(File file)
throws IOException {
if (!file.exists() || file.length() == 0) {
throw new IOException("archivo de config. vacío o inexistente");
throw new IOException(I18nUtil.fmt("sputnik.config.error.empty",
"archivo de config. vacío o inexistente"));
}
try (DataInputStream in = new DataInputStream(
new BufferedInputStream(new FileInputStream(file)))) {
if (file.length() < 8) {
throw new IOException("archivo demasiado pequeño para ser válido");
throw new IOException(I18nUtil.fmt("sputnik.config.error.toosmall",
"archivo demasiado pequeño para ser válido"));
}
// verificar cabezal
int magic = in.readInt();
if (magic != MAGIC_NUMBER) {
throw new IOException("formato de archivo inválido: " + magic);
throw new IOException(I18nUtil.fmt("sputnik.config.error.invalidformat",
"formato de archivo inválido: %s", magic));
}
int version = in.readInt();
if (version != VERSION) {
throw new IOException("versión inválida: " + version);
if (version < MIN_VERSION || version > VERSION) {
throw new IOException(I18nUtil.fmt("sputnik.config.error.invalidversion",
"versión inválida: %s", version));
}
ClientConfig config = new ClientConfig();
// leer mapas. se leen a locales y la ClientConfig se construye al final: su
// constructor la suscribe al bus de eventos, así que crearla antes de terminar
// de leer dejaba una config. huérfana escuchando si el archivo estaba corrupto
Map<String, Boolean> moduleStates = readStringBooleanMap(in);
Map<String, Object> settings = readSettingsMap(in);
Map<String, int[]> settingsPanels = readStringIntArrayMap(in);
Map<String, ClientConfig.PanelData> extendablePanels = readExtendablePanels(in);
Map<String, int[]> hudPositions = readStringIntArrayMap(in);
Map<String, Boolean> hudVisibilities = readStringBooleanMap(in);
Map<String, String> hudImagePaths = readStringStringMap(in);
Map<String, String> friends = readStringStringMap(in);
// leer mapas
config.moduleStates = readStringBooleanMap(in);
config.settings = readSettingsMap(in);
config.settingsPanels = readStringIntArrayMap(in);
config.extendablePanels = readExtendablePanels(in);
config.hudPositions = readStringIntArrayMap(in);
config.hudVisibilities = readStringBooleanMap(in);
config.hudImagePaths = readStringStringMap(in);
config.friends = readStringStringMap(in);
List<ClientConfig.MacroData> macros = version >= 3 ? readMacros(in) : new ArrayList<>();
String currentProfile = version >= 3 ? readString(in) : "";
// leer pos. de la barra de búsqueda
if (in.readBoolean()) {
config.searchBarPosition = new int[]{in.readInt(), in.readInt()};
}
int[] searchBarPosition = in.readBoolean()
? new int[]{in.readInt(), in.readInt()}
: null;
ClientConfig config = new ClientConfig();
config.moduleStates = moduleStates;
config.settings = settings;
config.settingsPanels = settingsPanels;
config.extendablePanels = extendablePanels;
config.hudPositions = hudPositions;
config.hudVisibilities = hudVisibilities;
config.hudImagePaths = hudImagePaths;
config.friends = friends;
config.macros = macros;
config.currentProfile = currentProfile == null ? "" : currentProfile;
if (searchBarPosition != null) config.searchBarPosition = searchBarPosition;
return config;
}
@@ -368,6 +393,35 @@ public class BinarySerializer {
return map;
}
private static void writeMacros(DataOutputStream out, List<ClientConfig.MacroData> macros)
throws IOException {
if (macros == null) {
out.writeInt(0);
return;
}
out.writeInt(macros.size());
for (ClientConfig.MacroData macro : macros) {
writeString(out, macro.name());
out.writeInt(macro.key());
out.writeInt(macro.lines().size());
for (String line : macro.lines()) writeString(out, line);
}
}
private static List<ClientConfig.MacroData> readMacros(DataInputStream in) throws IOException {
int size = in.readInt();
List<ClientConfig.MacroData> macros = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
String name = readString(in);
int key = in.readInt();
int lineCount = in.readInt();
List<String> lines = new ArrayList<>(lineCount);
for (int j = 0; j < lineCount; j++) lines.add(readString(in));
macros.add(new ClientConfig.MacroData(name, key, lines));
}
return macros;
}
private static void writeString(DataOutputStream out, String str) throws IOException {
if (str == null) {
out.writeInt(-1);
@@ -17,7 +17,7 @@ import java.util.Map;
import static me.retucio.sputnik.Sputnik.EVENT_BUS;
import static me.retucio.sputnik.Sputnik.mc;
// clase donde se guardan las configuraciones temporalmente hasta escribirlas en camera_tweaks.json
// clase donde se guardan las configuraciones temporalmente hasta escribirlas en sputnik.dat
public class ClientConfig {
// nombre del módulo -> estado del módulo
@@ -47,19 +47,31 @@ public class ClientConfig {
// amigos: uuid -> nombre
public Map<String, String> friends = new HashMap<>();
public List<MacroData> macros = new ArrayList<>();
// perfil cargado actualmente, vacío si es la config. suelta
public String currentProfile = "";
public ClientConfig() {
EVENT_BUS.subscribe(this);
}
// mientras se aplica una config. los cambios vienen de ella, así que reescribirla es
// como poco redundante, y al cambiar de perfil pisaba lo que aún no se había aplicado
private static boolean isApplying() {
return ConfigManager.INSTANCE != null && ConfigManager.INSTANCE.isApplying();
}
@EventListener
public void onToggleModule(ToggleModuleEvent event) {
if (isApplying()) return;
ConfigManager.INSTANCE.setModuleState(event.getModule());
}
@SuppressWarnings("rawtypes")
@EventListener
public void onUpdateSetting(UpdateSettingEvent event) {
if (mc == null || !event.shouldSave()) return;
if (mc == null || !event.shouldSave() || isApplying()) return;
// guardar ajustes con su respectivo tipo de valor, en formato "nombreMódulo:nombreAjuste"
Object value = null;
@@ -71,7 +83,8 @@ public class ClientConfig {
case StringSetting s: value = s.getValue(); break;
case ListSetting l: value = l.getConfigValues(); break;
case ColorSetting c: value = c.getConfigValue(); break;
case OptionSetting o: value = o.getDisplayName(); break;
case OptionSetting o: value = o.getConfigName(); break;
case FileSetting f: value = f.getValue(); break;
default: break;
}
@@ -104,9 +117,11 @@ public class ClientConfig {
ConfigManager.INSTANCE.markDirty();
}
// put y no replace: replace no hace nada si la clave no está todavía, así que mover un
// marco sin haberlo plegado antes (que es lo que crea la entrada) no se guardaba
@EventListener
public void onMoveModulePanel(ModulePanelEvent.Move event) {
extendablePanels.replace(event.getPanel().category.toString(), new PanelData(
extendablePanels.put(event.getPanel().category.toString(), new PanelData(
event.getPanel().getX(),
event.getPanel().getY(),
event.getPanel().isExtended()));
@@ -124,7 +139,7 @@ public class ClientConfig {
@EventListener
public void onMoveClientSettingsPanel(ClientSettingsPanelEvent.Move event) {
extendablePanels.replace("ajustes", new PanelData(
extendablePanels.put("ajustes", new PanelData(
ClickGui.INSTANCE.getClientSettingsPanel().getX(),
ClickGui.INSTANCE.getClientSettingsPanel().getY(),
ClickGui.INSTANCE.getClientSettingsPanel().isExtended()));
@@ -142,7 +157,7 @@ public class ClientConfig {
@EventListener
public void onMoveFriendsPanel(FriendPanelEvent.Move event) {
extendablePanels.replace("amigos", new PanelData(
extendablePanels.put("amigos", new PanelData(
ClickGui.INSTANCE.getFriendsPanel().getX(),
ClickGui.INSTANCE.getFriendsPanel().getY(),
ClickGui.INSTANCE.getFriendsPanel().isExtended()));
@@ -151,6 +166,7 @@ public class ClientConfig {
@EventListener
public void onAddFriend(FriendEvent.Add event) {
if (isApplying()) return;
String uuid = event.getFriend().getUuid().toString();
String name = event.getFriend().getName();
if (!friends.containsKey(uuid)) {
@@ -161,6 +177,7 @@ public class ClientConfig {
@EventListener
public void onRemoveFriend(FriendEvent.Remove event) {
if (isApplying()) return;
friends.remove(event.getFriend().getUuid().toString());
ConfigManager.INSTANCE.markDirty();
}
@@ -168,4 +185,8 @@ public class ClientConfig {
public record PanelData(int x, int y, boolean extended) implements Serializable {
@Serial private static final long serialVersionUID = 1L;
}
public record MacroData(String name, int key, List<String> lines) implements Serializable {
@Serial private static final long serialVersionUID = 1L;
}
}
@@ -8,27 +8,35 @@ import me.retucio.sputnik.friend.FriendManager;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.client.ClientSettingsModule;
import me.retucio.sputnik.macro.Macro;
import me.retucio.sputnik.macro.MacroManager;
import me.retucio.sputnik.module.setting.*;
import me.retucio.sputnik.module.setting.settings.*;
import me.retucio.sputnik.ui.hud.HudElement;
import me.retucio.sputnik.ui.hud.HudRenderer;
import me.retucio.sputnik.ui.widgets.Panel;
import me.retucio.sputnik.ui.widgets.panels.ExtendablePanel;
import me.retucio.sputnik.ui.widgets.panels.FriendsPanel;
import me.retucio.sputnik.ui.widgets.panels.ModulePanel;
import me.retucio.sputnik.ui.screen.ClickGui;
import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel;
import me.retucio.sputnik.ui.widgets.panels.SettingsPanel;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.I18nUtil;
import net.minecraft.network.chat.Component;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
// se ocupa de guardar, cargar y aplicar ajustes
@@ -40,8 +48,9 @@ public class ConfigManager {
public static ConfigManager INSTANCE;
private boolean loaded = false;
private boolean applying = false;
private boolean dirty = false;
private long lastDirtyTime = 0L;
private long lastSaveTime = System.currentTimeMillis();
private ClientConfig config = null;
private final ExecutorService saveExecutor = Executors.newSingleThreadExecutor(r -> {
@@ -50,7 +59,10 @@ public class ConfigManager {
return t;
});
private final AtomicBoolean saveQueued = new AtomicBoolean(false);
// instantánea pendiente de escribir. si llega otra antes de que el hilo de fondo escriba,
// sustituye a la anterior en vez de descartarse (que era lo que pasaba con un simple
// "ya hay uno en cola": se tiraba justo la más reciente y se perdían esos cambios)
private final AtomicReference<byte[]> pending = new AtomicReference<>();
// ------------------ MÉTODOS PRINCIPALES ------------------
@@ -58,58 +70,81 @@ public class ConfigManager {
// guardar configuraciones
public void save() {
if (!loaded) return;
ensureConfig();
// serializar a bytes en el hilo principal (snapshot inmediato, evita race conditions)
final byte[] snapshot;
try {
snapshot = BinarySerializer.toBytes(config);
} catch (IOException e) {
Sputnik.LOGGER.error("error al serializar ajustes", e);
return;
}
// si ya hay un guardado en cola, no añadir otro
if (!saveQueued.compareAndSet(false, true)) return;
saveExecutor.execute(() -> {
try {
BinarySerializer.writeBytes(snapshot, CONFIG_FILE);
Sputnik.LOGGER.info("ajustes guardados");
} catch (IOException e) {
Sputnik.LOGGER.error("error al guardar ajustes", e);
} finally {
saveQueued.set(false);
}
});
writeSnapshot(false);
}
public void saveOnShutdown() {
// esperar a que termine cualquier guardado en curso, luego guardar síncronamente
// no aceptar más tareas y esperar a que termine el guardado en curso
saveExecutor.shutdown();
try {
// dar hasta 5 segundos para que termine el guardado en curso
if (!saveExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
Sputnik.LOGGER.warn("el hilo de guardado no terminó a tiempo");
Sputnik.LOGGER.warn(I18nUtil.fmt("sputnik.config.warn.thread",
"el hilo de guardado no terminó a tiempo"));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// guardado final sincrónico por si había cambios pendientes no guardados
if (dirty) save();
// el guardado final va en este mismo hilo: el executor ya está cerrado, así que
// encolar la escritura lanzaba RejectedExecutionException y no se guardaba NADA al
// cerrar el juego. o sea que se perdía todo lo hecho desde el último autoguardado
if (!loaded) return;
writeSnapshot(true);
}
// serializa en el hilo actual (instantánea inmediata, evita race conditions) y escribe
// en el de fondo, salvo que se pida síncrono (al cerrar)
private void writeSnapshot(boolean sync) {
ensureConfig();
final byte[] snapshot;
try {
snapshot = BinarySerializer.toBytes(config);
} catch (IOException e) {
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.serialization",
"error al serializar ajustes"), e);
return;
}
dirty = false;
lastSaveTime = System.currentTimeMillis();
if (sync) {
writeToDisk(snapshot);
return;
}
// si ya había una instantánea esperando, la tarea encolada recogerá ésta
if (pending.getAndSet(snapshot) != null) return;
saveExecutor.execute(() -> {
byte[] bytes = pending.getAndSet(null);
if (bytes != null) writeToDisk(bytes);
});
}
private static void writeToDisk(byte[] bytes) {
try {
BinarySerializer.writeBytes(bytes, CONFIG_FILE);
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.saved", "ajustes guardados"));
} catch (IOException e) {
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.saving",
"error al guardar ajustes"), e);
}
}
public void load() {
Sputnik.LOGGER.info("cargando ajustes...");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.loading", "cargando ajustes..."));
if (CONFIG_FILE.exists()) {
try {
config = BinarySerializer.readConfig(CONFIG_FILE);
Sputnik.LOGGER.info("ajustes cargados");
setConfig(BinarySerializer.readConfig(CONFIG_FILE));
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.loaded", "ajustes cargados"));
return;
} catch (IOException e) {
Sputnik.LOGGER.error("error cargando ajustes, intentando json", e);
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.loading",
"error cargando ajustes, intentando json"), e);
}
}
@@ -117,35 +152,96 @@ public class ConfigManager {
if (LEGACY_JSON_FILE.exists()) {
try (FileReader reader = new FileReader(LEGACY_JSON_FILE)) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
config = gson.fromJson(reader, ClientConfig.class);
Sputnik.LOGGER.info("ajustes json cargados (migración)");
setConfig(gson.fromJson(reader, ClientConfig.class));
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.json",
"ajustes json cargados (migración)"));
save();
// aquí `loaded` todavía es false (se pone al aplicar), así que save() no
// haría nada: la migración se escribe directamente
writeSnapshot(true);
return;
} catch (IOException e) {
Sputnik.LOGGER.error("error cargando json, usando defaults", e);
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.json",
"error cargando json, usando defaults"), e);
}
}
// crear nuevo config
ensureConfig();
writeSnapshot(true);
}
// aplicar configuraciones cargadas. devuelve false si todavía no se puede (falta algo por
// construir), para que se reintente en el siguiente tick en vez de quedarse sin aplicar
public boolean applyConfig() {
if (!shouldApply()) return false;
applying = true;
try {
// los del mod primero: entre ellos está "teclas multimódulo", que decide si al
// asignar una tecla se le quita a los demás módulos. aplicándolos los últimos,
// las teclas de los módulos se cargaban con el valor por defecto de ese ajuste
applyClientSettings();
applyModuleStates();
applyModuleSettings();
applySettingsPanels();
applySearchBarPosition();
applyExtendablePanels();
applyFriends();
applyMacros();
applyHudElements();
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.applied", "ajustes aplicados"));
} catch (Exception e) {
// si algo peta a medio aplicar no se puede dejar `loaded` en false: save() no
// haría nada en toda la sesión y se perdería la config. entera al cerrar
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.applying",
"error aplicando ajustes"), e);
} finally {
applying = false;
loaded = true;
}
return true;
}
// true mientras se está cargando la config. sobre los ajustes. lo mira KeySetting para no
// tratar las teclas guardadas como si el usuario las acabara de asignar, y ClientConfig
// para no reescribir en la config. lo que se le está aplicando
public boolean isApplying() {
return applying;
}
// sustituye la config. viva por otra y la vuelca sobre el estado actual del cliente
public void replaceConfig(ClientConfig newConfig) {
applying = true;
try {
resetLiveState();
} finally {
applying = false;
}
setConfig(newConfig);
loaded = false;
applyConfig();
save();
}
// aplicar configuraciones cargadas
public void applyConfig() {
if (!shouldApply()) return;
// lo que no venga en la config. nueva tiene que quedarse en su valor por defecto, no en
// el que tuviera el perfil anterior
private void resetLiveState() {
if (ClickGui.INSTANCE != null) ClickGui.INSTANCE.closeAllSettingsPanels();
applyModuleStates();
applyModuleSettings();
applySettingsPanels();
applySearchBarPosition();
applyClientSettings();
applyExtendablePanels();
applyFriends();
for (Module module : ModuleManager.INSTANCE.getModules()) {
module.setEnabled(false);
module.getSettings().forEach(Setting::reset);
}
ClientSettingsPanel.clientSettings.getSettings().forEach(Setting::reset);
loaded = true;
Sputnik.LOGGER.info("ajustes aplicados");
for (Friend friend : new ArrayList<>(FriendManager.INSTANCE.getFriends()))
FriendManager.INSTANCE.remove(friend);
MacroManager.INSTANCE.setMacros(List.of());
}
// obtener configuración actual
@@ -202,6 +298,20 @@ public class ConfigManager {
markDirty();
}
public void setMacros(List<Macro> macros) {
ensureConfig();
config.macros = macros.stream()
.map(macro -> new ClientConfig.MacroData(macro.getName(), macro.getKey(), List.copyOf(macro.getLines())))
.toList();
markDirty();
}
public void setCurrentProfile(String name) {
ensureConfig();
config.currentProfile = name;
markDirty();
}
// ------------------ APLICAR AJUSTES ------------------
@@ -211,7 +321,28 @@ public class ConfigManager {
friend.setName(name);
FriendManager.INSTANCE.add(friend);
});
Sputnik.LOGGER.info("amigos aplicados");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.friends",
"amigos aplicados"));
}
public void applyMacros() {
MacroManager.INSTANCE.setMacros(config.macros.stream()
.map(data -> new Macro(data.name(), data.key(), data.lines()))
.toList());
}
// el HudRenderer coloca sus elementos al construirse, pero al cambiar de perfil ya existen
public void applyHudElements() {
if (HudRenderer.INSTANCE == null) return;
for (HudElement element : HudRenderer.INSTANCE.getElements()) {
int[] position = config.hudPositions.get(element.getId());
if (position != null && position.length == 2) element.setPosition(position[0], position[1]);
else element.resetPosition();
Boolean visible = config.hudVisibilities.get(element.getId());
element.setVisible(visible == null || visible);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
@@ -231,7 +362,9 @@ public class ConfigManager {
case ListSetting listSetting -> applyListSetting(listSetting, value);
case ColorSetting colorSetting -> applyColorSetting(colorSetting, value);
case OptionSetting optionSetting -> applyOptionSetting(optionSetting, value);
default -> Sputnik.LOGGER.warn("watafac queseso: {}", setting.getClass().getSimpleName());
case FileSetting fileSetting -> applyFileSetting(fileSetting, value);
default -> Sputnik.LOGGER.warn(I18nUtil.fmt("sputnik.config.warn.unknownsetting",
"watafac queseso: %s", setting.getClass().getSimpleName()));
}
}
@@ -241,14 +374,16 @@ public class ConfigManager {
module.setEnabled(config.moduleStates.get(module.getName()));
}
});
Sputnik.LOGGER.info("estados de módulos aplicados");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.modulestates",
"estados de módulos aplicados"));
}
private void applyModuleSettings() {
ModuleManager.INSTANCE.getModules().forEach(module -> {
module.getSettings().forEach(this::applySetting);
});
Sputnik.LOGGER.info("ajustes de módulos aplicados");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.modulesettings",
"ajustes de módulos aplicados"));
}
private void applySettingsPanels() {
@@ -256,14 +391,16 @@ public class ConfigManager {
Module module = ModuleManager.INSTANCE.getModuleByName(moduleName);
if (module == null) {
Sputnik.LOGGER.warn("\"null\" como módulo (\"{}\")", moduleName);
Sputnik.LOGGER.warn(I18nUtil.fmt("sputnik.config.warn.nullmodule",
"\"null\" como módulo (\"%s\")", moduleName));
return;
}
ClickGui.INSTANCE.openSettingsPanel(module, position[0], position[1]);
});
Sputnik.LOGGER.info("estados de marcos de ajustes aplicados");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.settingspanels",
"estados de marcos de ajustes aplicados"));
}
private void applySearchBarPosition() {
@@ -271,13 +408,15 @@ public class ConfigManager {
ClickGui.INSTANCE.getSearchBar().setX(config.searchBarPosition[0]);
ClickGui.INSTANCE.getSearchBar().setY(config.searchBarPosition[1]);
}
Sputnik.LOGGER.info("posición de la barra de búsqueda aplicada");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.searchbar",
"posición de la barra de búsqueda aplicada"));
}
private void applyClientSettings() {
ClientSettingsPanel.clientSettings.setEnabled(true);
ClientSettingsPanel.clientSettings.getSettings().forEach(this::applySetting);
Sputnik.LOGGER.info("ajustes del cliente aplicados");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.clientsettings",
"ajustes del cliente aplicados"));
}
private void applyExtendablePanels() {
@@ -287,7 +426,8 @@ public class ConfigManager {
applyExtendablePanel(ClickGui.INSTANCE.getClientSettingsPanel());
applyExtendablePanel(ClickGui.INSTANCE.getFriendsPanel());
Sputnik.LOGGER.info("posiciones de marcos extendibles aplicadas");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.extendablepanels",
"posiciones de marcos extendibles aplicadas"));
ClickGui.INSTANCE.refreshListButtons();
}
@@ -308,14 +448,8 @@ public class ConfigManager {
panel.setX(panelData.x());
panel.setY(panelData.y());
if (panel instanceof ModulePanel mPanel) {
mPanel.setExtended(panelData.extended());
}
if (panel instanceof ClientSettingsPanel sPanel) {
sPanel.setExtended(panelData.extended());
}
if (panel instanceof FriendsPanel fPanel) {
fPanel.setExtended(panelData.extended());
if (panel instanceof ExtendablePanel<?> ePanel) {
ePanel.setExtended(panelData.extended());
}
}
}
@@ -344,8 +478,8 @@ public class ConfigManager {
}
private static void applyStringSetting(StringSetting setting, Object value) {
if (value instanceof String) {
setting.setValue((String) value);
if (value instanceof String v) {
setting.setValue(v);
}
}
@@ -362,8 +496,14 @@ public class ConfigManager {
Map<T, Boolean> convertedValues = new HashMap<>();
for (T option : setting.getOptions()) {
String key = setting.getDisplayName(option);
String key = setting.getConfigName(option);
Object booleanValue = map.get(key);
// compatibilidad con configs. de antes, cuando la clave era el nombre ya
// traducido (el de vanilla en inglés). sin esto la lista entera se quedaba
// en false, o sea que al usuario se le borraba lo que tuviera marcado
if (booleanValue == null) booleanValue = map.get(I18nUtil.vanillaName(key));
convertedValues.put(option, booleanValue instanceof Boolean && (Boolean) booleanValue);
}
@@ -400,6 +540,12 @@ public class ConfigManager {
}
}
private static void applyFileSetting(FileSetting setting, Object value) {
if (value instanceof String v) {
setting.setValue(v);
}
}
private static <T> void applyOptionalProperty(Map<?, ?> map, String key, Class<T> type, Consumer<T> setter) {
Object value = map.get(key);
if (type.isInstance(value)) {
@@ -414,12 +560,24 @@ public class ConfigManager {
return setting.getSg().getModule().getName() + ":" + setting.getTrueName();
}
// ClickGui incluida: los marcos y la barra de búsqueda se aplican sobre ella, y se
// construye en una tarea aparte. si todavía no está, mejor esperar al siguiente tick
private boolean shouldApply() {
return Sputnik.mc != null && ModuleManager.INSTANCE != null && config != null;
return Sputnik.mc != null && ModuleManager.INSTANCE != null
&& ClickGui.INSTANCE != null && config != null;
}
private void ensureConfig() {
if (config == null) config = new ClientConfig();
if (config == null) setConfig(new ClientConfig());
}
// ClientConfig se suscribe al bus en su constructor, así que al reemplazarla hay que
// dar de baja a la anterior: si no, la vieja seguía escuchando y escribiendo en un
// mapa que ya no era el que se guarda
private void setConfig(ClientConfig newConfig) {
if (config == newConfig) return;
if (config != null) Sputnik.EVENT_BUS.unsubscribe(config);
config = newConfig;
}
public boolean hasLoaded() {
@@ -428,7 +586,6 @@ public class ConfigManager {
public void markDirty() {
this.dirty = true;
this.lastDirtyTime = System.currentTimeMillis();
}
public boolean isDirty() {
@@ -441,11 +598,14 @@ public class ConfigManager {
ClientSettingsModule settings = ClientSettingsPanel.clientSettings;
if (!settings.autosave.getValue()) return;
// desde el último guardado, no desde el último cambio: contando desde el cambio,
// tocando ajustes cada menos del intervalo no se autoguardaba nunca
long intervalMs = (long) (settings.autosaveInterval.getValue() * 1000);
if (System.currentTimeMillis() - lastDirtyTime < intervalMs) return;
if (System.currentTimeMillis() - lastSaveTime < intervalMs) return;
HudRenderer.INSTANCE.pushNotification("autoguardando...", "guardando configuración...");
HudRenderer.INSTANCE.pushNotification(
Component.translatable("sputnik.config.autosaving.title").getString(),
Component.translatable("sputnik.config.autosaving.desc").getString());
save();
dirty = false;
}
}
@@ -0,0 +1,99 @@
package me.retucio.sputnik.config;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.util.I18nUtil;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class ProfileManager {
public static final int MAX_NAME_LENGTH = 32;
private static final File DIR = new File("sputnik/profiles");
private static final String EXTENSION = ".dat";
public static ProfileManager INSTANCE;
public List<String> getProfileNames() {
File[] files = DIR.listFiles((dir, name) -> name.endsWith(EXTENSION));
if (files == null) return List.of();
List<String> names = new ArrayList<>();
for (File file : files) {
String name = file.getName();
names.add(name.substring(0, name.length() - EXTENSION.length()));
}
names.sort(Comparator.naturalOrder());
return names;
}
public boolean exists(String name) {
return fileOf(name).exists();
}
public String getCurrent() {
return ConfigManager.INSTANCE.getConfig().currentProfile;
}
// vuelca la config. viva en el perfil y lo deja como actual
public boolean save(String name) {
if (!isValidName(name)) return false;
if (!DIR.exists() && !DIR.mkdirs()) {
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.profile.error.dir",
"no se pudo crear el directorio de perfiles"));
return false;
}
ConfigManager.INSTANCE.setCurrentProfile(name);
try {
BinarySerializer.writeConfig(ConfigManager.INSTANCE.getConfig(), fileOf(name));
return true;
} catch (IOException e) {
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.profile.error.saving",
"error al guardar el perfil \"%s\"", name), e);
return false;
}
}
public boolean load(String name) {
if (!exists(name)) return false;
ClientConfig profile;
try {
profile = BinarySerializer.readConfig(fileOf(name));
} catch (IOException e) {
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.profile.error.loading",
"error al cargar el perfil \"%s\"", name), e);
return false;
}
profile.currentProfile = name;
ConfigManager.INSTANCE.replaceConfig(profile);
return true;
}
public boolean delete(String name) {
if (!exists(name)) return false;
if (!fileOf(name).delete()) return false;
if (name.equals(getCurrent())) ConfigManager.INSTANCE.setCurrentProfile("");
return true;
}
public static boolean isValidName(String name) {
return name != null
&& !name.isBlank()
&& name.length() <= MAX_NAME_LENGTH
&& name.matches("[a-zA-Z0-9 _-]+");
}
private static File fileOf(String name) {
return new File(DIR, name + EXTENSION);
}
}
@@ -0,0 +1,21 @@
package me.retucio.sputnik.event.interact;
import com.github.retucio.neutrino.Event;
import net.minecraft.world.item.ItemStack;
import java.util.List;
public class UpdateInventoryEvent extends Event {
private final List<ItemStack> prev;
public UpdateInventoryEvent(List<ItemStack> prev) {
this.prev = prev;
}
public List<ItemStack> getPrev() {
return prev;
}
}
@@ -0,0 +1,53 @@
package me.retucio.sputnik.macro;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.client.Minecraft;
import org.lwjgl.glfw.GLFW;
import java.util.ArrayList;
import java.util.List;
public class Macro {
private String name;
private int key;
private final List<String> lines = new ArrayList<>();
private boolean searchMatch = true;
public Macro(String name) {
this(name, GLFW.GLFW_KEY_UNKNOWN, List.of());
}
public Macro(String name, int key, List<String> lines) {
this.name = name;
this.key = key;
this.lines.addAll(lines);
}
// cada línea se manda como si el usuario la hubiera escrito en el chat, así que valen
// comandos del mod, comandos del servidor y mensajes normales
public void run() {
if (Minecraft.getInstance().player == null) return;
for (String line : lines) {
if (!line.isBlank()) ChatUtil.simulateChatMessage(line);
}
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getKey() { return key; }
public void setKey(int key) { this.key = key; }
public List<String> getLines() { return lines; }
public void setLines(List<String> lines) {
this.lines.clear();
this.lines.addAll(lines);
}
public boolean isSearchMatch() { return searchMatch; }
public void setSearchMatch(boolean searchMatch) { this.searchMatch = searchMatch; }
}
@@ -0,0 +1,60 @@
package me.retucio.sputnik.macro;
import me.retucio.sputnik.config.ConfigManager;
import net.minecraft.client.Minecraft;
import org.lwjgl.glfw.GLFW;
import java.util.ArrayList;
import java.util.List;
public class MacroManager {
public static MacroManager INSTANCE;
private final List<Macro> macros = new ArrayList<>();
public boolean add(Macro macro) {
if (getByName(macro.getName()) != null) return false;
macros.add(macro);
save();
return true;
}
public void remove(Macro macro) {
macros.remove(macro);
save();
}
public Macro getByName(String name) {
for (Macro macro : macros) {
if (macro.getName().equalsIgnoreCase(name)) return macro;
}
return null;
}
public boolean onKeyPress(int key) {
if (key == GLFW.GLFW_KEY_UNKNOWN || Minecraft.getInstance().gui.screen() != null) return false;
boolean ran = false;
for (Macro macro : new ArrayList<>(macros)) {
if (macro.getKey() != key) continue;
macro.run();
ran = true;
}
return ran;
}
public void setMacros(List<Macro> macros) {
this.macros.clear();
this.macros.addAll(macros);
}
public void save() {
if (ConfigManager.INSTANCE != null) ConfigManager.INSTANCE.setMacros(macros);
}
public List<Macro> getMacros() {
return macros;
}
}
@@ -3,7 +3,7 @@ package me.retucio.sputnik.mixin.mixins.entity;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.render.EntityESP;
import me.retucio.sputnik.module.modules.render.EntityEsp;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
@@ -17,21 +17,21 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class EntityRendererMixin {
@Unique
private EntityESP esp;
private EntityEsp esp;
@Inject(method = "<init>", at = @At("TAIL"))
private void getModules(CallbackInfo ci) {
esp = ModuleManager.INSTANCE.getModuleByClass(EntityESP.class);
esp = ModuleManager.INSTANCE.getModuleByClass(EntityEsp.class);
}
@ModifyExpressionValue(method = "extractRenderState", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;shouldEntityAppearGlowing(Lnet/minecraft/world/entity/Entity;)Z"))
private boolean renderGlowESP(boolean original, @Local(argsOnly = true) Entity entity) {
return (esp.isEnabled() && esp.mode.is(EntityESP.ESPMode.GLOW) && esp.entities.isEnabled(entity.getType())) || original;
return (esp.isEnabled() && esp.mode.is(EntityEsp.ESPMode.GLOW) && esp.entities.isEnabled(entity.getType())) || original;
}
@ModifyExpressionValue(method = "extractRenderState", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;getTeamColor()I"))
private int setGlowESPColor(int original, @Local(argsOnly = true) Entity entity) {
if (esp.isEnabled() && esp.mode.is(EntityESP.ESPMode.GLOW) && esp.entities.isEnabled(entity.getType())) return esp.glowColor.getRGB();
if (esp.isEnabled() && esp.mode.is(EntityEsp.ESPMode.GLOW) && esp.entities.isEnabled(entity.getType())) return esp.glowColor.getRGB();
return original;
}
}
@@ -8,6 +8,7 @@ import me.retucio.sputnik.module.modules.movement.Velocity;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
@@ -61,8 +62,8 @@ public abstract class LivingEntityMixin {
if (step.isEnabled()) cir.setReturnValue(step.height.getFloatValue());
}
@Inject(method = "knockback", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;setDeltaMovement(DDD)V"), cancellable = true)
private void onTakeKb(double strength, double x, double z, CallbackInfo ci, @Local(name = "deltaMovement") Vec3 dm, @Local(name = "deltaVector") Vec3 dv) {
@Inject(method = "knockback(DDDLnet/minecraft/world/damagesource/DamageSource;FZ)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;setDeltaMovement(DDD)V"), cancellable = true)
private void onTakeKb(double strength, double x, double z, DamageSource source, float damage, boolean comesFromEffect, CallbackInfo ci, @Local(name = "deltaMovement") Vec3 dm, @Local(name = "deltaVector") Vec3 dv) {
Velocity velocity = ModuleManager.INSTANCE.getModuleByClass(Velocity.class);
if (!velocity.isEnabled() || velocity.hits.getValue()) return;
boolean onGround = ((LivingEntity) (Object) this).onGround();
@@ -83,7 +83,7 @@ public abstract class ChatComponentMixin implements IChatComponent {
@ModifyExpressionValue(method = "addMessageToDisplayQueue", at = @At(value = "NEW", target = "(Lnet/minecraft/client/multiplayer/chat/GuiMessage;Lnet/minecraft/util/FormattedCharSequence;Z)Lnet/minecraft/client/multiplayer/chat/GuiMessage$Line;"))
private GuiMessage.Line onAddMessage_modifyChatHudLineVisible(GuiMessage.Line line, @Local(name = "i") int i) {
IChatListener iListener = (IChatListener) Sputnik.mc.getChatListener();
IChatListener iListener = (IChatListener) Sputnik.mc.gui.chatListener();
IGuiMessageLine iLine = (IGuiMessageLine) (Object) line;
iLine.sputnik$setSender(iListener.sputnik$getSender());
@@ -94,7 +94,7 @@ public abstract class ChatComponentMixin implements IChatComponent {
@ModifyExpressionValue(method = "addMessage", at = @At(value = "NEW", target = "(ILnet/minecraft/network/chat/Component;Lnet/minecraft/network/chat/MessageSignature;Lnet/minecraft/client/multiplayer/chat/GuiMessageSource;Lnet/minecraft/client/multiplayer/chat/GuiMessageTag;)Lnet/minecraft/client/multiplayer/chat/GuiMessage;"))
private GuiMessage onAddMessage_modifyChatHudLine(GuiMessage line) {
IChatListener iListener = (IChatListener) Sputnik.mc.getChatListener();
IChatListener iListener = (IChatListener) Sputnik.mc.gui.chatListener();
((IGuiMessage) (Object) line).sputnik$setSender(iListener.sputnik$getSender());
return line;
}
@@ -1,107 +1,21 @@
package me.retucio.sputnik.mixin.mixins.hud;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.event.render.Render2DEvent;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.camera.CrosshairPlus;
import me.retucio.sputnik.module.modules.misc.ChatPlus;
import me.retucio.sputnik.module.modules.camera.Freecam;
import me.retucio.sputnik.module.modules.render.NoRender;
import me.retucio.sputnik.ui.hud.HudRenderer;
import net.minecraft.client.DeltaTracker;
import me.retucio.sputnik.event.interact.OpenScreenEvent;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.resources.Identifier;
import net.minecraft.client.gui.screens.Screen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArgs;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.invoke.arg.Args;
// lo del hud se mudó a HudMixin; Gui ya sólo lleva las pantallas
@Mixin(Gui.class)
public abstract class GuiMixin {
@Unique
NoRender noRender;
@Inject(method = "<init>", at = @At("TAIL"))
private void getModules(CallbackInfo ci) {
noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class);
}
@Inject(method = "extractRenderState", at = @At("TAIL"))
private void onRenderHud(GuiGraphicsExtractor gui, DeltaTracker dt, CallbackInfo ci) {
Sputnik.EVENT_BUS.post(new Render2DEvent(gui, dt));
}
// norender
@Inject(method = "extractScoreboardSidebar", at = @At("HEAD"), cancellable = true)
private void noRenderScoreboard(CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.scoreboard.getValue()) ci.cancel();
}
@Inject(method = "extractTitle", at = @At("HEAD"), cancellable = true)
private void noRenderTitles(GuiGraphicsExtractor graphics, DeltaTracker deltaTracker, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.titles.getValue()) ci.cancel();
}
@Inject(method = "extractConfusionOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderNausea(GuiGraphicsExtractor graphics, float strength, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.nauseaEffect.getValue()) ci.cancel();
}
@Inject(method = "extractSpyglassOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderSpyglass(GuiGraphicsExtractor graphics, float scale, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.spyglassOverlay.getValue()) ci.cancel();
}
@Inject(method = "extractPortalOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderPortal(GuiGraphicsExtractor graphics, float alpha, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.portalOverlay.getValue()) ci.cancel();
}
@Inject(method = "extractTextureOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderPumpkinBlur(GuiGraphicsExtractor graphics, Identifier texture, float alpha, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.pumpkinOverlay.getValue() && texture.getPath().equals("textures/misc/pumpkinblur.png")) ci.cancel();
}
// otros
@Inject(method = "extractRenderState", at = @At("RETURN"))
private void renderHUD(GuiGraphicsExtractor gui, DeltaTracker dt, CallbackInfo ci) {
HudRenderer.INSTANCE.render(gui, dt);
}
@Redirect(method = "extractCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphicsExtractor;blitSprite(Lcom/mojang/blaze3d/pipeline/RenderPipeline;Lnet/minecraft/resources/Identifier;IIII)V", ordinal = 0))
private void onRenderCrosshair(GuiGraphicsExtractor gui, RenderPipeline pipeline, Identifier id, int x, int y, int width, int height) {
// dangerous abbreviation
CrosshairPlus cp = ModuleManager.INSTANCE.getModuleByClass(CrosshairPlus.class);
int color = cp.isEnabled() ? cp.color.getRGB() : -1;
int w = cp.isEnabled() ? cp.width.getIntValue() : width;
int h = cp.isEnabled() ? cp.height.getIntValue() : height;
gui.blitSprite(pipeline, id, (gui.guiWidth() - w) / 2, (gui.guiHeight() - h) / 2, w, h, color);
}
@ModifyExpressionValue(method = "extractCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/CameraType;isFirstPerson()Z"))
private boolean alwaysRenderCrosshairInFreecam(boolean firstPerson) {
return ModuleManager.INSTANCE.getModuleByClass(Freecam.class).isEnabled() || firstPerson;
}
@Inject(method = "onDisconnected", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/components/ChatComponent;clearMessages(Z)V"), cancellable = true)
private void onClear(CallbackInfo ci) {
ChatPlus chatPlus = ModuleManager.INSTANCE.getModuleByClass(ChatPlus.class);
if (chatPlus.isEnabled() && chatPlus.keepHistory.getValue()) ci.cancel();
@Inject(method = "setScreen", at = @At("HEAD"), cancellable = true)
private void onOpenScreen(Screen screen, CallbackInfo ci) {
OpenScreenEvent event = Sputnik.EVENT_BUS.post(new OpenScreenEvent(screen));
if (event.isCancelled()) ci.cancel();
}
}
@@ -0,0 +1,104 @@
package me.retucio.sputnik.mixin.mixins.hud;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.event.render.Render2DEvent;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.camera.CrosshairPlus;
import me.retucio.sputnik.module.modules.misc.ChatPlus;
import me.retucio.sputnik.module.modules.camera.Freecam;
import me.retucio.sputnik.module.modules.render.NoRender;
import me.retucio.sputnik.ui.hud.HudRenderer;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.gui.Hud;
import net.minecraft.resources.Identifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
// 26.2 partió Gui en Gui (pantallas) y Hud (el hud en sí); todo esto vivía en GuiMixin
@Mixin(Hud.class)
public abstract class HudMixin {
@Unique
NoRender noRender;
@Inject(method = "<init>", at = @At("TAIL"))
private void getModules(CallbackInfo ci) {
noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class);
}
@Inject(method = "extractRenderState", at = @At("TAIL"))
private void onRenderHud(GuiGraphicsExtractor gui, DeltaTracker dt, CallbackInfo ci) {
Sputnik.EVENT_BUS.post(new Render2DEvent(gui, dt));
}
// norender
@Inject(method = "extractScoreboardSidebar", at = @At("HEAD"), cancellable = true)
private void noRenderScoreboard(CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.scoreboard.getValue()) ci.cancel();
}
@Inject(method = "extractTitle", at = @At("HEAD"), cancellable = true)
private void noRenderTitles(GuiGraphicsExtractor graphics, DeltaTracker deltaTracker, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.titles.getValue()) ci.cancel();
}
@Inject(method = "extractConfusionOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderNausea(GuiGraphicsExtractor graphics, float strength, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.nauseaEffect.getValue()) ci.cancel();
}
@Inject(method = "extractSpyglassOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderSpyglass(GuiGraphicsExtractor graphics, float scale, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.spyglassOverlay.getValue()) ci.cancel();
}
@Inject(method = "extractPortalOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderPortal(GuiGraphicsExtractor graphics, float alpha, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.portalOverlay.getValue()) ci.cancel();
}
@Inject(method = "extractTextureOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderPumpkinBlur(GuiGraphicsExtractor graphics, Identifier texture, float alpha, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.pumpkinOverlay.getValue() && texture.getPath().equals("textures/misc/pumpkinblur.png")) ci.cancel();
}
// otros
@Inject(method = "extractRenderState", at = @At("RETURN"))
private void renderHUD(GuiGraphicsExtractor gui, DeltaTracker dt, CallbackInfo ci) {
HudRenderer.INSTANCE.render(gui, dt);
}
@Redirect(method = "extractCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphicsExtractor;blitSprite(Lcom/mojang/blaze3d/pipeline/RenderPipeline;Lnet/minecraft/resources/Identifier;IIII)V", ordinal = 0))
private void onRenderCrosshair(GuiGraphicsExtractor gui, RenderPipeline pipeline, Identifier id, int x, int y, int width, int height) {
// dangerous abbreviation
CrosshairPlus cp = ModuleManager.INSTANCE.getModuleByClass(CrosshairPlus.class);
int color = cp.isEnabled() ? cp.color.getRGB() : -1;
int w = cp.isEnabled() ? cp.width.getIntValue() : width;
int h = cp.isEnabled() ? cp.height.getIntValue() : height;
gui.blitSprite(pipeline, id, (gui.guiWidth() - w) / 2, (gui.guiHeight() - h) / 2, w, h, color);
}
@ModifyExpressionValue(method = "extractCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/CameraType;isFirstPerson()Z"))
private boolean alwaysRenderCrosshairInFreecam(boolean firstPerson) {
return ModuleManager.INSTANCE.getModuleByClass(Freecam.class).isEnabled() || firstPerson;
}
@Inject(method = "onDisconnected", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/components/ChatComponent;clearMessages(Z)V"), cancellable = true)
private void onClear(CallbackInfo ci) {
ChatPlus chatPlus = ModuleManager.INSTANCE.getModuleByClass(ChatPlus.class);
if (chatPlus.isEnabled() && chatPlus.keepHistory.getValue()) ci.cancel();
}
}
@@ -5,6 +5,7 @@ import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.misc.ScreenshotPlus;
import net.minecraft.client.Minecraft;
import net.minecraft.client.Screenshot;
import net.minecraft.network.chat.Component;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
@@ -19,14 +20,14 @@ import java.util.function.Consumer;
@Mixin(Screenshot.class)
public abstract class ScreenshotMixin {
@Inject(method = "lambda$grab$1", at = @At(value = "INVOKE", target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V"), cancellable = true)
private static void sendActionsMessage(NativeImage image, File file, Consumer<?> callback, CallbackInfo ci) {
@Inject(method = "lambda$grab$3", at = @At(value = "INVOKE", target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V"), cancellable = true)
private static void sendActionsMessage(NativeImage image, File file, Consumer<Component> callback, CallbackInfo ci) {
ScreenshotPlus screenshotPlus = getScreenshotPlus();
if (screenshotPlus.isEnabled()) ci.cancel();
}
@Inject(method = "lambda$grab$1", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/NativeImage;writeToFile(Ljava/io/File;)V"), cancellable = true)
private static void interceptScreenshotFile(NativeImage image, File file, Consumer<?> callback, CallbackInfo ci) throws IOException {
@Inject(method = "lambda$grab$3", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/NativeImage;writeToFile(Ljava/io/File;)V"), cancellable = true)
private static void interceptScreenshotFile(NativeImage image, File file, Consumer<Component> callback, CallbackInfo ci) throws IOException {
ScreenshotPlus screenshotPlus = getScreenshotPlus();
// aunque el módulo esté apagado, pasar las referencias para poder usar los comandos para las capturas
@@ -27,13 +27,13 @@ public abstract class ItemMixin {
if (stack.getItem() instanceof BlockItem blockItem && blockItem.getBlock() instanceof ShulkerBoxBlock) {
if (ShulkerPeek.isShulkerEmpty(stack)) {
builder.accept(Component.literal("vacío").withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC));
builder.accept(Component.translatable("sputnik.module.shulkerpeek.empty")
.withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC));
} else {
builder.accept(
Component.literal("mantén ")
.append(Component.literal(shulkerPeek.previewKey.getKeyName())
.withStyle(ChatFormatting.GOLD, ChatFormatting.BOLD))
.append(" para previsualizar")
Component.translatable("sputnik.module.shulkerpeek.holdtopreview",
Component.literal(shulkerPeek.previewKey.getKeyName())
.withStyle(ChatFormatting.GOLD, ChatFormatting.BOLD))
.withStyle(ChatFormatting.GRAY)
);
}
@@ -38,9 +38,6 @@ public abstract class MinecraftMixin {
// eventos
@Shadow @Nullable
public Screen screen;
@Shadow
public abstract @Nullable Entity getCameraEntity();
@@ -63,12 +60,6 @@ public abstract class MinecraftMixin {
Sputnik.EVENT_BUS.post(new ShutdownEvent());
}
@Inject(method = "setScreen", at = @At("HEAD"), cancellable = true)
private void onOpenScreen(Screen screen, CallbackInfo ci) {
OpenScreenEvent event = Sputnik.EVENT_BUS.post(new OpenScreenEvent(screen));
if (event.isCancelled()) ci.cancel();
}
@Inject(method = "setLevel", at = @At("HEAD"), cancellable = true)
private void onJoinWorld(ClientLevel level, CallbackInfo ci) {
JoinWorldEvent event = Sputnik.EVENT_BUS.post(new JoinWorldEvent(level));
@@ -46,7 +46,7 @@ public abstract class ClientCommonPacketListenerImplMixin {
final Screen screen = cir.getReturnValue();
((IConfirmScreen) screen).sputnik$setBypassAction(() -> {
minecraft.setScreen(((PackConfirmScreenAccessor) screen).getParentScreen());
minecraft.gui.setScreen(((PackConfirmScreenAccessor) screen).getParentScreen());
if (serverData != null) {
serverData.setResourcePackStatus(bypassPack.getStatus());
minecraft.getDownloadedPackSource().allowServerPacks();
@@ -48,7 +48,7 @@ public abstract class ClientPacketListenerMixin {
ChatUtil.error(e.getMessage());
}
mc.gui.getChat().addRecentChat(content);
mc.gui.hud.getChat().addRecentChat(content);
ci.cancel();
}
@@ -0,0 +1,27 @@
package me.retucio.sputnik.mixin.mixins.player;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.event.interact.UpdateInventoryEvent;
import net.minecraft.core.NonNullList;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Final;
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.CallbackInfo;
@Mixin(Inventory.class)
public abstract class InventoryMixin {
@Shadow @Final
private NonNullList<ItemStack> items;
@Inject(method = "setChanged", at = @At("HEAD"))
private void onChange(CallbackInfo ci) {
Sputnik.EVENT_BUS.post(new UpdateInventoryEvent(items));
}
}
@@ -5,14 +5,12 @@ import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import com.llamalad7.mixinextras.sugar.Local;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.world.NoMiningInterruptions;
import me.retucio.sputnik.module.modules.inventory.PortalGUI;
import me.retucio.sputnik.module.modules.inventory.PortalGui;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@@ -28,9 +26,9 @@ public abstract class LocalPlayerMixin {
throw new UnsupportedOperationException("Implemented via mixin");
}
@ModifyExpressionValue(method = "handlePortalTransitionEffect", at = @At(value = "FIELD", target = "Lnet/minecraft/client/Minecraft;screen:Lnet/minecraft/client/gui/screens/Screen;", opcode = Opcodes.GETFIELD))
@ModifyExpressionValue(method = "handlePortalTransitionEffect", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/Gui;screen()Lnet/minecraft/client/gui/screens/Screen;"))
private Screen allowScreensInPortals(Screen original) {
if (ModuleManager.INSTANCE.getModuleByClass(PortalGUI.class).isEnabled()) return null;
if (ModuleManager.INSTANCE.getModuleByClass(PortalGui.class).isEnabled()) return null;
return original;
}
@@ -1,5 +1,6 @@
package me.retucio.sputnik.mixin.mixins.render;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.event.render.GetFOVEvent;
@@ -78,6 +79,13 @@ public abstract class CameraMixin {
if (freecam.isEnabled()) this.detached = true;
}
// el culling inteligente se desactiva estando en espectador dentro de un bloque sólido,
// así el terreno se sigue renderizando con la cámara metida en bloques
@ModifyExpressionValue(method = "extractRenderState", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;isSpectator()Z"))
private boolean noSmartCullFreecam(boolean original) {
return freecam.isEnabled() || original;
}
@ModifyArgs(method = "alignWithEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setPosition(DDD)V"))
private void onUpdateSetPosArgs(Args args) {
if (freecam.isEnabled()) {
@@ -6,15 +6,16 @@ import me.retucio.sputnik.event.render.GetFOVEvent;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.camera.Freecam;
import me.retucio.sputnik.module.modules.misc.UnfocusedCpu;
import me.retucio.sputnik.module.modules.render.Lsd;
import me.retucio.sputnik.module.modules.render.NoRender;
import me.retucio.sputnik.module.modules.camera.Zoom;
import com.mojang.blaze3d.pipeline.RenderTarget;
import com.mojang.blaze3d.resource.CrossFrameResourcePool;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.ItemInHandRenderer;
import net.minecraft.client.renderer.RenderBuffers;
import net.minecraft.client.renderer.state.level.CameraRenderState;
import net.minecraft.client.resources.model.ModelManager;
import org.joml.Matrix4fc;
@@ -39,15 +40,31 @@ public abstract class GameRendererMixin {
@Unique Zoom zoom;
@Unique Freecam freecam;
@Unique NoRender noRender;
@Unique Lsd lsd;
@Shadow @Final
private Minecraft minecraft;
@Shadow @Final
private RenderTarget mainRenderTarget;
@Shadow @Final
private CrossFrameResourcePool resourcePool;
@Inject(method = "<init>", at = @At("TAIL"))
private void getModules(Minecraft minecraft, ItemInHandRenderer itemInHandRenderer, RenderBuffers renderBuffers, ModelManager modelManager, CallbackInfo ci) {
private void getModules(Minecraft minecraft, ItemInHandRenderer itemInHandRenderer, ModelManager modelManager, CallbackInfo ci) {
zoom = ModuleManager.INSTANCE.getModuleByClass(Zoom.class);;
freecam = ModuleManager.INSTANCE.getModuleByClass(Freecam.class);
noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class);
lsd = ModuleManager.INSTANCE.getModuleByClass(Lsd.class);
}
// se aplica justo donde vanilla mete sus efectos de post (nausea, etc.): el mundo ya está
// en mainRenderTarget y el HUD todavía no, así que sólo distorsiona la escena 3D
@Inject(method = "render", at = @At(value = "INVOKE", shift = At.Shift.AFTER,
target = "Lnet/minecraft/client/renderer/LevelRenderer;doEntityOutline()V"))
private void applyLsd(DeltaTracker deltaTracker, boolean advanceGameTime, CallbackInfo ci) {
if (lsd.isEnabled()) lsd.apply(mainRenderTarget, resourcePool);
}
@Inject(method = "render", at = @At("HEAD"), cancellable = true)
@@ -15,21 +15,14 @@ public abstract class ItemFeatureRendererMixin {
// obtener módulo en cada redirect, ya que tiene un inicializador estático
@Redirect(method = "getFoilRenderType", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/rendertype/RenderTypes;glint()Lnet/minecraft/client/renderer/rendertype/RenderType;"))
@Redirect(method = "getFoilBuffer", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/rendertype/RenderTypes;glint()Lnet/minecraft/client/renderer/rendertype/RenderType;"))
private static RenderType redirectGlint() {
GlintPlus glintPlus = ModuleManager.INSTANCE.getModuleByClass(GlintPlus.class);
if (glintPlus == null) return RenderTypes.glint();
return glintPlus.getGlint();
}
@Redirect(method = "getFoilRenderType", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/rendertype/RenderTypes;entityGlint()Lnet/minecraft/client/renderer/rendertype/RenderType;"))
private static RenderType redirectEntityGlint() {
GlintPlus glintPlus = ModuleManager.INSTANCE.getModuleByClass(GlintPlus.class);
if (glintPlus == null) return RenderTypes.entityGlint();
return glintPlus.getEntityGlint();
}
@Redirect(method = "getFoilRenderType", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/rendertype/RenderTypes;glintTranslucent()Lnet/minecraft/client/renderer/rendertype/RenderType;"))
@Redirect(method = "getFoilBuffer", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/rendertype/RenderTypes;glintTranslucent()Lnet/minecraft/client/renderer/rendertype/RenderType;"))
private static RenderType redirectGlintTranslucent() {
GlintPlus glintPlus = ModuleManager.INSTANCE.getModuleByClass(GlintPlus.class);
if (glintPlus == null) return RenderTypes.glintTranslucent();
@@ -49,12 +49,12 @@ public abstract class ItemInHandRendererMixin {
handView = ModuleManager.INSTANCE.getModuleByClass(HandView.class);
}
@Inject(method = "renderArmWithItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemInHandRenderer;renderItem(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemDisplayContext;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;I)V", shift = At.Shift.BEFORE))
@Inject(method = "submitArmWithItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemInHandRenderer;renderItem(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemDisplayContext;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;I)V", shift = At.Shift.BEFORE))
private void onRenderItem(AbstractClientPlayer player, float frameInterp, float xRot, InteractionHand hand, float attack, ItemStack itemStack, float inverseArmHeight, PoseStack matrices, SubmitNodeCollector submitNodeCollector, int lightCoords, CallbackInfo ci) {
EVENT_BUS.post(new RenderHeldItemEvent(matrices, hand));
}
@Inject(method = "renderArmWithItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemInHandRenderer;renderPlayerArm(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;IFFLnet/minecraft/world/entity/HumanoidArm;)V"))
@Inject(method = "submitArmWithItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemInHandRenderer;renderPlayerArm(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;IFFLnet/minecraft/world/entity/HumanoidArm;)V"))
private void onRenderArm(AbstractClientPlayer player, float frameInterp, float xRot, InteractionHand hand, float attack, ItemStack itemStack, float inverseArmHeight, PoseStack matrices, SubmitNodeCollector submitNodeCollector, int lightCoords, CallbackInfo ci) {
EVENT_BUS.post(new RenderArmEvent(matrices, hand));
}
@@ -10,10 +10,10 @@ import me.retucio.sputnik.event.render.Render3DEvent;
import me.retucio.sputnik.event.render.RenderBlockOutlineEvent;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.camera.Freecam;
import me.retucio.sputnik.util.render.SputnikBuffers;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.chunk.ChunkSectionsToRender;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.state.level.CameraRenderState;
import net.minecraft.client.renderer.state.level.LevelRenderState;
import net.minecraft.util.profiling.ProfilerFiller;
@@ -31,14 +31,8 @@ import static me.retucio.sputnik.Sputnik.mc;
@Mixin(LevelRenderer.class)
public abstract class LevelRendererMixin {
// renderLevel()??
@ModifyArg(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;cullTerrain(Lnet/minecraft/client/Camera;Lnet/minecraft/client/renderer/culling/Frustum;Z)V"), index = 2)
private boolean renderSetupTerrainModifyArg(boolean spectator) {
return ModuleManager.INSTANCE.getModuleByClass(Freecam.class).isEnabled() || spectator;
}
@Inject(method = "renderLevel", at = @At("RETURN"))
private void render(GraphicsResourceAllocator resourceAllocator, DeltaTracker dt, boolean renderOutline, CameraRenderState cameraState, Matrix4fc modelViewMatrix, GpuBufferSlice terrainFog, Vector4f fogColor, boolean shouldRenderSky, ChunkSectionsToRender chunkSectionsToRender, CallbackInfo ci, @Local(name = "profiler") ProfilerFiller profiler) {
@Inject(method = "render", at = @At("RETURN"))
private void render(GraphicsResourceAllocator resourceAllocator, DeltaTracker dt, boolean renderOutline, CameraRenderState cameraState, Matrix4fc modelViewMatrix, GpuBufferSlice terrainFog, Vector4f fogColor, boolean shouldRenderSky, CallbackInfo ci, @Local(name = "profiler") ProfilerFiller profiler) {
if (mc == null || mc.level == null) return;
PoseStack matrices = new PoseStack();
@@ -49,13 +43,14 @@ public abstract class LevelRendererMixin {
profiler.push(Sputnik.MOD_ID + "-3d");
EVENT_BUS.post(new Render3DEvent(matrices, dt, cameraState));
SputnikBuffers.flush(); // antes de que empiece el hud
profiler.pop();
matrices.popPose();
}
@Inject(method = "renderBlockOutline", at = @At("HEAD"), cancellable = true)
private void noRenderBlockOutlinesFreecam(MultiBufferSource.BufferSource bufferSource, PoseStack poseStack, boolean onlyTranslucentBlocks, LevelRenderState levelRenderState, CallbackInfo ci) {
@Inject(method = "submitBlockOutline", at = @At("HEAD"), cancellable = true)
private void noRenderBlockOutlinesFreecam(PoseStack poseStack, SubmitNodeCollector collector, LevelRenderState levelRenderState, CallbackInfo ci) {
Freecam freecam = ModuleManager.INSTANCE.getModuleByClass(Freecam.class);
if (freecam.isEnabled() && !freecam.blockOutlines.getValue()) ci.cancel();
}
@@ -68,13 +63,13 @@ public abstract class LevelRendererMixin {
// }
// puto mojang con sus nombres confusos
@ModifyArgs(method = "renderBlockOutline", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;renderHitOutline(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;DDDLnet/minecraft/client/renderer/state/level/BlockOutlineRenderState;IF)V"))
@ModifyArgs(method = "submitBlockOutline", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;submitHitOutline(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;Lnet/minecraft/client/renderer/rendertype/RenderType;Lnet/minecraft/client/renderer/state/level/BlockOutlineRenderState;IFZ)V"))
private void modifyBlockOutline(Args args) {
// SIX SEVEN!!! (me pegan en casa)
RenderBlockOutlineEvent event = EVENT_BUS.post(new RenderBlockOutlineEvent(args.get(6), args.get(7)));
if (event.getColor() != (int) args.get(6))
args.set(6, event.getColor());
if (event.getLineWidth() != (float) args.get(7))
args.set(7, event.getLineWidth());
// ahora cuatro cinco... (rip 67)
RenderBlockOutlineEvent event = EVENT_BUS.post(new RenderBlockOutlineEvent(args.get(4), args.get(5)));
if (event.getColor() != (int) args.get(4))
args.set(4, event.getColor());
if (event.getLineWidth() != (float) args.get(5))
args.set(5, event.getLineWidth());
}
}
@@ -31,7 +31,7 @@ public abstract class LightmapMixin {
profiler.push("lightmap");
RenderSystem.getDevice().createCommandEncoder()
.clearColorTexture(texture, ARGB.color(255, 255, 255, 255));
.clearColorTexture(texture, ARGB.vector4fFromARGB32(ARGB.color(255, 255, 255, 255)));
profiler.pop();
ci.cancel();
@@ -9,6 +9,7 @@ import me.retucio.sputnik.module.modules.render.Nametags;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.EntityTypes;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import org.spongepowered.asm.mixin.Mixin;
@@ -50,7 +51,7 @@ public abstract class LivingEntityRendererMixin<T extends LivingEntity> {
@ModifyReturnValue(method = "shouldShowName(Lnet/minecraft/world/entity/LivingEntity;D)Z", at = @At("RETURN"))
private boolean shouldRenderPlayerNametag(boolean original, @Local(argsOnly = true) T livingEntity) {
if (nametags.isEnabled() && livingEntity instanceof Player)
return nametags.entities.isEnabled(EntityType.PLAYER) && original;
return nametags.entities.isEnabled(EntityTypes.PLAYER) && original;
return original;
}
@@ -1,25 +1,18 @@
package me.retucio.sputnik.mixin.mixins.render;
import com.mojang.blaze3d.vertex.ByteBufferBuilder;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import me.retucio.sputnik.util.render.GlintRenderType;
import net.minecraft.client.renderer.MultiBufferSource;
import me.retucio.sputnik.util.render.SputnikBuffers;
import net.minecraft.client.renderer.RenderBuffers;
import net.minecraft.client.renderer.rendertype.RenderType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.SequencedMap;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(RenderBuffers.class)
public abstract class RenderBuffersMixin {
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/MultiBufferSource;immediateWithBuffers(Ljava/util/SequencedMap;Lcom/mojang/blaze3d/vertex/ByteBufferBuilder;)Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;", ordinal = 0))
private MultiBufferSource.BufferSource injectGlintBuffers(SequencedMap<RenderType, ByteBufferBuilder> fixedBuffers, ByteBufferBuilder sharedBuffer) {
if (fixedBuffers instanceof Object2ObjectLinkedOpenHashMap<RenderType, ByteBufferBuilder> map) {
GlintRenderType.addGlintTypes(map);
}
return MultiBufferSource.immediateWithBuffers(fixedBuffers, sharedBuffer);
// devolvemos nuestros buffers al pool cuando el juego hace lo propio
@Inject(method = "endFrame", at = @At("TAIL"))
private void endSputnikFrame(CallbackInfo ci) {
SputnikBuffers.endFrame();
}
}
}
@@ -4,8 +4,8 @@ import com.mojang.blaze3d.vertex.PoseStack;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.render.NoRender;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.ScreenEffectRenderer;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@@ -25,13 +25,13 @@ public abstract class ScreenEffectRendererMixin {
noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class);
}
@Inject(method = "renderFire", at = @At("HEAD"), cancellable = true)
private static void noRenderFireOverlay(PoseStack matrices, MultiBufferSource bufferSource, TextureAtlasSprite sprite, CallbackInfo ci) {
@Inject(method = "submitFire", at = @At("HEAD"), cancellable = true)
private static void noRenderFireOverlay(PoseStack matrices, SubmitNodeCollector collector, TextureAtlasSprite sprite, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.fireOverlay.getValue()) ci.cancel();
}
@Inject(method = "renderWater", at = @At("HEAD"), cancellable = true)
private static void noRenderUnderwaterOverlay(Minecraft minecraft, PoseStack matrices, MultiBufferSource bufferSource, CallbackInfo ci) {
@Inject(method = "submitWater", at = @At("HEAD"), cancellable = true)
private static void noRenderUnderwaterOverlay(Minecraft minecraft, PoseStack matrices, SubmitNodeCollector collector, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.fluidOverlay.getValue()) ci.cancel();
}
}
@@ -1,6 +1,7 @@
package me.retucio.sputnik.mixin.mixins.render;
import me.retucio.sputnik.util.render.Pipelines;
import me.retucio.sputnik.util.render.PostShaders;
import net.minecraft.client.renderer.ShaderManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -13,5 +14,6 @@ public abstract class ShaderManagerMixin {
@Inject(method = "apply(Lnet/minecraft/client/renderer/ShaderManager$Configs;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V", at = @At("TAIL"))
private void onReload(CallbackInfo ci) {
Pipelines.init();
PostShaders.reload();
}
}
@@ -2,11 +2,8 @@ package me.retucio.sputnik.mixin.mixins.render;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.render.NoRender;
import net.minecraft.client.Camera;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.WeatherEffectRenderer;
import net.minecraft.client.renderer.state.level.WeatherRenderState;
import net.minecraft.server.level.ParticleStatus;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@@ -26,11 +23,6 @@ public abstract class WeatherEffectRendererMixin {
noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class);
}
@Inject(method = "tickRainParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;addParticle(Lnet/minecraft/core/particles/ParticleOptions;DDDDDD)V"), cancellable = true)
private void noRainParticles(ClientLevel level, Camera camera, int ticks, ParticleStatus particleStatus, int weatherRadius, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.rain.getValue()) ci.cancel();
}
@Inject(method = "render", at = @At("HEAD"))
private void noRenderRainAndSnow(Vec3 cameraPos, WeatherRenderState renderState, CallbackInfo ci) {
if (noRender.isEnabled()) {
@@ -33,14 +33,14 @@ public abstract class AbstractContainerScreenMixin {
int by = bgTop - bh - 5;
if (burglar.showStealButton.getValue()) {
Button stealButton = Button.builder(Component.literal("robar"), bnutton -> {
Button stealButton = Button.builder(Component.translatable("sputnik.module.burglar.steal"), bnutton -> {
burglar.steal(screen);
}).bounds(bx, by, bw, bh).build();
screen.addRenderableWidget(stealButton);
}
if (burglar.showDumpButton.getValue()) {
Button dumpButton = Button.builder(Component.literal("dejar"), button -> {
Button dumpButton = Button.builder(Component.translatable("sputnik.module.burglar.dump"), button -> {
burglar.dump(screen);
}).bounds(bx + bw + gap, by, bw, bh).build();
screen.addRenderableWidget(dumpButton);
@@ -27,7 +27,7 @@ public abstract class DisconnectedScreenMixin {
if (reconnect == null || !reconnect.isEnabled()) return;
layout.addChild(Button.builder(
Component.literal("reconectarse"),
Component.translatable("sputnik.module.reconnect.button"),
button -> reconnect.reconnect()
).build());
}
@@ -2,6 +2,8 @@ package me.retucio.sputnik.mixin.mixins.world;
import me.retucio.sputnik.event.network.AddEntityEvent;
import me.retucio.sputnik.event.network.RemoveEntityEvent;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.render.NoRender;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity;
import org.jetbrains.annotations.Nullable;
@@ -29,4 +31,11 @@ public abstract class ClientLevelMixin {
Entity entity = getEntity(entityId);
if (entity != null) EVENT_BUS.post(new RemoveEntityEvent(entity));
}
// las partículas de lluvia se generan aquí desde 26.2, antes en WeatherEffectRenderer
@Inject(method = "tickWeatherEffects", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;addParticle(Lnet/minecraft/core/particles/ParticleOptions;DDDDDD)V"), cancellable = true)
private void noRainParticles(CallbackInfo ci) {
NoRender noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class);
if (noRender.isEnabled() && !noRender.rain.getValue()) ci.cancel();
}
}
@@ -1,5 +1,7 @@
package me.retucio.sputnik.module;
import me.retucio.sputnik.util.I18nUtil;
public enum Category {
COMBAT("combate"),
@@ -19,5 +21,12 @@ public enum Category {
private final String name;
Category(String name) { this.name = name; }
// OJO: toString() es la clave de la config. (ConfigManager#applyExtendablePanel guarda los
// marcos por categoría). no se traduce; para mostrarla usar getDisplayName()
@Override public String toString() { return name; }
public String getDisplayName() {
return I18nUtil.orElse("sputnik.category." + I18nUtil.slug(name), name);
}
}
@@ -11,7 +11,9 @@ import me.retucio.sputnik.module.setting.settings.KeySetting;
import me.retucio.sputnik.module.setting.Setting;
import me.retucio.sputnik.ui.hud.HudRenderer;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.I18nUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import org.lwjgl.glfw.GLFW;
import java.lang.reflect.Method;
@@ -33,34 +35,36 @@ public class Module {
// ajustes que están en todos los módulos
// estos cuatro están en los 93 módulos, así que comparten clave en vez de derivar una por módulo
protected KeySetting bind = new KeySetting(
"tecla",
"tecla asignada al módulo, ESC para desactivar",
GLFW.GLFW_KEY_UNKNOWN
);
).translationKey("sputnik.setting.common.bind");
// dejar al usuario elegir si el módulo debería apagarse tras soltar su tecla asignada, o si la tecla debería alternar su estado
protected EnumSetting<KeyMode> keyMode = new EnumSetting<>(
protected EnumSetting<KeyMode> keyMode = new EnumSetting<KeyMode>(
"modo de tecla",
"cómo interpretar la tecla configurada",
KeyMode.class, KeyMode.TOGGLE
);
).translationKey("sputnik.setting.common.keymode");
protected BooleanSetting notify = new BooleanSetting(
"notificar",
"notificar al activar / desactivar",
true
);
).translationKey("sputnik.setting.common.notify");
protected EnumSetting<NotifyMode> notifyMode = new EnumSetting<>(
protected EnumSetting<NotifyMode> notifyMode = new EnumSetting<NotifyMode>(
"modo de notificación",
"cómo notificar cuando se activa / desactiva",
NotifyMode.class, NotifyMode.WIDGET
);
).translationKey("sputnik.setting.common.notifymode");
protected final List<SettingGroup> sgs = new ArrayList<>();
protected final SettingGroup sgModule = addSg(new SettingGroup("módulo", false));
protected final SettingGroup sgGeneral = addSg(new SettingGroup("general", true));
protected final SettingGroup sgModule = addSg(new SettingGroup("módulo", false, "sputnik.group.common.module"));
protected final SettingGroup sgGeneral = addSg(new SettingGroup("general", true, "sputnik.group.common.general"));
protected static final Minecraft mc = Minecraft.getInstance();
@@ -80,7 +84,7 @@ public class Module {
// puto marcos
if (ConfigManager.INSTANCE.getConfig().settings
.get(this.getName() + ":" + bind.getName()) == null)
.get(this.getName() + ":" + bind.getTrueName()) == null)
this.bind.setValue(key);
}
@@ -101,9 +105,10 @@ public class Module {
return sgs;
}
// por nombre interno, no por el traducido (igual que SettingGroup#getSetting)
public SettingGroup getSg(String name) {
for (SettingGroup sg : sgs) {
if (sg.getName().equals(name)) {
if (sg.getTrueName().equals(name)) {
return sg;
}
}
@@ -134,10 +139,7 @@ public class Module {
}
public void onEnable() {
if (notify.getValue()) {
if (notifyMode.is(NotifyMode.CHAT)) ChatUtil.info(getName() + " fue activado");
else HudRenderer.INSTANCE.pushNotification(getName() + " fue activado", getName() + " fue activado.");
}
notifyToggle(true);
for (Method method : getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(EventListener.class)) {
Sputnik.EVENT_BUS.subscribe(this);
@@ -147,13 +149,24 @@ public class Module {
}
public void onDisable() {
if (notifyMode.is(NotifyMode.CHAT)) ChatUtil.info(getName() + " fue desactivado");
else HudRenderer.INSTANCE.pushNotification(getName() + " fue desactivado", getName() + " fue desactivado.");
notifyToggle(false);
if (Sputnik.EVENT_BUS.isSubscribed(this)) {
Sputnik.EVENT_BUS.unsubscribe(this);
}
}
// onDisable no miraba notify.getValue(), así que avisaba aunque estuviera desactivado
private void notifyToggle(boolean on) {
if (!notify.getValue()) return;
if (ConfigManager.INSTANCE != null && ConfigManager.INSTANCE.isApplying()) return;
String key = on ? "sputnik.module.toggled.on" : "sputnik.module.toggled.off";
Component message = Component.translatable(key, getDisplayName());
if (notifyMode.is(NotifyMode.CHAT)) ChatUtil.info(message);
else HudRenderer.INSTANCE.pushNotification(message.getString(), message.getString());
}
// tick (ejecutado 20 veces por segundo)
public void onTick() {}
@@ -161,6 +174,8 @@ public class Module {
// getters y setters
// OJO: nombre interno. es la clave de la config. (moduleStates, getSettingKey, settingsPanels)
// y no se traduce nunca. para mostrarlo al usuario usar getDisplayName()
public String getName() {
return name;
}
@@ -168,6 +183,19 @@ public class Module {
this.name = name;
}
public String getTranslationKey() {
return "sputnik.module." + I18nUtil.slug(name);
}
// nombre visible, traducido
public String getDisplayName() {
return I18nUtil.orElse(getTranslationKey() + ".name", name);
}
public String getDisplayDescription() {
return I18nUtil.orElse(getTranslationKey() + ".desc", description);
}
public String getDescription() {
return description;
}
@@ -2,7 +2,7 @@ package me.retucio.sputnik.module;
import com.github.retucio.neutrino.EventListener;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.module.modules.client.DiscordRPC;
import me.retucio.sputnik.module.modules.client.DiscordRpc;
import me.retucio.sputnik.module.modules.client.Friends;
import me.retucio.sputnik.module.modules.client.Hud;
import me.retucio.sputnik.module.modules.camera.*;
@@ -12,6 +12,7 @@ import me.retucio.sputnik.module.modules.inventory.*;
import me.retucio.sputnik.module.modules.misc.*;
import me.retucio.sputnik.module.modules.movement.*;
import me.retucio.sputnik.module.modules.network.*;
import me.retucio.sputnik.module.modules.network.PacketLogger;
import me.retucio.sputnik.module.modules.player.*;
import me.retucio.sputnik.module.modules.render.*;
import me.retucio.sputnik.module.modules.render.CritsPlus;
@@ -48,7 +49,7 @@ public class ModuleManager {
addRender();
addWorld();
modules.sort(Comparator.comparing(module -> MiscUtil.removeAccentMarks(module.getName().toLowerCase())));
modules.sort(Comparator.comparing(module -> MiscUtil.removeAccentMarks(module.getDisplayName().toLowerCase())));
// registrar los "listeners" necesarios
for (Module module : getEnabledModules()) {
@@ -79,7 +80,7 @@ public class ModuleManager {
}
private void addClient() {
add(new DiscordRPC());
add(new DiscordRpc());
add(new Friends());
add(new Hud());
add(new Notifications());
@@ -89,6 +90,8 @@ public class ModuleManager {
add(new AttributeSwapper());
add(new ChestplateSwitch());
add(new CrystalAura());
add(new HoldAttack());
add(new KillAura());
add(new MaceKill());
add(new ProjectileTrajectories());
add(new SpearKill());
@@ -111,10 +114,10 @@ public class ModuleManager {
add(new CreativeInventoryHotbarKeybinds());
add(new InventoryPlus());
add(new Offhand());
add(new PortalGUI());
add(new PortalGui());
add(new Replenish());
add(new ShulkerPeek());
add(new UIMove());
add(new UiMove());
add(new XCarry());
}
@@ -127,6 +130,7 @@ public class ModuleManager {
add(new Headhitters());
add(new HitboxDesync());
add(new InfiniteElytra());
add(new ItemMagnet());
add(new Jesus());
add(new Parkour());
add(new ReverseStep());
@@ -141,7 +145,9 @@ public class ModuleManager {
private void addNetwork() {
add(new BungeecordSpoofer());
add(new LogoutSpots());
add(new PacketCanceller());
add(new PacketDelay());
add(new PacketLogger());
add(new PacketMine());
add(new Reconnect());
add(new RPackBypass());
@@ -150,6 +156,7 @@ public class ModuleManager {
private void addPlayer() {
add(new AirPlace());
add(new AntiHunger());
add(new AutoEat());
add(new AutoFish());
add(new Capes());
add(new FastUse());
@@ -159,17 +166,18 @@ public class ModuleManager {
}
private void addRender() {
add(new BlockESP());
add(new BlockEsp());
add(new BlockOutline());
add(new Breadcrumbs());
add(new BreakingProgress());
add(new Confetti());
add(new CritsPlus());
add(new DamageOverlay());
add(new EntityESP());
add(new EntityEsp());
add(new Fonts());
add(new GlintPlus());
add(new Hitboxes());
add(new Lsd());
add(new Nametags());
add(new NoRender());
add(new Particles());
@@ -190,8 +198,6 @@ public class ModuleManager {
}
// para obtener módulos más fácilmente (por nombre, clase, o la lista completa)
public List<Module> getModules() {
return modules;
@@ -205,17 +211,34 @@ public class ModuleManager {
return enabledModules;
}
// busca por nombre interno, tal cual se guarda en la config. antes quitaba las tildes al
// nombre del módulo pero no al argumento, así que "cámara libre" no encontraba nada
public Module getModuleByName(String name) {
for (Module module : modules)
if (MiscUtil.removeAccentMarks(module.getName()).equalsIgnoreCase(name))
for (Module module : modules) {
if (module.getName().equalsIgnoreCase(name)) {
return module;
}
}
return null;
}
// busca por nombre visible: lo que el usuario teclea en los comandos, sin tildes y con "_"
public Module getModuleByDisplayName(String name) {
String target = MiscUtil.removeAccentMarks(name).replace("_", " ");
for (Module module : modules) {
if (MiscUtil.removeAccentMarks(module.getDisplayName()).equalsIgnoreCase(target)) {
return module;
}
}
return null;
}
public <T extends Module> T getModuleByClass(Class<T> clazz) {
for (Module module : modules)
if (clazz.isInstance(module))
for (Module module : modules) {
if (clazz.isInstance(module)) {
return clazz.cast(module);
}
}
return null;
}
}
@@ -1,12 +1,10 @@
package me.retucio.sputnik.module.modules.camera;
import com.github.retucio.neutrino.EventListener;
import me.retucio.sputnik.event.network.PacketEvent;
import me.retucio.sputnik.module.Category;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.EnumSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import me.retucio.sputnik.util.RotationUtil;
/**
@@ -47,18 +45,7 @@ public class Autism extends Module {
if (direction.is(RotationDirection.COUNTERCLOCKWISE))
yaw -= interval.getFloatValue();
mc.getConnection().send(new ServerboundMovePlayerPacket.Rot(
yaw,
mc.player.getXRot(),
mc.player.onGround(),
mc.player.horizontalCollision
));
}
@EventListener
private void onPacketSend(PacketEvent.Send event) {
if (event.getPacket() instanceof ServerboundMovePlayerPacket.Rot packet
&& packet.getYRot(yaw) != yaw) event.cancel();
RotationUtil.requestYaw(RotationUtil.HIGHEST, yaw, RotationUtil.Mode.SILENT);
}
private enum RotationDirection {
@@ -25,6 +25,7 @@ import me.retucio.sputnik.util.MiscUtil;
import net.minecraft.client.CameraType;
import net.minecraft.client.input.MouseButtonEvent;
import net.minecraft.client.input.MouseButtonInfo;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.*;
import net.minecraft.util.Mth;
@@ -150,8 +151,8 @@ public class Freecam extends Module {
speed = speedSetting.getValue();
MiscUtil.copyVector(pos, mc.gameRenderer.getMainCamera().position());
MiscUtil.copyVector(prevPos, mc.gameRenderer.getMainCamera().position());
MiscUtil.copyVector(pos, mc.gameRenderer.mainCamera().position());
MiscUtil.copyVector(prevPos, mc.gameRenderer.mainCamera().position());
if (mc.options.getCameraType() == CameraType.THIRD_PERSON_FRONT) {
yaw += 180;
@@ -171,14 +172,19 @@ public class Freecam extends Module {
down = KeyUtil.isKeyDown(mc.options.keyShift);
unpress();
if (reloadChunks.getValue()) mc.levelRenderer.allChanged();
if (reloadChunks.getValue()) invalidateChunks();
super.onEnable();
}
private static void invalidateChunks() {
if (mc.level == null) return;
mc.levelExtractor.allChanged();
}
@Override
public void onDisable() {
if (reloadChunks.getValue()) mc.execute(mc.levelRenderer::allChanged);
if (reloadChunks.getValue()) invalidateChunks();
mc.options.setCameraType(perspective);
crouching = false;
@@ -265,7 +271,7 @@ public class Freecam extends Module {
@EventListener
private void onKey(KeyEvent event) {
if (KeyUtil.isKeyDown(GLFW.GLFW_KEY_F3)) return;
if (mc.screen != null) return;
if (mc.gui.screen() != null) return;
net.minecraft.client.input.KeyEvent key = new net.minecraft.client.input.KeyEvent(event.getKey(), event.getScancode(), 0);
@@ -298,7 +304,7 @@ public class Freecam extends Module {
@EventListener
private void onMouseClick(MouseClickEvent event) { // por si el restrasado del usuario usa el ratón para moverse
if (KeyUtil.isKeyDown(GLFW.GLFW_KEY_F3)) return;
if (mc.screen != null) return;
if (mc.gui.screen() != null) return;
MouseButtonEvent click = new MouseButtonEvent(0, 0, new MouseButtonInfo(event.getButton(), 0));
@@ -330,7 +336,7 @@ public class Freecam extends Module {
@EventListener
private void onMouseScroll(MouseScrollEvent event) {
if (scrollSens.getValue() > 0 && mc.screen == null && scrollKey.isDown()) {
if (scrollSens.getValue() > 0 && mc.gui.screen() == null && scrollKey.isDown()) {
speed += event.getVertical() / 4 * (scrollSens.getValue() * speed);
if (speed < 0.1) speed = 0.1;
event.cancel();
@@ -356,7 +362,7 @@ public class Freecam extends Module {
toggle();
} else if (event.getPacket() instanceof ClientboundSetHealthPacket packet) {
if (mc.player.getHealth() - packet.getHealth() > 0 && toggleOnDamage.getValue()) {
ChatUtil.info("cámara libre se ha desactivado porque has recibido daño");
ChatUtil.info(Component.translatable("sputnik.module.freecam.damagedisable"));
toggle();
}
}
@@ -60,7 +60,7 @@ public class PerspectivePlus extends Module {
@EventListener
private void onMouseScroll(MouseScrollEvent event) {
if (mc.options.getCameraType() == CameraType.FIRST_PERSON
|| mc.screen != null || scrollSens.getValue() <= 0) return;
|| mc.gui.screen() != null || scrollSens.getValue() <= 0) return;
if (scrollKey.isDown()) {
distance -= event.getVertical() / 4 * (scrollSens.getValue() * distance);
@@ -6,6 +6,7 @@ import me.retucio.sputnik.module.Category;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.BooleanSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting;
import me.retucio.sputnik.util.RotationUtil;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
/**
@@ -30,8 +31,7 @@ public class Rotations extends Module {
public void onTick() {
if (mc.player == null) return;
mc.player.setYRot(yaw.getFloatValue());
mc.player.setXRot(pitch.getFloatValue());
RotationUtil.request(RotationUtil.HIGHEST, yaw.getFloatValue(), pitch.getFloatValue(), RotationUtil.Mode.CLIENT);
if (serverSide.getValue())
mc.player.connection.send(
@@ -58,13 +58,24 @@ public class Zoom extends Module {
notify.setValue(false);
}
// lo que hacía el antiguo LevelRenderer.needsUpdate(), que ya no existe
private static void needsUpdate() {
mc.levelRenderer.sectionOcclusionGraph().invalidate();
mc.levelRenderer.cloudRenderer().markForRebuild();
}
// el Hud ya sólo se puede alternar, no asignar
private static void setHudHidden(boolean hidden) {
if (mc.gui.hud.isHidden() != hidden) mc.gui.hud.toggle();
}
@Override
public void onEnable() {
prevSmoothCam = mc.options.smoothCamera;
prevMouseSens = mc.options.sensitivity().get();
prevFov = mc.options.fov().get();
prevHUD = mc.options.hideGui;
mc.options.hideGui = !showHUD.getValue();
prevHUD = mc.gui.hud.isHidden();
setHudHidden(!showHUD.getValue());
value = defaultZoom.getValue();
@@ -75,9 +86,9 @@ public class Zoom extends Module {
public void onDisable() {
mc.options.smoothCamera = prevSmoothCam;
mc.options.sensitivity().set(prevMouseSens);
mc.options.hideGui = prevHUD;
setHudHidden(prevHUD);
mc.levelRenderer.needsUpdate();
needsUpdate();
super.onDisable();
}
@@ -105,7 +116,7 @@ public class Zoom extends Module {
private void onGetFov(GetFOVEvent event) {
event.setFov((float) (event.getFov() / value));
if (prevFov != event.getFov()) mc.levelRenderer.needsUpdate();
if (prevFov != event.getFov()) needsUpdate();
prevFov = event.getFov();
}
}
@@ -1,6 +1,5 @@
package me.retucio.sputnik.module.modules.client;
import dev.firstdark.rpc.DiscordRpc;
import dev.firstdark.rpc.enums.ActivityType;
import dev.firstdark.rpc.enums.ErrorCode;
import dev.firstdark.rpc.exceptions.UnsupportedOsType;
@@ -15,13 +14,14 @@ import me.retucio.sputnik.module.setting.SettingGroup;
import me.retucio.sputnik.module.setting.settings.EnumSetting;
import me.retucio.sputnik.module.setting.settings.StringSetting;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.network.chat.Component;
/**
* @author retucio
*/
public class DiscordRPC extends Module {
public class DiscordRpc extends Module {
private final SettingGroup sgImage = addSg(new SettingGroup("imágenes", false));
private final SettingGroup sgButton = addSg(new SettingGroup("botón", false));
@@ -102,7 +102,7 @@ public class DiscordRPC extends Module {
// crear el rpc
private boolean initialized;
DiscordRpc rpc = new DiscordRpc();
dev.firstdark.rpc.DiscordRpc rpc = new dev.firstdark.rpc.DiscordRpc();
RPCEventHandler handler = new RPCEventHandler() {
@Override
public void ready(User user) {
@@ -111,19 +111,19 @@ public class DiscordRPC extends Module {
@Override
public void disconnected(ErrorCode errorCode, String message) {
ChatUtil.warn("desconectado: " + errorCode + " - " + message);
ChatUtil.warn(Component.translatable("sputnik.module.discordrpc.disconnected", errorCode, message));
toggle();
}
@Override
public void errored(ErrorCode errorCode, String message) {
ChatUtil.error("error: " + errorCode + " - " + message);
ChatUtil.error(Component.translatable("sputnik.module.discordrpc.error", errorCode, message));
toggle();
}
};
public DiscordRPC() {
public DiscordRpc() {
super("discordRPC",
"personaliza la actividad de tu perfil en dishkor",
Category.CLIENT);
@@ -142,7 +142,7 @@ public class DiscordRPC extends Module {
public void onEnable() {
String token = appId.getValue();
if (token == null || token.trim().isEmpty()) {
ChatUtil.error("configura el token del bot primero");
ChatUtil.error(Component.translatable("sputnik.module.discordrpc.notoken"));
toggle();
return;
}
@@ -151,10 +151,10 @@ public class DiscordRPC extends Module {
rpc.init(token, handler, false);
initialized = true;
} catch (UnsupportedOsType e) {
ChatUtil.error("sistema operativo no soportado");
ChatUtil.error(Component.translatable("sputnik.module.discordrpc.unsupportedos"));
toggle();
} catch (Exception e) {
ChatUtil.error("error al inicializar rpc: " + e.getMessage());
ChatUtil.error(Component.translatable("sputnik.module.discordrpc.initerror", e.getMessage()));
toggle();
}
@@ -191,7 +191,7 @@ public class DiscordRPC extends Module {
try {
rpc.updatePresence(getPresence());
} catch (Exception e) {
ChatUtil.error("error al actualizar rpc: " + e.getMessage());
ChatUtil.error(Component.translatable("sputnik.module.discordrpc.updateerror", e.getMessage()));
}
}
}
@@ -215,7 +215,7 @@ public class DiscordRPC extends Module {
try {
timestamp = Long.parseLong(startTimestamp.getValue());
} catch (NumberFormatException e) {
ChatUtil.error("introduce un NÚMERO (o déjalo vacío)");
ChatUtil.error(Component.translatable("sputnik.module.discordrpc.notanumber"));
timestamp = System.currentTimeMillis();
}
}
@@ -9,6 +9,7 @@ import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.BooleanSetting;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.Entity;
import org.lwjgl.glfw.GLFW;
@@ -42,12 +43,16 @@ public class Friends extends Module {
if (FriendManager.INSTANCE.isFriend(target)) {
FriendManager.INSTANCE.remove(target);
ChatUtil.info(ChatFormatting.GREEN + target.getName().getString() + ChatFormatting.RESET + " ya no es tu amigo :(");
ChatUtil.info(Component.translatable("sputnik.commands.friend.removed",
ChatFormatting.GREEN + target.getName().getString() + ChatFormatting.RESET));
} else {
switch (FriendManager.INSTANCE.add(target)) {
case SUCCESS -> ChatUtil.info(ChatFormatting.GREEN + target.getName().getString() + ChatFormatting.RESET + " agregado como amigo :)");
case ALREADY_BEFRIENDED -> ChatUtil.warn(ChatFormatting.GREEN + target.getName().getString() + ChatFormatting.RESET + " ya es tu amigo");
case NOT_PLAYER -> ChatUtil.error("no se pudo añadir a " + ChatFormatting.GREEN + target.getName().getString() + ChatFormatting.RESET + " como amigo");
case SUCCESS -> ChatUtil.info(Component.translatable("sputnik.commands.friend.added",
ChatFormatting.GREEN + target.getName().getString() + ChatFormatting.RESET));
case ALREADY_BEFRIENDED -> ChatUtil.warn(Component.translatable("sputnik.module.friends.already",
ChatFormatting.GREEN + target.getName().getString() + ChatFormatting.RESET));
case NOT_PLAYER -> ChatUtil.error(Component.translatable("sputnik.module.friends.notplayer",
ChatFormatting.GREEN + target.getName().getString() + ChatFormatting.RESET));
}
}
}
@@ -7,6 +7,7 @@ import me.retucio.sputnik.module.setting.settings.*;
import me.retucio.sputnik.ui.hud.HudElement;
import me.retucio.sputnik.ui.hud.HudRenderer;
import me.retucio.sputnik.ui.hud.HudEditorScreen;
import me.retucio.sputnik.ui.hud.elements.CustomImageElement;
import me.retucio.sputnik.ui.hud.elements.DynoElement;
import org.lwjgl.glfw.GLFW;
@@ -126,6 +127,12 @@ public class Hud extends Module {
Dynosaurs.class, Dynosaurs.SPINOSAURUS
));
public FileSetting customImagePath = sgElements.add(new FileSetting(
"ruta de imagen",
"ruta de la imagen a renderizar en el elemento de imagen custom",
""
));
public Hud() {
super("HUD",
"superposición de la pantalla con info. adicional",
@@ -137,6 +144,11 @@ public class Hud extends Module {
DynoElement element = (DynoElement) HudRenderer.INSTANCE.getElement(DynoElement.class);
if (element != null) element.reloadTexture();
});
customImagePath.onUpdate(v -> {
CustomImageElement element = (CustomImageElement) HudRenderer.INSTANCE.getElement(CustomImageElement.class);
if (element != null) element.reloadTexture();
});
}
public enum TimeFormat {
@@ -7,6 +7,7 @@ import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.BooleanSetting;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.InventoryUtil;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ServerboundAttackPacket;
import net.minecraft.network.protocol.game.ServerboundInteractPacket;
import net.minecraft.world.entity.EquipmentSlot;
@@ -36,7 +37,7 @@ public class ChestplateSwitch extends Module {
if (mc.player.hasItemInSlot(EquipmentSlot.CHEST) || mc.player.getItemBySlot(EquipmentSlot.CHEST).is(Items.ELYTRA)) {
int slot = InventoryUtil.findSlot(this::isChestplate);
if (!Inventory.isHotbarSlot(slot)) {
ChatUtil.error("no se encontró una pechera en la hotbar");
ChatUtil.error(Component.translatable("sputnik.module.chestplateswitch.nochestplate"));
return;
}
@@ -19,6 +19,7 @@ import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.EntityTypes;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.boss.enderdragon.EndCrystal;
import net.minecraft.world.entity.player.Inventory;
@@ -47,7 +48,7 @@ public class CrystalAura extends Module {
private final ListSetting<EntityType<?>> entities = sgGeneral.add(new ListSetting<>(
"entidades", "entidades a las que atacar",
Lists.entityList, Lists.allFalseExcept(Lists.entityList, EntityType.PLAYER), Lists.entityNames
Lists.entityList, Lists.allFalseExcept(Lists.entityList, EntityTypes.PLAYER), Lists.entityNames
));
private final NumberSetting targetRange = sgGeneral.add(new NumberSetting(
@@ -205,7 +206,7 @@ public class CrystalAura extends Module {
}
}
// asegurarse de que no está dentro del bloque
if (mc.level.isEmptyBlock(pos) && mc.level.getBlockState(pos.below()).isSolid() && mc.level.isEmptyBlock(pos.above()) && targetPos.distanceTo(pos.getCenter()) > 0.6) {
if (mc.level.isEmptyBlock(pos) && mc.level.getBlockState(pos.below()).isSolid() && mc.level.isEmptyBlock(pos.above()) && targetPos.distanceTo(Vec3.atCenterOf(pos)) > 0.6) {
double dist = targetPos.distanceToSqr(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5);
if (dist < bestObbyPlaceDist[0]) {
bestObbyPlaceDist[0] = dist;
@@ -233,11 +234,22 @@ public class CrystalAura extends Module {
private void doBreak() {
if (breakTimer > 0) return;
List<Entity> crystals = new ArrayList<>();
for (Entity entity : mc.level.getEntities().getAll()) {
if (!(entity instanceof EndCrystal) || entity.distanceTo(mc.player) > mc.player.entityInteractionRange()) continue;
if (rotateToBreak.getValue()) EntityUtil.lookAt(entity.getEyePosition());
mc.gameMode.attack(mc.player, entity);
crystals.add(entity);
}
if (!crystals.isEmpty()) {
Runnable attack = () -> crystals.forEach(crystal -> mc.gameMode.attack(mc.player, crystal));
// solo se puede apuntar a uno, así que se rompen todos rotando solo al primero
if (rotateToBreak.getValue())
RotationUtil.lookAt(RotationUtil.HIGH, crystals.getFirst().getEyePosition(), attack);
else attack.run();
}
breakTimer = breakDelay.getIntValue();
}
@@ -253,8 +265,11 @@ public class CrystalAura extends Module {
return;
}
if (rotateToPlace.getValue()) EntityUtil.lookAt(result.getBlockPos().getCenter());
mc.gameMode.startPrediction(mc.level, (sequence) -> new ServerboundUseItemOnPacket(hand, result, sequence));
Runnable place = () -> mc.gameMode.startPrediction(mc.level, (sequence) -> new ServerboundUseItemOnPacket(hand, result, sequence));
if (rotateToPlace.getValue())
RotationUtil.lookAt(RotationUtil.HIGH, Vec3.atCenterOf(result.getBlockPos()), place);
else place.run();
}
private void placeObby(BlockPos pos) {
@@ -272,8 +287,11 @@ public class CrystalAura extends Module {
BlockPos supportPos = pos.below();
BlockHitResult result = new BlockHitResult(Vec3.atCenterOf(supportPos), Direction.UP, supportPos, false);
if (rotateToPlace.getValue()) EntityUtil.lookAt(pos.getCenter());
mc.gameMode.startPrediction(mc.level, (sequence) -> new ServerboundUseItemOnPacket(hand, result, sequence));
Runnable place = () -> mc.gameMode.startPrediction(mc.level, (sequence) -> new ServerboundUseItemOnPacket(hand, result, sequence));
if (rotateToPlace.getValue())
RotationUtil.lookAt(RotationUtil.HIGH, Vec3.atCenterOf(pos), place);
else place.run();
}
private void doAutoSelect(Item item) {
@@ -0,0 +1,95 @@
package me.retucio.sputnik.module.modules.combat;
import me.retucio.sputnik.module.Category;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.BooleanSetting;
import me.retucio.sputnik.module.setting.settings.EnumSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting;
import net.minecraft.world.phys.EntityHitResult;
/**
* @author retucio
*/
public class HoldAttack extends Module {
private final EnumSetting<AttackMode> mode = sgGeneral.add(new EnumSetting<>(
"modo de ataque",
"cómo atacar",
AttackMode.class,
AttackMode.TIME
));
private final NumberSetting timeCooldown = sgGeneral.add(new NumberSetting(
"cooldown",
"cómo de llena tiene que estar la barra de ataque antes de atacar",
84.8f, 0.0f, 100.0f, 0.1f
)).visibility(() -> mode.is(AttackMode.TIME));
private final NumberSetting spamCooldown = sgGeneral.add(new NumberSetting(
"cooldown ",
"cada cuánto atacar, en ticks",
0, 0, 40, 1
)).visibility(() -> mode.is(AttackMode.SPAM));
private final BooleanSetting attackInScreens = sgGeneral.add(new BooleanSetting(
"atacar en pantallas",
"atacar aun estando en una pantalla",
false
));
private int counter = 0;
public HoldAttack() {
super("autoataque", "mantén pulsado el clic para atacar automáticamente", Category.COMBAT);
mode.onUpdate(v -> counter = 0);
}
@Override
public void onEnable() {
counter = 0;
super.onEnable();
}
@Override
public void onTick() {
if (mc.player == null || mc.gameMode == null || mc.getCameraEntity() == null) return;
counter++;
if (mc.gui.screen() != null && !attackInScreens.getValue()) {
return;
}
if (!mc.options.keyAttack.isDown()) {
return;
}
if (!(mc.hitResult instanceof EntityHitResult ehr)) {
return;
}
if (mode.is(AttackMode.TIME)) {
if (mc.player.getAttackStrengthScale(0) < timeCooldown.getNormalizedValue()) {
return;
}
} else if (mode.is(AttackMode.SPAM)) {
if (counter < spamCooldown.getIntValue()) {
return;
}
}
mc.gameMode.attack(mc.player, ehr.getEntity());
counter = 0;
}
private enum AttackMode {
SPAM("spamear"),
TIME("timear");
private final String name;
AttackMode(String name) { this.name = name; }
@Override public String toString() { return name; }
}
}
@@ -0,0 +1,117 @@
package me.retucio.sputnik.module.modules.combat;
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.EnumSetting;
import me.retucio.sputnik.module.setting.settings.ListSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting;
import me.retucio.sputnik.util.Lists;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
public class KillAura extends Module {
private final SettingGroup sgEntities = addSg(new SettingGroup("entidades", true));
private final NumberSetting reach = sgGeneral.add(new NumberSetting(
"alcance", "qué tan lejos extender el brazo",
3.0f, 0.1f, 64.0f, 0.1f
));
private final EnumSetting<AttackMode> attackMode = sgGeneral.add(new EnumSetting<>(
"modo de ataque", "elige de qué manera atacar",
AttackMode.class, AttackMode.TIMED
));
private final NumberSetting cooldownPercentage = sgGeneral.add(new NumberSetting(
"porcentaje de cooldown", "cuánto esperar a que se llene la barrita de ataque",
100, 0, 100, 1
));
private final BooleanSetting swingHand = sgGeneral.add(new BooleanSetting(
"mover la mano", "mover la mano al atacar",
true
));
private final EnumSetting<ListMode> listMode = sgEntities.add(new EnumSetting<>(
"modo de lista", "cómo interpretar la lista",
ListMode.class, ListMode.WHITELIST
));
private final ListSetting<EntityType<? extends Entity>> whitelist = sgEntities.add(new ListSetting<>(
"entidades", "qué entidades atacar",
Lists.entityList, Lists.allFalse(Lists.entityList), Lists.entityNames
)).visibility(() -> listMode.is(ListMode.WHITELIST));
private final ListSetting<EntityType<? extends Entity>> blacklist = sgEntities.add(new ListSetting<>(
"entidades ", "qué entidades NO atacar",
Lists.entityList, Lists.allFalse(Lists.entityList), Lists.entityNames
)).visibility(() -> listMode.is(ListMode.BLACKLIST));
public KillAura() {
super("killaura", "si la violencia no funciona, es porque no has aplicado la suficiente", Category.COMBAT);
}
@Override
public void onTick() {
if (mc.player == null || mc.level == null) return;
for (Entity entity : mc.level.getEntities().getAll()) {
if (shouldAttack(entity)) {
attack(entity);
}
}
}
private void attack(Entity entity) {
if (swingHand.getValue()) {
mc.player.swing(InteractionHand.MAIN_HAND);
}
if (attackMode.is(AttackMode.TIMED)) {
if (mc.player.getAttackStrengthScale(0) < cooldownPercentage.getNormalizedValue()) {
return;
}
}
if (mc.level.isTickingEntity(entity)) {
mc.gameMode.attack(mc.player, entity);
}
}
private boolean shouldAttack(Entity entity) {
if (entity.is(mc.player)) return false;
if (!entity.isAlive()) return false;
if (entity.distanceToSqr(mc.player) > reach.getValue() * reach.getValue()) {
return false;
}
return switch (listMode.getValue()) {
case WHITELIST -> whitelist.isEnabled(entity.getType());
case BLACKLIST -> !blacklist.isEnabled(entity.getType());
};
}
private enum AttackMode {
TIMED("timeado"),
SPAM("spam");
private final String name;
AttackMode(String name) { this.name = name; }
@Override public String toString() { return name; }
}
private 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; }
}
}
@@ -8,6 +8,7 @@ import me.retucio.sputnik.module.setting.settings.BooleanSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ServerboundAttackPacket;
import net.minecraft.network.protocol.game.ServerboundInteractPacket;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
@@ -154,7 +155,7 @@ public class MaceKill extends Module {
}
}
} catch (Exception e) {
ChatUtil.error("upsi: " + e.getMessage());
ChatUtil.error(Component.translatable("sputnik.module.macekill.oops", e.getMessage()));
}
}
}
@@ -183,7 +183,7 @@ public class ProjectileTrajectories extends Module {
}
private Vec3 calculateHandToEyeDelta(Vec3 offset, Vec3 startPos, Vec3 eye, int handMultiplier, float tickProgress) {
if (mc.gameRenderer.getMainCamera().isDetached()) {
if (mc.gameRenderer.mainCamera().isDetached()) {
offset = offset.scale(0);
}
@@ -204,7 +204,7 @@ public class ProjectileTrajectories extends Module {
private void renderTrajectory(PoseStack matrices, List<Vec3> trajectoryPoints, Vec3 handToEyeDelta, boolean hasHit) {
if (trajectoryPoints.isEmpty()) return;
Vec3 cameraPos = mc.gameRenderer.getMainCamera().position();
Vec3 cameraPos = mc.gameRenderer.mainCamera().position();
matrices.pushPose();
matrices.translate(-cameraPos.x, -cameraPos.y, -cameraPos.z);
@@ -5,6 +5,7 @@ import me.retucio.sputnik.module.Category;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.BooleanSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting;
import me.retucio.sputnik.util.RotationUtil;
import net.minecraft.tags.ItemTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
@@ -73,13 +74,11 @@ public class SpearKill extends Module {
float yaw = (float) (Math.toDegrees(Math.atan2(toTarget.z, toTarget.x)) - 90.0);
float pitch = (float) -Math.toDegrees(Math.asin(toTarget.y));
mc.player.setYRot(yaw);
mc.player.setYHeadRot(yaw);
mc.player.setXRot(pitch);
RotationUtil.request(RotationUtil.HIGH, yaw, pitch, RotationUtil.Mode.CLIENT);
if (heldTicks >= 10) {
double lungeSpeed = speed.getValue();
Vec3 viewDir = Vec3.directionFromRotation(mc.player.getXRot(), mc.player.getYRot());
Vec3 viewDir = Vec3.directionFromRotation(pitch, yaw);
mc.player.setSprinting(true);
mc.player.setDeltaMovement(viewDir.scale(lungeSpeed));
}
@@ -68,7 +68,7 @@ public class Burglar extends Module {
@EventListener
private void onKey(KeyEvent event) {
if (!triggerKey.isDown() || event.getAction() != GLFW.GLFW_PRESS
|| !(mc.screen instanceof AbstractContainerScreen<?> container)) return;
|| !(mc.gui.screen() instanceof AbstractContainerScreen<?> container)) return;
if (event.getKey() == stealKey.getValue()) steal(container);
if (event.getKey() == dumpKey.getValue()) dump(container);
}
@@ -44,7 +44,7 @@ public class InventoryPlus extends Module {
}
private void processInput(int inputCode, Event event) {
if (!(mc.screen instanceof AbstractContainerScreen<?> screen) || mc.player == null) return;
if (!(mc.gui.screen() instanceof AbstractContainerScreen<?> screen) || mc.player == null) return;
Slot focusedSlot = ((AbstractContainerScreenAccessor) screen).getHoveredSlot();
if (focusedSlot == null) return;
@@ -8,9 +8,9 @@ import me.retucio.sputnik.module.Module;
* @see LocalPlayerMixin
*/
public class PortalGUI extends Module {
public class PortalGui extends Module {
public PortalGUI() {
public PortalGui() {
super("interfaz en portales",
"te permite abrir interfaces dentro de portales, como el chat o el inventario",
Category.INVENTORY);
@@ -14,7 +14,9 @@ import me.retucio.sputnik.util.InventoryUtil;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
@@ -40,22 +42,22 @@ public class ShulkerPeek extends Module {
// colores y tal (https://github.com/kgriff0n/shulker-preview)
SHULKER_COLORS.put(Items.SHULKER_BOX, Colors.LAVENDER);
SHULKER_COLORS.put(Items.WHITE_SHULKER_BOX, Colors.WHITE);
SHULKER_COLORS.put(Items.LIGHT_GRAY_SHULKER_BOX, Colors.SILVER);
SHULKER_COLORS.put(Items.GRAY_SHULKER_BOX, Colors.GRAY);
SHULKER_COLORS.put(Items.BLACK_SHULKER_BOX, Colors.BLACK);
SHULKER_COLORS.put(Items.BROWN_SHULKER_BOX, Colors.BROWN);
SHULKER_COLORS.put(Items.RED_SHULKER_BOX, Colors.RED);
SHULKER_COLORS.put(Items.ORANGE_SHULKER_BOX, Colors.ORANGE);
SHULKER_COLORS.put(Items.YELLOW_SHULKER_BOX, Colors.YELLOW);
SHULKER_COLORS.put(Items.LIME_SHULKER_BOX, Colors.LIME);
SHULKER_COLORS.put(Items.GREEN_SHULKER_BOX, Colors.GREEN);
SHULKER_COLORS.put(Items.CYAN_SHULKER_BOX, Colors.CYAN);
SHULKER_COLORS.put(Items.LIGHT_BLUE_SHULKER_BOX, Colors.CELESTE);
SHULKER_COLORS.put(Items.BLUE_SHULKER_BOX, Colors.BLUE);
SHULKER_COLORS.put(Items.PURPLE_SHULKER_BOX, Colors.PURPLE);
SHULKER_COLORS.put(Items.MAGENTA_SHULKER_BOX, Colors.MAGENTA);
SHULKER_COLORS.put(Items.PINK_SHULKER_BOX, Colors.PINK);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.WHITE), Colors.WHITE);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.LIGHT_GRAY), Colors.SILVER);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.GRAY), Colors.GRAY);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.BLACK), Colors.BLACK);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.BROWN), Colors.BROWN);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.RED), Colors.RED);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.ORANGE), Colors.ORANGE);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.YELLOW), Colors.YELLOW);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.LIME), Colors.LIME);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.GREEN), Colors.GREEN);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.CYAN), Colors.CYAN);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.LIGHT_BLUE), Colors.CELESTE);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.BLUE), Colors.BLUE);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.PURPLE), Colors.PURPLE);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.MAGENTA), Colors.MAGENTA);
SHULKER_COLORS.put(Items.DYED_SHULKER_BOX.pick(DyeColor.PINK), Colors.PINK);
}
@EventListener
@@ -69,7 +71,7 @@ public class ShulkerPeek extends Module {
}
if (event.getAction() == GLFW.GLFW_REPEAT) {
if (mc.screen instanceof PreviewScreen preview) {
if (mc.gui.screen() instanceof PreviewScreen preview) {
if (preview.getType() != PreviewScreen.PreviewType.SHULKER)
openPreviewScreen();
} else openPreviewScreen();
@@ -77,7 +79,7 @@ public class ShulkerPeek extends Module {
}
if (event.getAction() == GLFW.GLFW_RELEASE) {
if (mc.screen instanceof PreviewScreen preview)
if (mc.gui.screen() instanceof PreviewScreen preview)
preview.onClose();
}
}
@@ -85,25 +87,25 @@ public class ShulkerPeek extends Module {
private void openPreviewScreen() {
ItemStack stack;
if (mc.screen instanceof PreviewScreen previewScreen) {
if (mc.gui.screen() instanceof PreviewScreen previewScreen) {
int focusedSlot = previewScreen.getFocusedSlot();
if (focusedSlot == -1) return;
stack = previewScreen.getInventory().get(focusedSlot);
} else if (mc.screen instanceof AbstractContainerScreen<?> handledScreen) {
} else if (mc.gui.screen() instanceof AbstractContainerScreen<?> handledScreen) {
stack = ((AbstractContainerScreenAccessor) handledScreen).getHoveredSlot().getItem();
} else return;
if (stack.getItem() == Items.ENDER_CHEST) {
if (InventoryUtil.getEchestInv() == null) {
ChatUtil.warn("abre un enderchest primero");
ChatUtil.warn(Component.translatable("sputnik.module.shulkerpeek.openechest"));
return;
}
mc.setScreen(new PreviewScreen(InventoryUtil.getEchestInv(), mc.screen));
mc.gui.setScreen(new PreviewScreen(InventoryUtil.getEchestInv(), mc.gui.screen()));
return;
}
else if (isShulkerEmpty(stack)) return;
mc.setScreen(new PreviewScreen(stack, mc.screen));
mc.gui.setScreen(new PreviewScreen(stack, mc.gui.screen()));
}
public static boolean isShulkerEmpty(ItemStack stack) {
@@ -16,7 +16,7 @@ import java.util.Arrays;
import java.util.List;
public class UIMove extends Module {
public class UiMove extends Module {
public ListSetting<MenuType<?>> screens = sgGeneral.add(new ListSetting<>("interfaces", "interfaces en las que te podrás mover",
Lists.screenList, Lists.allTrue(Lists.screenList), Lists.screenNames));
@@ -28,7 +28,7 @@ public class UIMove extends Module {
private final MenuType<?> clickGuiHandlerType = MenuType.register(
"sputnik_clickgui", ChestMenu::threeRows); // 9x3 porque no importa (creo)
public UIMove() {
public UiMove() {
super("moverse en interfaz",
"te permite seguir usando las teclas de movimiento aún estando en ciertas interfaces",
Category.INVENTORY);
@@ -60,13 +60,13 @@ public class UIMove extends Module {
@Override
public void onTick() {
if (mc.player == null || mc.screen == null) return;
if (mc.player == null || mc.gui.screen() == null) return;
MenuType<?> handler = null;
if (mc.screen == ClickGui.INSTANCE)
if (mc.gui.screen() == ClickGui.INSTANCE)
handler = clickGuiHandlerType;
if (mc.screen instanceof AbstractContainerScreen<?> screen) {
if (mc.gui.screen() instanceof AbstractContainerScreen<?> screen) {
try {
handler = screen.getMenu().getType();
} catch (UnsupportedOperationException e) {
@@ -74,10 +74,10 @@ public class AnvilFont extends Module {
@Override
public void onTick() {
if (mc.screen == null) return;
if (mc.gui.screen() == null) return;
if (mc.screen instanceof AnvilScreen) {
EditBox nameField = ((AnvilScreenAccessor) mc.screen).getNameField();
if (mc.gui.screen() instanceof AnvilScreen) {
EditBox nameField = ((AnvilScreenAccessor) mc.gui.screen()).getNameField();
if (fontMode.getValue() != FontMode.NORMAL)
text = fonts.get(fontMode.getIndex()).replace(nameField.getValue());
@@ -87,7 +87,7 @@ public class AnvilFont extends Module {
if (!text.equals(nameField.getValue()))
nameField.setValue(text);
((AnvilScreenAccessor) mc.screen).setNameField(nameField);
((AnvilScreenAccessor) mc.gui.screen()).setNameField(nameField);
}
}
@@ -107,7 +107,7 @@ public class BookBot extends Module {
ItemStack book = InventoryUtil.findStack(bookPredicate);
if (book == null) {
ChatUtil.warn("no se encontró ningún libro");
ChatUtil.warn(Component.translatable("sputnik.module.bookbot.nobook"));
toggle();
return;
}
@@ -193,7 +193,7 @@ public class BookBot extends Module {
}
if (bookCount > 0) {
ChatUtil.info("creado libro n. " + bookCount);
ChatUtil.info(Component.translatable("sputnik.module.bookbot.created", bookCount));
}
createBook(pages, filteredPages);
@@ -123,7 +123,7 @@ public class ChatPlus extends Module {
private void onSendMessage(SendMessageEvent event) {
// evitar mandar coordenadas por el chat
if (coordsProtection.getValue() && containsCoordinates(event.getMessage())) {
ChatUtil.warn(Component.literal("cuidadito con las coordenadas chavalín").append(
ChatUtil.warn(Component.translatable("sputnik.module.chatplus.coordswarn").append(
getSendButton(event.getMessage())));
event.cancel();
@@ -200,10 +200,11 @@ public class ChatPlus extends Module {
@SuppressWarnings("DataFlowIssue")
private MutableComponent getSendButton(String message) {
// botón para enviar mensaje con coordenadas de todos modos
MutableComponent sendButton = Component.literal("\n[ME LA SUDA]");
MutableComponent sendButton = Component.literal("\n").append(
Component.translatable("sputnik.module.chatplus.sendanyway"));
MutableComponent hintBaseText = Component.literal("");
MutableComponent hintMsg = Component.literal("enviar de todos modos:");
MutableComponent hintMsg = Component.translatable("sputnik.module.chatplus.sendanyway.hint");
hintMsg.setStyle(hintBaseText.getStyle().applyFormat(ChatFormatting.GRAY));
hintBaseText.append(hintMsg);
@@ -52,7 +52,8 @@ public class ScreenshotPlus extends Module {
}
public void sendScreenshotMessage() {
MutableComponent baseText = Component.literal("captura de pantalla tomada\n");
MutableComponent baseText = Component.translatable("sputnik.module.screenshot.taken")
.append(Component.literal("\n"));
if (saveButton.getValue() && !defaultAction.is(ScreenshotActions.SAVE)) baseText.append(getSaveButton().append(" "));
if (copyButton.getValue()) baseText.append(getCopyButton().append(" "));
if (openButton.getValue() && defaultAction.is(ScreenshotActions.SAVE)) baseText.append(getOpenButton().append(" "));
@@ -72,9 +73,9 @@ public class ScreenshotPlus extends Module {
ScreenshotPlus.TransferableImage trans = new TransferableImage(bufferedImage);
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(trans, null);
ChatUtil.info("captura copiada al portapapeles");
ChatUtil.info(Component.translatable("sputnik.module.screenshot.copied"));
} catch (Exception e) {
ChatUtil.error("no se pudo copiar la captura al portapapeles");
ChatUtil.error(Component.translatable("sputnik.module.screenshot.copyfailed"));
e.printStackTrace();
}
}
@@ -85,7 +86,7 @@ public class ScreenshotPlus extends Module {
public void saveScreenshot(NativeImage image) throws IOException {
image.writeToFile(screenshotFile);
ChatUtil.info(Component.literal("captura guardada como: ").append(
ChatUtil.info(Component.translatable("sputnik.module.screenshot.savedas").append(
Component.literal(screenshotFile.getName())
.withStyle(ChatFormatting.UNDERLINE)
.withStyle(style -> style.withClickEvent(new ClickEvent.OpenFile(screenshotFile.getAbsoluteFile())))
@@ -129,7 +130,7 @@ public class ScreenshotPlus extends Module {
@SuppressWarnings("DataFlowIssue")
private MutableComponent getSaveButton() {
MutableComponent button = Component.literal("[GUARDAR]");
MutableComponent button = Component.translatable("sputnik.module.screenshot.button.save");
MutableComponent hintBaseText = Component.literal("");
MutableComponent hintMsg = Component.literal(ScreenshotActions.SAVE.toString());
@@ -146,7 +147,7 @@ public class ScreenshotPlus extends Module {
@SuppressWarnings("DataFlowIssue")
private MutableComponent getCopyButton() {
MutableComponent button = Component.literal("[COPIAR]");
MutableComponent button = Component.translatable("sputnik.module.screenshot.button.copy");
MutableComponent hintBaseText = Component.literal("");
MutableComponent hintMsg = Component.literal(ScreenshotActions.COPY.toString());
@@ -162,10 +163,10 @@ public class ScreenshotPlus extends Module {
}
private MutableComponent getOpenButton() {
MutableComponent button = Component.literal("[ABRIR]");
MutableComponent button = Component.translatable("sputnik.module.screenshot.button.open");
MutableComponent hintBaseText = Component.literal("");
MutableComponent hintMsg = Component.literal("abrir archivo de imagen");
MutableComponent hintMsg = Component.translatable("sputnik.module.screenshot.button.open.hint");
hintMsg.setStyle(hintBaseText.getStyle().applyFormats(ChatFormatting.GRAY));
hintBaseText.append(hintMsg);
@@ -179,7 +180,7 @@ public class ScreenshotPlus extends Module {
@SuppressWarnings("DataFlowIssue")
private MutableComponent getDiscardButton() {
MutableComponent button = Component.literal("[DESCARTAR]");
MutableComponent button = Component.translatable("sputnik.module.screenshot.button.discard");
MutableComponent hintBaseText = Component.literal("");
MutableComponent hintMsg = Component.literal(ScreenshotActions.NONE.toString());
@@ -5,6 +5,7 @@ import me.retucio.sputnik.event.interact.UseItemEvent;
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.RotationUtil;
import net.minecraft.world.item.Items;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
@@ -99,7 +100,7 @@ public class InfiniteElytra extends Module {
}
}
mc.player.setXRot(pitch);
RotationUtil.requestPitch(RotationUtil.NORMAL, pitch, RotationUtil.Mode.CLIENT);
}
@EventListener
@@ -107,7 +108,7 @@ public class InfiniteElytra extends Module {
if (mc.player == null) return;
if (mc.player.getItemInHand(event.getHand()).getItem() == Items.FIREWORK_ROCKET && !mc.player.isSpectator() && mc.player.isFallFlying()) {
pitch = pitchUp.getFloatValue();
mc.player.setXRot(pitch);
RotationUtil.requestPitch(RotationUtil.NORMAL, pitch, RotationUtil.Mode.CLIENT);
state = FlyState.PITCHING_DOWN;
lowestY = mc.player.getY();
}
@@ -0,0 +1,122 @@
package me.retucio.sputnik.module.modules.movement;
import com.github.retucio.neutrino.EventListener;
import me.retucio.sputnik.event.interact.UpdateInventoryEvent;
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.InventoryUtil;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class ItemMagnet extends Module {
private final NumberSetting range = sgGeneral.add(new NumberSetting(
"rango", "rango de recogida de items",
6, 1, 32, 0.5f
));
@Nullable
private ItemEntity current = null;
private AABB box = null;
private final List<ItemEntity> cannotPickUp = new ArrayList<>();
private int cooldown = 0;
public ItemMagnet() {
super("imán de items", "recoge items distantes rápidamente mediante paquetes de movimiento", Category.MOVEMENT);
}
@Override
public void onEnable() {
current = null;
cooldown = 0;
}
@Override
public void onDisable() {
current = null;
cooldown = 0;
}
@Override
public void onTick() {
if (mc.player == null || mc.level == null || mc.getConnection() == null) return;
if (current != null && !current.isAlive()) {
current = null;
cooldown = 0;
}
if (current != null) {
box = mc.player.getVehicle() == null
? mc.player.getBoundingBox()
: mc.player.getVehicle().getBoundingBox().minmax(mc.player.getBoundingBox()).inflate(1.0, 0, 1.0);
if (!box.intersects(current.getBoundingBox())) {
if (cooldown > 0) {
cooldown--;
return;
}
Vec3 realPos = mc.player.position();
// tparse al item
mc.getConnection().send(new ServerboundMovePlayerPacket.Pos(
current.getX(), current.getY(), current.getZ(),
mc.player.onGround(),
mc.player.horizontalCollision
));
// para evitar desync
mc.getConnection().send(new ServerboundMovePlayerPacket.Pos(
realPos.x, realPos.y, realPos.z,
mc.player.onGround(),
mc.player.horizontalCollision
));
// pequeño margen antes de reintentar con el mismo ítem
cooldown = 2;
}
return;
}
for (Entity entity : mc.level.getEntities().getAll()) {
if (entity.is(mc.player)) continue;
if (entity.distanceToSqr(mc.player) > range.getFloatValue() * range.getFloatValue()) continue;
if (entity instanceof ItemEntity item) {
if (cannotPickUp.contains(item)) continue;
if (InventoryUtil.findSlot(stack -> stack.is(item.getItem().getItem()) && stack.getCount() < stack.getMaxStackSize()) == -1
&& mc.player.getInventory().getFreeSlot() == -1
) {
cannotPickUp.add(item);
continue;
}
current = item;
cooldown = 0;
return;
}
}
}
@EventListener
private void onInventoryChange(UpdateInventoryEvent event) {
cannotPickUp.clear();
}
}
@@ -334,8 +334,8 @@ public class Scaffold extends Module {
if (NetworkUtil.placeBlock(
pos, InteractionHand.MAIN_HAND,
slot,
rotate.getValue(),
rotate.getValue(),
rotate.getValue() ? RotationUtil.Mode.CLIENT : null,
RotationUtil.NORMAL,
swingHand.getValue(),
true,
true
@@ -4,6 +4,7 @@ import me.retucio.sputnik.module.Category;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.StringSetting;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.network.chat.Component;
public class BungeecordSpoofer extends Module {
@@ -17,7 +18,7 @@ public class BungeecordSpoofer extends Module {
address.onUpdate(text -> {
if (!text.matches("^[0-9a-f\\\\.:]{0,45}$")) {
address.setValue(address.getDefaultValue());
ChatUtil.error("dirección IP inválida");
ChatUtil.error(Component.translatable("sputnik.module.bungeespoofer.invalidip"));
}
});
}
Loaded 100 of 192 files, more files were not shown because too many files have changed in this diff. Show more