Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4307b0a479 | ||
|
|
ab191a9016 | ||
|
|
629d03ff92 | ||
|
|
c1a03f3ddb | ||
|
|
44494830be | ||
|
|
73682cb85d | ||
|
|
ac7de7a07a | ||
|
|
0bd31971a0 | ||
|
|
3010b164ab | ||
|
|
6cb4656365 | ||
|
|
272c1059ef | ||
|
|
e0f8523809 | ||
|
|
81fc25c33d | ||
|
|
81b043270b | ||
|
|
3913b31374 | ||
|
|
aeadfb28b1 |
No files matched your search
@@ -44,3 +44,4 @@ replay_*.log
|
||||
todo.txt
|
||||
cambios.md
|
||||
remappedSrc/
|
||||
.claude/
|
||||
@@ -0,0 +1,82 @@
|
||||
<p align="center">
|
||||
<a href="README.md"><img src="https://img.shields.io/badge/lang-Español-yellow.svg" alt="español"></a>
|
||||
<a href="README.en.md"><img src="https://img.shields.io/badge/lang-English-blue.svg" alt="english"></a>
|
||||
</p>
|
||||
|
||||
# jenny's mod 2 (100% real)
|
||||
|
||||
> Minecraft Fabric 26.3 mod that adds some "QOL" features, mostly related to the camera.
|
||||
|
||||
## installation
|
||||
|
||||
### for normal people:
|
||||
1. go to [releases](https://git.retucio.me/retucio/sputnik/releases)
|
||||
2. choose the most recent version
|
||||
3. download the .jar file
|
||||
4. move it to your mods dir (prolly `%appdata%/.minecraft/mods`)
|
||||
|
||||
|
||||
make sure to have a Minecraft and Fabric version compatible with the mod (MC `26.3`; Fabric `0.19.5`)
|
||||
|
||||
issues could arise when using it with other mods, especially other clients, since they could conflict
|
||||
|
||||
### for nerds
|
||||
1. clone the repo (`git clone https://git.retucio.me/retucio/sputnik`)
|
||||
2. enter the repo dir (`cd sputnik`)
|
||||
3. `gradlew build` for Windows (ew) or `chmod +x gradlew` + `./gradlew build` for Unix-likes (Linux & Mac)
|
||||
4. the built .jar will be found in `/build/libs/sputnik-X.X.jar`
|
||||
|
||||
## usage
|
||||
|
||||
`rshift` by default to open the GUI
|
||||
|
||||
`insert` by default to open the HUD editor
|
||||
|
||||
`$` as prefix for commands (autocompleted)
|
||||
|
||||
some modules have keys assigned by default, assign them `ESC` to unbind them.
|
||||
|
||||
## ui
|
||||
|
||||
- left click to toggle modules
|
||||
- right click to open its settings
|
||||
- left click + drag to move a panel
|
||||
- right click on a panel's header to expand/contract it, or to close it
|
||||
- left click over settings to change them
|
||||
- shift + right click on a setting to reset it to its default value
|
||||
- place the mouse cursor over a module/setting to read its description
|
||||
|
||||
## screenshots
|
||||
|
||||
<img src="screenshots/automine.png" alt="auto mine">
|
||||
<img src="screenshots/blockesp.png" alt="block ESP + light overlay">
|
||||
<img src="screenshots/camtweaks.png" alt="perspective plus">
|
||||
<img src="screenshots/chatplus.png" alt="chatplus + nametags + screenshots plus">
|
||||
<img src="screenshots/clickgui.png" alt="click GUI">
|
||||
<img src="screenshots/freecam.png" alt="freecam + tracers">
|
||||
<img src="screenshots/hudeditor.png" alt="HUD editor">
|
||||
<img src="screenshots/itemphysics.png" alt="item physics">
|
||||
<img src="screenshots/lsd.png" alt="lsd module">
|
||||
<img src="screenshots/macros.png" alt="macros tab">
|
||||
<img src="screenshots/trajectories.png" alt="projectile trajectories">
|
||||
|
||||
|
||||
## contribution
|
||||
|
||||
yes please (PRs are welcome)
|
||||
|
||||
## license
|
||||
|
||||
[CC0 license](LICENSE)
|
||||
|
||||
## disclaimer
|
||||
|
||||
i don't give a single fuck what happens to you when using this mod on servers where its features are bannable and such.
|
||||
|
||||
## deps
|
||||
|
||||
- [fabric api](https://modrinth.com/mod/fabric-api/)
|
||||
|
||||
## notes
|
||||
|
||||
this mod IS BUGGY. VERY BUGGY, i know. i started this project when i was 14 and the codebase is such a mess i refuse to fix it. i might rewrite it in a future, though.
|
||||
@@ -1,23 +1,28 @@
|
||||
<p align="center">
|
||||
<a href="README.md"><img src="https://img.shields.io/badge/lang-Español-yellow.svg" alt="español"></a>
|
||||
<a href="README.en.md"><img src="https://img.shields.io/badge/lang-English-blue.svg" alt="english"></a>
|
||||
</p>
|
||||
|
||||
# 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.3 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)
|
||||
2. selecionar la versión más reciente
|
||||
1. ir a [lanzamientos](https://git.retucio.me/retucio/sputnik/releases)
|
||||
2. seleccionar 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.3`; Fabric `0.19.5`)
|
||||
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
|
||||
1. clona el respositorio (`git clone https://github.com/retuci0/sputnik`)
|
||||
1. clona el respositorio (`git clone https://git.retucio.me/retucio/sputnik`)
|
||||
2. entra al directorio del repositorio (`cd sputnik`)
|
||||
3. `gradlew build` para Windows o `chmod +x gradlew` + `./gradlew build` para Linux (Mac es Linux)
|
||||
4. el resultado estará en `/build/libs/sputnik-X.X.jar` (el que no lleva "sources" en el nombre)
|
||||
3. `gradlew build` para Windows (ew) o `chmod +x gradlew` + `./gradlew build` para Unix-likes (Linux & Mac)
|
||||
4. el resultado estará en `/build/libs/sputnik-X.X.jar`
|
||||
|
||||
## uso
|
||||
|
||||
@@ -33,55 +38,42 @@ algunos módulos tienen teclas asignadas por defecto, asígnales `ESC` para desa
|
||||
|
||||
- clic izquierdo para activar / desactivar módulos
|
||||
- clic derecho para abrir sus ajustes
|
||||
- clic izquierdo + arrastrar para mover marcos
|
||||
- clic izquierdo + arrastrar para mover paneles
|
||||
- clic derecho en la cabeza del marco para expandirlo / contraerlo o para cerrarlo
|
||||
- clic izquierdo sobre ajustes para modificarlos
|
||||
- shift + clic derecho sobre ajustes para restablecerlos
|
||||
- posiciona el puntero del ratón sobre un módulo o ajuste para leer su descripción
|
||||
|
||||
### protips
|
||||
- shift mientras pasas el puntero por encima de un ajuste de modos para ver los modos disponibles
|
||||
- clic derecho para ciclar hacia atrás en ajustes de modo
|
||||
- usa la rueda del ratón para mover la interfaz verticalmente
|
||||
- shift + rueda del ratón para mover todos los marcos a un rango visible en la pantalla
|
||||
|
||||
## capturas
|
||||
|
||||
<img src="screenshots/ui.png" alt="interfaz">
|
||||
<img src="screenshots/nametags.png" alt="nametags mostrando daño de un tridente">
|
||||
<img src="screenshots/chatplus.png" alt="chatplus y capturas en uso">
|
||||
<img src="screenshots/crits.png" alt="críticos">
|
||||
<img src="screenshots/fullbright.png" alt="fullbright en modo gamma cambiando el color de los shaders">
|
||||
<img src="screenshots/hud.png" alt="HUD">
|
||||
<img src="screenshots/automine.png" alt="auto minado">
|
||||
<img src="screenshots/blockesp.png" alt="resaltado de bloques + superposición de luz">
|
||||
<img src="screenshots/camtweaks.png" alt="perspectiva plus">
|
||||
<img src="screenshots/chatplus.png" alt="chatplus + nametags + capturas">
|
||||
<img src="screenshots/clickgui.png" alt="interfaz">
|
||||
<img src="screenshots/freecam.png" alt="cámara libre + trazos">
|
||||
<img src="screenshots/hudeditor.png" alt="editor de HUD">
|
||||
<img src="screenshots/colorpicker.png" alt="selector de colores">
|
||||
<img src="screenshots/itemphysics.png" alt="físicas de items">
|
||||
<img src="screenshots/lsd.png" alt="LSD">
|
||||
<img src="screenshots/macros.png" alt="macros">
|
||||
<img src="screenshots/trajectories.png" alt="trayectorias">
|
||||
|
||||
## contibuciones
|
||||
## contribuciones
|
||||
|
||||
sí por favor
|
||||
|
||||
## licencia
|
||||
|
||||
no sé, haz lo que quieras con esto, es puta basura xd
|
||||
|
||||
aunque estaría guay que acreditases
|
||||
|
||||
pero bueno
|
||||
[licencia CC0](LICENSE)
|
||||
|
||||
## descargo de responsabilidad
|
||||
|
||||
me importa una puta mierda lo que pase al usar este mod en servers donde es baneable y así.
|
||||
|
||||
## mods recomendados
|
||||
## dependencias
|
||||
|
||||
> mods que recomiendo usar junto a este:
|
||||
- [fabric api](https://modrinth.com/mod/fabric-api/)
|
||||
|
||||
- [fabric api (necesario)](https://modrinth.com/mod/fabric-api/): necesario para el mod
|
||||
- [sodium](https://modrinth.com/mod/sodium): mejora el rendimiento general del juego
|
||||
- [viafabricplus](https://modrinth.com/mod/viafabricplus): te permite conectarte a servidores que tengan una versión distinta a la de tu cliente
|
||||
- [xaero's world map](https://modrinth.com/mod/xaeros-world-map/) y [xaero's minimap](https://modrinth.com/mod/xaeros-minimap/): mapa chulo
|
||||
- [mod menu](https://modrinth.com/mod/modmenu): lista de los mods instalados, y sus ajustes
|
||||
- [iris](https://modrinth.com/mod/iris): shaders
|
||||
- [lambdynamiclights](https://modrinth.com/mod/lambdynamiclights): iluminación dinámica
|
||||
- [appleskin](https://modrinth.com/mod/appleskin/): información relacionada con la comida
|
||||
- [baritone (desactualizado)](https://github.com/cabaletta/baritone): google maps para juego de cubos
|
||||
## notas
|
||||
|
||||
este mod tiene MUCHOS bugs. empecé este proyecto cuando tenía 14 años y no sabía una mierda de programar. el código es un puto desastre y me niego a arreglarlo. puede que en un futuro reescriba el mod o algo. no sé.
|
||||
@@ -1,18 +1,17 @@
|
||||
# v5.2
|
||||
# v6.7
|
||||
|
||||
## novedades
|
||||
|
||||
### adiciones
|
||||
- SIC SEBEN
|
||||
- migrado de Codeberg a mi propia instancia de Gitea
|
||||
|
||||
#### módulos nuevos:
|
||||
### adiciones
|
||||
|
||||
### 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
|
||||
|
||||
## 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
|
||||
- `reponer` no funciona
|
||||
- `copiar` tras sacar una captura con `capturas` no funciona correctamente
|
||||
- `manos` no funciona del todo
|
||||
- \+ lo de siempre.
|
||||
@@ -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 :)
|
||||
también es probable que haya créditos sueltos en forma de comentarios donde haya usado código ajeno
|
||||
@@ -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.3
|
||||
loader_version=0.19.5
|
||||
loom_version=1.18-SNAPSHOT
|
||||
|
||||
fabric_api_version=0.146.0+26.1.2
|
||||
fabric_api_version=0.160.7+26.3
|
||||
discord_rpc_version=1.0.3
|
||||
neutrino_version=1.1.0
|
||||
jdk_version=25
|
||||
|
||||
mod_version=5.2
|
||||
mod_version=6.7
|
||||
maven_group=me.retucio
|
||||
archives_base_name=sputnik
|
||||
@@ -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.8.0-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 3.4 MiB |
|
Before Width: | Height: | Size: 864 KiB After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 796 KiB |
|
Before Width: | Height: | Size: 915 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 566 KiB |
|
Before Width: | Height: | Size: 469 KiB |
|
Before Width: | Height: | Size: 243 KiB After Width: | Height: | Size: 700 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 506 KiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 691 KiB |
@@ -2,11 +2,14 @@ package me.retucio.sputnik;
|
||||
|
||||
import com.github.retucio.neutrino.EventBus;
|
||||
import com.github.retucio.neutrino.EventListener;
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
import me.retucio.sputnik.cape.CapeManager;
|
||||
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 +20,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,11 +52,10 @@ 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;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
|
||||
public class Sputnik implements ClientModInitializer {
|
||||
|
||||
@@ -98,12 +102,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 +143,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);
|
||||
@@ -150,44 +156,82 @@ public class Sputnik implements ClientModInitializer {
|
||||
|
||||
// se ejecuta cada vez que se presiona una tecla
|
||||
public void onInput(int key, int action) {
|
||||
// En SDL el valor 0 es UNKNOWN (y los botones del ratón empiezan en 1).
|
||||
if (key == InputConstants.UNKNOWN.getValue() || key == -1) return;
|
||||
// prevenir interrumpir combos de F3
|
||||
if (key == GLFW.GLFW_KEY_F3) return;
|
||||
if (key == InputConstants.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) {
|
||||
// Las KeySetting y los campos de texto reciben el mismo código SDL scancode que
|
||||
// guardan sus valores; procesarlos antes evita que el bind capturado dispare un módulo.
|
||||
if (anyFocused) handleSettingButtonsKey(key, action);
|
||||
|
||||
if (action != InputConstants.RELEASE) {
|
||||
if (BindCommand.onKeyPress(key)) return;
|
||||
if (action == GLFW.GLFW_PRESS) {
|
||||
if (action == InputConstants.PRESS && MacroManager.INSTANCE.onKeyPress(key)) return;
|
||||
if (action == InputConstants.PRESS) {
|
||||
handleModuleToggle(key, anyFocused);
|
||||
handleClickGUIKey(key, anyFocused);
|
||||
handleClickGuiKey(key, anyFocused);
|
||||
handleHudEditorKey(key, anyFocused);
|
||||
}
|
||||
// solo reenviar teclas del teclado
|
||||
if (key >= 32) {
|
||||
handleSettingButtonsKey(key, action);
|
||||
}
|
||||
} else if (!anyFocused) {
|
||||
handleModuleRelease(key);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onMouseInput(int button, int action) {
|
||||
KeyUtil.setMouseButtonDown(button, action == InputConstants.PRESS);
|
||||
|
||||
boolean captured = false;
|
||||
for (Button settingButton : ClickGui.INSTANCE.getClientSettingsPanel().getButtons()) {
|
||||
if (settingButton instanceof BindButton bindButton && bindButton.isFocused()) {
|
||||
bindButton.onMouse(button, action);
|
||||
captured = true;
|
||||
}
|
||||
}
|
||||
for (SettingsPanel panel : ClickGui.INSTANCE.getSettingsPanels()) {
|
||||
for (Button settingButton : panel.getButtons()) {
|
||||
if (settingButton instanceof BindButton bindButton && bindButton.isFocused()) {
|
||||
bindButton.onMouse(button, action);
|
||||
captured = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (captured) return true;
|
||||
onInput(KeyUtil.encodeMouseButton(button), action);
|
||||
return false;
|
||||
}
|
||||
|
||||
// verifica si algún botón de ajustes está escuchando
|
||||
private boolean isAnySettingButtonFocused() {
|
||||
if (ClickGui.INSTANCE == null) return false;
|
||||
for (Button button : ClickGui.INSTANCE.getClientSettingsPanel().getButtons())
|
||||
if ((button instanceof BindButton bindButton && bindButton.isFocused())
|
||||
|| (button instanceof TextButton textButton && textButton.isFocused())
|
||||
|| (button instanceof FileButton fileButton && fileButton.isFocused())) {
|
||||
return true;
|
||||
}
|
||||
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
|
||||
if (key != module.getKey() || anyFocused || KeyUtil.isKeyDown(GLFW.GLFW_KEY_F3)) continue;
|
||||
if (key != module.getKey() || anyFocused || KeyUtil.isKeyDown(InputConstants.KEY_F3)) continue;
|
||||
|
||||
if (module.shouldToggleOnBindRelease() && !module.isEnabled())
|
||||
module.setEnabled(true);
|
||||
@@ -198,49 +242,51 @@ public class Sputnik implements ClientModInitializer {
|
||||
|
||||
// se ocupa de hacer los botones de ajustes que lo necesiten escuchar teclas
|
||||
private void handleSettingButtonsKey(int key, int action) {
|
||||
ClickGui.INSTANCE.getClientSettingsPanel().onKey(key, action);
|
||||
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 +294,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) {
|
||||
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.screen = new UpdateScreen());
|
||||
mc.execute(() -> mc.gui.setScreen(new UpdateScreen()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,23 +9,26 @@ 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;
|
||||
|
||||
public abstract class Command {
|
||||
|
||||
// utilizar los "registries" ya existentes de Brigadier para el autocompletado
|
||||
protected static final CommandBuildContext REGISTRY_ACCESS = Commands.createValidationContext(VanillaRegistries.createLookup());
|
||||
protected static final CommandBuildContext REGISTRY_ACCESS = Commands.createValidationContext(VanillaRegistries.createWorldLookup());
|
||||
protected static final int SUCCESS = com.mojang.brigadier.Command.SINGLE_SUCCESS;
|
||||
|
||||
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,136 @@
|
||||
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;
|
||||
}))
|
||||
// por ahora "add" solo crea macros de chat
|
||||
.then(literal("add")
|
||||
.then(argument("nombre", StringArgumentType.word()).executes(ctx -> {
|
||||
String name = ctx.getArgument("nombre", String.class);
|
||||
|
||||
if (MacroManager.INSTANCE.add(new Macro.Chat(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.Chat macro = getChat(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.Chat macro = getChat(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;
|
||||
}
|
||||
|
||||
// para los subcomandos que solo tienen sentido con líneas de chat (line, clear): si la
|
||||
// macro existe pero es de otro tipo, avisa y devuelve null
|
||||
private static Macro.Chat getChat(String name) {
|
||||
Macro macro = get(name);
|
||||
if (macro == null) return null;
|
||||
|
||||
if (!(macro instanceof Macro.Chat chat)) {
|
||||
ChatUtil.error(Component.translatable("sputnik.commands.macro.notchat", macro.getName()));
|
||||
return null;
|
||||
}
|
||||
return chat;
|
||||
}
|
||||
|
||||
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) {
|
||||
// líneas en las de chat, teclas en las Keybind
|
||||
int count = switch (macro) {
|
||||
case Macro.Chat chat -> chat.getLines().size();
|
||||
case Macro.Keybind keybind -> keybind.getKeys().size();
|
||||
default -> 0;
|
||||
};
|
||||
|
||||
ChatUtil.addMessage(ChatFormatting.GRAY + " - " + macro.getName()
|
||||
+ ChatFormatting.DARK_GRAY + " [" + KeyUtil.getKeyName(macro.getKey()) + "] "
|
||||
+ count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,7 +13,7 @@ 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
|
||||
@@ -22,7 +22,7 @@ public class PrefixCommand extends Command {
|
||||
.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));
|
||||
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(
|
||||
@@ -50,7 +50,7 @@ public class SendCommand extends Command {
|
||||
message,
|
||||
instant,
|
||||
salt,
|
||||
messageSignature,
|
||||
java.util.Optional.ofNullable(messageSignature),
|
||||
lastSeenMessages.update()
|
||||
));
|
||||
}
|
||||
|
||||
@@ -8,18 +8,20 @@ import me.retucio.sputnik.event.input.KeyEvent;
|
||||
import me.retucio.sputnik.util.EntityUtil;
|
||||
import net.minecraft.commands.SharedSuggestionProvider;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
|
||||
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,8 +42,8 @@ public class SpectateCommand extends Command {
|
||||
|
||||
@EventListener
|
||||
public void onKey(KeyEvent event) {
|
||||
if (mc.getCameraEntity() != mc.player && mc.screen == null
|
||||
&& event.getKey() == GLFW.GLFW_KEY_ESCAPE && event.getAction() != GLFW.GLFW_RELEASE ) {
|
||||
if (mc.getCameraEntity() != mc.player && mc.gui.screen() == null
|
||||
&& event.getKey() == InputConstants.KEY_ESCAPE && event.getAction() != InputConstants.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,8 @@
|
||||
package me.retucio.sputnik.config;
|
||||
|
||||
import me.retucio.sputnik.util.I18nUtil;
|
||||
import me.retucio.sputnik.util.KeyUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
@@ -12,7 +15,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 = 6;
|
||||
private static final int MIN_VERSION = 2;
|
||||
|
||||
private static final byte TYPE_NULL = 0;
|
||||
private static final byte TYPE_BOOLEAN = 1;
|
||||
@@ -23,6 +27,9 @@ public class BinarySerializer {
|
||||
private static final byte TYPE_INT_ARRAY = 6;
|
||||
private static final byte TYPE_FRAME_DATA = 7;
|
||||
|
||||
private static final byte MACRO_CHAT = 0;
|
||||
private static final byte MACRO_KEYBIND = 1;
|
||||
|
||||
public static void writeConfig(ClientConfig config, File file) throws IOException {
|
||||
byte[] bytes = toBytes(config);
|
||||
writeBytes(bytes, file);
|
||||
@@ -69,7 +76,9 @@ public class BinarySerializer {
|
||||
writeStringIntArrayMap(out, config.hudPositions);
|
||||
writeStringBooleanMap(out, config.hudVisibilities);
|
||||
writeStringStringMap(out, config.hudImagePaths);
|
||||
writeStringStringMap(out, config.friends);
|
||||
writeFriendsMap(out, config.friends);
|
||||
writeMacros(out, config.macros);
|
||||
writeString(out, config.currentProfile);
|
||||
|
||||
if (config.searchBarPosition != null) {
|
||||
out.writeBoolean(true);
|
||||
@@ -83,48 +92,86 @@ 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, ClientConfig.FriendData> friends = version >= 4
|
||||
? readFriendsMap(in)
|
||||
: migrateFriendsMap(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);
|
||||
// v3 y v4: macros sin tipo (todas eran de chat). v5+: con tipo
|
||||
List<ClientConfig.MacroData> macros;
|
||||
if (version >= 5) macros = readMacros(in);
|
||||
else if (version >= 3) macros = readLegacyMacros(in);
|
||||
else macros = 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;
|
||||
config.legacyKeyCodes = version < 6;
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
public static void migrateLegacyKeyCodes(ClientConfig config) {
|
||||
config.macros = config.macros.stream().map(macro -> {
|
||||
int key = KeyUtil.fromLegacyGlfwKey(macro.key());
|
||||
List<ClientConfig.KeyData> keys = macro.keys() == null ? List.of() : macro.keys().stream()
|
||||
.map(entry -> new ClientConfig.KeyData(KeyUtil.fromLegacyGlfwKey(entry.key()), entry.action()))
|
||||
.toList();
|
||||
return new ClientConfig.MacroData(macro.type(), macro.name(), key, macro.lines(), keys);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private static void writeStringBooleanMap(DataOutputStream out, Map<String, Boolean> map)
|
||||
throws IOException {
|
||||
if (map == null) {
|
||||
@@ -368,6 +415,122 @@ public class BinarySerializer {
|
||||
return map;
|
||||
}
|
||||
|
||||
// v5+: por macro va un id de tipo, luego nombre y tecla, y por último lo propio del tipo
|
||||
// (líneas en las de chat, pares tecla/acción en las Keybind). los null (que pueden venir
|
||||
// de un json antiguo) se tratan como chat / lista vacía
|
||||
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) {
|
||||
boolean keybind = macro.type() == ClientConfig.MacroType.KEYBIND;
|
||||
|
||||
out.writeByte(keybind ? MACRO_KEYBIND : MACRO_CHAT);
|
||||
writeString(out, macro.name());
|
||||
out.writeInt(macro.key());
|
||||
|
||||
if (keybind) {
|
||||
List<ClientConfig.KeyData> keys = macro.keys() == null ? List.of() : macro.keys();
|
||||
out.writeInt(keys.size());
|
||||
for (ClientConfig.KeyData k : keys) {
|
||||
out.writeInt(k.key());
|
||||
out.writeInt(k.action());
|
||||
}
|
||||
} else {
|
||||
List<String> lines = macro.lines() == null ? List.of() : macro.lines();
|
||||
out.writeInt(lines.size());
|
||||
for (String line : 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++) {
|
||||
byte type = in.readByte();
|
||||
String name = readString(in);
|
||||
int key = in.readInt();
|
||||
|
||||
switch (type) {
|
||||
case MACRO_CHAT -> {
|
||||
int lineCount = in.readInt();
|
||||
List<String> lines = new ArrayList<>(lineCount);
|
||||
for (int j = 0; j < lineCount; j++) lines.add(readString(in));
|
||||
macros.add(ClientConfig.MacroData.chat(name, key, lines));
|
||||
}
|
||||
case MACRO_KEYBIND -> {
|
||||
int keyCount = in.readInt();
|
||||
List<ClientConfig.KeyData> keys = new ArrayList<>(keyCount);
|
||||
for (int j = 0; j < keyCount; j++) keys.add(new ClientConfig.KeyData(in.readInt(), in.readInt()));
|
||||
macros.add(ClientConfig.MacroData.keybind(name, key, keys));
|
||||
}
|
||||
// no hay forma de saltarse una macro de un tipo que no conocemos (no se sabe
|
||||
// cuánto ocupa), así que lo único seguro es fallar
|
||||
default -> throw new IOException(I18nUtil.fmt("sputnik.config.error.invalidmacro",
|
||||
"tipo de macro desconocido: %s", type));
|
||||
}
|
||||
}
|
||||
return macros;
|
||||
}
|
||||
|
||||
// v3 y v4: formato de antes de que hubiera tipos (nombre, tecla, líneas). eran todas de chat
|
||||
private static List<ClientConfig.MacroData> readLegacyMacros(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(ClientConfig.MacroData.chat(name, key, lines));
|
||||
}
|
||||
return macros;
|
||||
}
|
||||
|
||||
private static void writeFriendsMap(DataOutputStream out, Map<String, ClientConfig.FriendData> map)
|
||||
throws IOException {
|
||||
if (map == null) {
|
||||
out.writeInt(0);
|
||||
return;
|
||||
}
|
||||
out.writeInt(map.size());
|
||||
for (Map.Entry<String, ClientConfig.FriendData> entry : map.entrySet()) {
|
||||
writeString(out, entry.getKey());
|
||||
ClientConfig.FriendData data = entry.getValue();
|
||||
writeString(out, data.name());
|
||||
writeString(out, data.nick());
|
||||
out.writeLong(data.befriendedAt());
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, ClientConfig.FriendData> readFriendsMap(DataInputStream in)
|
||||
throws IOException {
|
||||
int size = in.readInt();
|
||||
Map<String, ClientConfig.FriendData> map = new HashMap<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
String uuid = readString(in);
|
||||
String name = readString(in);
|
||||
String nick = readString(in);
|
||||
long befriendedAt = in.readLong();
|
||||
map.put(uuid, new ClientConfig.FriendData(name, nick, befriendedAt));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<String, ClientConfig.FriendData> migrateFriendsMap(Map<String, String> legacy) {
|
||||
Map<String, ClientConfig.FriendData> map = new HashMap<>(legacy.size());
|
||||
long now = System.currentTimeMillis();
|
||||
for (Map.Entry<String, String> entry : legacy.entrySet()) {
|
||||
map.put(entry.getKey(), new ClientConfig.FriendData(entry.getValue(), entry.getValue(), now));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static void writeString(DataOutputStream out, String str) throws IOException {
|
||||
if (str == null) {
|
||||
out.writeInt(-1);
|
||||
|
||||
@@ -4,8 +4,6 @@ import com.github.retucio.neutrino.EventListener;
|
||||
import me.retucio.sputnik.event.sputnik.*;
|
||||
import me.retucio.sputnik.module.setting.settings.*;
|
||||
import me.retucio.sputnik.ui.screen.ClickGui;
|
||||
import me.retucio.sputnik.ui.widgets.panels.FriendsPanel;
|
||||
import me.retucio.sputnik.util.ChatUtil;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
@@ -17,7 +15,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
|
||||
@@ -44,22 +42,38 @@ public class ClientConfig {
|
||||
// posición de la barra de búsqueda (x, y)
|
||||
public int[] searchBarPosition = new int[] { 340, 16 };
|
||||
|
||||
// amigos: uuid -> nombre
|
||||
public Map<String, String> friends = new HashMap<>();
|
||||
// amigos: uuid -> FriendData (nombre, apodo, fecha)
|
||||
public Map<String, FriendData> friends = new HashMap<>();
|
||||
|
||||
// macros, de cualquier tipo (ver MacroData)
|
||||
public List<MacroData> macros = new ArrayList<>();
|
||||
|
||||
// perfil cargado actualmente, vacío si es la config. suelta
|
||||
public String currentProfile = "";
|
||||
|
||||
// Set only when loading a config written with GLFW keycodes (pre-26.3).
|
||||
public transient boolean legacyKeyCodes;
|
||||
|
||||
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 +85,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 +119,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,48 +141,69 @@ 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()));
|
||||
ConfigManager.INSTANCE.markDirty();
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void onExtendFriendsPanel(FriendPanelEvent.Extend event) {
|
||||
extendablePanels.put("amigos", new PanelData(
|
||||
ClickGui.INSTANCE.getFriendsPanel().getX(),
|
||||
ClickGui.INSTANCE.getFriendsPanel().getY(),
|
||||
ClickGui.INSTANCE.getFriendsPanel().isExtended()));
|
||||
ConfigManager.INSTANCE.markDirty();
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void onMoveFriendsPanel(FriendPanelEvent.Move event) {
|
||||
extendablePanels.replace("amigos", new PanelData(
|
||||
ClickGui.INSTANCE.getFriendsPanel().getX(),
|
||||
ClickGui.INSTANCE.getFriendsPanel().getY(),
|
||||
ClickGui.INSTANCE.getFriendsPanel().isExtended()));
|
||||
ConfigManager.INSTANCE.markDirty();
|
||||
}
|
||||
|
||||
@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)) {
|
||||
friends.put(uuid, name);
|
||||
friends.put(uuid, new FriendData(
|
||||
event.getFriend().getName(),
|
||||
event.getFriend().getNick(),
|
||||
event.getFriend().getBefriendedAt()));
|
||||
}
|
||||
ConfigManager.INSTANCE.markDirty();
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void onRemoveFriend(FriendEvent.Remove event) {
|
||||
if (isApplying()) return;
|
||||
friends.remove(event.getFriend().getUuid().toString());
|
||||
ConfigManager.INSTANCE.markDirty();
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void onRenameFriend(FriendEvent.Rename event) {
|
||||
if (isApplying()) return;
|
||||
String uuid = event.getFriend().getUuid().toString();
|
||||
FriendData existing = friends.get(uuid);
|
||||
if (existing == null) return; // no debería pasar, pero porsiaca
|
||||
|
||||
friends.put(uuid, new FriendData(existing.name(), event.getFriend().getNick(), existing.befriendedAt()));
|
||||
ConfigManager.INSTANCE.markDirty();
|
||||
}
|
||||
|
||||
public record PanelData(int x, int y, boolean extended) implements Serializable {
|
||||
@Serial private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
public record FriendData(String name, String nick, long befriendedAt) implements Serializable {
|
||||
@Serial private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
public enum MacroType { CHAT, KEYBIND }
|
||||
|
||||
public record KeyData(int key, int action) implements Serializable {
|
||||
@Serial private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
public record MacroData(MacroType type, String name, int key,
|
||||
List<String> lines, List<KeyData> keys
|
||||
) implements Serializable {
|
||||
@Serial private static final long serialVersionUID = 2L;
|
||||
|
||||
public static MacroData chat(String name, int key, List<String> lines) {
|
||||
return new MacroData(MacroType.CHAT, name, key, List.copyOf(lines), List.of());
|
||||
}
|
||||
|
||||
public static MacroData keybind(String name, int key, List<KeyData> keys) {
|
||||
return new MacroData(MacroType.KEYBIND, name, key, List.of(), List.copyOf(keys));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,27 +8,34 @@ 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.FriendsPanel;
|
||||
import me.retucio.sputnik.ui.widgets.panels.ExtendablePanel;
|
||||
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 me.retucio.sputnik.util.KeyUtil;
|
||||
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 +47,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 +58,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 +69,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 +151,122 @@ 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));
|
||||
config.legacyKeyCodes = true;
|
||||
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();
|
||||
save();
|
||||
writeSnapshot(true);
|
||||
}
|
||||
|
||||
// aplicar configuraciones cargadas
|
||||
public void applyConfig() {
|
||||
if (!shouldApply()) return;
|
||||
// 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;
|
||||
boolean migratedKeys = config.legacyKeyCodes;
|
||||
try {
|
||||
migrateLegacyKeyCodes();
|
||||
// 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();
|
||||
applyClientSettings();
|
||||
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;
|
||||
Sputnik.LOGGER.info("ajustes aplicados");
|
||||
}
|
||||
|
||||
if (migratedKeys) writeSnapshot(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void migrateLegacyKeyCodes() {
|
||||
if (!config.legacyKeyCodes) return;
|
||||
|
||||
ClientSettingsPanel.clientSettings.getSettings().forEach(this::migrateLegacyKeySetting);
|
||||
ModuleManager.INSTANCE.getModules().forEach(module ->
|
||||
module.getSettings().forEach(this::migrateLegacyKeySetting));
|
||||
BinarySerializer.migrateLegacyKeyCodes(config);
|
||||
config.legacyKeyCodes = false;
|
||||
}
|
||||
|
||||
private void migrateLegacyKeySetting(Setting<?> setting) {
|
||||
if (!(setting instanceof KeySetting)) return;
|
||||
String key = getSettingKey(setting);
|
||||
Object value = config.settings.get(key);
|
||||
if (value instanceof Integer code) {
|
||||
config.settings.put(key, KeyUtil.fromLegacyGlfwKey(code));
|
||||
} else if (value instanceof Double code) {
|
||||
config.settings.put(key, KeyUtil.fromLegacyGlfwKey(code.intValue()));
|
||||
}
|
||||
}
|
||||
|
||||
// true mientras se está cargando la config. sobre los ajustes. lo mira KeySetting para no
|
||||
// tratar las teclas guardadas como si el usuario las acabara de asignar, y ClientConfig
|
||||
// para no reescribir en la config. lo que se le está aplicando
|
||||
public boolean isApplying() {
|
||||
return applying;
|
||||
}
|
||||
|
||||
// sustituye la config. viva por otra y la vuelca sobre el estado actual del cliente
|
||||
public void replaceConfig(ClientConfig newConfig) {
|
||||
applying = true;
|
||||
try {
|
||||
resetLiveState();
|
||||
} finally {
|
||||
applying = false;
|
||||
}
|
||||
|
||||
setConfig(newConfig);
|
||||
loaded = false;
|
||||
applyConfig();
|
||||
save();
|
||||
}
|
||||
|
||||
// lo que no venga en la config. nueva tiene que quedarse en su valor por defecto, no en
|
||||
// el que tuviera el perfil anterior
|
||||
private void resetLiveState() {
|
||||
if (ClickGui.INSTANCE != null) ClickGui.INSTANCE.closeAllSettingsPanels();
|
||||
|
||||
for (Module module : ModuleManager.INSTANCE.getModules()) {
|
||||
module.setEnabled(false);
|
||||
module.getSettings().forEach(Setting::reset);
|
||||
}
|
||||
ClientSettingsPanel.clientSettings.getSettings().forEach(Setting::reset);
|
||||
|
||||
for (Friend friend : new ArrayList<>(FriendManager.INSTANCE.getFriends()))
|
||||
FriendManager.INSTANCE.remove(friend);
|
||||
|
||||
MacroManager.INSTANCE.setMacros(List.of());
|
||||
}
|
||||
|
||||
// obtener configuración actual
|
||||
@@ -202,16 +323,79 @@ public class ConfigManager {
|
||||
markDirty();
|
||||
}
|
||||
|
||||
public void setMacros(List<Macro> macros) {
|
||||
ensureConfig();
|
||||
config.macros = macros.stream()
|
||||
.map(ConfigManager::toMacroData)
|
||||
.toList();
|
||||
markDirty();
|
||||
}
|
||||
|
||||
public void setCurrentProfile(String name) {
|
||||
ensureConfig();
|
||||
config.currentProfile = name;
|
||||
markDirty();
|
||||
}
|
||||
|
||||
|
||||
// ------------------ APLICAR AJUSTES ------------------
|
||||
|
||||
public void applyFriends() {
|
||||
config.friends.forEach((uuid, name) -> {
|
||||
Friend friend = new Friend(UUID.fromString(uuid));
|
||||
friend.setName(name);
|
||||
config.friends.forEach((uuid, data) -> {
|
||||
Friend friend = new Friend(UUID.fromString(uuid), data.befriendedAt());
|
||||
friend.setName(data.name());
|
||||
friend.setNick(data.nick());
|
||||
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(ConfigManager::fromMacroData)
|
||||
.toList());
|
||||
}
|
||||
|
||||
private static ClientConfig.MacroData toMacroData(Macro macro) {
|
||||
return switch (macro) {
|
||||
case Macro.Chat chat -> ClientConfig.MacroData.chat(
|
||||
chat.getName(), chat.getKey(), chat.getLines());
|
||||
|
||||
case Macro.Keybind keybind -> ClientConfig.MacroData.keybind(
|
||||
keybind.getName(), keybind.getKey(),
|
||||
keybind.getKeys().stream()
|
||||
.map(k -> new ClientConfig.KeyData(k.key(), k.action()))
|
||||
.toList());
|
||||
|
||||
default -> throw new IllegalArgumentException(
|
||||
"tipo de macro sin serializar: " + macro.getClass().getName());
|
||||
};
|
||||
}
|
||||
|
||||
private static Macro fromMacroData(ClientConfig.MacroData data) {
|
||||
if (data.type() == ClientConfig.MacroType.KEYBIND) {
|
||||
List<Macro.Keybind.Key> keys = data.keys() == null ? List.of() : data.keys().stream()
|
||||
.map(k -> new Macro.Keybind.Key(k.key(), k.action()))
|
||||
.toList();
|
||||
return new Macro.Keybind(data.name(), data.key(), keys);
|
||||
}
|
||||
|
||||
return new Macro.Chat(data.name(), data.key(), data.lines() == null ? List.of() : data.lines());
|
||||
}
|
||||
|
||||
// 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 +415,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 +427,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 +444,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 +461,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() {
|
||||
@@ -285,9 +477,9 @@ public class ConfigManager {
|
||||
applyExtendablePanel(panel);
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -300,22 +492,13 @@ public class ConfigManager {
|
||||
if (panel instanceof ClientSettingsPanel) {
|
||||
panelData = config.extendablePanels.get("ajustes");
|
||||
}
|
||||
if (panel instanceof FriendsPanel) {
|
||||
panelData = config.extendablePanels.get("amigos");
|
||||
}
|
||||
|
||||
if (panelData != null) {
|
||||
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 +527,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 +545,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 +589,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 +609,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 +635,6 @@ public class ConfigManager {
|
||||
|
||||
public void markDirty() {
|
||||
this.dirty = true;
|
||||
this.lastDirtyTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public boolean isDirty() {
|
||||
@@ -442,10 +648,11 @@ public class ConfigManager {
|
||||
if (!settings.autosave.getValue()) return;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,8 @@ package me.retucio.sputnik.event.sputnik;
|
||||
import com.github.retucio.neutrino.Event;
|
||||
import me.retucio.sputnik.friend.Friend;
|
||||
|
||||
// evento lanzado cuando se agrega o se elimina un amigo
|
||||
|
||||
// evento lanzado cuando se agrega, elimina o renombra un amigo
|
||||
public class FriendEvent extends Event {
|
||||
|
||||
private final Friend friend;
|
||||
@@ -23,4 +24,8 @@ public class FriendEvent extends Event {
|
||||
public static class Remove extends FriendEvent {
|
||||
public Remove(Friend friend) { super(friend); }
|
||||
}
|
||||
|
||||
public static class Rename extends FriendEvent {
|
||||
public Rename(Friend friend) { super(friend); }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package me.retucio.sputnik.event.sputnik;
|
||||
|
||||
import com.github.retucio.neutrino.Event;
|
||||
|
||||
public class FriendPanelEvent {
|
||||
|
||||
public static class Extend extends Event {} // al extenderlo / contraerlo
|
||||
public static class Move extends Event {} // al cambiarlo de posición
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package me.retucio.sputnik.friend;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.PlayerInfo;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -13,12 +14,20 @@ public class Friend {
|
||||
private static final Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
private String name;
|
||||
private String nick;
|
||||
private final UUID uuid;
|
||||
private final long befriendedAt;
|
||||
|
||||
private boolean searchMatch;
|
||||
|
||||
public Friend(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
this.befriendedAt = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public Friend(UUID uuid, long befriendedAt) {
|
||||
this.uuid = uuid;
|
||||
this.befriendedAt = befriendedAt;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -29,10 +38,27 @@ public class Friend {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getNick() {
|
||||
return nick;
|
||||
}
|
||||
|
||||
public void setNick(String nick) {
|
||||
this.nick = nick;
|
||||
}
|
||||
|
||||
// apodo si existe, si no el nombre real
|
||||
public String getDisplayName() {
|
||||
return (nick == null || nick.isBlank()) ? name : nick;
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public long getBefriendedAt() {
|
||||
return befriendedAt;
|
||||
}
|
||||
|
||||
public @Nullable Player getPlayer() {
|
||||
if (mc.level == null) return null;
|
||||
Entity entity = mc.level.getEntity(uuid);
|
||||
@@ -40,6 +66,11 @@ public class Friend {
|
||||
return player;
|
||||
}
|
||||
|
||||
public @Nullable PlayerInfo getPlayerInfo() {
|
||||
if (mc.getConnection() == null) return null;
|
||||
return mc.getConnection().getPlayerInfo(uuid);
|
||||
}
|
||||
|
||||
public boolean isSearchMatch() {
|
||||
return searchMatch;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ public class FriendManager {
|
||||
public void add(UUID uuid) {
|
||||
Friend friend = new Friend(uuid);
|
||||
friend.setName(friend.getPlayer().getName().getString());
|
||||
friend.setNick(friend.getName());
|
||||
add(friend);
|
||||
}
|
||||
|
||||
@@ -61,6 +62,11 @@ public class FriendManager {
|
||||
return Friend.Status.SUCCESS;
|
||||
}
|
||||
|
||||
public void rename(Friend friend, String nick) {
|
||||
friend.setNick(nick);
|
||||
Sputnik.EVENT_BUS.post(new FriendEvent.Rename(friend));
|
||||
}
|
||||
|
||||
public Friend get(UUID uuid) {
|
||||
for (Friend friend : friends) {
|
||||
if (friend.getUuid().equals(uuid)) {
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package me.retucio.sputnik.macro;
|
||||
|
||||
import me.retucio.sputnik.util.ChatUtil;
|
||||
import me.retucio.sputnik.util.KeyUtil;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public abstract class Macro {
|
||||
|
||||
protected String name;
|
||||
protected int key;
|
||||
|
||||
protected Macro(String name, int key) {
|
||||
this.name = name;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
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; }
|
||||
|
||||
private boolean searchMatch = true;
|
||||
public boolean isSearchMatch() { return searchMatch; }
|
||||
public void setSearchMatch(boolean searchMatch) { this.searchMatch = searchMatch; }
|
||||
|
||||
public abstract void run();
|
||||
|
||||
|
||||
// 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 static class Chat extends Macro {
|
||||
|
||||
private final List<String> lines = new ArrayList<>();
|
||||
|
||||
public Chat(String name) {
|
||||
this(name, InputConstants.UNKNOWN.getValue(), List.of());
|
||||
}
|
||||
|
||||
public Chat(String name, int key, List<String> lines) {
|
||||
super(name, key);
|
||||
this.lines.addAll(lines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (Minecraft.getInstance().player == null) return;
|
||||
for (String line : lines) {
|
||||
if (!line.isBlank()) ChatUtil.simulateChatMessage(line);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getLines() {
|
||||
return lines;
|
||||
}
|
||||
|
||||
public void setLines(List<String> lines) {
|
||||
this.lines.clear();
|
||||
this.lines.addAll(lines);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class Keybind extends Macro {
|
||||
|
||||
private final List<Key> keys = new ArrayList<>();
|
||||
|
||||
public Keybind(String name) {
|
||||
this(name, InputConstants.UNKNOWN.getValue(), List.of());
|
||||
}
|
||||
|
||||
public Keybind(String name, int key, List<Key> keys) {
|
||||
super(name, key);
|
||||
this.keys.addAll(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (Key k : keys) {
|
||||
k.use();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Key> getKeys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
public void setKeys(List<Key> keys) {
|
||||
this.keys.clear();
|
||||
this.keys.addAll(keys);
|
||||
}
|
||||
|
||||
public record Key(int key, int action) {
|
||||
public void use() {
|
||||
KeyUtil.pressKey(key, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package me.retucio.sputnik.macro;
|
||||
|
||||
import me.retucio.sputnik.config.ConfigManager;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class MacroManager {
|
||||
|
||||
public static MacroManager INSTANCE;
|
||||
|
||||
private final List<Macro> macros = new ArrayList<>();
|
||||
|
||||
// para evitar disparar un macro que presiona su propia tecla infinitamente
|
||||
private boolean running = false;
|
||||
|
||||
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 (running || key == InputConstants.UNKNOWN.getValue() || Minecraft.getInstance().gui.screen() != null) return false;
|
||||
|
||||
running = true;
|
||||
try {
|
||||
boolean ran = false;
|
||||
for (Macro macro : new ArrayList<>(macros)) {
|
||||
if (macro.getKey() != key) continue;
|
||||
macro.run();
|
||||
ran = true;
|
||||
}
|
||||
return ran;
|
||||
} finally {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
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,18 @@
|
||||
package me.retucio.sputnik.mixin.accessors;
|
||||
|
||||
import net.minecraft.client.renderer.item.ItemStackRenderState;
|
||||
import net.minecraft.client.resources.model.geometry.ItemQuads;
|
||||
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();
|
||||
|
||||
@Accessor("quads")
|
||||
ItemQuads sputnik$getQuads();
|
||||
}
|
||||
@@ -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.renderpearl.api.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/renderpearl/api/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();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package me.retucio.sputnik.mixin.mixins.io;
|
||||
|
||||
import me.retucio.sputnik.Sputnik;
|
||||
import me.retucio.sputnik.event.input.KeyEvent;
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
import net.minecraft.client.KeyboardHandler;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -14,9 +15,9 @@ public abstract class KeyboardHandlerMixin {
|
||||
|
||||
@Inject(method = "keyPress", at = @At("HEAD"), cancellable = true)
|
||||
private void onKeyPress(long handle, int action, net.minecraft.client.input.KeyEvent input, CallbackInfo ci) {
|
||||
if (input.key() == -1) return;
|
||||
if (input.key() == InputConstants.UNKNOWN.getValue() || input.key() == -1) return;
|
||||
Sputnik.INSTANCE.onInput(input.key(), action);
|
||||
KeyEvent event = Sputnik.EVENT_BUS.post(new KeyEvent(input.key(), input.scancode(), action));
|
||||
KeyEvent event = Sputnik.EVENT_BUS.post(new KeyEvent(input.key(), input.keycode(), action));
|
||||
if (event.isCancelled()) ci.cancel();
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,10 @@ public abstract class MouseHandlerMixin {
|
||||
|
||||
@Inject(method = "onButton", at = @At("HEAD"), cancellable = true)
|
||||
private void onMouseButton(long handle, MouseButtonInfo input, int action, CallbackInfo ci) {
|
||||
Sputnik.INSTANCE.onInput(input.button(), action);
|
||||
if (Sputnik.INSTANCE.onMouseInput(input.button(), action)) {
|
||||
ci.cancel();
|
||||
return;
|
||||
}
|
||||
MouseClickEvent event = Sputnik.EVENT_BUS.post(new MouseClickEvent(action, input.button()));
|
||||
if (event.isCancelled()) 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())
|
||||
Component.translatable("sputnik.module.shulkerpeek.holdtopreview",
|
||||
Component.literal(shulkerPeek.previewKey.getKeyName())
|
||||
.withStyle(ChatFormatting.GOLD, ChatFormatting.BOLD))
|
||||
.append(" para previsualizar")
|
||||
.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();
|
||||
|
||||
@@ -3,18 +3,13 @@ package me.retucio.sputnik.mixin.mixins.network;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import me.retucio.sputnik.command.CommandManager;
|
||||
import me.retucio.sputnik.event.network.SendMessageEvent;
|
||||
import me.retucio.sputnik.module.ModuleManager;
|
||||
import me.retucio.sputnik.module.modules.render.NoRender;
|
||||
import me.retucio.sputnik.util.ChatUtil;
|
||||
import net.minecraft.client.multiplayer.ClientPacketListener;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
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;
|
||||
|
||||
import static me.retucio.sputnik.Sputnik.*;
|
||||
@@ -48,14 +43,7 @@ public abstract class ClientPacketListenerMixin {
|
||||
ChatUtil.error(e.getMessage());
|
||||
}
|
||||
|
||||
mc.gui.getChat().addRecentChat(content);
|
||||
mc.gui.hud.getChat().addRecentChat(content);
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@Redirect(method = "handleEntityEvent", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;displayItemActivation(Lnet/minecraft/world/item/ItemStack;)V"))
|
||||
private void noRenderTotemPop(GameRenderer instance, ItemStack itemStack) {
|
||||
NoRender noRender = ModuleManager.INSTANCE.getModuleByClass(NoRender.class);
|
||||
if (!noRender.isEnabled() || noRender.totemPop.getValue())
|
||||
mc.gameRenderer.displayItemActivation(itemStack);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
package me.retucio.sputnik.mixin.mixins.network;
|
||||
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilUserApiService;
|
||||
import com.mojang.authlib.yggdrasil.response.UserAttributesResponse;
|
||||
import com.mojang.authlib.services.MinecraftServicesUserApiService;
|
||||
import com.mojang.authlib.services.response.UserAttributesResponse;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
// código robado de: https://github.com/kb-1000/no-telemetry
|
||||
|
||||
@Mixin(YggdrasilUserApiService.class)
|
||||
@Mixin(MinecraftServicesUserApiService.class)
|
||||
public abstract class YggdrasilUserApiServiceMixin {
|
||||
|
||||
@Redirect(method = "fetchProperties", at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/yggdrasil/response/UserAttributesResponse$Privileges;getTelemetry()Z", remap = false), remap = false, require = 0)
|
||||
@Redirect(method = "fetchProperties", at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/services/response/UserAttributesResponse$Privileges;getTelemetry()Z", remap = false), remap = false, require = 0)
|
||||
private boolean getTelemetry(UserAttributesResponse.Privileges instance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import me.retucio.sputnik.module.ModuleManager;
|
||||
import me.retucio.sputnik.module.modules.render.GlintPlus;
|
||||
import net.minecraft.client.renderer.entity.layers.EquipmentLayerRenderer;
|
||||
import net.minecraft.client.renderer.rendertype.RenderType;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
@@ -12,9 +13,9 @@ import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
@Mixin(EquipmentLayerRenderer.class)
|
||||
public abstract class EquipmentLayerRendererMixin {
|
||||
|
||||
@Redirect(method = "renderLayers(Lnet/minecraft/client/resources/model/EquipmentClientInfo$LayerType;Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/client/model/Model;Ljava/lang/Object;Lnet/minecraft/world/item/ItemStack;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;ILnet/minecraft/resources/Identifier;II)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/rendertype/RenderTypes;armorEntityGlint()Lnet/minecraft/client/renderer/rendertype/RenderType;"))
|
||||
private RenderType redirectArmorEntityGlint() {
|
||||
@Redirect(method = "renderLayers(Lnet/minecraft/client/resources/model/EquipmentClientInfo$LayerType;Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/client/model/Model;Ljava/lang/Object;Lnet/minecraft/world/item/ItemStack;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;ILnet/minecraft/resources/Identifier;II)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/rendertype/RenderTypes;armorCutoutNoCullGlint(Lnet/minecraft/resources/Identifier;)Lnet/minecraft/client/renderer/rendertype/RenderType;"))
|
||||
private RenderType redirectArmorEntityGlint(Identifier texture) {
|
||||
GlintPlus glintPlus = ModuleManager.INSTANCE.getModuleByClass(GlintPlus.class);
|
||||
return glintPlus.getArmorEntityGlint();
|
||||
return glintPlus.getArmorEntityGlint(texture);
|
||||
}
|
||||
}
|
||||
@@ -6,16 +6,20 @@ 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.FirstPersonHandsAndItemsRenderer;
|
||||
import net.minecraft.client.renderer.state.level.CameraRenderState;
|
||||
import net.minecraft.client.renderer.state.level.PlayerRenderState;
|
||||
import net.minecraft.client.renderer.item.ItemModelResolver;
|
||||
import com.mojang.renderpearl.api.textures.GpuTextureView;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import org.joml.Matrix4fc;
|
||||
|
||||
@@ -39,19 +43,32 @@ 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, FirstPersonHandsAndItemsRenderer itemInHandRenderer, ModelManager modelManager, ItemModelResolver itemModelResolver, 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);
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/client/renderer/LevelRenderer;blitEntityOutline()V"))
|
||||
private void applyLsd(CallbackInfo ci) {
|
||||
if (lsd.isEnabled()) lsd.apply(mainRenderTarget, resourcePool);
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At("HEAD"), cancellable = true)
|
||||
private void avoidRenderingUnfocusedCpu(DeltaTracker dt, boolean tick, CallbackInfo ci) {
|
||||
private void avoidRenderingUnfocusedCpu(CallbackInfo ci) {
|
||||
if (ModuleManager.INSTANCE.getModuleByClass(UnfocusedCpu.class).isEnabled() && !mc.isWindowActive()) {
|
||||
ci.cancel();
|
||||
}
|
||||
@@ -64,7 +81,7 @@ public abstract class GameRendererMixin {
|
||||
|
||||
|
||||
@Inject(method = "renderItemInHand", at = @At("HEAD"), cancellable = true)
|
||||
private void renderHand(CameraRenderState cameraState, float deltaPartialTick, Matrix4fc modelViewMatrix, CallbackInfo ci) {
|
||||
private void renderHand(CameraRenderState cameraState, PlayerRenderState playerRenderState, GpuTextureView texture, CallbackInfo ci) {
|
||||
if ((zoom.isEnabled() && !zoom.showHands.getValue())
|
||||
|| (freecam.isEnabled() && !freecam.renderHands.getValue()))
|
||||
ci.cancel();
|
||||
|
||||
@@ -3,11 +3,12 @@ package me.retucio.sputnik.mixin.mixins.render;
|
||||
import me.retucio.sputnik.module.ModuleManager;
|
||||
import me.retucio.sputnik.module.modules.render.GlintPlus;
|
||||
import net.minecraft.client.renderer.feature.ItemFeatureRenderer;
|
||||
import net.minecraft.client.resources.model.geometry.BakedQuad;
|
||||
import net.minecraft.client.renderer.rendertype.RenderType;
|
||||
import net.minecraft.client.renderer.rendertype.RenderTypes;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
|
||||
|
||||
@Mixin(ItemFeatureRenderer.class)
|
||||
@@ -15,24 +16,15 @@ 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;"))
|
||||
private static RenderType redirectGlint() {
|
||||
@ModifyExpressionValue(method = "prepareMainSubmit", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/geometry/BakedQuad$MaterialInfo;itemGlintRenderType()Lnet/minecraft/client/renderer/rendertype/RenderType;"))
|
||||
private RenderType sputnik$colorGlint(RenderType original, @Local BakedQuad.MaterialInfo materialInfo) {
|
||||
GlintPlus glintPlus = ModuleManager.INSTANCE.getModuleByClass(GlintPlus.class);
|
||||
if (glintPlus == null) return RenderTypes.glint();
|
||||
return glintPlus.getGlint();
|
||||
return glintPlus == null ? original : glintPlus.getGlint(original, materialInfo);
|
||||
}
|
||||
|
||||
@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() {
|
||||
@ModifyExpressionValue(method = "prepareMainSubmit", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/geometry/BakedQuad$MaterialInfo;itemGlintSpecialRenderType()Lnet/minecraft/client/renderer/rendertype/RenderType;"))
|
||||
private RenderType sputnik$colorSpecialGlint(RenderType original, @Local BakedQuad.MaterialInfo materialInfo) {
|
||||
GlintPlus glintPlus = ModuleManager.INSTANCE.getModuleByClass(GlintPlus.class);
|
||||
if (glintPlus == null) return RenderTypes.entityGlint();
|
||||
return glintPlus.getEntityGlint();
|
||||
}
|
||||
|
||||
@Redirect(method = "getFoilRenderType", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/rendertype/RenderTypes;glintTranslucent()Lnet/minecraft/client/renderer/rendertype/RenderType;"))
|
||||
private static RenderType redirectGlintTranslucent() {
|
||||
GlintPlus glintPlus = ModuleManager.INSTANCE.getModuleByClass(GlintPlus.class);
|
||||
if (glintPlus == null) return RenderTypes.glintTranslucent();
|
||||
return glintPlus.getGlintTranslucent();
|
||||
return glintPlus == null ? original : glintPlus.getGlint(original, materialInfo);
|
||||
}
|
||||
}
|
||||