Compare commits

...
150 Commits
Author SHA1 Message Date
selimaj-dev 0d3a27b5e7 Beta 2 2025-08-22 17:36:44 +02:00
selimaj-dev b6eca22097 Fixed alignment in bmw and amg cloaks 2025-08-21 01:49:58 +02:00
selimaj-dev 4de4605ee8 Red Bull cloak (sick af) 2025-08-21 01:44:45 +02:00
selimaj-dev 8018caf26b Ferrari 2025-08-21 01:22:03 +02:00
selimaj-dev 39266df45e Amg petronas f1 cloak 2025-08-20 18:14:45 +02:00
selimaj-dev 25046b53f3 Amg cloak 2025-08-20 17:26:11 +02:00
selimaj-dev 8e89a850b3 Bmw preview 2025-08-19 20:27:55 +02:00
selimaj-dev ccaa79e89b Bmw cloak 2025-08-19 20:24:23 +02:00
selimaj-dev 30b375aa6f Bmw cloak 2025-08-19 20:16:39 +02:00
selimaj-dev 6d2cad1c7a Bmw cloak 2025-08-19 20:16:32 +02:00
selimaj-dev 720ee3d8bd change version to 0.0.4-beta1 2025-08-18 14:00:40 +02:00
selimaj-dev b4b348a54c change version to 0.0.2-beta1 2025-08-18 14:00:23 +02:00
selimaj-dev a3be72c443 Loading in theme manager with refactor 2025-08-18 11:58:14 +02:00
selimaj-dev 1c553d14bf Fixed theme manager bug 2025-08-18 09:25:38 +02:00
selimaj-dev cd967a907c Changed emote icons 2025-08-17 20:34:40 +02:00
selimaj-dev ba04c45b75 readme change 2025-08-17 15:26:45 +02:00
selimaj-dev ecb07ce730 readme change 2025-08-17 15:25:41 +02:00
selimaj-dev 6cd10240fc Merge branch 'refactored-clientcom' 2025-08-17 15:23:24 +02:00
selimaj-dev d162db3464 Fixed keypress and hud rendering 2025-08-17 15:22:00 +02:00
Leo Codes 7e3b6cf7f8 Merge pull request #3 from saturnclientmc/refactored-clientcom
Refactored clientcom
2025-08-16 10:39:06 -04:00
selimaj-dev 6fb38e4f9b Updated ip 2025-08-16 16:33:19 +02:00
selimaj-dev 997fbbe9a9 Working emote system 2025-08-16 16:29:48 +02:00
selimaj-dev 01a8ea15a0 Better cosmetic update system 2025-08-16 16:05:44 +02:00
selimaj-dev 08cee2172c Removed the reload system 2025-08-16 14:17:55 +02:00
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
selimaj-dev 486232623a added name tags 2025-04-25 17:10:30 +02:00
selimaj-dev 76dd1fe0a4 name icon colors 2025-04-25 09:45:57 -05:00
selimaj-dev 12c53099f2 ui and module improvements 2025-04-24 03:37:37 -05:00
selimaj-dev 3f89760282 Better module system 2025-04-23 08:18:27 -05:00
selimaj-dev 3b5b51e6d2 fixed config load bug 2025-04-23 14:59:23 +02:00
selimaj-dev d2894d0454 small change 2025-04-23 14:14:26 +02:00
selimaj-dev ed23cb44f2 improving modulation, bug: Config not working 2025-04-23 07:05:56 -05:00
selimaj-dev 683a9f41e8 refactor of the module system. Bug: all mods are enabled bc the Module has static enabled 2025-04-22 17:06:41 -05:00
selimaj-dev 052a56bcb0 i almost got cooked 2025-04-22 16:20:35 -05:00
selimaj-dev 81056dd7d7 renamed 2025-04-22 15:23:36 -05:00
selimaj-dev 8dd13364fb replaced getInstance with .client 2025-04-22 15:19:39 -05:00
selimaj-dev 5149194cb3 added theming 2025-04-22 14:52:38 -05:00
selimaj-dev 98330ed8a9 added theming bug needs bug fix 2025-04-22 14:49:44 -05:00
selimaj-dev ad54ed63f3 logo change 2025-04-22 12:17:42 +02:00
selimaj-dev 08a48c8f92 button element 2025-04-21 17:37:28 +02:00
selimaj-dev 404838b70d fixed rounded rectangle 2025-04-21 17:18:23 +02:00
selimaj-dev 4794e12bed almost there 2025-04-21 17:10:28 +02:00
selimaj-dev 3b44ead173 almost there 2025-04-21 16:52:38 +02:00
selimaj-dev d1cc19a9db almost there 2025-04-21 16:48:37 +02:00
selimaj-dev 9b36603f86 almost there 2025-04-21 16:48:11 +02:00
selimaj-dev 2e5d3296d4 weird bug 2025-04-21 16:13:21 +02:00
selimaj-dev 50e5a453a4 Rounded rectangles 2025-04-21 01:28:27 +02:00
selimaj-dev 8f1d1f0ce8 tesing rounded corners 2025-04-21 00:20:43 +02:00
selimaj-dev 1741e8c0b9 tesing new ui scope 2025-04-21 00:15:52 +02:00
selimaj-dev 9f0d5ef78b tesing new ui scope 2025-04-20 17:24:53 +02:00
selimaj-dev 5c15af0590 added renderScope 2025-04-19 10:40:56 -05:00
201 changed files with 14085 additions and 5187 deletions
+3 -1
View File
@@ -8,10 +8,12 @@
<b>Saturn Client is a free and open source Minecraft client</b>
</p>
![image](https://github.com/user-attachments/assets/b0b23e72-c2eb-49aa-b49c-4d3cc4d60fcc)
# Credits
- Cloak rendering: `IIpho3nix` & `Dhyan Blum`
- Who Am I: `FokshaWasTaken`
- 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
+10 -1
View File
@@ -19,6 +19,10 @@ dependencies {
modApi "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"
url = "https://maven.felnull.dev"
}
maven {
name "KosmX's maven"
url 'https://maven.kosmx.dev/'
}
}
processResources {
@@ -43,7 +52,7 @@ processResources {
}
}
def targetJavaVersion = 17
def targetJavaVersion = 21
tasks.withType(JavaCompile).configureEach {
it.options.encoding = "UTF-8"
if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
+8 -12
View File
@@ -1,17 +1,13 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
# Fabric Properties
# check these on https://modmuss50.me/fabric.html
minecraft_version=1.21.4
yarn_mappings=1.21.4+build.7
loader_version=0.16.10
minecraft_version=1.21.4
yarn_mappings=1.21.4+build.7
loader_version=0.16.10
fabric_version=0.114.0+1.21.4
# Mod Properties
mod_version = 0.0.1-alpha0
maven_group = me.iipho3nix
archives_base_name = saturnclient
mod_version = 0.0.4-beta2
maven_group = org.saturnclient
archives_base_name = saturnclient
# Dependencies
# check this on https://modmuss50.me/fabric.html
fabric_version=0.114.0+1.21.4
player_anim = 2.0.3+1.21.4
@@ -0,0 +1,11 @@
package org.saturnclient.modules;
import org.saturnclient.ui2.RenderScope;
public interface HudMod {
public ModDimensions getDimensions();
public void renderDummy(RenderScope scope);
public void renderHud(RenderScope scope);
}
@@ -0,0 +1,54 @@
package org.saturnclient.modules;
import java.util.Map;
import org.saturnclient.saturnclient.config.NamedProperty;
import org.saturnclient.saturnclient.config.Property;
public class ModDimensions {
public Property<Integer> x = Property.integer(0);
public Property<Integer> y = Property.integer(0);
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 height = 0;
public NamedProperty<Map<String, Property<?>>> prop() {
return Property.namespace(Map.of(
"X", x,
"Y", y,
"Scale", scale,
"Background Color", bgColor,
"Foreground Color", fgColor,
"Corner Radius", radius,
"Font", font
)).named("In-Game Display");
}
public ModDimensions() {}
public ModDimensions(int width, int height) {
this.width = width;
this.height = height;
}
public ModDimensions(int x, int y, int width, int height) {
this.x.value = x;
this.y.value = y;
this.width = width;
this.height = height;
}
public ModDimensions(int x, int y, int width, int height, float scale) {
this.x.value = x;
this.y.value = y;
this.scale.value = scale;
this.width = width;
this.height = height;
}
}
@@ -0,0 +1,28 @@
package org.saturnclient.modules;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import org.saturnclient.modules.mods.*;
public class ModManager {
public static Module[] MODS = {
new Crosshair(),
new AutoSprint(),
new ArmorDisplay(),
new Fps(),
new PotionDisplay(),
new Coordinates(),
new Freelook(),
new Keystrokes()
};
public static void init() {
ClientTickEvents.END_CLIENT_TICK.register(client -> {
for (Module m : MODS) {
if (m.isEnabled()) {
m.tick();
}
}
});
}
}
@@ -0,0 +1,52 @@
package org.saturnclient.modules;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.NamedProperty;
import org.saturnclient.ui2.resources.Textures;
import org.saturnclient.ui2.RenderScope;
import net.minecraft.util.Identifier;
public abstract class Module {
private final ModuleDetails details;
private final ConfigManager configManager;
public Module(ModuleDetails details, NamedProperty<?>... props) {
this.details = details;
configManager = new ConfigManager(details.name);
for (NamedProperty<?> prop : props) {
configManager.property(prop.name, prop.prop);
}
}
public void render(RenderScope scope) {}
public void tick() {}
public abstract boolean isEnabled();
public abstract void setEnabled(boolean e);
public final String getName() {
return details.name;
}
public Identifier getIconTexture() {
return Textures.getModIcon(details.namespace);
}
public ConfigManager getConfig() {
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;
}
}
@@ -0,0 +1,48 @@
package org.saturnclient.modules.mods;
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.Textures;
import net.minecraft.client.render.RenderLayer;
public class Crosshair extends Module {
public static Property<Boolean> enabled = Property.bool(false);
public Crosshair() {
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
public void render(RenderScope scope) {
if (enabled.value
&& SaturnClient.client.targetedEntity != null
&& SaturnClient.client.targetedEntity.isAlive()) {
int scaledWidth = 15;
int scaledHeight = 15;
scope.setRenderLayer(RenderLayer::getCrosshair);
scope.drawTexture(Textures.CROSSHAIR_RANGE,
(scope.getScaledWindowWidth() - scaledWidth) / 2,
(scope.getScaledWindowHeight() - scaledHeight) / 2, 0.0F, 0.0F, scaledWidth, scaledHeight);
}
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void setEnabled(boolean e) {
enabled.value = e;
}
}
@@ -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;
}
}
@@ -1,21 +1,20 @@
package org.saturnclient.saturnclient.mixin;
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.saturnclient.mod_utils.CameraOverriddenEntity;
import org.saturnclient.saturnmods.mods.FreeLook;
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.Shadow;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.Entity;
import net.minecraft.world.BlockView;
@Mixin(Camera.class)
public abstract class CameraMixin {
@Unique
@@ -25,9 +24,8 @@ public abstract class CameraMixin {
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) {
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) {
@@ -35,13 +33,12 @@ public abstract class CameraMixin {
cameraOverriddenEntity.freelook$setCameraYaw(MinecraftClient.getInstance().player.getYaw());
firstTime = false;
}
this.setRotation(cameraOverriddenEntity.freelook$getCameraYaw(),
cameraOverriddenEntity.freelook$getCameraPitch());
this.setRotation(cameraOverriddenEntity.freelook$getCameraYaw(), cameraOverriddenEntity.freelook$getCameraPitch());
}
if (!FreeLook.isFreeLooking && cameraEntity instanceof ClientPlayerEntity) {
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,58 @@
package org.saturnclient.modules.mods.mixins;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.ModManager;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.menus.HudEditor;
import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
import org.saturnclient.ui2.RenderScope;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderTickCounter;
@Mixin(InGameHud.class)
public class RenderMixin {
@Inject(method = "renderChat", at = @At("TAIL"))
public void renderMods(DrawContext context, RenderTickCounter tickCounter, CallbackInfo ci) {
TextRenderer textRenderer = SaturnClient.client.textRenderer;
if (textRenderer != null &&
!(SaturnClient.client.currentScreen instanceof HudEditor)) {
RenderScope renderScope = new RenderScope(context.getMatrices(),
((DrawContextAccessor) context).getVertexConsumers());
for (org.saturnclient.modules.Module m : ModManager.MODS) {
if (m instanceof HudMod && m.isEnabled()) {
ModDimensions dim = ((HudMod) m).getDimensions();
renderScope.matrices.push();
renderScope.matrices.translate(dim.x.value, (float) dim.y.value, 0);
renderScope.matrices.scale(dim.scale.value, dim.scale.value, 1.0f);
if (dim.renderBackground) {
renderScope.drawRoundedRectangle(0, 0, dim.width, dim.height, dim.radius.value,
dim.bgColor.value);
}
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
((HudMod) m).renderHud(renderScope);
renderScope.matrices.pop();
} else {
m.render(renderScope);
}
}
}
}
}
@@ -1,9 +1,9 @@
package org.saturnclient.saturnclient.mod_utils;
package org.saturnclient.modules.mods.utils;
public interface CameraOverriddenEntity {
float freelook$getCameraPitch();
float freelook$getCameraPitch();
float freelook$getCameraYaw();
void freelook$setCameraPitch(float pitch);
void freelook$setCameraYaw(float yaw);
}
}
@@ -3,16 +3,22 @@ package org.saturnclient.saturnclient;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
import net.minecraft.client.gui.screen.TitleScreen;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.util.math.MatrixStack;
import org.saturnclient.modules.ModManager;
import org.saturnclient.saturnclient.auth.Auth;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.cosmetics.Emotes;
import org.saturnclient.saturnclient.cosmetics.Hats;
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
import org.saturnclient.saturnclient.event.KeyInputHandler;
import org.saturnclient.saturnclient.menus.SaturnMenu;
import org.saturnclient.saturnclient.menus.SaturnTitleScreen;
import org.saturnclient.saturnmods.ModManager;
import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
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.LoggerFactory;
@@ -25,26 +31,50 @@ public class SaturnClient implements ModInitializer {
public static final String MOD_ID = "saturnclient";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
public static MinecraftClient client;
@Override
public void onInitialize() {
LOGGER.info("Initializing " + MOD_ID);
client = MinecraftClient.getInstance();
SaturnClientConfig.init();
ModManager.init();
ConfigManager.load();
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(() -> {
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();
Emotes.initialize();
if (Auth.authenticate()) {
Cloaks.initialize();
Hats.initialize();
@@ -1,53 +1,20 @@
package org.saturnclient.saturnclient;
import org.saturnclient.saturnclient.config.Property;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.ui.SaturnAnimation;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.Textures;
import org.saturnclient.ui.animations.FadeIn;
import org.saturnclient.ui2.resources.Textures;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.ColorHelper;
public class SaturnClientConfig {
public static ConfigManager config = new ConfigManager("Saturn Client");
public static ConfigManager config;
public static Property<Integer> color = config.property(
"Primary color",
new Property<>(
ColorHelper.getArgb(255, 251, 60, 79),
Property.PropertyType.HEX));
public static Property<Boolean> realisticLogo = Property.bool(false);
public static Property<Integer> normal = config.property(
"Default color",
new Property<>(
ColorHelper.getArgb(255, 182, 182, 182),
Property.PropertyType.HEX));
public static Property<Boolean> realisticLogo = config.property(
"Realistic logo",
new Property<>(false));
public static Property<Boolean> saturnTitleScreen = config.property(
"Saturn client title screen",
new Property<>(true));
public static int WHITE = ColorHelper.getArgb(255, 255, 255, 255);
public static TextRenderer textRenderer = null;
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 Property<Boolean> saturnTitleScreen = Property.bool(true);
public static Property<Integer> openEmoteWheel = Property.keybinding(GLFW.GLFW_KEY_B);
public static Identifier getLogo() {
return realisticLogo.value ? Textures.REALISTIC_LOGO : Textures.LOGO;
@@ -56,4 +23,28 @@ public class SaturnClientConfig {
public static String getSaturnIndicator() {
return realisticLogo.value ? "" : "";
}
/*
* Gets the icon color of a specific individual, here are the different colors
*
* - Owner: Dark Red
* - Admin: Red
* - Partners: Gold
* - Contributor: Aqua
* - Other/player: White
*/
public static Formatting getIconColor(String uuid) {
if (uuid.equals("d362a04228bd49e1a807ae74dbe8aba9")) {
return Formatting.DARK_RED; // Owner
}
return Formatting.WHITE;
}
public static void init() {
config = new ConfigManager("Saturn Client");
config.property("Realistic logo", realisticLogo);
config.property("Saturn client title screen", saturnTitleScreen);
config.property("Open Emote Wheel", openEmoteWheel);
}
}
@@ -2,10 +2,22 @@ package org.saturnclient.saturnclient.auth;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.util.Identifier;
import org.saturnclient.saturnclient.cosmetics.Hats;
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 dev.kosmx.playerAnim.api.layered.AnimationStack;
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationAccess;
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationRegistry;
import org.saturnclient.saturnclient.SaturnClient;
public class Auth {
@@ -22,13 +34,12 @@ public class Auth {
ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> close());
try {
MinecraftClient client = MinecraftClient.getInstance();
if (client.getSession() == null) {
if (SaturnClient.client.getSession() == null) {
SaturnClient.LOGGER.error("No active Minecraft session found");
return false;
}
String accessToken = client.getSession().getAccessToken();
String accessToken = SaturnClient.client.getSession().getAccessToken();
SaturnClient.LOGGER.info("Authenticating");
Network.init();
@@ -55,7 +66,7 @@ public class Auth {
Cloaks.loadCloak(uuid);
playerNames.put(client.getSession().getUsername(), uuid);
playerNames.put(SaturnClient.client.getSession().getUsername(), uuid);
afterAuth();
@@ -98,9 +109,72 @@ public class Auth {
}
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);
}
case "update_cloak":
String targetUuid = parser.getStringOrNull("uuid");
String targetCloak = parser.getStringOrNull("cloak");
if (targetUuid != null && targetCloak != null) {
SaturnPlayer player = players.get(targetUuid);
if (player != null) {
player.cloak = targetCloak;
}
}
break;
case "update_hat":
targetUuid = parser.getStringOrNull("uuid");
String targetHat = parser.getStringOrNull("hat");
if (targetUuid != null && targetHat != null) {
SaturnPlayer player = players.get(targetUuid);
if (player != null) {
player.hat = targetHat;
}
}
break;
case "emote":
targetUuid = parser.getStringOrNull("uuid");
String emote = parser.getStringOrNull("name");
for (AbstractClientPlayerEntity player : SaturnClient.client.world.getPlayers()) {
if (player.getUuidAsString().replace("-", "").equals(targetUuid)) {
AnimationStack animationStack = PlayerAnimationAccess.getPlayerAnimLayer(player);
if (emote != null && !emote.isEmpty()) {
if (animationStack.isActive() && animationStack.getPriority() == 1000) {
animationStack.removeLayer(1000);
}
animationStack.addAnimLayer(1000,
PlayerAnimationRegistry
.getAnimation(Identifier.of("saturnclient", emote))
.playAnimation());
} else {
animationStack.removeLayer(1000);
}
}
}
break;
default:
if (parser.error != null) {
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;
}
@@ -119,16 +193,21 @@ public class Auth {
private static void startPingThread() {
pingThread = new Thread(() -> {
try {
while (running) {
Thread.sleep(25000); // 25 seconds
if (running) {
Network.write("ping");
while (running && !Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(25000); // 25 seconds
if (running) {
Network.write("ping");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Restore interrupt status
break; // Exit the loop on interruption
}
}
} catch (InterruptedException e) {
SaturnClient.LOGGER.warn("Ping thread interrupted", e);
} catch (Exception e) {
SaturnClient.LOGGER.error("Error in ping thread", e);
if (!(e instanceof InterruptedException)) {
SaturnClient.LOGGER.error("Error in ping thread", e);
}
}
});
@@ -158,7 +237,7 @@ public class Auth {
public static void setCloak(String cloakName) {
try {
Network.write("set_cloak@cloak=" + cloakName);
Network.write("set_cloak@cloak=" + cloakName + "@notify=" + stringifyPlayers());
} catch (Exception e) {
SaturnClient.LOGGER.error("Request failed", e);
}
@@ -166,12 +245,26 @@ public class Auth {
public static void setHat(String hatName) {
try {
Network.write("set_hat@hat=" + hatName);
Network.write("set_hat@hat=" + hatName + "@notify=" + stringifyPlayers());
} catch (Exception e) {
SaturnClient.LOGGER.error("Request failed", e);
}
}
public static void emote(String name) {
try {
Network.write("emote@name=" + name + "@notify=" + stringifyPlayers());
} catch (Exception e) {
SaturnClient.LOGGER.error("Request failed", e);
}
}
public static String stringifyPlayers() {
return players.keySet().stream()
.filter(key -> key != uuid)
.collect(Collectors.joining("$"));
}
public static void player(String name, String uuid) {
try {
playerNames.put(name, uuid.replaceAll("-", ""));
@@ -181,14 +274,19 @@ public class Auth {
}
}
public static void sendReload() {
for (Map.Entry<String, String> player : playerNames.entrySet()) {
if (players.containsKey(player.getValue()) && !player.getValue().equals(uuid)) {
MinecraftClient.getInstance().player.networkHandler
.sendChatCommand("msg " + player.getKey() + " "
+ "$SATURN_RELOAD 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);
}
}
}
@@ -2,8 +2,9 @@ package org.saturnclient.saturnclient.auth;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.minecraft.client.MinecraftClient;
import org.saturnclient.saturnclient.SaturnClient;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.client.MinecraftClient;
import java.util.HashSet;
import java.util.Set;
@@ -19,7 +20,7 @@ public class PlayerTracker {
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> {
trackedPlayers.clear();
client.execute(() -> {
checkForNewPlayers(MinecraftClient.getInstance());
checkForNewPlayers(SaturnClient.client);
});
});
@@ -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 {
String val = params.get(i);
if (val != null) {
@@ -12,38 +12,79 @@ import java.nio.file.Files;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import net.minecraft.client.MinecraftClient;
import org.saturnclient.saturnclient.SaturnClient;
public class ConfigManager {
private static File configFile = new File(
MinecraftClient.getInstance().runDirectory,
SaturnClient.client.runDirectory,
"saturn.json");
private static Map<String, Map<String, Property<?>>> properties = new HashMap<>();
private static JsonObject cachedThemeJson = null;
private Map<String, Property<?>> currentMap;
private String namespace;
public ConfigManager(String namespace) {
currentMap = new LinkedHashMap<>();
properties.put(namespace, currentMap);
System.out.println("Created namespace: " + namespace);
}
public ConfigManager(ConfigManager config, String namespace) {
currentMap = new LinkedHashMap<>();
Property<Map<String, Property<?>>> namespaceProperty = new Property<>(currentMap,
Property.PropertyType.NAMESPACE);
Property<Map<String, Property<?>>> namespaceProperty = Property.namespace(currentMap);
config.property(namespace, namespaceProperty);
// Update the parent's state
config.currentMap.put(namespace, namespaceProperty);
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
public <T> Property<T> property(String name, Property<T> value) {
currentMap.put(name, value);
loadProp(name, 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() {
try {
SaturnClient.LOGGER.info("Starting to load config...");
@@ -92,25 +133,17 @@ public class ConfigManager {
Map<String, Property<?>> nestedProperties = p.getNamespaceValue();
loadProperties(nestedConfig, 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;
if (p.value instanceof Integer) {
((Property<Integer>) p).value = c.getAsInt();
} else if (p.value instanceof String) {
((Property<String>) p).value = c.getAsString();
} else if (p.value instanceof Float) {
((Property<Float>) p).value = c.getAsFloat();
} else if (p.value instanceof Boolean) {
((Property<Boolean>) p).value = c.getAsBoolean();
} else {
SaturnClient.LOGGER.warn(
"Unknown property type for: " + propertyName);
}
}
} else {
@@ -156,36 +189,29 @@ public class ConfigManager {
for (String propertyName : propertyMap.keySet()) {
Property<?> property = propertyMap.get(propertyName);
JsonElement propertyValue = null;
if (property.getType() == Property.PropertyType.NAMESPACE) {
// Handle nested namespace
JsonObject nestedConfig = new JsonObject();
Map<String, Property<?>> nestedProperties = (Map<String, Property<?>>) property.value;
saveProperties(nestedConfig, nestedProperties);
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:
// Convert the property value based on its type
switch (property.getType()) {
case NAMESPACE:
// Handle nested namespace
JsonObject nestedConfig = new JsonObject();
Map<String, Property<?>> nestedProperties = (Map<String, Property<?>>) property.value;
saveProperties(nestedConfig, nestedProperties);
propertyValue = nestedConfig;
break;
default:
if (property.value instanceof Integer) {
propertyValue = new JsonPrimitive((Integer) property.value);
break;
case FLOAT:
propertyValue = new JsonPrimitive((Float) property.value);
break;
case STRING:
} else if (property.value instanceof String) {
propertyValue = new JsonPrimitive((String) property.value);
break;
case HEX:
propertyValue = new JsonPrimitive((Integer) property.value);
break;
default:
} 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(
"Unknown property type for: " + propertyName);
break;
}
}
break;
}
if (propertyValue != null) {
@@ -0,0 +1,11 @@
package org.saturnclient.saturnclient.config;
public class NamedProperty<T> {
public String name;
public Property<T> prop;
public NamedProperty(String name, Property<T> value) {
this.name = name;
this.prop = value;
}
}
@@ -2,6 +2,9 @@ package org.saturnclient.saturnclient.config;
import java.util.Map;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.SaturnClient;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
@@ -13,62 +16,108 @@ public class Property<T> {
FLOAT,
STRING,
HEX,
NAMESPACE
NAMESPACE,
SELECT,
KEYBINDING,
}
public T value;
public boolean isReset = false;
private T defaultValue;
private String[] availableValues;
private PropertyType type;
private boolean wasPressedLastTick = false;
public Property(T value) {
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) {
private Property(T value, PropertyType type) {
this.value = value;
this.defaultValue = value;
this.type = type;
}
public boolean getBool() {
public static <T> Property<T> from(T value) {
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() {
if (value instanceof Integer) {
return (int) value;
}
throw new IllegalStateException("Property does not contain an int");
public Property<T> copy() {
return new Property<>(this.value, this.type);
}
public float getFloat() {
if (value instanceof Float) {
return (float) value;
}
throw new IllegalStateException("Property does not contain a float");
public static Property<Integer> font(int value) {
return select(value, "Default", "Inter", "Inter bold");
}
public String getString() {
if (value instanceof String) {
return (String) value;
public static Property<Integer> select(Integer value, String... availableValues) {
Property<Integer> property = new Property<>(value, PropertyType.SELECT);
property.availableValues = availableValues;
return property;
}
public static Property<Map<String, Property<?>>> namespace(Map<String, Property<?>> value) {
return new Property<>(value, PropertyType.NAMESPACE);
}
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() {
@@ -115,12 +164,8 @@ public class Property<T> {
return true;
if (value instanceof String && primitive.isString())
return true;
// Check if JSON string represents a valid hex integer
if (value instanceof String && primitive.isString()) {
String str = primitive.getAsString();
return str.matches("0x[0-9A-Fa-f]+");
}
if (type == PropertyType.HEX && primitive.isString())
return true;
} else if (element.isJsonObject()) {
return isNamespace(value);
}
@@ -139,4 +184,75 @@ public class Property<T> {
public static boolean valueIsNamespace(Object obj) {
return obj instanceof Map;
}
@SuppressWarnings("unchecked")
public void setValue(Object value) {
this.value = (T) value;
}
@SuppressWarnings("unchecked")
public void setValue(Object value, Object defaultValue) {
this.value = (T) value;
this.defaultValue = (T) defaultValue;
}
public NamedProperty<T> named(String name) {
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 && SaturnClient.client.currentScreen == null;
}
public boolean wasKeyPressed() {
boolean isPressed = this.isKeyPressed();
wasPressedLastTick = isPressed && !wasPressedLastTick;
return wasPressedLastTick;
}
@SuppressWarnings("unchecked")
public void load(String name, JsonElement element) {
if (element != null && element.isJsonObject()) {
JsonElement value = element.getAsJsonObject().get(name);
if (value != null) {
switch (this.getType()) {
case BOOLEAN:
((Property<Boolean>) this).setValue(value.getAsBoolean());
break;
case INTEGER:
JsonPrimitive primitive = value.getAsJsonPrimitive();
if (primitive.isNumber())
((Property<Integer>) this).setValue(value.getAsInt());
else if (primitive.isString())
((Property<Integer>) this).setValue(parseHexToInt(value.getAsString()));
break;
case FLOAT:
((Property<Float>) this).setValue(value.getAsFloat());
break;
case STRING:
((Property<String>) this).setValue(value.getAsString());
break;
case HEX:
((Property<Integer>) this).setValue(parseHexToInt(value.getAsString()));
break;
default:
break;
}
}
}
}
public static int parseHexToInt(String hex) {
hex = hex.replaceAll("#", "");
if (hex.length() == 6) {
hex = "FF" + hex;
} else if (hex.length() != 8) {
throw new IllegalArgumentException("Hex string must be 6 or 8 characters long");
}
return (int) Long.parseLong(hex, 16);
}
}
@@ -0,0 +1,93 @@
package org.saturnclient.saturnclient.config;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.saturnclient.saturnclient.SaturnClient;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class ThemeManager {
private static File themeFile = new File(
SaturnClient.client.runDirectory,
"saturn.theme.json");
private static List<ThemeManager> theme = new ArrayList<>();
private static JsonObject cachedThemeJson = null;
private Map<String, Map<String, Object>> states;
private Map<String, Property<?>> currentStyling;
private String namespace;
public ThemeManager(String namespace, String... states) {
this.namespace = namespace;
this.states = new LinkedHashMap<>();
this.currentStyling = new LinkedHashMap<>();
this.states.put(null, new LinkedHashMap<>());
for (String s : states) {
this.states.put(s, new LinkedHashMap<>());
}
theme.add(this);
}
public void setState(String state) {
for (Map.Entry<String, Object> tProp : states.get(state).entrySet()) {
currentStyling.get(tProp.getKey()).setValue(tProp.getValue());
}
}
public void applyState(String state) {
for (Map.Entry<String, Object> tProp : states.get(state).entrySet()) {
if (!states.get(null).get(tProp.getKey()).equals(tProp.getValue()))
currentStyling.get(tProp.getKey()).setValue(tProp.getValue());
}
}
public <T> Property<T> property(String name, Property<T> prop) {
for (Map.Entry<String, Map<String, Object>> state : this.states.entrySet()) {
Property<?> property = prop.copy();
if (cachedThemeJson == null) {
load();
}
String stateName = state.getKey();
property.load(name, cachedThemeJson.get(stateName != null ? namespace + "@" + stateName : namespace));
state.getValue().put(name, property.value);
}
currentStyling.put(name, prop);
return prop;
}
public <T> void propertyStateDefault(String state, String name, T value) {
Property<?> prop = currentStyling.get(name).copy();
prop.setValue(value, value);
if (cachedThemeJson == null) {
load();
}
prop.load(name, cachedThemeJson.get(state != null ? namespace + "@" + state : namespace));
states.get(state).put(name, prop.value);
}
public static void load() {
try {
SaturnClient.LOGGER.info("Starting to load theme...");
if (!themeFile.exists()) {
themeFile.createNewFile();
Files.write(themeFile.toPath(), "{}".getBytes());
}
cachedThemeJson = JsonParser.parseString(
new String(Files.readAllBytes(themeFile.toPath()))).getAsJsonObject();
SaturnClient.LOGGER.info("Theme loaded");
} catch (IOException e) {
SaturnClient.LOGGER.error("Error reading the theme file", e);
}
}
}
@@ -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");
}
}
@@ -3,7 +3,7 @@ package org.saturnclient.saturnclient.cosmetics;
import org.saturnclient.saturnclient.auth.SaturnPlayer;
import org.saturnclient.saturnclient.auth.Auth;
import net.minecraft.client.MinecraftClient;
import org.saturnclient.saturnclient.SaturnClient;
import net.minecraft.client.render.OverlayTexture;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.feature.FeatureRenderer;
@@ -30,7 +30,7 @@ public class HatFeatureRenderer extends FeatureRenderer<PlayerEntityRenderState,
PlayerEntityRenderState state, float limbAngle, float limbDistance) {
ItemStack headItem = state.equippedHeadStack;
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);
if (player == null || player.hat == null || player.hat.isEmpty()) {
return;
@@ -48,7 +48,7 @@ public class HatFeatureRenderer extends FeatureRenderer<PlayerEntityRenderState,
customHat.set(DataComponentTypes.EQUIPPABLE,
EquippableComponent.builder(EquipmentSlot.HEAD).build());
ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer();
ItemRenderer itemRenderer = SaturnClient.client.getItemRenderer();
itemRenderer.renderItem(
customHat,
@@ -6,6 +6,7 @@ import org.saturnclient.saturnclient.auth.SaturnPlayer;
import org.saturnclient.saturnclient.auth.Auth;
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 void initialize() {
@@ -15,7 +16,6 @@ public class Hats {
public static void setHat(String uuid, String hatName) {
setHatSilent(uuid, hatName);
Auth.setHat(hatName);
Auth.sendReload();
}
public static void setHatSilent(String uuid, String hatName) {
@@ -20,6 +20,7 @@ import org.saturnclient.saturnclient.SaturnClient;
* Originally created by IIpho3nix and modified for Saturn Client by leo.
*/
public class Cloaks {
public static final String[] ALL_CLOAKS = { "glitch", "mercedes_flow", "crimson_mark", "bmw", "amg", "amg_petronas", "ferrari", "redbull" };
private static final String[] ANIMATED_CLOAKS = { "glitch" };
private static final String CLOAKS_RESOURCE_PATH = "assets/saturnclient/textures/cloaks/";
@@ -44,10 +45,20 @@ public class Cloaks {
public static void setCloak(String uuid, String cloakName) {
Auth.setCloak(cloakName);
setCloakSilent(uuid, cloakName);
Auth.sendReload();
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
* server.
@@ -96,29 +107,34 @@ public class Cloaks {
}
// Register textures on main thread
net.minecraft.client.MinecraftClient.getInstance().execute(() -> {
SaturnClient.client.execute(() -> {
try {
List<AnimatedCloakData> animatedFrames = new ArrayList<>();
for (int i = 0; i < frameCount; i++) {
String frameId = fileName.replace(".gif", "") + "_frame_" + i;
Identifier frameIdentifier = Identifier.of(SaturnClient.MOD_ID, "cloaks_" + frameId);
IdentifierUtils.registerBufferedImageTexture(frameIdentifier, frames.get(i));
Identifier frameIdentifier = Identifier.of(SaturnClient.MOD_ID,
"cloaks_" + frameId);
IdentifierUtils.registerBufferedImageTexture(frameIdentifier,
frames.get(i));
animatedFrames.add(new AnimatedCloakData(frameIdentifier, delays.get(i)));
}
animatedCloaks.put(uuid, animatedFrames);
lastFrameTime.put(uuid, System.currentTimeMillis());
SaturnClient.LOGGER.info("Loaded " + frames.size() + " frames for animated cloak: " + fileName);
SaturnClient.LOGGER.info(
"Loaded " + frames.size() + " frames for animated cloak: " + fileName);
} catch (Exception e) {
SaturnClient.LOGGER.error("Failed to register animated cloak textures: " + fileName, e);
SaturnClient.LOGGER
.error("Failed to register animated cloak textures: " + fileName, e);
}
});
}
} catch (IOException e) {
SaturnClient.LOGGER.error("Failed to load animated cloak from resources: " + player.cloak + ".gif", e);
SaturnClient.LOGGER
.error("Failed to load animated cloak from resources: " + player.cloak + ".gif", e);
}
}).start();
} else {
net.minecraft.client.MinecraftClient.getInstance().execute(() -> loadStaticCloak(player.cloak + ".png"));
SaturnClient.client.execute(() -> loadStaticCloak(player.cloak + ".png"));
}
}
}
@@ -1,6 +1,6 @@
package org.saturnclient.saturnclient.cosmetics.cloaks.utils;
import net.minecraft.client.MinecraftClient;
import org.saturnclient.saturnclient.SaturnClient;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.NativeImageBackedTexture;
import net.minecraft.util.Identifier;
@@ -20,7 +20,7 @@ public class IdentifierUtils {
ByteBuffer bb = BufferUtils.createByteBuffer(bytes.length).put(bytes);
bb.flip();
NativeImageBackedTexture nibt = new NativeImageBackedTexture(NativeImage.read(bb));
MinecraftClient.getInstance().getTextureManager().registerTexture(i, nibt);
SaturnClient.client.getTextureManager().registerTexture(i, nibt);
} catch (Exception e) {
e.printStackTrace();
}
@@ -3,10 +3,17 @@ package org.saturnclient.saturnclient.event;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.option.Perspective;
import net.minecraft.client.util.InputUtil;
import org.saturnclient.saturnclient.menus.SaturnMenu;
import org.saturnclient.saturnmods.mods.FreeLook;
import org.saturnclient.saturnclient.SaturnClient;
import org.saturnclient.saturnclient.SaturnClientConfig;
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;
/**
@@ -19,52 +26,28 @@ public class KeyInputHandler {
private static final int DEFAULT_MENU_KEY = GLFW.GLFW_KEY_RIGHT_SHIFT;
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() {
// Register the main menu key binding
mainMenuKeyBinding = KeyBindingHelper.registerKeyBinding(new KeyBinding(
MAIN_MENU_KEY_ID,
InputUtil.Type.KEYSYM,
DEFAULT_MENU_KEY,
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
ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (mainMenuKeyBinding.wasPressed()) {
client.setScreen(new SaturnMenu());
} 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;
client.setScreen(new ShiftMenu());
}
if (SaturnClientConfig.openEmoteWheel.wasKeyPressed() && client.currentScreen == null) {
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.emote("");
}
}
});
@@ -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();
}
}
@@ -5,12 +5,14 @@ import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.ModManager;
import org.saturnclient.modules.Module;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnmods.HudMod;
import org.saturnclient.saturnmods.ModDimensions;
import org.saturnclient.saturnmods.ModManager;
import org.saturnclient.saturnmods.SaturnMod;
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.List;
@@ -24,7 +26,7 @@ public class HudEditor extends Screen {
public HudEditor() {
super(Text.literal("HUD Editor"));
for (SaturnMod m : ModManager.MODS) {
for (Module m : ModManager.MODS) {
if (m.isEnabled() && m instanceof HudMod) {
hudMods.add((HudMod) m);
}
@@ -90,9 +92,11 @@ public class HudEditor extends Screen {
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
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) {
ModDimensions dim = mod.getDimensions();
MatrixStack matrices = context.getMatrices();
@@ -102,7 +106,7 @@ public class HudEditor extends Screen {
matrices.scale(dim.scale.value, dim.scale.value, 1.0f);
// Render the mod
mod.renderDummy(context);
mod.renderDummy(scope);
matrices.pop();
@@ -1,146 +0,0 @@
package org.saturnclient.saturnclient.menus;
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.saturnmods.ModManager;
import org.saturnclient.saturnmods.SaturnMod;
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 (SaturnMod 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,77 +0,0 @@
package org.saturnclient.saturnclient.menus;
import net.minecraft.client.MinecraftClient;
import net.minecraft.text.Text;
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.ui.widgets.SaturnImageButton;
public class SaturnMenu extends SaturnUi {
public SaturnMenu() {
super(Text.literal("Saturn Client"));
}
@Override
protected void init() {
SaturnClientConfig.textRenderer = MinecraftClient.getInstance().textRenderer;
int buttonWidth = 96;
int buttonHeight = 25;
draw(
new SaturnButton("Mods", () -> {
client.setScreen(new ModMenu());
})
.setX((width - buttonWidth) / 2)
.setY((height - buttonHeight) / 2)
.setWidth(buttonWidth)
.setHeight(buttonHeight)
.setAnimations(new FadeIn(2)));
draw(
new SaturnImageButton(Textures.HUD_ICON, 14, 14, () -> {
client.setScreen(new HudEditor());
})
.setX((width - buttonWidth - (buttonHeight * 2) - 6) / 2)
.setY((height - buttonHeight) / 2)
.setWidth(buttonHeight)
.setHeight(buttonHeight)
.setAnimations(new FadeIn(2)));
draw(
new SaturnImageButton(Textures.COSMETICS, 14, 14, () -> {
client.setScreen(new CloaksMenu());
})
.setX((width / 2) + (buttonWidth / 2) + 3)
.setY((height - buttonHeight) / 2)
.setWidth(buttonHeight)
.setHeight(buttonHeight)
.setAnimations(new FadeIn(2)));
int logoSize = 49;
draw(
new SaturnImage(Textures.LOGO_TEXT)
.setX((width - logoSize) / 2)
.setY((height - 8) / 2 - 23)
.setWidth(logoSize)
.setHeight(8)
.setAnimations(new FadeIn(2)));
draw(
new SaturnImage(SaturnClientConfig.getLogo())
.setX((width - logoSize) / 2)
.setY(height / 2 - logoSize - 24)
.setWidth(logoSize)
.setHeight(logoSize)
.setAnimations(new Slide(2, 14)));
super.init();
}
}
@@ -1,77 +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 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 SaturnMenu());
}).setBold(true).setWidth(210).setHeight(26).setX(bX).setY(bY).setScale(0.69f));
super.init();
}
}
@@ -1,47 +0,0 @@
/*
* Code from: https://github.com/Selyss
* Repo: https://github.com/Selyss/CrosshairIndicator/tree/main
*/
package org.saturnclient.saturnclient.mixin;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderTickCounter;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.gui.DrawContext;
@Mixin(InGameHud.class)
public class Crosshair {
@Shadow
@Final
private MinecraftClient client;
@Unique
Identifier textureLocation = Identifier.of("saturnclient", "textures/gui/hud/crosshair_range.png");
@Inject(method = "renderCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawGuiTexture(Ljava/util/function/Function;Lnet/minecraft/util/Identifier;IIII)V", ordinal = 0))
private void drawCrosshair(DrawContext context, RenderTickCounter tickCounter, CallbackInfo ci) {
if (org.saturnclient.saturnmods.mods.Crosshair.enabled.value
&& org.saturnclient.saturnmods.mods.Crosshair.range_indicator.value
&& this.client.targetedEntity != null
&& this.client.targetedEntity.isAlive()) {
RenderSystem.setShaderTexture(0, textureLocation);
int scaledWidth = 15;
int scaledHeight = 15;
context.drawTexture(RenderLayer::getCrosshair, textureLocation,
(context.getScaledWindowWidth() - scaledWidth) / 2,
(context.getScaledWindowHeight() - scaledHeight) / 2, 0.0F, 0.0F, scaledWidth, scaledHeight,
scaledWidth, scaledHeight);
}
}
}
@@ -0,0 +1,12 @@
package org.saturnclient.saturnclient.mixin;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.VertexConsumerProvider;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(DrawContext.class)
public interface DrawContextAccessor {
@Accessor("vertexConsumers")
VertexConsumerProvider.Immediate getVertexConsumers();
}
@@ -1,62 +0,0 @@
package org.saturnclient.saturnclient.mixin;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.MathHelper;
import org.saturnclient.saturnclient.mod_utils.CameraOverriddenEntity;
import org.saturnclient.saturnmods.mods.FreeLook;
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;
}
}
@@ -1,6 +1,6 @@
package org.saturnclient.saturnclient.mixin;
import net.minecraft.client.MinecraftClient;
import org.saturnclient.saturnclient.SaturnClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.font.TextRenderer.TextLayerType;
import net.minecraft.client.render.VertexConsumerProvider;
@@ -9,7 +9,9 @@ import net.minecraft.client.render.entity.EntityRenderer;
import net.minecraft.client.render.entity.state.EntityRenderState;
import net.minecraft.client.render.entity.state.PlayerEntityRenderState;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.math.Vec3d;
import org.saturnclient.saturnclient.SaturnClientConfig;
@@ -34,20 +36,32 @@ public abstract class NameTagMixin<S extends EntityRenderState> {
@Inject(method = "renderLabelIfPresent(Lnet/minecraft/client/render/entity/state/EntityRenderState;Lnet/minecraft/text/Text;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", at = @At("HEAD"), cancellable = true)
private void onRenderLabelIfPresent(S state, Text text, MatrixStack matrices,
VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) {
if (isSaturn(state)) {
renderLabel(state, Text.literal(SaturnClientConfig.getSaturnIndicator()).append(text), matrices,
String uuid = isSaturn(state);
if (uuid != null) {
MutableText iconText = Text.literal(SaturnClientConfig.getSaturnIndicator())
.styled(style -> style.withColor(SaturnClientConfig.getIconColor(uuid)));
Text nameText = text.copy().styled(style -> style.withColor(Formatting.WHITE));
renderLabel(state, iconText.append(nameText), matrices,
vertexConsumers, light);
ci.cancel();
}
}
private boolean isSaturn(S state) {
private String isSaturn(S state) {
if (state instanceof PlayerEntityRenderState) {
String name = ((PlayerEntityRenderState) state).name;
String uuid = Auth.playerNames.get(name);
return uuid != null && Auth.players.containsKey(uuid);
if (Auth.players.containsKey(uuid)) {
return uuid;
}
}
return false;
return null;
}
private void renderLabel(S state, Text text, MatrixStack matrices,
@@ -61,9 +75,9 @@ public abstract class NameTagMixin<S extends EntityRenderState> {
matrices.multiply(this.dispatcher.getRotation());
matrices.scale(0.025F, -0.025F, 0.025F);
Matrix4f matrix4f = matrices.peek().getPositionMatrix();
TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
TextRenderer textRenderer = SaturnClient.client.textRenderer;
float f = (float) (-textRenderer.getWidth(text)) / 2.0F;
int j = (int) (MinecraftClient.getInstance().options.getTextBackgroundOpacity(0.25F) * 255.0F) << 24;
int j = (int) (SaturnClient.client.options.getTextBackgroundOpacity(0.25F) * 255.0F) << 24;
textRenderer.draw(text, f, (float) i, -2130706433, false, matrix4f, vertexConsumers,
bl ? TextLayerType.SEE_THROUGH : TextLayerType.NORMAL, j, light);
if (bl) {
@@ -1,33 +0,0 @@
package org.saturnclient.saturnclient.mixin;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import org.saturnclient.saturnclient.auth.Auth;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.client.gui.hud.MessageIndicator;
import net.minecraft.network.message.MessageSignatureData;
import net.minecraft.text.Text;
@Mixin(ChatHud.class)
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)
public void onAddMessage(Text message, @Nullable MessageSignatureData signatureData,
@Nullable MessageIndicator indicator, CallbackInfo ci) {
String msg = message.toString();
if (msg.contains("$SATURN_RELOAD")) {
for (Map.Entry<String, String> player : Auth.playerNames.entrySet()) {
String name = player.getKey();
if (msg.contains(name)) {
Auth.player(name, player.getValue());
}
ci.cancel();
}
}
}
}
@@ -1,7 +1,10 @@
package org.saturnclient.saturnclient.mixin;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import org.jetbrains.annotations.Nullable;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.auth.Auth;
@@ -29,7 +32,11 @@ public abstract class TabListMixin {
String name = profile.getName();
String uuid = profile.getId().toString().replaceAll("-", "");
return Text.literal(
Auth.players.containsKey(uuid) ? SaturnClientConfig.getSaturnIndicator() + name : name);
MutableText iconText = Text.literal(SaturnClientConfig.getSaturnIndicator())
.styled(style -> style.withColor(SaturnClientConfig.getIconColor(uuid)));
Text nameText = Text.literal(name).styled(style -> style.withColor(Formatting.WHITE));
return Auth.players.containsKey(uuid) ? iconText.append(nameText) : Text.literal(name);
}
}
@@ -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,37 +0,0 @@
package org.saturnclient.saturnclient.mixin;
import net.minecraft.client.particle.AnimatedParticle;
import net.minecraft.client.particle.SpriteProvider;
import net.minecraft.client.particle.TotemParticle;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.ColorHelper;
import org.saturnclient.saturnmods.mods.Particles;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(TotemParticle.class)
public class TotemParticleMixin extends AnimatedParticle {
protected TotemParticleMixin(ClientWorld world, double x, double y, double z, SpriteProvider spriteProvider,
float upwardsAcceleration) {
super(world, x, y, z, spriteProvider, upwardsAcceleration);
}
@Inject(at = @At("TAIL"), method = "<init>")
private void colorChanger(ClientWorld world, double x, double y, double z, double velocityX, double velocityY,
double velocityZ, SpriteProvider spriteProvider, CallbackInfo ci) {
if (Particles.enabled.value && Particles.totem_enabled.value) {
if (this.random.nextInt(2) == 0) {
this.setColor(ColorHelper.getRedFloat(Particles.totem_1.value),
ColorHelper.getGreenFloat(Particles.totem_1.value),
ColorHelper.getBlueFloat(Particles.totem_1.value));
} else {
this.setColor(ColorHelper.getRedFloat(Particles.totem_2.value),
ColorHelper.getGreenFloat(Particles.totem_2.value),
ColorHelper.getBlueFloat(Particles.totem_2.value));
}
}
}
}
@@ -1,11 +0,0 @@
package org.saturnclient.saturnmods;
import net.minecraft.client.gui.DrawContext;
public interface HudMod {
public ModDimensions getDimensions();
public void renderDummy(DrawContext context);
public void render(DrawContext context);
}
@@ -1,64 +0,0 @@
package org.saturnclient.saturnmods;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
public class ModDimensions {
public Property<Integer> x = new Property<>(0);
public Property<Integer> y = new Property<>(0);
public Property<Float> scale = new Property<>(1.0f);
public int width = 0;
public int height = 0;
private final ConfigManager dimensionsConfig;
public ModDimensions(ConfigManager configManager) {
dimensionsConfig = new ConfigManager(configManager, "Dimensions");
dimensionsConfig.property("x", this.x);
dimensionsConfig.property("y", this.y);
dimensionsConfig.property("scale", this.scale);
}
public ModDimensions(ConfigManager configManager, int x, int y) {
dimensionsConfig = new ConfigManager(configManager, "Dimensions");
this.x.value = x;
this.y.value = y;
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) {
dimensionsConfig = new ConfigManager(configManager, "Dimensions");
this.x.value = x;
this.y.value = y;
this.width = width;
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) {
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.y.value = y;
this.scale.value = scale;
this.width = width;
this.height = height;
dimensionsConfig.property("x", this.x);
dimensionsConfig.property("y", this.y);
dimensionsConfig.property("scale", this.scale);
}
}
@@ -1,50 +0,0 @@
package org.saturnclient.saturnmods;
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.util.math.MatrixStack;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.menus.HudEditor;
import org.saturnclient.saturnmods.mods.*;
public class ModManager {
public static SaturnMod[] MODS = {
new Coordinates(),
new ArmorDisplay(),
new FpsDisplay(),
new Particles(),
new Crosshair(),
new FreeLook()
};
public static void init() {
HudRenderCallback.EVENT.register((context, _o) -> {
MinecraftClient client = MinecraftClient.getInstance();
TextRenderer textRenderer = client.textRenderer;
if (textRenderer != null &&
!(client.currentScreen instanceof HudEditor)) {
SaturnClientConfig.textRenderer = textRenderer;
for (SaturnMod m : MODS) {
if (m instanceof HudMod && m.isEnabled()) {
ModDimensions dim = ((HudMod) m).getDimensions();
MatrixStack matrices = context.getMatrices();
matrices.push();
matrices.translate(dim.x.value, (float) dim.y.value, 0);
matrices.scale(dim.scale.value, dim.scale.value, 1.0f);
((HudMod) m).render(context);
matrices.pop();
}
}
}
});
}
}
@@ -1,17 +0,0 @@
package org.saturnclient.saturnmods;
import org.saturnclient.saturnclient.config.ConfigManager;
import net.minecraft.util.Identifier;
public interface SaturnMod {
public boolean isEnabled();
public void setEnabled(boolean e);
public String getName();
public Identifier getIconTexture();
public ConfigManager getConfig();
}
@@ -1,130 +0,0 @@
package org.saturnclient.saturnmods.mods;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.Identifier;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnmods.HudMod;
import org.saturnclient.saturnmods.ModDimensions;
import org.saturnclient.saturnmods.SaturnMod;
import org.saturnclient.ui.Textures;
public class ArmorDisplay implements SaturnMod, HudMod {
private static ConfigManager config = new ConfigManager("Armor Display");
public static Property<Boolean> enabled = config.property(
"Enabled",
new Property<>(false));
private static ModDimensions dimensions = new ModDimensions(
config,
0,
0,
34,
75);
public static Property<Integer> fgColor = config.property(
"Foreground color",
new Property<>(SaturnClientConfig.WHITE, Property.PropertyType.HEX));
private boolean renderItem(DrawContext context, ItemStack item, int y) {
if (item.isEmpty()) {
return false;
}
context.drawItem(item, 0, y);
if (item.getMaxDamage() > 0) {
MatrixStack matrices = context.getMatrices();
matrices.push();
matrices.translate(17, y + 5, 0);
matrices.scale(0.8f, 0.8f, 0f);
context.drawText(
SaturnClientConfig.textRenderer,
"" + (item.getMaxDamage() - item.getDamage()),
0,
0,
fgColor.value,
false);
matrices.pop();
}
return true;
}
@Override
public void render(DrawContext context) {
PlayerEntity player = MinecraftClient.getInstance().player;
ItemStack[] items = {
player.getEquippedStack(EquipmentSlot.MAINHAND),
player.getEquippedStack(EquipmentSlot.FEET),
player.getEquippedStack(EquipmentSlot.LEGS),
player.getEquippedStack(EquipmentSlot.CHEST),
player.getEquippedStack(EquipmentSlot.HEAD),
};
int y = 60;
for (ItemStack item : items) {
if (renderItem(context, item, y)) {
y -= 15;
}
}
}
@Override
public void renderDummy(DrawContext context) {
ItemStack[] items = {
new ItemStack(Items.DIAMOND_SWORD),
new ItemStack(Items.DIAMOND_BOOTS),
new ItemStack(Items.DIAMOND_LEGGINGS),
new ItemStack(Items.DIAMOND_CHESTPLATE),
new ItemStack(Items.DIAMOND_HELMET),
};
int y = 60;
for (ItemStack item : items) {
if (renderItem(context, item, y)) {
y -= 15;
}
}
}
public String getName() {
return "Armor display";
}
@Override
public Identifier getIconTexture() {
return Textures.getModIcon("armor");
}
public ModDimensions getDimensions() {
return dimensions;
}
public boolean isEnabled() {
return enabled.value;
}
public void setEnabled(boolean e) {
enabled.value = e;
}
@Override
public ConfigManager getConfig() {
return config;
}
}
@@ -1,92 +0,0 @@
package org.saturnclient.saturnmods.mods;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Identifier;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnmods.HudMod;
import org.saturnclient.saturnmods.ModDimensions;
import org.saturnclient.saturnmods.SaturnMod;
import org.saturnclient.ui.Textures;
public class Coordinates implements SaturnMod, HudMod {
private static ConfigManager config = new ConfigManager("Coordinates");
public static Property<Boolean> enabled = config.property(
"Enabled",
new Property<>(false));
private static ModDimensions dimensions = new ModDimensions(config);
public static Property<Integer> fgColor = config.property(
"Foreground color",
new Property<>(SaturnClientConfig.WHITE, Property.PropertyType.HEX));
public Coordinates() {
// Ensure fgColor has a value
if (fgColor.value == null) {
fgColor.value = SaturnClientConfig.WHITE;
}
}
public void renderDummy(DrawContext context) {
int playerX = 124;
int playerY = 69;
int playerZ = 83;
context.drawText(
SaturnClientConfig.textRenderer,
playerX + " " + playerY + " " + playerZ,
0,
0,
fgColor.value,
false);
dimensions.width = SaturnClientConfig.textRenderer.getWidth(
playerX + " " + playerY + " " + playerZ);
dimensions.height = SaturnClientConfig.textRenderer.fontHeight;
}
public void render(DrawContext context) {
PlayerEntity player = MinecraftClient.getInstance().player;
int playerX = (int) player.getX();
int playerY = (int) player.getY();
int playerZ = (int) player.getZ();
context.drawText(
SaturnClientConfig.textRenderer,
playerX + " " + playerY + " " + playerZ,
0,
0,
fgColor.value,
false);
}
public String getName() {
return "Coords";
}
@Override
public Identifier getIconTexture() {
return Textures.getModIcon("coords");
}
public ModDimensions getDimensions() {
return dimensions;
}
public boolean isEnabled() {
return enabled.value;
}
public void setEnabled(boolean e) {
enabled.value = e;
}
@Override
public ConfigManager getConfig() {
return config;
}
}
@@ -1,44 +0,0 @@
package org.saturnclient.saturnmods.mods;
import net.minecraft.util.Identifier;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnmods.SaturnMod;
import org.saturnclient.ui.Textures;
public class Crosshair implements SaturnMod {
public static ConfigManager config = new ConfigManager("Crosshair");
public static Property<Boolean> enabled = config.property(
"Enabled",
new Property<>(false));
public static Property<Boolean> range_indicator = config.property(
"Hit range indicator",
new Property<>(false));
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void setEnabled(boolean e) {
enabled.value = e;
}
@Override
public String getName() {
return "Crosshair";
}
@Override
public Identifier getIconTexture() {
return Textures.getModIcon("crosshair");
}
@Override
public ConfigManager getConfig() {
return config;
}
}
@@ -1,88 +0,0 @@
package org.saturnclient.saturnmods.mods;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.util.Identifier;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnmods.HudMod;
import org.saturnclient.saturnmods.ModDimensions;
import org.saturnclient.saturnmods.SaturnMod;
import org.saturnclient.ui.Textures;
public class FpsDisplay implements SaturnMod, HudMod {
public static ConfigManager config = new ConfigManager("Fps Display");
public static Property<Boolean> enabled = config.property(
"Enabled",
new Property<>(false));
public static ModDimensions dimensions = new ModDimensions(
config,
0,
0,
40,
8);
public static Property<Integer> fgColor = config.property(
"Foreground color",
new Property<>(SaturnClientConfig.WHITE, Property.PropertyType.HEX));
@Override
public ModDimensions getDimensions() {
return dimensions;
}
@Override
public void renderDummy(DrawContext context) {
int fps = 128;
context.drawText(
SaturnClientConfig.textRenderer,
String.valueOf(fps) + " FPS",
0,
0,
fgColor.value,
false);
}
@Override
public void render(DrawContext context) {
int fps = MinecraftClient.getInstance().getCurrentFps();
context.drawText(
SaturnClientConfig.textRenderer,
String.valueOf(fps) + " FPS",
0,
0,
fgColor.value,
false);
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void setEnabled(boolean e) {
enabled.value = e;
}
@Override
public String getName() {
return "Fps Display";
}
@Override
public Identifier getIconTexture() {
return Textures.getModIcon("fps");
}
@Override
public ConfigManager getConfig() {
return config;
}
}
@@ -1,46 +0,0 @@
package org.saturnclient.saturnmods.mods;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnmods.SaturnMod;
import org.saturnclient.ui.Textures;
import net.minecraft.client.option.Perspective;
import net.minecraft.util.Identifier;
public class FreeLook implements SaturnMod {
public static boolean isFreeLooking = false;
public static Perspective lastPerspective = null;
private static ConfigManager config = new ConfigManager("Free Look");
public static Property<Boolean> enabled = config.property(
"Enabled",
new Property<>(false));
public static Property<Boolean> toggle = config.property(
"Toggle Free Look",
new Property<>(false));
public boolean isEnabled() {
return enabled.value;
}
public void setEnabled(boolean e) {
enabled.value = e;
}
@Override
public ConfigManager getConfig() {
return config;
}
@Override
public Identifier getIconTexture() {
return Textures.getModIcon("freelook");
}
@Override
public String getName() {
return "Free Look";
}
}
@@ -1,55 +0,0 @@
package org.saturnclient.saturnmods.mods;
import net.minecraft.util.Identifier;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.config.ConfigManager;
import org.saturnclient.saturnclient.config.Property;
import org.saturnclient.saturnmods.SaturnMod;
import org.saturnclient.ui.Textures;
public class Particles implements SaturnMod {
public static ConfigManager config = new ConfigManager("Particles");
public static Property<Boolean> enabled = config.property(
"Enabled",
new Property<>(false));
public static ConfigManager totem = new ConfigManager(config, "Totem particles");
public static Property<Boolean> totem_enabled = totem.property(
"Change totem color",
new Property<>(false));
public static Property<Integer> totem_1 = totem.property(
"Totem particle color 1",
new Property<>(SaturnClientConfig.color.value, Property.PropertyType.HEX));
public static Property<Integer> totem_2 = totem.property(
"Totem particle color 2",
new Property<>(SaturnClientConfig.color.value, Property.PropertyType.HEX));
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void setEnabled(boolean e) {
enabled.value = e;
}
@Override
public String getName() {
return "Particles";
}
@Override
public Identifier getIconTexture() {
return Textures.getModIcon("particles");
}
@Override
public ConfigManager getConfig() {
return config;
}
}
@@ -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,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 net.minecraft.client.MinecraftClient;
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 = MinecraftClient.getInstance().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 net.minecraft.client.MinecraftClient;
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 = MinecraftClient.getInstance().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,179 +0,0 @@
package org.saturnclient.ui.components;
import net.minecraft.client.MinecraftClient;
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 SaturnInteger extends SaturnWidget {
String text = "";
public Property<Integer> prop;
public int cursorPosition = 0;
private TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
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 = 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) && 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(
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;
}
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,105 +0,0 @@
package org.saturnclient.ui.components;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import org.saturnclient.saturnclient.SaturnClientConfig;
import org.saturnclient.saturnclient.menus.ConfigEditor;
import org.saturnclient.saturnmods.SaturnMod;
import org.saturnclient.ui.SaturnUi;
import org.saturnclient.ui.SaturnWidget;
import org.saturnclient.ui.Textures;
public class SaturnModComp extends SaturnWidget {
SaturnMod mod;
public SaturnModComp(SaturnMod 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(
SaturnClientConfig.textRenderer,
SaturnUi.text(mod.getName()),
20,
(21 - SaturnClientConfig.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)) {
MinecraftClient.getInstance()
.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 net.minecraft.client.MinecraftClient;
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 = MinecraftClient.getInstance().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,79 +0,0 @@
package org.saturnclient.ui.components;
import org.jetbrains.annotations.Nullable;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.saturnclient.ui.SaturnWidget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.DiffuseLighting;
import net.minecraft.client.render.entity.EntityRenderDispatcher;
import net.minecraft.entity.LivingEntity;
// If you think this code is bad, it's because it's written by Mojang
public class SkinPreview extends SaturnWidget {
public static void drawEntity(DrawContext context, int x1, int y1, int x2, int y2, int size, float f, float mouseX,
float mouseY, LivingEntity entity, float angle, boolean negativeAngle) {
float g = (float) (x1 + x2) / 2.0F;
float h = (float) (y1 + y2) / 2.0F;
context.enableScissor(0, 6, x2, y2);
float i = (float) Math.atan((double) ((g - mouseX) / 40.0F));
float j = (float) Math.atan((double) ((h - mouseY) / 40.0F));
Quaternionf quaternionf = (new Quaternionf()).rotateZ(3.1415927F);
float k = entity.bodyYaw;
float l = entity.getYaw();
float m = entity.getPitch();
float n = entity.prevHeadYaw;
float o = entity.headYaw;
entity.setYaw(angle);
entity.bodyYaw = angle;
entity.setPitch(-j * 20.0F);
entity.headYaw = negativeAngle ? -i * 40.0F : i * 40.0F;
entity.prevHeadYaw = entity.getHeadYaw();
entity.setCustomNameVisible(false);
float p = entity.getScale();
Vector3f vector3f = new Vector3f(0.0F, entity.getHeight() / 2.0F + f * p, 0.0F);
float q = (float) size / p;
drawEntity(context, g, h, q, vector3f, quaternionf, null, entity);
entity.setCustomNameVisible(true);
entity.bodyYaw = k;
entity.setYaw(l);
entity.setPitch(m);
entity.prevHeadYaw = n;
entity.headYaw = o;
context.disableScissor();
}
public static void drawEntity(DrawContext context, float x, float y, float size, Vector3f vector3f,
Quaternionf quaternionf, @Nullable Quaternionf quaternionf2, LivingEntity entity) {
context.getMatrices().push();
context.getMatrices().translate((double) x, (double) y, 50.0);
context.getMatrices().scale(size, size, -size);
context.getMatrices().translate(vector3f.x, vector3f.y, vector3f.z);
context.getMatrices().multiply(quaternionf);
context.draw();
DiffuseLighting.method_34742();
EntityRenderDispatcher entityRenderDispatcher = MinecraftClient.getInstance().getEntityRenderDispatcher();
entityRenderDispatcher.setRenderShadows(false);
context.draw((vertexConsumers) -> {
entityRenderDispatcher.render(entity, 0.0, 0.0, 0.0, 1.0F, context.getMatrices(), vertexConsumers,
15728880);
});
context.draw();
entityRenderDispatcher.setRenderShadows(true);
context.getMatrices().pop();
DiffuseLighting.enableGuiDepthLighting();
}
@Override
public void render(DrawContext context, boolean hovering, int mouseX, int mouseY) {
LivingEntity entity = MinecraftClient.getInstance().player;
if (entity != null) {
drawEntity(context, 0, 0, 75, 78, 30, 0.0625F, mouseX, mouseY, entity, -30.0f, true);
}
}
}
@@ -1,104 +0,0 @@
package org.saturnclient.ui.widgets;
import com.mojang.blaze3d.systems.RenderSystem;
import java.util.function.Consumer;
import net.minecraft.client.MinecraftClient;
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;
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 = MinecraftClient.getInstance();
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 net.minecraft.client.MinecraftClient;
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 = MinecraftClient.getInstance().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,36 +0,0 @@
package org.saturnclient.ui.widgets;
import net.minecraft.client.gui.DrawContext;
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 = SaturnClientConfig.textRenderer.getWidth(SaturnUi.text(text));
this.height = SaturnClientConfig.textRenderer.fontHeight;
}
public SaturnText(String text, int color) {
this(text);
}
@Override
public void render(
DrawContext context,
boolean hovering,
int mouseX,
int mouseY) {
context.drawText(
SaturnClientConfig.textRenderer,
SaturnUi.text(text),
0,
0,
SaturnClientConfig.getColor(hovering, alpha),
false);
}
}
@@ -0,0 +1,83 @@
package org.saturnclient.ui2;
import java.util.function.Function;
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 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.height = height;
return this;
}
public Element position(int x, int y) {
this.x = x;
this.y = y;
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);
}
}
@@ -0,0 +1,370 @@
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 org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.saturnclient.ui2.resources.Fonts;
import com.mojang.blaze3d.systems.RenderSystem;
import org.saturnclient.saturnclient.SaturnClient;
import net.minecraft.client.font.TextRenderer;
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.VertexConsumer;
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.entity.LivingEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ModelTransformationMode;
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.world.World;
public class RenderScope {
public MatrixStack matrices;
public VertexConsumerProvider.Immediate vertexConsumers;
// private Function<Identifier, RenderLayer> renderLayers;
private ScissorStack scissorStack = new ScissorStack();
private int opacity = 255 << 24;
private final ItemRenderState itemRenderState;
public RenderScope(MatrixStack matrices, VertexConsumerProvider.Immediate vertexConsumers) {
this.matrices = matrices;
this.vertexConsumers = vertexConsumers;
this.scissorStack = new ScissorStack();
this.itemRenderState = new ItemRenderState();
}
public void setOpacity(float alpha) {
this.opacity = (int) (alpha * 255) << 24;
}
public int getColor(int color) {
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) {
color = getColor(color);
Matrix4f matrix4f = this.matrices.peek().getPositionMatrix();
int x1 = x;
int x2 = x + width;
int y1 = y;
int y2 = y + height;
int i;
if (x1 < x2) {
i = x1;
x1 = x2;
x2 = i;
}
if (y1 < y2) {
i = y1;
y1 = y2;
y2 = i;
}
VertexConsumer vertexConsumer = this.vertexConsumers.getBuffer(RenderLayer.getGui());
vertexConsumer.vertex(matrix4f, (float) x1, (float) y1, 0).color(color);
vertexConsumer.vertex(matrix4f, (float) x1, (float) y2, 0).color(color);
vertexConsumer.vertex(matrix4f, (float) x2, (float) y2, 0).color(color);
vertexConsumer.vertex(matrix4f, (float) x2, (float) y1, 0).color(color);
}
public void drawText(String text, int x, int y, int font, int color) {
drawText(1.0f, text, x, y, font, 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 h = height * 20;
for (int y = 0; y < h; y++) {
int startX = 0;
if (y < radius) {
double dy = radius - y - 0.5;
double dx = Math.sqrt(Math.max(0, radius * radius - dy * dy));
startX = radius - (int) dx;
}
this.drawRect(startX, y, w - startX, 1, color);
}
}
private void drawRoundedSide(int cornerWidth, int cornerHeight, int radius, int color) {
// Top
this.matrices.push();
this.matrices.scale(0.05f, 0.05f, 1.0f);
this.drawRoundedCorner(cornerWidth, cornerHeight, radius * 10, color);
this.matrices.pop();
// Bottom
this.matrices.push();
this.matrices.translate(cornerWidth, cornerHeight * 2, 0);
this.matrices.multiply(RotationAxis.NEGATIVE_Z.rotationDegrees(90));
this.matrices.translate(0, -cornerWidth, 0);
this.matrices.scale(0.05f, 0.05f, 1.0f);
this.drawRoundedCorner(cornerHeight, cornerWidth, radius * 10, color);
this.matrices.pop();
}
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 cornerHeight = height / 2;
this.matrices.push();
this.matrices.translate(x, y, 0);
this.drawRoundedSide(cornerWidth, cornerHeight, radius, color);
this.matrices.translate(cornerWidth * 2, cornerHeight * 2, 0);
this.matrices.multiply(RotationAxis.NEGATIVE_Z.rotationDegrees(180));
this.matrices.translate(0, 0, 0);
this.drawRoundedSide(cornerWidth, cornerHeight, radius, color);
this.matrices.pop();
}
public int getScaledWindowWidth() {
return SaturnClient.client.getWindow().getScaledWidth();
}
public int getScaledWindowHeight() {
return SaturnClient.client.getWindow().getScaledHeight();
}
public void drawTexture(Identifier sprite, int x, int y, float u, float v, int width, int height, int color) {
this.drawTexture(sprite, x, y, u, v, width, height, width, height, width, height, color);
}
public void drawTexture(Identifier sprite, int x, int y, float u, float v, int width, int height) {
this.drawTexture(sprite, x, y, u, v, width, height, width, height, width, height);
}
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(sprite, x, y, u, v, width, height, regionWith, regionHeight, textureWidth, textureHeight, -1);
}
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(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(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);
RenderLayer renderLayer = (RenderLayer)RenderLayer.getGuiTextured(sprite);
Matrix4f matrix4f = this.matrices.peek().getPositionMatrix();
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)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)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);
}
}
}
@@ -0,0 +1,161 @@
package org.saturnclient.ui2;
import java.util.ArrayList;
import java.util.List;
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.RotatingCubeMapRenderer;
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.util.Identifier;
public abstract class SaturnScreen extends Screen {
private final Pool pool = new Pool(3);
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) {
super(Text.literal(title));
}
public void draw(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);
}
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
protected void init() {
elements.clear();
width *= 2;
height *= 2;
ui(); // abstraction to render the saturn ui and also render extra stuff here
Animation.execute((Float progress) -> {
blurProgress = progress;
}, blurDuration);
}
public abstract void ui();
@SuppressWarnings("deprecation")
@Override
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
// use DrawContext
RenderScope renderScope = new RenderScope(context.getMatrices(),
((DrawContextAccessor) context).getVertexConsumers());
renderScope.matrices.push();
renderScope.matrices.scale(0.5f, 0.5f, 0.5f);
ElementRenderer.render(new ArrayList<>(elements), renderScope, mouseX, mouseY);
renderScope.matrices.pop();
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
mouseX *= 2;
mouseY *= 2;
ElementRenderer.mouseClicked(elements, mouseX, mouseY, button);
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);
}
}
@@ -0,0 +1,55 @@
package org.saturnclient.ui2;
import java.util.List;
import org.lwjgl.glfw.GLFW;
import org.saturnclient.saturnclient.SaturnClient;
public class Utils {
public static boolean isHovering(int mouseX, int mouseY, int elementWidth, int elementHeight, float elementScale) {
return mouseX >= 0 &&
mouseX <= (elementWidth * elementScale) &&
mouseY >= 0 &&
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;
}
}
}
}
@@ -0,0 +1,87 @@
package org.saturnclient.ui2.components;
import org.jetbrains.annotations.Nullable;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.saturnclient.saturnclient.SaturnClient;
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.entity.EntityRenderDispatcher;
import net.minecraft.entity.LivingEntity;
public class SkinPreview extends Element {
private boolean negativeAngle = false;
private float angle = 0.0f;
public SkinPreview(float angle, boolean negativeAngle) {
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 g = (float) (x1 + x2) / 2.0F;
float h = (float) (y1 + y2) / 2.0F;
renderScope.enableScissor(0, 6, x2, y2);
float i = (float) Math.atan((double) ((g - mouseX) / 40.0F));
float j = (float) Math.atan((double) ((h - mouseY) / 40.0F));
Quaternionf quaternionf = (new Quaternionf()).rotateZ(3.1415927F);
float k = entity.bodyYaw;
float l = entity.getYaw();
float m = entity.getPitch();
float n = entity.prevHeadYaw;
float o = entity.headYaw;
entity.setYaw(angle);
entity.bodyYaw = angle;
entity.setPitch(-(j * 20.0F));
entity.headYaw = negativeAngle ? (i * 40.0F) + 180f : -(i * 40.0F);
entity.prevHeadYaw = entity.getHeadYaw();
entity.setCustomNameVisible(false);
float p = entity.getScale();
Vector3f vector3f = new Vector3f(0.0F, entity.getHeight() / 2.0F + f * p, 0.0F);
float q = (float) size / p;
drawEntity(renderScope, g, h, q, vector3f, quaternionf, null, entity);
entity.setCustomNameVisible(true);
entity.bodyYaw = k;
entity.setYaw(l);
entity.setPitch(m);
entity.prevHeadYaw = n;
entity.headYaw = o;
renderScope.disableScissor();
}
public static void drawEntity(RenderScope renderScope, float x, float y, float size, Vector3f vector3f,
Quaternionf quaternionf, @Nullable Quaternionf quaternionf2, LivingEntity entity) {
renderScope.matrices.push();
renderScope.matrices.translate((double) x, (double) y, 50.0);
renderScope.matrices.scale(size, size, -size);
renderScope.matrices.translate(vector3f.x, vector3f.y, vector3f.z);
renderScope.matrices.multiply(quaternionf);
renderScope.draw();
DiffuseLighting.method_34742();
EntityRenderDispatcher entityRenderDispatcher = SaturnClient.client.getEntityRenderDispatcher();
entityRenderDispatcher.setRenderShadows(false);
renderScope.draw((vertexConsumers) -> {
entityRenderDispatcher.render(entity, 0.0, 0.0, 0.0, 1.0F, renderScope.matrices, vertexConsumers,
15728880);
});
renderScope.draw();
entityRenderDispatcher.setRenderShadows(true);
renderScope.matrices.pop();
DiffuseLighting.enableGuiDepthLighting();
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
LivingEntity entity = SaturnClient.client.player;
if (entity != null) {
drawEntity(renderScope, 0, 0, 75, 78, 30, 0.0625F, ctx.mouseX / scale, ctx.mouseY / scale, entity, angle, negativeAngle);
}
}
}

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