Compare commits

...
12 Commits
Author SHA1 Message Date
retucio 44494830be v6.3 2026-09-04 12:58:41 +02:00
retucio 73682cb85d v6.2 2026-08-28 12:27:07 +02:00
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
226 changed files with 10325 additions and 1185 deletions

No files matched your search

+2 -1
View File
@@ -43,4 +43,5 @@ replay_*.log
todo.txt
cambios.md
remappedSrc/
remappedSrc/
.claude/
+4 -4
View File
@@ -1,16 +1,16 @@
# 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
### 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
3. descargar el .jar
4. moverlo a tu directorio de mods (probablemente `%appdata%/.minecraft/mods`)
asegúrate de tener una version de Minecraft y Fabric compatibles con el mod (MC `26.1.2`; Fabric `0.19.2`)
asegúrate de tener una version de Minecraft y Fabric compatibles con el mod (MC `26.2`; Fabric `0.19.3`)
es posible que haya problemas al usarlo junto a otros mods, especialmente junto a otros clientes, pues pueden conflictar entre ellos.
### para frikis de 200 de iq
@@ -56,7 +56,7 @@ algunos módulos tienen teclas asignadas por defecto, asígnales `ESC` para desa
<img src="screenshots/hudeditor.png" alt="editor de HUD">
<img src="screenshots/colorpicker.png" alt="selector de colores">
## contibuciones
## contribuciones
sí por favor
+9 -5
View File
@@ -1,18 +1,22 @@
# v5.2
# v6.3
## novedades
- actualizado a Gradle 9.7.1
### adiciones
#### módulos nuevos:
- autominado: mina automáticamente los bloques seleccionados, con distintos modos de selección
- estiloso: haz truquitos con el ítem que tienes en la mano
### arreglos / mejoras
- `andamios` ya funciona correctamente
- autoguardado cada cierto intervalo de tiempo en vez de guardar inmediatamente tras cada cambio en los ajustes, por optimización
- el guardado se realiza desde otro hilo ahora, para evitar que el I/O bloque el hilo principal
- mejoras varias a `puto gordo`
- ahora `trazos` con `uno mismo` solo te apunta en cámara libre
- nuevo ajuste de distancia máxima de renderizado para `nametags`
## problemas reconocidos
- lo de siempre.
- el ítem del tótem no se renderiza en el editor del HUD si es abierto en la pantalla de inicio
+3 -1
View File
@@ -10,6 +10,8 @@ elytra-recast: https://github.com/InLieuOfLuna/elytra-recast/
SpearHax: https://github.com/etianl/spearHax/
ProjectileTrajectoryPreview: https://github.com/maDU59/ProjectilesTrajectoryPreview
DupersUnited public addon: https://discord.gg/dupes | https://dupers.wtf
Stardust: https://github.com/0xTas/stardust/
probablemente más pero como tengo demencia se me olvida
si quieres que te acredite o quite tu código de mi mod, mándame un mensaje en discord (username: retucio) y sin problemas :)
si quieres que te acredite o quite tu código de mi mod, mándame un mensaje en discord (username: retucio) y sin problemas :)
también es probable que haya créditos sueltos en forma de comentarios donde haya usado código ajeno
+5 -5
View File
@@ -1,15 +1,15 @@
org.gradle.jvmargs=-Xmx2G
org.gradle.parallel=true
minecraft_version=26.1.2
loader_version=0.19.2
loom_version=1.16-SNAPSHOT
minecraft_version=26.2
loader_version=0.19.3
loom_version=1.17-SNAPSHOT
fabric_api_version=0.146.0+26.1.2
fabric_api_version=0.154.0+26.2
discord_rpc_version=1.0.3
neutrino_version=1.1.0
jdk_version=25
mod_version=5.2
mod_version=6.3
maven_group=me.retucio
archives_base_name=sputnik
+2 -3
View File
@@ -1,7 +1,6 @@
#Sat Aug 29 22:10:52 CEST 2026
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
View File
Whitespace-only changes.
+53 -29
View File
@@ -7,6 +7,8 @@ import me.retucio.sputnik.command.CommandManager;
import me.retucio.sputnik.command.commands.BindCommand;
import me.retucio.sputnik.config.ConfigManager;
import me.retucio.sputnik.config.ProfileManager;
import me.retucio.sputnik.macro.MacroManager;
import me.retucio.sputnik.event.ShutdownEvent;
import me.retucio.sputnik.event.interact.OpenScreenEvent;
@@ -17,12 +19,14 @@ import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.client.Hud;
import me.retucio.sputnik.ui.hud.HudEditor;
import me.retucio.sputnik.ui.hud.HudRenderer;
import me.retucio.sputnik.ui.screen.ClickGui;
import me.retucio.sputnik.ui.hud.HudEditorScreen;
import me.retucio.sputnik.ui.screen.UpdateScreen;
import me.retucio.sputnik.ui.widgets.buttons.settings.BindButton;
import me.retucio.sputnik.ui.widgets.buttons.SettingButton;
import me.retucio.sputnik.ui.widgets.buttons.settings.FileButton;
import me.retucio.sputnik.ui.widgets.buttons.settings.TextButton;
import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel;
import me.retucio.sputnik.ui.widgets.panels.SettingsPanel;
@@ -47,6 +51,7 @@ import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.client.gui.screens.inventory.AbstractCommandBlockEditScreen;
import net.minecraft.client.gui.screens.inventory.AbstractSignEditScreen;
import net.minecraft.client.gui.screens.inventory.AnvilScreen;
import net.minecraft.network.chat.Component;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -98,12 +103,17 @@ public class Sputnik implements ClientModInitializer {
EVENT_BUS.subscribe(MiscUtil.class);
EVENT_BUS.subscribe(NetworkUtil.class);
EVENT_BUS.subscribe(RenderUtil.class);
EVENT_BUS.subscribe(RotationUtil.class);
EVENT_BUS.subscribe(BlockIterator.class);
Lists.init();
HudRenderer.INSTANCE = new HudRenderer();
HudEditor.INSTANCE = new HudEditor();
MacroManager.INSTANCE = new MacroManager();
ProfileManager.INSTANCE = new ProfileManager();
CapeManager.INSTANCE = new CapeManager();
EVENT_BUS.post(new LoadCapeManagerEvent());
@@ -134,11 +144,8 @@ public class Sputnik implements ClientModInitializer {
// se ejecuta cada tick, es decir, 20 veces por segundo
public void onTick() {
if (!settingsApplied
&& ConfigManager.INSTANCE.getConfig() != null
&& !ConfigManager.INSTANCE.hasLoaded()) {
ConfigManager.INSTANCE.applyConfig();
settingsApplied = true;
if (!settingsApplied && !ConfigManager.INSTANCE.hasLoaded()) {
settingsApplied = ConfigManager.INSTANCE.applyConfig();
}
ModuleManager.INSTANCE.getEnabledModules().forEach(Module::onTick);
@@ -153,14 +160,15 @@ public class Sputnik implements ClientModInitializer {
// prevenir interrumpir combos de F3
if (key == GLFW.GLFW_KEY_F3) return;
boolean anyFocused = isAnySettingButtonFocused() || ClickGui.INSTANCE.getSearchBar().isFocused();
boolean anyFocused = isAnySettingButtonFocused() || ClickGui.INSTANCE.isCapturingInput();
ClickGui.INSTANCE.setAnyFocused(anyFocused);
if (action != GLFW.GLFW_RELEASE) {
if (BindCommand.onKeyPress(key)) return;
if (action == GLFW.GLFW_PRESS && MacroManager.INSTANCE.onKeyPress(key)) return;
if (action == GLFW.GLFW_PRESS) {
handleModuleToggle(key, anyFocused);
handleClickGUIKey(key, anyFocused);
handleClickGuiKey(key, anyFocused);
handleHudEditorKey(key, anyFocused);
}
// solo reenviar teclas del teclado
@@ -174,16 +182,21 @@ public class Sputnik implements ClientModInitializer {
// verifica si algún botón de ajustes está escuchando
private boolean isAnySettingButtonFocused() {
if (ClickGui.INSTANCE == null) return false;
for (SettingsPanel sf : ClickGui.INSTANCE.getSettingsPanels())
for (Button sb : sf.getButtons())
if ((sb instanceof BindButton b && b.isFocused()) || (sb instanceof TextButton t && t.isFocused()))
if ((sb instanceof BindButton b && b.isFocused())
|| (sb instanceof TextButton t && t.isFocused())
|| (sb instanceof FileButton f && f.isFocused())
) {
return true;
}
return false;
}
// se ocupa de la lógica de encendido y apagado de los módulos
private void handleModuleToggle(int key, boolean anyFocused) {
if (mc.screen != null && mc.screen != ClickGui.INSTANCE) return;
if (mc.gui.screen() != null && mc.gui.screen() != ClickGui.INSTANCE) return;
for (Module module : ModuleManager.INSTANCE.getModules()) {
// evitar interrumpir combinaciones de teclas del F3
@@ -201,46 +214,47 @@ public class Sputnik implements ClientModInitializer {
for (SettingsPanel sf : ClickGui.INSTANCE.getSettingsPanels()) {
for (SettingButton<?> sb : sf.getButtons()) {
if (sb instanceof BindButton bb) bb.onKey(key, action);
if (sb instanceof FileButton fb) fb.onKey(key, action);
if (sb instanceof TextButton tb) tb.onKey(key, action);
}
}
}
// maneja la lógica de apertura de la interfaz
private void handleClickGUIKey(int key, boolean anyFocused) {
private void handleClickGuiKey(int key, boolean anyFocused) {
if (key != ClientSettingsPanel.clientSettings.getKey() || anyFocused || isOnTypingScreen())
return;
if (mc.screen != ClickGui.INSTANCE) {
prevScreen = mc.screen;
mc.setScreen(ClickGui.INSTANCE);
if (mc.gui.screen() != ClickGui.INSTANCE) {
prevScreen = mc.gui.screen();
mc.gui.setScreen(ClickGui.INSTANCE);
} else {
ClickGui.INSTANCE.onClose();
mc.setScreen(prevScreen);
mc.gui.setScreen(prevScreen);
}
}
// manejar la lógica de apertura del editor de elementos del hud, con la misma lógica que handleClickGUIKey()
// manejar la lógica de apertura del editor de elementos del hud, con la misma lógica que handleClickGuiKey()
private void handleHudEditorKey(int key, boolean anyFocused) {
Hud hud = ModuleManager.INSTANCE.getModuleByClass(Hud.class);
if (key != hud.editorKey.getValue() || anyFocused || isOnTypingScreen()) return;
if (mc.screen != HudEditorScreen.INSTANCE) {
if (mc.gui.screen() != HudEditorScreen.INSTANCE) {
if (!hud.isEnabled()) {
ChatUtil.warn("HUD está desactivado, lumbreras");
ChatUtil.warn(Component.translatable("sputnik.hud.disabled"));
return;
}
prevScreen = mc.screen;
mc.setScreen(HudEditorScreen.INSTANCE);
prevScreen = mc.gui.screen();
mc.gui.setScreen(HudEditorScreen.INSTANCE);
} else {
HudEditorScreen.INSTANCE.onClose();
mc.setScreen(prevScreen);
mc.gui.setScreen(prevScreen);
}
}
// se ocupa de apagar los módulos que tengan configurado hacerlo tras soltar su tecla
private void handleModuleRelease(int key) {
if (mc.screen != null && mc.screen != ClickGui.INSTANCE) return;
if (mc.gui.screen() != null && mc.gui.screen() != ClickGui.INSTANCE) return;
for (Module module : ModuleManager.INSTANCE.getEnabledModules())
if (module.shouldToggleOnBindRelease() && key == module.getKey())
@@ -248,19 +262,29 @@ public class Sputnik implements ClientModInitializer {
}
private boolean isOnTypingScreen() {
return mc.screen instanceof ChatScreen
|| mc.screen instanceof AnvilScreen
|| mc.screen instanceof AbstractSignEditScreen
|| mc.screen instanceof AbstractCommandBlockEditScreen;
return mc.gui.screen() instanceof ChatScreen
|| mc.gui.screen() instanceof AnvilScreen
|| mc.gui.screen() instanceof AbstractSignEditScreen
|| mc.gui.screen() instanceof AbstractCommandBlockEditScreen;
}
private boolean keysVerified = false;
@EventListener
// notificar al usuario de que hay una actualización disponible, si la hay
public void onSetScreen(OpenScreenEvent event) {
if (VersionChecker.INSTANCE.shouldShowScreen && event.getScreen() instanceof TitleScreen) {
event.cancel();
VersionChecker.INSTANCE.shouldShowScreen = false;
mc.execute(() -> mc.screen = new UpdateScreen());
if (event.getScreen() instanceof TitleScreen) {
// aquí el idioma ya está cargado, que en onInitializeClient todavía no
if (!keysVerified) {
keysVerified = true;
I18nUtil.verifyKeys();
}
if (VersionChecker.INSTANCE.shouldShowScreen) {
event.cancel();
VersionChecker.INSTANCE.shouldShowScreen = false;
mc.execute(() -> mc.gui.setScreen(new UpdateScreen()));
}
}
}
@@ -9,6 +9,7 @@ import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.Commands;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.data.registries.VanillaRegistries;
import net.minecraft.network.chat.Component;
import java.util.List;
@@ -20,12 +21,14 @@ public abstract class Command {
protected static final Minecraft mc = Minecraft.getInstance();
private final String name, description;
private final String name, descriptionKey;
private final List<String> aliases;
public Command(String name, String description, String... aliases) {
// la descripción se guarda como clave de traducción, no como texto ya resuelto: los comandos
// se construyen antes de que se cargue el idioma, así que hay que traducirla al mostrarla
public Command(String name, String descriptionKey, String... aliases) {
this.name = name;
this.description = description;
this.descriptionKey = descriptionKey;
this.aliases = List.of(aliases);
}
@@ -56,8 +59,12 @@ public abstract class Command {
return name;
}
public String getDescription() {
return description;
public String getDescriptionKey() {
return descriptionKey;
}
public Component getDescription() {
return Component.translatable(descriptionKey);
}
public List<String> getAliases() {
@@ -38,9 +38,13 @@ public class CommandManager {
addCommand(new EnderChestCommand());
addCommand(new FriendCommand());
addCommand(new GarbageCleanerCommand());
addCommand(new HelpCommand());
addCommand(new MacroCommand());
addCommand(new PanoramaCommand());
addCommand(new PeekCommand());
addCommand(new PluginsCommand());
addCommand(new PrefixCommand());
addCommand(new ProfileCommand());
addCommand(new PurgeCommand());
addCommand(new RepeatCommand());
addCommand(new RepeatWaitCommand());
@@ -25,7 +25,7 @@ public class EntityArgumentType implements ArgumentType<Entity> {
public static final EntityArgumentType INSTANCE = new EntityArgumentType();
private static final DynamicCommandExceptionType NO_SUCH_ENTITY = new DynamicCommandExceptionType(
name -> Component.literal("Entidad \"" + name + "\" no encontrada"));
name -> Component.translatable("sputnik.args.entity.notfound", name));
private static final Collection<String> EXAMPLES = List.of(
"AdlerHitdolf",
@@ -15,7 +15,8 @@ import java.util.concurrent.CompletableFuture;
public class EnumArgumentType<T extends Enum<T>> implements ArgumentType<T> {
private static final DynamicCommandExceptionType NO_SUCH_TYPE = new DynamicCommandExceptionType((value) -> Component.nullToEmpty(" argumento inválido: " + value));
private static final DynamicCommandExceptionType NO_SUCH_TYPE = new DynamicCommandExceptionType(
(value) -> Component.translatable("sputnik.args.enum.invalid", value));
private final T[] values;
@@ -33,8 +34,10 @@ public class EnumArgumentType<T extends Enum<T>> implements ArgumentType<T> {
public T parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString();
// ignorando mayúsculas: listSuggestions sugiere en minúsculas, así que comparar
// con equals() hacía que las propias sugerencias no se pudieran parsear
return Arrays.stream(this.values)
.filter((value) -> value.toString().equals(argument))
.filter((value) -> value.toString().equalsIgnoreCase(argument))
.findFirst()
.orElseThrow(() -> NO_SUCH_TYPE.create(argument)
);
@@ -22,15 +22,15 @@ public class ModuleArgumentType implements ArgumentType<Module> {
public static final ModuleArgumentType INSTANCE = new ModuleArgumentType();
private static final DynamicCommandExceptionType UNKNOWN_MODULE = new DynamicCommandExceptionType(
name -> Component.literal("módulo \"" + name + "\" no encontrado"));
name -> Component.translatable("sputnik.args.module.notfound", name));
private static final Collection<String> examples = ModuleManager.INSTANCE.getModules()
.stream().limit(3).map(module -> module.getName().replace(" ", "_")).toList();
.stream().limit(3).map(module -> module.getDisplayName().replace(" ", "_")).toList();
@Override
public Module parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString().replace("_", " ");
Module module = ModuleManager.INSTANCE.getModuleByName(argument);
Module module = ModuleManager.INSTANCE.getModuleByDisplayName(argument);
if (module == null) throw UNKNOWN_MODULE.create(argument);
return module;
}
@@ -38,7 +38,7 @@ public class ModuleArgumentType implements ArgumentType<Module> {
@Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return SharedSuggestionProvider.suggest(ModuleManager.INSTANCE.getModules()
.stream().map(module -> MiscUtil.removeAccentMarks(module.getName().replace(" ", "_"))), builder);
.stream().map(module -> MiscUtil.removeAccentMarks(module.getDisplayName().replace(" ", "_"))), builder);
}
@Override
@@ -21,7 +21,8 @@ import static me.retucio.sputnik.Sputnik.mc;
public class PlayerArgumentType implements ArgumentType<Player> {
public static final PlayerArgumentType INSTANCE = new PlayerArgumentType();
private static final DynamicCommandExceptionType NO_SUCH_PLAYER = new DynamicCommandExceptionType(name -> Component.literal("Player with name " + name + " doesn't exist."));
private static final DynamicCommandExceptionType NO_SUCH_PLAYER = new DynamicCommandExceptionType(
name -> Component.translatable("sputnik.args.player.notfound", name));
private static final Collection<String> EXAMPLES = List.of("retucio", "prepucio", "postpucio", "pucio", "adler hitdolf");
@@ -24,7 +24,7 @@ public class BindCommand extends Command {
private static Module listeningModule = null;
public BindCommand() {
super("bind", "asigna una tecla a un módulo", "keybind");
super("bind", "sputnik.commands.bind.desc", "keybind", "kb", "tecla");
}
@Override
@@ -34,14 +34,16 @@ public class BindCommand extends Command {
.executes(ctx -> {
Module module = ctx.getArgument("módulo", Module.class);
listeningModule = module;
ChatUtil.info("presiona una tecla para asignarla al módulo " + ChatFormatting.GREEN + module.getName());
ChatUtil.info(Component.translatable("sputnik.commands.bind.presskey", ChatFormatting.GREEN + module.getDisplayName()));
return SUCCESS;
})
.then(literal("reset").executes(ctx -> {
Module module = ctx.getArgument("módulo", Module.class);
module.getBind().reset();
ChatUtil.info("tecla para el módulo " + ChatFormatting.GREEN + module.getName()
+ " restablecida a " + ChatFormatting.AQUA + KeyUtil.getKeyName(module.getKey()));
ChatUtil.info(Component.translatable("sputnik.commands.bind.keyreset",
ChatFormatting.GREEN + module.getDisplayName(),
ChatFormatting.AQUA + KeyUtil.getKeyName(module.getKey())
));
return SUCCESS;
}))
);
@@ -51,17 +53,18 @@ public class BindCommand extends Command {
if (listeningModule == null) return false;
KeySetting bind = listeningModule.getBind();
if (ClientSettingsPanel.clientSettings.multipleKeybinds.getValue()) {
// solo hay que avisar de que la tecla está cogida cuando NO se permiten teclas repetidas.
// antes esto entraba con multipleKeybinds activado y luego comprobaba !allowMultiple con el
// mismo valor, así que nunca se llegaba a avisar
if (!ClientSettingsPanel.clientSettings.multipleKeybinds.getValue()) {
List<KeyMapping> keys = new ArrayList<>(List.of(mc.options.keyMappings));
keys.removeAll(List.of(mc.options.debugKeys));
for (KeyMapping kb : keys) {
boolean keyAlreadyBound = kb.matches(new KeyEvent(key, 0, 0));
boolean allowMultiple = ClientSettingsPanel.clientSettings.multipleKeybinds.getValue();
if (keyAlreadyBound && !allowMultiple) {
ChatUtil.warn("esa tecla ya está cogida por "
+ ChatFormatting.GREEN + "\"" + I18n.get(kb.getName() + "\""));
if (kb.matches(new KeyEvent(key, 0, 0))) {
// la comilla iba dentro del I18n.get, así que buscaba una clave inexistente
ChatUtil.warn(Component.translatable("sputnik.commands.bind.keytaken",
ChatFormatting.GREEN + "\"" + I18n.get(kb.getName()) + "\""));
listeningModule = null;
return true;
}
@@ -70,10 +73,10 @@ public class BindCommand extends Command {
if (bind != null) bind.setValue(key);
ChatUtil.info(
Component.nullToEmpty("la tecla " + ChatFormatting.AQUA + KeyUtil.getKeyName(key) + ChatFormatting.RESET +
" ha sido asignada al módulo " + ChatFormatting.GREEN + listeningModule.getName())
);
ChatUtil.info(Component.translatable("sputnik.commands.bind.keybound",
ChatFormatting.AQUA + KeyUtil.getKeyName(key) + ChatFormatting.RESET,
ChatFormatting.GREEN + listeningModule.getDisplayName()
));
listeningModule = null;
return true;
@@ -13,7 +13,7 @@ import net.minecraft.world.inventory.ContainerInput;
public class ClickSlotCommand extends Command {
public ClickSlotCommand() {
super("clickslot", "simula un clic a un slot");
super("clickslot", "sputnik.commands.clickslot.desc", "cs");
}
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
@@ -23,7 +23,7 @@ public class ClickSlotCommand extends Command {
.executes(context -> {
int slot = context.getArgument("slot", Integer.class), button = context.getArgument("botón", Integer.class);
ContainerInput action = context.getArgument("acción", ContainerInput.class);
Screen screen = mc.screen;
Screen screen = mc.gui.screen();
int syncId = (screen instanceof ContainerScreen cScreen) ? cScreen.getMenu().containerId : 0;
if (mc.gameMode == null) return 0;
mc.gameMode.handleContainerInput(syncId, slot, button, action, mc.player);
@@ -13,7 +13,7 @@ import me.retucio.sputnik.module.Module;
public class ConfigCommand extends Command {
public ConfigCommand() {
super("config", "abre el marco de ajustes de un módulo", "ajustes", "settings");
super("config", "sputnik.commands.config.desc", "ajustes", "settings");
}
@Override
@@ -40,7 +40,7 @@ public class ConfigCommand extends Command {
private void openSf(CommandContext<SharedSuggestionProvider> ctx) {
Module module = ctx.getArgument("módulo", Module.class);
mc.setScreen(ClickGui.INSTANCE);
mc.gui.setScreen(ClickGui.INSTANCE);
if (ClickGui.INSTANCE.isSettingsPanelOpen(module)) return;
@@ -53,7 +53,7 @@ public class ConfigCommand extends Command {
private void openSfWithPos(CommandContext<SharedSuggestionProvider> ctx, int x, int y) {
Module module = ctx.getArgument("módulo", Module.class);
mc.setScreen(ClickGui.INSTANCE);
mc.gui.setScreen(ClickGui.INSTANCE);
if (ClickGui.INSTANCE.isSettingsPanelOpen(module)) {
SettingsPanel sf = ClickGui.INSTANCE.getSettingsPanelOfModule(module);
@@ -10,7 +10,7 @@ import net.minecraft.commands.SharedSuggestionProvider;
public class CopyScreenshotCommand extends Command {
public CopyScreenshotCommand() {
super("copiarcaptura", "copia la captura más reciente al portapapeles");
super("copiarcaptura", "sputnik.commands.copyscreenshot.desc", "copyscreenshot");
}
@Override
@@ -8,6 +8,7 @@ import me.retucio.sputnik.ui.screen.PreviewScreen;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.InventoryUtil;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
// accede a los contenidos de tu enderchest
public class EnderChestCommand extends Command {
@@ -15,7 +16,7 @@ public class EnderChestCommand extends Command {
private boolean shouldOpenEchest = false;
public EnderChestCommand() {
super("echest", "muestra el contenido de tu enderchest (solo de ver)", "ec", "enderchest");
super("echest", "sputnik.commands.echest.desc", "ec", "enderchest");
}
@Override
@@ -24,7 +25,7 @@ public class EnderChestCommand extends Command {
mc.execute(() -> {
if (mc.player == null) return;
if (InventoryUtil.getEchestInv() == null) {
ChatUtil.warn("necesitas abrir un enderchest una vez primero");
ChatUtil.warn(Component.translatable( "sputnik.commands.echest.openfirst"));
return;
}
@@ -40,7 +41,7 @@ public class EnderChestCommand extends Command {
if (shouldOpenEchest) {
shouldOpenEchest = false;
if (InventoryUtil.getEchestInv() != null)
mc.setScreen(new PreviewScreen(InventoryUtil.getEchestInv(), null));
mc.gui.setScreen(new PreviewScreen(InventoryUtil.getEchestInv(), null));
}
}
}
@@ -8,12 +8,14 @@ import me.retucio.sputnik.friend.FriendManager;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Player;
public class FriendCommand extends Command {
public FriendCommand() {
super("friend", "gestión de amigos", "amigo", "f");
super("friend", "sputnik.commands.friend.desc", "amigo", "f");
}
@Override
@@ -24,7 +26,10 @@ public class FriendCommand extends Command {
.executes(ctx -> {
Player friend = ctx.getArgument("amigo", Player.class);
FriendManager.INSTANCE.add(friend.getUUID());
ChatUtil.info(ChatFormatting.GREEN + friend.getName().getString() + ChatFormatting.RESET + " ahora es tu amigo :)");
ChatUtil.info(Component.translatable(
"sputnik.commands.friend.added",
ChatFormatting.GREEN + friend.getName().getString() + ChatFormatting.RESET
));
return SUCCESS;
})
)
@@ -34,14 +39,17 @@ public class FriendCommand extends Command {
.executes(ctx -> {
Player friend = ctx.getArgument("amigo", Player.class);
FriendManager.INSTANCE.remove(friend.getUUID());
ChatUtil.info(ChatFormatting.GREEN + friend.getName().getString() + ChatFormatting.RESET + " ya no es tu amigo :(");
ChatUtil.info(Component.translatable(
"sputnik.commands.friend.removed",
ChatFormatting.GREEN + friend.getName().getString() + ChatFormatting.RESET
));
return SUCCESS;
})
)
)
.then(literal("list")
.executes(ctx -> {
ChatUtil.info(ChatFormatting.GOLD + "amigos:");
ChatUtil.info(ChatFormatting.GOLD + Component.translatable("sputnik.commands.friend.list").getString());
for (Friend friend : FriendManager.INSTANCE.getFriends()) {
ChatUtil.info("- " + friend.getName());
}
@@ -9,14 +9,14 @@ import net.minecraft.network.chat.Component;
public class GarbageCleanerCommand extends Command {
public GarbageCleanerCommand() {
super("gc", "limpia la ram mediante el recolector de basura (gc)", "garbagecollect");
super("gc", "sputnik.commands.gc.desc", "garbagecollect");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.executes(context -> {
builder.executes(ctx -> {
System.gc();
ChatUtil.info(Component.nullToEmpty("basura recolectada"));
ChatUtil.info(Component.translatable("sputnik.commands.gc.collected"));
return SUCCESS;
});
}
@@ -0,0 +1,28 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.command.CommandManager;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
public class HelpCommand extends Command {
public HelpCommand() {
super("help", Component.translatable("sputnik.commands.help.desc").getString(), "?", "ayuda", "h");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.executes(ctx -> {
for (Command command : CommandManager.commands) {
ChatUtil.info(CommandManager.INSTANCE.getPrefix()
+ ChatFormatting.BOLD + command.getName() + ": "
+ ChatFormatting.RESET + ChatFormatting.GRAY + command.getDescription().getString());
}
return SUCCESS;
});
}
}
@@ -0,0 +1,114 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.macro.Macro;
import me.retucio.sputnik.macro.MacroManager;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.KeyUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.util.List;
public class MacroCommand extends Command {
private static final SuggestionProvider<SharedSuggestionProvider> MACROS = (ctx, builder) ->
SharedSuggestionProvider.suggest(
MacroManager.INSTANCE.getMacros().stream().map(Macro::getName), builder);
public MacroCommand() {
super("macro", "sputnik.commands.macro.desc", "macros");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder
.executes(ctx -> {
list();
return SUCCESS;
})
.then(literal("list").executes(ctx -> {
list();
return SUCCESS;
}))
.then(literal("add")
.then(argument("nombre", StringArgumentType.word()).executes(ctx -> {
String name = ctx.getArgument("nombre", String.class);
if (MacroManager.INSTANCE.add(new Macro(name)))
ChatUtil.info(Component.translatable("sputnik.commands.macro.added",
ChatFormatting.AQUA + name));
else
ChatUtil.error(Component.translatable("sputnik.commands.macro.exists", name));
return SUCCESS;
})))
.then(literal("remove")
.then(argument("nombre", StringArgumentType.word()).suggests(MACROS).executes(ctx -> {
Macro macro = get(ctx.getArgument("nombre", String.class));
if (macro == null) return SUCCESS;
MacroManager.INSTANCE.remove(macro);
ChatUtil.info(Component.translatable("sputnik.commands.macro.removed",
ChatFormatting.AQUA + macro.getName()));
return SUCCESS;
})))
.then(literal("line")
.then(argument("nombre", StringArgumentType.word()).suggests(MACROS)
.then(argument("línea", StringArgumentType.greedyString()).executes(ctx -> {
Macro macro = get(ctx.getArgument("nombre", String.class));
if (macro == null) return SUCCESS;
String line = ctx.getArgument("línea", String.class);
macro.getLines().add(line);
MacroManager.INSTANCE.save();
ChatUtil.info(Component.translatable("sputnik.commands.macro.lineadded",
ChatFormatting.AQUA + macro.getName()));
return SUCCESS;
}))))
.then(literal("clear")
.then(argument("nombre", StringArgumentType.word()).suggests(MACROS).executes(ctx -> {
Macro macro = get(ctx.getArgument("nombre", String.class));
if (macro == null) return SUCCESS;
macro.getLines().clear();
MacroManager.INSTANCE.save();
ChatUtil.info(Component.translatable("sputnik.commands.macro.cleared",
ChatFormatting.AQUA + macro.getName()));
return SUCCESS;
})))
.then(literal("run")
.then(argument("nombre", StringArgumentType.word()).suggests(MACROS).executes(ctx -> {
Macro macro = get(ctx.getArgument("nombre", String.class));
if (macro != null) macro.run();
return SUCCESS;
})));
}
private static Macro get(String name) {
Macro macro = MacroManager.INSTANCE.getByName(name);
if (macro == null) ChatUtil.error(Component.translatable("sputnik.commands.macro.notfound", name));
return macro;
}
private static void list() {
List<Macro> macros = MacroManager.INSTANCE.getMacros();
if (macros.isEmpty()) {
ChatUtil.info(Component.translatable("sputnik.commands.macro.none"));
return;
}
ChatUtil.info(Component.translatable("sputnik.commands.macro.list", macros.size()));
for (Macro macro : macros)
ChatUtil.addMessage(ChatFormatting.GRAY + " - " + macro.getName()
+ ChatFormatting.DARK_GRAY + " [" + KeyUtil.getKeyName(macro.getKey()) + "] "
+ macro.getLines().size());
}
}
@@ -0,0 +1,265 @@
package me.retucio.sputnik.command.commands;
import com.github.retucio.neutrino.EventListener;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.event.TickEvent;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.MiscUtil;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.SharedConstants;
import net.minecraft.WorldVersion;
import net.minecraft.client.Screenshot;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.metadata.pack.PackFormat;
import net.minecraft.sounds.SoundEvents;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.arguments.StringArgumentType;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NonNull;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.ThreadLocalRandom;
/**
* literalmente robado de <a href="https://github.com/0xTas/stardust/blob/main/src/main/java/dev/stardust/commands/Panorama.java">aquí</a>
*/
public class PanoramaCommand extends Command {
private int i = -1;
int timer = 10;
boolean ready = false;
boolean warming = false;
private float prevYaw;
private float prevPitch;
private int prevWidth;
private int prevHeight;
private boolean prevHudHidden;
@Nullable Path dir = null;
public PanoramaCommand() {
super("panorama", "sputnik.commands.panorama.desc");
}
private void takeScreenshot() {
if (dir == null) return;
Screenshot.grab(
dir.toFile(),
"panorama_" + i + ".png",
mc.gameRenderer.mainRenderTarget(),
1, msg -> Sputnik.LOGGER.info(msg.getString())
);
i++;
warming = false;
}
private void begin(String name) {
if (mc.player == null) return;
dir = FabricLoader.getInstance().getGameDir().resolve(Sputnik.MOD_ID + "/panoramas/" + name);
dir.toFile().mkdirs();
i = 0;
warming = false;
prevYaw = mc.player.getYRot();
prevPitch = mc.player.getXRot();
prevWidth = mc.getWindow().getWidth();
prevHeight = mc.getWindow().getHeight();
prevHudHidden = mc.gui.hud.isHidden();
mc.getWindow().setWidth(4096);
mc.getWindow().setHeight(4096);
mc.gameRenderer.mainRenderTarget().resize(4096, 4096);
}
private void assembleResourcePack() {
if (dir == null) return;
Path resourcePacks = FabricLoader.getInstance().getGameDir().resolve("resourcepacks");
Path screenshotsFolder = FabricLoader.getInstance().getGameDir().resolve(Sputnik.MOD_ID + "/panoramas/" + dir.getFileName() + "/screenshots");
Path customBaseFolder = resourcePacks.resolve(dir.getFileName());
Path customPackFolder = customBaseFolder.resolve("assets/minecraft/textures/gui/title/background/");
customPackFolder.toFile().mkdirs();
String mcMeta =
"""
{
"pack":
{
"min_format": [65, 0],
"max_format": [%d, 0],
"description": "%s"
}
}
""".formatted(
getPackVersion(),
Component.translatable("sputnik.commands.panorama.packdesc").getString()
);
try {
int luckyNumber = ThreadLocalRandom.current().nextInt(6);
Files.walkFileTree(screenshotsFolder, new SimpleFileVisitor<>() {
public @NonNull FileVisitResult visitFile(@NonNull Path file, @NonNull BasicFileAttributes attrs) {
MiscUtil.copy(file.toFile(), customPackFolder.resolve(file.getFileName()).toFile());
// pack.png random de alguna de los screenshots
if (file.getFileName().toString().contains(String.valueOf(luckyNumber))) {
MiscUtil.copy(file.toFile(), customBaseFolder.resolve("pack.png").toFile());
}
return FileVisitResult.CONTINUE;
}
});
File packMeta = customBaseFolder.resolve("pack.mcmeta").toFile();
if (!packMeta.createNewFile()) {
ChatUtil.error(Component.translatable("sputnik.commands.panorama.errorassemble").getString());
return;
}
Files.write(packMeta.toPath(), mcMeta.getBytes());
// eliminar el efecto vignette feo de cojones que usa Minecraft por defecto
BufferedImage transparentOverlay = new BufferedImage(16, 128, BufferedImage.TYPE_INT_ARGB);
File overlayImage = customPackFolder.resolve("panorama_overlay.png").toFile();
ImageIO.write(transparentOverlay, "PNG", overlayImage);
} catch (Exception e) {
ChatUtil.error("error de i/o");
Sputnik.LOGGER.error(e);
}
ready = false;
if (mc.player != null) {
mc.player.playSound(SoundEvents.AMETHYST_BLOCK_CHIME, 1f, 1f);
ChatUtil.info(Component.translatable("sputnik.commands.panorama.ready").getString());
}
}
@EventListener
private void onTick(TickEvent.Post event) {
if (mc.player == null || dir == null) return;
if (timer > 0) {
--timer;
return;
} else {
timer = 10;
}
if (ready) {
assembleResourcePack();
}
if (i < 0) return;
switch (i) {
case 0 -> {
if (!warming) {
mc.gameRenderer.mainCamera().enablePanoramicMode();
mc.gameRenderer.setRenderBlockOutline(false);
mc.execute(() -> {
synchronized (mc.levelRenderer) {
mc.levelRenderer.notifyAll();
}
});
if (!mc.gui.hud.isHidden()) mc.gui.hud.toggle();
mc.player.setYRot(prevYaw);
mc.player.setXRot(0f);
warming = true;
} else takeScreenshot();
}
case 1 -> {
if (!warming) {
mc.player.setYRot((prevYaw + 90f) % 360f);
mc.player.setXRot(0f);
warming = true;
} else takeScreenshot();
}
case 2 -> {
if (!warming) {
mc.player.setYRot((prevYaw + 180f) % 360f);
mc.player.setXRot(0f);
warming = true;
} else takeScreenshot();
}
case 3 -> {
if (!warming) {
mc.player.setYRot((prevYaw - 90f) % 360f);
mc.player.setXRot(0f);
warming = true;
} else takeScreenshot();
}
case 4 -> {
if (!warming) {
mc.player.setYRot(prevYaw);
mc.player.setXRot(-90f);
warming = true;
} else takeScreenshot();
}
default -> {
if (!warming) {
mc.player.setYRot(prevYaw);
mc.player.setXRot(90f);
warming = true;
} else {
takeScreenshot();
i = -1;
mc.player.setYRot(prevYaw);
mc.player.setXRot(prevPitch);
mc.gameRenderer.mainCamera().disablePanoramicMode();
mc.gameRenderer.setRenderBlockOutline(true);
mc.getWindow().setWidth(prevWidth);
mc.getWindow().setHeight(prevHeight);
if (mc.gui.hud.isHidden() != prevHudHidden) mc.gui.hud.toggle();
mc.gameRenderer.mainRenderTarget().resize(prevWidth, prevHeight);
mc.execute(() -> {
synchronized (mc.levelRenderer) {
mc.levelRenderer.notifyAll();
}
});
timer = 100; // esperar a que las capturas se escriban
ready = true;
ChatUtil.info(Component.translatable("sputnik.commands.panorama.finalizing").getString());
}
}
}
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.then(
argument("name", StringArgumentType.word()).executes(ctx -> {
String name = ctx.getArgument("name", String.class);
begin(name);
return SUCCESS;
})
);
}
public static int getPackVersion() {
WorldVersion version = SharedConstants.getCurrentVersion();
PackFormat format = version.packVersion(PackType.CLIENT_RESOURCES);
return format.major();
}
}
@@ -7,11 +7,13 @@ import me.retucio.sputnik.util.MiscUtil;
import net.minecraft.client.gui.screens.inventory.InventoryScreen;
import net.minecraft.commands.SharedSuggestionProvider;
import java.awt.*;
// comando que debería de mostrar el inventario de un jugador, pero por cómo funcionan los servers, solo te deja ver el ítem que tenga en la mano
public class PeekCommand extends Command {
public PeekCommand() {
super("inventario", "cotillea el inventario de otro jugador", "invsee", "peek", "inv");
super("inventario", "sputnik.commands.peek.desc", "invsee", "peek", "inv");
}
@Override
@@ -12,6 +12,7 @@ import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.multiplayer.ClientSuggestionProvider;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundCommandsPacket;
import java.util.HashSet;
@@ -23,7 +24,7 @@ public class PluginsCommand extends Command {
private boolean scanning = false;
public PluginsCommand() {
super("plugins", "busca los plugins que tenga el server", "pl", "serverplugins");
super("plugins", "sputnik.commands.plugins.desc", "pl", "serverplugins");
Sputnik.EVENT_BUS.subscribe(this);
}
@@ -31,12 +32,12 @@ public class PluginsCommand extends Command {
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.executes(context -> {
if (mc.hasSingleplayerServer()) {
ChatUtil.error("estás en un solo jugador");
ChatUtil.error(Component.translatable("sputnik.commands.plugins.singleplayer"));
return SUCCESS;
}
if (scanning) {
ChatUtil.error("calmaaa");
ChatUtil.error(Component.translatable("sputnik.commands.plugins.calmdown"));
return SUCCESS;
}
@@ -60,7 +61,7 @@ public class PluginsCommand extends Command {
root.getChildren().forEach(this::checkNodeForPlugins);
if (!plugins.isEmpty()) {
ChatUtil.info(plugins.size() + " namespace(s) encontrado(s) de comandos:");
ChatUtil.info(Component.translatable("sputnik.commands.plugins.namespacesfound", plugins.size()));
plugins.stream()
.sorted()
.forEach(plugin -> ChatUtil.info(" - " + plugin));
@@ -69,7 +70,7 @@ public class PluginsCommand extends Command {
displayResults();
} catch (Exception e) {
ChatUtil.error("error analizando comandos: " + e.getMessage());
ChatUtil.error(Component.translatable("sputnik.commands.plugins.erroranalyzingcommands", e.getMessage()));
}
}
@@ -99,11 +100,11 @@ public class PluginsCommand extends Command {
private void displayResults() {
if (!plugins.isEmpty()) {
ChatUtil.info(plugins.size() + " plugin(s) encontrado(s)");
ChatUtil.info(Component.translatable("sputnik.commands.plugins.pluginsfound", plugins.size()));
plugins.stream().sorted()
.forEach(plugin -> ChatUtil.info(" - " + plugin));
} else {
ChatUtil.warn("sin plugins (probablemente)");
ChatUtil.warn(Component.translatable("sputnik.commands.plugins.noplugins"));
}
scanning = false;
@@ -13,17 +13,17 @@ import net.minecraft.network.chat.Component;
public class PrefixCommand extends Command {
public PrefixCommand() {
super("prefijo", "cambia el prefijo de los comandos", "prefix");
super("prefijo", "sputnik.commands.prefix.desc", "prefix");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder
.then(argument("prefijo", StringArgumentType.word()).executes(ctx -> {
String prefix = ctx.getArgument("prefijo", String.class);
ClientSettingsPanel.clientSettings.commandPrefix.setValue(prefix);
ChatUtil.info(Component.literal("prefijo cambiado a " + ChatFormatting.AQUA + prefix));
return SUCCESS;
}));
.then(argument("prefijo", StringArgumentType.word()).executes(ctx -> {
String prefix = ctx.getArgument("prefijo", String.class);
ClientSettingsPanel.clientSettings.commandPrefix.setValue(prefix);
ChatUtil.info(Component.translatable("sputnik.commands.prefix.changed", ChatFormatting.AQUA + prefix));
return SUCCESS;
}));
}
}
@@ -0,0 +1,92 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.config.ConfigManager;
import me.retucio.sputnik.config.ProfileManager;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.util.List;
public class ProfileCommand extends Command {
private static final SuggestionProvider<SharedSuggestionProvider> PROFILES = (ctx, builder) ->
SharedSuggestionProvider.suggest(ProfileManager.INSTANCE.getProfileNames(), builder);
public ProfileCommand() {
super("perfil", "sputnik.commands.profile.desc", "profile", "perfiles", "profiles");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder
.executes(ctx -> {
list();
return SUCCESS;
})
.then(literal("list").executes(ctx -> {
list();
return SUCCESS;
}))
.then(literal("save")
.then(argument("nombre", StringArgumentType.word()).executes(ctx -> {
String name = ctx.getArgument("nombre", String.class);
if (!ProfileManager.isValidName(name)) {
ChatUtil.error(Component.translatable("sputnik.commands.profile.invalidname", name));
return SUCCESS;
}
if (ProfileManager.INSTANCE.save(name)) {
ConfigManager.INSTANCE.save();
ChatUtil.info(Component.translatable("sputnik.commands.profile.saved",
ChatFormatting.AQUA + name));
} else {
ChatUtil.error(Component.translatable("sputnik.commands.profile.savefailed", name));
}
return SUCCESS;
})))
.then(literal("load")
.then(argument("nombre", StringArgumentType.word()).suggests(PROFILES).executes(ctx -> {
String name = ctx.getArgument("nombre", String.class);
if (ProfileManager.INSTANCE.load(name))
ChatUtil.info(Component.translatable("sputnik.commands.profile.loaded",
ChatFormatting.AQUA + name));
else
ChatUtil.error(Component.translatable("sputnik.commands.profile.notfound", name));
return SUCCESS;
})))
.then(literal("delete")
.then(argument("nombre", StringArgumentType.word()).suggests(PROFILES).executes(ctx -> {
String name = ctx.getArgument("nombre", String.class);
if (ProfileManager.INSTANCE.delete(name))
ChatUtil.info(Component.translatable("sputnik.commands.profile.deleted",
ChatFormatting.AQUA + name));
else
ChatUtil.error(Component.translatable("sputnik.commands.profile.notfound", name));
return SUCCESS;
})));
}
private static void list() {
List<String> profiles = ProfileManager.INSTANCE.getProfileNames();
if (profiles.isEmpty()) {
ChatUtil.info(Component.translatable("sputnik.commands.profile.none"));
return;
}
String current = ProfileManager.INSTANCE.getCurrent();
ChatUtil.info(Component.translatable("sputnik.commands.profile.list", profiles.size()));
for (String profile : profiles)
ChatUtil.addMessage((profile.equals(current) ? ChatFormatting.GREEN : ChatFormatting.GRAY) + " - " + profile);
}
}
@@ -3,7 +3,7 @@ package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
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.multiplayer.chat.GuiMessage;
import net.minecraft.commands.SharedSuggestionProvider;
@@ -13,7 +13,7 @@ import java.util.List;
public class PurgeCommand extends Command {
public PurgeCommand() {
super("purgar", "elimina mensajes del chat", "purge");
super("purgar", "sputnik.commands.purge.desc", "purge");
}
@Override
@@ -28,7 +28,7 @@ public class PurgeCommand extends Command {
}
private void purgeMessages(int amount) {
ChatComponent chatHud = mc.gui.getChat();
ChatComponent chatHud = mc.gui.hud.getChat();
synchronized (chatHud) {
List<GuiMessage.Line> visibleMessages = ((IChatComponent) chatHud).sputnik$getVisibleMessages();
@@ -10,7 +10,7 @@ import net.minecraft.commands.SharedSuggestionProvider;
public class RepeatCommand extends Command {
public RepeatCommand() {
super("repetir", "repite un comando x veces", "repeat");
super("repetir", "sputnik.commands.repeat.desc", "repeat");
}
@Override
@@ -13,7 +13,7 @@ import java.util.concurrent.TimeUnit;
public class RepeatWaitCommand extends Command {
public RepeatWaitCommand() {
super("repesperar", "combinación de $repetir y $esperar");
super("repesperar", "sputnik.commands.repeatwait.desc", "repeatwait");
}
@Override
@@ -6,6 +6,7 @@ import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.misc.ScreenshotPlus;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.io.IOException;
@@ -13,7 +14,7 @@ import java.io.IOException;
public class SaveScreenshotCommand extends Command {
public SaveScreenshotCommand() {
super("guardarcaptura", "guarda localmente la captura más reciente");
super("guardarcaptura", "sputnik.commands.savescreenshot.desc", "savescreenshot");
}
@Override
@@ -26,7 +27,7 @@ public class SaveScreenshotCommand extends Command {
try {
screenshotPlus.saveScreenshot();
} catch (IOException e) {
ChatUtil.error("no se pudo guardar la captura");
ChatUtil.error(Component.translatable("sputnik.commands.savescreenshot.error"));
}
}
});
@@ -18,7 +18,7 @@ import java.time.Instant;
public class SendCommand extends Command {
public SendCommand() {
super("enviar", "manda un mensaje en el chat", "send");
super("enviar", "sputnik.commands.send.desc", "send");
}
@Override
@@ -37,7 +37,7 @@ public class SendCommand extends Command {
LastSeenMessagesTracker tracker = ((ClientPacketListenerAccessor) handler).getLastSeenMessagesTracker();
LastSeenMessagesTracker.Update lastSeenMessages = tracker.generateAndApplyUpdate();
// empacar firma para un chat seguro
// empacar firma
MessageSignature messageSignature = ((ClientPacketListenerAccessor) handler)
.getSignedMessageEncoder()
.pack(new SignedMessageBody(
@@ -10,16 +10,18 @@ import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.world.entity.Entity;
import org.lwjgl.glfw.GLFW;
import java.awt.*;
public class SpectateCommand extends Command {
public SpectateCommand() {
super("espectar", "te permite adquirir el punto de vista de una entidad", "spectate", "spec");
super("espectar", "sputnik.commands.spectate.desc", "spectate", "spec");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder
.executes(ctx -> { // If no argument, use the entity player is looking at
.executes(ctx -> {
mc.execute(() -> {
Entity target = EntityUtil.getEntityPlayerIsLookingAt();
if (target != null)
@@ -40,7 +42,7 @@ public class SpectateCommand extends Command {
@EventListener
public void onKey(KeyEvent event) {
if (mc.getCameraEntity() != mc.player && mc.screen == null
if (mc.getCameraEntity() != mc.player && mc.gui.screen() == null
&& event.getKey() == GLFW.GLFW_KEY_ESCAPE && event.getAction() != GLFW.GLFW_RELEASE ) {
event.cancel();
mc.setCameraEntity(mc.player);
@@ -7,11 +7,13 @@ import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.ModuleManager;
import net.minecraft.commands.SharedSuggestionProvider;
import java.awt.*;
// porque usar el ratón es más lento que escribir, a menos que tengas toque con el teclado como yo
public class ToggleCommand extends Command {
public ToggleCommand() {
super("alternar", "enciende / apaga módulos", "toggle");
super("alternar", "sputnik.commands.toggle.desc", "toggle");
}
@Override
@@ -16,7 +16,7 @@ public class WaitCommand extends Command {
private static final ScheduledExecutorService DELAYED_EXECUTOR = new ScheduledThreadPoolExecutor(2);
public WaitCommand() {
super("wait", "espera un tiempo determinado antes de ejecutar otro comando", "esperar", "sleep", "delay");
super("wait", "sputnik.commands.wait.desc", "esperar", "sleep", "delay");
}
@Override
@@ -1,5 +1,7 @@
package me.retucio.sputnik.config;
import me.retucio.sputnik.util.I18nUtil;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -12,7 +14,8 @@ import java.util.Map;
public class BinarySerializer {
private static final int MAGIC_NUMBER = 0x53505554; // "SPUT"
private static final int VERSION = 2;
private static final int VERSION = 3;
private static final int MIN_VERSION = 2;
private static final byte TYPE_NULL = 0;
private static final byte TYPE_BOOLEAN = 1;
@@ -70,6 +73,8 @@ public class BinarySerializer {
writeStringBooleanMap(out, config.hudVisibilities);
writeStringStringMap(out, config.hudImagePaths);
writeStringStringMap(out, config.friends);
writeMacros(out, config.macros);
writeString(out, config.currentProfile);
if (config.searchBarPosition != null) {
out.writeBoolean(true);
@@ -83,43 +88,63 @@ public class BinarySerializer {
public static ClientConfig readConfig(File file)
throws IOException {
if (!file.exists() || file.length() == 0) {
throw new IOException("archivo de config. vacío o inexistente");
throw new IOException(I18nUtil.fmt("sputnik.config.error.empty",
"archivo de config. vacío o inexistente"));
}
try (DataInputStream in = new DataInputStream(
new BufferedInputStream(new FileInputStream(file)))) {
if (file.length() < 8) {
throw new IOException("archivo demasiado pequeño para ser válido");
throw new IOException(I18nUtil.fmt("sputnik.config.error.toosmall",
"archivo demasiado pequeño para ser válido"));
}
// verificar cabezal
int magic = in.readInt();
if (magic != MAGIC_NUMBER) {
throw new IOException("formato de archivo inválido: " + magic);
throw new IOException(I18nUtil.fmt("sputnik.config.error.invalidformat",
"formato de archivo inválido: %s", magic));
}
int version = in.readInt();
if (version != VERSION) {
throw new IOException("versión inválida: " + version);
if (version < MIN_VERSION || version > VERSION) {
throw new IOException(I18nUtil.fmt("sputnik.config.error.invalidversion",
"versión inválida: %s", version));
}
ClientConfig config = new ClientConfig();
// leer mapas. se leen a locales y la ClientConfig se construye al final: su
// constructor la suscribe al bus de eventos, así que crearla antes de terminar
// de leer dejaba una config. huérfana escuchando si el archivo estaba corrupto
Map<String, Boolean> moduleStates = readStringBooleanMap(in);
Map<String, Object> settings = readSettingsMap(in);
Map<String, int[]> settingsPanels = readStringIntArrayMap(in);
Map<String, ClientConfig.PanelData> extendablePanels = readExtendablePanels(in);
Map<String, int[]> hudPositions = readStringIntArrayMap(in);
Map<String, Boolean> hudVisibilities = readStringBooleanMap(in);
Map<String, String> hudImagePaths = readStringStringMap(in);
Map<String, String> friends = readStringStringMap(in);
// leer mapas
config.moduleStates = readStringBooleanMap(in);
config.settings = readSettingsMap(in);
config.settingsPanels = readStringIntArrayMap(in);
config.extendablePanels = readExtendablePanels(in);
config.hudPositions = readStringIntArrayMap(in);
config.hudVisibilities = readStringBooleanMap(in);
config.hudImagePaths = readStringStringMap(in);
config.friends = readStringStringMap(in);
List<ClientConfig.MacroData> macros = version >= 3 ? readMacros(in) : new ArrayList<>();
String currentProfile = version >= 3 ? readString(in) : "";
// leer pos. de la barra de búsqueda
if (in.readBoolean()) {
config.searchBarPosition = new int[]{in.readInt(), in.readInt()};
}
int[] searchBarPosition = in.readBoolean()
? new int[]{in.readInt(), in.readInt()}
: null;
ClientConfig config = new ClientConfig();
config.moduleStates = moduleStates;
config.settings = settings;
config.settingsPanels = settingsPanels;
config.extendablePanels = extendablePanels;
config.hudPositions = hudPositions;
config.hudVisibilities = hudVisibilities;
config.hudImagePaths = hudImagePaths;
config.friends = friends;
config.macros = macros;
config.currentProfile = currentProfile == null ? "" : currentProfile;
if (searchBarPosition != null) config.searchBarPosition = searchBarPosition;
return config;
}
@@ -368,6 +393,35 @@ public class BinarySerializer {
return map;
}
private static void writeMacros(DataOutputStream out, List<ClientConfig.MacroData> macros)
throws IOException {
if (macros == null) {
out.writeInt(0);
return;
}
out.writeInt(macros.size());
for (ClientConfig.MacroData macro : macros) {
writeString(out, macro.name());
out.writeInt(macro.key());
out.writeInt(macro.lines().size());
for (String line : macro.lines()) writeString(out, line);
}
}
private static List<ClientConfig.MacroData> readMacros(DataInputStream in) throws IOException {
int size = in.readInt();
List<ClientConfig.MacroData> macros = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
String name = readString(in);
int key = in.readInt();
int lineCount = in.readInt();
List<String> lines = new ArrayList<>(lineCount);
for (int j = 0; j < lineCount; j++) lines.add(readString(in));
macros.add(new ClientConfig.MacroData(name, key, lines));
}
return macros;
}
private static void writeString(DataOutputStream out, String str) throws IOException {
if (str == null) {
out.writeInt(-1);
@@ -17,7 +17,7 @@ import java.util.Map;
import static me.retucio.sputnik.Sputnik.EVENT_BUS;
import static me.retucio.sputnik.Sputnik.mc;
// clase donde se guardan las configuraciones temporalmente hasta escribirlas en camera_tweaks.json
// clase donde se guardan las configuraciones temporalmente hasta escribirlas en sputnik.dat
public class ClientConfig {
// nombre del módulo -> estado del módulo
@@ -47,19 +47,31 @@ public class ClientConfig {
// amigos: uuid -> nombre
public Map<String, String> friends = new HashMap<>();
public List<MacroData> macros = new ArrayList<>();
// perfil cargado actualmente, vacío si es la config. suelta
public String currentProfile = "";
public ClientConfig() {
EVENT_BUS.subscribe(this);
}
// mientras se aplica una config. los cambios vienen de ella, así que reescribirla es
// como poco redundante, y al cambiar de perfil pisaba lo que aún no se había aplicado
private static boolean isApplying() {
return ConfigManager.INSTANCE != null && ConfigManager.INSTANCE.isApplying();
}
@EventListener
public void onToggleModule(ToggleModuleEvent event) {
if (isApplying()) return;
ConfigManager.INSTANCE.setModuleState(event.getModule());
}
@SuppressWarnings("rawtypes")
@EventListener
public void onUpdateSetting(UpdateSettingEvent event) {
if (mc == null || !event.shouldSave()) return;
if (mc == null || !event.shouldSave() || isApplying()) return;
// guardar ajustes con su respectivo tipo de valor, en formato "nombreMódulo:nombreAjuste"
Object value = null;
@@ -71,7 +83,8 @@ public class ClientConfig {
case StringSetting s: value = s.getValue(); break;
case ListSetting l: value = l.getConfigValues(); break;
case ColorSetting c: value = c.getConfigValue(); break;
case OptionSetting o: value = o.getDisplayName(); break;
case OptionSetting o: value = o.getConfigName(); break;
case FileSetting f: value = f.getValue(); break;
default: break;
}
@@ -104,9 +117,11 @@ public class ClientConfig {
ConfigManager.INSTANCE.markDirty();
}
// put y no replace: replace no hace nada si la clave no está todavía, así que mover un
// marco sin haberlo plegado antes (que es lo que crea la entrada) no se guardaba
@EventListener
public void onMoveModulePanel(ModulePanelEvent.Move event) {
extendablePanels.replace(event.getPanel().category.toString(), new PanelData(
extendablePanels.put(event.getPanel().category.toString(), new PanelData(
event.getPanel().getX(),
event.getPanel().getY(),
event.getPanel().isExtended()));
@@ -124,7 +139,7 @@ public class ClientConfig {
@EventListener
public void onMoveClientSettingsPanel(ClientSettingsPanelEvent.Move event) {
extendablePanels.replace("ajustes", new PanelData(
extendablePanels.put("ajustes", new PanelData(
ClickGui.INSTANCE.getClientSettingsPanel().getX(),
ClickGui.INSTANCE.getClientSettingsPanel().getY(),
ClickGui.INSTANCE.getClientSettingsPanel().isExtended()));
@@ -142,7 +157,7 @@ public class ClientConfig {
@EventListener
public void onMoveFriendsPanel(FriendPanelEvent.Move event) {
extendablePanels.replace("amigos", new PanelData(
extendablePanels.put("amigos", new PanelData(
ClickGui.INSTANCE.getFriendsPanel().getX(),
ClickGui.INSTANCE.getFriendsPanel().getY(),
ClickGui.INSTANCE.getFriendsPanel().isExtended()));
@@ -151,6 +166,7 @@ public class ClientConfig {
@EventListener
public void onAddFriend(FriendEvent.Add event) {
if (isApplying()) return;
String uuid = event.getFriend().getUuid().toString();
String name = event.getFriend().getName();
if (!friends.containsKey(uuid)) {
@@ -161,6 +177,7 @@ public class ClientConfig {
@EventListener
public void onRemoveFriend(FriendEvent.Remove event) {
if (isApplying()) return;
friends.remove(event.getFriend().getUuid().toString());
ConfigManager.INSTANCE.markDirty();
}
@@ -168,4 +185,8 @@ public class ClientConfig {
public record PanelData(int x, int y, boolean extended) implements Serializable {
@Serial private static final long serialVersionUID = 1L;
}
public record MacroData(String name, int key, List<String> lines) implements Serializable {
@Serial private static final long serialVersionUID = 1L;
}
}
@@ -8,27 +8,35 @@ import me.retucio.sputnik.friend.FriendManager;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.client.ClientSettingsModule;
import me.retucio.sputnik.macro.Macro;
import me.retucio.sputnik.macro.MacroManager;
import me.retucio.sputnik.module.setting.*;
import me.retucio.sputnik.module.setting.settings.*;
import me.retucio.sputnik.ui.hud.HudElement;
import me.retucio.sputnik.ui.hud.HudRenderer;
import me.retucio.sputnik.ui.widgets.Panel;
import me.retucio.sputnik.ui.widgets.panels.ExtendablePanel;
import me.retucio.sputnik.ui.widgets.panels.FriendsPanel;
import me.retucio.sputnik.ui.widgets.panels.ModulePanel;
import me.retucio.sputnik.ui.screen.ClickGui;
import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel;
import me.retucio.sputnik.ui.widgets.panels.SettingsPanel;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.I18nUtil;
import net.minecraft.network.chat.Component;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
// se ocupa de guardar, cargar y aplicar ajustes
@@ -40,8 +48,9 @@ public class ConfigManager {
public static ConfigManager INSTANCE;
private boolean loaded = false;
private boolean applying = false;
private boolean dirty = false;
private long lastDirtyTime = 0L;
private long lastSaveTime = System.currentTimeMillis();
private ClientConfig config = null;
private final ExecutorService saveExecutor = Executors.newSingleThreadExecutor(r -> {
@@ -50,7 +59,10 @@ public class ConfigManager {
return t;
});
private final AtomicBoolean saveQueued = new AtomicBoolean(false);
// instantánea pendiente de escribir. si llega otra antes de que el hilo de fondo escriba,
// sustituye a la anterior en vez de descartarse (que era lo que pasaba con un simple
// "ya hay uno en cola": se tiraba justo la más reciente y se perdían esos cambios)
private final AtomicReference<byte[]> pending = new AtomicReference<>();
// ------------------ MÉTODOS PRINCIPALES ------------------
@@ -58,58 +70,81 @@ public class ConfigManager {
// guardar configuraciones
public void save() {
if (!loaded) return;
ensureConfig();
// serializar a bytes en el hilo principal (snapshot inmediato, evita race conditions)
final byte[] snapshot;
try {
snapshot = BinarySerializer.toBytes(config);
} catch (IOException e) {
Sputnik.LOGGER.error("error al serializar ajustes", e);
return;
}
// si ya hay un guardado en cola, no añadir otro
if (!saveQueued.compareAndSet(false, true)) return;
saveExecutor.execute(() -> {
try {
BinarySerializer.writeBytes(snapshot, CONFIG_FILE);
Sputnik.LOGGER.info("ajustes guardados");
} catch (IOException e) {
Sputnik.LOGGER.error("error al guardar ajustes", e);
} finally {
saveQueued.set(false);
}
});
writeSnapshot(false);
}
public void saveOnShutdown() {
// esperar a que termine cualquier guardado en curso, luego guardar síncronamente
// no aceptar más tareas y esperar a que termine el guardado en curso
saveExecutor.shutdown();
try {
// dar hasta 5 segundos para que termine el guardado en curso
if (!saveExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
Sputnik.LOGGER.warn("el hilo de guardado no terminó a tiempo");
Sputnik.LOGGER.warn(I18nUtil.fmt("sputnik.config.warn.thread",
"el hilo de guardado no terminó a tiempo"));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// guardado final sincrónico por si había cambios pendientes no guardados
if (dirty) save();
// el guardado final va en este mismo hilo: el executor ya está cerrado, así que
// encolar la escritura lanzaba RejectedExecutionException y no se guardaba NADA al
// cerrar el juego. o sea que se perdía todo lo hecho desde el último autoguardado
if (!loaded) return;
writeSnapshot(true);
}
// serializa en el hilo actual (instantánea inmediata, evita race conditions) y escribe
// en el de fondo, salvo que se pida síncrono (al cerrar)
private void writeSnapshot(boolean sync) {
ensureConfig();
final byte[] snapshot;
try {
snapshot = BinarySerializer.toBytes(config);
} catch (IOException e) {
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.serialization",
"error al serializar ajustes"), e);
return;
}
dirty = false;
lastSaveTime = System.currentTimeMillis();
if (sync) {
writeToDisk(snapshot);
return;
}
// si ya había una instantánea esperando, la tarea encolada recogerá ésta
if (pending.getAndSet(snapshot) != null) return;
saveExecutor.execute(() -> {
byte[] bytes = pending.getAndSet(null);
if (bytes != null) writeToDisk(bytes);
});
}
private static void writeToDisk(byte[] bytes) {
try {
BinarySerializer.writeBytes(bytes, CONFIG_FILE);
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.saved", "ajustes guardados"));
} catch (IOException e) {
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.saving",
"error al guardar ajustes"), e);
}
}
public void load() {
Sputnik.LOGGER.info("cargando ajustes...");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.loading", "cargando ajustes..."));
if (CONFIG_FILE.exists()) {
try {
config = BinarySerializer.readConfig(CONFIG_FILE);
Sputnik.LOGGER.info("ajustes cargados");
setConfig(BinarySerializer.readConfig(CONFIG_FILE));
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.loaded", "ajustes cargados"));
return;
} catch (IOException e) {
Sputnik.LOGGER.error("error cargando ajustes, intentando json", e);
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.loading",
"error cargando ajustes, intentando json"), e);
}
}
@@ -117,35 +152,96 @@ public class ConfigManager {
if (LEGACY_JSON_FILE.exists()) {
try (FileReader reader = new FileReader(LEGACY_JSON_FILE)) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
config = gson.fromJson(reader, ClientConfig.class);
Sputnik.LOGGER.info("ajustes json cargados (migración)");
setConfig(gson.fromJson(reader, ClientConfig.class));
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.json",
"ajustes json cargados (migración)"));
save();
// aquí `loaded` todavía es false (se pone al aplicar), así que save() no
// haría nada: la migración se escribe directamente
writeSnapshot(true);
return;
} catch (IOException e) {
Sputnik.LOGGER.error("error cargando json, usando defaults", e);
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.json",
"error cargando json, usando defaults"), e);
}
}
// crear nuevo config
ensureConfig();
writeSnapshot(true);
}
// aplicar configuraciones cargadas. devuelve false si todavía no se puede (falta algo por
// construir), para que se reintente en el siguiente tick en vez de quedarse sin aplicar
public boolean applyConfig() {
if (!shouldApply()) return false;
applying = true;
try {
// los del mod primero: entre ellos está "teclas multimódulo", que decide si al
// asignar una tecla se le quita a los demás módulos. aplicándolos los últimos,
// las teclas de los módulos se cargaban con el valor por defecto de ese ajuste
applyClientSettings();
applyModuleStates();
applyModuleSettings();
applySettingsPanels();
applySearchBarPosition();
applyExtendablePanels();
applyFriends();
applyMacros();
applyHudElements();
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.applied", "ajustes aplicados"));
} catch (Exception e) {
// si algo peta a medio aplicar no se puede dejar `loaded` en false: save() no
// haría nada en toda la sesión y se perdería la config. entera al cerrar
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.applying",
"error aplicando ajustes"), e);
} finally {
applying = false;
loaded = true;
}
return true;
}
// true mientras se está cargando la config. sobre los ajustes. lo mira KeySetting para no
// tratar las teclas guardadas como si el usuario las acabara de asignar, y ClientConfig
// para no reescribir en la config. lo que se le está aplicando
public boolean isApplying() {
return applying;
}
// sustituye la config. viva por otra y la vuelca sobre el estado actual del cliente
public void replaceConfig(ClientConfig newConfig) {
applying = true;
try {
resetLiveState();
} finally {
applying = false;
}
setConfig(newConfig);
loaded = false;
applyConfig();
save();
}
// aplicar configuraciones cargadas
public void applyConfig() {
if (!shouldApply()) return;
// lo que no venga en la config. nueva tiene que quedarse en su valor por defecto, no en
// el que tuviera el perfil anterior
private void resetLiveState() {
if (ClickGui.INSTANCE != null) ClickGui.INSTANCE.closeAllSettingsPanels();
applyModuleStates();
applyModuleSettings();
applySettingsPanels();
applySearchBarPosition();
applyClientSettings();
applyExtendablePanels();
applyFriends();
for (Module module : ModuleManager.INSTANCE.getModules()) {
module.setEnabled(false);
module.getSettings().forEach(Setting::reset);
}
ClientSettingsPanel.clientSettings.getSettings().forEach(Setting::reset);
loaded = true;
Sputnik.LOGGER.info("ajustes aplicados");
for (Friend friend : new ArrayList<>(FriendManager.INSTANCE.getFriends()))
FriendManager.INSTANCE.remove(friend);
MacroManager.INSTANCE.setMacros(List.of());
}
// obtener configuración actual
@@ -202,6 +298,20 @@ public class ConfigManager {
markDirty();
}
public void setMacros(List<Macro> macros) {
ensureConfig();
config.macros = macros.stream()
.map(macro -> new ClientConfig.MacroData(macro.getName(), macro.getKey(), List.copyOf(macro.getLines())))
.toList();
markDirty();
}
public void setCurrentProfile(String name) {
ensureConfig();
config.currentProfile = name;
markDirty();
}
// ------------------ APLICAR AJUSTES ------------------
@@ -211,7 +321,28 @@ public class ConfigManager {
friend.setName(name);
FriendManager.INSTANCE.add(friend);
});
Sputnik.LOGGER.info("amigos aplicados");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.friends",
"amigos aplicados"));
}
public void applyMacros() {
MacroManager.INSTANCE.setMacros(config.macros.stream()
.map(data -> new Macro(data.name(), data.key(), data.lines()))
.toList());
}
// el HudRenderer coloca sus elementos al construirse, pero al cambiar de perfil ya existen
public void applyHudElements() {
if (HudRenderer.INSTANCE == null) return;
for (HudElement element : HudRenderer.INSTANCE.getElements()) {
int[] position = config.hudPositions.get(element.getId());
if (position != null && position.length == 2) element.setPosition(position[0], position[1]);
else element.resetPosition();
Boolean visible = config.hudVisibilities.get(element.getId());
element.setVisible(visible == null || visible);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
@@ -231,7 +362,9 @@ public class ConfigManager {
case ListSetting listSetting -> applyListSetting(listSetting, value);
case ColorSetting colorSetting -> applyColorSetting(colorSetting, value);
case OptionSetting optionSetting -> applyOptionSetting(optionSetting, value);
default -> Sputnik.LOGGER.warn("watafac queseso: {}", setting.getClass().getSimpleName());
case FileSetting fileSetting -> applyFileSetting(fileSetting, value);
default -> Sputnik.LOGGER.warn(I18nUtil.fmt("sputnik.config.warn.unknownsetting",
"watafac queseso: %s", setting.getClass().getSimpleName()));
}
}
@@ -241,14 +374,16 @@ public class ConfigManager {
module.setEnabled(config.moduleStates.get(module.getName()));
}
});
Sputnik.LOGGER.info("estados de módulos aplicados");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.modulestates",
"estados de módulos aplicados"));
}
private void applyModuleSettings() {
ModuleManager.INSTANCE.getModules().forEach(module -> {
module.getSettings().forEach(this::applySetting);
});
Sputnik.LOGGER.info("ajustes de módulos aplicados");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.modulesettings",
"ajustes de módulos aplicados"));
}
private void applySettingsPanels() {
@@ -256,14 +391,16 @@ public class ConfigManager {
Module module = ModuleManager.INSTANCE.getModuleByName(moduleName);
if (module == null) {
Sputnik.LOGGER.warn("\"null\" como módulo (\"{}\")", moduleName);
Sputnik.LOGGER.warn(I18nUtil.fmt("sputnik.config.warn.nullmodule",
"\"null\" como módulo (\"%s\")", moduleName));
return;
}
ClickGui.INSTANCE.openSettingsPanel(module, position[0], position[1]);
});
Sputnik.LOGGER.info("estados de marcos de ajustes aplicados");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.settingspanels",
"estados de marcos de ajustes aplicados"));
}
private void applySearchBarPosition() {
@@ -271,13 +408,15 @@ public class ConfigManager {
ClickGui.INSTANCE.getSearchBar().setX(config.searchBarPosition[0]);
ClickGui.INSTANCE.getSearchBar().setY(config.searchBarPosition[1]);
}
Sputnik.LOGGER.info("posición de la barra de búsqueda aplicada");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.searchbar",
"posición de la barra de búsqueda aplicada"));
}
private void applyClientSettings() {
ClientSettingsPanel.clientSettings.setEnabled(true);
ClientSettingsPanel.clientSettings.getSettings().forEach(this::applySetting);
Sputnik.LOGGER.info("ajustes del cliente aplicados");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.clientsettings",
"ajustes del cliente aplicados"));
}
private void applyExtendablePanels() {
@@ -287,7 +426,8 @@ public class ConfigManager {
applyExtendablePanel(ClickGui.INSTANCE.getClientSettingsPanel());
applyExtendablePanel(ClickGui.INSTANCE.getFriendsPanel());
Sputnik.LOGGER.info("posiciones de marcos extendibles aplicadas");
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.extendablepanels",
"posiciones de marcos extendibles aplicadas"));
ClickGui.INSTANCE.refreshListButtons();
}
@@ -308,14 +448,8 @@ public class ConfigManager {
panel.setX(panelData.x());
panel.setY(panelData.y());
if (panel instanceof ModulePanel mPanel) {
mPanel.setExtended(panelData.extended());
}
if (panel instanceof ClientSettingsPanel sPanel) {
sPanel.setExtended(panelData.extended());
}
if (panel instanceof FriendsPanel fPanel) {
fPanel.setExtended(panelData.extended());
if (panel instanceof ExtendablePanel<?> ePanel) {
ePanel.setExtended(panelData.extended());
}
}
}
@@ -344,8 +478,8 @@ public class ConfigManager {
}
private static void applyStringSetting(StringSetting setting, Object value) {
if (value instanceof String) {
setting.setValue((String) value);
if (value instanceof String v) {
setting.setValue(v);
}
}
@@ -362,8 +496,14 @@ public class ConfigManager {
Map<T, Boolean> convertedValues = new HashMap<>();
for (T option : setting.getOptions()) {
String key = setting.getDisplayName(option);
String key = setting.getConfigName(option);
Object booleanValue = map.get(key);
// compatibilidad con configs. de antes, cuando la clave era el nombre ya
// traducido (el de vanilla en inglés). sin esto la lista entera se quedaba
// en false, o sea que al usuario se le borraba lo que tuviera marcado
if (booleanValue == null) booleanValue = map.get(I18nUtil.vanillaName(key));
convertedValues.put(option, booleanValue instanceof Boolean && (Boolean) booleanValue);
}
@@ -400,6 +540,12 @@ public class ConfigManager {
}
}
private static void applyFileSetting(FileSetting setting, Object value) {
if (value instanceof String v) {
setting.setValue(v);
}
}
private static <T> void applyOptionalProperty(Map<?, ?> map, String key, Class<T> type, Consumer<T> setter) {
Object value = map.get(key);
if (type.isInstance(value)) {
@@ -414,12 +560,24 @@ public class ConfigManager {
return setting.getSg().getModule().getName() + ":" + setting.getTrueName();
}
// ClickGui incluida: los marcos y la barra de búsqueda se aplican sobre ella, y se
// construye en una tarea aparte. si todavía no está, mejor esperar al siguiente tick
private boolean shouldApply() {
return Sputnik.mc != null && ModuleManager.INSTANCE != null && config != null;
return Sputnik.mc != null && ModuleManager.INSTANCE != null
&& ClickGui.INSTANCE != null && config != null;
}
private void ensureConfig() {
if (config == null) config = new ClientConfig();
if (config == null) setConfig(new ClientConfig());
}
// ClientConfig se suscribe al bus en su constructor, así que al reemplazarla hay que
// dar de baja a la anterior: si no, la vieja seguía escuchando y escribiendo en un
// mapa que ya no era el que se guarda
private void setConfig(ClientConfig newConfig) {
if (config == newConfig) return;
if (config != null) Sputnik.EVENT_BUS.unsubscribe(config);
config = newConfig;
}
public boolean hasLoaded() {
@@ -428,7 +586,6 @@ public class ConfigManager {
public void markDirty() {
this.dirty = true;
this.lastDirtyTime = System.currentTimeMillis();
}
public boolean isDirty() {
@@ -441,11 +598,14 @@ public class ConfigManager {
ClientSettingsModule settings = ClientSettingsPanel.clientSettings;
if (!settings.autosave.getValue()) return;
// desde el último guardado, no desde el último cambio: contando desde el cambio,
// tocando ajustes cada menos del intervalo no se autoguardaba nunca
long intervalMs = (long) (settings.autosaveInterval.getValue() * 1000);
if (System.currentTimeMillis() - lastDirtyTime < intervalMs) return;
if (System.currentTimeMillis() - lastSaveTime < intervalMs) return;
HudRenderer.INSTANCE.pushNotification("autoguardando...", "guardando configuración...");
HudRenderer.INSTANCE.pushNotification(
Component.translatable("sputnik.config.autosaving.title").getString(),
Component.translatable("sputnik.config.autosaving.desc").getString());
save();
dirty = false;
}
}
@@ -0,0 +1,99 @@
package me.retucio.sputnik.config;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.util.I18nUtil;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class ProfileManager {
public static final int MAX_NAME_LENGTH = 32;
private static final File DIR = new File("sputnik/profiles");
private static final String EXTENSION = ".dat";
public static ProfileManager INSTANCE;
public List<String> getProfileNames() {
File[] files = DIR.listFiles((dir, name) -> name.endsWith(EXTENSION));
if (files == null) return List.of();
List<String> names = new ArrayList<>();
for (File file : files) {
String name = file.getName();
names.add(name.substring(0, name.length() - EXTENSION.length()));
}
names.sort(Comparator.naturalOrder());
return names;
}
public boolean exists(String name) {
return fileOf(name).exists();
}
public String getCurrent() {
return ConfigManager.INSTANCE.getConfig().currentProfile;
}
// vuelca la config. viva en el perfil y lo deja como actual
public boolean save(String name) {
if (!isValidName(name)) return false;
if (!DIR.exists() && !DIR.mkdirs()) {
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.profile.error.dir",
"no se pudo crear el directorio de perfiles"));
return false;
}
ConfigManager.INSTANCE.setCurrentProfile(name);
try {
BinarySerializer.writeConfig(ConfigManager.INSTANCE.getConfig(), fileOf(name));
return true;
} catch (IOException e) {
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.profile.error.saving",
"error al guardar el perfil \"%s\"", name), e);
return false;
}
}
public boolean load(String name) {
if (!exists(name)) return false;
ClientConfig profile;
try {
profile = BinarySerializer.readConfig(fileOf(name));
} catch (IOException e) {
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.profile.error.loading",
"error al cargar el perfil \"%s\"", name), e);
return false;
}
profile.currentProfile = name;
ConfigManager.INSTANCE.replaceConfig(profile);
return true;
}
public boolean delete(String name) {
if (!exists(name)) return false;
if (!fileOf(name).delete()) return false;
if (name.equals(getCurrent())) ConfigManager.INSTANCE.setCurrentProfile("");
return true;
}
public static boolean isValidName(String name) {
return name != null
&& !name.isBlank()
&& name.length() <= MAX_NAME_LENGTH
&& name.matches("[a-zA-Z0-9 _-]+");
}
private static File fileOf(String name) {
return new File(DIR, name + EXTENSION);
}
}
@@ -0,0 +1,21 @@
package me.retucio.sputnik.event.interact;
import com.github.retucio.neutrino.Event;
import net.minecraft.world.item.ItemStack;
import java.util.List;
public class UpdateInventoryEvent extends Event {
private final List<ItemStack> prev;
public UpdateInventoryEvent(List<ItemStack> prev) {
this.prev = prev;
}
public List<ItemStack> getPrev() {
return prev;
}
}
@@ -0,0 +1,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.Velocity;
import me.retucio.sputnik.module.modules.render.Nametags;
import me.retucio.sputnik.util.interfaces.IVec3;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.Entity;
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.vehicle.boat.AbstractBoat;
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.Shadow;
import org.spongepowered.asm.mixin.Unique;
@@ -3,13 +3,15 @@ package me.retucio.sputnik.mixin.mixins.entity;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.render.EntityESP;
import me.retucio.sputnik.module.modules.render.EntityEsp;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@@ -17,21 +19,21 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class EntityRendererMixin {
@Unique
private EntityESP esp;
private EntityEsp esp;
@Inject(method = "<init>", at = @At("TAIL"))
private void getModules(CallbackInfo ci) {
esp = ModuleManager.INSTANCE.getModuleByClass(EntityESP.class);
esp = ModuleManager.INSTANCE.getModuleByClass(EntityEsp.class);
}
@ModifyExpressionValue(method = "extractRenderState", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;shouldEntityAppearGlowing(Lnet/minecraft/world/entity/Entity;)Z"))
private boolean renderGlowESP(boolean original, @Local(argsOnly = true) Entity entity) {
return (esp.isEnabled() && esp.mode.is(EntityESP.ESPMode.GLOW) && esp.entities.isEnabled(entity.getType())) || original;
return (esp.isEnabled() && esp.mode.is(EntityEsp.ESPMode.GLOW) && esp.entities.isEnabled(entity.getType())) || original;
}
@ModifyExpressionValue(method = "extractRenderState", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;getTeamColor()I"))
private int setGlowESPColor(int original, @Local(argsOnly = true) Entity entity) {
if (esp.isEnabled() && esp.mode.is(EntityESP.ESPMode.GLOW) && esp.entities.isEnabled(entity.getType())) return esp.glowColor.getRGB();
if (esp.isEnabled() && esp.mode.is(EntityEsp.ESPMode.GLOW) && esp.entities.isEnabled(entity.getType())) return esp.glowColor.getRgb();
return original;
}
}
@@ -8,6 +8,7 @@ import me.retucio.sputnik.module.modules.movement.Velocity;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
@@ -61,8 +62,8 @@ public abstract class LivingEntityMixin {
if (step.isEnabled()) cir.setReturnValue(step.height.getFloatValue());
}
@Inject(method = "knockback", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;setDeltaMovement(DDD)V"), cancellable = true)
private void onTakeKb(double strength, double x, double z, CallbackInfo ci, @Local(name = "deltaMovement") Vec3 dm, @Local(name = "deltaVector") Vec3 dv) {
@Inject(method = "knockback(DDDLnet/minecraft/world/damagesource/DamageSource;FZ)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;setDeltaMovement(DDD)V"), cancellable = true)
private void onTakeKb(double strength, double x, double z, DamageSource source, float damage, boolean comesFromEffect, CallbackInfo ci, @Local(name = "deltaMovement") Vec3 dm, @Local(name = "deltaVector") Vec3 dv) {
Velocity velocity = ModuleManager.INSTANCE.getModuleByClass(Velocity.class);
if (!velocity.isEnabled() || velocity.hits.getValue()) return;
boolean onGround = ((LivingEntity) (Object) this).onGround();
@@ -5,10 +5,10 @@ import me.retucio.sputnik.event.network.ReceiveMessageEvent;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.misc.ChatPlus;
import me.retucio.sputnik.util.interfaces.IChatComponent;
import me.retucio.sputnik.util.interfaces.IGuiMessage;
import me.retucio.sputnik.util.interfaces.IGuiMessageLine;
import me.retucio.sputnik.util.interfaces.IChatListener;
import me.retucio.sputnik.mixinterfaces.IChatComponent;
import me.retucio.sputnik.mixinterfaces.IGuiMessage;
import me.retucio.sputnik.mixinterfaces.IGuiMessageLine;
import me.retucio.sputnik.mixinterfaces.IChatListener;
import net.minecraft.client.gui.components.ChatComponent;
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;"))
private GuiMessage.Line onAddMessage_modifyChatHudLineVisible(GuiMessage.Line line, @Local(name = "i") int i) {
IChatListener iListener = (IChatListener) Sputnik.mc.getChatListener();
IChatListener iListener = (IChatListener) Sputnik.mc.gui.chatListener();
IGuiMessageLine iLine = (IGuiMessageLine) (Object) line;
iLine.sputnik$setSender(iListener.sputnik$getSender());
@@ -94,7 +94,7 @@ public abstract class ChatComponentMixin implements IChatComponent {
@ModifyExpressionValue(method = "addMessage", at = @At(value = "NEW", target = "(ILnet/minecraft/network/chat/Component;Lnet/minecraft/network/chat/MessageSignature;Lnet/minecraft/client/multiplayer/chat/GuiMessageSource;Lnet/minecraft/client/multiplayer/chat/GuiMessageTag;)Lnet/minecraft/client/multiplayer/chat/GuiMessage;"))
private GuiMessage onAddMessage_modifyChatHudLine(GuiMessage line) {
IChatListener iListener = (IChatListener) Sputnik.mc.getChatListener();
IChatListener iListener = (IChatListener) Sputnik.mc.gui.chatListener();
((IGuiMessage) (Object) line).sputnik$setSender(iListener.sputnik$getSender());
return line;
}
@@ -1,7 +1,7 @@
package me.retucio.sputnik.mixin.mixins.hud;
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.util.FormattedCharSequence;
import org.spongepowered.asm.mixin.Final;
@@ -1,7 +1,7 @@
package me.retucio.sputnik.mixin.mixins.hud;
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.network.chat.Component;
import org.spongepowered.asm.mixin.Final;
@@ -1,107 +1,21 @@
package me.retucio.sputnik.mixin.mixins.hud;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.event.render.Render2DEvent;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.camera.CrosshairPlus;
import me.retucio.sputnik.module.modules.misc.ChatPlus;
import me.retucio.sputnik.module.modules.camera.Freecam;
import me.retucio.sputnik.module.modules.render.NoRender;
import me.retucio.sputnik.ui.hud.HudRenderer;
import net.minecraft.client.DeltaTracker;
import me.retucio.sputnik.event.interact.OpenScreenEvent;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.resources.Identifier;
import net.minecraft.client.gui.screens.Screen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArgs;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.invoke.arg.Args;
// lo del hud se mudó a HudMixin; Gui ya sólo lleva las pantallas
@Mixin(Gui.class)
public abstract class GuiMixin {
@Unique
NoRender noRender;
@Inject(method = "<init>", at = @At("TAIL"))
private void getModules(CallbackInfo ci) {
noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class);
}
@Inject(method = "extractRenderState", at = @At("TAIL"))
private void onRenderHud(GuiGraphicsExtractor gui, DeltaTracker dt, CallbackInfo ci) {
Sputnik.EVENT_BUS.post(new Render2DEvent(gui, dt));
}
// norender
@Inject(method = "extractScoreboardSidebar", at = @At("HEAD"), cancellable = true)
private void noRenderScoreboard(CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.scoreboard.getValue()) ci.cancel();
}
@Inject(method = "extractTitle", at = @At("HEAD"), cancellable = true)
private void noRenderTitles(GuiGraphicsExtractor graphics, DeltaTracker deltaTracker, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.titles.getValue()) ci.cancel();
}
@Inject(method = "extractConfusionOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderNausea(GuiGraphicsExtractor graphics, float strength, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.nauseaEffect.getValue()) ci.cancel();
}
@Inject(method = "extractSpyglassOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderSpyglass(GuiGraphicsExtractor graphics, float scale, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.spyglassOverlay.getValue()) ci.cancel();
}
@Inject(method = "extractPortalOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderPortal(GuiGraphicsExtractor graphics, float alpha, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.portalOverlay.getValue()) ci.cancel();
}
@Inject(method = "extractTextureOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderPumpkinBlur(GuiGraphicsExtractor graphics, Identifier texture, float alpha, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.pumpkinOverlay.getValue() && texture.getPath().equals("textures/misc/pumpkinblur.png")) ci.cancel();
}
// otros
@Inject(method = "extractRenderState", at = @At("RETURN"))
private void renderHUD(GuiGraphicsExtractor gui, DeltaTracker dt, CallbackInfo ci) {
HudRenderer.INSTANCE.render(gui, dt);
}
@Redirect(method = "extractCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphicsExtractor;blitSprite(Lcom/mojang/blaze3d/pipeline/RenderPipeline;Lnet/minecraft/resources/Identifier;IIII)V", ordinal = 0))
private void onRenderCrosshair(GuiGraphicsExtractor gui, RenderPipeline pipeline, Identifier id, int x, int y, int width, int height) {
// dangerous abbreviation
CrosshairPlus cp = ModuleManager.INSTANCE.getModuleByClass(CrosshairPlus.class);
int color = cp.isEnabled() ? cp.color.getRGB() : -1;
int w = cp.isEnabled() ? cp.width.getIntValue() : width;
int h = cp.isEnabled() ? cp.height.getIntValue() : height;
gui.blitSprite(pipeline, id, (gui.guiWidth() - w) / 2, (gui.guiHeight() - h) / 2, w, h, color);
}
@ModifyExpressionValue(method = "extractCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/CameraType;isFirstPerson()Z"))
private boolean alwaysRenderCrosshairInFreecam(boolean firstPerson) {
return ModuleManager.INSTANCE.getModuleByClass(Freecam.class).isEnabled() || firstPerson;
}
@Inject(method = "onDisconnected", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/components/ChatComponent;clearMessages(Z)V"), cancellable = true)
private void onClear(CallbackInfo ci) {
ChatPlus chatPlus = ModuleManager.INSTANCE.getModuleByClass(ChatPlus.class);
if (chatPlus.isEnabled() && chatPlus.keepHistory.getValue()) ci.cancel();
@Inject(method = "setScreen", at = @At("HEAD"), cancellable = true)
private void onOpenScreen(Screen screen, CallbackInfo ci) {
OpenScreenEvent event = Sputnik.EVENT_BUS.post(new OpenScreenEvent(screen));
if (event.isCancelled()) ci.cancel();
}
}
@@ -0,0 +1,104 @@
package me.retucio.sputnik.mixin.mixins.hud;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.event.render.Render2DEvent;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.camera.CrosshairPlus;
import me.retucio.sputnik.module.modules.misc.ChatPlus;
import me.retucio.sputnik.module.modules.camera.Freecam;
import me.retucio.sputnik.module.modules.render.NoRender;
import me.retucio.sputnik.ui.hud.HudRenderer;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.gui.Hud;
import net.minecraft.resources.Identifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
// 26.2 partió Gui en Gui (pantallas) y Hud (el hud en sí); todo esto vivía en GuiMixin
@Mixin(Hud.class)
public abstract class HudMixin {
@Unique
NoRender noRender;
@Inject(method = "<init>", at = @At("TAIL"))
private void getModules(CallbackInfo ci) {
noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class);
}
@Inject(method = "extractRenderState", at = @At("TAIL"))
private void onRenderHud(GuiGraphicsExtractor gui, DeltaTracker dt, CallbackInfo ci) {
Sputnik.EVENT_BUS.post(new Render2DEvent(gui, dt));
}
// norender
@Inject(method = "extractScoreboardSidebar", at = @At("HEAD"), cancellable = true)
private void noRenderScoreboard(CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.scoreboard.getValue()) ci.cancel();
}
@Inject(method = "extractTitle", at = @At("HEAD"), cancellable = true)
private void noRenderTitles(GuiGraphicsExtractor graphics, DeltaTracker deltaTracker, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.titles.getValue()) ci.cancel();
}
@Inject(method = "extractConfusionOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderNausea(GuiGraphicsExtractor graphics, float strength, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.nauseaEffect.getValue()) ci.cancel();
}
@Inject(method = "extractSpyglassOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderSpyglass(GuiGraphicsExtractor graphics, float scale, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.spyglassOverlay.getValue()) ci.cancel();
}
@Inject(method = "extractPortalOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderPortal(GuiGraphicsExtractor graphics, float alpha, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.portalOverlay.getValue()) ci.cancel();
}
@Inject(method = "extractTextureOverlay", at = @At("HEAD"), cancellable = true)
private void noRenderPumpkinBlur(GuiGraphicsExtractor graphics, Identifier texture, float alpha, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.pumpkinOverlay.getValue() && texture.getPath().equals("textures/misc/pumpkinblur.png")) ci.cancel();
}
// otros
@Inject(method = "extractRenderState", at = @At("RETURN"))
private void renderHUD(GuiGraphicsExtractor gui, DeltaTracker dt, CallbackInfo ci) {
HudRenderer.INSTANCE.render(gui, dt);
}
@Redirect(method = "extractCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphicsExtractor;blitSprite(Lcom/mojang/blaze3d/pipeline/RenderPipeline;Lnet/minecraft/resources/Identifier;IIII)V", ordinal = 0))
private void onRenderCrosshair(GuiGraphicsExtractor gui, RenderPipeline pipeline, Identifier id, int x, int y, int width, int height) {
// dangerous abbreviation
CrosshairPlus cp = ModuleManager.INSTANCE.getModuleByClass(CrosshairPlus.class);
int color = cp.isEnabled() ? cp.color.getRgb() : -1;
int w = cp.isEnabled() ? cp.width.getIntValue() : width;
int h = cp.isEnabled() ? cp.height.getIntValue() : height;
gui.blitSprite(pipeline, id, (gui.guiWidth() - w) / 2, (gui.guiHeight() - h) / 2, w, h, color);
}
@ModifyExpressionValue(method = "extractCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/CameraType;isFirstPerson()Z"))
private boolean alwaysRenderCrosshairInFreecam(boolean firstPerson) {
return ModuleManager.INSTANCE.getModuleByClass(Freecam.class).isEnabled() || firstPerson;
}
@Inject(method = "onDisconnected", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/components/ChatComponent;clearMessages(Z)V"), cancellable = true)
private void onClear(CallbackInfo ci) {
ChatPlus chatPlus = ModuleManager.INSTANCE.getModuleByClass(ChatPlus.class);
if (chatPlus.isEnabled() && chatPlus.keepHistory.getValue()) ci.cancel();
}
}
@@ -5,6 +5,7 @@ import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.misc.ScreenshotPlus;
import net.minecraft.client.Minecraft;
import net.minecraft.client.Screenshot;
import net.minecraft.network.chat.Component;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
@@ -19,14 +20,14 @@ import java.util.function.Consumer;
@Mixin(Screenshot.class)
public abstract class ScreenshotMixin {
@Inject(method = "lambda$grab$1", at = @At(value = "INVOKE", target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V"), cancellable = true)
private static void sendActionsMessage(NativeImage image, File file, Consumer<?> callback, CallbackInfo ci) {
@Inject(method = "lambda$grab$3", at = @At(value = "INVOKE", target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V"), cancellable = true)
private static void sendActionsMessage(NativeImage image, File file, Consumer<Component> callback, CallbackInfo ci) {
ScreenshotPlus screenshotPlus = getScreenshotPlus();
if (screenshotPlus.isEnabled()) ci.cancel();
}
@Inject(method = "lambda$grab$1", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/NativeImage;writeToFile(Ljava/io/File;)V"), cancellable = true)
private static void interceptScreenshotFile(NativeImage image, File file, Consumer<?> callback, CallbackInfo ci) throws IOException {
@Inject(method = "lambda$grab$3", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/NativeImage;writeToFile(Ljava/io/File;)V"), cancellable = true)
private static void interceptScreenshotFile(NativeImage image, File file, Consumer<Component> callback, CallbackInfo ci) throws IOException {
ScreenshotPlus screenshotPlus = getScreenshotPlus();
// aunque el módulo esté apagado, pasar las referencias para poder usar los comandos para las capturas
@@ -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 (ShulkerPeek.isShulkerEmpty(stack)) {
builder.accept(Component.literal("vacío").withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC));
builder.accept(Component.translatable("sputnik.module.shulkerpeek.empty")
.withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC));
} else {
builder.accept(
Component.literal("mantén ")
.append(Component.literal(shulkerPeek.previewKey.getKeyName())
.withStyle(ChatFormatting.GOLD, ChatFormatting.BOLD))
.append(" para previsualizar")
Component.translatable("sputnik.module.shulkerpeek.holdtopreview",
Component.literal(shulkerPeek.previewKey.getKeyName())
.withStyle(ChatFormatting.GOLD, ChatFormatting.BOLD))
.withStyle(ChatFormatting.GRAY)
);
}
@@ -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 me.retucio.sputnik.Sputnik;
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.TickEvent;
import me.retucio.sputnik.event.interact.UseItemEvent;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.camera.Freecam;
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.gui.screens.Screen;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity;
@@ -38,9 +36,6 @@ public abstract class MinecraftMixin {
// eventos
@Shadow @Nullable
public Screen screen;
@Shadow
public abstract @Nullable Entity getCameraEntity();
@@ -63,12 +58,6 @@ public abstract class MinecraftMixin {
Sputnik.EVENT_BUS.post(new ShutdownEvent());
}
@Inject(method = "setScreen", at = @At("HEAD"), cancellable = true)
private void onOpenScreen(Screen screen, CallbackInfo ci) {
OpenScreenEvent event = Sputnik.EVENT_BUS.post(new OpenScreenEvent(screen));
if (event.isCancelled()) ci.cancel();
}
@Inject(method = "setLevel", at = @At("HEAD"), cancellable = true)
private void onJoinWorld(ClientLevel level, CallbackInfo ci) {
JoinWorldEvent event = Sputnik.EVENT_BUS.post(new JoinWorldEvent(level));
@@ -0,0 +1,33 @@
package me.retucio.sputnik.mixin.mixins.misc;
import me.retucio.sputnik.util.Lists;
import net.minecraft.core.Holder;
import net.minecraft.core.component.DataComponentMap;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.item.Item;
import org.jspecify.annotations.Nullable;
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(Holder.Reference.class)
public abstract class ReferenceMixin<T> {
@Shadow
private @Nullable DataComponentMap components;
@Shadow
private @Nullable T value;
@Inject(method = "bindComponents", at = @At("TAIL"))
private void onBindComponents(DataComponentMap components, CallbackInfo ci) {
if (this.components == null || !this.components.has(DataComponents.FOOD)) return;
if (!(this.value instanceof Item item)) return;
Lists.foodList.add(item);
Lists.foodNames.put(item, BuiltInRegistries.ITEM.getKey(item).toLanguageKey());
}
}
@@ -1,6 +1,6 @@
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 org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
@@ -1,7 +1,7 @@
package me.retucio.sputnik.mixin.mixins.network;
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.network.chat.ChatType;
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.module.ModuleManager;
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.gui.screens.Screen;
import net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl;
@@ -46,7 +46,7 @@ public abstract class ClientCommonPacketListenerImplMixin {
final Screen screen = cir.getReturnValue();
((IConfirmScreen) screen).sputnik$setBypassAction(() -> {
minecraft.setScreen(((PackConfirmScreenAccessor) screen).getParentScreen());
minecraft.gui.setScreen(((PackConfirmScreenAccessor) screen).getParentScreen());
if (serverData != null) {
serverData.setResourcePackStatus(bypassPack.getStatus());
minecraft.getDownloadedPackSource().allowServerPacks();
@@ -48,7 +48,7 @@ public abstract class ClientPacketListenerMixin {
ChatUtil.error(e.getMessage());
}
mc.gui.getChat().addRecentChat(content);
mc.gui.hud.getChat().addRecentChat(content);
ci.cancel();
}
@@ -0,0 +1,27 @@
package me.retucio.sputnik.mixin.mixins.player;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.event.interact.UpdateInventoryEvent;
import net.minecraft.core.NonNullList;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Inventory.class)
public abstract class InventoryMixin {
@Shadow @Final
private NonNullList<ItemStack> items;
@Inject(method = "setChanged", at = @At("HEAD"))
private void onChange(CallbackInfo ci) {
Sputnik.EVENT_BUS.post(new UpdateInventoryEvent(items));
}
}
@@ -5,14 +5,12 @@ import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import com.llamalad7.mixinextras.sugar.Local;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.world.NoMiningInterruptions;
import me.retucio.sputnik.module.modules.inventory.PortalGUI;
import me.retucio.sputnik.module.modules.inventory.PortalGui;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@@ -28,9 +26,9 @@ public abstract class LocalPlayerMixin {
throw new UnsupportedOperationException("Implemented via mixin");
}
@ModifyExpressionValue(method = "handlePortalTransitionEffect", at = @At(value = "FIELD", target = "Lnet/minecraft/client/Minecraft;screen:Lnet/minecraft/client/gui/screens/Screen;", opcode = Opcodes.GETFIELD))
@ModifyExpressionValue(method = "handlePortalTransitionEffect", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/Gui;screen()Lnet/minecraft/client/gui/screens/Screen;"))
private Screen allowScreensInPortals(Screen original) {
if (ModuleManager.INSTANCE.getModuleByClass(PortalGUI.class).isEnabled()) return null;
if (ModuleManager.INSTANCE.getModuleByClass(PortalGui.class).isEnabled()) return null;
return original;
}
@@ -1,5 +1,6 @@
package me.retucio.sputnik.mixin.mixins.render;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.event.render.GetFOVEvent;
@@ -78,6 +79,13 @@ public abstract class CameraMixin {
if (freecam.isEnabled()) this.detached = true;
}
// el culling inteligente se desactiva estando en espectador dentro de un bloque sólido,
// así el terreno se sigue renderizando con la cámara metida en bloques
@ModifyExpressionValue(method = "extractRenderState", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;isSpectator()Z"))
private boolean noSmartCullFreecam(boolean original) {
return freecam.isEnabled() || original;
}
@ModifyArgs(method = "alignWithEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setPosition(DDD)V"))
private void onUpdateSetPosArgs(Args args) {
if (freecam.isEnabled()) {
@@ -15,7 +15,7 @@ public abstract class EntityHitboxDebugRendererMixin {
@ModifyVariable(method = "showHitboxes", at = @At("STORE"), name = "mainColor")
private int modifyHitboxColor(int original) {
Hitboxes hitboxes = ModuleManager.INSTANCE.getModuleByClass(Hitboxes.class);
if (hitboxes.isEnabled()) return hitboxes.color.getRGB();
if (hitboxes.isEnabled()) return hitboxes.color.getRgb();
return original;
}
@@ -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.modules.camera.Freecam;
import me.retucio.sputnik.module.modules.misc.UnfocusedCpu;
import me.retucio.sputnik.module.modules.render.Lsd;
import me.retucio.sputnik.module.modules.render.NoRender;
import me.retucio.sputnik.module.modules.camera.Zoom;
import com.mojang.blaze3d.pipeline.RenderTarget;
import com.mojang.blaze3d.resource.CrossFrameResourcePool;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.ItemInHandRenderer;
import net.minecraft.client.renderer.RenderBuffers;
import net.minecraft.client.renderer.state.level.CameraRenderState;
import net.minecraft.client.resources.model.ModelManager;
import org.joml.Matrix4fc;
@@ -39,15 +40,31 @@ public abstract class GameRendererMixin {
@Unique Zoom zoom;
@Unique Freecam freecam;
@Unique NoRender noRender;
@Unique Lsd lsd;
@Shadow @Final
private Minecraft minecraft;
@Shadow @Final
private RenderTarget mainRenderTarget;
@Shadow @Final
private CrossFrameResourcePool resourcePool;
@Inject(method = "<init>", at = @At("TAIL"))
private void getModules(Minecraft minecraft, ItemInHandRenderer itemInHandRenderer, RenderBuffers renderBuffers, ModelManager modelManager, CallbackInfo ci) {
private void getModules(Minecraft minecraft, ItemInHandRenderer itemInHandRenderer, ModelManager modelManager, CallbackInfo ci) {
zoom = ModuleManager.INSTANCE.getModuleByClass(Zoom.class);;
freecam = ModuleManager.INSTANCE.getModuleByClass(Freecam.class);
noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class);
lsd = ModuleManager.INSTANCE.getModuleByClass(Lsd.class);
}
// se aplica justo donde vanilla mete sus efectos de post (nausea, etc.): el mundo ya está
// en mainRenderTarget y el HUD todavía no, así que sólo distorsiona la escena 3D
@Inject(method = "render", at = @At(value = "INVOKE", shift = At.Shift.AFTER,
target = "Lnet/minecraft/client/renderer/LevelRenderer;doEntityOutline()V"))
private void applyLsd(DeltaTracker deltaTracker, boolean advanceGameTime, CallbackInfo ci) {
if (lsd.isEnabled()) lsd.apply(mainRenderTarget, resourcePool);
}
@Inject(method = "render", at = @At("HEAD"), cancellable = true)
@@ -15,21 +15,14 @@ public abstract class ItemFeatureRendererMixin {
// obtener módulo en cada redirect, ya que tiene un inicializador estático
@Redirect(method = "getFoilRenderType", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/rendertype/RenderTypes;glint()Lnet/minecraft/client/renderer/rendertype/RenderType;"))
@Redirect(method = "getFoilBuffer", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/rendertype/RenderTypes;glint()Lnet/minecraft/client/renderer/rendertype/RenderType;"))
private static RenderType redirectGlint() {
GlintPlus glintPlus = ModuleManager.INSTANCE.getModuleByClass(GlintPlus.class);
if (glintPlus == null) return RenderTypes.glint();
return glintPlus.getGlint();
}
@Redirect(method = "getFoilRenderType", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/rendertype/RenderTypes;entityGlint()Lnet/minecraft/client/renderer/rendertype/RenderType;"))
private static RenderType redirectEntityGlint() {
GlintPlus glintPlus = ModuleManager.INSTANCE.getModuleByClass(GlintPlus.class);
if (glintPlus == null) return RenderTypes.entityGlint();
return glintPlus.getEntityGlint();
}
@Redirect(method = "getFoilRenderType", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/rendertype/RenderTypes;glintTranslucent()Lnet/minecraft/client/renderer/rendertype/RenderType;"))
@Redirect(method = "getFoilBuffer", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/rendertype/RenderTypes;glintTranslucent()Lnet/minecraft/client/renderer/rendertype/RenderType;"))
private static RenderType redirectGlintTranslucent() {
GlintPlus glintPlus = ModuleManager.INSTANCE.getModuleByClass(GlintPlus.class);
if (glintPlus == null) return RenderTypes.glintTranslucent();
@@ -49,12 +49,12 @@ public abstract class ItemInHandRendererMixin {
handView = ModuleManager.INSTANCE.getModuleByClass(HandView.class);
}
@Inject(method = "renderArmWithItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemInHandRenderer;renderItem(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemDisplayContext;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;I)V", shift = At.Shift.BEFORE))
@Inject(method = "submitArmWithItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemInHandRenderer;renderItem(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemDisplayContext;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;I)V", shift = At.Shift.BEFORE))
private void onRenderItem(AbstractClientPlayer player, float frameInterp, float xRot, InteractionHand hand, float attack, ItemStack itemStack, float inverseArmHeight, PoseStack matrices, SubmitNodeCollector submitNodeCollector, int lightCoords, CallbackInfo ci) {
EVENT_BUS.post(new RenderHeldItemEvent(matrices, hand));
}
@Inject(method = "renderArmWithItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemInHandRenderer;renderPlayerArm(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;IFFLnet/minecraft/world/entity/HumanoidArm;)V"))
@Inject(method = "submitArmWithItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemInHandRenderer;renderPlayerArm(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;IFFLnet/minecraft/world/entity/HumanoidArm;)V"))
private void onRenderArm(AbstractClientPlayer player, float frameInterp, float xRot, InteractionHand hand, float attack, ItemStack itemStack, float inverseArmHeight, PoseStack matrices, SubmitNodeCollector submitNodeCollector, int lightCoords, CallbackInfo ci) {
EVENT_BUS.post(new RenderArmEvent(matrices, hand));
}
@@ -10,10 +10,10 @@ import me.retucio.sputnik.event.render.Render3DEvent;
import me.retucio.sputnik.event.render.RenderBlockOutlineEvent;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.camera.Freecam;
import me.retucio.sputnik.util.render.SputnikBuffers;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.chunk.ChunkSectionsToRender;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.state.level.CameraRenderState;
import net.minecraft.client.renderer.state.level.LevelRenderState;
import net.minecraft.util.profiling.ProfilerFiller;
@@ -31,14 +31,8 @@ import static me.retucio.sputnik.Sputnik.mc;
@Mixin(LevelRenderer.class)
public abstract class LevelRendererMixin {
// renderLevel()??
@ModifyArg(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;cullTerrain(Lnet/minecraft/client/Camera;Lnet/minecraft/client/renderer/culling/Frustum;Z)V"), index = 2)
private boolean renderSetupTerrainModifyArg(boolean spectator) {
return ModuleManager.INSTANCE.getModuleByClass(Freecam.class).isEnabled() || spectator;
}
@Inject(method = "renderLevel", at = @At("RETURN"))
private void render(GraphicsResourceAllocator resourceAllocator, DeltaTracker dt, boolean renderOutline, CameraRenderState cameraState, Matrix4fc modelViewMatrix, GpuBufferSlice terrainFog, Vector4f fogColor, boolean shouldRenderSky, ChunkSectionsToRender chunkSectionsToRender, CallbackInfo ci, @Local(name = "profiler") ProfilerFiller profiler) {
@Inject(method = "render", at = @At("RETURN"))
private void render(GraphicsResourceAllocator resourceAllocator, DeltaTracker dt, boolean renderOutline, CameraRenderState cameraState, Matrix4fc modelViewMatrix, GpuBufferSlice terrainFog, Vector4f fogColor, boolean shouldRenderSky, CallbackInfo ci, @Local(name = "profiler") ProfilerFiller profiler) {
if (mc == null || mc.level == null) return;
PoseStack matrices = new PoseStack();
@@ -49,13 +43,14 @@ public abstract class LevelRendererMixin {
profiler.push(Sputnik.MOD_ID + "-3d");
EVENT_BUS.post(new Render3DEvent(matrices, dt, cameraState));
SputnikBuffers.flush(); // antes de que empiece el hud
profiler.pop();
matrices.popPose();
}
@Inject(method = "renderBlockOutline", at = @At("HEAD"), cancellable = true)
private void noRenderBlockOutlinesFreecam(MultiBufferSource.BufferSource bufferSource, PoseStack poseStack, boolean onlyTranslucentBlocks, LevelRenderState levelRenderState, CallbackInfo ci) {
@Inject(method = "submitBlockOutline", at = @At("HEAD"), cancellable = true)
private void noRenderBlockOutlinesFreecam(PoseStack poseStack, SubmitNodeCollector collector, LevelRenderState levelRenderState, CallbackInfo ci) {
Freecam freecam = ModuleManager.INSTANCE.getModuleByClass(Freecam.class);
if (freecam.isEnabled() && !freecam.blockOutlines.getValue()) ci.cancel();
}
@@ -68,13 +63,13 @@ public abstract class LevelRendererMixin {
// }
// puto mojang con sus nombres confusos
@ModifyArgs(method = "renderBlockOutline", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;renderHitOutline(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;DDDLnet/minecraft/client/renderer/state/level/BlockOutlineRenderState;IF)V"))
@ModifyArgs(method = "submitBlockOutline", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;submitHitOutline(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;Lnet/minecraft/client/renderer/rendertype/RenderType;Lnet/minecraft/client/renderer/state/level/BlockOutlineRenderState;IFZ)V"))
private void modifyBlockOutline(Args args) {
// SIX SEVEN!!! (me pegan en casa)
RenderBlockOutlineEvent event = EVENT_BUS.post(new RenderBlockOutlineEvent(args.get(6), args.get(7)));
if (event.getColor() != (int) args.get(6))
args.set(6, event.getColor());
if (event.getLineWidth() != (float) args.get(7))
args.set(7, event.getLineWidth());
// ahora cuatro cinco... (rip 67)
RenderBlockOutlineEvent event = EVENT_BUS.post(new RenderBlockOutlineEvent(args.get(4), args.get(5)));
if (event.getColor() != (int) args.get(4))
args.set(4, event.getColor());
if (event.getLineWidth() != (float) args.get(5))
args.set(5, event.getLineWidth());
}
}
@@ -31,7 +31,7 @@ public abstract class LightmapMixin {
profiler.push("lightmap");
RenderSystem.getDevice().createCommandEncoder()
.clearColorTexture(texture, ARGB.color(255, 255, 255, 255));
.clearColorTexture(texture, ARGB.vector4fFromARGB32(ARGB.color(255, 255, 255, 255)));
profiler.pop();
ci.cancel();
@@ -8,13 +8,14 @@ import me.retucio.sputnik.module.modules.camera.Freecam;
import me.retucio.sputnik.module.modules.render.Nametags;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.EntityTypes;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import org.spongepowered.asm.mixin.Mixin;
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.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@@ -48,19 +49,27 @@ public abstract class LivingEntityRendererMixin<T extends LivingEntity> {
}
@ModifyReturnValue(method = "shouldShowName(Lnet/minecraft/world/entity/LivingEntity;D)Z", at = @At("RETURN"))
private boolean shouldRenderPlayerNametag(boolean original, @Local(argsOnly = true) T livingEntity) {
if (nametags.isEnabled() && livingEntity instanceof Player)
return nametags.entities.isEnabled(EntityType.PLAYER) && original;
private boolean shouldRenderPlayerNametag(boolean original, @Local(argsOnly = true, name = "entity") T entity) {
if (nametags.isEnabled() && entity instanceof Player)
return nametags.entities.isEnabled(EntityTypes.PLAYER) && original;
return original;
}
@Inject(method = "shouldShowName(Lnet/minecraft/world/entity/LivingEntity;D)Z", at = @At("RETURN"), cancellable = true)
private void renderSelfNametag(T livingEntity, double d, CallbackInfoReturnable<Boolean> cir) {
if (livingEntity instanceof Player player) {
private void renderSelfNametag(T entity, double distanceToCameraSq, CallbackInfoReturnable<Boolean> cir) {
if (entity instanceof Player player) {
if (player == mc.player && nametags.showSelf.getValue()) {
cir.setReturnValue(true);
cir.cancel();
}
}
}
@ModifyArg(method = "extractNameTags(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/client/renderer/entity/state/LivingEntityRenderState;F)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/EntityRenderer;extractNameTags(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/client/renderer/entity/state/EntityRenderState;FDD)V"), index = 3)
private double modifyNametagRenderDistance(double original) {
if (nametags.isEnabled()) {
return nametags.distance.getValue();
}
return original;
}
}
@@ -1,25 +1,18 @@
package me.retucio.sputnik.mixin.mixins.render;
import com.mojang.blaze3d.vertex.ByteBufferBuilder;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import me.retucio.sputnik.util.render.GlintRenderType;
import net.minecraft.client.renderer.MultiBufferSource;
import me.retucio.sputnik.util.render.SputnikBuffers;
import net.minecraft.client.renderer.RenderBuffers;
import net.minecraft.client.renderer.rendertype.RenderType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.SequencedMap;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(RenderBuffers.class)
public abstract class RenderBuffersMixin {
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/MultiBufferSource;immediateWithBuffers(Ljava/util/SequencedMap;Lcom/mojang/blaze3d/vertex/ByteBufferBuilder;)Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;", ordinal = 0))
private MultiBufferSource.BufferSource injectGlintBuffers(SequencedMap<RenderType, ByteBufferBuilder> fixedBuffers, ByteBufferBuilder sharedBuffer) {
if (fixedBuffers instanceof Object2ObjectLinkedOpenHashMap<RenderType, ByteBufferBuilder> map) {
GlintRenderType.addGlintTypes(map);
}
return MultiBufferSource.immediateWithBuffers(fixedBuffers, sharedBuffer);
// devolvemos nuestros buffers al pool cuando el juego hace lo propio
@Inject(method = "endFrame", at = @At("TAIL"))
private void endSputnikFrame(CallbackInfo ci) {
SputnikBuffers.endFrame();
}
}
}
@@ -4,8 +4,8 @@ import com.mojang.blaze3d.vertex.PoseStack;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.render.NoRender;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.ScreenEffectRenderer;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@@ -25,13 +25,13 @@ public abstract class ScreenEffectRendererMixin {
noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class);
}
@Inject(method = "renderFire", at = @At("HEAD"), cancellable = true)
private static void noRenderFireOverlay(PoseStack matrices, MultiBufferSource bufferSource, TextureAtlasSprite sprite, CallbackInfo ci) {
@Inject(method = "submitFire", at = @At("HEAD"), cancellable = true)
private static void noRenderFireOverlay(PoseStack matrices, SubmitNodeCollector collector, TextureAtlasSprite sprite, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.fireOverlay.getValue()) ci.cancel();
}
@Inject(method = "renderWater", at = @At("HEAD"), cancellable = true)
private static void noRenderUnderwaterOverlay(Minecraft minecraft, PoseStack matrices, MultiBufferSource bufferSource, CallbackInfo ci) {
@Inject(method = "submitWater", at = @At("HEAD"), cancellable = true)
private static void noRenderUnderwaterOverlay(Minecraft minecraft, PoseStack matrices, SubmitNodeCollector collector, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.fluidOverlay.getValue()) ci.cancel();
}
}
@@ -1,6 +1,7 @@
package me.retucio.sputnik.mixin.mixins.render;
import me.retucio.sputnik.util.render.Pipelines;
import me.retucio.sputnik.util.render.PostShaders;
import net.minecraft.client.renderer.ShaderManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -13,5 +14,6 @@ public abstract class ShaderManagerMixin {
@Inject(method = "apply(Lnet/minecraft/client/renderer/ShaderManager$Configs;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V", at = @At("TAIL"))
private void onReload(CallbackInfo ci) {
Pipelines.init();
PostShaders.reload();
}
}
@@ -2,11 +2,8 @@ package me.retucio.sputnik.mixin.mixins.render;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.render.NoRender;
import net.minecraft.client.Camera;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.WeatherEffectRenderer;
import net.minecraft.client.renderer.state.level.WeatherRenderState;
import net.minecraft.server.level.ParticleStatus;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@@ -26,11 +23,6 @@ public abstract class WeatherEffectRendererMixin {
noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class);
}
@Inject(method = "tickRainParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;addParticle(Lnet/minecraft/core/particles/ParticleOptions;DDDDDD)V"), cancellable = true)
private void noRainParticles(ClientLevel level, Camera camera, int ticks, ParticleStatus particleStatus, int weatherRadius, CallbackInfo ci) {
if (noRender.isEnabled() && !noRender.rain.getValue()) ci.cancel();
}
@Inject(method = "render", at = @At("HEAD"))
private void noRenderRainAndSnow(Vec3 cameraPos, WeatherRenderState renderState, CallbackInfo ci) {
if (noRender.isEnabled()) {
@@ -33,14 +33,14 @@ public abstract class AbstractContainerScreenMixin {
int by = bgTop - bh - 5;
if (burglar.showStealButton.getValue()) {
Button stealButton = Button.builder(Component.literal("robar"), bnutton -> {
Button stealButton = Button.builder(Component.translatable("sputnik.module.burglar.steal"), bnutton -> {
burglar.steal(screen);
}).bounds(bx, by, bw, bh).build();
screen.addRenderableWidget(stealButton);
}
if (burglar.showDumpButton.getValue()) {
Button dumpButton = Button.builder(Component.literal("dejar"), button -> {
Button dumpButton = Button.builder(Component.translatable("sputnik.module.burglar.dump"), button -> {
burglar.dump(screen);
}).bounds(bx + bw + gap, by, bw, bh).build();
screen.addRenderableWidget(dumpButton);
@@ -2,7 +2,7 @@ package me.retucio.sputnik.mixin.mixins.screen;
import me.retucio.sputnik.module.ModuleManager;
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.layouts.LinearLayout;
@@ -27,7 +27,7 @@ public abstract class DisconnectedScreenMixin {
if (reconnect == null || !reconnect.isEnabled()) return;
layout.addChild(Button.builder(
Component.literal("reconectarse"),
Component.translatable("sputnik.module.reconnect.button"),
button -> reconnect.reconnect()
).build());
}
@@ -33,7 +33,7 @@ public class ScreenMixin {
if (watermark == null || watermark.isEmpty()) return;
gui.text(mc.font, watermark,
mc.getWindow().getGuiScaledWidth() - mc.font.width(watermark) - 2, 2,
ClientSettingsPanel.clientSettings.color.getRGB(), false
ClientSettingsPanel.clientSettings.color.getRgb(), false
);
}
}
@@ -2,6 +2,8 @@ package me.retucio.sputnik.mixin.mixins.world;
import me.retucio.sputnik.event.network.AddEntityEvent;
import me.retucio.sputnik.event.network.RemoveEntityEvent;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.render.NoRender;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity;
import org.jetbrains.annotations.Nullable;
@@ -29,4 +31,11 @@ public abstract class ClientLevelMixin {
Entity entity = getEntity(entityId);
if (entity != null) EVENT_BUS.post(new RemoveEntityEvent(entity));
}
// las partículas de lluvia se generan aquí desde 26.2, antes en WeatherEffectRenderer
@Inject(method = "tickWeatherEffects", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;addParticle(Lnet/minecraft/core/particles/ParticleOptions;DDDDDD)V"), cancellable = true)
private void noRainParticles(CallbackInfo ci) {
NoRender noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class);
if (noRender.isEnabled() && !noRender.rain.getValue()) ci.cancel();
}
}
@@ -1,4 +1,4 @@
package me.retucio.sputnik.util.interfaces;
package me.retucio.sputnik.mixinterfaces;
import net.minecraft.client.multiplayer.chat.GuiMessage;
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;
@@ -1,4 +1,4 @@
package me.retucio.sputnik.util.interfaces;
package me.retucio.sputnik.mixinterfaces;
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;
@@ -1,4 +1,4 @@
package me.retucio.sputnik.util.interfaces;
package me.retucio.sputnik.mixinterfaces;
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 org.joml.Vector3d;
@@ -1,6 +1,6 @@
/* 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.Style;
@@ -1,5 +1,7 @@
package me.retucio.sputnik.module;
import me.retucio.sputnik.util.I18nUtil;
public enum Category {
COMBAT("combate"),
@@ -19,5 +21,12 @@ public enum Category {
private final String name;
Category(String name) { this.name = name; }
// OJO: toString() es la clave de la config. (ConfigManager#applyExtendablePanel guarda los
// marcos por categoría). no se traduce; para mostrarla usar getDisplayName()
@Override public String toString() { return name; }
public String getDisplayName() {
return I18nUtil.orElse("sputnik.category." + I18nUtil.slug(name), name);
}
}
@@ -6,14 +6,19 @@ import me.retucio.sputnik.config.ConfigManager;
import me.retucio.sputnik.event.sputnik.ToggleModuleEvent;
import me.retucio.sputnik.module.setting.SettingGroup;
import me.retucio.sputnik.module.setting.settings.BooleanSetting;
import me.retucio.sputnik.module.setting.settings.ColorSetting;
import me.retucio.sputnik.module.setting.settings.EnumSetting;
import me.retucio.sputnik.module.setting.settings.KeySetting;
import me.retucio.sputnik.module.setting.Setting;
import me.retucio.sputnik.ui.hud.HudRenderer;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.Colors;
import me.retucio.sputnik.util.I18nUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import org.lwjgl.glfw.GLFW;
import java.awt.*;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -37,30 +42,44 @@ public class Module {
"tecla",
"tecla asignada al módulo, ESC para desactivar",
GLFW.GLFW_KEY_UNKNOWN
);
).translationKey("sputnik.setting.common.bind");
// dejar al usuario elegir si el módulo debería apagarse tras soltar su tecla asignada, o si la tecla debería alternar su estado
protected EnumSetting<KeyMode> keyMode = new EnumSetting<>(
"modo de tecla",
"cómo interpretar la tecla configurada",
KeyMode.class, KeyMode.TOGGLE
);
).translationKey("sputnik.setting.common.keymode");
protected BooleanSetting notify = new BooleanSetting(
"notificar",
"notificar al activar / desactivar",
true
);
).translationKey("sputnik.setting.common.notify");
protected EnumSetting<NotifyMode> notifyMode = new EnumSetting<>(
"modo de notificación",
"cómo notificar cuando se activa / desactiva",
NotifyMode.class, NotifyMode.WIDGET
);
).translationKey("sputnik.setting.common.notifymode");
protected BooleanSetting displayed = new BooleanSetting(
"visible",
"mostrar en el arraylist del HUD",
true
).translationKey("sputnik.setting.common.displayed");;
protected ColorSetting arraylistColor = new ColorSetting(
"color del arraylist",
"color a usar en el arraylist para el nombre del módulo",
new Color(Colors.red, Colors.green, Colors.blue, Colors.alpha),
true
).translationKey("sputnik.setting.common.arraylistcolor")
.visibility(displayed::getValue);
protected final List<SettingGroup> sgs = new ArrayList<>();
protected final SettingGroup sgModule = addSg(new SettingGroup("módulo", false));
protected final SettingGroup sgGeneral = addSg(new SettingGroup("general", true));
protected final SettingGroup sgModule = addSg(new SettingGroup("módulo", false, "sputnik.group.common.module"));
protected final SettingGroup sgGeneral = addSg(new SettingGroup("general", true, "sputnik.group.common.general"));
protected static final Minecraft mc = Minecraft.getInstance();
@@ -68,19 +87,16 @@ public class Module {
this.name = name;
this.description = description;
this.category = category;
sgModule.addAll(bind, keyMode, notify, notifyMode);
sgModule.addAll(bind, keyMode, notify, notifyMode, displayed, arraylistColor);
}
public Module(String name, String description, Category category, int key) {
this.name = name;
this.description = description;
this.category = category;
sgModule.addAll(bind, keyMode, notify, notifyMode);
this(name, description, category);
this.bind.setDefaultValue(key);
// puto marcos
if (ConfigManager.INSTANCE.getConfig().settings
.get(this.getName() + ":" + bind.getName()) == null)
.get(this.getName() + ":" + bind.getTrueName()) == null)
this.bind.setValue(key);
}
@@ -101,9 +117,10 @@ public class Module {
return sgs;
}
// por nombre interno, no por el traducido (igual que SettingGroup#getSetting)
public SettingGroup getSg(String name) {
for (SettingGroup sg : sgs) {
if (sg.getName().equals(name)) {
if (sg.getTrueName().equals(name)) {
return sg;
}
}
@@ -134,10 +151,7 @@ public class Module {
}
public void onEnable() {
if (notify.getValue()) {
if (notifyMode.is(NotifyMode.CHAT)) ChatUtil.info(getName() + " fue activado");
else HudRenderer.INSTANCE.pushNotification(getName() + " fue activado", getName() + " fue activado.");
}
notifyToggle(true);
for (Method method : getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(EventListener.class)) {
Sputnik.EVENT_BUS.subscribe(this);
@@ -147,13 +161,23 @@ public class Module {
}
public void onDisable() {
if (notifyMode.is(NotifyMode.CHAT)) ChatUtil.info(getName() + " fue desactivado");
else HudRenderer.INSTANCE.pushNotification(getName() + " fue desactivado", getName() + " fue desactivado.");
notifyToggle(false);
if (Sputnik.EVENT_BUS.isSubscribed(this)) {
Sputnik.EVENT_BUS.unsubscribe(this);
}
}
private void notifyToggle(boolean on) {
if (!notify.getValue()) return;
if (ConfigManager.INSTANCE != null && ConfigManager.INSTANCE.isApplying()) return;
String key = on ? "sputnik.module.toggled.on" : "sputnik.module.toggled.off";
Component message = Component.translatable(key, getDisplayName());
if (notifyMode.is(NotifyMode.CHAT)) ChatUtil.info(message);
else HudRenderer.INSTANCE.pushNotification(message.getString(), message.getString());
}
// tick (ejecutado 20 veces por segundo)
public void onTick() {}
@@ -168,6 +192,19 @@ public class Module {
this.name = name;
}
public String getTranslationKey() {
return "sputnik.module." + I18nUtil.slug(name);
}
// nombre visible, traducido
public String getDisplayName() {
return I18nUtil.orElse(getTranslationKey() + ".name", name);
}
public String getDisplayDescription() {
return I18nUtil.orElse(getTranslationKey() + ".desc", description);
}
public String getDescription() {
return description;
}
@@ -222,6 +259,10 @@ public class Module {
return bind;
}
public boolean isVisible() { return displayed.getValue(); }
public Color getArraylistColor() { return arraylistColor.getValue(); }
public boolean isSearchMatch() {
return searchMatch;
}
@@ -2,7 +2,7 @@ package me.retucio.sputnik.module;
import com.github.retucio.neutrino.EventListener;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.module.modules.client.DiscordRPC;
import me.retucio.sputnik.module.modules.client.DiscordRpc;
import me.retucio.sputnik.module.modules.client.Friends;
import me.retucio.sputnik.module.modules.client.Hud;
import me.retucio.sputnik.module.modules.camera.*;
@@ -12,6 +12,7 @@ import me.retucio.sputnik.module.modules.inventory.*;
import me.retucio.sputnik.module.modules.misc.*;
import me.retucio.sputnik.module.modules.movement.*;
import me.retucio.sputnik.module.modules.network.*;
import me.retucio.sputnik.module.modules.network.PacketLogger;
import me.retucio.sputnik.module.modules.player.*;
import me.retucio.sputnik.module.modules.render.*;
import me.retucio.sputnik.module.modules.render.CritsPlus;
@@ -48,7 +49,7 @@ public class ModuleManager {
addRender();
addWorld();
modules.sort(Comparator.comparing(module -> MiscUtil.removeAccentMarks(module.getName().toLowerCase())));
modules.sort(Comparator.comparing(module -> MiscUtil.removeAccentMarks(module.getDisplayName().toLowerCase())));
// registrar los "listeners" necesarios
for (Module module : getEnabledModules()) {
@@ -79,7 +80,7 @@ public class ModuleManager {
}
private void addClient() {
add(new DiscordRPC());
add(new DiscordRpc());
add(new Friends());
add(new Hud());
add(new Notifications());
@@ -89,6 +90,8 @@ public class ModuleManager {
add(new AttributeSwapper());
add(new ChestplateSwitch());
add(new CrystalAura());
add(new HoldAttack());
add(new KillAura());
add(new MaceKill());
add(new ProjectileTrajectories());
add(new SpearKill());
@@ -111,10 +114,10 @@ public class ModuleManager {
add(new CreativeInventoryHotbarKeybinds());
add(new InventoryPlus());
add(new Offhand());
add(new PortalGUI());
add(new PortalGui());
add(new Replenish());
add(new ShulkerPeek());
add(new UIMove());
add(new UiMove());
add(new XCarry());
}
@@ -127,6 +130,7 @@ public class ModuleManager {
add(new Headhitters());
add(new HitboxDesync());
add(new InfiniteElytra());
add(new ItemMagnet());
add(new Jesus());
add(new Parkour());
add(new ReverseStep());
@@ -141,7 +145,9 @@ public class ModuleManager {
private void addNetwork() {
add(new BungeecordSpoofer());
add(new LogoutSpots());
add(new PacketCanceller());
add(new PacketDelay());
add(new PacketLogger());
add(new PacketMine());
add(new Reconnect());
add(new RPackBypass());
@@ -150,6 +156,7 @@ public class ModuleManager {
private void addPlayer() {
add(new AirPlace());
add(new AntiHunger());
add(new AutoEat());
add(new AutoFish());
add(new Capes());
add(new FastUse());
@@ -159,24 +166,28 @@ public class ModuleManager {
}
private void addRender() {
add(new BlockESP());
add(new BlockEsp());
add(new BlockOutline());
add(new Breadcrumbs());
add(new BreakingProgress());
add(new Confetti());
add(new CritsPlus());
add(new DamageOverlay());
add(new EntityESP());
add(new EntityEsp());
add(new Fonts());
add(new GlintPlus());
add(new Hitboxes());
add(new ItemPhysics());
add(new Lsd());
add(new Nametags());
add(new NoRender());
add(new Particles());
add(new Stylish());
add(new Tracers());
}
private void addWorld() {
add(new AutoMine());
add(new AutoSign());
add(new AutoTool());
add(new BlockInfo());
@@ -190,8 +201,6 @@ public class ModuleManager {
}
// para obtener módulos más fácilmente (por nombre, clase, o la lista completa)
public List<Module> getModules() {
return modules;
@@ -205,17 +214,34 @@ public class ModuleManager {
return enabledModules;
}
// busca por nombre interno, tal cual se guarda en la config. antes quitaba las tildes al
// nombre del módulo pero no al argumento, así que "cámara libre" no encontraba nada
public Module getModuleByName(String name) {
for (Module module : modules)
if (MiscUtil.removeAccentMarks(module.getName()).equalsIgnoreCase(name))
for (Module module : modules) {
if (module.getName().equalsIgnoreCase(name)) {
return module;
}
}
return null;
}
// busca por nombre visible: lo que el usuario teclea en los comandos, sin tildes y con "_"
public Module getModuleByDisplayName(String name) {
String target = MiscUtil.removeAccentMarks(name).replace("_", " ");
for (Module module : modules) {
if (MiscUtil.removeAccentMarks(module.getDisplayName()).equalsIgnoreCase(target)) {
return module;
}
}
return null;
}
public <T extends Module> T getModuleByClass(Class<T> clazz) {
for (Module module : modules)
if (clazz.isInstance(module))
for (Module module : modules) {
if (clazz.isInstance(module)) {
return clazz.cast(module);
}
}
return null;
}
}
@@ -1,12 +1,10 @@
package me.retucio.sputnik.module.modules.camera;
import com.github.retucio.neutrino.EventListener;
import me.retucio.sputnik.event.network.PacketEvent;
import me.retucio.sputnik.module.Category;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.EnumSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import me.retucio.sputnik.util.RotationUtil;
/**
@@ -47,18 +45,7 @@ public class Autism extends Module {
if (direction.is(RotationDirection.COUNTERCLOCKWISE))
yaw -= interval.getFloatValue();
mc.getConnection().send(new ServerboundMovePlayerPacket.Rot(
yaw,
mc.player.getXRot(),
mc.player.onGround(),
mc.player.horizontalCollision
));
}
@EventListener
private void onPacketSend(PacketEvent.Send event) {
if (event.getPacket() instanceof ServerboundMovePlayerPacket.Rot packet
&& packet.getYRot(yaw) != yaw) event.cancel();
RotationUtil.requestYaw(RotationUtil.HIGHEST, yaw, RotationUtil.Mode.SILENT);
}
private enum RotationDirection {
@@ -25,6 +25,7 @@ import me.retucio.sputnik.util.MiscUtil;
import net.minecraft.client.CameraType;
import net.minecraft.client.input.MouseButtonEvent;
import net.minecraft.client.input.MouseButtonInfo;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.*;
import net.minecraft.util.Mth;
@@ -150,8 +151,8 @@ public class Freecam extends Module {
speed = speedSetting.getValue();
MiscUtil.copyVector(pos, mc.gameRenderer.getMainCamera().position());
MiscUtil.copyVector(prevPos, mc.gameRenderer.getMainCamera().position());
MiscUtil.copyVector(pos, mc.gameRenderer.mainCamera().position());
MiscUtil.copyVector(prevPos, mc.gameRenderer.mainCamera().position());
if (mc.options.getCameraType() == CameraType.THIRD_PERSON_FRONT) {
yaw += 180;
@@ -171,14 +172,19 @@ public class Freecam extends Module {
down = KeyUtil.isKeyDown(mc.options.keyShift);
unpress();
if (reloadChunks.getValue()) mc.levelRenderer.allChanged();
if (reloadChunks.getValue()) invalidateChunks();
super.onEnable();
}
private static void invalidateChunks() {
if (mc.level == null) return;
mc.levelExtractor.allChanged();
}
@Override
public void onDisable() {
if (reloadChunks.getValue()) mc.execute(mc.levelRenderer::allChanged);
if (reloadChunks.getValue()) invalidateChunks();
mc.options.setCameraType(perspective);
crouching = false;
@@ -265,7 +271,7 @@ public class Freecam extends Module {
@EventListener
private void onKey(KeyEvent event) {
if (KeyUtil.isKeyDown(GLFW.GLFW_KEY_F3)) return;
if (mc.screen != null) return;
if (mc.gui.screen() != null) return;
net.minecraft.client.input.KeyEvent key = new net.minecraft.client.input.KeyEvent(event.getKey(), event.getScancode(), 0);
@@ -298,7 +304,7 @@ public class Freecam extends Module {
@EventListener
private void onMouseClick(MouseClickEvent event) { // por si el restrasado del usuario usa el ratón para moverse
if (KeyUtil.isKeyDown(GLFW.GLFW_KEY_F3)) return;
if (mc.screen != null) return;
if (mc.gui.screen() != null) return;
MouseButtonEvent click = new MouseButtonEvent(0, 0, new MouseButtonInfo(event.getButton(), 0));
@@ -330,7 +336,7 @@ public class Freecam extends Module {
@EventListener
private void onMouseScroll(MouseScrollEvent event) {
if (scrollSens.getValue() > 0 && mc.screen == null && scrollKey.isDown()) {
if (scrollSens.getValue() > 0 && mc.gui.screen() == null && scrollKey.isDown()) {
speed += event.getVertical() / 4 * (scrollSens.getValue() * speed);
if (speed < 0.1) speed = 0.1;
event.cancel();
@@ -352,11 +358,12 @@ public class Freecam extends Module {
if (mc.player == null) return; // el jugador probablemente nunca sea nulo bajo estas circunstancias pero quién sabe
if (event.getPacket() instanceof ClientboundPlayerCombatKillPacket packet) {
if (mc.player.getId() == packet.playerId())
if (mc.player.getId() == packet.playerId()) {
toggle();
}
} else if (event.getPacket() instanceof ClientboundSetHealthPacket packet) {
if (mc.player.getHealth() - packet.getHealth() > 0 && toggleOnDamage.getValue()) {
ChatUtil.info("cámara libre se ha desactivado porque has recibido daño");
ChatUtil.info(Component.translatable("sputnik.module.freecam.damagedisable"));
toggle();
}
}
@@ -60,7 +60,7 @@ public class PerspectivePlus extends Module {
@EventListener
private void onMouseScroll(MouseScrollEvent event) {
if (mc.options.getCameraType() == CameraType.FIRST_PERSON
|| mc.screen != null || scrollSens.getValue() <= 0) return;
|| mc.gui.screen() != null || scrollSens.getValue() <= 0) return;
if (scrollKey.isDown()) {
distance -= event.getVertical() / 4 * (scrollSens.getValue() * distance);
Loaded 100 of 226 files, more files were not shown because too many files have changed in this diff. Show more