From ec3e633cecb30e6baf067c90f9b994005b4c5078 Mon Sep 17 00:00:00 2001 From: retuci0 Date: Mon, 5 Jan 2026 22:16:01 +0100 Subject: [PATCH] v2.4 --- cambios.md | 9 +- gradle.properties | 4 +- src/main/java/me/retucio/sputnik/Sputnik.java | 17 +- .../java/me/retucio/sputnik/cape/Cape.java | 61 +++ .../me/retucio/sputnik/cape/CapeManager.java | 87 +++++ .../sputnik/command/CommandManager.java | 3 +- .../command/args/EntityArgumentType.java | 3 +- .../command/commands/PurgeCommand.java | 2 +- .../sputnik/config/BinarySerializer.java | 357 ++++++++++++++++++ .../retucio/sputnik/config/ClientConfig.java | 9 +- .../retucio/sputnik/config/ConfigManager.java | 83 ++-- .../event/{ => events}/JoinWorldEvent.java | 3 +- .../events/sputnik/LoadCapeManagerEvent.java | 6 + .../mixin/CapeFeatureRendererMixin.java | 26 ++ .../sputnik/mixin/ChatHudLineMixin.java | 10 +- .../mixin/ChatHudLineVisibleMixin.java | 14 +- .../retucio/sputnik/mixin/ChatHudMixin.java | 18 +- .../ClientCommonNetworkHandlerMixin.java | 2 +- .../sputnik/mixin/ConfirmScreenMixin.java | 2 +- .../mixin/ElytraFeatureRendererMixin.java | 32 ++ .../sputnik/mixin/GameRendererMixin.java | 4 +- .../retucio/sputnik/mixin/KeyboardMixin.java | 3 +- .../sputnik/mixin/MessageHandlerMixin.java | 2 +- .../sputnik/mixin/MinecraftClientMixin.java | 21 +- .../sputnik/mixin/PlayerEntityMixin.java | 3 +- .../me/retucio/sputnik/mixin/Vec3dMixin.java | 6 +- .../sputnik/mixin/WorldRendererMixin.java | 3 +- .../me/retucio/sputnik/module/Module.java | 13 +- .../retucio/sputnik/module/ModuleManager.java | 4 +- .../sputnik/module/modules/misc/ChatPlus.java | 6 +- .../module/modules/network/PacketDelay.java | 2 +- .../sputnik/module/modules/player/Capes.java | 17 + .../sputnik/module/modules/player/UIMove.java | 2 +- .../sputnik/module/settings/ColorSetting.java | 4 +- .../module/settings/OptionSetting.java | 31 +- .../sputnik/module/settings/Setting.java | 3 +- .../sputnik/ui/hud/ImageHudElement.java | 9 +- .../retucio/sputnik/ui/screen/ClickGUI.java | 23 +- .../sputnik/ui/screen/PreviewScreen.java | 3 +- .../ui/widgets/frames/ModuleFrame.java | 5 +- .../ui/widgets/frames/SettingsFrame.java | 8 +- .../frames/settings/ClientSettingsFrame.java | 5 +- .../me/retucio/sputnik/util/ChatUtil.java | 5 +- .../sputnik/util/interfaces/IChatHud.java | 4 +- .../sputnik/util/interfaces/IChatHudLine.java | 10 +- .../util/interfaces/IChatHudLineVisible.java | 4 +- .../util/interfaces/IConfirmScreen.java | 2 +- .../util/interfaces/IMessageHandler.java | 2 +- .../sputnik/util/interfaces/IVec3d.java | 10 +- .../sputnik/util/interfaces/TextVisitor.java | 8 - .../sputnik/util/render/GlintRenderLayer.java | 7 +- .../retucio/sputnik/util/render/Layers.java | 9 +- .../sputnik/util/render/Pipelines.java | 9 +- .../textures/capes/15th-anniversary.png | Bin 0 -> 1105 bytes .../assets/sputnik/textures/capes/bedrock.png | Bin 0 -> 572 bytes .../sputnik/textures/capes/birthday.png | Bin 0 -> 1393 bytes .../sputnik/textures/capes/cheapsh0t.png | Bin 0 -> 1743 bytes .../sputnik/textures/capes/cherry-blossom.png | Bin 0 -> 694 bytes .../textures/capes/chinese-translator.png | Bin 0 -> 1799 bytes .../assets/sputnik/textures/capes/cobalt.png | Bin 0 -> 4095 bytes .../assets/sputnik/textures/capes/copper.png | Bin 0 -> 918 bytes .../sputnik/textures/capes/dannybstyle.png | Bin 0 -> 1364 bytes .../textures/capes/experience-2025.png | Bin 0 -> 784 bytes .../sputnik/textures/capes/eye-blossom.png | Bin 0 -> 578 bytes .../sputnik/textures/capes/half-life.png | Bin 0 -> 625 bytes .../sputnik/textures/capes/hollow-knight.png | Bin 0 -> 779 bytes .../sputnik/textures/capes/julianclark.png | Bin 0 -> 1398 bytes .../assets/sputnik/textures/capes/mcc-15.png | Bin 0 -> 717 bytes .../sputnik/textures/capes/migrator.png | Bin 0 -> 705 bytes .../textures/capes/millionth-customer.png | Bin 0 -> 3831 bytes .../sputnik/textures/capes/minecon-2011.png | Bin 0 -> 937 bytes .../sputnik/textures/capes/minecon-2012.png | Bin 0 -> 3549 bytes .../sputnik/textures/capes/minecon-2013.png | Bin 0 -> 1111 bytes .../sputnik/textures/capes/minecon-2015.png | Bin 0 -> 3753 bytes .../sputnik/textures/capes/minecon-2016.png | Bin 0 -> 754 bytes .../sputnik/textures/capes/minecraft.png | Bin 0 -> 686 bytes .../sputnik/textures/capes/mojang-old-old.png | Bin 0 -> 1370 bytes .../sputnik/textures/capes/mojang-old.png | Bin 0 -> 1076 bytes .../assets/sputnik/textures/capes/mojang.png | Bin 0 -> 560 bytes .../sputnik/textures/capes/mojira-mod.png | Bin 0 -> 1213 bytes .../sputnik/textures/capes/mrmessiah.png | Bin 0 -> 1128 bytes .../sputnik/textures/capes/pancakes.png | Bin 0 -> 657 bytes .../sputnik/textures/capes/prismarine.png | Bin 0 -> 4432 bytes .../textures/capes/realms-mapmaker.png | Bin 0 -> 1111 bytes .../textures/capes/scrolls-champion.png | Bin 0 -> 1432 bytes .../assets/sputnik/textures/capes/sputnik.png | Bin 0 -> 1150 bytes .../assets/sputnik/textures/capes/tiktok.png | Bin 0 -> 926 bytes .../sputnik/textures/capes/translator.png | Bin 0 -> 1723 bytes .../assets/sputnik/textures/capes/turtle.png | Bin 0 -> 19028 bytes .../assets/sputnik/textures/capes/twitch.png | Bin 0 -> 1253 bytes .../sputnik/textures/capes/valentine.png | Bin 0 -> 417 bytes .../assets/sputnik/textures/capes/vanilla.png | Bin 0 -> 616 bytes src/main/resources/sputnik.mixins.json | 2 + 93 files changed, 823 insertions(+), 174 deletions(-) create mode 100644 src/main/java/me/retucio/sputnik/cape/Cape.java create mode 100644 src/main/java/me/retucio/sputnik/cape/CapeManager.java create mode 100644 src/main/java/me/retucio/sputnik/config/BinarySerializer.java rename src/main/java/me/retucio/sputnik/event/{ => events}/JoinWorldEvent.java (81%) create mode 100644 src/main/java/me/retucio/sputnik/event/events/sputnik/LoadCapeManagerEvent.java create mode 100644 src/main/java/me/retucio/sputnik/mixin/CapeFeatureRendererMixin.java create mode 100644 src/main/java/me/retucio/sputnik/mixin/ElytraFeatureRendererMixin.java create mode 100644 src/main/java/me/retucio/sputnik/module/modules/player/Capes.java create mode 100644 src/main/resources/assets/sputnik/textures/capes/15th-anniversary.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/bedrock.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/birthday.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/cheapsh0t.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/cherry-blossom.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/chinese-translator.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/cobalt.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/copper.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/dannybstyle.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/experience-2025.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/eye-blossom.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/half-life.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/hollow-knight.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/julianclark.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/mcc-15.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/migrator.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/millionth-customer.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/minecon-2011.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/minecon-2012.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/minecon-2013.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/minecon-2015.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/minecon-2016.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/minecraft.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/mojang-old-old.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/mojang-old.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/mojang.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/mojira-mod.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/mrmessiah.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/pancakes.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/prismarine.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/realms-mapmaker.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/scrolls-champion.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/sputnik.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/tiktok.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/translator.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/turtle.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/twitch.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/valentine.png create mode 100644 src/main/resources/assets/sputnik/textures/capes/vanilla.png diff --git a/cambios.md b/cambios.md index 64f3481..e251d78 100644 --- a/cambios.md +++ b/cambios.md @@ -1,17 +1,18 @@ -# v2.3 +# v2.4 ## novedades -- renombrado el mod a sputnik porque esmegma o cameratweaks era un asco y sputnik ta to wapo +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 -- ahora tienes un múltiplo en críticos, es decir, un número por el que se multiplica la cantidad de partículas a generar -- ya no se guardan los ajustes mientras se cargan +- renombrado bien todo que lo hice con prisa ## problemas reconocidos diff --git a/gradle.properties b/gradle.properties index f3f3366..74a4a2b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -org.gradle.jvmargs=-Xmx1G +org.gradle.jvmargs=-Xmx2G org.gradle.parallel=true minecraft_version=1.21.11 @@ -8,6 +8,6 @@ loom_version=1.13-SNAPSHOT fabric_api_version=0.140.2+1.21.11 -mod_version=2.3 +mod_version=2.4 maven_group=me.retucio archives_base_name=sputnik \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/Sputnik.java b/src/main/java/me/retucio/sputnik/Sputnik.java index d6fea20..c846429 100644 --- a/src/main/java/me/retucio/sputnik/Sputnik.java +++ b/src/main/java/me/retucio/sputnik/Sputnik.java @@ -1,6 +1,7 @@ package me.retucio.sputnik; +import me.retucio.sputnik.cape.CapeManager; import me.retucio.sputnik.command.CommandManager; import me.retucio.sputnik.command.commands.BindCommand; @@ -9,14 +10,15 @@ 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; @@ -24,21 +26,19 @@ 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.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; @@ -86,6 +86,9 @@ public class Sputnik implements ClientModInitializer { Lists.init(); + CapeManager.INSTANCE = new CapeManager(); + EVENT_BUS.post(new LoadCapeManagerEvent()); + ModuleManager.INSTANCE = new ModuleManager(); EVENT_BUS.post(new LoadModuleManagerEvent()); diff --git a/src/main/java/me/retucio/sputnik/cape/Cape.java b/src/main/java/me/retucio/sputnik/cape/Cape.java new file mode 100644 index 0000000..7377cad --- /dev/null +++ b/src/main/java/me/retucio/sputnik/cape/Cape.java @@ -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('-', ' '); + } +} \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/cape/CapeManager.java b/src/main/java/me/retucio/sputnik/cape/CapeManager.java new file mode 100644 index 0000000..6f1e196 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/cape/CapeManager.java @@ -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 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 getCapes() { + return capes; + } +} \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/command/CommandManager.java b/src/main/java/me/retucio/sputnik/command/CommandManager.java index 5e38ecb..2dd47e3 100644 --- a/src/main/java/me/retucio/sputnik/command/CommandManager.java +++ b/src/main/java/me/retucio/sputnik/command/CommandManager.java @@ -2,6 +2,7 @@ package me.retucio.sputnik.command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.command.commands.*; import me.retucio.sputnik.event.SubscribeEvent; import net.minecraft.client.MinecraftClient; @@ -47,7 +48,7 @@ public class CommandManager { for (Command command : commands) { for (Method method : command.getClass().getDeclaredMethods()) { if (method.isAnnotationPresent(SubscribeEvent.class)) { - me.retucio.sputnik.Sputnik.EVENT_BUS.register(command); + Sputnik.EVENT_BUS.register(command); break; } } diff --git a/src/main/java/me/retucio/sputnik/command/args/EntityArgumentType.java b/src/main/java/me/retucio/sputnik/command/args/EntityArgumentType.java index 8ff83f2..5c17ce7 100644 --- a/src/main/java/me/retucio/sputnik/command/args/EntityArgumentType.java +++ b/src/main/java/me/retucio/sputnik/command/args/EntityArgumentType.java @@ -17,9 +17,10 @@ import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.regex.Pattern; -// para autocompletar entidades import static me.retucio.sputnik.Sputnik.mc; + +// para autocompletar entidades public class EntityArgumentType implements ArgumentType { public static final EntityArgumentType INSTANCE = new EntityArgumentType(); diff --git a/src/main/java/me/retucio/sputnik/command/commands/PurgeCommand.java b/src/main/java/me/retucio/sputnik/command/commands/PurgeCommand.java index b5cae1d..10d8696 100644 --- a/src/main/java/me/retucio/sputnik/command/commands/PurgeCommand.java +++ b/src/main/java/me/retucio/sputnik/command/commands/PurgeCommand.java @@ -31,7 +31,7 @@ public class PurgeCommand extends Command { ChatHud chatHud = mc.inGameHud.getChatHud(); synchronized (chatHud) { - List visibleMessages = ((IChatHud) chatHud).smegma$getVisibleMessages(); + List visibleMessages = ((IChatHud) chatHud).sputnik$getVisibleMessages(); int toRemove = Math.min(amount, visibleMessages.size()); for (int i = 0; i < toRemove; i++) visibleMessages.removeFirst(); diff --git a/src/main/java/me/retucio/sputnik/config/BinarySerializer.java b/src/main/java/me/retucio/sputnik/config/BinarySerializer.java new file mode 100644 index 0000000..2d343ce --- /dev/null +++ b/src/main/java/me/retucio/sputnik/config/BinarySerializer.java @@ -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 map) + throws IOException { + if (map == null) { + out.writeInt(0); + return; + } + out.writeInt(map.size()); + for (Map.Entry entry : map.entrySet()) { + writeString(out, entry.getKey()); + out.writeBoolean(entry.getValue()); + } + } + + private static Map readStringBooleanMap(DataInputStream in) + throws IOException { + int size = in.readInt(); + Map 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 map) + throws IOException { + if (map == null) { + out.writeInt(0); + return; + } + out.writeInt(map.size()); + for (Map.Entry entry : map.entrySet()) { + writeString(out, entry.getKey()); + writeSettingValue(out, entry.getValue()); + } + } + + private static Map readSettingsMap(DataInputStream in) + throws IOException { + int size = in.readInt(); + Map 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 readGenericMap(DataInputStream in) + throws IOException { + int size = in.readInt(); + Map 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 map) + throws IOException { + if (map == null) { + out.writeInt(0); + return; + } + out.writeInt(map.size()); + for (Map.Entry 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 readStringIntArrayMap(DataInputStream in) + throws IOException { + int size = in.readInt(); + Map 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 map) + throws IOException { + if (map == null) { + out.writeInt(0); + return; + } + out.writeInt(map.size()); + for (Map.Entry 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 readExtendableFrames(DataInputStream in) + throws IOException { + int size = in.readInt(); + Map 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 map) + throws IOException { + if (map == null) { + out.writeInt(0); + return; + } + out.writeInt(map.size()); + for (Map.Entry entry : map.entrySet()) { + writeString(out, entry.getKey()); + writeString(out, entry.getValue()); + } + } + + private static Map readStringStringMap(DataInputStream in) + throws IOException { + int size = in.readInt(); + Map 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"); + } +} diff --git a/src/main/java/me/retucio/sputnik/config/ClientConfig.java b/src/main/java/me/retucio/sputnik/config/ClientConfig.java index 2a6e866..1652c34 100644 --- a/src/main/java/me/retucio/sputnik/config/ClientConfig.java +++ b/src/main/java/me/retucio/sputnik/config/ClientConfig.java @@ -5,6 +5,8 @@ import me.retucio.sputnik.event.events.sputnik.*; import me.retucio.sputnik.module.settings.*; import me.retucio.sputnik.ui.screen.ClickGUI; +import java.io.Serial; +import java.io.Serializable; import java.util.HashMap; import java.util.Map; @@ -62,7 +64,7 @@ public class ClientConfig { case StringSetting s: value = s.getValue(); break; case ListSetting l: value = l.getConfigValues(); break; case ColorSetting c: value = c.getConfigValue(); break; - case OptionSetting o: value = o.getIndex(); break; + case OptionSetting o: value = o.getDisplayName(); break; default: break; } @@ -71,7 +73,6 @@ public class ClientConfig { @SubscribeEvent public void onOpenSettingsFrame(SettingsFrameEvent.Open event) { - if (settingsFrames.containsKey(event.getFrame().module.getName())) return; ConfigManager.setFramePosition(event.getFrame()); } @@ -119,5 +120,7 @@ public class ClientConfig { 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; + } } diff --git a/src/main/java/me/retucio/sputnik/config/ConfigManager.java b/src/main/java/me/retucio/sputnik/config/ConfigManager.java index f77b877..1795e6a 100644 --- a/src/main/java/me/retucio/sputnik/config/ConfigManager.java +++ b/src/main/java/me/retucio/sputnik/config/ConfigManager.java @@ -2,6 +2,7 @@ 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.*; @@ -10,20 +11,20 @@ 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.FileWriter; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; // 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("sputnik.json"); + 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; @@ -36,31 +37,44 @@ public class ConfigManager { if (!loaded) return; ensureConfig(); - try (FileWriter writer = new FileWriter(CONFIG_FILE)) { - GSON.toJson(config, writer); - me.retucio.sputnik.Sputnik.LOGGER.info("ajustes guardados"); + try { + BinarySerializer.writeConfig(config, CONFIG_FILE); + Sputnik.LOGGER.info("ajustes guardados"); } catch (IOException e) { - me.retucio.sputnik.Sputnik.LOGGER.error("error al guardar ajustes", e); + Sputnik.LOGGER.error("error al guardar ajustes", e); } } - // cargar configuraciones public static void load() { - me.retucio.sputnik.Sputnik.LOGGER.info("cargando ajustes..."); + Sputnik.LOGGER.info("cargando ajustes..."); - if (!CONFIG_FILE.exists()) { - ensureConfig(); - save(); - return; + 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); + } } - try (FileReader reader = new FileReader(CONFIG_FILE)) { - config = GSON.fromJson(reader, ClientConfig.class); - me.retucio.sputnik.Sputnik.LOGGER.info("ajustes cargados"); - } catch (IOException e) { - me.retucio.sputnik.Sputnik.LOGGER.error("no se pudieron cargar los ajustes, usando ajustes por defecto", e); - ensureConfig(); + // 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 @@ -75,7 +89,7 @@ public class ConfigManager { applyExtendableFrames(); loaded = true; - me.retucio.sputnik.Sputnik.LOGGER.info("ajustes aplicados"); + Sputnik.LOGGER.info("ajustes aplicados"); } // obtener configuración actual @@ -101,7 +115,10 @@ public class ConfigManager { public static void setFramePosition(SettingsFrame frame) { ensureConfig(); - config.settingsFrames.put(frame.module.getName(), new int[] {frame.getX(), frame.getY()}); + 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(); } @@ -160,7 +177,7 @@ public class ConfigManager { case ListSetting listSetting -> applyListSetting(listSetting, value); case ColorSetting colorSetting -> applyColorSetting(colorSetting, value); case OptionSetting optionSetting -> applyOptionSetting(optionSetting, value); - default -> me.retucio.sputnik.Sputnik.LOGGER.warn("watafac queseso: {}", setting.getClass().getSimpleName()); + default -> Sputnik.LOGGER.warn("watafac queseso: {}", setting.getClass().getSimpleName()); } } @@ -170,14 +187,14 @@ public class ConfigManager { module.setEnabled(config.moduleStates.get(module.getName())); } }); - me.retucio.sputnik.Sputnik.LOGGER.info("estados de módulos aplicados"); + Sputnik.LOGGER.info("estados de módulos aplicados"); } private static void applyModuleSettings() { ModuleManager.INSTANCE.getModules().forEach(module -> { module.getSettings().forEach(setting -> applySetting(module, setting)); }); - me.retucio.sputnik.Sputnik.LOGGER.info("ajustes de módulos aplicados"); + Sputnik.LOGGER.info("ajustes de módulos aplicados"); } private static void applySettingsFrames() { @@ -185,7 +202,7 @@ public class ConfigManager { Module module = ModuleManager.INSTANCE.getModuleByName(moduleName); ClickGUI.INSTANCE.openSettingsFrame(module, position[0], position[1]); }); - me.retucio.sputnik.Sputnik.LOGGER.info("estados de marcos de ajustes aplicados"); + Sputnik.LOGGER.info("estados de marcos de ajustes aplicados"); } private static void applySearchBarPosition() { @@ -193,7 +210,7 @@ public class ConfigManager { ClickGUI.INSTANCE.getSearchBar().setX(config.searchBarPosition[0]); ClickGUI.INSTANCE.getSearchBar().setY(config.searchBarPosition[1]); } - me.retucio.sputnik.Sputnik.LOGGER.info("posición de la barra de búsqueda aplicada"); + Sputnik.LOGGER.info("posición de la barra de búsqueda aplicada"); } private static void applyGuiSettings() { @@ -201,14 +218,14 @@ public class ConfigManager { ClientSettingsFrame.guiSettings.getSettings().forEach(setting -> { applySetting(ClientSettingsFrame.guiSettings, setting); }); - me.retucio.sputnik.Sputnik.LOGGER.info("ajustes del cliente aplicados"); + Sputnik.LOGGER.info("ajustes del cliente aplicados"); } private static void applyExtendableFrames() { applyExtendableFrame(ClickGUI.INSTANCE.getModulesFrame()); applyExtendableFrame(ClickGUI.INSTANCE.getGuiSettingsFrame()); - me.retucio.sputnik.Sputnik.LOGGER.info("posiciones de marcos extendibles aplicadasa"); + Sputnik.LOGGER.info("posiciones de marcos extendibles aplicadas"); ClickGUI.INSTANCE.refreshListButtons(); } @@ -299,9 +316,8 @@ public class ConfigManager { } private static void applyOptionSetting(OptionSetting setting, Object value) { - if (value instanceof Double) { - int index = ((Double) value).intValue(); - setting.setValue(setting.getOption(index)); + if (value instanceof String name) { + setting.setValueByName(name); } } @@ -316,10 +332,9 @@ public class ConfigManager { // ------------------ MÉTODOS DE AYUDA ------------------ private static boolean shouldApply() { - return me.retucio.sputnik.Sputnik.mc != null && ModuleManager.INSTANCE != null && config != null; + return Sputnik.mc != null && ModuleManager.INSTANCE != null && config != null; } - private static void ensureConfig() { if (config == null) config = new ClientConfig(); } diff --git a/src/main/java/me/retucio/sputnik/event/JoinWorldEvent.java b/src/main/java/me/retucio/sputnik/event/events/JoinWorldEvent.java similarity index 81% rename from src/main/java/me/retucio/sputnik/event/JoinWorldEvent.java rename to src/main/java/me/retucio/sputnik/event/events/JoinWorldEvent.java index 343fe38..c095403 100644 --- a/src/main/java/me/retucio/sputnik/event/JoinWorldEvent.java +++ b/src/main/java/me/retucio/sputnik/event/events/JoinWorldEvent.java @@ -1,5 +1,6 @@ -package me.retucio.sputnik.event; +package me.retucio.sputnik.event.events; +import me.retucio.sputnik.event.Event; import net.minecraft.client.world.ClientWorld; diff --git a/src/main/java/me/retucio/sputnik/event/events/sputnik/LoadCapeManagerEvent.java b/src/main/java/me/retucio/sputnik/event/events/sputnik/LoadCapeManagerEvent.java new file mode 100644 index 0000000..41c9183 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/event/events/sputnik/LoadCapeManagerEvent.java @@ -0,0 +1,6 @@ +package me.retucio.sputnik.event.events.sputnik; + +import me.retucio.sputnik.event.Event; + +// se genera cuando carga el gestor de capas +public class LoadCapeManagerEvent extends Event {} diff --git a/src/main/java/me/retucio/sputnik/mixin/CapeFeatureRendererMixin.java b/src/main/java/me/retucio/sputnik/mixin/CapeFeatureRendererMixin.java new file mode 100644 index 0000000..566f8c0 --- /dev/null +++ b/src/main/java/me/retucio/sputnik/mixin/CapeFeatureRendererMixin.java @@ -0,0 +1,26 @@ +package me.retucio.sputnik.mixin; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import me.retucio.sputnik.module.ModuleManager; +import me.retucio.sputnik.module.modules.player.Capes; +import net.minecraft.client.render.command.OrderedRenderCommandQueue; +import net.minecraft.client.render.entity.feature.CapeFeatureRenderer; +import net.minecraft.client.render.entity.state.PlayerEntityRenderState; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.AssetInfo; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(CapeFeatureRenderer.class) +public abstract class CapeFeatureRendererMixin { + + @ModifyExpressionValue(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/command/OrderedRenderCommandQueue;ILnet/minecraft/client/render/entity/state/PlayerEntityRenderState;FF)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/SkinTextures;cape()Lnet/minecraft/util/AssetInfo$TextureAsset;")) + private AssetInfo.TextureAsset modifyCapeTexture(AssetInfo.TextureAsset original, MatrixStack matrices, OrderedRenderCommandQueue entityRenderCommandQueue, int i, PlayerEntityRenderState state, float f, float g) { + Capes capes = ModuleManager.INSTANCE.getModuleByClass(Capes.class); + if (!capes.isEnabled()) return original; + + Identifier id = capes.cape.getValue().getId(); + return id == null ? original : new AssetInfo.TextureAssetInfo(id, id); + } +} \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/mixin/ChatHudLineMixin.java b/src/main/java/me/retucio/sputnik/mixin/ChatHudLineMixin.java index b183d64..725ff1b 100644 --- a/src/main/java/me/retucio/sputnik/mixin/ChatHudLineMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/ChatHudLineMixin.java @@ -22,17 +22,17 @@ public class ChatHudLineMixin implements IChatHudLine { private GameProfile sender; @Override - public String smegma$getText() { return content.toString(); } + public String sputnik$getText() { return content.toString(); } @Override - public int smegma$getId() { return id; } + public int sputnik$getId() { return id; } @Override - public void smegma$setId(int id) { this.id = id; } + public void sputnik$setId(int id) { this.id = id; } @Override - public GameProfile smegma$getSender() { return sender; } + public GameProfile sputnik$getSender() { return sender; } @Override - public void smegma$setSender(GameProfile profile) { this.sender = profile; } + public void sputnik$setSender(GameProfile profile) { this.sender = profile; } } diff --git a/src/main/java/me/retucio/sputnik/mixin/ChatHudLineVisibleMixin.java b/src/main/java/me/retucio/sputnik/mixin/ChatHudLineVisibleMixin.java index 669023e..c21c56f 100644 --- a/src/main/java/me/retucio/sputnik/mixin/ChatHudLineVisibleMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/ChatHudLineVisibleMixin.java @@ -25,7 +25,7 @@ public class ChatHudLineVisibleMixin implements IChatHudLineVisible { private boolean startOfEntry; @Override - public String smegma$getText() { + public String sputnik$getText() { StringBuilder sb = new StringBuilder(); content.accept((index, style, codePoint) -> { @@ -36,20 +36,20 @@ public class ChatHudLineVisibleMixin implements IChatHudLineVisible { } @Override - public int smegma$getId() { return id; } + public int sputnik$getId() { return id; } @Override - public void smegma$setId(int id) { this.id = id; } + public void sputnik$setId(int id) { this.id = id; } @Override - public GameProfile smegma$getSender() { return sender; } + public GameProfile sputnik$getSender() { return sender; } @Override - public void smegma$setSender(GameProfile profile) { this.sender = profile; } + public void sputnik$setSender(GameProfile profile) { this.sender = profile; } @Override - public boolean smegma$isStartOfEntry() { return startOfEntry; } + public boolean sputnik$isStartOfEntry() { return startOfEntry; } @Override - public void smegma$setStartOfEntry(boolean start) { this.startOfEntry = start; } + public void sputnik$setStartOfEntry(boolean start) { this.startOfEntry = start; } } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/mixin/ChatHudMixin.java b/src/main/java/me/retucio/sputnik/mixin/ChatHudMixin.java index 79b0d46..10c7aa8 100644 --- a/src/main/java/me/retucio/sputnik/mixin/ChatHudMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/ChatHudMixin.java @@ -62,7 +62,7 @@ public abstract class ChatHudMixin implements IChatHud { // métodos relacionados a las interfaces IChatHud, IChatHudLine, IChadHudLineVisible y IMessageHandler @Override - public void smegma$add(Text message, int id) { + public void sputnik$add(Text message, int id) { nextId = id; addMessage(message); nextId = 0; @@ -70,12 +70,12 @@ public abstract class ChatHudMixin implements IChatHud { @Inject(method = "addVisibleMessage", at = @At(value = "INVOKE", target = "Ljava/util/List;addFirst(Ljava/lang/Object;)V", shift = At.Shift.AFTER)) private void onAddMessageAfterNewChatHudLineVisible(ChatHudLine message, CallbackInfo ci) { - ((IChatHudLine) (Object) visibleMessages.getFirst()).smegma$setId(nextId); + ((IChatHudLine) (Object) visibleMessages.getFirst()).sputnik$setId(nextId); } @Inject(method = "addMessage(Lnet/minecraft/client/gui/hud/ChatHudLine;)V", at = @At(value = "INVOKE", target = "Ljava/util/List;addFirst(Ljava/lang/Object;)V", shift = At.Shift.AFTER)) private void onAddMessageAfterNewChatHudLine(ChatHudLine message, CallbackInfo ci) { - ((IChatHudLine) (Object) messages.getFirst()).smegma$setId(nextId); + ((IChatHudLine) (Object) messages.getFirst()).sputnik$setId(nextId); } @SuppressWarnings("DataFlowIssue") @@ -86,8 +86,8 @@ public abstract class ChatHudMixin implements IChatHud { IChatHudLineVisible iLine = (IChatHudLineVisible) (Object) line; - iLine.smegma$setSender(handler.smegma$getSender()); - iLine.smegma$setStartOfEntry(j == 0); + iLine.sputnik$setSender(handler.sputnik$getSender()); + iLine.sputnik$setStartOfEntry(j == 0); return line; } @@ -97,12 +97,12 @@ public abstract class ChatHudMixin implements IChatHud { IMessageHandler handler = (IMessageHandler) mc.getMessageHandler(); if (handler == null) return line; - ((IChatHudLine) (Object) line).smegma$setSender(handler.smegma$getSender()); + ((IChatHudLine) (Object) line).sputnik$setSender(handler.sputnik$getSender()); return line; } @Override - public List smegma$getVisibleMessages() { + public List sputnik$getVisibleMessages() { return visibleMessages; } @@ -118,10 +118,10 @@ public abstract class ChatHudMixin implements IChatHud { if (event.isCancelled()) { ci.cancel(); } else { - visibleMessages.removeIf(msg -> ((IChatHudLine) (Object) msg).smegma$getId() == nextId && nextId != 0); + visibleMessages.removeIf(msg -> ((IChatHudLine) (Object) msg).sputnik$getId() == nextId && nextId != 0); for (int i = messages.size() - 1; i > -1; i--) { - if (((IChatHudLine) (Object) messages.get(i)).smegma$getId() == nextId && nextId != 0) { + if (((IChatHudLine) (Object) messages.get(i)).sputnik$getId() == nextId && nextId != 0) { messages.remove(i); chatPlus.removeLine(i); } diff --git a/src/main/java/me/retucio/sputnik/mixin/ClientCommonNetworkHandlerMixin.java b/src/main/java/me/retucio/sputnik/mixin/ClientCommonNetworkHandlerMixin.java index b149d79..dd66760 100644 --- a/src/main/java/me/retucio/sputnik/mixin/ClientCommonNetworkHandlerMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/ClientCommonNetworkHandlerMixin.java @@ -45,7 +45,7 @@ public abstract class ClientCommonNetworkHandlerMixin { if (!bypassPack.isEnabled()) return; final Screen screen = cir.getReturnValue(); - ((IConfirmScreen) screen).smegma$setBypassAction(() -> { + ((IConfirmScreen) screen).sputnik$setBypassAction(() -> { client.setScreen(((ConfirmServerResourcePackScreenAccessor) screen).getParentScreen()); if (this.serverInfo != null) { this.serverInfo.setResourcePackPolicy(bypassPack.getPolicy()); diff --git a/src/main/java/me/retucio/sputnik/mixin/ConfirmScreenMixin.java b/src/main/java/me/retucio/sputnik/mixin/ConfirmScreenMixin.java index e20f826..e2b6a00 100644 --- a/src/main/java/me/retucio/sputnik/mixin/ConfirmScreenMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/ConfirmScreenMixin.java @@ -33,7 +33,7 @@ public abstract class ConfirmScreenMixin extends Screen implements IConfirmScree } @Override - public void smegma$setBypassAction(Runnable bypass) { + public void sputnik$setBypassAction(Runnable bypass) { this.bypassAction = bypass; } } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/mixin/ElytraFeatureRendererMixin.java b/src/main/java/me/retucio/sputnik/mixin/ElytraFeatureRendererMixin.java new file mode 100644 index 0000000..a7b21ee --- /dev/null +++ b/src/main/java/me/retucio/sputnik/mixin/ElytraFeatureRendererMixin.java @@ -0,0 +1,32 @@ +package me.retucio.sputnik.mixin; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import me.retucio.sputnik.module.ModuleManager; +import me.retucio.sputnik.module.modules.player.Capes; +import net.minecraft.client.render.command.OrderedRenderCommandQueue; +import net.minecraft.client.render.entity.feature.ElytraFeatureRenderer; +import net.minecraft.client.render.entity.feature.FeatureRenderer; +import net.minecraft.client.render.entity.feature.FeatureRendererContext; +import net.minecraft.client.render.entity.model.EntityModel; +import net.minecraft.client.render.entity.state.BipedEntityRenderState; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(ElytraFeatureRenderer.class) +public abstract class ElytraFeatureRendererMixin> extends FeatureRenderer { + + public ElytraFeatureRendererMixin(FeatureRendererContext context) { + super(context); + } + + @ModifyExpressionValue(method = "render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/command/OrderedRenderCommandQueue;ILnet/minecraft/client/render/entity/state/BipedEntityRenderState;FF)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/feature/ElytraFeatureRenderer;getTexture(Lnet/minecraft/client/render/entity/state/BipedEntityRenderState;)Lnet/minecraft/util/Identifier;")) + private Identifier modifyCapeTexture(Identifier original, MatrixStack matrices, OrderedRenderCommandQueue entityRenderCommandQueue, int i, S state, float f, float g) { + Capes capes = ModuleManager.INSTANCE.getModuleByClass(Capes.class); + if (!capes.isEnabled()) return original; + + Identifier id = capes.cape.getValue().getId(); + return id == null ? original : id; + } +} \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/mixin/GameRendererMixin.java b/src/main/java/me/retucio/sputnik/mixin/GameRendererMixin.java index 8d57889..0cf1f4f 100644 --- a/src/main/java/me/retucio/sputnik/mixin/GameRendererMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/GameRendererMixin.java @@ -84,7 +84,7 @@ public abstract class GameRendererMixin { float lastYaw = cameraEntity.lastYaw; float lastPitch = cameraEntity.lastPitch; - ((IVec3d) cameraEntity.getEntityPos()).smegma$set(pos.x, pos.y - cameraEntity.getEyeHeight(cameraEntity.getPose()), pos.z); + ((IVec3d) cameraEntity.getEntityPos()).sputnik$set(pos.x, pos.y - cameraEntity.getEyeHeight(cameraEntity.getPose()), pos.z); cameraEntity.lastX = prevPos.x; cameraEntity.lastY = prevPos.y - cameraEntity.getEyeHeight(cameraEntity.getPose()); cameraEntity.lastZ = prevPos.z; @@ -97,7 +97,7 @@ public abstract class GameRendererMixin { updateCrosshairTarget(tickDelta); freecamDone = false; - ((IVec3d) cameraEntity.getEntityPos()).smegma$set(x, y, z); + ((IVec3d) cameraEntity.getEntityPos()).sputnik$set(x, y, z); cameraEntity.lastX = lastX; cameraEntity.lastY = lastY; cameraEntity.lastZ = lastZ; diff --git a/src/main/java/me/retucio/sputnik/mixin/KeyboardMixin.java b/src/main/java/me/retucio/sputnik/mixin/KeyboardMixin.java index 7db7543..f92a381 100644 --- a/src/main/java/me/retucio/sputnik/mixin/KeyboardMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/KeyboardMixin.java @@ -1,5 +1,6 @@ package me.retucio.sputnik.mixin; +import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.event.events.KeyEvent; import net.minecraft.client.Keyboard; import net.minecraft.client.input.KeyInput; @@ -16,7 +17,7 @@ public abstract class KeyboardMixin { @Inject(method = "onKey", at = @At("HEAD"), cancellable = true) private void onKeyPress(long window, int action, KeyInput input, CallbackInfo ci) { if (input.key() == -1) return; - me.retucio.sputnik.Sputnik.INSTANCE.onKey(input.key(), action); + Sputnik.INSTANCE.onKey(input.key(), action); KeyEvent event = EVENT_BUS.post(new KeyEvent(input.key(), input.scancode(), action)); if (event.isCancelled()) ci.cancel(); } diff --git a/src/main/java/me/retucio/sputnik/mixin/MessageHandlerMixin.java b/src/main/java/me/retucio/sputnik/mixin/MessageHandlerMixin.java index 09a2dcd..ae857dd 100644 --- a/src/main/java/me/retucio/sputnik/mixin/MessageHandlerMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/MessageHandlerMixin.java @@ -31,7 +31,7 @@ public abstract class MessageHandlerMixin implements IMessageHandler { } @Override - public GameProfile smegma$getSender() { + public GameProfile sputnik$getSender() { return sender; } } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/mixin/MinecraftClientMixin.java b/src/main/java/me/retucio/sputnik/mixin/MinecraftClientMixin.java index 9d888cf..74ae770 100644 --- a/src/main/java/me/retucio/sputnik/mixin/MinecraftClientMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/MinecraftClientMixin.java @@ -1,7 +1,8 @@ package me.retucio.sputnik.mixin; import com.llamalad7.mixinextras.sugar.Local; -import me.retucio.sputnik.event.JoinWorldEvent; +import me.retucio.sputnik.Sputnik; +import me.retucio.sputnik.event.events.JoinWorldEvent; import me.retucio.sputnik.event.events.OpenScreenEvent; import me.retucio.sputnik.event.events.ShutdownEvent; import me.retucio.sputnik.event.events.TickEvent; @@ -20,8 +21,6 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import static me.retucio.sputnik.Sputnik.EVENT_BUS; -import static me.retucio.sputnik.Sputnik.mc; @Mixin(MinecraftClient.class) public abstract class MinecraftClientMixin { @@ -31,23 +30,23 @@ public abstract class MinecraftClientMixin { @Inject(method = "tick", at = @At("HEAD")) private void onTickPre(CallbackInfo ci) { - me.retucio.sputnik.Sputnik.INSTANCE.onTick(); - EVENT_BUS.post(new TickEvent.Pre()); + Sputnik.INSTANCE.onTick(); + Sputnik.EVENT_BUS.post(new TickEvent.Pre()); } @Inject(method = "tick", at = @At("TAIL")) private void onTickPost(CallbackInfo ci) { - EVENT_BUS.post(new TickEvent.Post()); + Sputnik.EVENT_BUS.post(new TickEvent.Post()); } @Inject(method = "stop", at = @At("HEAD")) private void onStop(CallbackInfo ci) { - EVENT_BUS.post(new ShutdownEvent()); + Sputnik.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)); + OpenScreenEvent event = Sputnik.EVENT_BUS.post(new OpenScreenEvent(screen)); if (event.isCancelled()) ci.cancel(); } @@ -58,8 +57,8 @@ public abstract class MinecraftClientMixin { @Inject(method = "doItemUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;getStackInHand(Lnet/minecraft/util/Hand;)Lnet/minecraft/item/ItemStack;"), cancellable = true) private void onUseItem(CallbackInfo ci, @Local Hand hand) { - if (mc.player == null) return; - UseItemEvent event = EVENT_BUS.post(new UseItemEvent(mc.player.getStackInHand(hand), hand)); + if (Sputnik.mc.player == null) return; + UseItemEvent event = Sputnik.EVENT_BUS.post(new UseItemEvent(Sputnik.mc.player.getStackInHand(hand), hand)); if (event.isCancelled()) ci.cancel(); } @@ -72,7 +71,7 @@ public abstract class MinecraftClientMixin { @Inject(method = "joinWorld", at = @At("HEAD"), cancellable = true) private void onJoinWorld(ClientWorld world, CallbackInfo ci) { - JoinWorldEvent event = EVENT_BUS.post(new JoinWorldEvent(world)); + JoinWorldEvent event = Sputnik.EVENT_BUS.post(new JoinWorldEvent(world)); if (event.isCancelled()) ci.cancel(); } } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/mixin/PlayerEntityMixin.java b/src/main/java/me/retucio/sputnik/mixin/PlayerEntityMixin.java index a5bf87f..85019cb 100644 --- a/src/main/java/me/retucio/sputnik/mixin/PlayerEntityMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/PlayerEntityMixin.java @@ -1,6 +1,7 @@ package me.retucio.sputnik.mixin; import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.event.events.ClipAtLedgeEvent; import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.modules.render.Nametags; @@ -56,7 +57,7 @@ public abstract class PlayerEntityMixin extends LivingEntity { @Inject(method = "clipAtLedge", at = @At("HEAD"), cancellable = true) private void clipAtLedge(CallbackInfoReturnable cir) { if (!getEntityWorld().isClient()) return; - ClipAtLedgeEvent event = me.retucio.sputnik.Sputnik.EVENT_BUS.post(new ClipAtLedgeEvent(isSneaking())); + ClipAtLedgeEvent event = Sputnik.EVENT_BUS.post(new ClipAtLedgeEvent(isSneaking())); cir.setReturnValue(event.isClipping()); } } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/mixin/Vec3dMixin.java b/src/main/java/me/retucio/sputnik/mixin/Vec3dMixin.java index 52cb8ac..ab3d767 100644 --- a/src/main/java/me/retucio/sputnik/mixin/Vec3dMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/Vec3dMixin.java @@ -20,20 +20,20 @@ public abstract class Vec3dMixin implements IVec3d { public double z; @Override - public void smegma$set(double x, double y, double z) { + public void sputnik$set(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } @Override - public void smegma$setXZ(double x, double z) { + public void sputnik$setXZ(double x, double z) { this.x = x; this.z = z; } @Override - public void smegma$setY(double y) { + public void sputnik$setY(double y) { this.y = y; } } diff --git a/src/main/java/me/retucio/sputnik/mixin/WorldRendererMixin.java b/src/main/java/me/retucio/sputnik/mixin/WorldRendererMixin.java index f384c83..be0e33e 100644 --- a/src/main/java/me/retucio/sputnik/mixin/WorldRendererMixin.java +++ b/src/main/java/me/retucio/sputnik/mixin/WorldRendererMixin.java @@ -2,6 +2,7 @@ package me.retucio.sputnik.mixin; import com.llamalad7.mixinextras.sugar.Local; import com.mojang.blaze3d.buffers.GpuBufferSlice; +import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.event.events.RenderWorldEvent; import me.retucio.sputnik.module.ModuleManager; import me.retucio.sputnik.module.modules.render.BlockOutline; @@ -45,7 +46,7 @@ public abstract class WorldRendererMixin { matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(camera.getPitch())); matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(camera.getYaw() + 180f)); - profiler.push(me.retucio.sputnik.Sputnik.MOD_ID + "-3d"); + profiler.push(Sputnik.MOD_ID + "-3d"); EVENT_BUS.post(new RenderWorldEvent(matrices, tickCounter, camera)); profiler.pop(); diff --git a/src/main/java/me/retucio/sputnik/module/Module.java b/src/main/java/me/retucio/sputnik/module/Module.java index 83fa144..0646122 100644 --- a/src/main/java/me/retucio/sputnik/module/Module.java +++ b/src/main/java/me/retucio/sputnik/module/Module.java @@ -1,5 +1,6 @@ package me.retucio.sputnik.module; +import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.config.ConfigManager; import me.retucio.sputnik.event.SubscribeEvent; import me.retucio.sputnik.event.events.sputnik.ToggleModuleEvent; @@ -78,24 +79,24 @@ public class Module { enabled = !enabled; if (enabled) onEnable(); else onDisable(); - me.retucio.sputnik.Sputnik.EVENT_BUS.post(new ToggleModuleEvent(this)); + Sputnik.EVENT_BUS.post(new ToggleModuleEvent(this)); } public void onEnable() { if (notify.isEnabled()) ChatUtil.info(getName() + " fue activado"); for (Method method : getClass().getDeclaredMethods()) if (method.isAnnotationPresent(SubscribeEvent.class)) { - me.retucio.sputnik.Sputnik.EVENT_BUS.register(this); + Sputnik.EVENT_BUS.register(this); break; } - me.retucio.sputnik.Sputnik.EVENT_BUS.post(new ToggleModuleEvent(this)); + Sputnik.EVENT_BUS.post(new ToggleModuleEvent(this)); } public void onDisable() { if (notify.isEnabled()) ChatUtil.info(getName() + " fue desactivado"); - if (me.retucio.sputnik.Sputnik.EVENT_BUS.isRegistered(this)) - me.retucio.sputnik.Sputnik.EVENT_BUS.unregister(this); - me.retucio.sputnik.Sputnik.EVENT_BUS.post(new ToggleModuleEvent(this)); + if (Sputnik.EVENT_BUS.isRegistered(this)) + Sputnik.EVENT_BUS.unregister(this); + Sputnik.EVENT_BUS.post(new ToggleModuleEvent(this)); } diff --git a/src/main/java/me/retucio/sputnik/module/ModuleManager.java b/src/main/java/me/retucio/sputnik/module/ModuleManager.java index 06c6684..771dfd0 100644 --- a/src/main/java/me/retucio/sputnik/module/ModuleManager.java +++ b/src/main/java/me/retucio/sputnik/module/ModuleManager.java @@ -1,5 +1,6 @@ package me.retucio.sputnik.module; +import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.event.SubscribeEvent; import me.retucio.sputnik.module.modules.camera.*; import me.retucio.sputnik.module.modules.client.HUD; @@ -38,6 +39,7 @@ public class ModuleManager { modules.add(new BossbarStack()); modules.add(new BreakingProgress()); modules.add(new BungeecordSpoofer()); + modules.add(new Capes()); modules.add(new ChatPlus()); modules.add(new ColoredSigns()); modules.add(new CritsPlus()); @@ -80,7 +82,7 @@ public class ModuleManager { for (Module module : getEnabledModules()) { for (Method method : module.getClass().getDeclaredMethods()) { if (method.isAnnotationPresent(SubscribeEvent.class)) { - me.retucio.sputnik.Sputnik.EVENT_BUS.register(module); + Sputnik.EVENT_BUS.register(module); break; } } diff --git a/src/main/java/me/retucio/sputnik/module/modules/misc/ChatPlus.java b/src/main/java/me/retucio/sputnik/module/modules/misc/ChatPlus.java index c4d35c1..2fefdb6 100644 --- a/src/main/java/me/retucio/sputnik/module/modules/misc/ChatPlus.java +++ b/src/main/java/me/retucio/sputnik/module/modules/misc/ChatPlus.java @@ -127,7 +127,7 @@ public class ChatPlus extends Module { if (!isEnabled() || !showHeads.isEnabled()) return; // dibujar la cabeza al principio del mensaje - if (((IChatHudLineVisible) (Object) line).smegma$isStartOfEntry()) + if (((IChatHudLineVisible) (Object) line).sputnik$isStartOfEntry()) drawTexture(context, (IChatHudLine) (Object) line, y, color); context.getMatrices().pushMatrix(); @@ -142,7 +142,7 @@ public class ChatPlus extends Module { @SuppressWarnings("DataFlowIssue") private void drawTexture(DrawContext context, IChatHudLine line, int y, int color) { - String text = line.smegma$getText().trim(); + String text = line.sputnik$getText().trim(); int startOffset = 0; @@ -173,7 +173,7 @@ public class ChatPlus extends Module { @SuppressWarnings("DataFlowIssue") private GameProfile getSender(IChatHudLine line, String text) { // obtener el jugador que envió un mensaje - GameProfile sender = line.smegma$getSender(); + GameProfile sender = line.sputnik$getSender(); if (sender == null) { Matcher usernameMatcher = usernameRegex.matcher(text); diff --git a/src/main/java/me/retucio/sputnik/module/modules/network/PacketDelay.java b/src/main/java/me/retucio/sputnik/module/modules/network/PacketDelay.java index cd75406..ef1f012 100644 --- a/src/main/java/me/retucio/sputnik/module/modules/network/PacketDelay.java +++ b/src/main/java/me/retucio/sputnik/module/modules/network/PacketDelay.java @@ -1,6 +1,6 @@ package me.retucio.sputnik.module.modules.network; -import me.retucio.sputnik.event.JoinWorldEvent; +import me.retucio.sputnik.event.events.JoinWorldEvent; import me.retucio.sputnik.event.SubscribeEvent; import me.retucio.sputnik.event.events.PacketEvent; import me.retucio.sputnik.module.Category; diff --git a/src/main/java/me/retucio/sputnik/module/modules/player/Capes.java b/src/main/java/me/retucio/sputnik/module/modules/player/Capes.java new file mode 100644 index 0000000..7775eea --- /dev/null +++ b/src/main/java/me/retucio/sputnik/module/modules/player/Capes.java @@ -0,0 +1,17 @@ +package me.retucio.sputnik.module.modules.player; + +import me.retucio.sputnik.cape.Cape; +import me.retucio.sputnik.cape.CapeManager; +import me.retucio.sputnik.module.Category; +import me.retucio.sputnik.module.Module; +import me.retucio.sputnik.module.settings.OptionSetting; + +public class Capes extends Module { + + public OptionSetting cape = addSetting(new OptionSetting<>("capa", "qué capa llevar puesta", + CapeManager.INSTANCE.getCapes(), CapeManager.INSTANCE.getCape("hollow-knight"))); + + public Capes() { + super("capas", "elige qué capa llevar (solo visible para ti)", Category.PLAYER); + } +} diff --git a/src/main/java/me/retucio/sputnik/module/modules/player/UIMove.java b/src/main/java/me/retucio/sputnik/module/modules/player/UIMove.java index 59c53fa..3102324 100644 --- a/src/main/java/me/retucio/sputnik/module/modules/player/UIMove.java +++ b/src/main/java/me/retucio/sputnik/module/modules/player/UIMove.java @@ -27,7 +27,7 @@ public class UIMove extends Module { private final ScreenHandlerType inventoryHandlerType = ScreenHandlerType.register( "player_inventory", GenericContainerScreenHandler::createGeneric9x3); private final ScreenHandlerType clickGuiHandlerType = ScreenHandlerType.register( - "camtweaks_clickgui", GenericContainerScreenHandler::createGeneric9x3); // 9x3 porque no importa (creo) + "sputnik_clickgui", GenericContainerScreenHandler::createGeneric9x3); // 9x3 porque no importa (creo) public UIMove() { super("moverse en interfaz", diff --git a/src/main/java/me/retucio/sputnik/module/settings/ColorSetting.java b/src/main/java/me/retucio/sputnik/module/settings/ColorSetting.java index d9d5924..195cb50 100644 --- a/src/main/java/me/retucio/sputnik/module/settings/ColorSetting.java +++ b/src/main/java/me/retucio/sputnik/module/settings/ColorSetting.java @@ -270,8 +270,8 @@ public class ColorSetting extends Setting { map.put("a", a); map.put("rb", rainbow); map.put("rs", rainbowSpeed); - map.put("sat", saturation); - map.put("bri", brightness); + map.put("sat", (double) saturation); + map.put("bri", (double) brightness); return map; } } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/module/settings/OptionSetting.java b/src/main/java/me/retucio/sputnik/module/settings/OptionSetting.java index bd5efc9..6735133 100644 --- a/src/main/java/me/retucio/sputnik/module/settings/OptionSetting.java +++ b/src/main/java/me/retucio/sputnik/module/settings/OptionSetting.java @@ -1,16 +1,15 @@ package me.retucio.sputnik.module.settings; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Consumer; public class OptionSetting extends Setting{ - List options = new ArrayList<>(); - Map displayNames; - T option; - T defaultOption; + private final List options; + private Map displayNames; + private T option; + private final T defaultOption; private Consumer updateListener; @@ -39,6 +38,20 @@ public class OptionSetting extends Setting{ if (updateListener != null) updateListener.accept(option); } + public void setValueByName(String name) { + for (T opt : options) { + if (displayNames != null && !displayNames.isEmpty()) { + if (displayNames.get(opt).equals(name)) { + setValue(opt); + return; + } + } else if (opt.toString().equals(name)) { + setValue(opt); + return; + } + } + } + public List getOptions() { return options; } @@ -64,6 +77,10 @@ public class OptionSetting extends Setting{ return index; } + public void setIndex(int index) { + setValue(getOption(index)); + } + public boolean is(T option) { return getValue() == option; } @@ -94,4 +111,8 @@ public class OptionSetting extends Setting{ if (displayNames != null) return displayNames.get(key); return key.toString(); } + + public String getDisplayName() { + return getDisplayName(option); + } } diff --git a/src/main/java/me/retucio/sputnik/module/settings/Setting.java b/src/main/java/me/retucio/sputnik/module/settings/Setting.java index 51e670b..abdca85 100644 --- a/src/main/java/me/retucio/sputnik/module/settings/Setting.java +++ b/src/main/java/me/retucio/sputnik/module/settings/Setting.java @@ -1,5 +1,6 @@ package me.retucio.sputnik.module.settings; +import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.event.events.sputnik.UpdateSettingEvent; import me.retucio.sputnik.module.Module; @@ -52,6 +53,6 @@ public abstract class Setting { } public void fireUpdateEvent() { - me.retucio.sputnik.Sputnik.EVENT_BUS.post(new UpdateSettingEvent(this, module.shouldSaveSettings())); + Sputnik.EVENT_BUS.post(new UpdateSettingEvent(this, module.shouldSaveSettings())); } } diff --git a/src/main/java/me/retucio/sputnik/ui/hud/ImageHudElement.java b/src/main/java/me/retucio/sputnik/ui/hud/ImageHudElement.java index 99269b4..5b2b949 100644 --- a/src/main/java/me/retucio/sputnik/ui/hud/ImageHudElement.java +++ b/src/main/java/me/retucio/sputnik/ui/hud/ImageHudElement.java @@ -1,5 +1,6 @@ package me.retucio.sputnik.ui.hud; +import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.module.modules.client.HUD; import me.retucio.sputnik.util.Colors; @@ -22,7 +23,7 @@ public abstract class ImageHudElement extends HudElement { public ImageHudElement(String id, int defaultX, int defaultY) { super(id, defaultX, defaultY); - this.textureId = Identifier.of(me.retucio.sputnik.Sputnik.MOD_ID, "textures/" + id); + this.textureId = Identifier.of(Sputnik.MOD_ID, "textures/" + id); this.w = imageWidth; this.h = imageHeight; } @@ -36,7 +37,7 @@ public abstract class ImageHudElement extends HudElement { try { // cargar - Identifier resourceId = Identifier.of(me.retucio.sputnik.Sputnik.MOD_ID, imagePath); + Identifier resourceId = Identifier.of(Sputnik.MOD_ID, imagePath); try (InputStream stream = mc.getResourceManager().getResource(resourceId).get().getInputStream()) { NativeImage image = NativeImage.read(stream); @@ -57,10 +58,10 @@ public abstract class ImageHudElement extends HudElement { mc.getTextureManager().registerTexture(textureId, texture); textureLoaded = true; - me.retucio.sputnik.Sputnik.LOGGER.info("textura para el elemento del HUD {} cargada", getId()); + Sputnik.LOGGER.info("textura para el elemento del HUD {} cargada", getId()); } } catch (Exception e) { - me.retucio.sputnik.Sputnik.LOGGER.error("no se pudo cargar la textura para el elemento del HUD {}: {}", getId(), e.getMessage()); + Sputnik.LOGGER.error("no se pudo cargar la textura para el elemento del HUD {}: {}", getId(), e.getMessage()); } } diff --git a/src/main/java/me/retucio/sputnik/ui/screen/ClickGUI.java b/src/main/java/me/retucio/sputnik/ui/screen/ClickGUI.java index 00a3bff..57b23fb 100644 --- a/src/main/java/me/retucio/sputnik/ui/screen/ClickGUI.java +++ b/src/main/java/me/retucio/sputnik/ui/screen/ClickGUI.java @@ -1,5 +1,6 @@ package me.retucio.sputnik.ui.screen; +import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.event.SubscribeEvent; import me.retucio.sputnik.event.events.KeyEvent; import me.retucio.sputnik.event.events.MouseClickEvent; @@ -57,7 +58,7 @@ public class ClickGUI extends Screen { public ClickGUI() { super(Text.of("interfaz")); settingsFrames.add(guiSettingsFrame); - me.retucio.sputnik.Sputnik.EVENT_BUS.register(this); + Sputnik.EVENT_BUS.register(this); } @Override @@ -179,9 +180,9 @@ public class ClickGUI extends Screen { @SubscribeEvent public void onMouseMiddleButton(MouseClickEvent event) { // mover todos los marcos a un punto visible al presionar shift + la rueda del ratón - if (me.retucio.sputnik.Sputnik.mc.currentScreen != this || event.getButton() != 2 || !KeyUtil.isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT)) return; + if (Sputnik.mc.currentScreen != this || event.getButton() != 2 || !KeyUtil.isKeyDown(GLFW.GLFW_KEY_LEFT_SHIFT)) return; - int h = me.retucio.sputnik.Sputnik.mc.getWindow().getScaledHeight(); + int h = Sputnik.mc.getWindow().getScaledHeight(); int minY = Math.min(modulesFrame.getY(), settingsFrames.stream().mapToInt(Frame::getY).min().orElse(modulesFrame.getY())); int maxY = Math.max(modulesFrame.getY() + modulesFrame.getH(), settingsFrames.stream().mapToInt(sf -> sf.getY() + sf.getH()).max().orElse(modulesFrame.getY() + modulesFrame.getH())); int correction = minY < 0 ? -minY + 4 : (maxY > h ? h - maxY - 4 : 0); @@ -247,10 +248,18 @@ public class ClickGUI extends Screen { // abrir un marco donde se encuentran los ajustes del módulo deseado public void openSettingsFrame(Module module, int x, int y) { - if (isSettingsFrameOpen(module)) return; // permitir un solo marco de ajustes por módulo - SettingsFrame frame = new SettingsFrame(module, x, y, 120, 20); + for (SettingsFrame frame : settingsFrames) { + if (frame.module == module) { + frame.setX(x); + frame.setY(y); + return; + } + } + + SettingsFrame frame = new SettingsFrame(module, x, y, 100, 20); settingsFrames.add(frame); - me.retucio.sputnik.Sputnik.EVENT_BUS.post(new SettingsFrameEvent.Open(frame)); + + Sputnik.EVENT_BUS.post(new SettingsFrameEvent.Open(frame)); } // abrir un marco de ajustes específicamente para ajustes de selección múltiple @@ -268,7 +277,7 @@ public class ClickGUI extends Screen { if ((sf instanceof ColorPickerFrame cpf && cpf.dummyModule == module) // para los selectores de colores || (!(sf instanceof ColorPickerFrame) && sf.module == module)) { // lógica muy mierdas, lo sé, pero paso de hacerlo bien toRemove.add(sf); - me.retucio.sputnik.Sputnik.EVENT_BUS.post(new SettingsFrameEvent.Close(sf)); + Sputnik.EVENT_BUS.post(new SettingsFrameEvent.Close(sf)); unselect(sf); } } diff --git a/src/main/java/me/retucio/sputnik/ui/screen/PreviewScreen.java b/src/main/java/me/retucio/sputnik/ui/screen/PreviewScreen.java index e3ec257..899acee 100644 --- a/src/main/java/me/retucio/sputnik/ui/screen/PreviewScreen.java +++ b/src/main/java/me/retucio/sputnik/ui/screen/PreviewScreen.java @@ -1,5 +1,6 @@ package me.retucio.sputnik.ui.screen; +import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.module.modules.misc.ShulkerPeek; import me.retucio.sputnik.ui.widgets.frames.settings.ClientSettingsFrame; import me.retucio.sputnik.util.Colors; @@ -19,7 +20,7 @@ import java.util.List; public class PreviewScreen extends Screen { - private static final Identifier TEXTURE = Identifier.of(me.retucio.sputnik.Sputnik.MOD_ID, "textures/gui/preview.png"); + private static final Identifier TEXTURE = Identifier.of(Sputnik.MOD_ID, "textures/gui/preview.png"); private int x, y; private final int bgWidth = 176; diff --git a/src/main/java/me/retucio/sputnik/ui/widgets/frames/ModuleFrame.java b/src/main/java/me/retucio/sputnik/ui/widgets/frames/ModuleFrame.java index 9b89142..00af7ec 100644 --- a/src/main/java/me/retucio/sputnik/ui/widgets/frames/ModuleFrame.java +++ b/src/main/java/me/retucio/sputnik/ui/widgets/frames/ModuleFrame.java @@ -1,5 +1,6 @@ package me.retucio.sputnik.ui.widgets.frames; +import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.event.events.sputnik.ModuleFrameEvent; import me.retucio.sputnik.module.Module; import me.retucio.sputnik.module.ModuleManager; @@ -101,7 +102,7 @@ public class ModuleFrame extends Frame { dragY = mouseY - y; } else if (button == 1) { // clic derecho para extenderlo extended = !extended; - me.retucio.sputnik.Sputnik.EVENT_BUS.post(new ModuleFrameEvent.Extend()); + Sputnik.EVENT_BUS.post(new ModuleFrameEvent.Extend()); } } @@ -132,7 +133,7 @@ public class ModuleFrame extends Frame { } if (isHovered(mouseX, mouseY)) - me.retucio.sputnik.Sputnik.EVENT_BUS.post(new ModuleFrameEvent.Move()); + Sputnik.EVENT_BUS.post(new ModuleFrameEvent.Move()); } @Override diff --git a/src/main/java/me/retucio/sputnik/ui/widgets/frames/SettingsFrame.java b/src/main/java/me/retucio/sputnik/ui/widgets/frames/SettingsFrame.java index 7611f57..9f482d4 100644 --- a/src/main/java/me/retucio/sputnik/ui/widgets/frames/SettingsFrame.java +++ b/src/main/java/me/retucio/sputnik/ui/widgets/frames/SettingsFrame.java @@ -1,5 +1,6 @@ package me.retucio.sputnik.ui.widgets.frames; +import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.event.events.sputnik.SettingsFrameEvent; import me.retucio.sputnik.module.Module; import me.retucio.sputnik.module.settings.*; @@ -130,7 +131,9 @@ public class SettingsFrame extends Frame> { super.mouseReleased(mouseX, mouseY, button); if (button == 0) { dragging = false; - me.retucio.sputnik.Sputnik.EVENT_BUS.post(new SettingsFrameEvent.Move(this)); + + if (isHovered(mouseX, mouseY)) + Sputnik.EVENT_BUS.post(new SettingsFrameEvent.Move(this)); } for (SettingButton sb : visibleButtons) @@ -143,9 +146,6 @@ public class SettingsFrame extends Frame> { sb.mouseDragged(mouseX, mouseY); } - @Override - public void onKey(int key, int action) {} - @Override public void updateWidth() { // asegurarse de que todos los botones caben en el marco, haciendo que la anchura se ajuste al texto más largo diff --git a/src/main/java/me/retucio/sputnik/ui/widgets/frames/settings/ClientSettingsFrame.java b/src/main/java/me/retucio/sputnik/ui/widgets/frames/settings/ClientSettingsFrame.java index 9d7dd28..e46a07a 100644 --- a/src/main/java/me/retucio/sputnik/ui/widgets/frames/settings/ClientSettingsFrame.java +++ b/src/main/java/me/retucio/sputnik/ui/widgets/frames/settings/ClientSettingsFrame.java @@ -1,5 +1,6 @@ package me.retucio.sputnik.ui.widgets.frames.settings; +import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.event.events.sputnik.GUISettingsFrameEvent; import me.retucio.sputnik.module.modules.client.GUI; import me.retucio.sputnik.ui.widgets.frames.SettingsFrame; @@ -65,7 +66,7 @@ public class ClientSettingsFrame extends SettingsFrame { dragY = mouseY - y; } else if (button == 1) { extended = !extended; - me.retucio.sputnik.Sputnik.EVENT_BUS.post(new GUISettingsFrameEvent.Extend()); + Sputnik.EVENT_BUS.post(new GUISettingsFrameEvent.Extend()); } } @@ -78,7 +79,7 @@ public class ClientSettingsFrame extends SettingsFrame { public void mouseReleased(int mouseX, int mouseY, int button) { ClickGUI.INSTANCE.unselect(this); if (button == 0 && dragging) - me.retucio.sputnik.Sputnik.EVENT_BUS.post(new GUISettingsFrameEvent.Move()); + Sputnik.EVENT_BUS.post(new GUISettingsFrameEvent.Move()); super.mouseReleased(mouseX, mouseY, button); } diff --git a/src/main/java/me/retucio/sputnik/util/ChatUtil.java b/src/main/java/me/retucio/sputnik/util/ChatUtil.java index 13af915..c23dd7b 100644 --- a/src/main/java/me/retucio/sputnik/util/ChatUtil.java +++ b/src/main/java/me/retucio/sputnik/util/ChatUtil.java @@ -1,5 +1,6 @@ package me.retucio.sputnik.util; +import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.event.SubscribeEvent; import me.retucio.sputnik.event.events.sputnik.LoadModuleManagerEvent; import me.retucio.sputnik.ui.widgets.frames.settings.ClientSettingsFrame; @@ -24,7 +25,7 @@ public class ChatUtil { } public static void addMessage(Text text) { - if (mc.inGameHud == null || !me.retucio.sputnik.Sputnik.settingsApplied) return; + if (mc.inGameHud == null || !Sputnik.settingsApplied) return; mc.inGameHud.getChatHud().addMessage(text); } @@ -87,6 +88,6 @@ public class ChatUtil { } public static String getDefaultPrefix() { - return Colors.getFormatting(Colors.mainColor) + "[smegma] "; + return Colors.getFormatting(Colors.mainColor) + "[sputnik] "; } } diff --git a/src/main/java/me/retucio/sputnik/util/interfaces/IChatHud.java b/src/main/java/me/retucio/sputnik/util/interfaces/IChatHud.java index d7e913f..c46f5ae 100644 --- a/src/main/java/me/retucio/sputnik/util/interfaces/IChatHud.java +++ b/src/main/java/me/retucio/sputnik/util/interfaces/IChatHud.java @@ -7,7 +7,7 @@ import java.util.List; public interface IChatHud { - void smegma$add(Text message, int id); + void sputnik$add(Text message, int id); - List smegma$getVisibleMessages(); + List sputnik$getVisibleMessages(); } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/util/interfaces/IChatHudLine.java b/src/main/java/me/retucio/sputnik/util/interfaces/IChatHudLine.java index 8313318..e4be705 100644 --- a/src/main/java/me/retucio/sputnik/util/interfaces/IChatHudLine.java +++ b/src/main/java/me/retucio/sputnik/util/interfaces/IChatHudLine.java @@ -4,11 +4,11 @@ import com.mojang.authlib.GameProfile; public interface IChatHudLine { - String smegma$getText(); + String sputnik$getText(); - int smegma$getId(); - void smegma$setId(int id); + int sputnik$getId(); + void sputnik$setId(int id); - GameProfile smegma$getSender(); - void smegma$setSender(GameProfile profile); + GameProfile sputnik$getSender(); + void sputnik$setSender(GameProfile profile); } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/util/interfaces/IChatHudLineVisible.java b/src/main/java/me/retucio/sputnik/util/interfaces/IChatHudLineVisible.java index 1482b3f..c60cff2 100644 --- a/src/main/java/me/retucio/sputnik/util/interfaces/IChatHudLineVisible.java +++ b/src/main/java/me/retucio/sputnik/util/interfaces/IChatHudLineVisible.java @@ -2,6 +2,6 @@ package me.retucio.sputnik.util.interfaces; public interface IChatHudLineVisible extends IChatHudLine { - boolean smegma$isStartOfEntry(); - void smegma$setStartOfEntry(boolean start); + boolean sputnik$isStartOfEntry(); + void sputnik$setStartOfEntry(boolean start); } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/util/interfaces/IConfirmScreen.java b/src/main/java/me/retucio/sputnik/util/interfaces/IConfirmScreen.java index 938eccb..6e303d7 100644 --- a/src/main/java/me/retucio/sputnik/util/interfaces/IConfirmScreen.java +++ b/src/main/java/me/retucio/sputnik/util/interfaces/IConfirmScreen.java @@ -2,5 +2,5 @@ package me.retucio.sputnik.util.interfaces; public interface IConfirmScreen { - void smegma$setBypassAction(Runnable action); + void sputnik$setBypassAction(Runnable action); } diff --git a/src/main/java/me/retucio/sputnik/util/interfaces/IMessageHandler.java b/src/main/java/me/retucio/sputnik/util/interfaces/IMessageHandler.java index bb0ada7..ed468d1 100644 --- a/src/main/java/me/retucio/sputnik/util/interfaces/IMessageHandler.java +++ b/src/main/java/me/retucio/sputnik/util/interfaces/IMessageHandler.java @@ -4,5 +4,5 @@ import com.mojang.authlib.GameProfile; public interface IMessageHandler { - GameProfile smegma$getSender(); + GameProfile sputnik$getSender(); } diff --git a/src/main/java/me/retucio/sputnik/util/interfaces/IVec3d.java b/src/main/java/me/retucio/sputnik/util/interfaces/IVec3d.java index 85fad3e..10aad54 100644 --- a/src/main/java/me/retucio/sputnik/util/interfaces/IVec3d.java +++ b/src/main/java/me/retucio/sputnik/util/interfaces/IVec3d.java @@ -5,10 +5,10 @@ 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 sputnik$set(double x, double y, double z); + default void sputnik$set(Vec3i vec) { sputnik$set(vec.getX(), vec.getY(), vec.getZ()); } + default void sputnik$set(Vector3d vec) { sputnik$set(vec.x, vec.y, vec.z); } - void smegma$setXZ(double x, double z); - void smegma$setY(double y); + void sputnik$setXZ(double x, double z); + void sputnik$setY(double y); } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/util/interfaces/TextVisitor.java b/src/main/java/me/retucio/sputnik/util/interfaces/TextVisitor.java index 16440b3..a0731b2 100644 --- a/src/main/java/me/retucio/sputnik/util/interfaces/TextVisitor.java +++ b/src/main/java/me/retucio/sputnik/util/interfaces/TextVisitor.java @@ -23,14 +23,6 @@ public interface TextVisitor { return text.visit((style, string) -> visitor.accept(queue.remove(), style, string), baseStyle); } - /** - * Collapses the tree of {@link Text} siblings into a one dimensional FIFO {@link Queue}. To match the behaviours of - * the {@link Text#visit(StringVisitable.Visitor)} and {@link Text#visit(StringVisitable.StyledVisitor, Style)} - * methods, texts with empty contents (created from {@link Text#empty()}) are ignored but their siblings are still - * processed. - * @param text the text - * @return the text and its siblings in the order they appear when rendered. - */ static ArrayDeque collectSiblings(Text text) { ArrayDeque queue = new ArrayDeque<>(); collectSiblings(text, queue); diff --git a/src/main/java/me/retucio/sputnik/util/render/GlintRenderLayer.java b/src/main/java/me/retucio/sputnik/util/render/GlintRenderLayer.java index 72bc9c9..96cdf70 100644 --- a/src/main/java/me/retucio/sputnik/util/render/GlintRenderLayer.java +++ b/src/main/java/me/retucio/sputnik/util/render/GlintRenderLayer.java @@ -3,6 +3,7 @@ package me.retucio.sputnik.util.render; import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.vertex.VertexFormat; import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; +import me.retucio.sputnik.Sputnik; import me.retucio.sputnik.module.modules.render.GlintPlus; import net.minecraft.client.gl.RenderPipelines; import net.minecraft.client.render.*; @@ -55,7 +56,7 @@ public class GlintRenderLayer extends RenderLayer { } private static RenderLayer buildGlintRenderLayer(String name) { - final Identifier res = Identifier.of(me.retucio.sputnik.Sputnik.MOD_ID, "textures/misc/glint_" + name.toLowerCase() + ".png"); + final Identifier res = Identifier.of(Sputnik.MOD_ID, "textures/misc/glint_" + name.toLowerCase() + ".png"); return RenderLayer.of("glint_" + name, RenderSetup.builder(RenderPipelines.GLINT) .texture("Sampler0", res) @@ -66,7 +67,7 @@ public class GlintRenderLayer extends RenderLayer { } private static RenderLayer buildEntityGlintRenderLayer(String name) { - final Identifier res = Identifier.of(me.retucio.sputnik.Sputnik.MOD_ID, "textures/misc/glint_" + name.toLowerCase() + ".png"); + final Identifier res = Identifier.of(Sputnik.MOD_ID, "textures/misc/glint_" + name.toLowerCase() + ".png"); return RenderLayer.of("entity_glint_" + name, RenderSetup.builder(RenderPipelines.GLINT) .texture("Sampler0", res) @@ -78,7 +79,7 @@ public class GlintRenderLayer extends RenderLayer { private static RenderLayer buildArmorEntityGlintRenderLayer(String name) { - final Identifier res = Identifier.of(me.retucio.sputnik.Sputnik.MOD_ID, "textures/misc/glint_" + name.toLowerCase() + ".png"); + final Identifier res = Identifier.of(Sputnik.MOD_ID, "textures/misc/glint_" + name.toLowerCase() + ".png"); return RenderLayer.of("armor_glint_" + name, RenderSetup.builder(RenderPipelines.GLINT) .texture("Sampler0", res) diff --git a/src/main/java/me/retucio/sputnik/util/render/Layers.java b/src/main/java/me/retucio/sputnik/util/render/Layers.java index 11cc095..07c5e39 100644 --- a/src/main/java/me/retucio/sputnik/util/render/Layers.java +++ b/src/main/java/me/retucio/sputnik/util/render/Layers.java @@ -1,5 +1,6 @@ package me.retucio.sputnik.util.render; +import me.retucio.sputnik.Sputnik; import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderSetup; @@ -31,11 +32,11 @@ public class Layers { } static { - LINES = RenderLayer.of(me.retucio.sputnik.Sputnik.MOD_ID + "_lines", RenderSetup.builder(LINES_PIPELINE).build()); - LINES_CULL = RenderLayer.of(me.retucio.sputnik.Sputnik.MOD_ID + "_lines_cull", RenderSetup.builder(LINES_CULL_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()); - QUADS = RenderLayer.of(me.retucio.sputnik.Sputnik.MOD_ID + "_quads", RenderSetup.builder(QUADS_PIPELINE).build()); - QUADS_CULL = RenderLayer.of(me.retucio.sputnik.Sputnik.MOD_ID + "quads_cull", RenderSetup.builder(QUADS_CULL_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()); } } \ No newline at end of file diff --git a/src/main/java/me/retucio/sputnik/util/render/Pipelines.java b/src/main/java/me/retucio/sputnik/util/render/Pipelines.java index f2204ad..a63bcf5 100644 --- a/src/main/java/me/retucio/sputnik/util/render/Pipelines.java +++ b/src/main/java/me/retucio/sputnik/util/render/Pipelines.java @@ -4,6 +4,7 @@ import com.mojang.blaze3d.pipeline.BlendFunction; import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.platform.DepthTestFunction; import com.mojang.blaze3d.vertex.VertexFormat; +import me.retucio.sputnik.Sputnik; import net.minecraft.client.gl.RenderPipelines; import net.minecraft.client.render.VertexFormats; import net.minecraft.util.Identifier; @@ -12,7 +13,7 @@ import net.minecraft.util.Identifier; public class Pipelines { static final RenderPipeline LINES_PIPELINE = RenderPipeline.builder(RenderPipelines.RENDERTYPE_LINES_SNIPPET) - .withLocation(Identifier.of(me.retucio.sputnik.Sputnik.MOD_ID, "pipeline/lines")) + .withLocation(Identifier.of(Sputnik.MOD_ID, "pipeline/lines")) .withVertexFormat(VertexFormats.POSITION_COLOR_NORMAL_LINE_WIDTH, VertexFormat.DrawMode.LINES) .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) .withDepthWrite(false) @@ -20,7 +21,7 @@ public class Pipelines { .build(); static final RenderPipeline LINES_CULL_PIPELINE = RenderPipeline.builder(RenderPipelines.RENDERTYPE_LINES_SNIPPET) - .withLocation(Identifier.of(me.retucio.sputnik.Sputnik.MOD_ID, "pipeline/lines_cull")) + .withLocation(Identifier.of(Sputnik.MOD_ID, "pipeline/lines_cull")) .withVertexFormat(VertexFormats.POSITION_COLOR_NORMAL_LINE_WIDTH, VertexFormat.DrawMode.LINES) .withDepthTestFunction(DepthTestFunction.LEQUAL_DEPTH_TEST) .withDepthWrite(false) @@ -28,7 +29,7 @@ public class Pipelines { .build(); static final RenderPipeline QUADS_PIPELINE = RenderPipeline.builder(RenderPipelines.POSITION_COLOR_SNIPPET) - .withLocation(Identifier.of(me.retucio.sputnik.Sputnik.MOD_ID, "pipeline/quads")) + .withLocation(Identifier.of(Sputnik.MOD_ID, "pipeline/quads")) .withVertexFormat(VertexFormats.POSITION_COLOR, VertexFormat.DrawMode.QUADS) .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) .withBlend(BlendFunction.TRANSLUCENT) @@ -37,7 +38,7 @@ public class Pipelines { .build(); static final RenderPipeline QUADS_CULL_PIPELINE = RenderPipeline.builder(RenderPipelines.POSITION_COLOR_SNIPPET) - .withLocation(Identifier.of(me.retucio.sputnik.Sputnik.MOD_ID, "pipeline/quads_cull")) + .withLocation(Identifier.of(Sputnik.MOD_ID, "pipeline/quads_cull")) .withVertexFormat(VertexFormats.POSITION_COLOR, VertexFormat.DrawMode.QUADS) .withDepthTestFunction(DepthTestFunction.LEQUAL_DEPTH_TEST) .withBlend(BlendFunction.TRANSLUCENT) diff --git a/src/main/resources/assets/sputnik/textures/capes/15th-anniversary.png b/src/main/resources/assets/sputnik/textures/capes/15th-anniversary.png new file mode 100644 index 0000000000000000000000000000000000000000..5e05cf739f73e691cec453384f33230ae58004fe GIT binary patch literal 1105 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-V_;xdG`FsWfq_NB)5S3)qw(#Gc#r6C znd9~=r(3i{Om(PUeVVoPo&Symg-cCq1tMR1#e12b@>trFm;d%MTXTlIIjj6HlZF1N zJ$gO1C$_ck+!%OJ$wf(nJ8|}cg5bxdK1oRqQI4D1cNg#Jyl8g)$kd7FFaNwdr~2QY zy8WO3&XpEFFjqaitK0Z=&bpl7&TsF;s@SU{=0vhh_Ann;E>8JVs~cuDW5ew~>qU!C zrvE?Lw(98DZ|qauVO_m8CCdoSm9`ElFD6v|uZrz{~(Ua|f$zqr$>f zdVHmKD~pz9NJ*#k{8_lN*2}tcRprmZoM}xPi(f2|JA3x>{B1Ip2ksa#+&F9c{)p{j((DMI@en6Z0sey?Q;{ZGi(iAe7bzrvTe_oY*u@@=Hwrzrq}(y zzNMZF41ALwmt&IrMEjPcvi@Ryv#JSiTdrQQ^34qsTd`IuYc<>52~j5(WyKwTEZ3$a zHCy|3(1SmT@(lgQg)ZHTxjD^!@x3?CW<{P|d-=+@7^}p*s+D@hnochz8}gGbSD0+? zsqrcfHYi(@Wsp$neKzXQmqk7&ROg58ol$aaiE!w)lTEjN=c^@HCYV(2j_}qEcylbl zMLqdR>XX*mm>;{WXUyLA%ud|ubl$p|@9TeTJu)xe<4TENYGGQWTg=rxMV}L78)x%7 z&1eh#^=d6++0*UYjotPog|1*VVm5M%<2pO@a~#*Ru#;(h%EHgYg<|;Pm>x{8)c*9a z+@3+<*XQjFEX-Duk7S!ISJHD}S;IQV^1bSpg?tCTgmd~NAC3wUsQX{N(1rO=qP*s{ zk6+)%&I^jzf4@dRE3CS5rcBpvz5kV-({>dtPw%l|$y&|q^^C%Sh7d}b{X5Kb_x8lU?grgd@||Dr{q8%quH};T1?LxQ%Nv3gZd^WFV9`tay2;XCSFCMN zo$)QdeX(5QB2-5|DkQPO&eLyRAn7x#&oys3Y&>)(p?5ICUy&+LKzz4_XP=8=Bv-wzDpk^4qhc+t|uF!(xy z-p(?q+|3bo%AfCOY?<^L?_k|$E2G(DL?^O7m7XipwY%>#^IFGdHeM-{j0+16{-0XU a;B@)N`Nz6@YZw?97(8A5T-G@yGywnx%^pGk literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/bedrock.png b/src/main/resources/assets/sputnik/textures/capes/bedrock.png new file mode 100644 index 0000000000000000000000000000000000000000..3b5cad3b5c67c98d85ebc7c77d27987393d1cdad GIT binary patch literal 572 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-V_;xdG`FsWfq{wH)5S5QBJS<OpTg|p&7^bG3qDMDlYW0|-puJU z9eEBr&Oaai`1IkYKPskg`=R?ShUc(WxApa`n%$CD5BcBtefeg}zWqCu4i;F{-SMA4 zQOC{Tk63m7`t#v4f1m6;^7r@MCHWRYviswvt0in>XxJmPN2;dY*fr?k0dKj-*54`? zSr*$bf8x3P%y|<*-3P`B*S;v8k7nXvOjvbf=O>dk1N&fI)&+f2Y+lPWT>1Vkz4wE8 zs58?cwY3WvM2^HW6>wOI|MA(%ATV9nh9M$&)=HOXrh*UL988DLhjucGPoK=gIN`=h z`{pyfVhl9`a*8Lu*RAtnm@!9I=BTkJ!?DJUM`sy5PHJ=XUOXSF$#i|+_wCY+1`ReR z&oX%Id_4P!IqLzL3oStmLG$?(zU^Lb8pQDMvt>(&OM=9a=qi()A~d zPo8%zo&Jo$fc+bb!^-6hChHlN&SMs+zQ&N?pUK_gVRxO$wEX?DSqgv8ZeAvu{qCNx zsiT~B(K;L7y3PN&qE~P8uWt%qE;pab=P2T-mo};K!{7gYTN#}8d_3wf@9g)oQW*H_ bcmG<$o~u)L7)diQFfe$!`njxgN@xNAC6@W) literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/birthday.png b/src/main/resources/assets/sputnik/textures/capes/birthday.png new file mode 100644 index 0000000000000000000000000000000000000000..b9ec94e1e003030881533d7929fc66515c5c5a91 GIT binary patch literal 1393 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-V_;xdG`FsWfq{XsILO_JVcj{ImkbOH zY)RhkE)4o0s3c^s$H2hAS>O>_%)r2R7=#&*=dVa%U|?V`@$_|Nzt6?SDXnqYRQd!1 z1ItHG7srr{dv9mN=7@xf9IrPnFH7GZeIw}Br^o{tot+;JFi$iWKGb|MgDFtRA^FJr z1ST-$Zt=hG_2+EAYXx~p&fnhc^k9iK_p9{Sd&~<&E^Ys{P`T*C z=4^Gw4Zh3`M{eHQFYO-0q9H7QL$h_!!sCxJ9!h9r{Y$MW6|R-tJ7c~rGuIBgGgs<$ zG9DD`9(Qwbc+G!$s^G0S5hfD`-|K=8HSU+>ExBGA8F<&_&rY>%c01;k*XJFG{HFNr z;nfA43`S|!bJ=^3EECCbIQQ|Pm&CfZ+Zu^J0gNV!7S{sZU+%JcwCX{?>s!)7feyVI z9!x*~`x*%6ru68Yagjc`PVSD6l%ms=^Y@jRl^IOi*gWn#OMWn(ylI8~R%z{-YEwNH zW^8njJ+JB5Auug5@oUV{tyU*gCP%cf3UF|0IhZA1OuZFze`BxQ@rPcw4%zLQS(4v% zMBcV#e>MC4-Cy_n&);$CrEqw6Qcw1G>(@8$dR%9^uQ)sBc)9PKDc%WOCuZHXiTr0a zciZWsDYJY{&wAWXPJL$Uq`vLJPQGWe)=E)rKh1UPTaP)Jtp2@kt>cv{=$l(j6Erh3fWyYw-`Nxzxu2Mv6}uJ}q$HQbgdpm%jc&H;IyN>)=F zOAFQ?aZ)c+Hs}b4X6)IO#m8=!@9x$4{L90edo`M;-EH+=cK?Y@L*fjbSmW|DZk&6& zF0^DWXbaq;(be%N^&gK4|CjcL%kA%%T9mJxe(daL%Yr4sp&Bck6lyt*n%}Q>wu{-3 zVs+B5GjWDQ8)tyXbH+s<>vn3dVpyr5GLmuJqJIzx#LkulVoB zJAa>=5gK5&dDaKv>Q0-z{rg;Bclxe#s8Z~j$6_kzcvXEu<SK|JBmlm9NWnHpZD&`?S3j)(VhcoBnAEmk8^krGY{@OTMHqzr5G?NN7RZ^6_x}9m zqWO;_@3pfycyihwXYBIZq0+U8=k&Hq$G84Ip%VOKRrH6lAK$MffHvJ-2R&uGv+S$5vuW1eL3bm(9dM}lhdfY#Jb7QZh6Uzo!K8EB-)}pTsuV>iUl&W99 zf8TS}%84!OKRx<;*5c*s|Mjd-&a?V9A75tnyBj@P0Feg`ZVAG7ob8~7c#+q0j+t5M}q)|~et3=9kmp00i_ I>zopr08Si??f?J) literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/cheapsh0t.png b/src/main/resources/assets/sputnik/textures/capes/cheapsh0t.png new file mode 100644 index 0000000000000000000000000000000000000000..75ed53d34ee4e81bffb29cedbf8c03a7c8af8b3f GIT binary patch literal 1743 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/cobalt.png b/src/main/resources/assets/sputnik/textures/capes/cobalt.png new file mode 100644 index 0000000000000000000000000000000000000000..639e6b151eb0d5313c79644e5c934e4d85aa322c GIT binary patch literal 4095 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 a_pi(Dot3Wm{}uxS1B0ilpUXO@geCyKSAF;Z literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/experience-2025.png b/src/main/resources/assets/sputnik/textures/capes/experience-2025.png new file mode 100644 index 0000000000000000000000000000000000000000..658424805d9114e6ed22e960ef7e334516c71c32 GIT binary patch literal 784 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-V_;xdG`FsWfr06|r;B4qMcmtn(^(;o zB4@;Z9N-Xab`&!7W9s7*n&{%&#M30f=E9N0!fJlZSBx!$XGO=1R-?QU#f}GlcQ^{4 zpNki`^F!NxwR3FWQ`yTet(BR}95432e0Mpt_FQX9!UOSF%D&wX8)LU>JUS!cBfBqU zUe&p>b6#vmd-<+%uljK7!(O;rEus&mGo5qjF23>V z*Sdv1EnRa*Y z$JOpl>bKjLQ~P*RuFd{@d%HEazkkpD{q33KO3t`FeWrdqHEb7H^4I^yQ})>gsj`b?8*friwD3YN>uT@6>~XCJ%1inxeb?Irm2sr})bI z=K`hWtwK4j6z?|%x>{mOq^p~n7 z_gF8~u`P{P4LI@U(T=q?c{k^NbK-b)v^i$^5=O6HVdg#S(k4w)+Z^&;?XdI(iQBPW z5(Y*UO9a~Y$scCPXQ{hvVR%93P^1AvQzp}|SaSyPV+#+w?(7WUoBu9$`%?Aehhu)Q zy~!1MbRnt7?s+ZuvY4RL+_lrxS}$|loz25#X;!Sk;;eQiUShMj_iTA5iAKBDz%AdV zYO*oR^4-DTS}j}>Z&dN{x)!NeViG3MVV)LFmEw&{8Ackv}eOX+273P&*w=U z*x2PZ2I>;KcD5-Kl620D|<@$reBNL_+jbMGcK7< z7QPp3YuRI>o+mvt_VnKTTJnN+$j|(~X(FAM{^;|(2#I>L_^NGfyA~V6zv`g0j&<*? mL-sD|3V-#h2M*poRG*%`o#96g|4{}81_n=8KbLh*2~7ZHRbqMo literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/eye-blossom.png b/src/main/resources/assets/sputnik/textures/capes/eye-blossom.png new file mode 100644 index 0000000000000000000000000000000000000000..b7d93984a9113b59953de2a3d205211b2956263a GIT binary patch literal 578 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-V_;xdG`FsWfq{wB)5S5QBJORdw->Xc z$Qi|$js+bWfzE#fn^GPg3bknxFL-*+c!xmvy#vh^3jGh(ZDsS)I$E-9?mWqs632^^ z-_`xSx4Lexk9J!*qpl|Ds|(k;@LSBpM1*9 zX08osV|&bLeVeW8^IylO(pP3Ply2wkD?fhq#cOBo1&6lVmg~mJ-(NlZ*o*DFzkW?% zdXb;Yz5R64y|SKN_m(q6h^^fu=p4p$4{PtqZ-?D#L8S}?o5J%%yq?TqNyW<_(4{Jp(dj0fc7_t{G;Gc4G?Zu|YC gH(6m|c3(cv?XI@zKiE$&FfcH9y85}Sb4q9e0QUh5dH?_b literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/half-life.png b/src/main/resources/assets/sputnik/textures/capes/half-life.png new file mode 100644 index 0000000000000000000000000000000000000000..91925b38dffb15fe7b23acddbc8799562106792d GIT binary patch literal 625 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-W?*2L>fE=Dfq{XsILO_JVcj{ImkbOH zEa{HEjtmSN`?>!lvNA9*NC)_YxL)>U_@Bt|ztO_j*f_>gb*_tGqKk=}nH~cJ!>0?r zTNoG^I7@>3g8xGSL$H5^5J<4VBeIx*f$tCqGm2_>H83zRNS3%plmzFem6RtIr7{F0 zX6BXX`MHKDnCcno85(wRh@E3#VAzow;hE;?sl~v-z`(%DAjQbaz{tSB;01CJlnwHV z1|u_AoQZ*f!H|)OL6CugA&P;4A+wzYES|#v0iVhk8D20mFu-VZTNxOb85kz8Gq5l) zFc=sa8!#?_m3utn7}H7EG-zA7@)cg4GcgEV%{eH;(W1cHv%sBJc|IYD*9M=uLo6oL!Ciq7w>4k#3`*dL`*C{f*E$6>P zx^z}HU%JiPea)0%!E2Taa~Pild2w>MWG3Gey>NGi_s(lk%uP%i3>jXjHMn?l+^~93 za-A_~C({F_26qOB>zoTL#UD&(IB<{gY%Ht6tmWE}B!kZS6sE=>Xq{AGSYhS@)9BhT&rF0q$u@MkjCYSTi5waZgu2mvv4FO#s2- ByJ-Lb literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/hollow-knight.png b/src/main/resources/assets/sputnik/textures/capes/hollow-knight.png new file mode 100644 index 0000000000000000000000000000000000000000..db35cbcd2d7ace43e4f2deb6a0b49f74afa52904 GIT binary patch literal 779 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-W?*2L>fE=Dfq{XsILO_JVcj{ImkbOH zEa{HEjtmSN`?>!lvNA9*m<9NRxc>kDUr^LhR>!&R$ro+Aat4MVNjYC>#i*WD=f8dX zzh?KX9VhQFi+M1ya4>*?90P+X0|UeE!^dF#mLIQ$iTqh#lXNI4P}G8tHH<&7H48$U@&B4Vi06tV2EO1V90D| z0gLB=T+G10kN^q^BpTgT1_ovZh6(HpEDQ_`21dpPj0+&w(K7{fhX978<3-%feBu33Sn)w`NSUE|k3{pw(s z4R2=Y+`M0V(#&Gp@ynhnMd~_zjh>8`FE)4x?oQhI>*@i^hr3?09@zV5?&$^F#H5o8 zWWqP?zLqI`<9@a0=KbBgMeFWd=?*{g+ASy7JM9XWq4isyM(3X|j%{3-d9o);QBdBA zW5;N;Zb*=6PpMuwJ@zV;S$Qvk~fl&5y_(op!bzy zSZBQEvPsE3xn|9sIN5Tx=4oe6ooYU4kmH?}`t)Fe%r=Q9bJ&00OxP28@B5t3|8^d{ ry&>}V_u47)ymrsiKNtt>GyB0jQ!tnN(hXlw{4;pE`njxgN@xNAvvUpp literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/julianclark.png b/src/main/resources/assets/sputnik/textures/capes/julianclark.png new file mode 100644 index 0000000000000000000000000000000000000000..949a966d5332d42f5388c3c81537d6e9c096b5ad GIT binary patch literal 1398 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-W?*2L>fE=Dfq{XsILO_JVcj{ImkbQd z3;{kNt_%zeii(OB78cGf&MvMlu5PZbZf@=#?jD{V9$p@vUS3{4Uf#amK7KyF{=S}p zo?by-UcufzAwIsLzQGZ}A(0`WQK8{6;gPWsk#Uhx2~p7r(Xoj!u}QH>xk<@+$tn3M zDFrF11*xfpY3W7j>BSit#Tl6;nOUV-H*Vbc|NlS3C>RZafeQikrJI{T8KWe~FPOpM z*^M+1C&}C0g`tC0)`Nk8fwRCPvY3H^?=T269?xHq!oa}5UgGKN%6^}Vjf>aFp!1~- z0|VnDPZ!4!kK=Er1Wuo=AkaG1?TDw-q_W_p+J#JPE&u;BThE$jJ8{zGyx$k>-h4e; zoMQJrm8~nIuyT#;UxP)P#h)LG*psPL>7pZaR9?&G*6N)z55}wstzCaBe{RrQ#r-ZSP;75rsL7O^aa zOE*L%dhW6dkAgR^v~*y4v866?&80|o6Q_^w z7<^A>ze>F7usvRQ%f311qwKytR6Jy=! z`n8b%!0#gmRZ4GfvN^D@cz*x#!x#IWY4;i`GMo!$n(^%Z>BLiW-HhegOco!V-K6U~ zvBCF!@Bx#6pDM+RbM_jZleCvyJ>^x%|68k7oE}aWwmcEjGUIBMFt^OKqHoU|eyi-@am5sY8-g|4{n@O3ZYU13d;fNt zeEVgwz;-j`HMH3v2<=L z5PCjWwZrV%!DG)Qna&(l_%`*Px>QMDU+F|frj-o-bx(W?WtK90x%%hu)|ZB#>jD?- zapd7^d908yA%k1}ZXvHj57Uf}mpj-MD;cire1HD?1A~8zS`N)iEE!Z~!e%ZDaM(Gc z{f_h3*7^;nSWo6N`>Y6`%hnJet<`WYi{ZWWx1*y7!8t+-srI%$LC>@_Pa_LfeG>HjX> z`{CB=`Lllgx~#78dQ0rnl_Ayhvo&I-YD6B7c~ZNLFIkjf#pZW5{Y$^!v^t{EU>+Qw z=T*gG)+)iuaK_T+d(@({Z@2SpoGJC!c7Mak^#^?w!x$Yrj(y#HWAdikX(=$Urait_ Y<}AbA?Elvo7#J8lUHx3vIVCg!009k4hX4Qo literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/migrator.png b/src/main/resources/assets/sputnik/textures/capes/migrator.png new file mode 100644 index 0000000000000000000000000000000000000000..feb6c4c4e33d01e01785013139be2a543080c92b GIT binary patch literal 705 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-V_;xdG`FsWfq|*R)5S5QBJSxl|142Q zi6i#~n>Y-27zCCGn{-G7I0{aAv-zajgLA#=A6VY8l*=97I-%4|?<wupWPA zIXj+zUg*asFK@s6@UXk&-_`#z4`mE|gT>9VF7;lQx!oqdhnMX^6{q>7Q$h!pf8V`Q zpkYyj!~sL|h(p|Z2TMQxe)hy>VfODD^Gja07{zKoR@$>L^d2wGj)T~?j^~KspZc(1ymrfj?y15i#x;gIdn>OU>yAEHecrjj?e5vD2j+%-zBN^9opfd@XG7XX z0pjt4kZW_s-zvW%E{OczO4y5W}&Q8GJj8cDF2GG-y#|I`DI6^n?8`8-8vT<(W2t zFT&}=-S@u~pB&0yNi=CPkaCphRp@aOa8u@3EZB0911$WH$y-VNhU~)UXBik67(8A5 KT-G@yGywn=5Iqn8 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/millionth-customer.png b/src/main/resources/assets/sputnik/textures/capes/millionth-customer.png new file mode 100644 index 0000000000000000000000000000000000000000..936946b8ddf5f925127e9f919a37e318a2a7de8b GIT binary patch literal 3831 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 aWWW2y|&1m-s#uIVtWj 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 W4VO+%)@5K|VDNPHb6Mw<&;$UWr3AwO literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/minecon-2015.png b/src/main/resources/assets/sputnik/textures/capes/minecon-2015.png new file mode 100644 index 0000000000000000000000000000000000000000..b0db9b75d3d29975e9a327d4b41f569351b52fb4 GIT binary patch literal 3753 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/minecon-2016.png b/src/main/resources/assets/sputnik/textures/capes/minecon-2016.png new file mode 100644 index 0000000000000000000000000000000000000000..7983535f3c60fe4b2e24e7317afbe28b592b6eaa GIT binary patch literal 754 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-V_;xdG`FsWfq{X&#M9T6{Q;){r-_6D z-<3xU3=HBWt`Q}{`DrEPiAAXl?mjL7e(nlosYMFLdM1UfOCB;XFmRL<__@3LxEM1q zFfe#EXv#A%FtDUM`Z_W&Z0zU$lgP@zz`&N|?e41_EE#u{bGc2skZr6yYdjsM0&YXmM~}^XJ@N7iVVPD}T7U&c2HLy(n@rOGhs6 z>e@?_Wz|3S%umlzPumzV<7CPt9=7Hsx8J4-egELIHmthi#pl3t&p)@!@mp@bZT`#E zJiI*#1qObK9x9cavOjgOXrMiJNFc^VFZ`t6j4lcHiYwVo1LgE1}6G z;mW`l!=-q1hd84mYXiT=N~~r+Ey@gAJyrK}jV8DDfv;7jSF^qz>x^4J{ThF}bHL%s2H%Fs8}GkA`?P3d z>695O7EFkqcc*AZyT(opBUXubn@WNi-v3;}_-5g}aufb}Ol!lmi`6#0^L(jtJ1f2JGHFZDeV!C*SU&EcK{|GoRxR91~sg^wntGu%HR(SJN&L-SXGg~`{dyQ|MTwb+Ds{Q7fd!u*Fe z97lfz3i&e37nb&~{p4|G{nZQ04^9_pP}*^yp{Ju>xxwcD?wU$Qr<$#)e?__emTSyn zsBeA6ml1rIF?W^q0^d^hEi2zOm|c~LF z04dwP*-pW+{a?@?~y6T;FC*}3|qrZ!4 zrSIcqc=0IVl2_FNwV!9cYqBf+zLC$;ki3y0pnQ3?B-z?WM?0Fj^u%s*E zo2e;-fo9(M=f%R8ng6OvOn-cD{&^mO+OujQj2dSSFKJddVzJplYUs%X2MQRasW_0g4mQ%v+1wlF2Eofyf$@xnYz%!1*~o#o4t zg$^t;I^ekCH6z1@{Ir&R>$+taEGL+q+`Zs&l#Liy!!uPD%k>hG4;UVt#e649gh2lA3tYc+z@#|onbFe6{CXh;-?E%Gh_(V zh?LDt;AD6&SDVhzvSoSYtY_yhsWDXTHg34Jr{%zG<>QyGw`OH>p4k0zve{0rss*Jg zXPtGn1v0!iAh5*PAuU0QVFwS_Mz#~G4Y{?xD|`|-v=}S081Au{{|%qX$>SQswn<{K z$KtEc%O-O2xSnCSS1aMp_TuxdQ!GJ~IhUvjd-$qNnB}8jI@2M`sPRe))5?=9L6<%1 w?mmPL-{>3=9kmp00i_>zopr02L@Cy8r+H literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/mojang-old-old.png b/src/main/resources/assets/sputnik/textures/capes/mojang-old-old.png new file mode 100644 index 0000000000000000000000000000000000000000..fb193151ad37d06f9423436f96c10692cab3d3f0 GIT binary patch literal 1370 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-V_;xdG`FsWfq{XsILO_JVcj{ImkbOH zY)RhkE)4o0s3c^s$H2hAS>O>_%)r2R7=#&*=dVa%U|?V`@$_|Nzt6?SsmO3xGuNAe zf#sH`i(^Q}y|>eSyJJJe+UD>5cI)?@RT9dx~ z+T8N}?{{A-N=wVPt8R+YmYOojIPP}n5$1%niuZz{YLB&9dY4=g-pzM-M$hph28S%! zCDhNyvJ}L)&$0g;e*0I9ccRfgtNOZsf1Y38dfuc*wr$_%JMjjqJ2+HT3!1|HPqxkx zj+;8CN+Brw>Fd?p74I88W=>(7WA7m;KF30)Z~FYWr(6HmY+39wuk!K!zGwj}x%>yF z3ilN2S^^Ks+^crtS+dXlN^)4?x#s5lx*bmQ)p~ZZdg(5yV_v*ymBsTPj~VZNZ2xaB zoR-5dqi6lQDV*13h1``xnBxvlalR(;&}LEN<=6j}kMR{$ zC{Im6r#%l_?0%M=S(>k2^W$oN%)fn~UY2*B-)H`BTg{noEDWpN*0xR(-l>wcGNN^g zy3e{Ch6%-PrU69EzS}6BkUBuwHdlR((?_(|*}eo+IqYe<#4m_|$5)pjVau z=f^XA_&)ugOOIf3g^-7N)Pe4zS7+3(>&&w$p2(eU?(ceJqBk3lVE;@;w{w!}e8z_@ z+b7>Wx%HaW|B43we_vI%Ub8iHoY}MfQOfVrTfd&Yud^p(a#Q%3o=MTH0?(JvGB5I+ z5|eLn^X#Ycx1P`Ey!?Igfzbp3W!34q^7}V!IrZ_keCp4GPc1q8X3To1bJXj4uZC%I znC8-iw368NQ^&AAWOuQmSOcu>+{_1Dc6MK3+Q&K?PL-@fC-sl#X4z4Rhh7I#@a zKcRW~=*2C8L3_S0xx+sxBBrM1+@Yh*+Qo}yWg6VpZ{hu0x$|}P91A(?qCJgISb7=X z+t%JsPRoh;x%2P#lsUqCuE(mI8b`_69&6qF;eCC~9O<`m3=b~o@>qO5bbN{3L(`z5 z{^EJh<97L+>zq=p_SkT#h!aN#hha%aMV8?jDJ7?KKijta`t$Sr-HnGUxfXqVr#=7d z^?TJ{Ki!FG|H4@6u+~hs{O!}1U;Wn`9#x+VQ6x~kw&y3<$y5x5xCOfJqyC>+CV5Ee~ z&b95Ew(;3y)hLdWH)8%!-y5e z<;7q2?Y5|T&nWln@WNPC6XQe6KHoPxbWo+>)t%$jmy+zZ?%Jd7c4OC(hYkXHhhMmH z-|i`RU8|Y;!|g>`_vGh#yOP_BXB+H({JAgS(R}vIE!#XBZkz17$aHTz-_}#gM{ZpF zE}ZnG+>SX-Ct{!dhRhD0TMTDUul>90GS8;(x(Cj_lVH2E<_7zN?|cpS{HpGxAFyNi k&G3eK1OEZvzIcXrmZ>{LS?(w>FfcH9y85}Sb4q9e0Fw-V`2YX_ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/mojang-old.png b/src/main/resources/assets/sputnik/textures/capes/mojang-old.png new file mode 100644 index 0000000000000000000000000000000000000000..deac5c5aec5cb2226faab05adb21072ad350d66e GIT binary patch literal 1076 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-V_;xdG`FsWfq{XsILO_JVcj{ImkbOH zY)RhkE)4%caKYZ?lNlHoI14-?iy0XB4ude`@%$Aj3=9nHC7!;n?Dx6YxV7~sPR{wp zz`(r4)5S3)a4hp&svN6vbz1I*vTPyqt-|qgLe^zc8JBM<3R?!I#NU7Y zy87Sq`hVYVESeG&e_r=v_-4n$#g#ouS@{Y+ui4hmy!_|eBY(f7?R?(^1uakB$bU9H znZM@Rw~8g%iPg2~uU$SfXeSXBu$jUhWd*+4n9f>mxOc-2iyXMaiuUscGU*vu-?~lCd=5D1u#Hw(+*^x*fjn{(aPD zusrN?KIP|uSqrb`@t)dY@XSuDr*3`z4-t-t1+U#IZ2Mb_1CAT`?DJ=A$tg(koxk|q z?;8!FGZ+`#EIDMykhOba^zJX=3py`MmY;g!(3?;G3^^j}m#t#FWnXGCn{V07=T%#4 z_WU{)HL2rs>TZ{tdK-7u&aXKq#mf+u$hO+RdG9x+V-1gErhEL>vHGy)t?W**REG&{ zoy#2r-cCKWH{!W|vDxhI>k%vOBo}|Yb#b@rp%2{+>zP6=ZcCjm)AVWF6np6NohuR( zSTnXpyU$@y`qIC7p?X8$G4+QvA}uSP7Rws?ur}0nyHyJqT)6p7=B~o?T^Sn}z5k-X zBD&!D&KJ83g~T%cKil*83R_S-i;T1TlJGxY#23cv=|K-R$rw{|nTIiFY36x-xzwPd4o1_S%4eoJ`^Io8E;lESG@&sVheWW1PZ ztgGSCtnlgLi{;aps+Rnzd7sJJaFctwv)GsGOuT!QW%mW$Eq48(G)+OYN#w*B_}iYEt!{*G;w166PsTB-hPy1wjyxx8Nr z$Fqe8MYaU=Sk7jj%aHr+>C1z)d-j(;u$Z!-_t*ue*%#D(FaC)tX^rncSLMs!ILDBc z$2PaCZ)KzS0kz6C&y%?s*03ijmH24J9r%8Jy?$S^?R>8T&tF#ZPhFVo^^#?Q!0f)p zIhHIdIAnvpwtov?sBSP@%QRs%$ZSm>PL9|8iU+wl3j1p%E#^BjWCt@mU+~z=z?Jj* z{$pD%Nx1PR#k?I^AFzE; zYq<5hL7c&fQOASv-LEWdMv2u&gE&}U^h)?L%!*cL_-M)U;avYtLA_l}|IZ%%#?TP1 zQUr3x3RQ&~Wy3xh+3DTl2mUkNvQayrYA}`IF8h>KOgChtdHO5Q$<$;mHY{RoxM3&Y zb-{4w3^pgsB`eVAWPzl~PnHJX-wn^6Coyf+nEPbe*?`Z*hK*5A p{t59!syh}J8#Z1uz>R(|Z}r=KYVMo-dkhQ=44$rjF6*2UngE@x?#ciF literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/mojira-mod.png b/src/main/resources/assets/sputnik/textures/capes/mojira-mod.png new file mode 100644 index 0000000000000000000000000000000000000000..ccfaceac9755ef03209d85412dcbac7d6390d11d GIT binary patch literal 1213 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-V_;xdG`FsWfq{XsILO_JVcj{ImkbOH zY)RhkE(`?>1q`)SSL_lQ7#KJUJR*x382Ao@Fyrz36)6l14D2PIzOL-|x!5?=M5Y(G z9%Eo&vGsIu49U3nc6w|^bgIPhdUcgAZ&EsAFPm;$%&n4ne3q|9m-ECb#d{~(49s<= zthFi-zsmiR&D(X)1S_K(r}T34tqSCCMZT2j`&p=X&nJJ|?0Y8KH~Lj0Z>e5h_f7es z2lIMy_2(up+jB1qi%k3Pd#>{Q$d|bm{N<$@?x%5!%n2oA+?7V&El*>plzzE_5)cIH|we@H^6?NQ>h@FSp>M zeuasb?i+U)O&5GxbFHJ~bEqFWO#jYpp51OYH=EZa{e0aS6!_Nk@ zx<3~z`sZ^bos6p4z3keO1KYJ-bt1WaM7J);udC~wxj*gZI*pVNQP*iFS7`lxSiMMO zN(4h$^%g63-3R9Pz860UnXtaTO76Px)MJ~2R+*|S@j8C0(3Ug)?}al{^;|rK7I^cQ z)z{ko+hrwFX8rs7J4b(^hU>;tZ$DJMxjol-UGmAOJ@X!w#kaW3?O)3( zOr6YD?PLwy@cFc7g@LAzJ~Ow2%}$dGH>X`Yb7r-ne6PBhNno9%$m*9j5A8aC{C?Wj zqMz3oFP}c3kYG3WVb1OY&%ZwS!p_hmbR+nEbZL7yca^rBfeqJ7pJN4euG0#`cNc{3 zjyRLX_#@eE#{7ntdpy5B_%-av%=hwbxTz=aue)aS9N_+qQ(r5c6Zmrex`e&<2m(Jzy zZ@ZWMYEa$JyyyGN8pjXk9t76J@$>Djy}b4QmhC_02BpmGmKFW6M@~wbi8XG)iA^i* zCQN5woxa*@cVN3k&5q==vT}0k^S zuSU#;zAr`r?HpfkbQS$wE0C}`>gR^v`HQ9J=8C&K=6v_xF3gH4W}Viek1Px2W?%e& z+d9eRF{ez~(gV!je!5@(+OSq&!;G_wP8bPqRPn1nka5yVHccquYX464`@8mC={uGX z&+uSZ;e3ySCw}}AzIM$)XQl9lpRL>4T1u9_Ubmy>HA6sR$&$umfma&^d)13rW=v*z zz4f-o^}PJFO`39cOdacfm6$BsYittqhwE$D_3yQ^iSH%+>KW2MXzbm4;oiTwKUtXRPDR`Wd`e3T+VO&*0^JdDr~?q`=Uqb4zA)^B9RsK6!RF&t~4U z<;@L$gzvlfShxL>P?7R4yx_vL=D#~r3D>W~-;X4PeF%KNdgsLu&wcEtv{tX#1R<)V bzViDl-jZ}Vw(~y&0|SGntDnm{r-UW|`AkBX literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/mrmessiah.png b/src/main/resources/assets/sputnik/textures/capes/mrmessiah.png new file mode 100644 index 0000000000000000000000000000000000000000..c79e156c0634f3397a60a5287e5ccc241ac60c9c GIT binary patch literal 1128 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-V_;xdG`FsWfq{XsILO_JVcj{ImkbOH zY)RhkE)4%caKYZ?lNlHoI14-?iy0XB4ude`@%$Aj3=9nHC7!;n><^e&_{B7Mo%LB6 z7?@vpx;Tbp+%KA#u4c_GNfoxSPN{8K7Qr#2)P|4>Q|IOwI4QoMtGbI{|A%{SCupZzZ* ze=cv5+S_YecCxC*@3>R^ZttAxy8EAP_uv1n!BFk5_2^M~laI_I$7u^LvWiK+xwE?9 z---m&8GAbpX%!ymXW*!qUSI$3#;fB|+&b?ZEgNImMeFSJGD@b_Busw4$oRpVspr@E z-K`1uu5idc?Btn zv~sKkjY*0pmOV+$ zpC*1_vb};&aKmEfvN^fAH5+apzY{NLb~kNlVmb5v-hUpor>ZVHO2t0inXKz&@xcAY z?1d{+q+dN`;=CHxv~g{_My<*YvIqes|T{`x>tl#e z_cC4S0@EhF)`=f(RjYEcvHACZ`m+7~jLocHvW;F$6Pww1%vSwJ-3yB+EPcM)m0M1% zR@oor_<5UShF9Fd`3sh^?|bq2(~tD^v(j4_Hr><_*ZXl|*(HCPO1+gKMl7Hc&$dM{6KuV46IO75K8Mb_m42V&;9!6zi5KW;ULkF#Yg_Ba~OmaybSsI ma7CL`){2Et;#z+_gwt{f^&0=h|?#S{}ABXU@_-cXgk+qyyO zx>HAo*h=^M&2vtlJh{erU2*%>z4A40{uH09*EY@Et$K zcm@?4Q^pG`eJmC3F($apezaqwfzW@ZgJli8>+b3^WMwiK@osoi-KfA|##g`dz&7Rz zSuswBLzfwEyf%4|$l1SS!dr$V`&n``a%Zw_^P6=}U+i)C`aQKP&dyrEc;#OI-Jknb zUSeQr+O~p&LCQ&=VP`ob|L@H8Vt;r1dVMIWurGVZw(CqQ7!#t^eZ?4J85qQz_li|8 zAK);beb%MePMe`I;q{Wv2ai-3HtAKc9tb(`slMS2cZ1Hu_uLNkjYf|?g{}}k9%J9H z%fNYlqY|TnYzV^%v-p;{>kTT|8m^29vKedl{$OvB`+0|vXI6^gm4kbG|0#5B+a9a1 zc)j(Ze|HxL39;;BZCk`F;3$3iyzuS=albQ`D)PksSO5b|CA@KbFJ^t{Hk4#wU|{fc L^>bP0l+XkKzeXMh literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/prismarine.png b/src/main/resources/assets/sputnik/textures/capes/prismarine.png new file mode 100644 index 0000000000000000000000000000000000000000..53c248d08fe1f81f1b3874bd1bbbb0cadb4058d7 GIT binary patch literal 4432 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/realms-mapmaker.png b/src/main/resources/assets/sputnik/textures/capes/realms-mapmaker.png new file mode 100644 index 0000000000000000000000000000000000000000..0dae4b22dab27c045629e78b1a5fbc97195e8ae9 GIT binary patch literal 1111 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/translator.png b/src/main/resources/assets/sputnik/textures/capes/translator.png new file mode 100644 index 0000000000000000000000000000000000000000..e2d649258b2e1effc4e08d00b459a01c0c645a77 GIT binary patch literal 1723 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-V_;xdG`FsWfq{XsILO_JVcj{ImkbOH zY)RhkE)4o0s3c^s$H2hAS>O>_%)r2R7=#&*=dVa%U|?V`@$_|Nzt6?SDI=}Cf+bLO~99;^SHzCZT1boJY1n|LLnGkH{UHKuS(brzg1C*>Ndt!J`j zWr}$VmmbUVl^(f|x44>cuJr0;$`ZcidyB2pD>t=?MW}tw0<9}Y17$STKH5)hIdgxc}F`?S;Bu^K^GB&< z0Vjo)1CfUp-v9S*7Q>CgAH0RVJHuHS-u`A?a!G?%xU%tRp|_A4cVD!T=e@tv`JFx- zJ@aVU%A1vcE~r0Uy5XDOTAs%8uviZ4#`aZOV!xo%v} z@Zr@wrb>iyuZQ-Ou37ngU5Znw%7+WV(hL^2%zg5%6+Brho3pc8pFw}mzM02& z`CSTIKF#34?%Vet9_=l!f6~~$=d=5-`fVE*r9?@+W_+rYfcg@S0s`}yl##lau-PIkRFBiwmI9d3@{QHD8k?z;` zvS;LFJt)@o&3XCXsq%99w!OU$32QWs6MtWrZ!qy-@+AMVpt(A$QO669>OH?58>@J8FZHiJ(Em>^_>y>r zY`ToYn_x`?9-eK-hO0TlT+&Jf$maO}BYFWhDqKLEu+O^6YsvS*_ z4z&ig1m5ZLV&s2&bHfthDM8a5Jye8dus-ft)FIA)zw$26kCIb{cDC;OBp4?6Rle9| z;!rC2;nji6CWE9bxoajFZN+_Tx*6e$k9STuZMN_5#(kG1(k-U<$J=r(-VqzR>JF2) z=EbPX|C_%{RDApPI_bcRqcW4l^(WeH6y9%eXj?wtrE4`e$JxU%MTx~Qz)`~B!<%EQY$4OGMBJOC&z9o< zCgt_ATJK$#KJVPHYN~_$TWL1Mi5Z79Tr?UlajB{}8aiCnXc5?R$GS(PVBdm_$f;dl zU&uIdY?%}x?tSz|)VzD9L4}%cPV#jz-~GOG;miJaUe=d3?UH*ir&!1NOIht|C4P;c z2X?-+nJljV@zCqpp4ZkSzg||m#dN*f^`)y{GwKA_tl$55*SGb#eX|!G-{r@oo|zrz zo1UF~b#+6G9s?g=Pw1ZQ>edO_E)xa@1_n=8KbLh*2~7a(0W|*r literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/turtle.png b/src/main/resources/assets/sputnik/textures/capes/turtle.png new file mode 100644 index 0000000000000000000000000000000000000000..75326189350e48a91bbb8a0edb829f8847cceaf8 GIT binary patch literal 19028 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/twitch.png b/src/main/resources/assets/sputnik/textures/capes/twitch.png new file mode 100644 index 0000000000000000000000000000000000000000..c4f18a347bb444c628d1a771c06975e9d03d5dfd GIT binary patch literal 1253 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-V_;xdG`FsWfq`X>r;B4qM&sM*XY-|9 zCEDlD+?XvtA!edQPPdDbpvIP!p0~W*_9`j70#&&$Qd=XG;rRo;yH{xFea=NyR% zXT9#vNSP|lyYpOH>YnJ2F@C@NWRf3zc=(r-L8(dj!JqQZ6UR<$ulX4FODSXRQH|_} z?ST^3dU6772F6@d%WJl6ODRxO*5*xGW0+JUq4sFW!55;L>E|4uzMb8&@XX!locFSG z7dY}r$bSm-5SuUAdfxBnTE}p=@_#EYPB5E2HCx?9=5($4B}YRxJN>&kU+ecMIPFQX zn!R22R?cjbO$EC`+{_=xYfD&09!phEKJvmxEi(7|u9g)cL7bN!ocVB5_HX~My6Ut| zcl52gj;z?;$Gb@6&aSuHd9HE&oAdeRjWw4uR95qz%u3&!R3qd4`$wVG!UsJ6A74AW z?fYdXY2}k|9_2J{?X_C&o!Zm%aG}x0)`RA3Gxr2eI2d*4s3gBE4^H}~uV^aUWXErreTMryIt^Bxle@)o+*EL039b~+Fwns;1tE!Y$yQ#A<`kWK1 z*brHtbo4yW{HLqG=Vm1?h~>SXp=DR*YxD5N0fnirS&J-x{BxFkylJChQiZ??mBbk> zj@kAf<)vpYFDx%?tcm+{;>K2|)54eHD%<&(3^P9M+VS&@hR+$X8NXbA74l?DmmUgu z_37HQ4uRv3EYH3D{L@8dyZ;USb;sBpH>t0T5f_ZB|LkHV_TDMnz2;co@3nXD-g4U; zdTgJizP8oB;>K4U=Z{SAPrZ?!`sZym({tWHmSpum-|}xOpF6zIR{iahoQ0x7yNjKd zzerl|o|7t{uvX*Uqsq_ot&SZyGNC^#;LXPiUcV;n*mqjFQ0KhW+9th)c2VoMmn}}* z5!|umf+TC)`u0qzXEA#&Hk|5ZdVGAT&bO~s8bX44^S(rHlyWtyOY%5Y<+^@x|17Jg zi?^zz9`~2`&gNOOetntA|1y&fL4EV{pQq1iUl`Lj?W22Foc7L4)uagR^INA~m?<@J zYW>pdhcErh*)P8AIa^5ldUpx0`%fpkzy5ly^2vhY|LbEce*JFr4&7>!KYiMowO7~7 z{deHj)vHg!S4ZAHd1trh%O8{Mrn{*ekz1?&@m_|^oo<*3LHm%#OU2D zSbCIOvmtp^rPA5wOJ&>wi(>q{eO7H=dg9BShE>y^y}h!sg!kR0rQXx^Vt19iyyW^r ae1fU;uWrtpR~Q%=7(8A5T-G@yGywpT&}5JR literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/sputnik/textures/capes/valentine.png b/src/main/resources/assets/sputnik/textures/capes/valentine.png new file mode 100644 index 0000000000000000000000000000000000000000..72ad2e87582796011107fb0a5f9cec16a9792c98 GIT binary patch literal 417 zcmeAS@N?(olHy`uVBq!ia0y~yU~phyU{K&-W?*2L>fE=Dfq}s|z$e6&fq~&#$Na}L z*6m2H*$`Fy;oakHV#@Xz;z126WqjiI@0>YRHSu76yH}aqhljEMUt9kF|G!hD=s5!e z17k^$UoeBivm0p)42&|KE{-7{$KOu6IIUTM$EDHitblL%|Nq(3B?`U0e?176w^}TB z{-UQ|brH#w9@|7Z+a9PPjcwImaDsXPC{VHtDiF#Z0F!;S!3nt z(a>Dz=b*BcE6}IF;2h_sjtL%}=?Sa1{F&1*tM1*m9?m~=SXeH-c)zyc1T*6?#VLEL z_i-zU$(J>{x6C_#S#rylvZvo!Tm%~hCU052=0#ISu7rfshha{TZ8PFA@vsrG^~ z(~8gl1!LwtU&B8Xi_3p$Q4qNF*>i)cPntky{Pml^re9_;Ykl^a<*&*db&peiAFhVW bud+|R$CFVN`&a75dDZy888x0)vHiYyA9Y<(9q{IX1PS`ho5J`LlQK z^5Zbb)iAM_%sBA)>>A;=ivKI45_hmYyu`pAex38+cA?BS_k0#G&OW6L_{vI5L4WH;#|sQ14h}3_!2-@w5W#%;V|+|=r-g6!n-cRZN~9ue-MVB(U%%Wp z3OlAcN=)14Ey8tL_rjs8r8)~3*VWxD|0K)Yyf;qgmSb^Q)6qA#Ge``v*_PW$xf>rU!o9Lap`dIILZtv-Rdz-t%aREbA z-MMY&fBiRne|kef^7K_E2UB&_9iF>7^TlvIn)J9<*Wie3GK2l{*keUZC2=NC7HD?p z))fe7yf8j>TsE0uoBhu_yVV?@yEZesDYB(cy}r~yyS#_7g7XqXu1@cT%8L`6mMF{% zo+2ju=$I74!%t_|_deZuuDeY{_2`DF|9|c-Nakfwh_hq56tN}wmz~5ho*-$3URx>V zC#NPAim)-9c>b^c3fl>O#dTd9Yvoxq**rI{yKtum4(@(nx5x;6xx6uMD+2=qgQu&X J%Q~loCIE&{66yc| literal 0 HcmV?d00001 diff --git a/src/main/resources/sputnik.mixins.json b/src/main/resources/sputnik.mixins.json index 7d6fd86..a52f689 100644 --- a/src/main/resources/sputnik.mixins.json +++ b/src/main/resources/sputnik.mixins.json @@ -30,6 +30,7 @@ "BossBarHudMixin", "BufferStorageMixin", "CameraMixin", + "CapeFeatureRendererMixin", "ChatHudLineMixin", "ChatHudLineVisibleMixin", "ChatHudMixin", @@ -47,6 +48,7 @@ "DamageParticleFactoryMixin", "DamageParticleMixin", "DisconnectedScreenMixin", + "ElytraFeatureRendererMixin", "EmitterParticleMixin", "EquipmentRendererMixin", "FogRendererMixin",