Compare commits

...
31 Commits
Author SHA1 Message Date
retucio c1a03f3ddb v6.4 2026-09-06 11:55:58 +02:00
retucio 44494830be v6.3 2026-09-04 12:58:41 +02:00
retucio 73682cb85d v6.2 2026-08-28 12:27:07 +02:00
retucio ac7de7a07a v6.1 2026-08-27 01:00:19 +02:00
retucio 0bd31971a0 v6.0 2026-08-23 16:50:27 +02:00
retucio 3010b164ab v5.9 2026-08-23 16:03:32 +02:00
retucio 6cb4656365 v5.8 2026-08-22 18:20:52 +02:00
retucio 272c1059ef v5.7 2026-08-19 22:57:41 +02:00
retucio e0f8523809 v5.6 2026-08-18 13:35:24 +02:00
retucio 81fc25c33d v5.5 2026-07-20 22:17:44 +02:00
retucio 81b043270b v5.4.1 2026-07-19 23:20:48 +02:00
retucio 3913b31374 v5.4 2026-07-19 23:14:33 +02:00
retucio aeadfb28b1 v5.3 2026-07-15 21:50:51 +02:00
retuci0 957fab15aa v5.2 2026-05-10 10:54:59 +02:00
retuci0 321505657c v5.1 2026-05-09 20:55:38 +02:00
retuci0 b2bc2b36ab v5.0 2026-05-07 22:51:36 +02:00
retuci0 09ed268576 v4.8 2026-05-07 16:27:24 +02:00
retuci0 531c3ef9aa v4.8 2026-05-06 21:15:59 +02:00
retuci0 67cc34a08c v4.7 2026-05-06 17:59:19 +02:00
retuci0 c7b8e96d68 v4.6 2026-05-04 19:12:16 +02:00
retuci0 65afbf6ae6 v4.5 2026-05-03 15:06:20 +02:00
retuci0 8933565e07 v4.4 2026-05-01 23:45:42 +02:00
retuci0 47ad2c67c5 v4.3 2026-04-30 22:24:33 +02:00
retucio e84382367e actualizar build.yml 2026-04-30 20:19:02 +02:00
retuci0 ac99a609a0 v4.2 2026-04-28 17:38:38 +02:00
retuci0 1d22ec5176 4.1 (broken) 2026-04-27 21:55:51 +02:00
retuci0 05c5300cd8 v4.0 2026-03-23 19:39:59 +01:00
retuci0 a879458b4a v3.9 2026-03-21 09:48:30 +01:00
retuci0 bab752c589 v3.8 2026-03-01 22:38:57 +01:00
retuci0 4d30a16a6a v3.7 2026-02-27 18:06:32 +01:00
retuci0 412fe93a89 v3.6 2026-02-22 12:01:00 +01:00
453 changed files with 20298 additions and 7180 deletions

No files matched your search

+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
- name: setup jdk - name: setup jdk
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
java-version: '21' java-version: '25'
distribution: 'microsoft' distribution: 'microsoft'
- name: make gradle wrapper executable - name: make gradle wrapper executable
run: chmod +x ./gradlew run: chmod +x ./gradlew
+1
View File
@@ -44,3 +44,4 @@ replay_*.log
todo.txt todo.txt
cambios.md cambios.md
remappedSrc/ remappedSrc/
.claude/
+82
View File
@@ -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.2 mod that adds some "QOL" features, mostly related to the camera.
## installation
### for normal people:
1. go to [releases](https://codeberg.org/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.2`; Fabric `0.19.3`)
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://codeberg.org/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.
+30 -38
View File
@@ -1,29 +1,34 @@
<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 # jenny's mod 2 100% real
mod para Minecraft Fabric 1.21.11 que añade algunas cosas QOL, mayormente relacionadas a la cámara. > mod para Minecraft Fabric 26.2 que añade algunas cosas "QOL", mayormente relacionadas a la cámara.
## instalación ## instalación
### para gente normal: ### para gente normal:
1. ir a [lanzamientos](https://github.com/retuci0/sputnik/releases) 1. ir a [lanzamientos](https://codeberg.org/retucio/sputnik/releases)
2. selecionar la versión más reciente 2. selecionar la versión más reciente
3. descargar el .jar 3. descargar el .jar
4. moverlo a tu directorio de mods (probablemente `%appdata%/.minecraft/mods`) 4. moverlo a tu directorio de mods (probablemente `%appdata%/.minecraft/mods`)
asegúrate de tener una version de Minecraft y Fabric compatibles con el mod (MC `1.21.11`; Fabric `0.17.3`) asegúrate de tener una version de Minecraft y Fabric compatibles con el mod (MC `26.2`; Fabric `0.19.3`)
es posible que haya problemas al usarlo junto a otros mods, especialmente junto a otros clientes, pues pueden conflictar entre ellos. es posible que haya problemas al usarlo junto a otros mods, especialmente junto a otros clientes, pues pueden conflictar entre ellos.
### para frikis de 200 de iq ### para frikis de 200 de iq
1. clona el respositorio (`git clone https://github.com/retuci0/sputnik`) 1. clona el respositorio (`git clone https://codeberg.org/retucio/sputnik`)
2. entra al directorio del repositorio (`cd 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) 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` (el que no lleva "sources" en el nombre) 4. el resultado estará en `/build/libs/sputnik-X.X.jar`
## uso ## uso
`shift derecho` por defecto para abrir la interfaz `shift derecho` por defecto para abrir la interfaz
`pág. arriba` por defecto para abrir el editor del hud `insertar` por defecto para abrir el editor del hud
`$` como prefijo por defecto para comandos (se autocompletan) `$` como prefijo por defecto para comandos (se autocompletan)
@@ -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 izquierdo para activar / desactivar módulos
- clic derecho para abrir sus ajustes - 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 derecho en la cabeza del marco para expandirlo / contraerlo o para cerrarlo
- clic izquierdo sobre ajustes para modificarlos - clic izquierdo sobre ajustes para modificarlos
- shift + clic derecho sobre ajustes para restablecerlos - shift + clic derecho sobre ajustes para restablecerlos
- posiciona el puntero del ratón sobre un módulo o ajuste para leer su descripción - 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 ## capturas
<img src="screenshots/ui.png" alt="interfaz"> <img src="screenshots/automine.png" alt="auto minado">
<img src="screenshots/nametags.png" alt="nametags mostrando daño de un tridente"> <img src="screenshots/blockesp.png" alt="resaltado de bloques + superposición de luz">
<img src="screenshots/chatplus.png" alt="chatplus y capturas en uso"> <img src="screenshots/camtweaks.png" alt="perspectiva plus">
<img src="screenshots/crits.png" alt="críticos"> <img src="screenshots/chatplus.png" alt="chatplus + nametags + capturas">
<img src="screenshots/fullbright.png" alt="fullbright en modo gamma cambiando el color de los shaders"> <img src="screenshots/clickgui.png" alt="interfaz">
<img src="screenshots/hud.png" alt="HUD"> <img src="screenshots/freecam.png" alt="cámara libre + trazos">
<img src="screenshots/hudeditor.png" alt="editor de HUD"> <img src="screenshots/hudeditor.png" alt="editor de HUD">
<img src="screenshots/colorpicker.png" alt="selector de colores"> <img src="screenshots/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 sí por favor
## licencia ## licencia
no sé, haz lo que quieras con esto, es puta basura xd [licencia CC0](LICENSE)
aunque estaría guay que acreditases
pero bueno
## descargo de responsabilidad ## descargo de responsabilidad
me importa una puta mierda lo que pase al usar este mod en servers donde es baneable y así. 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 ## notas
- [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 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é.
- [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
+17 -12
View File
@@ -1,5 +1,5 @@
plugins { plugins {
id 'fabric-loom' version "${loom_version}" id "net.fabricmc.fabric-loom" version "${loom_version}"
id 'maven-publish' id 'maven-publish'
} }
@@ -23,18 +23,15 @@ dependencies {
// mc // mc
minecraft "com.mojang:minecraft:${project.minecraft_version}" minecraft "com.mojang:minecraft:${project.minecraft_version}"
// mappings
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
// fabric & su api // fabric & su api
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" implementation "net.fabricmc:fabric-loader:${project.loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}" implementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
// sistema de eventos // sistema de eventos
include(modImplementation "com.github.retuci0:neutrino:${project.neutrino_version}") include(implementation "com.github.retuci0:neutrino:${project.neutrino_version}")
// rpc de discord // rpc de discord
include(modImplementation "dev.firstdark.discordrpc:discord-rpc:1.0.3") include(implementation "dev.firstdark.discordrpc:discord-rpc:${project.discord_rpc_version}")
} }
loom { loom {
@@ -43,19 +40,27 @@ loom {
processResources { processResources {
inputs.property "version", project.version inputs.property "version", project.version
inputs.property "loader_version", project.loader_version
inputs.property "minecraft_version", project.minecraft_version
inputs.property "jdk_version", project.jdk_version
filesMatching("fabric.mod.json") { filesMatching("fabric.mod.json") {
expand "version": inputs.properties.version expand(
"version": project.version,
"loader_version": project.loader_version,
"minecraft_version": project.minecraft_version,
"jdk_version": project.jdk_version
)
} }
} }
tasks.withType(JavaCompile).configureEach { tasks.withType(JavaCompile).configureEach {
it.options.release = 21 it.options.release = jdk_version.toInteger()
} }
java { java {
sourceCompatibility = JavaVersion.VERSION_21 sourceCompatibility = JavaVersion.toVersion(jdk_version)
targetCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.toVersion(jdk_version)
} }
jar { jar {
+9 -7
View File
@@ -1,18 +1,20 @@
# v3.5 # v6.4
## novedades ## novedades
### adiciones - README en inglés
- capturas actualizadas (las de los README)
### adiciones
#### módulos nuevos: #### módulos nuevos:
### arreglos / mejoras ### arreglos / mejoras
- arreglado el crash por no incluir el sistema de eventos en el mod (soy gilipollas) - ahora la detección de clics sobre cabezales de grupos de ajustes funciona correctamente
- mejoras varias al módulo tobillos fuertes
- ahora el módulo de interfaces en portales funciona correctamente
- reponer ya no está repe
## problemas reconocidos ## problemas reconocidos
- `reponer` no funciona
- `copiar` tras sacar una captura con `capturas` no funciona correctamente
- lo de siempre.
+2
View File
@@ -10,6 +10,8 @@ elytra-recast: https://github.com/InLieuOfLuna/elytra-recast/
SpearHax: https://github.com/etianl/spearHax/ SpearHax: https://github.com/etianl/spearHax/
ProjectileTrajectoryPreview: https://github.com/maDU59/ProjectilesTrajectoryPreview ProjectileTrajectoryPreview: https://github.com/maDU59/ProjectilesTrajectoryPreview
DupersUnited public addon: https://discord.gg/dupes | https://dupers.wtf DupersUnited public addon: https://discord.gg/dupes | https://dupers.wtf
Stardust: https://github.com/0xTas/stardust/
probablemente más pero como tengo demencia se me olvida probablemente más pero como tengo demencia se me olvida
si quieres que te acredite o quite tu código de mi mod, mándame un mensaje en discord (username: retucio) y sin problemas :) si quieres que te acredite o quite tu código de mi mod, mándame un mensaje en discord (username: retucio) y sin problemas :)
también es probable que haya créditos sueltos en forma de comentarios donde haya usado código ajeno
+7 -7
View File
@@ -1,15 +1,15 @@
org.gradle.jvmargs=-Xmx2G org.gradle.jvmargs=-Xmx2G
org.gradle.parallel=true org.gradle.parallel=true
minecraft_version=1.21.11 minecraft_version=26.2
yarn_mappings=1.21.11+build.3 loader_version=0.19.3
loader_version=0.18.4 loom_version=1.17-SNAPSHOT
loom_version=1.13-SNAPSHOT
fabric_api_version=0.140.2+1.21.11
fabric_api_version=0.154.0+26.2
discord_rpc_version=1.0.3
neutrino_version=1.1.0 neutrino_version=1.1.0
jdk_version=25
mod_version=3.5 mod_version=6.4
maven_group=me.retucio maven_group=me.retucio
archives_base_name=sputnik archives_base_name=sputnik
+2 -3
View File
@@ -1,7 +1,6 @@
#Sat Aug 29 22:10:52 CEST 2026
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
View File
Whitespace-only changes.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 864 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 566 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 KiB

After

Width:  |  Height:  |  Size: 700 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 506 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 691 KiB

+110 -65
View File
@@ -1,36 +1,41 @@
package me.retucio.sputnik; package me.retucio.sputnik;
import com.github.retucio.neutrino.EventBus; import com.github.retucio.neutrino.EventBus;
import com.github.retucio.neutrino.EventListener;
import me.retucio.sputnik.cape.CapeManager; import me.retucio.sputnik.cape.CapeManager;
import me.retucio.sputnik.command.CommandManager; import me.retucio.sputnik.command.CommandManager;
import me.retucio.sputnik.command.commands.BindCommand; import me.retucio.sputnik.command.commands.BindCommand;
import me.retucio.sputnik.config.ConfigManager; import me.retucio.sputnik.config.ConfigManager;
import me.retucio.sputnik.config.ProfileManager;
import me.retucio.sputnik.macro.MacroManager;
import me.retucio.sputnik.event.ShutdownEvent; import me.retucio.sputnik.event.ShutdownEvent;
import me.retucio.sputnik.event.interact.OpenScreenEvent; import me.retucio.sputnik.event.interact.OpenScreenEvent;
import me.retucio.sputnik.event.sputnik.LoadCapeManagerEvent; import me.retucio.sputnik.event.sputnik.*;
import me.retucio.sputnik.event.sputnik.LoadClickGUIEvent;
import me.retucio.sputnik.event.sputnik.LoadCommandManagerEvent;
import me.retucio.sputnik.event.sputnik.LoadModuleManagerEvent;
import me.retucio.sputnik.friend.FriendManager;
import me.retucio.sputnik.module.Module; import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.client.HUD; import me.retucio.sputnik.module.modules.client.Hud;
import me.retucio.sputnik.ui.hud.HudEditor;
import me.retucio.sputnik.ui.hud.HudRenderer; import me.retucio.sputnik.ui.hud.HudRenderer;
import me.retucio.sputnik.ui.screen.ClickGUI; import me.retucio.sputnik.ui.screen.ClickGui;
import me.retucio.sputnik.ui.hud.HudEditorScreen; import me.retucio.sputnik.ui.hud.HudEditorScreen;
import me.retucio.sputnik.ui.screen.UpdateScreen; import me.retucio.sputnik.ui.screen.UpdateScreen;
import me.retucio.sputnik.ui.widgets.buttons.settings.BindButton; import me.retucio.sputnik.ui.widgets.buttons.settings.BindButton;
import me.retucio.sputnik.ui.widgets.buttons.SettingButton; import me.retucio.sputnik.ui.widgets.buttons.SettingButton;
import me.retucio.sputnik.ui.widgets.buttons.settings.FileButton;
import me.retucio.sputnik.ui.widgets.buttons.settings.TextButton; import me.retucio.sputnik.ui.widgets.buttons.settings.TextButton;
import me.retucio.sputnik.ui.widgets.frames.settings.ClientSettingsFrame; import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel;
import me.retucio.sputnik.ui.widgets.frames.SettingsFrame; import me.retucio.sputnik.ui.widgets.panels.SettingsPanel;
import me.retucio.sputnik.ui.widgets.Button; import me.retucio.sputnik.ui.widgets.Button;
import me.retucio.sputnik.util.*; import me.retucio.sputnik.util.*;
import me.retucio.sputnik.util.KeyUtil; import me.retucio.sputnik.util.KeyUtil;
import me.retucio.sputnik.util.misc.BlockIterator;
import me.retucio.sputnik.util.misc.VersionChecker;
import me.retucio.sputnik.util.render.DrawUtil; import me.retucio.sputnik.util.render.DrawUtil;
import me.retucio.sputnik.util.render.RenderUtil; import me.retucio.sputnik.util.render.RenderUtil;
@@ -39,13 +44,14 @@ import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.SharedConstants; import net.minecraft.SharedConstants;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.ChatScreen; import net.minecraft.client.gui.screens.ChatScreen;
import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screen.TitleScreen; import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.client.gui.screen.ingame.AbstractCommandBlockScreen; import net.minecraft.client.gui.screens.inventory.AbstractCommandBlockEditScreen;
import net.minecraft.client.gui.screen.ingame.AbstractSignEditScreen; import net.minecraft.client.gui.screens.inventory.AbstractSignEditScreen;
import net.minecraft.client.gui.screen.ingame.AnvilScreen; import net.minecraft.client.gui.screens.inventory.AnvilScreen;
import net.minecraft.network.chat.Component;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@@ -54,11 +60,13 @@ import org.lwjgl.glfw.GLFW;
public class Sputnik implements ClientModInitializer { public class Sputnik implements ClientModInitializer {
// cosas necesarias
public static final Sputnik INSTANCE = new Sputnik(); public static final Sputnik INSTANCE = new Sputnik();
// cosas del mod
public static final EventBus EVENT_BUS = new EventBus(); public static final EventBus EVENT_BUS = new EventBus();
public static final Logger LOGGER = LogManager.getLogger(Sputnik.class); public static final Logger LOGGER = LogManager.getLogger(Sputnik.class);
public static MinecraftClient mc; public static final long LAUNCH_TIME = System.currentTimeMillis();
public static Minecraft mc;
// id y versión // id y versión
public static final String MOD_ID = "sputnik"; public static final String MOD_ID = "sputnik";
@@ -79,10 +87,12 @@ public class Sputnik implements ClientModInitializer {
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
mc = MinecraftClient.getInstance(); mc = Minecraft.getInstance();
ConfigManager.load();
VersionChecker.check(); ConfigManager.INSTANCE = new ConfigManager();
ConfigManager.INSTANCE.load();
VersionChecker.INSTANCE.check();
EVENT_BUS.subscribe(this); EVENT_BUS.subscribe(this);
@@ -93,9 +103,17 @@ public class Sputnik implements ClientModInitializer {
EVENT_BUS.subscribe(MiscUtil.class); EVENT_BUS.subscribe(MiscUtil.class);
EVENT_BUS.subscribe(NetworkUtil.class); EVENT_BUS.subscribe(NetworkUtil.class);
EVENT_BUS.subscribe(RenderUtil.class); EVENT_BUS.subscribe(RenderUtil.class);
EVENT_BUS.subscribe(RotationUtil.class);
EVENT_BUS.subscribe(BlockIterator.class);
Lists.init(); Lists.init();
Textures.init();
HudRenderer.INSTANCE = new HudRenderer();
HudEditor.INSTANCE = new HudEditor();
MacroManager.INSTANCE = new MacroManager();
ProfileManager.INSTANCE = new ProfileManager();
CapeManager.INSTANCE = new CapeManager(); CapeManager.INSTANCE = new CapeManager();
EVENT_BUS.post(new LoadCapeManagerEvent()); EVENT_BUS.post(new LoadCapeManagerEvent());
@@ -106,11 +124,18 @@ public class Sputnik implements ClientModInitializer {
CommandManager.INSTANCE = new CommandManager(); CommandManager.INSTANCE = new CommandManager();
EVENT_BUS.post(new LoadCommandManagerEvent()); EVENT_BUS.post(new LoadCommandManagerEvent());
ClickGUI.INSTANCE = new ClickGUI(); FriendManager.INSTANCE = new FriendManager();
HudEditorScreen.INSTANCE = new HudEditorScreen(); EVENT_BUS.post(new LoadFriendManagerEvent());
EVENT_BUS.post(new LoadClickGUIEvent());
mc.execute(HudRenderer::init); mc.execute(() -> {
Textures.init();
ClickGui.INSTANCE = new ClickGui();
HudEditorScreen.INSTANCE = new HudEditorScreen();
EVENT_BUS.post(new LoadClickGuiEvent());
HudRenderer.INSTANCE.init();
});
Runtime.getRuntime().addShutdownHook(new Thread(() -> { Runtime.getRuntime().addShutdownHook(new Thread(() -> {
EVENT_BUS.post(new ShutdownEvent()); EVENT_BUS.post(new ShutdownEvent());
@@ -119,30 +144,31 @@ public class Sputnik implements ClientModInitializer {
// se ejecuta cada tick, es decir, 20 veces por segundo // se ejecuta cada tick, es decir, 20 veces por segundo
public void onTick() { public void onTick() {
if (!settingsApplied if (!settingsApplied && !ConfigManager.INSTANCE.hasLoaded()) {
&& ConfigManager.getConfig() != null settingsApplied = ConfigManager.INSTANCE.applyConfig();
&& !ConfigManager.hasLoaded()) {
ConfigManager.applyConfig();
settingsApplied = true;
} }
ModuleManager.INSTANCE.getEnabledModules().forEach(Module::onTick); ModuleManager.INSTANCE.getEnabledModules().forEach(Module::onTick);
mc.getWindow().setTitle(ClientSettingsFrame.guiSettings.windowTitle.getValue()); mc.getWindow().setTitle(ClientSettingsPanel.clientSettings.windowTitle.getValue());
ConfigManager.INSTANCE.tickAutosave();
} }
// se ejecuta cada vez que se presiona una tecla // se ejecuta cada vez que se presiona una tecla
public void onInput(int key, int action) { public void onInput(int key, int action) {
if (key == GLFW.GLFW_KEY_F3) return; // prevenir interrumpir combos de F3 // prevenir interrumpir combos de F3
if (key == GLFW.GLFW_KEY_F3) return;
boolean anyFocused = isAnySettingButtonFocused() || ClickGUI.INSTANCE.getSearchBar().isFocused(); boolean anyFocused = isAnySettingButtonFocused() || ClickGui.INSTANCE.isCapturingInput();
ClickGUI.INSTANCE.setAnyFocused(anyFocused); ClickGui.INSTANCE.setAnyFocused(anyFocused);
if (action != GLFW.GLFW_RELEASE) { if (action != GLFW.GLFW_RELEASE) {
if (BindCommand.onKeyPress(key)) return; if (BindCommand.onKeyPress(key)) return;
if (action == GLFW.GLFW_PRESS && MacroManager.INSTANCE.onKeyPress(key)) return;
if (action == GLFW.GLFW_PRESS) { if (action == GLFW.GLFW_PRESS) {
handleModuleToggle(key, anyFocused); handleModuleToggle(key, anyFocused);
handleClickGUIKey(key, anyFocused); handleClickGuiKey(key, anyFocused);
handleHudEditorKey(key, anyFocused); handleHudEditorKey(key, anyFocused);
} }
// solo reenviar teclas del teclado // solo reenviar teclas del teclado
@@ -156,19 +182,25 @@ public class Sputnik implements ClientModInitializer {
// verifica si algún botón de ajustes está escuchando // verifica si algún botón de ajustes está escuchando
private boolean isAnySettingButtonFocused() { private boolean isAnySettingButtonFocused() {
for (SettingsFrame sf : ClickGUI.INSTANCE.getSettingsFrames()) if (ClickGui.INSTANCE == null) return false;
for (SettingsPanel sf : ClickGui.INSTANCE.getSettingsPanels())
for (Button sb : sf.getButtons()) for (Button sb : sf.getButtons())
if ((sb instanceof BindButton b && b.isFocused()) || (sb instanceof TextButton t && t.isFocused())) if ((sb instanceof BindButton b && b.isFocused())
|| (sb instanceof TextButton t && t.isFocused())
|| (sb instanceof FileButton f && f.isFocused())
) {
return true; return true;
}
return false; return false;
} }
// se ocupa de la lógica de encendido y apagado de los módulos // se ocupa de la lógica de encendido y apagado de los módulos
private void handleModuleToggle(int key, boolean anyFocused) { private void handleModuleToggle(int key, boolean anyFocused) {
if (mc.currentScreen != null && mc.currentScreen != ClickGUI.INSTANCE) return; if (mc.gui.screen() != null && mc.gui.screen() != ClickGui.INSTANCE) return;
for (Module module : ModuleManager.INSTANCE.getModules()) { for (Module module : ModuleManager.INSTANCE.getModules()) {
if (key != module.getKey() || anyFocused || KeyUtil.isKeyDown(GLFW.GLFW_KEY_F3)) continue; // evitar interrumpir combinaciones de teclas del F3 // evitar interrumpir combinaciones de teclas del F3
if (key != module.getKey() || anyFocused || KeyUtil.isKeyDown(GLFW.GLFW_KEY_F3)) continue;
if (module.shouldToggleOnBindRelease() && !module.isEnabled()) if (module.shouldToggleOnBindRelease() && !module.isEnabled())
module.setEnabled(true); module.setEnabled(true);
@@ -179,49 +211,50 @@ public class Sputnik implements ClientModInitializer {
// se ocupa de hacer los botones de ajustes que lo necesiten escuchar teclas // se ocupa de hacer los botones de ajustes que lo necesiten escuchar teclas
private void handleSettingButtonsKey(int key, int action) { private void handleSettingButtonsKey(int key, int action) {
for (SettingsFrame sf : ClickGUI.INSTANCE.getSettingsFrames()) { for (SettingsPanel sf : ClickGui.INSTANCE.getSettingsPanels()) {
for (SettingButton<?> sb : sf.getButtons()) { for (SettingButton<?> sb : sf.getButtons()) {
if (sb instanceof BindButton bb) bb.onKey(key, action); if (sb instanceof BindButton bb) bb.onKey(key, action);
if (sb instanceof FileButton fb) fb.onKey(key, action);
if (sb instanceof TextButton tb) tb.onKey(key, action); if (sb instanceof TextButton tb) tb.onKey(key, action);
} }
} }
} }
// maneja la lógica de apertura de la interfaz // maneja la lógica de apertura de la interfaz
private void handleClickGUIKey(int key, boolean anyFocused) { private void handleClickGuiKey(int key, boolean anyFocused) {
if (key != ClientSettingsFrame.guiSettings.getKey() || anyFocused || isOnTypingScreen()) if (key != ClientSettingsPanel.clientSettings.getKey() || anyFocused || isOnTypingScreen())
return; return;
if (mc.currentScreen != ClickGUI.INSTANCE) { if (mc.gui.screen() != ClickGui.INSTANCE) {
prevScreen = mc.currentScreen; prevScreen = mc.gui.screen();
mc.setScreen(ClickGUI.INSTANCE); mc.gui.setScreen(ClickGui.INSTANCE);
} else { } else {
ClickGUI.INSTANCE.close(); ClickGui.INSTANCE.onClose();
mc.setScreen(prevScreen); mc.gui.setScreen(prevScreen);
} }
} }
// manejar la lógica de apertura del editor de elementos del hud, con la misma lógica que handleClickGUIKey() // manejar la lógica de apertura del editor de elementos del hud, con la misma lógica que handleClickGuiKey()
private void handleHudEditorKey(int key, boolean anyFocused) { private void handleHudEditorKey(int key, boolean anyFocused) {
HUD hud = ModuleManager.INSTANCE.getModuleByClass(HUD.class); Hud hud = ModuleManager.INSTANCE.getModuleByClass(Hud.class);
if (key != hud.editorKey.getValue() || anyFocused || isOnTypingScreen()) return; if (key != hud.editorKey.getValue() || anyFocused || isOnTypingScreen()) return;
if (mc.currentScreen != HudEditorScreen.INSTANCE) { if (mc.gui.screen() != HudEditorScreen.INSTANCE) {
if (!hud.isEnabled()) { if (!hud.isEnabled()) {
ChatUtil.warn("HUD está desactivado, lumbreras"); ChatUtil.warn(Component.translatable("sputnik.hud.disabled"));
return; return;
} }
prevScreen = mc.currentScreen; prevScreen = mc.gui.screen();
mc.setScreen(HudEditorScreen.INSTANCE); mc.gui.setScreen(HudEditorScreen.INSTANCE);
} else { } else {
HudEditorScreen.INSTANCE.close(); HudEditorScreen.INSTANCE.onClose();
mc.setScreen(prevScreen); mc.gui.setScreen(prevScreen);
} }
} }
// se ocupa de apagar los módulos que tengan configurado hacerlo tras soltar su tecla // se ocupa de apagar los módulos que tengan configurado hacerlo tras soltar su tecla
private void handleModuleRelease(int key) { private void handleModuleRelease(int key) {
if (mc.currentScreen != null && mc.currentScreen != ClickGUI.INSTANCE) return; if (mc.gui.screen() != null && mc.gui.screen() != ClickGui.INSTANCE) return;
for (Module module : ModuleManager.INSTANCE.getEnabledModules()) for (Module module : ModuleManager.INSTANCE.getEnabledModules())
if (module.shouldToggleOnBindRelease() && key == module.getKey()) if (module.shouldToggleOnBindRelease() && key == module.getKey())
@@ -229,26 +262,38 @@ public class Sputnik implements ClientModInitializer {
} }
private boolean isOnTypingScreen() { private boolean isOnTypingScreen() {
return mc.currentScreen instanceof ChatScreen return mc.gui.screen() instanceof ChatScreen
|| mc.currentScreen instanceof AnvilScreen || mc.gui.screen() instanceof AnvilScreen
|| mc.currentScreen instanceof AbstractSignEditScreen || mc.gui.screen() instanceof AbstractSignEditScreen
|| mc.currentScreen instanceof AbstractCommandBlockScreen; || mc.gui.screen() instanceof AbstractCommandBlockEditScreen;
} }
private boolean keysVerified = false;
@EventListener
// notificar al usuario de que hay una actualización disponible, si la hay // notificar al usuario de que hay una actualización disponible, si la hay
public void onSetScreen(OpenScreenEvent event) { public void onSetScreen(OpenScreenEvent event) {
if (VersionChecker.shouldShowScreen && event.getScreen() instanceof TitleScreen) { if (event.getScreen() instanceof TitleScreen) {
// aquí el idioma ya está cargado, que en onInitializeClient todavía no
if (!keysVerified) {
keysVerified = true;
I18nUtil.verifyKeys();
}
if (VersionChecker.INSTANCE.shouldShowScreen) {
event.cancel(); event.cancel();
VersionChecker.shouldShowScreen = false; VersionChecker.INSTANCE.shouldShowScreen = false;
mc.execute(() -> mc.currentScreen = new UpdateScreen()); mc.execute(() -> mc.gui.setScreen(new UpdateScreen()));
}
} }
} }
@EventListener
public void onStop(ShutdownEvent event) { public void onStop(ShutdownEvent event) {
ConfigManager.save(); ConfigManager.INSTANCE.saveOnShutdown();
} }
public static String getVersionName() { public static String getVersionName() {
return MOD_ID + "_v" + MOD_VERSION + "_" + SharedConstants.getGameVersion().name(); return MOD_ID + "_v" + MOD_VERSION + "_" + SharedConstants.getCurrentVersion().name();
} }
} }
+12 -11
View File
@@ -1,26 +1,27 @@
package me.retucio.sputnik.cape; package me.retucio.sputnik.cape;
import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.Sputnik;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.Minecraft;
import net.minecraft.client.texture.NativeImage; import com.mojang.blaze3d.platform.NativeImage;
import net.minecraft.client.texture.NativeImageBackedTexture; import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.util.Identifier; import net.minecraft.resources.Identifier;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
public class Cape { public class Cape {
private final MinecraftClient mc = MinecraftClient.getInstance(); private final Minecraft mc = Minecraft.getInstance();
private final String name; private final String name;
private final Identifier id; private final Identifier id;
private NativeImage img; private NativeImage img;
private NativeImageBackedTexture texture; private DynamicTexture texture;
public Cape(String name) { public Cape(String name) {
this.name = name; this.name = name;
this.id = Identifier.of(Sputnik.MOD_ID, "textures/capes/" + name + ".png"); this.id = Identifier.fromNamespaceAndPath(Sputnik.MOD_ID, "textures/capes/" + name + ".png");
} }
public void load() { public void load() {
@@ -28,7 +29,7 @@ public class Cape {
} }
public NativeImage loadTexture(Identifier id) { public NativeImage loadTexture(Identifier id) {
try (InputStream inputStream = mc.getResourceManager().getResource(id).get().getInputStream()) { try (InputStream inputStream = mc.getResourceManager().getResource(id).get().open()) {
return NativeImage.read(inputStream); return NativeImage.read(inputStream);
} catch (IOException e) { } catch (IOException e) {
Sputnik.LOGGER.error("ups: {}", e.getMessage()); Sputnik.LOGGER.error("ups: {}", e.getMessage());
@@ -37,8 +38,8 @@ public class Cape {
} }
public void register() { public void register() {
texture = new NativeImageBackedTexture(null, img); texture = new DynamicTexture(null, img);
mc.getTextureManager().registerTexture(id, texture); mc.getTextureManager().register(id, texture);
img = null; img = null;
} }
@@ -50,7 +51,7 @@ public class Cape {
return id; return id;
} }
public NativeImageBackedTexture getTexture() { public DynamicTexture getTexture() {
return texture; return texture;
} }
@@ -4,60 +4,67 @@ import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.Minecraft;
import net.minecraft.command.CommandRegistryAccess; import net.minecraft.commands.CommandBuildContext;
import net.minecraft.command.CommandSource; import net.minecraft.commands.Commands;
import net.minecraft.registry.BuiltinRegistries; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.server.command.CommandManager; import net.minecraft.data.registries.VanillaRegistries;
import net.minecraft.network.chat.Component;
import java.util.List; import java.util.List;
public abstract class Command { public abstract class Command {
// utilizar los "registries" ya existentes de Brigadier para el autocompletado // utilizar los "registries" ya existentes de Brigadier para el autocompletado
protected static final CommandRegistryAccess REGISTRY_ACCESS = CommandManager.createRegistryAccess(BuiltinRegistries.createWrapperLookup()); protected static final CommandBuildContext REGISTRY_ACCESS = Commands.createValidationContext(VanillaRegistries.createLookup());
protected static final int SUCCESS = com.mojang.brigadier.Command.SINGLE_SUCCESS; protected static final int SUCCESS = com.mojang.brigadier.Command.SINGLE_SUCCESS;
protected static final MinecraftClient mc = MinecraftClient.getInstance(); protected static final Minecraft mc = Minecraft.getInstance();
private final String name, description; private final String name, descriptionKey;
private final List<String> aliases; private final List<String> aliases;
public Command(String name, String description, String... aliases) { // la descripción se guarda como clave de traducción, no como texto ya resuelto: los comandos
// se construyen antes de que se cargue el idioma, así que hay que traducirla al mostrarla
public Command(String name, String descriptionKey, String... aliases) {
this.name = name; this.name = name;
this.description = description; this.descriptionKey = descriptionKey;
this.aliases = List.of(aliases); this.aliases = List.of(aliases);
} }
// métodos "helper" para reducir el "boilerplate" // métodos "helper" para reducir el "boilerplate"
protected static <T> RequiredArgumentBuilder<CommandSource, T> argument(final String name, final ArgumentType<T> type) { protected static <T> RequiredArgumentBuilder<SharedSuggestionProvider, T> argument(final String name, final ArgumentType<T> type) {
return RequiredArgumentBuilder.argument(name, type); return RequiredArgumentBuilder.argument(name, type);
} }
protected static LiteralArgumentBuilder<CommandSource> literal(final String name) { protected static LiteralArgumentBuilder<SharedSuggestionProvider> literal(final String name) {
return LiteralArgumentBuilder.literal(name); return LiteralArgumentBuilder.literal(name);
} }
// para registrar comandos // para registrar comandos
public final void registerTo(CommandDispatcher<CommandSource> dispatcher) { public final void registerTo(CommandDispatcher<SharedSuggestionProvider> dispatcher) {
register(dispatcher, name); register(dispatcher, name);
for (String alias : aliases) register(dispatcher, alias); for (String alias : aliases) register(dispatcher, alias);
} }
public void register(CommandDispatcher<CommandSource> dispatcher, String name) { public void register(CommandDispatcher<SharedSuggestionProvider> dispatcher, String name) {
LiteralArgumentBuilder<CommandSource> builder = LiteralArgumentBuilder.literal(name); LiteralArgumentBuilder<SharedSuggestionProvider> builder = LiteralArgumentBuilder.literal(name);
build(builder); build(builder);
dispatcher.register(builder); dispatcher.register(builder);
} }
public abstract void build(LiteralArgumentBuilder<CommandSource> builder); public abstract void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder);
public String getName() { public String getName() {
return name; return name;
} }
public String getDescription() { public String getDescriptionKey() {
return description; return descriptionKey;
}
public Component getDescription() {
return Component.translatable(descriptionKey);
} }
public List<String> getAliases() { public List<String> getAliases() {
@@ -5,8 +5,8 @@ import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.command.commands.*; import me.retucio.sputnik.command.commands.*;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.Minecraft;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
@@ -22,9 +22,9 @@ public class CommandManager {
private String prefix = "$"; private String prefix = "$";
public static final List<Command> commands = new ArrayList<>(); public static final List<Command> commands = new ArrayList<>();
public static final CommandDispatcher<CommandSource> dispatcher = new CommandDispatcher<>(); public static final CommandDispatcher<SharedSuggestionProvider> dispatcher = new CommandDispatcher<>();
private static final MinecraftClient mc = MinecraftClient.getInstance(); private static final Minecraft mc = Minecraft.getInstance();
public CommandManager() { public CommandManager() {
registerCommands(); registerCommands();
@@ -36,12 +36,18 @@ public class CommandManager {
addCommand(new ConfigCommand()); addCommand(new ConfigCommand());
addCommand(new CopyScreenshotCommand()); addCommand(new CopyScreenshotCommand());
addCommand(new EnderChestCommand()); addCommand(new EnderChestCommand());
addCommand(new FriendCommand());
addCommand(new GarbageCleanerCommand()); addCommand(new GarbageCleanerCommand());
addCommand(new HelpCommand());
addCommand(new MacroCommand());
addCommand(new PanoramaCommand());
addCommand(new PeekCommand()); addCommand(new PeekCommand());
addCommand(new PluginsCommand()); addCommand(new PluginsCommand());
addCommand(new PrefixCommand()); addCommand(new PrefixCommand());
addCommand(new ProfileCommand());
addCommand(new PurgeCommand()); addCommand(new PurgeCommand());
addCommand(new RepeatCommand()); addCommand(new RepeatCommand());
addCommand(new RepeatWaitCommand());
addCommand(new SaveScreenshotCommand()); addCommand(new SaveScreenshotCommand());
addCommand(new SendCommand()); addCommand(new SendCommand());
addCommand(new SpectateCommand()); addCommand(new SpectateCommand());
@@ -67,7 +73,7 @@ public class CommandManager {
} }
public static void dispatch(String message) throws CommandSyntaxException { public static void dispatch(String message) throws CommandSyntaxException {
dispatcher.execute(message, Objects.requireNonNull(mc.getNetworkHandler()).getCommandSource()); dispatcher.execute(message, Objects.requireNonNull(mc.getConnection()).getSuggestionsProvider());
} }
public static Command getCommandByName(String name) { public static Command getCommandByName(String name) {
@@ -9,9 +9,9 @@ import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import me.retucio.sputnik.util.EntityUtil; import me.retucio.sputnik.util.EntityUtil;
import net.minecraft.entity.Entity; import net.minecraft.network.chat.Component;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.entity.Entity;
import net.minecraft.text.Text; import net.minecraft.world.entity.player.Player;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@@ -25,7 +25,7 @@ public class EntityArgumentType implements ArgumentType<Entity> {
public static final EntityArgumentType INSTANCE = new EntityArgumentType(); public static final EntityArgumentType INSTANCE = new EntityArgumentType();
private static final DynamicCommandExceptionType NO_SUCH_ENTITY = new DynamicCommandExceptionType( private static final DynamicCommandExceptionType NO_SUCH_ENTITY = new DynamicCommandExceptionType(
name -> Text.literal("Entidad \"" + name + "\" no encontrada")); name -> Component.translatable("sputnik.args.entity.notfound", name));
private static final Collection<String> EXAMPLES = List.of( private static final Collection<String> EXAMPLES = List.of(
"AdlerHitdolf", "AdlerHitdolf",
@@ -42,7 +42,7 @@ public class EntityArgumentType implements ArgumentType<Entity> {
public Entity parse(StringReader reader) throws CommandSyntaxException { public Entity parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString(); String argument = reader.readString();
if (mc.world == null) if (mc.level == null)
throw NO_SUCH_ENTITY.create(argument); throw NO_SUCH_ENTITY.create(argument);
if (argument.startsWith("@")) if (argument.startsWith("@"))
@@ -83,13 +83,13 @@ public class EntityArgumentType implements ArgumentType<Entity> {
} }
private Entity findNearestPlayer() throws CommandSyntaxException { private Entity findNearestPlayer() throws CommandSyntaxException {
PlayerEntity nearest = null; Player nearest = null;
double nearestDistance = Double.MAX_VALUE; double nearestDistance = Double.MAX_VALUE;
for (PlayerEntity player : mc.world.getPlayers()) { for (Player player : mc.level.players()) {
if (player == mc.player) continue; if (player == mc.player) continue;
double distance = mc.player.squaredDistanceTo(player); double distance = mc.player.distanceToSqr(player);
if (distance < nearestDistance) { if (distance < nearestDistance) {
nearestDistance = distance; nearestDistance = distance;
nearest = player; nearest = player;
@@ -103,7 +103,7 @@ public class EntityArgumentType implements ArgumentType<Entity> {
} }
private Entity findRandomPlayer() throws CommandSyntaxException { private Entity findRandomPlayer() throws CommandSyntaxException {
List<PlayerEntity> players = new ArrayList<>(mc.world.getPlayers()); List<Player> players = new ArrayList<>(mc.level.players());
if (players.isEmpty()) throw NO_SUCH_ENTITY.create("@r"); if (players.isEmpty()) throw NO_SUCH_ENTITY.create("@r");
players.remove(mc.player); players.remove(mc.player);
@@ -116,8 +116,8 @@ public class EntityArgumentType implements ArgumentType<Entity> {
private Entity parseEntitySelector(String selector) throws CommandSyntaxException { private Entity parseEntitySelector(String selector) throws CommandSyntaxException {
if (selector.equals("@e")) { if (selector.equals("@e")) {
List<Entity> entities = new ArrayList<>(); List<Entity> entities = new ArrayList<>();
for (Entity entity : mc.world.getEntities()) for (Entity entity : mc.level.getEntities().getAll())
if (!(entity instanceof PlayerEntity)) if (!(entity instanceof Player))
entities.add(entity); entities.add(entity);
if (entities.isEmpty()) throw NO_SUCH_ENTITY.create(selector); if (entities.isEmpty()) throw NO_SUCH_ENTITY.create(selector);
@@ -141,8 +141,8 @@ public class EntityArgumentType implements ArgumentType<Entity> {
private Entity findEntityByType(String type) throws CommandSyntaxException { private Entity findEntityByType(String type) throws CommandSyntaxException {
List<Entity> matchingEntities = new ArrayList<>(); List<Entity> matchingEntities = new ArrayList<>();
for (Entity entity : mc.world.getEntities()) { for (Entity entity : mc.level.getEntities().getAll()) {
String entityType = entity.getType().getTranslationKey(); String entityType = entity.getType().getDescriptionId();
if (entityType.contains(type.toLowerCase().replace("minecraft:", ""))) if (entityType.contains(type.toLowerCase().replace("minecraft:", "")))
matchingEntities.add(entity); matchingEntities.add(entity);
} }
@@ -153,7 +153,7 @@ public class EntityArgumentType implements ArgumentType<Entity> {
double nearestDistance = Double.MAX_VALUE; double nearestDistance = Double.MAX_VALUE;
for (Entity entity : matchingEntities) { for (Entity entity : matchingEntities) {
double distance = mc.player.squaredDistanceTo(entity); double distance = mc.player.distanceToSqr(entity);
if (distance < nearestDistance) { if (distance < nearestDistance) {
nearestDistance = distance; nearestDistance = distance;
nearest = entity; nearest = entity;
@@ -164,15 +164,15 @@ public class EntityArgumentType implements ArgumentType<Entity> {
} }
private Entity findEntityByUUID(UUID uuid) throws CommandSyntaxException { private Entity findEntityByUUID(UUID uuid) throws CommandSyntaxException {
for (Entity entity : mc.world.getEntities()) for (Entity entity : mc.level.getEntities().getAll())
if (entity.getUuid().equals(uuid)) if (entity.getUUID().equals(uuid))
return entity; return entity;
throw NO_SUCH_ENTITY.create(uuid.toString()); throw NO_SUCH_ENTITY.create(uuid.toString());
} }
private PlayerEntity findPlayerByName(String name) throws CommandSyntaxException { private Player findPlayerByName(String name) throws CommandSyntaxException {
for (PlayerEntity player : mc.world.getPlayers()) for (Player player : mc.level.players())
if (player.getName().getString().equalsIgnoreCase(name)) if (player.getName().getString().equalsIgnoreCase(name))
return player; return player;
@@ -181,8 +181,8 @@ public class EntityArgumentType implements ArgumentType<Entity> {
@Override @Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) { public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
if (mc.world != null) if (mc.level != null)
for (PlayerEntity player : mc.world.getPlayers()) for (Player player : mc.level.players())
builder.suggest(player.getName().getString()); builder.suggest(player.getName().getString());
@@ -192,7 +192,7 @@ public class EntityArgumentType implements ArgumentType<Entity> {
Entity lookingAt = EntityUtil.getEntityPlayerIsLookingAt(); Entity lookingAt = EntityUtil.getEntityPlayerIsLookingAt();
if (lookingAt != null) if (lookingAt != null)
builder.suggest(lookingAt.getUuid().toString()); builder.suggest(lookingAt.getUUID().toString());
return builder.buildFuture(); return builder.buildFuture();
} }
@@ -7,15 +7,16 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.text.Text; import net.minecraft.network.chat.Component;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class EnumArgumentType<T extends Enum<T>> implements ArgumentType<T> { public class EnumArgumentType<T extends Enum<T>> implements ArgumentType<T> {
private static final DynamicCommandExceptionType NO_SUCH_TYPE = new DynamicCommandExceptionType((value) -> Text.of(" argumento inválido: " + value)); private static final DynamicCommandExceptionType NO_SUCH_TYPE = new DynamicCommandExceptionType(
(value) -> Component.translatable("sputnik.args.enum.invalid", value));
private final T[] values; private final T[] values;
@@ -33,14 +34,16 @@ public class EnumArgumentType<T extends Enum<T>> implements ArgumentType<T> {
public T parse(StringReader reader) throws CommandSyntaxException { public T parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString(); String argument = reader.readString();
// ignorando mayúsculas: listSuggestions sugiere en minúsculas, así que comparar
// con equals() hacía que las propias sugerencias no se pudieran parsear
return Arrays.stream(this.values) return Arrays.stream(this.values)
.filter((value) -> value.toString().equals(argument)) .filter((value) -> value.toString().equalsIgnoreCase(argument))
.findFirst() .findFirst()
.orElseThrow(() -> NO_SUCH_TYPE.create(argument) .orElseThrow(() -> NO_SUCH_TYPE.create(argument)
); );
} }
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) { public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return CommandSource.suggestMatching(Arrays.stream(this.values).map(e -> e.toString().toLowerCase()), builder); return SharedSuggestionProvider.suggest(Arrays.stream(this.values).map(e -> e.toString().toLowerCase()), builder);
} }
} }
@@ -10,8 +10,8 @@ import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import me.retucio.sputnik.module.Module; import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.util.MiscUtil; import me.retucio.sputnik.util.MiscUtil;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.text.Text; import net.minecraft.network.chat.Component;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@@ -22,23 +22,23 @@ public class ModuleArgumentType implements ArgumentType<Module> {
public static final ModuleArgumentType INSTANCE = new ModuleArgumentType(); public static final ModuleArgumentType INSTANCE = new ModuleArgumentType();
private static final DynamicCommandExceptionType UNKNOWN_MODULE = new DynamicCommandExceptionType( private static final DynamicCommandExceptionType UNKNOWN_MODULE = new DynamicCommandExceptionType(
name -> Text.literal("módulo \"" + name + "\" no encontrado")); name -> Component.translatable("sputnik.args.module.notfound", name));
private static final Collection<String> examples = ModuleManager.INSTANCE.getModules() private static final Collection<String> examples = ModuleManager.INSTANCE.getModules()
.stream().limit(3).map(module -> module.getName().replace(" ", "_")).toList(); .stream().limit(3).map(module -> module.getDisplayName().replace(" ", "_")).toList();
@Override @Override
public Module parse(StringReader reader) throws CommandSyntaxException { public Module parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString().replace("_", " "); String argument = reader.readString().replace("_", " ");
Module module = ModuleManager.INSTANCE.getModuleByName(argument); Module module = ModuleManager.INSTANCE.getModuleByDisplayName(argument);
if (module == null) throw UNKNOWN_MODULE.create(argument); if (module == null) throw UNKNOWN_MODULE.create(argument);
return module; return module;
} }
@Override @Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) { public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return CommandSource.suggestMatching(ModuleManager.INSTANCE.getModules() return SharedSuggestionProvider.suggest(ModuleManager.INSTANCE.getModules()
.stream().map(module -> MiscUtil.removeAccentMarks(module.getName().replace(" ", "_"))), builder); .stream().map(module -> MiscUtil.removeAccentMarks(module.getDisplayName().replace(" ", "_"))), builder);
} }
@Override @Override
@@ -46,7 +46,7 @@ public class ModuleArgumentType implements ArgumentType<Module> {
return examples; return examples;
} }
public static Module get(CommandContext<CommandSource> ctx) { public static Module get(CommandContext<SharedSuggestionProvider> ctx) {
return ctx.getArgument("módulo", Module.class); return ctx.getArgument("módulo", Module.class);
} }
} }
@@ -7,9 +7,9 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.network.chat.Component;
import net.minecraft.text.Text; import net.minecraft.world.entity.player.Player;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -18,21 +18,22 @@ import java.util.concurrent.CompletableFuture;
import static me.retucio.sputnik.Sputnik.mc; import static me.retucio.sputnik.Sputnik.mc;
// para autocompletar nombres de jugadores // para autocompletar nombres de jugadores
public class PlayerArgumentType implements ArgumentType<PlayerEntity> { public class PlayerArgumentType implements ArgumentType<Player> {
public static final PlayerArgumentType INSTANCE = new PlayerArgumentType(); public static final PlayerArgumentType INSTANCE = new PlayerArgumentType();
private static final DynamicCommandExceptionType NO_SUCH_PLAYER = new DynamicCommandExceptionType(name -> Text.literal("Player with name " + name + " doesn't exist.")); private static final DynamicCommandExceptionType NO_SUCH_PLAYER = new DynamicCommandExceptionType(
name -> Component.translatable("sputnik.args.player.notfound", name));
private static final Collection<String> EXAMPLES = List.of("retucio", "prepucio", "postpucio", "pucio", "adler hitdolf"); private static final Collection<String> EXAMPLES = List.of("retucio", "prepucio", "postpucio", "pucio", "adler hitdolf");
private PlayerArgumentType() {} private PlayerArgumentType() {}
@Override @Override
public PlayerEntity parse(StringReader reader) throws CommandSyntaxException { public Player parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString(); String argument = reader.readString();
PlayerEntity playerEntity = null; Player playerEntity = null;
for (PlayerEntity p : mc.world.getPlayers()) for (Player p : mc.level.players())
if (p.getName().getString().equalsIgnoreCase(argument)) { if (p.getName().getString().equalsIgnoreCase(argument)) {
playerEntity = p; playerEntity = p;
break; break;
@@ -45,7 +46,7 @@ public class PlayerArgumentType implements ArgumentType<PlayerEntity> {
@Override @Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) { public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return CommandSource.suggestMatching(mc.world.getPlayers().stream().map( return SharedSuggestionProvider.suggest(mc.level.players().stream().map(
abstractClientPlayerEntity -> abstractClientPlayerEntity.getName().getString()), builder); abstractClientPlayerEntity -> abstractClientPlayerEntity.getName().getString()), builder);
} }
@@ -54,8 +55,8 @@ public class PlayerArgumentType implements ArgumentType<PlayerEntity> {
return EXAMPLES; return EXAMPLES;
} }
public static PlayerEntity get(CommandContext<?> context) { public static Player get(CommandContext<?> context) {
return context.getArgument("jugador", PlayerEntity.class); return context.getArgument("jugador", Player.class);
} }
} }
@@ -5,17 +5,16 @@ import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.command.args.ModuleArgumentType; import me.retucio.sputnik.command.args.ModuleArgumentType;
import me.retucio.sputnik.module.Module; import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.KeySetting; import me.retucio.sputnik.module.setting.settings.KeySetting;
import me.retucio.sputnik.ui.widgets.frames.settings.ClientSettingsFrame; import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel;
import me.retucio.sputnik.util.ChatUtil; import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.KeyUtil; import me.retucio.sputnik.util.KeyUtil;
import net.minecraft.client.input.KeyInput; import net.minecraft.ChatFormatting;
import net.minecraft.client.option.KeyBinding; import net.minecraft.client.KeyMapping;
import net.minecraft.client.resource.language.I18n; import net.minecraft.client.input.KeyEvent;
import net.minecraft.command.CommandSource; import net.minecraft.client.resources.language.I18n;
import net.minecraft.text.Text; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.util.Formatting; import net.minecraft.network.chat.Component;
import javax.swing.text.JTextComponent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -25,23 +24,26 @@ public class BindCommand extends Command {
private static Module listeningModule = null; private static Module listeningModule = null;
public BindCommand() { public BindCommand() {
super("bind", "asigna una tecla a un módulo", "keybind"); super("bind", "sputnik.commands.bind.desc", "keybind", "kb", "tecla");
} }
@Override @Override
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder builder
.then(argument("módulo", ModuleArgumentType.INSTANCE) .then(argument("módulo", ModuleArgumentType.INSTANCE)
.executes(ctx -> { .executes(ctx -> {
Module module = ctx.getArgument("módulo", Module.class); Module module = ctx.getArgument("módulo", Module.class);
listeningModule = module; listeningModule = module;
ChatUtil.info("presiona una tecla para asignarla al módulo " + Formatting.GREEN + module.getName()); ChatUtil.info(Component.translatable("sputnik.commands.bind.presskey", ChatFormatting.GREEN + module.getDisplayName()));
return SUCCESS; return SUCCESS;
}) })
.then(literal("reset").executes(ctx -> { .then(literal("reset").executes(ctx -> {
Module module = ctx.getArgument("módulo", Module.class); Module module = ctx.getArgument("módulo", Module.class);
module.getBind().reset(); module.getBind().reset();
ChatUtil.info("tecla para el módulo " + Formatting.GREEN + module.getName() + " restablecida a " + Formatting.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; return SUCCESS;
})) }))
); );
@@ -51,17 +53,18 @@ public class BindCommand extends Command {
if (listeningModule == null) return false; if (listeningModule == null) return false;
KeySetting bind = listeningModule.getBind(); KeySetting bind = listeningModule.getBind();
if (ClientSettingsFrame.guiSettings.multipleKeybinds.getValue()) { // solo hay que avisar de que la tecla está cogida cuando NO se permiten teclas repetidas.
List<KeyBinding> keys = new ArrayList<>(List.of(mc.options.allKeys)); // 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)); keys.removeAll(List.of(mc.options.debugKeys));
for (KeyBinding kb : keys) { for (KeyMapping kb : keys) {
boolean keyAlreadyBound = kb.matchesKey(new KeyInput(key, 0, 0)); if (kb.matches(new KeyEvent(key, 0, 0))) {
boolean allowMultiple = ClientSettingsFrame.guiSettings.multipleKeybinds.getValue(); // la comilla iba dentro del I18n.get, así que buscaba una clave inexistente
ChatUtil.warn(Component.translatable("sputnik.commands.bind.keytaken",
if (keyAlreadyBound && !allowMultiple) { ChatFormatting.GREEN + "\"" + I18n.get(kb.getName()) + "\""));
ChatUtil.warn("esa tecla ya está cogida por "
+ Formatting.GREEN + "\"" + I18n.translate(kb.getId()) + "\"");
listeningModule = null; listeningModule = null;
return true; return true;
} }
@@ -70,10 +73,10 @@ public class BindCommand extends Command {
if (bind != null) bind.setValue(key); if (bind != null) bind.setValue(key);
ChatUtil.info( ChatUtil.info(Component.translatable("sputnik.commands.bind.keybound",
Text.of("la tecla " + Formatting.AQUA + KeyUtil.getKeyName(key) + Formatting.RESET + ChatFormatting.AQUA + KeyUtil.getKeyName(key) + ChatFormatting.RESET,
" ha sido asignada al módulo " + Formatting.GREEN + listeningModule.getName()) ChatFormatting.GREEN + listeningModule.getDisplayName()
); ));
listeningModule = null; listeningModule = null;
return true; return true;
@@ -4,28 +4,29 @@ import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.command.args.EnumArgumentType; import me.retucio.sputnik.command.args.EnumArgumentType;
import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; import net.minecraft.client.gui.screens.inventory.ContainerScreen;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.screen.slot.SlotActionType; import net.minecraft.world.inventory.ContainerInput;
// https://cdn.discordapp.com/attachments/1456079799045980400/1457154232737206375/dupersunited-public-addon-1.21.11-3.1.3.jar?ex=69735be1&is=69720a61&hm=77fb6faee49762e0a812661f0d98931e89cb725db711c45722249d0efe3b91e4& // https://cdn.discordapp.com/attachments/1456079799045980400/1457154232737206375/dupersunited-public-addon-1.21.11-3.1.3.jar?ex=69735be1&is=69720a61&hm=77fb6faee49762e0a812661f0d98931e89cb725db711c45722249d0efe3b91e4&
public class ClickSlotCommand extends Command { public class ClickSlotCommand extends Command {
public ClickSlotCommand() { public ClickSlotCommand() {
super("clickslot", "simula un clic a un slot"); super("clickslot", "sputnik.commands.clickslot.desc", "cs");
} }
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.then(argument("slot", IntegerArgumentType.integer()) builder.then(argument("slot", IntegerArgumentType.integer())
.then(argument("botón", IntegerArgumentType.integer()) .then(argument("botón", IntegerArgumentType.integer())
.then(argument("acción", EnumArgumentType.enumArgument(SlotActionType.SWAP)) .then(argument("acción", EnumArgumentType.enumArgument(ContainerInput.SWAP))
.executes(context -> { .executes(context -> {
int slot = context.getArgument("slot", Integer.class), button = context.getArgument("botón", Integer.class); int slot = context.getArgument("slot", Integer.class), button = context.getArgument("botón", Integer.class);
SlotActionType action = context.getArgument("acción", SlotActionType.class); ContainerInput action = context.getArgument("acción", ContainerInput.class);
Screen screen = mc.currentScreen; Screen screen = mc.gui.screen();
int syncId = (screen instanceof GenericContainerScreen gcScreen) ? gcScreen.getScreenHandler().syncId : 0; int syncId = (screen instanceof ContainerScreen cScreen) ? cScreen.getMenu().containerId : 0;
mc.interactionManager.clickSlot(syncId, slot, button, action, mc.player); if (mc.gameMode == null) return 0;
mc.gameMode.handleContainerInput(syncId, slot, button, action, mc.player);
return SUCCESS; return SUCCESS;
}) })
) )
@@ -5,19 +5,19 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import me.retucio.sputnik.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.command.args.ModuleArgumentType; import me.retucio.sputnik.command.args.ModuleArgumentType;
import me.retucio.sputnik.ui.screen.ClickGUI; import me.retucio.sputnik.ui.screen.ClickGui;
import me.retucio.sputnik.ui.widgets.frames.SettingsFrame; import me.retucio.sputnik.ui.widgets.panels.SettingsPanel;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import me.retucio.sputnik.module.Module; import me.retucio.sputnik.module.Module;
public class ConfigCommand extends Command { public class ConfigCommand extends Command {
public ConfigCommand() { public ConfigCommand() {
super("config", "abre el marco de ajustes de un módulo", "ajustes", "settings"); super("config", "sputnik.commands.config.desc", "ajustes", "settings");
} }
@Override @Override
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder builder
.then(argument("módulo", ModuleArgumentType.INSTANCE) .then(argument("módulo", ModuleArgumentType.INSTANCE)
.executes(ctx -> { .executes(ctx -> {
@@ -38,29 +38,29 @@ public class ConfigCommand extends Command {
} }
private void openSf(CommandContext<CommandSource> ctx) { private void openSf(CommandContext<SharedSuggestionProvider> ctx) {
Module module = ctx.getArgument("módulo", Module.class); Module module = ctx.getArgument("módulo", Module.class);
mc.setScreen(ClickGUI.INSTANCE); mc.gui.setScreen(ClickGui.INSTANCE);
if (ClickGUI.INSTANCE.isSettingsFrameOpen(module)) return; if (ClickGui.INSTANCE.isSettingsPanelOpen(module)) return;
SettingsFrame sf = ClickGUI.INSTANCE.getSfOfModule(module); SettingsPanel sf = ClickGui.INSTANCE.getSettingsPanelOfModule(module);
int x = mc.getWindow().getScaledWidth() / 2 - sf.getW() / 2; int x = mc.getWindow().getGuiScaledWidth() / 2 - sf.getW() / 2;
int y = 67; int y = 67;
ClickGUI.INSTANCE.openSettingsFrame(module, x, y); ClickGui.INSTANCE.openSettingsPanel(module, x, y);
} }
private void openSfWithPos(CommandContext<CommandSource> ctx, int x, int y) { private void openSfWithPos(CommandContext<SharedSuggestionProvider> ctx, int x, int y) {
Module module = ctx.getArgument("módulo", Module.class); Module module = ctx.getArgument("módulo", Module.class);
mc.setScreen(ClickGUI.INSTANCE); mc.gui.setScreen(ClickGui.INSTANCE);
if (ClickGUI.INSTANCE.isSettingsFrameOpen(module)) { if (ClickGui.INSTANCE.isSettingsPanelOpen(module)) {
SettingsFrame sf = ClickGUI.INSTANCE.getSfOfModule(module); SettingsPanel sf = ClickGui.INSTANCE.getSettingsPanelOfModule(module);
sf.setX(x); sf.setX(x);
sf.setY(y); sf.setY(y);
} }
ClickGUI.INSTANCE.openSettingsFrame(module, x, y); ClickGui.INSTANCE.openSettingsPanel(module, x, y);
} }
} }
@@ -4,17 +4,17 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.misc.ScreenshotPlus; import me.retucio.sputnik.module.modules.misc.ScreenshotPlus;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
// se usa principalmente para el módulo ScreenshotPlus, pero también se puede usar sin él supongo // se usa principalmente para el módulo ScreenshotPlus, pero también se puede usar sin él supongo
public class CopyScreenshotCommand extends Command { public class CopyScreenshotCommand extends Command {
public CopyScreenshotCommand() { public CopyScreenshotCommand() {
super("copiarcaptura", "copia la captura más reciente al portapapeles"); super("copiarcaptura", "sputnik.commands.copyscreenshot.desc", "copyscreenshot");
} }
@Override @Override
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.executes(ctx -> { builder.executes(ctx -> {
ScreenshotPlus screenshotPlus = ModuleManager.INSTANCE.getModuleByClass(ScreenshotPlus.class); ScreenshotPlus screenshotPlus = ModuleManager.INSTANCE.getModuleByClass(ScreenshotPlus.class);
@@ -1,12 +1,14 @@
package me.retucio.sputnik.command.commands; package me.retucio.sputnik.command.commands;
import com.github.retucio.neutrino.EventListener;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.event.TickEvent; import me.retucio.sputnik.event.TickEvent;
import me.retucio.sputnik.ui.screen.PreviewScreen; import me.retucio.sputnik.ui.screen.PreviewScreen;
import me.retucio.sputnik.util.ChatUtil; import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.InventoryUtil; import me.retucio.sputnik.util.InventoryUtil;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
// accede a los contenidos de tu enderchest // accede a los contenidos de tu enderchest
public class EnderChestCommand extends Command { public class EnderChestCommand extends Command {
@@ -14,16 +16,16 @@ public class EnderChestCommand extends Command {
private boolean shouldOpenEchest = false; private boolean shouldOpenEchest = false;
public EnderChestCommand() { public EnderChestCommand() {
super("echest", "muestra el contenido de tu enderchest (solo de ver)", "ec", "enderchest"); super("echest", "sputnik.commands.echest.desc", "ec", "enderchest");
} }
@Override @Override
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.executes(ctx -> { builder.executes(ctx -> {
mc.execute(() -> { mc.execute(() -> {
if (mc.player == null) return; if (mc.player == null) return;
if (InventoryUtil.getEchestInv() == null) { if (InventoryUtil.getEchestInv() == null) {
ChatUtil.warn("necesitas abrir un enderchest una vez primero"); ChatUtil.warn(Component.translatable( "sputnik.commands.echest.openfirst"));
return; return;
} }
@@ -34,11 +36,12 @@ public class EnderChestCommand extends Command {
}); });
} }
@EventListener
public void onTick(TickEvent.Post event) { public void onTick(TickEvent.Post event) {
if (shouldOpenEchest) { if (shouldOpenEchest) {
shouldOpenEchest = false; shouldOpenEchest = false;
if (InventoryUtil.getEchestInv() != null) if (InventoryUtil.getEchestInv() != null)
mc.setScreen(new PreviewScreen(InventoryUtil.getEchestInv(), null)); mc.gui.setScreen(new PreviewScreen(InventoryUtil.getEchestInv(), null));
} }
} }
} }
@@ -0,0 +1,60 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.command.args.PlayerArgumentType;
import me.retucio.sputnik.friend.Friend;
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", "sputnik.commands.friend.desc", "amigo", "f");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder
.then(literal("add")
.then(argument("amigo", PlayerArgumentType.INSTANCE)
.executes(ctx -> {
Player friend = ctx.getArgument("amigo", Player.class);
FriendManager.INSTANCE.add(friend.getUUID());
ChatUtil.info(Component.translatable(
"sputnik.commands.friend.added",
ChatFormatting.GREEN + friend.getName().getString() + ChatFormatting.RESET
));
return SUCCESS;
})
)
)
.then(literal("remove")
.then(argument("amigo", PlayerArgumentType.INSTANCE)
.executes(ctx -> {
Player friend = ctx.getArgument("amigo", Player.class);
FriendManager.INSTANCE.remove(friend.getUUID());
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 + Component.translatable("sputnik.commands.friend.list").getString());
for (Friend friend : FriendManager.INSTANCE.getFriends()) {
ChatUtil.info("- " + friend.getName());
}
return SUCCESS;
})
);
}
}
@@ -3,20 +3,20 @@ package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.util.ChatUtil; import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.text.Text; import net.minecraft.network.chat.Component;
public class GarbageCleanerCommand extends Command { public class GarbageCleanerCommand extends Command {
public GarbageCleanerCommand() { public GarbageCleanerCommand() {
super("gc", "limpia la ram mediante el recolector de basura (gc)", "garbagecollect"); super("gc", "sputnik.commands.gc.desc", "garbagecollect");
} }
@Override @Override
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.executes(context -> { builder.executes(ctx -> {
System.gc(); System.gc();
ChatUtil.info(Text.of("basura recolectada")); ChatUtil.info(Component.translatable("sputnik.commands.gc.collected"));
return SUCCESS; return SUCCESS;
}); });
} }
@@ -0,0 +1,28 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.command.CommandManager;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
public class HelpCommand extends Command {
public HelpCommand() {
super("help", Component.translatable("sputnik.commands.help.desc").getString(), "?", "ayuda", "h");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.executes(ctx -> {
for (Command command : CommandManager.commands) {
ChatUtil.info(CommandManager.INSTANCE.getPrefix()
+ ChatFormatting.BOLD + command.getName() + ": "
+ ChatFormatting.RESET + ChatFormatting.GRAY + command.getDescription().getString());
}
return SUCCESS;
});
}
}
@@ -0,0 +1,114 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.macro.Macro;
import me.retucio.sputnik.macro.MacroManager;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.KeyUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.util.List;
public class MacroCommand extends Command {
private static final SuggestionProvider<SharedSuggestionProvider> MACROS = (ctx, builder) ->
SharedSuggestionProvider.suggest(
MacroManager.INSTANCE.getMacros().stream().map(Macro::getName), builder);
public MacroCommand() {
super("macro", "sputnik.commands.macro.desc", "macros");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder
.executes(ctx -> {
list();
return SUCCESS;
})
.then(literal("list").executes(ctx -> {
list();
return SUCCESS;
}))
.then(literal("add")
.then(argument("nombre", StringArgumentType.word()).executes(ctx -> {
String name = ctx.getArgument("nombre", String.class);
if (MacroManager.INSTANCE.add(new Macro(name)))
ChatUtil.info(Component.translatable("sputnik.commands.macro.added",
ChatFormatting.AQUA + name));
else
ChatUtil.error(Component.translatable("sputnik.commands.macro.exists", name));
return SUCCESS;
})))
.then(literal("remove")
.then(argument("nombre", StringArgumentType.word()).suggests(MACROS).executes(ctx -> {
Macro macro = get(ctx.getArgument("nombre", String.class));
if (macro == null) return SUCCESS;
MacroManager.INSTANCE.remove(macro);
ChatUtil.info(Component.translatable("sputnik.commands.macro.removed",
ChatFormatting.AQUA + macro.getName()));
return SUCCESS;
})))
.then(literal("line")
.then(argument("nombre", StringArgumentType.word()).suggests(MACROS)
.then(argument("línea", StringArgumentType.greedyString()).executes(ctx -> {
Macro macro = get(ctx.getArgument("nombre", String.class));
if (macro == null) return SUCCESS;
String line = ctx.getArgument("línea", String.class);
macro.getLines().add(line);
MacroManager.INSTANCE.save();
ChatUtil.info(Component.translatable("sputnik.commands.macro.lineadded",
ChatFormatting.AQUA + macro.getName()));
return SUCCESS;
}))))
.then(literal("clear")
.then(argument("nombre", StringArgumentType.word()).suggests(MACROS).executes(ctx -> {
Macro macro = get(ctx.getArgument("nombre", String.class));
if (macro == null) return SUCCESS;
macro.getLines().clear();
MacroManager.INSTANCE.save();
ChatUtil.info(Component.translatable("sputnik.commands.macro.cleared",
ChatFormatting.AQUA + macro.getName()));
return SUCCESS;
})))
.then(literal("run")
.then(argument("nombre", StringArgumentType.word()).suggests(MACROS).executes(ctx -> {
Macro macro = get(ctx.getArgument("nombre", String.class));
if (macro != null) macro.run();
return SUCCESS;
})));
}
private static Macro get(String name) {
Macro macro = MacroManager.INSTANCE.getByName(name);
if (macro == null) ChatUtil.error(Component.translatable("sputnik.commands.macro.notfound", name));
return macro;
}
private static void list() {
List<Macro> macros = MacroManager.INSTANCE.getMacros();
if (macros.isEmpty()) {
ChatUtil.info(Component.translatable("sputnik.commands.macro.none"));
return;
}
ChatUtil.info(Component.translatable("sputnik.commands.macro.list", macros.size()));
for (Macro macro : macros)
ChatUtil.addMessage(ChatFormatting.GRAY + " - " + macro.getName()
+ ChatFormatting.DARK_GRAY + " [" + KeyUtil.getKeyName(macro.getKey()) + "] "
+ macro.getLines().size());
}
}
@@ -0,0 +1,265 @@
package me.retucio.sputnik.command.commands;
import com.github.retucio.neutrino.EventListener;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.event.TickEvent;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.MiscUtil;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.SharedConstants;
import net.minecraft.WorldVersion;
import net.minecraft.client.Screenshot;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.metadata.pack.PackFormat;
import net.minecraft.sounds.SoundEvents;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.arguments.StringArgumentType;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NonNull;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.ThreadLocalRandom;
/**
* literalmente robado de <a href="https://github.com/0xTas/stardust/blob/main/src/main/java/dev/stardust/commands/Panorama.java">aquí</a>
*/
public class PanoramaCommand extends Command {
private int i = -1;
int timer = 10;
boolean ready = false;
boolean warming = false;
private float prevYaw;
private float prevPitch;
private int prevWidth;
private int prevHeight;
private boolean prevHudHidden;
@Nullable Path dir = null;
public PanoramaCommand() {
super("panorama", "sputnik.commands.panorama.desc");
}
private void takeScreenshot() {
if (dir == null) return;
Screenshot.grab(
dir.toFile(),
"panorama_" + i + ".png",
mc.gameRenderer.mainRenderTarget(),
1, msg -> Sputnik.LOGGER.info(msg.getString())
);
i++;
warming = false;
}
private void begin(String name) {
if (mc.player == null) return;
dir = FabricLoader.getInstance().getGameDir().resolve(Sputnik.MOD_ID + "/panoramas/" + name);
dir.toFile().mkdirs();
i = 0;
warming = false;
prevYaw = mc.player.getYRot();
prevPitch = mc.player.getXRot();
prevWidth = mc.getWindow().getWidth();
prevHeight = mc.getWindow().getHeight();
prevHudHidden = mc.gui.hud.isHidden();
mc.getWindow().setWidth(4096);
mc.getWindow().setHeight(4096);
mc.gameRenderer.mainRenderTarget().resize(4096, 4096);
}
private void assembleResourcePack() {
if (dir == null) return;
Path resourcePacks = FabricLoader.getInstance().getGameDir().resolve("resourcepacks");
Path screenshotsFolder = FabricLoader.getInstance().getGameDir().resolve(Sputnik.MOD_ID + "/panoramas/" + dir.getFileName() + "/screenshots");
Path customBaseFolder = resourcePacks.resolve(dir.getFileName());
Path customPackFolder = customBaseFolder.resolve("assets/minecraft/textures/gui/title/background/");
customPackFolder.toFile().mkdirs();
String mcMeta =
"""
{
"pack":
{
"min_format": [65, 0],
"max_format": [%d, 0],
"description": "%s"
}
}
""".formatted(
getPackVersion(),
Component.translatable("sputnik.commands.panorama.packdesc").getString()
);
try {
int luckyNumber = ThreadLocalRandom.current().nextInt(6);
Files.walkFileTree(screenshotsFolder, new SimpleFileVisitor<>() {
public @NonNull FileVisitResult visitFile(@NonNull Path file, @NonNull BasicFileAttributes attrs) {
MiscUtil.copy(file.toFile(), customPackFolder.resolve(file.getFileName()).toFile());
// pack.png random de alguna de los screenshots
if (file.getFileName().toString().contains(String.valueOf(luckyNumber))) {
MiscUtil.copy(file.toFile(), customBaseFolder.resolve("pack.png").toFile());
}
return FileVisitResult.CONTINUE;
}
});
File packMeta = customBaseFolder.resolve("pack.mcmeta").toFile();
if (!packMeta.createNewFile()) {
ChatUtil.error(Component.translatable("sputnik.commands.panorama.errorassemble").getString());
return;
}
Files.write(packMeta.toPath(), mcMeta.getBytes());
// eliminar el efecto vignette feo de cojones que usa Minecraft por defecto
BufferedImage transparentOverlay = new BufferedImage(16, 128, BufferedImage.TYPE_INT_ARGB);
File overlayImage = customPackFolder.resolve("panorama_overlay.png").toFile();
ImageIO.write(transparentOverlay, "PNG", overlayImage);
} catch (Exception e) {
ChatUtil.error("error de i/o");
Sputnik.LOGGER.error(e);
}
ready = false;
if (mc.player != null) {
mc.player.playSound(SoundEvents.AMETHYST_BLOCK_CHIME, 1f, 1f);
ChatUtil.info(Component.translatable("sputnik.commands.panorama.ready").getString());
}
}
@EventListener
private void onTick(TickEvent.Post event) {
if (mc.player == null || dir == null) return;
if (timer > 0) {
--timer;
return;
} else {
timer = 10;
}
if (ready) {
assembleResourcePack();
}
if (i < 0) return;
switch (i) {
case 0 -> {
if (!warming) {
mc.gameRenderer.mainCamera().enablePanoramicMode();
mc.gameRenderer.setRenderBlockOutline(false);
mc.execute(() -> {
synchronized (mc.levelRenderer) {
mc.levelRenderer.notifyAll();
}
});
if (!mc.gui.hud.isHidden()) mc.gui.hud.toggle();
mc.player.setYRot(prevYaw);
mc.player.setXRot(0f);
warming = true;
} else takeScreenshot();
}
case 1 -> {
if (!warming) {
mc.player.setYRot((prevYaw + 90f) % 360f);
mc.player.setXRot(0f);
warming = true;
} else takeScreenshot();
}
case 2 -> {
if (!warming) {
mc.player.setYRot((prevYaw + 180f) % 360f);
mc.player.setXRot(0f);
warming = true;
} else takeScreenshot();
}
case 3 -> {
if (!warming) {
mc.player.setYRot((prevYaw - 90f) % 360f);
mc.player.setXRot(0f);
warming = true;
} else takeScreenshot();
}
case 4 -> {
if (!warming) {
mc.player.setYRot(prevYaw);
mc.player.setXRot(-90f);
warming = true;
} else takeScreenshot();
}
default -> {
if (!warming) {
mc.player.setYRot(prevYaw);
mc.player.setXRot(90f);
warming = true;
} else {
takeScreenshot();
i = -1;
mc.player.setYRot(prevYaw);
mc.player.setXRot(prevPitch);
mc.gameRenderer.mainCamera().disablePanoramicMode();
mc.gameRenderer.setRenderBlockOutline(true);
mc.getWindow().setWidth(prevWidth);
mc.getWindow().setHeight(prevHeight);
if (mc.gui.hud.isHidden() != prevHudHidden) mc.gui.hud.toggle();
mc.gameRenderer.mainRenderTarget().resize(prevWidth, prevHeight);
mc.execute(() -> {
synchronized (mc.levelRenderer) {
mc.levelRenderer.notifyAll();
}
});
timer = 100; // esperar a que las capturas se escriban
ready = true;
ChatUtil.info(Component.translatable("sputnik.commands.panorama.finalizing").getString());
}
}
}
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.then(
argument("name", StringArgumentType.word()).executes(ctx -> {
String name = ctx.getArgument("name", String.class);
begin(name);
return SUCCESS;
})
);
}
public static int getPackVersion() {
WorldVersion version = SharedConstants.getCurrentVersion();
PackFormat format = version.packVersion(PackType.CLIENT_RESOURCES);
return format.major();
}
}
@@ -4,18 +4,20 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.command.args.PlayerArgumentType; import me.retucio.sputnik.command.args.PlayerArgumentType;
import me.retucio.sputnik.util.MiscUtil; import me.retucio.sputnik.util.MiscUtil;
import net.minecraft.client.gui.screen.ingame.InventoryScreen; import net.minecraft.client.gui.screens.inventory.InventoryScreen;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import java.awt.*;
// comando que debería de mostrar el inventario de un jugador, pero por cómo funcionan los servers, solo te deja ver el ítem que tenga en la mano // comando que debería de mostrar el inventario de un jugador, pero por cómo funcionan los servers, solo te deja ver el ítem que tenga en la mano
public class PeekCommand extends Command { public class PeekCommand extends Command {
public PeekCommand() { public PeekCommand() {
super("inventario", "cotillea el inventario de otro jugador", "invsee", "peek", "inv"); super("inventario", "sputnik.commands.peek.desc", "invsee", "peek", "inv");
} }
@Override @Override
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder builder
.then(argument("jugador", PlayerArgumentType.INSTANCE) .then(argument("jugador", PlayerArgumentType.INSTANCE)
.executes(ctx -> { .executes(ctx -> {
@@ -9,10 +9,11 @@ import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.event.network.PacketEvent; import me.retucio.sputnik.event.network.PacketEvent;
import me.retucio.sputnik.util.ChatUtil; import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.client.network.ClientCommandSource; import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.multiplayer.ClientSuggestionProvider;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.packet.s2c.play.CommandTreeS2CPacket; import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundCommandsPacket;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@@ -23,20 +24,20 @@ public class PluginsCommand extends Command {
private boolean scanning = false; private boolean scanning = false;
public PluginsCommand() { public PluginsCommand() {
super("plugins", "busca los plugins que tenga el server", "pl", "serverplugins"); super("plugins", "sputnik.commands.plugins.desc", "pl", "serverplugins");
Sputnik.EVENT_BUS.subscribe(this); Sputnik.EVENT_BUS.subscribe(this);
} }
@Override @Override
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.executes(context -> { builder.executes(context -> {
if (mc.isIntegratedServerRunning()) { if (mc.hasSingleplayerServer()) {
ChatUtil.error("estás en un solo jugador"); ChatUtil.error(Component.translatable("sputnik.commands.plugins.singleplayer"));
return SUCCESS; return SUCCESS;
} }
if (scanning) { if (scanning) {
ChatUtil.error("calmaaa"); ChatUtil.error(Component.translatable("sputnik.commands.plugins.calmdown"));
return SUCCESS; return SUCCESS;
} }
@@ -51,16 +52,16 @@ public class PluginsCommand extends Command {
private void findNamespaces() { private void findNamespaces() {
try { try {
ClientPlayNetworkHandler networkHandler = mc.getNetworkHandler(); ClientPacketListener networkHandler = mc.getConnection();
if (networkHandler == null || networkHandler.getCommandDispatcher() == null) { if (networkHandler == null) {
return; return;
} }
RootCommandNode<ClientCommandSource> root = networkHandler.getCommandDispatcher().getRoot(); RootCommandNode<ClientSuggestionProvider> root = networkHandler.getCommands().getRoot();
root.getChildren().forEach(this::checkNodeForPlugins); root.getChildren().forEach(this::checkNodeForPlugins);
if (!plugins.isEmpty()) { if (!plugins.isEmpty()) {
ChatUtil.info(plugins.size() + " namespace(s) encontrado(s) de comandos:"); ChatUtil.info(Component.translatable("sputnik.commands.plugins.namespacesfound", plugins.size()));
plugins.stream() plugins.stream()
.sorted() .sorted()
.forEach(plugin -> ChatUtil.info(" - " + plugin)); .forEach(plugin -> ChatUtil.info(" - " + plugin));
@@ -69,11 +70,11 @@ public class PluginsCommand extends Command {
displayResults(); displayResults();
} catch (Exception e) { } catch (Exception e) {
ChatUtil.error("error analizando comandos: " + e.getMessage()); ChatUtil.error(Component.translatable("sputnik.commands.plugins.erroranalyzingcommands", e.getMessage()));
} }
} }
private void checkNodeForPlugins(CommandNode<ClientCommandSource> node) { private void checkNodeForPlugins(CommandNode<ClientSuggestionProvider> node) {
String name = node.getName(); String name = node.getName();
if (name.contains(":")) { if (name.contains(":")) {
@@ -92,18 +93,18 @@ public class PluginsCommand extends Command {
private void onPacketReceive(PacketEvent.Receive event) { private void onPacketReceive(PacketEvent.Receive event) {
if (!scanning) return; if (!scanning) return;
if (event.getPacket() instanceof CommandTreeS2CPacket) { if (event.getPacket() instanceof ClientboundCommandsPacket) {
findNamespaces(); findNamespaces();
} }
} }
private void displayResults() { private void displayResults() {
if (!plugins.isEmpty()) { if (!plugins.isEmpty()) {
ChatUtil.info(plugins.size() + " plugin(s) encontrado(s)"); ChatUtil.info(Component.translatable("sputnik.commands.plugins.pluginsfound", plugins.size()));
plugins.stream().sorted() plugins.stream().sorted()
.forEach(plugin -> ChatUtil.info(" - " + plugin)); .forEach(plugin -> ChatUtil.info(" - " + plugin));
} else { } else {
ChatUtil.warn("sin plugins (probablemente)"); ChatUtil.warn(Component.translatable("sputnik.commands.plugins.noplugins"));
} }
scanning = false; scanning = false;
@@ -3,26 +3,26 @@ package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.ui.widgets.frames.settings.ClientSettingsFrame; import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel;
import me.retucio.sputnik.util.ChatUtil; import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.command.CommandSource; import net.minecraft.ChatFormatting;
import net.minecraft.text.Text; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.util.Formatting; import net.minecraft.network.chat.Component;
// también se puede hacer desde los ajustes de la interfaz // también se puede hacer desde los ajustes de la interfaz
public class PrefixCommand extends Command { public class PrefixCommand extends Command {
public PrefixCommand() { public PrefixCommand() {
super("prefijo", "cambia el prefijo de los comandos", "prefix"); super("prefijo", "sputnik.commands.prefix.desc", "prefix");
} }
@Override @Override
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder builder
.then(argument("prefijo", StringArgumentType.word()).executes(ctx -> { .then(argument("prefijo", StringArgumentType.word()).executes(ctx -> {
String prefix = ctx.getArgument("prefijo", String.class); String prefix = ctx.getArgument("prefijo", String.class);
ClientSettingsFrame.guiSettings.commandPrefix.setValue(prefix); ClientSettingsPanel.clientSettings.commandPrefix.setValue(prefix);
ChatUtil.info(Text.literal("prefijo cambiado a " + Formatting.AQUA + prefix)); ChatUtil.info(Component.translatable("sputnik.commands.prefix.changed", ChatFormatting.AQUA + prefix));
return SUCCESS; return SUCCESS;
})); }));
} }
@@ -0,0 +1,92 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.config.ConfigManager;
import me.retucio.sputnik.config.ProfileManager;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.util.List;
public class ProfileCommand extends Command {
private static final SuggestionProvider<SharedSuggestionProvider> PROFILES = (ctx, builder) ->
SharedSuggestionProvider.suggest(ProfileManager.INSTANCE.getProfileNames(), builder);
public ProfileCommand() {
super("perfil", "sputnik.commands.profile.desc", "profile", "perfiles", "profiles");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder
.executes(ctx -> {
list();
return SUCCESS;
})
.then(literal("list").executes(ctx -> {
list();
return SUCCESS;
}))
.then(literal("save")
.then(argument("nombre", StringArgumentType.word()).executes(ctx -> {
String name = ctx.getArgument("nombre", String.class);
if (!ProfileManager.isValidName(name)) {
ChatUtil.error(Component.translatable("sputnik.commands.profile.invalidname", name));
return SUCCESS;
}
if (ProfileManager.INSTANCE.save(name)) {
ConfigManager.INSTANCE.save();
ChatUtil.info(Component.translatable("sputnik.commands.profile.saved",
ChatFormatting.AQUA + name));
} else {
ChatUtil.error(Component.translatable("sputnik.commands.profile.savefailed", name));
}
return SUCCESS;
})))
.then(literal("load")
.then(argument("nombre", StringArgumentType.word()).suggests(PROFILES).executes(ctx -> {
String name = ctx.getArgument("nombre", String.class);
if (ProfileManager.INSTANCE.load(name))
ChatUtil.info(Component.translatable("sputnik.commands.profile.loaded",
ChatFormatting.AQUA + name));
else
ChatUtil.error(Component.translatable("sputnik.commands.profile.notfound", name));
return SUCCESS;
})))
.then(literal("delete")
.then(argument("nombre", StringArgumentType.word()).suggests(PROFILES).executes(ctx -> {
String name = ctx.getArgument("nombre", String.class);
if (ProfileManager.INSTANCE.delete(name))
ChatUtil.info(Component.translatable("sputnik.commands.profile.deleted",
ChatFormatting.AQUA + name));
else
ChatUtil.error(Component.translatable("sputnik.commands.profile.notfound", name));
return SUCCESS;
})));
}
private static void list() {
List<String> profiles = ProfileManager.INSTANCE.getProfileNames();
if (profiles.isEmpty()) {
ChatUtil.info(Component.translatable("sputnik.commands.profile.none"));
return;
}
String current = ProfileManager.INSTANCE.getCurrent();
ChatUtil.info(Component.translatable("sputnik.commands.profile.list", profiles.size()));
for (String profile : profiles)
ChatUtil.addMessage((profile.equals(current) ? ChatFormatting.GREEN : ChatFormatting.GRAY) + " - " + profile);
}
}
@@ -3,21 +3,21 @@ package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.util.interfaces.IChatHud; import me.retucio.sputnik.mixinterfaces.IChatComponent;
import net.minecraft.client.gui.hud.ChatHud; import net.minecraft.client.gui.components.ChatComponent;
import net.minecraft.client.gui.hud.ChatHudLine; import net.minecraft.client.multiplayer.chat.GuiMessage;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import java.util.List; import java.util.List;
public class PurgeCommand extends Command { public class PurgeCommand extends Command {
public PurgeCommand() { public PurgeCommand() {
super("purgar", "elimina mensajes del chat", "purge"); super("purgar", "sputnik.commands.purge.desc", "purge");
} }
@Override @Override
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.then(argument("cantidad", IntegerArgumentType.integer(1)) builder.then(argument("cantidad", IntegerArgumentType.integer(1))
.executes(context -> { .executes(context -> {
int amount = IntegerArgumentType.getInteger(context, "cantidad"); int amount = IntegerArgumentType.getInteger(context, "cantidad");
@@ -28,10 +28,10 @@ public class PurgeCommand extends Command {
} }
private void purgeMessages(int amount) { private void purgeMessages(int amount) {
ChatHud chatHud = mc.inGameHud.getChatHud(); ChatComponent chatHud = mc.gui.hud.getChat();
synchronized (chatHud) { synchronized (chatHud) {
List<ChatHudLine.Visible> visibleMessages = ((IChatHud) chatHud).sputnik$getVisibleMessages(); List<GuiMessage.Line> visibleMessages = ((IChatComponent) chatHud).sputnik$getVisibleMessages();
int toRemove = Math.min(amount, visibleMessages.size()); int toRemove = Math.min(amount, visibleMessages.size());
for (int i = 0; i < toRemove; i++) for (int i = 0; i < toRemove; i++)
visibleMessages.removeFirst(); visibleMessages.removeFirst();
@@ -5,16 +5,16 @@ import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.util.ChatUtil; import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
public class RepeatCommand extends Command { public class RepeatCommand extends Command {
public RepeatCommand() { public RepeatCommand() {
super("repetir", "repite un comando x veces", "repeat"); super("repetir", "sputnik.commands.repeat.desc", "repeat");
} }
@Override @Override
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder builder
.then(argument("veces", IntegerArgumentType.integer(1)) .then(argument("veces", IntegerArgumentType.integer(1))
.then(argument("comando", StringArgumentType.greedyString()) .then(argument("comando", StringArgumentType.greedyString())
@@ -0,0 +1,46 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.commands.SharedSuggestionProvider;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class RepeatWaitCommand extends Command {
public RepeatWaitCommand() {
super("repesperar", "sputnik.commands.repeatwait.desc", "repeatwait");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.then(argument("ms", IntegerArgumentType.integer(1))
.then(argument("veces", IntegerArgumentType.integer(1))
.then(argument("comando", StringArgumentType.greedyString())
.executes((context) -> {
int ms = context.getArgument("ms", Integer.class);
int times = context.getArgument("veces", Integer.class);
String cmd = context.getArgument("comando", String.class);
for (int i = 0; i < times; i++) {
int delay = i * ms;
int finalI = i;
new ScheduledThreadPoolExecutor(2).schedule(
() -> ChatUtil.info(
cmd.replaceAll("(?i)%index%",
String.valueOf(finalI))),
delay,
TimeUnit.MILLISECONDS
);
}
return SUCCESS;
})
)
)
);
}
}
@@ -5,7 +5,8 @@ import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.misc.ScreenshotPlus; import me.retucio.sputnik.module.modules.misc.ScreenshotPlus;
import me.retucio.sputnik.util.ChatUtil; import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.io.IOException; import java.io.IOException;
@@ -13,11 +14,11 @@ import java.io.IOException;
public class SaveScreenshotCommand extends Command { public class SaveScreenshotCommand extends Command {
public SaveScreenshotCommand() { public SaveScreenshotCommand() {
super("guardarcaptura", "guarda localmente la captura más reciente"); super("guardarcaptura", "sputnik.commands.savescreenshot.desc", "savescreenshot");
} }
@Override @Override
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.executes(ctx -> { builder.executes(ctx -> {
ScreenshotPlus screenshotPlus = ModuleManager.INSTANCE.getModuleByClass(ScreenshotPlus.class); ScreenshotPlus screenshotPlus = ModuleManager.INSTANCE.getModuleByClass(ScreenshotPlus.class);
@@ -26,7 +27,7 @@ public class SaveScreenshotCommand extends Command {
try { try {
screenshotPlus.saveScreenshot(); screenshotPlus.saveScreenshot();
} catch (IOException e) { } catch (IOException e) {
ChatUtil.error("no se pudo guardar la captura"); ChatUtil.error(Component.translatable("sputnik.commands.savescreenshot.error"));
} }
} }
}); });
@@ -3,50 +3,54 @@ package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.mixin.accessors.ClientPlayNetworkHandlerAccessor; import me.retucio.sputnik.mixin.accessors.ClientPacketListenerAccessor;
import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.encryption.NetworkEncryptionUtils; import net.minecraft.network.chat.LastSeenMessagesTracker;
import net.minecraft.network.message.LastSeenMessagesCollector; import net.minecraft.network.chat.MessageSignature;
import net.minecraft.network.message.MessageBody; import net.minecraft.network.chat.SignedMessageBody;
import net.minecraft.network.message.MessageSignatureData; import net.minecraft.network.protocol.game.ServerboundChatPacket;
import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket; import net.minecraft.util.Crypt;
import java.time.Instant; import java.time.Instant;
// uso principal: protección de coordenadas de ChatPlus, que ni siquiera funciona // uso principal: protección de coordenadas de ChatPlus
public class SendCommand extends Command { public class SendCommand extends Command {
public SendCommand() { public SendCommand() {
super("enviar", "manda un mensaje en el chat", "send"); super("enviar", "sputnik.commands.send.desc", "send");
} }
@Override @Override
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.then(argument("message", StringArgumentType.greedyString()).executes(context -> { builder.then(argument("message", StringArgumentType.greedyString()).executes(context -> {
String message = context.getArgument("message", String.class); String message = context.getArgument("message", String.class);
if (message != null && !message.isEmpty()) { if (message != null && !message.isEmpty()) {
Instant instant = Instant.now(); Instant instant = Instant.now();
long salt = NetworkEncryptionUtils.SecureRandomUtil.nextLong(); long salt = Crypt.SaltSupplier.getLong();
ClientPlayNetworkHandler handler = mc.getNetworkHandler(); ClientPacketListener handler = mc.getConnection();
// obtener últimos mensajes vistos para la firma // obtener últimos mensajes vistos para la firma
LastSeenMessagesCollector.LastSeenMessages lastSeenMessages = ClientPacketListenerAccessor listener = ((ClientPacketListenerAccessor) handler);
((ClientPlayNetworkHandlerAccessor) handler).getLastSeenMessagesCollector().collect(); if (listener == null) return 0;
LastSeenMessagesTracker tracker = ((ClientPacketListenerAccessor) handler).getLastSeenMessagesTracker();
LastSeenMessagesTracker.Update lastSeenMessages = tracker.generateAndApplyUpdate();
// empacar firma para un chat seguro // empacar firma
MessageSignatureData messageSignatureData = MessageSignature messageSignature = ((ClientPacketListenerAccessor) handler)
((ClientPlayNetworkHandlerAccessor) handler).getMessagePacker().pack( .getSignedMessageEncoder()
new MessageBody(message, instant, salt, lastSeenMessages.lastSeen()) .pack(new SignedMessageBody(
message, instant, salt, lastSeenMessages.lastSeen()
)
); );
// enviar paquete para enviar mensaje // enviar paquete para enviar mensaje
handler.sendPacket(new ChatMessageC2SPacket( handler.send(new ServerboundChatPacket(
message, message,
instant, instant,
salt, salt,
messageSignatureData, messageSignature,
lastSeenMessages.update() lastSeenMessages.update()
)); ));
} }
@@ -1,24 +1,27 @@
package me.retucio.sputnik.command.commands; package me.retucio.sputnik.command.commands;
import com.github.retucio.neutrino.EventListener;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.command.args.EntityArgumentType; import me.retucio.sputnik.command.args.EntityArgumentType;
import me.retucio.sputnik.event.input.KeyEvent; import me.retucio.sputnik.event.input.KeyEvent;
import me.retucio.sputnik.util.EntityUtil; import me.retucio.sputnik.util.EntityUtil;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.entity.Entity; import net.minecraft.world.entity.Entity;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import java.awt.*;
public class SpectateCommand extends Command { public class SpectateCommand extends Command {
public SpectateCommand() { public SpectateCommand() {
super("espectar", "te permite adquirir el punto de vista de una entidad", "spectate", "spec"); super("espectar", "sputnik.commands.spectate.desc", "spectate", "spec");
} }
@Override @Override
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder builder
.executes(ctx -> { // If no argument, use the entity player is looking at .executes(ctx -> {
mc.execute(() -> { mc.execute(() -> {
Entity target = EntityUtil.getEntityPlayerIsLookingAt(); Entity target = EntityUtil.getEntityPlayerIsLookingAt();
if (target != null) if (target != null)
@@ -37,8 +40,9 @@ public class SpectateCommand extends Command {
); );
} }
@EventListener
public void onKey(KeyEvent event) { public void onKey(KeyEvent event) {
if (mc.getCameraEntity() != mc.player && mc.currentScreen == null if (mc.getCameraEntity() != mc.player && mc.gui.screen() == null
&& event.getKey() == GLFW.GLFW_KEY_ESCAPE && event.getAction() != GLFW.GLFW_RELEASE ) { && event.getKey() == GLFW.GLFW_KEY_ESCAPE && event.getAction() != GLFW.GLFW_RELEASE ) {
event.cancel(); event.cancel();
mc.setCameraEntity(mc.player); mc.setCameraEntity(mc.player);
@@ -5,17 +5,19 @@ import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.command.args.ModuleArgumentType; import me.retucio.sputnik.command.args.ModuleArgumentType;
import me.retucio.sputnik.module.Module; import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import java.awt.*;
// porque usar el ratón es más lento que escribir, a menos que tengas toque con el teclado como yo // porque usar el ratón es más lento que escribir, a menos que tengas toque con el teclado como yo
public class ToggleCommand extends Command { public class ToggleCommand extends Command {
public ToggleCommand() { public ToggleCommand() {
super("alternar", "enciende / apaga módulos", "toggle"); super("alternar", "sputnik.commands.toggle.desc", "toggle");
} }
@Override @Override
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder builder
.then(literal("todos") .then(literal("todos")
.then(literal("on") .then(literal("on")
@@ -3,12 +3,9 @@ package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.retucio.sputnik.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.command.CommandManager;
import me.retucio.sputnik.ui.widgets.frames.settings.ClientSettingsFrame;
import me.retucio.sputnik.util.ChatUtil; import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.command.CommandSource; import net.minecraft.commands.SharedSuggestionProvider;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -19,11 +16,11 @@ public class WaitCommand extends Command {
private static final ScheduledExecutorService DELAYED_EXECUTOR = new ScheduledThreadPoolExecutor(2); private static final ScheduledExecutorService DELAYED_EXECUTOR = new ScheduledThreadPoolExecutor(2);
public WaitCommand() { public WaitCommand() {
super("wait", "espera un tiempo determinado antes de ejecutar otro comando", "esperar", "sleep", "delay"); super("wait", "sputnik.commands.wait.desc", "esperar", "sleep", "delay");
} }
@Override @Override
public void build(LiteralArgumentBuilder<CommandSource> builder) { public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder builder
.then(argument("delay", IntegerArgumentType.integer(0)) .then(argument("delay", IntegerArgumentType.integer(0))
.then(argument("comando", StringArgumentType.greedyString()) .then(argument("comando", StringArgumentType.greedyString())
@@ -1,14 +1,21 @@
package me.retucio.sputnik.config; package me.retucio.sputnik.config;
import me.retucio.sputnik.util.I18nUtil;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
public class BinarySerializer { public class BinarySerializer {
private static final int MAGIC_NUMBER = 0x53505554; // "SPUT" private static final int MAGIC_NUMBER = 0x53505554; // "SPUT"
private static final int VERSION = 1; private static final int VERSION = 3;
private static final int MIN_VERSION = 2;
private static final byte TYPE_NULL = 0; private static final byte TYPE_NULL = 0;
private static final byte TYPE_BOOLEAN = 1; private static final byte TYPE_BOOLEAN = 1;
@@ -19,25 +26,56 @@ public class BinarySerializer {
private static final byte TYPE_INT_ARRAY = 6; private static final byte TYPE_INT_ARRAY = 6;
private static final byte TYPE_FRAME_DATA = 7; private static final byte TYPE_FRAME_DATA = 7;
public static void writeConfig(ClientConfig config, File file) public static void writeConfig(ClientConfig config, File file) throws IOException {
throws IOException { byte[] bytes = toBytes(config);
try (DataOutputStream out = new DataOutputStream( writeBytes(bytes, file);
new BufferedOutputStream(new FileOutputStream(file)))) { }
// serializar a bytes en memoria
public static byte[] toBytes(ClientConfig config) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (DataOutputStream out = new DataOutputStream(baos)) {
writeConfigTo(config, out);
}
return baos.toByteArray();
}
// escribir bytes a disco (puede llamarse desde un hilo de fondo)
public static void writeBytes(byte[] bytes, File file) throws IOException {
// escribir a archivo temporal primero para evitar corrupción si el proceso muere a mitad
File temp = new File(file.getParent(), file.getName() + ".tmp");
try (FileOutputStream fos = new FileOutputStream(temp);
BufferedOutputStream out = new BufferedOutputStream(fos)) {
out.write(bytes);
out.flush();
fos.getFD().sync(); // forzar flush al disco antes de renombrar
}
// renombrar atómicamente
if (!temp.renameTo(file)) {
// renameTo puede fallar entre sistemas de archivos distintos
Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
// lógica de serialización compartida
private static void writeConfigTo(ClientConfig config, DataOutputStream out) throws IOException {
// cabezal // cabezal
out.writeInt(MAGIC_NUMBER); out.writeInt(MAGIC_NUMBER);
out.writeInt(VERSION); out.writeInt(VERSION);
// mapas // el resto
writeStringBooleanMap(out, config.moduleStates); writeStringBooleanMap(out, config.moduleStates);
writeSettingsMap(out, config.settings); writeSettingsMap(out, config.settings);
writeStringIntArrayMap(out, config.settingsFrames); writeStringIntArrayMap(out, config.settingsPanels);
writeExtendableFrames(out, config.extendableFrames); writeExtendableFrames(out, config.extendablePanels);
writeStringIntArrayMap(out, config.hudPositions); writeStringIntArrayMap(out, config.hudPositions);
writeStringBooleanMap(out, config.hudVisibilities); writeStringBooleanMap(out, config.hudVisibilities);
writeStringStringMap(out, config.hudImagePaths); writeStringStringMap(out, config.hudImagePaths);
writeStringStringMap(out, config.friends);
writeMacros(out, config.macros);
writeString(out, config.currentProfile);
// posición de la barra de búsqueda
if (config.searchBarPosition != null) { if (config.searchBarPosition != null) {
out.writeBoolean(true); out.writeBoolean(true);
out.writeInt(config.searchBarPosition[0]); out.writeInt(config.searchBarPosition[0]);
@@ -46,47 +84,67 @@ public class BinarySerializer {
out.writeBoolean(false); out.writeBoolean(false);
} }
} }
}
public static ClientConfig readConfig(File file) public static ClientConfig readConfig(File file)
throws IOException { throws IOException {
if (!file.exists() || file.length() == 0) { if (!file.exists() || file.length() == 0) {
throw new IOException("archivo de config. vacío o inexistente"); throw new IOException(I18nUtil.fmt("sputnik.config.error.empty",
"archivo de config. vacío o inexistente"));
} }
try (DataInputStream in = new DataInputStream( try (DataInputStream in = new DataInputStream(
new BufferedInputStream(new FileInputStream(file)))) { new BufferedInputStream(new FileInputStream(file)))) {
if (file.length() < 8) { if (file.length() < 8) {
throw new IOException("archivo demasiado pequeño para ser válido"); throw new IOException(I18nUtil.fmt("sputnik.config.error.toosmall",
"archivo demasiado pequeño para ser válido"));
} }
// verificar cabezal // verificar cabezal
int magic = in.readInt(); int magic = in.readInt();
if (magic != MAGIC_NUMBER) { if (magic != MAGIC_NUMBER) {
throw new IOException("formato de archivo inválido: " + magic); throw new IOException(I18nUtil.fmt("sputnik.config.error.invalidformat",
"formato de archivo inválido: %s", magic));
} }
int version = in.readInt(); int version = in.readInt();
if (version != VERSION) { if (version < MIN_VERSION || version > VERSION) {
throw new IOException("versión inválida: " + version); throw new IOException(I18nUtil.fmt("sputnik.config.error.invalidversion",
"versión inválida: %s", version));
} }
ClientConfig config = new ClientConfig(); // leer mapas. se leen a locales y la ClientConfig se construye al final: su
// constructor la suscribe al bus de eventos, así que crearla antes de terminar
// de leer dejaba una config. huérfana escuchando si el archivo estaba corrupto
Map<String, Boolean> moduleStates = readStringBooleanMap(in);
Map<String, Object> settings = readSettingsMap(in);
Map<String, int[]> settingsPanels = readStringIntArrayMap(in);
Map<String, ClientConfig.PanelData> extendablePanels = readExtendablePanels(in);
Map<String, int[]> hudPositions = readStringIntArrayMap(in);
Map<String, Boolean> hudVisibilities = readStringBooleanMap(in);
Map<String, String> hudImagePaths = readStringStringMap(in);
Map<String, String> friends = readStringStringMap(in);
// leer mapas List<ClientConfig.MacroData> macros = version >= 3 ? readMacros(in) : new ArrayList<>();
config.moduleStates = readStringBooleanMap(in); String currentProfile = version >= 3 ? readString(in) : "";
config.settings = readSettingsMap(in);
config.settingsFrames = readStringIntArrayMap(in);
config.extendableFrames = readExtendableFrames(in);
config.hudPositions = readStringIntArrayMap(in);
config.hudVisibilities = readStringBooleanMap(in);
config.hudImagePaths = readStringStringMap(in);
// leer pos. de la barra de búsqueda // leer pos. de la barra de búsqueda
if (in.readBoolean()) { int[] searchBarPosition = in.readBoolean()
config.searchBarPosition = new int[]{in.readInt(), in.readInt()}; ? new int[]{in.readInt(), in.readInt()}
} : null;
ClientConfig config = new ClientConfig();
config.moduleStates = moduleStates;
config.settings = settings;
config.settingsPanels = settingsPanels;
config.extendablePanels = extendablePanels;
config.hudPositions = hudPositions;
config.hudVisibilities = hudVisibilities;
config.hudImagePaths = hudImagePaths;
config.friends = friends;
config.macros = macros;
config.currentProfile = currentProfile == null ? "" : currentProfile;
if (searchBarPosition != null) config.searchBarPosition = searchBarPosition;
return config; return config;
} }
@@ -281,32 +339,31 @@ public class BinarySerializer {
return map; return map;
} }
private static void writeExtendableFrames(DataOutputStream out, Map<String, ClientConfig.FrameData> map) private static void writeExtendableFrames(DataOutputStream out, Map<String, ClientConfig.PanelData> map)
throws IOException { throws IOException {
if (map == null) { if (map == null) {
out.writeInt(0); out.writeInt(0);
return; return;
} }
out.writeInt(map.size()); out.writeInt(map.size());
for (Map.Entry<String, ClientConfig.FrameData> entry : map.entrySet()) { for (Map.Entry<String, ClientConfig.PanelData> entry : map.entrySet()) {
writeString(out, entry.getKey()); writeString(out, entry.getKey());
ClientConfig.FrameData data = entry.getValue(); ClientConfig.PanelData data = entry.getValue();
out.writeInt(data.x()); out.writeInt(data.x());
out.writeInt(data.y()); out.writeInt(data.y());
out.writeBoolean(data.extended()); out.writeBoolean(data.extended());
} }
} }
private static Map<String, ClientConfig.FrameData> readExtendableFrames(DataInputStream in) private static Map<String, ClientConfig.PanelData> readExtendablePanels(DataInputStream in) throws IOException {
throws IOException {
int size = in.readInt(); int size = in.readInt();
Map<String, ClientConfig.FrameData> map = new HashMap<>(size); Map<String, ClientConfig.PanelData> map = new HashMap<>(size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
String key = readString(in); String key = readString(in);
int x = in.readInt(); int x = in.readInt();
int y = in.readInt(); int y = in.readInt();
boolean extended = in.readBoolean(); boolean extended = in.readBoolean();
map.put(key, new ClientConfig.FrameData(x, y, extended)); map.put(key, new ClientConfig.PanelData(x, y, extended));
} }
return map; return map;
} }
@@ -336,7 +393,35 @@ public class BinarySerializer {
return map; return map;
} }
// Optimized string writing (UTF-8 with length prefix) private static void writeMacros(DataOutputStream out, List<ClientConfig.MacroData> macros)
throws IOException {
if (macros == null) {
out.writeInt(0);
return;
}
out.writeInt(macros.size());
for (ClientConfig.MacroData macro : macros) {
writeString(out, macro.name());
out.writeInt(macro.key());
out.writeInt(macro.lines().size());
for (String line : macro.lines()) writeString(out, line);
}
}
private static List<ClientConfig.MacroData> readMacros(DataInputStream in) throws IOException {
int size = in.readInt();
List<ClientConfig.MacroData> macros = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
String name = readString(in);
int key = in.readInt();
int lineCount = in.readInt();
List<String> lines = new ArrayList<>(lineCount);
for (int j = 0; j < lineCount; j++) lines.add(readString(in));
macros.add(new ClientConfig.MacroData(name, key, lines));
}
return macros;
}
private static void writeString(DataOutputStream out, String str) throws IOException { private static void writeString(DataOutputStream out, String str) throws IOException {
if (str == null) { if (str == null) {
out.writeInt(-1); out.writeInt(-1);
@@ -352,6 +437,6 @@ public class BinarySerializer {
if (length == -1) return null; if (length == -1) return null;
byte[] bytes = new byte[length]; byte[] bytes = new byte[length];
in.readFully(bytes); in.readFully(bytes);
return new String(bytes, "UTF-8"); return new String(bytes, StandardCharsets.UTF_8);
} }
} }
@@ -1,18 +1,23 @@
package me.retucio.sputnik.config; package me.retucio.sputnik.config;
import com.github.retucio.neutrino.EventListener;
import me.retucio.sputnik.event.sputnik.*; import me.retucio.sputnik.event.sputnik.*;
import me.retucio.sputnik.module.setting.settings.*; import me.retucio.sputnik.module.setting.settings.*;
import me.retucio.sputnik.ui.screen.ClickGUI; 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.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import static me.retucio.sputnik.Sputnik.EVENT_BUS; import static me.retucio.sputnik.Sputnik.EVENT_BUS;
import static me.retucio.sputnik.Sputnik.mc; import static me.retucio.sputnik.Sputnik.mc;
// clase donde se guardan las configuraciones temporalmente hasta escribirlas en camera_tweaks.json // clase donde se guardan las configuraciones temporalmente hasta escribirlas en sputnik.dat
public class ClientConfig { public class ClientConfig {
// nombre del módulo -> estado del módulo // nombre del módulo -> estado del módulo
@@ -22,10 +27,10 @@ public class ClientConfig {
public Map<String, Object> settings = new HashMap<>(); public Map<String, Object> settings = new HashMap<>();
// nombre del módulo -> posición (x, y) // nombre del módulo -> posición (x, y)
public Map<String, int[]> settingsFrames = new HashMap<>(); public Map<String, int[]> settingsPanels = new HashMap<>();
// nombre del frame -> FrameData (que contiene extended, x & y) // nombre del frame -> FrameData (que contiene extended, x & y)
public Map<String, FrameData> extendableFrames = new HashMap<>(); public Map<String, PanelData> extendablePanels = new HashMap<>();
// id del elemento del hud -> posición (x, y) // id del elemento del hud -> posición (x, y)
public Map<String, int[]> hudPositions = new HashMap<>(); public Map<String, int[]> hudPositions = new HashMap<>();
@@ -33,23 +38,40 @@ public class ClientConfig {
// id del elemento del hud -> true: visible / false: no visible // id del elemento del hud -> true: visible / false: no visible
public Map<String, Boolean> hudVisibilities = new HashMap<>(); public Map<String, Boolean> hudVisibilities = new HashMap<>();
// id del elemento del hud -> ruta de la imagen (NEW) // id del elemento del hud -> ruta de la imagen
public Map<String, String> hudImagePaths = new HashMap<>(); public Map<String, String> hudImagePaths = new HashMap<>();
// posición de la barra de búsqueda (x, y) // posición de la barra de búsqueda (x, y)
public int[] searchBarPosition = new int[] { 340, 16 }; public int[] searchBarPosition = new int[] { 340, 16 };
// amigos: uuid -> nombre
public Map<String, String> friends = new HashMap<>();
public List<MacroData> macros = new ArrayList<>();
// perfil cargado actualmente, vacío si es la config. suelta
public String currentProfile = "";
public ClientConfig() { public ClientConfig() {
EVENT_BUS.subscribe(this); EVENT_BUS.subscribe(this);
} }
// mientras se aplica una config. los cambios vienen de ella, así que reescribirla es
// como poco redundante, y al cambiar de perfil pisaba lo que aún no se había aplicado
private static boolean isApplying() {
return ConfigManager.INSTANCE != null && ConfigManager.INSTANCE.isApplying();
}
@EventListener
public void onToggleModule(ToggleModuleEvent event) { public void onToggleModule(ToggleModuleEvent event) {
ConfigManager.setModuleState(event.getModule()); if (isApplying()) return;
ConfigManager.INSTANCE.setModuleState(event.getModule());
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@EventListener
public void onUpdateSetting(UpdateSettingEvent event) { public void onUpdateSetting(UpdateSettingEvent event) {
if (mc == null || !event.shouldSave()) return; if (mc == null || !event.shouldSave() || isApplying()) return;
// guardar ajustes con su respectivo tipo de valor, en formato "nombreMódulo:nombreAjuste" // guardar ajustes con su respectivo tipo de valor, en formato "nombreMódulo:nombreAjuste"
Object value = null; Object value = null;
@@ -61,56 +83,110 @@ public class ClientConfig {
case StringSetting s: value = s.getValue(); break; case StringSetting s: value = s.getValue(); break;
case ListSetting l: value = l.getConfigValues(); break; case ListSetting l: value = l.getConfigValues(); break;
case ColorSetting c: value = c.getConfigValue(); break; case ColorSetting c: value = c.getConfigValue(); break;
case OptionSetting o: value = o.getDisplayName(); break; case OptionSetting o: value = o.getConfigName(); break;
case FileSetting f: value = f.getValue(); break;
default: break; default: break;
} }
ConfigManager.setSetting(event.getSetting(), value); ConfigManager.INSTANCE.setSetting(event.getSetting(), value);
} }
public void onOpenSettingsFrame(SettingsFrameEvent.Open event) { @EventListener
ConfigManager.setFramePosition(event.getFrame()); public void onOpenSettingsPanel(SettingsPanelEvent.Open event) {
ConfigManager.INSTANCE.setPanelPosition(event.getPanel());
} }
public void onCloseSettingsFrame(SettingsFrameEvent.Close event) { @EventListener
settingsFrames.remove(event.getFrame().getModule().getName()); public void onCloseSettingsPanel(SettingsPanelEvent.Close event) {
ConfigManager.save(); settingsPanels.remove(event.getPanel().getModule().getName());
ConfigManager.INSTANCE.markDirty();
} }
public void onMoveSettingsFrame(SettingsFrameEvent.Move event) { @EventListener
settingsFrames.replace(event.getFrame().getModule().getName(), new int[]{event.getFrame().getX(), event.getFrame().getY()}); public void onMoveSettingsPanel(SettingsPanelEvent.Move event) {
ConfigManager.save(); settingsPanels.replace(event.getPanel().getModule().getName(), new int[]{event.getPanel().getX(), event.getPanel().getY()});
ConfigManager.INSTANCE.markDirty();
} }
public void onExtendModuleFrame(ModuleFrameEvent.Extend event) { @EventListener
extendableFrames.put("M", new FrameData( public void onExtendModulePanel(ModulePanelEvent.Extend event) {
ClickGUI.INSTANCE.getModulesFrame().getX(), extendablePanels.put(event.getPanel().category.toString(), new PanelData(
ClickGUI.INSTANCE.getModulesFrame().getY(), event.getPanel().getX(),
ClickGUI.INSTANCE.getModulesFrame().extended)); event.getPanel().getY(),
event.getPanel().isExtended()));
ConfigManager.INSTANCE.markDirty();
} }
public void onMoveModuleFrame(ModuleFrameEvent.Move event) { // put y no replace: replace no hace nada si la clave no está todavía, así que mover un
extendableFrames.replace("M", new FrameData( // marco sin haberlo plegado antes (que es lo que crea la entrada) no se guardaba
ClickGUI.INSTANCE.getModulesFrame().getX(), @EventListener
ClickGUI.INSTANCE.getModulesFrame().getY(), public void onMoveModulePanel(ModulePanelEvent.Move event) {
ClickGUI.INSTANCE.getModulesFrame().extended)); extendablePanels.put(event.getPanel().category.toString(), new PanelData(
event.getPanel().getX(),
event.getPanel().getY(),
event.getPanel().isExtended()));
ConfigManager.INSTANCE.markDirty();
} }
public void onExtendGUISettingsFrame(GUISettingsFrameEvent.Extend event) { @EventListener
extendableFrames.put("S", new FrameData( public void onExtendClientSettingsPanel(ClientSettingsPanelEvent.Extend event) {
ClickGUI.INSTANCE.getGuiSettingsFrame().getX(), extendablePanels.put("ajustes", new PanelData(
ClickGUI.INSTANCE.getGuiSettingsFrame().getY(), ClickGui.INSTANCE.getClientSettingsPanel().getX(),
ClickGUI.INSTANCE.getGuiSettingsFrame().extended)); ClickGui.INSTANCE.getClientSettingsPanel().getY(),
ClickGui.INSTANCE.getClientSettingsPanel().isExtended()));
ConfigManager.INSTANCE.markDirty();
} }
public void onMoveGUISettingsFrame(GUISettingsFrameEvent.Move event) { @EventListener
extendableFrames.replace("S", new FrameData( public void onMoveClientSettingsPanel(ClientSettingsPanelEvent.Move event) {
ClickGUI.INSTANCE.getGuiSettingsFrame().getX(), extendablePanels.put("ajustes", new PanelData(
ClickGUI.INSTANCE.getGuiSettingsFrame().getY(), ClickGui.INSTANCE.getClientSettingsPanel().getX(),
ClickGUI.INSTANCE.getGuiSettingsFrame().extended)); ClickGui.INSTANCE.getClientSettingsPanel().getY(),
ClickGui.INSTANCE.getClientSettingsPanel().isExtended()));
ConfigManager.INSTANCE.markDirty();
} }
public record FrameData(int x, int y, boolean extended) implements Serializable { @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.put("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);
}
ConfigManager.INSTANCE.markDirty();
}
@EventListener
public void onRemoveFriend(FriendEvent.Remove event) {
if (isApplying()) return;
friends.remove(event.getFriend().getUuid().toString());
ConfigManager.INSTANCE.markDirty();
}
public record PanelData(int x, int y, boolean extended) implements Serializable {
@Serial private static final long serialVersionUID = 1L;
}
public record MacroData(String name, int key, List<String> lines) implements Serializable {
@Serial private static final long serialVersionUID = 1L; @Serial private static final long serialVersionUID = 1L;
} }
} }
@@ -3,21 +3,40 @@ package me.retucio.sputnik.config;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.friend.Friend;
import me.retucio.sputnik.friend.FriendManager;
import me.retucio.sputnik.module.Module; import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.client.ClientSettingsModule;
import me.retucio.sputnik.macro.Macro;
import me.retucio.sputnik.macro.MacroManager;
import me.retucio.sputnik.module.setting.*; import me.retucio.sputnik.module.setting.*;
import me.retucio.sputnik.module.setting.settings.*; import me.retucio.sputnik.module.setting.settings.*;
import me.retucio.sputnik.ui.widgets.Frame; import me.retucio.sputnik.ui.hud.HudElement;
import me.retucio.sputnik.ui.widgets.frames.ModuleFrame; import me.retucio.sputnik.ui.hud.HudRenderer;
import me.retucio.sputnik.ui.screen.ClickGUI; import me.retucio.sputnik.ui.widgets.Panel;
import me.retucio.sputnik.ui.widgets.frames.settings.ClientSettingsFrame; import me.retucio.sputnik.ui.widgets.panels.ExtendablePanel;
import me.retucio.sputnik.ui.widgets.frames.SettingsFrame; import me.retucio.sputnik.ui.widgets.panels.FriendsPanel;
import me.retucio.sputnik.ui.widgets.panels.ModulePanel;
import me.retucio.sputnik.ui.screen.ClickGui;
import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel;
import me.retucio.sputnik.ui.widgets.panels.SettingsPanel;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.I18nUtil;
import net.minecraft.network.chat.Component;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer; import java.util.function.Consumer;
// se ocupa de guardar, cargar y aplicar ajustes // se ocupa de guardar, cargar y aplicar ajustes
@@ -26,74 +45,207 @@ public class ConfigManager {
private static final File CONFIG_FILE = new File("sputnik.dat"); private static final File CONFIG_FILE = new File("sputnik.dat");
private static final File LEGACY_JSON_FILE = new File("sputnik.json"); private static final File LEGACY_JSON_FILE = new File("sputnik.json");
private static boolean loaded = false; public static ConfigManager INSTANCE;
private static ClientConfig config = null;
private boolean loaded = false;
private boolean applying = false;
private boolean dirty = false;
private long lastSaveTime = System.currentTimeMillis();
private ClientConfig config = null;
private final ExecutorService saveExecutor = Executors.newSingleThreadExecutor(r -> {
Thread t = new Thread(r, "sputnik-config-save");
t.setDaemon(true); // no bloquear el cierre de la JVM
return t;
});
// instantánea pendiente de escribir. si llega otra antes de que el hilo de fondo escriba,
// sustituye a la anterior en vez de descartarse (que era lo que pasaba con un simple
// "ya hay uno en cola": se tiraba justo la más reciente y se perdían esos cambios)
private final AtomicReference<byte[]> pending = new AtomicReference<>();
// ------------------ MÉTODOS PRINCIPALES ------------------ // ------------------ MÉTODOS PRINCIPALES ------------------
// guardar configuraciones // guardar configuraciones
public static void save() { public void save() {
if (!loaded) return; if (!loaded) return;
writeSnapshot(false);
}
public void saveOnShutdown() {
// 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(I18nUtil.fmt("sputnik.config.warn.thread",
"el hilo de guardado no terminó a tiempo"));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 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(); ensureConfig();
final byte[] snapshot;
try { try {
BinarySerializer.writeConfig(config, CONFIG_FILE); snapshot = BinarySerializer.toBytes(config);
Sputnik.LOGGER.info("ajustes guardados");
} catch (IOException e) { } catch (IOException e) {
Sputnik.LOGGER.error("error al guardar ajustes", 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 static void load() { public void load() {
Sputnik.LOGGER.info("cargando ajustes..."); Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.loading", "cargando ajustes..."));
if (CONFIG_FILE.exists()) { if (CONFIG_FILE.exists()) {
try { try {
config = BinarySerializer.readConfig(CONFIG_FILE); setConfig(BinarySerializer.readConfig(CONFIG_FILE));
Sputnik.LOGGER.info("ajustes cargados"); Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.loaded", "ajustes cargados"));
return; return;
} catch (IOException e) { } catch (IOException e) {
Sputnik.LOGGER.error("error cargando ajustes, intentando json", e); Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.loading",
"error cargando ajustes, intentando json"), e);
} }
} }
// Fallback to legacy JSON // fallback to legacy JSON
if (LEGACY_JSON_FILE.exists()) { if (LEGACY_JSON_FILE.exists()) {
try (FileReader reader = new FileReader(LEGACY_JSON_FILE)) { try (FileReader reader = new FileReader(LEGACY_JSON_FILE)) {
Gson gson = new GsonBuilder().setPrettyPrinting().create(); Gson gson = new GsonBuilder().setPrettyPrinting().create();
config = gson.fromJson(reader, ClientConfig.class); setConfig(gson.fromJson(reader, ClientConfig.class));
Sputnik.LOGGER.info("ajustes json cargados (migración)"); Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.json",
"ajustes json cargados (migración)"));
save(); // aquí `loaded` todavía es false (se pone al aplicar), así que save() no
// haría nada: la migración se escribe directamente
writeSnapshot(true);
return; return;
} catch (IOException e) { } catch (IOException e) {
Sputnik.LOGGER.error("error cargando json, usando defaults", e); Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.json",
"error cargando json, usando defaults"), e);
} }
} }
// crear nuevo config // crear nuevo config
ensureConfig(); ensureConfig();
writeSnapshot(true);
}
// aplicar configuraciones cargadas. devuelve false si todavía no se puede (falta algo por
// construir), para que se reintente en el siguiente tick en vez de quedarse sin aplicar
public boolean applyConfig() {
if (!shouldApply()) return false;
applying = true;
try {
// los del mod primero: entre ellos está "teclas multimódulo", que decide si al
// asignar una tecla se le quita a los demás módulos. aplicándolos los últimos,
// las teclas de los módulos se cargaban con el valor por defecto de ese ajuste
applyClientSettings();
applyModuleStates();
applyModuleSettings();
applySettingsPanels();
applySearchBarPosition();
applyExtendablePanels();
applyFriends();
applyMacros();
applyHudElements();
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.applied", "ajustes aplicados"));
} catch (Exception e) {
// si algo peta a medio aplicar no se puede dejar `loaded` en false: save() no
// haría nada en toda la sesión y se perdería la config. entera al cerrar
Sputnik.LOGGER.error(I18nUtil.fmt("sputnik.config.error.applying",
"error aplicando ajustes"), e);
} finally {
applying = false;
loaded = true;
}
return true;
}
// true mientras se está cargando la config. sobre los ajustes. lo mira KeySetting para no
// tratar las teclas guardadas como si el usuario las acabara de asignar, y ClientConfig
// para no reescribir en la config. lo que se le está aplicando
public boolean isApplying() {
return applying;
}
// sustituye la config. viva por otra y la vuelca sobre el estado actual del cliente
public void replaceConfig(ClientConfig newConfig) {
applying = true;
try {
resetLiveState();
} finally {
applying = false;
}
setConfig(newConfig);
loaded = false;
applyConfig();
save(); save();
} }
// aplicar configuraciones cargadas // lo que no venga en la config. nueva tiene que quedarse en su valor por defecto, no en
public static void applyConfig() { // el que tuviera el perfil anterior
if (!shouldApply()) return; private void resetLiveState() {
if (ClickGui.INSTANCE != null) ClickGui.INSTANCE.closeAllSettingsPanels();
applyModuleStates(); for (Module module : ModuleManager.INSTANCE.getModules()) {
applyModuleSettings(); module.setEnabled(false);
applySettingsFrames(); module.getSettings().forEach(Setting::reset);
applySearchBarPosition(); }
applyGuiSettings(); ClientSettingsPanel.clientSettings.getSettings().forEach(Setting::reset);
applyExtendableFrames();
loaded = true; for (Friend friend : new ArrayList<>(FriendManager.INSTANCE.getFriends()))
Sputnik.LOGGER.info("ajustes aplicados"); FriendManager.INSTANCE.remove(friend);
MacroManager.INSTANCE.setMacros(List.of());
} }
// obtener configuración actual // obtener configuración actual
public static ClientConfig getConfig() { public ClientConfig getConfig() {
ensureConfig(); ensureConfig();
return config; return config;
} }
@@ -101,56 +253,100 @@ public class ConfigManager {
// ------------------ GUARDAR VALORES INDIVIDUALES EN LA CONFIG. ------------------ // ------------------ GUARDAR VALORES INDIVIDUALES EN LA CONFIG. ------------------
public static void setModuleState(Module module) { public void setModuleState(Module module) {
ensureConfig(); ensureConfig();
config.moduleStates.put(module.getName(), module.isEnabled()); config.moduleStates.put(module.getName(), module.isEnabled());
save(); markDirty();
} }
public static void setSetting(Setting<?> setting, Object value) { public void setSetting(Setting<?> setting, Object value) {
ensureConfig(); ensureConfig();
config.settings.put(getSettingKey(setting), value); config.settings.put(getSettingKey(setting), value);
save(); markDirty();
} }
public static void setFramePosition(SettingsFrame frame) { public void setPanelPosition(SettingsPanel panel) {
ensureConfig(); ensureConfig();
if (!config.settingsFrames.containsKey(frame.getModule().getName())) if (!config.settingsPanels.containsKey(panel.getModule().getName()))
config.settingsFrames.put(frame.getModule().getName(), new int[] {frame.getX(), frame.getY()}); config.settingsPanels.put(panel.getModule().getName(), new int[] {panel.getX(), panel.getY()});
else else
config.settingsFrames.replace(frame.getModule().getName(), new int[] {frame.getX(), frame.getY()}); config.settingsPanels.replace(panel.getModule().getName(), new int[] {panel.getX(), panel.getY()});
save(); markDirty();
} }
public static void setExtendableFrame(String key, ClientConfig.FrameData data) { public void setExtendablePanel(String key, ClientConfig.PanelData data) {
ensureConfig(); ensureConfig();
config.extendableFrames.put(key, data); config.extendablePanels.put(key, data);
save(); markDirty();
} }
public static void setHudPosition(String id, int x, int y) { public void setHudPosition(String id, int x, int y) {
ensureConfig(); ensureConfig();
config.hudPositions.put(id, new int[] {x, y}); config.hudPositions.put(id, new int[] {x, y});
save(); markDirty();
} }
public static void setHudVisibility(String id, Boolean visible) { public void setHudVisibility(String id, Boolean visible) {
ensureConfig(); ensureConfig();
config.hudVisibilities.put(id, visible); config.hudVisibilities.put(id, visible);
save(); markDirty();
} }
public static void setSearchBarPosition(int x, int y) { public void setSearchBarPosition(int x, int y) {
ensureConfig(); ensureConfig();
config.searchBarPosition = new int[] {x, y}; config.searchBarPosition = new int[] {x, y};
save(); markDirty();
}
public void setMacros(List<Macro> macros) {
ensureConfig();
config.macros = macros.stream()
.map(macro -> new ClientConfig.MacroData(macro.getName(), macro.getKey(), List.copyOf(macro.getLines())))
.toList();
markDirty();
}
public void setCurrentProfile(String name) {
ensureConfig();
config.currentProfile = name;
markDirty();
} }
// ------------------ APLICAR AJUSTES ------------------ // ------------------ APLICAR AJUSTES ------------------
public void applyFriends() {
config.friends.forEach((uuid, name) -> {
Friend friend = new Friend(UUID.fromString(uuid));
friend.setName(name);
FriendManager.INSTANCE.add(friend);
});
Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.friends",
"amigos aplicados"));
}
public void applyMacros() {
MacroManager.INSTANCE.setMacros(config.macros.stream()
.map(data -> new Macro(data.name(), data.key(), data.lines()))
.toList());
}
// el HudRenderer coloca sus elementos al construirse, pero al cambiar de perfil ya existen
public void applyHudElements() {
if (HudRenderer.INSTANCE == null) return;
for (HudElement element : HudRenderer.INSTANCE.getElements()) {
int[] position = config.hudPositions.get(element.getId());
if (position != null && position.length == 2) element.setPosition(position[0], position[1]);
else element.resetPosition();
Boolean visible = config.hudVisibilities.get(element.getId());
element.setVisible(visible == null || visible);
}
}
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
public static void applySetting(Module parent, Setting<?> setting) { public void applySetting(Setting<?> setting) {
String key = getSettingKey(setting); String key = getSettingKey(setting);
if (!config.settings.containsKey(key)) return; if (!config.settings.containsKey(key)) return;
@@ -166,81 +362,95 @@ public class ConfigManager {
case ListSetting listSetting -> applyListSetting(listSetting, value); case ListSetting listSetting -> applyListSetting(listSetting, value);
case ColorSetting colorSetting -> applyColorSetting(colorSetting, value); case ColorSetting colorSetting -> applyColorSetting(colorSetting, value);
case OptionSetting optionSetting -> applyOptionSetting(optionSetting, value); case OptionSetting optionSetting -> applyOptionSetting(optionSetting, value);
default -> Sputnik.LOGGER.warn("watafac queseso: {}", setting.getClass().getSimpleName()); case FileSetting fileSetting -> applyFileSetting(fileSetting, value);
default -> Sputnik.LOGGER.warn(I18nUtil.fmt("sputnik.config.warn.unknownsetting",
"watafac queseso: %s", setting.getClass().getSimpleName()));
} }
} }
private static void applyModuleStates() { private void applyModuleStates() {
ModuleManager.INSTANCE.getModules().forEach(module -> { ModuleManager.INSTANCE.getModules().forEach(module -> {
if (config.moduleStates.containsKey(module.getName())) { if (config.moduleStates.containsKey(module.getName())) {
module.setEnabled(config.moduleStates.get(module.getName())); module.setEnabled(config.moduleStates.get(module.getName()));
} }
}); });
Sputnik.LOGGER.info("estados de módulos aplicados"); Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.modulestates",
"estados de módulos aplicados"));
} }
private static void applyModuleSettings() { private void applyModuleSettings() {
ModuleManager.INSTANCE.getModules().forEach(module -> { ModuleManager.INSTANCE.getModules().forEach(module -> {
module.getSettings().forEach(setting -> applySetting(module, setting)); 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 static void applySettingsFrames() { private void applySettingsPanels() {
config.settingsFrames.forEach((moduleName, position) -> { config.settingsPanels.forEach((moduleName, position) -> {
Module module = ModuleManager.INSTANCE.getModuleByName(moduleName); Module module = ModuleManager.INSTANCE.getModuleByName(moduleName);
if (module == null) { if (module == null) {
Sputnik.LOGGER.warn("\"null\" como módulo (\"{}\")", moduleName); Sputnik.LOGGER.warn(I18nUtil.fmt("sputnik.config.warn.nullmodule",
"\"null\" como módulo (\"%s\")", moduleName));
return; return;
} }
ClickGUI.INSTANCE.openSettingsFrame(module, position[0], position[1]); ClickGui.INSTANCE.openSettingsPanel(module, position[0], position[1]);
}); });
Sputnik.LOGGER.info("estados de marcos de ajustes aplicados"); Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.settingspanels",
"estados de marcos de ajustes aplicados"));
} }
private static void applySearchBarPosition() { private void applySearchBarPosition() {
if (config.searchBarPosition != null && config.searchBarPosition.length == 2) { if (config.searchBarPosition != null && config.searchBarPosition.length == 2) {
ClickGUI.INSTANCE.getSearchBar().setX(config.searchBarPosition[0]); ClickGui.INSTANCE.getSearchBar().setX(config.searchBarPosition[0]);
ClickGUI.INSTANCE.getSearchBar().setY(config.searchBarPosition[1]); ClickGui.INSTANCE.getSearchBar().setY(config.searchBarPosition[1]);
} }
Sputnik.LOGGER.info("posición de la barra de búsqueda aplicada"); Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.searchbar",
"posición de la barra de búsqueda aplicada"));
} }
private static void applyGuiSettings() { private void applyClientSettings() {
ClientSettingsFrame.guiSettings.setEnabled(true); ClientSettingsPanel.clientSettings.setEnabled(true);
ClientSettingsFrame.guiSettings.getSettings().forEach(setting -> { ClientSettingsPanel.clientSettings.getSettings().forEach(this::applySetting);
applySetting(ClientSettingsFrame.guiSettings, setting); Sputnik.LOGGER.info(I18nUtil.fmt("sputnik.config.info.clientsettings",
}); "ajustes del cliente aplicados"));
Sputnik.LOGGER.info("ajustes del cliente aplicados");
} }
private static void applyExtendableFrames() { private void applyExtendablePanels() {
applyExtendableFrame(ClickGUI.INSTANCE.getModulesFrame()); for (ModulePanel panel : ClickGui.INSTANCE.getModulePanels()) {
applyExtendableFrame(ClickGUI.INSTANCE.getGuiSettingsFrame()); 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",
ClickGUI.INSTANCE.refreshListButtons(); "posiciones de marcos extendibles aplicadas"));
ClickGui.INSTANCE.refreshListButtons();
} }
private static void applyExtendableFrame(Frame<?> frame) { private void applyExtendablePanel(Panel<?> panel) {
ClientConfig.FrameData frameData = null; ClientConfig.PanelData panelData = null;
if (frame instanceof ModuleFrame)
frameData = config.extendableFrames.get("M");
if (frame instanceof ClientSettingsFrame) if (panel instanceof ModulePanel mp) {
frameData = config.extendableFrames.get("S"); panelData = config.extendablePanels.get(mp.category.toString());
}
if (panel instanceof ClientSettingsPanel) {
panelData = config.extendablePanels.get("ajustes");
}
if (panel instanceof FriendsPanel) {
panelData = config.extendablePanels.get("amigos");
}
if (frameData != null) { if (panelData != null) {
frame.setX(frameData.x()); panel.setX(panelData.x());
frame.setY(frameData.y()); panel.setY(panelData.y());
if (frame instanceof ModuleFrame mFrame) if (panel instanceof ExtendablePanel<?> ePanel) {
mFrame.extended = frameData.extended(); ePanel.setExtended(panelData.extended());
if (frame instanceof ClientSettingsFrame sFrame) }
sFrame.extended = frameData.extended();
} }
} }
@@ -268,8 +478,8 @@ public class ConfigManager {
} }
private static void applyStringSetting(StringSetting setting, Object value) { private static void applyStringSetting(StringSetting setting, Object value) {
if (value instanceof String) { if (value instanceof String v) {
setting.setValue((String) value); setting.setValue(v);
} }
} }
@@ -286,8 +496,14 @@ public class ConfigManager {
Map<T, Boolean> convertedValues = new HashMap<>(); Map<T, Boolean> convertedValues = new HashMap<>();
for (T option : setting.getOptions()) { for (T option : setting.getOptions()) {
String key = setting.getDisplayName(option); String key = setting.getConfigName(option);
Object booleanValue = map.get(key); Object booleanValue = map.get(key);
// compatibilidad con configs. de antes, cuando la clave era el nombre ya
// traducido (el de vanilla en inglés). sin esto la lista entera se quedaba
// en false, o sea que al usuario se le borraba lo que tuviera marcado
if (booleanValue == null) booleanValue = map.get(I18nUtil.vanillaName(key));
convertedValues.put(option, booleanValue instanceof Boolean && (Boolean) booleanValue); convertedValues.put(option, booleanValue instanceof Boolean && (Boolean) booleanValue);
} }
@@ -324,6 +540,12 @@ public class ConfigManager {
} }
} }
private static void applyFileSetting(FileSetting setting, Object value) {
if (value instanceof String v) {
setting.setValue(v);
}
}
private static <T> void applyOptionalProperty(Map<?, ?> map, String key, Class<T> type, Consumer<T> setter) { private static <T> void applyOptionalProperty(Map<?, ?> map, String key, Class<T> type, Consumer<T> setter) {
Object value = map.get(key); Object value = map.get(key);
if (type.isInstance(value)) { if (type.isInstance(value)) {
@@ -334,19 +556,56 @@ public class ConfigManager {
// ------------------ MÉTODOS DE AYUDA ------------------ // ------------------ MÉTODOS DE AYUDA ------------------
private static boolean shouldApply() {
return Sputnik.mc != null && ModuleManager.INSTANCE != null && config != null;
}
private static void ensureConfig() {
if (config == null) config = new ClientConfig();
}
private static String getSettingKey(Setting<?> setting) { private static String getSettingKey(Setting<?> setting) {
return setting.getSg().getModule().getName() + ":" + setting.getTrueName(); return setting.getSg().getModule().getName() + ":" + setting.getTrueName();
} }
public static boolean hasLoaded() { // 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
&& ClickGui.INSTANCE != null && config != null;
}
private void ensureConfig() {
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() {
return loaded; return loaded;
} }
public void markDirty() {
this.dirty = true;
}
public boolean isDirty() {
return dirty;
}
public void tickAutosave() {
if (!loaded || !dirty) return;
ClientSettingsModule settings = ClientSettingsPanel.clientSettings;
if (!settings.autosave.getValue()) return;
// desde el último guardado, no desde el último cambio: contando desde el cambio,
// tocando ajustes cada menos del intervalo no se autoguardaba nunca
long intervalMs = (long) (settings.autosaveInterval.getValue() * 1000);
if (System.currentTimeMillis() - lastSaveTime < intervalMs) return;
HudRenderer.INSTANCE.pushNotification(
Component.translatable("sputnik.config.autosaving.title").getString(),
Component.translatable("sputnik.config.autosaving.desc").getString());
save();
}
} }
@@ -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);
}
}
@@ -1,11 +1,11 @@
package me.retucio.sputnik.event; package me.retucio.sputnik.event;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.misc.MinecraftClientMixin; import me.retucio.sputnik.mixin.mixins.misc.MinecraftMixin;
/** /**
* @see MinecraftClientMixin#onStop * @see MinecraftMixin#onStop
*/ */
public class ShutdownEvent extends Event { public class ShutdownEvent extends Event {
@@ -1,12 +1,12 @@
package me.retucio.sputnik.event; package me.retucio.sputnik.event;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.misc.MinecraftClientMixin; import me.retucio.sputnik.mixin.mixins.misc.MinecraftMixin;
/** /**
* @see MinecraftClientMixin#onTickPre * @see MinecraftMixin#onTickPre
* @see MinecraftClientMixin#onTickPost * @see MinecraftMixin#onTickPost
*/ */
public class TickEvent { public class TickEvent {
@@ -1,12 +1,14 @@
package me.retucio.sputnik.event.input; package me.retucio.sputnik.event.input;
import me.retucio.sputnik.module.modules.misc.ChatPlus; import me.retucio.sputnik.module.modules.misc.ChatPlus;
import net.minecraft.text.ClickEvent; import net.minecraft.network.chat.ClickEvent;
import org.jspecify.annotations.NonNull;
/** este evento es solo para asegurarse de que comandos del mod se puedan ejecutar solo desde el lado del cliente /** este evento es solo para asegurarse de que comandos del mod se puedan ejecutar solo desde el lado del cliente
* @see ChatPlus#getSendButton * @see ChatPlus#getSendButton
*/ */
public class ClientClickEvent implements ClickEvent { public class ClientClickEvent implements ClickEvent {
private final String value; private final String value;
@@ -20,7 +22,7 @@ public class ClientClickEvent implements ClickEvent {
} }
@Override @Override
public Action getAction() { public @NonNull Action action() {
return Action.RUN_COMMAND; return Action.RUN_COMMAND;
} }
@@ -1,11 +1,11 @@
package me.retucio.sputnik.event.input; package me.retucio.sputnik.event.input;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.io.KeyboardMixin; import me.retucio.sputnik.mixin.mixins.io.KeyboardHandlerMixin;
/** /**
* @see KeyboardMixin#onKeyPress * @see KeyboardHandlerMixin#onKeyPress
*/ */
public class KeyEvent extends Event { public class KeyEvent extends Event {
@@ -1,11 +1,11 @@
package me.retucio.sputnik.event.input; package me.retucio.sputnik.event.input;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.io.MouseMixin; import me.retucio.sputnik.mixin.mixins.io.MouseHandlerMixin;
/** /**
* @see MouseMixin#onMouseButton * @see MouseHandlerMixin#onMouseButton
*/ */
public class MouseClickEvent extends Event { public class MouseClickEvent extends Event {
@@ -1,11 +1,11 @@
package me.retucio.sputnik.event.input; package me.retucio.sputnik.event.input;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.io.MouseMixin; import me.retucio.sputnik.mixin.mixins.io.MouseHandlerMixin;
/** /**
* @see MouseMixin#onMouseScroll * @see MouseHandlerMixin#onMouseScroll
*/ */
public class MouseScrollEvent extends Event { public class MouseScrollEvent extends Event {
@@ -0,0 +1,5 @@
package me.retucio.sputnik.event.input;
import com.github.retucio.neutrino.Event;
public class SneakEvent extends Event {}
@@ -1,8 +1,8 @@
package me.retucio.sputnik.event.interact; package me.retucio.sputnik.event.interact;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import net.minecraft.util.math.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.core.Direction;
public class AttackBlockEvent extends Event { public class AttackBlockEvent extends Event {
@@ -1,13 +1,14 @@
package me.retucio.sputnik.event.interact; package me.retucio.sputnik.event.interact;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.player.ClientPlayerInteractionManagerMixin; import me.retucio.sputnik.mixin.mixins.player.MultiPlayerGameModeMixin;
import net.minecraft.entity.Entity; import net.minecraft.world.entity.Entity;
/** /**
* @see ClientPlayerInteractionManagerMixin#onAttackEntity * @see MultiPlayerGameModeMixin#onAttackEntity
*/ */
public class AttackEntityEvent extends Event { public class AttackEntityEvent extends Event {
private final Entity entity; private final Entity entity;
@@ -1,9 +1,9 @@
package me.retucio.sputnik.event.interact; package me.retucio.sputnik.event.interact;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import net.minecraft.block.BlockState; import net.minecraft.core.BlockPos;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.world.phys.shapes.VoxelShape;
public class BlockShapeEvent extends Event { public class BlockShapeEvent extends Event {
@@ -1,28 +1,28 @@
package me.retucio.sputnik.event.interact; package me.retucio.sputnik.event.interact;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import net.minecraft.entity.vehicle.AbstractBoatEntity; import net.minecraft.world.entity.vehicle.boat.AbstractBoat;
import net.minecraft.util.math.Vec3d; import net.minecraft.world.phys.Vec3;
public class BoatMoveEvent extends Event { public class BoatMoveEvent extends Event {
private final AbstractBoatEntity boat; private final AbstractBoat boat;
private Vec3d pos; private Vec3 pos;
public BoatMoveEvent(AbstractBoatEntity boat, Vec3d pos) { public BoatMoveEvent(AbstractBoat boat, Vec3 pos) {
this.boat = boat; this.boat = boat;
this.pos = pos; this.pos = pos;
} }
public AbstractBoatEntity getBoat() { public AbstractBoat getBoat() {
return boat; return boat;
} }
public Vec3d getPos() { public Vec3 getPos() {
return pos; return pos;
} }
public void setPos(Vec3d pos) { public void setPos(Vec3 pos) {
this.pos = pos; this.pos = pos;
} }
} }
@@ -1,13 +1,14 @@
package me.retucio.sputnik.event.interact; package me.retucio.sputnik.event.interact;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.player.ClientPlayerInteractionManagerMixin; import me.retucio.sputnik.mixin.mixins.player.MultiPlayerGameModeMixin;
import net.minecraft.util.math.BlockPos; import net.minecraft.core.BlockPos;
/** /**
* @see ClientPlayerInteractionManagerMixin#onBlockBreak * @see MultiPlayerGameModeMixin#onBlockBreak
*/ */
public class BreakBlockEvent extends Event { public class BreakBlockEvent extends Event {
private BlockPos pos; private BlockPos pos;
@@ -2,7 +2,7 @@ package me.retucio.sputnik.event.interact;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.item.ItemStackMixin; import me.retucio.sputnik.mixin.mixins.item.ItemStackMixin;
import net.minecraft.item.ItemStack; import net.minecraft.world.item.ItemStack;
/** /**
@@ -1,20 +1,21 @@
package me.retucio.sputnik.event.interact; package me.retucio.sputnik.event.interact;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.player.ClientPlayerInteractionManagerMixin; import me.retucio.sputnik.mixin.mixins.player.MultiPlayerGameModeMixin;
import net.minecraft.entity.Entity; import net.minecraft.world.InteractionHand;
import net.minecraft.util.Hand; import net.minecraft.world.entity.Entity;
/** /**
* @see ClientPlayerInteractionManagerMixin#onEntityInteract * @see MultiPlayerGameModeMixin#onEntityInteract
*/ */
public class InteractEntityEvent extends Event { public class InteractEntityEvent extends Event {
private final Entity entity; private final Entity entity;
private Hand hand; private InteractionHand hand;
public InteractEntityEvent(Entity entity, Hand hand) { public InteractEntityEvent(Entity entity, InteractionHand hand) {
this.entity = entity; this.entity = entity;
this.hand = hand; this.hand = hand;
} }
@@ -23,11 +24,11 @@ public class InteractEntityEvent extends Event {
return entity; return entity;
} }
public Hand getHand() { public InteractionHand getHand() {
return hand; return hand;
} }
public void setHand(Hand hand) { public void setHand(InteractionHand hand) {
this.hand = hand; this.hand = hand;
} }
} }
@@ -0,0 +1,46 @@
package me.retucio.sputnik.event.interact;
import com.github.retucio.neutrino.Event;
import net.minecraft.world.entity.LivingEntity;
public class KnockbackEvent extends Event {
private double strength;
private double x, z;
private final LivingEntity target;
public KnockbackEvent(LivingEntity target, double z, double x, double strength) {
this.target = target;
this.z = z;
this.x = x;
this.strength = strength;
}
public double getStrength() {
return strength;
}
public void setStrength(double strength) {
this.strength = strength;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getZ() {
return z;
}
public void setZ(double z) {
this.z = z;
}
public LivingEntity getTarget() {
return target;
}
}
@@ -1,12 +1,12 @@
package me.retucio.sputnik.event.interact; package me.retucio.sputnik.event.interact;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.misc.MinecraftClientMixin; import me.retucio.sputnik.mixin.mixins.misc.MinecraftMixin;
import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screens.Screen;
/** /**
* @see MinecraftClientMixin#onOpenScreen * @see MinecraftMixin#onOpenScreen
*/ */
public class OpenScreenEvent extends Event { public class OpenScreenEvent extends Event {
@@ -1,29 +1,29 @@
package me.retucio.sputnik.event.interact; package me.retucio.sputnik.event.interact;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.player.ClientPlayerInteractionManagerMixin; import me.retucio.sputnik.mixin.mixins.player.MultiPlayerGameModeMixin;
import net.minecraft.util.Hand; import net.minecraft.world.InteractionHand;
import net.minecraft.util.hit.BlockHitResult; import net.minecraft.world.phys.BlockHitResult;
/** /**
* @see ClientPlayerInteractionManagerMixin#onBlockPlace * @see MultiPlayerGameModeMixin#onBlockPlace
*/ */
public class PlaceBlockEvent extends Event { public class PlaceBlockEvent extends Event {
private Hand hand; private InteractionHand hand;
private BlockHitResult result; private BlockHitResult result;
public PlaceBlockEvent(Hand hand, BlockHitResult result) { public PlaceBlockEvent(InteractionHand hand, BlockHitResult result) {
this.hand = hand; this.hand = hand;
this.result = result; this.result = result;
} }
public Hand getHand() { public InteractionHand getHand() {
return hand; return hand;
} }
public void setHand(Hand hand) { public void setHand(InteractionHand hand) {
this.hand = hand; this.hand = hand;
} }
@@ -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;
}
}
@@ -1,20 +1,21 @@
package me.retucio.sputnik.event.interact; package me.retucio.sputnik.event.interact;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.misc.MinecraftClientMixin; import me.retucio.sputnik.mixin.mixins.misc.MinecraftMixin;
import net.minecraft.item.ItemStack; import net.minecraft.world.InteractionHand;
import net.minecraft.util.Hand; import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
/** /**
* @see MinecraftClientMixin#onUseItem * @see MinecraftMixin#onUseItem
*/ */
public class UseItemEvent extends Event { public class UseItemEvent extends Event {
private ItemStack stack; private ItemStack stack;
private Hand hand; private InteractionHand hand;
public UseItemEvent(ItemStack stack, Hand hand) { public UseItemEvent(ItemStack stack, InteractionHand hand) {
this.stack = stack; this.stack = stack;
this.hand = hand; this.hand = hand;
} }
@@ -27,11 +28,15 @@ public class UseItemEvent extends Event {
this.stack = stack; this.stack = stack;
} }
public Hand getHand() { public Item getItem() {
return stack.getItem();
}
public InteractionHand getHand() {
return hand; return hand;
} }
public void setHand(Hand hand) { public void setHand(InteractionHand hand) {
this.hand = hand; this.hand = hand;
} }
} }
@@ -1,12 +1,12 @@
package me.retucio.sputnik.event.network; package me.retucio.sputnik.event.network;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.world.ClientWorldMixin; import me.retucio.sputnik.mixin.mixins.world.ClientLevelMixin;
import net.minecraft.entity.Entity; import net.minecraft.world.entity.Entity;
/** /**
* @see ClientWorldMixin#onAddEntity * @see ClientLevelMixin#onAddEntity
*/ */
public class AddEntityEvent extends Event { public class AddEntityEvent extends Event {
@@ -1,10 +1,10 @@
package me.retucio.sputnik.event.network; package me.retucio.sputnik.event.network;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.world.ChunkOcclusionDataBuilderMixin; import me.retucio.sputnik.mixin.mixins.world.VisGraphMixin;
/** /**
* @see ChunkOcclusionDataBuilderMixin#onChunkOcclusion * @see VisGraphMixin#onChunkOcclusion
*/ */
public class ChunkOcclusionEvent extends Event {} public class ChunkOcclusionEvent extends Event {}
@@ -1,29 +1,29 @@
package me.retucio.sputnik.event.network; package me.retucio.sputnik.event.network;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.network.ClientConnectionMixin; import me.retucio.sputnik.mixin.mixins.network.ConnectionMixin;
import net.minecraft.client.network.ServerInfo; import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.network.DisconnectionInfo; import net.minecraft.network.DisconnectionDetails;
/** /**
* @see ClientConnectionMixin#onDisconnect * @see ConnectionMixin#onDisconnect
*/ */
public class DisconnectEvent extends Event { public class DisconnectEvent extends Event {
private final DisconnectionInfo info; private final DisconnectionDetails details;
private final ServerInfo server; private final ServerData server;
public DisconnectEvent(DisconnectionInfo info, ServerInfo server) { public DisconnectEvent(DisconnectionDetails details, ServerData server) {
this.info = info; this.details = details;
this.server = server; this.server = server;
} }
public DisconnectionInfo getInfo() { public DisconnectionDetails getDetails() {
return info; return details;
} }
public ServerInfo getServer() { public ServerData getServer() {
return server; return server;
} }
} }
@@ -1,22 +1,23 @@
package me.retucio.sputnik.event.network; package me.retucio.sputnik.event.network;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.misc.MinecraftClientMixin; import me.retucio.sputnik.mixin.mixins.misc.MinecraftMixin;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.multiplayer.ClientLevel;
/** /**
* @see MinecraftClientMixin#onJoinWorld * @see MinecraftMixin#onJoinWorld
*/ */
public class JoinWorldEvent extends Event { public class JoinWorldEvent extends Event {
private final ClientWorld world; private final ClientLevel world;
public JoinWorldEvent(ClientWorld world) { public JoinWorldEvent(ClientLevel world) {
this.world = world; this.world = world;
} }
public ClientWorld getWorld() { public ClientLevel getWorld() {
return world; return world;
} }
} }
@@ -1,14 +1,14 @@
package me.retucio.sputnik.event.network; package me.retucio.sputnik.event.network;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.network.ClientConnectionMixin; import me.retucio.sputnik.mixin.mixins.network.ConnectionMixin;
import net.minecraft.network.packet.Packet; import net.minecraft.network.protocol.Packet;
/** /**
* @see ClientConnectionMixin#onSendPacketPre * @see ConnectionMixin#onSendPacketPre
* @see ClientConnectionMixin#onSendPacketPost * @see ConnectionMixin#onSendPacketPost
* @see ClientConnectionMixin#onReceivePacket * @see ConnectionMixin#onReceivePacket
*/ */
public class PacketEvent { public class PacketEvent {
@@ -1,41 +1,43 @@
package me.retucio.sputnik.event.network; package me.retucio.sputnik.event.network;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.hud.ChatHudMixin; import me.retucio.sputnik.mixin.mixins.hud.ChatComponentMixin;
import net.minecraft.client.gui.hud.MessageIndicator; import net.minecraft.client.multiplayer.chat.GuiMessageTag;
import net.minecraft.text.Text; import net.minecraft.network.chat.Component;
/** /**
* @see ChatHudMixin#onAddMessage * @see ChatComponentMixin#onAddMessage
*/ */
public class ReceiveMessageEvent extends Event { public class ReceiveMessageEvent extends Event {
private Text text; private Component text;
private MessageIndicator indicator; private GuiMessageTag tag;
private final int id; private final int id;
private boolean modified = false; private boolean modified = false;
public ReceiveMessageEvent(Text text, MessageIndicator indicator, int id) { public ReceiveMessageEvent(Component text, GuiMessageTag tag, int id) {
this.text = text; this.text = text;
this.indicator = indicator; this.tag = tag;
this.id = id; this.id = id;
} }
public Text getMessage() { public Component getMessage() {
return text; return text;
} }
public MessageIndicator getIndicator() { public GuiMessageTag getTag() {
return indicator; return tag;
} }
public void setMessage(Text message) { public void setMessage(Component message) {
this.text = message; this.text = message;
this.modified = true; this.modified = true;
} }
public void setIndicator(MessageIndicator indicator) { public void setTag(GuiMessageTag tag) {
this.indicator = indicator; this.tag = tag;
this.modified = true; this.modified = true;
} }
@@ -1,12 +1,12 @@
package me.retucio.sputnik.event.network; package me.retucio.sputnik.event.network;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.world.ClientWorldMixin; import me.retucio.sputnik.mixin.mixins.world.ClientLevelMixin;
import net.minecraft.entity.Entity; import net.minecraft.world.entity.Entity;
/** /**
* @see ClientWorldMixin#onRemoveEntity * @see ClientLevelMixin#onRemoveEntity
*/ */
public class RemoveEntityEvent extends Event { public class RemoveEntityEvent extends Event {
@@ -1,11 +1,11 @@
package me.retucio.sputnik.event.network; package me.retucio.sputnik.event.network;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.network.ClientPlayNetworkHandlerMixin; import me.retucio.sputnik.mixin.mixins.network.ClientPacketListenerMixin;
/** /**
* @see ClientPlayNetworkHandlerMixin#onSendMessage * @see ClientPacketListenerMixin#onSendMessage
*/ */
public class SendMessageEvent extends Event { public class SendMessageEvent extends Event {
@@ -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;
}
}
@@ -1,26 +1,26 @@
package me.retucio.sputnik.event.render; package me.retucio.sputnik.event.render;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.misc.GameOptionsMixin; import me.retucio.sputnik.mixin.mixins.misc.OptionsMixin;
import net.minecraft.client.option.Perspective; import net.minecraft.client.CameraType;
/** /**
* @see GameOptionsMixin#changePerspective * @see OptionsMixin#changePerspective
*/ */
public class PerspectiveChangeEvent extends Event { public class PerspectiveChangeEvent extends Event {
private Perspective perspective; private CameraType perspective;
public PerspectiveChangeEvent(Perspective perspective) { public PerspectiveChangeEvent(CameraType perspective) {
this.perspective = perspective; this.perspective = perspective;
} }
public Perspective getPerspective() { public CameraType getPerspective() {
return perspective; return perspective;
} }
public void setPerspective(Perspective perspective) { public void setPerspective(CameraType perspective) {
this.perspective = perspective; this.perspective = perspective;
} }
} }
@@ -1,24 +1,26 @@
package me.retucio.sputnik.event.render; package me.retucio.sputnik.event.render;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.RenderTickCounter; import net.minecraft.client.DeltaTracker;
import net.minecraft.client.gui.GuiGraphicsExtractor;
public class Render2DEvent extends Event { public class Render2DEvent extends Event {
private final DrawContext ctx; private final GuiGraphicsExtractor gui;
private final RenderTickCounter tc; private final DeltaTracker dt;
public Render2DEvent(DrawContext ctx, RenderTickCounter tc) { public Render2DEvent(GuiGraphicsExtractor gui, DeltaTracker dt) {
this.ctx = ctx; this.gui = gui;
this.tc = tc; this.dt = dt;
} }
public DrawContext getCtx() { public GuiGraphicsExtractor getGui() {
return ctx; return gui;
} }
public RenderTickCounter getTc() { public DeltaTracker getDt() {
return tc; return dt;
} }
} }
@@ -1,40 +1,42 @@
package me.retucio.sputnik.event.render; package me.retucio.sputnik.event.render;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.render.WorldRendererMixin; import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.render.Camera; import me.retucio.sputnik.mixin.mixins.render.LevelRendererMixin;
import net.minecraft.client.render.RenderTickCounter; import net.minecraft.client.Camera;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.DeltaTracker;
import net.minecraft.client.renderer.state.level.CameraRenderState;
/** /**
* @see WorldRendererMixin#render * @see LevelRendererMixin#renderLevel
*/ */
public class Render3DEvent extends Event { public class Render3DEvent extends Event {
private final MatrixStack matrices; private final PoseStack matrices;
private final RenderTickCounter tc; private final DeltaTracker dt;
private final Camera camera; private final CameraRenderState cameraState;
public Render3DEvent(MatrixStack matrices, RenderTickCounter tc, Camera camera) { public Render3DEvent(PoseStack matrices, DeltaTracker dt, CameraRenderState cameraState) {
this.matrices = matrices; this.matrices = matrices;
this.tc = tc; this.dt = dt;
this.camera = camera; this.cameraState = cameraState;
} }
public MatrixStack getMatrices() { public PoseStack getMatrices() {
return matrices; return matrices;
} }
public RenderTickCounter getTickCounter() { public DeltaTracker getDeltaTracker() {
return tc; return dt;
} }
public Camera getCamera() { public CameraRenderState getCamera() {
return camera; return cameraState;
} }
public float getTickDelta() { public float getTickDelta() {
return tc.getDynamicDeltaTicks(); return dt.getGameTimeDeltaTicks();
} }
} }
@@ -1,37 +1,37 @@
package me.retucio.sputnik.event.render; package me.retucio.sputnik.event.render;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.render.HeldItemRendererMixin; import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.util.math.MatrixStack; import me.retucio.sputnik.mixin.mixins.render.ItemInHandRendererMixin;
import net.minecraft.util.Hand; import net.minecraft.world.InteractionHand;
/** /**
* @see HeldItemRendererMixin#onRenderArm * @see ItemInHandRendererMixin#onRenderArm
*/ */
public class RenderArmEvent extends Event { public class RenderArmEvent extends Event {
private MatrixStack matrices; private PoseStack matrices;
private Hand hand; private InteractionHand hand;
public RenderArmEvent(MatrixStack matrices, Hand hand) { public RenderArmEvent(PoseStack matrices, InteractionHand hand) {
this.matrices = matrices; this.matrices = matrices;
this.hand = hand; this.hand = hand;
} }
public MatrixStack getMatrices() { public PoseStack getMatrices() {
return matrices; return matrices;
} }
public void setMatrices(MatrixStack matrices) { public void setMatrices(PoseStack matrices) {
this.matrices = matrices; this.matrices = matrices;
} }
public Hand getHand() { public InteractionHand getHand() {
return hand; return hand;
} }
public void setHand(Hand hand) { public void setHand(InteractionHand hand) {
this.hand = hand; this.hand = hand;
} }
} }
@@ -2,9 +2,10 @@ package me.retucio.sputnik.event.render;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.hud.BossBarHudMixin; import me.retucio.sputnik.mixin.mixins.hud.BossBarHudMixin;
import net.minecraft.client.gui.hud.ClientBossBar; import net.minecraft.client.gui.components.LerpingBossEvent;
import net.minecraft.text.Text; import net.minecraft.network.chat.MutableComponent;
import java.awt.*;
import java.util.Iterator; import java.util.Iterator;
@@ -17,27 +18,27 @@ public class RenderBossbarEvent {
public static class BossText extends Event { public static class BossText extends Event {
private ClientBossBar bossBar; private LerpingBossEvent bossBar;
private Text name; private MutableComponent name;
public BossText(ClientBossBar bossBar, Text name) { public BossText(LerpingBossEvent bossBar, MutableComponent name) {
this.bossBar = bossBar; this.bossBar = bossBar;
this.name = name; this.name = name;
} }
public ClientBossBar getBossBar() { public LerpingBossEvent getBossBar() {
return bossBar; return bossBar;
} }
public void setBossBar(ClientBossBar bossBar) { public void setBossBar(LerpingBossEvent bossBar) {
this.bossBar = bossBar; this.bossBar = bossBar;
} }
public Text getName() { public MutableComponent getName() {
return name; return name;
} }
public void setName(Text name) { public void setName(MutableComponent name) {
this.name = name; this.name = name;
} }
} }
@@ -61,17 +62,17 @@ public class RenderBossbarEvent {
public static class BossIterator extends Event { public static class BossIterator extends Event {
private Iterator<ClientBossBar> iterator; private Iterator<LerpingBossEvent> iterator;
public BossIterator(Iterator<ClientBossBar> iterator) { public BossIterator(Iterator<LerpingBossEvent> iterator) {
this.iterator = iterator; this.iterator = iterator;
} }
public Iterator<ClientBossBar> getIterator() { public Iterator<LerpingBossEvent> getIterator() {
return iterator; return iterator;
} }
public void setIterator(Iterator<ClientBossBar> iterator) { public void setIterator(Iterator<LerpingBossEvent> iterator) {
this.iterator = iterator; this.iterator = iterator;
} }
} }
@@ -1,37 +1,38 @@
package me.retucio.sputnik.event.render; package me.retucio.sputnik.event.render;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
import me.retucio.sputnik.mixin.mixins.render.HeldItemRendererMixin; import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.util.math.MatrixStack; import me.retucio.sputnik.mixin.mixins.render.ItemInHandRendererMixin;
import net.minecraft.util.Hand; import net.minecraft.world.InteractionHand;
/** /**
* @see HeldItemRendererMixin#onRenderItem * @see ItemInHandRendererMixin#onRenderItem
*/ */
public class RenderHeldItemEvent extends Event { public class RenderHeldItemEvent extends Event {
private MatrixStack matrices; private PoseStack matrices;
private Hand hand; private InteractionHand hand;
public RenderHeldItemEvent(MatrixStack matrices, Hand hand) { public RenderHeldItemEvent(PoseStack matrices, InteractionHand hand) {
this.matrices = matrices; this.matrices = matrices;
this.hand = hand; this.hand = hand;
} }
public MatrixStack getMatrices() { public PoseStack getMatrices() {
return matrices; return matrices;
} }
public void setMatrices(MatrixStack matrices) { public void setMatrices(PoseStack matrices) {
this.matrices = matrices; this.matrices = matrices;
} }
public Hand getHand() { public InteractionHand getHand() {
return hand; return hand;
} }
public void setHand(Hand hand) { public void setHand(InteractionHand hand) {
this.hand = hand; this.hand = hand;
} }
} }
@@ -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,7 @@ package me.retucio.sputnik.event.sputnik;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
// se genera cada que se interactúa con el marco de ajustes de la interfaz // se genera cada que se interactúa con el marco de ajustes de la interfaz
public class GUISettingsFrameEvent { public class ClientSettingsPanelEvent {
public static class Extend extends Event {} // al extenderlo / contraerlo public static class Extend extends Event {} // al extenderlo / contraerlo
public static class Move extends Event {} // al cambiarlo de posición public static class Move extends Event {} // al cambiarlo de posición
@@ -0,0 +1,26 @@
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
public class FriendEvent extends Event {
private final Friend friend;
public FriendEvent(Friend friend) {
this.friend = friend;
}
public Friend getFriend() {
return friend;
}
public static class Add extends FriendEvent {
public Add(Friend friend) { super(friend); }
}
public static class Remove extends FriendEvent {
public Remove(Friend friend) { super(friend); }
}
}
@@ -2,8 +2,7 @@ package me.retucio.sputnik.event.sputnik;
import com.github.retucio.neutrino.Event; import com.github.retucio.neutrino.Event;
// se genera cada que se interactúa con el marco de módulos public class FriendPanelEvent {
public class ModuleFrameEvent {
public static class Extend extends Event {} // al extenderlo / contraerlo public static class Extend extends Event {} // al extenderlo / contraerlo
public static class Move extends Event {} // al cambiarlo de posición public static class Move extends Event {} // al cambiarlo de posición
Loaded 100 of 453 files, more files were not shown because too many files have changed in this diff. Show more