This commit is contained in:
retuci0 committed 2026-01-11 11:12:44 +01:00
1 parent f794457538
commit 9afaa4e0c8
59 files changed
+1606 -218

No files matched your search

-1
View File
@@ -25,7 +25,6 @@ dependencies {
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
} }
loom { loom {
+16 -3
View File
@@ -1,15 +1,28 @@
# v2.5 # v2.6
## novedades ## novedades
- grupos de ajustes, para mayor orden, y arreglo de algún que otro bug
### adiciones ### adiciones
- layouts de teclado (en_us y es_es) (fuck azerty)
- comando `$config` para abrir el marco de ajustes de un módulo
- elemento del hud que muestra la rotación de la cámara del jugador
#### módulos nuevos: #### módulos nuevos:
- fuentes: cambia la tipografía que usa todo el juego a una custom
- escalones: actúa con la altura deseada como si se estuviesen subiendo escalones
- escalones inversos: escalones, pabajo
- partículas: genera particulas artificiales
### arreglos / mejoras ### arreglos / mejoras
- ahora puedes cambiar el grosor de las líneas del contorno de los bloques
- arreglada la compatibilidad con meteor client
- optimizado y mejorado superposición de luz y resaltado de bloques
- ahora, en los marcos de seleccion única, en el tooltip de cada opción dice "seleccionar" en lugar de "incluir"
- ahora precoz también tiene una opción de reducir el cooldown al minar bloques
## problemas reconocidos ## problemas reconocidos
- muchos, pero repórtalos en la página de problemas si quieres para que queden bien registrados - muchos, pero repórtalos en la página de problemas si quieres para que queden bien registrados
+1 -1
View File
@@ -8,6 +8,6 @@ loom_version=1.13-SNAPSHOT
fabric_api_version=0.140.2+1.21.11 fabric_api_version=0.140.2+1.21.11
mod_version=2.5 mod_version=2.6
maven_group=me.retucio maven_group=me.retucio
archives_base_name=sputnik archives_base_name=sputnik
@@ -29,6 +29,7 @@ import me.retucio.sputnik.ui.widgets.frames.SettingsFrame;
import me.retucio.sputnik.ui.widgets.Button; import me.retucio.sputnik.ui.widgets.Button;
import me.retucio.sputnik.util.*; import me.retucio.sputnik.util.*;
import me.retucio.sputnik.util.KeyUtil;
import me.retucio.sputnik.util.render.DrawUtil; import me.retucio.sputnik.util.render.DrawUtil;
import me.retucio.sputnik.util.render.RenderUtil; import me.retucio.sputnik.util.render.RenderUtil;
@@ -69,6 +70,11 @@ public class Sputnik implements ClientModInitializer {
private Screen prevScreen; private Screen prevScreen;
public static boolean settingsApplied = false; public static boolean settingsApplied = false;
static {
// puta vida
System.setProperty("java.awt.headless", "false");
}
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
mc = MinecraftClient.getInstance(); mc = MinecraftClient.getInstance();
@@ -32,6 +32,7 @@ public class CommandManager {
public static void registerCommands() { public static void registerCommands() {
addCommand(new BindCommand()); addCommand(new BindCommand());
addCommand(new ConfigCommand());
addCommand(new CopyScreenshotCommand()); addCommand(new CopyScreenshotCommand());
addCommand(new EnderChestCommand()); addCommand(new EnderChestCommand());
addCommand(new GarbageCleanerCommand()); addCommand(new GarbageCleanerCommand());
@@ -9,6 +9,7 @@ import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import me.retucio.sputnik.module.Module; import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.util.MiscUtil;
import net.minecraft.command.CommandSource; import net.minecraft.command.CommandSource;
import net.minecraft.text.Text; import net.minecraft.text.Text;
@@ -19,6 +20,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 UNKNOWN_MODULE = 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"));
@@ -35,7 +37,8 @@ public class ModuleArgumentType implements ArgumentType<Module> {
@Override @Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) { public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return CommandSource.suggestMatching(ModuleManager.INSTANCE.getModules().stream().map(module -> module.getName().replace(" ", "_")), builder); return CommandSource.suggestMatching(ModuleManager.INSTANCE.getModules()
.stream().map(module -> MiscUtil.removeAccentMarks(module.getName().replace(" ", "_"))), builder);
} }
@Override @Override
@@ -0,0 +1,67 @@
package me.retucio.sputnik.command.commands;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import me.retucio.sputnik.command.Command;
import me.retucio.sputnik.command.args.ModuleArgumentType;
import me.retucio.sputnik.module.setting.Setting;
import me.retucio.sputnik.ui.screen.ClickGUI;
import me.retucio.sputnik.ui.widgets.frames.SettingsFrame;
import net.minecraft.command.CommandSource;
import me.retucio.sputnik.module.Module;
public class ConfigCommand extends Command {
public ConfigCommand() {
super("config", "abre el marco de ajustes de un módulo", "ajustes", "settings");
}
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder
.then(argument("módulo", ModuleArgumentType.INSTANCE)
.executes(ctx -> {
mc.execute(() -> openSf(ctx));
return SUCCESS;
})
.then(argument("x", IntegerArgumentType.integer())
.then(argument("y", IntegerArgumentType.integer())
.executes(ctx -> {
mc.execute(() -> openSfWithPos(ctx,
ctx.getArgument("x", Integer.class),
ctx.getArgument("y", Integer.class)));
return SUCCESS;
})
)
)
);
}
private void openSf(CommandContext<CommandSource> ctx) {
Module module = ctx.getArgument("módulo", Module.class);
mc.setScreen(ClickGUI.INSTANCE);
if (ClickGUI.INSTANCE.isSettingsFrameOpen(module)) return;
SettingsFrame sf = ClickGUI.INSTANCE.getSfOfModule(module);
int x = mc.getWindow().getScaledWidth() / 2 - sf.getW() / 2;
int y = 67;
ClickGUI.INSTANCE.openSettingsFrame(module, x, y);
}
private void openSfWithPos(CommandContext<CommandSource> ctx, int x, int y) {
Module module = ctx.getArgument("módulo", Module.class);
mc.setScreen(ClickGUI.INSTANCE);
if (ClickGUI.INSTANCE.isSettingsFrameOpen(module)) {
SettingsFrame sf = ClickGUI.INSTANCE.getSfOfModule(module);
sf.setX(x);
sf.setY(y);
}
ClickGUI.INSTANCE.openSettingsFrame(module, x, y);
}
}
@@ -189,8 +189,15 @@ public class ConfigManager {
private static void applySettingsFrames() { private static void applySettingsFrames() {
config.settingsFrames.forEach((moduleName, position) -> { config.settingsFrames.forEach((moduleName, position) -> {
Module module = ModuleManager.INSTANCE.getModuleByName(moduleName); Module module = ModuleManager.INSTANCE.getModuleByName(moduleName);
if (module == null) {
Sputnik.LOGGER.warn("\"null\" como módulo (\"{}\")", moduleName);
return;
}
ClickGUI.INSTANCE.openSettingsFrame(module, position[0], position[1]); ClickGUI.INSTANCE.openSettingsFrame(module, position[0], position[1]);
}); });
Sputnik.LOGGER.info("estados de marcos de ajustes aplicados"); Sputnik.LOGGER.info("estados de marcos de ajustes aplicados");
} }
@@ -15,7 +15,8 @@ import java.util.Iterator;
import static me.retucio.sputnik.Sputnik.EVENT_BUS; import static me.retucio.sputnik.Sputnik.EVENT_BUS;
@Mixin(BossBarHud.class)
@Mixin(value = BossBarHud.class)
public abstract class BossBarHudMixin { public abstract class BossBarHudMixin {
@ModifyExpressionValue(method = "render", at = @At(value = "INVOKE", target = "Ljava/util/Collection;iterator()Ljava/util/Iterator;")) @ModifyExpressionValue(method = "render", at = @At(value = "INVOKE", target = "Ljava/util/Collection;iterator()Ljava/util/Iterator;"))
@@ -30,7 +31,8 @@ public abstract class BossBarHudMixin {
return event.getName(); return event.getName();
} }
@ModifyConstant(method = "render", constant = @Constant(intValue = 9, ordinal = 1)) // require = 0 para meteor
@ModifyConstant(method = "render", constant = @Constant(intValue = 9, ordinal = 1), require = 0)
public int modifySpacingConstant(int constant) { public int modifySpacingConstant(int constant) {
RenderBossbarEvent.BossSpacing event = EVENT_BUS.post(new RenderBossbarEvent.BossSpacing(constant)); RenderBossbarEvent.BossSpacing event = EVENT_BUS.post(new RenderBossbarEvent.BossSpacing(constant));
return event.getSpacing(); return event.getSpacing();
@@ -6,6 +6,7 @@ import me.retucio.sputnik.event.events.InteractEntityEvent;
import me.retucio.sputnik.event.events.PlaceBlockEvent; import me.retucio.sputnik.event.events.PlaceBlockEvent;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.camera.Freecam; import me.retucio.sputnik.module.modules.camera.Freecam;
import me.retucio.sputnik.module.modules.player.FastUse;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity;
@@ -16,6 +17,8 @@ import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
@@ -31,8 +34,8 @@ import static me.retucio.sputnik.Sputnik.mc;
@Mixin(ClientPlayerInteractionManager.class) @Mixin(ClientPlayerInteractionManager.class)
public abstract class ClientPlayerInteractionManagerMixin { public abstract class ClientPlayerInteractionManagerMixin {
@Shadow @Final @Shadow
private MinecraftClient client; private int blockBreakingCooldown;
@Unique @Unique
Freecam freecam; Freecam freecam;
@@ -69,4 +72,13 @@ public abstract class ClientPlayerInteractionManagerMixin {
AttackEntityEvent event = EVENT_BUS.post(new AttackEntityEvent(target)); AttackEntityEvent event = EVENT_BUS.post(new AttackEntityEvent(target));
if (event.isCancelled()) ci.cancel(); if (event.isCancelled()) ci.cancel();
} }
@Inject(method = "updateBlockBreakingProgress", at = @At(value = "FIELD", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;blockBreakingCooldown:I"))
private void modifyBlockBreakingCooldown(BlockPos pos, Direction direction, CallbackInfoReturnable<Boolean> cir) {
FastUse fastUse = ModuleManager.INSTANCE.getModuleByClass(FastUse.class);
if (!fastUse.isEnabled() || !fastUse.mining.isEnabled()) return;
blockBreakingCooldown = fastUse.miningCooldown.getIntValue();
}
} }
@@ -0,0 +1,38 @@
package me.retucio.sputnik.mixin;
import me.retucio.sputnik.Sputnik;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.render.Fonts;
import net.minecraft.client.font.FontManager;
import net.minecraft.client.font.FontStorage;
import net.minecraft.text.StyleSpriteSource;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(FontManager.class)
public abstract class FontManagerMixin {
@Shadow
public abstract FontStorage getStorageInternal(Identifier id);
@Inject(method = "getStorageInternal", at = @At("HEAD"), cancellable = true)
private void onGetStorageInternal(Identifier id, CallbackInfoReturnable<FontStorage> cir) {
Fonts fonts = ModuleManager.INSTANCE.getModuleByClass(Fonts.class);
if (!fonts.isEnabled()) return;
if (id.equals(StyleSpriteSource.Font.DEFAULT.id())) {
cir.setReturnValue(getStorageInternal(
Identifier.of(
Sputnik.MOD_ID,
fonts.font.getValue())
));
}
}
}
@@ -2,6 +2,7 @@ package me.retucio.sputnik.mixin;
import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.player.ElytraBounce; import me.retucio.sputnik.module.modules.player.ElytraBounce;
import me.retucio.sputnik.module.modules.player.Step;
import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundCategory;
@@ -12,6 +13,7 @@ import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import static me.retucio.sputnik.Sputnik.mc; import static me.retucio.sputnik.Sputnik.mc;
@@ -26,7 +28,7 @@ public abstract class LivingEntityMixin {
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
@Inject(method = "tickMovement", at = @At("TAIL")) @Inject(method = "tickMovement", at = @At("TAIL"))
public void recastIfLanded(CallbackInfo ci) { private void recastIfLanded(CallbackInfo ci) {
ElytraBounce bounce = ModuleManager.INSTANCE.getModuleByClass(ElytraBounce.class); ElytraBounce bounce = ModuleManager.INSTANCE.getModuleByClass(ElytraBounce.class);
if (!((Object) this instanceof ClientPlayerEntity) if (!((Object) this instanceof ClientPlayerEntity)
@@ -47,4 +49,11 @@ public abstract class LivingEntityMixin {
prevElytra = elytra; prevElytra = elytra;
} }
@Inject(method = "getStepHeight", at = @At("RETURN"), cancellable = true)
private void step(CallbackInfoReturnable<Float> cir) {
Step step = ModuleManager.INSTANCE.getModuleByClass(Step.class);
if (step.isEnabled()) cir.setReturnValue(step.height.getFloatValue());
}
} }
@@ -14,9 +14,9 @@ import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@@ -27,6 +27,12 @@ public abstract class MinecraftClientMixin {
@Shadow @Shadow
private int itemUseCooldown; private int itemUseCooldown;
@Shadow
public int attackCooldown;
// eventos
@Inject(method = "tick", at = @At("HEAD")) @Inject(method = "tick", at = @At("HEAD"))
private void onTickPre(CallbackInfo ci) { private void onTickPre(CallbackInfo ci) {
Sputnik.INSTANCE.onTick(); Sputnik.INSTANCE.onTick();
@@ -49,9 +55,10 @@ public abstract class MinecraftClientMixin {
if (event.isCancelled()) ci.cancel(); if (event.isCancelled()) ci.cancel();
} }
@Inject(method = "isTelemetryEnabledByApi", at = @At("RETURN"), cancellable = true) @Inject(method = "joinWorld", at = @At("HEAD"), cancellable = true)
private void disableMicropenisTelemetryShi(CallbackInfoReturnable<Boolean> cir) { private void onJoinWorld(ClientWorld world, CallbackInfo ci) {
cir.setReturnValue(false); JoinWorldEvent event = Sputnik.EVENT_BUS.post(new JoinWorldEvent(world));
if (event.isCancelled()) ci.cancel();
} }
@Inject(method = "doItemUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;getStackInHand(Lnet/minecraft/util/Hand;)Lnet/minecraft/item/ItemStack;"), cancellable = true) @Inject(method = "doItemUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;getStackInHand(Lnet/minecraft/util/Hand;)Lnet/minecraft/item/ItemStack;"), cancellable = true)
@@ -61,16 +68,21 @@ public abstract class MinecraftClientMixin {
if (event.isCancelled()) ci.cancel(); if (event.isCancelled()) ci.cancel();
} }
// telemetría
@Inject(method = "isTelemetryEnabledByApi", at = @At("RETURN"), cancellable = true)
private void disableMicropenisTelemetryShi(CallbackInfoReturnable<Boolean> cir) {
cir.setReturnValue(false);
}
// precoz
@Inject(method = "doItemUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isItemEnabled(Lnet/minecraft/resource/featuretoggle/FeatureSet;)Z")) @Inject(method = "doItemUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isItemEnabled(Lnet/minecraft/resource/featuretoggle/FeatureSet;)Z"))
private void modifyItemUseCooldown(CallbackInfo ci, @Local ItemStack stack) { private void modifyItemUseCooldown(CallbackInfo ci, @Local ItemStack stack) {
FastUse fastUse = ModuleManager.INSTANCE.getModuleByClass(FastUse.class); FastUse fastUse = ModuleManager.INSTANCE.getModuleByClass(FastUse.class);
if (!fastUse.isEnabled()) return; if (!fastUse.isEnabled()) return;
itemUseCooldown = fastUse.getCooldown(stack); itemUseCooldown = fastUse.getCooldown(stack);
} }
@Inject(method = "joinWorld", at = @At("HEAD"), cancellable = true)
private void onJoinWorld(ClientWorld world, CallbackInfo ci) {
JoinWorldEvent event = Sputnik.EVENT_BUS.post(new JoinWorldEvent(world));
if (event.isCancelled()) ci.cancel();
}
} }
@@ -1,5 +1,6 @@
package me.retucio.sputnik.mixin; package me.retucio.sputnik.mixin;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.buffers.GpuBufferSlice; import com.mojang.blaze3d.buffers.GpuBufferSlice;
import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.Sputnik;
@@ -18,13 +19,12 @@ import net.minecraft.util.profiler.Profiler;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Vector4f; import org.joml.Vector4f;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.lang.annotation.Target;
import static me.retucio.sputnik.Sputnik.EVENT_BUS; import static me.retucio.sputnik.Sputnik.EVENT_BUS;
import static me.retucio.sputnik.Sputnik.mc; import static me.retucio.sputnik.Sputnik.mc;
@@ -66,11 +66,18 @@ public abstract class WorldRendererMixin {
if (!noRender.blindnessEffect.isEnabled() || !noRender.darknessEffect.isEnabled()) cir.setReturnValue(null); if (!noRender.blindnessEffect.isEnabled() || !noRender.darknessEffect.isEnabled()) cir.setReturnValue(null);
} }
@Redirect(method = "renderTargetBlockOutline", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/ColorHelper;toAlpha(I)I"))
private int modifyBlockOutlineColor(int alpha) {
BlockOutline outline = ModuleManager.INSTANCE.getModuleByClass(BlockOutline.class);
if (!outline.isEnabled()) return ColorHelper.toAlpha(alpha);
@ModifyArg(method = "renderTargetBlockOutline", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;drawBlockOutline(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;DDDLnet/minecraft/client/render/state/OutlineRenderState;IF)V"))
private int modifyBlockOutlineColor(int original) {
BlockOutline outline = ModuleManager.INSTANCE.getModuleByClass(BlockOutline.class);
if (!outline.isEnabled()) return original;
return outline.color.getRGB(); return outline.color.getRGB();
} }
@ModifyArg(method = "renderTargetBlockOutline", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;drawBlockOutline(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;DDDLnet/minecraft/client/render/state/OutlineRenderState;IF)V"))
private float modifyBlockOutlineLineWidth(float original) {
BlockOutline outline = ModuleManager.INSTANCE.getModuleByClass(BlockOutline.class);
if (!outline.isEnabled()) return original;
return outline.lineWidth.getFloatValue();
}
} }
@@ -10,6 +10,7 @@ import me.retucio.sputnik.module.modules.player.*;
import me.retucio.sputnik.module.modules.render.*; import me.retucio.sputnik.module.modules.render.*;
import me.retucio.sputnik.module.modules.render.CritsPlus; import me.retucio.sputnik.module.modules.render.CritsPlus;
import me.retucio.sputnik.module.modules.world.*; import me.retucio.sputnik.module.modules.world.*;
import me.retucio.sputnik.util.MiscUtil;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
@@ -47,6 +48,7 @@ public class ModuleManager {
modules.add(new ElytraBounce()); modules.add(new ElytraBounce());
modules.add(new FakePlayer()); modules.add(new FakePlayer());
modules.add(new FastUse()); modules.add(new FastUse());
modules.add(new Fonts());
modules.add(new Freecam()); modules.add(new Freecam());
modules.add(new Freelook()); modules.add(new Freelook());
modules.add(new Fullbright()); modules.add(new Fullbright());
@@ -62,15 +64,18 @@ public class ModuleManager {
modules.add(new NoRender()); modules.add(new NoRender());
modules.add(new Offhand()); modules.add(new Offhand());
modules.add(new PacketDelay()); modules.add(new PacketDelay());
modules.add(new Particles());
modules.add(new PerspectivePlus()); modules.add(new PerspectivePlus());
modules.add(new PortalGUI()); modules.add(new PortalGUI());
modules.add(new Racist()); modules.add(new Racist());
modules.add(new Reconnect()); modules.add(new Reconnect());
modules.add(new ReverseStep());
modules.add(new Rotations()); modules.add(new Rotations());
modules.add(new RPackBypass()); modules.add(new RPackBypass());
modules.add(new SafeWalk()); modules.add(new SafeWalk());
modules.add(new ScreenshotPlus()); modules.add(new ScreenshotPlus());
modules.add(new ShulkerPeek()); modules.add(new ShulkerPeek());
modules.add(new Step());
modules.add(new TimeChanger()); modules.add(new TimeChanger());
modules.add(new UIMove()); modules.add(new UIMove());
modules.add(new WarnLowDurability()); modules.add(new WarnLowDurability());
@@ -105,7 +110,8 @@ public class ModuleManager {
public Module getModuleByName(String name) { public Module getModuleByName(String name) {
for (Module module : modules) for (Module module : modules)
if (module.getName().equalsIgnoreCase(name)) return module; if (MiscUtil.removeAccentMarks(module.getName()).equalsIgnoreCase(name))
return module;
return null; return null;
} }
@@ -4,21 +4,41 @@ import me.retucio.sputnik.event.events.RenderBossbarEvent;
import me.retucio.sputnik.event.SubscribeEvent; import me.retucio.sputnik.event.SubscribeEvent;
import me.retucio.sputnik.module.Category; import me.retucio.sputnik.module.Category;
import me.retucio.sputnik.module.Module; import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.SettingGroup;
import me.retucio.sputnik.module.setting.settings.BooleanSetting; import me.retucio.sputnik.module.setting.settings.BooleanSetting;
import me.retucio.sputnik.module.setting.settings.ColorSetting;
import me.retucio.sputnik.module.setting.settings.EnumSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting; import me.retucio.sputnik.module.setting.settings.NumberSetting;
import net.minecraft.client.gui.hud.ClientBossBar; import net.minecraft.client.gui.hud.ClientBossBar;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import java.awt.*;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
public class BossbarStack extends Module { public class BossbarStack extends Module {
public BooleanSetting stackBars = sgGeneral.add(new BooleanSetting("apilar barras", "apilar las bossbars", true)); public BooleanSetting stackBars = sgGeneral.add(new BooleanSetting(
public BooleanSetting hideNames = sgGeneral.add(new BooleanSetting("esconder nombres", "no renderiza el nombre de los bosses", false)); "apilar barras", "apilar las bossbars",
public NumberSetting spaceReduction = sgGeneral.add(new NumberSetting("reducción de espacio", "cuánto reducir el espacio entre bossbars", true
0, 0, 10, 0.1)); ));
public BooleanSetting hideNames = sgGeneral.add(new BooleanSetting(
"esconder nombres", "no renderiza el nombre de los bosses",
false
));
public NumberSetting spaceReduction = sgGeneral.add(new NumberSetting(
"reducción de espacio",
"cuánto reducir el espacio entre bossbars",
0,
0,
10,
0.1
));
private final Map<ClientBossBar, Integer> bossBarMap = new WeakHashMap<>(); private final Map<ClientBossBar, Integer> bossBarMap = new WeakHashMap<>();
@@ -6,19 +6,60 @@ import me.retucio.sputnik.module.setting.settings.BooleanSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting; import me.retucio.sputnik.module.setting.settings.NumberSetting;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import org.lwjgl.glfw.GLFW;
public class FastUse extends Module { public class FastUse extends Module {
public BooleanSetting items = sgGeneral.add(new BooleanSetting("items", "usar items precozmente", true)); public BooleanSetting items = sgGeneral.add(new BooleanSetting(
public NumberSetting itemCooldown = sgGeneral.add(new NumberSetting("cooldown de items", "cooldown para usar items en ticks", 0, 0, 4, 1)); "items",
"usar items precozmente",
true
));
public NumberSetting itemCooldown = sgGeneral.add(new NumberSetting(
"cooldown de items",
"cooldown para usar items en ticks",
0,
0,
4,
1
));
public BooleanSetting blocks = sgGeneral.add(new BooleanSetting(
"bloques",
"colocar bloques precozmente",
true
));
public NumberSetting blockCooldown = sgGeneral.add(new NumberSetting(
"cooldown de bloques",
"cooldown para colocar bloques en ticks",
0,
0,
4,
1
));
public BooleanSetting mining = sgGeneral.add(new BooleanSetting(
"minar",
"minar precozmente",
true
));
public NumberSetting miningCooldown = sgGeneral.add(new NumberSetting(
"cooldown al minar",
"cooldown entre rotura de bloques en ticks",
5, 0, 20, 1
));
public BooleanSetting blocks = sgGeneral.add(new BooleanSetting("bloques", "colocar bloques precozmente", true));
public NumberSetting blockCooldown = sgGeneral.add(new NumberSetting("cooldown de bloques", "cooldown para colocar bloques en ticks", 0, 0, 4, 1));
public FastUse() { public FastUse() {
super("precoz", super("precoz",
"hace cosas como usar items o colocar bloques más rápido", "hace cosas como usar items o colocar bloques más rápido",
Category.PLAYER); Category.PLAYER,
GLFW.GLFW_KEY_F6);
items.onUpdate(v -> itemCooldown.setVisible(v)); items.onUpdate(v -> itemCooldown.setVisible(v));
blocks.onUpdate(v -> blockCooldown.setVisible(v)); blocks.onUpdate(v -> blockCooldown.setVisible(v));
@@ -0,0 +1,85 @@
package me.retucio.sputnik.module.modules.player;
import me.retucio.sputnik.module.Category;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.BooleanSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting;
import me.retucio.sputnik.util.ChatUtil;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.RaycastContext;
public class ReverseStep extends Module {
public NumberSetting height = sgGeneral.add(new NumberSetting(
"altura", "altura máxima a bajar",
1, 0, 20, 0.1
));
public NumberSetting velocity = sgGeneral.add(new NumberSetting(
"velocidad", "velocidad a la que caer en bps",
1, 0, 10, 0.1
));
public BooleanSetting jumping = sgGeneral.add(new BooleanSetting(
"saltar", "permitir saltar",
true
));
public BooleanSetting disableInWater = sgGeneral.add(new BooleanSetting(
"desactivar en agua", ".",
true
));
public ReverseStep() {
super("escalones inversos",
"escalones pero pabajo",
Category.PLAYER
);
}
@Override
public void onTick() {
if (mc.player == null || mc.world == null) return;
if (!mc.player.isOnGround()) {
if (jumping.isEnabled()) return;
}
if ((mc.player.isTouchingWater() || mc.player.isInLava())) {
if (disableInWater.isEnabled()) return;
}
double dropHeight = getHeight();
if (dropHeight > 0 && dropHeight <= height.getValue()) {
mc.player.addVelocity(0, -height.getValue() + mc.player.getVelocity().getY(), 0);
}
}
private double getHeight() {
Vec3d start = new Vec3d(mc.player.getX(), mc.player.getY() - 0.1, mc.player.getZ());
Vec3d end = new Vec3d(mc.player.getX(), mc.player.getY() - height.getValue() - 1, mc.player.getZ());
RaycastContext context = new RaycastContext(
start, end,
RaycastContext.ShapeType.COLLIDER,
RaycastContext.FluidHandling.ANY,
mc.player
);
HitResult hit = mc.world.raycast(context);
if (hit != null && hit.getType() == HitResult.Type.BLOCK) {
BlockPos hitPos = new BlockPos(
(int) hit.getPos().x,
(int) hit.getPos().y,
(int) hit.getPos().z
);
return mc.player.getY() - (hitPos.getY());
}
return -1;
}
}
@@ -0,0 +1,17 @@
package me.retucio.sputnik.module.modules.player;
import me.retucio.sputnik.module.Category;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.NumberSetting;
public class Step extends Module {
public NumberSetting height = sgGeneral.add(new NumberSetting(
"altura", "altura máxima a subir",
1, 0, 3, 0.1
));
public Step() {
super("escalones", "hace que el bloque de subida de escalón sea la distancia deseada", Category.PLAYER);
}
}
@@ -14,30 +14,96 @@ import me.retucio.sputnik.util.Lists;
import me.retucio.sputnik.util.render.RenderUtil; import me.retucio.sputnik.util.render.RenderUtil;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3i; import net.minecraft.util.math.Vec3d;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import java.awt.*; import java.awt.*;
import java.util.ArrayList; import java.util.*;
import java.util.List; import java.util.concurrent.ConcurrentHashMap;
public class BlockESP extends Module { public class BlockESP extends Module {
SettingGroup sgSearch = addSg(new SettingGroup("búsqueda", true));
SettingGroup sgOulines = addSg(new SettingGroup("contorno", true)); SettingGroup sgOulines = addSg(new SettingGroup("contorno", true));
SettingGroup sgFilling = addSg(new SettingGroup("relleno", true)); SettingGroup sgFilling = addSg(new SettingGroup("relleno", true));
public ListSetting<Block> blocks = sgGeneral.add(new ListSetting<>("bloques", "bloques a resaltar",
Lists.blockList, Lists.allFalse(Lists.blockList), Lists.blockNames));
public NumberSetting radius = sgGeneral.add(new NumberSetting("radio", "radio de búsqueda de bloques",
24, 2, 128, 1));
public BooleanSetting outlines = sgOulines.add(new BooleanSetting("contorno", "renderizar contorno de bloques", true)); // bloques
public ColorSetting outlineColor = sgOulines.add(new ColorSetting("color del contorno", "color del contorno", Colors.CELESTE, false));
public NumberSetting lineWidth = sgOulines.add(new NumberSetting("grosor del contorno", "grosor de las líneas del contorno", 1, 1, 5, 0.2));
public BooleanSetting fillings = sgFilling.add(new BooleanSetting("relleno", "renderizar relleno de bloques", true)); public ListSetting<Block> blocks = sgGeneral.add(new ListSetting<>(
public ColorSetting fillingColor = sgFilling.add(new ColorSetting("color del relleno", "color del relleno", new Color(57, 177, 215, 67), false)); "bloques", "bloques a resaltar",
Lists.blockList,
Lists.allFalse(Lists.blockList),
Lists.blockNames
));
public NumberSetting maxBlocks = sgGeneral.add(new NumberSetting(
"máx. bloques", "número máximo de bloques a resaltar",
500, 1, 2000, 1
));
// búsqueda
public NumberSetting radius = sgSearch.add(new NumberSetting(
"radio", "radio de búsqueda de bloques",
24, 2, 128, 1
));
public NumberSetting searchInterval = sgSearch.add(new NumberSetting(
"intervalo", "intervalo de búsqueda en ticks",
10, 1, 40, 1
));
public BooleanSetting asyncSearch = sgSearch.add(new BooleanSetting(
"búsqueda asíncrona", "busca los bloques en un hilo separado (optimización)",
true
));
public NumberSetting distanceToMove = sgSearch.add(new NumberSetting(
"distancia a moverse", "distancia a moverse antes de actualizar los bloques",
3, 0, 10, 0.1
));
// contorno
public BooleanSetting outlines = sgOulines.add(new BooleanSetting(
"contorno", "renderizar contorno de bloques",
true
));
public ColorSetting outlineColor = sgOulines.add(new ColorSetting(
"color del contorno", "color del contorno",
Colors.CELESTE, false
));
public NumberSetting lineWidth = sgOulines.add(new NumberSetting(
"grosor del contorno", "grosor de las líneas del contorno",
1, 1, 5, 0.2
));
// relleno
public BooleanSetting fillings = sgFilling.add(new BooleanSetting(
"relleno", "renderizar relleno de bloques",
true
));
public ColorSetting fillingColor = sgFilling.add(new ColorSetting(
"color del relleno", "color del relleno",
new Color(57, 177, 215, 67), false
));
// caché
private final Map<BlockPos, VoxelShape> cachedBlocks = new ConcurrentHashMap<>();
private final Set<Block> enabledBlocks = Collections.newSetFromMap(new ConcurrentHashMap<>());
private BlockPos lastPlayerPos = null;
private int lastRadius = -1;
private int searchTimer = 0;
private boolean isSearching = false;
public BlockESP() { public BlockESP() {
@@ -45,61 +111,166 @@ public class BlockESP extends Module {
"te muestra la posición de cierto tipo de bloque renderizando una caja con su forma", "te muestra la posición de cierto tipo de bloque renderizando una caja con su forma",
Category.RENDER); Category.RENDER);
blocks.onUpdate(v ->
updateEnabledBlocks()
);
outlines.onUpdate(v -> { outlines.onUpdate(v -> {
outlineColor.setVisible(v); outlineColor.setVisible(v);
lineWidth.setVisible(v); lineWidth.setVisible(v);
}); });
fillings.onUpdate(v -> fillingColor.setVisible(v)); fillings.onUpdate(v ->
fillingColor.setVisible(v)
);
}
@Override
public void onEnable() {
cachedBlocks.clear();
enabledBlocks.clear();
updateEnabledBlocks();
searchTimer = 0;
super.onEnable();
}
@Override
public void onDisable() {
cachedBlocks.clear();
enabledBlocks.clear();
super.onDisable();
} }
@SubscribeEvent @SubscribeEvent
public void onRenderWorld(Render3DEvent event) { public void onRenderWorld(Render3DEvent event) {
if (mc.player == null || mc.world == null) return; if (mc.player == null || mc.world == null) return;
for (Block block : blocks.getOptions()) { // aplicar el intervalo
if (blocks.isEnabled(block)) { if (searchTimer++ >= searchInterval.getIntValue()) {
List<BlockPos> blocksToHighlight = getNearbyBlocks(block); searchTimer = 0;
for (BlockPos pos : blocksToHighlight) { searchBlocks();
if (outlines.isEnabled()) { }
RenderUtil.drawVoxelShapeOutline(
event.getMatrices(), Vec3d cameraPos = mc.gameRenderer.getCamera().getCameraPos();
getVoxelShape(pos),
pos, int rendered = 0;
outlineColor.getColor(), Iterator<Map.Entry<BlockPos, VoxelShape>> iterator = cachedBlocks.entrySet().iterator();
lineWidth.getFloatValue(),
false); while (iterator.hasNext() && rendered < maxBlocks.getIntValue()) {
} Map.Entry<BlockPos, VoxelShape> entry = iterator.next();
if (fillings.isEnabled()) { BlockPos pos = entry.getKey();
RenderUtil.drawVoxelShapeFilled( VoxelShape shape = entry.getValue();
event.getMatrices(),
getVoxelShape(pos), if (!isValidBlock(pos)) {
pos, iterator.remove();
fillingColor.getColor(), continue;
false); }
// validación de distancia
double distance = Math.sqrt(pos.getSquaredDistance(cameraPos));
if (distance > radius.getIntValue()) {
iterator.remove();
continue;
}
// renderizado
if (outlines.isEnabled()) {
Color color = outlineColor.getColor();
RenderUtil.drawVoxelShapeOutline(
event.getMatrices(),
shape,
pos,
color,
lineWidth.getFloatValue(),
false);
}
if (fillings.isEnabled()) {
Color color = fillingColor.getColor();
RenderUtil.drawVoxelShapeFilled(
event.getMatrices(),
shape,
pos,
color,
false);
}
rendered++;
}
}
private void updateEnabledBlocks() {
enabledBlocks.clear();
enabledBlocks.addAll(blocks.getEnabledOptions());
}
private void searchBlocks() {
if (mc.player == null || mc.world == null || enabledBlocks.isEmpty()) return;
BlockPos playerPos = mc.player.getBlockPos();
int currentRadius = radius.getIntValue();
// si el jugador no se ha movido mucho, omitir la búsqueda
if (lastPlayerPos != null && lastRadius == currentRadius &&
playerPos.getSquaredDistance(lastPlayerPos) < (distanceToMove.getValue() * distanceToMove.getIntValue())) {
return;
}
lastPlayerPos = playerPos;
lastRadius = currentRadius;
// multithreading
if (asyncSearch.isEnabled()) {
if (isSearching) return;
isSearching = true;
new Thread(() -> {
try {
performBlockSearch(playerPos, currentRadius);
} finally {
isSearching = false;
}
}, "sputnik-blockESP-search").start();
} else {
performBlockSearch(playerPos, currentRadius);
}
}
// buscar los bloques
private void performBlockSearch(BlockPos center, int radius) {
Map<BlockPos, VoxelShape> newBlocks = new HashMap<>();
int radiusSq = radius * radius;
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
for (int z = -radius; z <= radius; z++) {
if (x*x + y*y + z*z > radiusSq) continue;
BlockPos pos = center.add(x, y, z);
Block block = mc.world.getBlockState(pos).getBlock();
if (enabledBlocks.contains(block)) {
VoxelShape shape = mc.world.getBlockState(pos).getOutlineShape(mc.world, pos);
if (!shape.isEmpty()) {
newBlocks.put(pos.toImmutable(), shape);
}
} }
} }
} }
} }
// actualizar caché
cachedBlocks.keySet().retainAll(newBlocks.keySet());
cachedBlocks.putAll(newBlocks);
} }
private List<BlockPos> getNearbyBlocks(Block block) { // verificar que el bloque sea válido
List<BlockPos> blockPosList = new ArrayList<>(); @SuppressWarnings("deprecation")
private boolean isValidBlock(BlockPos pos) {
for (int i = -radius.getIntValue(); i < radius.getIntValue(); i++) { if (mc.world == null || !mc.world.isChunkLoaded(pos)) return false;
for (int j = -radius.getIntValue(); j < radius.getIntValue(); j++) { Block block = mc.world.getBlockState(pos).getBlock();
for (int k = -radius.getIntValue(); k < radius.getIntValue(); k++) { return enabledBlocks.contains(block);
BlockPos pos = mc.player.getBlockPos().add(new Vec3i(i, j, k));
if (mc.world.getBlockState(pos).getBlock() == block) {
blockPosList.add(pos);
}
}
}
}
return blockPosList;
}
private VoxelShape getVoxelShape(BlockPos pos) {
return mc.world.getBlockState(pos).getOutlineShape(mc.world, pos);
} }
} }
@@ -3,6 +3,8 @@ package me.retucio.sputnik.module.modules.render;
import me.retucio.sputnik.module.Category; import me.retucio.sputnik.module.Category;
import me.retucio.sputnik.module.Module; import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.ColorSetting; import me.retucio.sputnik.module.setting.settings.ColorSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting;
import me.retucio.sputnik.util.Colors;
import java.awt.*; import java.awt.*;
@@ -12,8 +14,21 @@ import java.awt.*;
public class BlockOutline extends Module { public class BlockOutline extends Module {
public ColorSetting color = sgGeneral.add(new ColorSetting("color", "color del contorno de los bloques", public ColorSetting color = sgGeneral.add(new ColorSetting(
new Color(0, 0, 0, 102), true)); "color",
"color del contorno de los bloques",
Colors.mainColor,
true)
);
public NumberSetting lineWidth = sgGeneral.add(new NumberSetting(
"grosor",
"grosor de las líneas",
0.5,
0.1,
10,
0.1
));
public BlockOutline() { public BlockOutline() {
super("contorno", super("contorno",
@@ -6,6 +6,7 @@ import me.retucio.sputnik.module.setting.SettingGroup;
import me.retucio.sputnik.module.setting.settings.BooleanSetting; import me.retucio.sputnik.module.setting.settings.BooleanSetting;
import me.retucio.sputnik.module.setting.settings.ColorSetting; import me.retucio.sputnik.module.setting.settings.ColorSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting; import me.retucio.sputnik.module.setting.settings.NumberSetting;
import me.retucio.sputnik.util.Colors;
import java.awt.*; import java.awt.*;
@@ -19,7 +20,7 @@ public class CritsPlus extends Module {
SettingGroup sgAppearance = addSg(new SettingGroup("aspecto", true)); SettingGroup sgAppearance = addSg(new SettingGroup("aspecto", true));
SettingGroup sgBehaviour = addSg(new SettingGroup("funcionamiento", true)); SettingGroup sgBehaviour = addSg(new SettingGroup("funcionamiento", true));
public ColorSetting color = sgAppearance.add(new ColorSetting("color", "color de las partículas", new Color(0, 0, 255, 255), false)); public ColorSetting color = sgAppearance.add(new ColorSetting("color", "color de las partículas", Colors.mainColor, false));
public NumberSetting scale = sgAppearance.add(new NumberSetting("escala", "tamaño", 1, 0, 2, 0.05)); public NumberSetting scale = sgAppearance.add(new NumberSetting("escala", "tamaño", 1, 0, 2, 0.05));
public NumberSetting multiplier = sgAppearance.add(new NumberSetting("múltiplo", "número por el que multiplicar la cantidad de partículas generadas", 1, 0, 10, 0.1)); public NumberSetting multiplier = sgAppearance.add(new NumberSetting("múltiplo", "número por el que multiplicar la cantidad de partículas generadas", 1, 0, 10, 0.1));
@@ -0,0 +1,45 @@
package me.retucio.sputnik.module.modules.render;
import me.retucio.sputnik.module.Category;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.BooleanSetting;
import me.retucio.sputnik.module.setting.settings.OptionSetting;
import me.retucio.sputnik.util.Lists;
public class Fonts extends Module {
public OptionSetting<String> font = sgGeneral.add(new OptionSetting<>(
"fuente", "fuente a emplear",
Lists.fontList, "ubuntu")
);
public BooleanSetting reload = sgGeneral.add(new BooleanSetting(
"recargar", "recargar automáticamente",
false
));
public Fonts() {
super("fuentes",
"modifica el tipo de letra utilizado al renderizar texto",
Category.RENDER);
font.onUpdate(v -> reload());
}
@Override
public void onEnable() {
reload();
}
@Override
public void onDisable() {
reload();
}
private void reload() {
if (mc.getResourceManager() != null && reload.isEnabled()) {
mc.reloadResources();
}
}
}
@@ -4,6 +4,7 @@ import me.retucio.sputnik.module.Category;
import me.retucio.sputnik.module.Module; import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.settings.BooleanSetting; import me.retucio.sputnik.module.setting.settings.BooleanSetting;
import me.retucio.sputnik.module.setting.settings.EnumSetting; import me.retucio.sputnik.module.setting.settings.EnumSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting;
import me.retucio.sputnik.util.render.GlintRenderLayer; import me.retucio.sputnik.util.render.GlintRenderLayer;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderLayers; import net.minecraft.client.render.RenderLayers;
@@ -17,9 +18,24 @@ import net.minecraft.util.DyeColor;
public class GlintPlus extends Module { public class GlintPlus extends Module {
public BooleanSetting items = sgGeneral.add(new BooleanSetting("items", "modificar el destello de encantamiento de los items", true)); public BooleanSetting items = sgGeneral.add(new BooleanSetting(
public BooleanSetting armor = sgGeneral.add(new BooleanSetting("armadura", "modificar el destello de encantamiento de la armadura", true)); "items",
public EnumSetting<GlintColors> glintColor = sgGeneral.add(new EnumSetting<>("colores", "color del glint", GlintColors.class, GlintColors.PURPLE)); "modificar el destello de encantamiento de los items",
true
));
public BooleanSetting armor = sgGeneral.add(new BooleanSetting(
"armadura",
"modificar el destello de encantamiento de la armadura",
true
));
public EnumSetting<GlintColors> glintColor = sgGeneral.add(new EnumSetting<>(
"colores",
"color del glint",
GlintColors.class,
GlintColors.PURPLE
));
public GlintPlus() { public GlintPlus() {
super("destello de enchants.", super("destello de enchants.",
@@ -0,0 +1,189 @@
package me.retucio.sputnik.module.modules.render;
import me.retucio.sputnik.module.Category;
import me.retucio.sputnik.module.Module;
import me.retucio.sputnik.module.setting.SettingGroup;
import me.retucio.sputnik.module.setting.settings.ListSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting;
import me.retucio.sputnik.util.Lists;
import net.minecraft.particle.SimpleParticleType;
import net.minecraft.registry.Registries;
import net.minecraft.text.Text;
import net.minecraft.util.math.Vec3d;
import java.util.List;
import java.util.Map;
import java.util.Random;
public class Particles extends Module {
SettingGroup sgDispersion = addSg(new SettingGroup("dispersión", true));
SettingGroup sgOffset = addSg(new SettingGroup("desplazamiento", true));
List<SimpleParticleType> particleList = Lists.particleList.stream()
.filter(particleType -> particleType instanceof SimpleParticleType)
.map(particleType -> (SimpleParticleType) particleType)
.toList();
Map<SimpleParticleType, String> particleNames = Lists.getMapOfLists(particleList,
particleList.stream().map(particle -> Text.translatable(
Registries.PARTICLE_TYPE.getId(particle).toShortTranslationKey()).getString()
).toList());
public ListSetting<SimpleParticleType> particles = sgGeneral.add(new ListSetting<>(
"partículas",
"partículas a generar",
particleList,
Lists.allFalse(particleList),
particleNames
));
public NumberSetting spawnInterval = sgGeneral.add(new NumberSetting(
"intervalo",
"intervalo entre generaciones (en ticks)",
4, 1, 100, 1
));
public NumberSetting particleCount = sgGeneral.add(new NumberSetting(
"cantidad",
"partículas por generación",
10, 1, 100, 1
));
public NumberSetting velocity = sgDispersion.add(new NumberSetting(
"velocidad",
"multiplicador de la velocidad de dispersión",
0.2, 0, 2, 0.05
));
public NumberSetting spreadX = sgDispersion.add(new NumberSetting(
"dispersión X",
"dispersión horizontal de posición",
0.5, 0, 5, 0.1
));
public NumberSetting spreadY = sgDispersion.add(new NumberSetting(
"dispersión Y",
"dispersión vertical de posición",
0.5, 0, 5, 0.1
));
public NumberSetting spreadZ = sgDispersion.add(new NumberSetting(
"dispersión Z",
"dispersión de profundidad de posición",
0.5, 0, 5, 0.1
));
public NumberSetting velocityRandomness = sgDispersion.add(new NumberSetting(
"aleatoriedad velocidad",
"variación aleatoria en la velocidad",
0.3, 0, 1, 0.05
));
public NumberSetting positionRandomness = sgDispersion.add(new NumberSetting(
"aleatoriedad posición",
"variación aleatoria en la posición",
0.2, 0, 1, 0.05
));
public NumberSetting offsetX = sgOffset.add(new NumberSetting(
"desplazamiento pos. X",
"desplazamiento en el eje X respecto al jugador",
0, -1, 1, 0.01
));
public NumberSetting offsetY = sgOffset.add(new NumberSetting(
"desplazamiento pos. Y",
"desplazamiento en el eje Y respecto al jugador",
0, -1, 1, 0.01
));
public NumberSetting offsetZ = sgOffset.add(new NumberSetting(
"desplazamiento pos. Z",
"desplazamiento en el eje Z respecto al jugador",
-0.8, -1, 1, 0.01
));
private final Random random = new Random();
private int tickCounter = 0;
public Particles() {
super("partículas", "emite particulas artificiales", Category.RENDER);
}
@Override
public void onEnable() {
tickCounter = 0;
super.onEnable();
}
@Override
public void onTick() {
if (mc.player == null || mc.world == null) return;
tickCounter++;
if (tickCounter < spawnInterval.getIntValue()) {
return;
}
tickCounter = 0;
List<SimpleParticleType> enabledParticles = particles.getEnabledOptions();
if (enabledParticles.isEmpty()) return;
Vec3d basePos = new Vec3d(
mc.player.getX(),
mc.player.getY() + mc.player.getEyeHeight(mc.player.getPose()),
mc.player.getZ()
);
for (int i = 0; i < particleCount.getIntValue(); i++) {
SimpleParticleType particle = enabledParticles.get(
random.nextInt(enabledParticles.size())
);
Vec3d pos = getPos(basePos);
Vec3d vel = getVel();
double yaw = Math.toRadians(mc.player.getYaw());
double offX = offsetX.getValue() * Math.cos(yaw) - offsetZ.getValue() * Math.sin(yaw);
double offZ = offsetX.getValue() * Math.sin(yaw) + offsetZ.getValue() * Math.cos(yaw);
mc.world.addParticleClient(
particle,
pos.x + offX,
pos.y + offsetY.getValue(),
pos.z + offZ,
vel.x, vel.y, vel.z
);
}
}
private Vec3d getPos(Vec3d basePos) {
double randX = (random.nextDouble() - 0.5) * 2 * positionRandomness.getValue();
double randY = (random.nextDouble() - 0.5) * 2 * positionRandomness.getValue();
double randZ = (random.nextDouble() - 0.5) * 2 * positionRandomness.getValue();
return new Vec3d(
basePos.x + (random.nextDouble() - 0.5) * spreadX.getValue() + randX,
basePos.y + (random.nextDouble() - 0.5) * spreadY.getValue() + randY,
basePos.z + (random.nextDouble() - 0.5) * spreadZ.getValue() + randZ
);
}
private Vec3d getVel() {
double randX = (random.nextDouble() - 0.5) * 2 * velocityRandomness.getValue();
double randY = (random.nextDouble() - 0.5) * 2 * velocityRandomness.getValue();
double randZ = (random.nextDouble() - 0.5) * 2 * velocityRandomness.getValue();
return new Vec3d(
(random.nextDouble() - 0.5) * velocity.getValue() + randX,
(random.nextDouble() - 0.5) * velocity.getValue() + randY,
(random.nextDouble() - 0.5) * velocity.getValue() + randZ
);
}
}
@@ -10,140 +10,350 @@ import me.retucio.sputnik.module.setting.settings.ColorSetting;
import me.retucio.sputnik.module.setting.settings.NumberSetting; import me.retucio.sputnik.module.setting.settings.NumberSetting;
import me.retucio.sputnik.util.render.RenderUtil; import me.retucio.sputnik.util.render.RenderUtil;
import net.minecraft.block.*; import net.minecraft.block.*;
import net.minecraft.block.Blocks;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.Heightmap;
import net.minecraft.world.LightType; import net.minecraft.world.LightType;
import java.awt.*; import java.awt.*;
import java.util.ArrayList; import java.util.*;
import java.util.List; import java.util.concurrent.ConcurrentHashMap;
public class LightOverlay extends Module { public class LightOverlay extends Module {
SettingGroup sgDetection = addSg(new SettingGroup("detección", true)); SettingGroup sgDetection = addSg(new SettingGroup("detección", true));
SettingGroup sgColors = addSg(new SettingGroup("colores", true)); SettingGroup sgColors = addSg(new SettingGroup("colores", true));
SettingGroup sgOptimization = addSg(new SettingGroup("optimización", true));
public NumberSetting radius = sgDetection.add(new NumberSetting("radio", "radio a tener en cuenta al renderizar superposición", // detección
public NumberSetting radius = sgDetection.add(new NumberSetting(
"radio",
"radio a tener en cuenta al renderizar superposición",
16, 5, 128, 1)); 16, 5, 128, 1));
public NumberSetting yRadius = sgDetection.add(new NumberSetting("rango vertical", "distancia vertical a tener en cuenta",
public NumberSetting yRadius = sgDetection.add(new NumberSetting(
"rango vertical",
"distancia vertical a tener en cuenta",
4, 1, 16, 1)); 4, 1, 16, 1));
public NumberSetting interval = sgDetection.add(new NumberSetting("intervalo", "cada cuántos ticks comprobar el nivel de luz", public BooleanSetting onWater = sgDetection.add(new BooleanSetting(
"en agua",
"mostrar también bloques cubiertos en agua", true));
public BooleanSetting dontCullWater = sgDetection.add(new BooleanSetting(
"evitar culling en agua",
"mostrar superposición a través de bloques para poder verla desde fuera del agua", false));
// optimización, porque iba como la mierda
public NumberSetting updateInterval = sgOptimization.add(new NumberSetting(
"intervalo de búsqueda",
"cada cuántos ticks actualizar la búsqueda de bloques",
10, 1, 80, 1)); 10, 1, 80, 1));
public BooleanSetting onWater = sgDetection.add(new BooleanSetting("en agua", "mostrar también bloques cubiertos en agua", true)); public NumberSetting movementThreshold = sgOptimization.add(new NumberSetting(
public BooleanSetting dontCullWater = sgDetection.add(new BooleanSetting("evitar culling en agua", "mostrar superposición a través de bloques para poder verla desde fuera del agua", false)); "umbral de movimiento",
"distancia que debe moverse el jugador antes de forzar una actualización",
3.0, 0.5, 10.0, 0.5));
public ColorSetting lightColor = sgColors.add(new ColorSetting("color de la luz", "color de los bloques con luz", public BooleanSetting asyncSearch = sgOptimization.add(new BooleanSetting(
"búsqueda asíncrona",
"buscar bloques en un hilo separado (reduce lag)",
true));
public BooleanSetting incrementalUpdates = sgOptimization.add(new BooleanSetting(
"actualizaciones incrementales",
"actualizar solo los bloques nuevos / eliminados en lugar de recalcular todo",
true));
// colores
public ColorSetting lightColor = sgColors.add(new ColorSetting(
"color de la luz",
"color de los bloques con luz",
new Color(0, 255, 0, 67), false)); new Color(0, 255, 0, 67), false));
public ColorSetting darknessColor = sgColors.add(new ColorSetting("color de oscuridad", "color de bloques con 0 luz",
public ColorSetting darknessColor = sgColors.add(new ColorSetting(
"color de oscuridad",
"color de bloques con 0 luz",
new Color(255, 0, 0, 67), false)); new Color(255, 0, 0, 67), false));
List<BlockPos> blocks = new ArrayList<>(); // caché y estado
private final Set<BlockPos> cachedBlocks = Collections.newSetFromMap(new ConcurrentHashMap<>());
private final Set<BlockPos> blocksToRender = Collections.newSetFromMap(new ConcurrentHashMap<>());
private BlockPos lastPlayerPos = null;
private int lastRadius = -1;
private int lastYRadius = -1;
private int updateTimer = 0;
private boolean isSearching = false;
// para actualizaciones incrementales
private int incrementalLayer = 0;
private boolean fullRescanNeeded = false;
public LightOverlay() { public LightOverlay() {
super("superposición de luz", super("superposición de luz",
"te muestra el nivel de luz en bloques, para prevenir mob spawns", "te muestra el nivel de luz en bloques, para prevenir mob spawns",
Category.WORLD); Category.WORLD);
onWater.onUpdate(v -> dontCullWater.setVisible(v));
}
@Override
public void onEnable() {
cachedBlocks.clear();
blocksToRender.clear();
lastPlayerPos = null;
lastRadius = -1;
lastYRadius = -1;
updateTimer = 0;
incrementalLayer = 0;
fullRescanNeeded = true;
super.onEnable();
}
@Override
public void onDisable() {
cachedBlocks.clear();
blocksToRender.clear();
super.onDisable();
} }
@SubscribeEvent @SubscribeEvent
@SuppressWarnings("deprecation")
public void onRenderWorld(Render3DEvent event) { public void onRenderWorld(Render3DEvent event) {
if (mc.player == null || mc.world == null) return; if (mc.player == null || mc.world == null) return;
if (mc.player.age % interval.getIntValue() == 0) {
blocks = findExposedBlocks( // actualización periódica
radius.getIntValue(), if (updateTimer++ >= updateInterval.getIntValue()) {
yRadius.getIntValue() updateTimer = 0;
); updateBlocks();
} }
for (BlockPos block : blocks) { // renderizado
if (mc.world.getBlockState(block).isAir() || !canSpawnOn(block)) continue; Vec3d cameraPos = mc.gameRenderer.getCamera().getCameraPos();
if (!mc.world.getBlockState(block).isOpaque()) continue; int radiusValue = radius.getIntValue();
double radiusSq = radiusValue * radiusValue;
// actualizar lista de renderizado basado en distancia
blocksToRender.clear();
for (BlockPos pos : cachedBlocks) {
double distanceSq = pos.getSquaredDistance(cameraPos);
if (distanceSq <= radiusSq) {
blocksToRender.add(pos);
}
}
// renderizar bloques visibles
for (BlockPos block : blocksToRender) {
if (!mc.world.isChunkLoaded(block)) continue;
BlockState state = mc.world.getBlockState(block);
BlockState aboveState = mc.world.getBlockState(block.up());
if (!isValidSpawnSurface(block, state, aboveState)) continue;
// nivel de luz
int light = mc.world.getLightLevel(LightType.BLOCK, block.up()); int light = mc.world.getLightLevel(LightType.BLOCK, block.up());
Color color = light == 0 ? darknessColor.getColor() // color
: new Color( Color color;
(lightColor.getR() / 15) * light, if (light == 0) {
(lightColor.getG() / 15) * light, color = darknessColor.getColor();
(lightColor.getB() / 15) * light, } else {
lightColor.getA()); float ratio = light / 15.0f;
color = new Color(
(int)(lightColor.getR() * ratio),
(int)(lightColor.getG() * ratio),
(int)(lightColor.getB() * ratio),
lightColor.getA()
);
}
boolean shouldCull = !(onWater.isEnabled() && mc.world.getBlockState(block.up()).isOf(Blocks.WATER) && dontCullWater.isEnabled()); // renderizar
boolean shouldCull = !(onWater.isEnabled() && aboveState.isOf(Blocks.WATER) && dontCullWater.isEnabled());
RenderUtil.drawBlockFaceFilled(event.getMatrices(), block, Direction.UP, color, 0.001f, shouldCull); RenderUtil.drawBlockFaceFilled(event.getMatrices(), block, Direction.UP, color, 0.001f, shouldCull);
} }
} }
private List<BlockPos> findExposedBlocks(int radius, int yRadius) { private void updateBlocks() {
List<BlockPos> results = new ArrayList<>(); if (mc.player == null || mc.world == null) return;
BlockPos pos = mc.player.getBlockPos(); BlockPos playerPos = mc.player.getBlockPos();
int currentRadius = radius.getIntValue();
int currentYRadius = yRadius.getIntValue();
int minX = pos.getX() - radius; // verificar si hace falta actualizar
int maxX = pos.getX() + radius; boolean needsUpdate = fullRescanNeeded ||
int minY = Math.max(mc.world.getDimension().minY(), pos.getY() - yRadius); lastRadius != currentRadius ||
int maxY = Math.min(mc.world.getDimension().height() - 1, pos.getY() + yRadius); lastYRadius != currentYRadius ||
int minZ = pos.getZ() - radius; (lastPlayerPos != null &&
int maxZ = pos.getZ() + radius; playerPos.getSquaredDistance(lastPlayerPos) > (movementThreshold.getValue() * movementThreshold.getValue()));
maxY = Math.min(maxY, mc.world.getDimension().height() - 2); if (!needsUpdate && incrementalUpdates.isEnabled() && incrementalLayer >= 0) {
// actualización incremental de una sola capa
performIncrementalUpdate(playerPos, currentRadius, currentYRadius);
return;
}
if (!needsUpdate) {
return;
}
lastPlayerPos = playerPos;
lastRadius = currentRadius;
lastYRadius = currentYRadius;
fullRescanNeeded = false;
incrementalLayer = 0;
// búsqueda
if (asyncSearch.isEnabled()) {
if (isSearching) return;
isSearching = true;
new Thread(() -> {
try {
performFullBlockSearch(playerPos, currentRadius, currentYRadius);
} finally {
isSearching = false;
}
}, "sputnik-lightoverlay-search").start();
} else {
performFullBlockSearch(playerPos, currentRadius, currentYRadius);
}
}
@SuppressWarnings("deprecation")
private void performFullBlockSearch(BlockPos center, int radius, int yRadius) {
Set<BlockPos> newBlocks = new HashSet<>();
int radiusSq = radius * radius;
int minY = Math.max(mc.world.getBottomY(), center.getY() - yRadius);
int maxY = Math.min(mc.world.getTopY(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, mc.player.getBlockPos()) - 1, center.getY() + yRadius);
for (int x = -radius; x <= radius; x++) {
for (int z = -radius; z <= radius; z++) {
if (x*x + z*z > radiusSq) continue;
for (int x = minX; x <= maxX; x++) {
for (int z = minZ; z <= maxZ; z++) {
for (int y = minY; y <= maxY; y++) { for (int y = minY; y <= maxY; y++) {
BlockPos current = new BlockPos(x, y, z); BlockPos pos = center.add(x, y - center.getY(), z);
if (!mc.world.isAir(current) && canSpawnOn(current)) // nuh uh si el chunk no está cargado
results.add(current); if (!mc.world.isChunkLoaded(pos)) continue;
BlockState state = mc.world.getBlockState(pos);
BlockState aboveState = mc.world.getBlockState(pos.up());
if (isValidSpawnSurface(pos, state, aboveState)) {
newBlocks.add(pos.toImmutable());
}
} }
} }
} }
return results; // actualizar caché
cachedBlocks.clear();
cachedBlocks.addAll(newBlocks);
} }
private boolean canSpawnOn(BlockPos pos) { @SuppressWarnings("deprecation")
BlockState aboveState = mc.world.getBlockState(pos.up()); private void performIncrementalUpdate(BlockPos center, int radius, int yRadius) {
if (incrementalLayer > radius * 2) {
incrementalLayer = -radius;
}
int currentX = center.getX() - radius + incrementalLayer;
int radiusSq = radius * radius;
int minY = Math.max(mc.world.getBottomY(), center.getY() - yRadius);
int maxY = Math.min(mc.world.getTopY(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, mc.player.getBlockPos()) - 1, center.getY() + yRadius);
// buscar en una columna específica (x fijo, variar z)
for (int z = -radius; z <= radius; z++) {
int xDist = incrementalLayer;
if (xDist*xDist + z*z > radiusSq) continue;
for (int y = minY; y <= maxY; y++) {
BlockPos pos = new BlockPos(currentX, y, center.getZ() + z);
if (!mc.world.isChunkLoaded(pos)) continue;
BlockState state = mc.world.getBlockState(pos);
BlockState aboveState = mc.world.getBlockState(pos.up());
boolean isValid = isValidSpawnSurface(pos, state, aboveState);
if (isValid) {
cachedBlocks.add(pos.toImmutable());
} else {
cachedBlocks.remove(pos);
}
}
}
incrementalLayer++;
// cada 10 actualizaciones incrementales, hacer una limpieza de bloques lejanos
if (incrementalLayer % 10 == 0) {
cleanupDistantBlocks(center, radius);
}
}
private void cleanupDistantBlocks(BlockPos center, int radius) {
int radiusSq = radius * radius * 2;
Iterator<BlockPos> iterator = cachedBlocks.iterator();
while (iterator.hasNext()) {
BlockPos pos = iterator.next();
int dx = pos.getX() - center.getX();
int dz = pos.getZ() - center.getZ();
if (dx * dx + dz * dz > radiusSq) {
iterator.remove();
}
}
}
private boolean isValidSpawnSurface(BlockPos pos, BlockState state, BlockState aboveState) {
if (state.isAir() || !state.isOpaque() || !state.isFullCube(mc.world, pos)) {
return false;
}
Block aboveBlock = aboveState.getBlock(); Block aboveBlock = aboveState.getBlock();
if (aboveState.isAir()) return true; if (aboveState.isAir()) return true;
if (aboveBlock == Blocks.WATER && onWater.isEnabled()) return true; if (aboveBlock == Blocks.WATER && onWater.isEnabled()) return true;
if (aboveBlock instanceof PlantBlock) return true; if (aboveBlock instanceof PlantBlock) return true;
if (!aboveState.isOpaque() && !aboveState.isFullCube(mc.world, pos.up())) { if (aboveBlock instanceof SlabBlock ||
aboveBlock instanceof StairsBlock ||
if (aboveBlock instanceof SlabBlock aboveBlock instanceof ButtonBlock ||
|| aboveBlock instanceof StairsBlock aboveBlock instanceof CarpetBlock ||
|| aboveBlock instanceof ButtonBlock aboveBlock instanceof PressurePlateBlock ||
|| aboveBlock instanceof CarpetBlock aboveBlock instanceof PaneBlock ||
|| aboveBlock instanceof PressurePlateBlock aboveBlock instanceof TrapdoorBlock ||
|| aboveBlock instanceof PaneBlock aboveBlock instanceof FenceBlock ||
|| aboveBlock instanceof TrapdoorBlock aboveBlock instanceof WallBlock ||
|| aboveBlock instanceof FenceBlock aboveBlock instanceof AnvilBlock ||
|| aboveBlock instanceof WallBlock aboveBlock instanceof BedBlock) {
|| aboveBlock instanceof AnvilBlock return false;
|| aboveBlock instanceof BedBlock) {
return false;
}
if (aboveBlock == Blocks.CHEST ||
aboveBlock == Blocks.ENDER_CHEST ||
aboveBlock == Blocks.BARREL ||
aboveBlock == Blocks.TRAPPED_CHEST) {
return false;
}
return aboveBlock != Blocks.ENCHANTING_TABLE
&& aboveBlock != Blocks.GRINDSTONE
&& aboveBlock != Blocks.STONECUTTER
&& aboveBlock != Blocks.LOOM
&& aboveBlock != Blocks.COMPOSTER
&& aboveBlock != Blocks.CAKE;
} }
return true; if (aboveBlock == Blocks.CHEST ||
aboveBlock == Blocks.ENDER_CHEST ||
aboveBlock == Blocks.BARREL ||
aboveBlock == Blocks.TRAPPED_CHEST ||
aboveBlock == Blocks.ENCHANTING_TABLE ||
aboveBlock == Blocks.GRINDSTONE ||
aboveBlock == Blocks.STONECUTTER ||
aboveBlock == Blocks.LOOM ||
aboveBlock == Blocks.COMPOSTER ||
aboveBlock == Blocks.CAKE) {
return false;
}
return !aboveState.isOpaque() || !aboveState.isFullCube(mc.world, pos.up());
} }
} }
@@ -105,6 +105,16 @@ public class ListSetting<T> extends Setting {
return options; return options;
} }
public List<T> getEnabledOptions() {
List<T> enabledOptions = new ArrayList<>();
for (T option : options) {
if (isEnabled(option)) {
enabledOptions.add(option);
}
}
return enabledOptions;
}
public Map<T, String> getDisplayNames() { public Map<T, String> getDisplayNames() {
return displayNames; return displayNames;
} }
@@ -36,6 +36,7 @@ public class HudRenderer {
addElement(new CustomTextElement(), positions, visibilities); addElement(new CustomTextElement(), positions, visibilities);
addElement(new TimeElement(), positions, visibilities); addElement(new TimeElement(), positions, visibilities);
addElement(new PingElement(), positions, visibilities); addElement(new PingElement(), positions, visibilities);
addElement(new RotationElement(), positions, visibilities);
addElement(new DynoElement(), positions, visibilities); addElement(new DynoElement(), positions, visibilities);
addElement(new EchestElement(), positions, visibilities); addElement(new EchestElement(), positions, visibilities);
@@ -0,0 +1,43 @@
package me.retucio.sputnik.ui.hud.elements;
import me.retucio.sputnik.module.ModuleManager;
import me.retucio.sputnik.module.modules.client.HUD;
import me.retucio.sputnik.ui.hud.TextHudElement;
import net.minecraft.text.Text;
import java.util.List;
public class RotationElement extends TextHudElement {
public RotationElement() {
super("rotation", mc.getWindow().getScaledWidth() - mc.textRenderer.getWidth("180º 180º (N)"), mc.getWindow().getScaledHeight() - mc.textRenderer.fontHeight);
}
@Override
public String getText(float delta, HUD hud) {
if (mc.player == null) return getPreviewText();
return String.format("%.2f", mc.player.getYaw()) + "º " + String.format("%.2f", mc.player.getPitch()) + "º (" + getDirection() + ")";
}
@Override
public String getPreviewText() {
if (mc.player == null)
return "69º 69º (W)";
return getText(mc.getRenderTickCounter().getDynamicDeltaTicks(), ModuleManager.INSTANCE.getModuleByClass(HUD.class));
}
@Override
public List<Text> getTooltip() {
return List.of();
}
private String getDirection() {
return switch (mc.player.getHorizontalFacing()) {
case NORTH -> "N";
case SOUTH -> "S";
case EAST -> "E";
case WEST -> "O";
default -> "?";
};
}
}
@@ -35,6 +35,7 @@ import org.lwjgl.glfw.GLFW;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional;
import static me.retucio.sputnik.ui.widgets.frames.settings.ClientSettingsFrame.guiSettings; import static me.retucio.sputnik.ui.widgets.frames.settings.ClientSettingsFrame.guiSettings;
@@ -248,15 +249,21 @@ public class ClickGUI extends Screen {
} }
} }
public SettingsFrame getSfOfModule(Module module) {
Optional<SettingsFrame> frame = settingsFrames.stream()
.filter(sf -> sf.getModule().equals(module))
.findFirst();
return frame.orElseGet(() ->
new SettingsFrame(module, 0, 0, 100, 20)
);
}
// abrir un marco donde se encuentran los ajustes del módulo deseado // abrir un marco donde se encuentran los ajustes del módulo deseado
public void openSettingsFrame(Module module, int x, int y) { public void openSettingsFrame(Module module, int x, int y) {
for (SettingsFrame frame : settingsFrames) { // asegurarse de que no se sale de la pantalla
if (frame.getModule() == module) { x = Math.clamp(x, 0, mc.getWindow().getScaledWidth() - 80);
frame.setX(x); y = Math.clamp(y, 0, mc.getWindow().getScaledHeight() - 120);
frame.setY(y);
return;
}
}
SettingsFrame frame = new SettingsFrame(module, x, y, 100, 20); SettingsFrame frame = new SettingsFrame(module, x, y, 100, 20);
settingsFrames.add(frame); settingsFrames.add(frame);
@@ -289,8 +296,8 @@ public class ClickGUI extends Screen {
// verificar si un módulo tiene su marco de ajustes abierto // verificar si un módulo tiene su marco de ajustes abierto
public boolean isSettingsFrameOpen(Module module) { public boolean isSettingsFrameOpen(Module module) {
return settingsFrames.stream() return settingsFrames.stream().anyMatch(
.anyMatch(sf -> sf.getModule().getName().equals(module.getName())); sf -> sf.getModule().equals(module));
} }
public boolean isColorPickerFrameOpen(ColorSetting setting) { public boolean isColorPickerFrameOpen(ColorSetting setting) {
@@ -43,7 +43,7 @@ public class ModuleButton extends Button {
@Override @Override
public void mouseClicked(int mouseX, int mouseY, int button) { public void mouseClicked(int mouseX, int mouseY, int button) {
if (isHovered((int) mouseX, (int) mouseY) && ClickGUI.INSTANCE.trySelect(this)) { if (isHovered(mouseX, mouseY) && ClickGUI.INSTANCE.trySelect(this)) {
if (button == 0) { // clic izquierdo para activar / desactivar el módulo if (button == 0) { // clic izquierdo para activar / desactivar el módulo
module.toggle(); module.toggle();
} else if (button == 1) { // clic derecho para el marco de ajustes (también lo cierra si está abierto) } else if (button == 1) { // clic derecho para el marco de ajustes (también lo cierra si está abierto)
@@ -56,12 +56,6 @@ public class ModuleButton extends Button {
} }
} }
@Override
public void mouseDragged(int mouseX, int mouseY) {}
@Override
public void onKey(int key, int action) {}
// verifica si el puntero del ratón se encuentra sobre el botón del módulo // verifica si el puntero del ratón se encuentra sobre el botón del módulo
@Override @Override
public boolean isHovered(int mouseX, int mouseY) { public boolean isHovered(int mouseX, int mouseY) {
@@ -81,7 +81,7 @@ public class ChooseButton<T> extends SettingButton<OptionSetting<T>> {
String displayName = setting.getDisplayName(option); String displayName = setting.getDisplayName(option);
BooleanSetting b = new BooleanSetting( BooleanSetting b = new BooleanSetting(
displayName, displayName,
"incluir " + displayName, "seleccionar " + displayName,
setting.is(option) setting.is(option)
); );
@@ -7,6 +7,7 @@ import me.retucio.sputnik.module.setting.settings.ListSetting;
import me.retucio.sputnik.ui.widgets.buttons.SettingButton; import me.retucio.sputnik.ui.widgets.buttons.SettingButton;
import me.retucio.sputnik.ui.screen.ClickGUI; import me.retucio.sputnik.ui.screen.ClickGUI;
import me.retucio.sputnik.ui.widgets.frames.SettingsFrame; import me.retucio.sputnik.ui.widgets.frames.SettingsFrame;
import me.retucio.sputnik.util.ChatUtil;
import me.retucio.sputnik.util.Colors; import me.retucio.sputnik.util.Colors;
import me.retucio.sputnik.util.KeyUtil; import me.retucio.sputnik.util.KeyUtil;
import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.DrawContext;
@@ -5,6 +5,11 @@ import net.minecraft.client.input.KeyInput;
import net.minecraft.client.option.KeyBinding; import net.minecraft.client.option.KeyBinding;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.im.InputContext;
import java.util.Locale;
import static me.retucio.sputnik.Sputnik.mc; import static me.retucio.sputnik.Sputnik.mc;
// cosas útiles relacionadas a las teclas // cosas útiles relacionadas a las teclas
@@ -28,6 +33,11 @@ public class KeyUtil {
|| isKeyDown(GLFW.GLFW_KEY_RIGHT_SHIFT); || isKeyDown(GLFW.GLFW_KEY_RIGHT_SHIFT);
} }
public static boolean isCapsLockOn() throws HeadlessException {
return Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_CAPS_LOCK);
}
public static void pressKey(int key, int action) { public static void pressKey(int key, int action) {
pressKey(key, action, 0, 0); pressKey(key, action, 0, 0);
} }
@@ -36,7 +46,12 @@ public class KeyUtil {
mc.keyboard.onKey( mc.keyboard.onKey(
mc.getWindow().getHandle(), mc.getWindow().getHandle(),
action, action,
new KeyInput(key, scancode, modifiers)); new KeyInput(
key,
scancode,
modifiers
)
);
} }
public static String getKeyName(int key) { public static String getKeyName(int key) {
@@ -72,31 +87,93 @@ public class KeyUtil {
}; };
} }
// quizás debería de añadir layouts...
public static String shiftKey(String c) { public static String shiftKey(String c) {
return switch (c) { return shiftKey(c, getLayout());
case "`" -> "~"; }
case "1" -> "!";
case "2" -> "@"; public static String shiftKey(String c, KeyboardLayout layout) {
case "3" -> "#"; String uppercase = isCapsLockOn() ? c.toLowerCase() : c.toUpperCase();
case "4" -> "$";
case "5" -> "%"; switch (layout) {
case "6" -> "^"; case EN_US -> {
case "7" -> "&"; return switch (c) {
case "8" -> "*"; case "`" -> "~";
case "9" -> "("; case "1" -> "!";
case "0" -> ")"; case "2" -> "@";
case "-" -> "_"; case "3" -> "#";
case "=" -> "+"; case "4" -> "$";
case "[" -> "{"; case "5" -> "%";
case "]" -> "}"; case "6" -> "^";
case "\\" -> "|"; case "7" -> "&";
case ";" -> ":"; case "8" -> "*";
case "'" -> "\""; case "9" -> "(";
case "," -> "<"; case "0" -> ")";
case "." -> ">"; case "-" -> "_";
case "/" -> "?"; case "=" -> "+";
default -> c.toUpperCase(); case "[" -> "{";
}; case "]" -> "}";
case "\\" -> "|";
case ";" -> ":";
case "'" -> "\"";
case "," -> "<";
case "." -> ">";
case "/" -> "?";
default -> uppercase;
};
}
case ES_ES -> {
return switch (c) {
case "º" -> "ª";
case "1" -> "!";
case "2" -> "\"";
case "3" -> "·";
case "4" -> "$";
case "5" -> "%";
case "6" -> "&";
case "7" -> "/";
case "8" -> "(";
case "9" -> ")";
case "0" -> "=";
case "'" -> "?";
case "¡" -> "¿";
case "`" -> "^";
case "+" -> "*";
case "´" -> "¨";
case "," -> ";";
case "." -> ":";
case "-" -> "_";
case "<" -> ">";
default -> uppercase;
};
}
}
return uppercase;
}
public static KeyboardLayout getLayout() {
Locale locale = InputContext.getInstance().getLocale();
if (locale == null) locale = Locale.getDefault();
String country = locale.getCountry();
String lang = locale.getLanguage();
if (country.equals("US") && lang.equals("en")) {
return KeyboardLayout.EN_US;
} else if (country.equals("ES") && lang.equals("es")) {
return KeyboardLayout.ES_ES;
}
// fallback
return KeyboardLayout.EN_US;
}
public enum KeyboardLayout {
EN_US,
ES_ES;
} }
} }
@@ -36,6 +36,7 @@ public class Lists {
public static Map<ScreenHandlerType<?>, String> screenNames; public static Map<ScreenHandlerType<?>, String> screenNames;
public static final List<Color> colorList = new ArrayList<>(); public static final List<Color> colorList = new ArrayList<>();
public static final List<String> fontList = new ArrayList<>();
// intentar traducir nombres y fallar miserablemente // intentar traducir nombres y fallar miserablemente
public static void init() { public static void init() {
@@ -68,7 +69,16 @@ public class Lists {
).toList()); ).toList());
colorList.addAll(Arrays.asList( colorList.addAll(Arrays.asList(
RED, ORANGE, YELLOW, LIME, GREEN, CYAN, CELESTE, BLUE, PURPLE, MAGENTA, PINK, LAVENDER, WHITE, SILVER, GRAY, BLACK, BROWN)); RED, ORANGE, YELLOW, LIME, GREEN, CYAN,
CELESTE, BLUE, PURPLE, MAGENTA, PINK,
LAVENDER, WHITE, SILVER, GRAY, BLACK, BROWN
));
fontList.addAll(List.of(
"bahnschrift", "calibri", "cantarell", "comic-sans-ms",
"deja-vu-sans-mono", "new-times-roman", "roboto", "segoe-ui",
"ubuntu"
));
} }
public static <T> Map<T, Boolean> allTrue(List<T> options) { public static <T> Map<T, Boolean> allTrue(List<T> options) {
@@ -14,6 +14,8 @@ public class Layers {
private static final RenderLayer QUADS; private static final RenderLayer QUADS;
private static final RenderLayer QUADS_CULL; private static final RenderLayer QUADS_CULL;
private static final RenderLayer TEXT;
public static RenderLayer lines() { public static RenderLayer lines() {
return LINES; return LINES;
@@ -31,12 +33,18 @@ public class Layers {
return QUADS_CULL; return QUADS_CULL;
} }
public static RenderLayer text() {
return TEXT;
}
static { static {
LINES = RenderLayer.of(Sputnik.MOD_ID + "_lines", RenderSetup.builder(LINES_PIPELINE).build()); LINES = RenderLayer.of(Sputnik.MOD_ID + "_lines", RenderSetup.builder(LINES_PIPELINE).build());
LINES_CULL = RenderLayer.of(Sputnik.MOD_ID + "_lines_cull", RenderSetup.builder(LINES_CULL_PIPELINE).build()); LINES_CULL = RenderLayer.of(Sputnik.MOD_ID + "_lines_cull", RenderSetup.builder(LINES_CULL_PIPELINE).build());
QUADS = RenderLayer.of(Sputnik.MOD_ID + "_quads", RenderSetup.builder(QUADS_PIPELINE).build()); QUADS = RenderLayer.of(Sputnik.MOD_ID + "_quads", RenderSetup.builder(QUADS_PIPELINE).build());
QUADS_CULL = RenderLayer.of(Sputnik.MOD_ID + "quads_cull", RenderSetup.builder(QUADS_CULL_PIPELINE).build()); QUADS_CULL = RenderLayer.of(Sputnik.MOD_ID + "_quads_cull", RenderSetup.builder(QUADS_CULL_PIPELINE).build());
TEXT = RenderLayer.of(Sputnik.MOD_ID + "_text", RenderSetup.builder(TEXT_PIPELINE).build());
} }
} }
@@ -6,12 +6,18 @@ import com.mojang.blaze3d.platform.DepthTestFunction;
import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormat;
import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.Sputnik;
import net.minecraft.client.gl.RenderPipelines; import net.minecraft.client.gl.RenderPipelines;
import net.minecraft.client.gl.UniformType;
import net.minecraft.client.render.VertexFormats; import net.minecraft.client.render.VertexFormats;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
public class Pipelines { public class Pipelines {
static final RenderPipeline.Snippet MESH_UNIFORMS = RenderPipeline.builder()
.withUniform("MeshData",UniformType.UNIFORM_BUFFER)
.buildSnippet();
static final RenderPipeline LINES_PIPELINE = RenderPipeline.builder(RenderPipelines.RENDERTYPE_LINES_SNIPPET) static final RenderPipeline LINES_PIPELINE = RenderPipeline.builder(RenderPipelines.RENDERTYPE_LINES_SNIPPET)
.withLocation(Identifier.of(Sputnik.MOD_ID, "pipeline/lines")) .withLocation(Identifier.of(Sputnik.MOD_ID, "pipeline/lines"))
.withVertexFormat(VertexFormats.POSITION_COLOR_NORMAL_LINE_WIDTH, VertexFormat.DrawMode.LINES) .withVertexFormat(VertexFormats.POSITION_COLOR_NORMAL_LINE_WIDTH, VertexFormat.DrawMode.LINES)
@@ -45,4 +51,16 @@ public class Pipelines {
.withDepthWrite(false) .withDepthWrite(false)
.withCull(false) .withCull(false)
.build(); .build();
public static final RenderPipeline TEXT_PIPELINE = RenderPipeline.builder(RenderPipelines.POSITION_TEX_COLOR_SNIPPET)
.withLocation(Identifier.of(Sputnik.MOD_ID, "pipeline/text"))
.withVertexFormat(VertexFormats.POSITION_TEXTURE_COLOR, VertexFormat.DrawMode.TRIANGLES)
.withVertexShader(Identifier.of(Sputnik.MOD_ID, "shaders/text.vert"))
.withFragmentShader(Identifier.of(Sputnik.MOD_ID,"shaders/text.frag"))
.withSampler("u_Texture")
.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST)
.withDepthWrite(false)
.withBlend(BlendFunction.TRANSLUCENT)
.withCull(false)
.build();
} }
@@ -0,0 +1,89 @@
package me.retucio.sputnik.util.render;
import com.mojang.blaze3d.platform.TextureUtil;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.AddressMode;
import com.mojang.blaze3d.textures.FilterMode;
import com.mojang.blaze3d.textures.TextureFormat;
import net.minecraft.client.texture.AbstractTexture;
import net.minecraft.client.texture.NativeImage;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.BufferUtils;
import org.lwjgl.stb.STBImage;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
public class Texture extends AbstractTexture {
public Texture(int width, int height, TextureFormat format, FilterMode min, FilterMode mag) {
glTexture = RenderSystem.getDevice().createTexture("", 15, format, width, height, 1, 1);
sampler = RenderSystem.getSamplerCache().get(AddressMode.REPEAT, AddressMode.REPEAT, min, mag, false);
glTextureView = RenderSystem.getDevice().createTextureView(glTexture);
}
public int getWidth() {
return getGlTexture().getWidth(0);
}
public int getHeight() {
return getGlTexture().getHeight(0);
}
public void upload(byte[] bytes) {
upload(BufferUtils.createByteBuffer(bytes.length).put(bytes));
}
public void upload(ByteBuffer buffer) {
NativeImage image = getImage();
buffer.rewind();
MemoryUtil.memCopy(MemoryUtil.memAddress(buffer), image.imageId(), buffer.remaining());
RenderSystem.getDevice().createCommandEncoder().writeToTexture(glTexture, image);
image.close();
}
private @NotNull NativeImage getImage() {
NativeImage.Format imageFormat = switch (glTexture.getFormat()) {
case RGBA8 -> NativeImage.Format.RGBA;
case RED8 -> NativeImage.Format.LUMINANCE;
default -> throw new IllegalArgumentException();
};
// Workaround for writeToTexture(IntBuffer) overload comparing width * height to the size of the int buffer.
// And since we are working with pixels which are only one byte in size, the sizes don't match
return new NativeImage(imageFormat, getWidth(), getHeight(), false);
}
public static Texture readResource(String path, boolean flipY, FilterMode filter) {
try (var in = Texture.class.getResourceAsStream(path)) {
if (in == null) return null;
var data = TextureUtil.readResource(in).rewind();
try (MemoryStack stack = MemoryStack.stackPush()) {
IntBuffer width = stack.mallocInt(1);
IntBuffer height = stack.mallocInt(1);
IntBuffer comp = stack.mallocInt(1);
STBImage.stbi_set_flip_vertically_on_load(flipY);
ByteBuffer image = STBImage.stbi_load_from_memory(data, width, height, comp, 4);
var texture = new Texture(width.get(0), height.get(0), TextureFormat.RGBA8, filter, filter);
texture.upload(image);
STBImage.stbi_image_free(image);
STBImage.stbi_set_flip_vertically_on_load(false);
return texture;
}
} catch (IOException e) {
return null;
}
}
}
@@ -0,0 +1,12 @@
{
"providers": [
{
"type": "ttf",
"file": "sputnik:bahnschrift.ttf",
"size": 10.0,
"oversample": 2.0,
"shift": [0.0, 0.0],
"range": "0x0000-0x00FF"
}
]
}
@@ -0,0 +1,12 @@
{
"providers": [
{
"type": "ttf",
"file": "sputnik:calibri.ttf",
"size": 10.0,
"oversample": 2.0,
"shift": [0.0, 0.0],
"range": "0x0000-0x00FF"
}
]
}
Binary file not shown.
@@ -0,0 +1,12 @@
{
"providers": [
{
"type": "ttf",
"file": "sputnik:cantarell.ttf",
"size": 10.0,
"oversample": 2.0,
"shift": [0.0, 0.0],
"range": "0x0000-0x00FF"
}
]
}
Binary file not shown.
@@ -0,0 +1,12 @@
{
"providers": [
{
"type": "ttf",
"file": "sputnik:comic-sans-ms.ttf",
"size": 10.0,
"oversample": 2.0,
"shift": [0.0, 0.0],
"range": "0x0000-0x00FF"
}
]
}
@@ -0,0 +1,12 @@
{
"providers": [
{
"type": "ttf",
"file": "sputnik:deja-vu-sans-mono.ttf",
"size": 10.0,
"oversample": 2.0,
"shift": [0.0, 0.0],
"range": "0x0000-0x00FF"
}
]
}
@@ -0,0 +1,12 @@
{
"providers": [
{
"type": "ttf",
"file": "sputnik:new-times-roman.ttf",
"size": 10.0,
"oversample": 2.0,
"shift": [0.0, 0.0],
"range": "0x0000-0x00FF"
}
]
}
@@ -0,0 +1,12 @@
{
"providers": [
{
"type": "ttf",
"file": "sputnik:roboto.ttf",
"size": 10.0,
"oversample": 2.0,
"shift": [0.0, 0.0],
"range": "0x0000-0x00FF"
}
]
}
Binary file not shown.
@@ -0,0 +1,12 @@
{
"providers": [
{
"type": "ttf",
"file": "sputnik:segoe-ui.ttf",
"size": 10.0,
"oversample": 2.0,
"shift": [0.0, 0.0],
"range": "0x0000-0x00FF"
}
]
}
Binary file not shown.
@@ -0,0 +1,12 @@
{
"providers": [
{
"type": "ttf",
"file": "sputnik:ubuntu.ttf",
"size": 10.0,
"oversample": 2.0,
"shift": [0.0, 0.0],
"range": "0x0000-0x00FF"
}
]
}
Binary file not shown.
@@ -0,0 +1,14 @@
#version 150
in vec2 vTexCoord;
in vec4 vColor;
out vec4 fragColor;
uniform sampler2D u_Texture;
void main() {
float distance = texture(u_Texture, vTexCoord).r;
float alpha = smoothstep(0.4, 0.6, distance);
fragColor = vec4(vColor.rgb, vColor.a * alpha);
}
@@ -0,0 +1,15 @@
#version 150
in vec3 aPos;
in vec2 aTexCoord;
in vec4 aColor;
out vec2 vTexCoord;
out vec4 vColor;
uniform mat4 u_ProjectionMatrix;
void main() {
gl_Position = u_ProjectionMatrix * vec4(aPos, 1.0);
vTexCoord = aTexCoord;
vColor = aColor;
}
+1
View File
@@ -52,6 +52,7 @@
"EmitterParticleMixin", "EmitterParticleMixin",
"EquipmentRendererMixin", "EquipmentRendererMixin",
"FogRendererMixin", "FogRendererMixin",
"FontManagerMixin",
"GameOptionsMixin", "GameOptionsMixin",
"GameRendererMixin", "GameRendererMixin",
"HeldItemRendererMixin", "HeldItemRendererMixin",