Compare commits

..
Author SHA1 Message Date
selimaj-dev 2c64eed329 new version 2025-07-07 01:01:08 +02:00
selimaj-dev 66d7d7eab9 Disabled online emotes 2025-07-07 00:54:56 +02:00
selimaj-dev 21b1dcc997 saturn client branding 2025-07-05 05:20:34 +02:00
selimaj-dev c2470378e1 saturn client branding 2025-07-05 05:20:27 +02:00
selimaj-dev f57e773a44 fixed hat still visible when invisible 2025-07-05 04:02:30 +02:00
selimaj-dev 6c0575a03f Notifications 2025-06-23 17:03:18 -05:00
selimaj-dev 9f93ea923e Cosmetic preview slight improvement 2025-06-23 17:16:28 +02:00
selimaj-dev ecf5987434 sidebar gap 2025-06-23 17:04:03 +02:00
selimaj-dev 89a5b2437e fully functional store 2025-06-23 16:49:09 +02:00
selimaj-dev c8d28133a0 Crimson Mark 2025-06-23 07:12:54 +02:00
selimaj-dev 0980e3a5d4 semi working Store 2025-06-22 16:56:18 -05:00
selimaj-dev 420ba82b44 more emotes 2025-06-22 11:24:38 -05:00
selimaj-dev 2f3c125bba Keystrokes mouse and space options 2025-06-19 15:13:29 -05:00
selimaj-dev 9e64b2332e added keystrokes 2025-06-18 13:47:50 -05:00
selimaj-dev 089cf4778c freelook 2025-06-18 08:51:14 -05:00
selimaj-dev b9984c35a2 coord display methods 2025-06-18 05:10:15 -05:00
selimaj-dev b035381ba7 removed unnecessary resources 2025-06-18 04:30:17 -05:00
selimaj-dev 87b326ce9f coords 2025-06-18 04:26:39 -05:00
selimaj-dev 786f9cf6f7 small change 2025-06-18 00:29:57 +02:00
selimaj-dev 464d0b502b NONE key selection when backspace 2025-06-17 13:13:56 -05:00
selimaj-dev e38f4480a3 config save bug fix 2025-06-17 11:54:50 -05:00
selimaj-dev d178f1504b config for keybidnings 2025-06-17 11:42:19 -05:00
selimaj-dev 5b77ea7363 Small property refactor 2025-06-17 10:19:49 -05:00
selimaj-dev 78097b1d8f prep for module mixins 2025-06-17 09:54:03 -05:00
selimaj-dev 51040900cf potion display 2025-06-16 16:12:40 -05:00
selimaj-dev f06e2a0aaa updated icon 2025-06-16 16:06:19 -05:00
selimaj-dev 70fcd64f25 potion display (needs dummy and proper dimensions) 2025-06-16 11:49:56 -05:00
selimaj-dev b2c7e9f03e default transparent hud mod background 2025-06-16 07:31:56 -05:00
selimaj-dev 08155bd49a fixed armor display 2025-06-16 07:31:14 -05:00
selimaj-dev 67a5b219f5 better font management 2025-06-16 07:06:15 -05:00
selimaj-dev 6c608409e0 Added emote cancel (shift) 2025-06-15 09:53:50 -05:00
selimaj-dev 4c72f8e39f online emotes (todo: make the emotes cancel across the players) 2025-06-15 01:32:28 +02:00
selimaj-dev 3d9d01a28f fps display 2025-06-14 14:09:27 -05:00
selimaj-dev 7e39532d1d almost fully working emotes 2025-06-14 13:52:29 -05:00
selimaj-dev 76c20d8733 emotes (real) 2025-06-14 06:14:25 +02:00
selimaj-dev a1c68609d9 working emote test 2025-06-14 04:21:34 +02:00
selimaj-dev d49afbb981 updated mercedes flow 2025-05-18 23:03:16 +02:00
selimaj-dev fd0bf75750 beta2 2025-05-17 15:31:22 +02:00
selimaj-dev edbfd0306a added mod detials 2025-05-17 01:56:26 +02:00
selimaj-dev 324ffe0c8b some ui stuff 2025-05-17 01:24:27 +02:00
selimaj-dev 21daf473b1 added armor display 2025-05-16 07:34:40 -05:00
Leo Codes d1030bb235 Merge pull request #2 from saturnclientmc/refactor
Refactor
2025-05-13 02:06:52 -05:00
selimaj-dev 05fcd3032b default colors changed 2025-05-13 00:03:09 +02:00
selimaj-dev 47cafad92b cosmetic skin fix? 2025-05-12 23:40:34 +02:00
selimaj-dev 744698fd71 bye bye old ui 2025-05-12 23:07:06 +02:00
selimaj-dev 86b638afca few changes to finish today 2025-05-12 15:04:08 -05:00
selimaj-dev a016fd72c6 added the settings menu for ui 2 2025-05-12 14:39:03 -05:00
selimaj-dev 8e8737afcd added a toggle 2025-05-12 11:19:26 -05:00
selimaj-dev 6eb3fc8092 color picker 2025-05-12 10:28:37 -05:00
selimaj-dev f9baa0fe5f added FloatInput 2025-05-12 07:18:32 -05:00
selimaj-dev a6e762b0d8 Keypress implementation on ui2 2025-05-12 07:03:32 -05:00
selimaj-dev d72b152a26 ok 2025-05-11 05:00:18 -05:00
selimaj-dev 2a202c4160 added cosmetic menus 2025-05-11 04:36:14 -05:00
selimaj-dev 930d002b81 this time it should be good (aside from colors) 2025-05-11 02:53:31 -05:00
selimaj-dev 35440180ea made the mod actually good 2025-05-09 01:54:17 +02:00
selimaj-dev 7fd0565f7f better mod card design 2025-05-08 09:54:15 -05:00
selimaj-dev 1421d94647 a lot of stuff idk 2025-05-07 12:36:23 -05:00
selimaj-dev fbb1460872 slighly better font 2025-05-07 12:20:43 -05:00
selimaj-dev e7ca5c0297 slighly better font 2025-05-07 11:32:16 -05:00
selimaj-dev 3a54739410 slighly better font 2025-05-07 10:07:57 -05:00
selimaj-dev 1c80803994 title screen change 2025-05-07 12:41:01 +02:00
selimaj-dev 191e9eba4c column fix 2025-05-07 03:05:16 +02:00
selimaj-dev 3d654685d0 small ui changes 2025-05-07 00:58:38 +02:00
selimaj-dev d9bd2c60c9 logo update 2025-05-07 00:26:45 +02:00
selimaj-dev 984132991d Better module display 2025-05-06 23:46:26 +02:00
selimaj-dev 7d649efa22 Themified mods and state merging 2025-05-06 20:48:23 +02:00
selimaj-dev 1ffadc9b45 font 2025-05-06 08:00:21 -05:00
selimaj-dev 9a7b93ccbe mod ui 2025-05-06 08:00:09 -05:00
selimaj-dev d37d4e49f9 New mod ui 2025-05-06 07:23:07 -05:00
selimaj-dev b491f246c8 Dupe fix 2025-05-06 06:31:50 -05:00
selimaj-dev cfe518380a Theming 2025-05-06 06:27:42 -05:00
selimaj-dev fe2521bee2 height fix on scroll 2025-05-06 05:02:11 -05:00
selimaj-dev ae4d9786b2 functional scrolling and functional mod menu 2025-05-06 05:37:06 +02:00
selimaj-dev 0f4af22346 scrolling 2025-05-06 03:18:56 +02:00
selimaj-dev 5a8ae263e9 scissor stack 2025-05-06 01:51:32 +02:00
selimaj-dev 2458906e3b Merge branch 'refactor' of github.com:saturnclientmc/saturnclient into refactor 2025-05-05 11:21:04 -05:00
selimaj-dev 42611d4aed added scrolling (need to add scissor) 2025-05-05 11:20:17 -05:00
selimaj-dev e5cc68ecad updated horns model 2025-05-04 00:58:52 +02:00
selimaj-dev b2ba693f92 alpha2 2025-05-03 22:36:48 +02:00
selimaj-dev cf9fd50e4b slight title menu changes 2025-05-03 13:53:42 -05:00
selimaj-dev f1f17d807f updated the title screen with the new ui 2025-05-03 11:32:41 -05:00
selimaj-dev 22ad088ca4 fixed panorama 2025-05-03 11:19:33 -05:00
selimaj-dev 52e8e572be fixed panorama 2025-05-03 10:15:52 -05:00
selimaj-dev a9ec3311c5 added sprint 2025-05-01 18:10:44 +02:00
selimaj-dev be1cca9c9a alpha blending 2025-05-01 09:33:13 -05:00
selimaj-dev 84f76ac577 lowered the blur 2025-05-01 09:26:51 -05:00
selimaj-dev 76b882599b blur and animation abstraction 2025-05-01 16:21:27 +02:00
selimaj-dev 5b6810a32f removed borders 2025-04-30 07:52:30 -05:00
selimaj-dev f0e554d8bc double the resolution 2025-04-29 05:55:09 -05:00
selimaj-dev bb9cc623c7 added animations 2025-04-29 05:42:25 -05:00
selimaj-dev bf4e16d940 added animations 2025-04-29 05:40:10 -05:00
selimaj-dev 9849a38fe9 working animations 2025-04-29 01:10:23 +02:00
selimaj-dev d16b66945f working animations 2025-04-29 01:10:10 +02:00
selimaj-dev c0b088bb8a experiment: animation curve functions 2025-04-28 15:20:16 -05:00
selimaj-dev 5cce4eabaf new ui elements 2025-04-27 10:45:45 -05:00
selimaj-dev 1254cd4b89 few theme stuff to button and rounded corners 2025-04-26 15:43:10 +02:00
selimaj-dev ff02427aa3 Rounded borders 2025-04-26 14:55:13 +02:00
selimaj-dev 79d5419fcc Fixed crash when exit 2025-04-26 00:23:39 +02:00
selimaj-dev ece5a16d34 Fixed crash when exit 2025-04-26 00:10:39 +02:00
selimaj-dev 32c1391336 Fixed crash when exit 2025-04-25 23:52:37 +02:00
168 changed files with 13406 additions and 4553 deletions
+1
View File
@@ -13,5 +13,6 @@
- Cloak rendering: `IIpho3nix` & `Dhyan Blum` - Cloak rendering: `IIpho3nix` & `Dhyan Blum`
- Who Am I: `FokshaWasTaken` - Who Am I: `FokshaWasTaken`
- Panton font (ui): `Ivan Petrov` & `Fontfabric` - Panton font (ui): `Ivan Petrov` & `Fontfabric`
- Panorama: https://www.planetminecraft.com/texture-pack/minecraft-cavesand-cliffs-part-ii-main-menu-panorama/
If any of the code or assets are in your name and you want to remove them, please submit an issue, we are still in alpha to have any artists so we would also appreciate if you could help us make some cosmetics If any of the code or assets are in your name and you want to remove them, please submit an issue, we are still in alpha to have any artists so we would also appreciate if you could help us make some cosmetics
+9
View File
@@ -19,6 +19,10 @@ dependencies {
modApi "dev.felnull:special-model-loader:1.4.0+pre.1" modApi "dev.felnull:special-model-loader:1.4.0+pre.1"
include "dev.felnull:special-model-loader:1.4.0+pre.1" include "dev.felnull:special-model-loader:1.4.0+pre.1"
include modImplementation("dev.kosmx.player-anim:player-animation-lib-fabric:${project.player_anim}")
//You might want bendy-lib. playerAnimator will wrap it.
// include modRuntimeOnly("io.github.kosmx.bendy-lib:bendy-lib-fabric:${project.bendylib_version}")
} }
@@ -28,6 +32,11 @@ repositories {
name = "FelNull Maven" name = "FelNull Maven"
url = "https://maven.felnull.dev" url = "https://maven.felnull.dev"
} }
maven {
name "KosmX's maven"
url 'https://maven.kosmx.dev/'
}
} }
processResources { processResources {
+8 -12
View File
@@ -1,17 +1,13 @@
# Done to increase the memory available to gradle. # Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G org.gradle.jvmargs=-Xmx1G
# Fabric Properties minecraft_version=1.21.4
# check these on https://modmuss50.me/fabric.html yarn_mappings=1.21.4+build.7
minecraft_version=1.21.4 loader_version=0.16.10
yarn_mappings=1.21.4+build.7 fabric_version=0.114.0+1.21.4
loader_version=0.16.10
# Mod Properties mod_version = 0.0.3-beta0
mod_version = 0.0.1-alpha1 maven_group = org.saturnclient
maven_group = me.iipho3nix archives_base_name = saturnclient
archives_base_name = saturnclient
# Dependencies player_anim = 2.0.3+1.21.4
# check this on https://modmuss50.me/fabric.html
fabric_version=0.114.0+1.21.4
@@ -1,11 +1,11 @@
package org.saturnclient.modules; package org.saturnclient.modules;
import net.minecraft.client.gui.DrawContext; import org.saturnclient.ui2.RenderScope;
public interface HudMod { public interface HudMod {
public ModDimensions getDimensions(); public ModDimensions getDimensions();
public void renderDummy(DrawContext context); public void renderDummy(RenderScope scope);
public void render(DrawContext context); public void renderHud(RenderScope scope);
} }
@@ -1,64 +1,54 @@
package org.saturnclient.modules; package org.saturnclient.modules;
import org.saturnclient.saturnclient.config.ConfigManager; import java.util.Map;
import org.saturnclient.saturnclient.config.NamedProperty;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Property;
public class ModDimensions { public class ModDimensions {
public Property<Integer> x = new Property<>(0); public Property<Integer> x = Property.integer(0);
public Property<Integer> y = new Property<>(0); public Property<Integer> y = Property.integer(0);
public Property<Float> scale = new Property<>(1.0f); public Property<Float> scale = Property.floatProp(1.0f);
public Property<Integer> bgColor = Property.color(0x00000000);
public Property<Integer> fgColor = Property.color(0xFFffffff);
public Property<Integer> radius = Property.integer(0);
public Property<Integer> font = Property.font(0);
public boolean renderBackground = true;
public int width = 0; public int width = 0;
public int height = 0; public int height = 0;
private final ConfigManager dimensionsConfig; public NamedProperty<Map<String, Property<?>>> prop() {
return Property.namespace(Map.of(
public ModDimensions(ConfigManager configManager) { "X", x,
dimensionsConfig = new ConfigManager(configManager, "Dimensions"); "Y", y,
dimensionsConfig.property("x", this.x); "Scale", scale,
dimensionsConfig.property("y", this.y); "Background Color", bgColor,
dimensionsConfig.property("scale", this.scale); "Foreground Color", fgColor,
"Corner Radius", radius,
"Font", font
)).named("In-Game Display");
} }
public ModDimensions(ConfigManager configManager, int x, int y) { public ModDimensions() {}
dimensionsConfig = new ConfigManager(configManager, "Dimensions");
this.x.value = x; public ModDimensions(int width, int height) {
this.y.value = y; this.width = width;
dimensionsConfig.property("x", this.x); this.height = height;
dimensionsConfig.property("y", this.y);
dimensionsConfig.property("scale", this.scale);
} }
public ModDimensions(ConfigManager configManager, int x, int y, int width, int height) { public ModDimensions(int x, int y, int width, int height) {
dimensionsConfig = new ConfigManager(configManager, "Dimensions");
this.x.value = x; this.x.value = x;
this.y.value = y; this.y.value = y;
this.width = width; this.width = width;
this.height = height; this.height = height;
dimensionsConfig.property("x", this.x);
dimensionsConfig.property("y", this.y);
dimensionsConfig.property("scale", this.scale);
} }
public ModDimensions(ConfigManager configManager, int x, int y, float scale) { public ModDimensions(int x, int y, int width, int height, float scale) {
dimensionsConfig = new ConfigManager(configManager, "Dimensions");
this.x.value = x;
this.y.value = y;
this.scale.value = scale;
dimensionsConfig.property("x", this.x);
dimensionsConfig.property("y", this.y);
dimensionsConfig.property("scale", this.scale);
}
public ModDimensions(ConfigManager configManager, int x, int y, int width, int height, float scale) {
dimensionsConfig = new ConfigManager(configManager, "Dimensions");
this.x.value = x; this.x.value = x;
this.y.value = y; this.y.value = y;
this.scale.value = scale; this.scale.value = scale;
this.width = width; this.width = width;
this.height = height; this.height = height;
dimensionsConfig.property("x", this.x);
dimensionsConfig.property("y", this.y);
dimensionsConfig.property("scale", this.scale);
} }
} }
@@ -3,7 +3,7 @@ package org.saturnclient.modules;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
import net.minecraft.client.font.TextRenderer; import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.render.RenderLayer;
import org.saturnclient.modules.mods.*; import org.saturnclient.modules.mods.*;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
@@ -13,7 +13,14 @@ import org.saturnclient.ui2.RenderScope;
public class ModManager { public class ModManager {
public static Module[] MODS = { public static Module[] MODS = {
new Crosshair() new Crosshair(),
new AutoSprint(),
new ArmorDisplay(),
new Fps(),
new PotionDisplay(),
new Coordinates(),
new Freelook(),
new Keystrokes()
}; };
public static void init() { public static void init() {
@@ -37,16 +44,21 @@ public class ModManager {
if (m instanceof HudMod && m.isEnabled()) { if (m instanceof HudMod && m.isEnabled()) {
ModDimensions dim = ((HudMod) m).getDimensions(); ModDimensions dim = ((HudMod) m).getDimensions();
MatrixStack matrices = context.getMatrices(); renderScope.matrices.push();
matrices.push();
matrices.translate(dim.x.value, (float) dim.y.value, 0); renderScope.matrices.translate(dim.x.value, (float) dim.y.value, 0);
matrices.scale(dim.scale.value, dim.scale.value, 1.0f); renderScope.matrices.scale(dim.scale.value, dim.scale.value, 1.0f);
((HudMod) m).render(context); if (dim.renderBackground) {
renderScope.drawRoundedRectangle(0, 0, dim.width, dim.height, dim.radius.value, dim.bgColor.value);
}
matrices.pop(); renderScope.setRenderLayer(RenderLayer::getGuiTextured);
((HudMod) m).renderHud(renderScope);
renderScope.matrices.pop();
} else { } else {
m.render(renderScope); m.render(renderScope);
} }
@@ -2,34 +2,23 @@ package org.saturnclient.modules;
import org.saturnclient.saturnclient.config.ConfigManager; import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.NamedProperty; import org.saturnclient.saturnclient.config.NamedProperty;
import org.saturnclient.ui.Textures; import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
public abstract class Module { public abstract class Module {
private final String name; private final ModuleDetails details;
private final String namespace;
private final ConfigManager configManager; private final ConfigManager configManager;
public Module(String name, String namespace, NamedProperty<?>... props) { public Module(ModuleDetails details, NamedProperty<?>... props) {
this.name = name; this.details = details;
this.namespace = namespace; configManager = new ConfigManager(details.name);
configManager = new ConfigManager(name);
for (NamedProperty<?> prop : props) { for (NamedProperty<?> prop : props) {
configManager.property(prop.name, prop.prop); configManager.property(prop.name, prop.prop);
} }
} }
public Module(ConfigManager config, String name, String namespace) {
this.name = name;
this.namespace = namespace;
configManager = config;
}
public void render(RenderScope scope) {} public void render(RenderScope scope) {}
public void tick() {} public void tick() {}
@@ -38,14 +27,26 @@ public abstract class Module {
public abstract void setEnabled(boolean e); public abstract void setEnabled(boolean e);
public final String getName() { public final String getName() {
return name; return details.name;
} }
public Identifier getIconTexture() { public Identifier getIconTexture() {
return Textures.getModIcon(namespace); return Textures.getModIcon(details.namespace);
} }
public ConfigManager getConfig() { public ConfigManager getConfig() {
return configManager; return configManager;
} }
public String getDescription() {
return details.description;
}
public String[] getTags() {
return details.tags;
}
public String getVersion() {
return details.version;
}
} }
@@ -0,0 +1,39 @@
package org.saturnclient.modules;
public class ModuleDetails {
public String name;
public String namespace;
public String description;
public String version;
public String[] tags = {};
public ModuleDetails(String name, String namespace) {
this.name = name;
this.namespace = namespace;
}
public ModuleDetails name(String name) {
this.name = name;
return this;
}
public ModuleDetails namespace(String namespace) {
this.namespace = namespace;
return this;
}
public ModuleDetails description(String description) {
this.description = description;
return this;
}
public ModuleDetails version(String version) {
this.version = version;
return this;
}
public ModuleDetails tags(String... tags) {
this.tags = tags;
return this;
}
}
@@ -0,0 +1,6 @@
# Credits
### Freelook
- https://github.com/Celibistrial/freelook
- https://modrinth.com/mod/freelook
**The other mods are coded by us, if you have a concern or we missed something feel free to submit a issue with the credits tag**
@@ -0,0 +1,89 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui2.RenderScope;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
public class ArmorDisplay extends Module implements HudMod {
private static Property<Boolean> enabled = Property.bool(false);
private static ModDimensions dimensions = new ModDimensions(40, 60);
public ArmorDisplay() {
super(new ModuleDetails("Armor Display", "armor")
.description("Displays armor durability")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
dimensions.prop());
}
public void renderArmor(RenderScope scope, ItemStack helmet, ItemStack chestplate, ItemStack leggings, ItemStack boots) {
int row = 0;
if (!helmet.isEmpty()) {
scope.drawItem(helmet, 0, 0);
renderHealth(scope, row, helmet.getMaxDamage(), helmet.getDamage());
row++;
}
if (!chestplate.isEmpty()) {
scope.drawItem(chestplate, 0, 15 * row);
renderHealth(scope, row, chestplate.getMaxDamage(), chestplate.getDamage());
row++;
}
if (!leggings.isEmpty()) {
scope.drawItem(leggings, 0, 15 * row);
renderHealth(scope, row, leggings.getMaxDamage(), leggings.getDamage());
row++;
}
if (!boots.isEmpty()) {
scope.drawItem(boots, 0, 15 * row);
renderHealth(scope, row, boots.getMaxDamage(), boots.getDamage());
row++;
}
}
public void renderHealth(RenderScope scope, int i, int max_damage, int damage) {
if (max_damage > 0 ) {
scope.drawText(0.5f, "" + (max_damage - damage), 17, (15 * i) + 3, dimensions.font.value, dimensions.fgColor.value);
}
}
@Override
public void renderHud(RenderScope scope) {
renderArmor(scope,
SaturnClient.client.player.getEquippedStack(EquipmentSlot.HEAD),
SaturnClient.client.player.getEquippedStack(EquipmentSlot.CHEST),
SaturnClient.client.player.getEquippedStack(EquipmentSlot.LEGS),
SaturnClient.client.player.getEquippedStack(EquipmentSlot.FEET));
}
@Override
public void renderDummy(RenderScope scope) {
renderArmor(scope, new ItemStack(Items.DIAMOND_HELMET), new ItemStack(Items.DIAMOND_CHESTPLATE), new ItemStack(Items.DIAMOND_LEGGINGS), new ItemStack(Items.DIAMOND_BOOTS));
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void setEnabled(boolean e) {
enabled.value = e;
}
}
@@ -0,0 +1,42 @@
package org.saturnclient.modules.mods;
import java.util.Objects;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property;
public class AutoSprint extends Module {
private static Property<Boolean> enabled = Property.bool(false);
public AutoSprint() {
super(
new ModuleDetails("Auto Sprint", "sprint")
.description("Makes the player always sprint")
.tags("Movement")
.version("v0.1.0"),
enabled.named("Enabled"));
}
@Override
public void tick() {
if (SaturnClient.client.player == null || SaturnClient.client.getNetworkHandler() == null) {
return;
}
if (SaturnClient.client.options.forwardKey.isPressed() && !SaturnClient.client.options.backKey.isPressed() && !SaturnClient.client.player.isSneaking() &&
!SaturnClient.client.player.horizontalCollision) {
Objects.requireNonNull(SaturnClient.client.player).setSprinting(true);
}
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void setEnabled(boolean e) {
enabled.value = e;
}
}
@@ -0,0 +1,75 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
import net.minecraft.util.math.Vec3d;
public class Coordinates extends Module implements HudMod {
private static Property<Boolean> enabled = Property.bool(false);
private static Property<Integer> displayMethod = Property.select(0, "Flat", "Flat annotated", "Newline", "Newline annotated");
private static ModDimensions dimensions = new ModDimensions(40, 18);
public Coordinates() {
super(new ModuleDetails("Coordinates", "coords")
.description("Displays your current coordinates")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
displayMethod.named("Display Method"),
dimensions.prop()
);
}
@Override
public void renderDummy(RenderScope scope) {
renderCoords(532, 69, 253, scope);
}
@Override
public void renderHud(RenderScope scope) {
Vec3d pos = SaturnClient.client.player.getPos();
renderCoords((int) pos.x, (int) pos.y, (int) pos.z, scope);
}
public void renderCoords(int x, int y, int z, RenderScope scope) {
String f = "%d %d %d"; // Flat
switch (displayMethod.value) {
case 1: // Flat annotated
f = "X: %d Y: %d Z: %d";
break;
case 2: // Newline
f = "%d\n%d\n%d";
break;
case 3: // Newline annotated
f = "X: %d\nY: %d\nZ: %d";
break;
}
String text = String.format(f, x, y, z);
scope.drawText(text,
0, 0, dimensions.font.value, dimensions.fgColor.value);
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
dimensions.height = 18 * text.split("\n").length;
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
@Override
public void setEnabled(boolean e) {
enabled.value = e;
}
}
@@ -1,6 +1,7 @@
package org.saturnclient.modules.mods; package org.saturnclient.modules.mods;
import org.saturnclient.modules.Module; import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
@@ -9,26 +10,29 @@ import org.saturnclient.ui2.resources.Textures;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
public class Crosshair extends Module { public class Crosshair extends Module {
public static Property<Boolean> enabled = new Property<>(false); public static Property<Boolean> enabled = Property.bool(false);
public static Property<Boolean> range_indicator = new Property<>(false);
public Crosshair() { public Crosshair() {
super("Crosshair", "crosshair", enabled.named("Enabled"), range_indicator.named("Range Indicator")); super(
new ModuleDetails("Crosshair", "crosshair")
.description("Changes the crosshair if you're aiming at a entity")
.tags("Visuals", "Utility")
.version("v0.1.0"),
enabled.named("Enabled"));
} }
@Override @Override
public void render(RenderScope scope) { public void render(RenderScope scope) {
if (enabled.value if (enabled.value
&& range_indicator.value
&& SaturnClient.client.targetedEntity != null && SaturnClient.client.targetedEntity != null
&& SaturnClient.client.targetedEntity.isAlive()) { && SaturnClient.client.targetedEntity.isAlive()) {
int scaledWidth = 15; int scaledWidth = 15;
int scaledHeight = 15; int scaledHeight = 15;
scope.drawTexture(RenderLayer::getCrosshair, Textures.CROSSHAIR_RANGE, scope.setRenderLayer(RenderLayer::getCrosshair);
scope.drawTexture(Textures.CROSSHAIR_RANGE,
(scope.getScaledWindowWidth() - scaledWidth) / 2, (scope.getScaledWindowWidth() - scaledWidth) / 2,
(scope.getScaledWindowHeight() - scaledHeight) / 2, 0.0F, 0.0F, scaledWidth, scaledHeight, (scope.getScaledWindowHeight() - scaledHeight) / 2, 0.0F, 0.0F, scaledWidth, scaledHeight);
scaledWidth, scaledHeight);
} }
} }
@@ -0,0 +1,53 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
public class Fps extends Module implements HudMod {
private static Property<Boolean> enabled = Property.bool(false);
private static ModDimensions dimensions = new ModDimensions(60, Fonts.getHeight());
public Fps() {
super(new ModuleDetails("FPS Display", "fps")
.description("Displays current FPS")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
dimensions.prop());
}
@Override
public void renderHud(RenderScope scope) {
String text = String.valueOf(SaturnClient.client.getCurrentFps()) + " FPS";
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
}
@Override
public void renderDummy(RenderScope scope) {
String text = "369 FPS";
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void setEnabled(boolean e) {
enabled.value = e;
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
}
@@ -0,0 +1,71 @@
package org.saturnclient.modules.mods;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property;
import net.minecraft.client.option.Perspective;
public class Freelook extends Module {
private static Property<Boolean> enabled = Property.bool(false);
private static Property<Boolean> toggle = Property.bool(true);
public static Property<Integer> freelookKey = Property.keybinding(GLFW.GLFW_KEY_H);
public static boolean isFreeLooking = false;
private static boolean was1stPerson = false;
public Freelook() {
super(
new ModuleDetails("Freelook", "freelook")
.description("Allows you to look around freely without moving your character")
.tags("Movement", "Camera")
.version("0.1.0"),
enabled.named("Enabled"),
toggle.named("Toggle freelook"),
freelookKey.named("Freelook Keybinding")
);
}
@Override
public void tick() {
if (toggle.value) {
if (freelookKey.wasKeyPressed()) {
if (isFreeLooking) {
stopFreelook();
} else {
startFreelook();
}
}
} else if (freelookKey.isKeyPressed()) {
if (!isFreeLooking) {
startFreelook();
}
} else if (isFreeLooking) {
stopFreelook();
}
}
private void startFreelook() {
isFreeLooking = true;
was1stPerson = SaturnClient.client.options.getPerspective() == Perspective.FIRST_PERSON;
SaturnClient.client.options.setPerspective(Perspective.THIRD_PERSON_BACK);
}
private void stopFreelook() {
if (was1stPerson) {
SaturnClient.client.options.setPerspective(Perspective.FIRST_PERSON);
}
isFreeLooking = false;
}
@Override
public void setEnabled(boolean e) {
enabled.value = e;
}
@Override
public boolean isEnabled() {
return enabled.value;
}
}
@@ -0,0 +1,139 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.modules.ModuleDetails;
public class Keystrokes extends Module implements HudMod {
public static Property<Boolean> enabled = Property.bool(false);
public static Property<Boolean> showMouse = Property.bool(true);
public static Property<Boolean> showSpace = Property.bool(false);
public static ModDimensions dimensions = new ModDimensions(78, 46);
private boolean w = false;
private boolean a = false;
private boolean s = false;
private boolean d = false;
private boolean lmb = false;
private boolean rmb = false;
private boolean space = false;
private static Property<Integer> clickBg = Property.color(0xFFCCCCCC);
private static Property<Integer> clickFg = Property.color(0xFFFFFFFF);
public Keystrokes() {
super(new ModuleDetails("Keystrokes", "keystrokes")
.description("Displays the keystrokes for movement")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
showMouse.named("Show mouse clicks"),
showSpace.named("Show space clicks"),
dimensions.prop(),
clickFg.named("Clicked fg"),
clickBg.named("Clicked bg"));
dimensions.renderBackground = false;
}
@Override
public void tick() {
w = SaturnClient.client.options.forwardKey.isPressed();
a = SaturnClient.client.options.leftKey.isPressed();
s = SaturnClient.client.options.backKey.isPressed();
d = SaturnClient.client.options.rightKey.isPressed();
lmb = SaturnClient.client.options.attackKey.isPressed();
rmb = SaturnClient.client.options.useKey.isPressed();
space = SaturnClient.client.options.jumpKey.isPressed();
}
@Override
public void renderDummy(RenderScope scope) {
dimensions.height = 54; // base height with padding considered
// WASD keys (3px padding between each key)
renderKey(scope, true, 'W', 27, 0); // Top center
renderKey(scope, false, 'A', 0, 27); // Bottom left
renderKey(scope, false, 'S', 27, 27); // Bottom center
renderKey(scope, false, 'D', 54, 27); // Bottom right
if (showMouse.value) {
renderKeyM(scope, true, "LMB", 0, 54); // Below A/S/D
renderKeyM(scope, false, "RMB", 40, 54); // With 3px between
dimensions.height += 27;
}
if (showSpace.value) {
renderKeySpace(scope, false, 0, dimensions.height);
dimensions.height += 22;
}
}
@Override
public void renderHud(RenderScope scope) {
dimensions.height = 54; // base height with padding considered
// WASD keys (3px padding between each key)
renderKey(scope, w, 'W', 27, 0); // Top center
renderKey(scope, a, 'A', 0, 27); // Bottom left
renderKey(scope, s, 'S', 27, 27); // Bottom center
renderKey(scope, d, 'D', 54, 27); // Bottom right
if (showMouse.value) {
renderKeyM(scope, lmb, "LMB", 0, 54); // Below A/S/D
renderKeyM(scope, rmb, "RMB", 40, 54); // With 3px between
dimensions.height += 27;
}
if (showSpace.value) {
renderKeySpace(scope, space, 0, dimensions.height);
dimensions.height += 22;
}
}
// Consistent 3px padding applied below:
private void renderKeySpace(RenderScope scope, boolean isPressed, int x, int y) {
int height = 19;
scope.drawRoundedRectangle(x, y, dimensions.width, height, dimensions.radius.value, isPressed ? clickBg.value : dimensions.bgColor.value);
// A 30-pixel line centered vertically with 3px top padding
int lineY = y + (height - 1) / 2;
scope.drawRect(x + (dimensions.width - 30) / 2, lineY, 30, 1, isPressed ? clickFg.value : dimensions.fgColor.value);
}
private void renderKeyM(RenderScope scope, boolean isPressed, String c, int x, int y) {
int width = 38, height = 24;
scope.drawRoundedRectangle(x, y, width, height, dimensions.radius.value, isPressed ? clickBg.value : dimensions.bgColor.value);
// Apply padding: 3px top and left -> add slight offset to x/y
scope.drawText(0.6f, c, x + 9, y + 7, dimensions.font.value, isPressed ? clickFg.value : dimensions.fgColor.value);
}
private void renderKey(RenderScope scope, boolean isPressed, char c, int x, int y) {
int size = 24;
scope.drawRoundedRectangle(x, y, size, size, dimensions.radius.value, isPressed ? clickBg.value : dimensions.bgColor.value);
// Center the character with 3px top padding
scope.drawText(0.6f, String.valueOf(c), x + 9, y + 7, dimensions.font.value, isPressed ? clickFg.value : dimensions.fgColor.value);
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
@Override
public void setEnabled(boolean e) {
enabled.value = e;
}
}
@@ -0,0 +1,105 @@
package org.saturnclient.modules.mods;
import java.util.Arrays;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui2.RenderScope;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.StatusEffectSpriteManager;
import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.registry.Registries;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
public class PotionDisplay extends Module implements HudMod {
public static Property<Boolean> enabled = Property.bool(false);
private static ModDimensions dimensions = new ModDimensions(60, 0);
private final StatusEffectInstance[] dummyEffects = {
new StatusEffectInstance(Registries.STATUS_EFFECT.getEntry(Identifier.ofVanilla("speed")).get(), 12000, 2),
new StatusEffectInstance(Registries.STATUS_EFFECT.getEntry(Identifier.ofVanilla("strength")).get(), 12000, 2),
new StatusEffectInstance(Registries.STATUS_EFFECT.getEntry(Identifier.ofVanilla("fire_resistance")).get(), 12000, 2),
};
public PotionDisplay() {
super(new ModuleDetails("Potion Display", "potion")
.description("Displays potion effects")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
dimensions.prop());
}
private void renderEffects(RenderScope scope, Iterable<StatusEffectInstance> effects) {
int row = 0;
StatusEffectSpriteManager statusEffectSpriteManager = SaturnClient.client.getStatusEffectSpriteManager();
for (StatusEffectInstance effect : effects) {
if (effect.shouldShowIcon()) {
RegistryEntry<StatusEffect> registryEntry = effect.getEffectType();
Sprite sprite = statusEffectSpriteManager.getSprite(registryEntry);
scope.drawSpriteStretched(sprite, 0, 18 * row, 16, 16);
scope.drawText(0.5f, getDurationAsString(effect), 18, 18 * row + 3, dimensions.font.value, -1);
row++;
}
}
dimensions.height = 18 * row;
}
@Override
public void renderHud(RenderScope scope) {
renderEffects(scope, SaturnClient.client.player.getStatusEffects());
}
@Override
public void renderDummy(RenderScope scope) {
renderEffects(scope, Arrays.asList(dummyEffects));
}
// This code was from: https://github.com/magicus/statuseffecttimer/blob/master/src/main/java/se/icus/mag/statuseffecttimer/mixin/StatusEffectTimerMixin.java
private String getDurationAsString(StatusEffectInstance statusEffectInstance) {
if (statusEffectInstance.isInfinite()) {
return I18n.translate("effect.duration.infinite");
}
int ticks = MathHelper.floor((float) statusEffectInstance.getDuration());
int totalSeconds = ticks / 20;
if (totalSeconds >= 3600) {
return totalSeconds / 3600 + "h";
} else if (totalSeconds >= 60) {
int minutes = totalSeconds / 60;
int seconds = totalSeconds % 60;
return String.format("%d:%02d", minutes, seconds);
} else {
return String.valueOf(totalSeconds) + "s";
}
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void setEnabled(boolean e) {
enabled.value = e;
}
}
@@ -0,0 +1,44 @@
package org.saturnclient.modules.mods.mixins;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.render.Camera;
import net.minecraft.entity.Entity;
import net.minecraft.world.BlockView;
import org.saturnclient.modules.mods.Freelook;
import org.saturnclient.modules.mods.utils.CameraOverriddenEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Camera.class)
public abstract class CameraMixin {
@Unique
boolean firstTime = true;
@Shadow
protected abstract void setRotation(float yaw, float pitch);
@Inject(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/Camera;setRotation(FF)V", ordinal = 1, shift = At.Shift.AFTER))
public void lockRotation(BlockView focusedBlock, Entity cameraEntity, boolean isThirdPerson, boolean isFrontFacing, float tickDelta, CallbackInfo ci) {
if (Freelook.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) {
CameraOverriddenEntity cameraOverriddenEntity = (CameraOverriddenEntity) cameraEntity;
if (firstTime && MinecraftClient.getInstance().player != null) {
cameraOverriddenEntity.freelook$setCameraPitch(MinecraftClient.getInstance().player.getPitch());
cameraOverriddenEntity.freelook$setCameraYaw(MinecraftClient.getInstance().player.getYaw());
firstTime = false;
}
this.setRotation(cameraOverriddenEntity.freelook$getCameraYaw(), cameraOverriddenEntity.freelook$getCameraPitch());
}
if (!Freelook.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) {
firstTime = true;
}
}
}
@@ -0,0 +1,62 @@
package org.saturnclient.modules.mods.mixins;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.MathHelper;
import org.saturnclient.modules.mods.Freelook;
import org.saturnclient.modules.mods.utils.CameraOverriddenEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Entity.class)
public class EntityMixin implements CameraOverriddenEntity {
@Unique
private float cameraPitch;
@Unique
private float cameraYaw;
@Inject(method = "changeLookDirection", at = @At("HEAD"), cancellable = true)
public void changeCameraLookDirection(double xDelta, double yDelta, CallbackInfo ci) {
//noinspection ConstantValue// IntelliJ is incorrect here, this code block is reachable
if (Freelook.isFreeLooking && (Object) this instanceof ClientPlayerEntity) {
double pitchDelta = (yDelta * 0.15);
double yawDelta = (xDelta * 0.15);
this.cameraPitch = MathHelper.clamp(this.cameraPitch + (float) pitchDelta, -90.0f, 90.0f);
this.cameraYaw += (float) yawDelta;
ci.cancel();
}
}
@Override
@Unique
public float freelook$getCameraPitch() {
return this.cameraPitch;
}
@Override
@Unique
public float freelook$getCameraYaw() {
return this.cameraYaw;
}
@Override
@Unique
public void freelook$setCameraPitch(float pitch) {
this.cameraPitch = pitch;
}
@Override
@Unique
public void freelook$setCameraYaw(float yaw) {
this.cameraYaw = yaw;
}
}
@@ -0,0 +1,9 @@
package org.saturnclient.modules.mods.utils;
public interface CameraOverriddenEntity {
float freelook$getCameraPitch();
float freelook$getCameraYaw();
void freelook$setCameraPitch(float pitch);
void freelook$setCameraYaw(float yaw);
}
@@ -4,17 +4,21 @@ import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.TitleScreen; import net.minecraft.client.util.math.MatrixStack;
import org.saturnclient.modules.ModManager; import org.saturnclient.modules.ModManager;
import org.saturnclient.saturnclient.auth.Auth; import org.saturnclient.saturnclient.auth.Auth;
import org.saturnclient.saturnclient.config.ConfigManager; import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.ThemeManager; import org.saturnclient.saturnclient.cosmetics.Emotes;
import org.saturnclient.saturnclient.cosmetics.Hats; import org.saturnclient.saturnclient.cosmetics.Hats;
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks; import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
import org.saturnclient.saturnclient.event.KeyInputHandler; import org.saturnclient.saturnclient.event.KeyInputHandler;
import org.saturnclient.saturnclient.menus.SaturnMenu; import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
import org.saturnclient.saturnclient.menus.SaturnTitleScreen; import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.elements.ImageTexture;
import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui2.screens.TitleMenu;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -33,24 +37,43 @@ public class SaturnClient implements ModInitializer {
public void onInitialize() { public void onInitialize() {
LOGGER.info("Initializing " + MOD_ID); LOGGER.info("Initializing " + MOD_ID);
client = MinecraftClient.getInstance(); client = MinecraftClient.getInstance();
ModManager.init();
SaturnClientConfig.init(); SaturnClientConfig.init();
ThemeManager.load(); ModManager.init();
ConfigManager.load();
ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> {ConfigManager.save(); ThemeManager.save();}); ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> ConfigManager.save());
ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
if (SaturnClientConfig.saturnTitleScreen.value && screen instanceof TitleScreen
&& !(screen instanceof SaturnMenu)) {
client.setScreen(new SaturnTitleScreen());
}
});
SpecialModelLoaderEvents.LOAD_SCOPE.register(() -> { SpecialModelLoaderEvents.LOAD_SCOPE.register(() -> {
return (resourceManager, location) -> MOD_ID.equals(location.getNamespace()); return (resourceManager, location) -> MOD_ID.equals(location.getNamespace());
}); });
ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
if (screen != null && !(screen instanceof TitleMenu)) {
if (screen instanceof SaturnScreen) {
((SaturnScreen) screen).draw(new ImageTexture(Textures.LOGO_TEXT_BIG).dimensions(180, 18).position(screen.width - 180 - 20, screen.height - 18 - 20));
} else {
ScreenEvents.afterRender(screen).register((screen1, context, mouseX, mouseY, tickDelta) -> {
RenderScope renderScope = new RenderScope(context.getMatrices(),
((DrawContextAccessor) context).getVertexConsumers());
int width = context.getScaledWindowWidth();
int height = context.getScaledWindowHeight();
float scaleX = (float) screen1.width / width;
float scaleY = (float) screen1.height / height;
MatrixStack matrixStack = context.getMatrices();
matrixStack.push();
matrixStack.translate(width - 95, height - 14, 0);
matrixStack.scale(0.5f / scaleX, 0.5f / scaleY, 1.0f);
renderScope.drawTexture(Textures.LOGO_TEXT_BIG, 0, 0, 0, 0, 180, 18);
matrixStack.pop();
});
}
}
});
KeyInputHandler.register(); KeyInputHandler.register();
Emotes.initialize();
if (Auth.authenticate()) { if (Auth.authenticate()) {
Cloaks.initialize(); Cloaks.initialize();
Hats.initialize(); Hats.initialize();
@@ -1,48 +1,20 @@
package org.saturnclient.saturnclient; package org.saturnclient.saturnclient;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Property;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.config.ConfigManager; import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.ui.SaturnAnimation; import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.Textures;
import org.saturnclient.ui.animations.FadeIn;
import net.minecraft.util.Formatting; import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.ColorHelper;
public class SaturnClientConfig { public class SaturnClientConfig {
public static ConfigManager config; public static ConfigManager config;
public static Property<Integer> color = public static Property<Boolean> realisticLogo = Property.bool(false);
new Property<>(
ColorHelper.getArgb(255, 251, 60, 79),
Property.PropertyType.HEX);
public static Property<Integer> normal = public static Property<Boolean> saturnTitleScreen = Property.bool(true);
new Property<>( public static Property<Integer> openEmoteWheel = Property.keybinding(GLFW.GLFW_KEY_B);
ColorHelper.getArgb(255, 182, 182, 182),
Property.PropertyType.HEX);
public static Property<Boolean> realisticLogo =
new Property<>(false);
public static Property<Boolean> saturnTitleScreen =
new Property<>(true);
public static int WHITE = ColorHelper.getArgb(255, 255, 255, 255);
public static int getWhite(float alpha) {
return ((int) (alpha * 255) << 24) | (normal.value & 0x00FFFFFF);
}
public static int getColor(boolean hovering, float alpha) {
return SaturnUi.getAlpha(hovering ? color.value : normal.value, alpha);
}
public static SaturnAnimation[] getAnimations() {
return new SaturnAnimation[] { new FadeIn(1) };
}
public static Identifier getLogo() { public static Identifier getLogo() {
return realisticLogo.value ? Textures.REALISTIC_LOGO : Textures.LOGO; return realisticLogo.value ? Textures.REALISTIC_LOGO : Textures.LOGO;
@@ -71,10 +43,8 @@ public class SaturnClientConfig {
public static void init() { public static void init() {
config = new ConfigManager("Saturn Client"); config = new ConfigManager("Saturn Client");
config.property("Primary color", color);
config.property("Default color", normal);
config.property("Realistic logo", realisticLogo); config.property("Realistic logo", realisticLogo);
config.property("Saturn client title screen", saturnTitleScreen); config.property("Saturn client title screen", saturnTitleScreen);
config.property("Open Emote Wheel", openEmoteWheel);
} }
} }
@@ -5,6 +5,9 @@ import java.util.Map;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import org.saturnclient.saturnclient.cosmetics.Hats; import org.saturnclient.saturnclient.cosmetics.Hats;
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks; import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.elements.Notification;
import org.saturnclient.ui2.elements.Notification.NotificationKind;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
public class Auth { public class Auth {
@@ -96,9 +99,24 @@ public class Auth {
} }
break; break;
case "successful_transaction":
String cloak = parser.getStringOrNull("cloak");
String hat = parser.getStringOrNull("hat");
if (cloak != null) {
Cloaks.availableCloaks.add(cloak);
}
if (hat != null) {
Hats.availableHats.add(hat);
}
default: default:
if (parser.error != null) { if (parser.error != null) {
SaturnClient.LOGGER.error("Error from the server: " + parser.error); SaturnClient.LOGGER.error("Error from the server: " + parser.error);
if (SaturnClient.client.currentScreen instanceof SaturnScreen) {
String[] a = parser.error.replace("!", "").split(": ");
((SaturnScreen) SaturnClient.client.currentScreen).draw(new Notification(SaturnClient.client.currentScreen.width, SaturnClient.client.currentScreen.height, NotificationKind.Error, a[0], a[1]));
}
} }
break; break;
} }
@@ -117,17 +135,22 @@ public class Auth {
private static void startPingThread() { private static void startPingThread() {
pingThread = new Thread(() -> { pingThread = new Thread(() -> {
try { try {
while (running) { while (running && !Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(25000); // 25 seconds Thread.sleep(25000); // 25 seconds
if (running) { if (running) {
Network.write("ping"); Network.write("ping");
} }
}
} catch (InterruptedException e) { } catch (InterruptedException e) {
SaturnClient.LOGGER.warn("Ping thread interrupted", e); Thread.currentThread().interrupt(); // Restore interrupt status
break; // Exit the loop on interruption
}
}
} catch (Exception e) { } catch (Exception e) {
if (!(e instanceof InterruptedException)) {
SaturnClient.LOGGER.error("Error in ping thread", e); SaturnClient.LOGGER.error("Error in ping thread", e);
} }
}
}); });
pingThread.setDaemon(true); pingThread.setDaemon(true);
@@ -189,4 +212,44 @@ public class Auth {
} }
} }
} }
public static void sendEmote(String emote) {
String uuid = SaturnClient.client.player.getUuidAsString().replace("-", "");
for (Map.Entry<String, String> player : playerNames.entrySet()) {
if (players.containsKey(player.getValue()) && !player.getValue().equals(uuid)) {
SaturnClient.client.player.networkHandler
.sendChatCommand("msg " + player.getKey() + " "
+ "$SATURN_EMOTE&@"+uuid+"&@"+emote+"&@ if you are seeing this as a player, please report this to https://github.com/saturnclientmc/saturnclient/issues");
;
}
}
}
public static void cancelEmote() {
String uuid = SaturnClient.client.player.getUuidAsString().replace("-", "");
for (Map.Entry<String, String> player : playerNames.entrySet()) {
if (players.containsKey(player.getValue()) && !player.getValue().equals(uuid)) {
SaturnClient.client.player.networkHandler
.sendChatCommand("msg " + player.getKey() + " "
+ "$SATURN_CANCEL_EMOTE&@"+uuid+"&@ if you are seeing this as a player, please report this to https://github.com/saturnclientmc/saturnclient/issues");
;
}
}
}
public static void buyCloak(String name) {
try {
Network.write("buy_cloak@cloak=" + name);
} catch (Exception e) {
SaturnClient.LOGGER.error("Request failed", e);
}
}
public static void buyHat(String name) {
try {
Network.write("buy_hat@hat=" + name);
} catch (Exception e) {
SaturnClient.LOGGER.error("Request failed", e);
}
}
} }
@@ -43,6 +43,11 @@ public class SaturnParser {
} }
} }
public String getStringOrNull(String i) throws IOException {
String val = params.get(i);
return val;
}
public boolean getBool(String i) throws IOException { public boolean getBool(String i) throws IOException {
String val = params.get(i); String val = params.get(i);
if (val != null) { if (val != null) {
@@ -15,13 +15,14 @@ import java.util.Map;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
public class ConfigManager { public class ConfigManager {
private static File configFile = new File( private static File configFile = new File(
SaturnClient.client.runDirectory, SaturnClient.client.runDirectory,
"saturn.json"); "saturn.json");
private static Map<String, Map<String, Property<?>>> properties = new HashMap<>(); private static Map<String, Map<String, Property<?>>> properties = new HashMap<>();
private static JsonObject cachedThemeJson = null;
private Map<String, Property<?>> currentMap; private Map<String, Property<?>> currentMap;
private String namespace;
public ConfigManager(String namespace) { public ConfigManager(String namespace) {
currentMap = new LinkedHashMap<>(); currentMap = new LinkedHashMap<>();
@@ -31,21 +32,60 @@ public class ConfigManager {
public ConfigManager(ConfigManager config, String namespace) { public ConfigManager(ConfigManager config, String namespace) {
currentMap = new LinkedHashMap<>(); currentMap = new LinkedHashMap<>();
Property<Map<String, Property<?>>> namespaceProperty = new Property<>(currentMap, Property<Map<String, Property<?>>> namespaceProperty = Property.namespace(currentMap);
Property.PropertyType.NAMESPACE);
config.property(namespace, namespaceProperty); config.property(namespace, namespaceProperty);
// Update the parent's state // Update the parent's state
config.currentMap.put(namespace, namespaceProperty); config.currentMap.put(namespace, namespaceProperty);
System.out.println("Created sub-namespace: " + namespace); System.out.println("Created sub-namespace: " + namespace);
} }
public void sub(String namespace) {
Property<Map<String, Property<?>>> namespaceProperty = Property.namespace(currentMap);
property(namespace, namespaceProperty);
}
// Generic method to store any type of property // Generic method to store any type of property
public <T> Property<T> property(String name, Property<T> value) { public <T> Property<T> property(String name, Property<T> value) {
System.out.println("Adding property: " + name); System.out.println("Adding property: " + name);
currentMap.put(name, value); currentMap.put(name, value);
loadProp(name, value);
return value; return value;
} }
@SuppressWarnings("unchecked")
public <T> void loadProp(String name, Property<T> prop) {
if (cachedThemeJson != null) {
JsonElement element = cachedThemeJson.get(namespace);
if (element != null && element.isJsonObject()) {
JsonObject theme = element.getAsJsonObject();
JsonElement value = theme.get(name);
if (value != null && prop.matchesJson(value)) {
switch (prop.getType()) {
case BOOLEAN:
((Property<Boolean>) prop).setValue(value.getAsBoolean());
break;
case INTEGER:
((Property<Integer>) prop).setValue(value.getAsInt());
break;
case FLOAT:
((Property<Float>) prop).setValue(value.getAsFloat());
break;
case STRING:
((Property<String>) prop).setValue(value.getAsString());
break;
case HEX:
((Property<Integer>) prop).setValue(value.getAsInt());
break;
default:
break;
}
}
}
} else {
load();
}
}
public static void load() { public static void load() {
try { try {
SaturnClient.LOGGER.info("Starting to load config..."); SaturnClient.LOGGER.info("Starting to load config...");
@@ -97,25 +137,17 @@ public class ConfigManager {
Map<String, Property<?>> nestedProperties = p.getNamespaceValue(); Map<String, Property<?>> nestedProperties = p.getNamespaceValue();
loadProperties(nestedConfig, nestedProperties); loadProperties(nestedConfig, nestedProperties);
} else { } else {
// Handle regular properties if (p.value instanceof Integer) {
switch (p.getType()) {
case BOOLEAN:
((Property<Boolean>) p).value = c.getAsBoolean();
break;
case INTEGER:
((Property<Integer>) p).value = c.getAsInt(); ((Property<Integer>) p).value = c.getAsInt();
break; } else if (p.value instanceof String) {
case FLOAT:
((Property<Float>) p).value = c.getAsFloat();
break;
case STRING:
((Property<String>) p).value = c.getAsString(); ((Property<String>) p).value = c.getAsString();
break; } else if (p.value instanceof Float) {
case HEX: ((Property<Float>) p).value = c.getAsFloat();
((Property<Integer>) p).value = c.getAsInt(); } else if (p.value instanceof Boolean) {
break; ((Property<Boolean>) p).value = c.getAsBoolean();
default: } else {
break; SaturnClient.LOGGER.warn(
"Unknown property type for: " + propertyName);
} }
} }
} else { } else {
@@ -161,36 +193,29 @@ public class ConfigManager {
for (String propertyName : propertyMap.keySet()) { for (String propertyName : propertyMap.keySet()) {
Property<?> property = propertyMap.get(propertyName); Property<?> property = propertyMap.get(propertyName);
JsonElement propertyValue = null; JsonElement propertyValue = null;
// Convert the property value based on its type
if (property.getType() == Property.PropertyType.NAMESPACE) { switch (property.getType()) {
case NAMESPACE:
// Handle nested namespace // Handle nested namespace
JsonObject nestedConfig = new JsonObject(); JsonObject nestedConfig = new JsonObject();
Map<String, Property<?>> nestedProperties = (Map<String, Property<?>>) property.value; Map<String, Property<?>> nestedProperties = (Map<String, Property<?>>) property.value;
saveProperties(nestedConfig, nestedProperties); saveProperties(nestedConfig, nestedProperties);
propertyValue = nestedConfig; propertyValue = nestedConfig;
} else {
// Convert the property value based on its type
switch (property.getType()) {
case BOOLEAN:
propertyValue = new JsonPrimitive((Boolean) property.value);
break;
case INTEGER:
propertyValue = new JsonPrimitive((Integer) property.value);
break;
case FLOAT:
propertyValue = new JsonPrimitive((Float) property.value);
break;
case STRING:
propertyValue = new JsonPrimitive((String) property.value);
break;
case HEX:
propertyValue = new JsonPrimitive((Integer) property.value);
break; break;
default: default:
if (property.value instanceof Integer) {
propertyValue = new JsonPrimitive((Integer) property.value);
} else if (property.value instanceof String) {
propertyValue = new JsonPrimitive((String) property.value);
} else if (property.value instanceof Float) {
propertyValue = new JsonPrimitive((Float) property.value);
} else if (property.value instanceof Boolean) {
propertyValue = new JsonPrimitive((Boolean) property.value);
} else {
SaturnClient.LOGGER.warn( SaturnClient.LOGGER.warn(
"Unknown property type for: " + propertyName); "Unknown property type for: " + propertyName);
break;
} }
break;
} }
if (propertyValue != null) { if (propertyValue != null) {
@@ -2,6 +2,9 @@ package org.saturnclient.saturnclient.config;
import java.util.Map; import java.util.Map;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.SaturnClient;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive; import com.google.gson.JsonPrimitive;
@@ -13,62 +16,100 @@ public class Property<T> {
FLOAT, FLOAT,
STRING, STRING,
HEX, HEX,
NAMESPACE NAMESPACE,
SELECT,
KEYBINDING,
} }
public T value; public T value;
public boolean isReset = false; public boolean isReset = false;
private T defaultValue; private T defaultValue;
private String[] availableValues;
private PropertyType type; private PropertyType type;
private boolean wasPressedLastTick = false;
public Property(T value) { private Property(T value, PropertyType type) {
this.value = value;
this.defaultValue = value;
if (value instanceof Boolean) {
type = PropertyType.BOOLEAN;
} else if (value instanceof Integer) {
type = PropertyType.INTEGER;
} else if (value instanceof Float) {
type = PropertyType.FLOAT;
} else if (value instanceof String) {
type = PropertyType.STRING;
} else if (valueIsNamespace(value)) {
type = PropertyType.NAMESPACE;
}
}
public Property(T value, PropertyType type) {
this.value = value; this.value = value;
this.defaultValue = value; this.defaultValue = value;
this.type = type; this.type = type;
} }
public boolean getBool() { public static <T> Property<T> from(T value) {
if (value instanceof Boolean) { if (value instanceof Boolean) {
return (boolean) value; return new Property<>(value, PropertyType.BOOLEAN);
} else if (value instanceof Integer) {
return new Property<>(value, PropertyType.INTEGER);
} else if (value instanceof Float) {
return new Property<>(value, PropertyType.FLOAT);
} else if (value instanceof String) {
return new Property<>(value, PropertyType.STRING);
} else if (valueIsNamespace(value)) {
return new Property<>(value, PropertyType.NAMESPACE);
} else {
return null;
} }
throw new IllegalStateException("Property does not contain a boolean");
} }
public int getInt() { public static Property<Integer> font(int value) {
if (value instanceof Integer) { return select(value, "Default", "Inter", "Inter bold");
return (int) value;
}
throw new IllegalStateException("Property does not contain an int");
} }
public float getFloat() { public static Property<Integer> select(Integer value, String... availableValues) {
if (value instanceof Float) { Property<Integer> property = new Property<>(value, PropertyType.SELECT);
return (float) value; property.availableValues = availableValues;
} return property;
throw new IllegalStateException("Property does not contain a float");
} }
public String getString() { public static Property<Map<String, Property<?>>> namespace(Map<String, Property<?>> value) {
if (value instanceof String) { return new Property<>(value, PropertyType.NAMESPACE);
return (String) value; }
public static Property<Boolean> bool(boolean value) {
return new Property<>(value, PropertyType.BOOLEAN);
}
public static Property<Integer> integer(int value) {
return new Property<>(value, PropertyType.INTEGER);
}
public static Property<Float> floatProp(float value) {
return new Property<>(value, PropertyType.FLOAT);
}
public static Property<String> string(String value) {
return new Property<>(value, PropertyType.STRING);
}
public static Property<Integer> color(int value) {
return new Property<>(value, PropertyType.HEX);
}
public static Property<Integer> keybinding(int value) {
return new Property<>(value, PropertyType.KEYBINDING);
}
public void next() {
if ((Integer) value < availableValues.length-1) setValue((Integer)value+1);
else setValue(0);
}
public void prev() {
if ((Integer) value > 0) setValue((Integer)value-1);
else setValue(availableValues.length - 1);
}
public void setSelection(int selection) {
if (selection >= 0 && selection < availableValues.length && type == PropertyType.SELECT) {
setValue(selection);
}
}
public String getSelection() {
if (type == PropertyType.SELECT) {
return availableValues[(Integer) value];
} else {
return null;
} }
throw new IllegalStateException("Property does not contain a string");
} }
public void reset() { public void reset() {
@@ -154,4 +195,20 @@ public class Property<T> {
public NamedProperty<T> named(String name) { public NamedProperty<T> named(String name) {
return new NamedProperty<>(name, this); return new NamedProperty<>(name, this);
} }
public boolean isKeyPressed() {
return (Integer) value == -1 ? false : GLFW.glfwGetKey(SaturnClient.client.getWindow().getHandle(), (Integer) (Object) value) == GLFW.GLFW_PRESS;
}
public boolean wasKeyPressed() {
long window = SaturnClient.client.getWindow().getHandle();
int key = (Integer) value;
boolean isPressed = key != -1 && GLFW.glfwGetKey(window, key) == GLFW.GLFW_PRESS;
boolean wasJustPressed = isPressed && !wasPressedLastTick;
wasPressedLastTick = isPressed;
return wasJustPressed;
}
} }
@@ -22,6 +22,7 @@ public class ThemeManager {
SaturnClient.client.runDirectory, SaturnClient.client.runDirectory,
"saturn.theme.json"); "saturn.theme.json");
private static Map<String, Map<String, Property<?>>> properties = new HashMap<>(); private static Map<String, Map<String, Property<?>>> properties = new HashMap<>();
private static JsonObject cachedThemeJson = null;
// private Map<String, Map<String, Property<?>>> states; // private Map<String, Map<String, Property<?>>> states;
private Map<String, Property<?>> currentStyling; private Map<String, Property<?>> currentStyling;
@@ -44,32 +45,85 @@ public class ThemeManager {
public void setState(String state) { public void setState(String state) {
this.state = state; this.state = state;
if (this.state != null) { if (this.state != null) {
for (Map.Entry<String, Property<?>> tProp : properties.get(namespace+"@"+state).entrySet()) { for (Map.Entry<String, Property<?>> tProp : properties.get(namespace+"@"+state).entrySet()) {
currentStyling.get(tProp.getKey()).setValue(tProp.getValue().value); currentStyling.get(tProp.getKey()).setValue(tProp.getValue().value);
} }
} else { } else {
for (Map.Entry<String, Property<?>> tProp : properties.get(namespace).entrySet()) { for (Map.Entry<String, Property<?>> tProp : properties.get(namespace).entrySet()) {
currentStyling.get(tProp.getKey()).setValue(tProp.getValue().value); Property<?> p = currentStyling.get(tProp.getKey());
if (p != null) p.setValue(tProp.getValue().value);
} }
} }
} }
public void applyState(String state) {
if (this.state != null) {
for (Map.Entry<String, Property<?>> tProp : properties.get(namespace+"@"+state).entrySet()) {
Property<?> p = currentStyling.get(tProp.getKey());
Property<?> currProp = properties.get(namespace).get(tProp.getKey());
if (currProp != null && p.value.equals(currProp.value)) {
p.setValue(tProp.getValue().value);
}
}
} else {
setState(state);
}
}
// Generic method to store any type of property // Generic method to store any type of property
public <T> Property<T> property(String name, Property<T> prop) { public <T> Property<T> property(String name, Property<T> prop) {
for (String state : states) { for (String state : states) {
properties.get(namespace+"@"+state).put(name, new Property<T>(prop.value)); Property<?> property = Property.from(prop.value);
properties.get(namespace+"@"+state).put(name, property);
loadProp(state, name, property);
} }
properties.get(namespace).put(name, new Property<T>(prop.value)); loadProp(null, name, prop);
properties.get(namespace).put(name, Property.from(prop.value));
currentStyling.put(name, prop); currentStyling.put(name, prop);
return prop; return prop;
} }
public <T> void propertyStateDefault(String state, String name, T value) { public <T> void propertyStateDefault(String state, String name, T value) {
properties.get(namespace+"@"+state).get(name).setValue(value, value); // sets the current and default value Property<?> prop = properties.get(namespace+"@"+state).get(name);
prop.setValue(value, value); // sets the current and default value
loadProp(state, name, prop);
}
@SuppressWarnings("unchecked")
public <T> void loadProp(String state, String name, Property<T> prop) {
if (cachedThemeJson == null) {
load();
}
JsonElement element = cachedThemeJson.get(state != null ? namespace+"@"+state : namespace);
if (element != null && element.isJsonObject()) {
JsonObject theme = element.getAsJsonObject();
JsonElement value = theme.get(name);
if (value != null && prop.matchesJson(value)) {
switch (prop.getType()) {
case BOOLEAN:
((Property<Boolean>) prop).setValue(value.getAsBoolean());
break;
case INTEGER:
((Property<Integer>) prop).setValue(value.getAsInt());
break;
case FLOAT:
((Property<Float>) prop).setValue(value.getAsFloat());
break;
case STRING:
((Property<String>) prop).setValue(value.getAsString());
break;
case HEX:
((Property<Integer>) prop).setValue(value.getAsInt());
break;
default:
break;
}
}
}
} }
public static void load() { public static void load() {
@@ -80,74 +134,15 @@ public class ThemeManager {
Files.write(themeFile.toPath(), "{}".getBytes()); Files.write(themeFile.toPath(), "{}".getBytes());
} }
JsonObject jsonObject = JsonParser.parseString( cachedThemeJson = JsonParser.parseString(
new String(Files.readAllBytes(themeFile.toPath()))).getAsJsonObject(); new String(Files.readAllBytes(themeFile.toPath()))).getAsJsonObject();
for (String namespace : properties.keySet()) { SaturnClient.LOGGER.info("Theme loaded");
JsonElement themeElement = jsonObject.get(namespace);
if (themeElement == null)
continue;
JsonObject theme = jsonObject.get(namespace).getAsJsonObject();
if (theme == null) {
continue;
}
Map<String, Property<?>> propertyMap = properties.get(namespace);
loadProperties(theme, propertyMap);
}
} catch (IOException e) { } catch (IOException e) {
SaturnClient.LOGGER.error("Error reading the theme file", e); SaturnClient.LOGGER.error("Error reading the theme file", e);
} }
} }
@SuppressWarnings("unchecked")
private static void loadProperties(JsonObject theme, Map<String, Property<?>> propertyMap) {
for (String propertyName : propertyMap.keySet()) {
JsonElement c = theme.get(propertyName);
if (c == null) {
continue;
}
Property<?> p = propertyMap.get(propertyName);
if (p.matchesJson(c)) {
if (p.getType() == Property.PropertyType.NAMESPACE) {
// Handle nested namespace
JsonObject nestedTheme = c.getAsJsonObject();
Map<String, Property<?>> nestedProperties = p.getNamespaceValue();
loadProperties(nestedTheme, nestedProperties);
} else {
// Handle regular properties
switch (p.getType()) {
case BOOLEAN:
((Property<Boolean>) p).value = c.getAsBoolean();
break;
case INTEGER:
((Property<Integer>) p).value = c.getAsInt();
break;
case FLOAT:
((Property<Float>) p).value = c.getAsFloat();
break;
case STRING:
((Property<String>) p).value = c.getAsString();
break;
case HEX:
((Property<Integer>) p).value = c.getAsInt();
break;
default:
break;
}
}
} else {
SaturnClient.LOGGER.warn(
"Property does not match JSON: " + propertyName);
}
}
}
public static void save() { public static void save() {
try { try {
SaturnClient.LOGGER.info("Starting to save theme..."); SaturnClient.LOGGER.info("Starting to save theme...");
@@ -0,0 +1,17 @@
package org.saturnclient.saturnclient.cosmetics;
import java.util.ArrayList;
import java.util.List;
public class Emotes {
public static List<String> availableEmotes = new ArrayList<>();
public static void initialize() {
availableEmotes.add("tpose");
availableEmotes.add("back_flip");
availableEmotes.add("facepalm");
availableEmotes.add("cry");
availableEmotes.add("giga_chad");
availableEmotes.add("front_flip");
}
}
@@ -30,7 +30,7 @@ public class HatFeatureRenderer extends FeatureRenderer<PlayerEntityRenderState,
PlayerEntityRenderState state, float limbAngle, float limbDistance) { PlayerEntityRenderState state, float limbAngle, float limbDistance) {
ItemStack headItem = state.equippedHeadStack; ItemStack headItem = state.equippedHeadStack;
String uuid = Auth.playerNames.get(state.name); String uuid = Auth.playerNames.get(state.name);
if (headItem.isEmpty() && uuid != null) { if (headItem.isEmpty() && uuid != null && !state.invisible) {
SaturnPlayer player = Auth.players.get(uuid); SaturnPlayer player = Auth.players.get(uuid);
if (player == null || player.hat == null || player.hat.isEmpty()) { if (player == null || player.hat == null || player.hat.isEmpty()) {
return; return;
@@ -6,6 +6,7 @@ import org.saturnclient.saturnclient.auth.SaturnPlayer;
import org.saturnclient.saturnclient.auth.Auth; import org.saturnclient.saturnclient.auth.Auth;
public class Hats { public class Hats {
public static final String[] ALL_HATS = { "horns_black", "horns_white", "halo", "halo_black" };
public static List<String> availableHats = new ArrayList<>(); public static List<String> availableHats = new ArrayList<>();
public static void initialize() { public static void initialize() {
@@ -20,7 +20,8 @@ import org.saturnclient.saturnclient.SaturnClient;
* Originally created by IIpho3nix and modified for Saturn Client by leo. * Originally created by IIpho3nix and modified for Saturn Client by leo.
*/ */
public class Cloaks { public class Cloaks {
private static final String[] ANIMATED_CLOAKS = { "glitch" }; public static final String[] ALL_CLOAKS = { "glitch", "mercedes_flow", "crimson_mark", };
private static final String[] ANIMATED_CLOAKS = { "glitch", "mercedes_flow" };
private static final String CLOAKS_RESOURCE_PATH = "assets/saturnclient/textures/cloaks/"; private static final String CLOAKS_RESOURCE_PATH = "assets/saturnclient/textures/cloaks/";
public static final List<String> availableCloaks = new ArrayList<>(); public static final List<String> availableCloaks = new ArrayList<>();
@@ -48,6 +49,17 @@ public class Cloaks {
SaturnClient.LOGGER.info("Cloak set to " + cloakName); SaturnClient.LOGGER.info("Cloak set to " + cloakName);
} }
/**
* Sets cloak of the current player
*
* @param cloakName Name of the cloak file to load
*/
public static void setCloak(String cloakName) {
if (availableCloaks.contains(cloakName)) {
setCloak(Auth.uuid, cloakName);
}
}
/** /**
* Handles loading and caching of a new cloak texture, without connecting to the * Handles loading and caching of a new cloak texture, without connecting to the
* server. * server.
@@ -3,12 +3,17 @@ package org.saturnclient.saturnclient.event;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.minecraft.client.option.KeyBinding; import net.minecraft.client.option.KeyBinding;
// import net.minecraft.client.option.Perspective;
import net.minecraft.client.util.InputUtil; import net.minecraft.client.util.InputUtil;
// import org.saturnclient.modules.mods.FreeLook; import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.menus.ModMenu; import org.saturnclient.saturnclient.SaturnClientConfig;
// import org.saturnclient.saturnclient.menus.SaturnMenu; // import org.saturnclient.saturnclient.auth.Auth;
import org.saturnclient.ui2.screens.EmoteWheel;
import org.saturnclient.ui2.screens.ShiftMenu;
import dev.kosmx.playerAnim.api.layered.AnimationStack;
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationAccess;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
/** /**
@@ -21,55 +26,30 @@ public class KeyInputHandler {
private static final int DEFAULT_MENU_KEY = GLFW.GLFW_KEY_RIGHT_SHIFT; private static final int DEFAULT_MENU_KEY = GLFW.GLFW_KEY_RIGHT_SHIFT;
private static KeyBinding mainMenuKeyBinding; private static KeyBinding mainMenuKeyBinding;
// private static final String FREELOOK_KEY_ID = "key.saturnclient.freelook";
// private static final int FREELOOK_KEY = GLFW.GLFW_KEY_M;
// private static KeyBinding freelookKeyBinding;
/**
* Registers all keybindings and their associated event handlers.
* Should be called during mod initialization.
*/
public static void register() { public static void register() {
// Register the main menu key binding
mainMenuKeyBinding = KeyBindingHelper.registerKeyBinding(new KeyBinding( mainMenuKeyBinding = KeyBindingHelper.registerKeyBinding(new KeyBinding(
MAIN_MENU_KEY_ID, MAIN_MENU_KEY_ID,
InputUtil.Type.KEYSYM, InputUtil.Type.KEYSYM,
DEFAULT_MENU_KEY, DEFAULT_MENU_KEY,
KEY_CATEGORY)); KEY_CATEGORY));
// freelookKeyBinding = KeyBindingHelper.registerKeyBinding(new KeyBinding(
// FREELOOK_KEY_ID,
// InputUtil.Type.KEYSYM,
// FREELOOK_KEY,
// KEY_CATEGORY));
// Register the event handler for the main menu key // Register the event handler for the main menu key
ClientTickEvents.END_CLIENT_TICK.register(client -> { ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (mainMenuKeyBinding.wasPressed()) { if (mainMenuKeyBinding.wasPressed()) {
client.setScreen(new ModMenu()); client.setScreen(new ShiftMenu());
}
if (SaturnClientConfig.openEmoteWheel.wasKeyPressed()) {
client.setScreen(new EmoteWheel());
}
if (SaturnClient.client.player != null && SaturnClient.client.player.isSneaking()) {
AnimationStack animationStack = PlayerAnimationAccess.getPlayerAnimLayer(SaturnClient.client.player);
if (animationStack.isActive() && animationStack.getPriority() == 1000) {
animationStack.removeLayer(1000);
// Auth.cancelEmote();
}
} }
// else if (freelookKeyBinding.wasPressed() && FreeLook.enabled.value && FreeLook.toggle.value) {
// FreeLook.isFreeLooking = !FreeLook.isFreeLooking;
// if (FreeLook.isFreeLooking) {
// FreeLook.lastPerspective = client.options.getPerspective();
// client.options.setPerspective(Perspective.THIRD_PERSON_BACK);
// } else if (FreeLook.lastPerspective != null) {
// client.options.setPerspective(FreeLook.lastPerspective);
// FreeLook.lastPerspective = null;
// }
// } else if (FreeLook.enabled.value && !FreeLook.toggle.value) {
// if (freelookKeyBinding.isPressed()) {
// if (!FreeLook.isFreeLooking) {
// FreeLook.lastPerspective = client.options.getPerspective();
// client.options.setPerspective(Perspective.THIRD_PERSON_BACK);
// FreeLook.isFreeLooking = true;
// }
// } else if (FreeLook.isFreeLooking && FreeLook.lastPerspective != null) {
// client.options.setPerspective(FreeLook.lastPerspective);
// FreeLook.isFreeLooking = false;
// FreeLook.lastPerspective = null;
// }
// }
}); });
} }
} }
@@ -1,158 +0,0 @@
package org.saturnclient.saturnclient.menus;
import net.minecraft.text.Text;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.auth.Auth;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.Textures;
import org.saturnclient.ui.components.SkinPreview;
import org.saturnclient.ui.widgets.SaturnClickableImage;
import org.saturnclient.ui.widgets.SaturnClickableSprite;
import org.saturnclient.ui.widgets.SaturnImage;
import org.saturnclient.ui.widgets.SaturnInputBox;
import org.saturnclient.ui.widgets.SaturnScroll;
import org.saturnclient.ui.widgets.SaturnSprite;
public class CloaksMenu extends SaturnUi {
private SaturnScroll scroll;
private Property<String> search = new Property<>("");
public CloaksMenu() {
super(Text.of("Cloaks Menu"));
}
@Override
protected void init() {
int rectWidth = 330;
int rectHeight = 228;
int rectX = (width - rectWidth) / 2;
int rectY = (height - rectHeight + 15) / 2;
draw(
new SaturnSprite(Textures.SETTINGS_BG)
.setX(rectX)
.setY(rectY)
.setWidth(rectWidth)
.setHeight(rectHeight)
.setAnimations(SaturnClientConfig.getAnimations()));
int tabsX = (width - 43) / 2;
int tabsY = rectY - 17;
draw(
new SaturnSprite(Textures.RECT)
.setX(tabsX)
.setY(tabsY)
.setWidth(43)
.setHeight(15)
.setAnimations(SaturnClientConfig.getAnimations()));
draw(
new SaturnInputBox(search, rectX + rectWidth - 72, tabsY, 70).onUpdate(() -> {
scroll.clear();
drawItems(rectX, rectY, rectWidth, rectHeight);
})
.setHeight(15)
.setWidth(72)
.setAnimations(SaturnClientConfig.getAnimations()));
int tabSize = 9;
tabsX = (width - tabSize) / 2;
draw(
new SaturnClickableImage(Textures.HAT, () -> {
client.setScreen(new HatsMenu());
})
.setX(tabsX)
.setY(tabsY + 4)
.setWidth(tabSize)
.setHeight(7)
.setAnimations(SaturnClientConfig.getAnimations()));
draw(
new SaturnClickableImage(Textures.CLOAK, () -> {
})
.setSelected(true)
.setX(tabsX - 12)
.setY(tabsY + 3)
.setWidth(tabSize)
.setHeight(tabSize)
.setAnimations(SaturnClientConfig.getAnimations()));
draw(
new SaturnClickableImage(Textures.SEARCH, () -> {
System.out.println("Pressed");
})
.setX(tabsX + 12)
.setY(tabsY + 3)
.setWidth(tabSize)
.setHeight(tabSize)
.setAnimations(SaturnClientConfig.getAnimations()));
scroll = new SaturnScroll();
drawItems(rectX, rectY, rectWidth, rectHeight);
draw(scroll
.setX(rectX + 17)
.setY(rectY + 10)
.setWidth(rectWidth - 17)
.setHeight(rectHeight - 10)
.setAnimations(SaturnClientConfig.getAnimations()));
draw(new SkinPreview()
.setScale(3.0f)
.setX((rectX + rectWidth) - 180)
.setY(rectY + 8)
.setAnimations(SaturnClientConfig.getAnimations()));
super.init();
}
private void drawItems(int rectX, int rectY, int rectWidth, int rectHeight) {
int cloakWidth = 28;
int cloakHeight = 61;
int modX = 0;
int modY = 0;
int col = 0;
String[] searchTokens = search.value.toLowerCase().split("\\ ");
outer: for (String cloak : Cloaks.availableCloaks) {
for (String searchToken : searchTokens) {
if (!cloak.contains(searchToken)) {
continue outer;
}
}
scroll.draw(
new SaturnClickableSprite(Textures.BUTTON_BORDER, () -> {
Cloaks.setCloak(Auth.uuid, cloak);
})
.setX(modX)
.setY(modY)
.setWidth(cloakWidth + 6)
.setHeight(cloakHeight + 6));
scroll.draw(
new SaturnImage(Textures.getCloakPreview(cloak))
.setX(modX + 3)
.setY(modY + 3)
.setWidth(cloakWidth)
.setHeight(cloakHeight));
col++;
if (col < 4) {
modX += cloakWidth + 21;
} else {
modX = 0;
modY += cloakHeight + 20;
col = 0;
}
}
}
}
@@ -1,234 +0,0 @@
package org.saturnclient.saturnclient.menus;
import java.util.Map;
import net.minecraft.text.Text;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.SaturnWidget;
import org.saturnclient.ui.Textures;
import org.saturnclient.ui.components.SaturnFloat;
import org.saturnclient.ui.components.SaturnHex;
import org.saturnclient.ui.components.SaturnInteger;
import org.saturnclient.ui.components.SaturnString;
import org.saturnclient.ui.components.SaturnToggle;
import org.saturnclient.ui.widgets.SaturnImageButton;
import org.saturnclient.ui.widgets.SaturnScroll;
import org.saturnclient.ui.widgets.SaturnSprite;
import org.saturnclient.ui.widgets.SaturnText;
public class ConfigEditor extends SaturnUi {
private ConfigManager config;
public ConfigEditor(ConfigManager config) {
super(Text.of("Config editor"));
this.config = config;
}
@Override
protected void init() {
int rectWidth = 330;
int rectHeight = 228;
int rectX = (width - rectWidth) / 2;
int rectY = (height - rectHeight + 15) / 2;
draw(
new SaturnSprite(Textures.SETTINGS_BG)
.setX(rectX)
.setY(rectY)
.setWidth(rectWidth)
.setHeight(rectHeight)
.setAnimations(SaturnClientConfig.getAnimations()));
draw(
new SaturnImageButton(Textures.CLOSE, 9, 9, () -> {
this.close();
})
.setX(rectX)
.setY(rectY - 15)
.setWidth(13)
.setHeight(13)
.setAnimations(SaturnClientConfig.getAnimations()));
SaturnScroll configScroll = new SaturnScroll();
int row = 0;
int col = 0;
drawProperties(configScroll, config.getProperties(), rectWidth, row, col);
draw(
configScroll
.setX(rectX + 17)
.setY(rectY + 10)
.setWidth(rectWidth - 17)
.setHeight(rectHeight - 10)
.setAnimations(SaturnClientConfig.getAnimations()));
super.init();
}
@SuppressWarnings("unchecked")
private int drawProperties(SaturnScroll configScroll, Map<String, Property<?>> properties, int rectWidth,
int row, int col) {
int totalRows = row;
for (Map.Entry<String, Property<?>> propEntry : properties.entrySet()) {
Property<?> prop = propEntry.getValue();
String propName = propEntry.getKey();
boolean full = isFull(prop);
if (full && col > 0) {
col = 0;
totalRows++;
}
int modX = ((rectWidth / 2) - 17) * col;
int modY = 14 * totalRows;
switch (prop.getType()) {
case BOOLEAN:
configScroll.draw(
new SaturnToggle((Property<Boolean>) prop)
.setX(modX)
.setY(modY));
configScroll.draw(
new SaturnText(
propName.substring(0, 1).toUpperCase() +
propName.substring(1))
.setX(modX + 18)
.setY(modY)
.setScale(0.8f));
break;
case INTEGER:
configScroll.draw(
new SaturnText(
propName.substring(0, 1).toUpperCase() +
propName.substring(1))
.setX(modX)
.setY(modY)
.setScale(0.8f));
configScroll.draw(
new SaturnInteger(
(Property<Integer>) prop,
(rectWidth / 2) - 17,
modY,
70));
configScroll.draw(resetButton(prop, modX, modY));
break;
case FLOAT:
configScroll.draw(
new SaturnText(
propName.substring(0, 1).toUpperCase() +
propName.substring(1))
.setX(modX)
.setY(modY)
.setScale(0.8f));
configScroll.draw(
new SaturnFloat(
(Property<Float>) prop,
(rectWidth / 2) - 17,
modY,
70));
configScroll.draw(resetButton(prop, modX, modY));
break;
case STRING:
configScroll.draw(
new SaturnText(
propName.substring(0, 1).toUpperCase() +
propName.substring(1))
.setX(modX)
.setY(modY)
.setScale(0.8f));
configScroll.draw(
new SaturnString(
(Property<String>) prop,
(rectWidth / 2) - 17,
modY,
70));
configScroll.draw(resetButton(prop, modX, modY));
break;
case HEX:
configScroll.draw(
new SaturnText(
propName.substring(0, 1).toUpperCase() +
propName.substring(1))
.setX(modX)
.setY(modY)
.setScale(0.8f));
configScroll.draw(
new SaturnHex(
(Property<Integer>) prop,
(rectWidth / 2) - 17,
modY,
70));
configScroll.draw(resetButton(prop, modX, modY));
break;
case NAMESPACE:
configScroll.draw(
new SaturnText(
propName.substring(0, 1).toUpperCase() +
propName.substring(1))
.setX(modX)
.setY(modY)
.setScale(0.8f));
// Recursively draw nested properties and update total rows
Map<String, Property<?>> nestedProperties = (Map<String, Property<?>>) prop.value;
totalRows = drawProperties(configScroll, nestedProperties, rectWidth,
totalRows + 1, 0);
}
if (full) {
totalRows++;
} else {
col++;
if (col > 1) {
col = 0;
totalRows++;
}
}
}
return totalRows;
}
public static boolean isFull(Property<?> prop) {
switch (prop.getType()) {
case BOOLEAN:
return false;
default:
return true;
}
}
@Override
public void close() {
ConfigManager.save();
super.close();
}
public SaturnWidget resetButton(Property<?> prop, int modX, int modY) {
int size = textRenderer.fontHeight - 1;
return new SaturnImageButton(
Textures.RESET,
size,
size,
() -> {
prop.reset();
})
.setX(modX + 209)
.setY(modY)
.setWidth(size + 3)
.setHeight(size + 3);
}
}
@@ -1,133 +0,0 @@
package org.saturnclient.saturnclient.menus;
import net.minecraft.text.Text;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.auth.Auth;
import org.saturnclient.saturnclient.cosmetics.Hats;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.Textures;
import org.saturnclient.ui.components.SkinPreview;
import org.saturnclient.ui.widgets.SaturnClickableImage;
import org.saturnclient.ui.widgets.SaturnClickableSprite;
import org.saturnclient.ui.widgets.SaturnImage;
import org.saturnclient.ui.widgets.SaturnScroll;
import org.saturnclient.ui.widgets.SaturnSprite;
public class HatsMenu extends SaturnUi {
public HatsMenu() {
super(Text.of("Hats Menu"));
}
@Override
protected void init() {
int rectWidth = 330;
int rectHeight = 228;
int rectX = (width - rectWidth) / 2;
int rectY = (height - rectHeight + 15) / 2;
draw(
new SaturnSprite(Textures.SETTINGS_BG)
.setX(rectX)
.setY(rectY)
.setWidth(rectWidth)
.setHeight(rectHeight)
.setAnimations(SaturnClientConfig.getAnimations()));
int tabsX = (width - 43) / 2;
int tabsY = rectY - 17;
draw(
new SaturnSprite(Textures.RECT)
.setX(tabsX)
.setY(tabsY)
.setWidth(43)
.setHeight(15)
.setAnimations(SaturnClientConfig.getAnimations()));
int tabSize = 9;
tabsX = (width - tabSize) / 2;
draw(
new SaturnClickableImage(Textures.HAT, () -> {
client.setScreen(new HatsMenu());
})
.setSelected(true)
.setX(tabsX)
.setY(tabsY + 4)
.setWidth(tabSize)
.setHeight(7)
.setAnimations(SaturnClientConfig.getAnimations()));
draw(
new SaturnClickableImage(Textures.CLOAK, () -> {
client.setScreen(new CloaksMenu());
})
.setX(tabsX - 12)
.setY(tabsY + 3)
.setWidth(tabSize)
.setHeight(tabSize)
.setAnimations(SaturnClientConfig.getAnimations()));
draw(
new SaturnClickableImage(Textures.SEARCH, () -> {
System.out.println("Pressed");
})
.setX(tabsX + 12)
.setY(tabsY + 3)
.setWidth(tabSize)
.setHeight(tabSize)
.setAnimations(SaturnClientConfig.getAnimations()));
int hatSize = 28;
int modX = 0;
int modY = 0;
int col = 0;
SaturnScroll modsScroll = new SaturnScroll();
for (String hat : Hats.availableHats) {
modsScroll.draw(
new SaturnClickableSprite(Textures.BUTTON_BORDER, () -> {
Hats.setHat(Auth.uuid, hat);
})
.setX(modX)
.setY(modY)
.setWidth(hatSize + 6)
.setHeight(hatSize + 6));
modsScroll.draw(
new SaturnImage(Textures.getHatPreview(hat))
.setX(modX + 3)
.setY(modY + 3)
.setWidth(hatSize)
.setHeight(hatSize));
col++;
if (col < 4) {
modX += hatSize + 21;
} else {
modX = 0;
modY += hatSize + 20;
col = 0;
}
}
draw(
modsScroll
.setX(rectX + 17)
.setY(rectY + 10)
.setWidth(rectWidth - 17)
.setHeight(rectHeight - 10)
.setAnimations(SaturnClientConfig.getAnimations()));
draw(new SkinPreview()
.setScale(3.0f)
.setX((rectX + rectWidth) - 180)
.setY(rectY + 8)
.setAnimations(SaturnClientConfig.getAnimations()));
super.init();
}
}
@@ -10,7 +10,9 @@ import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.ModManager; import org.saturnclient.modules.ModManager;
import org.saturnclient.modules.Module; import org.saturnclient.modules.Module;
import org.saturnclient.saturnclient.config.ConfigManager; import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.ui.SaturnUi; import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.SaturnScreen;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -90,9 +92,11 @@ public class HudEditor extends Screen {
@Override @Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) { public void render(DrawContext context, int mouseX, int mouseY, float delta) {
if (client.world == null && client.getCurrentServerEntry() == null) { if (client.world == null && client.getCurrentServerEntry() == null) {
SaturnUi.ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, 1.0F, delta); SaturnScreen.ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, 1.0F, delta);
} }
RenderScope scope = new RenderScope(context.getMatrices(), ((DrawContextAccessor) context).getVertexConsumers());
for (HudMod mod : hudMods) { for (HudMod mod : hudMods) {
ModDimensions dim = mod.getDimensions(); ModDimensions dim = mod.getDimensions();
MatrixStack matrices = context.getMatrices(); MatrixStack matrices = context.getMatrices();
@@ -102,7 +106,7 @@ public class HudEditor extends Screen {
matrices.scale(dim.scale.value, dim.scale.value, 1.0f); matrices.scale(dim.scale.value, dim.scale.value, 1.0f);
// Render the mod // Render the mod
mod.renderDummy(context); mod.renderDummy(scope);
matrices.pop(); matrices.pop();
@@ -1,147 +0,0 @@
package org.saturnclient.saturnclient.menus;
import net.minecraft.text.Text;
import org.saturnclient.modules.ModManager;
import org.saturnclient.modules.Module;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.Textures;
import org.saturnclient.ui.components.SaturnModComp;
import org.saturnclient.ui.widgets.SaturnClickableImage;
import org.saturnclient.ui.widgets.SaturnInputBox;
import org.saturnclient.ui.widgets.SaturnScroll;
import org.saturnclient.ui.widgets.SaturnSprite;
public class ModMenu extends SaturnUi {
private SaturnScroll scroll;
private Property<String> search = new Property<>("");
public ModMenu() {
super(Text.literal("Mods"));
}
@Override
protected void init() {
int rectWidth = 330;
int rectHeight = 228;
int rectX = (width - rectWidth) / 2;
int rectY = (height - rectHeight + 15) / 2;
draw(
new SaturnSprite(Textures.SETTINGS_BG)
.setX(rectX)
.setY(rectY)
.setWidth(rectWidth)
.setHeight(rectHeight)
.setAnimations(SaturnClientConfig.getAnimations()));
int tabsX = (width - 43) / 2;
int tabsY = rectY - 17;
draw(
new SaturnSprite(Textures.RECT)
.setX(tabsX)
.setY(tabsY)
.setWidth(43)
.setHeight(15)
.setAnimations(SaturnClientConfig.getAnimations()));
draw(
new SaturnInputBox(search, rectX + rectWidth - 72, tabsY, 70).onUpdate(() -> {
scroll.clear();
drawItems(rectX, rectY, rectWidth, rectHeight);
})
.setHeight(15)
.setWidth(72)
.setAnimations(SaturnClientConfig.getAnimations()));
int tabSize = 9;
tabsX = (width - tabSize) / 2;
draw(
new SaturnClickableImage(Textures.SETTINGS, () -> {
client.setScreen(new SaturnConfigEditor());
})
.setX(tabsX)
.setY(tabsY + 3)
.setWidth(tabSize)
.setHeight(tabSize)
.setAnimations(SaturnClientConfig.getAnimations()));
draw(
new SaturnClickableImage(Textures.MODS_TAB, () -> {
})
.setSelected(true)
.setX(tabsX - 12)
.setY(tabsY + 3)
.setWidth(tabSize)
.setHeight(tabSize)
.setAnimations(SaturnClientConfig.getAnimations()));
draw(
new SaturnClickableImage(Textures.SEARCH, () -> {
System.out.println("Pressed");
})
.setX(tabsX + 12)
.setY(tabsY + 3)
.setWidth(tabSize)
.setHeight(tabSize)
.setAnimations(SaturnClientConfig.getAnimations()));
scroll = new SaturnScroll();
drawItems(rectX, rectY, rectWidth, rectHeight);
draw(scroll
.setX(rectX + 17)
.setY(rectY + 10)
.setWidth(rectWidth - 17)
.setHeight(rectHeight - 20)
.setAnimations(SaturnClientConfig.getAnimations()));
super.init();
}
@Override
public void close() {
ConfigManager.save();
super.close();
}
private void drawItems(int rectX, int rectY, int rectWidth, int rectHeight) {
int modX = 0;
int modY = 0;
int col = 0;
String[] searchTokens = search.value.toLowerCase().split("\\ ");
outer: for (Module mod : ModManager.MODS) {
String modName = mod.getName().toLowerCase();
for (String searchToken : searchTokens) {
if (!modName.contains(searchToken)) {
continue outer;
}
}
scroll.draw(
new SaturnModComp(mod)
.setX(modX)
.setY(modY)
.setWidth(86)
.setHeight(20));
col++;
if (col < 3) {
modX += 86 + 18;
} else {
modX = 0;
modY += 20 + 20;
col = 0;
}
}
}
}
@@ -1,64 +0,0 @@
package org.saturnclient.saturnclient.menus;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.ui.Textures;
import org.saturnclient.ui.widgets.SaturnClickableImage;
import org.saturnclient.ui.widgets.SaturnSprite;
public class SaturnConfigEditor extends ConfigEditor {
public SaturnConfigEditor() {
super(SaturnClientConfig.config);
}
@Override
protected void init() {
int rectHeight = 228;
int rectY = (height - rectHeight + 15) / 2;
int tabsX = (width - 43) / 2;
int tabsY = rectY - 17;
draw(
new SaturnSprite(Textures.RECT)
.setX(tabsX)
.setY(tabsY)
.setWidth(43)
.setHeight(15)
.setAnimations(SaturnClientConfig.getAnimations()));
int tabSize = 9;
tabsX = (width - tabSize) / 2;
draw(
new SaturnClickableImage(Textures.SETTINGS, () -> {
})
.setSelected(true)
.setX(tabsX)
.setY(tabsY + 3)
.setWidth(tabSize)
.setHeight(tabSize)
.setAnimations(SaturnClientConfig.getAnimations()));
draw(
new SaturnClickableImage(Textures.MODS_TAB, () -> {
client.setScreen(new ModMenu());
})
.setX(tabsX - 12)
.setY(tabsY + 3)
.setWidth(tabSize)
.setHeight(tabSize)
.setAnimations(SaturnClientConfig.getAnimations()));
draw(
new SaturnClickableImage(Textures.SEARCH, () -> {
System.out.println("Pressed");
})
.setX(tabsX + 12)
.setY(tabsY + 3)
.setWidth(tabSize)
.setHeight(tabSize)
.setAnimations(SaturnClientConfig.getAnimations()));
super.init();
}
}
@@ -1,13 +0,0 @@
package org.saturnclient.saturnclient.menus;
import org.saturnclient.ui2.SaturnScreen;
public class SaturnMenu extends SaturnScreen {
public SaturnMenu() {
super("Saturn Client");
}
@Override
public void ui() {
}
}
@@ -1,78 +0,0 @@
package org.saturnclient.saturnclient.menus;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.Textures;
import org.saturnclient.ui.animations.FadeIn;
import org.saturnclient.ui.animations.Slide;
import org.saturnclient.ui.widgets.SaturnButton;
import org.saturnclient.ui.widgets.SaturnImage;
import org.saturnclient.ui2.screens.ShiftMenu;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.gui.screen.option.OptionsScreen;
import net.minecraft.client.gui.screen.world.SelectWorldScreen;
import net.minecraft.text.Text;
public class SaturnTitleScreen extends SaturnUi {
public SaturnTitleScreen() {
super(Text.literal("Title Screen"));
}
@Override
protected void init() {
blur = false;
int logoSize = 49;
int bX = (width - 150) / 2;
int bY = height / 2 - logoSize - 27;
draw(
new SaturnImage(SaturnClientConfig.getLogo())
.setX((width - logoSize) / 2)
.setY(bY)
.setWidth(logoSize)
.setHeight(logoSize)
.setAnimations(new Slide(3, 14)));
bY += 44;
draw(
new SaturnImage(Textures.LOGO_TEXT)
.setX((width - logoSize) / 2)
.setY(bY)
.setWidth(logoSize)
.setHeight(8)
.setAnimations(new FadeIn(3)));
bY += 15;
draw(new SaturnButton("SINGLEPLAYER", () -> {
client.setScreen(new SelectWorldScreen(new SaturnTitleScreen()));
}).setBold(true).setWidth(210).setHeight(26).setX(bX).setY(bY).setScale(0.69f));
bY += 20;
draw(new SaturnButton("MULTIPLAYER", () -> {
client.setScreen(new MultiplayerScreen(new SaturnTitleScreen()));
}).setBold(true).setWidth(210).setHeight(26).setX(bX).setY(bY).setScale(0.69f));
bY += 20;
draw(new SaturnButton("OPTIONS", () -> {
client.setScreen(new OptionsScreen(new SaturnTitleScreen(), this.client.options));
}).setBold(true).setWidth(103).setHeight(26).setX(bX).setY(bY).setScale(0.69f));
draw(new SaturnButton("QUIT", () -> {
client.scheduleStop();
}).setBold(true).setWidth(103).setHeight(26).setX(bX + 74).setY(bY).setScale(0.69f));
bY += 20;
draw(new SaturnButton("SATURN OPTIONS", () -> {
client.setScreen(new ShiftMenu());
}).setBold(true).setWidth(210).setHeight(26).setX(bX).setY(bY).setScale(0.69f));
super.init();
}
}
@@ -1,24 +1,30 @@
package org.saturnclient.saturnclient.mixin; package org.saturnclient.saturnclient.mixin;
import java.util.Map; import java.util.Map;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.auth.Auth; import org.saturnclient.saturnclient.auth.Auth;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import dev.kosmx.playerAnim.api.layered.AnimationStack;
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationAccess;
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationRegistry;
import net.minecraft.client.gui.hud.ChatHud; import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.client.gui.hud.MessageIndicator; import net.minecraft.client.gui.hud.MessageIndicator;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.network.message.MessageSignatureData; import net.minecraft.network.message.MessageSignatureData;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
@Mixin(ChatHud.class) @Mixin(ChatHud.class)
public class SaturnReload { public class SaturnReload {
@Inject(method = "addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)V", at = @At("HEAD"), cancellable = true) @Inject(method = "addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)V", at = @At("HEAD"), cancellable = true)
public void onAddMessage(Text message, @Nullable MessageSignatureData signatureData, public void onAddMessage(Text message, @Nullable MessageSignatureData signatureData,
@Nullable MessageIndicator indicator, CallbackInfo ci) { @Nullable MessageIndicator indicator, CallbackInfo ci) {
try {
String msg = message.toString(); String msg = message.toString();
if (msg.contains("$SATURN_RELOAD")) { if (msg.contains("$SATURN_RELOAD")) {
for (Map.Entry<String, String> player : Auth.playerNames.entrySet()) { for (Map.Entry<String, String> player : Auth.playerNames.entrySet()) {
@@ -28,6 +34,29 @@ public class SaturnReload {
} }
ci.cancel(); ci.cancel();
} }
} else if (msg.contains("$SATURN_EMOTE")) {
String[] args = msg.split("&@");
for (AbstractClientPlayerEntity player : SaturnClient.client.world.getPlayers()) {
if (player.getUuidAsString().replace("-", "").equals(args[1])) {
AnimationStack animationStack = PlayerAnimationAccess.getPlayerAnimLayer(player);
animationStack.addAnimLayer(1000, PlayerAnimationRegistry.getAnimation(Identifier.of("saturnclient", args[2])).playAnimation());
ci.cancel();
break;
}
}
} else if (msg.contains("$SATURN_CANCEL_EMOTE")) {
String[] args = msg.split("&@");
for (AbstractClientPlayerEntity player : SaturnClient.client.world.getPlayers()) {
if (player.getUuidAsString().replace("-", "").equals(args[1])) {
AnimationStack animationStack = PlayerAnimationAccess.getPlayerAnimLayer(player);
animationStack.removeLayer(1000);
ci.cancel();
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
} }
} }
} }
@@ -0,0 +1,22 @@
package org.saturnclient.saturnclient.mixin;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.ui2.screens.TitleMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.gui.screen.TitleScreen;
@Mixin(TitleScreen.class)
public class TitleScreenMixin {
@Inject(method = "init", at = @At("HEAD"), cancellable = true)
private void onInit(CallbackInfo ci) {
if (SaturnClientConfig.saturnTitleScreen.value) {
SaturnClient.client.setScreen(new TitleMenu());
ci.cancel();
}
}
}
@@ -1,13 +0,0 @@
package org.saturnclient.ui;
public class SaturnAnimation {
public int delay = 0;
public int lastTick = 0;
public void init(SaturnWidget widget) {}
public boolean run(SaturnWidget widget, int tick) {
return true;
}
}
@@ -1,322 +0,0 @@
package org.saturnclient.ui;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gl.PostEffectProcessor;
import net.minecraft.client.gui.CubeMapRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.RotatingCubeMapRenderer;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.render.DefaultFramebufferSet;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.util.Pool;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.MutableText;
import net.minecraft.text.Style;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import org.lwjgl.glfw.GLFW;
public class SaturnUi extends Screen {
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private final Pool pool = new Pool(3);
private float f = 0.0f;
public boolean blur = true;
public boolean titleBackground = false;
public List<SaturnWidget> widgets = new ArrayList<>();
private int tick = 0;
protected static final CubeMapRenderer PANORAMA_RENDERER = new CubeMapRenderer(
Identifier.of("saturnclient", "textures/gui/title/background/panorama"));
public static final RotatingCubeMapRenderer ROTATING_PANORAMA_RENDERER;
static {
ROTATING_PANORAMA_RENDERER = new RotatingCubeMapRenderer(PANORAMA_RENDERER);
}
public static MutableText text(String text) {
return Text.literal(text).setStyle(
Style.EMPTY.withFont(Identifier.of("saturnclient:panton")));
}
public static MutableText textBold(String text) {
return Text.literal(text).setStyle(
Style.EMPTY.withFont(Identifier.of("saturnclient:panton_bold")));
}
public static void drawHighResTexture(DrawContext context, Identifier t, int x, int y, int w, int h) {
MatrixStack matricies = context.getMatrices();
matricies.push();
matricies.translate(x, y, 0);
matricies.scale(.25f, .25f, 1.0f);
context.drawTexture(RenderLayer::getGuiTextured, t, 0, 0, 0, 0,
w * 4, h * 4, w * 4, h * 4);
matricies.pop();
}
public static void drawHighResTexture(DrawContext context, Identifier t, int x, int y, int w, int h, int c) {
MatrixStack matricies = context.getMatrices();
matricies.push();
matricies.translate(x, y, 0);
matricies.scale(.25f, .25f, 1.0f);
context.drawTexture(RenderLayer::getGuiTextured, t, 0, 0, 0, 0,
w * 4, h * 4, w * 4, h * 4, c);
matricies.pop();
}
public static void drawHighResGuiTexture(DrawContext context, Identifier t, int x, int y, int w, int h, int c) {
MatrixStack matricies = context.getMatrices();
matricies.push();
matricies.translate(x, y, 0);
matricies.scale(.25f, .25f, 1.0f);
context.drawGuiTexture(
RenderLayer::getGuiTextured,
t,
0,
0,
w * 4,
h * 4,
c);
matricies.pop();
}
public static int getAlpha(int color, float alpha) {
return ((int) (alpha * 255) << 24) |
(color & 0x00FFFFFF);
}
public SaturnUi(Text title) {
super(title);
}
@Override
protected void init() {
scheduler.scheduleAtFixedRate(
this::saturnTick,
0,
7,
TimeUnit.MILLISECONDS);
super.init();
}
public void draw(SaturnWidget widget) {
synchronized (widgets) {
widget.init();
widgets.add(widget);
}
}
public void refresh() {
synchronized (widgets) {
widgets.clear();
f = 0.0f;
tick = 0;
init();
}
}
@SuppressWarnings("deprecation")
@Override
public void render(
DrawContext context,
int mouseX,
int mouseY,
float delta) {
if (client.world == null && client.getCurrentServerEntry() == null) {
ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, 1.0F, delta);
}
// Blur
if (!(f < 1.0F) && blur) {
PostEffectProcessor postEffectProcessor = this.client.getShaderLoader().loadPostEffect(
Identifier.ofVanilla("blur"),
DefaultFramebufferSet.MAIN_ONLY);
if (postEffectProcessor != null) {
postEffectProcessor.setUniforms("Radius", f);
postEffectProcessor.render(this.client.getFramebuffer(), this.pool);
}
}
for (SaturnWidget widget : new ArrayList<>(widgets)) {
if (!widget.visible)
continue;
double adjustedMouseX = mouseX - widget.x;
double adjustedMouseY = mouseY - widget.y;
boolean isMouseInside = adjustedMouseX >= 0 &&
adjustedMouseX <= (widget.width * widget.scale) &&
adjustedMouseY >= 0 &&
adjustedMouseY <= (widget.height * widget.scale);
MatrixStack matrices = context.getMatrices();
matrices.push();
matrices.translate(widget.x, widget.y, 0);
matrices.scale(widget.scale, widget.scale, 1.0f);
widget.render(
context,
isMouseInside,
(int) (adjustedMouseX / widget.scale),
(int) (adjustedMouseY / widget.scale));
matrices.pop();
}
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
if (button == 0) {
for (SaturnWidget widget : new ArrayList<>(widgets)) {
widget.focused = false;
if (!widget.visible)
continue;
double adjustedMouseX = mouseX - widget.x;
double adjustedMouseY = mouseY - widget.y;
boolean isMouseInside = adjustedMouseX >= 0 &&
adjustedMouseX <= (widget.width * widget.scale) &&
adjustedMouseY >= 0 &&
adjustedMouseY <= (widget.height * widget.scale);
if (isMouseInside) {
widget.focused = true;
widget.click(
(int) (adjustedMouseX / widget.scale),
(int) (adjustedMouseY / widget.scale));
}
}
}
return false;
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
for (SaturnWidget widget : new ArrayList<>(widgets)) {
if (!widget.visible && !widget.focused)
continue;
widget.keyPressed(keyCode, scanCode, modifiers);
char typedChar = getCharFromKey(keyCode, modifiers);
if (typedChar != '\0') {
widget.charTyped(typedChar);
}
}
return super.keyPressed(keyCode, scanCode, modifiers);
}
@Override
public boolean mouseScrolled(
double mouseX,
double mouseY,
double horizontalAmount,
double verticalAmount) {
for (SaturnWidget widget : new ArrayList<>(widgets)) {
double adjustedMouseX = mouseX - widget.x;
double adjustedMouseY = mouseY - widget.y;
boolean isMouseInside = adjustedMouseX >= 0 &&
adjustedMouseX <= (widget.width * widget.scale) &&
adjustedMouseY >= 0 &&
adjustedMouseY <= (widget.height * widget.scale);
if (isMouseInside) {
widget.mouseScrolled(
(int) (adjustedMouseX / widget.scale),
(int) (adjustedMouseY / widget.scale),
horizontalAmount,
verticalAmount);
}
}
return super.mouseScrolled(
mouseX,
mouseY,
horizontalAmount,
verticalAmount);
}
private char getCharFromKey(int keyCode, int modifiers) {
boolean shift = (modifiers & GLFW.GLFW_MOD_SHIFT) != 0;
// A-Z
if (keyCode >= GLFW.GLFW_KEY_A && keyCode <= GLFW.GLFW_KEY_Z) {
return (char) (shift
? 'A' + (keyCode - GLFW.GLFW_KEY_A)
: 'a' + (keyCode - GLFW.GLFW_KEY_A));
}
// 0-9
if (keyCode >= GLFW.GLFW_KEY_0 && keyCode <= GLFW.GLFW_KEY_9) {
return shift
? ")!@#$%^&*(".charAt(keyCode - GLFW.GLFW_KEY_0)
: (char) ('0' + (keyCode - GLFW.GLFW_KEY_0));
}
// Special characters
return switch (keyCode) {
case GLFW.GLFW_KEY_SPACE -> ' ';
case GLFW.GLFW_KEY_PERIOD -> shift ? '>' : '.';
case GLFW.GLFW_KEY_COMMA -> shift ? '<' : ',';
case GLFW.GLFW_KEY_MINUS -> shift ? '_' : '-';
case GLFW.GLFW_KEY_EQUAL -> shift ? '+' : '=';
case GLFW.GLFW_KEY_SEMICOLON -> shift ? ':' : ';';
case GLFW.GLFW_KEY_APOSTROPHE -> shift ? '"' : '\'';
case GLFW.GLFW_KEY_SLASH -> shift ? '?' : '/';
case GLFW.GLFW_KEY_BACKSLASH -> shift ? '|' : '\\';
case GLFW.GLFW_KEY_LEFT_BRACKET -> shift ? '{' : '[';
case GLFW.GLFW_KEY_RIGHT_BRACKET -> shift ? '}' : ']';
case GLFW.GLFW_KEY_GRAVE_ACCENT -> shift ? '~' : '`';
default -> '\0';
};
}
public void saturnTick() {
synchronized (scheduler) {
if (f < 15.0f) {
f += 0.25f;
}
}
synchronized (widgets) {
for (SaturnWidget widget : widgets) {
if (widget.visible && widget.animations != null) {
for (SaturnAnimation animation : widget.animations) {
if (animation != null &&
tick - animation.lastTick == animation.delay) {
if (animation.run(widget, tick)) {
animation = null;
} else {
animation.lastTick = tick;
}
}
}
}
widget.tick();
}
tick++;
}
}
@Override
public void resize(MinecraftClient client, int width, int height) {
super.resize(client, width, height);
refresh();
}
}
@@ -1,88 +0,0 @@
package org.saturnclient.ui;
import net.minecraft.client.gui.DrawContext;
public class SaturnWidget {
public int x = 0;
public int y = 0;
public int width = 0;
public int height = 0;
public float alpha = 1.0f;
public float scale = 1.0f;
public SaturnAnimation[] animations = null;
public boolean visible = true;
public boolean focused = false;
public void init() {
}
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
}
public void click(int mouseX, int mouseY) {
}
public void keyPressed(int keyCode, int scanCode, int modifiers) {
}
public void charTyped(char typedChar) {
}
public void tick() {
}
public void mouseScrolled(
int mouseX,
int mouseY,
double horizontalAmount,
double verticalAmount) {
}
public SaturnWidget setX(int x) {
this.x = x;
return this;
}
public SaturnWidget setY(int y) {
this.y = y;
return this;
}
public SaturnWidget setWidth(int width) {
this.width = width;
return this;
}
public SaturnWidget setHeight(int height) {
this.height = height;
return this;
}
public SaturnWidget setAlpha(float alpha) {
this.alpha = alpha;
return this;
}
public SaturnWidget setAnimations(SaturnAnimation... animations) {
this.animations = animations;
for (SaturnAnimation anim : this.animations) {
anim.init(this);
}
return this;
}
public SaturnWidget setVisible(boolean visible) {
this.visible = visible;
return this;
}
public SaturnWidget setScale(float scale) {
this.scale = scale;
return this;
}
}
@@ -1,44 +0,0 @@
package org.saturnclient.ui;
import net.minecraft.util.Identifier;
public class Textures {
public static final Identifier COSMETICS = Identifier.of("saturnclient", "textures/gui/components/cosmetics.png");
public static final Identifier HUD_ICON = Identifier.of("saturnclient", "textures/gui/components/hud.png");
public static final Identifier SEARCH = Identifier.of("saturnclient", "textures/gui/components/search.png");
public static final Identifier CLOSE = Identifier.of("saturnclient", "textures/gui/components/close.png");
public static final Identifier MODS_TAB = Identifier.of("saturnclient", "textures/gui/components/mods_tab.png");
public static final Identifier SETTINGS = Identifier.of("saturnclient", "textures/gui/components/settings.png");
public static final Identifier CLOAK = Identifier.of("saturnclient", "textures/gui/components/cloak.png");
public static final Identifier HAT = Identifier.of("saturnclient", "textures/gui/components/hat.png");
public static final Identifier RESET = Identifier.of("saturnclient", "textures/gui/components/reset.png");
public static final Identifier TOGGLE_INDICATOR = Identifier.of("saturnclient",
"textures/gui/components/toggle_indicator.png");
public static final Identifier TOGGLE_BG = Identifier.of("saturnclient", "textures/gui/components/toggle_bg.png");
public static final Identifier LOGO = Identifier.of("saturnclient", "textures/logo/logo.png");
public static final Identifier REALISTIC_LOGO = Identifier.of("saturnclient", "textures/logo/realistic.png");
public static final Identifier LOGO_TEXT = Identifier.of("saturnclient", "textures/logo/text.png");
// Widgets
public static final Identifier BUTTON_BORDER = Identifier.of("saturnclient", "button_border");
public static final Identifier BUTTON = Identifier.of("saturnclient", "button");
public static final Identifier BUTTON_OP = Identifier.of("saturnclient", "button_op");
public static final Identifier SETTINGS_BG = Identifier.of("saturnclient", "settings_bg");
public static final Identifier RECT = Identifier.of("saturnclient", "rect");
public static final Identifier RECT_BORDER = Identifier.of("saturnclient", "rect_border");
public static final Identifier MOD = Identifier.of("saturnclient", "textures/gui/components/mod.png");
public static final Identifier MOD_BG = Identifier.of("saturnclient", "textures/gui/components/mod_bg.png");
public static Identifier getCloakPreview(String cloak) {
return Identifier.of("saturnclient", "textures/gui/cloak/" + (cloak.isEmpty() ? "none" : cloak) + ".png");
}
public static Identifier getHatPreview(String hat) {
return Identifier.of("saturnclient", "textures/gui/hat/" + (hat.isEmpty() ? "none" : hat) + ".png");
}
public static Identifier getModIcon(String modId) {
return Identifier.of("saturnclient", "textures/gui/mod/" + modId + ".png");
}
}
@@ -1,20 +0,0 @@
package org.saturnclient.ui.animations;
import org.saturnclient.ui.SaturnAnimation;
import org.saturnclient.ui.SaturnWidget;
public class FadeIn extends SaturnAnimation {
public FadeIn(int delay) {
this.delay = delay;
}
public void init(SaturnWidget widget) {
widget.alpha = 0.0f;
}
public boolean run(SaturnWidget widget, int tick) {
widget.alpha += 0.05f;
return widget.alpha > 0.9f;
}
}
@@ -1,24 +0,0 @@
package org.saturnclient.ui.animations;
import org.saturnclient.ui.SaturnAnimation;
import org.saturnclient.ui.SaturnWidget;
public class Slide extends SaturnAnimation {
private int offset;
public Slide(int delay, int offset) {
this.delay = delay;
this.offset = offset;
}
public void init(SaturnWidget widget) {
widget.y += offset;
}
public boolean run(SaturnWidget widget, int tick) {
widget.y--;
offset--;
return offset == 0;
}
}
@@ -1,179 +0,0 @@
package org.saturnclient.ui.components;
import org.saturnclient.saturnclient.SaturnClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.SaturnWidget;
import org.saturnclient.ui.Textures;
public class SaturnFloat extends SaturnWidget {
String text = "";
public Property<Float> prop;
public int cursorPosition = 0;
private TextRenderer textRenderer = SaturnClient.client.textRenderer;
public SaturnFloat(Property<Float> prop, int x, int y, int width) {
this.x = x;
this.y = y;
this.width = width;
this.prop = prop;
this.text = String.valueOf(prop.value);
this.height = textRenderer.fontHeight + 4;
this.scale = 0.8f;
}
@Override
public void click(int mouseX, int mouseY) {
int scrollOffset = getScrollOffset();
String visibleText = text.substring(scrollOffset);
int newCursorPos = scrollOffset;
int textWidth = 0;
for (int i = 0; i < visibleText.length(); i++) {
textWidth = textRenderer.getWidth(visibleText.substring(0, i + 1));
if (textWidth > mouseX) {
break;
}
newCursorPos++;
}
cursorPosition = newCursorPos;
}
@Override
public void charTyped(char chr) {
if ((Character.isDigit(chr) || chr == '.') && text.length() < 10) {
if (text.equals("0") && cursorPosition == 1) {
text = "" + chr;
} else {
text = text.substring(0, cursorPosition) +
chr +
text.substring(cursorPosition);
prop.value = text.isEmpty() ? 0 : Float.parseFloat(text);
ConfigManager.save();
cursorPosition++;
}
}
}
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
if (keyCode == GLFW.GLFW_KEY_BACKSPACE) {
if (cursorPosition > 0) {
text = text.substring(0, cursorPosition - 1) +
text.substring(cursorPosition);
cursorPosition--;
if (text.length() == 0) {
text = "0";
cursorPosition = 1;
}
prop.value = Float.parseFloat(text);
ConfigManager.save();
}
} else if (keyCode == GLFW.GLFW_KEY_LEFT) {
if (cursorPosition > 0) {
cursorPosition--;
}
} else if (keyCode == GLFW.GLFW_KEY_RIGHT) {
if (cursorPosition < text.length()) {
cursorPosition++;
}
}
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
if (prop.isReset) {
this.text = String.valueOf(prop.value);
this.cursorPosition = 0;
prop.isReset = false;
}
SaturnUi.drawHighResGuiTexture(
context,
Textures.BUTTON_BORDER,
0,
0,
this.width,
this.height,
focused
? SaturnClientConfig.color.value
: SaturnClientConfig.getWhite(this.alpha));
int scrollOffset = getScrollOffset();
String visibleText = getVisibleText(scrollOffset);
int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
context.drawText(
textRenderer,
SaturnUi.text(visibleText),
2,
2,
textColor,
false);
if (focused) {
int cursorX = 2 +
textRenderer.getWidth(
SaturnUi.text(
visibleText.substring(0, cursorPosition - scrollOffset)));
context.fill(
cursorX,
2,
cursorX + 1,
textRenderer.fontHeight + 4,
0xFFFFFFFF);
}
}
public void clearText() {
text = "0";
cursorPosition = 1;
prop.value = 0.0f;
}
private int getScrollOffset() {
if (text.isEmpty()) {
return 0;
}
int offset = 0;
int maxWidth = width - 4; // Available width for text
while (offset < cursorPosition) { // Ensure cursor remains visible
String visibleText = text.substring(offset, cursorPosition);
if (textRenderer.getWidth(SaturnUi.text(visibleText)) > maxWidth) {
offset++;
} else {
break;
}
}
return offset;
}
private String getVisibleText(int scrollOffset) {
String visibleText = text.substring(scrollOffset);
// Ensure text does not overflow past the available width
for (int i = 1; i <= visibleText.length(); i++) {
if (textRenderer.getWidth(
SaturnUi.text(visibleText.substring(0, i))) > width - 4) {
return visibleText.substring(0, i - 1); // Trim overflow
}
}
return visibleText;
}
}
@@ -1,140 +0,0 @@
package org.saturnclient.ui.components;
import org.saturnclient.saturnclient.SaturnClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.SaturnWidget;
import org.saturnclient.ui.Textures;
public class SaturnHex extends SaturnWidget {
private String text = "#"; // Always starts with '#'
public Property<Integer> prop;
public int cursorPosition = 1; // After '#'
private final TextRenderer textRenderer = SaturnClient.client.textRenderer;
public SaturnHex(Property<Integer> prop, int x, int y, int width) {
this.x = x;
this.y = y;
this.width = width;
this.prop = prop;
this.text = intToHex(prop.value);
this.height = textRenderer.fontHeight + 4;
this.scale = 0.8f;
}
@Override
public void click(int mouseX, int mouseY) {
cursorPosition = Math.min(7, text.length()); // Keep cursor inside hex length
}
@Override
public void charTyped(char chr) {
if (isHexChar(chr) && text.length() < 7) { // Limit to 6 hex digits + '#'
text = text.substring(0, cursorPosition) +
chr +
text.substring(cursorPosition);
cursorPosition++;
if (text.length() == 7) { // Only update value when full hex is entered
prop.value = hexToInt(text);
ConfigManager.save();
}
}
}
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
if (keyCode == GLFW.GLFW_KEY_BACKSPACE && cursorPosition > 1) { // Prevent deleting '#'
text = text.substring(0, cursorPosition - 1) +
text.substring(cursorPosition);
cursorPosition--;
} else if (keyCode == GLFW.GLFW_KEY_LEFT && cursorPosition > 1) {
cursorPosition--;
} else if (keyCode == GLFW.GLFW_KEY_RIGHT && cursorPosition < text.length()) {
cursorPosition++;
}
if (text.length() == 1) { // Reset if empty (keep '#')
text = "#";
cursorPosition = 1;
}
if (text.length() == 7) { // Update only when valid hex
prop.value = hexToInt(text);
ConfigManager.save();
}
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
if (prop.isReset) {
this.text = intToHex(prop.value);
this.cursorPosition = 0;
prop.isReset = false;
}
SaturnUi.drawHighResGuiTexture(
context,
Textures.BUTTON_BORDER,
0,
0,
this.width,
this.height,
focused
? SaturnClientConfig.color.value
: SaturnClientConfig.getWhite(this.alpha));
int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
context.drawText(
textRenderer,
SaturnUi.text(text),
2,
2,
textColor,
false);
if (focused) {
int cursorX = 2 +
textRenderer.getWidth(
SaturnUi.text(text.substring(0, cursorPosition)));
context.fill(
cursorX,
2,
cursorX + 1,
textRenderer.fontHeight + 4,
0xFFFFFFFF);
}
}
public void clearText() {
text = "#";
cursorPosition = 1;
prop.value = 0xFFFFFFFF; // Default white
}
private boolean isHexChar(char chr) {
return ((chr >= '0' && chr <= '9') ||
(chr >= 'A' && chr <= 'F') ||
(chr >= 'a' && chr <= 'f'));
}
public static int hexToInt(String hex) {
hex = hex.replace("#", "");
int rgb = Integer.parseInt(hex, 16);
return (0xFF << 24) | rgb;
}
public static String intToHex(int color) {
int rgb = color & 0xFFFFFF;
return String.format("#%06X", rgb).toLowerCase();
}
}
@@ -1,177 +0,0 @@
package org.saturnclient.ui.components;
import net.minecraft.client.gui.DrawContext;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.SaturnWidget;
import org.saturnclient.ui.Textures;
public class SaturnInteger extends SaturnWidget {
String text = "";
public Property<Integer> prop;
public int cursorPosition = 0;
public SaturnInteger(Property<Integer> prop, int x, int y, int width) {
this.x = x;
this.y = y;
this.width = width;
this.prop = prop;
this.text = String.valueOf(prop.value);
this.height = SaturnClient.client.textRenderer.fontHeight + 4;
this.scale = 0.8f;
}
@Override
public void click(int mouseX, int mouseY) {
int scrollOffset = getScrollOffset();
String visibleText = text.substring(scrollOffset);
int newCursorPos = scrollOffset;
int textWidth = 0;
for (int i = 0; i < visibleText.length(); i++) {
textWidth = SaturnClient.client.textRenderer.getWidth(visibleText.substring(0, i + 1));
if (textWidth > mouseX) {
break;
}
newCursorPos++;
}
cursorPosition = newCursorPos;
}
@Override
public void charTyped(char chr) {
if (Character.isDigit(chr) && text.length() < 10) {
if (text.equals("0") && cursorPosition == 1) {
text = "" + chr;
} else {
text = text.substring(0, cursorPosition) +
chr +
text.substring(cursorPosition);
prop.value = text.isEmpty() ? 0 : Integer.parseInt(text);
ConfigManager.save();
cursorPosition++;
}
}
}
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
if (keyCode == GLFW.GLFW_KEY_BACKSPACE) {
if (cursorPosition > 0) {
text = text.substring(0, cursorPosition - 1) +
text.substring(cursorPosition);
cursorPosition--;
if (text.length() == 0) {
text = "0";
cursorPosition = 1;
}
prop.value = Integer.parseInt(text);
ConfigManager.save();
}
} else if (keyCode == GLFW.GLFW_KEY_LEFT) {
if (cursorPosition > 0) {
cursorPosition--;
}
} else if (keyCode == GLFW.GLFW_KEY_RIGHT) {
if (cursorPosition < text.length()) {
cursorPosition++;
}
}
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
if (prop.isReset) {
this.text = String.valueOf(prop.value);
this.cursorPosition = 0;
prop.isReset = false;
}
SaturnUi.drawHighResGuiTexture(
context,
Textures.BUTTON_BORDER,
0,
0,
this.width,
this.height,
focused
? SaturnClientConfig.color.value
: SaturnClientConfig.getWhite(this.alpha));
int scrollOffset = getScrollOffset();
String visibleText = getVisibleText(scrollOffset);
int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
context.drawText(
SaturnClient.client.textRenderer,
SaturnUi.text(visibleText),
2,
2,
textColor,
false);
if (focused) {
int cursorX = 2 +
SaturnClient.client.textRenderer.getWidth(
SaturnUi.text(
visibleText.substring(0, cursorPosition - scrollOffset)));
context.fill(
cursorX,
2,
cursorX + 1,
SaturnClient.client.textRenderer.fontHeight + 4,
0xFFFFFFFF);
}
}
public void clearText() {
text = "0";
cursorPosition = 1;
prop.value = 0;
}
private int getScrollOffset() {
if (text.isEmpty()) {
return 0;
}
int offset = 0;
int maxWidth = width - 4; // Available width for text
while (offset < cursorPosition) { // Ensure cursor remains visible
String visibleText = text.substring(offset, cursorPosition);
if (SaturnClient.client.textRenderer.getWidth(SaturnUi.text(visibleText)) > maxWidth) {
offset++;
} else {
break;
}
}
return offset;
}
private String getVisibleText(int scrollOffset) {
String visibleText = text.substring(scrollOffset);
// Ensure text does not overflow past the available width
for (int i = 1; i <= visibleText.length(); i++) {
if (SaturnClient.client.textRenderer.getWidth(
SaturnUi.text(visibleText.substring(0, i))) > width - 4) {
return visibleText.substring(0, i - 1); // Trim overflow
}
}
return visibleText;
}
}
@@ -1,106 +0,0 @@
package org.saturnclient.ui.components;
import net.minecraft.client.gui.DrawContext;
import org.saturnclient.modules.Module;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.menus.ConfigEditor;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.SaturnWidget;
import org.saturnclient.ui.Textures;
public class SaturnModComp extends SaturnWidget {
Module mod;
public SaturnModComp(Module mod) {
this.mod = mod;
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
boolean enabled = mod.isEnabled();
boolean hovering_settings = settingsHovering(mouseX, mouseY);
int color = !hovering_settings && (enabled || hovering)
? SaturnClientConfig.color.value
: SaturnClientConfig.getWhite(alpha);
int size = 12;
if (enabled) {
SaturnUi.drawHighResTexture(
context,
Textures.MOD_BG,
0,
0,
86,
20,
SaturnClientConfig.color.value);
}
SaturnUi.drawHighResTexture(
context,
Textures.MOD,
0,
0,
86,
20,
color);
SaturnUi.drawHighResTexture(
context,
mod.getIconTexture(),
4,
4,
size,
size,
color);
context.drawText(
SaturnClient.client.textRenderer,
SaturnUi.text(mod.getName()),
20,
(21 - SaturnClient.client.textRenderer.fontHeight) / 2,
color,
false);
SaturnUi.drawHighResTexture(
context,
Textures.SETTINGS,
77,
11,
7,
7,
hovering_settings
? SaturnClientConfig.color.value
: SaturnClientConfig.getWhite(alpha));
}
@Override
public void click(int mouseX, int mouseY) {
if (settingsHovering(mouseX, mouseY)) {
SaturnClient.client
.setScreen(new ConfigEditor(mod.getConfig()));
} else {
mod.setEnabled(!mod.isEnabled());
}
}
public boolean settingsHovering(int mouseX, int mouseY) {
int sX = 77;
int sY = 11;
int size = 7;
return (sX < mouseX &&
sX + size > mouseX &&
sY < mouseY &&
sY + size > mouseY);
}
}
@@ -1,155 +0,0 @@
package org.saturnclient.ui.components;
import org.saturnclient.saturnclient.SaturnClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.SaturnWidget;
import org.saturnclient.ui.Textures;
public class SaturnString extends SaturnWidget {
public Property<String> prop;
public int cursorPosition = 0;
private TextRenderer textRenderer = SaturnClient.client.textRenderer;
public SaturnString(Property<String> prop, int x, int y, int width) {
this.x = x;
this.y = y;
this.width = width;
this.prop = prop;
this.height = textRenderer.fontHeight + 4;
this.scale = 0.8f;
}
@Override
public void click(int mouseX, int mouseY) {
int scrollOffset = getScrollOffset();
String visibleText = prop.value.substring(scrollOffset);
int newCursorPos = scrollOffset;
int textWidth = 0;
for (int i = 0; i < visibleText.length(); i++) {
textWidth = textRenderer.getWidth(visibleText.substring(0, i + 1));
if (textWidth > mouseX) {
break;
}
newCursorPos++;
}
cursorPosition = newCursorPos;
}
@Override
public void charTyped(char chr) {
prop.value = prop.value.substring(0, cursorPosition) +
chr +
prop.value.substring(cursorPosition);
cursorPosition++;
}
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
if (keyCode == GLFW.GLFW_KEY_BACKSPACE) {
if (cursorPosition > 0) {
prop.value = prop.value.substring(0, cursorPosition - 1) +
prop.value.substring(cursorPosition);
cursorPosition--;
}
} else if (keyCode == GLFW.GLFW_KEY_LEFT) {
if (cursorPosition > 0) {
cursorPosition--;
}
} else if (keyCode == GLFW.GLFW_KEY_RIGHT) {
if (cursorPosition < prop.value.length()) {
cursorPosition++;
}
}
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
SaturnUi.drawHighResGuiTexture(
context,
Textures.BUTTON_BORDER,
0,
0,
this.width,
this.height,
focused
? SaturnClientConfig.color.value
: SaturnClientConfig.getWhite(this.alpha));
int scrollOffset = getScrollOffset();
String visibleText = getVisibleText(scrollOffset);
int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
context.drawText(
textRenderer,
SaturnUi.text(visibleText),
2,
2,
textColor,
false);
if (focused) {
int cursorX = 2 +
textRenderer.getWidth(
SaturnUi.text(
visibleText.substring(0, cursorPosition - scrollOffset)));
context.fill(
cursorX,
2,
cursorX + 1,
textRenderer.fontHeight + 3,
0xFFFFFFFF);
}
}
public void clearText() {
prop.value = "";
cursorPosition = 1;
}
private int getScrollOffset() {
if (prop.value.isEmpty()) {
return 0;
}
int offset = 0;
int maxWidth = width - 4; // Available width for prop.value
while (offset < cursorPosition) { // Ensure cursor remains visible
String visibleText = prop.value.substring(offset, cursorPosition);
if (textRenderer.getWidth(SaturnUi.text(visibleText)) > maxWidth) {
offset++;
} else {
break;
}
}
return offset;
}
private String getVisibleText(int scrollOffset) {
String visibleText = prop.value.substring(scrollOffset);
// Ensure prop.value does not overflow past the available width
for (int i = 1; i <= visibleText.length(); i++) {
if (textRenderer.getWidth(
SaturnUi.text(visibleText.substring(0, i))) > width - 4) {
return visibleText.substring(0, i - 1); // Trim overflow
}
}
return visibleText;
}
}
@@ -1,66 +0,0 @@
package org.saturnclient.ui.components;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.RenderLayer;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui.SaturnWidget;
import org.saturnclient.ui.Textures;
public class SaturnToggle extends SaturnWidget {
Property<Boolean> prop;
public SaturnToggle(Property<Boolean> prop) {
this.prop = prop;
width = 16;
height = 8;
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
int color = prop.value
? SaturnClientConfig.color.value
: SaturnClientConfig.getWhite(alpha);
int w = 13;
int h = 6;
int s = 8;
context.drawTexture(
RenderLayer::getGuiTextured,
Textures.TOGGLE_BG,
0,
1,
0,
0,
w,
h,
w,
h,
color);
context.drawTexture(
RenderLayer::getGuiTextured,
Textures.TOGGLE_INDICATOR,
prop.value ? 5 : 0,
0,
0,
0,
s,
s,
s,
s,
color);
}
@Override
public void click(int mouseX, int mouseY) {
prop.value = !prop.value;
ConfigManager.save();
}
}
@@ -1,106 +0,0 @@
package org.saturnclient.ui.widgets;
import com.mojang.blaze3d.systems.RenderSystem;
import java.util.function.Consumer;
import org.saturnclient.saturnclient.SaturnClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.text.Text;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.SaturnWidget;
import org.saturnclient.ui.Textures;
import net.minecraft.client.MinecraftClient;
public class SaturnButton extends SaturnWidget {
public boolean active = true;
public String text;
public Consumer<SaturnButton> onPress;
public boolean background = true;
public boolean bold = false;
private float hoverAlpha = 0.0f;
public SaturnButton(String text, Consumer<SaturnButton> onPress) {
this.text = text;
this.onPress = onPress;
}
public SaturnButton(String text, Runnable onPress) {
this(text, b -> {
onPress.run();
});
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
MinecraftClient minecraftClient = SaturnClient.client;
if (background) {
RenderSystem.setShaderTexture(0, Textures.BUTTON);
SaturnUi.drawHighResGuiTexture(
context,
Textures.BUTTON,
0,
0,
this.width,
this.height,
SaturnClientConfig.getColor(hovering, alpha));
if (hovering) {
if (hoverAlpha == 0.0f)
hoverAlpha = 0.1f;
SaturnUi.drawHighResGuiTexture(
context,
Textures.BUTTON_BORDER,
0,
0,
this.width,
this.height,
SaturnClientConfig.getColor(hovering, alpha));
} else {
hoverAlpha = 0.0f;
}
}
if (alpha != 0.0f) {
Text t = bold ? SaturnUi.textBold(text) : SaturnUi.text(text);
context.drawText(
minecraftClient.textRenderer,
t,
((this.width -
minecraftClient.textRenderer.getWidth(t))
/
2),
((this.height - minecraftClient.textRenderer.fontHeight + 1) /
2),
SaturnClientConfig.getColor(hovering, alpha),
false);
}
}
@Override
public void click(int mouseX, int mouseY) {
this.onPress.accept(this);
}
@Override
public void tick() {
if (hoverAlpha > 0.0f && hoverAlpha < 0.9f)
hoverAlpha += 0.03f;
else if (hoverAlpha > 0.9f && hoverAlpha < 1.0f)
hoverAlpha = 1.0f;
}
public SaturnWidget setBold(boolean bold) {
this.bold = bold;
return this;
}
}
@@ -1,68 +0,0 @@
package org.saturnclient.ui.widgets;
import java.util.function.Consumer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.util.Identifier;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.SaturnWidget;
public class SaturnClickableImage extends SaturnWidget {
public Identifier texture;
public boolean selected = false;
public Consumer<SaturnClickableImage> onPress;
private float hoverAlpha = 0.0f;
public SaturnClickableImage(
Identifier texture,
Consumer<SaturnClickableImage> onPress) {
this.texture = texture;
this.onPress = onPress;
}
public SaturnClickableImage(Identifier texture, Runnable onPress) {
this(texture, _o -> {
onPress.run();
});
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
if (hovering) {
if (hoverAlpha == 0.0f)
hoverAlpha = 0.1f;
}
SaturnUi.drawHighResTexture(
context,
texture,
0,
0,
width,
height,
SaturnClientConfig.getColor(hovering || selected, alpha));
}
@Override
public void click(int mouseX, int mouseY) {
this.onPress.accept(this);
}
public SaturnClickableImage setSelected(boolean selected) {
this.selected = selected;
return this;
}
@Override
public void tick() {
if (hoverAlpha > 0.0f && hoverAlpha < 0.9f)
hoverAlpha += 0.03f;
else if (hoverAlpha > 0.9f && hoverAlpha < 1.0f)
hoverAlpha = 1.0f;
}
}
@@ -1,54 +0,0 @@
package org.saturnclient.ui.widgets;
import java.util.function.Consumer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.util.Identifier;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.SaturnWidget;
public class SaturnClickableSprite extends SaturnWidget {
public Identifier sprite;
public boolean selected = false;
public Consumer<SaturnClickableSprite> onPress;
public SaturnClickableSprite(
Identifier sprite,
Consumer<SaturnClickableSprite> onPress) {
this.sprite = sprite;
this.onPress = onPress;
}
public SaturnClickableSprite(Identifier sprite, Runnable onPress) {
this(sprite, _o -> {
onPress.run();
});
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
SaturnUi.drawHighResGuiTexture(
context,
sprite,
0,
0,
this.width,
this.height,
SaturnClientConfig.getColor(hovering, alpha));
}
@Override
public void click(int mouseX, int mouseY) {
this.onPress.accept(this);
}
public SaturnClickableSprite setSelected(boolean selected) {
this.selected = selected;
return this;
}
}
@@ -1,33 +0,0 @@
package org.saturnclient.ui.widgets;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.ColorHelper;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.SaturnWidget;
public class SaturnImage extends SaturnWidget {
private final Identifier sprite;
public SaturnImage(Identifier sprite) {
this.sprite = sprite;
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
SaturnUi.drawHighResTexture(
context,
sprite,
0,
0,
width,
height,
ColorHelper.getWhite(alpha));
}
}
@@ -1,101 +0,0 @@
package org.saturnclient.ui.widgets;
import java.util.function.Consumer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.SaturnWidget;
import org.saturnclient.ui.Textures;
import com.mojang.blaze3d.systems.RenderSystem;
public class SaturnImageButton extends SaturnWidget {
public Identifier sprite;
public int imageWidth;
public int imageHeight;
public Consumer<SaturnImageButton> onPress;
private float hoverAlpha = 0.0f;
public SaturnImageButton(
Identifier sprite,
int imageWidth,
int imageHeight,
Consumer<SaturnImageButton> onPress) {
this.sprite = sprite;
this.imageWidth = imageWidth;
this.imageHeight = imageHeight;
this.onPress = onPress;
}
public SaturnImageButton(
Identifier sprite,
int imageWidth,
int imageHeight,
Runnable onPress) {
this(sprite, imageWidth, imageHeight, _o -> {
onPress.run();
});
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
if (hovering) {
if (hoverAlpha == 0.0f)
hoverAlpha = 0.1f;
}
RenderSystem.setShaderTexture(0, Textures.BUTTON);
SaturnUi.drawHighResGuiTexture(
context,
Textures.BUTTON,
0,
0,
this.width,
this.height,
SaturnClientConfig.getColor(hovering, alpha));
if (hovering) {
SaturnUi.drawHighResGuiTexture(
context,
Textures.BUTTON_BORDER,
0,
0,
this.width,
this.height,
SaturnClientConfig.getColor(hovering, alpha));
}
context.drawTexture(
RenderLayer::getGuiTextured,
sprite,
((width - imageWidth) / 2),
((height - imageHeight) / 2),
0,
0,
imageWidth,
imageHeight,
imageWidth,
imageHeight,
SaturnClientConfig.getColor(hovering, alpha));
}
@Override
public void click(int mouseX, int mouseY) {
this.onPress.accept(this);
}
@Override
public void tick() {
if (hoverAlpha > 0.0f && hoverAlpha < 0.9f)
hoverAlpha += 0.03f;
else if (hoverAlpha > 0.9f && hoverAlpha < 1.0f)
hoverAlpha = 1.0f;
}
}
@@ -1,230 +0,0 @@
package org.saturnclient.ui.widgets;
import org.saturnclient.ui.SaturnWidget;
import org.saturnclient.saturnclient.SaturnClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.util.math.MatrixStack;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.Textures;
public class SaturnInputBox extends SaturnWidget {
private TextRenderer textRenderer = SaturnClient.client.textRenderer;
private int tick = 0;
private boolean cursorVisible = true;
public Property<String> prop;
public int cursorPosition = 0;
public Runnable onUpdate;
public SaturnInputBox(Property<String> prop, int x, int y, int width) {
this.x = x;
this.y = y;
this.width = width;
this.prop = prop;
this.height = textRenderer.fontHeight + 4;
this.scale = 1.0f;
}
@Override
public void click(int mouseX, int mouseY) {
int scrollOffset = getScrollOffset();
String visibleText = prop.value.substring(scrollOffset);
int newCursorPos = scrollOffset;
int textWidth = 0;
for (int i = 0; i < visibleText.length(); i++) {
textWidth = textRenderer.getWidth(visibleText.substring(0, i + 1));
if (textWidth > mouseX) {
break;
}
newCursorPos++;
}
cursorPosition = newCursorPos;
}
@Override
public void charTyped(char chr) {
prop.value = prop.value.substring(0, cursorPosition) +
chr +
prop.value.substring(cursorPosition);
cursorPosition++;
onUpdate.run();
}
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
if (keyCode == GLFW.GLFW_KEY_BACKSPACE) {
if (cursorPosition > 0) {
prop.value = prop.value.substring(0, cursorPosition - 1) +
prop.value.substring(cursorPosition);
cursorPosition--;
}
onUpdate.run();
} else if (keyCode == GLFW.GLFW_KEY_LEFT) {
if (cursorPosition > 0) {
cursorPosition--;
}
} else if (keyCode == GLFW.GLFW_KEY_RIGHT) {
if (cursorPosition < prop.value.length()) {
cursorPosition++;
}
}
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
SaturnUi.drawHighResGuiTexture(
context,
Textures.BUTTON,
0,
0,
this.width,
this.height,
focused
? SaturnClientConfig.color.value
: SaturnClientConfig.getWhite(this.alpha));
if (hovering || focused) {
SaturnUi.drawHighResGuiTexture(
context,
Textures.BUTTON_BORDER,
0,
0,
this.width,
this.height,
hovering
? SaturnClientConfig.color.value
: SaturnClientConfig.getWhite(this.alpha));
}
MatrixStack matrices = context.getMatrices();
matrices.push();
if (prop.value.isEmpty() && !focused) {
SaturnUi.drawHighResTexture(
context,
Textures.SEARCH,
3,
3,
9,
9,
hovering
? SaturnClientConfig.color.value
: SaturnClientConfig.getWhite(this.alpha));
matrices.scale(0.9f, 0.9f, 1.0f);
context.drawText(
textRenderer,
SaturnUi.text("Search"),
14,
textRenderer.fontHeight / 2 + 1,
hovering
? SaturnClientConfig.color.value
: SaturnClientConfig.getWhite(this.alpha),
false);
}
matrices.pop();
matrices.push();
matrices.scale(0.9f, 0.9f, 1.0f);
int scrollOffset = getScrollOffset();
String visibleText = getVisibleText(scrollOffset);
int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
context.drawText(
textRenderer,
SaturnUi.text(visibleText),
3,
textRenderer.fontHeight / 2,
textColor,
false);
if (focused && cursorVisible) {
int cursorX = 2 +
textRenderer.getWidth(
SaturnUi.text(
visibleText.substring(0, cursorPosition - scrollOffset)));
matrices.translate(cursorX, 0, 0);
context.fill(
1,
2,
2,
textRenderer.fontHeight + 3,
0xFFFFFFFF);
}
matrices.pop();
}
public void clearText() {
prop.value = "";
cursorPosition = 1;
}
private int getScrollOffset() {
if (prop.value.isEmpty()) {
return 0;
}
int offset = 0;
int maxWidth = width - 4; // Available width for prop.value
while (offset < cursorPosition) { // Ensure cursor remains visible
String visibleText = prop.value.substring(offset, cursorPosition);
if (textRenderer.getWidth(SaturnUi.text(visibleText)) > maxWidth) {
offset++;
} else {
break;
}
}
return offset;
}
private String getVisibleText(int scrollOffset) {
String visibleText = prop.value.substring(scrollOffset);
// Ensure prop.value does not overflow past the available width
for (int i = 1; i <= visibleText.length(); i++) {
if (textRenderer.getWidth(
SaturnUi.text(visibleText.substring(0, i))) > width - 4) {
return visibleText.substring(0, i - 1); // Trim overflow
}
}
return visibleText;
}
public SaturnInputBox onUpdate(Runnable onUpdate) {
this.onUpdate = onUpdate;
return this;
}
@Override
public void tick() {
if (focused) {
if (tick < 45 || tick < 60 && cursorVisible) {
tick++;
} else {
tick = 0;
cursorVisible = !cursorVisible;
}
}
}
}
@@ -1,150 +0,0 @@
package org.saturnclient.ui.widgets;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.util.math.MatrixStack;
import org.saturnclient.ui.SaturnWidget;
public class SaturnScroll extends SaturnWidget {
private int scroll = 0;
private int maxScroll = 0;
private List<SaturnWidget> children = new ArrayList<>();
@Override
public void mouseScrolled(
int mouseX,
int mouseY,
double horizontalAmount,
double verticalAmount
) {
verticalAmount *= 2;
if (scroll - verticalAmount <= 0) {
scroll = 0;
} else if (scroll - verticalAmount >= maxScroll) {
scroll = maxScroll;
} else {
scroll -= verticalAmount;
}
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY
) {
context.enableScissor(0, 0, width, height);
for (SaturnWidget widget : children) {
if (!widget.visible) continue;
double adjustedMouseX = (mouseX - widget.x);
double adjustedMouseY = (mouseY - widget.y + scroll);
boolean isMouseInside =
adjustedMouseX >= 0 &&
adjustedMouseX <= (widget.width * widget.scale) &&
adjustedMouseY >= 0 &&
adjustedMouseY <= (widget.height * widget.scale);
if (
widget.y - scroll < height && widget.y + widget.height > scroll
) {
MatrixStack matrices = context.getMatrices();
matrices.push();
matrices.translate(widget.x, widget.y - scroll, 0);
matrices.scale(widget.scale, widget.scale, 1.0f);
widget.render(
context,
isMouseInside,
(int) (adjustedMouseX / widget.scale),
(int) (adjustedMouseY / widget.scale)
);
matrices.pop();
}
}
context.disableScissor();
}
@Override
public void init() {
maxScroll = 0;
for (SaturnWidget child : children) {
maxScroll = Math.max(maxScroll, child.y + child.height - height);
}
}
public void draw(SaturnWidget widget) {
synchronized (children) {
widget.init();
children.add(widget);
}
}
public boolean isScrollable() {
for (SaturnWidget widget : children) {
if (widget.y + widget.height > height) {
return true;
}
}
return false;
}
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
for (SaturnWidget widget : children) {
if (!widget.visible || !widget.focused) {
continue;
}
widget.keyPressed(keyCode, scanCode, modifiers);
break;
}
}
@Override
public void charTyped(char typedChar) {
for (SaturnWidget widget : children) {
if (!widget.visible || !widget.focused) {
continue;
}
widget.charTyped(typedChar);
break;
}
}
@Override
public void click(int mouseX, int mouseY) {
for (SaturnWidget widget : children) {
widget.focused = false;
if (!widget.visible) {
continue;
}
double adjustedMouseX = (mouseX - widget.x);
double adjustedMouseY = (mouseY - (widget.y - scroll));
boolean isMouseInside =
adjustedMouseX >= 0 &&
adjustedMouseX <= (widget.width * widget.scale) &&
adjustedMouseY >= 0 &&
adjustedMouseY <= (widget.height * widget.scale);
if (isMouseInside) {
widget.focused = true;
widget.click(
(int) (adjustedMouseX / widget.scale),
(int) (adjustedMouseY / widget.scale)
);
}
}
}
public void clear() {
children.clear();
}
}
@@ -1,32 +0,0 @@
package org.saturnclient.ui.widgets;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.ColorHelper;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.SaturnWidget;
public class SaturnSprite extends SaturnWidget {
private final Identifier sprite;
public SaturnSprite(Identifier sprite) {
this.sprite = sprite;
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
SaturnUi.drawHighResGuiTexture(
context,
sprite,
0,
0,
this.width,
this.height,
ColorHelper.getWhite(this.alpha));
}
}
@@ -1,38 +0,0 @@
package org.saturnclient.ui.widgets;
import net.minecraft.client.gui.DrawContext;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.SaturnWidget;
public class SaturnText extends SaturnWidget {
private String text;
public SaturnText(String text) {
this.text = text;
this.width = SaturnClient.client.textRenderer.getWidth(SaturnUi.text(text));
this.height = SaturnClient.client.textRenderer.fontHeight;
}
public SaturnText(String text, int color) {
this(text);
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
context.drawText(
SaturnClient.client.textRenderer,
SaturnUi.text(text),
0,
0,
SaturnClientConfig.getColor(hovering, alpha),
false);
}
}
+65 -21
View File
@@ -1,39 +1,83 @@
package org.saturnclient.ui2; package org.saturnclient.ui2;
public class Element { import java.util.function.Function;
protected int x;
protected int y;
protected int width;
protected int height;
public boolean focused;
public void render(RenderScope renderScope, RenderContext ctx) { import org.saturnclient.ui2.anim.Animation;
import org.saturnclient.ui2.anim.Curve;
public class Element {
public int x;
public int y;
public int width;
public int height;
public float opacity = 1.0f;
public boolean focused;
public Animation animation;
public float scale = 1.0f;
public Function<Double, Double> curve = Curve::easeInOutCubic;
public Integer duration = null; // Element duration, will fade out if not null
public void render(RenderScope renderScope, ElementContext ctx) {
} }
public void click(int mouseX, int mouseY) { public void click(int mouseX, int mouseY) {
} }
public final void dimensions(int width, int height) { public void scroll(int mouseX, int mouseY, double horizontalAmount, double verticalAmount) {
}
public void keyPressed(int keyCode, int scanCode, int modifiers) {
}
public void charTyped(char typedChar) {
}
public Element dimensions(int width, int height) {
this.width = width; this.width = width;
this.height = height; this.height = height;
}
public final void position(int x, int y) {
this.x = x;
this.y = y;
}
public final Element center(int w, int h) {
this.x = (w - width) / 2;
this.y = (h - height) / 2;
return this; return this;
} }
public final Element centerOffset(int w, int h, int offsetX, int offsetY) { public Element position(int x, int y) {
this.x = (w - width) / 2 + offsetX; this.x = x;
this.y = (h - height) / 2 + offsetY; this.y = y;
return this; return this;
} }
public Element centerHorizontal(int w, int h, int offsetX, int offsetY) {
this.x = (w - (int) (width * scale)) / 2 + offsetX;
this.y = offsetY;
return this;
}
public Element center(int w, int h) {
this.centerOffset(w, h, 0, 0);
return this;
}
public Element centerOffset(int w, int h, int offsetX, int offsetY) {
this.x = (w - (int) (width * scale)) / 2 + offsetX;
this.y = (h - (int) (height * scale)) / 2 + offsetY;
return this;
}
public Element animation(Animation animation) {
this.animation = animation;
return this;
}
public Element animation(Animation animation, Function<Double, Double> curve) {
this.animation = animation;
this.curve = curve;
return this;
}
public Element scale(float scale) {
this.scale = scale;
return this;
}
} }
@@ -0,0 +1,23 @@
package org.saturnclient.ui2;
public class ElementContext {
public int mouseX, mouseY;
public int elementWidth, elementHeight;
public float elementScale;
public ElementContext(int mouseX, int mouseY, Element e) {
this.mouseX = mouseX - e.x;
this.mouseY = mouseY - e.y;
this.elementWidth = e.width;
this.elementHeight = e.height;
this.elementScale = e.scale;
}
public boolean isHovering() {
return Utils.isHovering(mouseX, mouseY, elementWidth, elementHeight, elementScale);
}
public boolean isHovering(int x, int y, int width, int height) {
return Utils.isHovering(mouseX - x, mouseY - y, width, height, elementScale);
}
}
@@ -1,23 +0,0 @@
package org.saturnclient.ui2;
public class RenderContext {
public int mouseX, mouseY;
public int elementX, elementY;
public int elementWidth, elementHeight;
public RenderContext(int mouseX, int mouseY, Element e) {
this.mouseX = mouseX;
this.mouseY = mouseY;
this.elementX = e.x;
this.elementY = e.y;
this.elementWidth = e.width;
this.elementHeight = e.height;
}
public boolean isHovering() {
return mouseX >= elementX &&
mouseX <= elementX + elementWidth &&
mouseY >= elementY &&
mouseY <= elementY + elementHeight;
}
}
@@ -1,7 +1,12 @@
package org.saturnclient.ui2; package org.saturnclient.ui2;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.saturnclient.ui2.resources.Fonts; import org.saturnclient.ui2.resources.Fonts;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
@@ -9,21 +14,39 @@ import com.mojang.blaze3d.systems.RenderSystem;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import net.minecraft.client.font.TextRenderer; import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.font.TextRenderer.TextLayerType; import net.minecraft.client.font.TextRenderer.TextLayerType;
import net.minecraft.client.gui.ScreenRect;
import net.minecraft.client.render.DiffuseLighting;
import net.minecraft.client.render.OverlayTexture;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.item.ItemRenderState;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.Window;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ModelTransformationMode;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.crash.CrashException;
import net.minecraft.util.crash.CrashReport;
import net.minecraft.util.crash.CrashReportSection;
import net.minecraft.util.math.RotationAxis; import net.minecraft.util.math.RotationAxis;
import net.minecraft.world.World;
public class RenderScope { public class RenderScope {
public MatrixStack matrices; public MatrixStack matrices;
public VertexConsumerProvider.Immediate vertexConsumers; public VertexConsumerProvider.Immediate vertexConsumers;
// private Function<Identifier, RenderLayer> renderLayers;
private ScissorStack scissorStack = new ScissorStack();
private int opacity = 255 << 24; private int opacity = 255 << 24;
private final ItemRenderState itemRenderState;
public RenderScope(MatrixStack matrices, VertexConsumerProvider.Immediate vertexConsumers) { public RenderScope(MatrixStack matrices, VertexConsumerProvider.Immediate vertexConsumers) {
this.matrices = matrices; this.matrices = matrices;
this.vertexConsumers = vertexConsumers; this.vertexConsumers = vertexConsumers;
this.scissorStack = new ScissorStack();
this.itemRenderState = new ItemRenderState();
} }
public void setOpacity(float alpha) { public void setOpacity(float alpha) {
@@ -31,7 +54,14 @@ public class RenderScope {
} }
public int getColor(int color) { public int getColor(int color) {
return opacity | (color & 0x00FFFFFF); int originalAlpha = (color >>> 24) & 0xFF;
int newAlpha = (opacity >>> 24) & 0xFF;
int mixedAlpha = (originalAlpha * newAlpha) / 255;
return (mixedAlpha << 24) | (color & 0x00FFFFFF);
}
public void setRenderLayer(Function<Identifier, RenderLayer> renderLayers) {
// this.renderLayers = renderLayers;
} }
public void drawRect(int x, int y, int width, int height, int color) { public void drawRect(int x, int y, int width, int height, int color) {
@@ -63,17 +93,29 @@ public class RenderScope {
vertexConsumer.vertex(matrix4f, (float) x2, (float) y1, 0).color(color); vertexConsumer.vertex(matrix4f, (float) x2, (float) y1, 0).color(color);
} }
public void drawText(String text, int x, int y, boolean bold, int color) { public void drawText(String text, int x, int y, int font, int color) {
color = getColor(color); drawText(1.0f, text, x, y, font, color);
Identifier font = bold ? Fonts.PANTON_BOLD : Fonts.PANTON;
TextRenderer textRenderer = SaturnClient.client.textRenderer;
textRenderer.draw(Fonts.setFont(text, font), (float) x, (float) y, color, false, this.matrices.peek().getPositionMatrix(),
this.vertexConsumers, TextLayerType.NORMAL, 0, 15728880);
} }
public void drawRoundedCorner(int width, int height, int radius, int color) { public void drawText(float scale, String text, int x, int y, int font, int color) {
if (font == 0) {
scale *= 2;
}
int i = 0;
for (String line : text.split("\n")) {
color = getColor(color);
matrices.push();
matrices.translate(x, y + (i * Fonts.getHeight()), 0);
matrices.scale(scale, scale, 1.0f);
TextRenderer textRenderer = SaturnClient.client.textRenderer;
textRenderer.draw(Fonts.setFont(line, Fonts.getFont(font)), 0, font == 0 ? 1: 7, color, false, this.matrices.peek().getPositionMatrix(),
this.vertexConsumers, TextLayerType.NORMAL, 0, 15728880);
matrices.pop();
i++;
}
}
private void drawRoundedCorner(int width, int height, int radius, int color) {
int w = width * 20; int w = width * 20;
int h = height * 20; int h = height * 20;
@@ -117,6 +159,7 @@ public class RenderScope {
} }
public void drawRoundedRectangle(int x, int y, int width, int height, int radius, int color) { public void drawRoundedRectangle(int x, int y, int width, int height, int radius, int color) {
radius = Math.min(radius, Math.min(width, height));
int cornerWidth = width / 2; int cornerWidth = width / 2;
int cornerHeight = height / 2; int cornerHeight = height / 2;
@@ -144,32 +187,184 @@ public class RenderScope {
public int getScaledWindowHeight() { public int getScaledWindowHeight() {
return SaturnClient.client.getWindow().getScaledHeight(); return SaturnClient.client.getWindow().getScaledHeight();
} }
public void drawTexture(Identifier sprite, int x, int y, float u, float v, int width, int height, int color) {
public void drawTexture(Function<Identifier, RenderLayer> renderLayers, Identifier sprite, int x, int y, float u, float v, int width, int height, int textureWidth, int textureHeight, int color) { this.drawTexture(sprite, x, y, u, v, width, height, width, height, width, height, color);
this.drawTexture(renderLayers, sprite, x, y, u, v, width, height, width, height, textureWidth, textureHeight, color);
} }
public void drawTexture(Function<Identifier, RenderLayer> renderLayers, Identifier sprite, int x, int y, float u, float v, int width, int height, int textureWidth, int textureHeight) { public void drawTexture(Identifier sprite, int x, int y, float u, float v, int width, int height) {
this.drawTexture(renderLayers, sprite, x, y, u, v, width, height, width, height, textureWidth, textureHeight); this.drawTexture(sprite, x, y, u, v, width, height, width, height, width, height);
} }
public void drawTexture(Function<Identifier, RenderLayer> renderLayers, Identifier sprite, int x, int y, float u, float v, int width, int height, int regionWith, int regionHeight, int textureWidth, int textureHeight) { public void drawTexture(Identifier sprite, int x, int y, float u, float v, int width, int height, int regionWith, int regionHeight, int textureWidth, int textureHeight) {
this.drawTexture(renderLayers, sprite, x, y, u, v, width, height, regionWith, regionHeight, textureWidth, textureHeight, -1); this.drawTexture(sprite, x, y, u, v, width, height, regionWith, regionHeight, textureWidth, textureHeight, -1);
} }
public void drawTexture(Function<Identifier, RenderLayer> renderLayers, Identifier sprite, int x, int y, float u, float v, int width, int height, int regionWidth, int regionHeight, int textureWidth, int textureHeight, int color) { public void drawTexture(Identifier sprite, int x, int y, float u, float v, int width, int height, int regionWidth, int regionHeight, int textureWidth, int textureHeight, int color) {
this.drawTexturedQuad(renderLayers, sprite, x, x + width, y, y + height, (u + 0.0F) / (float)textureWidth, (u + (float)regionWidth) / (float)textureWidth, (v + 0.0F) / (float)textureHeight, (v + (float)regionHeight) / (float)textureHeight, color); this.drawTexturedQuad(sprite, x, x + width, y, y + height, (u + 0.0F) / (float)textureWidth, (u + (float)regionWidth) / (float)textureWidth, (v + 0.0F) / (float)textureHeight, (v + (float)regionHeight) / (float)textureHeight, color);
} }
private void drawTexturedQuad(Function<Identifier, RenderLayer> renderLayers, Identifier sprite, int x1, int x2, int y1, int y2, float u1, float u2, float v1, float v2, int color) { private void drawTexturedQuad(Identifier sprite, int x1, int x2, int y1, int y2, float u1, float u2, float v1, float v2, int color) {
x1 *= 4;
x2 *= 4;
y1 *= 4;
y2 *= 4;
color = getColor(color);
matrices.push();
matrices.scale(0.25f, 0.25f, 1.0f);
RenderSystem.setShaderTexture(0, sprite); RenderSystem.setShaderTexture(0, sprite);
RenderLayer renderLayer = (RenderLayer)RenderLayer.getGuiTextured(sprite);
RenderLayer renderLayer = (RenderLayer)renderLayers.apply(sprite);
Matrix4f matrix4f = this.matrices.peek().getPositionMatrix(); Matrix4f matrix4f = this.matrices.peek().getPositionMatrix();
VertexConsumer vertexConsumer = this.vertexConsumers.getBuffer(renderLayer); VertexConsumer vertexConsumer = this.vertexConsumers.getBuffer(renderLayer);
vertexConsumer.vertex(matrix4f, (float)x1, (float)y1, 0.0F).texture(u1, v1).color(color); vertexConsumer.vertex(matrix4f, (float)x1, (float)y1, 0.0F).texture(u1, v1).color(color);
vertexConsumer.vertex(matrix4f, (float)x1, (float)y2, 0.0F).texture(u1, v2).color(color); vertexConsumer.vertex(matrix4f, (float)x1, (float)y2, 0.0F).texture(u1, v2).color(color);
vertexConsumer.vertex(matrix4f, (float)x2, (float)y2, 0.0F).texture(u2, v2).color(color); vertexConsumer.vertex(matrix4f, (float)x2, (float)y2, 0.0F).texture(u2, v2).color(color);
vertexConsumer.vertex(matrix4f, (float)x2, (float)y1, 0.0F).texture(u2, v1).color(color); vertexConsumer.vertex(matrix4f, (float)x2, (float)y1, 0.0F).texture(u2, v1).color(color);
matrices.pop();
}
public void enableScissor(int x1, int y1, int x2, int y2) {
ScreenRect screenRect = (new ScreenRect(x1, y1, x2 - x1, y2 - y1)).transform(this.matrices.peek().getPositionMatrix());
this.setScissor(this.scissorStack.push(screenRect));
}
public void disableScissor() {
this.setScissor(this.scissorStack.pop());
}
public boolean scissorContains(int x, int y) {
return this.scissorStack.containsPoint(x, y);
}
private void setScissor(@Nullable ScreenRect rect) {
this.draw();
if (rect != null) {
Window window = SaturnClient.client.getWindow();
int i = window.getFramebufferHeight();
double d = window.getScaleFactor();
double e = (double)rect.getLeft() * d;
double f = (double)i - (double)rect.getBottom() * d;
double g = (double)rect.width() * d;
double h = (double)rect.height() * d;
RenderSystem.enableScissor((int)e, (int)f, Math.max(0, (int)g), Math.max(0, (int)h));
} else {
RenderSystem.disableScissor();
}
}
public void draw() {
this.vertexConsumers.draw();
}
public void draw(Consumer<VertexConsumerProvider> drawer) {
drawer.accept(this.vertexConsumers);
this.vertexConsumers.draw();
}
static class ScissorStack {
private final Deque<ScreenRect> stack = new ArrayDeque<>();
public ScreenRect push(ScreenRect p_281812_) {
ScreenRect screenrectangle = this.stack.peekLast();
if (screenrectangle != null) {
ScreenRect screenrectangle1 = Objects.requireNonNullElse(p_281812_.intersection(screenrectangle), ScreenRect.empty());
this.stack.addLast(screenrectangle1);
return screenrectangle1;
} else {
this.stack.addLast(p_281812_);
return p_281812_;
}
}
@Nullable
public ScreenRect pop() {
if (this.stack.isEmpty()) {
throw new IllegalStateException("Scissor stack underflow");
} else {
this.stack.removeLast();
return this.stack.peekLast();
}
}
public boolean containsPoint(int p_329411_, int p_333404_) {
return this.stack.isEmpty() ? true : this.stack.peek().contains(p_329411_, p_333404_);
}
}
public void drawItem(ItemStack item, int x, int y) {
this.drawItem(SaturnClient.client.player, SaturnClient.client.world, item, x, y, 0);
}
public void drawItem(ItemStack stack, int x, int y, int seed) {
this.drawItem(SaturnClient.client.player, SaturnClient.client.world, stack, x, y, seed);
}
public void drawItem(ItemStack stack, int x, int y, int seed, int z) {
this.drawItem(SaturnClient.client.player, SaturnClient.client.world, stack, x, y, seed, z);
}
public void drawItemWithoutEntity(ItemStack stack, int x, int y) {
this.drawItemWithoutEntity(stack, x, y, 0);
}
public void drawItemWithoutEntity(ItemStack stack, int x, int y, int seed) {
this.drawItem((LivingEntity)null, SaturnClient.client.world, stack, x, y, seed);
}
public void drawItem(LivingEntity entity, ItemStack stack, int x, int y, int seed) {
this.drawItem(entity, entity.getWorld(), stack, x, y, seed);
}
private void drawItem(@Nullable LivingEntity entity, @Nullable World world, ItemStack stack, int x, int y, int seed) {
this.drawItem(entity, world, stack, x, y, seed, 0);
}
private void drawItem(@Nullable LivingEntity entity, @Nullable World world, ItemStack stack, int x, int y, int seed, int z) {
if (!stack.isEmpty()) {
SaturnClient.client.getItemModelManager().update(this.itemRenderState, stack, ModelTransformationMode.GUI, false, world, entity, seed);
this.matrices.push();
this.matrices.translate((float)(x + 8), (float)(y + 8), (float)(150 + (this.itemRenderState.hasDepth() ? z : 0)));
try {
this.matrices.scale(16.0F, -16.0F, 16.0F);
boolean bl = !this.itemRenderState.isSideLit();
if (bl) {
this.draw();
DiffuseLighting.disableGuiDepthLighting();
}
this.itemRenderState.render(this.matrices, this.vertexConsumers, 15728880, OverlayTexture.DEFAULT_UV);
this.draw();
if (bl) {
DiffuseLighting.enableGuiDepthLighting();
}
} catch (Throwable var11) {
CrashReport crashReport = CrashReport.create(var11, "Rendering item");
CrashReportSection crashReportSection = crashReport.addElement("Item being rendered");
crashReportSection.add("Item Type", () -> {
return String.valueOf(stack.getItem());
});
crashReportSection.add("Item Components", () -> {
return String.valueOf(stack.getComponents());
});
crashReportSection.add("Item Foil", () -> {
return String.valueOf(stack.hasGlint());
});
throw new CrashException(crashReport);
}
this.matrices.pop();
}
}
public void drawSpriteStretched(Sprite sprite, int x, int y, int width, int height) {
this.drawSpriteStretched(sprite, x, y, width, height, -1);
}
public void drawSpriteStretched(Sprite sprite, int x, int y, int width, int height, int color) {
if (width != 0 && height != 0) {
this.drawTexturedQuad(sprite.getAtlasId(), x, x + width, y, y + height, sprite.getMinU(), sprite.getMaxU(), sprite.getMinV(), sprite.getMaxV(), color);
}
} }
} }
@@ -3,37 +3,107 @@ package org.saturnclient.ui2;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.saturnclient.mixin.DrawContextAccessor; import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
import org.saturnclient.ui2.anim.Animation;
import org.saturnclient.ui2.anim.SlideXAbsolute;
import org.saturnclient.ui2.components.ElementRenderer;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gl.PostEffectProcessor;
import net.minecraft.client.gui.CubeMapRenderer;
import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.RotatingCubeMapRenderer;
import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.render.DefaultFramebufferSet;
import net.minecraft.client.util.Pool;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
public class SaturnScreen extends Screen { public abstract class SaturnScreen extends Screen {
private final Pool pool = new Pool(3);
protected List<Element> elements = new ArrayList<>(); protected List<Element> elements = new ArrayList<>();
public int blurDuration = 700;
public float blurProgress = 0.0f;
public int backgroundBlur = 10;
protected static final CubeMapRenderer PANORAMA_RENDERER = new CubeMapRenderer(
Identifier.of("saturnclient", "textures/gui/title/background/panorama"));
public static final RotatingCubeMapRenderer ROTATING_PANORAMA_RENDERER;
static {
ROTATING_PANORAMA_RENDERER = new RotatingCubeMapRenderer(PANORAMA_RENDERER);
}
public SaturnScreen(String title) { public SaturnScreen(String title) {
super(Text.literal(title)); super(Text.literal(title));
ThemeManager.load();
} }
public void draw(Element element) { public void draw(Element element) {
synchronized (elements) { synchronized (elements) {
elements.add(element); elements.add(element);
} }
if (element.animation != null) {
element.animation.init(element);
Animation.execute((Float progress) -> {
element.animation.tick(progress, element);
}, element.animation.duration);
}
if (element.duration != null) {
new Thread(() -> {
try {
Animation fadeOut = new SlideXAbsolute(700, -(width - element.x));
Thread.sleep(element.duration);
fadeOut.init(element);
Animation.executeSync((Float progress) -> {
fadeOut.tick(progress, element);
}, fadeOut.duration);
synchronized (elements) {
elements.remove(element);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
} }
@Override @Override
protected void init() { protected void init() {
elements.clear();
width *= 2;
height *= 2;
ui(); // abstraction to render the saturn ui and also render extra stuff here ui(); // abstraction to render the saturn ui and also render extra stuff here
Animation.execute((Float progress) -> {
blurProgress = progress;
}, blurDuration);
} }
public void ui() { public abstract void ui();
}
@SuppressWarnings("deprecation")
@Override @Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) { public void render(DrawContext context, int mouseX, int mouseY, float delta) {
mouseX *= 2;
mouseY *= 2;
if (client.world == null && client.getCurrentServerEntry() == null) {
ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, 1.0F, delta);
}
PostEffectProcessor postEffectProcessor = this.client.getShaderLoader().loadPostEffect(
Identifier.ofVanilla("blur"),
DefaultFramebufferSet.MAIN_ONLY);
if (postEffectProcessor != null) {
postEffectProcessor.setUniforms("Radius", backgroundBlur * blurProgress);
postEffectProcessor.render(this.client.getFramebuffer(), this.pool);
}
// We are using a Abstracted RenderScope because older minecraft versions don't // We are using a Abstracted RenderScope because older minecraft versions don't
// use DrawContext // use DrawContext
RenderScope renderScope = new RenderScope(context.getMatrices(), RenderScope renderScope = new RenderScope(context.getMatrices(),
@@ -41,32 +111,51 @@ public class SaturnScreen extends Screen {
renderScope.matrices.push(); renderScope.matrices.push();
for (Element element : elements) { renderScope.matrices.scale(0.5f, 0.5f, 0.5f);
renderScope.matrices.push();
renderScope.matrices.translate(element.x, element.y, 0); ElementRenderer.render(new ArrayList<>(elements), renderScope, mouseX, mouseY);
element.render(renderScope, new RenderContext(mouseX, mouseY, element));
renderScope.matrices.pop();
}
renderScope.matrices.pop(); renderScope.matrices.pop();
} }
@Override @Override
public boolean mouseClicked(double mouseX, double mouseY, int button) { public boolean mouseClicked(double mouseX, double mouseY, int button) {
if (button == 0) { mouseX *= 2;
for (Element element : new ArrayList<>(elements)) { mouseY *= 2;
element.focused = false;
int adjustedMouseX = (int) mouseX - element.x; ElementRenderer.mouseClicked(elements, mouseX, mouseY, button);
int adjustedMouseY = (int) mouseY - element.y;
if (Utils.isHovering(adjustedMouseX, adjustedMouseY, element.width, element.height)) {
element.focused = true;
element.click(adjustedMouseX, adjustedMouseY);
}
}
}
return false; return false;
} }
@Override
public boolean mouseScrolled(
double mouseX,
double mouseY,
double horizontalAmount,
double verticalAmount) {
mouseX *= 2;
mouseY *= 2;
ElementRenderer.mouseScrolled(elements, mouseX, mouseY, horizontalAmount, verticalAmount);
return super.mouseScrolled(
mouseX,
mouseY,
horizontalAmount,
verticalAmount);
}
@Override
public void resize(MinecraftClient client, int width, int height) {
elements.clear();
super.resize(client, width, height);
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
ElementRenderer.keyPressed(elements, keyCode, scanCode, modifiers);
return super.keyPressed(keyCode, scanCode, modifiers);
}
} }
+48 -3
View File
@@ -1,10 +1,55 @@
package org.saturnclient.ui2; package org.saturnclient.ui2;
import java.util.List;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.SaturnClient;
public class Utils { public class Utils {
public static boolean isHovering(int mouseX, int mouseY, int elementWidth, int elementHeight) { public static boolean isHovering(int mouseX, int mouseY, int elementWidth, int elementHeight, float elementScale) {
return mouseX >= 0 && return mouseX >= 0 &&
mouseX <= elementWidth && mouseX <= (elementWidth * elementScale) &&
mouseY >= 0 && mouseY >= 0 &&
mouseY <= elementHeight; mouseY <= (elementHeight * elementScale);
}
public static char getCharFromKey(int keyCode, int modifiers) {
boolean shift = (modifiers & GLFW.GLFW_MOD_SHIFT) != 0;
// A-Z
if (keyCode >= GLFW.GLFW_KEY_A && keyCode <= GLFW.GLFW_KEY_Z) {
return (char) (shift
? 'A' + (keyCode - GLFW.GLFW_KEY_A)
: 'a' + (keyCode - GLFW.GLFW_KEY_A));
}
// 0-9
if (keyCode >= GLFW.GLFW_KEY_0 && keyCode <= GLFW.GLFW_KEY_9) {
return shift
? ")!@#$%^&*(".charAt(keyCode - GLFW.GLFW_KEY_0)
: (char) ('0' + (keyCode - GLFW.GLFW_KEY_0));
}
// Special characters
return switch (keyCode) {
case GLFW.GLFW_KEY_SPACE -> ' ';
case GLFW.GLFW_KEY_PERIOD -> shift ? '>' : '.';
case GLFW.GLFW_KEY_COMMA -> shift ? '<' : ',';
case GLFW.GLFW_KEY_MINUS -> shift ? '_' : '-';
case GLFW.GLFW_KEY_EQUAL -> shift ? '+' : '=';
case GLFW.GLFW_KEY_SEMICOLON -> shift ? ':' : ';';
case GLFW.GLFW_KEY_APOSTROPHE -> shift ? '"' : '\'';
case GLFW.GLFW_KEY_SLASH -> shift ? '?' : '/';
case GLFW.GLFW_KEY_BACKSLASH -> shift ? '|' : '\\';
case GLFW.GLFW_KEY_LEFT_BRACKET -> shift ? '{' : '[';
case GLFW.GLFW_KEY_RIGHT_BRACKET -> shift ? '}' : ']';
case GLFW.GLFW_KEY_GRAVE_ACCENT -> shift ? '~' : '`';
default -> '\0';
};
}
public static <T> T getOrNull(List<T> list, int index) {
SaturnClient.LOGGER.info("Accessing index {} in list of size {}", index, list.size());
return (index >= 0 && index < list.size()) ? list.get(index) : null;
} }
} }
@@ -0,0 +1,75 @@
package org.saturnclient.ui2.anim;
import java.util.function.Consumer;
import java.util.function.Function;
import org.saturnclient.ui2.Element;
public abstract class Animation {
public int duration;
Animation(int duration) {
this.duration = duration;
}
public abstract void tick(float progress, Element element);
public abstract void init(Element element);
public static void execute(Consumer<Float> tick, int duration) {
new Thread(() -> {
Function<Double, Double> curveFunction = Curve::easeInOutCubic;
float progress = 0.0f;
double incremental = (1000 - duration) / 10000.0;
for (int i = 0; i <= 100; i++) {
float newProgress = (float) (double) curveFunction.apply(i / 100.0);
while (Math.abs(newProgress - progress) > 1e-6) {
if (progress < newProgress) {
progress += incremental;
if (progress > newProgress) progress = newProgress;
} else {
progress -= incremental;
if (progress < newProgress) progress = newProgress;
}
tick.accept(newProgress);
try {
Thread.sleep(duration / 100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
public static void executeSync(Consumer<Float> tick, int duration) {
Function<Double, Double> curveFunction = Curve::easeInOutCubic;
float progress = 0.0f;
double incremental = (1000 - duration) / 10000.0;
for (int i = 0; i <= 100; i++) {
float newProgress = (float) (double) curveFunction.apply(i / 100.0);
while (Math.abs(newProgress - progress) > 1e-6) {
if (progress < newProgress) {
progress += incremental;
if (progress > newProgress) progress = newProgress;
} else {
progress -= incremental;
if (progress < newProgress) progress = newProgress;
}
tick.accept(newProgress);
try {
Thread.sleep(duration / 100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
@@ -0,0 +1,26 @@
package org.saturnclient.ui2.anim;
public class Curve {
public static double easeOutCubic(double x) {
return 1 - Math.pow(1 - x, 3);
}
public static double easeInOutCubic(double x) {
return (x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2);
}
public static double easeInOutCubicReverse(double x) {
return -(x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2);
}
public static double easeInOutBack(double x) {
double c1 = 1.70158;
double c2 = c1 * 1.525;
if (x < 0.5) {
return (Math.pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2;
} else {
return (Math.pow(2 * x - 2, 2) * ((c2 + 1) * (2 * x - 2) + c2) + 2) / 2;
}
}
}
@@ -0,0 +1,22 @@
package org.saturnclient.ui2.anim;
import org.saturnclient.ui2.Element;
public class Fade extends Animation {
float targetOpacity;
public Fade(int duration) {
super(duration);
}
@Override
public void tick(float progress, Element element) {
element.opacity = targetOpacity * progress;
}
@Override
public void init(Element element) {
targetOpacity = element.opacity;
element.opacity = 0;
}
}
@@ -0,0 +1,22 @@
package org.saturnclient.ui2.anim;
import org.saturnclient.ui2.Element;
public class FadeOut extends Animation {
float targetOpacity;
public FadeOut(int duration) {
super(duration);
}
@Override
public void tick(float progress, Element element) {
element.opacity = targetOpacity - progress;
}
@Override
public void init(Element element) {
targetOpacity = element.opacity;
// element.opacity = 1.0f;
}
}
@@ -0,0 +1,36 @@
package org.saturnclient.ui2.anim;
import org.saturnclient.ui2.Element;
public class SlideX extends Animation {
int offset = 20;
int maxOffset = 20;
int elementX;
public SlideX(int duration) {
super(duration);
}
public SlideX(int duration, int offset) {
super(duration);
this.offset = offset;
this.maxOffset = offset;
}
public SlideX(int duration, int offset, int maxOffset) {
super(duration);
this.offset = offset;
this.maxOffset = maxOffset; // ✅ fixed
}
@Override
public void tick(float progress, Element element) {
element.x = elementX + (int) (offset * progress);
}
@Override
public void init(Element element) {
element.x -= offset;
elementX = element.x;
}
}
@@ -0,0 +1,35 @@
package org.saturnclient.ui2.anim;
import org.saturnclient.ui2.Element;
public class SlideXAbsolute extends Animation {
int offset = 20;
int maxOffset = 20;
int elementX;
public SlideXAbsolute(int duration) {
super(duration);
}
public SlideXAbsolute(int duration, int offset) {
super(duration);
this.offset = offset;
this.maxOffset = offset;
}
public SlideXAbsolute(int duration, int offset, int maxOffset) {
super(duration);
this.offset = offset;
this.maxOffset = maxOffset;
}
@Override
public void tick(float progress, Element element) {
element.x = elementX + (int) (offset * progress);
}
@Override
public void init(Element element) {
elementX = element.x;
}
}
@@ -0,0 +1,38 @@
package org.saturnclient.ui2.anim;
import org.saturnclient.ui2.Element;
public class SlideY extends Animation {
int offset = 20;
int maxOffset = 20;
int elementY;
public SlideY(int duration) {
super(duration);
}
public SlideY(int duration, int offset) {
super(duration);
this.offset = offset;
this.maxOffset = offset;
}
public SlideY(int duration, int offset, int maxOffset) {
super(duration);
this.offset = offset;
this.maxOffset = offset;
}
@Override
public void tick(float progress, Element element) {
element.y = elementY + (int) (offset * progress);
}
@Override
public void init(Element element) {
element.y -= offset;
elementY = element.y;
}
}
@@ -0,0 +1,60 @@
package org.saturnclient.ui2.components;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier;
public class CosmeticPreview extends Element {
private static ThemeManager theme = new ThemeManager("CosmeticPreview", "selected", "hovering");
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0x89000000));
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFFFFFFFF));
private static Property<Integer> padding = theme.property("padding", Property.integer(14));
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
static {}
Identifier sprite;
private Runnable onClick;
private boolean isSelected;
public CosmeticPreview(boolean isSelected, Identifier sprite, Runnable onClick) {
this.sprite = sprite;
this.onClick = onClick;
this.isSelected = isSelected;
this.width = 50;
this.height = 111;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
if (ctx.isHovering()) {
theme.setState("hovering");
} else if (isSelected) {
theme.setState("selected");
}
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
int texWidth = width - padding.value;
int texHeight = height - padding.value;
renderScope.drawTexture(sprite, padding.value / 2, padding.value / 2, 0, 0, texWidth, texHeight, fgColor.value);
theme.setState(null);
}
@Override
public void click(int mouseX, int mouseY) {
if (onClick != null) {
onClick.run();
}
}
}
@@ -0,0 +1,84 @@
package org.saturnclient.ui2.components;
import java.util.ArrayList;
import java.util.List;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.Utils;
import org.saturnclient.ui2.anim.Animation;
public class ElementRenderer {
public static void draw(List<Element> elements, Element element) {
synchronized (elements) {
elements.add(element);
}
if (element.animation != null) {
element.animation.init(element);
Animation.execute((Float progress) -> {
element.animation.tick(progress, element);
}, element.animation.duration);
}
}
public static void render(List<Element> elements, RenderScope renderScope, int mouseX, int mouseY) {
for (Element element : elements) {
renderScope.matrices.push();
renderScope.setOpacity(element.opacity);
renderScope.matrices.translate(element.x, element.y, 0);
renderScope.matrices.scale(element.scale, element.scale, 1.0f);
element.render(renderScope, new ElementContext(mouseX, mouseY, element));
renderScope.matrices.pop();
renderScope.setRenderLayer(null);
}
}
public static void mouseClicked(List<Element> elements, double mouseX, double mouseY, int button) {
if (button == 0) {
for (Element element : new ArrayList<>(elements)) {
element.focused = false;
int adjustedMouseX = (int) mouseX - element.x;
int adjustedMouseY = (int) mouseY - element.y;
if (element.opacity > 0 && Utils.isHovering(adjustedMouseX, adjustedMouseY, element.width, element.height, element.scale)) {
element.focused = true;
element.click((int) (adjustedMouseX * element.scale), (int) (adjustedMouseY * element.scale));
}
}
}
}
public static void mouseScrolled(
List<Element> elements,
double mouseX,
double mouseY,
double horizontalAmount,
double verticalAmount) {
for (Element element : new ArrayList<>(elements)) {
if (Utils.isHovering((int) mouseX - element.x, (int) mouseY - element.y, element.width, element.height, element.scale)) {
element.scroll(
(int) mouseX - element.x,
(int) mouseY - element.y,
horizontalAmount,
verticalAmount);
}
}
}
public static void keyPressed(List<Element> elements, int keyCode, int scanCode, int modifiers) {
for (Element element : new ArrayList<>(elements)) {
if (element.focused && element.opacity > 0) {
element.keyPressed(keyCode, scanCode, modifiers);
char typedChar = Utils.getCharFromKey(keyCode, modifiers);
if (typedChar != '\0') {
element.charTyped(typedChar);
}
}
}
}
}
@@ -0,0 +1,100 @@
package org.saturnclient.ui2.components;
import org.saturnclient.modules.Module;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.resources.Fonts;
import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.Utils;
import org.saturnclient.ui2.screens.ConfigEditor;
import net.minecraft.client.render.RenderLayer;
public class SaturnModule extends Element {
private static ThemeManager theme = new ThemeManager("Module", "enabled", "hovering");
private static Property<Integer> bgColor = theme.property("bg", Property.color(0xFF1d202d));
private static Property<Integer> fgColor = theme.property("fg", Property.color(0xFFA1A2B8));
private static Property<Integer> iconFg = theme.property("icon-fg", Property.color(0xFFA1A2B8));
private static Property<Integer> iconBg = theme.property("icon-bg", Property.color(0xFF2e3248));
private static Property<Integer> tagBg = theme.property("tag-bg", Property.color(0xFF2e3248));
private static Property<Integer> iconRadius = theme.property("icon-radius", Property.color(10));
private static Property<Integer> font = theme.property("font", Property.font(1));
private static Property<Integer> radius = theme.property("radius", Property.color(10));
static {
theme.propertyStateDefault("enabled", "bg", 0xFF1d202d);
theme.propertyStateDefault("enabled", "icon-bg", 0xFF845eee);
theme.propertyStateDefault("hovering", "fg", 0xFF845eee);
}
private Module mod;
public SaturnModule(Module mod) {
this.mod = mod;
this.width = 160;
this.height = 50;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
int p = 10;
int s = 14;
boolean settingsHover = false;
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
if (mod.isEnabled()) {
theme.setState("enabled");
}
if (ctx.isHovering(width - s - p - 5, height - s - p - 5, s + 10, s + 10)) {
settingsHover = true;
} else if (ctx.isHovering()) {
theme.applyState("hovering");
}
renderScope.drawRoundedRectangle(0, 0, width, height, radius.value, bgColor.value);
int h = height-(p * 2);
renderScope.drawRoundedRectangle(p, p, h, h, iconRadius.value, iconBg.value);
renderScope.drawTexture(mod.getIconTexture(), p + (p / 2), p + (p / 2), 0, 0, h - p, h - p, iconFg.value);
renderScope.drawText(0.6f, mod.getName(), p+h+4, p + 1, font.value, fgColor.value);
renderScope.drawText(0.35f, mod.getVersion(), width - p - (int) (Fonts.getWidth(mod.getVersion(), font.value) * 0.35f), p + 1, font.value, fgColor.value);
int xt = p + h + 4;
for (String tag : mod.getTags()) {
int xtb = (int) (Fonts.getWidth(tag, font.value) * 0.4f);
renderScope.drawRoundedRectangle(xt, p + (h/2), xtb + 6, 12, 5, tagBg.value);
renderScope.drawText(0.4f, tag, xt+2, p + (h/2) + 3, font.value, fgColor.value);
xt += xtb + 9;
}
if (settingsHover) {
theme.setState("hovering");
}
renderScope.drawTexture(Textures.SETTINGS, width - s - p, height - s - p, 0, 0, s, s, fgColor.value);
theme.setState(null);
}
@Override
public void click(int mouseX, int mouseY) {
int p = 10;
int s = 14;
if (Utils.isHovering(mouseX - (width - s - p - 5), mouseY - (height - s - p - 5), s + 10, s + 10, 1.0f)) {
SaturnClient.client.setScreen(new ConfigEditor(mod.getConfig()));
} else {
mod.setEnabled(!mod.isEnabled());
}
}
}
@@ -0,0 +1,147 @@
package org.saturnclient.ui2.components;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.Utils;
import org.saturnclient.ui2.screens.ConfigEditor;
import org.saturnclient.ui2.screens.ModMenu;
import org.saturnclient.ui2.screens.cosmetics.CloakMenu;
import org.saturnclient.ui2.screens.store.CloakStore;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier;
public class Sidebar extends Element {
private static ThemeManager theme = new ThemeManager("SideBar", "hovering");
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0x90000000));
private static Property<Integer> iconFg = theme.property("icon-fg", Property.color(0xFFFFFFFF));
private static Property<Integer> iconBg = theme.property("icon-bg", Property.color(0x00000000));
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
private static Property<Integer> iconRadius = theme.property("icon-radius", Property.integer(10));
private static Property<Integer> padding = theme.property("padding", Property.integer(10));
static {
theme.propertyStateDefault("hovering", "icon-bg", -7643914);
}
public static class SidebarComponent {
Runnable onClick;
boolean end;
Identifier sprite;
public SidebarComponent(Identifier sprite, Runnable onClick, boolean end) {
this.onClick = onClick;
this.end = end;
this.sprite = sprite;
}
}
Runnable onClose;
SidebarComponent[] components = {
new Sidebar.SidebarComponent(Textures.MODS_TAB, () -> {
SaturnClient.client.setScreen(new ModMenu());
}, false),
new Sidebar.SidebarComponent(Textures.SETTINGS, () -> {
SaturnClient.client.setScreen(new ConfigEditor(SaturnClientConfig.config));
}, false),
new Sidebar.SidebarComponent(Textures.SHIRT, () -> {
SaturnClient.client.setScreen(new CloakMenu());
}, false),
new Sidebar.SidebarComponent(Textures.CLOSE, () -> {
onClose.run();
}, true),
new Sidebar.SidebarComponent(Textures.STORE, () -> {
SaturnClient.client.setScreen(new CloakStore());
}, true),
};
int active = 0;
public Sidebar(int active, Runnable onClose) {
this.width = 30;
this.height = 350;
this.active = active;
this.onClose = onClose;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
int topRow = 0;
int bottomRow = 0;
int gap = 5; // 5px gap between icons
int sp = 30 - padding.value + gap;
int s = 30 - (padding.value * 2);
int s2 = s + padding.value;
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
int i = 0;
for (SidebarComponent component : components) {
if (component.end) {
int y = (height - (sp * bottomRow)) - (s * 2);
if (ctx.isHovering(0, y - (padding.value / 2), s2, s2) || active == i) {
theme.setState("hovering");
} else {
theme.setState(null);
}
renderScope.drawRoundedRectangle(padding.value / 2, y - (padding.value / 2), s + padding.value, s + padding.value, iconRadius.value, iconBg.value);
renderScope.drawTexture(component.sprite, padding.value, y, 0, 0, s, s, iconFg.value);
bottomRow += 1;
} else {
int y = padding.value + (sp * topRow);
if (ctx.isHovering(0, y - (padding.value / 2), s2, s2) || active == i) {
theme.setState("hovering");
} else {
theme.setState(null);
}
renderScope.drawRoundedRectangle(padding.value / 2, y - (padding.value / 2), s + padding.value, s + padding.value, iconRadius.value, iconBg.value);
renderScope.drawTexture(component.sprite, padding.value, y, 0, 0, s, s, iconFg.value);
topRow += 1;
}
i++;
}
}
@Override
public void click(int mouseX, int mouseY) {
int topRow = 0;
int bottomRow = 0;
int gap = 5; // same 5px gap here
int sp = 30 - padding.value + gap;
int s = 30 - (padding.value * 2);
int s2 = s + padding.value;
for (SidebarComponent component : components) {
if (component.end) {
int y = (height - (sp * bottomRow)) - (s * 2);
if (Utils.isHovering(mouseX - (padding.value / 2), mouseY - (y - (padding.value / 2)), s2, s2, scale)) {
component.onClick.run();
break;
}
bottomRow += 1;
} else {
int y = padding.value + (sp * topRow);
if (Utils.isHovering(mouseX - (padding.value / 2), mouseY - (y - (padding.value / 2)), s2, s2, scale)) {
component.onClick.run();
break;
}
topRow += 1;
}
}
}
}
@@ -1,23 +1,31 @@
package org.saturnclient.ui.components; package org.saturnclient.ui2.components;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.joml.Quaternionf; import org.joml.Quaternionf;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.saturnclient.ui.SaturnWidget;
import org.saturnclient.saturnclient.SaturnClient; import org.saturnclient.saturnclient.SaturnClient;
import net.minecraft.client.gui.DrawContext; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import net.minecraft.client.render.DiffuseLighting; import net.minecraft.client.render.DiffuseLighting;
import net.minecraft.client.render.entity.EntityRenderDispatcher; import net.minecraft.client.render.entity.EntityRenderDispatcher;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
// If you think this code is bad, it's because it's written by Mojang public class SkinPreview extends Element {
private boolean negativeAngle = false;
private float angle = 0.0f;
public class SkinPreview extends SaturnWidget { public SkinPreview(float angle, boolean negativeAngle) {
public static void drawEntity(DrawContext context, int x1, int y1, int x2, int y2, int size, float f, float mouseX, this.angle = angle;
this.negativeAngle = negativeAngle;
}
public static void drawEntity(RenderScope renderScope, int x1, int y1, int x2, int y2, int size, float f, float mouseX,
float mouseY, LivingEntity entity, float angle, boolean negativeAngle) { float mouseY, LivingEntity entity, float angle, boolean negativeAngle) {
float g = (float) (x1 + x2) / 2.0F; float g = (float) (x1 + x2) / 2.0F;
float h = (float) (y1 + y2) / 2.0F; float h = (float) (y1 + y2) / 2.0F;
context.enableScissor(0, 6, x2, y2); renderScope.enableScissor(0, 6, x2, y2);
float i = (float) Math.atan((double) ((g - mouseX) / 40.0F)); float i = (float) Math.atan((double) ((g - mouseX) / 40.0F));
float j = (float) Math.atan((double) ((h - mouseY) / 40.0F)); float j = (float) Math.atan((double) ((h - mouseY) / 40.0F));
Quaternionf quaternionf = (new Quaternionf()).rotateZ(3.1415927F); Quaternionf quaternionf = (new Quaternionf()).rotateZ(3.1415927F);
@@ -28,15 +36,15 @@ public class SkinPreview extends SaturnWidget {
float o = entity.headYaw; float o = entity.headYaw;
entity.setYaw(angle); entity.setYaw(angle);
entity.bodyYaw = angle; entity.bodyYaw = angle;
entity.setPitch(-j * 20.0F); entity.setPitch(-(j * 20.0F));
entity.headYaw = negativeAngle ? -i * 40.0F : i * 40.0F; entity.headYaw = negativeAngle ? (i * 40.0F) + 180f : -(i * 40.0F);
entity.prevHeadYaw = entity.getHeadYaw(); entity.prevHeadYaw = entity.getHeadYaw();
entity.setCustomNameVisible(false); entity.setCustomNameVisible(false);
float p = entity.getScale(); float p = entity.getScale();
Vector3f vector3f = new Vector3f(0.0F, entity.getHeight() / 2.0F + f * p, 0.0F); Vector3f vector3f = new Vector3f(0.0F, entity.getHeight() / 2.0F + f * p, 0.0F);
float q = (float) size / p; float q = (float) size / p;
drawEntity(context, g, h, q, vector3f, quaternionf, null, entity); drawEntity(renderScope, g, h, q, vector3f, quaternionf, null, entity);
entity.setCustomNameVisible(true); entity.setCustomNameVisible(true);
entity.bodyYaw = k; entity.bodyYaw = k;
@@ -44,36 +52,36 @@ public class SkinPreview extends SaturnWidget {
entity.setPitch(m); entity.setPitch(m);
entity.prevHeadYaw = n; entity.prevHeadYaw = n;
entity.headYaw = o; entity.headYaw = o;
context.disableScissor(); renderScope.disableScissor();
} }
public static void drawEntity(DrawContext context, float x, float y, float size, Vector3f vector3f, public static void drawEntity(RenderScope renderScope, float x, float y, float size, Vector3f vector3f,
Quaternionf quaternionf, @Nullable Quaternionf quaternionf2, LivingEntity entity) { Quaternionf quaternionf, @Nullable Quaternionf quaternionf2, LivingEntity entity) {
context.getMatrices().push(); renderScope.matrices.push();
context.getMatrices().translate((double) x, (double) y, 50.0); renderScope.matrices.translate((double) x, (double) y, 50.0);
context.getMatrices().scale(size, size, -size); renderScope.matrices.scale(size, size, -size);
context.getMatrices().translate(vector3f.x, vector3f.y, vector3f.z); renderScope.matrices.translate(vector3f.x, vector3f.y, vector3f.z);
context.getMatrices().multiply(quaternionf); renderScope.matrices.multiply(quaternionf);
context.draw(); renderScope.draw();
DiffuseLighting.method_34742(); DiffuseLighting.method_34742();
EntityRenderDispatcher entityRenderDispatcher = SaturnClient.client.getEntityRenderDispatcher(); EntityRenderDispatcher entityRenderDispatcher = SaturnClient.client.getEntityRenderDispatcher();
entityRenderDispatcher.setRenderShadows(false); entityRenderDispatcher.setRenderShadows(false);
context.draw((vertexConsumers) -> { renderScope.draw((vertexConsumers) -> {
entityRenderDispatcher.render(entity, 0.0, 0.0, 0.0, 1.0F, context.getMatrices(), vertexConsumers, entityRenderDispatcher.render(entity, 0.0, 0.0, 0.0, 1.0F, renderScope.matrices, vertexConsumers,
15728880); 15728880);
}); });
context.draw(); renderScope.draw();
entityRenderDispatcher.setRenderShadows(true); entityRenderDispatcher.setRenderShadows(true);
context.getMatrices().pop(); renderScope.matrices.pop();
DiffuseLighting.enableGuiDepthLighting(); DiffuseLighting.enableGuiDepthLighting();
} }
@Override @Override
public void render(DrawContext context, boolean hovering, int mouseX, int mouseY) { public void render(RenderScope renderScope, ElementContext ctx) {
LivingEntity entity = SaturnClient.client.player; LivingEntity entity = SaturnClient.client.player;
if (entity != null) { if (entity != null) {
drawEntity(context, 0, 0, 75, 78, 30, 0.0625F, mouseX, mouseY, entity, -30.0f, true); drawEntity(renderScope, 0, 0, 75, 78, 30, 0.0625F, ctx.mouseX / scale, ctx.mouseY / scale, entity, angle, negativeAngle);
} }
} }
} }
@@ -0,0 +1,246 @@
package org.saturnclient.ui2.components.dialog;
import java.awt.Color;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.SaturnScreen;
import net.minecraft.client.gui.DrawContext;
public class ColorDialog extends SaturnScreen {
private Property<Integer> prop;
private SaturnScreen parent;
private static final int PICKER_WIDTH = 200;
private static final int PICKER_HEIGHT = 200;
private static final int SLIDER_WIDTH = 200;
private static final int SLIDER_HEIGHT = 10;
private static final int HUE_SLIDER_HEIGHT = 10;
private float hue = 0.0f;
private int selectedX = 0;
private int selectedY = 0;
private float opacity = 1.0f;
private boolean dragging = false;
public ColorDialog(Property<Integer> prop , SaturnScreen parent) {
super("Color Dialog");
this.prop = prop;
this.parent = parent;
int alpha = (prop.value >> 24) & 0xFF;
if (alpha == 0) alpha = 255; // fallback if no alpha specified
this.opacity = alpha / 255.0f;
syncFromColor();
}
@Override
public void ui() {}
@Override
public void close() {
SaturnClient.client.setScreen(parent);
}
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta);
RenderScope renderScope = new RenderScope(context.getMatrices(),
((DrawContextAccessor) context).getVertexConsumers());
renderScope.matrices.push();
renderScope.matrices.scale(0.5f, 0.5f, 0.5f);
int xStart = (width - PICKER_WIDTH) / 2;
int yStart = (height - PICKER_HEIGHT) / 2;
// Draw the HSV saturation/value square (with current hue)
for (int x = 0; x < PICKER_WIDTH; x++) {
for (int y = 0; y < PICKER_HEIGHT; y++) {
float saturation = x / (float) PICKER_WIDTH;
float value = 1.0f - y / (float) PICKER_HEIGHT;
int color = Color.HSBtoRGB(hue, saturation, value);
context.fill(xStart + x, yStart + y, xStart + x + 1, yStart + y + 1, 0xFF000000 | color);
}
}
int x = (xStart + selectedX) - 4;
int y = (yStart + selectedY) - 4;
context.fill(x, y, x+8, y+8, 0xFF000000 | prop.value);
context.drawBorder(x, y, 8, 8, 0xFFffffff);
renderSlider(context, mouseX, mouseY, delta);
renderHueSlider(context);
renderScope.matrices.pop();
}
public void renderSlider(DrawContext context, int mouseX, int mouseY, float delta) {
int xStart = (width - SLIDER_WIDTH) / 2;
int yStart = (height - SLIDER_HEIGHT - PICKER_HEIGHT) / 2 - 10;
// Draw slider background (left = transparent, right = white)
for (int x = 0; x < SLIDER_WIDTH; x++) {
float localOpacity = x / (float) SLIDER_WIDTH;
int alpha = (int)(localOpacity * 255) & 0xFF;
int color = (alpha << 24) | (prop.value & 0xFFFFFF);
context.fill(xStart + x, yStart, xStart + x + 1, yStart + SLIDER_HEIGHT, color);
}
// Draw current value as a marker (thin black line)
int knobX = xStart + (int)(opacity * SLIDER_WIDTH);
context.fill(knobX - 1, yStart - 2, knobX + 1, yStart + SLIDER_HEIGHT + 2, 0xFF000000);
// Draw opacity text
// context.drawText(textRenderer, String.format("Opacity: %.2f", opacity), xStart, yStart - 15, 0xFFFFFF, false);
}
private void renderHueSlider(DrawContext context) {
int xStart = (width - SLIDER_WIDTH) / 2;
int yStart = (height + PICKER_HEIGHT) / 2 + 10;
for (int x = 0; x < SLIDER_WIDTH; x++) {
float localHue = x / (float) SLIDER_WIDTH;
int color = Color.HSBtoRGB(localHue, 1.0f, 1.0f);
context.fill(xStart + x, yStart, xStart + x + 1, yStart + HUE_SLIDER_HEIGHT, 0xFF000000 | color);
}
int knobX = xStart + (int)(hue * SLIDER_WIDTH);
context.fill(knobX - 1, yStart - 2, knobX + 1, yStart + HUE_SLIDER_HEIGHT + 2, 0xFF000000);
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
mouseX *= 2;
mouseY *= 2;
int xStart = (width - PICKER_WIDTH) / 2;
int yStart = (height - PICKER_HEIGHT) / 2;
int relX = (int) mouseX - xStart;
int relY = (int) mouseY - yStart;
if (relX >= 0 && relX < PICKER_WIDTH && relY >= 0 && relY < PICKER_HEIGHT) {
float saturation = relX / (float) PICKER_WIDTH;
float value = 1.0f - relY / (float) PICKER_HEIGHT;
prop.value = Color.HSBtoRGB(hue, saturation, value);
int alpha = (int)(opacity * 255) & 0xFF;
prop.value = (alpha << 24) | (prop.value & 0xFFFFFF);
selectedX = relX;
selectedY = relY;
return true;
} else if (isInSlider(mouseX, mouseY)) {
updateOpacity(mouseX);
dragging = true;
return true;
} else if (isInHueSlider(mouseX, mouseY)) {
updateHue(mouseX);
return true;
}
return super.mouseClicked(mouseX, mouseY, button);
}
@Override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
dragging = false;
return super.mouseReleased(mouseX, mouseY, button);
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
mouseX *= 2;
mouseY *= 2;
if (isInHueSlider(mouseX, mouseY)) {
updateHue(mouseX);
return true;
} else if (dragging) {
updateOpacity(mouseX);
return true;
} else {
int xStart = (width - PICKER_WIDTH) / 2;
int yStart = (height - PICKER_HEIGHT) / 2;
int relX = (int) mouseX - xStart;
int relY = (int) mouseY - yStart;
if (relX >= 0 && relX < PICKER_WIDTH && relY >= 0 && relY < PICKER_HEIGHT) {
float saturation = relX / (float) PICKER_WIDTH;
float value = 1.0f - relY / (float) PICKER_HEIGHT;
prop.value = Color.HSBtoRGB(hue, saturation, value);
int alpha = (int)(opacity * 255) & 0xFF;
prop.value = (alpha << 24) | (prop.value & 0xFFFFFF);
selectedX = relX;
selectedY = relY;
return true;
}
}
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
}
private boolean isInSlider(double mouseX, double mouseY) {
int xStart = (width - SLIDER_WIDTH) / 2;
int yStart = (height - SLIDER_HEIGHT - PICKER_HEIGHT) / 2 - 10;
return mouseX >= xStart && mouseX <= xStart + SLIDER_WIDTH &&
mouseY >= yStart && mouseY <= yStart + SLIDER_HEIGHT;
}
private boolean isInHueSlider(double mouseX, double mouseY) {
int xStart = (width - SLIDER_WIDTH) / 2;
int yStart = (height + PICKER_HEIGHT) / 2 + 10;
return mouseX >= xStart && mouseX <= xStart + SLIDER_WIDTH &&
mouseY >= yStart && mouseY <= yStart + HUE_SLIDER_HEIGHT;
}
private void updateOpacity(double mouseX) {
int xStart = (width - SLIDER_WIDTH) / 2;
float newOpacity = (float)((mouseX - xStart) / SLIDER_WIDTH);
opacity = Math.max(0.0f, Math.min(1.0f, newOpacity));
int alpha = (int)(opacity * 255) & 0xFF;
prop.value = (alpha << 24) | (prop.value & 0xFFFFFF);
}
private void updateHue(double mouseX) {
int xStart = (width - SLIDER_WIDTH) / 2;
float newHue = (float)((mouseX - xStart) / SLIDER_WIDTH);
hue = Math.max(0.0f, Math.min(1.0f, newHue));
float saturation = selectedX / (float) PICKER_WIDTH;
float value = 1.0f - selectedY / (float) PICKER_HEIGHT;
prop.value = Color.HSBtoRGB(hue, saturation, value);
int alpha = (int)(opacity * 255) & 0xFF;
prop.value = (alpha << 24) | (prop.value & 0xFFFFFF);
}
private void syncFromColor() {
int rgb = prop.value & 0xFFFFFF;
float[] hsb = Color.RGBtoHSB((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF, null);
this.hue = hsb[0];
float saturation = hsb[1];
float value = hsb[2];
this.selectedX = (int)(saturation * PICKER_WIDTH);
this.selectedY = (int)((1.0f - value) * PICKER_HEIGHT);
// Extract alpha and convert to opacity float
int alpha = (prop.value >> 24) & 0xFF;
if (alpha == 0) alpha = 255;
this.opacity = alpha / 255.0f;
}
}
@@ -0,0 +1,59 @@
package org.saturnclient.ui2.components.inputs;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui2.resources.Fonts;
public class FloatInput extends Input {
public Property<Float> prop;
public FloatInput(Property<Float> prop) {
this.width = 120;
this.height = Fonts.getHeight();
this.prop = prop;
this.text = String.valueOf(prop.value);
}
@Override
public void charTyped(char chr) {
if ((Character.isDigit(chr) || chr == '.' || (chr == '-' && cursorPosition == 0)) && text.length() < 10) {
if (text.equals("0") && cursorPosition == 1) {
text = "" + chr;
} else {
text = text.substring(0, cursorPosition) +
chr +
text.substring(cursorPosition);
try {
prop.value = text.isEmpty() ? 0 : Float.parseFloat(text);
cursorPosition++;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@Override
public void backspace() {
try {
if (text.length() == 0) {
text = "0";
cursorPosition = 1;
}
prop.value = Float.parseFloat(text);
ConfigManager.save();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void checkReset() {
if (prop.isReset) {
this.text = String.valueOf(prop.value);
this.cursorPosition = 0;
prop.isReset = false;
}
}
}
@@ -0,0 +1,32 @@
package org.saturnclient.ui2.components.inputs;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.components.dialog.ColorDialog;
public class HexInput extends Element {
public Property<Integer> prop;
public SaturnScreen screen;
public HexInput(Property<Integer> prop, SaturnScreen screen) {
this.width = 58;
this.height = 20;
this.prop = prop;
this.scale = 0.8f;
this.screen = screen;
}
@Override
public void click(int mouseX, int mouseY) {
SaturnClient.client.setScreen(new ColorDialog(prop, screen));
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, 17, prop.value);
}
}
@@ -0,0 +1,113 @@
package org.saturnclient.ui2.components.inputs;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
public abstract class Input extends Element {
private static ThemeManager theme = new ThemeManager("Input");
private static Property<Integer> font = theme.property("font", Property.font(1));
String text = "";
public int cursorPosition = 0;
public abstract void checkReset();
public abstract void backspace();
public boolean validateBackspace() {
return true;
}
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
if (keyCode == GLFW.GLFW_KEY_BACKSPACE) {
if (cursorPosition > 0 && validateBackspace()) {
text = text.substring(0, cursorPosition - 1) +
text.substring(cursorPosition);
cursorPosition--;
backspace();
}
} else if (keyCode == GLFW.GLFW_KEY_LEFT) {
if (cursorPosition > 0) {
cursorPosition--;
}
} else if (keyCode == GLFW.GLFW_KEY_RIGHT) {
if (cursorPosition < text.length()) {
cursorPosition++;
}
}
}
@Override
public void click(int mouseX, int mouseY) {
int scrollOffset = getScrollOffset();
String visibleText = text.substring(scrollOffset);
int newCursorPos = scrollOffset;
float textWidth = 0;
for (int i = 0; i < visibleText.length(); i++) {
textWidth = SaturnClient.client.textRenderer.getWidth(visibleText.substring(0, i + 1)) * 0.6f;
if (textWidth > mouseX) {
break;
}
newCursorPos++;
}
cursorPosition = newCursorPos;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
checkReset();
renderScope.drawRoundedRectangle(0, 0, width, height, 10, 0xFF000000);
int scrollOffset = getScrollOffset();
String visibleText = getVisibleText(scrollOffset);
int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
renderScope.drawText(0.6f, visibleText, 4, 4, font.value, textColor);
if (focused) {
int cursorX = 4 +
(int) (Fonts.getWidth(visibleText.substring(0, cursorPosition - scrollOffset), font.value) * 0.6f);
renderScope.drawRect(cursorX, 2, 1, height - 4, 0xFFFFFFFF);
}
}
protected int getScrollOffset() {
if (text.isEmpty()) {
return 0;
}
int offset = 0;
int maxWidth = width - 4; // Available width for text
while (offset < cursorPosition) { // Ensure cursor remains visible
String visibleText = text.substring(offset, cursorPosition);
if (Fonts.getWidth(visibleText, font.value) > maxWidth) {
offset++;
} else {
break;
}
}
return offset;
}
protected String getVisibleText(int scrollOffset) {
String visibleText = text.substring(scrollOffset);
// Ensure text does not overflow past the available width
for (int i = 1; i <= visibleText.length(); i++) {
if (Fonts.getWidth(visibleText.substring(0, i), font.value) > width - 4) {
return visibleText.substring(0, i - 1); // Trim overflow
}
}
return visibleText;
}
}
@@ -0,0 +1,59 @@
package org.saturnclient.ui2.components.inputs;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui2.resources.Fonts;
public class IntInput extends Input {
public Property<Integer> prop;
public IntInput(Property<Integer> prop) {
this.width = 120;
this.height = Fonts.getHeight();
this.prop = prop;
this.text = String.valueOf(prop.value);
}
@Override
public void charTyped(char chr) {
if ((Character.isDigit(chr) || chr == '.' || (chr == '-' && cursorPosition == 0)) && text.length() < 10) {
if (text.equals("0") && cursorPosition == 1) {
text = "" + chr;
} else {
text = text.substring(0, cursorPosition) +
chr +
text.substring(cursorPosition);
try {
prop.value = text.isEmpty() ? 0 : Integer.parseInt(text);
cursorPosition++;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@Override
public void backspace() {
try {
if (text.length() == 0) {
text = "0";
cursorPosition = 1;
}
prop.value = Integer.parseInt(text);
ConfigManager.save();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void checkReset() {
if (prop.isReset) {
this.text = String.valueOf(prop.value);
this.cursorPosition = 0;
prop.isReset = false;
}
}
}
@@ -0,0 +1,40 @@
package org.saturnclient.ui2.components.inputs;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
public class KeybindingSelector extends Element {
private static ThemeManager theme = new ThemeManager("Input");
private static Property<Integer> font = theme.property("font", Property.font(1));
Property<Integer> prop;
public KeybindingSelector(Property<Integer> prop) {
this.prop = prop;
this.width = 120;
this.height = Fonts.getHeight();
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
renderScope.drawRoundedRectangle(0, 0, width, height, 10, 0xFF000000);
renderScope.drawText(0.6f, prop.value == -1 ? "<NONE>" :
GLFW.glfwGetKeyName(prop.value, GLFW.glfwGetKeyScancode(prop.value)).toUpperCase(),
4, 4, font.value, textColor);
}
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
if (keyCode == GLFW.GLFW_KEY_BACKSPACE) {
prop.value = -1;
} else {
prop.value = keyCode;
}
focused = false;
}
}
@@ -0,0 +1,49 @@
package org.saturnclient.ui2.components.inputs;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.Utils;
import org.saturnclient.ui2.resources.Fonts;
import net.minecraft.client.render.RenderLayer;
public class Select extends Element {
private static ThemeManager theme = new ThemeManager("Toggle");
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xAAAAAA));
private static Property<Integer> arrowColor = theme.property("arrow-color", Property.color(0xff8B5CF6));
private static Property<Integer> font = theme.property("font", Property.font(1));
private Property<Integer> prop;
public Select(Property<Integer> prop) {
this.prop = prop;
this.width = 150;
this.height = 15;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
renderScope.drawText(0.8f, "<", 0, 0, font.value, arrowColor.value);
String text = prop.getSelection();
renderScope.drawText(0.6f, text, (int) (width - ((float) Fonts.getWidth(text, font.value) * 0.6f)) / 2, 1, font.value, fgColor.value);
renderScope.drawText(0.8f, ">", width - 10, 0, font.value, arrowColor.value);
theme.setState(null);
}
@Override
public void click(int mouseX, int mouseY) {
if (Utils.isHovering(mouseX - (width - 10), mouseY, 10, height, 1.0f)) {
prop.next();
} else if (Utils.isHovering(mouseX, mouseY, 10, height, 1.0f)) {
prop.prev();
}
}
}
@@ -0,0 +1,49 @@
package org.saturnclient.ui2.components.inputs;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
public class Toggle extends Element {
private static ThemeManager theme = new ThemeManager("Toggle", "hovering", "enabled");
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0xFF808080));
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFF909090));
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(30));
static {
theme.propertyStateDefault("enabled", "bg-color", 0xaf8B5CF6);
theme.propertyStateDefault("enabled", "fg-color", 0xff8B5CF6);
}
private Property<Boolean> prop;
public Toggle(Property<Boolean> prop) {
this.prop = prop;
this.width = 60;
this.height = 30;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
if (prop.value) {
theme.setState("enabled");
}
if (ctx.isHovering()) {
theme.applyState("hovering");
}
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
renderScope.drawRoundedRectangle(prop.value ? 30 : 0, 0, 30, 30, 30, fgColor.value);
theme.setState(null);
}
@Override
public void click(int mouseX, int mouseY) {
prop.value = !prop.value;
}
}
@@ -3,43 +3,41 @@ package org.saturnclient.ui2.elements;
import org.saturnclient.saturnclient.config.Property; import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager; import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.RenderContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts; import org.saturnclient.ui2.resources.Fonts;
import net.minecraft.util.Identifier;
public class Button extends Element { public class Button extends Element {
private static ThemeManager theme = new ThemeManager("Button", "hovering"); private static ThemeManager theme = new ThemeManager("Button", "hovering");
private static Property<Integer> bgColor = theme.property("bg-color", new Property<Integer>(0xFF000000)); private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0xFF000000));
private static Property<Integer> fgColor = theme.property("fg-color", new Property<Integer>(0xFFFFFFFF)); private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFFFFFFFF));
private static Property<Boolean> bold = theme.property("fg-bold", new Property<Boolean>(false)); private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
private static Property<Integer> font = theme.property("font", Property.font(1));
static { static {
theme.propertyStateDefault("hovering", "fg-color", 0xFFFF0000); theme.propertyStateDefault("hovering", "fg-color", 0xFF845eee);
} }
private String text; private String text;
private Identifier font;
private Runnable onClick; private Runnable onClick;
public Button(String text, Runnable onClick) { public Button(String text, Runnable onClick) {
this.text = text; this.text = text;
this.font = Fonts.getFont(bold.value);
this.onClick = onClick; this.onClick = onClick;
this.dimensions(Fonts.getWidth(text, font)+ 25 , Fonts.getHeight() + 10); this.dimensions(Fonts.getWidth(text, font.value) + 50, 38);
} }
@Override @Override
public void render(RenderScope renderScope, RenderContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
if (ctx.isHovering()) { if (ctx.isHovering()) {
theme.setState("hovering"); theme.setState("hovering");
} else { } else {
theme.setState(null); theme.setState(null);
} }
renderScope.drawRoundedRectangle(0, 0, width, height, 10, bgColor.value); renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
renderScope.drawText(text, width / 2 - Fonts.getWidth(text, font) / 2, height / 2 - Fonts.getHeight() / 2, bold.value, fgColor.value); // renderScope.drawText(text, (width - Fonts.getWidth(text, font)) / 2, (height - Fonts.getHeight()) / 2, bold.value, fgColor.value);
renderScope.drawText(text, Fonts.centerX(width, text, font.value), Fonts.centerY(height), font.value, fgColor.value);
} }
@Override @Override
@@ -0,0 +1,42 @@
package org.saturnclient.ui2.elements;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier;
public class ClickableTexture extends Element {
private static ThemeManager theme = new ThemeManager("ClickableTexture", "hovering");
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFFFFFFFF));
Identifier sprite;
private Runnable onClick;
public ClickableTexture(Identifier sprite, Runnable onClick) {
this.sprite = sprite;
this.onClick = onClick;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
if (ctx.isHovering()) {
theme.setState("hovering");
} else {
theme.setState(null);
}
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
renderScope.drawTexture(sprite, 0, 0, 0, 0, width, height, fgColor.value);
}
@Override
public void click(int mouseX, int mouseY) {
if (onClick != null) {
onClick.run();
}
}
}
@@ -0,0 +1,22 @@
package org.saturnclient.ui2.elements;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier;
public class ImageTexture extends Element {
Identifier sprite;
public ImageTexture(Identifier sprite) {
this.sprite = sprite;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
renderScope.drawTexture(sprite, 0, 0, 0, 0, width, height);
}
}
@@ -0,0 +1,60 @@
package org.saturnclient.ui2.elements;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.anim.SlideX;
import org.saturnclient.ui2.resources.Fonts;
import org.saturnclient.ui2.resources.Textures;
public class Notification extends Element {
private static ThemeManager theme = new ThemeManager("Notification");
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0xFF000000));
private static Property<Integer> error = theme.property("error", Property.color(0xFFbf212f));
private static Property<Integer> info = theme.property("info", Property.color(0xFF264b96));
private static Property<Integer> success = theme.property("success", Property.color(0xFF27b376));
public static enum NotificationKind {
Error,
Info,
Success,
}
NotificationKind kind;
String text;
String title;
public Notification(int width, int height, NotificationKind kind, String title, String text) {
this.kind = kind;
this.text = text;
this.title = title;
this.width = (int) Math.max(Fonts.getWidth(text, 1) * 0.6f, Fonts.getWidth(title, 1)) + 20;
this.height = 50;
this.x = width - this.width - 15;
this.y = height - this.height - 15;
this.animation = new SlideX(700, -width);
this.duration = 3000;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, 10, bgColor.value);
switch (kind) {
case Error:
renderScope.drawTexture(Textures.CIRCLE_X, 9, 9, 0, 0, 15, 15, error.value);
break;
case Info:
renderScope.drawTexture(Textures.INFO, 9, 9, 0, 0, 15, 15, info.value);
break;
case Success:
renderScope.drawTexture(Textures.CHECK, 9, 9, 0, 0, 15, 15, success.value);
break;
}
renderScope.drawText(0.7f, title, 32, 10, 1, -1);
renderScope.drawText(0.5f, text, 32, 28, 1, -1);
}
}
@@ -0,0 +1,89 @@
package org.saturnclient.ui2.elements;
import java.util.ArrayList;
import java.util.List;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.components.ElementRenderer;
public class Scroll extends Element {
private static ThemeManager theme = new ThemeManager("Scroll");
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0x90000000));
private static Property<Integer> scrollBarColor = theme.property("scrollbar-color", Property.color(-7643914));
private static Property<Integer> scrollBarRadius = theme.property("scrollbar-radius", Property.integer(10));
private static Property<Integer> scrollBarWidth = theme.property("scrollbar-width", Property.integer(5));
private static Property<Integer> scrollBarPadding = theme.property("scrollbar-padding", Property.integer(5));
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
int padding = 0;
protected List<Element> children = new ArrayList<>();
int scroll = 0;
int maxScroll = 0;
public Scroll(int padding) {
this.padding = padding;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
renderScope.enableScissor(padding, padding, width - padding, height - padding);
renderScope.matrices.push();
renderScope.matrices.translate(padding, (-scroll) + padding, 0);
ElementRenderer.render(children, renderScope, ctx.mouseX - padding, ctx.mouseY - padding + scroll);
renderScope.matrices.pop();
renderScope.disableScissor();
renderScope.drawRoundedRectangle(width - scrollBarWidth.value - scrollBarPadding.value, calculateScrollBarY(), scrollBarWidth.value, calculateScrollBarHeight(), scrollBarRadius.value, scrollBarColor.value);
}
@Override
public void scroll(int mouseX, int mouseY, double horizontalAmount, double verticalAmount) {
scroll -= verticalAmount;
if (scroll < 0) {
scroll = 0;
} else if (scroll > maxScroll) {
scroll = maxScroll;
}
}
@Override
public void click(int mouseX, int mouseY) {
ElementRenderer.mouseClicked(children, mouseX - padding, mouseY - padding + scroll, 0);
}
public void draw(Element element) {
children.add(element);
}
@Override
public Element dimensions(int width, int height) {
for (Element element : children) {
maxScroll = Math.max(maxScroll, element.y + element.height - height);
}
return super.dimensions(width, height);
}
int calculateScrollBarHeight() {
if (maxScroll <= 0) return height - (scrollBarPadding.value * 2); // No scrolling needed
return Math.max(20, (height * height) / (height + maxScroll)) - (scrollBarPadding.value * 2); // At least 20px tall
}
int calculateScrollBarY() {
int scrollBarHeight = calculateScrollBarHeight();
if (maxScroll <= 0) return scrollBarPadding.value;
return (scroll * (height - scrollBarHeight)) / maxScroll + scrollBarPadding.value;
}
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
ElementRenderer.keyPressed(children, keyCode, scanCode, modifiers);
}
}
@@ -0,0 +1,73 @@
package org.saturnclient.ui2.elements;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.Utils;
import net.minecraft.util.Identifier;
public class TabMenu extends Element {
private static ThemeManager theme = new ThemeManager("TabMenu", "hovering", "selected");
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0x90000000));
private static Property<Integer> iconFg = theme.property("icon-fg", Property.color(0xFFFFFFFF));
private static Property<Integer> iconBg = theme.property("icon-bg", Property.color(0x00000000));
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
static {
theme.propertyStateDefault("hovering", "icon-bg", -7643914);
}
public static class TabMenuComponent {
Runnable onClick;
Identifier sprite;
public TabMenuComponent(Identifier sprite, Runnable onClick) {
this.onClick = onClick;
this.sprite = sprite;
}
}
TabMenuComponent[] components;
public int selectedScreen = 0;
public TabMenu(int active, TabMenuComponent... components) {
this.components = components;
width = 25 * components.length + 6;
height = 30;
this.selectedScreen = active;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
int col = 0;
for (TabMenuComponent component : components) {
int x = 25 * col + 5;
if (ctx.isHovering(x, 5, 20, 20) || selectedScreen == col) {
theme.setState("hovering");
}
renderScope.drawRoundedRectangle(x, 5, 20, 20, cornerRadius.value, iconBg.value);
renderScope.drawTexture(component.sprite, x + 5, 10, 0, 0, 10, 10, iconFg.value);
theme.setState(null);
col++;
}
}
@Override
public void click(int mouseX, int mouseY) {
int col = 0;
for (TabMenuComponent component : components) {
int x = 25 * col + 5;
if (Utils.isHovering(mouseX - x, mouseY - 5, 20, 20, 1.0f)) {
component.onClick.run();
}
col++;
}
}
}
@@ -1,18 +1,27 @@
package org.saturnclient.ui2.elements; package org.saturnclient.ui2.elements;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element; import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.RenderContext; import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope; import org.saturnclient.ui2.RenderScope;
import org.saturnclient.ui2.resources.Fonts;
public class Text extends Element { public class Text extends Element {
private static ThemeManager theme = new ThemeManager("Text");
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFFFFFFFF));
public static Property<Integer> font = theme.property("font", Property.font(1));
private String text; private String text;
public Text(String text) { public Text(String text) {
this.text = text; this.text = text;
width = Fonts.getWidth(text, font.value);
height = Fonts.getHeight();
} }
@Override @Override
public void render(RenderScope renderScope, RenderContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawText(this.text, this.x, this.y, false, 0xFFFFFFFF); renderScope.drawText(this.text, 0, 0, font.value, fgColor.value);
} }
} }
@@ -0,0 +1,56 @@
package org.saturnclient.ui2.elements;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnclient.config.ThemeManager;
import org.saturnclient.ui2.Element;
import org.saturnclient.ui2.ElementContext;
import org.saturnclient.ui2.RenderScope;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.Identifier;
public class TextureButton extends Element {
private static ThemeManager theme = new ThemeManager("TextureButton", "hovering");
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0xFF000000));
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFFFFFFFF));
private static Property<Integer> padding = theme.property("padding", Property.integer(14));
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
static {
theme.propertyStateDefault("hovering", "fg-color", 0xFF845eee);
}
Identifier sprite;
private Runnable onClick;
public TextureButton(Identifier sprite, Runnable onClick) {
this.sprite = sprite;
this.onClick = onClick;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
if (ctx.isHovering()) {
theme.setState("hovering");
} else {
theme.setState(null);
}
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
int texWidth = width - padding.value;
int texHeight = height - padding.value;
renderScope.drawTexture(sprite, padding.value / 2, padding.value / 2, 0, 0, texWidth, texHeight, fgColor.value);
}
@Override
public void click(int mouseX, int mouseY) {
if (onClick != null) {
onClick.run();
}
}
}
@@ -6,14 +6,18 @@ import net.minecraft.text.Text;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
public class Fonts { public class Fonts {
public static final Identifier PANTON = Identifier.of("saturnclient", "panton"); public static final Identifier INTER = Identifier.of("saturnclient", "inter");
public static final Identifier PANTON_BOLD = Identifier.of("saturnclient", "panton_bold"); public static final Identifier INTER_BOLD = Identifier.of("saturnclient", "inter_bold");
public static final Identifier DEFAULT = Identifier.ofVanilla("default");
public static Identifier getFont(boolean bold) { public static Identifier getFont(int font) {
if (bold) { switch (font) {
return PANTON_BOLD; case 0:
} else { return DEFAULT;
return PANTON; case 1:
return INTER;
default:
return INTER_BOLD;
} }
} }
@@ -22,12 +26,31 @@ public class Fonts {
Style.EMPTY.withFont(font)); Style.EMPTY.withFont(font));
} }
public static int getWidth(String text, Identifier font) { public static int getWidth(String text, int font) {
return SaturnClient.client.textRenderer.getWidth(Text.literal(text).setStyle( int w = 0;
Style.EMPTY.withFont(font))); for (String line : text.split("\n")) {
w = Math.max(w, getWidth(line, getFont(font)));
}
if (font == 0) {
return w * 2;
} else {
return w;
}
}
private static int getWidth(String text, Identifier font) {
return SaturnClient.client.textRenderer.getWidth(setFont(text, font));
} }
public static int getHeight() { public static int getHeight() {
return SaturnClient.client.textRenderer.fontHeight; return 18;
}
public static int centerX(int w, String text, int font) {
return (w - Fonts.getWidth(text, font)) / 2;
}
public static int centerY(int h) {
return (h - 18) / 2;
} }
} }
@@ -4,4 +4,55 @@ import net.minecraft.util.Identifier;
public class Textures { public class Textures {
public static final Identifier CROSSHAIR_RANGE = Identifier.of("saturnclient", "textures/gui/hud/crosshair_range.png"); public static final Identifier CROSSHAIR_RANGE = Identifier.of("saturnclient", "textures/gui/hud/crosshair_range.png");
public static final Identifier COSMETICS = Identifier.of("saturnclient", "textures/gui/components/cosmetics.png");
public static final Identifier HUD_ICON = Identifier.of("saturnclient", "textures/gui/components/hud.png");
public static final Identifier SEARCH = Identifier.of("saturnclient", "textures/gui/components/search.png");
public static final Identifier CLOSE = Identifier.of("saturnclient", "textures/gui/components/close.png");
public static final Identifier MODS_TAB = Identifier.of("saturnclient", "textures/gui/components/mods_tab.png");
public static final Identifier SETTINGS = Identifier.of("saturnclient", "textures/gui/components/settings.png");
public static final Identifier CLOAK = Identifier.of("saturnclient", "textures/gui/components/cloak.png");
public static final Identifier HAT = Identifier.of("saturnclient", "textures/gui/components/hat.png");
public static final Identifier RESET = Identifier.of("saturnclient", "textures/gui/components/reset.png");
public static final Identifier TOGGLE_INDICATOR = Identifier.of("saturnclient",
"textures/gui/components/toggle_indicator.png");
public static final Identifier LEFT = Identifier.of("saturnclient", "textures/gui/components/left.png");
public static final Identifier RIGHT = Identifier.of("saturnclient", "textures/gui/components/right.png");
public static final Identifier STORE = Identifier.of("saturnclient", "textures/gui/components/store.png");
public static final Identifier COINS = Identifier.of("saturnclient", "textures/gui/components/coins.png");
public static final Identifier SHIRT = Identifier.of("saturnclient", "textures/gui/components/shirt.png");
public static final Identifier CIRCLE_X = Identifier.of("saturnclient", "textures/gui/components/circle_x.png");
public static final Identifier INFO = Identifier.of("saturnclient", "textures/gui/components/info.png");
public static final Identifier CHECK = Identifier.of("saturnclient", "textures/gui/components/check.png");
public static final Identifier LOGO = Identifier.of("saturnclient", "textures/logo/logo.png");
public static final Identifier REALISTIC_LOGO = Identifier.of("saturnclient", "textures/logo/realistic.png");
public static final Identifier LOGO_TEXT = Identifier.of("saturnclient", "textures/logo/text.png");
public static final Identifier LOGO_TEXT_BIG = Identifier.of("saturnclient", "textures/logo/text_big.png");
// Widgets
public static final Identifier BUTTON_BORDER = Identifier.of("saturnclient", "button_border");
public static final Identifier BUTTON = Identifier.of("saturnclient", "button");
public static final Identifier BUTTON_OP = Identifier.of("saturnclient", "button_op");
public static final Identifier SETTINGS_BG = Identifier.of("saturnclient", "settings_bg");
public static final Identifier RECT = Identifier.of("saturnclient", "rect");
public static final Identifier RECT_BORDER = Identifier.of("saturnclient", "rect_border");
public static final Identifier MOD = Identifier.of("saturnclient", "textures/gui/components/mod.png");
public static final Identifier MOD_BG = Identifier.of("saturnclient", "textures/gui/components/mod_bg.png");
public static Identifier getCloakPreview(String cloak) {
return Identifier.of("saturnclient", "textures/gui/cloak/" + (cloak.isEmpty() ? "none" : cloak) + ".png");
}
public static Identifier getHatPreview(String hat) {
return Identifier.of("saturnclient", "textures/gui/hat/" + (hat.isEmpty() ? "none" : hat) + ".png");
}
public static Identifier getEmotePreview(String emote) {
return Identifier.of("saturnclient", "textures/gui/emote/" + (emote == null ? "none" : emote) + ".png");
}
public static Identifier getModIcon(String modId) {
return Identifier.of("saturnclient", "textures/gui/mod/" + modId + ".png");
}
} }
@@ -0,0 +1,131 @@
package org.saturnclient.ui2.screens;
import java.util.Map;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.ui2.SaturnScreen;
import org.saturnclient.ui2.components.Sidebar;
import org.saturnclient.ui2.components.inputs.FloatInput;
import org.saturnclient.ui2.components.inputs.HexInput;
import org.saturnclient.ui2.components.inputs.IntInput;
import org.saturnclient.ui2.components.inputs.KeybindingSelector;
import org.saturnclient.ui2.components.inputs.Select;
import org.saturnclient.ui2.components.inputs.Toggle;
import org.saturnclient.ui2.elements.Scroll;
import org.saturnclient.ui2.elements.Text;
import org.saturnclient.ui2.resources.Fonts;
public class ConfigEditor extends SaturnScreen {
private ConfigManager config;
public ConfigEditor(ConfigManager config) {
super("Config Editor");
this.config = config;
}
@Override
public void ui() {
int p = 10;
int g = 10;
Scroll scroll = new Scroll(p);
drawProperties(scroll, config.getProperties(), 0, 0, 480 + 10 + (g * 2));
int scrollWidth = 480 + 10 + (g * 2) + (p * 2);
draw(scroll.dimensions(scrollWidth, 350).center(width, height));
draw(new Sidebar(1, this::close).centerOffset(width, height, -(scrollWidth / 2 + 20), 0));
}
@SuppressWarnings("unchecked")
private int drawProperties(Scroll configScroll, Map<String, Property<?>> properties, int row, int col, int w) {
for (Map.Entry<String, Property<?>> propEntry : properties.entrySet()) {
Property<?> prop = propEntry.getValue();
String propName = propEntry.getKey();
boolean full = isFull(prop);
// Always reset to col 0 if full-width component needs to be drawn mid-row
if (full && col != 0) {
col = 0;
row++;
}
int modX = (w / 2) * col; // two-column layout
int modY = 25 * row;
if (prop.getType() == Property.PropertyType.NAMESPACE) {
configScroll.draw(
new Text(
propName
).position(Fonts.centerX(520, propName, Text.font.value), modY + 1).scale(0.7f));
} else {
configScroll.draw(
new Text(
propName
).position(modX, modY + 1).scale(0.7f));
}
switch (prop.getType()) {
case BOOLEAN:
configScroll.draw(
new Toggle((Property<Boolean>) prop)
.position(modX + (w / 2) - 40, modY)
.scale(0.5f));
break;
case HEX:
configScroll.draw(new HexInput((Property<Integer>) prop, this).position(w / 2, modY));
break;
case INTEGER:
configScroll.draw(new IntInput((Property<Integer>) prop).position(w / 2, modY));
break;
case FLOAT:
configScroll.draw(new FloatInput((Property<Float>) prop).position(w / 2, modY));
break;
case NAMESPACE:
Map<String, Property<?>> nestedProperties = (Map<String, Property<?>>) prop.value;
row = drawProperties(configScroll, nestedProperties, row + 1, col, w);
break;
case SELECT:
configScroll.draw(new Select((Property<Integer>) prop).position(w / 2, modY));
break;
case KEYBINDING:
configScroll.draw(new KeybindingSelector((Property<Integer>) prop).position(w / 2, modY));
break;
case STRING:
}
if (full) {
row++; // full-width takes a whole row
col = 0;
} else {
col++;
if (col > 1) { // max 2 columns
col = 0;
row++;
}
}
}
return row;
}
public static boolean isFull(Property<?> prop) {
switch (prop.getType()) {
case BOOLEAN:
return false;
default:
return true;
}
}
}

Some files were not shown because too many files have changed in this diff Show More