Compare commits

...
65 Commits
Author SHA1 Message Date
retucio 4307b0a479 v6.7
build / build (push) Waiting to run
2026-11-01 17:57:33 +01:00
retucio ab191a9016 v6.6 2026-10-11 11:56:22 +02:00
retucio 629d03ff92 v6.5 2026-09-14 19:17:07 +02:00
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
retuci0 acecda3c0d v3.5 2026-02-19 17:20:31 +01:00
retuci0 ad0349aa29 v3.4 2026-02-17 22:19:09 +01:00
retuci0 f1eed6f957 v3.3 2026-02-15 22:41:02 +01:00
retuci0 c993d4fdac v3.2 2026-02-10 16:06:38 +01:00
retuci0 a9fd8da7f5 v3.1 2026-02-09 17:52:39 +01:00
retuci0 9b6d037b6c v3.0 2026-02-01 13:46:01 +01:00
retuci0 86594f1ca3 v2.9 2026-01-18 22:53:21 +01:00
retuci0 f074f05c51 v2.8 2026-01-17 17:12:54 +01:00
retuci0 436b79fd2b v2.7 2026-01-17 00:33:24 +01:00
retuci0 9afaa4e0c8 v2.6 2026-01-11 11:12:44 +01:00
retuci0 f794457538 v2.5 2026-01-06 18:09:52 +01:00
retuci0 ec3e633cec v2.4 2026-01-05 22:16:01 +01:00
retuci0 8f17bd8e67 v2.3 - sputnik rename 2026-01-04 17:46:51 +01:00
retuci0 dc2429918e v2.2 2026-01-04 11:40:06 +01:00
retuci0 94a9b1dade v2.1 2026-01-02 13:59:08 +01:00
retuci0 48899f85f9 v2.0.1 2026-01-01 18:29:46 +01:00
retuci0 347ec658bb v2.0 2025-12-31 00:44:36 +01:00
retuci0 a9cf6dd924 v1.9.1 2025-12-28 00:58:08 +01:00
retuci0 8e44dda9cf v1.9 2025-12-26 22:45:43 +01:00
retuci0 2329476fe4 v1.8 2025-12-22 18:54:02 +01:00
retuci0 6b2ec0e69a v1.7.1 2025-12-14 21:31:58 +01:00
retuci0 6fa409d60a v1.7 2025-12-10 19:26:51 +01:00
retuci0 4f816381c3 upsi 2025-12-07 09:01:21 +01:00
retuci0 36c38bdf02 ups 2025-12-07 09:00:41 +01:00
retuci0 a6a6f1261e v1.5 2025-12-07 08:59:33 +01:00
retuci0 8891a57c7c v1.4 2025-10-12 13:00:18 +02:00
retuci0 e358b142c6 v1.3 2025-09-28 14:35:16 +02:00
retuci0 e7d872d75e mejoras a interfaz 1 2025-09-04 12:35:36 +02:00
retuci0 589279342d actualizar README 2025-09-01 18:43:45 +02:00
retuci0 054702999b v1.2 2025-09-01 18:22:39 +02:00
retuci0 ec7ff3a058 v1.1 2025-08-27 19:24:04 +02:00
603 changed files with 38452 additions and 3776 deletions

No files matched your search

+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
- name: setup jdk
uses: actions/setup-java@v4
with:
java-version: '21'
java-version: '25'
distribution: 'microsoft'
- name: make gradle wrapper executable
run: chmod +x ./gradlew
+6 -1
View File
@@ -39,4 +39,9 @@ replay_*.log
*.hprof
*.jfr
todo.txt
# otros
todo.txt
cambios.md
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.3 mod that adds some "QOL" features, mostly related to the camera.
## installation
### for normal people:
1. go to [releases](https://git.retucio.me/retucio/sputnik/releases)
2. choose the most recent version
3. download the .jar file
4. move it to your mods dir (prolly `%appdata%/.minecraft/mods`)
make sure to have a Minecraft and Fabric version compatible with the mod (MC `26.3`; Fabric `0.19.5`)
issues could arise when using it with other mods, especially other clients, since they could conflict
### for nerds
1. clone the repo (`git clone https://git.retucio.me/retucio/sputnik`)
2. enter the repo dir (`cd sputnik`)
3. `gradlew build` for Windows (ew) or `chmod +x gradlew` + `./gradlew build` for Unix-likes (Linux & Mac)
4. the built .jar will be found in `/build/libs/sputnik-X.X.jar`
## usage
`rshift` by default to open the GUI
`insert` by default to open the HUD editor
`$` as prefix for commands (autocompleted)
some modules have keys assigned by default, assign them `ESC` to unbind them.
## ui
- left click to toggle modules
- right click to open its settings
- left click + drag to move a panel
- right click on a panel's header to expand/contract it, or to close it
- left click over settings to change them
- shift + right click on a setting to reset it to its default value
- place the mouse cursor over a module/setting to read its description
## screenshots
<img src="screenshots/automine.png" alt="auto mine">
<img src="screenshots/blockesp.png" alt="block ESP + light overlay">
<img src="screenshots/camtweaks.png" alt="perspective plus">
<img src="screenshots/chatplus.png" alt="chatplus + nametags + screenshots plus">
<img src="screenshots/clickgui.png" alt="click GUI">
<img src="screenshots/freecam.png" alt="freecam + tracers">
<img src="screenshots/hudeditor.png" alt="HUD editor">
<img src="screenshots/itemphysics.png" alt="item physics">
<img src="screenshots/lsd.png" alt="lsd module">
<img src="screenshots/macros.png" alt="macros tab">
<img src="screenshots/trajectories.png" alt="projectile trajectories">
## contribution
yes please (PRs are welcome)
## license
[CC0 license](LICENSE)
## disclaimer
i don't give a single fuck what happens to you when using this mod on servers where its features are bannable and such.
## deps
- [fabric api](https://modrinth.com/mod/fabric-api/)
## notes
this mod IS BUGGY. VERY BUGGY, i know. i started this project when i was 14 and the codebase is such a mess i refuse to fix it. i might rewrite it in a future, though.
+79
View File
@@ -0,0 +1,79 @@
<p align="center">
<a href="README.md"><img src="https://img.shields.io/badge/lang-Español-yellow.svg" alt="español"></a>
<a href="README.en.md"><img src="https://img.shields.io/badge/lang-English-blue.svg" alt="english"></a>
</p>
# jenny's mod 2 100% real
> mod para Minecraft Fabric 26.3 que añade algunas cosas "QOL", mayormente relacionadas a la cámara.
## instalación
### para gente normal:
1. ir a [lanzamientos](https://git.retucio.me/retucio/sputnik/releases)
2. seleccionar la versión más reciente
3. descargar el .jar
4. moverlo a tu directorio de mods (probablemente `%appdata%/.minecraft/mods`)
asegúrate de tener una version de Minecraft y Fabric compatibles con el mod (MC `26.3`; Fabric `0.19.5`)
es posible que haya problemas al usarlo junto a otros mods, especialmente junto a otros clientes, pues pueden conflictar entre ellos.
### para frikis de 200 de iq
1. clona el respositorio (`git clone https://git.retucio.me/retucio/sputnik`)
2. entra al directorio del repositorio (`cd sputnik`)
3. `gradlew build` para Windows (ew) o `chmod +x gradlew` + `./gradlew build` para Unix-likes (Linux & Mac)
4. el resultado estará en `/build/libs/sputnik-X.X.jar`
## uso
`shift derecho` por defecto para abrir la interfaz
`insertar` por defecto para abrir el editor del hud
`$` como prefijo por defecto para comandos (se autocompletan)
algunos módulos tienen teclas asignadas por defecto, asígnales `ESC` para desactivar sus atajos de teclado
## interfaz
- clic izquierdo para activar / desactivar módulos
- clic derecho para abrir sus ajustes
- clic izquierdo + arrastrar para mover paneles
- clic derecho en la cabeza del marco para expandirlo / contraerlo o para cerrarlo
- clic izquierdo sobre ajustes para modificarlos
- shift + clic derecho sobre ajustes para restablecerlos
- posiciona el puntero del ratón sobre un módulo o ajuste para leer su descripción
## capturas
<img src="screenshots/automine.png" alt="auto minado">
<img src="screenshots/blockesp.png" alt="resaltado de bloques + superposición de luz">
<img src="screenshots/camtweaks.png" alt="perspectiva plus">
<img src="screenshots/chatplus.png" alt="chatplus + nametags + capturas">
<img src="screenshots/clickgui.png" alt="interfaz">
<img src="screenshots/freecam.png" alt="cámara libre + trazos">
<img src="screenshots/hudeditor.png" alt="editor de HUD">
<img src="screenshots/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">
## contribuciones
sí por favor
## licencia
[licencia CC0](LICENSE)
## descargo de responsabilidad
me importa una puta mierda lo que pase al usar este mod en servers donde es baneable y así.
## dependencias
- [fabric api](https://modrinth.com/mod/fabric-api/)
## notas
este mod tiene MUCHOS bugs. empecé este proyecto cuando tenía 14 años y no sabía una mierda de programar. el código es un puto desastre y me niego a arreglarlo. puede que en un futuro reescriba el mod o algo. no sé.
+52 -45
View File
@@ -1,78 +1,85 @@
plugins {
id 'fabric-loom' version "${loom_version}"
id 'maven-publish'
id "net.fabricmc.fabric-loom" version "${loom_version}"
id 'maven-publish'
}
version = project.mod_version
group = project.maven_group
base {
archivesName = project.archives_base_name
archivesName = project.archives_base_name
}
repositories {
// Add repositories to retrieve artifacts from in here.
// You should only use this when depending on other mods because
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories.
mavenCentral()
maven { url 'https://jitpack.io' }
maven {
name = "FDD Maven"
url = "https://maven.firstdark.dev/releases"
}
}
dependencies {
// To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// mc
minecraft "com.mojang:minecraft:${project.minecraft_version}"
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
// fabric & su api
implementation "net.fabricmc:fabric-loader:${project.loader_version}"
implementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
// sistema de eventos
include(implementation "com.github.retuci0:neutrino:${project.neutrino_version}")
// rpc de discord
include(implementation "dev.firstdark.discordrpc:discord-rpc:${project.discord_rpc_version}")
}
loom {
accessWidenerPath = file("src/main/resources/sputnik.accesswidener")
}
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") {
expand "version": inputs.properties.version
}
filesMatching("fabric.mod.json") {
expand(
"version": project.version,
"loader_version": project.loader_version,
"minecraft_version": project.minecraft_version,
"jdk_version": project.jdk_version
)
}
}
tasks.withType(JavaCompile).configureEach {
it.options.release = 21
it.options.release = jdk_version.toInteger()
}
java {
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this line, sources will not be generated.
withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
sourceCompatibility = JavaVersion.toVersion(jdk_version)
targetCompatibility = JavaVersion.toVersion(jdk_version)
}
jar {
inputs.property "archivesName", project.base.archivesName
inputs.property "archivesName", project.base.archivesName
from("LICENSE") {
rename { "${it}_${inputs.properties.archivesName}"}
}
from("LICENSE") {
rename {
"${it}_${inputs.properties.archivesName}"
}
}
}
// configure the maven publication
publishing {
publications {
create("mavenJava", MavenPublication) {
artifactId = project.archives_base_name
from components.java
}
}
publications {
create("mavenJava", MavenPublication) {
artifactId = project.archives_base_name
from components.java
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
// Notice: This block does NOT have the same function as the block in the top level.
// The repositories here will be used for publishing your artifact, not for
// retrieving dependencies.
}
repositories {}
}
+17
View File
@@ -0,0 +1,17 @@
# v6.7
## novedades
- SIC SEBEN
- migrado de Codeberg a mi propia instancia de Gitea
### adiciones
### arreglos / mejoras
## problemas reconocidos
- `reponer` no funciona
- `copiar` tras sacar una captura con `capturas` no funciona correctamente
- `manos` no funciona del todo
- \+ lo de siempre.
+17 -3
View File
@@ -1,3 +1,17 @@
meteor client
oyvey client (1.21 port by mioclient)
lennyboi (youtube)
Meteor Client: https://github.com/MeteorDevelopment/meteor-client | https://meteorclient.com
OyVey Port: https://github.com/mioclient/oyvey-ported/
Lenny Boi: https://www.youtube.com/@lennyboi7195
Shotify: https://modrinth.com/mod/shotify
No Telemetry: https://modrinth.com/mod/no-telemetry
Trouser Streak: https://github.com/etianl/Trouser-Streak
No More Purple: https://github.com/Pepperoni-Jabroni/NoMorePurple/
bypass-resource-pack: https://github.com/emilyy-dev/bypass-resource-pack
elytra-recast: https://github.com/InLieuOfLuna/elytra-recast/
SpearHax: https://github.com/etianl/spearHax/
ProjectileTrajectoryPreview: https://github.com/maDU59/ProjectilesTrajectoryPreview
DupersUnited public addon: https://discord.gg/dupes | https://dupers.wtf
Stardust: https://github.com/0xTas/stardust/
probablemente más pero como tengo demencia se me olvida
si quieres que te acredite o quite tu código de mi mod, mándame un mensaje en discord (username: retucio) y sin problemas :)
también es probable que haya créditos sueltos en forma de comentarios donde haya usado código ajeno
+11 -14
View File
@@ -1,18 +1,15 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
org.gradle.jvmargs=-Xmx2G
org.gradle.parallel=true
# Fabric Properties
# check these on https://fabricmc.net/develop
minecraft_version=1.21.6
yarn_mappings=1.21.6+build.1
loader_version=0.17.2
loom_version=1.11-SNAPSHOT
minecraft_version=26.3
loader_version=0.19.5
loom_version=1.18-SNAPSHOT
# Mod Properties
mod_version=1.0.0
fabric_api_version=0.160.7+26.3
discord_rpc_version=1.0.3
neutrino_version=1.1.0
jdk_version=25
mod_version=6.7
maven_group=me.retucio
archives_base_name=camtweaks
# Dependencies
fabric_version=0.128.2+1.21.6
archives_base_name=sputnik
+2 -3
View File
@@ -1,7 +1,6 @@
#Sat Aug 29 22:10:52 CEST 2026
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored Regular → Executable
View File
File mode changed.
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.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

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.

After

Width:  |  Height:  |  Size: 1.5 MiB

+2 -2
View File
@@ -1,8 +1,8 @@
pluginManagement {
repositories {
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
name = "Fabric"
url = "https://maven.fabricmc.net/"
}
mavenCentral()
gradlePluginPortal()
@@ -1,114 +0,0 @@
package me.retucio.camtweaks;
import me.retucio.camtweaks.command.CommandManager;
import me.retucio.camtweaks.command.commands.BindCommand;
import me.retucio.camtweaks.event.EventBus;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.ModuleManager;
import me.retucio.camtweaks.ui.ClickGUI;
import me.retucio.camtweaks.ui.settings.*;
import net.fabricmc.api.ModInitializer;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.glfw.GLFW;
public class CameraTweaks implements ModInitializer {
// cosas necesarias
public static final String MOD_ID = "camtweaks";
public static final CameraTweaks INSTANCE = new CameraTweaks();
public static final Logger LOGGER = LogManager.getLogger(CameraTweaks.class);
public static final EventBus EVENT_BUS = new EventBus();
public static final MinecraftClient mc = MinecraftClient.getInstance();
private Screen prevScreen;
// managers
public static ModuleManager moduleManager = new ModuleManager(EVENT_BUS);
public static CommandManager commandManager = new CommandManager();
@Override
public void onInitialize() {}
// se ejecuta cada vez que se presiona una tecla
public void onKeyPress(int key, int action) {
moduleManager.getEnabledModules().forEach(module -> module.onKey(key, action));
boolean anyFocused = isAnySettingButtonFocused();
if (action == GLFW.GLFW_PRESS) {
handleModuleToggle(key, anyFocused);
handleSettingButtonsKey(key, action);
handleClickGUIKey(key, anyFocused);
BindCommand.onKeyPress(key);
} else if (action == GLFW.GLFW_RELEASE && !anyFocused) {
handleModuleRelease(key);
}
}
// verifica si algún botón de ajustes está escuchando
private boolean isAnySettingButtonFocused() {
for (SettingsFrame sf : ClickGUI.INSTANCE.getSettingsFrames()) {
for (SettingButton sb : sf.getButtons()) {
if ((sb instanceof BindButton b && b.isFocused()) || (sb instanceof TextButton t && t.isFocused()))
return true;
}
}
return false;
}
// se ocupa de la lógica de encendido y apagado de los módulos
private void handleModuleToggle(int key, boolean anyFocused) {
if (mc.currentScreen != null && mc.currentScreen != ClickGUI.INSTANCE) return;
for (Module module : moduleManager.getModules()) {
if (key != module.getKey() || anyFocused) continue;
if (module.shouldToggleOnBindRelease() && !module.isEnabled())
module.setEnabled(true);
else if (!module.shouldToggleOnBindRelease())
module.toggle();
}
}
// se ocupa de hacer los botones de ajustes que lo necesiten escuchar teclas
private void handleSettingButtonsKey(int key, int action) {
for (SettingsFrame sf : ClickGUI.INSTANCE.getSettingsFrames()) {
for (SettingButton sb : sf.getButtons()) {
if (sb instanceof BindButton b) b.onKey(key, action);
else if (sb instanceof TextButton t) t.onKey(key, action);
}
}
}
// maneja la lógica de apertura de la interfaz
private void handleClickGUIKey(int key, boolean anyFocused) {
if (key != ClickGUISettingsFrame.guiSettings.getKey() || anyFocused) return;
if (mc.currentScreen != ClickGUI.INSTANCE) {
prevScreen = mc.currentScreen;
mc.setScreen(ClickGUI.INSTANCE);
} else {
mc.setScreen(prevScreen);
}
}
// se ocupa de apagar los módulos que tengan configurado hacerlo tras soltar su tecla
private void handleModuleRelease(int key) {
if (mc.currentScreen != null && mc.currentScreen != ClickGUI.INSTANCE) return;
for (Module module : moduleManager.getEnabledModules()) {
if (module.shouldToggleOnBindRelease() && key == module.getKey()) {
module.setEnabled(false);
}
}
}
// se ejecuta cada tick, es decir, 20 veces por segundo
public void onTick() {
if (mc.player == null) return;
moduleManager.getEnabledModules().forEach(Module::onTick);
}
}
@@ -1,77 +0,0 @@
package me.retucio.camtweaks.command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import net.minecraft.client.MinecraftClient;
import net.minecraft.command.CommandRegistryAccess;
import net.minecraft.command.CommandSource;
import net.minecraft.registry.BuiltinRegistries;
import net.minecraft.server.command.CommandManager;
import java.util.List;
import static me.retucio.camtweaks.CameraTweaks.commandManager;
public abstract class Command {
// utilizar los "registries" ya existentes de Brigadier para el autocompletado
protected static final CommandRegistryAccess REGISTRY_ACCESS = CommandManager.createRegistryAccess(BuiltinRegistries.createWrapperLookup());
protected static final int SUCCESS = com.mojang.brigadier.Command.SINGLE_SUCCESS;
protected static final MinecraftClient mc = MinecraftClient.getInstance();
private final String name, description;
private final List<String> aliases;
public Command(String name, String description, String... aliases) {
this.name = name;
this.description = description;
this.aliases = List.of(aliases);
}
// métodos "helper" para reducir el "boilerplate"
protected static <T> RequiredArgumentBuilder<CommandSource, T> argument(final String name, final ArgumentType<T> type) {
return RequiredArgumentBuilder.argument(name, type);
}
protected static LiteralArgumentBuilder<CommandSource> literal(final String name) {
return LiteralArgumentBuilder.literal(name);
}
// para registrar comandos
public final void registerTo(CommandDispatcher<CommandSource> dispatcher) {
register(dispatcher, name);
for (String alias : aliases) register(dispatcher, alias);
}
public void register(CommandDispatcher<CommandSource> dispatcher, String name) {
LiteralArgumentBuilder<CommandSource> builder = LiteralArgumentBuilder.literal(name);
build(builder);
dispatcher.register(builder);
}
public abstract void build(LiteralArgumentBuilder<CommandSource> builder);
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public List<String> getAliases() {
return aliases;
}
public String toString() {
return commandManager.getPrefix() + name;
}
public String toString(String... args) {
StringBuilder sb = new StringBuilder(toString());
for (String arg : args) sb.append(" ").append(arg);
return sb.toString();
}
}
@@ -1,66 +0,0 @@
package me.retucio.camtweaks.command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.retucio.camtweaks.command.commands.*;
import net.minecraft.client.MinecraftClient;
import net.minecraft.command.CommandSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Comparator;
// registro de comandos
public class CommandManager {
private String prefix = "$";
public static final List<Command> commands = new ArrayList<>();
public static final CommandDispatcher<CommandSource> dispatcher = new CommandDispatcher<>();
private static final MinecraftClient mc = MinecraftClient.getInstance();
public CommandManager() {
registerCommands();
}
public static void registerCommands() {
addCommand(new BindCommand());
addCommand(new ToggleCommand());
addCommand(new SendCommand());
addCommand(new PrefixCommand());
commands.sort(Comparator.comparing(Command::getName));
}
public static void addCommand(Command command) {
commands.removeIf(existing -> existing.getName().equals(command.getName()));
command.registerTo(dispatcher);
commands.add(command);
}
public static void dispatch(String message) throws CommandSyntaxException {
dispatcher.execute(message, mc.getNetworkHandler().getCommandSource());
}
public static Command getCommandByName(String name) {
for (Command command : commands)
if (command.getName().equals(name))
return command;
return null;
}
public <T extends Command> T getCommandByClass(Class<T> clazz) {
for (Command command : commands)
if (clazz.isInstance(command))
return clazz.cast(command);
return null;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
}
@@ -1,49 +0,0 @@
package me.retucio.camtweaks.command.args;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import me.retucio.camtweaks.module.Module;
import net.minecraft.command.CommandSource;
import net.minecraft.text.Text;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import static me.retucio.camtweaks.CameraTweaks.moduleManager;
public class ModuleArgumentType implements ArgumentType<Module> {
public static final ModuleArgumentType INSTANCE = new ModuleArgumentType();
private static final DynamicCommandExceptionType unknownModuleE = new DynamicCommandExceptionType(
name -> Text.literal("módulo \"" + name + "\" no encontrado"));
private static final Collection<String> examples = moduleManager.getModules()
.stream().limit(3).map(Module::getName).toList();
@Override
public Module parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString();
Module module = moduleManager.getModuleByName(argument);
if (module == null) throw unknownModuleE.create(argument);
return module;
}
@Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return CommandSource.suggestMatching(moduleManager.getModules().stream().map(Module::getName), builder);
}
@Override
public Collection<String> getExamples() {
return examples;
}
public static Module get(CommandContext<CommandSource> ctx) {
return ctx.getArgument("módulo", Module.class);
}
}
@@ -1,55 +0,0 @@
package me.retucio.camtweaks.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.camtweaks.command.Command;
import me.retucio.camtweaks.command.args.ModuleArgumentType;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.KeySetting;
import me.retucio.camtweaks.util.ChatUtil;
import me.retucio.camtweaks.util.KeyUtil;
import net.minecraft.client.MinecraftClient;
import net.minecraft.command.CommandSource;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import static me.retucio.camtweaks.CameraTweaks.moduleManager;
public class BindCommand extends Command {
// The module currently waiting for a key press
private static Module listeningModule = null;
public BindCommand() {
super("bind", "asigna una tecla a un módulo", "keybind");
}
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder
.then(argument("módulo", ModuleArgumentType.INSTANCE)
.executes(ctx -> {
Module module = ctx.getArgument("módulo", Module.class);
listeningModule = module;
ChatUtil.info("presiona una tecla para asignarla al módulo " + module.getName());
return SUCCESS;
})
);
}
public static void onKeyPress(int key) {
if (listeningModule != null) {
KeySetting bind = listeningModule.getBind();
if (bind != null)
bind.setKey(key);
ChatUtil.info(
Text.of("la tecla " + Formatting.AQUA + KeyUtil.getKeyName(key) + Formatting.RESET +
" ha sido asignada al módulo " + Formatting.GREEN + listeningModule.getName())
);
listeningModule = null;
}
}
}
@@ -1,28 +0,0 @@
package me.retucio.camtweaks.command.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.camtweaks.command.Command;
import me.retucio.camtweaks.ui.settings.ClickGUISettingsFrame;
import me.retucio.camtweaks.util.ChatUtil;
import net.minecraft.command.CommandSource;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
public class PrefixCommand extends Command {
public PrefixCommand() {
super("prefijo", "cambia el prefijo de los comandos", "prefix");
}
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder
.then(argument("prefijo", StringArgumentType.word()).executes(ctx -> {
String prefix = ctx.getArgument("prefijo", String.class);
ClickGUISettingsFrame.guiSettings.commandPrefix.setValue(prefix);
ChatUtil.info(Text.literal("prefijo cambiado a " + Formatting.AQUA + prefix));
return SUCCESS;
}));
}
}
@@ -1,26 +0,0 @@
package me.retucio.camtweaks.command.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.camtweaks.command.Command;
import net.minecraft.command.CommandSource;
import net.minecraft.text.Text;
public class SendCommand extends Command {
public SendCommand() {
super("enviar", "manda un mensaje en el chat", "send");
}
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder
.then(argument("mensaje", StringArgumentType.greedyString())
.executes(ctx -> {
if (mc.player == null) return 0;
mc.player.sendMessage(Text.of(ctx.getArgument("mensaje", String.class)), false);
return SUCCESS;
})
);
}
}
@@ -1,14 +0,0 @@
package me.retucio.camtweaks.event;
// evento base, cancelable
public class Event {
private boolean cancelled;
public boolean isCancelled() {
return cancelled;
}
public void cancel() {
cancelled = true;
}
}
@@ -1,55 +0,0 @@
package me.retucio.camtweaks.event;
import java.lang.reflect.Method;
import java.util.*;
// al llamar un evento, se guarda en el bus de eventos, de donde luego se puede detectar si ese evento ha sucedido
public class EventBus {
private final Map<Class<?>, List<ListenerMethod>> listeners = new HashMap<>();
private record ListenerMethod(Object instance, Method method) {}
public void register(Object listener) {
for (Method method : listener.getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(Subscribe.class)) {
if (method.getParameterCount() != 1) continue; // must take 1 param
Class<?> eventType = method.getParameterTypes()[0];
method.setAccessible(true);
listeners.computeIfAbsent(eventType, k -> new ArrayList<>())
.add(new ListenerMethod(listener, method));
}
}
}
public void unregister(Object listener) {
for (List<ListenerMethod> list : listeners.values()) {
list.removeIf(lm -> lm.instance == listener);
}
}
public <T extends Event> T post(T event) {
List<ListenerMethod> list = listeners.get(event.getClass());
if (list != null) {
for (ListenerMethod lm : list) {
try {
lm.method.invoke(lm.instance, event);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return event;
}
public boolean isRegistered(Object listener) {
for (List<ListenerMethod> list : listeners.values()) {
for (ListenerMethod lm : list) {
if (lm.instance == listener) {
return true;
}
}
}
return false;
}
}
@@ -1,8 +0,0 @@
package me.retucio.camtweaks.event;
import java.lang.annotation.*;
// anotación para registar un "listener", que escucha cuando se llama un evento
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Subscribe {}
@@ -1,22 +0,0 @@
package me.retucio.camtweaks.event.events;
import net.minecraft.text.ClickEvent;
public class ClientClickEvent implements ClickEvent {
private final String value;
public ClientClickEvent(String value) {
this.value = value;
}
public String getValue() {
return value;
}
@Override
public Action getAction() {
return Action.RUN_COMMAND;
}
}
@@ -1,32 +0,0 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Hand;
public class HeldItemRendererEvent extends Event {
private MatrixStack matrices;
private Hand hand;
public HeldItemRendererEvent(MatrixStack matrices, Hand hand) {
this.matrices = matrices;
this.hand = hand;
}
public MatrixStack getMatrices() {
return matrices;
}
public void setMatrices(MatrixStack matrices) {
this.matrices = matrices;
}
public Hand getHand() {
return hand;
}
public void setHand(Hand hand) {
this.hand = hand;
}
}
@@ -1,22 +0,0 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
import net.minecraft.client.option.Perspective;
// se genera en GameOptionsMixin
public class PerspectiveChangeEvent extends Event {
private Perspective perspective;
public PerspectiveChangeEvent(Perspective perspective) {
this.perspective = perspective;
}
public Perspective getPerspective() {
return perspective;
}
public void setPerspective(Perspective perspective) {
this.perspective = perspective;
}
}
@@ -1,43 +0,0 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
import net.minecraft.client.gui.hud.MessageIndicator;
import net.minecraft.text.Text;
public class ReceiveMessageEvent extends Event {
private Text text;
private MessageIndicator indicator;
private int id;
private boolean modified = false;
public ReceiveMessageEvent(Text text, MessageIndicator indicator, int id) {
this.text = text;
this.indicator = indicator;
this.id = id;
}
public Text getMessage() {
return text;
}
public MessageIndicator getIndicator() {
return indicator;
}
public void setMessage(Text message) {
this.text = message;
this.modified = true;
}
public void setIndicator(MessageIndicator indicator) {
this.indicator = indicator;
this.modified = true;
}
public boolean wasModified() {
return modified;
}
}
@@ -1,32 +0,0 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Hand;
public class RenderArmEvent extends Event {
private MatrixStack matrices;
private Hand hand;
public RenderArmEvent(MatrixStack matrices, Hand hand) {
this.matrices = matrices;
this.hand = hand;
}
public MatrixStack getMatrices() {
return matrices;
}
public void setMatrices(MatrixStack matrices) {
this.matrices = matrices;
}
public Hand getHand() {
return hand;
}
public void setHand(Hand hand) {
this.hand = hand;
}
}
@@ -1,6 +0,0 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
public class RenderEvent extends Event {
}
@@ -1,29 +0,0 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.module.modules.Perspective;
import net.minecraft.client.render.Camera;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import static me.retucio.camtweaks.CameraTweaks.moduleManager;
@Mixin(Camera.class)
public abstract class CameraMixin {
@ModifyVariable(method = "clipToSpace", at = @At("HEAD"), ordinal = 0, argsOnly = true)
private float modifyClipToSpace(float distance) {
Perspective perspective = moduleManager.getModuleByClass(Perspective.class);
return perspective.isEnabled() ? (float) perspective.getDistance() : distance;
}
@Inject(method = "clipToSpace", at = @At("HEAD"), cancellable = true)
private void onClipToSpace(float distance, CallbackInfoReturnable<Float> cir) {
if (moduleManager.getModuleByClass(Perspective.class).isEnabled()
&& moduleManager.getModuleByClass(Perspective.class).clip.isEnabled())
cir.setReturnValue(distance);
}
}
@@ -1,38 +0,0 @@
package me.retucio.camtweaks.mixin;
import com.mojang.authlib.GameProfile;
import me.retucio.camtweaks.util.interfaces.IChatHudLine;
import net.minecraft.client.gui.hud.ChatHudLine;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
@Mixin(ChatHudLine.class)
public class ChatHudLineMixin implements IChatHudLine {
@Shadow @Final
private Text content;
@Unique
private int id;
@Unique
private GameProfile sender;
@Override
public String smegma$getText() { return content.toString(); }
@Override
public int smegma$getId() { return id; }
@Override
public void smegma$setId(int id) { this.id = id; }
@Override
public GameProfile smegma$getSender() { return sender; }
@Override
public void smegma$setSender(GameProfile profile) { this.sender = profile; }
}
@@ -1,55 +0,0 @@
package me.retucio.camtweaks.mixin;
import com.mojang.authlib.GameProfile;
import me.retucio.camtweaks.util.interfaces.IChatHudLineVisible;
import net.minecraft.client.gui.hud.ChatHudLine;
import net.minecraft.text.OrderedText;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
@Mixin(ChatHudLine.Visible.class)
public class ChatHudLineVisibleMixin implements IChatHudLineVisible {
@Shadow @Final
private OrderedText content;
@Unique
private int id;
@Unique
private GameProfile sender;
@Unique
private boolean startOfEntry;
@Override
public String smegma$getText() {
StringBuilder sb = new StringBuilder();
content.accept((index, style, codePoint) -> {
sb.appendCodePoint(codePoint);
return true;
});
return sb.toString();
}
@Override
public int smegma$getId() { return id; }
@Override
public void smegma$setId(int id) { this.id = id; }
@Override
public GameProfile smegma$getSender() { return sender; }
@Override
public void smegma$setSender(GameProfile profile) { this.sender = profile; }
@Override
public boolean smegma$isStartOfEntry() { return startOfEntry; }
@Override
public void smegma$setStartOfEntry(boolean start) { this.startOfEntry = start; }
}
@@ -1,194 +0,0 @@
package me.retucio.camtweaks.mixin;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.ModifyReceiver;
import com.llamalad7.mixinextras.sugar.Local;
import me.retucio.camtweaks.event.events.ReceiveMessageEvent;
import me.retucio.camtweaks.module.modules.BetterChat;
import me.retucio.camtweaks.util.interfaces.IChatHud;
import me.retucio.camtweaks.util.interfaces.IChatHudLine;
import me.retucio.camtweaks.util.interfaces.IChatHudLineVisible;
import me.retucio.camtweaks.util.interfaces.IMessageHandler;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.client.gui.hud.ChatHudLine;
import net.minecraft.client.gui.hud.MessageIndicator;
import net.minecraft.network.message.MessageSignatureData;
import net.minecraft.text.OrderedText;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
import static me.retucio.camtweaks.CameraTweaks.*;
@Mixin(ChatHud.class)
public abstract class ChatHudMixin implements IChatHud {
@Unique
private boolean skipOnAddMessage;
@Unique
private int nextId;
@Unique @Final
private BetterChat betterChat = moduleManager.getModuleByClass(BetterChat.class);
@Shadow @Final
private List<ChatHudLine> messages;
@Shadow @Final
private List<ChatHudLine.Visible> visibleMessages;
@Shadow
public abstract void addMessage(Text message);
@Shadow
public abstract void addMessage(Text message, @Nullable MessageSignatureData signatureData, @Nullable MessageIndicator indicator);
// métodos relacionados a las interfaces IChatHud, IChatHudLine, IChadHudLineVisible y IMessageHandler
@Override
public void smegma$add(Text message, int id) {
nextId = id;
addMessage(message);
nextId = 0;
}
@Inject(method = "addVisibleMessage", at = @At(value = "INVOKE", target = "Ljava/util/List;add(ILjava/lang/Object;)V", shift = At.Shift.AFTER))
private void onAddMessageAfterNewChatHudLineVisible(ChatHudLine message, CallbackInfo ci) {
((IChatHudLine) (Object) visibleMessages.getFirst()).smegma$setId(nextId);
}
@Inject(method = "addMessage(Lnet/minecraft/client/gui/hud/ChatHudLine;)V", at = @At(value = "INVOKE", target = "Ljava/util/List;add(ILjava/lang/Object;)V", shift = At.Shift.AFTER))
private void onAddMessageAfterNewChatHudLine(ChatHudLine message, CallbackInfo ci) {
((IChatHudLine) (Object) messages.getFirst()).smegma$setId(nextId);
}
@SuppressWarnings("DataFlowIssue")
@ModifyExpressionValue(method = "addVisibleMessage", at = @At(value = "NEW", target = "(ILnet/minecraft/text/OrderedText;Lnet/minecraft/client/gui/hud/MessageIndicator;Z)Lnet/minecraft/client/gui/hud/ChatHudLine$Visible;"))
private ChatHudLine.Visible onAddMessage_modifyChatHudLineVisible(ChatHudLine.Visible line, @Local(ordinal = 1) int j) {
IMessageHandler handler = (IMessageHandler) mc.getMessageHandler();
if (handler == null) return line;
IChatHudLineVisible iLine = (IChatHudLineVisible) (Object) line;
iLine.smegma$setSender(handler.smegma$getSender());
iLine.smegma$setStartOfEntry(j == 0);
return line;
}
@ModifyExpressionValue(method = "addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)V", at = @At(value = "NEW", target = "(ILnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)Lnet/minecraft/client/gui/hud/ChatHudLine;"))
private ChatHudLine onAddMessage_modifyChatHudLine(ChatHudLine line) {
IMessageHandler handler = (IMessageHandler) mc.getMessageHandler();
if (handler == null) return line;
((IChatHudLine) (Object) line).smegma$setSender(handler.smegma$getSender());
return line;
}
// modificar contenido del mensaje
@Inject(at = @At("HEAD"), method = "addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)V", cancellable = true)
private void onAddMessage(Text message, MessageSignatureData signatureData, MessageIndicator indicator, CallbackInfo ci) {
if (skipOnAddMessage) return;
ReceiveMessageEvent event = EVENT_BUS.post(new ReceiveMessageEvent(message, indicator, nextId));
if (event.isCancelled()) {
ci.cancel();
} else {
visibleMessages.removeIf(msg -> ((IChatHudLine) (Object) msg).smegma$getId() == nextId && nextId != 0);
for (int i = messages.size() - 1; i > -1; i--) {
if (((IChatHudLine) (Object) messages.get(i)).smegma$getId() == nextId && nextId != 0) {
messages.remove(i);
betterChat.removeLine(i);
}
}
if (event.wasModified()) {
ci.cancel();
skipOnAddMessage = true;
addMessage(event.getMessage(), signatureData, event.getIndicator());
skipOnAddMessage = false;
}
}
}
// cabezas
@ModifyExpressionValue(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;ceil(F)I"))
private int onRender_modifyWidth(int width) {
return (betterChat.isEnabled() && betterChat.showHeads.isEnabled()) ? width + 10 : width;
}
@ModifyReceiver(method = "method_71991", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTextWithShadow(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/OrderedText;III)V"))
private DrawContext onRender_beforeDrawTextWithShadow(DrawContext context, TextRenderer textRenderer, OrderedText text, int x, int y, int color, @Local(argsOnly = true) ChatHudLine.Visible line) {
betterChat.beforeDrawMessage(context, line, y, color);
return context;
}
@Inject(method = "method_71991", at = @At("TAIL"))
private void onRender_afterDrawTextWithShadow(int i, DrawContext context, float f, int j, int k, int l, ChatHudLine.Visible visible, int m, float g, CallbackInfo info) {
betterChat.afterDrawMessage(context);
}
// historial (registro) del chat
@ModifyExpressionValue(method = "addMessage(Lnet/minecraft/client/gui/hud/ChatHudLine;)V", at = @At(value = "CONSTANT", args = "intValue=100"))
private int maxLength(int size) {
if (!betterChat.isEnabled()) return size;
return size + betterChat.chatHistoryExtraLength.getIntValue();
}
@ModifyExpressionValue(method = "addVisibleMessage", at = @At(value = "CONSTANT", args = "intValue=100"))
private int maxLengthVisible(int size) {
if (!betterChat.isEnabled()) return size;
return size + betterChat.chatHistoryExtraLength.getIntValue();
}
@Inject(method = "addVisibleMessage", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;isChatFocused()Z"))
private void onBreakChatMessageLines(ChatHudLine message, CallbackInfo ci, @Local List<OrderedText> list) {
betterChat.lines.addFirst(list.size());
}
@Inject(method = "addMessage(Lnet/minecraft/client/gui/hud/ChatHudLine;)V", at = @At(value = "INVOKE", target = "Ljava/util/List;remove(I)Ljava/lang/Object;"))
private void onRemoveMessage(ChatHudLine message, CallbackInfo ci) {
int extra = betterChat.chatHistoryExtraLength.getIntValue();
int size = betterChat.lines.size();
while (size > 100 + extra) {
betterChat.lines.removeLast();
size--;
}
}
@Inject(method = "clear", at = @At("HEAD"))
private void onClear(boolean clearHistory, CallbackInfo ci) {
betterChat.lines.clear();
}
@Inject(method = "refresh", at = @At("HEAD"))
private void onRefresh(CallbackInfo ci) {
betterChat.lines.clear();
}
}
@@ -1,63 +0,0 @@
package me.retucio.camtweaks.mixin;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.brigadier.ParseResults;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.suggestion.Suggestions;
import me.retucio.camtweaks.command.CommandManager;
import net.minecraft.client.gui.screen.ChatInputSuggestor;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.command.CommandSource;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.concurrent.CompletableFuture;
import static me.retucio.camtweaks.CameraTweaks.commandManager;
import static me.retucio.camtweaks.CameraTweaks.mc;
@Mixin(ChatInputSuggestor.class)
public abstract class ChatInputSuggestorMixin {
@Shadow
private ParseResults<CommandSource> parse;
@Shadow @Final
TextFieldWidget textField;
@Shadow
private ChatInputSuggestor.SuggestionWindow window;
@Shadow
boolean completingSuggestions;
@Shadow
private CompletableFuture<Suggestions> pendingSuggestions;
@Shadow protected abstract void showCommandSuggestions();
@Inject(method = "refresh", at = @At(value = "INVOKE", target = "Lcom/mojang/brigadier/StringReader;canRead()Z", remap = false), cancellable = true)
private void onRefresh(CallbackInfo ci, @Local StringReader reader) {
String prefix = commandManager.getPrefix();
int length = prefix.length();
if (reader.canRead(length) && reader.getString().startsWith(prefix, reader.getCursor())) {
reader.setCursor(reader.getCursor() + length);
if (parse == null) this.parse = CommandManager.dispatcher.parse(reader, mc.getNetworkHandler().getCommandSource());
int cursor = textField.getCursor();
if (cursor >= length && (window == null || !completingSuggestions)) {
pendingSuggestions = CommandManager.dispatcher.getCompletionSuggestions(parse, cursor);
pendingSuggestions.thenRun(() -> {
if (pendingSuggestions.isDone()) showCommandSuggestions();
});
}
ci.cancel();
}
}
}
@@ -1,25 +0,0 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.module.modules.BetterChat;
import net.minecraft.client.gui.screen.ChatScreen;
import net.minecraft.client.gui.widget.TextFieldWidget;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static me.retucio.camtweaks.CameraTweaks.moduleManager;
@Mixin(value = ChatScreen.class, priority = 1001)
public class ChatScreenMixin {
@Shadow
protected TextFieldWidget chatField;
@Inject(method = "init", at = @At(value = "RETURN"))
private void onInit(CallbackInfo info) {
BetterChat betterChat = moduleManager.getModuleByClass(BetterChat.class);
if (betterChat.isEnabled() && betterChat.noCharLimit.isEnabled()) chatField.setMaxLength(Integer.MAX_VALUE);
}
}
@@ -1,49 +0,0 @@
package me.retucio.camtweaks.mixin;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.retucio.camtweaks.command.CommandManager;
import me.retucio.camtweaks.event.events.SendMessageEvent;
import me.retucio.camtweaks.util.ChatUtil;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static me.retucio.camtweaks.CameraTweaks.*;
@Mixin(ClientPlayNetworkHandler.class)
public abstract class ClientPlayNetworkHandlerMixin {
@Unique
private boolean ignoreChatMessage;
@Shadow
public abstract void sendChatMessage(String content);
@Inject(method = "sendChatMessage", at = @At("HEAD"), cancellable = true)
private void onSendMessage(String message, CallbackInfo ci) {
if (ignoreChatMessage) return;
if (!message.startsWith(commandManager.getPrefix())) {
SendMessageEvent event = EVENT_BUS.post(new SendMessageEvent(message));
if (!event.isCancelled()) {
ignoreChatMessage = true;
sendChatMessage(event.getMessage());
ignoreChatMessage = false;
}
ci.cancel();
return;
};
try {
CommandManager.dispatch(message.substring(commandManager.getPrefix().length()));
} catch (CommandSyntaxException e) {
ChatUtil.error(e.getMessage());
}
mc.inGameHud.getChatHud().addToMessageHistory(message);
ci.cancel();
}
}
@@ -1,22 +0,0 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.events.PerspectiveChangeEvent;
import net.minecraft.client.option.GameOptions;
import net.minecraft.client.option.Perspective;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static me.retucio.camtweaks.CameraTweaks.EVENT_BUS;
@Mixin(GameOptions.class)
public abstract class GameOptionsMixin {
@Inject(method = "setPerspective", at = @At("HEAD"), cancellable = true)
private void changePerspective(Perspective perspective, CallbackInfo ci) {
PerspectiveChangeEvent event = EVENT_BUS.post(new PerspectiveChangeEvent(perspective));
if (event.isCancelled()) ci.cancel();
}
}
@@ -1,40 +0,0 @@
package me.retucio.camtweaks.mixin;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.events.GetFOVEvent;
import me.retucio.camtweaks.event.events.RenderEvent;
import me.retucio.camtweaks.module.modules.Zoom;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.render.RenderTickCounter;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static me.retucio.camtweaks.CameraTweaks.EVENT_BUS;
@Mixin(GameRenderer.class)
public abstract class GameRendererMixin {
@Unique
Zoom zoom = CameraTweaks.moduleManager.getModuleByClass(Zoom.class);
@ModifyReturnValue(method = "getFov", at = @At("RETURN"))
private float modifyFov(float original) {
return EVENT_BUS.post(new GetFOVEvent(original)).getFov();
}
// @Inject(method = "render", at = @At("HEAD"))
// private void onRender(RenderTickCounter tickCounter, boolean tick, CallbackInfo ci) {
// CameraTweaks.INSTANCE.EVENT_BUS.post(new RenderEvent());
// }
@Inject(method = "renderHand", at = @At("HEAD"), cancellable = true)
private void renderHand(float tickProgress, boolean sleeping, Matrix4f positionMatrix, CallbackInfo ci) {
if (zoom.isEnabled() && !zoom.showHands.isEnabled())
ci.cancel();
}
}
@@ -1,49 +0,0 @@
package me.retucio.camtweaks.mixin;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import me.retucio.camtweaks.event.events.HeldItemRendererEvent;
import me.retucio.camtweaks.event.events.RenderArmEvent;
import me.retucio.camtweaks.module.modules.HandView;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.item.HeldItemRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Arm;
import net.minecraft.util.Hand;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static me.retucio.camtweaks.CameraTweaks.EVENT_BUS;
import static me.retucio.camtweaks.CameraTweaks.moduleManager;
@Mixin(HeldItemRenderer.class)
public abstract class HeldItemRendererMixin {
@Unique
HandView handView = moduleManager.getModuleByClass(HandView.class);
@Inject(method = "renderFirstPersonItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderItem(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemDisplayContext;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", shift = At.Shift.BEFORE))
private void onRenderItem(AbstractClientPlayerEntity player, float tickDelta, float pitch, Hand hand, float swingProgress, ItemStack item, float equipProgress, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) {
EVENT_BUS.post(new HeldItemRendererEvent(matrices, hand));
}
@ModifyReturnValue(method = "shouldSkipHandAnimationOnSwap", at = @At("RETURN"))
private boolean modifySkipSwapAnimation(boolean original) {
return original || (handView.isEnabled() && handView.skipSwapping.isEnabled());
}
@Inject(method = "applyEatOrDrinkTransformation", at = @At(value = "INVOKE", target = "Ljava/lang/Math;pow(DD)D", shift = At.Shift.BEFORE), cancellable = true)
private void cancelTransformations(MatrixStack matrices, float tickDelta, Arm arm, ItemStack stack, PlayerEntity player, CallbackInfo ci) {
if (handView.isEnabled() && handView.noFood.isEnabled()) ci.cancel();
}
@Inject(method = "renderFirstPersonItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderArmHoldingItem(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IFFLnet/minecraft/util/Arm;)V"))
private void onRenderArm(AbstractClientPlayerEntity player, float tickDelta, float pitch, Hand hand, float swingProgress, ItemStack item, float equipProgress, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) {
EVENT_BUS.post(new RenderArmEvent(matrices, hand));
}
}
@@ -1,28 +0,0 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.module.modules.BetterChat;
import me.retucio.camtweaks.ui.HUD;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.render.RenderTickCounter;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static me.retucio.camtweaks.CameraTweaks.moduleManager;
@Mixin(InGameHud.class)
public abstract class InGameHudMixin {
@Inject(method = "render", at = @At("RETURN"), cancellable = true)
private void renderHUD(DrawContext ctx, RenderTickCounter tc, CallbackInfo ci) {
HUD.render(ctx, tc);
}
@Inject(method = "clear", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;clear(Z)V"), cancellable = true)
private void onClear(CallbackInfo ci) {
BetterChat betterChat = moduleManager.getModuleByClass(BetterChat.class);
if (betterChat.isEnabled() && betterChat.logger.isEnabled()) ci.cancel();
}
}
@@ -1,23 +0,0 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.events.KeyEvent;
import net.minecraft.client.Keyboard;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static me.retucio.camtweaks.CameraTweaks.EVENT_BUS;
@Mixin(Keyboard.class)
public abstract class KeyboardMixin {
@Inject(method = "onKey", at = @At("HEAD"), cancellable = true)
private void onKeyPress(long window, int key, int scancode, int action, int modifiers, CallbackInfo ci) {
CameraTweaks.INSTANCE.onKeyPress(key, action);
KeyEvent event = EVENT_BUS.post(new KeyEvent(key, scancode, action));
if (event.isCancelled()) ci.cancel();
}
}
@@ -1,37 +0,0 @@
package me.retucio.camtweaks.mixin;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.GpuTexture;
import me.retucio.camtweaks.module.modules.Fullbright;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.util.math.ColorHelper;
import net.minecraft.util.profiler.Profiler;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static me.retucio.camtweaks.CameraTweaks.moduleManager;
@Mixin(LightmapTextureManager.class)
public abstract class LightmapTextureManagerMixin {
@Unique
Fullbright fullbright = moduleManager.getModuleByClass(Fullbright.class);
@Shadow @Final
private GpuTexture glTexture;
@Inject(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiler/Profiler;push(Ljava/lang/String;)V", shift = At.Shift.AFTER), cancellable = true)
private void update(float tickProgress, CallbackInfo ci, @Local Profiler profiler) {
if (fullbright.isEnabled() && fullbright.mode.is(Fullbright.Modes.GAMMA)) {
RenderSystem.getDevice().createCommandEncoder().clearColorTexture(glTexture, ColorHelper.getArgb(255, 255, 255, 255));
profiler.pop();
ci.cancel();
}
}
}
@@ -1,37 +0,0 @@
package me.retucio.camtweaks.mixin;
import com.mojang.authlib.GameProfile;
import me.retucio.camtweaks.util.interfaces.IMessageHandler;
import net.minecraft.client.network.message.MessageHandler;
import net.minecraft.network.message.MessageType;
import net.minecraft.network.message.SignedMessage;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.time.Instant;
@Mixin(MessageHandler.class)
public abstract class MessageHandlerMixin implements IMessageHandler {
@Unique
private GameProfile sender;
@Inject(method = "processChatMessageInternal", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)V", shift = At.Shift.BEFORE))
private void onProcessChatMessageInternal_beforeAddMessage(MessageType.Parameters params, SignedMessage message, Text decorated, GameProfile sender, boolean onlyShowSecureChat, Instant receptionTimestamp, CallbackInfoReturnable<Boolean> info) {
this.sender = sender;
}
@Inject(method = "processChatMessageInternal", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)V", shift = At.Shift.AFTER))
private void onProcessChatMessageInternal_afterAddMessage(MessageType.Parameters params, SignedMessage message, Text decorated, GameProfile sender, boolean onlyShowSecureChat, Instant receptionTimestamp, CallbackInfoReturnable<Boolean> info) {
this.sender = null;
}
@Override
public GameProfile smegma$getSender() {
return sender;
}
}
@@ -1,17 +0,0 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.CameraTweaks;
import net.minecraft.client.MinecraftClient;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MinecraftClient.class)
public abstract class MinecraftClientMixin {
@Inject(method = "tick", at = @At("HEAD"), cancellable = true)
private void onTick(CallbackInfo ci) {
CameraTweaks.INSTANCE.onTick();
}
}
@@ -1,28 +0,0 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.events.MouseClickEvent;
import me.retucio.camtweaks.event.events.MouseScrollEvent;
import net.minecraft.client.Mouse;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static me.retucio.camtweaks.CameraTweaks.EVENT_BUS;
@Mixin(Mouse.class)
public abstract class MouseMixin {
@Inject(method = "onMouseButton", at = @At("HEAD"), cancellable = true)
private void onMouseButton(long window, int button, int action, int mods, CallbackInfo ci) {
MouseClickEvent event = EVENT_BUS.post(new MouseClickEvent(action, button));
if (event.isCancelled()) ci.cancel();
}
@Inject(method = "onMouseScroll", at = @At("HEAD"), cancellable = true)
private void onMouseScroll(long window, double horizontal, double vertical, CallbackInfo ci) {
MouseScrollEvent event = EVENT_BUS.post(new MouseScrollEvent(horizontal, vertical));
if (event.isCancelled()) ci.cancel();
}
}
@@ -1,18 +0,0 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.module.modules.BetterChat;
import net.minecraft.util.StringHelper;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import static me.retucio.camtweaks.CameraTweaks.moduleManager;
@Mixin(StringHelper.class)
public abstract class StringHelperMixin {
@ModifyArg(method = "truncateChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/StringHelper;truncate(Ljava/lang/String;IZ)Ljava/lang/String;"), index = 1)
private static int truncate(int maxLength) {
BetterChat betterChat = moduleManager.getModuleByClass(BetterChat.class);
return ((betterChat.isEnabled() && betterChat.noCharLimit.isEnabled()) ? Integer.MAX_VALUE : maxLength);
}
}
@@ -1,12 +0,0 @@
package me.retucio.camtweaks.mixin.accessor;
import net.minecraft.entity.effect.StatusEffectInstance;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(StatusEffectInstance.class)
public interface StatusEffectInstanceAccessor {
@Accessor("duration")
void setDuration(int duration);
}
@@ -1,139 +0,0 @@
package me.retucio.camtweaks.module;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.Subscribe;
import me.retucio.camtweaks.module.settings.BooleanSetting;
import me.retucio.camtweaks.module.settings.EnumSetting;
import me.retucio.camtweaks.module.settings.KeySetting;
import me.retucio.camtweaks.module.settings.Setting;
import me.retucio.camtweaks.util.ChatUtil;
import net.minecraft.client.MinecraftClient;
import org.lwjgl.glfw.GLFW;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
// clase base para los módulos
public class Module {
// atributos
private String name;
private String description;
private boolean enabled;
// dejar al usuario elegir si el módulo debería apagarse tras soltar su tecla asignada, o si la tecla debería alternar su estado
protected EnumSetting<KeyModes> keyMode = new EnumSetting<>("modo de tecla", "cómo interpretar la tecla configurada", KeyModes.class, KeyModes.TOGGLE);
protected KeySetting bind = new KeySetting("tecla", "tecla asignada al módulo, ESC para desactivar", GLFW.GLFW_KEY_UNKNOWN);
protected BooleanSetting notify = new BooleanSetting("notificar", "notificar en el chat al activar / desactivar", true);
private final List<Setting> settings = new ArrayList<>();
protected MinecraftClient mc = MinecraftClient.getInstance();
public Module(String name, String description) {
this.name = name;
this.description = description;
addSettings(bind, keyMode, notify);
}
// ajustes
public List<Setting> getSettings() {
return settings;
}
public void addSetting(Setting setting) {
settings.add(setting);
}
public void addSettings(Setting... settings) {
for (Setting setting : settings) addSetting(setting);
}
// encendido y apagado del módulo
public void toggle() {
enabled = !enabled;
if (enabled) onEnable();
else onDisable();
}
public void onEnable() {
if (notify.isEnabled()) ChatUtil.info(getName() + " fue activado");
for (Method method : getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(Subscribe.class)) {
CameraTweaks.EVENT_BUS.register(this);
break;
}
}
}
public void onDisable() {
if (notify.isEnabled()) ChatUtil.info(getName() + " fue desactivado");
if (CameraTweaks.EVENT_BUS.isRegistered(this))
CameraTweaks.EVENT_BUS.unregister(this);
}
// otros métodos
public void onTick() {}
public void onKey(int key, int action) {}
public boolean nullCheck() {
return mc.player == null;
}
public boolean fullNullCheck() {
return mc.player == null || mc.world == null;
}
// getters y setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
if (enabled) onEnable();
else onDisable();
}
public boolean shouldToggleOnBindRelease() {
return keyMode.is(KeyModes.HOLD);
}
public int getKey() {
return bind.getKey();
}
public void setKey(int key) {
bind.setKey(key);
bind.setDefaultKey(key);
}
public KeySetting getBind() {
return bind;
}
public enum KeyModes {
HOLD("mantener"),
TOGGLE("alternar");
private final String name;
KeyModes(String name) { this.name = name; }
@Override public String toString() { return name; }
}
}
@@ -1,67 +0,0 @@
package me.retucio.camtweaks.module;
import me.retucio.camtweaks.event.EventBus;
import me.retucio.camtweaks.event.Subscribe;
import me.retucio.camtweaks.module.modules.*;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
// donde se regisran los módulos, y los "listeners" de eventos en cada módulo que lo necesite
public class ModuleManager {
private final List<Module> modules = new ArrayList<>();
private final EventBus eventBus;
public ModuleManager(EventBus bus) {
this.eventBus = bus;
addModules();
}
private void addModules() {
modules.add(new Fullbright());
modules.add(new Zoom());
modules.add(new Perspective());
modules.add(new HandView());
modules.add(new BetterChat());
// registrar los "listeners" necesarios
for (Module module : getEnabledModules()) {
for (Method method : module.getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(Subscribe.class)) {
eventBus.register(module);
break;
}
}
}
}
// para obtener módulos más fácilmente (por nombre, clase, o la lista completa)
public List<Module> getModules() {
return modules;
}
public List<Module> getEnabledModules() {
List<Module> enabledModules = new ArrayList<>();
for (Module module : modules)
if (module.isEnabled()) enabledModules.add(module);
return enabledModules;
}
public Module getModuleByName(String name) {
for (Module module : modules)
if (module.getName().equalsIgnoreCase(name)) return module;
return null;
}
public <T extends Module> T getModuleByClass(Class<T> clazz) {
for (Module module : modules)
if (clazz.isInstance(module))
return clazz.cast(module);
return null;
}
}
@@ -1,214 +0,0 @@
package me.retucio.camtweaks.module.modules;
import com.mojang.authlib.GameProfile;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.command.CommandManager;
import me.retucio.camtweaks.event.Subscribe;
import me.retucio.camtweaks.event.events.ClientClickEvent;
import me.retucio.camtweaks.event.events.ReceiveMessageEvent;
import me.retucio.camtweaks.event.events.SendMessageEvent;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.BooleanSetting;
import me.retucio.camtweaks.module.settings.NumberSetting;
import me.retucio.camtweaks.util.ChatUtil;
import me.retucio.camtweaks.util.interfaces.IChatHudLine;
import me.retucio.camtweaks.util.interfaces.IChatHudLineVisible;
import me.retucio.camtweaks.util.interfaces.TextVisitor;
import net.minecraft.client.gl.RenderPipelines;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.PlayerSkinDrawer;
import net.minecraft.client.gui.hud.ChatHudLine;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.text.*;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// continúa en ChatHudMixin, ChatScreenMixin, InGameHudMixin & StringHelperMixin
public class BetterChat extends Module {
// ajustes
public BooleanSetting timestamps = new BooleanSetting("sello de tiempo", "muestra a qué hora se ha enviado un mensaje", false);
public BooleanSetting timestampSecs = new BooleanSetting("mostrar segundos", "muestra segundos también en el sello de tiempo", false);
public BooleanSetting showHeads = new BooleanSetting("cabezas", "muestra la cabeza del jugador junto a su mensaje", true);
public BooleanSetting coordsProtection = new BooleanSetting("proteger coordenadas", "evitar enviar coordenadas por el chat", true);
public BooleanSetting keepHistory = new BooleanSetting("no borrar chat", "no borrar el chat tras desconectarse", true);
public BooleanSetting logger = new BooleanSetting("registro", "evita que se borre el chat de un server", true);
public BooleanSetting noCharLimit = new BooleanSetting("quitar límite de caracteres", "te deja escribir mensajes tan largos como desees", false);
public NumberSetting chatHistoryExtraLength = new NumberSetting("expandir chat", "cuántas líneas añadir al historial del chat",
0, 0, 1000, 1);
public BetterChat() {
super("chat plus", "mejoras para el chat");
addSettings(timestamps, timestampSecs, showHeads, coordsProtection, keepHistory, logger, noCharLimit, chatHistoryExtraLength);
updateDateFormat();
timestamps.onUpdate(b -> timestampSecs.setVisible(b));
timestampSecs.onUpdate(x -> updateDateFormat());
}
public final IntList lines = new IntArrayList();
private static final Pattern antiClearRegex = Pattern.compile("\\n(\\n|\\s)+\\n");
private static final Pattern usernameRegex = Pattern.compile("^(?:<[0-9]{2}:[0-9]{2}>\\s)?<(.*?)>.*");
private record CustomHeadEntry(String prefix, Identifier texture) {}
private static final List<CustomHeadEntry> CUSTOM_HEAD_ENTRIES = new ArrayList<>();
private static final Pattern TIMESTAMP_REGEX = Pattern.compile("^<\\d{1,2}:\\d{1,2}>");
private SimpleDateFormat dateFormat;
private void updateDateFormat() {
dateFormat = new SimpleDateFormat(timestampSecs.isEnabled() ? "HH:mm:ss" : "HH:mm");
}
private static final Pattern coordRegex = Pattern.compile("(?<x>-?\\d{3,}(?:\\.\\d*)?)(?:\\s+(?<y>-?\\d{1,3}(?:\\.\\d*)?))?\\s+(?<z>-?\\d{3,}(?:\\.\\d*)?)");
private boolean containsCoordinates(String message) {
return coordRegex.matcher(message).find();
}
@Subscribe
public void onReceiveMessage(ReceiveMessageEvent event) {
Text message = event.getMessage();
// registrar mensajes para evitar su eliminación
if (logger.isEnabled()) {
String messageString = message.getString();
if (antiClearRegex.matcher(messageString).find()) {
MutableText newMessage = Text.empty();
TextVisitor.visit(message, (text, style, string) -> {
Matcher antiClearMatcher = antiClearRegex.matcher(string);
if (antiClearMatcher.find())
newMessage.append(Text.literal(antiClearMatcher.replaceAll("\n\n")).setStyle(style));
else
newMessage.append(text.copyContentOnly().setStyle(style));
return Optional.empty();
}, Style.EMPTY);
message = newMessage;
}
}
// agregar sellos de tiempo a los mensajes
if (timestamps.isEnabled()) {
Text timestamp = Text.literal("<" + dateFormat.format(new Date()) + "> ").formatted(Formatting.GRAY);
message = Text.empty().append(timestamp).append(message);
}
// modificar el mensaje final
event.setMessage(message);
}
@Subscribe
private void onSendMessage(SendMessageEvent event) {
String message = event.getMessage();
// evitar mandar coordenadas por el chat
if (coordsProtection.isEnabled() && containsCoordinates(message)) {
ChatUtil.warn(Text.literal("cuidadito con las coordenadas chavalín").append(
getSendButton(message)));
event.cancel();
return;
}
event.setMessage(message);
}
static {
CUSTOM_HEAD_ENTRIES.add(new CustomHeadEntry("smegma", Identifier.of(CameraTweaks.MOD_ID, "icon_chat.png")));
}
public void beforeDrawMessage(DrawContext context, ChatHudLine.Visible line, int y, int color) {
if (!isEnabled() || !showHeads.isEnabled()) return;
// dibujar la cabeza al principio del mensaje
if (((IChatHudLineVisible) (Object) line).smegma$isStartOfEntry())
drawTexture(context, (IChatHudLine) (Object) line, y, color);
context.getMatrices().pushMatrix();
context.getMatrices().translate(10, 0);
}
public void afterDrawMessage(DrawContext context) {
// finalizar renderizado de la cabeza
if (!isEnabled() || !showHeads.isEnabled()) return;
context.getMatrices().popMatrix();
}
private void drawTexture(DrawContext context, IChatHudLine line, int y, int color) {
String text = line.smegma$getText().trim();
int startOffset = 0;
// buscar el sello del tiempo si lo hay para dibujar la cabeza correctamente
try {
Matcher m = TIMESTAMP_REGEX.matcher(text);
if (m.find()) startOffset = m.end() + 1;
} catch (IllegalStateException ignored) {}
// dibujar el icono del mod
for (CustomHeadEntry entry : CUSTOM_HEAD_ENTRIES) {
if (text.startsWith(entry.prefix(), startOffset)) {
context.drawTexture(RenderPipelines.GUI_TEXTURED, entry.texture(), 0, y, 0, 0, 8, 8, 64, 64, 64, 64, color);
return;
}
}
// obtener y dibujar la cabeza del jugador
GameProfile sender = getSender(line, text);
if (sender == null) return;
PlayerListEntry entry = mc.getNetworkHandler().getPlayerListEntry(sender.getId());
if (entry == null) return;
PlayerSkinDrawer.draw(context, entry.getSkinTextures(), 0, y, 8);
}
// obtener el jugador que envió un mensaje
private GameProfile getSender(IChatHudLine line, String text) {
GameProfile sender = line.smegma$getSender();
if (sender == null) {
Matcher usernameMatcher = usernameRegex.matcher(text);
if (usernameMatcher.matches()) {
String username = usernameMatcher.group(1);
PlayerListEntry entry = mc.getNetworkHandler().getPlayerListEntry(username);
if (entry != null) sender = entry.getProfile();
}
}
return sender;
}
// botón para enviar mensaje con coordenadas de todos modos
private MutableText getSendButton(String message) {
MutableText sendButton = Text.literal("[ME LA SUDA]");
MutableText hintBaseText = Text.literal("");
MutableText hintMsg = Text.literal("enviar de todos modos:");
hintMsg.setStyle(hintBaseText.getStyle().withFormatting(Formatting.GRAY));
hintBaseText.append(hintMsg);
hintBaseText.append(Text.literal("\n" + message));
sendButton.setStyle(sendButton.getStyle()
.withFormatting(Formatting.DARK_RED)
.withClickEvent(new ClientClickEvent(CommandManager.getCommandByName("send").toString(message)))
.withHoverEvent(new HoverEvent.ShowText(hintBaseText)));
return sendButton;
}
public void removeLine(int index) {
lines.removeInt(index);
}
}
@@ -1,58 +0,0 @@
package me.retucio.camtweaks.module.modules;
import me.retucio.camtweaks.mixin.accessor.StatusEffectInstanceAccessor;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.EnumSetting;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.registry.Registries;
import org.lwjgl.glfw.GLFW;
// continúa en LightmapTextureManagerMixin
public class Fullbright extends Module {
public EnumSetting<Modes> mode = new EnumSetting<>("modo", "qué modo de iluminación emplear (usar poción con shaders)", Modes.class, Modes.GAMMA);
public Fullbright() {
super("brilli-brilli", "deshabilita la oscuridad");
setKey(GLFW.GLFW_KEY_B);
addSetting(mode);
mode.onUpdate(mode -> { if (mode != Modes.POTION) disableNightVision(); });
}
@Override
public void onDisable() {
disableNightVision();
}
@Override
public void onTick() {
if (mc.player == null || !mode.getValue().equals(Modes.POTION)) return;
if (mc.player.hasStatusEffect(Registries.STATUS_EFFECT.getEntry(StatusEffects.NIGHT_VISION.value()))) {
StatusEffectInstance instance = mc.player.getStatusEffect(Registries.STATUS_EFFECT.getEntry(StatusEffects.NIGHT_VISION.value()));
if (instance != null && instance.getDuration() < 69) ((StatusEffectInstanceAccessor) instance).setDuration(69);
} else {
mc.player.addStatusEffect(new StatusEffectInstance(Registries.STATUS_EFFECT.getEntry(StatusEffects.NIGHT_VISION.value()), 69, 0));
}
}
private void disableNightVision() {
if (mc.player == null) return;
if (mc.player.hasStatusEffect(Registries.STATUS_EFFECT.getEntry(StatusEffects.NIGHT_VISION.value())))
mc.player.removeStatusEffect(Registries.STATUS_EFFECT.getEntry(StatusEffects.NIGHT_VISION.value()));
}
public enum Modes {
GAMMA("gamma"),
POTION("poción");
private final String name;
Modes(String name) { this.name = name; }
@Override
public String toString() { return name; }
}
}
@@ -1,49 +0,0 @@
package me.retucio.camtweaks.module.modules;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.NumberSetting;
import me.retucio.camtweaks.module.settings.StringSetting;
import me.retucio.camtweaks.util.Colors;
import org.lwjgl.glfw.GLFW;
import java.awt.*;
import static me.retucio.camtweaks.CameraTweaks.commandManager;
import static me.retucio.camtweaks.util.Colors.*;
public class GUI extends Module {
public NumberSetting red = new NumberSetting("rojo", "cantidad de rojo en el RGB del marco", 0, 0, 255, 1);
public NumberSetting green = new NumberSetting("verde", "cantidad de verde en el RGB del marco", 0, 0, 255, 1);
public NumberSetting blue = new NumberSetting("azul", "cantidad de azul en el RGB del marco", 255, 0, 255, 1);
public NumberSetting alpha = new NumberSetting("alpha", "opacidad del marco", 180, 0, 255, 1);
public NumberSetting scrollSens = new NumberSetting("sensibilidad del scroll", "qué tan sensible es la interfaz a la rueda del ratón",
5, 0, 15, 0.5);
public StringSetting commandPrefix = new StringSetting("prefijo", "prefijo de los comandos", commandManager.getPrefix(), 10);
public GUI() {
super("interfaz", "ajustes de la interfaz, y otros misceláneos");
addSettings(red, green, blue, alpha, scrollSens, commandPrefix);
setKey(GLFW.GLFW_KEY_RIGHT_SHIFT);
keyMode.setVisible(false);
notify.setVisible(false);
commandPrefix.onUpdate(prefix -> commandManager.setPrefix(prefix));
red.onUpdate(r -> {frameHeadRed = r.intValue(); updateFrameHeadColor();});
green.onUpdate(g -> {frameHeadGreen = g.intValue(); updateFrameHeadColor();});
blue.onUpdate(b -> {frameHeadBlue = b.intValue(); updateFrameHeadColor();});
alpha.onUpdate(a -> {frameHeadAlpha = a.intValue(); updateFrameHeadColor();});
}
private void updateFrameHeadColor() {
Colors.frameHeadColor = new Color(
frameHeadRed,
frameHeadGreen,
frameHeadBlue,
frameHeadAlpha
).getRGB();
}
}
@@ -1,140 +0,0 @@
package me.retucio.camtweaks.module.modules;
import me.retucio.camtweaks.event.Subscribe;
import me.retucio.camtweaks.event.events.HeldItemRendererEvent;
import me.retucio.camtweaks.event.events.RenderArmEvent;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.BooleanSetting;
import me.retucio.camtweaks.module.settings.NumberSetting;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Hand;
import net.minecraft.util.math.RotationAxis;
import org.joml.Vector3d;
import org.lwjgl.glfw.GLFW;
public class HandView extends Module {
// ajustes de animaciones
public BooleanSetting oldAnimations = new BooleanSetting("animaciones viejas", "como las de la 1.8, al pegar", false);
public BooleanSetting skipSwapping = new BooleanSetting("no swap", "se salta la animación de cambiar items de mano", false);
public BooleanSetting noFood = new BooleanSetting("no comer", "no renderiza la animación de comer", false);
// ajustes de mano principal
public NumberSetting scaleMainX = new NumberSetting("escala de la mano principal (X)", ".",
1, 0.01, 5, 0.1);
public NumberSetting scaleMainY = new NumberSetting("escala de la mano principal (Y)", ".",
1, 0.01, 5, 0.1);
public NumberSetting scaleMainZ = new NumberSetting("escala de la mano principal (Z)", ".",
1, 0.01, 5, 0.1);
public NumberSetting posMainX = new NumberSetting("posición de la mano principal (X)", ".",
0, -3, 3, 0.1);
public NumberSetting posMainY = new NumberSetting("posición de la mano principal (Y)", ".",
0, -3, 3, 0.1);
public NumberSetting posMainZ = new NumberSetting("posición de la mano principal (Z)", ".",
0, -3, 3, 0.1);
public NumberSetting rotMainX = new NumberSetting("rotación de la mano principal (X)", ".",
0, -180, 180, 1);
public NumberSetting rotMainY = new NumberSetting("rotación de la mano principal (Y)", ".",
0, -180, 180, 1);
public NumberSetting rotMainZ = new NumberSetting("rotación de la mano principal (Z)", ".",
0, -180, 180, 1);
// ajustes de mano secundaria
public NumberSetting scaleOffX = new NumberSetting("escala de la mano secundaria (X)", ".",
1, 0.01, 5, 0.1);
public NumberSetting scaleOffY = new NumberSetting("escala de la mano secundaria (Y)", ".",
1, 0.01, 5, 0.1);
public NumberSetting scaleOffZ = new NumberSetting("escala de la mano secundaria (Z)", ".",
1, 0.01, 5, 0.1);
public NumberSetting posOffX = new NumberSetting("posición de la mano secundaria (X)", ".",
0, -3, 3, 0.1);
public NumberSetting posOffY = new NumberSetting("posición de la mano secundaria (Y)", ".",
0, -3, 3, 0.1);
public NumberSetting posOffZ = new NumberSetting("posición de la mano secundaria (Z)", ".",
0, -3, 3, 0.1);
public NumberSetting rotOffX = new NumberSetting("rotación de la mano secundaria (X)", ".",
0, -180, 180, 1);
public NumberSetting rotOffY = new NumberSetting("rotación de la mano secundaria (Y)", ".",
0, -180, 180, 1);
public NumberSetting rotOffZ = new NumberSetting("rotación de la mano secundaria (Z)", ".",
0, -180, 180, 1);
// brazo
public NumberSetting scaleArmX = new NumberSetting("escala del brazo (X)", ".",
1, 0.01, 5, 0.1);
public NumberSetting scaleArmY = new NumberSetting("escala del brazo (Y)", ".",
1, 0.01, 5, 0.1);
public NumberSetting scaleArmZ = new NumberSetting("escala del brazo (Z)", ".",
1, 0.01, 5, 0.1);
public NumberSetting posArmX = new NumberSetting("posición del brazo (X)", ".",
0, -3, 3, 0.1);
public NumberSetting posArmY = new NumberSetting("posición del brazo (Y)", ".",
0, -3, 3, 0.1);
public NumberSetting posArmZ = new NumberSetting("posición del brazo (Z)", ".",
0, -3, 3, 0.1);
public NumberSetting rotArmX = new NumberSetting("rotación del brazo (X)", ".",
0, -180, 180, 1);
public NumberSetting rotArmY = new NumberSetting("rotación del brazo (Y)", ".",
0, -180, 180, 1);
public NumberSetting rotArmZ = new NumberSetting("rotación del brazo (Z)", ".",
0, -180, 180, 1);
public HandView() {
super("manos", "modifica la manera en la que se renderizan las manos");
addSettings(
oldAnimations, skipSwapping, noFood,
scaleMainX, scaleMainY, scaleMainZ,
posMainX, posMainY, posMainZ,
rotMainX, rotMainY, rotMainZ,
scaleOffX, scaleOffY, scaleOffZ,
posOffX, posOffY, posOffZ,
rotOffX, rotOffY, rotOffZ,
scaleArmX, scaleArmY, scaleArmZ,
posArmX, posArmY, posArmZ,
rotArmX, rotArmY, rotArmZ);
setKey(GLFW.GLFW_KEY_UNKNOWN);
}
@Subscribe
public void onHeldItemRender(HeldItemRendererEvent event) {
if (event.getHand() == Hand.MAIN_HAND) {
scale(event.getMatrices(), new Vector3d(scaleMainX.getValue(), scaleMainY.getValue(), scaleMainZ.getValue()));
translate(event.getMatrices(), new Vector3d(posMainX.getValue(), posMainY.getValue(), posMainZ.getValue()));
rotate(event.getMatrices(), new Vector3d(rotMainX.getValue(), rotMainY.getValue(), rotMainZ.getValue()));
}
else {
scale(event.getMatrices(), new Vector3d(scaleOffX.getValue(), scaleOffY.getValue(), scaleOffZ.getValue()));
translate(event.getMatrices(), new Vector3d(posOffX.getValue(), posOffY.getValue(), posOffZ.getValue()));
rotate(event.getMatrices(), new Vector3d(rotOffX.getValue(), rotOffY.getValue(), rotOffZ.getValue()));
}
}
@Subscribe
public void onRenderArm(RenderArmEvent event) {
scale(event.getMatrices(), new Vector3d(scaleArmX.getValue(), scaleArmY.getValue(), scaleArmZ.getValue()));
translate(event.getMatrices(), new Vector3d(posArmX.getValue(), posArmY.getValue(), posArmZ.getValue()));
rotate(event.getMatrices(), new Vector3d(rotArmX.getValue(), rotArmY.getValue(), rotArmZ.getValue()));
}
private void rotate(MatrixStack matrix, Vector3d rotation) {
matrix.multiply(RotationAxis.POSITIVE_X.rotationDegrees((float) rotation.x));
matrix.multiply(RotationAxis.POSITIVE_Y.rotationDegrees((float) rotation.y));
matrix.multiply(RotationAxis.POSITIVE_Z.rotationDegrees((float) rotation.z));
}
private void scale(MatrixStack matrix, Vector3d scale) {
matrix.scale((float) scale.x, (float) scale.y, (float) scale.z);
}
private void translate(MatrixStack matrix, Vector3d translation) {
matrix.translate((float) translation.x, (float) translation.y, (float) translation.z);
}
}
@@ -1,61 +0,0 @@
package me.retucio.camtweaks.module.modules;
import me.retucio.camtweaks.event.Subscribe;
import me.retucio.camtweaks.event.events.MouseScrollEvent;
import me.retucio.camtweaks.event.events.PerspectiveChangeEvent;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.*;
import me.retucio.camtweaks.util.KeyUtil;
import org.lwjgl.glfw.GLFW;
// continúa en CameraMixin
public class Perspective extends Module {
private double distance;
// ajustes
public BooleanSetting clip = new BooleanSetting(
"clip", "atravesar bloques con la cámara", true);
public NumberSetting defaultDistance = new NumberSetting(
"distancia", "distancia a la que está la cámara del jugador por defecto",
4, 1, 20, 0.2);
public NumberSetting scrollSens = new NumberSetting(
"sensibilidad del scroll", "sensibilidad de la rueda del ratón (0 para desactivar)",
1, 0, 8, 0.1);
public KeySetting scrollKey = new KeySetting("tecla del scroll", "qué tecla mantener para usar la rueda del ratón", GLFW.GLFW_KEY_LEFT_ALT);
public Perspective() {
super("perspectiva", "añade cositas chulas al cambio de perspectiva");
addSettings(clip, defaultDistance, scrollSens, scrollKey);
setKey(GLFW.GLFW_KEY_UNKNOWN);
}
@Override
public void onEnable() {
distance = defaultDistance.getValue();
super.onEnable();
}
@Subscribe
public void onPerspectiveChange(PerspectiveChangeEvent event) {
distance = defaultDistance.getValue();
}
@Subscribe
public void onMouseScroll(MouseScrollEvent event) {
if (mc.options.getPerspective() == net.minecraft.client.option.Perspective.FIRST_PERSON
|| mc.currentScreen != null || scrollSens.getValue() <= 0) return;
if (KeyUtil.isKeyDown(scrollKey.getKey())) {
distance -= event.getVertical() / 4 * (scrollSens.getValue() * distance);
event.cancel();
}
}
public double getDistance() {
return distance;
}
}
@@ -1,99 +0,0 @@
package me.retucio.camtweaks.module.modules;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.Subscribe;
import me.retucio.camtweaks.event.events.GetFOVEvent;
import me.retucio.camtweaks.event.events.MouseScrollEvent;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.BooleanSetting;
import me.retucio.camtweaks.module.settings.KeySetting;
import me.retucio.camtweaks.module.settings.NumberSetting;
import me.retucio.camtweaks.util.ChatUtil;
import me.retucio.camtweaks.util.KeyUtil;
import net.minecraft.text.Text;
import org.lwjgl.glfw.GLFW;
// continúa en GameRendererMixin
public class Zoom extends Module {
public BooleanSetting showHands = new BooleanSetting("mostrar manos", "esconde o muestra las manos al hacer zoom", true);
public BooleanSetting showHUD = new BooleanSetting("mostrar HUD", "esconde o muestra los indicadores en pantalla", true);
public NumberSetting scrollSens = new NumberSetting("sensibilidad del scroll", "sensibilidad de la rueda del ratón (0 para desactivar)",
0.4, 0, 8, 0.1);
public KeySetting scrollKey = new KeySetting("tecla del scroll", "qué tecla mantener para usar la rueda del ratón", GLFW.GLFW_KEY_LEFT_CONTROL);
public NumberSetting defaultZoom = new NumberSetting("zoom", "cantidad de zoom",
6, 1, 10, 0.1);
public NumberSetting mouseSensMultiplier = new NumberSetting("sensibilidad", "multiplicador de la sensibilidad del ratón",
0.7, 0.01, 1, 0.05);
public BooleanSetting smoothCam = new BooleanSetting("cámara cinemática", "usa la cámara cinemática mientras hagas zoom", false);
private boolean prevSmoothCam;
private double prevMouseSens;
private double prevFov;
private boolean prevHUD;
private double value;
public Zoom() {
super("zoom", "lupa");
super.keyMode.setValue(KeyModes.HOLD);
super.keyMode.setDefaultValue(KeyModes.HOLD);
super.notify.setEnabled(false);
super.notify.setDefaultValue(false);
setKey(GLFW.GLFW_KEY_F);
addSettings(showHands, showHUD, scrollSens, scrollKey, defaultZoom, mouseSensMultiplier, smoothCam);
}
@Override
public void onEnable() {
CameraTweaks.EVENT_BUS.register(this);
prevSmoothCam = mc.options.smoothCameraEnabled;
prevMouseSens = mc.options.getMouseSensitivity().getValue();
prevFov = mc.options.getFov().getValue();
prevHUD = mc.options.hudHidden;
mc.options.hudHidden = !showHUD.isEnabled();
value = defaultZoom.getValue();
}
@Override
public void onDisable() {
CameraTweaks.EVENT_BUS.unregister(this);
mc.options.smoothCameraEnabled = prevSmoothCam;
mc.options.getMouseSensitivity().setValue(prevMouseSens);
mc.options.hudHidden = prevHUD;
mc.worldRenderer.scheduleTerrainUpdate();
}
@Override
public void onTick() {
mc.options.smoothCameraEnabled = smoothCam.isEnabled();
if (!smoothCam.isEnabled())
mc.options.getMouseSensitivity().setValue(prevMouseSens * mouseSensMultiplier.getValue());
}
@Subscribe
private void onMouseScroll(MouseScrollEvent event) {
if (scrollSens.getValue() > 0 && isEnabled() && KeyUtil.isKeyDown(scrollKey.getKey())) {
value += event.getVertical() * 0.25 * (scrollSens.getValue() * value);
if (value < 1) value = 1;
event.cancel();
}
}
@Subscribe
private void onGetFov(GetFOVEvent event) {
event.setFov((float) (event.getFov() / value));
if (prevFov != event.getFov()) mc.worldRenderer.scheduleTerrainUpdate();
prevFov = event.getFov();
}
}
@@ -1,50 +0,0 @@
package me.retucio.camtweaks.module.settings;
import java.util.function.Consumer;
// ajuste booleano, es decir, o se encuentra encencido o apagado (similar a un interruptor)
public class BooleanSetting extends Setting {
private boolean defaultValue;
private boolean enabled;
private Consumer<Boolean> updateListener;
public BooleanSetting(String name, String description, boolean defaultValue) {
super(name, description);
this.enabled = defaultValue;
this.defaultValue = defaultValue;
}
public void toggle() {
enabled = !enabled;
if (updateListener != null) updateListener.accept(enabled);
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
if (this.enabled != enabled) {
this.enabled = enabled;
if (updateListener != null) updateListener.accept(enabled);
}
}
public boolean getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(boolean value) {
defaultValue = value;
}
public void reset() {
setEnabled(defaultValue);
}
public void onUpdate(Consumer<Boolean> listener) {
this.updateListener = listener;
}
}
@@ -1,49 +0,0 @@
package me.retucio.camtweaks.module.settings;
import me.retucio.camtweaks.util.KeyUtil;
import java.util.function.Consumer;
public class KeySetting extends Setting {
private int key;
private int defaultKey;
private Consumer<Integer> updateListener;
public KeySetting(String name, String description, int defaultKey) {
super(name, description);
this.key = defaultKey;
this.defaultKey = defaultKey;
}
public int getKey() {
return key;
}
public void setKey(int key) {
if (this.key == key) return;
this.key = key;
if (updateListener != null) updateListener.accept(key);
}
public int getDefaultKey() {
return defaultKey;
}
public void setDefaultKey(int key) {
this.defaultKey = key;
}
public String getKeyName() {
return KeyUtil.getKeyName(key);
}
public void reset() {
setKey(defaultKey);
}
public void onUpdate(Consumer<Integer> listener) {
this.updateListener = listener;
}
}
@@ -1,30 +0,0 @@
package me.retucio.camtweaks.module.settings;
// base para los tipos de ajustes
public class Setting {
private final String name;
private final String description;
private boolean visible = true;
protected Setting(String name, String description) {
this.name = name;
this.description = description;
}
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
}
@@ -1,50 +0,0 @@
package me.retucio.camtweaks.module.settings;
import java.util.Objects;
import java.util.function.Consumer;
public class StringSetting extends Setting {
private String value;
private String defaultValue;
private final int maxLength;
private Consumer<String> updateListener;
public StringSetting(String name, String description, String defaultValue, int maxLength) {
super(name, description);
this.value = defaultValue;
this.defaultValue = defaultValue;
this.maxLength = maxLength;
}
public String getValue() {
return value;
}
public void setValue(String value) {
if (this.value.equals(value)) return;
this.value = value.length() > maxLength ? value.substring(0, maxLength) : value;
if (updateListener != null) updateListener.accept(this.value);
}
public String getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
public void reset() {
setValue(defaultValue);
}
public int getMaxLength() {
return maxLength;
}
public void onUpdate(Consumer<String> listener) {
this.updateListener = listener;
}
}
@@ -1,111 +0,0 @@
package me.retucio.camtweaks.ui;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.Subscribe;
import me.retucio.camtweaks.event.events.MouseScrollEvent;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.ui.settings.ClickGUISettingsFrame;
import me.retucio.camtweaks.ui.settings.SettingsFrame;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import java.util.ArrayList;
import java.util.List;
import static me.retucio.camtweaks.ui.settings.ClickGUISettingsFrame.guiSettings;
// interfaz gráfica, se abre con el shift derecho por defecto. aquí se encuentran los módulos y sus ajustes
public class ClickGUI extends Screen {
public static final ClickGUI INSTANCE = new ClickGUI();
private final ModuleFrame modulesFrame = new ModuleFrame(20, 30, 100, 20);
private final List<SettingsFrame> settingsFrames = new ArrayList<>();
private final ClickGUISettingsFrame guiSettingsFrame = new ClickGUISettingsFrame(200, 30, 100, 20);
protected ClickGUI() {
super(Text.of("interfaz"));
settingsFrames.add(guiSettingsFrame);
CameraTweaks.EVENT_BUS.register(this);
}
@Override
public void render(DrawContext ctx, int mouseX, int mouseY, float delta) {
// renderizar el marco de los módulos
modulesFrame.render(ctx, mouseX, mouseY, delta);
modulesFrame.updatePosition(mouseX, mouseY);
// renderizar el marco de los ajustes de cada módulo que lo tenga abierto. se abre haciendo clic derecho sobre el módulo
for (SettingsFrame sf : new ArrayList<>(settingsFrames)) {
sf.render(ctx, mouseX, mouseY, delta);
sf.updatePosition(mouseX, mouseY);
}
super.render(ctx, mouseX, mouseY, delta);
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
// detectar clics sobre los marcos
modulesFrame.mouseClicked(mouseX, mouseY, button);
for (SettingsFrame sf : new ArrayList<>(settingsFrames))
sf.mouseClicked(mouseX, mouseY, button);
return super.mouseClicked(mouseX, mouseY, button);
}
@Override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
// registrar cuándo se suelta el clic, en cada marco respectivamente
modulesFrame.mouseReleased(mouseX, mouseY, button);
for (SettingsFrame sf : new ArrayList<>(settingsFrames))
sf.mouseRelease(mouseX, mouseY, button);
return super.mouseReleased(mouseX, mouseY, button);
}
@Subscribe
public void onMouseScroll(MouseScrollEvent event) {
// subir y bajar la pantalla con la rueda del ratón
if (CameraTweaks.mc.currentScreen != this) return;
modulesFrame.y += (int) (event.getVertical() * guiSettings.scrollSens.getValue());
for (SettingsFrame sf : settingsFrames)
sf.y += (int) (event.getVertical() * guiSettings.scrollSens.getValue());
}
public void openSettingsFrame(Module module, int x, int y) {
// abrir un marco donde se encuentran los ajustes del módulo deseado
if (isSettingsFrameOpen(module)) return; // permitir un solo marco de ajustes por módulo
settingsFrames.add(new SettingsFrame(module, x + 120, y, 120, 18));
}
// cerrar el marco de ajustes
public void closeSettingsFrame(Module module) {
settingsFrames.removeIf(sf -> sf.module == module);
}
// verificar si un módulo tiene su marco de ajustes abierto
public boolean isSettingsFrameOpen(Module module) {
return settingsFrames.stream()
.anyMatch(sf -> sf.module.getName().equals(module.getName()));
}
// no pausar el juego cuando se abre la interfaz
@Override
public boolean shouldPause() {
return false;
}
public ModuleFrame getModulesFrame() {
return modulesFrame;
}
public ClickGUISettingsFrame getGuiSettingsFrame() {
return guiSettingsFrame;
}
public List<SettingsFrame> getSettingsFrames() {
return settingsFrames;
}
}
@@ -1,13 +0,0 @@
package me.retucio.camtweaks.ui;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.RenderTickCounter;
// en un principio esto iba a mostrar en pantalla los módulos activados, pero me parece innecesario. se queda aquí por si acaso
public class HUD {
private static final MinecraftClient mc = MinecraftClient.getInstance();
public static void render(DrawContext ctx, RenderTickCounter tc) {}
}
@@ -1,68 +0,0 @@
package me.retucio.camtweaks.ui;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.util.Colors;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import java.awt.*;
// clase para el botón para cada módulo
public class ModuleButton {
public Module module;
public ModuleFrame parent;
public int offset;
public final int height = 18;
public ModuleButton(Module module, ModuleFrame parent, int offset) {
this.module = module;
this.parent = parent;
this.offset = offset;
}
public void render(DrawContext ctx, double mouseX, double mouseY, float delta) {
ctx.fill( // dibujar el contorno del botón
parent.x + 2, parent.y + offset + 3,
parent.x + parent.w - 2 , parent.y + height + offset,
determineColor(mouseX, mouseY));
ctx.drawTextWithShadow( // dibujar el nombre del módulo
parent.mc.textRenderer, module.getName(),
parent.x + 5, parent.y + offset + (height / 2) - (parent.mc.textRenderer.fontHeight / 2) + 2, -1);
// dibujar "tooltips" (cajas de texto) al pasar el puntero encima del botón, para mostrar su descripción
if (isHovered((int) mouseX, (int) mouseY)) {
Screen currentScreen = parent.mc.currentScreen;
if (currentScreen != null)
ctx.drawTooltip(Text.of(module.getDescription()), (int) mouseX, (int) mouseY + 20);
}
}
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered((int) mouseX, (int) mouseY)) {
if (button == 0) // clic izquierdo para activar / desactivar el módulo
module.toggle();
else if (button == 1) { // clic derecho para el marco de ajustes (también lo cierra si está abierto)
if (ClickGUI.INSTANCE.isSettingsFrameOpen(module))
ClickGUI.INSTANCE.closeSettingsFrame(module);
else
ClickGUI.INSTANCE.openSettingsFrame(module,
parent.x + parent.w, parent.y + offset);
}
}
}
// verifica si el puntero del ratón se encuentra sobre el botón del módulo
public boolean isHovered(int mouseX, int mouseY) {
return mouseX > parent.x && mouseX < parent.x + parent.w && mouseY > parent.y + offset && mouseY < parent.y + height + offset;
}
public int determineColor(double mouseX, double mouseY) {
// determina el color del botón, dependiendo de si está el puntero encima y si está activado
if (module.isEnabled())
return isHovered((int) mouseX, (int) mouseY) ? Colors.enabledHoveredModuleButtonColor : Colors.enabledModuleButtonColor;
return isHovered((int) mouseX, (int) mouseY) ? Colors.hoveredModuleButtonColor : Colors.moduleButtonColor;
}
}
@@ -1,116 +0,0 @@
package me.retucio.camtweaks.ui;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.EnumSetting;
import me.retucio.camtweaks.module.settings.KeySetting;
import me.retucio.camtweaks.module.settings.NumberSetting;
import me.retucio.camtweaks.ui.settings.BindButton;
import me.retucio.camtweaks.ui.settings.CycleButton;
import me.retucio.camtweaks.ui.settings.SettingButton;
import me.retucio.camtweaks.ui.settings.SliderButton;
import me.retucio.camtweaks.util.Colors;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import java.util.ArrayList;
import java.util.List;
// marco para los módulos
public class ModuleFrame {
public int x, y, w, h, dragX, dragY;
public boolean dragging, extended;
public String title = "módulos -";
private final List<ModuleButton> moduleButtons = new ArrayList<>();
protected MinecraftClient mc = MinecraftClient.getInstance();
public ModuleFrame(int x, int y, int w, int h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
dragging = false;
extended = true;
// determinar el saliente para cada botón de módulo
int offset = h;
for (Module module : CameraTweaks.moduleManager.getModules()) {
moduleButtons.add(new ModuleButton(module, this, offset));
updateWidth();
offset += h;
}
}
void updateWidth() {
// asegurarse de que todos los botones caben en el marco, haciendo que la anchura se ajuste al texto más largo
int maxWidth = mc.textRenderer.getWidth(title);
for (ModuleButton button : moduleButtons) {
String text = button.module.getName();
int textWidth = mc.textRenderer.getWidth(text);
maxWidth = Math.max(maxWidth, textWidth);
}
this.w = maxWidth + 22;
}
public void render(DrawContext ctx, int mouseX, int mouseY, float delta) {
updateWidth();
ctx.fill(x, y, x + w, y + h, Colors.frameHeadColor); // cabeza del marco
// título del marco
ctx.drawText(mc.textRenderer, title,
x + (w / 2) - (mc.textRenderer.getWidth(title) / 2),
y + ( h /2) - (mc.textRenderer.fontHeight / 2),
-1, false);
// dibujar sus módulos solo si está extendido
if (extended) {
int totalHeight = moduleButtons.size() * h + 3;
ctx.fill( // fondo para los botones
x, y + h + 1,
x + w, y + h + totalHeight,
Colors.frameBGColor);
// dibujar los botones para cada módulo
for (ModuleButton moduleButton : moduleButtons)
moduleButton.render(ctx, mouseX, mouseY, delta);
}
}
public void mouseClicked(double mouseX, double mouseY, int button) {
// registrar clics
if (isHovered(mouseX, mouseY)) {
if (button == 0) { // clic izquierdo para arrastrarlo
dragging = true;
dragX = (int) (mouseX - x);
dragY = (int) (mouseY - y);
} else if (button == 1) { // clic derecho para extenderlo
extended = !extended;
title = extended ? "módulos -" : "módulos +";
}
}
if (!extended) return; // solo dejar clicar en los módulos si el marco está extendido
for (ModuleButton moduleButton : moduleButtons)
moduleButton.mouseClicked(mouseX, mouseY, button);
}
// detectar cuándo se suelta el clic
public void mouseReleased(double mouseX, double mouseY, int button) {
if (button == 0 && dragging) dragging = false;
}
// verificar si el puntero del ratón se encuentra encima
public boolean isHovered(double mouseX, double mouseY) {
return mouseX > x && mouseX < x + w && mouseY > y && mouseY < y + h;
}
// actualizar la posición al arrastrar el marco
public void updatePosition(double mouseX, double mouseY) {
if (dragging) {
x = (int) (mouseX - dragX);
y = (int) (mouseY - dragY);
}
}
}
@@ -1,74 +0,0 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.settings.KeySetting;
import me.retucio.camtweaks.util.Colors;
import me.retucio.camtweaks.util.KeyUtil;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import org.lwjgl.glfw.GLFW;
// botón para configurar la tecla asignada a un módulo. comienza a escuchar al hacerle clic
public class BindButton extends SettingButton {
private final KeySetting setting;
private boolean listening = false;
public BindButton(KeySetting setting, SettingsFrame parent, int offset) {
super(setting, parent, offset);
this.setting = setting;
}
@Override
public void render(DrawContext ctx, double mouseX, double mouseY, float delta) {
int x = parent.x + 4;
int y = parent.y + offset + 3;
int w = parent.w - 8;
int bgColor = isHovered((int) mouseX, (int) mouseY)
? Colors.hoveredCycleButtonColor
: Colors.cycleButtonColor;
ctx.fill(x, y, x + w, y + height, bgColor);
String label = setting.getName() + ": " + (listening ? "..." : setting.getKeyName());
ctx.drawTextWithShadow(parent.mc.textRenderer, label, x + 5, y + 3, -1);
if (isHovered((int) mouseX, (int) mouseY)) {
Screen currentScreen = parent.mc.currentScreen;
if (currentScreen != null)
ctx.drawTooltip(Text.of(setting.getDescription()), (int) mouseX, (int) mouseY + 20);
}
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered((int) mouseX, (int) mouseY)) {
if (button == 0) listening = !listening;
else if (button == 1 && KeyUtil.isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT))
setting.reset();
} else {
listening = false;
}
}
public void onKey(int key, int action) {
if (listening && action == GLFW.GLFW_PRESS) {
if (key == GLFW.GLFW_KEY_ESCAPE)
setting.setKey(GLFW.GLFW_KEY_UNKNOWN);
else
setting.setKey(key);
listening = false;
}
}
public boolean isFocused() {
return listening;
}
public KeySetting getSetting() {
return setting;
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {}
}
@@ -1,53 +0,0 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.modules.GUI;
import me.retucio.camtweaks.util.Colors;
import net.minecraft.client.gui.DrawContext;
public class ClickGUISettingsFrame extends SettingsFrame {
public static final GUI guiSettings = new GUI();
private boolean extended = false;
private String title = "ajustes de la interfaz -";
public ClickGUISettingsFrame(int x, int y, int w, int h) {
super(guiSettings, x, y, w, h);
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered(mouseX, mouseY)) {
if (button == 0) {
dragging = true;
dragX = (int) (mouseX - x);
dragY = (int) (mouseY - y);
} else if (button == 1) {
extended = !extended;
title = extended ? "ajustes de la interfaz -" : "ajustes de la interfaz +";
}
}
if (!extended) return; // solo dejar clicar en los módulos si el marco está extendido
for (SettingButton settingButton : settingButtons)
settingButton.mouseClicked(mouseX, mouseY, button);
}
@Override
public void render(DrawContext ctx, int mouseX, int mouseY, float delta) {
updateWidth();
ctx.fill(x, y, x + w, y + h, Colors.frameHeadColor);
ctx.drawText(mc.textRenderer, title,
x + (w / 2) - (mc.textRenderer.getWidth(title) / 2),
y + (h / 2) - (mc.textRenderer.fontHeight / 2),
-1, false);
if (extended) {
int totalHeight = settingButtons.size() * h + 3;
ctx.fill(x, y + h + 1, x + w, y + h + totalHeight, Colors.frameBGColor);
for (SettingButton button : settingButtons)
button.render(ctx, mouseX, mouseY, delta);
}
}
}
@@ -1,84 +0,0 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.settings.EnumSetting;
import me.retucio.camtweaks.util.Colors;
import me.retucio.camtweaks.util.KeyUtil;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import org.lwjgl.glfw.GLFW;
import java.util.ArrayList;
import java.util.List;
// botón para los ajustes tipo EnumSetting (de modo)
public class CycleButton<E extends Enum<E>> extends SettingButton {
private final EnumSetting<E> setting;
public CycleButton(EnumSetting<E> setting, SettingsFrame parent, int offset) {
super(setting, parent, offset);
this.setting = setting;
}
@Override
public void render(DrawContext ctx, double mouseX, double mouseY, float delta) {
int x = parent.x + 4;
int y = parent.y + offset + 3;
int w = parent.w - 8;
int bgColor = isHovered((int) mouseX, (int) mouseY)
? Colors.hoveredCycleButtonColor
: Colors.cycleButtonColor;
ctx.fill(x, y, x + w, y + height, bgColor); // fondo del botón
// texto del botón: nombre + valor de texto del enum
String label = setting.getName() + ": " + setting.getValue().toString();
ctx.drawTextWithShadow(parent.mc.textRenderer, label, x + 5, y + 3, -1);
// tooltip
if (isHovered((int) mouseX, (int) mouseY)) {
Screen currentScreen = parent.mc.currentScreen;
if (currentScreen != null)
// si shift está presionado mientras el puntero está encima, mostrar los modos disponibles
if (GLFW.glfwGetKey(parent.mc.getWindow().getHandle(), GLFW.GLFW_KEY_LEFT_SHIFT) != GLFW.GLFW_RELEASE) {
List<Text> lines = new ArrayList<>();
lines.add(Text.literal("modos disponibles:"));
for (Enum<?> val : setting.getValues()) {
if (val == setting.getValue())
lines.add(Text.literal("> " + val.toString() + " <").formatted(Formatting.GREEN)); // destacar modo actualmente seleccionado
else
lines.add(Text.literal(val.toString()));
}
ctx.drawTooltip(parent.mc.textRenderer, lines, (int) mouseX, (int) mouseY + 20);
// si shift no está siendo presionado, simplemente mostrar la descripción
} else {
ctx.drawTooltip(Text.of(setting.getDescription()), (int) mouseX, (int) mouseY + 20);
}
}
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered((int) mouseX, (int) mouseY)) {
if (button == 0) {
setting.cycle(); // clic izquierdo -> ciclar
} else if (button == 1) {
// shift + clic derecho -> restablecer
if (KeyUtil.isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT))
setting.reset();
else
setting.cycleBackwards(); // simplemente clic derecho -> ciclar valores pero hacia atrás
}
}
}
public EnumSetting<E> getSetting() {
return setting;
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {}
}
@@ -1,28 +0,0 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.settings.Setting;
import net.minecraft.client.gui.DrawContext;
// clase base para los botones de los ajustes
public abstract class SettingButton {
protected final Setting setting;
protected final SettingsFrame parent;
protected int offset;
protected final int height = 15;
public SettingButton(Setting setting, SettingsFrame parent, int offset) {
this.setting = setting;
this.parent = parent;
this.offset = offset;
}
public abstract void render(DrawContext ctx, double mouseX, double mouseY, float delta);
public abstract void mouseClicked(double mouseX, double mouseY, int button);
public abstract void mouseReleased(double mouseX, double mouseY, int button);
protected boolean isHovered(int mouseX, int mouseY) {
return mouseX > parent.x + 4 && mouseX < parent.x + parent.w - 4 &&
mouseY > parent.y + offset && mouseY < parent.y + offset + height;
}
}
@@ -1,152 +0,0 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.*;
import me.retucio.camtweaks.ui.ClickGUI;
import me.retucio.camtweaks.util.Colors;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
// marco para los botones de los ajustes de cada módulo
public class SettingsFrame {
private final String title;
public final Module module;
final List<SettingButton> settingButtons = new ArrayList<>();
public int x, y, w, h;
boolean dragging;
int dragX;
int dragY;
final MinecraftClient mc = MinecraftClient.getInstance();
public SettingsFrame(Module module, int x, int y, int w, int h) {
this.module = module;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
// añadir el respectivo tipo de botón de cada ajuste a la lista de botones
int offset = h;
for (Setting setting : module.getSettings()) {
if (!setting.isVisible()) continue;
switch (setting) {
case BooleanSetting b -> {
addButton(new ToggleButton(b, this, offset));
offset += 18;
} case NumberSetting n -> {
addButton(new SliderButton(n, this, offset));
offset += 18;
} case EnumSetting e -> {
addButton(new CycleButton<>(e, this, offset));
offset += 18;
} case KeySetting k -> {
addButton(new BindButton(k, this, offset));
offset += 18;
} case StringSetting s -> {
addButton(new TextButton(s, this, offset));
offset += 18;
} default -> {}
}
}
title = "ajustes de " + module.getName() + " -";
}
public void addButton(SettingButton button) {
settingButtons.add(button);
updateWidth(); // actualizar anchura del frame tras cada iteración
}
void updateWidth() {
// asegurarse de que todos los botones caben en el marco, haciendo que la anchura se ajuste al texto más largo
int maxWidth = mc.textRenderer.getWidth(title);
for (SettingButton button : settingButtons) {
String text = button.setting.getName();
switch (button) {
case SliderButton sliderButton -> text += ": " + sliderButton.df.format((sliderButton.getSetting()).getValue());
case CycleButton cycleButton -> text += ": " + cycleButton.getSetting().getValue();
case BindButton bindButton -> text += ": " + bindButton.getSetting().getKeyName();
case TextButton textButton -> text += ": " + textButton.getSetting().getValue();
default -> {}
}
int textWidth = mc.textRenderer.getWidth(text);
maxWidth = Math.max(maxWidth, textWidth);
}
this.w = maxWidth + 22;
}
public void render(DrawContext ctx, int mouseX, int mouseY, float delta) {
updateWidth();
ctx.fill(x, y, x + w, y + h, module.isEnabled()
? Colors.enabledToggleButtonColor.getRGB()
: Colors.disabledToggleButtonColor.getRGB());
ctx.drawText(mc.textRenderer, title,
x + (w / 2) - (mc.textRenderer.getWidth(title) / 2),
y + (h / 2) - (mc.textRenderer.fontHeight / 2),
-1, false);
// si no hay ajustes disponibles para un módulo, hacerle saber al usuario
if (settingButtons.isEmpty()) {
String msg = "no hay ajustes :(";
ctx.fill(x, y + h, x + w, y + 2 * h, Colors.frameBGColor);
ctx.drawText(mc.textRenderer, msg,
x + (w / 2) - (mc.textRenderer.getWidth(msg) / 2),
(int) (y + h * 1.25),
-1, false);
return;
}
int totalHeight = settingButtons.size() * h + 3;
ctx.fill(x, y + h + 1, x + w, y + h + totalHeight, Colors.frameBGColor);
for (SettingButton sb : settingButtons)
if (sb.setting.isVisible()) sb.render(ctx, mouseX, mouseY, delta);
}
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered(mouseX, mouseY)) {
if (button == 0) {
dragging = true;
dragX = (int) (mouseX - x);
dragY = (int) (mouseY - y);
} else if (button == 1) {
ClickGUI.INSTANCE.closeSettingsFrame(module);
}
}
for (SettingButton sb : settingButtons)
if (sb.setting.isVisible()) sb.mouseClicked(mouseX, mouseY, button);
}
public void mouseRelease(double mouseX, double mouseY, int button) {
if (button == 0) dragging = false;
for (SettingButton sb : settingButtons)
if (sb.setting.isVisible()) sb.mouseReleased(mouseX, mouseY, button);
}
public void updatePosition(double mouseX, double mouseY) {
if (dragging) {
x = (int) (mouseX - dragX);
y = (int) (mouseY - dragY);
}
}
boolean isHovered(double mouseX, double mouseY) {
return mouseX > x && mouseX < x + w &&
mouseY > y && mouseY < y + h;
}
public List<SettingButton> getButtons() {
return settingButtons;
}
}
@@ -1,76 +0,0 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.settings.NumberSetting;
import me.retucio.camtweaks.util.Colors;
import me.retucio.camtweaks.util.KeyUtil;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import org.lwjgl.glfw.GLFW;
import java.awt.*;
import java.text.DecimalFormat;
// slider (barrita) para los ajustes numéricos
public class SliderButton extends SettingButton {
private final NumberSetting setting;
private boolean dragging = false;
public final DecimalFormat df; // usar formato decimal para el valor del ajuste
public SliderButton(NumberSetting setting, SettingsFrame parent, int offset) {
super(setting, parent, offset);
this.setting = setting;
this.df = new DecimalFormat("#.##"); // solo mostrar dos décimales
}
@Override
public void render(DrawContext ctx, double mouseX, double mouseY, float delta) {
int x = parent.x + 4;
int y = parent.y + offset + 3;
int w = parent.w - 8;
ctx.fill(x, y, x + w, y + height, Colors.moduleButtonColor);
// calcular cómo de llena tendría que estar el "slider"
double percent = (setting.getValue() - setting.getMin()) / (setting.getMax() - setting.getMin());
int filled = (int) (percent * w);
ctx.fill(x, y, x + filled, y + height, Colors.sliderFillingColor);
String label = setting.getName() + ": " + df.format(setting.getValue());
ctx.drawTextWithShadow(parent.mc.textRenderer, label, x + 5, y + 3, -1);
// lógica para el arrastre del valor
if (dragging) {
double newVal = setting.getMin() + ((mouseX - x) / (double) w) * (setting.getMax() - setting.getMin());
newVal = Math.max(setting.getMin(), Math.min(setting.getMax(), newVal));
newVal = Math.round(newVal / setting.getIncrement()) * setting.getIncrement();
setting.setValue((float) newVal);
}
// dibujar "tooltips" (cajas de texto) al pasar el puntero encima del botón, para mostrar su descripción
if (isHovered((int) mouseX, (int) mouseY)) {
Screen currentScreen = parent.mc.currentScreen;
if (currentScreen != null)
ctx.drawTooltip(Text.of(setting.getDescription()), (int) mouseX, (int) mouseY + 20);
}
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered((int) mouseX, (int) mouseY)) {
if (button == 0) dragging = true; // arrastrar con el clic izquierdo
else if (button == 1 && KeyUtil.isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT)) // restablecer al valor por defecto con shift izquerdo + clic derecho
setting.reset();
}
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {
if (button == 0) dragging = false;
}
public NumberSetting getSetting() {
return setting;
}
}
@@ -1,97 +0,0 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.settings.StringSetting;
import me.retucio.camtweaks.util.Colors;
import me.retucio.camtweaks.util.KeyUtil;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.text.Text;
import org.lwjgl.glfw.GLFW;
// botón para los ajustes que requieran introducir texto
public class TextButton extends SettingButton {
private boolean typing;
private final StringSetting setting;
private final StringBuilder buffer = new StringBuilder();
public TextButton(StringSetting setting, SettingsFrame parent, int offset) {
super(setting, parent, offset);
this.setting = setting;
}
@Override
public void render(DrawContext ctx, double mouseX, double mouseY, float delta) {
int x = parent.x + 4;
int y = parent.y + offset + 3;
int w = parent.w - 8;
int bgColor = isHovered((int) mouseX, (int) mouseY)
? Colors.hoveredCycleButtonColor
: Colors.cycleButtonColor;
ctx.fill(x, y, x + w, y + height, bgColor);
String label = setting.getName() + ": " + (typing ? buffer.toString() + "_" : setting.getValue());
ctx.drawTextWithShadow(parent.mc.textRenderer, label, x + 5, y + 3, -1);
if (isHovered((int) mouseX, (int) mouseY)) {
if (parent.mc.currentScreen != null)
ctx.drawTooltip(Text.of(setting.getDescription()), (int) mouseX, (int) mouseY + 20);
}
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered((int) mouseX, (int) mouseY)) {
if (button == 0) {
typing = true;
buffer.setLength(0);
buffer.append(setting.getValue()); // precargar valor anterior
} else if (button == 1 && KeyUtil.isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT)) {
typing = false;
setting.reset();
}
} else {
typing = false;
}
}
public void onKey(int key, int action) {
if (!typing || action != GLFW.GLFW_PRESS) return;
switch (key) { // casos para teclas especiales como enter, escape o borrar
case GLFW.GLFW_KEY_ENTER -> {
setting.setValue(buffer.toString());
typing = false;
} case GLFW.GLFW_KEY_BACKSPACE -> {
buffer.deleteCharAt(buffer.length() - 1);
} case GLFW.GLFW_KEY_ESCAPE -> {
typing = false;
} default -> {
String c = KeyUtil.getKeyName(key);
if (c.length() == 1) {
if (KeyUtil.isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT) || KeyUtil.isKeyDown(GLFW.GLFW_KEY_RIGHT_SHIFT))
charTyped(c.toUpperCase().charAt(0));
else
charTyped(c.toLowerCase().charAt(0));
}
}
}
}
private void charTyped(char c) {
if (!typing) return;
buffer.append(c);
}
public boolean isFocused() {
return typing;
}
public StringSetting getSetting() {
return setting;
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {}
}
@@ -1,63 +0,0 @@
package me.retucio.camtweaks.ui.settings;
import me.retucio.camtweaks.module.settings.BooleanSetting;
import me.retucio.camtweaks.util.Colors;
import me.retucio.camtweaks.util.KeyUtil;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import org.lwjgl.glfw.GLFW;
import java.awt.*;
// botón para los ajustes booleanos (funciona como un interruptor)
public class ToggleButton extends SettingButton {
private final BooleanSetting setting;
public ToggleButton(BooleanSetting setting, SettingsFrame parent, int offset) {
super(setting, parent, offset);
this.setting = setting;
}
@Override
public void render(DrawContext ctx, double mouseX, double mouseY, float delta) {
int x = parent.x + 4;
int y = parent.y + offset + 3;
int w = parent.w - 8;
Color color = setting.isEnabled()
? Colors.enabledToggleButtonColor
: Colors.disabledToggleButtonColor;
if (isHovered((int) mouseX, (int) mouseY))
color = color.brighter(); // simplemente utilizar el método "built-in" de Java para hacer el botón más claro cuando el puntero se encuentra encima de él
ctx.fill(x, y, x + w, y + height, color.getRGB());
ctx.drawTextWithShadow(parent.mc.textRenderer,
setting.getName(), x + 5, y + 3, -1);
// dibujar "tooltips" (cajas de texto) al pasar el puntero encima del botón, para mostrar su descripción
if (isHovered((int) mouseX, (int) mouseY)) {
Screen currentScreen = parent.mc.currentScreen;
if (currentScreen != null)
ctx.drawTooltip(Text.of(setting.getDescription()), (int) mouseX, (int) mouseY + 20);
}
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
if (isHovered((int) mouseX, (int) mouseY)) {
if (button == 0) setting.toggle(); // clic izquierdo para alternar
else if (button == 1 && KeyUtil.isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT)) // shift izquierdo + clic derecho para restablecer al valor por defecto
setting.reset();
}
}
public BooleanSetting getSetting() {
return setting;
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {}
}
@@ -1,60 +0,0 @@
package me.retucio.camtweaks.util;
import net.minecraft.client.MinecraftClient;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
// cosas útiles relacionadas al chat
public class ChatUtil {
private static final MinecraftClient mc = MinecraftClient.getInstance();
public static void addMessage(String text) {
mc.inGameHud.getChatHud().addMessage(Text.of(text));
}
public static void addMessage(Text text) {
mc.inGameHud.getChatHud().addMessage(text);
}
public static void addMessageWithPrefix(String text) {
addMessageWithPrefix(Text.of(text));
}
public static void addMessageWithPrefix(Text text) {
addMessage(Text.literal(Formatting.GREEN + "[smegma] " + Formatting.RESET).append(text));
}
public static void info(String text) {
addMessage(Text.literal(Formatting.GREEN + "[smegma] " + Formatting.RESET + text));
}
public static void info(Text text) {
addMessage(Text.literal(Formatting.GREEN + "[smegma] " + Formatting.RESET).append(text));
}
public static void warn(String text) {
addMessage(Text.literal(Formatting.GREEN + "[smegma] " + Formatting.YELLOW + text));
}
public static void warn(Text text) {
addMessage(Text.literal(Formatting.GREEN + "[smegma] " + Formatting.YELLOW).append(text));
}
public static void error(String text) {
addMessage(Text.literal(Formatting.GREEN + "[smegma] " + Formatting.RED + text));
}
public static void error(Text text) {
addMessage(Text.literal(Formatting.GREEN + "[smegma] " + Formatting.RED).append(text));
}
public static void sendServerMessage(String text) {
sendServerMessage(Text.of(text));
}
public static void sendServerMessage(Text text) {
if (mc.player == null) return;
mc.player.sendMessage(text, false);
}
}
@@ -1,41 +0,0 @@
package me.retucio.camtweaks.util;
import me.retucio.camtweaks.ui.ClickGUI;
import java.awt.*;
import static me.retucio.camtweaks.ui.settings.ClickGUISettingsFrame.guiSettings;
// interfaz para los colores, para no repetirme (regla DRY)
// si se ve especificado como color en algún lado "-1", significa blanco
public class Colors {
public static int frameHeadRed = guiSettings.red.getIntValue();
public static int frameHeadGreen = guiSettings.green.getIntValue();
public static int frameHeadBlue = guiSettings.blue.getIntValue();
public static int frameHeadAlpha = guiSettings.alpha.getIntValue();
public static int frameHeadColor = new Color(
frameHeadRed,
frameHeadGreen,
frameHeadBlue,
frameHeadAlpha
).getRGB();
public static int frameBGColor = new Color(60, 60, 60, 70).getRGB();
public static int enabledHoveredModuleButtonColor = new Color(20, 20, 255, 180).getRGB();
public static int enabledModuleButtonColor = new Color(0, 0, 255, 180).getRGB();
public static int hoveredModuleButtonColor = new Color(30, 30, 30, 100).getRGB();
public static int moduleButtonColor = new Color(40, 40, 40, 75).getRGB();
public static int hoveredCycleButtonColor = new Color(70, 70, 70, 180).getRGB();
public static int cycleButtonColor = new Color(50, 50, 50, 150).getRGB();
public static int sliderFillingColor = new Color(60, 120, 220, 180).getRGB();
// necesitan ser de tipo java.awt.Color para utilizar el método .brighter()
public static Color enabledToggleButtonColor = new Color(0, 180, 0, 180);
public static Color disabledToggleButtonColor = new Color(120, 30, 30, 150);
}
@@ -1,45 +0,0 @@
package me.retucio.camtweaks.util;
import me.retucio.camtweaks.CameraTweaks;
import org.lwjgl.glfw.GLFW;
// cosas útiles relacionadas a las teclas
public class KeyUtil {
public static boolean isKeyDown(int key) {
return GLFW.glfwGetKey(
CameraTweaks.mc.getWindow().getHandle(),
key) != GLFW.GLFW_RELEASE;
}
public static String getKeyName(int key) {
if (key == GLFW.GLFW_KEY_UNKNOWN) return "ninguna";
String name = GLFW.glfwGetKeyName(key, 0);
if (name != null) return name.toUpperCase();
// para teclas especiales
return switch (key) {
case GLFW.GLFW_KEY_SPACE -> "espacio";
case GLFW.GLFW_KEY_LEFT_SHIFT -> "shift izquierdo";
case GLFW.GLFW_KEY_RIGHT_SHIFT -> "shift derecho";
case GLFW.GLFW_KEY_LEFT_CONTROL -> "ctrl izquierdo";
case GLFW.GLFW_KEY_RIGHT_CONTROL -> "ctrl derecho";
case GLFW.GLFW_KEY_LEFT_ALT -> "alt izquierdo";
case GLFW.GLFW_KEY_RIGHT_ALT -> "alt gr";
case GLFW.GLFW_KEY_TAB -> "tab";
case GLFW.GLFW_KEY_ENTER -> "enter";
case GLFW.GLFW_KEY_BACKSPACE -> "borrar";
case GLFW.GLFW_KEY_DELETE -> "suprimir";
case GLFW.GLFW_KEY_UP -> "arriba";
case GLFW.GLFW_KEY_DOWN -> "abajo";
case GLFW.GLFW_KEY_LEFT -> "izquierda";
case GLFW.GLFW_KEY_RIGHT -> "derecha";
default -> { // F1–F25
if (key >= GLFW.GLFW_KEY_F1 && key <= GLFW.GLFW_KEY_F25)
yield "F" + (key - GLFW.GLFW_KEY_F1 + 1);
yield "tecla " + key;
}
};
}
}
@@ -1,8 +0,0 @@
package me.retucio.camtweaks.util.interfaces;
import net.minecraft.text.Text;
public interface IChatHud {
void smegma$add(Text message, int id);
}
@@ -1,14 +0,0 @@
package me.retucio.camtweaks.util.interfaces;
import com.mojang.authlib.GameProfile;
public interface IChatHudLine {
String smegma$getText();
int smegma$getId();
void smegma$setId(int id);
GameProfile smegma$getSender();
void smegma$setSender(GameProfile profile);
}
@@ -1,7 +0,0 @@
package me.retucio.camtweaks.util.interfaces;
public interface IChatHudLineVisible extends IChatHudLine {
boolean smegma$isStartOfEntry();
void smegma$setStartOfEntry(boolean start);
}
@@ -1,8 +0,0 @@
package me.retucio.camtweaks.util.interfaces;
import com.mojang.authlib.GameProfile;
public interface IMessageHandler {
GameProfile smegma$getSender();
}
@@ -1,41 +0,0 @@
package me.retucio.camtweaks.util.interfaces;
import net.minecraft.text.PlainTextContent;
import net.minecraft.text.StringVisitable;
import net.minecraft.text.Style;
import net.minecraft.text.Text;
import java.util.ArrayDeque;
import java.util.Optional;
import java.util.Queue;
@FunctionalInterface
public interface TextVisitor<T> {
Optional<T> accept(Text text, Style style, String string);
static <T> Optional<T> visit(Text text, TextVisitor<T> visitor, Style baseStyle) {
Queue<Text> queue = collectSiblings(text);
return text.visit((style, string) -> visitor.accept(queue.remove(), style, string), baseStyle);
}
/**
* Collapses the tree of {@link Text} siblings into a one dimensional FIFO {@link Queue}. To match the behaviours of
* the {@link Text#visit(StringVisitable.Visitor)} and {@link Text#visit(StringVisitable.StyledVisitor, Style)}
* methods, texts with empty contents (created from {@link Text#empty()}) are ignored but their siblings are still
* processed.
* @param text the text
* @return the text and its siblings in the order they appear when rendered.
*/
static ArrayDeque<Text> collectSiblings(Text text) {
ArrayDeque<Text> queue = new ArrayDeque<>();
collectSiblings(text, queue);
return queue;
}
private static void collectSiblings(Text text, Queue<Text> queue) {
if (!(text.getContent() instanceof PlainTextContent ptc) || !ptc.string().isEmpty()) queue.add(text);
for (Text sibling : text.getSiblings()) {
collectSiblings(sibling, queue);
}
}
}
@@ -0,0 +1,331 @@
package me.retucio.sputnik;
import com.github.retucio.neutrino.EventBus;
import com.github.retucio.neutrino.EventListener;
import com.mojang.blaze3d.platform.InputConstants;
import me.retucio.sputnik.cape.CapeManager;
import me.retucio.sputnik.command.CommandManager;
import me.retucio.sputnik.command.commands.BindCommand;
import me.retucio.sputnik.config.ConfigManager;
import me.retucio.sputnik.config.ProfileManager;
import me.retucio.sputnik.macro.MacroManager;
import me.retucio.sputnik.event.ShutdownEvent;
import me.retucio.sputnik.event.interact.OpenScreenEvent;
import me.retucio.sputnik.event.sputnik.*;
import me.retucio.sputnik.friend.FriendManager;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.client.Hud;
import me.retucio.sputnik.ui.hud.HudEditor;
import me.retucio.sputnik.ui.hud.HudRenderer;
import me.retucio.sputnik.ui.screen.ClickGui;
import me.retucio.sputnik.ui.hud.HudEditorScreen;
import me.retucio.sputnik.ui.screen.UpdateScreen;
import me.retucio.sputnik.ui.widgets.buttons.settings.BindButton;
import me.retucio.sputnik.ui.widgets.buttons.SettingButton;
import me.retucio.sputnik.ui.widgets.buttons.settings.FileButton;
import me.retucio.sputnik.ui.widgets.buttons.settings.TextButton;
import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel;
import me.retucio.sputnik.ui.widgets.panels.SettingsPanel;
import me.retucio.sputnik.ui.widgets.Button;
import me.retucio.sputnik.util.*;
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.RenderUtil;
import me.retucio.sputnik.util.render.Textures;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.ChatScreen;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.client.gui.screens.inventory.AbstractCommandBlockEditScreen;
import net.minecraft.client.gui.screens.inventory.AbstractSignEditScreen;
import net.minecraft.client.gui.screens.inventory.AnvilScreen;
import net.minecraft.network.chat.Component;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Sputnik implements ClientModInitializer {
public static final Sputnik INSTANCE = new Sputnik();
// cosas del mod
public static final EventBus EVENT_BUS = new EventBus();
public static final Logger LOGGER = LogManager.getLogger(Sputnik.class);
public static final long LAUNCH_TIME = System.currentTimeMillis();
public static Minecraft mc;
// id y versión
public static final String MOD_ID = "sputnik";
public static final String MOD_VERSION = FabricLoader.getInstance()
.getModContainer(MOD_ID)
.orElseThrow()
.getMetadata()
.getVersion()
.getFriendlyString();
private Screen prevScreen;
public static boolean settingsApplied = false;
static {
// puta vida
System.setProperty("java.awt.headless", "false");
}
@Override
public void onInitializeClient() {
mc = Minecraft.getInstance();
ConfigManager.INSTANCE = new ConfigManager();
ConfigManager.INSTANCE.load();
VersionChecker.INSTANCE.check();
EVENT_BUS.subscribe(this);
EVENT_BUS.subscribe(ChatUtil.class);
EVENT_BUS.subscribe(DrawUtil.class);
EVENT_BUS.subscribe(EntityUtil.class);
EVENT_BUS.subscribe(InventoryUtil.class);
EVENT_BUS.subscribe(MiscUtil.class);
EVENT_BUS.subscribe(NetworkUtil.class);
EVENT_BUS.subscribe(RenderUtil.class);
EVENT_BUS.subscribe(RotationUtil.class);
EVENT_BUS.subscribe(BlockIterator.class);
Lists.init();
HudRenderer.INSTANCE = new HudRenderer();
HudEditor.INSTANCE = new HudEditor();
MacroManager.INSTANCE = new MacroManager();
ProfileManager.INSTANCE = new ProfileManager();
CapeManager.INSTANCE = new CapeManager();
EVENT_BUS.post(new LoadCapeManagerEvent());
ModuleManager.INSTANCE = new ModuleManager();
EVENT_BUS.post(new LoadModuleManagerEvent());
CommandManager.INSTANCE = new CommandManager();
EVENT_BUS.post(new LoadCommandManagerEvent());
FriendManager.INSTANCE = new FriendManager();
EVENT_BUS.post(new LoadFriendManagerEvent());
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(() -> {
EVENT_BUS.post(new ShutdownEvent());
}));
}
// se ejecuta cada tick, es decir, 20 veces por segundo
public void onTick() {
if (!settingsApplied && !ConfigManager.INSTANCE.hasLoaded()) {
settingsApplied = ConfigManager.INSTANCE.applyConfig();
}
ModuleManager.INSTANCE.getEnabledModules().forEach(Module::onTick);
mc.getWindow().setTitle(ClientSettingsPanel.clientSettings.windowTitle.getValue());
ConfigManager.INSTANCE.tickAutosave();
}
// se ejecuta cada vez que se presiona una tecla
public void onInput(int key, int action) {
// En SDL el valor 0 es UNKNOWN (y los botones del ratón empiezan en 1).
if (key == InputConstants.UNKNOWN.getValue() || key == -1) return;
// prevenir interrumpir combos de F3
if (key == InputConstants.KEY_F3) return;
boolean anyFocused = isAnySettingButtonFocused() || ClickGui.INSTANCE.isCapturingInput();
ClickGui.INSTANCE.setAnyFocused(anyFocused);
// Las KeySetting y los campos de texto reciben el mismo código SDL scancode que
// guardan sus valores; procesarlos antes evita que el bind capturado dispare un módulo.
if (anyFocused) handleSettingButtonsKey(key, action);
if (action != InputConstants.RELEASE) {
if (BindCommand.onKeyPress(key)) return;
if (action == InputConstants.PRESS && MacroManager.INSTANCE.onKeyPress(key)) return;
if (action == InputConstants.PRESS) {
handleModuleToggle(key, anyFocused);
handleClickGuiKey(key, anyFocused);
handleHudEditorKey(key, anyFocused);
}
} else if (!anyFocused) {
handleModuleRelease(key);
}
}
public boolean onMouseInput(int button, int action) {
KeyUtil.setMouseButtonDown(button, action == InputConstants.PRESS);
boolean captured = false;
for (Button settingButton : ClickGui.INSTANCE.getClientSettingsPanel().getButtons()) {
if (settingButton instanceof BindButton bindButton && bindButton.isFocused()) {
bindButton.onMouse(button, action);
captured = true;
}
}
for (SettingsPanel panel : ClickGui.INSTANCE.getSettingsPanels()) {
for (Button settingButton : panel.getButtons()) {
if (settingButton instanceof BindButton bindButton && bindButton.isFocused()) {
bindButton.onMouse(button, action);
captured = true;
}
}
}
if (captured) return true;
onInput(KeyUtil.encodeMouseButton(button), action);
return false;
}
// verifica si algún botón de ajustes está escuchando
private boolean isAnySettingButtonFocused() {
if (ClickGui.INSTANCE == null) return false;
for (Button button : ClickGui.INSTANCE.getClientSettingsPanel().getButtons())
if ((button instanceof BindButton bindButton && bindButton.isFocused())
|| (button instanceof TextButton textButton && textButton.isFocused())
|| (button instanceof FileButton fileButton && fileButton.isFocused())) {
return true;
}
for (SettingsPanel sf : ClickGui.INSTANCE.getSettingsPanels())
for (Button sb : sf.getButtons())
if ((sb instanceof BindButton b && b.isFocused())
|| (sb instanceof TextButton t && t.isFocused())
|| (sb instanceof FileButton f && f.isFocused())
) {
return true;
}
return false;
}
// se ocupa de la lógica de encendido y apagado de los módulos
private void handleModuleToggle(int key, boolean anyFocused) {
if (mc.gui.screen() != null && mc.gui.screen() != ClickGui.INSTANCE) return;
for (Module module : ModuleManager.INSTANCE.getModules()) {
// evitar interrumpir combinaciones de teclas del F3
if (key != module.getKey() || anyFocused || KeyUtil.isKeyDown(InputConstants.KEY_F3)) continue;
if (module.shouldToggleOnBindRelease() && !module.isEnabled())
module.setEnabled(true);
else if (!module.shouldToggleOnBindRelease())
module.toggle();
}
}
// se ocupa de hacer los botones de ajustes que lo necesiten escuchar teclas
private void handleSettingButtonsKey(int key, int action) {
ClickGui.INSTANCE.getClientSettingsPanel().onKey(key, action);
for (SettingsPanel sf : ClickGui.INSTANCE.getSettingsPanels()) {
for (SettingButton<?> sb : sf.getButtons()) {
if (sb instanceof BindButton bb) bb.onKey(key, action);
if (sb instanceof FileButton fb) fb.onKey(key, action);
if (sb instanceof TextButton tb) tb.onKey(key, action);
}
}
}
// maneja la lógica de apertura de la interfaz
private void handleClickGuiKey(int key, boolean anyFocused) {
if (key != ClientSettingsPanel.clientSettings.getKey() || anyFocused || isOnTypingScreen())
return;
if (mc.gui.screen() != ClickGui.INSTANCE) {
prevScreen = mc.gui.screen();
mc.gui.setScreen(ClickGui.INSTANCE);
} else {
ClickGui.INSTANCE.onClose();
mc.gui.setScreen(prevScreen);
}
}
// manejar la lógica de apertura del editor de elementos del hud, con la misma lógica que handleClickGuiKey()
private void handleHudEditorKey(int key, boolean anyFocused) {
Hud hud = ModuleManager.INSTANCE.getModuleByClass(Hud.class);
if (key != hud.editorKey.getValue() || anyFocused || isOnTypingScreen()) return;
if (mc.gui.screen() != HudEditorScreen.INSTANCE) {
if (!hud.isEnabled()) {
ChatUtil.warn(Component.translatable("sputnik.hud.disabled"));
return;
}
prevScreen = mc.gui.screen();
mc.gui.setScreen(HudEditorScreen.INSTANCE);
} else {
HudEditorScreen.INSTANCE.onClose();
mc.gui.setScreen(prevScreen);
}
}
// se ocupa de apagar los módulos que tengan configurado hacerlo tras soltar su tecla
private void handleModuleRelease(int key) {
if (mc.gui.screen() != null && mc.gui.screen() != ClickGui.INSTANCE) return;
for (Module module : ModuleManager.INSTANCE.getEnabledModules())
if (module.shouldToggleOnBindRelease() && key == module.getKey())
module.setEnabled(false);
}
private boolean isOnTypingScreen() {
return mc.gui.screen() instanceof ChatScreen
|| mc.gui.screen() instanceof AnvilScreen
|| mc.gui.screen() instanceof AbstractSignEditScreen
|| mc.gui.screen() instanceof AbstractCommandBlockEditScreen;
}
private boolean keysVerified = false;
@EventListener
// notificar al usuario de que hay una actualización disponible, si la hay
public void onSetScreen(OpenScreenEvent event) {
if (event.getScreen() instanceof TitleScreen) {
// aquí el idioma ya está cargado, que en onInitializeClient todavía no
if (!keysVerified) {
keysVerified = true;
I18nUtil.verifyKeys();
}
if (VersionChecker.INSTANCE.shouldShowScreen) {
event.cancel();
VersionChecker.INSTANCE.shouldShowScreen = false;
mc.execute(() -> mc.gui.setScreen(new UpdateScreen()));
}
}
}
@EventListener
public void onStop(ShutdownEvent event) {
ConfigManager.INSTANCE.saveOnShutdown();
}
public static String getVersionName() {
return MOD_ID + "_v" + MOD_VERSION + "_" + SharedConstants.getCurrentVersion().name();
}
}
@@ -0,0 +1,62 @@
package me.retucio.sputnik.cape;
import me.retucio.sputnik.Sputnik;
import net.minecraft.client.Minecraft;
import com.mojang.blaze3d.platform.NativeImage;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.resources.Identifier;
import java.io.IOException;
import java.io.InputStream;
public class Cape {
private final Minecraft mc = Minecraft.getInstance();
private final String name;
private final Identifier id;
private NativeImage img;
private DynamicTexture texture;
public Cape(String name) {
this.name = name;
this.id = Identifier.fromNamespaceAndPath(Sputnik.MOD_ID, "textures/capes/" + name + ".png");
}
public void load() {
this.img = loadTexture(this.id);
}
public NativeImage loadTexture(Identifier id) {
try (InputStream inputStream = mc.getResourceManager().getResource(id).get().open()) {
return NativeImage.read(inputStream);
} catch (IOException e) {
Sputnik.LOGGER.error("ups: {}", e.getMessage());
return null;
}
}
public void register() {
texture = new DynamicTexture(null, img);
mc.getTextureManager().register(id, texture);
img = null;
}
public String getName() {
return name;
}
public Identifier getId() {
return id;
}
public DynamicTexture getTexture() {
return texture;
}
@Override
public String toString() {
return name.replace('-', ' ');
}
}
@@ -0,0 +1,87 @@
package me.retucio.sputnik.cape;
import java.util.ArrayList;
import java.util.List;
import static me.retucio.sputnik.Sputnik.mc;
public class CapeManager {
public static CapeManager INSTANCE;
private final List<Cape> capes = new ArrayList<>();
public CapeManager() {
addCapes();
}
public void addCapes() {
addCape("sputnik");
addCape("half-life");
addCape("hollow-knight");
addCape("cherry-blossom");
addCape("migrator");
addCape("minecraft");
addCape("pancakes");
addCape("vanilla");
addCape("tiktok");
addCape("twitch");
addCape("mojang");
addCape("mojang-old");
addCape("mojang-old-old");
addCape("minecon-2011");
addCape("minecon-2012");
addCape("minecon-2013");
addCape("minecon-2015");
addCape("minecon-2016");
addCape("experience-2025");
addCape("15th-anniversary");
addCape("bedrock");
addCape("birthday");
addCape("copper");
addCape("eye-blossom");
addCape("mcc-15");
addCape("prismarine");
addCape("turtle");
addCape("valentine");
addCape("chinese-translator");
addCape("cobalt");
addCape("mojira-mod");
addCape("millionth-customer");
addCape("realms-mapmaker");
addCape("scrolls-champion");
addCape("translator");
addCape("cheapsh0t");
addCape("dannybstyle");
addCape("julianclark");
addCape("mrmessiah");
for (Cape cape : capes) {
mc.execute(cape::load);
mc.execute(cape::register);
}
}
private void addCape(String name) {
capes.add(new Cape(name));
}
public Cape getCape(String name) {
for (Cape cape : capes)
if (cape.getName().equalsIgnoreCase(name))
return cape;
return null;
}
public List<Cape> getCapes() {
return capes;
}
}
@@ -0,0 +1,83 @@
package me.retucio.sputnik.command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import net.minecraft.client.Minecraft;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.Commands;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.data.registries.VanillaRegistries;
import net.minecraft.network.chat.Component;
import java.util.List;
public abstract class Command {
// utilizar los "registries" ya existentes de Brigadier para el autocompletado
protected static final CommandBuildContext REGISTRY_ACCESS = Commands.createValidationContext(VanillaRegistries.createWorldLookup());
protected static final int SUCCESS = com.mojang.brigadier.Command.SINGLE_SUCCESS;
protected static final Minecraft mc = Minecraft.getInstance();
private final String name, descriptionKey;
private final List<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.descriptionKey = descriptionKey;
this.aliases = List.of(aliases);
}
// métodos "helper" para reducir el "boilerplate"
protected static <T> RequiredArgumentBuilder<SharedSuggestionProvider, T> argument(final String name, final ArgumentType<T> type) {
return RequiredArgumentBuilder.argument(name, type);
}
protected static LiteralArgumentBuilder<SharedSuggestionProvider> literal(final String name) {
return LiteralArgumentBuilder.literal(name);
}
// para registrar comandos
public final void registerTo(CommandDispatcher<SharedSuggestionProvider> dispatcher) {
register(dispatcher, name);
for (String alias : aliases) register(dispatcher, alias);
}
public void register(CommandDispatcher<SharedSuggestionProvider> dispatcher, String name) {
LiteralArgumentBuilder<SharedSuggestionProvider> builder = LiteralArgumentBuilder.literal(name);
build(builder);
dispatcher.register(builder);
}
public abstract void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder);
public String getName() {
return name;
}
public String getDescriptionKey() {
return descriptionKey;
}
public Component getDescription() {
return Component.translatable(descriptionKey);
}
public List<String> getAliases() {
return aliases;
}
public String toString() {
return me.retucio.sputnik.command.CommandManager.INSTANCE.getPrefix() + name;
}
public String toString(String... args) {
StringBuilder sb = new StringBuilder(toString());
for (String arg : args) sb.append(" ").append(arg);
return sb.toString();
}
}
@@ -0,0 +1,104 @@
package me.retucio.sputnik.command;
import com.github.retucio.neutrino.EventListener;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.command.commands.*;
import net.minecraft.client.Minecraft;
import net.minecraft.commands.SharedSuggestionProvider;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Comparator;
import java.util.Objects;
// registro de comandos
public class CommandManager {
public static CommandManager INSTANCE;
private String prefix = "$";
public static final List<Command> commands = new ArrayList<>();
public static final CommandDispatcher<SharedSuggestionProvider> dispatcher = new CommandDispatcher<>();
private static final Minecraft mc = Minecraft.getInstance();
public CommandManager() {
registerCommands();
}
public static void registerCommands() {
addCommand(new BindCommand());
addCommand(new ClickSlotCommand());
addCommand(new ConfigCommand());
addCommand(new CopyScreenshotCommand());
addCommand(new EnderChestCommand());
addCommand(new FriendCommand());
addCommand(new GarbageCleanerCommand());
addCommand(new HelpCommand());
addCommand(new MacroCommand());
addCommand(new PanoramaCommand());
addCommand(new PeekCommand());
addCommand(new PluginsCommand());
addCommand(new PrefixCommand());
addCommand(new ProfileCommand());
addCommand(new PurgeCommand());
addCommand(new RepeatCommand());
addCommand(new RepeatWaitCommand());
addCommand(new SaveScreenshotCommand());
addCommand(new SendCommand());
addCommand(new SpectateCommand());
addCommand(new ToggleCommand());
addCommand(new WaitCommand());
commands.sort(Comparator.comparing(Command::getName));
for (Command command : commands) {
for (Method method : command.getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(EventListener.class)) {
Sputnik.EVENT_BUS.subscribe(command);
break;
}
}
}
}
public static void addCommand(Command command) {
commands.removeIf(existing -> existing.getName().equals(command.getName()));
command.registerTo(dispatcher);
commands.add(command);
}
public static void dispatch(String message) throws CommandSyntaxException {
dispatcher.execute(message, Objects.requireNonNull(mc.getConnection()).getSuggestionsProvider());
}
public static Command getCommandByName(String name) {
for (Command command : commands) {
if (command.getName().equals(name)) return command;
for (String alias : command.getAliases())
if (alias.equals(name)) return command;
}
return null;
}
public <T extends Command> T getCommandByClass(Class<T> clazz) {
for (Command command : commands)
if (clazz.isInstance(command))
return clazz.cast(command);
return null;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
}
@@ -0,0 +1,208 @@
package me.retucio.sputnik.command.args;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import me.retucio.sputnik.util.EntityUtil;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern;
import static me.retucio.sputnik.Sputnik.mc;
// para autocompletar entidades
public class EntityArgumentType implements ArgumentType<Entity> {
public static final EntityArgumentType INSTANCE = new EntityArgumentType();
private static final DynamicCommandExceptionType NO_SUCH_ENTITY = new DynamicCommandExceptionType(
name -> Component.translatable("sputnik.args.entity.notfound", name));
private static final Collection<String> EXAMPLES = List.of(
"AdlerHitdolf",
"@p"
);
private static final Pattern UUID_PATTERN = Pattern.compile(
"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
);
private EntityArgumentType() {}
@Override
public Entity parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString();
if (mc.level == null)
throw NO_SUCH_ENTITY.create(argument);
if (argument.startsWith("@"))
return parseSelector(argument);
if (isUUID(argument)) {
UUID uuid = UUID.fromString(argument);
return findEntityByUUID(uuid);
}
return findPlayerByName(argument);
}
private boolean isUUID(String input) {
return UUID_PATTERN.matcher(input).matches();
}
private Entity parseSelector(String selector) throws CommandSyntaxException {
if (mc.player == null) throw NO_SUCH_ENTITY.create(selector);
return switch (selector.toLowerCase()) {
case "@p" ->
findNearestPlayer();
case "@r" ->
findRandomPlayer();
case "@s" ->
mc.player;
case "@a" -> throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument()
.createWithContext(new StringReader(selector));
default -> {
if (selector.startsWith("@e"))
yield parseEntitySelector(selector);
else if (selector.startsWith("@p") && selector.length() > 2)
yield parsePlayerSelector(selector);
throw NO_SUCH_ENTITY.create(selector);
}
};
}
private Entity findNearestPlayer() throws CommandSyntaxException {
Player nearest = null;
double nearestDistance = Double.MAX_VALUE;
for (Player player : mc.level.players()) {
if (player == mc.player) continue;
double distance = mc.player.distanceToSqr(player);
if (distance < nearestDistance) {
nearestDistance = distance;
nearest = player;
}
}
if (nearest == null)
throw NO_SUCH_ENTITY.create("@p");
return nearest;
}
private Entity findRandomPlayer() throws CommandSyntaxException {
List<Player> players = new ArrayList<>(mc.level.players());
if (players.isEmpty()) throw NO_SUCH_ENTITY.create("@r");
players.remove(mc.player);
if (players.isEmpty()) return mc.player;
Random random = new Random();
return players.get(random.nextInt(players.size()));
}
private Entity parseEntitySelector(String selector) throws CommandSyntaxException {
if (selector.equals("@e")) {
List<Entity> entities = new ArrayList<>();
for (Entity entity : mc.level.getEntities().getAll())
if (!(entity instanceof Player))
entities.add(entity);
if (entities.isEmpty()) throw NO_SUCH_ENTITY.create(selector);
Random random = new Random();
return entities.get(random.nextInt(entities.size()));
}
if (selector.startsWith("@e[type=")) {
String type = selector.substring(8, selector.length() - 1);
return findEntityByType(type);
}
throw NO_SUCH_ENTITY.create(selector);
}
private Entity parsePlayerSelector(String selector) throws CommandSyntaxException {
return findNearestPlayer();
}
private Entity findEntityByType(String type) throws CommandSyntaxException {
List<Entity> matchingEntities = new ArrayList<>();
for (Entity entity : mc.level.getEntities().getAll()) {
String entityType = entity.getType().getDescriptionId();
if (entityType.contains(type.toLowerCase().replace("minecraft:", "")))
matchingEntities.add(entity);
}
if (matchingEntities.isEmpty()) throw NO_SUCH_ENTITY.create(type);
Entity nearest = null;
double nearestDistance = Double.MAX_VALUE;
for (Entity entity : matchingEntities) {
double distance = mc.player.distanceToSqr(entity);
if (distance < nearestDistance) {
nearestDistance = distance;
nearest = entity;
}
}
return nearest;
}
private Entity findEntityByUUID(UUID uuid) throws CommandSyntaxException {
for (Entity entity : mc.level.getEntities().getAll())
if (entity.getUUID().equals(uuid))
return entity;
throw NO_SUCH_ENTITY.create(uuid.toString());
}
private Player findPlayerByName(String name) throws CommandSyntaxException {
for (Player player : mc.level.players())
if (player.getName().getString().equalsIgnoreCase(name))
return player;
throw NO_SUCH_ENTITY.create(name);
}
@Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
if (mc.level != null)
for (Player player : mc.level.players())
builder.suggest(player.getName().getString());
builder.suggest("@p");
builder.suggest("@r");
builder.suggest("@s");
Entity lookingAt = EntityUtil.getEntityPlayerIsLookingAt();
if (lookingAt != null)
builder.suggest(lookingAt.getUUID().toString());
return builder.buildFuture();
}
@Override
public Collection<String> getExamples() {
return EXAMPLES;
}
public static Entity get(CommandContext<?> context, String name) {
return context.getArgument(name, Entity.class);
}
}
@@ -0,0 +1,49 @@
package me.retucio.sputnik.command.args;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
public class EnumArgumentType<T extends Enum<T>> implements ArgumentType<T> {
private static final DynamicCommandExceptionType NO_SUCH_TYPE = new DynamicCommandExceptionType(
(value) -> Component.translatable("sputnik.args.enum.invalid", value));
private final T[] values;
public EnumArgumentType(T defaultValue) {
this.values = defaultValue.getDeclaringClass().getEnumConstants();
}
public static <T extends Enum<T>> EnumArgumentType<T> enumArgument(T defaultValue) {
return new EnumArgumentType<>(defaultValue);
}
public static <T extends Enum<T>> T getEnum(CommandContext<?> context, String name, T defaultValue) {
return context.getArgument(name, defaultValue.getDeclaringClass());
}
public T parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString();
// ignorando mayúsculas: listSuggestions sugiere en minúsculas, así que comparar
// con equals() hacía que las propias sugerencias no se pudieran parsear
return Arrays.stream(this.values)
.filter((value) -> value.toString().equalsIgnoreCase(argument))
.findFirst()
.orElseThrow(() -> NO_SUCH_TYPE.create(argument)
);
}
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return SharedSuggestionProvider.suggest(Arrays.stream(this.values).map(e -> e.toString().toLowerCase()), builder);
}
}
@@ -0,0 +1,52 @@
package me.retucio.sputnik.command.args;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.util.MiscUtil;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
// para autocompletar nombres de módulos (los espacios se cambian por guiones bajos)
public class ModuleArgumentType implements ArgumentType<Module> {
public static final ModuleArgumentType INSTANCE = new ModuleArgumentType();
private static final DynamicCommandExceptionType UNKNOWN_MODULE = new DynamicCommandExceptionType(
name -> Component.translatable("sputnik.args.module.notfound", name));
private static final Collection<String> examples = ModuleManager.INSTANCE.getModules()
.stream().limit(3).map(module -> module.getDisplayName().replace(" ", "_")).toList();
@Override
public Module parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString().replace("_", " ");
Module module = ModuleManager.INSTANCE.getModuleByDisplayName(argument);
if (module == null) throw UNKNOWN_MODULE.create(argument);
return module;
}
@Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return SharedSuggestionProvider.suggest(ModuleManager.INSTANCE.getModules()
.stream().map(module -> MiscUtil.removeAccentMarks(module.getDisplayName().replace(" ", "_"))), builder);
}
@Override
public Collection<String> getExamples() {
return examples;
}
public static Module get(CommandContext<SharedSuggestionProvider> ctx) {
return ctx.getArgument("módulo", Module.class);
}
}
@@ -0,0 +1,62 @@
package me.retucio.sputnik.command.args;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Player;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static me.retucio.sputnik.Sputnik.mc;
// para autocompletar nombres de jugadores
public class PlayerArgumentType implements ArgumentType<Player> {
public static final PlayerArgumentType INSTANCE = new PlayerArgumentType();
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 PlayerArgumentType() {}
@Override
public Player parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString();
Player playerEntity = null;
for (Player p : mc.level.players())
if (p.getName().getString().equalsIgnoreCase(argument)) {
playerEntity = p;
break;
}
if (playerEntity == null) throw NO_SUCH_PLAYER.create(argument);
return playerEntity;
}
@Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return SharedSuggestionProvider.suggest(mc.level.players().stream().map(
abstractClientPlayerEntity -> abstractClientPlayerEntity.getName().getString()), builder);
}
@Override
public Collection<String> getExamples() {
return EXAMPLES;
}
public static Player get(CommandContext<?> context) {
return context.getArgument("jugador", Player.class);
}
}
@@ -0,0 +1,84 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.command.args.ModuleArgumentType;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.KeySetting;
import me.retucio.sputnik.ui.widgets.panels.settings.ClientSettingsPanel;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.KeyUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.input.KeyEvent;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import java.util.ArrayList;
import java.util.List;
// asignar una tecla a un módulo, lógica similar a BindButton
public class BindCommand extends Command {
private static Module listeningModule = null;
public BindCommand() {
super("bind", "sputnik.commands.bind.desc", "keybind", "kb", "tecla");
}
@Override
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder
.then(argument("módulo", ModuleArgumentType.INSTANCE)
.executes(ctx -> {
Module module = ctx.getArgument("módulo", Module.class);
listeningModule = module;
ChatUtil.info(Component.translatable("sputnik.commands.bind.presskey", ChatFormatting.GREEN + module.getDisplayName()));
return SUCCESS;
})
.then(literal("reset").executes(ctx -> {
Module module = ctx.getArgument("módulo", Module.class);
module.getBind().reset();
ChatUtil.info(Component.translatable("sputnik.commands.bind.keyreset",
ChatFormatting.GREEN + module.getDisplayName(),
ChatFormatting.AQUA + KeyUtil.getKeyName(module.getKey())
));
return SUCCESS;
}))
);
}
public static boolean onKeyPress(int key) {
if (listeningModule == null) return false;
KeySetting bind = listeningModule.getBind();
// solo hay que avisar de que la tecla está cogida cuando NO se permiten teclas repetidas.
// antes esto entraba con multipleKeybinds activado y luego comprobaba !allowMultiple con el
// mismo valor, así que nunca se llegaba a avisar
if (!ClientSettingsPanel.clientSettings.multipleKeybinds.getValue()) {
List<KeyMapping> keys = new ArrayList<>(List.of(mc.options.keyMappings));
keys.removeAll(List.of(mc.options.debugKeys));
for (KeyMapping kb : keys) {
if (kb.matches(new KeyEvent(key, 0, 0))) {
// la comilla iba dentro del I18n.get, así que buscaba una clave inexistente
ChatUtil.warn(Component.translatable("sputnik.commands.bind.keytaken",
ChatFormatting.GREEN + "\"" + I18n.get(kb.getName()) + "\""));
listeningModule = null;
return true;
}
}
}
if (bind != null) bind.setValue(key);
ChatUtil.info(Component.translatable("sputnik.commands.bind.keybound",
ChatFormatting.AQUA + KeyUtil.getKeyName(key) + ChatFormatting.RESET,
ChatFormatting.GREEN + listeningModule.getDisplayName()
));
listeningModule = null;
return true;
}
}
@@ -0,0 +1,36 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.command.args.EnumArgumentType;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.ContainerScreen;
import net.minecraft.commands.SharedSuggestionProvider;
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&
public class ClickSlotCommand extends Command {
public ClickSlotCommand() {
super("clickslot", "sputnik.commands.clickslot.desc", "cs");
}
public void build(LiteralArgumentBuilder<SharedSuggestionProvider> builder) {
builder.then(argument("slot", IntegerArgumentType.integer())
.then(argument("botón", IntegerArgumentType.integer())
.then(argument("acción", EnumArgumentType.enumArgument(ContainerInput.SWAP))
.executes(context -> {
int slot = context.getArgument("slot", Integer.class), button = context.getArgument("botón", Integer.class);
ContainerInput action = context.getArgument("acción", ContainerInput.class);
Screen screen = mc.gui.screen();
int syncId = (screen instanceof ContainerScreen cScreen) ? cScreen.getMenu().containerId : 0;
if (mc.gameMode == null) return 0;
mc.gameMode.handleContainerInput(syncId, slot, button, action, mc.player);
return SUCCESS;
})
)
)
);
}
}
Loaded 100 of 603 files, more files were not shown because too many files have changed in this diff. Show more