Compare commits

...
19 Commits
Author SHA1 Message Date
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
405 changed files with 13591 additions and 2768 deletions

No files matched your search

+4
View File
@@ -39,4 +39,8 @@ replay_*.log
*.hprof *.hprof
*.jfr *.jfr
# otros
todo.txt todo.txt
cambios.md
remappedSrc/
+43 -11
View File
@@ -1,28 +1,34 @@
# jenny's mod 2 100% real # jenny's mod 2 100% real
mod para Minecraft Fabric 1.21.6 que añade algunas cosas QOL, mayormente relacionadas a la cámara. mod para Minecraft Fabric 1.21.10 que añade algunas cosas QOL, mayormente relacionadas a la cámara.
## instalación ## instalación
### para gente normal: ### para gente normal:
1. ir a [lanzamientos](https://github.com/retuci0/camera-tweaks/releases) 1. ir a [lanzamientos](https://github.com/retuci0/sputnik/releases)
2. selecionar la versión más reciente 2. selecionar la versión más reciente
3. descargar el .jar 3. descargar el .jar
4. moverlo a tu directorio de mods (probablemente `%appdata%/.minecraft/mods`) 4. moverlo a tu directorio de mods (probablemente `%appdata%/.minecraft/mods`)
asegúrate de tener una version de Minecraft y Fabric compatibles con el mod asegúrate de tener una version de Minecraft y Fabric compatibles con el mod (MC `1.21.11`; Fabric `0.17.3`)
es posible que haya problemas al usarlo junto a otros mods, especialmente junto a otros clientes, pues pueden conflictar entre ellos.
### para frikis de 200 de iq ### para frikis de 200 de iq
1. clona el respositorio 1. clona el respositorio (`git clone https://github.com/retuci0/sputnik`)
2. `gradlew build` para Windows o `chmod +x gradlew` + `./gradlew build` para Linux 2. entra al directorio del repositorio (`cd sputnik`)
3. el resultado estará en `/build/libs` (el que no lleva "sources" en el nombre) 3. `gradlew build` para Windows o `chmod +x gradlew` + `./gradlew build` para Linux (Mac es Linux)
4. el resultado estará en `/build/libs/sputnik-X.X.jar` (el que no lleva "sources" en el nombre)
## uso ## uso
`shift derecho` por defecto para abrir la interfaz `shift derecho` por defecto para abrir la interfaz
`pág. arriba` por defecto para abrir el editor del hud
`$` como prefijo por defecto para comandos (se autocompletan) `$` 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 ## interfaz
- clic izquierdo para activar / desactivar módulos - clic izquierdo para activar / desactivar módulos
@@ -37,14 +43,18 @@ asegúrate de tener una version de Minecraft y Fabric compatibles con el mod
- shift mientras pasas el puntero por encima de un ajuste de modos para ver los modos disponibles - shift mientras pasas el puntero por encima de un ajuste de modos para ver los modos disponibles
- clic derecho para ciclar hacia atrás en ajustes de modo - clic derecho para ciclar hacia atrás en ajustes de modo
- usa la rueda del ratón para mover la interfaz verticalmente - usa la rueda del ratón para mover la interfaz verticalmente
- shift + rueda del ratón para mover todos los marcos a un rango visible en la pantalla
## capturas ## capturas
<img src="screenshots/2025-08-19_23.20.15.png" alt="captura 1 (foto vieja)"> <img src="screenshots/ui.png" alt="interfaz">
<img src="screenshots/2025-08-22_00.18.51.png" alt="captura 2 (interfaz)"> <img src="screenshots/nametags.png" alt="nametags mostrando daño de un tridente">
<img src="screenshots/2025-08-22_00.19.11.png" alt="captura 3 (interfaz)"> <img src="screenshots/chatplus.png" alt="chatplus y capturas en uso">
<img src="screenshots/2025-08-22_00.22.41.png" alt="captura 5 (comandos)"> <img src="screenshots/crits.png" alt="críticos">
<img src="screenshots/2025-08-27_18.44.41.png" alt="captura 6 (nametags & apilar bossbars)"> <img src="screenshots/fullbright.png" alt="fullbright en modo gamma cambiando el color de los shaders">
<img src="screenshots/hud.png" alt="HUD">
<img src="screenshots/hudeditor.png" alt="editor de HUD">
<img src="screenshots/colorpicker.png" alt="selector de colores">
## contibuciones ## contibuciones
@@ -53,3 +63,25 @@ sí por favor
## licencia ## licencia
no sé, haz lo que quieras con esto, es puta basura xd no sé, haz lo que quieras con esto, es puta basura xd
aunque estaría guay que acreditases
pero bueno
## descargo de responsabilidad
me importa una puta mierda lo que pase al usar este mod en servers donde es baneable y así.
## mods recomendados
> mods que recomiendo usar junto a este:
- [fabric api (necesario)](https://modrinth.com/mod/fabric-api/): necesario para el mod
- [sodium](https://modrinth.com/mod/sodium): mejora el rendimiento general del juego
- [viafabricplus](https://modrinth.com/mod/viafabricplus): te permite conectarte a servidores que tengan una versión distinta a la de tu cliente
- [xaero's world map](https://modrinth.com/mod/xaeros-world-map/) y [xaero's minimap](https://modrinth.com/mod/xaeros-minimap/): mapa chulo
- [mod menu](https://modrinth.com/mod/modmenu): lista de los mods instalados, y sus ajustes
- [iris](https://modrinth.com/mod/iris): shaders
- [lambdynamiclights](https://modrinth.com/mod/lambdynamiclights): iluminación dinámica
- [appleskin](https://modrinth.com/mod/appleskin/): información relacionada con la comida
- [baritone (desactualizado)](https://github.com/cabaletta/baritone): google maps para juego de cubos
+5 -2
View File
@@ -24,11 +24,14 @@ dependencies {
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
} }
loom {
accessWidenerPath = file("src/main/resources/sputnik.accesswidener")
}
processResources { processResources {
inputs.property "version", project.version inputs.property "version", project.version
+19
View File
@@ -0,0 +1,19 @@
# v2.4
## novedades
ahora se usa el formato binario (.dat) en vez de .json para guardar ajustes, lo que los hace más rápidos, y el archivo más liviano
### adiciones
#### módulos nuevos:
- capas: cambia (solo visualmente, para ti) la capa que usas a una custom de las disponibles
### arreglos / mejoras
- renombrado bien todo que lo hice con prisa
## problemas reconocidos
- muchos, pero repórtalos en la página de problemas si quieres para que queden bien registrados
+12 -4
View File
@@ -1,4 +1,12 @@
meteor client dev team Meteor Client: https://github.com/MeteorDevelopment/meteor-client | https://meteorclient.com
oyvey client (1.21 port by mioclient) OyVey Port: https://github.com/mioclient/oyvey-ported/
lennyboi (youtube) Lenny Boi: https://www.youtube.com/@lennyboi7195
packbar by cattyn on github 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/
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 :)
+9 -14
View File
@@ -1,18 +1,13 @@
# Done to increase the memory available to gradle. org.gradle.jvmargs=-Xmx2G
org.gradle.jvmargs=-Xmx1G
org.gradle.parallel=true org.gradle.parallel=true
# Fabric Properties minecraft_version=1.21.11
# check these on https://fabricmc.net/develop yarn_mappings=1.21.11+build.3
minecraft_version=1.21.6 loader_version=0.18.4
yarn_mappings=1.21.6+build.1 loom_version=1.13-SNAPSHOT
loader_version=0.17.2
loom_version=1.11-SNAPSHOT
# Mod Properties fabric_api_version=0.140.2+1.21.11
mod_version=1.1
mod_version=2.4
maven_group=me.retucio maven_group=me.retucio
archives_base_name=camtweaks archives_base_name=sputnik
# Dependencies
fabric_version=0.128.2+1.21.6
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 973 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1005 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 942 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 560 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 484 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 915 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: 566 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 KiB

@@ -1,158 +0,0 @@
package me.retucio.camtweaks;
import me.retucio.camtweaks.command.CommandManager;
import me.retucio.camtweaks.command.commands.BindCommand;
import me.retucio.camtweaks.config.ConfigManager;
import me.retucio.camtweaks.event.EventBus;
import me.retucio.camtweaks.event.SubscribeEvent;
import me.retucio.camtweaks.event.events.ShutdownEvent;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.ModuleManager;
import me.retucio.camtweaks.ui.ClickGUI;
import me.retucio.camtweaks.ui.buttons.BindButton;
import me.retucio.camtweaks.ui.buttons.SettingButton;
import me.retucio.camtweaks.ui.buttons.TextButton;
import me.retucio.camtweaks.ui.frames.ClickGUISettingsFrame;
import me.retucio.camtweaks.ui.frames.SettingsFrame;
import me.retucio.camtweaks.util.KeyUtil;
import me.retucio.camtweaks.util.MiscUtil;
import net.fabricmc.api.ClientModInitializer;
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 ClientModInitializer {
// 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 MinecraftClient mc;
private Screen prevScreen;
private boolean settingsApplied = false;
@Override
public void onInitializeClient() {
mc = MinecraftClient.getInstance();
ModuleManager.INSTANCE = new ModuleManager(EVENT_BUS);
CommandManager.INSTANCE = new CommandManager();
ClickGUI.INSTANCE = new ClickGUI();
ConfigManager.load();
EVENT_BUS.register(this);
EVENT_BUS.register(MiscUtil.class);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
ConfigManager.save();
EVENT_BUS.post(new ShutdownEvent());
}));
}
// se ejecuta cada tick, es decir, 20 veces por segundo
public void onTick() {
ModuleManager.INSTANCE.getEnabledModules().forEach(Module::onTick);
if (!settingsApplied
&& ConfigManager.getConfig() != null
&& mc.player != null && mc.world != null) {
ConfigManager.applyConfig();
settingsApplied = true;
}
}
// se ejecuta cada vez que se presiona una tecla
public void onKeyPress(int key, int action) {
ModuleManager.INSTANCE.getEnabledModules().forEach(module -> module.onKey(key, action));
boolean anyFocused = isAnySettingButtonFocused();
ClickGUI.INSTANCE.setAnyFocused(anyFocused);
if (action == GLFW.GLFW_PRESS) {
if (BindCommand.onKeyPress(key)) return;
handleSettingButtonsKey(key, action);
handleModuleToggle(key, anyFocused);
handleClickGUIKey(key, anyFocused);
} 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.INSTANCE.getModules()) {
if (key != module.getKey() || anyFocused || KeyUtil.isKeyDown(GLFW.GLFW_KEY_F3)) continue; // evitar interrumpir combinaciones de teclas del F3
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.INSTANCE.getEnabledModules())
if (module.shouldToggleOnBindRelease() && key == module.getKey())
module.setEnabled(false);
}
@SubscribeEvent
public void onStop(ShutdownEvent event) {
ConfigManager.save();
}
}
@@ -1,37 +0,0 @@
package me.retucio.camtweaks.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.camtweaks.command.Command;
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
import net.minecraft.command.CommandSource;
import net.minecraft.inventory.EnderChestInventory;
import net.minecraft.inventory.Inventories;
import net.minecraft.inventory.Inventory;
import net.minecraft.inventory.SimpleInventory;
import net.minecraft.screen.GenericContainerScreenHandler;
import net.minecraft.text.Text;
// accede a los contenidos de tu enderchest
public class EnderChestCommand extends Command {
public EnderChestCommand() {
super("echest", "muestra el contenido de tu enderchest", "ec", "enderchest");
}
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder.executes(ctx -> {
mc.execute(() -> { // para evitar llamar setScreen desde el hilo equivocado, que causa crashes
EnderChestInventory enderChestInv = mc.player.getEnderChestInventory();
// llámame imbécil pero no tengo ni idea de por qué no se abre
mc.setScreen(new GenericContainerScreen(
GenericContainerScreenHandler.createGeneric9x3(mc.player.currentScreenHandler.syncId + 1, mc.player.getInventory(), enderChestInv),
mc.player.getInventory(),
Text.translatable("container.enderchest")
));
});
return SUCCESS;
});
}
}
@@ -1,142 +0,0 @@
package me.retucio.camtweaks.config;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.ModuleManager;
import me.retucio.camtweaks.module.settings.*;
import me.retucio.camtweaks.ui.ClickGUI;
import me.retucio.camtweaks.ui.frames.ClickGUISettingsFrame;
import me.retucio.camtweaks.ui.frames.SettingsFrame;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
// se ocupa de guardar, cargar y aplicar ajustes
// pues no me ha dado dolores de cabeza el coso este con los NPE de los cojones...
public class ConfigManager {
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final File CONFIG_FILE = new File("camera_tweaks.json");
private static ClientConfig config = null;
// guardar configuraciones
public static void save() {
ensureConfig();
try (FileWriter writer = new FileWriter(CONFIG_FILE)) {
GSON.toJson(config, writer);
CameraTweaks.LOGGER.info("ajustes guardados");
} catch (IOException e) {
e.printStackTrace();
}
}
// cargar configuraciones
public static void load() {
CameraTweaks.LOGGER.info("ajustes cargados");
if (!CONFIG_FILE.exists()) {
ensureConfig();
save();
return;
}
try (FileReader reader = new FileReader(CONFIG_FILE)) {
config = GSON.fromJson(reader, ClientConfig.class);
} catch (IOException e) {
e.printStackTrace();
ensureConfig();
}
}
// aplicar configuraciones cargadas
public static void applyConfig() {
if (CameraTweaks.mc == null || ModuleManager.INSTANCE == null || config == null)
return;
ModuleManager.INSTANCE.getModules().forEach(module -> {
if (config.moduleStates.containsKey(module.getName()))
module.setEnabled(config.moduleStates.get(module.getName())); // estados
module.getSettings().forEach(setting -> applySetting(module, setting));
if (config.settingsFrames.containsKey(module.getName())) {
int[] position = config.settingsFrames.get(module.getName());
ClickGUI.INSTANCE.openSettingsFrame(module, position[0], position[1]);
}
});
ClickGUISettingsFrame.guiSettings.setEnabled(true);
ClickGUISettingsFrame.guiSettings.getSettings().forEach(setting -> applySetting(ClickGUISettingsFrame.guiSettings, setting));
CameraTweaks.LOGGER.info("módulos encendidos cargados");
CameraTweaks.LOGGER.info("ajustes de módulos cargados");
ClientConfig.FrameData moduleFrameData = config.extendableFrames.get("M");
if (moduleFrameData != null) {
ClickGUI.INSTANCE.getModulesFrame().x = moduleFrameData.x();
ClickGUI.INSTANCE.getModulesFrame().y = moduleFrameData.y();
ClickGUI.INSTANCE.getModulesFrame().extended = moduleFrameData.extended();
}
ClientConfig.FrameData guiSettingsData = config.extendableFrames.get("S");
if (guiSettingsData != null) {
ClickGUI.INSTANCE.getGuiSettingsFrame().x = guiSettingsData.x();
ClickGUI.INSTANCE.getGuiSettingsFrame().y = guiSettingsData.y();
ClickGUI.INSTANCE.getGuiSettingsFrame().extended = guiSettingsData.extended();
}
CameraTweaks.LOGGER.info("posiciones de frames cargadas");
}
public static ClientConfig getConfig() {
return config;
}
public static void setModuleState(Module module) {
ensureConfig();
config.moduleStates.put(module.getName(), module.isEnabled());
save();
}
public static void setSetting(Setting setting, Object value) {
ensureConfig();
config.settings.put(setting.getModule().getName() + ":" + setting.getName(), value);
save();
}
public static void setFramePosition(SettingsFrame frame) {
ensureConfig();
config.settingsFrames.put(frame.module.getName(), new int[]{frame.x, frame.y});
ConfigManager.save();
}
public static void setExtendableFrame(String key, ClientConfig.FrameData data) {
ensureConfig();
config.extendableFrames.put(key, data);
save();
}
public static void applySetting(Module parent, Setting setting) {
String id = parent.getName() + ":" + setting.getName();
if (config.settings.containsKey(id)) {
Object value = config.settings.get(id);
switch (setting) {
case BooleanSetting b -> b.setEnabled((boolean) value);
case EnumSetting e -> e.setIndex(value instanceof Double val ? val.intValue() : (int) value);
case NumberSetting n -> n.setValue((double) value);
case StringSetting s -> s.setValue((String) value);
case KeySetting k -> k.setKey(value instanceof Double val ? val.intValue() : (int) value);
default -> {}
}
}
}
private static void ensureConfig() {
if (config == null) config = new ClientConfig();
}
}
@@ -1,9 +0,0 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
/**
* @see me.retucio.camtweaks.mixin.ChunkOcclusionDataBuilderMixin#onChunkOcclusion
*/
public class ChunkOcclusionEvent extends Event {}
@@ -1,9 +0,0 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
/**
* @see me.retucio.camtweaks.mixin.ClientConnectionMixin#onDisconnect
*/
public class DisconnectEvent extends Event {}
@@ -1,12 +0,0 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
/**
* @see me.retucio.camtweaks.mixin.MinecraftClientMixin#onStop
*/
public class ShutdownEvent extends Event {
public ShutdownEvent() {}
}
@@ -1,14 +0,0 @@
package me.retucio.camtweaks.event.events;
import me.retucio.camtweaks.event.Event;
/**
* @see me.retucio.camtweaks.mixin.MinecraftClientMixin#onTickPre
* @see me.retucio.camtweaks.mixin.MinecraftClientMixin#onTickPost
*/
public class TickEvent {
public static class Pre extends Event {}
public static class Post extends Event {}
}
@@ -1,18 +0,0 @@
package me.retucio.camtweaks.event.events.camtweaks;
import me.retucio.camtweaks.event.Event;
import me.retucio.camtweaks.module.settings.Setting;
// se genera cada que se cambia el valor de un ajuste
public class UpdateSettingEvent extends Event {
private final Setting setting;
public UpdateSettingEvent(Setting setting) {
this.setting = setting;
}
public Setting getSetting() {
return setting;
}
}
@@ -1,48 +0,0 @@
package me.retucio.camtweaks.mixin;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.module.ModuleManager;
import me.retucio.camtweaks.module.modules.Freecam;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.item.property.numeric.CompassState;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import org.spongepowered.asm.mixin.Final;
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.mc;
@Mixin(CompassState.class)
public abstract class CompassStateMixin {
@Unique
private static Freecam freecam;
@Inject(method = "<init>", at = @At("TAIL"))
private void getModules(boolean wobble, CompassState.Target target, CallbackInfo ci) {
freecam = ModuleManager.INSTANCE.getModuleByClass(Freecam.class);
}
@ModifyExpressionValue(method = "getBodyYaw", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;getBodyYaw()F"))
private static float getBodyYaw(float original) {
if (freecam.isEnabled()) return mc.gameRenderer.getCamera().getYaw();
return original;
}
@ModifyReturnValue(method = "getAngleTo(Lnet/minecraft/entity/Entity;Lnet/minecraft/util/math/BlockPos;)D", at = @At("RETURN"))
private static double getAngleTo(double original, Entity entity, BlockPos blockPos) {
if (freecam.isEnabled()) {
Vec3d vec3d = Vec3d.ofCenter(blockPos);
Camera camera = mc.gameRenderer.getCamera();
return Math.atan2(vec3d.getZ() - camera.getPos().z, vec3d.getX() - camera.getPos().x) / (float) (Math.PI * 2);
}
return original;
}
}
@@ -1,22 +0,0 @@
package me.retucio.camtweaks.mixin;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import me.retucio.camtweaks.module.ModuleManager;
import me.retucio.camtweaks.module.modules.TimeChanger;
import net.minecraft.world.dimension.DimensionType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import static me.retucio.camtweaks.CameraTweaks.mc;
@Mixin(DimensionType.class)
public abstract class DimensionTypeMixin {
@ModifyReturnValue(method = "getMoonPhase", at = @At("RETURN"))
private int overrideMoonPhase(int original) {
TimeChanger timeChanger = ModuleManager.INSTANCE.getModuleByClass(TimeChanger.class);
if (timeChanger.isEnabled() && mc.world != null && !timeChanger.moonPhase.is(TimeChanger.MoonPhases.DEFAULT))
return timeChanger.getMoonPhase();
return original;
}
}
@@ -1,26 +0,0 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.mixin.accessor.EntityRenderStateAccessor;
import me.retucio.camtweaks.module.ModuleManager;
import me.retucio.camtweaks.module.modules.Nametags;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.EntityRenderer;
import net.minecraft.client.render.entity.state.EntityRenderState;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
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(EntityRenderer.class)
public abstract class EntityRendererMixin<S extends EntityRenderState> {
@Inject(method = "renderLabelIfPresent", at = @At("HEAD"))
private void renderSneakingPlayerNametags(S state, Text text, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) {
Nametags nametags = ModuleManager.INSTANCE.getModuleByClass(Nametags.class);
if (nametags.isEnabled() && nametags.alwaysVisible.isEnabled())
((EntityRenderStateAccessor) state).setSneaking(false);
}
}
@@ -1,34 +0,0 @@
package me.retucio.camtweaks.mixin;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import me.retucio.camtweaks.module.ModuleManager;
import me.retucio.camtweaks.module.modules.ChatPlus;
import me.retucio.camtweaks.module.modules.Freecam;
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;
@Mixin(InGameHud.class)
public abstract class InGameHudMixin {
@Inject(method = "render", at = @At("RETURN"))
private void renderHUD(DrawContext ctx, RenderTickCounter tc, CallbackInfo ci) {
HUD.render(ctx, tc);
}
@ModifyExpressionValue(method = "renderCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/option/Perspective;isFirstPerson()Z"))
private boolean alwaysRenderCrosshairInFreecam(boolean firstPerson) {
return ModuleManager.INSTANCE.getModuleByClass(Freecam.class).isEnabled() || firstPerson;
}
@Inject(method = "clear", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;clear(Z)V"), cancellable = true)
private void onClear(CallbackInfo ci) {
ChatPlus chatPlus = ModuleManager.INSTANCE.getModuleByClass(ChatPlus.class);
if (chatPlus.isEnabled() && chatPlus.keepHistory.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,18 +0,0 @@
package me.retucio.camtweaks.mixin;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import me.retucio.camtweaks.module.ModuleManager;
import me.retucio.camtweaks.module.modules.Freecam;
import net.minecraft.client.render.entity.LivingEntityRenderer;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(LivingEntityRenderer.class)
public abstract class LivingEntityRendererMixin {
@ModifyExpressionValue(method = "hasLabel(Lnet/minecraft/entity/LivingEntity;D)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;getCameraEntity()Lnet/minecraft/entity/Entity;"))
private Entity hasLabelGetCameraEntityProxy(Entity cameraEntity) {
return ModuleManager.INSTANCE.getModuleByClass(Freecam.class).isEnabled() ? null : cameraEntity;
}
}
@@ -1,40 +0,0 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.events.OpenScreenEvent;
import me.retucio.camtweaks.event.events.ShutdownEvent;
import me.retucio.camtweaks.event.events.TickEvent;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
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(MinecraftClient.class)
public abstract class MinecraftClientMixin {
@Inject(method = "tick", at = @At("HEAD"))
private void onTickPre(CallbackInfo ci) {
CameraTweaks.INSTANCE.onTick();
EVENT_BUS.post(new TickEvent.Pre());
}
@Inject(method = "tick", at = @At("TAIL"))
private void onTickPost(CallbackInfo ci) {
EVENT_BUS.post(new TickEvent.Post());
}
@Inject(method = "stop", at = @At("HEAD"))
private void onStop(CallbackInfo ci) {
EVENT_BUS.post(new ShutdownEvent());
}
@Inject(method = "setScreen", at = @At("HEAD"), cancellable = true)
private void onOpenScreen(Screen screen, CallbackInfo ci) {
OpenScreenEvent event = EVENT_BUS.post(new OpenScreenEvent(screen));
if (event.isCancelled()) ci.cancel();
}
}
@@ -1,40 +0,0 @@
package me.retucio.camtweaks.mixin;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import me.retucio.camtweaks.module.ModuleManager;
import me.retucio.camtweaks.module.modules.Nametags;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(PlayerEntity.class)
public abstract class PlayerEntityMixin extends LivingEntity {
protected PlayerEntityMixin(EntityType<? extends LivingEntity> entityType, World world) {
super(entityType, world);
}
@ModifyReturnValue(method = "getDisplayName", at = @At("RETURN"))
private Text addHealthIndicator(Text original) {
Nametags nametags = ModuleManager.INSTANCE.getModuleByClass(Nametags.class);
if (nametags.isEnabled() && !nametags.namePlayers.isEnabled()) return null;
if (!nametags.isEnabled() || !nametags.health.isEnabled()) return original;
float health = getHealth();
Formatting color;
if (health < 5) color = Formatting.RED;
else if (health < 10) color = Formatting.YELLOW;
else if (health < 15) color = Formatting.GREEN;
else color = Formatting.DARK_GREEN;
// me gustaría también añadir un modo en el que se renderizan los corazones directamente en la nametag, pero no sé cómo hacer para las imágenes :'(
return original.copy().append(Text.literal(" [" + color + (nametags.healthMode.is(Nametags.HealthMode.HEARTS)
? String.format("%.2f", health / 2) : (int) health) + Formatting.RESET + "]"));
} // redondear a dos decimales
}
@@ -1,40 +0,0 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.module.ModuleManager;
import me.retucio.camtweaks.module.modules.PerspectivePlus;
import me.retucio.camtweaks.module.modules.TimeChanger;
import net.minecraft.client.render.SkyRendering;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack;
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;
@Mixin(SkyRendering.class)
public abstract class SkyRendererMixin {
@Unique
TimeChanger timeChanger;
@Inject(method = "<init>", at = @At("TAIL"))
private void getModules(CallbackInfo ci) {
timeChanger = ModuleManager.INSTANCE.getModuleByClass(TimeChanger.class);
}
@Inject(method = "renderSun", at = @At("HEAD"), cancellable = true)
private void onRenderSun(float alpha, VertexConsumerProvider vertexConsumers, MatrixStack matrices, CallbackInfo ci) {
if (timeChanger.isEnabled() && !timeChanger.renderSun.isEnabled()) ci.cancel();
}
@Inject(method = "renderMoon", at = @At("HEAD"), cancellable = true)
private void onRenderMoon(int phase, float alpha, VertexConsumerProvider vertexConsumers, MatrixStack matrices, CallbackInfo ci) {
if (timeChanger.isEnabled() && !timeChanger.renderMoon.isEnabled()) ci.cancel();
}
@Inject(method = "renderStars", at = @At("HEAD"), cancellable = true)
private void onRenderStars(float brightness, MatrixStack matrices, CallbackInfo ci) {
if (timeChanger.isEnabled() && !timeChanger.renderStars.isEnabled()) ci.cancel();
}
}
@@ -1,17 +0,0 @@
package me.retucio.camtweaks.mixin;
import me.retucio.camtweaks.module.ModuleManager;
import me.retucio.camtweaks.module.modules.Freecam;
import net.minecraft.client.render.WorldRenderer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
@Mixin(WorldRenderer.class)
public abstract class WorldRendererMixin {
@ModifyArg(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;setupTerrain(Lnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/Frustum;ZZ)V"), index = 3)
private boolean renderSetupTerrainModifyArg(boolean hasForcedFrustum) {
return ModuleManager.INSTANCE.getModuleByClass(Freecam.class).isEnabled() || hasForcedFrustum;
}
}
@@ -1,12 +0,0 @@
package me.retucio.camtweaks.mixin.accessor;
import net.minecraft.client.render.entity.state.EntityRenderState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(EntityRenderState.class)
public interface EntityRenderStateAccessor {
@Accessor("sneaking")
void setSneaking(boolean sneaking);
}
@@ -1,75 +0,0 @@
package me.retucio.camtweaks.module;
import me.retucio.camtweaks.event.EventBus;
import me.retucio.camtweaks.event.SubscribeEvent;
import me.retucio.camtweaks.module.modules.*;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
// donde se registran los módulos, y los "listeners" de eventos en cada módulo que lo necesite
public class ModuleManager {
public static ModuleManager INSTANCE;
private final List<Module> modules = new ArrayList<>();
private final EventBus eventBus;
public ModuleManager(EventBus bus) {
this.eventBus = bus;
addModules();
}
// por order alfabético y todo, flipas
private void addModules() {
modules.add(new BossbarStack());
modules.add(new ChatPlus());
modules.add(new Freecam());
modules.add(new Freelook());
modules.add(new Fullbright());
modules.add(new HandView());
modules.add(new PerspectivePlus());
modules.add(new Nametags());
modules.add(new TimeChanger());
modules.add(new Zoom());
// registrar los "listeners" necesarios
for (Module module : getEnabledModules()) {
for (Method method : module.getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(SubscribeEvent.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,61 +0,0 @@
package me.retucio.camtweaks.module.modules;
import me.retucio.camtweaks.command.CommandManager;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.ModuleManager;
import me.retucio.camtweaks.module.settings.BooleanSetting;
import me.retucio.camtweaks.module.settings.NumberSetting;
import me.retucio.camtweaks.module.settings.StringSetting;
import me.retucio.camtweaks.util.ChatUtil;
import me.retucio.camtweaks.util.Colors;
import org.lwjgl.glfw.GLFW;
import java.awt.*;
import static me.retucio.camtweaks.util.Colors.*;
// módulo no visible solo para los ajustes de la interfaz (y del cliente en general)
public class GUI extends Module {
public NumberSetting red = addSetting(new NumberSetting("rojo", "cantidad de rojo en el RGB del marco", 0, 0, 255, 1));
public NumberSetting green = addSetting(new NumberSetting("verde", "cantidad de verde en el RGB del marco", 0, 0, 255, 1));
public NumberSetting blue = addSetting(new NumberSetting("azul", "cantidad de azul en el RGB del marco", 255, 0, 255, 1));
public NumberSetting alpha = addSetting(new NumberSetting("alpha", "opacidad del marco", 180, 0, 255, 1));
public NumberSetting scrollSens = addSetting(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 = addSetting(new StringSetting("prefijo", "prefijo de los comandos", "$", 10));
public StringSetting chatName = addSetting(new StringSetting("nombre", "qué nombre usar en notificaciones por el chat", "smegma", 20));
public BooleanSetting multipleKeybinds = addSetting(new BooleanSetting("teclas multimódulo", "permitir asignar la misma tecla a más de un módulo", false));
public GUI() {
super("interfaz", "ajustes de la interfaz, y otros misceláneos");
assignKey(GLFW.GLFW_KEY_RIGHT_SHIFT);
keyMode.setVisible(false);
notify.setVisible(false);
commandPrefix.onUpdate(prefix -> CommandManager.INSTANCE.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();});
if (mc != null) mc.execute(() -> commandPrefix.setDefaultValue(CommandManager.INSTANCE.getPrefix()));
chatName.onUpdate(name -> {
ChatUtil.updatePrefix(name);
ModuleManager.INSTANCE.getModuleByClass(ChatPlus.class).updateClientName();
});
}
private void updateFrameHeadColor() {
Colors.frameHeadColor = new Color(
frameHeadRed,
frameHeadGreen,
frameHeadBlue,
frameHeadAlpha
).getRGB();
}
}
@@ -1,133 +0,0 @@
package me.retucio.camtweaks.ui;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.SubscribeEvent;
import me.retucio.camtweaks.event.events.MouseScrollEvent;
import me.retucio.camtweaks.event.events.camtweaks.SettingsFrameEvent;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.ui.frames.ClickGUISettingsFrame;
import me.retucio.camtweaks.ui.frames.ModuleFrame;
import me.retucio.camtweaks.ui.frames.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.frames.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 ClickGUI INSTANCE;
private boolean anyFocused;
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);
public 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 ajustes de cada módulo que lo tenga abierto. se abre haciendo clic derecho sobre el módulo
for (SettingsFrame sf : settingsFrames.reversed()) {
sf.render(ctx, mouseX, mouseY, delta);
sf.updatePosition(mouseX, mouseY);
}
// renderizar el marco de los módulos
modulesFrame.render(ctx, mouseX, mouseY, delta);
modulesFrame.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);
}
@SubscribeEvent
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
SettingsFrame frame = new SettingsFrame(module, x, y, 120, 20);
settingsFrames.add(frame);
CameraTweaks.EVENT_BUS.post(new SettingsFrameEvent.Open(frame));
}
// cerrar el marco de ajustes
public void closeSettingsFrame(Module module) {
// porque java.util.ConcurrentModificationException o algo no sé es lo único que se me ha ocurrido hacer
List<SettingsFrame> toRemove = new ArrayList<>();
for (SettingsFrame sf : settingsFrames) {
if (sf.module == module) {
CameraTweaks.EVENT_BUS.post(new SettingsFrameEvent.Close(sf));
toRemove.add(sf);
}
}
settingsFrames.removeAll(toRemove);
}
// 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 void setAnyFocused(boolean anyFocused) {
this.anyFocused = anyFocused;
}
@Override
public boolean shouldCloseOnEsc() {
return super.shouldCloseOnEsc() && !anyFocused;
}
public List<SettingsFrame> getSettingsFrames() {
return settingsFrames;
}
}
@@ -1,14 +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
// igual lo uso para algo...
public class HUD {
private static final MinecraftClient mc = MinecraftClient.getInstance();
public static void render(DrawContext ctx, RenderTickCounter tc) {}
}
@@ -1,71 +0,0 @@
package me.retucio.camtweaks.ui.buttons;
import me.retucio.camtweaks.module.settings.KeySetting;
import me.retucio.camtweaks.ui.frames.SettingsFrame;
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 bgColor = isHovered((int) mouseX, (int) mouseY)
? Colors.hoveredSettingButtonColor
: Colors.settingButtonColor;
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,66 +0,0 @@
package me.retucio.camtweaks.ui.buttons;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.ui.ClickGUI;
import me.retucio.camtweaks.ui.frames.ModuleFrame;
import me.retucio.camtweaks.util.Colors;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
// 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 (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 + 120, 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,67 +0,0 @@
package me.retucio.camtweaks.ui.buttons;
import me.retucio.camtweaks.module.settings.Setting;
import me.retucio.camtweaks.ui.frames.SettingsFrame;
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;
protected int x, y, w, h;
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 > x && mouseX < x + w &&
mouseY > y && mouseY < y + h;
}
public Setting getSetting() {
return setting;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getW() {
return w;
}
public void setW(int w) {
this.w = w;
}
public int getH() {
return h;
}
public void setH(int h) {
this.h = h;
}
}
@@ -1,84 +0,0 @@
package me.retucio.camtweaks.ui.buttons;
import me.retucio.camtweaks.event.SubscribeEvent;
import me.retucio.camtweaks.event.events.KeyEvent;
import me.retucio.camtweaks.module.settings.NumberSetting;
import me.retucio.camtweaks.ui.frames.ClickGUISettingsFrame;
import me.retucio.camtweaks.ui.frames.SettingsFrame;
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.text.DecimalFormat;
import static me.retucio.camtweaks.CameraTweaks.EVENT_BUS;
// 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
EVENT_BUS.subscribe(this); // para poder escuchar teclas
}
@Override
public void render(DrawContext ctx, double mouseX, double mouseY, float delta) {
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 (arrastramiento?) 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;
}
@SubscribeEvent
public void onKey(KeyEvent event) {
// de no ser de esto, si se cierra la interfaz sin haber soltado el ratón mientras se arrastraba el valor, al reabrir la interfaz se seguía arrastrando, aún habiendo soltado ya el clic
if (event.getKey() == GLFW.GLFW_KEY_ESCAPE || event.getKey() == ClickGUISettingsFrame.guiSettings.getKey()) dragging = false;
}
public NumberSetting getSetting() {
return setting;
}
}
@@ -1,61 +0,0 @@
package me.retucio.camtweaks.ui.buttons;
import me.retucio.camtweaks.module.settings.BooleanSetting;
import me.retucio.camtweaks.ui.frames.SettingsFrame;
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) {
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
// quizás debería de utilizar .brighter() más, en vez de dar valores personalizados
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,77 +0,0 @@
package me.retucio.camtweaks.ui.frames;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.events.camtweaks.GUISettingsFrameEvent;
import me.retucio.camtweaks.module.modules.GUI;
import me.retucio.camtweaks.ui.buttons.SettingButton;
import me.retucio.camtweaks.util.Colors;
import net.minecraft.client.gui.DrawContext;
import java.util.List;
public class ClickGUISettingsFrame extends SettingsFrame {
public static final GUI guiSettings = new GUI();
public 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 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);
List<SettingButton> visibleButtons = settingButtons.stream()
.filter(sb -> sb.getSetting().isVisible())
.toList();
if (extended) {
int currentY = y + h + 3;
ctx.fill(x, currentY - 2, x + w, currentY + visibleButtons.size() * h, Colors.frameBGColor);
for (SettingButton sb : visibleButtons) {
sb.setX(x + 4);
sb.setY(currentY);
sb.setW(w - 8);
sb.setH(h);
sb.render(ctx, mouseX, mouseY, delta);
currentY += 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 +";
CameraTweaks.EVENT_BUS.post(new GUISettingsFrameEvent.Extend());
}
}
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 mouseRelease(double mouseX, double mouseY, int button) {
if (button == 0 && dragging) {
CameraTweaks.EVENT_BUS.post(new GUISettingsFrameEvent.Move());
}
super.mouseRelease(mouseX, mouseY, button);
}
}
@@ -1,122 +0,0 @@
package me.retucio.camtweaks.ui.frames;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.events.camtweaks.ModuleFrameEvent;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.ModuleManager;
import me.retucio.camtweaks.ui.buttons.ModuleButton;
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<>();
public 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 : ModuleManager.INSTANCE.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
if (mc.textRenderer == null) return;
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 +";
CameraTweaks.EVENT_BUS.post(new ModuleFrameEvent.Extend());
}
}
if (!extended) return; // solo dejar clicar en los módulos si el marco está extendido
for (ModuleButton moduleButton : moduleButtons) {
if (moduleButton.isHovered((int) mouseX, (int) mouseY)) {
moduleButton.mouseClicked(mouseX, mouseY, button);
break;
}
}
}
// detectar cuándo se suelta el clic
public void mouseReleased(double mouseX, double mouseY, int button) {
if (button == 0 && dragging) {
dragging = false;
}
CameraTweaks.EVENT_BUS.post(new ModuleFrameEvent.Move());
}
// 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,163 +0,0 @@
package me.retucio.camtweaks.ui.frames;
import me.retucio.camtweaks.CameraTweaks;
import me.retucio.camtweaks.event.events.camtweaks.SettingsFrameEvent;
import me.retucio.camtweaks.module.Module;
import me.retucio.camtweaks.module.settings.*;
import me.retucio.camtweaks.ui.ClickGUI;
import me.retucio.camtweaks.ui.buttons.*;
import me.retucio.camtweaks.ui.buttons.SliderButton;
import me.retucio.camtweaks.ui.buttons.TextButton;
import me.retucio.camtweaks.ui.buttons.ToggleButton;
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 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, dragY;
public final MinecraftClient mc = MinecraftClient.getInstance();
@SuppressWarnings({"rawtypes", "unchecked"})
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
}
@SuppressWarnings("rawtypes")
void updateWidth() {
// asegurarse de que todos los botones caben en el marco, haciendo que la anchura se ajuste al texto más largo
if (mc.textRenderer == null) return;
int maxWidth = mc.textRenderer.getWidth(title);
for (SettingButton button : settingButtons) {
String text = button.getSetting().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);
List<SettingButton> visibleButtons = settingButtons.stream()
.filter(sb -> sb.getSetting().isVisible())
.toList();
int currentY = y + h + 3;
ctx.fill(x, currentY - 2, x + w, currentY + visibleButtons.size() * h, Colors.frameBGColor);
for (SettingButton sb : visibleButtons) {
sb.setX(x + 4);
sb.setY(currentY);
sb.setW(w - 8);
sb.setH(h);
sb.render(ctx, mouseX, mouseY, delta);
currentY += h;
}
}
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.getSetting().isVisible())
sb.mouseClicked(mouseX, mouseY, button);
}
public void mouseRelease(double mouseX, double mouseY, int button) {
if (button == 0) {
dragging = false;
CameraTweaks.EVENT_BUS.post(new SettingsFrameEvent.Move(this));
}
for (SettingButton sb : settingButtons)
if (sb.getSetting().isVisible()) sb.mouseReleased(mouseX, mouseY, button);
}
public void updatePosition(double mouseX, double mouseY) {
if (dragging) {
x = (int) (mouseX - dragX);
y = (int) (mouseY - dragY);
}
}
public boolean isHovered(double mouseX, double mouseY) {
return mouseX > x && mouseX < x + w &&
mouseY > y && mouseY < y + h;
}
public List<SettingButton> getButtons() {
return settingButtons;
}
}
@@ -1,52 +0,0 @@
package me.retucio.camtweaks.ui.widgets;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gl.RenderPipelines;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
public class SearchBarWidget extends TextFieldWidget {
private static final Identifier MENU_LIST_BACKGROUND_TEXTURE = Identifier.ofVanilla("textures/gui/menu_list_background.png");
private static final Identifier INWORLD_MENU_LIST_BACKGROUND_TEXTURE = Identifier.ofVanilla("textures/gui/inworld_menu_list_background.png");
private boolean hookBackground;
public SearchBarWidget(TextRenderer textRenderer, int width, int height) {
super(textRenderer, width, height, Text.empty());
}
@Override
public void renderWidget(DrawContext context, int mouseX, int mouseY, float deltaTicks) {
if (drawsBackground()) drawBackground(context);
hookBackground = true;
super.renderWidget(context, mouseX, mouseY, deltaTicks);
hookBackground = false;
}
@Override
public boolean drawsBackground() {
if (hookBackground) return false;
return super.drawsBackground();
}
private void drawBackground(DrawContext context) {
Identifier identifier = MinecraftClient.getInstance().world == null ? MENU_LIST_BACKGROUND_TEXTURE : INWORLD_MENU_LIST_BACKGROUND_TEXTURE;
context.drawTexture(
RenderPipelines.GUI,
identifier,
this.getX(),
this.getY(),
this.getRight(),
this.getBottom(),
this.getWidth(),
this.getHeight(),
32,
32
);
}
}
@@ -1,76 +0,0 @@
package me.retucio.camtweaks.util;
import me.retucio.camtweaks.ui.frames.ClickGUISettingsFrame;
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();
private static String prefix = Formatting.GREEN + "[" + ClickGUISettingsFrame.guiSettings.chatName.getValue() + "] ";
public static void addMessage(String text) {
if (mc.inGameHud == null) return;
mc.inGameHud.getChatHud().addMessage(Text.of(text));
}
public static void addMessage(Text text) {
if (mc.inGameHud == null) return;
mc.inGameHud.getChatHud().addMessage(text);
}
public static void addMessageWithPrefix(String text) {
addMessageWithPrefix(Text.of(text));
}
public static void addMessageWithPrefix(Text text) {
addMessage(Text.literal(prefix + Formatting.RESET).append(text));
}
public static void info(String text) {
addMessage(Text.literal(prefix + Formatting.RESET + text));
}
public static void info(Text text) {
addMessage(Text.literal(prefix + Formatting.RESET).append(text));
}
public static void warn(String text) {
addMessage(Text.literal(prefix + Formatting.YELLOW + text));
}
public static void warn(Text text) {
addMessage(Text.literal(prefix + Formatting.YELLOW).append(text));
}
public static void error(String text) {
addMessage(Text.literal(prefix + Formatting.RED + text));
}
public static void error(Text text) {
addMessage(Text.literal(prefix + 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);
}
public static String getPrefix() {
return prefix;
}
public static String getPrefixNoFormatting() {
return "[" + ClickGUISettingsFrame.guiSettings.chatName.getValue() + "] ";
}
public static void updatePrefix(String newPrefix) {
prefix = Formatting.GREEN + "[" + newPrefix + "] ";
}
}
@@ -1,39 +0,0 @@
package me.retucio.camtweaks.util;
import java.awt.*;
import static me.retucio.camtweaks.ui.frames.ClickGUISettingsFrame.guiSettings;
// clase 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 hoveredSettingButtonColor = new Color(30, 30, 30, 100).getRGB();
public static int settingButtonColor = new Color(40, 40, 40, 75).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,39 +0,0 @@
package me.retucio.camtweaks.util;
import me.retucio.camtweaks.event.SubscribeEvent;
import me.retucio.camtweaks.event.events.TickEvent;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import org.joml.Vector3d;
import static me.retucio.camtweaks.CameraTweaks.mc;
public class MiscUtil {
public static Screen screen;
private MiscUtil() {}
@SubscribeEvent
public static void onTick(TickEvent.Post event) {
if (screen != null && mc.currentScreen == null) {
mc.setScreen(screen);
screen = null;
}
}
public static void copyVector(Vector3d destination, Vec3d source) {
destination.x = source.x;
destination.y = source.y;
destination.z = source.z;
}
public static Vector3d getEntityVector(Vector3d vector, Entity entity, double tickDelta) {
vector.x = MathHelper.lerp(tickDelta, entity.lastRenderX, entity.getX());
vector.y = MathHelper.lerp(tickDelta, entity.lastRenderY, entity.getY());
vector.z = MathHelper.lerp(tickDelta, entity.lastRenderZ, entity.getZ());
return vector;
}
}
@@ -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,14 +0,0 @@
package me.retucio.camtweaks.util.interfaces;
import net.minecraft.util.math.Vec3i;
import org.joml.Vector3d;
public interface IVec3d {
void smegma$set(double x, double y, double z);
default void smegma$set(Vec3i vec) { smegma$set(vec.getX(), vec.getY(), vec.getZ()); }
default void semgma$set(Vector3d vec) { smegma$set(vec.x, vec.y, vec.z); }
void smegma$setXZ(double x, double z);
void smegma$setY(double y);
}
@@ -0,0 +1,232 @@
package me.retucio.sputnik;
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.event.EventBus;
import me.retucio.sputnik.event.SubscribeEvent;
import me.retucio.sputnik.event.events.ShutdownEvent;
import me.retucio.sputnik.event.events.sputnik.LoadCapeManagerEvent;
import me.retucio.sputnik.event.events.sputnik.LoadClickGUIEvent;
import me.retucio.sputnik.event.events.sputnik.LoadCommandManagerEvent;
import me.retucio.sputnik.event.events.sputnik.LoadModuleManagerEvent;
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.screen.ClickGUI;
import me.retucio.sputnik.ui.hud.HudEditorScreen;
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.TextButton;
import me.retucio.sputnik.ui.widgets.frames.settings.ClientSettingsFrame;
import me.retucio.sputnik.ui.widgets.frames.SettingsFrame;
import me.retucio.sputnik.ui.widgets.Button;
import me.retucio.sputnik.util.*;
import me.retucio.sputnik.util.render.DrawUtil;
import me.retucio.sputnik.util.render.RenderUtil;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.SharedConstants;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ChatScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.AbstractCommandBlockScreen;
import net.minecraft.client.gui.screen.ingame.AbstractSignEditScreen;
import net.minecraft.client.gui.screen.ingame.AnvilScreen;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.glfw.GLFW;
public class Sputnik implements ClientModInitializer {
// cosas necesarias
public static final Sputnik INSTANCE = new Sputnik();
public static final EventBus EVENT_BUS = new EventBus();
public static final Logger LOGGER = LogManager.getLogger(Sputnik.class);
public static MinecraftClient 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;
@Override
public void onInitializeClient() {
mc = MinecraftClient.getInstance();
ConfigManager.load();
EVENT_BUS.register(this);
EVENT_BUS.register(ChatUtil.class);
EVENT_BUS.register(DrawUtil.class);
EVENT_BUS.register(EntityUtil.class);
EVENT_BUS.register(InventoryUtil.class);
EVENT_BUS.register(MiscUtil.class);
EVENT_BUS.register(NetworkUtil.class);
EVENT_BUS.register(RenderUtil.class);
Lists.init();
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());
ClickGUI.INSTANCE = new ClickGUI();
HudEditorScreen.INSTANCE = new HudEditorScreen();
EVENT_BUS.post(new LoadClickGUIEvent());
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.getConfig() != null
&& !ConfigManager.hasLoaded()) {
ConfigManager.applyConfig();
settingsApplied = true;
}
ModuleManager.INSTANCE.getEnabledModules().forEach(Module::onTick);
}
// se ejecuta cada vez que se presiona una tecla
public void onKey(int key, int action) {
if (key == GLFW.GLFW_KEY_F3) return; // prevenir activación de módulos al usar combinaciones de F3
ModuleManager.INSTANCE.getEnabledModules().forEach(module -> module.onKey(key, action));
boolean anyFocused = isAnySettingButtonFocused() || ClickGUI.INSTANCE.getSearchBar().isFocused();
ClickGUI.INSTANCE.setAnyFocused(anyFocused);
if (action != GLFW.GLFW_RELEASE) {
if (BindCommand.onKeyPress(key)) return;
if (action == GLFW.GLFW_PRESS) {
handleModuleToggle(key, anyFocused);
handleClickGUIKey(key, anyFocused);
handleHudEditorKey(key, anyFocused);
}
handleSettingButtonsKey(key, action);
} else if (!anyFocused) {
handleModuleRelease(key);
}
}
// verifica si algún botón de ajustes está escuchando
private boolean isAnySettingButtonFocused() {
for (SettingsFrame sf : ClickGUI.INSTANCE.getSettingsFrames())
for (Button 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.INSTANCE.getModules()) {
if (key != module.getKey() || anyFocused || KeyUtil.isKeyDown(GLFW.GLFW_KEY_F3)) continue; // evitar interrumpir combinaciones de teclas del F3
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 bb) bb.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 != ClientSettingsFrame.guiSettings.getKey() || anyFocused || isOnTypingScreen())
return;
// al parecer esto hace que con la tecla de la interfaz puedas ir cambiando de splash text en la pantalla del título
// pero me ha hecho gracia así que así se queda
if (mc.currentScreen != ClickGUI.INSTANCE) {
prevScreen = mc.currentScreen;
mc.setScreen(ClickGUI.INSTANCE);
} else {
ClickGUI.INSTANCE.close();
mc.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.getKey() || anyFocused || isOnTypingScreen()) return;
if (mc.currentScreen != HudEditorScreen.INSTANCE) {
if (!hud.isEnabled()) {
ChatUtil.warn("HUD está desactivado, lumbreras");
return;
}
prevScreen = mc.currentScreen;
mc.setScreen(HudEditorScreen.INSTANCE);
} else {
HudEditorScreen.INSTANCE.close();
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.INSTANCE.getEnabledModules())
if (module.shouldToggleOnBindRelease() && key == module.getKey())
module.setEnabled(false);
}
private boolean isOnTypingScreen() {
return mc.currentScreen instanceof ChatScreen
|| mc.currentScreen instanceof AnvilScreen
|| mc.currentScreen instanceof AbstractSignEditScreen
|| mc.currentScreen instanceof AbstractCommandBlockScreen;
}
@SubscribeEvent
public void onStop(ShutdownEvent event) {
ConfigManager.save();
}
public static String getVersionName() {
return MOD_ID + "_v" + MOD_VERSION + "_" + SharedConstants.getGameVersion().name();
}
}
@@ -0,0 +1,61 @@
package me.retucio.sputnik.cape;
import me.retucio.sputnik.Sputnik;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.NativeImageBackedTexture;
import net.minecraft.util.Identifier;
import java.io.IOException;
import java.io.InputStream;
public class Cape {
private final MinecraftClient mc = MinecraftClient.getInstance();
private final String name;
private final Identifier id;
private NativeImage img;
private NativeImageBackedTexture texture;
public Cape(String name) {
this.name = name;
this.id = Identifier.of(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().getInputStream()) {
return NativeImage.read(inputStream);
} catch (IOException e) {
Sputnik.LOGGER.error("ups: {}", e.getMessage());
return null;
}
}
public void register() {
texture = new NativeImageBackedTexture(null, img);
mc.getTextureManager().registerTexture(id, texture);
img = null;
}
public String getName() {
return name;
}
public Identifier getId() {
return id;
}
public NativeImageBackedTexture 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;
}
}
@@ -1,4 +1,4 @@
package me.retucio.camtweaks.command; package me.retucio.sputnik.command;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
@@ -65,7 +65,7 @@ public abstract class Command {
} }
public String toString() { public String toString() {
return me.retucio.camtweaks.command.CommandManager.INSTANCE.getPrefix() + name; return me.retucio.sputnik.command.CommandManager.INSTANCE.getPrefix() + name;
} }
public String toString(String... args) { public String toString(String... args) {
@@ -1,14 +1,18 @@
package me.retucio.camtweaks.command; package me.retucio.sputnik.command;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import me.retucio.camtweaks.command.commands.*; import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.command.commands.*;
import me.retucio.sputnik.event.SubscribeEvent;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.command.CommandSource; import net.minecraft.command.CommandSource;
import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Comparator; import java.util.Comparator;
import java.util.Objects;
// registro de comandos // registro de comandos
public class CommandManager { public class CommandManager {
@@ -28,13 +32,27 @@ public class CommandManager {
public static void registerCommands() { public static void registerCommands() {
addCommand(new BindCommand()); addCommand(new BindCommand());
// addCommand(new EnderChestCommand()); no funciona D: addCommand(new CopyScreenshotCommand());
addCommand(new EnderChestCommand());
addCommand(new GarbageCleanerCommand());
addCommand(new PeekCommand()); addCommand(new PeekCommand());
addCommand(new PrefixCommand()); addCommand(new PrefixCommand());
addCommand(new PurgeCommand());
addCommand(new SaveScreenshotCommand());
addCommand(new SendCommand()); addCommand(new SendCommand());
addCommand(new SpectateCommand());
addCommand(new ToggleCommand()); addCommand(new ToggleCommand());
commands.sort(Comparator.comparing(Command::getName)); commands.sort(Comparator.comparing(Command::getName));
for (Command command : commands) {
for (Method method : command.getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(SubscribeEvent.class)) {
Sputnik.EVENT_BUS.register(command);
break;
}
}
}
} }
public static void addCommand(Command command) { public static void addCommand(Command command) {
@@ -44,17 +62,17 @@ public class CommandManager {
} }
public static void dispatch(String message) throws CommandSyntaxException { public static void dispatch(String message) throws CommandSyntaxException {
dispatcher.execute(message, mc.getNetworkHandler().getCommandSource()); dispatcher.execute(message, Objects.requireNonNull(mc.getNetworkHandler()).getCommandSource());
} }
public static Command getCommandByName(String name) { public static Command getCommandByName(String name) {
for (Command command : commands) { for (Command command : commands) {
if (command.getName().equals(name)) if (command.getName().equals(name)) return command;
return command;
for (String alias : command.getAliases()) for (String alias : command.getAliases())
if (alias.equals(name)) if (alias.equals(name)) return command;
return command;
} }
return null; return null;
} }
@@ -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.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.text.Text;
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 -> Text.literal("Entidad \"" + name + "\" no encontrada"));
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.world == 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 {
PlayerEntity nearest = null;
double nearestDistance = Double.MAX_VALUE;
for (PlayerEntity player : mc.world.getPlayers()) {
if (player == mc.player) continue;
double distance = mc.player.squaredDistanceTo(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<PlayerEntity> players = new ArrayList<>(mc.world.getPlayers());
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.world.getEntities())
if (!(entity instanceof PlayerEntity))
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.world.getEntities()) {
String entityType = entity.getType().getTranslationKey();
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.squaredDistanceTo(entity);
if (distance < nearestDistance) {
nearestDistance = distance;
nearest = entity;
}
}
return nearest;
}
private Entity findEntityByUUID(UUID uuid) throws CommandSyntaxException {
for (Entity entity : mc.world.getEntities())
if (entity.getUuid().equals(uuid))
return entity;
throw NO_SUCH_ENTITY.create(uuid.toString());
}
private PlayerEntity findPlayerByName(String name) throws CommandSyntaxException {
for (PlayerEntity player : mc.world.getPlayers())
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.world != null)
for (PlayerEntity player : mc.world.getPlayers())
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);
}
}
@@ -1,4 +1,4 @@
package me.retucio.camtweaks.command.args; package me.retucio.sputnik.command.args;
import com.mojang.brigadier.StringReader; import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
@@ -7,8 +7,8 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import me.retucio.camtweaks.module.Module; import me.retucio.sputnik.module.Module;
import me.retucio.camtweaks.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import net.minecraft.command.CommandSource; import net.minecraft.command.CommandSource;
import net.minecraft.text.Text; import net.minecraft.text.Text;
@@ -19,7 +19,7 @@ import java.util.concurrent.CompletableFuture;
public class ModuleArgumentType implements ArgumentType<Module> { public class ModuleArgumentType implements ArgumentType<Module> {
public static final ModuleArgumentType INSTANCE = new ModuleArgumentType(); public static final ModuleArgumentType INSTANCE = new ModuleArgumentType();
private static final DynamicCommandExceptionType unknownModuleE = new DynamicCommandExceptionType( private static final DynamicCommandExceptionType UNKNOWN_MODULE = new DynamicCommandExceptionType(
name -> Text.literal("módulo \"" + name + "\" no encontrado")); name -> Text.literal("módulo \"" + name + "\" no encontrado"));
private static final Collection<String> examples = ModuleManager.INSTANCE.getModules() private static final Collection<String> examples = ModuleManager.INSTANCE.getModules()
@@ -29,7 +29,7 @@ public class ModuleArgumentType implements ArgumentType<Module> {
public Module parse(StringReader reader) throws CommandSyntaxException { public Module parse(StringReader reader) throws CommandSyntaxException {
String argument = reader.readString().replace("_", " "); String argument = reader.readString().replace("_", " ");
Module module = ModuleManager.INSTANCE.getModuleByName(argument); Module module = ModuleManager.INSTANCE.getModuleByName(argument);
if (module == null) throw unknownModuleE.create(argument); if (module == null) throw UNKNOWN_MODULE.create(argument);
return module; return module;
} }
@@ -1,4 +1,4 @@
package me.retucio.camtweaks.command.args; package me.retucio.sputnik.command.args;
import com.mojang.brigadier.StringReader; import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
@@ -15,7 +15,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import static me.retucio.camtweaks.CameraTweaks.mc; import static me.retucio.sputnik.Sputnik.mc;
// para autocompletar nombres de jugadores // para autocompletar nombres de jugadores
public class PlayerArgumentType implements ArgumentType<PlayerEntity> { public class PlayerArgumentType implements ArgumentType<PlayerEntity> {
@@ -45,7 +45,8 @@ public class PlayerArgumentType implements ArgumentType<PlayerEntity> {
@Override @Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) { public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return CommandSource.suggestMatching(mc.world.getPlayers().stream().map(abstractClientPlayerEntity -> abstractClientPlayerEntity.getName().getString()), builder); return CommandSource.suggestMatching(mc.world.getPlayers().stream().map(
abstractClientPlayerEntity -> abstractClientPlayerEntity.getName().getString()), builder);
} }
@Override @Override
@@ -1,12 +1,12 @@
package me.retucio.camtweaks.command.commands; package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.camtweaks.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.camtweaks.command.args.ModuleArgumentType; import me.retucio.sputnik.command.args.ModuleArgumentType;
import me.retucio.camtweaks.module.Module; import me.retucio.sputnik.module.Module;
import me.retucio.camtweaks.module.settings.KeySetting; import me.retucio.sputnik.module.settings.KeySetting;
import me.retucio.camtweaks.util.ChatUtil; import me.retucio.sputnik.util.ChatUtil;
import me.retucio.camtweaks.util.KeyUtil; import me.retucio.sputnik.util.KeyUtil;
import net.minecraft.command.CommandSource; import net.minecraft.command.CommandSource;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Formatting; import net.minecraft.util.Formatting;
@@ -42,8 +42,7 @@ public class BindCommand extends Command {
public static boolean onKeyPress(int key) { public static boolean onKeyPress(int key) {
if (listeningModule == null) return false; if (listeningModule == null) return false;
KeySetting bind = listeningModule.getBind(); KeySetting bind = listeningModule.getBind();
if (bind != null) if (bind != null) bind.setKey(key);
bind.setKey(key);
ChatUtil.info( ChatUtil.info(
Text.of("la tecla " + Formatting.AQUA + KeyUtil.getKeyName(key) + Formatting.RESET + Text.of("la tecla " + Formatting.AQUA + KeyUtil.getKeyName(key) + Formatting.RESET +
@@ -0,0 +1,26 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.misc.ScreenshotPlus;
import net.minecraft.command.CommandSource;
// se usa principalmente para el módulo ScreenshotPlus, pero también se puede usar sin él supongo
public class CopyScreenshotCommand extends Command {
public CopyScreenshotCommand() {
super("copiarcaptura", "copia la captura más reciente al portapapeles");
}
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder.executes(ctx -> {
ScreenshotPlus screenshotPlus = ModuleManager.INSTANCE.getModuleByClass(ScreenshotPlus.class);
if (screenshotPlus.getScreenshot() != null) mc.execute(screenshotPlus::copyScreenshot);
return SUCCESS;
});
}
}
@@ -0,0 +1,46 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.event.SubscribeEvent;
import me.retucio.sputnik.event.events.TickEvent;
import me.retucio.sputnik.ui.screen.PreviewScreen;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.InventoryUtil;
import net.minecraft.command.CommandSource;
// accede a los contenidos de tu enderchest
public class EnderChestCommand extends Command {
private boolean shouldOpenEchest = false;
public EnderChestCommand() {
super("echest", "muestra el contenido de tu enderchest (solo de ver)", "ec", "enderchest");
}
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder.executes(ctx -> {
mc.execute(() -> {
if (mc.player == null) return;
if (InventoryUtil.getEchestInv() == null) {
ChatUtil.warn("necesitas abrir un enderchest una vez primero");
return;
}
shouldOpenEchest = true;
});
return SUCCESS;
});
}
@SubscribeEvent
public void onTick(TickEvent.Post event) {
if (shouldOpenEchest) {
shouldOpenEchest = false;
if (InventoryUtil.getEchestInv() != null)
mc.setScreen(new PreviewScreen(InventoryUtil.getEchestInv(), null));
}
}
}
@@ -0,0 +1,23 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.command.CommandSource;
import net.minecraft.text.Text;
public class GarbageCleanerCommand extends Command {
public GarbageCleanerCommand() {
super("gc", "limpia la ram mediante el recolector de basura (gc)", "garbagecollect");
}
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder.executes(context -> {
System.gc();
ChatUtil.info(Text.of("basura recolectada"));
return SUCCESS;
});
}
}
@@ -1,9 +1,9 @@
package me.retucio.camtweaks.command.commands; package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.camtweaks.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.camtweaks.command.args.PlayerArgumentType; import me.retucio.sputnik.command.args.PlayerArgumentType;
import me.retucio.camtweaks.util.MiscUtil; import me.retucio.sputnik.util.MiscUtil;
import net.minecraft.client.gui.screen.ingame.InventoryScreen; import net.minecraft.client.gui.screen.ingame.InventoryScreen;
import net.minecraft.command.CommandSource; import net.minecraft.command.CommandSource;
@@ -1,10 +1,10 @@
package me.retucio.camtweaks.command.commands; package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.camtweaks.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.camtweaks.ui.frames.ClickGUISettingsFrame; import me.retucio.sputnik.ui.widgets.frames.settings.ClientSettingsFrame;
import me.retucio.camtweaks.util.ChatUtil; import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.command.CommandSource; import net.minecraft.command.CommandSource;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Formatting; import net.minecraft.util.Formatting;
@@ -21,7 +21,7 @@ public class PrefixCommand extends Command {
builder builder
.then(argument("prefijo", StringArgumentType.word()).executes(ctx -> { .then(argument("prefijo", StringArgumentType.word()).executes(ctx -> {
String prefix = ctx.getArgument("prefijo", String.class); String prefix = ctx.getArgument("prefijo", String.class);
ClickGUISettingsFrame.guiSettings.commandPrefix.setValue(prefix); ClientSettingsFrame.guiSettings.commandPrefix.setValue(prefix);
ChatUtil.info(Text.literal("prefijo cambiado a " + Formatting.AQUA + prefix)); ChatUtil.info(Text.literal("prefijo cambiado a " + Formatting.AQUA + prefix));
return SUCCESS; return SUCCESS;
})); }));
@@ -0,0 +1,40 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.util.interfaces.IChatHud;
import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.client.gui.hud.ChatHudLine;
import net.minecraft.command.CommandSource;
import java.util.List;
public class PurgeCommand extends Command {
public PurgeCommand() {
super("purgar", "elimina mensajes del chat", "purge");
}
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder.then(argument("cantidad", IntegerArgumentType.integer(1))
.executes(context -> {
int amount = IntegerArgumentType.getInteger(context, "cantidad");
purgeMessages(amount);
return 1;
})
);
}
private void purgeMessages(int amount) {
ChatHud chatHud = mc.inGameHud.getChatHud();
synchronized (chatHud) {
List<ChatHudLine.Visible> visibleMessages = ((IChatHud) chatHud).sputnik$getVisibleMessages();
int toRemove = Math.min(amount, visibleMessages.size());
for (int i = 0; i < toRemove; i++)
visibleMessages.removeFirst();
}
}
}
@@ -0,0 +1,37 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.misc.ScreenshotPlus;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.command.CommandSource;
import java.io.IOException;
// se usa principalmente para el módulo ScreenshotPlus, pero también se puede usar sin él supongo
public class SaveScreenshotCommand extends Command {
public SaveScreenshotCommand() {
super("guardarcaptura", "guarda localmente la captura más reciente");
}
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder.executes(ctx -> {
ScreenshotPlus screenshotPlus = ModuleManager.INSTANCE.getModuleByClass(ScreenshotPlus.class);
mc.execute(() -> {
if (screenshotPlus.getScreenshot() != null) {
try {
screenshotPlus.saveScreenshot();
} catch (IOException e) {
ChatUtil.error("no se pudo guardar la captura");
}
}
});
return SUCCESS;
});
}
}
@@ -1,9 +1,9 @@
package me.retucio.camtweaks.command.commands; package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.camtweaks.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.camtweaks.mixin.accessor.ClientPlayNetworkHandlerAccessor; import me.retucio.sputnik.mixin.accessor.ClientPlayNetworkHandlerAccessor;
import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.command.CommandSource; import net.minecraft.command.CommandSource;
import net.minecraft.network.encryption.NetworkEncryptionUtils; import net.minecraft.network.encryption.NetworkEncryptionUtils;
@@ -11,7 +11,6 @@ import net.minecraft.network.message.LastSeenMessagesCollector;
import net.minecraft.network.message.MessageBody; import net.minecraft.network.message.MessageBody;
import net.minecraft.network.message.MessageSignatureData; import net.minecraft.network.message.MessageSignatureData;
import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket; import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket;
import net.minecraft.text.Text;
import java.time.Instant; import java.time.Instant;
@@ -0,0 +1,49 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.command.args.EntityArgumentType;
import me.retucio.sputnik.event.SubscribeEvent;
import me.retucio.sputnik.event.events.KeyEvent;
import me.retucio.sputnik.util.EntityUtil;
import net.minecraft.command.CommandSource;
import net.minecraft.entity.Entity;
import org.lwjgl.glfw.GLFW;
public class SpectateCommand extends Command {
public SpectateCommand() {
super("espectar", "te permite adquirir el punto de vista de una entidad", "spectate", "spec");
}
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder
.executes(ctx -> { // If no argument, use the entity player is looking at
mc.execute(() -> {
Entity target = EntityUtil.getEntityPlayerIsLookingAt();
if (target != null)
mc.setCameraEntity(target);
});
return SUCCESS;
})
.then(argument("entidad", EntityArgumentType.INSTANCE)
.executes(ctx -> {
Entity entity = EntityArgumentType.get(ctx, "entidad");
if (mc.player != null && entity != null) {
mc.setCameraEntity(entity);
}
return SUCCESS;
})
);
}
@SubscribeEvent
public void onKey(KeyEvent event) {
if (mc.getCameraEntity() != mc.player && mc.currentScreen == null
&& event.getKey() == GLFW.GLFW_KEY_ESCAPE && event.getAction() != GLFW.GLFW_RELEASE ) {
event.cancel();
mc.setCameraEntity(mc.player);
}
}
}
@@ -1,10 +1,10 @@
package me.retucio.camtweaks.command.commands; package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.retucio.camtweaks.command.Command; import me.retucio.sputnik.command.Command;
import me.retucio.camtweaks.command.args.ModuleArgumentType; import me.retucio.sputnik.command.args.ModuleArgumentType;
import me.retucio.camtweaks.module.Module; import me.retucio.sputnik.module.Module;
import me.retucio.camtweaks.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import net.minecraft.command.CommandSource; import net.minecraft.command.CommandSource;
// porque usar el ratón es más lento que escribir, a menos que tengas toque con el teclado como yo // porque usar el ratón es más lento que escribir, a menos que tengas toque con el teclado como yo
@@ -0,0 +1,357 @@
package me.retucio.sputnik.config;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
public class BinarySerializer {
private static final int MAGIC_NUMBER = 0x53505554; // "SPUT"
private static final int VERSION = 1;
private static final byte TYPE_NULL = 0;
private static final byte TYPE_BOOLEAN = 1;
private static final byte TYPE_INT = 2;
private static final byte TYPE_DOUBLE = 3;
private static final byte TYPE_STRING = 4;
private static final byte TYPE_MAP = 5;
private static final byte TYPE_INT_ARRAY = 6;
private static final byte TYPE_FRAME_DATA = 7;
public static void writeConfig(ClientConfig config, File file)
throws IOException {
try (DataOutputStream out = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream(file)))) {
// cabezal
out.writeInt(MAGIC_NUMBER);
out.writeInt(VERSION);
// mapas
writeStringBooleanMap(out, config.moduleStates);
writeSettingsMap(out, config.settings);
writeStringIntArrayMap(out, config.settingsFrames);
writeExtendableFrames(out, config.extendableFrames);
writeStringIntArrayMap(out, config.hudPositions);
writeStringBooleanMap(out, config.hudVisibilities);
writeStringStringMap(out, config.hudImagePaths);
// posición de la barra de búsqueda
if (config.searchBarPosition != null) {
out.writeBoolean(true);
out.writeInt(config.searchBarPosition[0]);
out.writeInt(config.searchBarPosition[1]);
} else {
out.writeBoolean(false);
}
}
}
public static ClientConfig readConfig(File file)
throws IOException {
if (!file.exists() || file.length() == 0) {
throw new IOException("archivo de config. vacío o inexistente");
}
try (DataInputStream in = new DataInputStream(
new BufferedInputStream(new FileInputStream(file)))) {
if (file.length() < 8) {
throw new IOException("archivo demasiado pequeño para ser válido");
}
// verificar cabezal
int magic = in.readInt();
if (magic != MAGIC_NUMBER) {
throw new IOException("formato de archivo inválido: " + magic);
}
int version = in.readInt();
if (version != VERSION) {
throw new IOException("versión inválida: " + version);
}
ClientConfig config = new ClientConfig();
// leer mapas
config.moduleStates = readStringBooleanMap(in);
config.settings = readSettingsMap(in);
config.settingsFrames = readStringIntArrayMap(in);
config.extendableFrames = readExtendableFrames(in);
config.hudPositions = readStringIntArrayMap(in);
config.hudVisibilities = readStringBooleanMap(in);
config.hudImagePaths = readStringStringMap(in);
// leer pos. de la barra de búsqueda
if (in.readBoolean()) {
config.searchBarPosition = new int[]{in.readInt(), in.readInt()};
}
return config;
}
}
private static void writeStringBooleanMap(DataOutputStream out, Map<String, Boolean> map)
throws IOException {
if (map == null) {
out.writeInt(0);
return;
}
out.writeInt(map.size());
for (Map.Entry<String, Boolean> entry : map.entrySet()) {
writeString(out, entry.getKey());
out.writeBoolean(entry.getValue());
}
}
private static Map<String, Boolean> readStringBooleanMap(DataInputStream in)
throws IOException {
int size = in.readInt();
Map<String, Boolean> map = new HashMap<>(size);
for (int i = 0; i < size; i++) {
String key = readString(in);
boolean value = in.readBoolean();
map.put(key, value);
}
return map;
}
private static void writeSettingsMap(DataOutputStream out, Map<String, Object> map)
throws IOException {
if (map == null) {
out.writeInt(0);
return;
}
out.writeInt(map.size());
for (Map.Entry<String, Object> entry : map.entrySet()) {
writeString(out, entry.getKey());
writeSettingValue(out, entry.getValue());
}
}
private static Map<String, Object> readSettingsMap(DataInputStream in)
throws IOException {
int size = in.readInt();
Map<String, Object> map = new HashMap<>(size);
for (int i = 0; i < size; i++) {
String key = readString(in);
Object value = readSettingValue(in);
map.put(key, value);
}
return map;
}
private static void writeSettingValue(DataOutputStream out, Object value)
throws IOException {
switch (value) {
case Boolean b -> {
out.writeByte(TYPE_BOOLEAN);
out.writeBoolean(b);
}
case Integer i -> {
out.writeByte(TYPE_INT);
out.writeInt(i);
}
case Double v -> {
out.writeByte(TYPE_DOUBLE);
out.writeDouble(v);
}
case String s -> {
out.writeByte(TYPE_STRING);
writeString(out, s);
}
case Map<?, ?> map -> {
out.writeByte(TYPE_MAP);
writeGenericMap(out, map);
} // fallback
case null, default -> out.writeByte(TYPE_NULL);
}
}
private static Object readSettingValue(DataInputStream in) throws IOException {
byte type = in.readByte();
return switch (type) {
case TYPE_BOOLEAN -> in.readBoolean();
case TYPE_INT -> in.readInt();
case TYPE_DOUBLE -> in.readDouble();
case TYPE_STRING -> readString(in);
case TYPE_MAP -> readGenericMap(in);
default -> null;
};
}
private static void writeGenericMap(DataOutputStream out, Map<?, ?> map)
throws IOException {
if (map == null) {
out.writeInt(0);
return;
}
out.writeInt(map.size());
for (Map.Entry<?, ?> entry : map.entrySet()) {
writeObject(out, entry.getKey());
writeObject(out, entry.getValue());
}
}
private static Map<Object, Object> readGenericMap(DataInputStream in)
throws IOException {
int size = in.readInt();
Map<Object, Object> map = new HashMap<>(size);
for (int i = 0; i < size; i++) {
Object key = readObject(in);
Object value = readObject(in);
map.put(key, value);
}
return map;
}
private static void writeObject(DataOutputStream out, Object obj)
throws IOException {
switch (obj) {
case String s -> {
out.writeByte(TYPE_STRING);
writeString(out, s);
}
case Boolean b -> {
out.writeByte(TYPE_BOOLEAN);
out.writeBoolean(b);
}
case Number number -> {
if (obj instanceof Integer) {
out.writeByte(TYPE_INT);
out.writeInt((Integer) obj);
} else if (obj instanceof Double) {
out.writeByte(TYPE_DOUBLE);
out.writeDouble((Double) obj);
}
}
default -> {
out.writeByte(TYPE_STRING);
writeString(out, obj.toString());
}
}
}
private static Object readObject(DataInputStream in) throws IOException {
byte type = in.readByte();
return switch (type) {
case TYPE_STRING -> readString(in);
case TYPE_BOOLEAN -> in.readBoolean();
case TYPE_INT -> in.readInt();
case TYPE_DOUBLE -> in.readDouble();
default -> null;
};
}
private static void writeStringIntArrayMap(DataOutputStream out, Map<String, int[]> map)
throws IOException {
if (map == null) {
out.writeInt(0);
return;
}
out.writeInt(map.size());
for (Map.Entry<String, int[]> entry : map.entrySet()) {
writeString(out, entry.getKey());
int[] array = entry.getValue();
if (array == null) {
out.writeInt(0);
} else {
out.writeInt(array.length);
for (int value : array) {
out.writeInt(value);
}
}
}
}
private static Map<String, int[]> readStringIntArrayMap(DataInputStream in)
throws IOException {
int size = in.readInt();
Map<String, int[]> map = new HashMap<>(size);
for (int i = 0; i < size; i++) {
String key = readString(in);
int arraySize = in.readInt();
int[] array = new int[arraySize];
for (int j = 0; j < arraySize; j++) {
array[j] = in.readInt();
}
map.put(key, array);
}
return map;
}
private static void writeExtendableFrames(DataOutputStream out, Map<String, ClientConfig.FrameData> map)
throws IOException {
if (map == null) {
out.writeInt(0);
return;
}
out.writeInt(map.size());
for (Map.Entry<String, ClientConfig.FrameData> entry : map.entrySet()) {
writeString(out, entry.getKey());
ClientConfig.FrameData data = entry.getValue();
out.writeInt(data.x());
out.writeInt(data.y());
out.writeBoolean(data.extended());
}
}
private static Map<String, ClientConfig.FrameData> readExtendableFrames(DataInputStream in)
throws IOException {
int size = in.readInt();
Map<String, ClientConfig.FrameData> map = new HashMap<>(size);
for (int i = 0; i < size; i++) {
String key = readString(in);
int x = in.readInt();
int y = in.readInt();
boolean extended = in.readBoolean();
map.put(key, new ClientConfig.FrameData(x, y, extended));
}
return map;
}
private static void writeStringStringMap(DataOutputStream out, Map<String, String> map)
throws IOException {
if (map == null) {
out.writeInt(0);
return;
}
out.writeInt(map.size());
for (Map.Entry<String, String> entry : map.entrySet()) {
writeString(out, entry.getKey());
writeString(out, entry.getValue());
}
}
private static Map<String, String> readStringStringMap(DataInputStream in)
throws IOException {
int size = in.readInt();
Map<String, String> map = new HashMap<>(size);
for (int i = 0; i < size; i++) {
String key = readString(in);
String value = readString(in);
map.put(key, value);
}
return map;
}
// Optimized string writing (UTF-8 with length prefix)
private static void writeString(DataOutputStream out, String str) throws IOException {
if (str == null) {
out.writeInt(-1);
return;
}
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
out.writeInt(bytes.length);
out.write(bytes);
}
private static String readString(DataInputStream in) throws IOException {
int length = in.readInt();
if (length == -1) return null;
byte[] bytes = new byte[length];
in.readFully(bytes);
return new String(bytes, "UTF-8");
}
}
@@ -1,16 +1,17 @@
package me.retucio.camtweaks.config; package me.retucio.sputnik.config;
import me.retucio.camtweaks.event.SubscribeEvent; import me.retucio.sputnik.event.SubscribeEvent;
import me.retucio.camtweaks.event.events.camtweaks.*; import me.retucio.sputnik.event.events.sputnik.*;
import me.retucio.camtweaks.module.settings.*; import me.retucio.sputnik.module.settings.*;
import me.retucio.camtweaks.ui.ClickGUI; import me.retucio.sputnik.ui.screen.ClickGUI;
import me.retucio.camtweaks.ui.frames.ClickGUISettingsFrame;
import java.io.Serial;
import java.io.Serializable;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static me.retucio.camtweaks.CameraTweaks.EVENT_BUS; import static me.retucio.sputnik.Sputnik.EVENT_BUS;
import static me.retucio.camtweaks.CameraTweaks.mc; import static me.retucio.sputnik.Sputnik.mc;
// clase donde se guardan las configuraciones temporalmente hasta escribirlas en camera_tweaks.json // clase donde se guardan las configuraciones temporalmente hasta escribirlas en camera_tweaks.json
public class ClientConfig { public class ClientConfig {
@@ -27,19 +28,31 @@ public class ClientConfig {
// nombre del frame -> FrameData (que contiene extended, x & y) // nombre del frame -> FrameData (que contiene extended, x & y)
public Map<String, FrameData> extendableFrames = new HashMap<>(); public Map<String, FrameData> extendableFrames = new HashMap<>();
// id del elemento del hud -> posición (x, y)
public Map<String, int[]> hudPositions = new HashMap<>();
// id del elemento del hud -> true: visible / false: no visible
public Map<String, Boolean> hudVisibilities = new HashMap<>();
// id del elemento del hud -> ruta de la imagen (NEW)
public Map<String, String> hudImagePaths = new HashMap<>();
// posición de la barra de búsqueda (x, y)
public int[] searchBarPosition = new int[]{340, 16};
public ClientConfig() { public ClientConfig() {
EVENT_BUS.register(this); EVENT_BUS.register(this);
} }
@SubscribeEvent @SubscribeEvent
public void onToggleModule(ToggleModuleEvent event) { public void onToggleModule(ToggleModuleEvent event) {
ConfigManager.setModuleState(event.getModule()); ConfigManager.setModuleState(event.getModule());
} }
@SuppressWarnings("rawtypes")
@SubscribeEvent @SubscribeEvent
public void onUpdateSetting(UpdateSettingEvent event) { public void onUpdateSetting(UpdateSettingEvent event) {
if (mc == null) return; if (mc == null || !event.shouldSave()) return;
// guardar ajustes con su respectivo tipo de valor, en formato "nombreMódulo:nombreAjuste" // guardar ajustes con su respectivo tipo de valor, en formato "nombreMódulo:nombreAjuste"
Object value = null; Object value = null;
@@ -49,6 +62,9 @@ public class ClientConfig {
case KeySetting k: value = k.getKey(); break; case KeySetting k: value = k.getKey(); break;
case NumberSetting n: value = n.getValue(); break; case NumberSetting n: value = n.getValue(); break;
case StringSetting s: value = s.getValue(); break; case StringSetting s: value = s.getValue(); break;
case ListSetting l: value = l.getConfigValues(); break;
case ColorSetting c: value = c.getConfigValue(); break;
case OptionSetting o: value = o.getDisplayName(); break;
default: break; default: break;
} }
@@ -57,7 +73,6 @@ public class ClientConfig {
@SubscribeEvent @SubscribeEvent
public void onOpenSettingsFrame(SettingsFrameEvent.Open event) { public void onOpenSettingsFrame(SettingsFrameEvent.Open event) {
if (settingsFrames.containsKey(event.getFrame().module.getName())) return;
ConfigManager.setFramePosition(event.getFrame()); ConfigManager.setFramePosition(event.getFrame());
} }
@@ -69,41 +84,43 @@ public class ClientConfig {
@SubscribeEvent @SubscribeEvent
public void onMoveSettingsFrame(SettingsFrameEvent.Move event) { public void onMoveSettingsFrame(SettingsFrameEvent.Move event) {
settingsFrames.replace(event.getFrame().module.getName(), new int[]{event.getFrame().x, event.getFrame().y}); settingsFrames.replace(event.getFrame().module.getName(), new int[]{event.getFrame().getX(), event.getFrame().getY()});
ConfigManager.save(); ConfigManager.save();
} }
@SubscribeEvent @SubscribeEvent
public void onExtendModuleFrame(ModuleFrameEvent.Extend event) { public void onExtendModuleFrame(ModuleFrameEvent.Extend event) {
extendableFrames.put("M", new FrameData( extendableFrames.put("M", new FrameData(
ClickGUI.INSTANCE.getModulesFrame().x, ClickGUI.INSTANCE.getModulesFrame().getX(),
ClickGUI.INSTANCE.getModulesFrame().y, ClickGUI.INSTANCE.getModulesFrame().getY(),
ClickGUI.INSTANCE.getModulesFrame().extended)); ClickGUI.INSTANCE.getModulesFrame().extended));
} }
@SubscribeEvent @SubscribeEvent
public void onMoveModuleFrame(ModuleFrameEvent.Move event) { public void onMoveModuleFrame(ModuleFrameEvent.Move event) {
extendableFrames.replace("M", new FrameData( extendableFrames.replace("M", new FrameData(
ClickGUI.INSTANCE.getModulesFrame().x, ClickGUI.INSTANCE.getModulesFrame().getX(),
ClickGUI.INSTANCE.getModulesFrame().y, ClickGUI.INSTANCE.getModulesFrame().getY(),
ClickGUI.INSTANCE.getModulesFrame().extended)); ClickGUI.INSTANCE.getModulesFrame().extended));
} }
@SubscribeEvent @SubscribeEvent
public void onExtendGUISettingsFrame(GUISettingsFrameEvent.Extend event) { public void onExtendGUISettingsFrame(GUISettingsFrameEvent.Extend event) {
extendableFrames.put("S", new FrameData( extendableFrames.put("S", new FrameData(
ClickGUI.INSTANCE.getGuiSettingsFrame().x, ClickGUI.INSTANCE.getGuiSettingsFrame().getX(),
ClickGUI.INSTANCE.getGuiSettingsFrame().y, ClickGUI.INSTANCE.getGuiSettingsFrame().getY(),
ClickGUI.INSTANCE.getGuiSettingsFrame().extended)); ClickGUI.INSTANCE.getGuiSettingsFrame().extended));
} }
@SubscribeEvent @SubscribeEvent
public void onMoveGUISettingsFrame(GUISettingsFrameEvent.Move event) { public void onMoveGUISettingsFrame(GUISettingsFrameEvent.Move event) {
extendableFrames.replace("S", new FrameData( extendableFrames.replace("S", new FrameData(
ClickGUI.INSTANCE.getGuiSettingsFrame().x, ClickGUI.INSTANCE.getGuiSettingsFrame().getX(),
ClickGUI.INSTANCE.getGuiSettingsFrame().y, ClickGUI.INSTANCE.getGuiSettingsFrame().getY(),
ClickGUI.INSTANCE.getGuiSettingsFrame().extended)); ClickGUI.INSTANCE.getGuiSettingsFrame().extended));
} }
public record FrameData(int x, int y, boolean extended) {} public record FrameData(int x, int y, boolean extended) implements Serializable {
@Serial private static final long serialVersionUID = 1L;
}
} }
@@ -0,0 +1,349 @@
package me.retucio.sputnik.config;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.settings.*;
import me.retucio.sputnik.ui.widgets.Frame;
import me.retucio.sputnik.ui.widgets.frames.ModuleFrame;
import me.retucio.sputnik.ui.screen.ClickGUI;
import me.retucio.sputnik.ui.widgets.frames.settings.ClientSettingsFrame;
import me.retucio.sputnik.ui.widgets.frames.SettingsFrame;
import me.retucio.sputnik.util.ChatUtil;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
// se ocupa de guardar, cargar y aplicar ajustes
public class ConfigManager {
private static final File CONFIG_FILE = new File("sputnik.dat");
private static final File LEGACY_JSON_FILE = new File("sputnik.json");
private static boolean loaded = false;
private static ClientConfig config = null;
// ------------------ MÉTODOS PRINCIPALES ------------------
// guardar configuraciones
public static void save() {
if (!loaded) return;
ensureConfig();
try {
BinarySerializer.writeConfig(config, CONFIG_FILE);
Sputnik.LOGGER.info("ajustes guardados");
} catch (IOException e) {
Sputnik.LOGGER.error("error al guardar ajustes", e);
}
}
public static void load() {
Sputnik.LOGGER.info("cargando ajustes...");
if (CONFIG_FILE.exists()) {
try {
config = BinarySerializer.readConfig(CONFIG_FILE);
Sputnik.LOGGER.info("ajustes cargados");
return;
} catch (IOException e) {
Sputnik.LOGGER.error("error cargando ajustes, intentando json", e);
}
}
// Fallback to legacy JSON
if (LEGACY_JSON_FILE.exists()) {
try (FileReader reader = new FileReader(LEGACY_JSON_FILE)) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
config = gson.fromJson(reader, ClientConfig.class);
Sputnik.LOGGER.info("ajustes json cargados (migración)");
save();
return;
} catch (IOException e) {
Sputnik.LOGGER.error("error cargando json, usando defaults", e);
}
}
// crear nuevo config
ensureConfig();
save();
}
// aplicar configuraciones cargadas
public static void applyConfig() {
if (!shouldApply()) return;
applyModuleStates();
applyModuleSettings();
applySettingsFrames();
applySearchBarPosition();
applyGuiSettings();
applyExtendableFrames();
loaded = true;
Sputnik.LOGGER.info("ajustes aplicados");
}
// obtener configuración actual
public static ClientConfig getConfig() {
ensureConfig();
return config;
}
// ------------------ GUARDAR VALORES INDIVIDUALES EN LA CONFIG. ------------------
public static void setModuleState(Module module) {
ensureConfig();
config.moduleStates.put(module.getName(), module.isEnabled());
save();
}
public static void setSetting(Setting setting, Object value) {
ensureConfig();
config.settings.put(getSettingKey(setting), value);
save();
}
public static void setFramePosition(SettingsFrame frame) {
ensureConfig();
if (!config.settingsFrames.containsKey(frame.module.getName()))
config.settingsFrames.put(frame.module.getName(), new int[] {frame.getX(), frame.getY()});
else
config.settingsFrames.replace(frame.module.getName(), new int[] {frame.getX(), frame.getY()});
save();
}
public static void setExtendableFrame(String key, ClientConfig.FrameData data) {
ensureConfig();
config.extendableFrames.put(key, data);
save();
}
public static void setHudPosition(String id, int x, int y) {
ensureConfig();
config.hudPositions.put(id, new int[] {x, y});
save();
}
public static void setHudVisibility(String id, Boolean visible) {
ensureConfig();
config.hudVisibilities.put(id, visible);
save();
}
public static void setHudImagePath(String id, String imagePath) {
ensureConfig();
config.hudImagePaths.put(id, imagePath);
save();
}
public static String getHudImagePath(String id) {
ensureConfig();
return config.hudImagePaths.getOrDefault(id, "");
}
public static void setSearchBarPosition(int x, int y) {
ensureConfig();
config.searchBarPosition = new int[] {x, y};
save();
}
// ------------------ APLICAR AJUSTES ------------------
@SuppressWarnings({"rawtypes", "unchecked"})
public static void applySetting(Module parent, Setting setting) {
String key = getSettingKey(setting);
if (!config.settings.containsKey(key)) return;
Object value = config.settings.get(key);
switch (setting) {
case BooleanSetting booleanSetting -> applyBooleanSetting(booleanSetting, value);
case EnumSetting enumSetting -> applyEnumSetting(enumSetting, value);
case NumberSetting numberSetting -> applyNumberSetting(numberSetting, value);
case StringSetting stringSetting -> applyStringSetting(stringSetting, value);
case KeySetting keySetting -> applyKeySetting(keySetting, value);
case ListSetting listSetting -> applyListSetting(listSetting, value);
case ColorSetting colorSetting -> applyColorSetting(colorSetting, value);
case OptionSetting optionSetting -> applyOptionSetting(optionSetting, value);
default -> Sputnik.LOGGER.warn("watafac queseso: {}", setting.getClass().getSimpleName());
}
}
private static void applyModuleStates() {
ModuleManager.INSTANCE.getModules().forEach(module -> {
if (config.moduleStates.containsKey(module.getName())) {
module.setEnabled(config.moduleStates.get(module.getName()));
}
});
Sputnik.LOGGER.info("estados de módulos aplicados");
}
private static void applyModuleSettings() {
ModuleManager.INSTANCE.getModules().forEach(module -> {
module.getSettings().forEach(setting -> applySetting(module, setting));
});
Sputnik.LOGGER.info("ajustes de módulos aplicados");
}
private static void applySettingsFrames() {
config.settingsFrames.forEach((moduleName, position) -> {
Module module = ModuleManager.INSTANCE.getModuleByName(moduleName);
ClickGUI.INSTANCE.openSettingsFrame(module, position[0], position[1]);
});
Sputnik.LOGGER.info("estados de marcos de ajustes aplicados");
}
private static void applySearchBarPosition() {
if (config.searchBarPosition != null && config.searchBarPosition.length == 2) {
ClickGUI.INSTANCE.getSearchBar().setX(config.searchBarPosition[0]);
ClickGUI.INSTANCE.getSearchBar().setY(config.searchBarPosition[1]);
}
Sputnik.LOGGER.info("posición de la barra de búsqueda aplicada");
}
private static void applyGuiSettings() {
ClientSettingsFrame.guiSettings.setEnabled(true);
ClientSettingsFrame.guiSettings.getSettings().forEach(setting -> {
applySetting(ClientSettingsFrame.guiSettings, setting);
});
Sputnik.LOGGER.info("ajustes del cliente aplicados");
}
private static void applyExtendableFrames() {
applyExtendableFrame(ClickGUI.INSTANCE.getModulesFrame());
applyExtendableFrame(ClickGUI.INSTANCE.getGuiSettingsFrame());
Sputnik.LOGGER.info("posiciones de marcos extendibles aplicadas");
ClickGUI.INSTANCE.refreshListButtons();
}
private static void applyExtendableFrame(Frame<?> frame) {
ClientConfig.FrameData frameData = null;
if (frame instanceof ModuleFrame)
frameData = config.extendableFrames.get("M");
if (frame instanceof ClientSettingsFrame)
frameData = config.extendableFrames.get("S");
if (frameData != null) {
frame.setX(frameData.x());
frame.setY(frameData.y());
if (frame instanceof ModuleFrame mFrame)
mFrame.extended = frameData.extended();
if (frame instanceof ClientSettingsFrame sFrame)
sFrame.extended = frameData.extended();
}
}
// ajustes de módulos
private static void applyBooleanSetting(BooleanSetting setting, Object value) {
if (value instanceof Boolean) {
setting.setEnabled((Boolean) value);
}
}
private static <T extends Enum<T>> void applyEnumSetting(EnumSetting<T> setting, Object value) {
if (value instanceof Double) {
setting.setIndex(((Double) value).intValue());
} else if (value instanceof Integer) {
setting.setIndex((Integer) value);
}
}
private static void applyNumberSetting(NumberSetting setting, Object value) {
if (value instanceof Double) {
setting.setValue((Double) value);
}
}
private static void applyStringSetting(StringSetting setting, Object value) {
if (value instanceof String) {
setting.setValue((String) value);
}
}
private static void applyKeySetting(KeySetting setting, Object value) {
if (value instanceof Double) {
setting.setKey(((Double) value).intValue());
} else if (value instanceof Integer) {
setting.setKey((Integer) value);
}
}
private static <T> void applyListSetting(ListSetting<T> setting, Object value) {
if (value instanceof Map<?, ?> map) {
Map<T, Boolean> convertedValues = new HashMap<>();
for (T option : setting.getOptions()) {
String key = setting.getDisplayName(option);
Object booleanValue = map.get(key);
convertedValues.put(option, booleanValue instanceof Boolean && (Boolean) booleanValue);
}
setting.setValues(convertedValues);
}
}
private static void applyColorSetting(ColorSetting setting, Object value) {
if (!(value instanceof Map<?, ?> map)) return;
Number r = (Number) map.get("r");
Number g = (Number) map.get("g");
Number b = (Number) map.get("b");
Number a = (Number) map.get("a");
if (r != null && g != null && b != null && a != null)
setting.setRGB(r.intValue(), g.intValue(), b.intValue(), a.intValue());
applyOptionalProperty(map, "rb", Boolean.class, setting::setRainbow);
applyOptionalProperty(map, "rs", Number.class, val -> setting.setRainbowSpeed(val.intValue()));
applyOptionalProperty(map, "sat", Number.class, val -> setting.setSaturation(val.floatValue()));
applyOptionalProperty(map, "bri", Number.class, val -> setting.setBrightness(val.floatValue()));
}
private static <T> void applyOptionSetting(OptionSetting<T> setting, Object value) {
if (value instanceof String name) {
setting.setValueByName(name);
}
}
private static <T> void applyOptionalProperty(Map<?, ?> map, String key, Class<T> type, java.util.function.Consumer<T> setter) {
Object value = map.get(key);
if (type.isInstance(value)) {
setter.accept(type.cast(value));
}
}
// ------------------ MÉTODOS DE AYUDA ------------------
private static boolean shouldApply() {
return Sputnik.mc != null && ModuleManager.INSTANCE != null && config != null;
}
private static void ensureConfig() {
if (config == null) config = new ClientConfig();
}
private static String getSettingKey(Setting setting) {
return setting.getModule().getName() + ":" + setting.getName();
}
public static boolean hasLoaded() {
return loaded;
}
}
@@ -1,7 +1,8 @@
package me.retucio.camtweaks.event; package me.retucio.sputnik.event;
// evento base, cancelable (usado en mixins) // evento base, cancelable (usado en mixins)
public class Event { public class Event {
private boolean cancelled; private boolean cancelled;
public boolean isCancelled() { public boolean isCancelled() {
@@ -1,4 +1,4 @@
package me.retucio.camtweaks.event; package me.retucio.sputnik.event;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
@@ -6,6 +6,7 @@ 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 // 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 { public class EventBus {
private final Map<Class<?>, List<ListenerMethod>> listeners = new HashMap<>(); private final Map<Class<?>, List<ListenerMethod>> listeners = new HashMap<>();
private record ListenerMethod(Object instance, Method method) {} private record ListenerMethod(Object instance, Method method) {}
@@ -1,4 +1,4 @@
package me.retucio.camtweaks.event; package me.retucio.sputnik.event;
public enum Stage { public enum Stage {
PRE, // antes de que haya sucedido PRE, // antes de que haya sucedido
@@ -1,4 +1,4 @@
package me.retucio.camtweaks.event; package me.retucio.sputnik.event;
import java.lang.annotation.*; import java.lang.annotation.*;
@@ -0,0 +1,21 @@
package me.retucio.sputnik.event.events;
import me.retucio.sputnik.event.Event;
import net.minecraft.entity.Entity;
/**
* @see me.retucio.sputnik.mixin.ClientWorldMixin#onAddEntity
*/
public class AddEntityEvent extends Event {
private final Entity entity;
public AddEntityEvent(Entity entity) {
this.entity = entity;
}
public Entity getEntity() {
return entity;
}
}
@@ -0,0 +1,21 @@
package me.retucio.sputnik.event.events;
import me.retucio.sputnik.event.Event;
import net.minecraft.entity.Entity;
/**
* @see me.retucio.sputnik.mixin.ClientPlayerInteractionManagerMixin#onAttackEntity
*/
public class AttackEntityEvent extends Event {
private final Entity entity;
public AttackEntityEvent(Entity entity) {
this.entity = entity;
}
public Entity getEntity() {
return entity;
}
}
@@ -0,0 +1,25 @@
package me.retucio.sputnik.event.events;
import me.retucio.sputnik.event.Event;
import net.minecraft.util.math.BlockPos;
/**
* @see me.retucio.sputnik.mixin.ClientPlayerInteractionManagerMixin#onBlockBreak
*/
public class BreakBlockEvent extends Event {
private BlockPos pos;
public BreakBlockEvent(BlockPos pos) {
this.pos = pos;
}
public BlockPos getPos() {
return pos;
}
public void setPos(BlockPos pos) {
this.pos = pos;
}
}
@@ -0,0 +1,36 @@
package me.retucio.sputnik.event.events;
import me.retucio.sputnik.event.Event;
/**
* @see me.retucio.sputnik.mixin.EntityMixin#onRotation
* @see me.retucio.sputnik.mixin.EntityMixin#onChangeYaw
* @see me.retucio.sputnik.mixin.EntityMixin#onChangePitch
*/
public class ChangeRotationEvent extends Event {
private float yaw;
private float pitch;
public ChangeRotationEvent(float yaw, float pitch) {
this.yaw = yaw;
this.pitch = pitch;
}
public float getYaw() {
return yaw;
}
public void setYaw(float yaw) {
this.yaw = yaw;
}
public float getPitch() {
return pitch;
}
public void setPitch(float pitch) {
this.pitch = pitch;
}
}
@@ -0,0 +1,9 @@
package me.retucio.sputnik.event.events;
import me.retucio.sputnik.event.Event;
/**
* @see me.retucio.sputnik.mixin.ChunkOcclusionDataBuilderMixin#onChunkOcclusion
*/
public class ChunkOcclusionEvent extends Event {}
@@ -1,11 +1,12 @@
package me.retucio.camtweaks.event.events; package me.retucio.sputnik.event.events;
import me.retucio.sputnik.module.modules.misc.ChatPlus;
import net.minecraft.text.ClickEvent; import net.minecraft.text.ClickEvent;
/** este evento es solo para asegurarse de que comandos del mod se puedan ejecutar solo desde el lado del cliente
* @see me.retucio.camtweaks.module.modules.ChatPlus#getSendButton
*/
/** este evento es solo para asegurarse de que comandos del mod se puedan ejecutar solo desde el lado del cliente
* @see ChatPlus#getSendButton
*/
public class ClientClickEvent implements ClickEvent { public class ClientClickEvent implements ClickEvent {
private final String value; private final String value;
@@ -0,0 +1,20 @@
package me.retucio.sputnik.event.events;
import me.retucio.sputnik.event.Event;
public class ClipAtLedgeEvent extends Event {
private boolean clipping;
public ClipAtLedgeEvent(boolean clipping) {
this.clipping = clipping;
}
public boolean isClipping() {
return clipping;
}
public void setClipping(boolean clipping) {
this.clipping = clipping;
}
}
@@ -0,0 +1,27 @@
package me.retucio.sputnik.event.events;
import me.retucio.sputnik.event.Event;
import net.minecraft.item.ItemStack;
/**
* @see me.retucio.sputnik.mixin.ItemStackMixin#onDamage
*/
public class DamageItemEvent extends Event {
private final int amount;
private final ItemStack stack;
public DamageItemEvent(int amount, ItemStack stack) {
this.amount = amount;
this.stack = stack;
}
public int getAmount() {
return amount;
}
public ItemStack getStack() {
return stack;
}
}
@@ -0,0 +1,28 @@
package me.retucio.sputnik.event.events;
import me.retucio.sputnik.event.Event;
import net.minecraft.client.network.ServerInfo;
import net.minecraft.network.DisconnectionInfo;
/**
* @see me.retucio.sputnik.mixin.ClientConnectionMixin#onDisconnect
*/
public class DisconnectEvent extends Event {
private final DisconnectionInfo info;
private final ServerInfo server;
public DisconnectEvent(DisconnectionInfo info, ServerInfo server) {
this.info = info;
this.server = server;
}
public DisconnectionInfo getInfo() {
return info;
}
public ServerInfo getServer() {
return server;
}
}
@@ -1,11 +1,11 @@
package me.retucio.camtweaks.event.events; package me.retucio.sputnik.event.events;
import me.retucio.sputnik.event.Event;
import me.retucio.camtweaks.event.Event;
/** /**
* @see me.retucio.camtweaks.mixin.GameRendererMixin#modifyFov * @see me.retucio.sputnik.mixin.GameRendererMixin#modifyFov
*/ */
public class GetFOVEvent extends Event { public class GetFOVEvent extends Event {
private float fov; private float fov;
@@ -0,0 +1,32 @@
package me.retucio.sputnik.event.events;
import me.retucio.sputnik.event.Event;
import net.minecraft.entity.Entity;
import net.minecraft.util.Hand;
/**
* @see me.retucio.sputnik.mixin.ClientPlayerInteractionManagerMixin#onEntityInteract
*/
public class InteractEntityEvent extends Event {
private final Entity entity;
private Hand hand;
public InteractEntityEvent(Entity entity, Hand hand) {
this.entity = entity;
this.hand = hand;
}
public Entity getEntity() {
return entity;
}
public Hand getHand() {
return hand;
}
public void setHand(Hand hand) {
this.hand = hand;
}
}
@@ -0,0 +1,21 @@
package me.retucio.sputnik.event.events;
import me.retucio.sputnik.event.Event;
import net.minecraft.client.world.ClientWorld;
/**
* @see me.retucio.sputnik.mixin.MinecraftClientMixin#onJoinWorld
*/
public class JoinWorldEvent extends Event {
private final ClientWorld world;
public JoinWorldEvent(ClientWorld world) {
this.world = world;
}
public ClientWorld getWorld() {
return world;
}
}
@@ -1,11 +1,11 @@
package me.retucio.camtweaks.event.events; package me.retucio.sputnik.event.events;
import me.retucio.sputnik.event.Event;
import me.retucio.camtweaks.event.Event;
/** /**
* @see me.retucio.camtweaks.mixin.KeyboardMixin#onKeyPress * @see me.retucio.sputnik.mixin.KeyboardMixin#onKeyPress
*/ */
public class KeyEvent extends Event { public class KeyEvent extends Event {
private final int key, scancode, action; private final int key, scancode, action;
Loaded 100 of 405 files, more files were not shown because too many files have changed in this diff. Show more