Merge branch 'master' into master
@@ -1,44 +1,126 @@
|
||||
/*
|
||||
* ================================================================
|
||||
* Saturn Client - Fabric Mod Build Configuration
|
||||
* ================================================================
|
||||
*
|
||||
* This build file configures:
|
||||
* - Fabric Loom (Minecraft mod development)
|
||||
* - Dependency management
|
||||
* - Resource processing
|
||||
* - Java toolchain (Java 21)
|
||||
* - Publishing setup
|
||||
*
|
||||
* All version values are defined in gradle.properties.
|
||||
* ================================================================
|
||||
*/
|
||||
|
||||
plugins {
|
||||
// Fabric mod development plugin
|
||||
id 'fabric-loom' version '1.9-SNAPSHOT'
|
||||
|
||||
// Maven publishing support
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
/*
|
||||
* ================================================================
|
||||
* Project Metadata
|
||||
* ================================================================
|
||||
*/
|
||||
|
||||
version = project.mod_version
|
||||
group = project.maven_group
|
||||
|
||||
repositories {}
|
||||
/*
|
||||
* ================================================================
|
||||
* Repositories
|
||||
* ================================================================
|
||||
* All dependency sources.
|
||||
*/
|
||||
repositories {
|
||||
// Fabric
|
||||
maven { url 'https://maven.fabricmc.net/' }
|
||||
|
||||
// JitPack (GitHub releases)
|
||||
maven { url 'https://jitpack.io' }
|
||||
|
||||
// Parchment mappings (if needed later)
|
||||
maven { url 'https://maven.parchmentmc.org' }
|
||||
|
||||
// Special Model Loader
|
||||
maven { name = "FelNull Maven"; url = "https://maven.felnull.dev" }
|
||||
|
||||
// Player Animation Library
|
||||
maven { name = "KosmX Maven"; url = 'https://maven.kosmx.dev/' }
|
||||
|
||||
// Central repository
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
/*
|
||||
* ================================================================
|
||||
* Dependencies
|
||||
* ================================================================
|
||||
*/
|
||||
dependencies {
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Minecraft Core
|
||||
// ─────────────────────────────────────────────
|
||||
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
||||
|
||||
// Switch back to Yarn
|
||||
// Yarn mappings (named mappings for dev environment)
|
||||
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
|
||||
|
||||
// Fabric Loader
|
||||
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||
|
||||
// Fabric API
|
||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Rendering / Model Libraries
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
// Special Model Loader (included inside your mod jar)
|
||||
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}")
|
||||
// SVG Rendering Library
|
||||
implementation "com.formdev:svgSalamander:1.1.4"
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// Animation
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
// Player Animation Library (bundled into your mod)
|
||||
include modImplementation(
|
||||
"dev.kosmx.player-anim:player-animation-lib-fabric:${project.player_anim}"
|
||||
)
|
||||
|
||||
// Optional: BendyLib (only if you directly use it)
|
||||
// include modRuntimeOnly(
|
||||
// "io.github.kosmx.bendy-lib:bendy-lib-fabric:${project.bendylib_version}"
|
||||
// )
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// External Libraries
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
// Session Java (auth/session handling)
|
||||
implementation "com.github.selimaj-dev:session-java:0.1.6"
|
||||
}
|
||||
|
||||
|
||||
repositories {
|
||||
maven { url "https://maven.parchmentmc.org" }
|
||||
maven {
|
||||
name = "FelNull Maven"
|
||||
url = "https://maven.felnull.dev"
|
||||
}
|
||||
|
||||
maven {
|
||||
name "KosmX's maven"
|
||||
url 'https://maven.kosmx.dev/'
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ================================================================
|
||||
* Resource Processing
|
||||
* ================================================================
|
||||
* Injects version values into fabric.mod.json.
|
||||
*/
|
||||
processResources {
|
||||
inputs.property "version", project.version
|
||||
inputs.property "minecraft_version", project.minecraft_version
|
||||
@@ -46,29 +128,42 @@ processResources {
|
||||
filteringCharset "UTF-8"
|
||||
|
||||
filesMatching("fabric.mod.json") {
|
||||
expand "version": project.version,
|
||||
expand(
|
||||
"version": project.version,
|
||||
"minecraft_version": project.minecraft_version,
|
||||
"loader_version": project.loader_version
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ================================================================
|
||||
* Java Configuration
|
||||
* ================================================================
|
||||
* Targeting Java 21.
|
||||
*/
|
||||
def targetJavaVersion = 21
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
it.options.encoding = "UTF-8"
|
||||
if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
|
||||
it.options.release.set(targetJavaVersion)
|
||||
}
|
||||
options.encoding = "UTF-8"
|
||||
options.release.set(targetJavaVersion)
|
||||
}
|
||||
|
||||
java {
|
||||
def javaVersion = JavaVersion.toVersion(targetJavaVersion)
|
||||
if (JavaVersion.current() < javaVersion) {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
|
||||
}
|
||||
|
||||
archivesBaseName = project.archives_base_name
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ================================================================
|
||||
* Jar Packaging
|
||||
* ================================================================
|
||||
* Includes LICENSE and NOTICE inside the final jar.
|
||||
*/
|
||||
jar {
|
||||
from("LICENSE") {
|
||||
rename { "${it}_${project.archivesBaseName}" }
|
||||
@@ -78,6 +173,13 @@ jar {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ================================================================
|
||||
* Publishing
|
||||
* ================================================================
|
||||
* Configured but no remote repository defined yet.
|
||||
*/
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
@@ -85,5 +187,7 @@ publishing {
|
||||
}
|
||||
}
|
||||
|
||||
repositories {}
|
||||
repositories {
|
||||
// Add publishing repository here when needed
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package org.saturnclient.modules;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.saturnclient.saturnclient.config.NamedProperty;
|
||||
import org.saturnclient.saturnclient.config.Property.NamedProperty;
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
|
||||
public class ModDimensions {
|
||||
@@ -26,11 +26,11 @@ public class ModDimensions {
|
||||
"Background Color", bgColor,
|
||||
"Foreground Color", fgColor,
|
||||
"Corner Radius", radius,
|
||||
"Font", font
|
||||
)).named("In-Game Display");
|
||||
"Font", font)).named("In-Game Display");
|
||||
}
|
||||
|
||||
public ModDimensions() {}
|
||||
public ModDimensions() {
|
||||
}
|
||||
|
||||
public ModDimensions(int width, int height) {
|
||||
this.width = width;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
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 org.saturnclient.saturnclient.config.Property.NamedProperty;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
@@ -19,8 +19,11 @@ public abstract class Module {
|
||||
}
|
||||
}
|
||||
|
||||
public void render(RenderScope scope) {}
|
||||
public void tick() {}
|
||||
public void render(RenderScope scope) {
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
}
|
||||
|
||||
public abstract boolean isEnabled();
|
||||
|
||||
|
||||
@@ -9,46 +9,45 @@ 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);
|
||||
|
||||
// Cache key states to avoid repeated calls
|
||||
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 final Property<Boolean> enabled = Property.bool(false);
|
||||
private static final Property<Boolean> showMouse = Property.bool(true);
|
||||
private static final Property<Boolean> showSpace = Property.bool(false);
|
||||
|
||||
// Cache dimensions to avoid recalculation
|
||||
private int cachedHeight = 54;
|
||||
private boolean dimensionsDirty = true;
|
||||
private static final Property<Integer> clickBg = Property.color(0xFFCCCCCC);
|
||||
private static final Property<Integer> clickFg = Property.color(0xFFFFFFFF);
|
||||
|
||||
private static Property<Integer> clickBg = Property.color(0xFFCCCCCC);
|
||||
private static Property<Integer> clickFg = Property.color(0xFFFFFFFF);
|
||||
private static final ModDimensions dimensions = new ModDimensions(78, 54);
|
||||
|
||||
// Key states
|
||||
private boolean w, a, s, d, lmb, rmb, space;
|
||||
|
||||
// Layout constants
|
||||
private static final int KEY_SIZE = 24;
|
||||
private static final int KEY_SPACING = 3;
|
||||
private static final int MOUSE_WIDTH = 38;
|
||||
private static final int MOUSE_HEIGHT = 24;
|
||||
private static final int SPACE_HEIGHT = 19;
|
||||
|
||||
public Keystrokes() {
|
||||
super(new ModuleDetails("Keystrokes", "keystrokes")
|
||||
.description("Displays the keystrokes for movement")
|
||||
.version("v0.1.0")
|
||||
.description("Displays movement keystrokes")
|
||||
.version("v0.2.0")
|
||||
.tags("Utility"),
|
||||
|
||||
enabled.named("Enabled"),
|
||||
showMouse.named("Show mouse clicks"),
|
||||
showSpace.named("Show space clicks"),
|
||||
showSpace.named("Show space"),
|
||||
dimensions.prop(),
|
||||
clickFg.named("Clicked fg"),
|
||||
clickBg.named("Clicked bg"));
|
||||
clickBg.named("Clicked bg")
|
||||
);
|
||||
|
||||
dimensions.renderBackground = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
// Cache key states to reduce method calls
|
||||
w = SaturnClient.client.options.forwardKey.isPressed();
|
||||
a = SaturnClient.client.options.leftKey.isPressed();
|
||||
s = SaturnClient.client.options.backKey.isPressed();
|
||||
@@ -57,103 +56,84 @@ public class Keystrokes extends Module implements HudMod {
|
||||
rmb = SaturnClient.client.options.useKey.isPressed();
|
||||
space = SaturnClient.client.options.jumpKey.isPressed();
|
||||
|
||||
// Mark dimensions as dirty if mouse or space visibility changed
|
||||
if (dimensionsDirty) {
|
||||
updateDimensions();
|
||||
dimensionsDirty = false;
|
||||
}
|
||||
updateHeight();
|
||||
}
|
||||
|
||||
private void updateDimensions() {
|
||||
cachedHeight = 54; // base height with padding considered
|
||||
private void updateHeight() {
|
||||
int height = KEY_SIZE * 2 + KEY_SPACING;
|
||||
|
||||
if (showMouse.value) {
|
||||
cachedHeight += 27;
|
||||
}
|
||||
if (showSpace.value)
|
||||
height += SPACE_HEIGHT + KEY_SPACING;
|
||||
|
||||
if (showSpace.value) {
|
||||
cachedHeight += 22;
|
||||
}
|
||||
if (showMouse.value)
|
||||
height += MOUSE_HEIGHT + KEY_SPACING;
|
||||
|
||||
dimensions.height = cachedHeight;
|
||||
dimensions.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderDummy(RenderScope scope) {
|
||||
// Use cached dimensions
|
||||
dimensions.height = cachedHeight;
|
||||
|
||||
// WASD keys (3px padding between each key)
|
||||
renderKey(scope, false, '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, false, "LMB", 0, 54); // Below A/S/D
|
||||
renderKeyM(scope, false, "RMB", 40, 54); // With 3px between
|
||||
}
|
||||
|
||||
if (showSpace.value) {
|
||||
renderKeySpace(scope, false, 0, cachedHeight - (showSpace.value ? 22 : 0));
|
||||
}
|
||||
render(scope, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderHud(RenderScope scope) {
|
||||
// Use cached dimensions
|
||||
dimensions.height = cachedHeight;
|
||||
|
||||
// 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
|
||||
render(scope, true);
|
||||
}
|
||||
|
||||
private void render(RenderScope scope, boolean live) {
|
||||
|
||||
int yOffset = 0;
|
||||
|
||||
// --- W ---
|
||||
renderKey(scope, live && w, 'W', KEY_SIZE + KEY_SPACING, yOffset);
|
||||
|
||||
// --- A S D ---
|
||||
yOffset += KEY_SIZE + KEY_SPACING;
|
||||
|
||||
renderKey(scope, live && a, 'A', 0, yOffset);
|
||||
renderKey(scope, live && s, 'S', KEY_SIZE + KEY_SPACING, yOffset);
|
||||
renderKey(scope, live && d, 'D', (KEY_SIZE + KEY_SPACING) * 2, yOffset);
|
||||
|
||||
yOffset += KEY_SIZE + KEY_SPACING;
|
||||
|
||||
// --- SPACE ---
|
||||
if (showSpace.value) {
|
||||
renderKeySpace(scope, space, 0, cachedHeight - (showSpace.value ? 22 : 0));
|
||||
renderKeySpace(scope, live && space, 0, yOffset);
|
||||
yOffset += SPACE_HEIGHT + KEY_SPACING;
|
||||
}
|
||||
|
||||
// --- MOUSE ---
|
||||
if (showMouse.value) {
|
||||
renderKeyMouse(scope, live && lmb, "LMB", 0, yOffset);
|
||||
renderKeyMouse(scope, live && rmb, "RMB", MOUSE_WIDTH + KEY_SPACING, yOffset);
|
||||
}
|
||||
}
|
||||
|
||||
// Consistent 3px padding applied below:
|
||||
private void renderKey(RenderScope scope, boolean pressed, char c, int x, int y) {
|
||||
int bg = pressed ? clickBg.value : dimensions.bgColor.value;
|
||||
int fg = pressed ? clickFg.value : dimensions.fgColor.value;
|
||||
|
||||
private void renderKeySpace(RenderScope scope, boolean isPressed, int x, int y) {
|
||||
int height = 19;
|
||||
int bgColor = isPressed ? clickBg.value : dimensions.bgColor.value;
|
||||
int fgColor = isPressed ? clickFg.value : dimensions.fgColor.value;
|
||||
|
||||
scope.drawRoundedRectangle(x, y, dimensions.width, height, dimensions.radius.value, bgColor);
|
||||
|
||||
// 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, fgColor);
|
||||
scope.drawRoundedRectangle(x, y, KEY_SIZE, KEY_SIZE, dimensions.radius.value, bg);
|
||||
scope.drawText(0.6f, String.valueOf(c), x + 9, y + 7, dimensions.font.value, fg);
|
||||
}
|
||||
|
||||
private void renderKeyM(RenderScope scope, boolean isPressed, String c, int x, int y) {
|
||||
int width = 38, height = 24;
|
||||
int bgColor = isPressed ? clickBg.value : dimensions.bgColor.value;
|
||||
int fgColor = isPressed ? clickFg.value : dimensions.fgColor.value;
|
||||
private void renderKeyMouse(RenderScope scope, boolean pressed, String text, int x, int y) {
|
||||
int bg = pressed ? clickBg.value : dimensions.bgColor.value;
|
||||
int fg = pressed ? clickFg.value : dimensions.fgColor.value;
|
||||
|
||||
scope.drawRoundedRectangle(x, y, width, height, dimensions.radius.value, bgColor);
|
||||
|
||||
// Apply padding: 3px top and left -> add slight offset to x/y
|
||||
scope.drawText(0.6f, c, x + 9, y + 7, dimensions.font.value, fgColor);
|
||||
scope.drawRoundedRectangle(x, y, MOUSE_WIDTH, MOUSE_HEIGHT, dimensions.radius.value, bg);
|
||||
scope.drawText(0.6f, text, x + 9, y + 7, dimensions.font.value, fg);
|
||||
}
|
||||
|
||||
private void renderKey(RenderScope scope, boolean isPressed, char c, int x, int y) {
|
||||
int size = 24;
|
||||
int bgColor = isPressed ? clickBg.value : dimensions.bgColor.value;
|
||||
int fgColor = isPressed ? clickFg.value : dimensions.fgColor.value;
|
||||
private void renderKeySpace(RenderScope scope, boolean pressed, int x, int y) {
|
||||
int bg = pressed ? clickBg.value : dimensions.bgColor.value;
|
||||
int fg = pressed ? clickFg.value : dimensions.fgColor.value;
|
||||
|
||||
scope.drawRoundedRectangle(x, y, size, size, dimensions.radius.value, bgColor);
|
||||
scope.drawRoundedRectangle(x, y, dimensions.width, SPACE_HEIGHT, dimensions.radius.value, bg);
|
||||
|
||||
// Center the character with 3px top padding
|
||||
scope.drawText(0.6f, String.valueOf(c), x + 9, y + 7, dimensions.font.value, fgColor);
|
||||
int lineY = y + SPACE_HEIGHT / 2;
|
||||
scope.drawRect(x + (dimensions.width - 30) / 2, lineY, 30, 1, fg);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -169,6 +149,5 @@ public class Keystrokes extends Module implements HudMod {
|
||||
@Override
|
||||
public void onEnabled(boolean e) {
|
||||
enabled.value = e;
|
||||
dimensionsDirty = true;
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,9 @@ 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.client.ServiceClient;
|
||||
import org.saturnclient.saturnclient.config.ConfigManager;
|
||||
import org.saturnclient.saturnclient.config.SaturnClientConfig;
|
||||
import org.saturnclient.saturnclient.cosmetics.Emotes;
|
||||
import org.saturnclient.saturnclient.cosmetics.Hats;
|
||||
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
|
||||
@@ -40,6 +41,10 @@ public class SaturnClient implements ModInitializer {
|
||||
SaturnClientConfig.init();
|
||||
ModManager.init();
|
||||
|
||||
client.execute(() -> {
|
||||
SaturnScreen.preload(client);
|
||||
});
|
||||
|
||||
ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> ConfigManager.save());
|
||||
|
||||
SpecialModelLoaderEvents.LOAD_SCOPE.register(() -> {
|
||||
@@ -75,7 +80,7 @@ public class SaturnClient implements ModInitializer {
|
||||
|
||||
KeyInputHandler.register();
|
||||
Emotes.initialize();
|
||||
if (Auth.authenticate()) {
|
||||
if (ServiceClient.authenticate()) {
|
||||
Cloaks.initialize();
|
||||
Hats.initialize();
|
||||
LOGGER.info(MOD_ID + " initialization complete");
|
||||
|
||||
@@ -1,283 +0,0 @@
|
||||
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.network.AbstractClientPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import org.saturnclient.saturnclient.cosmetics.Hats;
|
||||
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
|
||||
import org.saturnclient.ui2.Utils;
|
||||
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 {
|
||||
public static Map<String, SaturnPlayer> players = new HashMap<>();
|
||||
public static Map<String, String> playerNames = new HashMap<>();
|
||||
public static String uuid;
|
||||
private static Thread listenerThread;
|
||||
private static Thread pingThread;
|
||||
private static volatile boolean running = false;
|
||||
|
||||
public static boolean authenticate() {
|
||||
// Register shutdown hook ONCE
|
||||
ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> close());
|
||||
|
||||
try {
|
||||
if (SaturnClient.client.getSession() == null) {
|
||||
SaturnClient.LOGGER.error("No active Minecraft session found");
|
||||
return false;
|
||||
}
|
||||
|
||||
String accessToken = SaturnClient.client.getSession().getAccessToken();
|
||||
SaturnClient.LOGGER.info("Authenticating");
|
||||
|
||||
Network.init();
|
||||
|
||||
Network.write(accessToken);
|
||||
|
||||
String response = Network.read();
|
||||
|
||||
SaturnParser parser = new SaturnParser(response);
|
||||
|
||||
uuid = parser.getString("uuid");
|
||||
String cloak = parser.getString("cloak");
|
||||
String hat = parser.getString("hat");
|
||||
|
||||
for (String availableCloak : parser.getArray("cloaks")) {
|
||||
Cloaks.availableCloaks.add(availableCloak);
|
||||
}
|
||||
|
||||
for (String availableHat : parser.getArray("hats")) {
|
||||
Hats.availableHats.add(availableHat);
|
||||
}
|
||||
|
||||
players.put(uuid, new SaturnPlayer(cloak, hat));
|
||||
|
||||
Cloaks.loadCloak(uuid);
|
||||
|
||||
playerNames.put(SaturnClient.client.getSession().getUsername(), uuid);
|
||||
|
||||
afterAuth();
|
||||
|
||||
startListenerThread();
|
||||
startPingThread();
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
SaturnClient.LOGGER.error("Authentication failed", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void startListenerThread() {
|
||||
running = true;
|
||||
listenerThread = new Thread(() -> {
|
||||
try {
|
||||
while (running) {
|
||||
String message = Network.read();
|
||||
if (message == null) {
|
||||
SaturnClient.LOGGER.error("Connection closed");
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
|
||||
SaturnClient.LOGGER.info("Received: " + message);
|
||||
|
||||
SaturnParser parser = new SaturnParser(message);
|
||||
|
||||
switch (parser.method) {
|
||||
case "player":
|
||||
String uuid = parser.getString("uuid");
|
||||
if (parser.getBool("saturn")) {
|
||||
String cloak = parser.getString("cloak");
|
||||
String hat = parser.getString("hat");
|
||||
players.put(uuid, new SaturnPlayer(cloak, hat));
|
||||
Cloaks.loadCloak(uuid);
|
||||
} else {
|
||||
playerNames.entrySet().removeIf(entry -> entry.getValue().equals(uuid));
|
||||
}
|
||||
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) {
|
||||
String[] a = parser.error.replace("!", "").split(": ");
|
||||
Utils.notify(NotificationKind.Error, a[0], a[1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
SaturnClient.LOGGER.error("Error reading from server", e);
|
||||
} finally {
|
||||
close();
|
||||
}
|
||||
});
|
||||
|
||||
listenerThread.setDaemon(true);
|
||||
listenerThread.start();
|
||||
}
|
||||
|
||||
private static void startPingThread() {
|
||||
pingThread = new Thread(() -> {
|
||||
try {
|
||||
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 (Exception e) {
|
||||
if (!(e instanceof InterruptedException)) {
|
||||
SaturnClient.LOGGER.error("Error in ping thread", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
pingThread.setDaemon(true);
|
||||
pingThread.start();
|
||||
}
|
||||
|
||||
public static void close() {
|
||||
running = false;
|
||||
if (pingThread != null) {
|
||||
pingThread.interrupt();
|
||||
}
|
||||
if (listenerThread != null) {
|
||||
listenerThread.interrupt();
|
||||
}
|
||||
try {
|
||||
close();
|
||||
SaturnClient.LOGGER.info("SaturnApi connection closed.");
|
||||
} catch (Exception e) {
|
||||
SaturnClient.LOGGER.error("Failed to close resources", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void afterAuth() {
|
||||
PlayerTracker.initialize();
|
||||
}
|
||||
|
||||
public static void setCloak(String cloakName) {
|
||||
try {
|
||||
Network.write("set_cloak@cloak=" + cloakName + "@notify=" + stringifyPlayers());
|
||||
} catch (Exception e) {
|
||||
SaturnClient.LOGGER.error("Request failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setHat(String hatName) {
|
||||
try {
|
||||
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("-", ""));
|
||||
Network.write("player@uuid=" + uuid.replaceAll("-", ""));
|
||||
} catch (Exception e) {
|
||||
SaturnClient.LOGGER.error("Request failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package org.saturnclient.saturnclient.auth;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.security.*;
|
||||
import java.util.Base64;
|
||||
import javax.crypto.*;
|
||||
import javax.crypto.spec.*;
|
||||
import java.security.interfaces.*;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
public class Network {
|
||||
static Socket socket;
|
||||
static PrintWriter out;
|
||||
static BufferedReader in;
|
||||
static byte[] key = new byte[32];
|
||||
|
||||
private static final int CONNECTION_TIMEOUT = 5000;
|
||||
private static final int READ_TIMEOUT = 5000;
|
||||
|
||||
public static class AES {
|
||||
public static byte[] encryptAES256(String message, byte[] key) throws Exception {
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
|
||||
byte[] encryptedMessage = cipher.doFinal(message.getBytes());
|
||||
return encryptedMessage;
|
||||
}
|
||||
|
||||
public static String decryptAES256(byte[] encryptedMessage, byte[] key) throws Exception {
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
|
||||
byte[] decryptedMessage = cipher.doFinal(encryptedMessage);
|
||||
return new String(decryptedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static void init() throws Exception {
|
||||
// Generate AES key
|
||||
new SecureRandom().nextBytes(key);
|
||||
|
||||
socket = new Socket();
|
||||
socket.connect(new InetSocketAddress("77.247.92.168", 8080), CONNECTION_TIMEOUT);
|
||||
|
||||
socket.setSoTimeout(READ_TIMEOUT);
|
||||
|
||||
// out = new DataOutputStream(socket.getOutputStream());
|
||||
out = new PrintWriter(socket.getOutputStream(), true);
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
StringBuilder publicKeyBuilder = new StringBuilder();
|
||||
String line;
|
||||
while (!(line = in.readLine()).contains("-----END PUBLIC KEY-----")) {
|
||||
publicKeyBuilder.append(line).append("\n");
|
||||
}
|
||||
publicKeyBuilder.append(line);
|
||||
|
||||
String publicKeyPem = publicKeyBuilder.toString();
|
||||
|
||||
String publicKeyBase64 = publicKeyPem.replace("-----BEGIN PUBLIC KEY-----", "")
|
||||
.replace("-----END PUBLIC KEY-----", "").replace("\n", "").replace("\r", "");
|
||||
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
|
||||
|
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
|
||||
|
||||
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||
rsaCipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
byte[] encryptedKey = rsaCipher.doFinal(key);
|
||||
|
||||
out.println(Base64.getEncoder().encodeToString(encryptedKey));
|
||||
}
|
||||
|
||||
public static void write(String message) {
|
||||
try {
|
||||
out.println(Base64.getEncoder().encodeToString(AES.encryptAES256(message, key)));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to write to server: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static String read() {
|
||||
try {
|
||||
String msg = in.readLine();
|
||||
if (msg != null) {
|
||||
return AES.decryptAES256(Base64.getDecoder().decode(msg), key);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static void close() {
|
||||
try {
|
||||
socket.close();
|
||||
out.close();
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package org.saturnclient.saturnclient.auth;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SaturnParser {
|
||||
public String method = "";
|
||||
public String error;
|
||||
private Map<String, String> params = new HashMap<>();
|
||||
|
||||
public SaturnParser(String input) throws IOException {
|
||||
String[] parts = input.split("@");
|
||||
|
||||
if (parts.length == 0) {
|
||||
throw new IOException("Invalid input string");
|
||||
}
|
||||
|
||||
if (parts[0].startsWith("!")) {
|
||||
error = input;
|
||||
}
|
||||
|
||||
method = parts[0];
|
||||
|
||||
for (int i = 1; i < parts.length; i++) {
|
||||
String[] keyValue = parts[i].split("=");
|
||||
if (keyValue.length == 2) {
|
||||
params.put(keyValue[0].trim(), keyValue[1].trim());
|
||||
} else if (keyValue.length == 1) {
|
||||
params.put(keyValue[0].trim(), "");
|
||||
} else {
|
||||
throw new IOException("Invalid parameter format: " + parts[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getString(String i) throws IOException {
|
||||
String val = params.get(i);
|
||||
if (val != null) {
|
||||
return val;
|
||||
} else {
|
||||
throw new IOException("Parameter not found " + i);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
return val.equals("true");
|
||||
} else {
|
||||
throw new IOException("Parameter not found " + i);
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getArray(String i) throws IOException {
|
||||
String val = params.get(i);
|
||||
if (val != null) {
|
||||
if (val.isEmpty()) {
|
||||
return new String[] {};
|
||||
}
|
||||
return val.split("\\$");
|
||||
} else {
|
||||
throw new IOException("Parameter not found " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package org.saturnclient.saturnclient.auth;
|
||||
|
||||
public class SaturnPlayer {
|
||||
public String cloak = "";
|
||||
public String hat = "";
|
||||
|
||||
public SaturnPlayer(String cloak, String hat) {
|
||||
this.cloak = cloak;
|
||||
this.hat = hat;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
package org.saturnclient.saturnclient.client;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
|
||||
import org.saturnclient.saturnclient.cosmetics.Hats;
|
||||
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
|
||||
|
||||
import dev.kosmx.playerAnim.api.layered.AnimationStack;
|
||||
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationAccess;
|
||||
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationRegistry;
|
||||
import dev.selimaj.session.Session;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
|
||||
import net.minecraft.client.network.AbstractClientPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class ServiceClient {
|
||||
private static Session session;
|
||||
public static UUID uuid;
|
||||
|
||||
public static boolean connectTimeout() {
|
||||
try {
|
||||
session = Session.connect("wss://saturn-server.selimaj.dev", 10, TimeUnit.SECONDS);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean authenticate() {
|
||||
ClientLifecycleEvents.CLIENT_STOPPING.register(_o -> {
|
||||
if (session != null)
|
||||
try {
|
||||
session.close();
|
||||
} catch (Exception e) {
|
||||
SaturnClient.LOGGER.error("Unable to close Saturn session");
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
var mcSession = SaturnClient.client.getSession();
|
||||
if (mcSession == null) {
|
||||
SaturnClient.LOGGER.error("No active Minecraft session found");
|
||||
return false;
|
||||
}
|
||||
|
||||
String accessToken = mcSession.getAccessToken();
|
||||
uuid = mcSession.getUuidOrNull();
|
||||
String username = mcSession.getUsername();
|
||||
|
||||
SaturnClient.LOGGER.info("Authenticating with UUID: " + uuid);
|
||||
|
||||
if (!connectTimeout()) {
|
||||
SaturnClient.LOGGER.error("Unable to authenticate: Session Server Timeout");
|
||||
return false;
|
||||
}
|
||||
|
||||
ServiceMethods.Types.Player response = session.request(ServiceMethods.Authenticate, accessToken)
|
||||
.get();
|
||||
|
||||
for (String availableCloak : response.cloaks()) {
|
||||
Cloaks.availableCloaks.add(availableCloak);
|
||||
}
|
||||
|
||||
for (String availableHat : response.hats()) {
|
||||
Hats.availableHats.add(availableHat);
|
||||
}
|
||||
|
||||
eventHandlers();
|
||||
|
||||
SaturnPlayer.set(new SaturnPlayer(uuid, username, response.cloak(), response.hat()));
|
||||
|
||||
Cloaks.loadCloak(uuid);
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
SaturnClient.LOGGER.error("Authentication failed", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setCloak(String itemId) {
|
||||
try {
|
||||
session.request(ServiceMethods.SetCloak, itemId).whenComplete((msg, throwable) -> {
|
||||
if (throwable != null) {
|
||||
throwable.printStackTrace();
|
||||
} else {
|
||||
try {
|
||||
session.request(ServiceMethods.SendPlayer,
|
||||
new ServiceMethods.Types.SendPlayerRequest(SaturnPlayer.getExternalUUIDAsString()));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
SaturnClient.LOGGER.error("Failed to set cloak (service): ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setHat(String itemId) {
|
||||
try {
|
||||
session.request(ServiceMethods.SetHat, itemId).whenComplete((msg, throwable) -> {
|
||||
if (throwable != null) {
|
||||
throwable.printStackTrace();
|
||||
} else {
|
||||
try {
|
||||
session.request(ServiceMethods.SendPlayer,
|
||||
new ServiceMethods.Types.SendPlayerRequest(SaturnPlayer.getExternalUUIDAsString()));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
SaturnClient.LOGGER.error("Failed to set hat (service): ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void buyCloak(String itemId) {
|
||||
try {
|
||||
session.request(ServiceMethods.BuyCloak, itemId).whenComplete((msg, throwable) -> {
|
||||
if (throwable != null) {
|
||||
throwable.printStackTrace();
|
||||
} else {
|
||||
Cloaks.availableCloaks.add(itemId);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
SaturnClient.LOGGER.error("Failed to buy cloak (service): ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void buyHat(String itemId) {
|
||||
try {
|
||||
session.request(ServiceMethods.BuyHat, itemId).whenComplete((msg, throwable) -> {
|
||||
if (throwable != null) {
|
||||
throwable.printStackTrace();
|
||||
} else {
|
||||
Hats.availableHats.add(itemId);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
SaturnClient.LOGGER.error("Failed to buy hat (service): ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void emote(String emote) {
|
||||
try {
|
||||
session.request(ServiceMethods.Emote,
|
||||
new ServiceMethods.Types.EmoteRequest(emote, SaturnPlayer.getExternalUUIDAsString()))
|
||||
.whenComplete((msg, throwable) -> {
|
||||
if (throwable != null) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
SaturnClient.LOGGER.error("Failed to emote (service): ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void eventHandlers() {
|
||||
session.onNotification(ServiceMethods.EmoteEvent, (data) -> {
|
||||
for (AbstractClientPlayerEntity player : SaturnClient.client.world.getPlayers()) {
|
||||
if (player.getUuidAsString().equals(data.from())) {
|
||||
AnimationStack animationStack = PlayerAnimationAccess.getPlayerAnimLayer(player);
|
||||
if (data.emote() != null && !data.emote().isEmpty()) {
|
||||
if (animationStack.isActive() && animationStack.getPriority() == 1000) {
|
||||
animationStack.removeLayer(1000);
|
||||
}
|
||||
animationStack.addAnimLayer(1000,
|
||||
PlayerAnimationRegistry
|
||||
.getAnimation(Identifier.of("saturnclient", data.emote()))
|
||||
.playAnimation());
|
||||
} else {
|
||||
animationStack.removeLayer(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
session.onNotification(ServiceMethods.Player, (player) -> {
|
||||
System.out.println(player);
|
||||
SaturnPlayer.set(player.toSaturnPlayer());
|
||||
});
|
||||
}
|
||||
|
||||
public static void player(UUID uuid, String name) {
|
||||
try {
|
||||
session.request(ServiceMethods.GetPlayer, uuid.toString())
|
||||
.whenComplete((user, throwable) -> {
|
||||
if (throwable != null) {
|
||||
throwable.printStackTrace();
|
||||
} else if (user != null) {
|
||||
SaturnPlayer.set(user.toSaturnPlayer(uuid, name));
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
SaturnClient.LOGGER.error("Failed to get player", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package org.saturnclient.saturnclient.client;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.saturnclient.saturnclient.client.ServiceMethods.Types.SendPlayerRequest;
|
||||
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
|
||||
|
||||
import dev.selimaj.session.types.Method;
|
||||
|
||||
public class ServiceMethods {
|
||||
|
||||
// =========================
|
||||
// Types (Rust structs)
|
||||
// =========================
|
||||
public static class Types {
|
||||
|
||||
public record EmoteRequest(String emote, String[] targets) {
|
||||
}
|
||||
|
||||
public record EmoteEvent(String emote, String from) {
|
||||
}
|
||||
|
||||
public record Player(
|
||||
String cloak,
|
||||
String hat,
|
||||
String[] cloaks,
|
||||
String[] hats) {
|
||||
public SaturnPlayer toSaturnPlayer(UUID uuid, String name) {
|
||||
return new SaturnPlayer(uuid, name, cloak, hat);
|
||||
}
|
||||
}
|
||||
|
||||
public record PlayerStream(Player player, String name, String uuid) {
|
||||
public SaturnPlayer toSaturnPlayer() {
|
||||
return new SaturnPlayer(UUID.fromString(uuid), name, this.player.cloak, this.player.hat);
|
||||
}
|
||||
}
|
||||
|
||||
public record SendPlayerRequest(String[] targets) {
|
||||
}
|
||||
}
|
||||
|
||||
// =========================
|
||||
// AUTH
|
||||
// =========================
|
||||
public static final Method<String, Types.Player, String> Authenticate = new Method<>("auth",
|
||||
String.class,
|
||||
Types.Player.class,
|
||||
String.class);
|
||||
|
||||
// =========================
|
||||
// EQUIP
|
||||
// =========================
|
||||
public static final Method<String, String, String> SetCloak = new Method<>("set_cloak",
|
||||
String.class,
|
||||
String.class,
|
||||
String.class);
|
||||
|
||||
public static final Method<String, String, String> SetHat = new Method<>("set_hat",
|
||||
String.class,
|
||||
String.class,
|
||||
String.class);
|
||||
|
||||
// =========================
|
||||
// BUY
|
||||
// =========================
|
||||
public static final Method<String, String, String> BuyCloak = new Method<>("buy_cloak",
|
||||
String.class,
|
||||
String.class,
|
||||
String.class);
|
||||
|
||||
public static final Method<String, String, String> BuyHat = new Method<>("buy_hat",
|
||||
String.class,
|
||||
String.class,
|
||||
String.class);
|
||||
|
||||
// =========================
|
||||
// EMOTE
|
||||
// =========================
|
||||
public static final Method<Types.EmoteRequest, Void, String> Emote = new Method<>("emote",
|
||||
Types.EmoteRequest.class,
|
||||
Void.class,
|
||||
String.class);
|
||||
|
||||
public static final Method<Types.EmoteEvent, Void, Void> EmoteEvent = new Method<>("emote_event",
|
||||
Types.EmoteEvent.class,
|
||||
Void.class,
|
||||
Void.class);
|
||||
|
||||
// =========================
|
||||
// GET PLAYER
|
||||
// =========================
|
||||
public static final Method<String, Types.Player, String> GetPlayer = new Method<>("get_player",
|
||||
String.class,
|
||||
Types.Player.class,
|
||||
String.class);
|
||||
|
||||
// =========================
|
||||
// PLAYER (Server Notification)
|
||||
// =========================
|
||||
public static final Method<Types.PlayerStream, Void, Void> Player = new Method<>("player",
|
||||
Types.PlayerStream.class,
|
||||
Void.class,
|
||||
Void.class);
|
||||
|
||||
// =========================
|
||||
// SEND PLAYER
|
||||
// =========================
|
||||
public static final Method<SendPlayerRequest, Void, String> SendPlayer = new Method<>("send_player",
|
||||
SendPlayerRequest.class,
|
||||
Void.class,
|
||||
String.class);
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package org.saturnclient.saturnclient.auth;
|
||||
package org.saturnclient.saturnclient.client.player;
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.saturnclient.client.ServiceClient;
|
||||
|
||||
import net.minecraft.client.network.PlayerListEntry;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
|
||||
@@ -45,8 +47,6 @@ public class PlayerTracker {
|
||||
|
||||
private static void onPlayerJoin(MinecraftClient client, PlayerListEntry player) {
|
||||
GameProfile profile = player.getProfile();
|
||||
String uuid = profile.getId().toString();
|
||||
if (uuid != Auth.uuid)
|
||||
Auth.player(profile.getName(), uuid);
|
||||
ServiceClient.player(profile.getId(), profile.getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.saturnclient.saturnclient.client.player;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.saturnclient.saturnclient.config.SaturnClientConfig;
|
||||
|
||||
import net.minecraft.util.Formatting;
|
||||
|
||||
public class Roles {
|
||||
static enum Role {
|
||||
OWNER,
|
||||
ADMIN,
|
||||
PARTNER,
|
||||
CONTRIBUTOR
|
||||
}
|
||||
|
||||
private static final Map<UUID, Role> ROLES = Map.of(
|
||||
UUID.fromString("d362a042-28bd-49e1-a807-ae74dbe8aba9"),
|
||||
Role.OWNER);
|
||||
|
||||
/*
|
||||
* 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(UUID uuid) {
|
||||
Role role = ROLES.get(uuid);
|
||||
|
||||
if (role == null) {
|
||||
return Formatting.WHITE;
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case OWNER:
|
||||
return Formatting.DARK_RED;
|
||||
|
||||
case ADMIN:
|
||||
return Formatting.RED;
|
||||
|
||||
case PARTNER:
|
||||
return Formatting.GOLD;
|
||||
|
||||
case CONTRIBUTOR:
|
||||
return Formatting.AQUA;
|
||||
|
||||
default:
|
||||
return Formatting.WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSaturnIndicator() {
|
||||
return SaturnClientConfig.realisticLogo.value ? "" : "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.saturnclient.saturnclient.client.player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.saturnclient.saturnclient.client.ServiceClient;
|
||||
|
||||
public class SaturnPlayer {
|
||||
private static Map<UUID, SaturnPlayer> PLAYERS = new HashMap<>();
|
||||
private static Map<String, UUID> PLAYER_NAMES = new HashMap<>();
|
||||
|
||||
public String cloak = "";
|
||||
public String hat = "";
|
||||
public final UUID uuid;
|
||||
public final String name;
|
||||
|
||||
public SaturnPlayer(UUID uuid, String name, String cloak, String hat) {
|
||||
this.cloak = cloak;
|
||||
this.hat = hat;
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static SaturnPlayer get() {
|
||||
return PLAYERS.get(ServiceClient.uuid);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static SaturnPlayer get(UUID uuid) {
|
||||
return PLAYERS.get(uuid);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static SaturnPlayer get(String name) {
|
||||
UUID uuid = PLAYER_NAMES.get(name);
|
||||
if (uuid == null)
|
||||
return null;
|
||||
return PLAYERS.get(uuid);
|
||||
}
|
||||
|
||||
public static void set(SaturnPlayer player) {
|
||||
PLAYER_NAMES.put(player.name, player.uuid);
|
||||
PLAYERS.put(player.uuid, player);
|
||||
}
|
||||
|
||||
public static String[] getExternalUUIDAsString() {
|
||||
return PLAYERS.keySet().stream().filter(id -> !id.equals(ServiceClient.uuid)).map(UUID::toString)
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,107 +1,94 @@
|
||||
package org.saturnclient.saturnclient.config;
|
||||
|
||||
import com.google.gson.*;
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ConfigManager {
|
||||
|
||||
private static final File configFile = new File(SaturnClient.client.runDirectory, "saturn.json");
|
||||
private static final Map<String, Map<String, Property<?>>> properties = new HashMap<>();
|
||||
private static JsonObject cachedThemeJson = null;
|
||||
|
||||
// SINGLE ROOT TREE
|
||||
private static final Map<String, Property<?>> ROOT = new LinkedHashMap<>();
|
||||
|
||||
private static JsonObject cachedJson = null;
|
||||
|
||||
private final Map<String, Property<?>> currentMap;
|
||||
private final String namespace;
|
||||
|
||||
private final String[] path;
|
||||
|
||||
/**
|
||||
* Creates a root namespace
|
||||
*/
|
||||
public ConfigManager(String namespace) {
|
||||
this.namespace = namespace;
|
||||
this.currentMap = new LinkedHashMap<>();
|
||||
properties.put(namespace, currentMap);
|
||||
System.out.println("Created namespace: " + namespace);
|
||||
this.path = new String[] { namespace }; // Path is just ["namespace"]
|
||||
ROOT.put(namespace, Property.namespace(currentMap));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a sub-namespace inside a parent
|
||||
*/
|
||||
public ConfigManager(ConfigManager parent, String namespace) {
|
||||
this.namespace = namespace;
|
||||
this.currentMap = new LinkedHashMap<>();
|
||||
Property<Map<String, Property<?>>> nsProp = Property.namespace(currentMap);
|
||||
parent.property(namespace, nsProp);
|
||||
System.out.println("Created sub-namespace: " + namespace);
|
||||
}
|
||||
|
||||
public void sub(String name) {
|
||||
property(name, Property.namespace(new LinkedHashMap<>()));
|
||||
// Build new path: parent path + current namespace
|
||||
this.path = new String[parent.path.length + 1];
|
||||
System.arraycopy(parent.path, 0, this.path, 0, parent.path.length);
|
||||
this.path[this.path.length - 1] = namespace;
|
||||
|
||||
parent.property(namespace, Property.namespace(currentMap));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a property
|
||||
*/
|
||||
public <T> Property<T> property(String name, Property<T> value) {
|
||||
currentMap.put(name, value);
|
||||
loadProp(name, value);
|
||||
loadSingleProperty(name, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
private <T> void loadProp(String name, Property<T> prop) {
|
||||
JsonObject source = cachedThemeJson != null ? cachedThemeJson : loadAndCache();
|
||||
if (source == null)
|
||||
return;
|
||||
|
||||
JsonElement namespaceJson = source.get(namespace);
|
||||
if (namespaceJson == null || !namespaceJson.isJsonObject())
|
||||
return;
|
||||
|
||||
JsonElement value = namespaceJson.getAsJsonObject().get(name);
|
||||
if (value != null)
|
||||
prop.loadFromJson(value);
|
||||
}
|
||||
|
||||
private static JsonObject loadAndCache() {
|
||||
try {
|
||||
if (!configFile.exists()) {
|
||||
Files.writeString(configFile.toPath(), "{}");
|
||||
return new JsonObject();
|
||||
}
|
||||
String json = Files.readString(configFile.toPath());
|
||||
cachedThemeJson = JsonParser.parseString(json).getAsJsonObject();
|
||||
return cachedThemeJson;
|
||||
} catch (IOException e) {
|
||||
SaturnClient.LOGGER.error("Error reading config file", e);
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Add empty namespace
|
||||
*/
|
||||
public ConfigManager sub(String name) {
|
||||
return new ConfigManager(this, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load entire config
|
||||
*/
|
||||
public static void load() {
|
||||
SaturnClient.LOGGER.info("Loading config...");
|
||||
|
||||
JsonObject json = loadAndCache();
|
||||
if (json == null)
|
||||
return;
|
||||
|
||||
properties.forEach((namespace, map) -> {
|
||||
JsonElement nsElement = json.get(namespace);
|
||||
if (nsElement != null && nsElement.isJsonObject()) {
|
||||
loadProperties(nsElement.getAsJsonObject(), map);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void loadProperties(JsonObject json, Map<String, Property<?>> props) {
|
||||
props.forEach((name, prop) -> {
|
||||
JsonElement el = json.get(name);
|
||||
if (el != null)
|
||||
prop.loadFromJson(el);
|
||||
});
|
||||
loadRecursive(json, ROOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save entire config
|
||||
*/
|
||||
public static void save() {
|
||||
SaturnClient.LOGGER.info("Saving config...");
|
||||
JsonObject root = new JsonObject();
|
||||
properties.forEach((namespace, map) -> {
|
||||
JsonObject nsJson = new JsonObject();
|
||||
saveProperties(nsJson, map);
|
||||
root.add(namespace, nsJson);
|
||||
});
|
||||
|
||||
JsonObject rootJson = new JsonObject();
|
||||
saveRecursive(rootJson, ROOT);
|
||||
|
||||
try {
|
||||
String formatted = new GsonBuilder().setPrettyPrinting().create().toJson(root);
|
||||
String formatted = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
.toJson(rootJson);
|
||||
|
||||
Files.writeString(configFile.toPath(), formatted);
|
||||
SaturnClient.LOGGER.info("Config saved successfully.");
|
||||
} catch (IOException e) {
|
||||
@@ -109,8 +96,83 @@ public class ConfigManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static void saveProperties(JsonObject json, Map<String, Property<?>> props) {
|
||||
props.forEach((name, prop) -> json.add(name, prop.toJson()));
|
||||
/**
|
||||
* Recursive save
|
||||
*/
|
||||
private static void saveRecursive(JsonObject json, Map<String, Property<?>> map) {
|
||||
map.forEach((name, prop) -> {
|
||||
if (prop.isNamespace()) {
|
||||
JsonObject child = new JsonObject();
|
||||
saveRecursive(child, prop.getNamespaceMap());
|
||||
json.add(name, child);
|
||||
} else {
|
||||
json.add(name, prop.toJson());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive load
|
||||
*/
|
||||
private static void loadRecursive(JsonObject json, Map<String, Property<?>> map) {
|
||||
map.forEach((name, prop) -> {
|
||||
JsonElement element = json.get(name);
|
||||
if (element == null)
|
||||
return;
|
||||
|
||||
if (prop.isNamespace() && element.isJsonObject()) {
|
||||
loadRecursive(element.getAsJsonObject(), prop.getNamespaceMap());
|
||||
} else {
|
||||
prop.loadFromJson(element);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load only a single property immediately after creation,
|
||||
* traversing the JSON tree based on this manager's path.
|
||||
*/
|
||||
private void loadSingleProperty(String name, Property<?> prop) {
|
||||
JsonObject source = cachedJson != null ? cachedJson : loadAndCache();
|
||||
if (source == null)
|
||||
return;
|
||||
|
||||
// Traverse the tree to the correct namespace
|
||||
JsonObject currentScope = source;
|
||||
for (String segment : path) {
|
||||
JsonElement next = currentScope.get(segment);
|
||||
if (next != null && next.isJsonObject()) {
|
||||
currentScope = next.getAsJsonObject();
|
||||
} else {
|
||||
return; // Path doesn't exist in config yet
|
||||
}
|
||||
}
|
||||
|
||||
// Now look for the property inside the correctly nested object
|
||||
JsonElement element = currentScope.get(name);
|
||||
if (element != null) {
|
||||
prop.loadFromJson(element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads file and caches json
|
||||
*/
|
||||
private static JsonObject loadAndCache() {
|
||||
try {
|
||||
if (!configFile.exists()) {
|
||||
Files.writeString(configFile.toPath(), "{}");
|
||||
return new JsonObject();
|
||||
}
|
||||
|
||||
String json = Files.readString(configFile.toPath());
|
||||
cachedJson = JsonParser.parseString(json).getAsJsonObject();
|
||||
return cachedJson;
|
||||
|
||||
} catch (IOException e) {
|
||||
SaturnClient.LOGGER.error("Error reading config file", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Property<?>> getProperties() {
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,15 @@ import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
public class Property<T> {
|
||||
public static class NamedProperty<T> {
|
||||
public String name;
|
||||
public Property<T> prop;
|
||||
|
||||
public NamedProperty(String name, Property<T> value) {
|
||||
this.name = name;
|
||||
this.prop = value;
|
||||
}
|
||||
}
|
||||
|
||||
public enum PropertyType {
|
||||
BOOLEAN,
|
||||
@@ -277,4 +286,13 @@ public class Property<T> {
|
||||
if (value != null)
|
||||
this.loadFromJson(value);
|
||||
}
|
||||
|
||||
public boolean isNamespace() {
|
||||
return type == PropertyType.NAMESPACE;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Property<?>> getNamespaceMap() {
|
||||
return isNamespace() ? (Map<String, Property<?>>) (Object) value : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package org.saturnclient.saturnclient;
|
||||
package org.saturnclient.saturnclient.config;
|
||||
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.saturnclient.saturnclient.config.ConfigManager;
|
||||
import org.saturnclient.ui2.resources.Textures;
|
||||
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class SaturnClientConfig {
|
||||
@@ -15,37 +12,21 @@ public class SaturnClientConfig {
|
||||
public static Property<Boolean> saturnTitleScreen = Property.bool(true);
|
||||
public static Property<Boolean> bendyCloaks = Property.bool(true);
|
||||
public static Property<Integer> openEmoteWheel = Property.keybinding(GLFW.GLFW_KEY_B);
|
||||
public static Property<Boolean> stagger = Property.bool(true);
|
||||
|
||||
public static Identifier getLogo() {
|
||||
return realisticLogo.value ? Textures.REALISTIC_LOGO : Textures.LOGO;
|
||||
}
|
||||
|
||||
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);
|
||||
config.property("Bendy Cloaks", bendyCloaks);
|
||||
config.property("Stagger Animations", stagger);
|
||||
|
||||
// Initialize a sub namespace for theme
|
||||
Theme.init(config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package org.saturnclient.saturnclient.config;
|
||||
|
||||
public class Theme {
|
||||
// Main UI background (slightly warm dark to match Saturn tone)
|
||||
public static final Property<Integer> BACKGROUND = Property.color(0xFF12100D);
|
||||
|
||||
// Default text color
|
||||
public static final Property<Integer> FOREGROUND = Property.color(0xFFF2E6D2);
|
||||
|
||||
// Widget default background (normal state)
|
||||
public static final Property<Integer> PRIMARY = Property.color(0xFF1C1915);
|
||||
|
||||
// Widget text color
|
||||
public static final Property<Integer> PRIMARY_FG = Property.color(0xFFEAD9B8);
|
||||
|
||||
// Saturn brand color (hovered / enabled / selected)
|
||||
public static final Property<Integer> ACCENT = Property.color(0xFFE2BF7D);
|
||||
|
||||
// Text/icons shown on accent background
|
||||
public static final Property<Integer> ACCENT_FG = Property.color(0xFF1A140A);
|
||||
|
||||
// Misc colors
|
||||
public static final Property<Integer> SCROLL = Property.color(0xFFE2BF7D);
|
||||
|
||||
// UI metrics
|
||||
public static final Property<Integer> BG_RADIUS = Property.integer(12);
|
||||
public static final Property<Integer> WIDGET_RADIUS = Property.integer(10);
|
||||
|
||||
// Font (keeping your current system)
|
||||
public static Property<Integer> FONT = Property.font(1);
|
||||
|
||||
public static void init(ConfigManager parent) {
|
||||
ConfigManager config = new ConfigManager(parent, "UI Style");
|
||||
config.property("Background", BACKGROUND);
|
||||
config.property("Foreground", FOREGROUND);
|
||||
config.property("Primary", PRIMARY);
|
||||
config.property("Primary Foreground", PRIMARY_FG);
|
||||
config.property("Accent", ACCENT);
|
||||
config.property("Accent Foreground", ACCENT_FG);
|
||||
config.property("Scrollbar Color", SCROLL);
|
||||
|
||||
config.property("Background Radius", BG_RADIUS);
|
||||
config.property("Widget Radius", WIDGET_RADIUS);
|
||||
|
||||
config.property("Font", FONT);
|
||||
}
|
||||
|
||||
/**
|
||||
* primary by default, accent if true
|
||||
*/
|
||||
public static int getBg(boolean hover) {
|
||||
return hover ? ACCENT.value : PRIMARY.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* (Foreground) primary by default, accent if true
|
||||
*/
|
||||
public static int getFg(boolean hover) {
|
||||
return hover ? ACCENT_FG.value : PRIMARY_FG.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies existing alpha with 0–255 alpha.
|
||||
*/
|
||||
public static int withAlpha(int alpha, int color) {
|
||||
alpha = Math.max(0, Math.min(255, alpha));
|
||||
|
||||
int originalAlpha = (color >>> 24) & 0xFF;
|
||||
|
||||
// Merge (multiply) alphas
|
||||
int merged = (originalAlpha * alpha) / 255;
|
||||
|
||||
return (color & 0x00FFFFFF) | (merged << 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies existing alpha with 0.0–1.0 alpha.
|
||||
*/
|
||||
public static int withAlpha(float alpha, int color) {
|
||||
alpha = Math.max(0f, Math.min(1f, alpha));
|
||||
|
||||
int originalAlpha = (color >>> 24) & 0xFF;
|
||||
|
||||
int merged = (int) (originalAlpha * alpha);
|
||||
|
||||
return (color & 0x00FFFFFF) | (merged << 24);
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.saturnclient.saturnclient.cosmetics;
|
||||
|
||||
import org.saturnclient.saturnclient.SaturnClientConfig;
|
||||
import org.saturnclient.saturnclient.auth.Auth;
|
||||
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
|
||||
import org.saturnclient.saturnclient.config.SaturnClientConfig;
|
||||
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
|
||||
|
||||
import net.minecraft.client.render.OverlayTexture;
|
||||
@@ -283,20 +283,18 @@ public class CloakFeatureRenderer extends FeatureRenderer<PlayerEntityRenderStat
|
||||
|
||||
public void render(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light,
|
||||
PlayerEntityRenderState playerEntityRenderState, float f, float g) {
|
||||
if (playerEntityRenderState.invisible || !playerEntityRenderState.capeVisible) {
|
||||
if (playerEntityRenderState.invisible || !playerEntityRenderState.capeVisible
|
||||
|| playerEntityRenderState.skinTextures.capeTexture() != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (playerEntityRenderState.skinTextures.capeTexture() != null) {
|
||||
return;
|
||||
}
|
||||
String name = playerEntityRenderState.name;
|
||||
String uuid = Auth.playerNames.get(name);
|
||||
if (uuid == null || !Auth.players.containsKey(uuid)) {
|
||||
SaturnPlayer player = SaturnPlayer.get(playerEntityRenderState.name);
|
||||
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Identifier customCape = Cloaks.getCurrentCloakTexture(uuid);
|
||||
Identifier customCape = Cloaks.getCurrentCloakTexture(player.uuid);
|
||||
if (customCape == null || this.hasCustomModelForLayer(playerEntityRenderState.equippedChestStack,
|
||||
LayerType.WINGS)) {
|
||||
return;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package org.saturnclient.saturnclient.cosmetics;
|
||||
|
||||
import org.saturnclient.saturnclient.auth.SaturnPlayer;
|
||||
import org.saturnclient.saturnclient.auth.Auth;
|
||||
|
||||
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import net.minecraft.client.render.OverlayTexture;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
@@ -29,10 +27,9 @@ public class HatFeatureRenderer extends FeatureRenderer<PlayerEntityRenderState,
|
||||
public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light,
|
||||
PlayerEntityRenderState state, float limbAngle, float limbDistance) {
|
||||
ItemStack headItem = state.equippedHeadStack;
|
||||
String uuid = Auth.playerNames.get(state.name);
|
||||
if (headItem.isEmpty() && uuid != null && !state.invisible) {
|
||||
SaturnPlayer player = Auth.players.get(uuid);
|
||||
if (player == null || player.hat == null || player.hat.isEmpty()) {
|
||||
SaturnPlayer player = SaturnPlayer.get(state.name);
|
||||
|
||||
if (!headItem.isEmpty() || state.invisible || player == null || player.hat.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -63,4 +60,3 @@ public class HatFeatureRenderer extends FeatureRenderer<PlayerEntityRenderState,
|
||||
matrices.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@ package org.saturnclient.saturnclient.cosmetics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.saturnclient.saturnclient.auth.SaturnPlayer;
|
||||
import org.saturnclient.saturnclient.auth.Auth;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.saturnclient.saturnclient.client.ServiceClient;
|
||||
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
|
||||
|
||||
public class Hats {
|
||||
public static final String[] ALL_HATS = { "horns_black", "horns_white", "halo", "halo_black" };
|
||||
@@ -13,16 +15,16 @@ public class Hats {
|
||||
availableHats.add(0, "");
|
||||
}
|
||||
|
||||
public static void setHat(String uuid, String hatName) {
|
||||
setHatSilent(uuid, hatName);
|
||||
Auth.setHat(hatName);
|
||||
public static void setHat(String hatName) {
|
||||
if (availableHats.contains(hatName)) {
|
||||
setHat(ServiceClient.uuid, hatName);
|
||||
ServiceClient.setHat(hatName);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setHatSilent(String uuid, String hatName) {
|
||||
SaturnPlayer player = Auth.players.get(uuid);
|
||||
if (player == null) {
|
||||
Auth.players.put(uuid, new SaturnPlayer("", hatName));
|
||||
} else {
|
||||
public static void setHat(UUID uuid, String hatName) {
|
||||
SaturnPlayer player = SaturnPlayer.get(uuid);
|
||||
if (player != null) {
|
||||
player.hat = hatName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package org.saturnclient.saturnclient.cosmetics.cloaks;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.saturnclient.saturnclient.auth.Auth;
|
||||
|
||||
import org.saturnclient.saturnclient.client.ServiceClient;
|
||||
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
|
||||
import org.saturnclient.saturnclient.cosmetics.cloaks.utils.AnimatedCloakData;
|
||||
import org.saturnclient.saturnclient.cosmetics.cloaks.utils.IdentifierUtils;
|
||||
import org.saturnclient.saturnclient.auth.SaturnPlayer;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
@@ -32,8 +33,8 @@ public class Cloaks {
|
||||
private static final String CLOAKS_RESOURCE_PATH = "assets/saturnclient/textures/cloaks/";
|
||||
public static final List<String> availableCloaks = new ArrayList<>();
|
||||
public static Identifier cloakCacheIdentifier = null;
|
||||
public static final Map<String, List<AnimatedCloakData>> animatedCloaks = new ConcurrentHashMap<>();
|
||||
private static final Map<String, Long> lastFrameTime = new ConcurrentHashMap<>();
|
||||
public static final Map<UUID, List<AnimatedCloakData>> animatedCloaks = new ConcurrentHashMap<>();
|
||||
private static final Map<UUID, Long> lastFrameTime = new ConcurrentHashMap<>();
|
||||
|
||||
private static final ExecutorService CLOAK_LOADER_EXECUTOR = Executors.newFixedThreadPool(
|
||||
Math.min(4, Runtime.getRuntime().availableProcessors()),
|
||||
@@ -54,17 +55,6 @@ public class Cloaks {
|
||||
availableCloaks.add(0, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles loading and caching of a new cloak texture.
|
||||
*
|
||||
* @param cloakName Name of the cloak file to load
|
||||
*/
|
||||
public static void setCloak(String uuid, String cloakName) {
|
||||
Auth.setCloak(cloakName);
|
||||
setCloakSilent(uuid, cloakName);
|
||||
SaturnClient.LOGGER.info("Cloak set to " + cloakName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets cloak of the current player
|
||||
*
|
||||
@@ -72,7 +62,8 @@ public class Cloaks {
|
||||
*/
|
||||
public static void setCloak(String cloakName) {
|
||||
if (availableCloaks.contains(cloakName)) {
|
||||
setCloak(Auth.uuid, cloakName);
|
||||
setCloak(ServiceClient.uuid, cloakName);
|
||||
ServiceClient.setCloak(cloakName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,11 +73,10 @@ public class Cloaks {
|
||||
*
|
||||
* @param cloakName Name of the cloak file to load
|
||||
*/
|
||||
public static void setCloakSilent(String uuid, String cloakName) {
|
||||
SaturnPlayer player = Auth.players.get(uuid);
|
||||
if (player == null) {
|
||||
Auth.players.put(uuid, new SaturnPlayer(cloakName, null));
|
||||
} else {
|
||||
public static void setCloak(UUID uuid, String cloakName) {
|
||||
SaturnPlayer player = SaturnPlayer.get(uuid);
|
||||
|
||||
if (player != null) {
|
||||
player.cloak = cloakName;
|
||||
}
|
||||
|
||||
@@ -99,8 +89,9 @@ public class Cloaks {
|
||||
*
|
||||
* @param cloakName Name of the cloak file to load
|
||||
*/
|
||||
public static void loadCloak(String uuid) {
|
||||
SaturnPlayer player = Auth.players.get(uuid);
|
||||
public static void loadCloak(UUID uuid) {
|
||||
SaturnPlayer player = SaturnPlayer.get(uuid);
|
||||
|
||||
if (player != null && player.cloak != null) {
|
||||
if (!player.cloak.isEmpty()) {
|
||||
if (Arrays.asList(ANIMATED_CLOAKS).contains(player.cloak)) {
|
||||
@@ -146,7 +137,7 @@ public class Cloaks {
|
||||
}
|
||||
}
|
||||
|
||||
public static CompletableFuture<Void> loadAnimatedCloakAsync(String uuid, String cloakName) {
|
||||
public static CompletableFuture<Void> loadAnimatedCloakAsync(UUID uuid, String cloakName) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
String fileName = cloakName + ".gif";
|
||||
|
||||
@@ -226,16 +217,14 @@ public class Cloaks {
|
||||
}, CLOAK_LOADER_EXECUTOR);
|
||||
}
|
||||
|
||||
public static Identifier getCurrentCloakTexture(String uuid) {
|
||||
if (!Auth.players.containsKey(uuid)) {
|
||||
public static Identifier getCurrentCloakTexture(UUID uuid) {
|
||||
SaturnPlayer player = SaturnPlayer.get(uuid);
|
||||
|
||||
if (player == null || player.cloak.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String cloakName = Auth.players.get(uuid).cloak;
|
||||
|
||||
if (cloakName.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String cloakName = player.cloak;
|
||||
|
||||
if (Arrays.asList(ANIMATED_CLOAKS).contains(cloakName)) {
|
||||
List<AnimatedCloakData> frames = animatedCloaks.get(uuid);
|
||||
|
||||
@@ -6,8 +6,8 @@ import net.minecraft.client.option.KeyBinding;
|
||||
import net.minecraft.client.util.InputUtil;
|
||||
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.saturnclient.SaturnClientConfig;
|
||||
import org.saturnclient.saturnclient.auth.Auth;
|
||||
import org.saturnclient.saturnclient.client.ServiceClient;
|
||||
import org.saturnclient.saturnclient.config.SaturnClientConfig;
|
||||
import org.saturnclient.ui2.screens.EmoteWheel;
|
||||
import org.saturnclient.ui2.screens.ShiftMenu;
|
||||
|
||||
@@ -47,7 +47,7 @@ public class KeyInputHandler {
|
||||
AnimationStack animationStack = PlayerAnimationAccess.getPlayerAnimLayer(SaturnClient.client.player);
|
||||
if (animationStack.isActive() && animationStack.getPriority() == 1000) {
|
||||
animationStack.removeLayer(1000);
|
||||
Auth.emote("");
|
||||
ServiceClient.emote("");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -14,8 +14,11 @@ import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
import org.saturnclient.saturnclient.SaturnClientConfig;
|
||||
import org.saturnclient.saturnclient.auth.Auth;
|
||||
import org.saturnclient.saturnclient.client.player.Roles;
|
||||
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.joml.Matrix4f;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@@ -37,11 +40,11 @@ public abstract class NameTagMixin<S extends EntityRenderState> {
|
||||
private void onRenderLabelIfPresent(S state, Text text, MatrixStack matrices,
|
||||
VertexConsumerProvider vertexConsumers, int light, CallbackInfo ci) {
|
||||
|
||||
String uuid = isSaturn(state);
|
||||
UUID uuid = isSaturn(state);
|
||||
|
||||
if (uuid != null) {
|
||||
MutableText iconText = Text.literal(SaturnClientConfig.getSaturnIndicator())
|
||||
.styled(style -> style.withColor(SaturnClientConfig.getIconColor(uuid)));
|
||||
MutableText iconText = Text.literal(Roles.getSaturnIndicator())
|
||||
.styled(style -> style.withColor(Roles.getIconColor(uuid)));
|
||||
|
||||
Text nameText = text.copy().styled(style -> style.withColor(Formatting.WHITE));
|
||||
|
||||
@@ -52,12 +55,12 @@ public abstract class NameTagMixin<S extends EntityRenderState> {
|
||||
}
|
||||
}
|
||||
|
||||
private String isSaturn(S state) {
|
||||
private UUID isSaturn(S state) {
|
||||
if (state instanceof PlayerEntityRenderState) {
|
||||
String name = ((PlayerEntityRenderState) state).name;
|
||||
String uuid = Auth.playerNames.get(name);
|
||||
if (Auth.players.containsKey(uuid)) {
|
||||
return uuid;
|
||||
SaturnPlayer player = SaturnPlayer.get(name);
|
||||
if (player != null) {
|
||||
return player.uuid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.saturnclient.saturnclient.mixin;
|
||||
|
||||
import org.saturnclient.saturnclient.config.SaturnClientConfig;
|
||||
import org.saturnclient.ui2.screens.TitleMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.screen.TitleScreen;
|
||||
|
||||
@Mixin(MinecraftClient.class)
|
||||
public class SetScreenMixin {
|
||||
@ModifyVariable(method = "setScreen", at = @At("HEAD"), argsOnly = true)
|
||||
private Screen replaceTitleScreen(Screen screen) {
|
||||
if (SaturnClientConfig.saturnTitleScreen.value) {
|
||||
if (screen instanceof TitleScreen)
|
||||
return new TitleMenu();
|
||||
if (screen == null && ((MinecraftClient) (Object) this).world == null)
|
||||
return new TitleMenu();
|
||||
}
|
||||
|
||||
return screen;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package org.saturnclient.saturnclient.mixin;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
import org.saturnclient.ui2.resources.Textures;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.screen.SplashOverlay;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.resource.ResourceReload;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
@Mixin(SplashOverlay.class)
|
||||
public abstract class SplashOverlayMixin {
|
||||
|
||||
@Shadow
|
||||
private MinecraftClient client;
|
||||
@Shadow
|
||||
private ResourceReload reload;
|
||||
|
||||
@Shadow(remap = false)
|
||||
private long reloadCompleteTime;
|
||||
|
||||
@Shadow(remap = false)
|
||||
private long reloadStartTime;
|
||||
|
||||
@Shadow(remap = false)
|
||||
private boolean reloading;
|
||||
|
||||
@Shadow(remap = false)
|
||||
private Consumer<Optional<Throwable>> exceptionHandler;
|
||||
|
||||
@Shadow(remap = false)
|
||||
private float progress;
|
||||
|
||||
/**
|
||||
* @author SaturnClient
|
||||
* @reason Replace vanilla splash rendering completely but keep exit logic
|
||||
*/
|
||||
@Overwrite
|
||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
int screenWidth = context.getScaledWindowWidth();
|
||||
int screenHeight = context.getScaledWindowHeight();
|
||||
long currentTime = Util.getMeasuringTimeMs();
|
||||
|
||||
// Initialize reload start time if not set
|
||||
if (reloadStartTime == -1L) {
|
||||
reloadStartTime = currentTime;
|
||||
}
|
||||
|
||||
// Compute fade-out progress after reload completes
|
||||
float fadeOutProgress = reloadCompleteTime > -1L
|
||||
? (float) (currentTime - reloadCompleteTime) / 1000f
|
||||
: -1f;
|
||||
|
||||
int alpha = fadeOutProgress >= 0f
|
||||
? MathHelper.ceil((1.0f - MathHelper.clamp(fadeOutProgress - 1f, 0f, 1f)) * 255f)
|
||||
: 255;
|
||||
|
||||
// Compute fade-in progress while reloading
|
||||
float reloadProgress = reloadStartTime > -1L
|
||||
? (float) (currentTime - reloadStartTime) / 500f
|
||||
: -1f;
|
||||
|
||||
// Get render scope for better UI
|
||||
RenderScope scope = new RenderScope(context);
|
||||
scope.setRenderLayer(RenderLayer::getGuiTextured);
|
||||
|
||||
// Draw background overlay
|
||||
if (fadeOutProgress >= 1.0F) {
|
||||
renderCurrentScreen(context, mouseX, mouseY, delta);
|
||||
int overlayAlpha = computeOverlayAlpha(fadeOutProgress - 1f);
|
||||
scope.drawTexture(Textures.SPLASH, 0, 0, 0, 0, screenWidth, screenHeight,
|
||||
Theme.withAlpha(overlayAlpha, 0xFFFFFFFF));
|
||||
} else if (reloading) {
|
||||
if (reloadProgress < 1.0F) {
|
||||
renderCurrentScreen(context, mouseX, mouseY, delta);
|
||||
}
|
||||
scope.drawTexture(Textures.SPLASH, 0, 0, 0, 0, screenWidth, screenHeight,
|
||||
Theme.withAlpha(alpha, 0xFFFFFFFF));
|
||||
} else {
|
||||
scope.drawTexture(Textures.SPLASH, 0, 0, 0, 0, screenWidth, screenHeight,
|
||||
Theme.withAlpha(alpha, 0xFFFFFFFF));
|
||||
}
|
||||
|
||||
float u = this.reload.getProgress();
|
||||
this.progress = MathHelper.clamp(this.progress * 0.95F + u * 0.050000012F, 0.0F, 1.0F);
|
||||
|
||||
// Draw central logo
|
||||
drawLogo(scope, screenWidth, screenHeight, alpha);
|
||||
renderProgressBar(scope, context, screenWidth, screenHeight, alpha);
|
||||
|
||||
// Remove overlay after fade-out completes
|
||||
if (fadeOutProgress >= 2.0F) {
|
||||
client.setOverlay(null);
|
||||
}
|
||||
|
||||
// Handle reload completion and exceptions
|
||||
handleReloadCompletion(context);
|
||||
}
|
||||
|
||||
private void renderCurrentScreen(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
if (client.currentScreen != null) {
|
||||
client.currentScreen.render(context, mouseX, mouseY, delta);
|
||||
}
|
||||
}
|
||||
|
||||
/** Draw the main logo centered */
|
||||
private void drawLogo(RenderScope scope, int screenWidth, int screenHeight, int alpha) {
|
||||
scope.drawTexture(Textures.LOGO, (screenWidth - 60) / 2, ((screenHeight - 60) / 2) - 20, 0, 0, 60, 60,
|
||||
Theme.withAlpha(alpha, 0xFFFFFFFF));
|
||||
}
|
||||
|
||||
private void renderProgressBar(RenderScope scope, DrawContext context, int screenWidth, int screenHeight,
|
||||
int alpha) {
|
||||
int progressY = ((screenHeight - 5) / 2) + 25;
|
||||
context.drawBorder((screenWidth - 120) / 2, progressY, 120, 5, Theme.withAlpha(alpha, 0xFFFFFFFF));
|
||||
|
||||
int progressWidth = Math.min((int) (progress * 120), 120);
|
||||
int progressX = (screenWidth - 120) / 2;
|
||||
|
||||
context.fill(progressX, progressY, progressX + progressWidth, progressY + 5,
|
||||
Theme.withAlpha(alpha, 0xFFFFFFFF));
|
||||
}
|
||||
|
||||
/** Compute overlay alpha from fade progress */
|
||||
private int computeOverlayAlpha(float progress) {
|
||||
return MathHelper.ceil((1.0F - MathHelper.clamp(progress, 0.0F, 1.0F)) * 255.0F);
|
||||
}
|
||||
|
||||
/** Handle reload completion logic */
|
||||
private void handleReloadCompletion(DrawContext context) {
|
||||
float reloadProgress = reloadStartTime > -1L ? (float) (Util.getMeasuringTimeMs() - reloadStartTime) / 500f
|
||||
: -1f;
|
||||
|
||||
if (reloadCompleteTime == -1L && reload.isComplete() && (!reloading || reloadProgress >= 2.0F)) {
|
||||
try {
|
||||
reload.throwException();
|
||||
exceptionHandler.accept(Optional.empty());
|
||||
} catch (Throwable ex) {
|
||||
exceptionHandler.accept(Optional.of(ex));
|
||||
}
|
||||
|
||||
reloadCompleteTime = Util.getMeasuringTimeMs();
|
||||
|
||||
if (client.currentScreen != null) {
|
||||
client.currentScreen.init(client, context.getScaledWindowWidth(), context.getScaledWindowHeight());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
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 java.util.UUID;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.saturnclient.saturnclient.SaturnClientConfig;
|
||||
import org.saturnclient.saturnclient.auth.Auth;
|
||||
import org.saturnclient.saturnclient.client.player.Roles;
|
||||
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -30,13 +31,13 @@ public abstract class TabListMixin {
|
||||
public Text getDisplayName() {
|
||||
GameProfile profile = ((PlayerListEntry) (Object) this).getProfile();
|
||||
String name = profile.getName();
|
||||
String uuid = profile.getId().toString().replaceAll("-", "");
|
||||
UUID uuid = profile.getId();
|
||||
|
||||
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);
|
||||
if (SaturnPlayer.get(uuid) != null) {
|
||||
return Text.literal(Roles.getSaturnIndicator())
|
||||
.styled(style -> style.withColor(Roles.getIconColor(uuid)))
|
||||
.append(Text.literal(name).styled(style -> style.withColor(Formatting.WHITE)));
|
||||
}
|
||||
return Text.literal(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,22 @@ public class Element {
|
||||
public void charTyped(char typedChar) {
|
||||
}
|
||||
|
||||
public void mouseClicked(double mouseX, double mouseY, int button) {
|
||||
}
|
||||
|
||||
public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
|
||||
}
|
||||
|
||||
public void mouseReleased(double mouseX, double mouseY, int button) {
|
||||
}
|
||||
|
||||
public void playAnimationFrame(long elapsed) {
|
||||
if (this.animation != null && elapsed >= this.animation.delay) {
|
||||
this.animation.tick(this.curve
|
||||
.apply(Math.min(1.0, (double) (elapsed - this.animation.delay) / this.animation.duration)), this);
|
||||
}
|
||||
}
|
||||
|
||||
public Element dimensions(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
@@ -4,13 +4,15 @@ public class ElementContext {
|
||||
public int mouseX, mouseY;
|
||||
public int elementWidth, elementHeight;
|
||||
public float elementScale;
|
||||
public long elapsed;
|
||||
|
||||
public ElementContext(int mouseX, int mouseY, Element e) {
|
||||
public ElementContext(long elapsed, 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;
|
||||
this.elapsed = elapsed;
|
||||
}
|
||||
|
||||
public boolean isHovering() {
|
||||
|
||||
@@ -9,11 +9,17 @@ import java.util.function.Function;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix4f;
|
||||
import org.saturnclient.ui2.resources.Fonts;
|
||||
import org.saturnclient.ui2.resources.SvgTexture;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.font.TextRenderer.TextLayerType;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.ScreenRect;
|
||||
import net.minecraft.client.render.DiffuseLighting;
|
||||
import net.minecraft.client.render.OverlayTexture;
|
||||
@@ -42,6 +48,10 @@ public class RenderScope {
|
||||
private int opacity = 255 << 24;
|
||||
private final ItemRenderState itemRenderState;
|
||||
|
||||
public RenderScope(DrawContext context) {
|
||||
this(context.getMatrices(), ((DrawContextAccessor) context).getVertexConsumers());
|
||||
}
|
||||
|
||||
public RenderScope(MatrixStack matrices, VertexConsumerProvider.Immediate vertexConsumers) {
|
||||
this.matrices = matrices;
|
||||
this.vertexConsumers = vertexConsumers;
|
||||
@@ -217,6 +227,11 @@ public class RenderScope {
|
||||
|
||||
private void drawTexturedQuad(Identifier sprite, int x1, int x2, int y1, int y2, float u1, float u2, float v1,
|
||||
float v2, int color) {
|
||||
|
||||
if (sprite.toString().endsWith(".svg")) {
|
||||
sprite = SvgTexture.getSvg(MinecraftClient.getInstance(), sprite, (x2 - x1) * 2, (y2 - y1) * 2);
|
||||
}
|
||||
|
||||
if (color == 0)
|
||||
return;
|
||||
x1 *= 4;
|
||||
@@ -391,4 +406,45 @@ public class RenderScope {
|
||||
sprite.getMinV(), sprite.getMaxV(), color);
|
||||
}
|
||||
}
|
||||
|
||||
public void fill(int x1, int y1, int x2, int y2, int color) {
|
||||
this.fill(x1, y1, x2, y2, 0, color);
|
||||
}
|
||||
|
||||
public void fill(int x1, int y1, int x2, int y2, int z, int color) {
|
||||
this.fill(RenderLayer.getGui(), x1, y1, x2, y2, z, color);
|
||||
}
|
||||
|
||||
public void fill(RenderLayer layer, int x1, int y1, int x2, int y2, int color) {
|
||||
this.fill(layer, x1, y1, x2, y2, 0, color);
|
||||
}
|
||||
|
||||
public void fill(RenderLayer layer, int x1, int y1, int x2, int y2, int z, int color) {
|
||||
Matrix4f matrix4f = this.matrices.peek().getPositionMatrix();
|
||||
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(layer);
|
||||
vertexConsumer.vertex(matrix4f, (float) x1, (float) y1, (float) z).color(color);
|
||||
vertexConsumer.vertex(matrix4f, (float) x1, (float) y2, (float) z).color(color);
|
||||
vertexConsumer.vertex(matrix4f, (float) x2, (float) y2, (float) z).color(color);
|
||||
vertexConsumer.vertex(matrix4f, (float) x2, (float) y1, (float) z).color(color);
|
||||
}
|
||||
|
||||
public void drawBorder(int x, int y, int width, int height, int color) {
|
||||
this.fill(x, y, x + width, y + 1, color);
|
||||
this.fill(x, y + height - 1, x + width, y + height, color);
|
||||
this.fill(x, y + 1, x + 1, y + height - 1, color);
|
||||
this.fill(x + width - 1, y + 1, x + width, y + height - 1, color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package org.saturnclient.ui2;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
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;
|
||||
@@ -15,6 +15,8 @@ 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.texture.ResourceTexture;
|
||||
import net.minecraft.client.texture.TextureManager;
|
||||
import net.minecraft.client.util.Pool;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
@@ -22,12 +24,13 @@ 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;
|
||||
protected float backgroundOpacity = 1.0f;
|
||||
protected Instant start = null;
|
||||
public int backgroundBlur = 10;
|
||||
|
||||
protected static final CubeMapRenderer PANORAMA_RENDERER = new CubeMapRenderer(
|
||||
Identifier.of("saturnclient", "textures/gui/title/background/panorama"));
|
||||
protected static final Identifier PANORAMA = Identifier.of("saturnclient",
|
||||
"textures/gui/title/background/panorama");
|
||||
protected static final CubeMapRenderer PANORAMA_RENDERER = new CubeMapRenderer(PANORAMA);
|
||||
|
||||
public static final RotatingCubeMapRenderer ROTATING_PANORAMA_RENDERER;
|
||||
|
||||
@@ -35,41 +38,24 @@ public abstract class SaturnScreen extends Screen {
|
||||
ROTATING_PANORAMA_RENDERER = new RotatingCubeMapRenderer(PANORAMA_RENDERER);
|
||||
}
|
||||
|
||||
public static void preload(MinecraftClient client) {
|
||||
TextureManager textureManager = client.getTextureManager();
|
||||
|
||||
// Force upload each texture to GPU
|
||||
for (int i = 0; i < 6; i++) {
|
||||
String var10001 = PANORAMA.getPath();
|
||||
Identifier tex = PANORAMA.withPath(var10001 + "_" + i + ".png");
|
||||
ResourceTexture resourceTexture = new ResourceTexture(tex);
|
||||
textureManager.registerTexture(tex, resourceTexture);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
ElementRenderer.draw(elements, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,10 +64,6 @@ public abstract class SaturnScreen extends Screen {
|
||||
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();
|
||||
@@ -89,18 +71,24 @@ public abstract class SaturnScreen extends Screen {
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
if (start == null) {
|
||||
start = Instant.now();
|
||||
}
|
||||
long elapsed = Duration.between(start, Instant.now()).toMillis();
|
||||
|
||||
mouseX *= 2;
|
||||
mouseY *= 2;
|
||||
|
||||
if (client.world == null && client.getCurrentServerEntry() == null) {
|
||||
ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, 1.0F, delta);
|
||||
ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, backgroundOpacity, delta);
|
||||
}
|
||||
|
||||
PostEffectProcessor postEffectProcessor = this.client.getShaderLoader().loadPostEffect(
|
||||
Identifier.ofVanilla("blur"),
|
||||
DefaultFramebufferSet.MAIN_ONLY);
|
||||
|
||||
if (postEffectProcessor != null) {
|
||||
postEffectProcessor.setUniforms("Radius", backgroundBlur * blurProgress);
|
||||
postEffectProcessor.setUniforms("Radius", backgroundBlur * Math.min((float) elapsed / 700, 1.0f));
|
||||
postEffectProcessor.render(this.client.getFramebuffer(), this.pool);
|
||||
}
|
||||
|
||||
@@ -113,7 +101,7 @@ public abstract class SaturnScreen extends Screen {
|
||||
|
||||
renderScope.matrices.scale(0.5f, 0.5f, 0.5f);
|
||||
|
||||
ElementRenderer.render(new ArrayList<>(elements), renderScope, mouseX, mouseY);
|
||||
ElementRenderer.render(new ArrayList<>(elements), elapsed, renderScope, mouseX, mouseY);
|
||||
|
||||
renderScope.matrices.pop();
|
||||
}
|
||||
@@ -128,6 +116,26 @@ public abstract class SaturnScreen extends Screen {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
|
||||
mouseX *= 2;
|
||||
mouseY *= 2;
|
||||
|
||||
ElementRenderer.mouseDragged(elements, mouseX, mouseY, button, deltaX, deltaY);
|
||||
|
||||
return super.mouseReleased(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseReleased(double mouseX, double mouseY, int button) {
|
||||
mouseX *= 2;
|
||||
mouseY *= 2;
|
||||
|
||||
ElementRenderer.mouseReleased(elements, mouseX, mouseY, button);
|
||||
|
||||
return super.mouseReleased(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseScrolled(
|
||||
double mouseX,
|
||||
|
||||
@@ -51,7 +51,6 @@ public class Utils {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,75 +1,16 @@
|
||||
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;
|
||||
public int delay = 0;
|
||||
|
||||
Animation(int duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public abstract void tick(float progress, Element element);
|
||||
public abstract void tick(double 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@ public class Fade extends Animation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(float progress, Element element) {
|
||||
element.opacity = targetOpacity * progress;
|
||||
public void tick(double progress, Element element) {
|
||||
element.opacity = Math.max(0.1f, targetOpacity * (float) progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Element element) {
|
||||
targetOpacity = element.opacity;
|
||||
element.opacity = 0;
|
||||
element.opacity = 0.1f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ public class FadeOut extends Animation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(float progress, Element element) {
|
||||
element.opacity = targetOpacity - progress;
|
||||
public void tick(double progress, Element element) {
|
||||
element.opacity = targetOpacity - (float) progress;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.saturnclient.ui2.anim;
|
||||
|
||||
import org.saturnclient.ui2.Element;
|
||||
|
||||
public class SlideFade extends Animation {
|
||||
public Fade fade;
|
||||
public SlideY slideY;
|
||||
|
||||
public SlideFade(int duration, int offset) {
|
||||
super(duration);
|
||||
this.fade = new Fade(duration);
|
||||
this.slideY = new SlideY(duration, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(double progress, Element element) {
|
||||
fade.delay = this.delay;
|
||||
fade.duration = this.duration;
|
||||
|
||||
slideY.delay = this.delay;
|
||||
slideY.duration = this.duration;
|
||||
|
||||
fade.tick(progress, element);
|
||||
slideY.tick(progress, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Element element) {
|
||||
fade.init(element);
|
||||
slideY.init(element);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ public class SlideX extends Animation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(float progress, Element element) {
|
||||
public void tick(double progress, Element element) {
|
||||
element.x = elementX + (int) (offset * progress);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ public class SlideXAbsolute extends Animation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(float progress, Element element) {
|
||||
public void tick(double progress, Element element) {
|
||||
element.x = elementX + (int) (offset * progress);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import org.saturnclient.ui2.Element;
|
||||
public class SlideY extends Animation {
|
||||
int offset = 20;
|
||||
int maxOffset = 20;
|
||||
int elementY;
|
||||
double startY;
|
||||
|
||||
public SlideY(int duration) {
|
||||
super(duration);
|
||||
@@ -26,13 +26,14 @@ public class SlideY extends Animation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(float progress, Element element) {
|
||||
element.y = elementY + (int) (offset * progress);
|
||||
public void init(Element element) {
|
||||
startY = element.y - offset;
|
||||
element.y = (int) startY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Element element) {
|
||||
element.y -= offset;
|
||||
elementY = element.y;
|
||||
public void tick(double progress, Element element) {
|
||||
double currentY = startY + offset * progress;
|
||||
element.y = (int) currentY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.saturnclient.ui2.components;
|
||||
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.saturnclient.saturnclient.config.ThemeManager;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
@@ -10,13 +9,7 @@ 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 {}
|
||||
static int padding = 14;
|
||||
|
||||
Identifier sprite;
|
||||
private Runnable onClick;
|
||||
@@ -33,22 +26,16 @@ public class CosmeticPreview extends Element {
|
||||
|
||||
@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.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
|
||||
isSelected ? Theme.ACCENT.value : Theme.PRIMARY.value);
|
||||
|
||||
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
|
||||
|
||||
int texWidth = width - padding.value;
|
||||
int texHeight = height - padding.value;
|
||||
int texWidth = width - padding;
|
||||
int texHeight = height - padding;
|
||||
|
||||
renderScope.drawTexture(sprite, padding.value / 2, padding.value / 2, 0, 0, texWidth, texHeight, fgColor.value);
|
||||
|
||||
theme.setState(null);
|
||||
renderScope.drawTexture(sprite, padding / 2, padding / 2, 0, 0, texWidth, texHeight,
|
||||
Theme.FOREGROUND.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -7,7 +7,6 @@ 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) {
|
||||
@@ -17,20 +16,18 @@ public class ElementRenderer {
|
||||
|
||||
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) {
|
||||
public static void render(List<Element> elements, long elapsed, RenderScope renderScope, int mouseX, int mouseY) {
|
||||
for (Element element : elements) {
|
||||
element.playAnimationFrame(elapsed);
|
||||
|
||||
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));
|
||||
element.render(renderScope, new ElementContext(elapsed, mouseX, mouseY, element));
|
||||
renderScope.matrices.pop();
|
||||
renderScope.setRenderLayer(null);
|
||||
}
|
||||
@@ -44,7 +41,10 @@ public class ElementRenderer {
|
||||
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.mouseClicked(adjustedMouseX * element.scale, adjustedMouseY * element.scale, button);
|
||||
|
||||
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));
|
||||
}
|
||||
@@ -59,7 +59,8 @@ public class ElementRenderer {
|
||||
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)) {
|
||||
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,
|
||||
@@ -81,4 +82,17 @@ public class ElementRenderer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void mouseDragged(List<Element> elements, double mouseX, double mouseY, int button, double deltaX,
|
||||
double deltaY) {
|
||||
for (Element element : new ArrayList<>(elements)) {
|
||||
element.mouseDragged(mouseX - element.x, mouseY - element.y, button, deltaX, deltaY);
|
||||
}
|
||||
}
|
||||
|
||||
public static void mouseReleased(List<Element> elements, double mouseX, double mouseY, int button) {
|
||||
for (Element element : new ArrayList<>(elements)) {
|
||||
element.mouseReleased(mouseX - element.x, mouseY - element.y, button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@ 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.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.resources.Fonts;
|
||||
import org.saturnclient.ui2.resources.Textures;
|
||||
import org.saturnclient.ui2.Element;
|
||||
@@ -15,23 +14,12 @@ 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 final Module mod;
|
||||
|
||||
private Module mod;
|
||||
static int p = 10; // padding
|
||||
static int s = 14; // settings icon size
|
||||
static int expand = 5; // settings icon click area
|
||||
|
||||
public SaturnModule(Module mod) {
|
||||
this.mod = mod;
|
||||
@@ -41,60 +29,137 @@ public class SaturnModule extends Element {
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
int p = 10;
|
||||
int s = 14;
|
||||
boolean settingsHover = false;
|
||||
boolean hovered = ctx.isHovering();
|
||||
boolean enabled = mod.isEnabled();
|
||||
|
||||
int iconX = width - s - p;
|
||||
int iconY = height - s - p;
|
||||
|
||||
boolean settingsHover = ctx.isHovering(
|
||||
getSettingsX(),
|
||||
getSettingsY(),
|
||||
getSettingsHitBox(),
|
||||
getSettingsHitBox());
|
||||
|
||||
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
|
||||
|
||||
if (mod.isEnabled()) {
|
||||
theme.setState("enabled");
|
||||
}
|
||||
// Resolve theme colors
|
||||
int bgColor = Theme.PRIMARY.value;
|
||||
int fgColor = hovered && !settingsHover ? Theme.ACCENT.value : Theme.PRIMARY_FG.value;
|
||||
|
||||
if (ctx.isHovering(width - s - p - 5, height - s - p - 5, s + 10, s + 10)) {
|
||||
settingsHover = true;
|
||||
} else if (ctx.isHovering()) {
|
||||
theme.applyState("hovering");
|
||||
}
|
||||
int iconBg = enabled ? Theme.ACCENT.value : Theme.PRIMARY.value;
|
||||
int iconFg = enabled ? Theme.ACCENT_FG.value : fgColor;
|
||||
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, radius.value, bgColor.value);
|
||||
int settingsColor = settingsHover
|
||||
? Theme.ACCENT.value
|
||||
: Theme.withAlpha(160, fgColor);
|
||||
|
||||
int h = height - (p * 2);
|
||||
|
||||
renderScope.drawRoundedRectangle(p, p, h, h, iconRadius.value, iconBg.value);
|
||||
// Main background
|
||||
renderScope.drawRoundedRectangle(
|
||||
0, 0,
|
||||
width, height,
|
||||
Theme.WIDGET_RADIUS.value,
|
||||
bgColor);
|
||||
|
||||
renderScope.drawTexture(mod.getIconTexture(), p + (p / 2), p + (p / 2), 0, 0, h - p, h - p, iconFg.value);
|
||||
// Icon background
|
||||
renderScope.drawRoundedRectangle(
|
||||
p, p,
|
||||
h, h,
|
||||
Theme.WIDGET_RADIUS.value,
|
||||
iconBg);
|
||||
|
||||
renderScope.drawText(0.6f, mod.getName(), p+h+4, p + 1, font.value, fgColor.value);
|
||||
// Icon
|
||||
renderScope.drawTexture(
|
||||
mod.getIconTexture(),
|
||||
p + (p / 2),
|
||||
p + (p / 2),
|
||||
0, 0,
|
||||
h - p,
|
||||
h - p,
|
||||
iconFg);
|
||||
|
||||
renderScope.drawText(0.35f, mod.getVersion(), width - p - (int) (Fonts.getWidth(mod.getVersion(), font.value) * 0.35f), p + 1, font.value, fgColor.value);
|
||||
// Module name
|
||||
renderScope.drawText(
|
||||
0.6f,
|
||||
mod.getName(),
|
||||
p + h + 4,
|
||||
p + 1,
|
||||
Theme.FONT.value,
|
||||
fgColor);
|
||||
|
||||
// Version (right aligned)
|
||||
String version = mod.getVersion();
|
||||
int versionWidth = (int) (Fonts.getWidth(version, Theme.FONT.value) * 0.35f);
|
||||
|
||||
renderScope.drawText(
|
||||
0.35f,
|
||||
version,
|
||||
width - p - versionWidth,
|
||||
p + 1,
|
||||
Theme.FONT.value,
|
||||
fgColor);
|
||||
|
||||
// Tags
|
||||
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;
|
||||
int tagWidth = (int) (Fonts.getWidth(tag, Theme.FONT.value) * 0.4f);
|
||||
|
||||
renderScope.drawRoundedRectangle(
|
||||
xt,
|
||||
p + (h / 2),
|
||||
tagWidth + 6,
|
||||
12,
|
||||
5,
|
||||
Theme.ACCENT.value);
|
||||
|
||||
renderScope.drawText(
|
||||
0.4f,
|
||||
tag,
|
||||
xt + 2,
|
||||
p + (h / 2) + 3,
|
||||
Theme.FONT.value,
|
||||
Theme.ACCENT_FG.value);
|
||||
|
||||
xt += tagWidth + 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);
|
||||
// Settings icon
|
||||
renderScope.drawTexture(
|
||||
Textures.SETTINGS,
|
||||
iconX,
|
||||
iconY,
|
||||
0, 0,
|
||||
s, s,
|
||||
settingsColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(int mouseX, int mouseY) {
|
||||
int p = 10;
|
||||
int s = 14;
|
||||
boolean settingsHover = Utils.isHovering(
|
||||
mouseX - getSettingsX(),
|
||||
mouseY - getSettingsY(),
|
||||
getSettingsHitBox(),
|
||||
getSettingsHitBox(),
|
||||
1.0f);
|
||||
|
||||
if (Utils.isHovering(mouseX - (width - s - p - 5), mouseY - (height - s - p - 5), s + 10, s + 10, 1.0f)) {
|
||||
if (settingsHover) {
|
||||
SaturnClient.client.setScreen(new ConfigEditor(mod.getConfig()));
|
||||
} else {
|
||||
mod.setEnabled(!mod.isEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
private int getSettingsX() {
|
||||
return width - s - p - expand;
|
||||
}
|
||||
|
||||
private int getSettingsY() {
|
||||
return height - s - p - expand;
|
||||
}
|
||||
|
||||
private int getSettingsHitBox() {
|
||||
return s + expand * 2;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
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.saturnclient.config.SaturnClientConfig;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.resources.Textures;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
@@ -18,19 +17,6 @@ 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;
|
||||
@@ -79,36 +65,29 @@ public class Sidebar extends Element {
|
||||
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;
|
||||
int padding = 10;
|
||||
int sp = 30 - padding + gap;
|
||||
int s = 30 - (padding * 2);
|
||||
int s2 = s + padding;
|
||||
|
||||
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
|
||||
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.BG_RADIUS.value, Theme.BACKGROUND.value);
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (SidebarComponent component : components) {
|
||||
int y = component.end ? ((height - (sp * bottomRow)) - (s * 2)) : (padding + (sp * topRow));
|
||||
boolean selected = ctx.isHovering(0, y - (padding / 2), s2, s2) || active == i;
|
||||
|
||||
renderScope.drawRoundedRectangle(padding / 2, y - (padding / 2), s + padding,
|
||||
s + padding, Theme.WIDGET_RADIUS.value, Theme.getBg(selected));
|
||||
|
||||
renderScope.drawTexture(component.sprite, padding, y, 0, 0, s, s, Theme.getFg(selected));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -121,25 +100,24 @@ public class Sidebar extends Element {
|
||||
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;
|
||||
int gap = 5;
|
||||
int padding = 10;
|
||||
int sp = 30 - padding + gap;
|
||||
int s = 30 - (padding * 2);
|
||||
int s2 = s + padding;
|
||||
|
||||
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)) {
|
||||
int y = component.end ? ((height - (sp * bottomRow)) - (s * 2)) : (padding + (sp * topRow));
|
||||
boolean hovering = Utils.isHovering(mouseX - (padding / 2), mouseY - (y - (padding / 2)), s2, s2, scale);
|
||||
|
||||
if (hovering) {
|
||||
component.onClick.run();
|
||||
break;
|
||||
}
|
||||
|
||||
if (component.end) {
|
||||
bottomRow += 1;
|
||||
} else {
|
||||
int y = padding.value + (sp * topRow);
|
||||
if (Utils.isHovering(mouseX - (padding.value / 2), mouseY - (y - (padding.value / 2)), s2, s2, scale)) {
|
||||
component.onClick.run();
|
||||
break;
|
||||
}
|
||||
topRow += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
package org.saturnclient.ui2.components.dialog;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.saturnclient.saturnclient.mixin.DrawContextAccessor;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
import org.saturnclient.ui2.SaturnScreen;
|
||||
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
|
||||
public class ColorDialog extends SaturnScreen {
|
||||
private Property<Integer> prop;
|
||||
private SaturnScreen parent;
|
||||
private static final int PICKER_WIDTH = 200;
|
||||
private static final int PICKER_HEIGHT = 200;
|
||||
private static final int SLIDER_WIDTH = 200;
|
||||
private static final int SLIDER_HEIGHT = 10;
|
||||
private static final int HUE_SLIDER_HEIGHT = 10;
|
||||
private float hue = 0.0f;
|
||||
|
||||
private int selectedX = 0;
|
||||
private int selectedY = 0;
|
||||
|
||||
private float opacity = 1.0f;
|
||||
private boolean dragging = false;
|
||||
|
||||
public ColorDialog(Property<Integer> prop , SaturnScreen parent) {
|
||||
super("Color Dialog");
|
||||
this.prop = prop;
|
||||
this.parent = parent;
|
||||
|
||||
int alpha = (prop.value >> 24) & 0xFF;
|
||||
if (alpha == 0) alpha = 255; // fallback if no alpha specified
|
||||
this.opacity = alpha / 255.0f;
|
||||
syncFromColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ui() {}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
SaturnClient.client.setScreen(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
super.render(context, mouseX, mouseY, delta);
|
||||
RenderScope renderScope = new RenderScope(context.getMatrices(),
|
||||
((DrawContextAccessor) context).getVertexConsumers());
|
||||
|
||||
renderScope.matrices.push();
|
||||
|
||||
renderScope.matrices.scale(0.5f, 0.5f, 0.5f);
|
||||
|
||||
int xStart = (width - PICKER_WIDTH) / 2;
|
||||
int yStart = (height - PICKER_HEIGHT) / 2;
|
||||
|
||||
// Draw the HSV saturation/value square (with current hue)
|
||||
for (int x = 0; x < PICKER_WIDTH; x++) {
|
||||
for (int y = 0; y < PICKER_HEIGHT; y++) {
|
||||
float saturation = x / (float) PICKER_WIDTH;
|
||||
float value = 1.0f - y / (float) PICKER_HEIGHT;
|
||||
int color = Color.HSBtoRGB(hue, saturation, value);
|
||||
context.fill(xStart + x, yStart + y, xStart + x + 1, yStart + y + 1, 0xFF000000 | color);
|
||||
}
|
||||
}
|
||||
|
||||
int x = (xStart + selectedX) - 4;
|
||||
int y = (yStart + selectedY) - 4;
|
||||
context.fill(x, y, x+8, y+8, 0xFF000000 | prop.value);
|
||||
context.drawBorder(x, y, 8, 8, 0xFFffffff);
|
||||
|
||||
renderSlider(context, mouseX, mouseY, delta);
|
||||
renderHueSlider(context);
|
||||
|
||||
renderScope.matrices.pop();
|
||||
}
|
||||
|
||||
public void renderSlider(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
int xStart = (width - SLIDER_WIDTH) / 2;
|
||||
int yStart = (height - SLIDER_HEIGHT - PICKER_HEIGHT) / 2 - 10;
|
||||
|
||||
// Draw slider background (left = transparent, right = white)
|
||||
for (int x = 0; x < SLIDER_WIDTH; x++) {
|
||||
float localOpacity = x / (float) SLIDER_WIDTH;
|
||||
int alpha = (int)(localOpacity * 255) & 0xFF;
|
||||
int color = (alpha << 24) | (prop.value & 0xFFFFFF);
|
||||
context.fill(xStart + x, yStart, xStart + x + 1, yStart + SLIDER_HEIGHT, color);
|
||||
}
|
||||
|
||||
// Draw current value as a marker (thin black line)
|
||||
int knobX = xStart + (int)(opacity * SLIDER_WIDTH);
|
||||
context.fill(knobX - 1, yStart - 2, knobX + 1, yStart + SLIDER_HEIGHT + 2, 0xFF000000);
|
||||
|
||||
// Draw opacity text
|
||||
// context.drawText(textRenderer, String.format("Opacity: %.2f", opacity), xStart, yStart - 15, 0xFFFFFF, false);
|
||||
}
|
||||
|
||||
private void renderHueSlider(DrawContext context) {
|
||||
int xStart = (width - SLIDER_WIDTH) / 2;
|
||||
int yStart = (height + PICKER_HEIGHT) / 2 + 10;
|
||||
|
||||
for (int x = 0; x < SLIDER_WIDTH; x++) {
|
||||
float localHue = x / (float) SLIDER_WIDTH;
|
||||
int color = Color.HSBtoRGB(localHue, 1.0f, 1.0f);
|
||||
context.fill(xStart + x, yStart, xStart + x + 1, yStart + HUE_SLIDER_HEIGHT, 0xFF000000 | color);
|
||||
}
|
||||
|
||||
int knobX = xStart + (int)(hue * SLIDER_WIDTH);
|
||||
context.fill(knobX - 1, yStart - 2, knobX + 1, yStart + HUE_SLIDER_HEIGHT + 2, 0xFF000000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||
mouseX *= 2;
|
||||
mouseY *= 2;
|
||||
|
||||
int xStart = (width - PICKER_WIDTH) / 2;
|
||||
int yStart = (height - PICKER_HEIGHT) / 2;
|
||||
|
||||
int relX = (int) mouseX - xStart;
|
||||
int relY = (int) mouseY - yStart;
|
||||
|
||||
if (relX >= 0 && relX < PICKER_WIDTH && relY >= 0 && relY < PICKER_HEIGHT) {
|
||||
float saturation = relX / (float) PICKER_WIDTH;
|
||||
float value = 1.0f - relY / (float) PICKER_HEIGHT;
|
||||
prop.value = Color.HSBtoRGB(hue, saturation, value);
|
||||
|
||||
int alpha = (int)(opacity * 255) & 0xFF;
|
||||
prop.value = (alpha << 24) | (prop.value & 0xFFFFFF);
|
||||
|
||||
selectedX = relX;
|
||||
selectedY = relY;
|
||||
|
||||
return true;
|
||||
} else if (isInSlider(mouseX, mouseY)) {
|
||||
updateOpacity(mouseX);
|
||||
dragging = true;
|
||||
return true;
|
||||
} else if (isInHueSlider(mouseX, mouseY)) {
|
||||
updateHue(mouseX);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.mouseClicked(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseReleased(double mouseX, double mouseY, int button) {
|
||||
dragging = false;
|
||||
return super.mouseReleased(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
|
||||
mouseX *= 2;
|
||||
mouseY *= 2;
|
||||
|
||||
if (isInHueSlider(mouseX, mouseY)) {
|
||||
updateHue(mouseX);
|
||||
return true;
|
||||
} else if (dragging) {
|
||||
updateOpacity(mouseX);
|
||||
return true;
|
||||
} else {
|
||||
int xStart = (width - PICKER_WIDTH) / 2;
|
||||
int yStart = (height - PICKER_HEIGHT) / 2;
|
||||
|
||||
int relX = (int) mouseX - xStart;
|
||||
int relY = (int) mouseY - yStart;
|
||||
|
||||
if (relX >= 0 && relX < PICKER_WIDTH && relY >= 0 && relY < PICKER_HEIGHT) {
|
||||
float saturation = relX / (float) PICKER_WIDTH;
|
||||
float value = 1.0f - relY / (float) PICKER_HEIGHT;
|
||||
prop.value = Color.HSBtoRGB(hue, saturation, value);
|
||||
|
||||
int alpha = (int)(opacity * 255) & 0xFF;
|
||||
prop.value = (alpha << 24) | (prop.value & 0xFFFFFF);
|
||||
|
||||
selectedX = relX;
|
||||
selectedY = relY;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
|
||||
}
|
||||
|
||||
private boolean isInSlider(double mouseX, double mouseY) {
|
||||
int xStart = (width - SLIDER_WIDTH) / 2;
|
||||
int yStart = (height - SLIDER_HEIGHT - PICKER_HEIGHT) / 2 - 10;
|
||||
return mouseX >= xStart && mouseX <= xStart + SLIDER_WIDTH &&
|
||||
mouseY >= yStart && mouseY <= yStart + SLIDER_HEIGHT;
|
||||
}
|
||||
|
||||
private boolean isInHueSlider(double mouseX, double mouseY) {
|
||||
int xStart = (width - SLIDER_WIDTH) / 2;
|
||||
int yStart = (height + PICKER_HEIGHT) / 2 + 10;
|
||||
return mouseX >= xStart && mouseX <= xStart + SLIDER_WIDTH &&
|
||||
mouseY >= yStart && mouseY <= yStart + HUE_SLIDER_HEIGHT;
|
||||
}
|
||||
|
||||
private void updateOpacity(double mouseX) {
|
||||
int xStart = (width - SLIDER_WIDTH) / 2;
|
||||
float newOpacity = (float)((mouseX - xStart) / SLIDER_WIDTH);
|
||||
opacity = Math.max(0.0f, Math.min(1.0f, newOpacity));
|
||||
|
||||
int alpha = (int)(opacity * 255) & 0xFF;
|
||||
prop.value = (alpha << 24) | (prop.value & 0xFFFFFF);
|
||||
}
|
||||
|
||||
private void updateHue(double mouseX) {
|
||||
int xStart = (width - SLIDER_WIDTH) / 2;
|
||||
float newHue = (float)((mouseX - xStart) / SLIDER_WIDTH);
|
||||
hue = Math.max(0.0f, Math.min(1.0f, newHue));
|
||||
|
||||
float saturation = selectedX / (float) PICKER_WIDTH;
|
||||
float value = 1.0f - selectedY / (float) PICKER_HEIGHT;
|
||||
prop.value = Color.HSBtoRGB(hue, saturation, value);
|
||||
|
||||
int alpha = (int)(opacity * 255) & 0xFF;
|
||||
prop.value = (alpha << 24) | (prop.value & 0xFFFFFF);
|
||||
}
|
||||
|
||||
private void syncFromColor() {
|
||||
int rgb = prop.value & 0xFFFFFF;
|
||||
float[] hsb = Color.RGBtoHSB((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF, null);
|
||||
|
||||
this.hue = hsb[0];
|
||||
|
||||
float saturation = hsb[1];
|
||||
float value = hsb[2];
|
||||
|
||||
this.selectedX = (int)(saturation * PICKER_WIDTH);
|
||||
this.selectedY = (int)((1.0f - value) * PICKER_HEIGHT);
|
||||
|
||||
// Extract alpha and convert to opacity float
|
||||
int alpha = (prop.value >> 24) & 0xFF;
|
||||
if (alpha == 0) alpha = 255;
|
||||
this.opacity = alpha / 255.0f;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,183 @@
|
||||
package org.saturnclient.ui2.components.inputs;
|
||||
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import java.awt.Color;
|
||||
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
import org.saturnclient.ui2.SaturnScreen;
|
||||
import org.saturnclient.ui2.components.dialog.ColorDialog;
|
||||
|
||||
public class HexInput extends Element {
|
||||
public Property<Integer> prop;
|
||||
public SaturnScreen screen;
|
||||
|
||||
public HexInput(Property<Integer> prop, SaturnScreen screen) {
|
||||
// Picker state
|
||||
private boolean open = false;
|
||||
private float hue = 0f;
|
||||
private float opacity = 1f;
|
||||
private int selectedX = 0;
|
||||
private int selectedY = 0;
|
||||
|
||||
private boolean draggingOpacity = false;
|
||||
|
||||
// Dimensions
|
||||
private static final int PICKER_WIDTH = 100;
|
||||
private static final int PICKER_HEIGHT = 100;
|
||||
private static final int SLIDER_WIDTH = 100;
|
||||
private static final int SLIDER_HEIGHT = 10;
|
||||
private static final int HUE_SLIDER_HEIGHT = 10;
|
||||
|
||||
public HexInput(Property<Integer> prop) {
|
||||
this.prop = prop;
|
||||
this.width = 58;
|
||||
this.height = 20;
|
||||
this.prop = prop;
|
||||
this.scale = 0.8f;
|
||||
this.screen = screen;
|
||||
syncFromColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(int mouseX, int mouseY) {
|
||||
SaturnClient.client.setScreen(new ColorDialog(prop, screen));
|
||||
open = !open;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(double mouseX, double mouseY, int button) {
|
||||
if (open) {
|
||||
handlePickerClick(mouseX - width - 8, mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
|
||||
if (open) {
|
||||
handlePickerDrag(mouseX - width - 8, mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(double mouseX, double mouseY, int button) {
|
||||
draggingOpacity = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, 17, prop.value);
|
||||
// Draw the main color rectangle
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, 6, prop.value);
|
||||
|
||||
if (!open)
|
||||
return;
|
||||
|
||||
// Draw the picker relative to (width + 8, 0)
|
||||
int pickerX = width + 8;
|
||||
int pickerY = 0;
|
||||
|
||||
renderColorSquare(renderScope, pickerX, pickerY);
|
||||
renderOpacitySlider(renderScope, pickerX, pickerY - SLIDER_HEIGHT - 10);
|
||||
renderHueSlider(renderScope, pickerX, pickerY + PICKER_HEIGHT + 10);
|
||||
}
|
||||
|
||||
private void renderColorSquare(RenderScope renderScope, int xStart, int yStart) {
|
||||
for (int x = 0; x < PICKER_WIDTH; x++) {
|
||||
for (int y = 0; y < PICKER_HEIGHT; y++) {
|
||||
float saturation = x / (float) PICKER_WIDTH;
|
||||
float value = 1f - y / (float) PICKER_HEIGHT;
|
||||
int color = Color.HSBtoRGB(hue, saturation, value);
|
||||
int alpha = (int) (opacity * 255) & 0xFF;
|
||||
color = (alpha << 24) | (color & 0xFFFFFF);
|
||||
renderScope.fill(xStart + x, yStart + y, xStart + x + 1, yStart + y + 1, color);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw selection square
|
||||
int selX = xStart + selectedX - 4;
|
||||
int selY = yStart + selectedY - 4;
|
||||
renderScope.fill(selX, selY, selX + 8, selY + 8, 0xFF000000 | prop.value);
|
||||
renderScope.drawBorder(selX, selY, 8, 8, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
private void renderOpacitySlider(RenderScope renderScope, int xStart, int yStart) {
|
||||
for (int x = 0; x < SLIDER_WIDTH; x++) {
|
||||
float localOpacity = x / (float) SLIDER_WIDTH;
|
||||
int alpha = (int) (localOpacity * 255) & 0xFF;
|
||||
int color = (alpha << 24) | (prop.value & 0xFFFFFF);
|
||||
renderScope.fill(xStart + x, yStart, xStart + x + 1, yStart + SLIDER_HEIGHT, color);
|
||||
}
|
||||
|
||||
int knobX = xStart + (int) (opacity * SLIDER_WIDTH);
|
||||
renderScope.fill(knobX - 1, yStart - 2, knobX + 1, yStart + SLIDER_HEIGHT + 2, 0xFF000000);
|
||||
}
|
||||
|
||||
private void renderHueSlider(RenderScope renderScope, int xStart, int yStart) {
|
||||
for (int x = 0; x < SLIDER_WIDTH; x++) {
|
||||
float localHue = x / (float) SLIDER_WIDTH;
|
||||
int color = Color.HSBtoRGB(localHue, 1f, 1f);
|
||||
renderScope.fill(xStart + x, yStart, xStart + x + 1, yStart + HUE_SLIDER_HEIGHT, 0xFF000000 | color);
|
||||
}
|
||||
|
||||
int knobX = xStart + (int) (hue * SLIDER_WIDTH);
|
||||
renderScope.fill(knobX - 1, yStart - 2, knobX + 1, yStart + HUE_SLIDER_HEIGHT + 2, 0xFF000000);
|
||||
}
|
||||
|
||||
private void handlePickerClick(double mouseX, double mouseY) {
|
||||
// Color square
|
||||
if (mouseX >= 0 && mouseX < PICKER_WIDTH && mouseY >= 0 && mouseY < PICKER_HEIGHT) {
|
||||
selectedX = (int) mouseX;
|
||||
selectedY = (int) mouseY;
|
||||
updateColorFromSelection();
|
||||
}
|
||||
|
||||
// Opacity slider
|
||||
int opY = -SLIDER_HEIGHT - 10;
|
||||
if (mouseX >= 0 && mouseX < SLIDER_WIDTH && mouseY >= opY && mouseY <= opY + SLIDER_HEIGHT) {
|
||||
updateOpacity(mouseX);
|
||||
draggingOpacity = true;
|
||||
}
|
||||
|
||||
// Hue slider
|
||||
int hueY = PICKER_HEIGHT + 10;
|
||||
if (mouseX >= 0 && mouseX < SLIDER_WIDTH && mouseY >= hueY && mouseY <= hueY + HUE_SLIDER_HEIGHT) {
|
||||
updateHue(mouseX);
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePickerDrag(double mouseX, double mouseY) {
|
||||
// Opacity
|
||||
if (draggingOpacity)
|
||||
updateOpacity(mouseX);
|
||||
|
||||
// Color square
|
||||
if (mouseX >= 0 && mouseX < PICKER_WIDTH && mouseY >= 0 && mouseY < PICKER_HEIGHT) {
|
||||
selectedX = (int) mouseX;
|
||||
selectedY = (int) mouseY;
|
||||
updateColorFromSelection();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateColorFromSelection() {
|
||||
float saturation = selectedX / (float) PICKER_WIDTH;
|
||||
float value = 1f - selectedY / (float) PICKER_HEIGHT;
|
||||
int color = Color.HSBtoRGB(hue, saturation, value);
|
||||
int alpha = (int) (opacity * 255) & 0xFF;
|
||||
prop.value = (alpha << 24) | (color & 0xFFFFFF);
|
||||
}
|
||||
|
||||
private void updateHue(double mouseX) {
|
||||
hue = (float) Math.max(0, Math.min(1, mouseX / SLIDER_WIDTH));
|
||||
updateColorFromSelection();
|
||||
}
|
||||
|
||||
private void updateOpacity(double mouseX) {
|
||||
opacity = (float) Math.max(0, Math.min(1, mouseX / SLIDER_WIDTH));
|
||||
updateColorFromSelection();
|
||||
}
|
||||
|
||||
private void syncFromColor() {
|
||||
int rgb = prop.value & 0xFFFFFF;
|
||||
float[] hsb = Color.RGBtoHSB((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF, null);
|
||||
hue = hsb[0];
|
||||
selectedX = (int) (hsb[1] * PICKER_WIDTH);
|
||||
selectedY = (int) ((1f - hsb[2]) * PICKER_HEIGHT);
|
||||
int alpha = (prop.value >> 24) & 0xFF;
|
||||
if (alpha == 0)
|
||||
alpha = 255;
|
||||
opacity = alpha / 255f;
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,20 @@ package org.saturnclient.ui2.components.inputs;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.saturnclient.saturnclient.config.ThemeManager;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
import org.saturnclient.ui2.resources.Fonts;
|
||||
|
||||
public abstract class Input extends Element {
|
||||
private static ThemeManager theme = new ThemeManager("Input");
|
||||
private static Property<Integer> font = theme.property("font", Property.font(1));
|
||||
|
||||
String text = "";
|
||||
public int cursorPosition = 0;
|
||||
|
||||
public abstract void checkReset();
|
||||
|
||||
public abstract void backspace();
|
||||
|
||||
public boolean validateBackspace() {
|
||||
return true;
|
||||
}
|
||||
@@ -71,11 +70,12 @@ public abstract class Input extends Element {
|
||||
int scrollOffset = getScrollOffset();
|
||||
String visibleText = getVisibleText(scrollOffset);
|
||||
int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
|
||||
renderScope.drawText(0.6f, visibleText, 4, 4, font.value, textColor);
|
||||
renderScope.drawText(0.6f, visibleText, 4, 4, Theme.FONT.value, textColor);
|
||||
|
||||
if (focused) {
|
||||
int cursorX = 4 +
|
||||
(int) (Fonts.getWidth(visibleText.substring(0, cursorPosition - scrollOffset), font.value) * 0.6f);
|
||||
(int) (Fonts.getWidth(visibleText.substring(0, cursorPosition - scrollOffset), Theme.FONT.value)
|
||||
* 0.6f);
|
||||
|
||||
renderScope.drawRect(cursorX, 2, 1, height - 4, 0xFFFFFFFF);
|
||||
}
|
||||
@@ -90,7 +90,7 @@ public abstract class Input extends Element {
|
||||
int maxWidth = width - 4; // Available width for text
|
||||
while (offset < cursorPosition) { // Ensure cursor remains visible
|
||||
String visibleText = text.substring(offset, cursorPosition);
|
||||
if (Fonts.getWidth(visibleText, font.value) > maxWidth) {
|
||||
if (Fonts.getWidth(visibleText, Theme.FONT.value) > maxWidth) {
|
||||
offset++;
|
||||
} else {
|
||||
break;
|
||||
@@ -104,7 +104,7 @@ public abstract class Input extends Element {
|
||||
|
||||
// Ensure text does not overflow past the available width
|
||||
for (int i = 1; i <= visibleText.length(); i++) {
|
||||
if (Fonts.getWidth(visibleText.substring(0, i), font.value) > width - 4) {
|
||||
if (Fonts.getWidth(visibleText.substring(0, i), Theme.FONT.value) > width - 4) {
|
||||
return visibleText.substring(0, i - 1); // Trim overflow
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,13 @@ package org.saturnclient.ui2.components.inputs;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.saturnclient.saturnclient.config.ThemeManager;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
import org.saturnclient.ui2.resources.Fonts;
|
||||
|
||||
public class KeybindingSelector extends Element {
|
||||
private static ThemeManager theme = new ThemeManager("Input");
|
||||
private static Property<Integer> font = theme.property("font", Property.font(1));
|
||||
Property<Integer> prop;
|
||||
|
||||
public KeybindingSelector(Property<Integer> prop) {
|
||||
@@ -23,9 +21,12 @@ public class KeybindingSelector extends Element {
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, 10, 0xFF000000);
|
||||
renderScope.drawText(0.6f, prop.value == -1 ? "<NONE>" :
|
||||
GLFW.glfwGetKeyName(prop.value, GLFW.glfwGetKeyScancode(prop.value)).toUpperCase(),
|
||||
4, 4, font.value, textColor);
|
||||
String name = GLFW.glfwGetKeyName(prop.value, GLFW.glfwGetKeyScancode(prop.value));
|
||||
|
||||
renderScope.drawText(0.6f,
|
||||
prop.value == -1 ? "<NONE>"
|
||||
: name != null ? name.toUpperCase() : "<Invalid-Key>",
|
||||
4, 4, Theme.FONT.value, textColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.saturnclient.ui2.components.inputs;
|
||||
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.saturnclient.saturnclient.config.ThemeManager;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
@@ -10,11 +10,6 @@ import org.saturnclient.ui2.resources.Fonts;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
|
||||
public class Select extends Element {
|
||||
private static ThemeManager theme = new ThemeManager("Toggle");
|
||||
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xAAAAAA));
|
||||
private static Property<Integer> arrowColor = theme.property("arrow-color", Property.color(0xff8B5CF6));
|
||||
private static Property<Integer> font = theme.property("font", Property.font(1));
|
||||
|
||||
private Property<Integer> prop;
|
||||
|
||||
public Select(Property<Integer> prop) {
|
||||
@@ -27,15 +22,14 @@ public class Select extends Element {
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
|
||||
|
||||
renderScope.drawText(0.8f, "<", 0, 0, font.value, arrowColor.value);
|
||||
renderScope.drawText(0.8f, "<", 0, 0, Theme.FONT.value, Theme.FOREGROUND.value);
|
||||
|
||||
String text = prop.getSelection();
|
||||
|
||||
renderScope.drawText(0.6f, text, (int) (width - ((float) Fonts.getWidth(text, font.value) * 0.6f)) / 2, 1, font.value, fgColor.value);
|
||||
renderScope.drawText(0.6f, text, (int) (width - ((float) Fonts.getWidth(text, Theme.FONT.value) * 0.6f)) / 2, 1,
|
||||
Theme.FONT.value, Theme.FOREGROUND.value);
|
||||
|
||||
renderScope.drawText(0.8f, ">", width - 10, 0, font.value, arrowColor.value);
|
||||
|
||||
theme.setState(null);
|
||||
renderScope.drawText(0.8f, ">", width - 10, 0, Theme.FONT.value, Theme.FOREGROUND.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,22 +1,12 @@
|
||||
package org.saturnclient.ui2.components.inputs;
|
||||
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.saturnclient.saturnclient.config.ThemeManager;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
|
||||
public class Toggle extends Element {
|
||||
private static ThemeManager theme = new ThemeManager("Toggle", "hovering", "enabled");
|
||||
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0xFF808080));
|
||||
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFF909090));
|
||||
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(30));
|
||||
|
||||
static {
|
||||
theme.propertyStateDefault("enabled", "bg-color", 0xaf8B5CF6);
|
||||
theme.propertyStateDefault("enabled", "fg-color", 0xff8B5CF6);
|
||||
}
|
||||
|
||||
private Property<Boolean> prop;
|
||||
|
||||
public Toggle(Property<Boolean> prop) {
|
||||
@@ -27,19 +17,10 @@ public class Toggle extends Element {
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
theme.setState(null);
|
||||
|
||||
if (prop.value) {
|
||||
theme.setState("enabled");
|
||||
}
|
||||
|
||||
if (ctx.isHovering()) {
|
||||
theme.applyState("hovering");
|
||||
}
|
||||
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
|
||||
|
||||
renderScope.drawRoundedRectangle(prop.value ? 30 : 0, 0, 30, 30, 30, fgColor.value);
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, height,
|
||||
Theme.withAlpha(0.5f, prop.value ? Theme.ACCENT.value : Theme.PRIMARY.value));
|
||||
renderScope.drawRoundedRectangle(prop.value ? 30 : 0, 0, 30, 30, 30,
|
||||
prop.value ? Theme.ACCENT.value : Theme.PRIMARY.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.saturnclient.ui2.elements;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.saturnclient.saturnclient.config.SaturnClientConfig;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
import org.saturnclient.ui2.components.ElementRenderer;
|
||||
|
||||
public class AnimationStagger extends Element {
|
||||
protected List<Element> children = new ArrayList<>();
|
||||
public int delay;
|
||||
|
||||
public AnimationStagger(int delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
public void draw(Element element) {
|
||||
if (!SaturnClientConfig.stagger.value) {
|
||||
element.animation = null;
|
||||
}
|
||||
|
||||
if (element.animation != null) {
|
||||
element.animation.delay = delay * children.size();
|
||||
}
|
||||
|
||||
ElementRenderer.draw(children, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
ElementRenderer.render(children, ctx.elapsed, renderScope,
|
||||
ctx.mouseX,
|
||||
ctx.mouseY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(double mouseX, double mouseY, int button) {
|
||||
ElementRenderer.mouseClicked(children, mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
ElementRenderer.keyPressed(children, keyCode, scanCode, modifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
|
||||
ElementRenderer.mouseDragged(children, mouseX, mouseY, button, deltaX, deltaY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(double mouseX, double mouseY, int button) {
|
||||
ElementRenderer.mouseReleased(children, mouseX, mouseY, button);
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,29 @@
|
||||
package org.saturnclient.ui2.elements;
|
||||
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.saturnclient.saturnclient.config.ThemeManager;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
import org.saturnclient.ui2.resources.Fonts;
|
||||
|
||||
public class Button extends Element {
|
||||
private static ThemeManager theme = new ThemeManager("Button", "hovering");
|
||||
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0xFF000000));
|
||||
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFFFFFFFF));
|
||||
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
|
||||
private static Property<Integer> font = theme.property("font", Property.font(1));
|
||||
|
||||
static {
|
||||
theme.propertyStateDefault("hovering", "fg-color", 0xFF845eee);
|
||||
}
|
||||
|
||||
private String text;
|
||||
private Runnable onClick;
|
||||
|
||||
public Button(String text, Runnable onClick) {
|
||||
this.text = text;
|
||||
this.onClick = onClick;
|
||||
this.dimensions(Fonts.getWidth(text, font.value) + 50, 38);
|
||||
this.dimensions(Fonts.getWidth(text, Theme.FONT.value) + 50, 38);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
if (ctx.isHovering()) {
|
||||
theme.setState("hovering");
|
||||
} else {
|
||||
theme.setState(null);
|
||||
}
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
|
||||
ctx.isHovering() ? Theme.ACCENT.value : Theme.PRIMARY.value);
|
||||
|
||||
// renderScope.drawText(text, (width - Fonts.getWidth(text, font)) / 2, (height - Fonts.getHeight()) / 2, bold.value, fgColor.value);
|
||||
renderScope.drawText(text, Fonts.centerX(width, text, font.value), Fonts.centerY(height), font.value, fgColor.value);
|
||||
renderScope.drawText(text, Fonts.centerX(width, text, Theme.FONT.value), Fonts.centerY(height),
|
||||
Theme.FONT.value,
|
||||
ctx.isHovering() ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.saturnclient.ui2.elements;
|
||||
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.saturnclient.saturnclient.config.ThemeManager;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
@@ -10,9 +9,6 @@ import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class ClickableTexture extends Element {
|
||||
private static ThemeManager theme = new ThemeManager("ClickableTexture", "hovering");
|
||||
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFFFFFFFF));
|
||||
|
||||
Identifier sprite;
|
||||
private Runnable onClick;
|
||||
|
||||
@@ -23,14 +19,9 @@ public class ClickableTexture extends Element {
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
if (ctx.isHovering()) {
|
||||
theme.setState("hovering");
|
||||
} else {
|
||||
theme.setState(null);
|
||||
}
|
||||
|
||||
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
|
||||
renderScope.drawTexture(sprite, 0, 0, 0, 0, width, height, fgColor.value);
|
||||
renderScope.drawTexture(sprite, 0, 0, 0, 0, width, height,
|
||||
ctx.isHovering() ? Theme.ACCENT.value : Theme.PRIMARY.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,8 +3,9 @@ package org.saturnclient.ui2.elements;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
import org.saturnclient.ui2.resources.SvgTexture;
|
||||
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class ImageTexture extends Element {
|
||||
@@ -16,7 +17,16 @@ public class ImageTexture extends Element {
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
|
||||
if (sprite.toString().endsWith(".svg")) {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
|
||||
// Get the actual window pixel dimensions for the image
|
||||
int renderWidth = (int) (width * client.getWindow().getFramebufferWidth() / client.getWindow().getWidth());
|
||||
int renderHeight = (int) (height * client.getWindow().getFramebufferHeight() / client.getWindow().getHeight());
|
||||
|
||||
sprite = SvgTexture.getSvg(client, sprite, renderWidth * 2, renderHeight * 2);
|
||||
}
|
||||
|
||||
renderScope.drawTexture(sprite, 0, 0, 0, 0, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.saturnclient.ui2.elements;
|
||||
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.saturnclient.saturnclient.config.ThemeManager;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
@@ -10,11 +10,9 @@ import org.saturnclient.ui2.resources.Fonts;
|
||||
import org.saturnclient.ui2.resources.Textures;
|
||||
|
||||
public class Notification extends Element {
|
||||
private static ThemeManager theme = new ThemeManager("Notification");
|
||||
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0xFF000000));
|
||||
private static Property<Integer> error = theme.property("error", Property.color(0xFFbf212f));
|
||||
private static Property<Integer> info = theme.property("info", Property.color(0xFF264b96));
|
||||
private static Property<Integer> success = theme.property("success", Property.color(0xFF27b376));
|
||||
private static Property<Integer> error = Property.color(0xFFbf212f);
|
||||
private static Property<Integer> info = Property.color(0xFF264b96);
|
||||
private static Property<Integer> success = Property.color(0xFF27b376);
|
||||
|
||||
public static enum NotificationKind {
|
||||
Error,
|
||||
@@ -40,7 +38,7 @@ public class Notification extends Element {
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, 10, bgColor.value);
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, 10, Theme.PRIMARY.value);
|
||||
switch (kind) {
|
||||
case Error:
|
||||
renderScope.drawTexture(Textures.CIRCLE_X, 9, 9, 0, 0, 15, 15, error.value);
|
||||
|
||||
@@ -4,21 +4,16 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.saturnclient.saturnclient.config.ThemeManager;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
import org.saturnclient.ui2.components.ElementRenderer;
|
||||
|
||||
public class Scroll extends Element {
|
||||
private static ThemeManager theme = new ThemeManager("Scroll");
|
||||
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0x90000000));
|
||||
private static Property<Integer> scrollBarColor = theme.property("scrollbar-color", Property.color(-7643914));
|
||||
|
||||
private static Property<Integer> scrollBarRadius = theme.property("scrollbar-radius", Property.integer(10));
|
||||
private static Property<Integer> scrollBarWidth = theme.property("scrollbar-width", Property.integer(5));
|
||||
private static Property<Integer> scrollBarPadding = theme.property("scrollbar-padding", Property.integer(5));
|
||||
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
|
||||
private static Property<Integer> scrollBarRadius = Property.integer(10);
|
||||
private static Property<Integer> scrollBarWidth = Property.integer(5);
|
||||
private static Property<Integer> scrollBarPadding = Property.integer(5);
|
||||
|
||||
int padding = 0;
|
||||
|
||||
@@ -30,18 +25,37 @@ public class Scroll extends Element {
|
||||
this.padding = padding;
|
||||
}
|
||||
|
||||
public void draw(Element element) {
|
||||
ElementRenderer.draw(children, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
|
||||
calculateMaxScroll();
|
||||
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height,
|
||||
Theme.BG_RADIUS.value, Theme.BACKGROUND.value);
|
||||
|
||||
renderScope.enableScissor(padding, padding, width - padding, height - padding);
|
||||
renderScope.matrices.push();
|
||||
renderScope.matrices.translate(padding, (-scroll) + padding, 0);
|
||||
ElementRenderer.render(children, renderScope, ctx.mouseX - padding, ctx.mouseY - padding + scroll);
|
||||
renderScope.matrices.translate(padding, -scroll + padding, 0);
|
||||
|
||||
ElementRenderer.render(children, ctx.elapsed, renderScope,
|
||||
ctx.mouseX - padding,
|
||||
ctx.mouseY - padding + scroll);
|
||||
|
||||
renderScope.matrices.pop();
|
||||
renderScope.disableScissor();
|
||||
|
||||
renderScope.drawRoundedRectangle(width - scrollBarWidth.value - scrollBarPadding.value, calculateScrollBarY(), scrollBarWidth.value, calculateScrollBarHeight(), scrollBarRadius.value, scrollBarColor.value);
|
||||
if (maxScroll > 0) {
|
||||
renderScope.drawRoundedRectangle(
|
||||
width - scrollBarWidth.value - scrollBarPadding.value,
|
||||
calculateScrollBarY(),
|
||||
scrollBarWidth.value,
|
||||
calculateScrollBarHeight(),
|
||||
scrollBarRadius.value,
|
||||
Theme.SCROLL.value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,15 +68,6 @@ public class Scroll extends Element {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(int mouseX, int mouseY) {
|
||||
ElementRenderer.mouseClicked(children, mouseX - padding, mouseY - padding + scroll, 0);
|
||||
}
|
||||
|
||||
public void draw(Element element) {
|
||||
children.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Element dimensions(int width, int height) {
|
||||
super.dimensions(width, height);
|
||||
@@ -76,8 +81,9 @@ public class Scroll extends Element {
|
||||
}
|
||||
|
||||
int calculateScrollBarHeight() {
|
||||
if (maxScroll <= 0) return height - (scrollBarPadding.value * 2); // No scrolling needed
|
||||
return Math.max(20, (height * height) / (height + maxScroll)) - (scrollBarPadding.value * 2); // At least 20px tall
|
||||
if (maxScroll <= 0)
|
||||
return height - (scrollBarPadding.value * 2);
|
||||
return Math.max(20, (height * height) / (height + maxScroll + 10)) - (scrollBarPadding.value * 2);
|
||||
}
|
||||
|
||||
int calculateScrollBarY() {
|
||||
@@ -87,8 +93,38 @@ public class Scroll extends Element {
|
||||
return Math.min(y, height - scrollBarHeight - scrollBarPadding.value);
|
||||
}
|
||||
|
||||
private void calculateMaxScroll() {
|
||||
maxScroll = 0;
|
||||
|
||||
for (Element element : children) {
|
||||
int bottom = element.y + element.height;
|
||||
maxScroll = Math.max(maxScroll, bottom - height + padding);
|
||||
}
|
||||
|
||||
if (maxScroll > 0) {
|
||||
maxScroll = maxScroll + 10;
|
||||
} else {
|
||||
maxScroll = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
ElementRenderer.keyPressed(children, keyCode, scanCode, modifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(double mouseX, double mouseY, int button) {
|
||||
ElementRenderer.mouseClicked(children, mouseX - padding, mouseY - padding + scroll, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
|
||||
ElementRenderer.mouseDragged(children, mouseX - padding, mouseY - padding + scroll, button, deltaX, deltaY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(double mouseX, double mouseY, int button) {
|
||||
ElementRenderer.mouseReleased(children, mouseX - padding, mouseY - padding + scroll, button);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.saturnclient.ui2.elements;
|
||||
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.saturnclient.saturnclient.config.ThemeManager;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
@@ -10,16 +9,6 @@ import org.saturnclient.ui2.Utils;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class TabMenu extends Element {
|
||||
private static ThemeManager theme = new ThemeManager("TabMenu", "hovering", "selected");
|
||||
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0x90000000));
|
||||
private static Property<Integer> iconFg = theme.property("icon-fg", Property.color(0xFFFFFFFF));
|
||||
private static Property<Integer> iconBg = theme.property("icon-bg", Property.color(0x00000000));
|
||||
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
|
||||
|
||||
static {
|
||||
theme.propertyStateDefault("hovering", "icon-bg", -7643914);
|
||||
}
|
||||
|
||||
public static class TabMenuComponent {
|
||||
Runnable onClick;
|
||||
Identifier sprite;
|
||||
@@ -42,19 +31,21 @@ public class TabMenu extends Element {
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, Theme.PRIMARY.value);
|
||||
|
||||
int col = 0;
|
||||
for (TabMenuComponent component : components) {
|
||||
int x = 25 * col + 5;
|
||||
if (ctx.isHovering(x, 5, 20, 20) || selectedScreen == col) {
|
||||
theme.setState("hovering");
|
||||
|
||||
boolean hovering = ctx.isHovering(x, 5, 20, 20) || selectedScreen == col;
|
||||
|
||||
if (hovering) {
|
||||
renderScope.drawRoundedRectangle(x, 5, 20, 20, Theme.WIDGET_RADIUS.value, Theme.ACCENT.value);
|
||||
}
|
||||
|
||||
renderScope.drawRoundedRectangle(x, 5, 20, 20, cornerRadius.value, iconBg.value);
|
||||
renderScope.drawTexture(component.sprite, x + 5, 10, 0, 0, 10, 10, iconFg.value);
|
||||
renderScope.drawTexture(component.sprite, x + 5, 10, 0, 0, 10, 10,
|
||||
hovering ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value);
|
||||
|
||||
theme.setState(null);
|
||||
col++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,22 @@
|
||||
package org.saturnclient.ui2.elements;
|
||||
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.saturnclient.saturnclient.config.ThemeManager;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
import org.saturnclient.ui2.resources.Fonts;
|
||||
|
||||
public class Text extends Element {
|
||||
private static ThemeManager theme = new ThemeManager("Text");
|
||||
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFFFFFFFF));
|
||||
public static Property<Integer> font = theme.property("font", Property.font(1));
|
||||
|
||||
private String text;
|
||||
|
||||
public Text(String text) {
|
||||
this.text = text;
|
||||
width = Fonts.getWidth(text, font.value);
|
||||
width = Fonts.getWidth(text, Theme.FONT.value);
|
||||
height = Fonts.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
renderScope.drawText(this.text, 0, 0, font.value, fgColor.value);
|
||||
renderScope.drawText(this.text, 0, 0, Theme.FONT.value, Theme.FOREGROUND.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.saturnclient.ui2.elements;
|
||||
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.saturnclient.saturnclient.config.ThemeManager;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.ElementContext;
|
||||
import org.saturnclient.ui2.RenderScope;
|
||||
@@ -10,19 +9,9 @@ import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class TextureButton extends Element {
|
||||
private static ThemeManager theme = new ThemeManager("TextureButton", "hovering");
|
||||
private static Property<Integer> bgColor = theme.property("bg-color", Property.color(0xFF000000));
|
||||
private static Property<Integer> fgColor = theme.property("fg-color", Property.color(0xFFFFFFFF));
|
||||
private static Property<Integer> padding = theme.property("padding", Property.integer(14));
|
||||
private static Property<Integer> cornerRadius = theme.property("corner-radius", Property.integer(10));
|
||||
|
||||
|
||||
static {
|
||||
theme.propertyStateDefault("hovering", "fg-color", 0xFF845eee);
|
||||
}
|
||||
|
||||
Identifier sprite;
|
||||
private Runnable onClick;
|
||||
static int padding = 14;
|
||||
|
||||
public TextureButton(Identifier sprite, Runnable onClick) {
|
||||
this.sprite = sprite;
|
||||
@@ -31,20 +20,16 @@ public class TextureButton extends Element {
|
||||
|
||||
@Override
|
||||
public void render(RenderScope renderScope, ElementContext ctx) {
|
||||
if (ctx.isHovering()) {
|
||||
theme.setState("hovering");
|
||||
} else {
|
||||
theme.setState(null);
|
||||
}
|
||||
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, cornerRadius.value, bgColor.value);
|
||||
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
|
||||
ctx.isHovering() ? Theme.ACCENT.value : Theme.PRIMARY.value);
|
||||
|
||||
renderScope.setRenderLayer(RenderLayer::getGuiTextured);
|
||||
|
||||
int texWidth = width - padding.value;
|
||||
int texHeight = height - padding.value;
|
||||
int texWidth = width - padding;
|
||||
int texHeight = height - padding;
|
||||
|
||||
renderScope.drawTexture(sprite, padding.value / 2, padding.value / 2, 0, 0, texWidth, texHeight, fgColor.value);
|
||||
renderScope.drawTexture(sprite, padding / 2, padding / 2, 0, 0, texWidth, texHeight,
|
||||
ctx.isHovering() ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package org.saturnclient.ui2.resources;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.kitfox.svg.SVGDiagram;
|
||||
import com.kitfox.svg.SVGUniverse;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.saturnclient.saturnclient.cosmetics.cloaks.utils.IdentifierUtils;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class SvgTexture {
|
||||
|
||||
private static List<Identifier> svgCache = new ArrayList<>();
|
||||
|
||||
private static BufferedImage renderSvg(InputStream svgStream, int width, int height) throws Exception {
|
||||
// Load SVG
|
||||
SVGUniverse universe = new SVGUniverse();
|
||||
java.net.URI uri = universe.loadSVG(svgStream, "tempSvg");
|
||||
SVGDiagram diagram = universe.getDiagram(uri);
|
||||
|
||||
// Get intrinsic size of SVG
|
||||
float svgWidth = (float) diagram.getWidth();
|
||||
float svgHeight = (float) diagram.getHeight();
|
||||
|
||||
// Compute scale factors
|
||||
float scaleX = width / svgWidth;
|
||||
float scaleY = height / svgHeight;
|
||||
|
||||
// Create output image
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g = image.createGraphics();
|
||||
|
||||
// Enable anti-aliasing for smooth scaling
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||
|
||||
// Apply scaling transform
|
||||
g.scale(scaleX, scaleY);
|
||||
|
||||
// Render SVG
|
||||
diagram.render(g);
|
||||
|
||||
g.dispose();
|
||||
return image;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Identifier getSvg(MinecraftClient client, Identifier svgImage, int width, int height) {
|
||||
Identifier id = Identifier
|
||||
.of(svgImage.toString().replaceAll("\\.svg$", (width + "_" + height).toString() + ".png"));
|
||||
|
||||
if (svgCache.contains(id)) {
|
||||
return id;
|
||||
}
|
||||
|
||||
try (InputStream svgStream = client
|
||||
.getResourceManager()
|
||||
.getResource(svgImage).get()
|
||||
.getInputStream()) {
|
||||
|
||||
BufferedImage image = renderSvg(svgStream, width, height);
|
||||
|
||||
IdentifierUtils.registerBufferedImageTexture(id, image);
|
||||
|
||||
return id;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ public class Textures {
|
||||
public static final Identifier REALISTIC_LOGO = Identifier.of("saturnclient", "textures/logo/realistic.png");
|
||||
public static final Identifier LOGO_TEXT = Identifier.of("saturnclient", "textures/logo/text.png");
|
||||
public static final Identifier LOGO_TEXT_BIG = Identifier.of("saturnclient", "textures/logo/text_big.png");
|
||||
public static final Identifier SPLASH = Identifier.of("saturnclient", "textures/gui/splash.png");
|
||||
|
||||
// Widgets
|
||||
public static final Identifier BUTTON_BORDER = Identifier.of("saturnclient", "button_border");
|
||||
@@ -52,7 +53,7 @@ public class Textures {
|
||||
}
|
||||
|
||||
public static Identifier getModIcon(String modId) {
|
||||
return Identifier.of("saturnclient", "textures/gui/mod/" + modId + ".png");
|
||||
return Identifier.of("saturnclient", "textures/gui/mod/" + modId + ".svg");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.util.Map;
|
||||
|
||||
import org.saturnclient.saturnclient.config.ConfigManager;
|
||||
import org.saturnclient.saturnclient.config.Property;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.ui2.SaturnScreen;
|
||||
import org.saturnclient.ui2.components.Sidebar;
|
||||
import org.saturnclient.ui2.components.inputs.FloatInput;
|
||||
@@ -60,13 +61,12 @@ public class ConfigEditor extends SaturnScreen {
|
||||
if (prop.getType() == Property.PropertyType.NAMESPACE) {
|
||||
configScroll.draw(
|
||||
new Text(
|
||||
propName
|
||||
).position(Fonts.centerX(520, propName, Text.font.value), modY + 1).scale(0.7f));
|
||||
propName).position(Fonts.centerX(520, propName, Theme.FONT.value), modY + 1)
|
||||
.scale(0.7f));
|
||||
} else {
|
||||
configScroll.draw(
|
||||
new Text(
|
||||
propName
|
||||
).position(modX, modY + 1).scale(0.7f));
|
||||
propName).position(modX, modY + 1).scale(0.7f));
|
||||
}
|
||||
|
||||
switch (prop.getType()) {
|
||||
@@ -78,7 +78,7 @@ public class ConfigEditor extends SaturnScreen {
|
||||
break;
|
||||
|
||||
case HEX:
|
||||
configScroll.draw(new HexInput((Property<Integer>) prop, this).position(w / 2, modY));
|
||||
configScroll.draw(new HexInput((Property<Integer>) prop).position(w / 2, modY));
|
||||
break;
|
||||
|
||||
case INTEGER:
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package org.saturnclient.ui2.screens;
|
||||
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.saturnclient.SaturnClientConfig;
|
||||
import org.saturnclient.saturnclient.auth.Auth;
|
||||
// import org.saturnclient.saturnclient.auth.Auth;
|
||||
import org.saturnclient.saturnclient.client.ServiceClient;
|
||||
import org.saturnclient.saturnclient.config.SaturnClientConfig;
|
||||
import org.saturnclient.saturnclient.cosmetics.Emotes;
|
||||
import org.saturnclient.ui2.SaturnScreen;
|
||||
import org.saturnclient.ui2.Utils;
|
||||
@@ -71,7 +70,7 @@ public class EmoteWheel extends SaturnScreen {
|
||||
animationStack.addAnimLayer(1000,
|
||||
PlayerAnimationRegistry.getAnimation(Identifier.of("saturnclient",
|
||||
emote)).playAnimation());
|
||||
Auth.emote(emote);
|
||||
ServiceClient.emote(emote);
|
||||
close();
|
||||
}).dimensions(70, 70).centerOffset(width, height, -80 + (col * 80), -35 + (row * 80))
|
||||
.animation(new Fade(700)));
|
||||
|
||||
@@ -3,8 +3,10 @@ package org.saturnclient.ui2.screens;
|
||||
import org.saturnclient.modules.ModManager;
|
||||
import org.saturnclient.modules.Module;
|
||||
import org.saturnclient.ui2.SaturnScreen;
|
||||
import org.saturnclient.ui2.anim.SlideFade;
|
||||
import org.saturnclient.ui2.components.SaturnModule;
|
||||
import org.saturnclient.ui2.components.Sidebar;
|
||||
import org.saturnclient.ui2.elements.AnimationStagger;
|
||||
import org.saturnclient.ui2.elements.ImageTexture;
|
||||
import org.saturnclient.ui2.elements.Scroll;
|
||||
import org.saturnclient.ui2.resources.Textures;
|
||||
@@ -24,8 +26,11 @@ public class ModMenu extends SaturnScreen {
|
||||
int row = 0;
|
||||
int col = 0;
|
||||
|
||||
AnimationStagger moduleStagger = new AnimationStagger(80);
|
||||
|
||||
for (Module mod : ModManager.ALL_MODS) {
|
||||
scroll.draw(new SaturnModule(mod).position((160 + g) * col, (50 + g) * row));
|
||||
moduleStagger.draw(
|
||||
new SaturnModule(mod).position((160 + g) * col, (50 + g) * row).animation(new SlideFade(600, -12)));
|
||||
|
||||
if (col == 2) {
|
||||
col = 0;
|
||||
@@ -35,6 +40,8 @@ public class ModMenu extends SaturnScreen {
|
||||
}
|
||||
}
|
||||
|
||||
scroll.draw(moduleStagger);
|
||||
|
||||
int scrollWidth = 480 + 10 + (g * 2) + (p * 2);
|
||||
|
||||
draw(scroll.dimensions(scrollWidth, 350).center(width, height));
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package org.saturnclient.ui2.screens;
|
||||
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.saturnclient.SaturnClientConfig;
|
||||
import org.saturnclient.saturnclient.config.SaturnClientConfig;
|
||||
import org.saturnclient.saturnclient.menus.HudEditor;
|
||||
import org.saturnclient.ui2.resources.Textures;
|
||||
import org.saturnclient.ui2.screens.cosmetics.CloakMenu;
|
||||
import org.saturnclient.ui2.Element;
|
||||
import org.saturnclient.ui2.SaturnScreen;
|
||||
import org.saturnclient.ui2.elements.AnimationStagger;
|
||||
import org.saturnclient.ui2.elements.Button;
|
||||
import org.saturnclient.ui2.elements.ImageTexture;
|
||||
import org.saturnclient.ui2.elements.TextureButton;
|
||||
@@ -20,21 +20,42 @@ public class ShiftMenu extends SaturnScreen {
|
||||
|
||||
@Override
|
||||
public void ui() {
|
||||
draw(new ImageTexture(Textures.LOGO_TEXT).dimensions(98, 10).centerOffset(width, height, 0, -36).animation(new Fade(700)));
|
||||
draw(new ImageTexture(SaturnClientConfig.getLogo()).dimensions(98, 98).centerOffset(width, height, 0, -80).animation(new SlideY(700, -20)));
|
||||
// Logo animations (unchanged)
|
||||
draw(new ImageTexture(Textures.LOGO_TEXT)
|
||||
.dimensions(98, 10)
|
||||
.centerOffset(width, height, 0, -36)
|
||||
.animation(new Fade(700)));
|
||||
|
||||
Element button = new Button("Settings", () -> {
|
||||
SaturnClient.client.setScreen(new ModMenu());
|
||||
}).center(width, height);
|
||||
draw(new ImageTexture(SaturnClientConfig.getLogo())
|
||||
.dimensions(98, 98)
|
||||
.centerOffset(width, height, 0, -80)
|
||||
.animation(new SlideY(700, -20)));
|
||||
|
||||
draw(button);
|
||||
// Spacing and sizes
|
||||
int btnWidth = 120;
|
||||
int btnHeight = 40;
|
||||
int spacing = 4;
|
||||
|
||||
draw(new TextureButton(Textures.COSMETICS, () -> {
|
||||
client.setScreen(new CloakMenu());
|
||||
}).dimensions(button.height, button.height).centerOffset(width, height, (button.width / 2) + (button.height / 2) + 4, 0));
|
||||
// Create stagger
|
||||
AnimationStagger stagger = new AnimationStagger(120);
|
||||
|
||||
draw(new TextureButton(Textures.HUD_ICON, () -> {
|
||||
stagger.draw(new TextureButton(Textures.HUD_ICON, () -> {
|
||||
client.setScreen(new HudEditor());
|
||||
}).dimensions(button.height, button.height).centerOffset(width, height, -((button.width / 2) + (button.height / 2) + 4), 0));
|
||||
}).dimensions(btnHeight, btnHeight).position(0, 0)
|
||||
.animation(new Fade(300)));
|
||||
|
||||
stagger.draw(new Button("Settings", () -> {
|
||||
SaturnClient.client.setScreen(new ModMenu());
|
||||
}).dimensions(btnWidth, btnHeight).position(btnHeight + spacing, 0)
|
||||
.animation(new Fade(300)));
|
||||
|
||||
stagger.draw(new TextureButton(Textures.COSMETICS, () -> {
|
||||
client.setScreen(new CloakMenu());
|
||||
}).dimensions(btnHeight, btnHeight).position(btnWidth + btnHeight + (spacing * 2), 0)
|
||||
.animation(new Fade(300)));
|
||||
|
||||
// Draw the stagger at the absolute center of the screen
|
||||
draw(stagger.dimensions(btnWidth + (btnHeight * 2) + (spacing * 2), btnHeight).centerOffset(width, height, 0,
|
||||
0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package org.saturnclient.ui2.screens;
|
||||
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.saturnclient.SaturnClientConfig;
|
||||
import org.saturnclient.saturnclient.config.SaturnClientConfig;
|
||||
import org.saturnclient.saturnclient.menus.HudEditor;
|
||||
import org.saturnclient.ui2.resources.Textures;
|
||||
import org.saturnclient.ui2.screens.cosmetics.CloakMenu;
|
||||
import org.saturnclient.ui2.SaturnScreen;
|
||||
import org.saturnclient.ui2.anim.Fade;
|
||||
import org.saturnclient.ui2.anim.SlideFade;
|
||||
import org.saturnclient.ui2.anim.SlideY;
|
||||
import org.saturnclient.ui2.elements.AnimationStagger;
|
||||
import org.saturnclient.ui2.elements.Button;
|
||||
import org.saturnclient.ui2.elements.ImageTexture;
|
||||
import org.saturnclient.ui2.elements.TextureButton;
|
||||
@@ -19,44 +21,54 @@ import net.minecraft.client.gui.screen.world.SelectWorldScreen;
|
||||
|
||||
public class TitleMenu extends SaturnScreen {
|
||||
public TitleMenu() {
|
||||
super("Tile Menu");
|
||||
super("Title Menu");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ui() {
|
||||
backgroundBlur = 0;
|
||||
|
||||
draw(new ImageTexture(Textures.LOGO_TEXT).dimensions(98, 10).centerOffset(width, height, 0, -36).animation(new Fade(700)));
|
||||
draw(new ImageTexture(SaturnClientConfig.getLogo()).dimensions(98, 98).centerOffset(width, height, 0, -82).animation(new SlideY(700, -20)));
|
||||
draw(new ImageTexture(Textures.LOGO_TEXT).dimensions(98, 10).centerOffset(width, height, 0, -36)
|
||||
.animation(new Fade(700)));
|
||||
draw(new ImageTexture(SaturnClientConfig.getLogo()).dimensions(98, 98).centerOffset(width, height, 0, -82)
|
||||
.animation(new SlideY(700, -20)));
|
||||
|
||||
draw(new Button("SINGLEPLAYER", () -> {
|
||||
AnimationStagger mainButtonStagger = new AnimationStagger(120);
|
||||
|
||||
mainButtonStagger.draw(new Button("SINGLEPLAYER", () -> {
|
||||
client.setScreen(new SelectWorldScreen(new TitleMenu()));
|
||||
}).scale(0.69f).dimensions(420, 52).centerOffset(width, height, 0, 0));
|
||||
}).scale(0.69f).dimensions(420, 52).position(0, 0).animation(new SlideFade(1000, -10)));
|
||||
|
||||
draw(new Button("MULTIPLAYER", () -> {
|
||||
mainButtonStagger.draw(new Button("MULTIPLAYER", () -> {
|
||||
client.setScreen(new MultiplayerScreen(new TitleMenu()));
|
||||
}).scale(0.69f).dimensions(420, 52).centerOffset(width, height, 0, 40));
|
||||
}).scale(0.69f).dimensions(420, 52).position(0, 40).animation(new SlideFade(1000, -10)));
|
||||
|
||||
draw(new Button("OPTIONS", () -> {
|
||||
mainButtonStagger.draw(new Button("OPTIONS", () -> {
|
||||
client.setScreen(new OptionsScreen(new TitleMenu(), SaturnClient.client.options));
|
||||
}).scale(0.69f).dimensions(206, 52).centerOffset(width, height, -73, 80));
|
||||
}).scale(0.69f).dimensions(206, 52).position(0, 80).animation(new SlideFade(1000, -10)));
|
||||
|
||||
draw(new Button("QUIT", () -> {
|
||||
mainButtonStagger.draw(new Button("QUIT", () -> {
|
||||
client.scheduleStop();
|
||||
}).scale(0.69f).dimensions(206, 52).centerOffset(width, height, 73, 80));
|
||||
}).scale(0.69f).dimensions(206, 52).position(146, 80).animation(new SlideFade(1000, -10)));
|
||||
|
||||
draw(mainButtonStagger.dimensions(289, 143).centerOffset(width, height, 0, 60));
|
||||
|
||||
int s = Fonts.getHeight() + 20;
|
||||
|
||||
draw(new TextureButton(Textures.HUD_ICON, () -> {
|
||||
AnimationStagger saturnStagger = new AnimationStagger(180);
|
||||
|
||||
saturnStagger.draw(new TextureButton(Textures.HUD_ICON, () -> {
|
||||
client.setScreen(new HudEditor());
|
||||
}).dimensions(s, s).centerHorizontal(width, height, -(s + 2), 8).animation(new SlideY(700, s + 8)));
|
||||
}).dimensions(s, s).position(0, 0).animation(new SlideFade(1000, s + 8)));
|
||||
|
||||
draw(new TextureButton(Textures.SETTINGS, () -> {
|
||||
saturnStagger.draw(new TextureButton(Textures.SETTINGS, () -> {
|
||||
SaturnClient.client.setScreen(new ModMenu());
|
||||
}).dimensions(s, s).centerHorizontal(width, height, 0, 8).animation(new SlideY(700, s + 8)));
|
||||
}).dimensions(s, s).position(s + 2, 0).animation(new SlideFade(1000, s + 8)));
|
||||
|
||||
draw(new TextureButton(Textures.CLOAK, () -> {
|
||||
saturnStagger.draw(new TextureButton(Textures.CLOAK, () -> {
|
||||
client.setScreen(new CloakMenu());
|
||||
}).dimensions(s, s).centerHorizontal(width, height, s + 2, 8).animation(new SlideY(700, s + 8)));
|
||||
}).dimensions(s, s).position((s + 2) * 2, 0).animation(new SlideFade(1000, s + 8)));
|
||||
|
||||
draw(saturnStagger.dimensions(((s + 2) * 2) + s, s).centerHorizontal(width, height, 0, 8));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package org.saturnclient.ui2.screens.cosmetics;
|
||||
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.saturnclient.auth.Auth;
|
||||
import org.saturnclient.saturnclient.auth.SaturnPlayer;
|
||||
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
|
||||
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
|
||||
import org.saturnclient.ui2.resources.Textures;
|
||||
import org.saturnclient.ui2.SaturnScreen;
|
||||
@@ -28,7 +27,7 @@ public class CloakMenu extends SaturnScreen {
|
||||
int row = 0;
|
||||
int col = 0;
|
||||
|
||||
SaturnPlayer player = Auth.players.get(Auth.uuid);
|
||||
SaturnPlayer player = SaturnPlayer.get();
|
||||
|
||||
if (player != null) {
|
||||
for (String cloak : Cloaks.availableCloaks) {
|
||||
@@ -54,10 +53,10 @@ public class CloakMenu extends SaturnScreen {
|
||||
draw(new Sidebar(2, this::close).centerOffset(width, height, -(scrollWidth / 2 + 20), 0));
|
||||
|
||||
draw(new TabMenu(0,
|
||||
new TabMenuComponent(Textures.CLOAK, () -> {}),
|
||||
new TabMenuComponent(Textures.CLOAK, () -> {
|
||||
}),
|
||||
new TabMenuComponent(Textures.HAT, () -> {
|
||||
SaturnClient.client.setScreen(new HatMenu());
|
||||
})
|
||||
).centerOffset(width, height, 0, -195));
|
||||
})).centerOffset(width, height, 0, -195));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package org.saturnclient.ui2.screens.cosmetics;
|
||||
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.saturnclient.auth.Auth;
|
||||
import org.saturnclient.saturnclient.auth.SaturnPlayer;
|
||||
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
|
||||
import org.saturnclient.saturnclient.cosmetics.Hats;
|
||||
import org.saturnclient.ui2.resources.Textures;
|
||||
import org.saturnclient.ui2.SaturnScreen;
|
||||
@@ -28,12 +27,12 @@ public class HatMenu extends SaturnScreen {
|
||||
int row = 0;
|
||||
int col = 0;
|
||||
|
||||
SaturnPlayer player = Auth.players.get(Auth.uuid);
|
||||
SaturnPlayer player = SaturnPlayer.get();
|
||||
|
||||
if (player != null) {
|
||||
for (String hat : Hats.availableHats) {
|
||||
scroll.draw(new CosmeticPreview(hat == player.hat, Textures.getHatPreview(hat), () -> {
|
||||
Hats.setHat(Auth.uuid, hat);
|
||||
Hats.setHat(hat);
|
||||
}).dimensions(50, 50).position((50 + g) * col, (50 + g) * row));
|
||||
|
||||
if (col == 5) {
|
||||
@@ -57,7 +56,7 @@ public class HatMenu extends SaturnScreen {
|
||||
new TabMenuComponent(Textures.CLOAK, () -> {
|
||||
SaturnClient.client.setScreen(new CloakMenu());
|
||||
}),
|
||||
new TabMenuComponent(Textures.HAT, () -> {})
|
||||
).centerOffset(width, height, 0, -195));
|
||||
new TabMenuComponent(Textures.HAT, () -> {
|
||||
})).centerOffset(width, height, 0, -195));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package org.saturnclient.ui2.screens.store;
|
||||
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.saturnclient.auth.Auth;
|
||||
import org.saturnclient.saturnclient.auth.SaturnPlayer;
|
||||
import org.saturnclient.saturnclient.client.ServiceClient;
|
||||
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.saturnclient.cosmetics.cloaks.Cloaks;
|
||||
import org.saturnclient.ui2.SaturnScreen;
|
||||
import org.saturnclient.ui2.Utils;
|
||||
@@ -42,7 +43,7 @@ public class CloakStore extends SaturnScreen {
|
||||
int row = 0;
|
||||
int col = 0;
|
||||
|
||||
SaturnPlayer player = Auth.players.get(Auth.uuid);
|
||||
SaturnPlayer player = SaturnPlayer.get();
|
||||
|
||||
if (player != null) {
|
||||
for (String cloak : Cloaks.ALL_CLOAKS) {
|
||||
@@ -54,7 +55,7 @@ public class CloakStore extends SaturnScreen {
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - lastPurchaseTime >= 3000) {
|
||||
Utils.notify(NotificationKind.Info, "Purchase processing", "Please wait");
|
||||
Auth.buyCloak(cloak);
|
||||
ServiceClient.buyCloak(cloak);
|
||||
lastPurchaseTime = now;
|
||||
new Thread(() -> {
|
||||
try {
|
||||
@@ -75,10 +76,10 @@ public class CloakStore extends SaturnScreen {
|
||||
|
||||
String t = "100";
|
||||
|
||||
scroll.draw(new Text(t).position(x + Fonts.centerX(50, t, Text.font.value), y + 113).scale(0.5f));
|
||||
scroll.draw(new Text(t).position(x + Fonts.centerX(50, t, Theme.FONT.value), y + 113).scale(0.5f));
|
||||
|
||||
scroll.draw(new ImageTexture(Textures.COINS).dimensions(16, 16)
|
||||
.position(x + Fonts.getWidth(t, Text.font.value) - 1, y + 112).scale(0.5f));
|
||||
.position(x + Fonts.getWidth(t, Theme.FONT.value) - 1, y + 112).scale(0.5f));
|
||||
|
||||
if (col == 8) {
|
||||
col = 0;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package org.saturnclient.ui2.screens.store;
|
||||
|
||||
import org.saturnclient.saturnclient.SaturnClient;
|
||||
import org.saturnclient.saturnclient.auth.Auth;
|
||||
import org.saturnclient.saturnclient.auth.SaturnPlayer;
|
||||
import org.saturnclient.saturnclient.client.ServiceClient;
|
||||
import org.saturnclient.saturnclient.client.player.SaturnPlayer;
|
||||
import org.saturnclient.saturnclient.config.Theme;
|
||||
import org.saturnclient.saturnclient.cosmetics.Hats;
|
||||
import org.saturnclient.ui2.SaturnScreen;
|
||||
import org.saturnclient.ui2.Utils;
|
||||
@@ -42,7 +43,7 @@ public class HatStore extends SaturnScreen {
|
||||
int row = 0;
|
||||
int col = 0;
|
||||
|
||||
SaturnPlayer player = Auth.players.get(Auth.uuid);
|
||||
SaturnPlayer player = SaturnPlayer.get();
|
||||
|
||||
if (player != null) {
|
||||
for (String hat : Hats.ALL_HATS) {
|
||||
@@ -54,7 +55,7 @@ public class HatStore extends SaturnScreen {
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - lastPurchaseTime >= 3000) {
|
||||
Utils.notify(NotificationKind.Info, "Purchase processing", "Please wait");
|
||||
Auth.buyHat(hat);
|
||||
ServiceClient.buyHat(hat);
|
||||
lastPurchaseTime = now;
|
||||
new Thread(() -> {
|
||||
try {
|
||||
@@ -75,10 +76,10 @@ public class HatStore extends SaturnScreen {
|
||||
|
||||
String t = "50";
|
||||
|
||||
scroll.draw(new Text(t).position(x + Fonts.centerX(50, t, Text.font.value), y + 53).scale(0.5f));
|
||||
scroll.draw(new Text(t).position(x + Fonts.centerX(50, t, Theme.FONT.value), y + 53).scale(0.5f));
|
||||
|
||||
scroll.draw(new ImageTexture(Textures.COINS).dimensions(16, 16)
|
||||
.position(x + Fonts.getWidth(t, Text.font.value) + 4, y + 52).scale(0.5f));
|
||||
.position(x + Fonts.getWidth(t, Theme.FONT.value) + 4, y + 52).scale(0.5f));
|
||||
|
||||
if (col == 8) {
|
||||
col = 0;
|
||||
|
||||
|
Before Width: | Height: | Size: 675 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-shield-icon lucide-shield"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/></svg>
|
||||
|
After Width: | Height: | Size: 404 B |
|
Before Width: | Height: | Size: 526 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-move3d-icon lucide-move-3d"><path d="M5 3v16h16"/><path d="m5 19 6-6"/><path d="m2 6 3-3 3 3"/><path d="m18 16 3 3-3 3"/></svg>
|
||||
|
After Width: | Height: | Size: 324 B |
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-plus-icon lucide-plus"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
|
||||
|
After Width: | Height: | Size: 266 B |
|
Before Width: | Height: | Size: 294 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-calendar-days-icon lucide-calendar-days"><path d="M8 2v4"/><path d="M16 2v4"/><rect width="18" height="18" x="3" y="4" rx="2"/><path d="M3 10h18"/><path d="M8 14h.01"/><path d="M12 14h.01"/><path d="M16 14h.01"/><path d="M8 18h.01"/><path d="M12 18h.01"/><path d="M16 18h.01"/></svg>
|
||||
|
After Width: | Height: | Size: 480 B |
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-sparkles-icon lucide-sparkles"><path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"/><path d="M20 2v4"/><path d="M22 4h-4"/><circle cx="4" cy="20" r="2"/></svg>
|
||||
|
After Width: | Height: | Size: 577 B |
|
Before Width: | Height: | Size: 478 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chart-area-icon lucide-chart-area"><path d="M3 3v16a2 2 0 0 0 2 2h16"/><path d="M7 11.207a.5.5 0 0 1 .146-.353l2-2a.5.5 0 0 1 .708 0l3.292 3.292a.5.5 0 0 0 .708 0l4.292-4.292a.5.5 0 0 1 .854.353V16a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1z"/></svg>
|
||||
|
After Width: | Height: | Size: 434 B |
|
Before Width: | Height: | Size: 959 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-switch-camera-icon lucide-switch-camera"><path d="M11 19H4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h5"/><path d="M13 5h7a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-5"/><circle cx="12" cy="12" r="3"/><path d="m18 22-3-3 3-3"/><path d="m6 2 3 3-3 3"/></svg>
|
||||
|
After Width: | Height: | Size: 430 B |
|
Before Width: | Height: | Size: 697 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-sun-icon lucide-sun"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg>
|
||||
|
After Width: | Height: | Size: 465 B |
|
Before Width: | Height: | Size: 962 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-heart-icon lucide-heart"><path d="M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5"/></svg>
|
||||
|
After Width: | Height: | Size: 389 B |
|
Before Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-keyboard-icon lucide-keyboard"><path d="M10 8h.01"/><path d="M12 12h.01"/><path d="M14 8h.01"/><path d="M16 12h.01"/><path d="M18 8h.01"/><path d="M6 8h.01"/><path d="M7 16h10"/><path d="M8 12h.01"/><rect width="20" height="16" x="2" y="4" rx="2"/></svg>
|
||||
|
After Width: | Height: | Size: 451 B |
|
Before Width: | Height: | Size: 282 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-cloud-fog-icon lucide-cloud-fog"><path d="M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242"/><path d="M16 17H7"/><path d="M17 21H9"/></svg>
|
||||
|
After Width: | Height: | Size: 344 B |
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-wand-sparkles-icon lucide-wand-sparkles"><path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72"/><path d="m14 7 3 3"/><path d="M5 6v4"/><path d="M19 14v4"/><path d="M10 2v2"/><path d="M7 8H3"/><path d="M21 16h-4"/><path d="M11 3H9"/></svg>
|
||||
|
After Width: | Height: | Size: 533 B |
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-gauge-icon lucide-gauge"><path d="m12 14 4-4"/><path d="M3.34 19a10 10 0 1 1 17.32 0"/></svg>
|
||||
|
After Width: | Height: | Size: 290 B |
|
Before Width: | Height: | Size: 961 B |
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="1167.000000pt" height="1280.000000pt" viewBox="0 0 1167.000000 1280.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.15, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,1280.000000) scale(0.100000,-0.100000)"
|
||||
fill="white" stroke="none">
|
||||
<path d="M9270 12789 c-229 -29 -473 -124 -657 -256 -272 -194 -468 -494 -539
|
||||
-825 -25 -114 -25 -369 -1 -491 94 -472 445 -861 918 -1021 167 -56 260 -70
|
||||
459 -70 199 0 292 14 459 70 400 135 727 449 864 829 50 141 70 257 71 425 1
|
||||
176 -11 259 -59 406 -165 505 -614 864 -1166 934 -96 12 -254 11 -349 -1z"/>
|
||||
<path d="M5145 10889 c-80 -12 -193 -69 -252 -127 -42 -41 -1667 -2180 -1780
|
||||
-2342 -24 -35 -54 -97 -69 -143 -23 -69 -27 -100 -27 -197 -1 -103 2 -124 29
|
||||
-200 70 -200 212 -329 394 -362 128 -22 284 28 378 123 27 27 397 512 823
|
||||
1078 l774 1030 443 0 443 1 -19 -27 c-11 -16 -769 -1026 -1684 -2246 l-1664
|
||||
-2219 -1215 -1 -1214 -2 -68 -27 c-93 -38 -164 -87 -239 -166 -323 -343 -232
|
||||
-974 171 -1186 134 -70 29 -66 1631 -66 1549 0 1541 0 1705 51 124 38 264 118
|
||||
331 190 18 19 321 405 674 859 354 454 648 831 655 838 10 11 162 -154 768
|
||||
-832 417 -465 757 -852 757 -860 0 -9 -140 -728 -311 -1598 -223 -1137 -312
|
||||
-1611 -316 -1681 -21 -363 196 -688 508 -762 302 -71 612 129 718 465 11 36
|
||||
180 886 376 1890 195 1004 376 1926 401 2049 40 196 45 239 46 355 0 197 -40
|
||||
357 -124 486 -27 42 -731 859 -1325 1537 l-55 62 728 968 c400 532 728 969
|
||||
729 971 1 1 211 -240 467 -535 256 -296 489 -560 517 -586 63 -59 178 -115
|
||||
260 -127 144 -20 301 37 430 156 98 91 1610 1844 1649 1912 57 100 77 181 76
|
||||
317 0 87 -5 137 -18 180 -49 163 -154 293 -292 361 -77 39 -79 39 -193 39
|
||||
-104 -1 -122 -3 -174 -27 -32 -15 -77 -41 -100 -58 -24 -17 -328 -358 -677
|
||||
-760 -349 -401 -638 -728 -641 -727 -4 1 -354 402 -780 891 -440 506 -796 907
|
||||
-827 931 -30 24 -80 55 -111 71 -127 62 -38 58 -1401 60 -685 1 -1272 -2
|
||||
-1305 -7z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |