Compare commits

..
10 Commits
Author SHA1 Message Date
retucio ac7de7a07a v6.1 2026-08-27 01:00:19 +02:00
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
217 changed files with 8939 additions and 1089 deletions

No files matched your search

+1
View File
@@ -44,3 +44,4 @@ replay_*.log
todo.txt todo.txt
cambios.md cambios.md
remappedSrc/ remappedSrc/
.claude/
+4 -4
View File
@@ -1,16 +1,16 @@
# jenny's mod 2 100% real # 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 ## instalación
### para gente normal: ### para gente normal:
1. ir a [lanzamientos](https://github.com/retuci0/sputnik/releases) 1. ir a [lanzamientos](https://codeberg.org/retucio/sputnik/releases)
2. selecionar la versión más reciente 2. selecionar la versión más reciente
3. descargar el .jar 3. descargar el .jar
4. moverlo a tu directorio de mods (probablemente `%appdata%/.minecraft/mods`) 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. 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 ### 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/hudeditor.png" alt="editor de HUD">
<img src="screenshots/colorpicker.png" alt="selector de colores"> <img src="screenshots/colorpicker.png" alt="selector de colores">
## contibuciones ## contribuciones
sí por favor sí por favor
+10 -5
View File
@@ -1,4 +1,4 @@
# v5.2 # v6.1
## novedades ## novedades
@@ -6,13 +6,18 @@
#### módulos nuevos: #### módulos nuevos:
- físicas de items: aplica físicas "realistas" a los items del suelo
#### elementos del HUD nuevos:
- velocidad: velocidad del jugador
### arreglos / mejoras ### arreglos / mejoras
- `andamios` ya funciona correctamente - killaura ya no te echa por exceder el límite de paquetes
- autoguardado cada cierto intervalo de tiempo en vez de guardar inmediatamente tras cada cambio en los ajustes, por optimización - el mj bridge de andamios ya funciona correctamente al no usarse en el aire
- el guardado se realiza desde otro hilo ahora, para evitar que el I/O bloque el hilo principal - ahora no hace falta volver a clicar cada vez que quieras cambiar de herramienta en `auto herramienta`, con el modo `cada tick`
## problemas reconocidos ## problemas reconocidos
- lo de siempre. - lo de siempre.
- el ítem del tótem no se renderiza en el editor del HUD si es abierto en la pantalla de inicio
+2
View File
@@ -10,6 +10,8 @@ elytra-recast: https://github.com/InLieuOfLuna/elytra-recast/
SpearHax: https://github.com/etianl/spearHax/ SpearHax: https://github.com/etianl/spearHax/
ProjectileTrajectoryPreview: https://github.com/maDU59/ProjectilesTrajectoryPreview ProjectileTrajectoryPreview: https://github.com/maDU59/ProjectilesTrajectoryPreview
DupersUnited public addon: https://discord.gg/dupes | https://dupers.wtf 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 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.jvmargs=-Xmx2G
org.gradle.parallel=true org.gradle.parallel=true
minecraft_version=26.1.2 minecraft_version=26.2
loader_version=0.19.2 loader_version=0.19.3
loom_version=1.16-SNAPSHOT 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 discord_rpc_version=1.0.3
neutrino_version=1.1.0 neutrino_version=1.1.0
jdk_version=25 jdk_version=25
mod_version=5.2 mod_version=6.1
maven_group=me.retucio maven_group=me.retucio
archives_base_name=sputnik archives_base_name=sputnik
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
View File
Whitespace-only changes.
+51 -27
View File
@@ -7,6 +7,8 @@ import me.retucio.sputnik.command.CommandManager;
import me.retucio.sputnik.command.commands.BindCommand; import me.retucio.sputnik.command.commands.BindCommand;
import me.retucio.sputnik.config.ConfigManager; 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.ShutdownEvent;
import me.retucio.sputnik.event.interact.OpenScreenEvent; 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.ModuleManager;
import me.retucio.sputnik.module.modules.client.Hud; 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.hud.HudRenderer;
import me.retucio.sputnik.ui.screen.ClickGui; import me.retucio.sputnik.ui.screen.ClickGui;
import me.retucio.sputnik.ui.hud.HudEditorScreen; import me.retucio.sputnik.ui.hud.HudEditorScreen;
import me.retucio.sputnik.ui.screen.UpdateScreen; import me.retucio.sputnik.ui.screen.UpdateScreen;
import me.retucio.sputnik.ui.widgets.buttons.settings.BindButton; import me.retucio.sputnik.ui.widgets.buttons.settings.BindButton;
import me.retucio.sputnik.ui.widgets.buttons.SettingButton; 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.buttons.settings.TextButton;
import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel; import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel;
import me.retucio.sputnik.ui.widgets.panels.SettingsPanel; 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.AbstractCommandBlockEditScreen;
import net.minecraft.client.gui.screens.inventory.AbstractSignEditScreen; import net.minecraft.client.gui.screens.inventory.AbstractSignEditScreen;
import net.minecraft.client.gui.screens.inventory.AnvilScreen; 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.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@@ -98,12 +103,17 @@ public class Sputnik implements ClientModInitializer {
EVENT_BUS.subscribe(MiscUtil.class); EVENT_BUS.subscribe(MiscUtil.class);
EVENT_BUS.subscribe(NetworkUtil.class); EVENT_BUS.subscribe(NetworkUtil.class);
EVENT_BUS.subscribe(RenderUtil.class); EVENT_BUS.subscribe(RenderUtil.class);
EVENT_BUS.subscribe(RotationUtil.class);
EVENT_BUS.subscribe(BlockIterator.class); EVENT_BUS.subscribe(BlockIterator.class);
Lists.init(); Lists.init();
HudRenderer.INSTANCE = new HudRenderer(); HudRenderer.INSTANCE = new HudRenderer();
HudEditor.INSTANCE = new HudEditor();
MacroManager.INSTANCE = new MacroManager();
ProfileManager.INSTANCE = new ProfileManager();
CapeManager.INSTANCE = new CapeManager(); CapeManager.INSTANCE = new CapeManager();
EVENT_BUS.post(new LoadCapeManagerEvent()); EVENT_BUS.post(new LoadCapeManagerEvent());
@@ -134,11 +144,8 @@ public class Sputnik implements ClientModInitializer {
// se ejecuta cada tick, es decir, 20 veces por segundo // se ejecuta cada tick, es decir, 20 veces por segundo
public void onTick() { public void onTick() {
if (!settingsApplied if (!settingsApplied && !ConfigManager.INSTANCE.hasLoaded()) {
&& ConfigManager.INSTANCE.getConfig() != null settingsApplied = ConfigManager.INSTANCE.applyConfig();
&& !ConfigManager.INSTANCE.hasLoaded()) {
ConfigManager.INSTANCE.applyConfig();
settingsApplied = true;
} }
ModuleManager.INSTANCE.getEnabledModules().forEach(Module::onTick); ModuleManager.INSTANCE.getEnabledModules().forEach(Module::onTick);
@@ -153,14 +160,15 @@ public class Sputnik implements ClientModInitializer {
// prevenir interrumpir combos de F3 // prevenir interrumpir combos de F3
if (key == GLFW.GLFW_KEY_F3) return; if (key == GLFW.GLFW_KEY_F3) return;
boolean anyFocused = isAnySettingButtonFocused() || ClickGui.INSTANCE.getSearchBar().isFocused(); boolean anyFocused = isAnySettingButtonFocused() || ClickGui.INSTANCE.isCapturingInput();
ClickGui.INSTANCE.setAnyFocused(anyFocused); ClickGui.INSTANCE.setAnyFocused(anyFocused);
if (action != GLFW.GLFW_RELEASE) { if (action != GLFW.GLFW_RELEASE) {
if (BindCommand.onKeyPress(key)) return; if (BindCommand.onKeyPress(key)) return;
if (action == GLFW.GLFW_PRESS && MacroManager.INSTANCE.onKeyPress(key)) return;
if (action == GLFW.GLFW_PRESS) { if (action == GLFW.GLFW_PRESS) {
handleModuleToggle(key, anyFocused); handleModuleToggle(key, anyFocused);
handleClickGUIKey(key, anyFocused); handleClickGuiKey(key, anyFocused);
handleHudEditorKey(key, anyFocused); handleHudEditorKey(key, anyFocused);
} }
// solo reenviar teclas del teclado // solo reenviar teclas del teclado
@@ -174,16 +182,21 @@ public class Sputnik implements ClientModInitializer {
// verifica si algún botón de ajustes está escuchando // verifica si algún botón de ajustes está escuchando
private boolean isAnySettingButtonFocused() { private boolean isAnySettingButtonFocused() {
if (ClickGui.INSTANCE == null) return false;
for (SettingsPanel sf : ClickGui.INSTANCE.getSettingsPanels()) for (SettingsPanel sf : ClickGui.INSTANCE.getSettingsPanels())
for (Button sb : sf.getButtons()) 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 true;
}
return false; return false;
} }
// se ocupa de la lógica de encendido y apagado de los módulos // se ocupa de la lógica de encendido y apagado de los módulos
private void handleModuleToggle(int key, boolean anyFocused) { 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()) { for (Module module : ModuleManager.INSTANCE.getModules()) {
// evitar interrumpir combinaciones de teclas del F3 // evitar interrumpir combinaciones de teclas del F3
@@ -201,46 +214,47 @@ public class Sputnik implements ClientModInitializer {
for (SettingsPanel sf : ClickGui.INSTANCE.getSettingsPanels()) { for (SettingsPanel sf : ClickGui.INSTANCE.getSettingsPanels()) {
for (SettingButton<?> sb : sf.getButtons()) { for (SettingButton<?> sb : sf.getButtons()) {
if (sb instanceof BindButton bb) bb.onKey(key, action); 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); if (sb instanceof TextButton tb) tb.onKey(key, action);
} }
} }
} }
// maneja la lógica de apertura de la interfaz // 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()) if (key != ClientSettingsPanel.clientSettings.getKey() || anyFocused || isOnTypingScreen())
return; return;
if (mc.screen != ClickGui.INSTANCE) { if (mc.gui.screen() != ClickGui.INSTANCE) {
prevScreen = mc.screen; prevScreen = mc.gui.screen();
mc.setScreen(ClickGui.INSTANCE); mc.gui.setScreen(ClickGui.INSTANCE);
} else { } else {
ClickGui.INSTANCE.onClose(); 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) { private void handleHudEditorKey(int key, boolean anyFocused) {
Hud hud = ModuleManager.INSTANCE.getModuleByClass(Hud.class); Hud hud = ModuleManager.INSTANCE.getModuleByClass(Hud.class);
if (key != hud.editorKey.getValue() || anyFocused || isOnTypingScreen()) return; if (key != hud.editorKey.getValue() || anyFocused || isOnTypingScreen()) return;
if (mc.screen != HudEditorScreen.INSTANCE) { if (mc.gui.screen() != HudEditorScreen.INSTANCE) {
if (!hud.isEnabled()) { if (!hud.isEnabled()) {
ChatUtil.warn("HUD está desactivado, lumbreras"); ChatUtil.warn(Component.translatable("sputnik.hud.disabled"));
return; return;
} }
prevScreen = mc.screen; prevScreen = mc.gui.screen();
mc.setScreen(HudEditorScreen.INSTANCE); mc.gui.setScreen(HudEditorScreen.INSTANCE);
} else { } else {
HudEditorScreen.INSTANCE.onClose(); 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 // se ocupa de apagar los módulos que tengan configurado hacerlo tras soltar su tecla
private void handleModuleRelease(int key) { 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()) for (Module module : ModuleManager.INSTANCE.getEnabledModules())
if (module.shouldToggleOnBindRelease() && key == module.getKey()) if (module.shouldToggleOnBindRelease() && key == module.getKey())
@@ -248,19 +262,29 @@ public class Sputnik implements ClientModInitializer {
} }
private boolean isOnTypingScreen() { private boolean isOnTypingScreen() {
return mc.screen instanceof ChatScreen return mc.gui.screen() instanceof ChatScreen
|| mc.screen instanceof AnvilScreen || mc.gui.screen() instanceof AnvilScreen
|| mc.screen instanceof AbstractSignEditScreen || mc.gui.screen() instanceof AbstractSignEditScreen
|| mc.screen instanceof AbstractCommandBlockEditScreen; || mc.gui.screen() instanceof AbstractCommandBlockEditScreen;
} }
private boolean keysVerified = false;
@EventListener @EventListener
// notificar al usuario de que hay una actualización disponible, si la hay // notificar al usuario de que hay una actualización disponible, si la hay
public void onSetScreen(OpenScreenEvent event) { public void onSetScreen(OpenScreenEvent event) {
if (VersionChecker.INSTANCE.shouldShowScreen && event.getScreen() instanceof TitleScreen) { 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(); event.cancel();
VersionChecker.INSTANCE.shouldShowScreen = false; VersionChecker.INSTANCE.shouldShowScreen = false;
mc.execute(() -> mc.screen = new UpdateScreen()); 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.Commands;
import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.data.registries.VanillaRegistries; import net.minecraft.data.registries.VanillaRegistries;
import net.minecraft.network.chat.Component;
import java.util.List; import java.util.List;
@@ -20,12 +21,14 @@ public abstract class Command {
protected static final Minecraft mc = Minecraft.getInstance(); protected static final Minecraft mc = Minecraft.getInstance();
private final String name, description; private final String name, descriptionKey;
private final List<String> aliases; 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.name = name;
this.description = description; this.descriptionKey = descriptionKey;
this.aliases = List.of(aliases); this.aliases = List.of(aliases);
} }
@@ -56,8 +59,12 @@ public abstract class Command {
return name; return name;
} }
public String getDescription() { public String getDescriptionKey() {
return description; return descriptionKey;
}
public Component getDescription() {
return Component.translatable(descriptionKey);
} }
public List<String> getAliases() { public List<String> getAliases() {
@@ -38,9 +38,13 @@ public class CommandManager {
addCommand(new EnderChestCommand()); addCommand(new EnderChestCommand());
addCommand(new FriendCommand()); addCommand(new FriendCommand());
addCommand(new GarbageCleanerCommand()); addCommand(new GarbageCleanerCommand());
addCommand(new HelpCommand());
addCommand(new MacroCommand());
addCommand(new PanoramaCommand());
addCommand(new PeekCommand()); addCommand(new PeekCommand());
addCommand(new PluginsCommand()); addCommand(new PluginsCommand());
addCommand(new PrefixCommand()); addCommand(new PrefixCommand());
addCommand(new ProfileCommand());
addCommand(new PurgeCommand()); addCommand(new PurgeCommand());
addCommand(new RepeatCommand()); addCommand(new RepeatCommand());
addCommand(new RepeatWaitCommand()); addCommand(new RepeatWaitCommand());
@@ -25,7 +25,7 @@ public class EntityArgumentType implements ArgumentType<Entity> {
public static final EntityArgumentType INSTANCE = new EntityArgumentType(); public static final EntityArgumentType INSTANCE = new EntityArgumentType();
private static final DynamicCommandExceptionType NO_SUCH_ENTITY = new DynamicCommandExceptionType( 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( private static final Collection<String> EXAMPLES = List.of(
"AdlerHitdolf", "AdlerHitdolf",
@@ -15,7 +15,8 @@ import java.util.concurrent.CompletableFuture;
public class EnumArgumentType<T extends Enum<T>> implements ArgumentType<T> { 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; 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 { public T parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString(); 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) return Arrays.stream(this.values)
.filter((value) -> value.toString().equals(argument)) .filter((value) -> value.toString().equalsIgnoreCase(argument))
.findFirst() .findFirst()
.orElseThrow(() -> NO_SUCH_TYPE.create(argument) .orElseThrow(() -> NO_SUCH_TYPE.create(argument)
); );
@@ -22,15 +22,15 @@ public class ModuleArgumentType implements ArgumentType<Module> {
public static final ModuleArgumentType INSTANCE = new ModuleArgumentType(); public static final ModuleArgumentType INSTANCE = new ModuleArgumentType();
private static final DynamicCommandExceptionType UNKNOWN_MODULE = new DynamicCommandExceptionType( 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() 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 @Override
public Module parse(StringReader reader) throws CommandSyntaxException { public Module parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString().replace("_", " "); 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); if (module == null) throw UNKNOWN_MODULE.create(argument);
return module; return module;
} }
@@ -38,7 +38,7 @@ public class ModuleArgumentType implements ArgumentType<Module> {
@Override @Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) { public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return SharedSuggestionProvider.suggest(ModuleManager.INSTANCE.getModules() 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 @Override
@@ -21,7 +21,8 @@ import static me.retucio.sputnik.Sputnik.mc;
public class PlayerArgumentType implements ArgumentType<Player> { public class PlayerArgumentType implements ArgumentType<Player> {
public static final PlayerArgumentType INSTANCE = new PlayerArgumentType(); 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"); 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; private static Module listeningModule = null;
public BindCommand() { public BindCommand() {
super("bind", "asigna una tecla a un módulo", "keybind"); super("bind", "sputnik.commands.bind.desc", "keybind", "kb", "tecla");
} }
@Override @Override
@@ -34,14 +34,16 @@ public class BindCommand extends Command {
.executes(ctx -> { .executes(ctx -> {
Module module = ctx.getArgument("módulo", Module.class); Module module = ctx.getArgument("módulo", Module.class);
listeningModule = module; 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; return SUCCESS;
}) })
.then(literal("reset").executes(ctx -> { .then(literal("reset").executes(ctx -> {
Module module = ctx.getArgument("módulo", Module.class); Module module = ctx.getArgument("módulo", Module.class);
module.getBind().reset(); module.getBind().reset();
ChatUtil.info("tecla para el módulo " + ChatFormatting.GREEN + module.getName() ChatUtil.info(Component.translatable("sputnik.commands.bind.keyreset",
+ " restablecida a " + ChatFormatting.AQUA + KeyUtil.getKeyName(module.getKey())); ChatFormatting.GREEN + module.getDisplayName(),
ChatFormatting.AQUA + KeyUtil.getKeyName(module.getKey())
));
return SUCCESS; return SUCCESS;
})) }))
); );
@@ -51,17 +53,18 @@ public class BindCommand extends Command {
if (listeningModule == null) return false; if (listeningModule == null) return false;
KeySetting bind = listeningModule.getBind(); 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)); List<KeyMapping> keys = new ArrayList<>(List.of(mc.options.keyMappings));
keys.removeAll(List.of(mc.options.debugKeys)); keys.removeAll(List.of(mc.options.debugKeys));
for (KeyMapping kb : keys) { for (KeyMapping kb : keys) {
boolean keyAlreadyBound = kb.matches(new KeyEvent(key, 0, 0)); if (kb.matches(new KeyEvent(key, 0, 0))) {
boolean allowMultiple = ClientSettingsPanel.clientSettings.multipleKeybinds.getValue(); // la comilla iba dentro del I18n.get, así que buscaba una clave inexistente
ChatUtil.warn(Component.translatable("sputnik.commands.bind.keytaken",
if (keyAlreadyBound && !allowMultiple) { ChatFormatting.GREEN + "\"" + I18n.get(kb.getName()) + "\""));
ChatUtil.warn("esa tecla ya está cogida por "
+ ChatFormatting.GREEN + "\"" + I18n.get(kb.getName() + "\""));
listeningModule = null; listeningModule = null;
return true; return true;
} }
@@ -70,10 +73,10 @@ public class BindCommand extends Command {
if (bind != null) bind.setValue(key); if (bind != null) bind.setValue(key);
ChatUtil.info( ChatUtil.info(Component.translatable("sputnik.commands.bind.keybound",
Component.nullToEmpty("la tecla " + ChatFormatting.AQUA + KeyUtil.getKeyName(key) + ChatFormatting.RESET + ChatFormatting.AQUA + KeyUtil.getKeyName(key) + ChatFormatting.RESET,
" ha sido asignada al módulo " + ChatFormatting.GREEN + listeningModule.getName()) ChatFormatting.GREEN + listeningModule.getDisplayName()
); ));
listeningModule = null; listeningModule = null;
return true; return true;
@@ -13,7 +13,7 @@ import net.minecraft.world.inventory.ContainerInput;
public class ClickSlotCommand extends Command { public class ClickSlotCommand extends Command {
public ClickSlotCommand() { public ClickSlotCommand() {
super("clickslot", "simula un clic a un slot"); super("clickslot", "sputnik.commands.clickslot.desc", "cs");
} }
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
@@ -23,7 +23,7 @@ public class ClickSlotCommand extends Command {
.executes(context -> { .executes(context -> {
int slot = context.getArgument("slot", Integer.class), button = context.getArgument("botón", Integer.class); int slot = context.getArgument("slot", Integer.class), button = context.getArgument("botón", Integer.class);
ContainerInput action = context.getArgument("acción", ContainerInput.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; int syncId = (screen instanceof ContainerScreen cScreen) ? cScreen.getMenu().containerId : 0;
if (mc.gameMode == null) return 0; if (mc.gameMode == null) return 0;
mc.gameMode.handleContainerInput(syncId, slot, button, action, mc.player); 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 class ConfigCommand extends Command {
public ConfigCommand() { public ConfigCommand() {
super("config", "abre el marco de ajustes de un módulo", "ajustes", "settings"); super("config", "sputnik.commands.config.desc", "ajustes", "settings");
} }
@Override @Override
@@ -40,7 +40,7 @@ public class ConfigCommand extends Command {
private void openSf(CommandContext<SharedSuggestionProvider> ctx) { private void openSf(CommandContext<SharedSuggestionProvider> ctx) {
Module module = ctx.getArgument("módulo", Module.class); Module module = ctx.getArgument("módulo", Module.class);
mc.setScreen(ClickGui.INSTANCE); mc.gui.setScreen(ClickGui.INSTANCE);
if (ClickGui.INSTANCE.isSettingsPanelOpen(module)) return; 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) { private void openSfWithPos(CommandContext<SharedSuggestionProvider> ctx, int x, int y) {
Module module = ctx.getArgument("módulo", Module.class); Module module = ctx.getArgument("módulo", Module.class);
mc.setScreen(ClickGui.INSTANCE); mc.gui.setScreen(ClickGui.INSTANCE);
if (ClickGui.INSTANCE.isSettingsPanelOpen(module)) { if (ClickGui.INSTANCE.isSettingsPanelOpen(module)) {
SettingsPanel sf = ClickGui.INSTANCE.getSettingsPanelOfModule(module); SettingsPanel sf = ClickGui.INSTANCE.getSettingsPanelOfModule(module);
@@ -10,7 +10,7 @@ import net.minecraft.commands.SharedSuggestionProvider;
public class CopyScreenshotCommand extends Command { public class CopyScreenshotCommand extends Command {
public CopyScreenshotCommand() { public CopyScreenshotCommand() {
super("copiarcaptura", "copia la captura más reciente al portapapeles"); super("copiarcaptura", "sputnik.commands.copyscreenshot.desc", "copyscreenshot");
} }
@Override @Override
@@ -8,6 +8,7 @@ import me.retucio.sputnik.ui.screen.PreviewScreen;
import me.retucio.sputnik.util.ChatUtil; import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.InventoryUtil; import me.retucio.sputnik.util.InventoryUtil;
import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
// accede a los contenidos de tu enderchest // accede a los contenidos de tu enderchest
public class EnderChestCommand extends Command { public class EnderChestCommand extends Command {
@@ -15,7 +16,7 @@ public class EnderChestCommand extends Command {
private boolean shouldOpenEchest = false; private boolean shouldOpenEchest = false;
public EnderChestCommand() { public EnderChestCommand() {
super("echest", "muestra el contenido de tu enderchest (solo de ver)", "ec", "enderchest"); super("echest", "sputnik.commands.echest.desc", "ec", "enderchest");
} }
@Override @Override
@@ -24,7 +25,7 @@ public class EnderChestCommand extends Command {
mc.execute(() -> { mc.execute(() -> {
if (mc.player == null) return; if (mc.player == null) return;
if (InventoryUtil.getEchestInv() == null) { if (InventoryUtil.getEchestInv() == null) {
ChatUtil.warn("necesitas abrir un enderchest una vez primero"); ChatUtil.warn(Component.translatable( "sputnik.commands.echest.openfirst"));
return; return;
} }
@@ -40,7 +41,7 @@ public class EnderChestCommand extends Command {
if (shouldOpenEchest) { if (shouldOpenEchest) {
shouldOpenEchest = false; shouldOpenEchest = false;
if (InventoryUtil.getEchestInv() != null) 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 me.retucio.sputnik.util.ChatUtil;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
public class FriendCommand extends Command { public class FriendCommand extends Command {
public FriendCommand() { public FriendCommand() {
super("friend", "gestión de amigos", "amigo", "f"); super("friend", "sputnik.commands.friend.desc", "amigo", "f");
} }
@Override @Override
@@ -24,7 +26,10 @@ public class FriendCommand extends Command {
.executes(ctx -> { .executes(ctx -> {
Player friend = ctx.getArgument("amigo", Player.class); Player friend = ctx.getArgument("amigo", Player.class);
FriendManager.INSTANCE.add(friend.getUUID()); 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; return SUCCESS;
}) })
) )
@@ -34,14 +39,17 @@ public class FriendCommand extends Command {
.executes(ctx -> { .executes(ctx -> {
Player friend = ctx.getArgument("amigo", Player.class); Player friend = ctx.getArgument("amigo", Player.class);
FriendManager.INSTANCE.remove(friend.getUUID()); 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; return SUCCESS;
}) })
) )
) )
.then(literal("list") .then(literal("list")
.executes(ctx -> { .executes(ctx -> {
ChatUtil.info(ChatFormatting.GOLD + "amigos:"); ChatUtil.info(ChatFormatting.GOLD + Component.translatable("sputnik.commands.friend.list").getString());
for (Friend friend : FriendManager.INSTANCE.getFriends()) { for (Friend friend : FriendManager.INSTANCE.getFriends()) {
ChatUtil.info("- " + friend.getName()); ChatUtil.info("- " + friend.getName());
} }
@@ -9,14 +9,14 @@ import net.minecraft.network.chat.Component;
public class GarbageCleanerCommand extends Command { public class GarbageCleanerCommand extends Command {
public GarbageCleanerCommand() { public GarbageCleanerCommand() {
super("gc", "limpia la ram mediante el recolector de basura (gc)", "garbagecollect"); super("gc", "sputnik.commands.gc.desc", "garbagecollect");
} }
@Override @Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.executes(context -> { builder.executes(ctx -> {
System.gc(); System.gc();
ChatUtil.info(Component.nullToEmpty("basura recolectada")); ChatUtil.info(Component.translatable("sputnik.commands.gc.collected"));
return SUCCESS; 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.client.gui.screens.inventory.InventoryScreen;
import net.minecraft.commands.SharedSuggestionProvider; 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 // 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 class PeekCommand extends Command {
public PeekCommand() { public PeekCommand() {
super("inventario", "cotillea el inventario de otro jugador", "invsee", "peek", "inv"); super("inventario", "sputnik.commands.peek.desc", "invsee", "peek", "inv");
} }
@Override @Override
@@ -12,6 +12,7 @@ import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.multiplayer.ClientSuggestionProvider; import net.minecraft.client.multiplayer.ClientSuggestionProvider;
import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundCommandsPacket; import net.minecraft.network.protocol.game.ClientboundCommandsPacket;
import java.util.HashSet; import java.util.HashSet;
@@ -23,7 +24,7 @@ public class PluginsCommand extends Command {
private boolean scanning = false; private boolean scanning = false;
public PluginsCommand() { 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); Sputnik.EVENT_BUS.subscribe(this);
} }
@@ -31,12 +32,12 @@ public class PluginsCommand extends Command {
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.executes(context -> { builder.executes(context -> {
if (mc.hasSingleplayerServer()) { if (mc.hasSingleplayerServer()) {
ChatUtil.error("estás en un solo jugador"); ChatUtil.error(Component.translatable("sputnik.commands.plugins.singleplayer"));
return SUCCESS; return SUCCESS;
} }
if (scanning) { if (scanning) {
ChatUtil.error("calmaaa"); ChatUtil.error(Component.translatable("sputnik.commands.plugins.calmdown"));
return SUCCESS; return SUCCESS;
} }
@@ -60,7 +61,7 @@ public class PluginsCommand extends Command {
root.getChildren().forEach(this::checkNodeForPlugins); root.getChildren().forEach(this::checkNodeForPlugins);
if (!plugins.isEmpty()) { 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() plugins.stream()
.sorted() .sorted()
.forEach(plugin -> ChatUtil.info(" - " + plugin)); .forEach(plugin -> ChatUtil.info(" - " + plugin));
@@ -69,7 +70,7 @@ public class PluginsCommand extends Command {
displayResults(); displayResults();
} catch (Exception e) { } 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() { private void displayResults() {
if (!plugins.isEmpty()) { if (!plugins.isEmpty()) {
ChatUtil.info(plugins.size() + " plugin(s) encontrado(s)"); ChatUtil.info(Component.translatable("sputnik.commands.plugins.pluginsfound", plugins.size()));
plugins.stream().sorted() plugins.stream().sorted()
.forEach(plugin -> ChatUtil.info(" - " + plugin)); .forEach(plugin -> ChatUtil.info(" - " + plugin));
} else { } else {
ChatUtil.warn("sin plugins (probablemente)"); ChatUtil.warn(Component.translatable("sputnik.commands.plugins.noplugins"));
} }
scanning = false; scanning = false;
@@ -13,7 +13,7 @@ import net.minecraft.network.chat.Component;
public class PrefixCommand extends Command { public class PrefixCommand extends Command {
public PrefixCommand() { public PrefixCommand() {
super("prefijo", "cambia el prefijo de los comandos", "prefix"); super("prefijo", "sputnik.commands.prefix.desc", "prefix");
} }
@Override @Override
@@ -22,7 +22,7 @@ public class PrefixCommand extends Command {
.then(argument("prefijo", StringArgumentType.word()).executes(ctx -> { .then(argument("prefijo", StringArgumentType.word()).executes(ctx -> {
String prefix = ctx.getArgument("prefijo", String.class); String prefix = ctx.getArgument("prefijo", String.class);
ClientSettingsPanel.clientSettings.commandPrefix.setValue(prefix); ClientSettingsPanel.clientSettings.commandPrefix.setValue(prefix);
ChatUtil.info(Component.literal("prefijo cambiado a " + ChatFormatting.AQUA + prefix)); ChatUtil.info(Component.translatable("sputnik.commands.prefix.changed", ChatFormatting.AQUA + prefix));
return SUCCESS; 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);
}
}
@@ -3,7 +3,7 @@ package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.util.interfaces.IChatComponent; import me.retucio.sputnik.mixinterfaces.IChatComponent;
import net.minecraft.client.gui.components.ChatComponent; import net.minecraft.client.gui.components.ChatComponent;
import net.minecraft.client.multiplayer.chat.GuiMessage; import net.minecraft.client.multiplayer.chat.GuiMessage;
import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.SharedSuggestionProvider;
@@ -13,7 +13,7 @@ import java.util.List;
public class PurgeCommand extends Command { public class PurgeCommand extends Command {
public PurgeCommand() { public PurgeCommand() {
super("purgar", "elimina mensajes del chat", "purge"); super("purgar", "sputnik.commands.purge.desc", "purge");
} }
@Override @Override
@@ -28,7 +28,7 @@ public class PurgeCommand extends Command {
} }
private void purgeMessages(int amount) { private void purgeMessages(int amount) {
ChatComponent chatHud = mc.gui.getChat(); ChatComponent chatHud = mc.gui.hud.getChat();
synchronized (chatHud) { synchronized (chatHud) {
List<GuiMessage.Line> visibleMessages = ((IChatComponent) chatHud).sputnik$getVisibleMessages(); List<GuiMessage.Line> visibleMessages = ((IChatComponent) chatHud).sputnik$getVisibleMessages();
@@ -10,7 +10,7 @@ import net.minecraft.commands.SharedSuggestionProvider;
public class RepeatCommand extends Command { public class RepeatCommand extends Command {
public RepeatCommand() { public RepeatCommand() {
super("repetir", "repite un comando x veces", "repeat"); super("repetir", "sputnik.commands.repeat.desc", "repeat");
} }
@Override @Override
@@ -13,7 +13,7 @@ import java.util.concurrent.TimeUnit;
public class RepeatWaitCommand extends Command { public class RepeatWaitCommand extends Command {
public RepeatWaitCommand() { public RepeatWaitCommand() {
super("repesperar", "combinación de $repetir y $esperar"); super("repesperar", "sputnik.commands.repeatwait.desc", "repeatwait");
} }
@Override @Override
@@ -6,6 +6,7 @@ import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.misc.ScreenshotPlus; import me.retucio.sputnik.module.modules.misc.ScreenshotPlus;
import me.retucio.sputnik.util.ChatUtil; import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.io.IOException; import java.io.IOException;
@@ -13,7 +14,7 @@ import java.io.IOException;
public class SaveScreenshotCommand extends Command { public class SaveScreenshotCommand extends Command {
public SaveScreenshotCommand() { public SaveScreenshotCommand() {
super("guardarcaptura", "guarda localmente la captura más reciente"); super("guardarcaptura", "sputnik.commands.savescreenshot.desc", "savescreenshot");
} }
@Override @Override
@@ -26,7 +27,7 @@ public class SaveScreenshotCommand extends Command {
try { try {
screenshotPlus.saveScreenshot(); screenshotPlus.saveScreenshot();
} catch (IOException e) { } 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 class SendCommand extends Command {
public SendCommand() { public SendCommand() {
super("enviar", "manda un mensaje en el chat", "send"); super("enviar", "sputnik.commands.send.desc", "send");
} }
@Override @Override
@@ -37,7 +37,7 @@ public class SendCommand extends Command {
LastSeenMessagesTracker tracker = ((ClientPacketListenerAccessor) handler).getLastSeenMessagesTracker(); LastSeenMessagesTracker tracker = ((ClientPacketListenerAccessor) handler).getLastSeenMessagesTracker();
LastSeenMessagesTracker.Update lastSeenMessages = tracker.generateAndApplyUpdate(); LastSeenMessagesTracker.Update lastSeenMessages = tracker.generateAndApplyUpdate();
// empacar firma para un chat seguro // empacar firma
MessageSignature messageSignature = ((ClientPacketListenerAccessor) handler) MessageSignature messageSignature = ((ClientPacketListenerAccessor) handler)
.getSignedMessageEncoder() .getSignedMessageEncoder()
.pack(new SignedMessageBody( .pack(new SignedMessageBody(
@@ -10,16 +10,18 @@ import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import java.awt.*;
public class SpectateCommand extends Command { public class SpectateCommand extends Command {
public SpectateCommand() { 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 @Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder builder
.executes(ctx -> { // If no argument, use the entity player is looking at .executes(ctx -> {
mc.execute(() -> { mc.execute(() -> {
Entity target = EntityUtil.getEntityPlayerIsLookingAt(); Entity target = EntityUtil.getEntityPlayerIsLookingAt();
if (target != null) if (target != null)
@@ -40,7 +42,7 @@ public class SpectateCommand extends Command {
@EventListener @EventListener
public void onKey(KeyEvent event) { 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.getKey() == GLFW.GLFW_KEY_ESCAPE && event.getAction() != GLFW.GLFW_RELEASE ) {
event.cancel(); event.cancel();
mc.setCameraEntity(mc.player); mc.setCameraEntity(mc.player);
@@ -7,11 +7,13 @@ import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import net.minecraft.commands.SharedSuggestionProvider; 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 // 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 class ToggleCommand extends Command {
public ToggleCommand() { public ToggleCommand() {
super("alternar", "enciende / apaga módulos", "toggle"); super("alternar", "sputnik.commands.toggle.desc", "toggle");
} }
@Override @Override
@@ -16,7 +16,7 @@ public class WaitCommand extends Command {
private static final ScheduledExecutorService DELAYED_EXECUTOR = new ScheduledThreadPoolExecutor(2); private static final ScheduledExecutorService DELAYED_EXECUTOR = new ScheduledThreadPoolExecutor(2);
public WaitCommand() { 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 @Override
@@ -1,5 +1,7 @@
package me.retucio.sputnik.config; package me.retucio.sputnik.config;
import me.retucio.sputnik.util.I18nUtil;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
@@ -12,7 +14,8 @@ import java.util.Map;
public class BinarySerializer { public class BinarySerializer {
private static final int MAGIC_NUMBER = 0x53505554; // "SPUT" 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_NULL = 0;
private static final byte TYPE_BOOLEAN = 1; private static final byte TYPE_BOOLEAN = 1;
@@ -70,6 +73,8 @@ public class BinarySerializer {
writeStringBooleanMap(out, config.hudVisibilities); writeStringBooleanMap(out, config.hudVisibilities);
writeStringStringMap(out, config.hudImagePaths); writeStringStringMap(out, config.hudImagePaths);
writeStringStringMap(out, config.friends); writeStringStringMap(out, config.friends);
writeMacros(out, config.macros);
writeString(out, config.currentProfile);
if (config.searchBarPosition != null) { if (config.searchBarPosition != null) {
out.writeBoolean(true); out.writeBoolean(true);
@@ -83,43 +88,63 @@ public class BinarySerializer {
public static ClientConfig readConfig(File file) public static ClientConfig readConfig(File file)
throws IOException { throws IOException {
if (!file.exists() || file.length() == 0) { 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( try (DataInputStream in = new DataInputStream(
new BufferedInputStream(new FileInputStream(file)))) { new BufferedInputStream(new FileInputStream(file)))) {
if (file.length() < 8) { 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 // verificar cabezal
int magic = in.readInt(); int magic = in.readInt();
if (magic != MAGIC_NUMBER) { 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(); int version = in.readInt();
if (version != VERSION) { if (version < MIN_VERSION || version > VERSION) {
throw new IOException("versión inválida: " + 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 List<ClientConfig.MacroData> macros = version >= 3 ? readMacros(in) : new ArrayList<>();
config.moduleStates = readStringBooleanMap(in); String currentProfile = version >= 3 ? readString(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);
// leer pos. de la barra de búsqueda // leer pos. de la barra de búsqueda
if (in.readBoolean()) { int[] searchBarPosition = in.readBoolean()
config.searchBarPosition = new int[]{in.readInt(), in.readInt()}; ? 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; return config;
} }
@@ -368,6 +393,35 @@ public class BinarySerializer {
return map; 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 { private static void writeString(DataOutputStream out, String str) throws IOException {
if (str == null) { if (str == null) {
out.writeInt(-1); 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.EVENT_BUS;
import static me.retucio.sputnik.Sputnik.mc; 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 { public class ClientConfig {
// nombre del módulo -> estado del módulo // nombre del módulo -> estado del módulo
@@ -47,19 +47,31 @@ public class ClientConfig {
// amigos: uuid -> nombre // amigos: uuid -> nombre
public Map<String, String> friends = new HashMap<>(); 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() { public ClientConfig() {
EVENT_BUS.subscribe(this); 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 @EventListener
public void onToggleModule(ToggleModuleEvent event) { public void onToggleModule(ToggleModuleEvent event) {
if (isApplying()) return;
ConfigManager.INSTANCE.setModuleState(event.getModule()); ConfigManager.INSTANCE.setModuleState(event.getModule());
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@EventListener @EventListener
public void onUpdateSetting(UpdateSettingEvent event) { 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" // guardar ajustes con su respectivo tipo de valor, en formato "nombreMódulo:nombreAjuste"
Object value = null; Object value = null;
@@ -71,7 +83,8 @@ public class ClientConfig {
case StringSetting s: value = s.getValue(); break; case StringSetting s: value = s.getValue(); break;
case ListSetting l: value = l.getConfigValues(); break; case ListSetting l: value = l.getConfigValues(); break;
case ColorSetting c: value = c.getConfigValue(); 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; default: break;
} }
@@ -104,9 +117,11 @@ public class ClientConfig {
ConfigManager.INSTANCE.markDirty(); 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 @EventListener
public void onMoveModulePanel(ModulePanelEvent.Move event) { 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().getX(),
event.getPanel().getY(), event.getPanel().getY(),
event.getPanel().isExtended())); event.getPanel().isExtended()));
@@ -124,7 +139,7 @@ public class ClientConfig {
@EventListener @EventListener
public void onMoveClientSettingsPanel(ClientSettingsPanelEvent.Move event) { public void onMoveClientSettingsPanel(ClientSettingsPanelEvent.Move event) {
extendablePanels.replace("ajustes", new PanelData( extendablePanels.put("ajustes", new PanelData(
ClickGui.INSTANCE.getClientSettingsPanel().getX(), ClickGui.INSTANCE.getClientSettingsPanel().getX(),
ClickGui.INSTANCE.getClientSettingsPanel().getY(), ClickGui.INSTANCE.getClientSettingsPanel().getY(),
ClickGui.INSTANCE.getClientSettingsPanel().isExtended())); ClickGui.INSTANCE.getClientSettingsPanel().isExtended()));
@@ -142,7 +157,7 @@ public class ClientConfig {
@EventListener @EventListener
public void onMoveFriendsPanel(FriendPanelEvent.Move event) { public void onMoveFriendsPanel(FriendPanelEvent.Move event) {
extendablePanels.replace("amigos", new PanelData( extendablePanels.put("amigos", new PanelData(
ClickGui.INSTANCE.getFriendsPanel().getX(), ClickGui.INSTANCE.getFriendsPanel().getX(),
ClickGui.INSTANCE.getFriendsPanel().getY(), ClickGui.INSTANCE.getFriendsPanel().getY(),
ClickGui.INSTANCE.getFriendsPanel().isExtended())); ClickGui.INSTANCE.getFriendsPanel().isExtended()));
@@ -151,6 +166,7 @@ public class ClientConfig {
@EventListener @EventListener
public void onAddFriend(FriendEvent.Add event) { public void onAddFriend(FriendEvent.Add event) {
if (isApplying()) return;
String uuid = event.getFriend().getUuid().toString(); String uuid = event.getFriend().getUuid().toString();
String name = event.getFriend().getName(); String name = event.getFriend().getName();
if (!friends.containsKey(uuid)) { if (!friends.containsKey(uuid)) {
@@ -161,6 +177,7 @@ public class ClientConfig {
@EventListener @EventListener
public void onRemoveFriend(FriendEvent.Remove event) { public void onRemoveFriend(FriendEvent.Remove event) {
if (isApplying()) return;
friends.remove(event.getFriend().getUuid().toString()); friends.remove(event.getFriend().getUuid().toString());
ConfigManager.INSTANCE.markDirty(); ConfigManager.INSTANCE.markDirty();
} }
@@ -168,4 +185,8 @@ public class ClientConfig {
public record PanelData(int x, int y, boolean extended) implements Serializable { public record PanelData(int x, int y, boolean extended) implements Serializable {
@Serial private static final long serialVersionUID = 1L; @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.Module;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.client.ClientSettingsModule; 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.*;
import me.retucio.sputnik.module.setting.settings.*; 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.HudRenderer;
import me.retucio.sputnik.ui.widgets.Panel; 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.FriendsPanel;
import me.retucio.sputnik.ui.widgets.panels.ModulePanel; import me.retucio.sputnik.ui.widgets.panels.ModulePanel;
import me.retucio.sputnik.ui.screen.ClickGui; import me.retucio.sputnik.ui.screen.ClickGui;
import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel; import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel;
import me.retucio.sputnik.ui.widgets.panels.SettingsPanel; import me.retucio.sputnik.ui.widgets.panels.SettingsPanel;
import me.retucio.sputnik.util.ChatUtil; 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.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer; import java.util.function.Consumer;
// se ocupa de guardar, cargar y aplicar ajustes // se ocupa de guardar, cargar y aplicar ajustes
@@ -40,8 +48,9 @@ public class ConfigManager {
public static ConfigManager INSTANCE; public static ConfigManager INSTANCE;
private boolean loaded = false; private boolean loaded = false;
private boolean applying = false;
private boolean dirty = false; private boolean dirty = false;
private long lastDirtyTime = 0L; private long lastSaveTime = System.currentTimeMillis();
private ClientConfig config = null; private ClientConfig config = null;
private final ExecutorService saveExecutor = Executors.newSingleThreadExecutor(r -> { private final ExecutorService saveExecutor = Executors.newSingleThreadExecutor(r -> {
@@ -50,7 +59,10 @@ public class ConfigManager {
return t; 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 ------------------ // ------------------ MÉTODOS PRINCIPALES ------------------
@@ -58,58 +70,81 @@ public class ConfigManager {
// guardar configuraciones // guardar configuraciones
public void save() { public void save() {
if (!loaded) return; if (!loaded) return;
ensureConfig(); writeSnapshot(false);
// 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);
}
});
} }
public void saveOnShutdown() { 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(); saveExecutor.shutdown();
try { try {
// dar hasta 5 segundos para que termine el guardado en curso // dar hasta 5 segundos para que termine el guardado en curso
if (!saveExecutor.awaitTermination(5, TimeUnit.SECONDS)) { 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) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
// guardado final sincrónico por si había cambios pendientes no guardados // el guardado final va en este mismo hilo: el executor ya está cerrado, así que
if (dirty) save(); // 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() { public void load() {
Sputnik.LOGGER.info("cargando ajustes..."); Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.loading", "cargando ajustes..."));
if (CONFIG_FILE.exists()) { if (CONFIG_FILE.exists()) {
try { try {
config = BinarySerializer.readConfig(CONFIG_FILE); setConfig(BinarySerializer.readConfig(CONFIG_FILE));
Sputnik.LOGGER.info("ajustes cargados"); Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.loaded", "ajustes cargados"));
return; return;
} catch (IOException e) { } 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()) { if (LEGACY_JSON_FILE.exists()) {
try (FileReader reader = new FileReader(LEGACY_JSON_FILE)) { try (FileReader reader = new FileReader(LEGACY_JSON_FILE)) {
Gson gson = new GsonBuilder().setPrettyPrinting().create(); Gson gson = new GsonBuilder().setPrettyPrinting().create();
config = gson.fromJson(reader, ClientConfig.class); setConfig(gson.fromJson(reader, ClientConfig.class));
Sputnik.LOGGER.info("ajustes json cargados (migración)"); 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; return;
} catch (IOException e) { } 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 // crear nuevo config
ensureConfig(); ensureConfig();
save(); writeSnapshot(true);
} }
// aplicar configuraciones cargadas // aplicar configuraciones cargadas. devuelve false si todavía no se puede (falta algo por
public void applyConfig() { // construir), para que se reintente en el siguiente tick en vez de quedarse sin aplicar
if (!shouldApply()) return; 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(); applyModuleStates();
applyModuleSettings(); applyModuleSettings();
applySettingsPanels(); applySettingsPanels();
applySearchBarPosition(); applySearchBarPosition();
applyClientSettings();
applyExtendablePanels(); applyExtendablePanels();
applyFriends(); 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; loaded = true;
Sputnik.LOGGER.info("ajustes aplicados"); }
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();
}
// 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();
for (Module module : ModuleManager.INSTANCE.getModules()) {
module.setEnabled(false);
module.getSettings().forEach(Setting::reset);
}
ClientSettingsPanel.clientSettings.getSettings().forEach(Setting::reset);
for (Friend friend : new ArrayList<>(FriendManager.INSTANCE.getFriends()))
FriendManager.INSTANCE.remove(friend);
MacroManager.INSTANCE.setMacros(List.of());
} }
// obtener configuración actual // obtener configuración actual
@@ -202,6 +298,20 @@ public class ConfigManager {
markDirty(); 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 ------------------ // ------------------ APLICAR AJUSTES ------------------
@@ -211,7 +321,28 @@ public class ConfigManager {
friend.setName(name); friend.setName(name);
FriendManager.INSTANCE.add(friend); 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"}) @SuppressWarnings({"rawtypes", "unchecked"})
@@ -231,7 +362,9 @@ public class ConfigManager {
case ListSetting listSetting -> applyListSetting(listSetting, value); case ListSetting listSetting -> applyListSetting(listSetting, value);
case ColorSetting colorSetting -> applyColorSetting(colorSetting, value); case ColorSetting colorSetting -> applyColorSetting(colorSetting, value);
case OptionSetting optionSetting -> applyOptionSetting(optionSetting, 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())); 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() { private void applyModuleSettings() {
ModuleManager.INSTANCE.getModules().forEach(module -> { ModuleManager.INSTANCE.getModules().forEach(module -> {
module.getSettings().forEach(this::applySetting); 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() { private void applySettingsPanels() {
@@ -256,14 +391,16 @@ public class ConfigManager {
Module module = ModuleManager.INSTANCE.getModuleByName(moduleName); Module module = ModuleManager.INSTANCE.getModuleByName(moduleName);
if (module == null) { 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; return;
} }
ClickGui.INSTANCE.openSettingsPanel(module, position[0], position[1]); 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() { private void applySearchBarPosition() {
@@ -271,13 +408,15 @@ public class ConfigManager {
ClickGui.INSTANCE.getSearchBar().setX(config.searchBarPosition[0]); ClickGui.INSTANCE.getSearchBar().setX(config.searchBarPosition[0]);
ClickGui.INSTANCE.getSearchBar().setY(config.searchBarPosition[1]); 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() { private void applyClientSettings() {
ClientSettingsPanel.clientSettings.setEnabled(true); ClientSettingsPanel.clientSettings.setEnabled(true);
ClientSettingsPanel.clientSettings.getSettings().forEach(this::applySetting); 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() { private void applyExtendablePanels() {
@@ -287,7 +426,8 @@ public class ConfigManager {
applyExtendablePanel(ClickGui.INSTANCE.getClientSettingsPanel()); applyExtendablePanel(ClickGui.INSTANCE.getClientSettingsPanel());
applyExtendablePanel(ClickGui.INSTANCE.getFriendsPanel()); 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(); ClickGui.INSTANCE.refreshListButtons();
} }
@@ -308,14 +448,8 @@ public class ConfigManager {
panel.setX(panelData.x()); panel.setX(panelData.x());
panel.setY(panelData.y()); panel.setY(panelData.y());
if (panel instanceof ModulePanel mPanel) { if (panel instanceof ExtendablePanel<?> ePanel) {
mPanel.setExtended(panelData.extended()); ePanel.setExtended(panelData.extended());
}
if (panel instanceof ClientSettingsPanel sPanel) {
sPanel.setExtended(panelData.extended());
}
if (panel instanceof FriendsPanel fPanel) {
fPanel.setExtended(panelData.extended());
} }
} }
} }
@@ -344,8 +478,8 @@ public class ConfigManager {
} }
private static void applyStringSetting(StringSetting setting, Object value) { private static void applyStringSetting(StringSetting setting, Object value) {
if (value instanceof String) { if (value instanceof String v) {
setting.setValue((String) value); setting.setValue(v);
} }
} }
@@ -362,8 +496,14 @@ public class ConfigManager {
Map<T, Boolean> convertedValues = new HashMap<>(); Map<T, Boolean> convertedValues = new HashMap<>();
for (T option : setting.getOptions()) { for (T option : setting.getOptions()) {
String key = setting.getDisplayName(option); String key = setting.getConfigName(option);
Object booleanValue = map.get(key); 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); 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) { private static <T> void applyOptionalProperty(Map<?, ?> map, String key, Class<T> type, Consumer<T> setter) {
Object value = map.get(key); Object value = map.get(key);
if (type.isInstance(value)) { if (type.isInstance(value)) {
@@ -414,12 +560,24 @@ public class ConfigManager {
return setting.getSg().getModule().getName() + ":" + setting.getTrueName(); 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() { 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() { 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() { public boolean hasLoaded() {
@@ -428,7 +586,6 @@ public class ConfigManager {
public void markDirty() { public void markDirty() {
this.dirty = true; this.dirty = true;
this.lastDirtyTime = System.currentTimeMillis();
} }
public boolean isDirty() { public boolean isDirty() {
@@ -441,11 +598,14 @@ public class ConfigManager {
ClientSettingsModule settings = ClientSettingsPanel.clientSettings; ClientSettingsModule settings = ClientSettingsPanel.clientSettings;
if (!settings.autosave.getValue()) return; 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); 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(); 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,24 @@
package me.retucio.sputnik.event.render;
import com.github.retucio.neutrino.Event;
import net.minecraft.client.resources.model.cuboid.ItemTransform;
public class ApplyTransformationEvent extends Event {
private final ItemTransform transformation;
private final boolean leftHanded;
public ApplyTransformationEvent(ItemTransform transformation, boolean leftHanded) {
this.transformation = transformation;
this.leftHanded = leftHanded;
}
public ItemTransform getTransformation() {
return transformation;
}
public boolean isLeftHanded() {
return leftHanded;
}
}
@@ -0,0 +1,25 @@
package me.retucio.sputnik.event.render;
import com.github.retucio.neutrino.Event;
import com.mojang.blaze3d.vertex.PoseStack;
import me.retucio.sputnik.mixinterfaces.IEntityRenderState;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.entity.state.ItemEntityRenderState;
import net.minecraft.world.entity.item.ItemEntity;
public class RenderItemEntityEvent extends Event {
public final ItemEntity itemEntity;
public final ItemEntityRenderState renderState;
public final PoseStack matrixStack;
public final SubmitNodeCollector renderCommandQueue;
public final int light;
public RenderItemEntityEvent(ItemEntityRenderState renderState, PoseStack matrixStack, SubmitNodeCollector renderCommandQueue, int light) {
this.itemEntity = (ItemEntity) ((IEntityRenderState) renderState).sputnik$getEntity();
this.renderState = renderState;
this.matrixStack = matrixStack;
this.renderCommandQueue = renderCommandQueue;
this.light = light;
}
}
@@ -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;
}
}
@@ -0,0 +1,16 @@
package me.retucio.sputnik.mixin.accessors;
import net.minecraft.client.renderer.item.ItemStackRenderState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(ItemStackRenderState.class)
public interface ItemStackRenderStateAccessor {
@Accessor("activeLayerCount")
int getActiveLayerCount();
@Accessor("layers")
ItemStackRenderState.LayerRenderState[] getLayers();
}
@@ -0,0 +1,14 @@
package me.retucio.sputnik.mixin.accessors;
import net.minecraft.client.renderer.item.ItemStackRenderState;
import net.minecraft.client.resources.model.cuboid.ItemTransform;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(ItemStackRenderState.LayerRenderState.class)
public interface LayerRenderStateAccessor {
@Accessor("itemTransform")
ItemTransform sputnik$getTransform();
}
@@ -11,9 +11,7 @@ import me.retucio.sputnik.module.modules.misc.AntiInvis;
import me.retucio.sputnik.module.modules.movement.BoatFly; import me.retucio.sputnik.module.modules.movement.BoatFly;
import me.retucio.sputnik.module.modules.movement.Velocity; import me.retucio.sputnik.module.modules.movement.Velocity;
import me.retucio.sputnik.module.modules.render.Nametags; import me.retucio.sputnik.module.modules.render.Nametags;
import me.retucio.sputnik.util.interfaces.IVec3;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
@@ -25,8 +23,6 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.arrow.AbstractArrow; import net.minecraft.world.entity.projectile.arrow.AbstractArrow;
import net.minecraft.world.entity.vehicle.boat.AbstractBoat; import net.minecraft.world.entity.vehicle.boat.AbstractBoat;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.joml.Vector3d;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
@@ -3,7 +3,7 @@ package me.retucio.sputnik.mixin.mixins.entity;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import me.retucio.sputnik.module.ModuleManager; 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.client.renderer.entity.EntityRenderer;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -17,21 +17,21 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class EntityRendererMixin { public abstract class EntityRendererMixin {
@Unique @Unique
private EntityESP esp; private EntityEsp esp;
@Inject(method = "<init>", at = @At("TAIL")) @Inject(method = "<init>", at = @At("TAIL"))
private void getModules(CallbackInfo ci) { 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")) @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) { 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")) @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) { 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; return original;
} }
} }
@@ -8,6 +8,7 @@ import me.retucio.sputnik.module.modules.movement.Velocity;
import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.player.LocalPlayer;
import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundSource;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@@ -61,8 +62,8 @@ public abstract class LivingEntityMixin {
if (step.isEnabled()) cir.setReturnValue(step.height.getFloatValue()); 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) @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, CallbackInfo ci, @Local(name = "deltaMovement") Vec3 dm, @Local(name = "deltaVector") Vec3 dv) { 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); Velocity velocity = ModuleManager.INSTANCE.getModuleByClass(Velocity.class);
if (!velocity.isEnabled() || velocity.hits.getValue()) return; if (!velocity.isEnabled() || velocity.hits.getValue()) return;
boolean onGround = ((LivingEntity) (Object) this).onGround(); boolean onGround = ((LivingEntity) (Object) this).onGround();
@@ -5,10 +5,10 @@ import me.retucio.sputnik.event.network.ReceiveMessageEvent;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.misc.ChatPlus; import me.retucio.sputnik.module.modules.misc.ChatPlus;
import me.retucio.sputnik.util.interfaces.IChatComponent; import me.retucio.sputnik.mixinterfaces.IChatComponent;
import me.retucio.sputnik.util.interfaces.IGuiMessage; import me.retucio.sputnik.mixinterfaces.IGuiMessage;
import me.retucio.sputnik.util.interfaces.IGuiMessageLine; import me.retucio.sputnik.mixinterfaces.IGuiMessageLine;
import me.retucio.sputnik.util.interfaces.IChatListener; import me.retucio.sputnik.mixinterfaces.IChatListener;
import net.minecraft.client.gui.components.ChatComponent; import net.minecraft.client.gui.components.ChatComponent;
import net.minecraft.client.multiplayer.chat.GuiMessage; import net.minecraft.client.multiplayer.chat.GuiMessage;
@@ -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;")) @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) { 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; IGuiMessageLine iLine = (IGuiMessageLine) (Object) line;
iLine.sputnik$setSender(iListener.sputnik$getSender()); 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;")) @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) { 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()); ((IGuiMessage) (Object) line).sputnik$setSender(iListener.sputnik$getSender());
return line; return line;
} }
@@ -1,7 +1,7 @@
package me.retucio.sputnik.mixin.mixins.hud; package me.retucio.sputnik.mixin.mixins.hud;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import me.retucio.sputnik.util.interfaces.IGuiMessageLine; import me.retucio.sputnik.mixinterfaces.IGuiMessageLine;
import net.minecraft.client.multiplayer.chat.GuiMessage; import net.minecraft.client.multiplayer.chat.GuiMessage;
import net.minecraft.util.FormattedCharSequence; import net.minecraft.util.FormattedCharSequence;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
@@ -1,7 +1,7 @@
package me.retucio.sputnik.mixin.mixins.hud; package me.retucio.sputnik.mixin.mixins.hud;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import me.retucio.sputnik.util.interfaces.IGuiMessage; import me.retucio.sputnik.mixinterfaces.IGuiMessage;
import net.minecraft.client.multiplayer.chat.GuiMessage; import net.minecraft.client.multiplayer.chat.GuiMessage;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
@@ -1,107 +1,21 @@
package me.retucio.sputnik.mixin.mixins.hud; 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.Sputnik;
import me.retucio.sputnik.event.render.Render2DEvent; import me.retucio.sputnik.event.interact.OpenScreenEvent;
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.Gui; import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.resources.Identifier;
import org.spongepowered.asm.mixin.Mixin; 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.At;
import org.spongepowered.asm.mixin.injection.Inject; 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.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) @Mixin(Gui.class)
public abstract class GuiMixin { public abstract class GuiMixin {
@Unique @Inject(method = "setScreen", at = @At("HEAD"), cancellable = true)
NoRender noRender; private void onOpenScreen(Screen screen, CallbackInfo ci) {
OpenScreenEvent event = Sputnik.EVENT_BUS.post(new OpenScreenEvent(screen));
@Inject(method = "<init>", at = @At("TAIL")) if (event.isCancelled()) ci.cancel();
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();
} }
} }
@@ -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 me.retucio.sputnik.module.modules.misc.ScreenshotPlus;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.Screenshot; import net.minecraft.client.Screenshot;
import net.minecraft.network.chat.Component;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -19,14 +20,14 @@ import java.util.function.Consumer;
@Mixin(Screenshot.class) @Mixin(Screenshot.class)
public abstract class ScreenshotMixin { 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) @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<?> callback, CallbackInfo ci) { private static void sendActionsMessage(NativeImage image, File file, Consumer<Component> callback, CallbackInfo ci) {
ScreenshotPlus screenshotPlus = getScreenshotPlus(); ScreenshotPlus screenshotPlus = getScreenshotPlus();
if (screenshotPlus.isEnabled()) ci.cancel(); 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) @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<?> callback, CallbackInfo ci) throws IOException { private static void interceptScreenshotFile(NativeImage image, File file, Consumer<Component> callback, CallbackInfo ci) throws IOException {
ScreenshotPlus screenshotPlus = getScreenshotPlus(); ScreenshotPlus screenshotPlus = getScreenshotPlus();
// aunque el módulo esté apagado, pasar las referencias para poder usar los comandos para las capturas // aunque el módulo esté apagado, pasar las referencias para poder usar los comandos para las capturas
@@ -0,0 +1,36 @@
package me.retucio.sputnik.mixin.mixins.item;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.event.render.RenderItemEntityEvent;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.entity.ItemEntityRenderer;
import net.minecraft.client.renderer.entity.state.ItemEntityRenderState;
import net.minecraft.client.renderer.state.level.CameraRenderState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ItemEntityRenderer.class)
public class ItemEntityRendererMixin {
@Inject(method = "submit(Lnet/minecraft/client/renderer/entity/state/ItemEntityRenderState;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;Lnet/minecraft/client/renderer/state/level/CameraRenderState;)V", at = @At("HEAD"), cancellable = true)
private void onSubmit(ItemEntityRenderState state, PoseStack poseStack, SubmitNodeCollector submitNodeCollector, CameraRenderState camera, CallbackInfo ci) {
RenderItemEntityEvent event = Sputnik.EVENT_BUS.post(
new RenderItemEntityEvent(
state,
poseStack,
submitNodeCollector,
state.lightCoords
)
);
if (event.isCancelled()) {
ci.cancel();
}
}
}
@@ -27,13 +27,13 @@ public abstract class ItemMixin {
if (stack.getItem() instanceof BlockItem blockItem && blockItem.getBlock() instanceof ShulkerBoxBlock) { if (stack.getItem() instanceof BlockItem blockItem && blockItem.getBlock() instanceof ShulkerBoxBlock) {
if (ShulkerPeek.isShulkerEmpty(stack)) { 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 { } else {
builder.accept( builder.accept(
Component.literal("mantén ") Component.translatable("sputnik.module.shulkerpeek.holdtopreview",
.append(Component.literal(shulkerPeek.previewKey.getKeyName()) Component.literal(shulkerPeek.previewKey.getKeyName())
.withStyle(ChatFormatting.GOLD, ChatFormatting.BOLD)) .withStyle(ChatFormatting.GOLD, ChatFormatting.BOLD))
.append(" para previsualizar")
.withStyle(ChatFormatting.GRAY) .withStyle(ChatFormatting.GRAY)
); );
} }
@@ -0,0 +1,21 @@
package me.retucio.sputnik.mixin.mixins.item;
import com.mojang.blaze3d.vertex.PoseStack;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.event.render.ApplyTransformationEvent;
import net.minecraft.client.resources.model.cuboid.ItemTransform;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ItemTransform.class)
public abstract class ItemTransformMixin {
@Inject(method = "apply", at = @At("HEAD"), cancellable = true)
private void onApply(boolean applyLeftHandFix, PoseStack.Pose pose, CallbackInfo ci) {
ApplyTransformationEvent event = Sputnik.EVENT_BUS.post(new ApplyTransformationEvent((ItemTransform) (Object) this, applyLeftHandFix));
if (event.isCancelled()) {
ci.cancel();
}
}
}
@@ -3,16 +3,14 @@ package me.retucio.sputnik.mixin.mixins.misc;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.event.network.JoinWorldEvent; import me.retucio.sputnik.event.network.JoinWorldEvent;
import me.retucio.sputnik.event.interact.OpenScreenEvent;
import me.retucio.sputnik.event.ShutdownEvent; import me.retucio.sputnik.event.ShutdownEvent;
import me.retucio.sputnik.event.TickEvent; import me.retucio.sputnik.event.TickEvent;
import me.retucio.sputnik.event.interact.UseItemEvent; import me.retucio.sputnik.event.interact.UseItemEvent;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.camera.Freecam; import me.retucio.sputnik.module.modules.camera.Freecam;
import me.retucio.sputnik.module.modules.player.FastUse; import me.retucio.sputnik.module.modules.player.FastUse;
import me.retucio.sputnik.util.interfaces.IVec3; import me.retucio.sputnik.mixinterfaces.IVec3;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
@@ -38,9 +36,6 @@ public abstract class MinecraftMixin {
// eventos // eventos
@Shadow @Nullable
public Screen screen;
@Shadow @Shadow
public abstract @Nullable Entity getCameraEntity(); public abstract @Nullable Entity getCameraEntity();
@@ -63,12 +58,6 @@ public abstract class MinecraftMixin {
Sputnik.EVENT_BUS.post(new ShutdownEvent()); 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) @Inject(method = "setLevel", at = @At("HEAD"), cancellable = true)
private void onJoinWorld(ClientLevel level, CallbackInfo ci) { private void onJoinWorld(ClientLevel level, CallbackInfo ci) {
JoinWorldEvent event = Sputnik.EVENT_BUS.post(new JoinWorldEvent(level)); JoinWorldEvent event = Sputnik.EVENT_BUS.post(new JoinWorldEvent(level));
@@ -1,6 +1,6 @@
package me.retucio.sputnik.mixin.mixins.misc; package me.retucio.sputnik.mixin.mixins.misc;
import me.retucio.sputnik.util.interfaces.IVec3; import me.retucio.sputnik.mixinterfaces.IVec3;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -1,7 +1,7 @@
package me.retucio.sputnik.mixin.mixins.network; package me.retucio.sputnik.mixin.mixins.network;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import me.retucio.sputnik.util.interfaces.IChatListener; import me.retucio.sputnik.mixinterfaces.IChatListener;
import net.minecraft.client.multiplayer.chat.ChatListener; import net.minecraft.client.multiplayer.chat.ChatListener;
import net.minecraft.network.chat.ChatType; import net.minecraft.network.chat.ChatType;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
@@ -4,7 +4,7 @@ import me.retucio.sputnik.mixin.accessors.PackConfirmScreenAccessor;
import me.retucio.sputnik.mixin.accessors.PendingRequestAccessor; import me.retucio.sputnik.mixin.accessors.PendingRequestAccessor;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.network.RPackBypass; import me.retucio.sputnik.module.modules.network.RPackBypass;
import me.retucio.sputnik.util.interfaces.IConfirmScreen; import me.retucio.sputnik.mixinterfaces.IConfirmScreen;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl; import net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl;
@@ -46,7 +46,7 @@ public abstract class ClientCommonPacketListenerImplMixin {
final Screen screen = cir.getReturnValue(); final Screen screen = cir.getReturnValue();
((IConfirmScreen) screen).sputnik$setBypassAction(() -> { ((IConfirmScreen) screen).sputnik$setBypassAction(() -> {
minecraft.setScreen(((PackConfirmScreenAccessor) screen).getParentScreen()); minecraft.gui.setScreen(((PackConfirmScreenAccessor) screen).getParentScreen());
if (serverData != null) { if (serverData != null) {
serverData.setResourcePackStatus(bypassPack.getStatus()); serverData.setResourcePackStatus(bypassPack.getStatus());
minecraft.getDownloadedPackSource().allowServerPacks(); minecraft.getDownloadedPackSource().allowServerPacks();
@@ -48,7 +48,7 @@ public abstract class ClientPacketListenerMixin {
ChatUtil.error(e.getMessage()); ChatUtil.error(e.getMessage());
} }
mc.gui.getChat().addRecentChat(content); mc.gui.hud.getChat().addRecentChat(content);
ci.cancel(); 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 com.llamalad7.mixinextras.sugar.Local;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.world.NoMiningInterruptions; 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.gui.screens.Screen;
import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -28,9 +26,9 @@ public abstract class LocalPlayerMixin {
throw new UnsupportedOperationException("Implemented via mixin"); 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) { 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; return original;
} }
@@ -1,5 +1,6 @@
package me.retucio.sputnik.mixin.mixins.render; package me.retucio.sputnik.mixin.mixins.render;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.event.render.GetFOVEvent; import me.retucio.sputnik.event.render.GetFOVEvent;
@@ -78,6 +79,13 @@ public abstract class CameraMixin {
if (freecam.isEnabled()) this.detached = true; 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")) @ModifyArgs(method = "alignWithEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setPosition(DDD)V"))
private void onUpdateSetPosArgs(Args args) { private void onUpdateSetPosArgs(Args args) {
if (freecam.isEnabled()) { if (freecam.isEnabled()) {
@@ -0,0 +1,22 @@
package me.retucio.sputnik.mixin.mixins.render;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import me.retucio.sputnik.mixinterfaces.IEntityRenderState;
import net.minecraft.client.renderer.entity.EntityRenderDispatcher;
import net.minecraft.client.renderer.entity.state.EntityRenderState;
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(EntityRenderDispatcher.class)
public abstract class EntityRenderDispatcherMixin {
@ModifyExpressionValue(
method = "extractEntity(Lnet/minecraft/world/entity/Entity;F)Lnet/minecraft/client/renderer/entity/state/EntityRenderState;",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/EntityRenderer;createRenderState(Lnet/minecraft/world/entity/Entity;F)Lnet/minecraft/client/renderer/entity/state/EntityRenderState;")
)
private <E extends Entity> EntityRenderState getAndUpdateRenderState$setEntity(EntityRenderState state, E entity, float partialTicks) {
((IEntityRenderState) state).sputnik$setEntity(entity);
return state;
}
}
@@ -0,0 +1,26 @@
package me.retucio.sputnik.mixin.mixins.render;
import me.retucio.sputnik.mixinterfaces.IEntityRenderState;
import net.minecraft.client.renderer.entity.state.EntityRenderState;
import net.minecraft.world.entity.Entity;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(EntityRenderState.class)
public abstract class EntityRenderStateMixin implements IEntityRenderState {
@Unique
private Entity entity;
@Override
@Nullable
public Entity sputnik$getEntity() {
return entity;
}
@Override
public void sputnik$setEntity(Entity entity) {
this.entity = entity;
}
}
@@ -6,15 +6,16 @@ import me.retucio.sputnik.event.render.GetFOVEvent;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.camera.Freecam; import me.retucio.sputnik.module.modules.camera.Freecam;
import me.retucio.sputnik.module.modules.misc.UnfocusedCpu; 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.render.NoRender;
import me.retucio.sputnik.module.modules.camera.Zoom; 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.DeltaTracker;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.ItemInHandRenderer; import net.minecraft.client.renderer.ItemInHandRenderer;
import net.minecraft.client.renderer.RenderBuffers;
import net.minecraft.client.renderer.state.level.CameraRenderState; import net.minecraft.client.renderer.state.level.CameraRenderState;
import net.minecraft.client.resources.model.ModelManager; import net.minecraft.client.resources.model.ModelManager;
import org.joml.Matrix4fc; import org.joml.Matrix4fc;
@@ -39,15 +40,31 @@ public abstract class GameRendererMixin {
@Unique Zoom zoom; @Unique Zoom zoom;
@Unique Freecam freecam; @Unique Freecam freecam;
@Unique NoRender noRender; @Unique NoRender noRender;
@Unique Lsd lsd;
@Shadow @Final @Shadow @Final
private Minecraft minecraft; private Minecraft minecraft;
@Shadow @Final
private RenderTarget mainRenderTarget;
@Shadow @Final
private CrossFrameResourcePool resourcePool;
@Inject(method = "<init>", at = @At("TAIL")) @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);; zoom = ModuleManager.INSTANCE.getModuleByClass(Zoom.class);;
freecam = ModuleManager.INSTANCE.getModuleByClass(Freecam.class); freecam = ModuleManager.INSTANCE.getModuleByClass(Freecam.class);
noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.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) @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 // 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() { private static RenderType redirectGlint() {
GlintPlus glintPlus = ModuleManager.INSTANCE.getModuleByClass(GlintPlus.class); GlintPlus glintPlus = ModuleManager.INSTANCE.getModuleByClass(GlintPlus.class);
if (glintPlus == null) return RenderTypes.glint(); if (glintPlus == null) return RenderTypes.glint();
return glintPlus.getGlint(); return glintPlus.getGlint();
} }
@Redirect(method = "getFoilRenderType", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/rendertype/RenderTypes;entityGlint()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 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;"))
private static RenderType redirectGlintTranslucent() { private static RenderType redirectGlintTranslucent() {
GlintPlus glintPlus = ModuleManager.INSTANCE.getModuleByClass(GlintPlus.class); GlintPlus glintPlus = ModuleManager.INSTANCE.getModuleByClass(GlintPlus.class);
if (glintPlus == null) return RenderTypes.glintTranslucent(); if (glintPlus == null) return RenderTypes.glintTranslucent();
@@ -49,12 +49,12 @@ public abstract class ItemInHandRendererMixin {
handView = ModuleManager.INSTANCE.getModuleByClass(HandView.class); 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) { 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)); 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) { 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)); 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.event.render.RenderBlockOutlineEvent;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.camera.Freecam; import me.retucio.sputnik.module.modules.camera.Freecam;
import me.retucio.sputnik.util.render.SputnikBuffers;
import net.minecraft.client.DeltaTracker; import net.minecraft.client.DeltaTracker;
import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.chunk.ChunkSectionsToRender;
import net.minecraft.client.renderer.state.level.CameraRenderState; import net.minecraft.client.renderer.state.level.CameraRenderState;
import net.minecraft.client.renderer.state.level.LevelRenderState; import net.minecraft.client.renderer.state.level.LevelRenderState;
import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.util.profiling.ProfilerFiller;
@@ -31,14 +31,8 @@ import static me.retucio.sputnik.Sputnik.mc;
@Mixin(LevelRenderer.class) @Mixin(LevelRenderer.class)
public abstract class LevelRendererMixin { public abstract class LevelRendererMixin {
// renderLevel()?? @Inject(method = "render", at = @At("RETURN"))
@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 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) {
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) {
if (mc == null || mc.level == null) return; if (mc == null || mc.level == null) return;
PoseStack matrices = new PoseStack(); PoseStack matrices = new PoseStack();
@@ -49,13 +43,14 @@ public abstract class LevelRendererMixin {
profiler.push(Sputnik.MOD_ID + "-3d"); profiler.push(Sputnik.MOD_ID + "-3d");
EVENT_BUS.post(new Render3DEvent(matrices, dt, cameraState)); EVENT_BUS.post(new Render3DEvent(matrices, dt, cameraState));
SputnikBuffers.flush(); // antes de que empiece el hud
profiler.pop(); profiler.pop();
matrices.popPose(); matrices.popPose();
} }
@Inject(method = "renderBlockOutline", at = @At("HEAD"), cancellable = true) @Inject(method = "submitBlockOutline", at = @At("HEAD"), cancellable = true)
private void noRenderBlockOutlinesFreecam(MultiBufferSource.BufferSource bufferSource, PoseStack poseStack, boolean onlyTranslucentBlocks, LevelRenderState levelRenderState, CallbackInfo ci) { private void noRenderBlockOutlinesFreecam(PoseStack poseStack, SubmitNodeCollector collector, LevelRenderState levelRenderState, CallbackInfo ci) {
Freecam freecam = ModuleManager.INSTANCE.getModuleByClass(Freecam.class); Freecam freecam = ModuleManager.INSTANCE.getModuleByClass(Freecam.class);
if (freecam.isEnabled() && !freecam.blockOutlines.getValue()) ci.cancel(); if (freecam.isEnabled() && !freecam.blockOutlines.getValue()) ci.cancel();
} }
@@ -68,13 +63,13 @@ public abstract class LevelRendererMixin {
// } // }
// puto mojang con sus nombres confusos // 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) { private void modifyBlockOutline(Args args) {
// SIX SEVEN!!! (me pegan en casa) // ahora cuatro cinco... (rip 67)
RenderBlockOutlineEvent event = EVENT_BUS.post(new RenderBlockOutlineEvent(args.get(6), args.get(7))); RenderBlockOutlineEvent event = EVENT_BUS.post(new RenderBlockOutlineEvent(args.get(4), args.get(5)));
if (event.getColor() != (int) args.get(6)) if (event.getColor() != (int) args.get(4))
args.set(6, event.getColor()); args.set(4, event.getColor());
if (event.getLineWidth() != (float) args.get(7)) if (event.getLineWidth() != (float) args.get(5))
args.set(7, event.getLineWidth()); args.set(5, event.getLineWidth());
} }
} }
@@ -31,7 +31,7 @@ public abstract class LightmapMixin {
profiler.push("lightmap"); profiler.push("lightmap");
RenderSystem.getDevice().createCommandEncoder() RenderSystem.getDevice().createCommandEncoder()
.clearColorTexture(texture, ARGB.color(255, 255, 255, 255)); .clearColorTexture(texture, ARGB.vector4fFromARGB32(ARGB.color(255, 255, 255, 255)));
profiler.pop(); profiler.pop();
ci.cancel(); ci.cancel();
@@ -9,6 +9,7 @@ import me.retucio.sputnik.module.modules.render.Nametags;
import net.minecraft.client.renderer.entity.LivingEntityRenderer; import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.EntityTypes;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import org.spongepowered.asm.mixin.Mixin; 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")) @ModifyReturnValue(method = "shouldShowName(Lnet/minecraft/world/entity/LivingEntity;D)Z", at = @At("RETURN"))
private boolean shouldRenderPlayerNametag(boolean original, @Local(argsOnly = true) T livingEntity) { private boolean shouldRenderPlayerNametag(boolean original, @Local(argsOnly = true) T livingEntity) {
if (nametags.isEnabled() && livingEntity instanceof Player) if (nametags.isEnabled() && livingEntity instanceof Player)
return nametags.entities.isEnabled(EntityType.PLAYER) && original; return nametags.entities.isEnabled(EntityTypes.PLAYER) && original;
return original; return original;
} }
@@ -1,25 +1,18 @@
package me.retucio.sputnik.mixin.mixins.render; package me.retucio.sputnik.mixin.mixins.render;
import com.mojang.blaze3d.vertex.ByteBufferBuilder; import me.retucio.sputnik.util.render.SputnikBuffers;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import me.retucio.sputnik.util.render.GlintRenderType;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderBuffers; import net.minecraft.client.renderer.RenderBuffers;
import net.minecraft.client.renderer.rendertype.RenderType;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.SequencedMap;
@Mixin(RenderBuffers.class) @Mixin(RenderBuffers.class)
public abstract class RenderBuffersMixin { 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)) // devolvemos nuestros buffers al pool cuando el juego hace lo propio
private MultiBufferSource.BufferSource injectGlintBuffers(SequencedMap<RenderType, ByteBufferBuilder> fixedBuffers, ByteBufferBuilder sharedBuffer) { @Inject(method = "endFrame", at = @At("TAIL"))
if (fixedBuffers instanceof Object2ObjectLinkedOpenHashMap<RenderType, ByteBufferBuilder> map) { private void endSputnikFrame(CallbackInfo ci) {
GlintRenderType.addGlintTypes(map); SputnikBuffers.endFrame();
}
return MultiBufferSource.immediateWithBuffers(fixedBuffers, sharedBuffer);
} }
} }
@@ -4,8 +4,8 @@ import com.mojang.blaze3d.vertex.PoseStack;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.render.NoRender; import me.retucio.sputnik.module.modules.render.NoRender;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.ScreenEffectRenderer; import net.minecraft.client.renderer.ScreenEffectRenderer;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
@@ -25,13 +25,13 @@ public abstract class ScreenEffectRendererMixin {
noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class); noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class);
} }
@Inject(method = "renderFire", at = @At("HEAD"), cancellable = true) @Inject(method = "submitFire", at = @At("HEAD"), cancellable = true)
private static void noRenderFireOverlay(PoseStack matrices, MultiBufferSource bufferSource, TextureAtlasSprite sprite, CallbackInfo ci) { private static void noRenderFireOverlay(PoseStack matrices, SubmitNodeCollector collector, TextureAtlasSprite sprite, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.fireOverlay.getValue()) ci.cancel(); if (noRender.isEnabled() && !noRender.fireOverlay.getValue()) ci.cancel();
} }
@Inject(method = "renderWater", at = @At("HEAD"), cancellable = true) @Inject(method = "submitWater", at = @At("HEAD"), cancellable = true)
private static void noRenderUnderwaterOverlay(Minecraft minecraft, PoseStack matrices, MultiBufferSource bufferSource, CallbackInfo ci) { private static void noRenderUnderwaterOverlay(Minecraft minecraft, PoseStack matrices, SubmitNodeCollector collector, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.fluidOverlay.getValue()) ci.cancel(); if (noRender.isEnabled() && !noRender.fluidOverlay.getValue()) ci.cancel();
} }
} }
@@ -1,6 +1,7 @@
package me.retucio.sputnik.mixin.mixins.render; package me.retucio.sputnik.mixin.mixins.render;
import me.retucio.sputnik.util.render.Pipelines; import me.retucio.sputnik.util.render.Pipelines;
import me.retucio.sputnik.util.render.PostShaders;
import net.minecraft.client.renderer.ShaderManager; import net.minecraft.client.renderer.ShaderManager;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; 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")) @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) { private void onReload(CallbackInfo ci) {
Pipelines.init(); 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.ModuleManager;
import me.retucio.sputnik.module.modules.render.NoRender; 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.WeatherEffectRenderer;
import net.minecraft.client.renderer.state.level.WeatherRenderState; import net.minecraft.client.renderer.state.level.WeatherRenderState;
import net.minecraft.server.level.ParticleStatus;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
@@ -26,11 +23,6 @@ public abstract class WeatherEffectRendererMixin {
noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class); 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")) @Inject(method = "render", at = @At("HEAD"))
private void noRenderRainAndSnow(Vec3 cameraPos, WeatherRenderState renderState, CallbackInfo ci) { private void noRenderRainAndSnow(Vec3 cameraPos, WeatherRenderState renderState, CallbackInfo ci) {
if (noRender.isEnabled()) { if (noRender.isEnabled()) {
@@ -33,14 +33,14 @@ public abstract class AbstractContainerScreenMixin {
int by = bgTop - bh - 5; int by = bgTop - bh - 5;
if (burglar.showStealButton.getValue()) { 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); burglar.steal(screen);
}).bounds(bx, by, bw, bh).build(); }).bounds(bx, by, bw, bh).build();
screen.addRenderableWidget(stealButton); screen.addRenderableWidget(stealButton);
} }
if (burglar.showDumpButton.getValue()) { 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); burglar.dump(screen);
}).bounds(bx + bw + gap, by, bw, bh).build(); }).bounds(bx + bw + gap, by, bw, bh).build();
screen.addRenderableWidget(dumpButton); screen.addRenderableWidget(dumpButton);
@@ -2,7 +2,7 @@ package me.retucio.sputnik.mixin.mixins.screen;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.network.RPackBypass; import me.retucio.sputnik.module.modules.network.RPackBypass;
import me.retucio.sputnik.util.interfaces.IConfirmScreen; import me.retucio.sputnik.mixinterfaces.IConfirmScreen;
import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.layouts.LinearLayout; import net.minecraft.client.gui.layouts.LinearLayout;
@@ -27,7 +27,7 @@ public abstract class DisconnectedScreenMixin {
if (reconnect == null || !reconnect.isEnabled()) return; if (reconnect == null || !reconnect.isEnabled()) return;
layout.addChild(Button.builder( layout.addChild(Button.builder(
Component.literal("reconectarse"), Component.translatable("sputnik.module.reconnect.button"),
button -> reconnect.reconnect() button -> reconnect.reconnect()
).build()); ).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.AddEntityEvent;
import me.retucio.sputnik.event.network.RemoveEntityEvent; 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.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -29,4 +31,11 @@ public abstract class ClientLevelMixin {
Entity entity = getEntity(entityId); Entity entity = getEntity(entityId);
if (entity != null) EVENT_BUS.post(new RemoveEntityEvent(entity)); 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,4 +1,4 @@
package me.retucio.sputnik.util.interfaces; package me.retucio.sputnik.mixinterfaces;
import net.minecraft.client.multiplayer.chat.GuiMessage; import net.minecraft.client.multiplayer.chat.GuiMessage;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
@@ -1,4 +1,4 @@
package me.retucio.sputnik.util.interfaces; package me.retucio.sputnik.mixinterfaces;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
@@ -1,4 +1,4 @@
package me.retucio.sputnik.util.interfaces; package me.retucio.sputnik.mixinterfaces;
public interface IConfirmScreen { public interface IConfirmScreen {
@@ -0,0 +1,13 @@
package me.retucio.sputnik.mixinterfaces;
import net.minecraft.world.entity.Entity;
import org.jspecify.annotations.Nullable;
public interface IEntityRenderState {
@Nullable
Entity sputnik$getEntity();
void sputnik$setEntity(Entity entity);
}
@@ -1,4 +1,4 @@
package me.retucio.sputnik.util.interfaces; package me.retucio.sputnik.mixinterfaces;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
@@ -1,4 +1,4 @@
package me.retucio.sputnik.util.interfaces; package me.retucio.sputnik.mixinterfaces;
public interface IGuiMessageLine extends IGuiMessage { public interface IGuiMessageLine extends IGuiMessage {
@@ -1,4 +1,4 @@
package me.retucio.sputnik.util.interfaces; package me.retucio.sputnik.mixinterfaces;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
import org.joml.Vector3d; import org.joml.Vector3d;
@@ -1,6 +1,6 @@
/* code by meteorclient dev team */ /* code by meteorclient dev team */
package me.retucio.sputnik.util.interfaces; package me.retucio.sputnik.mixinterfaces;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.Style; import net.minecraft.network.chat.Style;
@@ -1,5 +1,7 @@
package me.retucio.sputnik.module; package me.retucio.sputnik.module;
import me.retucio.sputnik.util.I18nUtil;
public enum Category { public enum Category {
COMBAT("combate"), COMBAT("combate"),
@@ -19,5 +21,12 @@ public enum Category {
private final String name; private final String name;
Category(String name) { this.name = 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; } @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.module.setting.Setting;
import me.retucio.sputnik.ui.hud.HudRenderer; import me.retucio.sputnik.ui.hud.HudRenderer;
import me.retucio.sputnik.util.ChatUtil; import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.I18nUtil;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@@ -33,34 +35,36 @@ public class Module {
// ajustes que están en todos los módulos // 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( protected KeySetting bind = new KeySetting(
"tecla", "tecla",
"tecla asignada al módulo, ESC para desactivar", "tecla asignada al módulo, ESC para desactivar",
GLFW.GLFW_KEY_UNKNOWN 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 // 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", "modo de tecla",
"cómo interpretar la tecla configurada", "cómo interpretar la tecla configurada",
KeyMode.class, KeyMode.TOGGLE KeyMode.class, KeyMode.TOGGLE
); ).translationKey("sputnik.setting.common.keymode");
protected BooleanSetting notify = new BooleanSetting( protected BooleanSetting notify = new BooleanSetting(
"notificar", "notificar",
"notificar al activar / desactivar", "notificar al activar / desactivar",
true true
); ).translationKey("sputnik.setting.common.notify");
protected EnumSetting<NotifyMode> notifyMode = new EnumSetting<>( protected EnumSetting<NotifyMode> notifyMode = new EnumSetting<NotifyMode>(
"modo de notificación", "modo de notificación",
"cómo notificar cuando se activa / desactiva", "cómo notificar cuando se activa / desactiva",
NotifyMode.class, NotifyMode.WIDGET NotifyMode.class, NotifyMode.WIDGET
); ).translationKey("sputnik.setting.common.notifymode");
protected final List<SettingGroup> sgs = new ArrayList<>(); protected final List<SettingGroup> sgs = new ArrayList<>();
protected final SettingGroup sgModule = addSg(new SettingGroup("módulo", false)); protected final SettingGroup sgModule = addSg(new SettingGroup("módulo", false, "sputnik.group.common.module"));
protected final SettingGroup sgGeneral = addSg(new SettingGroup("general", true)); protected final SettingGroup sgGeneral = addSg(new SettingGroup("general", true, "sputnik.group.common.general"));
protected static final Minecraft mc = Minecraft.getInstance(); protected static final Minecraft mc = Minecraft.getInstance();
@@ -80,7 +84,7 @@ public class Module {
// puto marcos // puto marcos
if (ConfigManager.INSTANCE.getConfig().settings if (ConfigManager.INSTANCE.getConfig().settings
.get(this.getName() + ":" + bind.getName()) == null) .get(this.getName() + ":" + bind.getTrueName()) == null)
this.bind.setValue(key); this.bind.setValue(key);
} }
@@ -101,9 +105,10 @@ public class Module {
return sgs; return sgs;
} }
// por nombre interno, no por el traducido (igual que SettingGroup#getSetting)
public SettingGroup getSg(String name) { public SettingGroup getSg(String name) {
for (SettingGroup sg : sgs) { for (SettingGroup sg : sgs) {
if (sg.getName().equals(name)) { if (sg.getTrueName().equals(name)) {
return sg; return sg;
} }
} }
@@ -134,10 +139,7 @@ public class Module {
} }
public void onEnable() { public void onEnable() {
if (notify.getValue()) { notifyToggle(true);
if (notifyMode.is(NotifyMode.CHAT)) ChatUtil.info(getName() + " fue activado");
else HudRenderer.INSTANCE.pushNotification(getName() + " fue activado", getName() + " fue activado.");
}
for (Method method : getClass().getDeclaredMethods()) { for (Method method : getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(EventListener.class)) { if (method.isAnnotationPresent(EventListener.class)) {
Sputnik.EVENT_BUS.subscribe(this); Sputnik.EVENT_BUS.subscribe(this);
@@ -147,13 +149,24 @@ public class Module {
} }
public void onDisable() { public void onDisable() {
if (notifyMode.is(NotifyMode.CHAT)) ChatUtil.info(getName() + " fue desactivado"); notifyToggle(false);
else HudRenderer.INSTANCE.pushNotification(getName() + " fue desactivado", getName() + " fue desactivado.");
if (Sputnik.EVENT_BUS.isSubscribed(this)) { if (Sputnik.EVENT_BUS.isSubscribed(this)) {
Sputnik.EVENT_BUS.unsubscribe(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) // tick (ejecutado 20 veces por segundo)
public void onTick() {} public void onTick() {}
@@ -161,6 +174,8 @@ public class Module {
// getters y setters // 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() { public String getName() {
return name; return name;
} }
@@ -168,6 +183,19 @@ public class Module {
this.name = name; 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() { public String getDescription() {
return description; return description;
} }
@@ -2,7 +2,7 @@ package me.retucio.sputnik.module;
import com.github.retucio.neutrino.EventListener; import com.github.retucio.neutrino.EventListener;
import me.retucio.sputnik.Sputnik; 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.Friends;
import me.retucio.sputnik.module.modules.client.Hud; import me.retucio.sputnik.module.modules.client.Hud;
import me.retucio.sputnik.module.modules.camera.*; 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.misc.*;
import me.retucio.sputnik.module.modules.movement.*; import me.retucio.sputnik.module.modules.movement.*;
import me.retucio.sputnik.module.modules.network.*; 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.player.*;
import me.retucio.sputnik.module.modules.render.*; import me.retucio.sputnik.module.modules.render.*;
import me.retucio.sputnik.module.modules.render.CritsPlus; import me.retucio.sputnik.module.modules.render.CritsPlus;
@@ -48,7 +49,7 @@ public class ModuleManager {
addRender(); addRender();
addWorld(); 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 // registrar los "listeners" necesarios
for (Module module : getEnabledModules()) { for (Module module : getEnabledModules()) {
@@ -79,7 +80,7 @@ public class ModuleManager {
} }
private void addClient() { private void addClient() {
add(new DiscordRPC()); add(new DiscordRpc());
add(new Friends()); add(new Friends());
add(new Hud()); add(new Hud());
add(new Notifications()); add(new Notifications());
@@ -89,6 +90,8 @@ public class ModuleManager {
add(new AttributeSwapper()); add(new AttributeSwapper());
add(new ChestplateSwitch()); add(new ChestplateSwitch());
add(new CrystalAura()); add(new CrystalAura());
add(new HoldAttack());
add(new KillAura());
add(new MaceKill()); add(new MaceKill());
add(new ProjectileTrajectories()); add(new ProjectileTrajectories());
add(new SpearKill()); add(new SpearKill());
@@ -111,10 +114,10 @@ public class ModuleManager {
add(new CreativeInventoryHotbarKeybinds()); add(new CreativeInventoryHotbarKeybinds());
add(new InventoryPlus()); add(new InventoryPlus());
add(new Offhand()); add(new Offhand());
add(new PortalGUI()); add(new PortalGui());
add(new Replenish()); add(new Replenish());
add(new ShulkerPeek()); add(new ShulkerPeek());
add(new UIMove()); add(new UiMove());
add(new XCarry()); add(new XCarry());
} }
@@ -127,6 +130,7 @@ public class ModuleManager {
add(new Headhitters()); add(new Headhitters());
add(new HitboxDesync()); add(new HitboxDesync());
add(new InfiniteElytra()); add(new InfiniteElytra());
add(new ItemMagnet());
add(new Jesus()); add(new Jesus());
add(new Parkour()); add(new Parkour());
add(new ReverseStep()); add(new ReverseStep());
@@ -141,7 +145,9 @@ public class ModuleManager {
private void addNetwork() { private void addNetwork() {
add(new BungeecordSpoofer()); add(new BungeecordSpoofer());
add(new LogoutSpots()); add(new LogoutSpots());
add(new PacketCanceller());
add(new PacketDelay()); add(new PacketDelay());
add(new PacketLogger());
add(new PacketMine()); add(new PacketMine());
add(new Reconnect()); add(new Reconnect());
add(new RPackBypass()); add(new RPackBypass());
@@ -150,6 +156,7 @@ public class ModuleManager {
private void addPlayer() { private void addPlayer() {
add(new AirPlace()); add(new AirPlace());
add(new AntiHunger()); add(new AntiHunger());
add(new AutoEat());
add(new AutoFish()); add(new AutoFish());
add(new Capes()); add(new Capes());
add(new FastUse()); add(new FastUse());
@@ -159,17 +166,19 @@ public class ModuleManager {
} }
private void addRender() { private void addRender() {
add(new BlockESP()); add(new BlockEsp());
add(new BlockOutline()); add(new BlockOutline());
add(new Breadcrumbs()); add(new Breadcrumbs());
add(new BreakingProgress()); add(new BreakingProgress());
add(new Confetti()); add(new Confetti());
add(new CritsPlus()); add(new CritsPlus());
add(new DamageOverlay()); add(new DamageOverlay());
add(new EntityESP()); add(new EntityEsp());
add(new Fonts()); add(new Fonts());
add(new GlintPlus()); add(new GlintPlus());
add(new Hitboxes()); add(new Hitboxes());
add(new ItemPhysics());
add(new Lsd());
add(new Nametags()); add(new Nametags());
add(new NoRender()); add(new NoRender());
add(new Particles()); add(new Particles());
@@ -190,8 +199,6 @@ public class ModuleManager {
} }
// para obtener módulos más fácilmente (por nombre, clase, o la lista completa) // para obtener módulos más fácilmente (por nombre, clase, o la lista completa)
public List<Module> getModules() { public List<Module> getModules() {
return modules; return modules;
@@ -205,17 +212,34 @@ public class ModuleManager {
return enabledModules; 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) { public Module getModuleByName(String name) {
for (Module module : modules) for (Module module : modules) {
if (MiscUtil.removeAccentMarks(module.getName()).equalsIgnoreCase(name)) if (module.getName().equalsIgnoreCase(name)) {
return module; 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; return null;
} }
public <T extends Module> T getModuleByClass(Class<T> clazz) { public <T extends Module> T getModuleByClass(Class<T> clazz) {
for (Module module : modules) for (Module module : modules) {
if (clazz.isInstance(module)) if (clazz.isInstance(module)) {
return clazz.cast(module); return clazz.cast(module);
}
}
return null; return null;
} }
} }
@@ -1,12 +1,10 @@
package me.retucio.sputnik.module.modules.camera; 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.Category;
import me.retucio.sputnik.module.Module; import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.EnumSetting; import me.retucio.sputnik.module.setting.settings.EnumSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting; 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)) if (direction.is(RotationDirection.COUNTERCLOCKWISE))
yaw -= interval.getFloatValue(); yaw -= interval.getFloatValue();
mc.getConnection().send(new ServerboundMovePlayerPacket.Rot( RotationUtil.requestYaw(RotationUtil.HIGHEST, yaw, RotationUtil.Mode.SILENT);
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();
} }
private enum RotationDirection { private enum RotationDirection {
@@ -25,6 +25,7 @@ import me.retucio.sputnik.util.MiscUtil;
import net.minecraft.client.CameraType; import net.minecraft.client.CameraType;
import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.client.input.MouseButtonEvent;
import net.minecraft.client.input.MouseButtonInfo; import net.minecraft.client.input.MouseButtonInfo;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.*; import net.minecraft.network.protocol.game.*;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
@@ -150,8 +151,8 @@ public class Freecam extends Module {
speed = speedSetting.getValue(); speed = speedSetting.getValue();
MiscUtil.copyVector(pos, mc.gameRenderer.getMainCamera().position()); MiscUtil.copyVector(pos, mc.gameRenderer.mainCamera().position());
MiscUtil.copyVector(prevPos, mc.gameRenderer.getMainCamera().position()); MiscUtil.copyVector(prevPos, mc.gameRenderer.mainCamera().position());
if (mc.options.getCameraType() == CameraType.THIRD_PERSON_FRONT) { if (mc.options.getCameraType() == CameraType.THIRD_PERSON_FRONT) {
yaw += 180; yaw += 180;
@@ -171,14 +172,19 @@ public class Freecam extends Module {
down = KeyUtil.isKeyDown(mc.options.keyShift); down = KeyUtil.isKeyDown(mc.options.keyShift);
unpress(); unpress();
if (reloadChunks.getValue()) mc.levelRenderer.allChanged(); if (reloadChunks.getValue()) invalidateChunks();
super.onEnable(); super.onEnable();
} }
private static void invalidateChunks() {
if (mc.level == null) return;
mc.levelExtractor.allChanged();
}
@Override @Override
public void onDisable() { public void onDisable() {
if (reloadChunks.getValue()) mc.execute(mc.levelRenderer::allChanged); if (reloadChunks.getValue()) invalidateChunks();
mc.options.setCameraType(perspective); mc.options.setCameraType(perspective);
crouching = false; crouching = false;
@@ -265,7 +271,7 @@ public class Freecam extends Module {
@EventListener @EventListener
private void onKey(KeyEvent event) { private void onKey(KeyEvent event) {
if (KeyUtil.isKeyDown(GLFW.GLFW_KEY_F3)) return; 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); 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 @EventListener
private void onMouseClick(MouseClickEvent event) { // por si el restrasado del usuario usa el ratón para moverse 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 (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)); MouseButtonEvent click = new MouseButtonEvent(0, 0, new MouseButtonInfo(event.getButton(), 0));
@@ -330,7 +336,7 @@ public class Freecam extends Module {
@EventListener @EventListener
private void onMouseScroll(MouseScrollEvent event) { 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); speed += event.getVertical() / 4 * (scrollSens.getValue() * speed);
if (speed < 0.1) speed = 0.1; if (speed < 0.1) speed = 0.1;
event.cancel(); event.cancel();
@@ -356,7 +362,7 @@ public class Freecam extends Module {
toggle(); toggle();
} else if (event.getPacket() instanceof ClientboundSetHealthPacket packet) { } else if (event.getPacket() instanceof ClientboundSetHealthPacket packet) {
if (mc.player.getHealth() - packet.getHealth() > 0 && toggleOnDamage.getValue()) { 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(); toggle();
} }
} }
@@ -60,7 +60,7 @@ public class PerspectivePlus extends Module {
@EventListener @EventListener
private void onMouseScroll(MouseScrollEvent event) { private void onMouseScroll(MouseScrollEvent event) {
if (mc.options.getCameraType() == CameraType.FIRST_PERSON 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()) { if (scrollKey.isDown()) {
distance -= event.getVertical() / 4 * (scrollSens.getValue() * distance); 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.Module;
import me.retucio.sputnik.module.setting.settings.BooleanSetting; import me.retucio.sputnik.module.setting.settings.BooleanSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting; import me.retucio.sputnik.module.setting.settings.NumberSetting;
import me.retucio.sputnik.util.RotationUtil;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
/** /**
@@ -30,8 +31,7 @@ public class Rotations extends Module {
public void onTick() { public void onTick() {
if (mc.player == null) return; if (mc.player == null) return;
mc.player.setYRot(yaw.getFloatValue()); RotationUtil.request(RotationUtil.HIGHEST, yaw.getFloatValue(), pitch.getFloatValue(), RotationUtil.Mode.CLIENT);
mc.player.setXRot(pitch.getFloatValue());
if (serverSide.getValue()) if (serverSide.getValue())
mc.player.connection.send( mc.player.connection.send(
@@ -58,13 +58,24 @@ public class Zoom extends Module {
notify.setValue(false); 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 @Override
public void onEnable() { public void onEnable() {
prevSmoothCam = mc.options.smoothCamera; prevSmoothCam = mc.options.smoothCamera;
prevMouseSens = mc.options.sensitivity().get(); prevMouseSens = mc.options.sensitivity().get();
prevFov = mc.options.fov().get(); prevFov = mc.options.fov().get();
prevHUD = mc.options.hideGui; prevHUD = mc.gui.hud.isHidden();
mc.options.hideGui = !showHUD.getValue(); setHudHidden(!showHUD.getValue());
value = defaultZoom.getValue(); value = defaultZoom.getValue();
@@ -75,9 +86,9 @@ public class Zoom extends Module {
public void onDisable() { public void onDisable() {
mc.options.smoothCamera = prevSmoothCam; mc.options.smoothCamera = prevSmoothCam;
mc.options.sensitivity().set(prevMouseSens); mc.options.sensitivity().set(prevMouseSens);
mc.options.hideGui = prevHUD; setHudHidden(prevHUD);
mc.levelRenderer.needsUpdate(); needsUpdate();
super.onDisable(); super.onDisable();
} }
@@ -105,7 +116,7 @@ public class Zoom extends Module {
private void onGetFov(GetFOVEvent event) { private void onGetFov(GetFOVEvent event) {
event.setFov((float) (event.getFov() / value)); event.setFov((float) (event.getFov() / value));
if (prevFov != event.getFov()) mc.levelRenderer.needsUpdate(); if (prevFov != event.getFov()) needsUpdate();
prevFov = event.getFov(); prevFov = event.getFov();
} }
} }
@@ -1,6 +1,5 @@
package me.retucio.sputnik.module.modules.client; package me.retucio.sputnik.module.modules.client;
import dev.firstdark.rpc.DiscordRpc;
import dev.firstdark.rpc.enums.ActivityType; import dev.firstdark.rpc.enums.ActivityType;
import dev.firstdark.rpc.enums.ErrorCode; import dev.firstdark.rpc.enums.ErrorCode;
import dev.firstdark.rpc.exceptions.UnsupportedOsType; 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.EnumSetting;
import me.retucio.sputnik.module.setting.settings.StringSetting; import me.retucio.sputnik.module.setting.settings.StringSetting;
import me.retucio.sputnik.util.ChatUtil; import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.network.chat.Component;
/** /**
* @author retucio * @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 sgImage = addSg(new SettingGroup("imágenes", false));
private final SettingGroup sgButton = addSg(new SettingGroup("botón", false)); private final SettingGroup sgButton = addSg(new SettingGroup("botón", false));
@@ -102,7 +102,7 @@ public class DiscordRPC extends Module {
// crear el rpc // crear el rpc
private boolean initialized; private boolean initialized;
DiscordRpc rpc = new DiscordRpc(); dev.firstdark.rpc.DiscordRpc rpc = new dev.firstdark.rpc.DiscordRpc();
RPCEventHandler handler = new RPCEventHandler() { RPCEventHandler handler = new RPCEventHandler() {
@Override @Override
public void ready(User user) { public void ready(User user) {
@@ -111,19 +111,19 @@ public class DiscordRPC extends Module {
@Override @Override
public void disconnected(ErrorCode errorCode, String message) { public void disconnected(ErrorCode errorCode, String message) {
ChatUtil.warn("desconectado: " + errorCode + " - " + message); ChatUtil.warn(Component.translatable("sputnik.module.discordrpc.disconnected", errorCode, message));
toggle(); toggle();
} }
@Override @Override
public void errored(ErrorCode errorCode, String message) { public void errored(ErrorCode errorCode, String message) {
ChatUtil.error("error: " + errorCode + " - " + message); ChatUtil.error(Component.translatable("sputnik.module.discordrpc.error", errorCode, message));
toggle(); toggle();
} }
}; };
public DiscordRPC() { public DiscordRpc() {
super("discordRPC", super("discordRPC",
"personaliza la actividad de tu perfil en dishkor", "personaliza la actividad de tu perfil en dishkor",
Category.CLIENT); Category.CLIENT);
@@ -142,7 +142,7 @@ public class DiscordRPC extends Module {
public void onEnable() { public void onEnable() {
String token = appId.getValue(); String token = appId.getValue();
if (token == null || token.trim().isEmpty()) { if (token == null || token.trim().isEmpty()) {
ChatUtil.error("configura el token del bot primero"); ChatUtil.error(Component.translatable("sputnik.module.discordrpc.notoken"));
toggle(); toggle();
return; return;
} }
@@ -151,10 +151,10 @@ public class DiscordRPC extends Module {
rpc.init(token, handler, false); rpc.init(token, handler, false);
initialized = true; initialized = true;
} catch (UnsupportedOsType e) { } catch (UnsupportedOsType e) {
ChatUtil.error("sistema operativo no soportado"); ChatUtil.error(Component.translatable("sputnik.module.discordrpc.unsupportedos"));
toggle(); toggle();
} catch (Exception e) { } catch (Exception e) {
ChatUtil.error("error al inicializar rpc: " + e.getMessage()); ChatUtil.error(Component.translatable("sputnik.module.discordrpc.initerror", e.getMessage()));
toggle(); toggle();
} }
@@ -191,7 +191,7 @@ public class DiscordRPC extends Module {
try { try {
rpc.updatePresence(getPresence()); rpc.updatePresence(getPresence());
} catch (Exception e) { } 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 { try {
timestamp = Long.parseLong(startTimestamp.getValue()); timestamp = Long.parseLong(startTimestamp.getValue());
} catch (NumberFormatException e) { } 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(); timestamp = System.currentTimeMillis();
} }
} }
Loaded 100 of 217 files, more files were not shown because too many files have changed in this diff. Show more