Removed common to replace it with a git submodule

This commit is contained in:
2026-03-07 21:22:32 +01:00
parent 04a4a1b226
commit ea6d2958e7
226 changed files with 0 additions and 18591 deletions
-35
View File
@@ -1,35 +0,0 @@
# gradle
.gradle/
build/
out/
classes/
# eclipse
*.launch
# idea
.idea/
*.iml
*.ipr
*.iws
# vscode
.settings/
.vscode/
bin/
.classpath
.project
# macos
*.DS_Store
# fabric
run/
logs/
-201
View File
@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-2
View File
@@ -1,2 +0,0 @@
# common
The cross-version code powering Saturn Client
-7
View File
@@ -1,7 +0,0 @@
plugins {
id "java-library"
}
java {
toolchain.languageVersion = JavaLanguageVersion.of(21)
}
@@ -1,198 +0,0 @@
package org.saturnclient.client;
import java.util.UUID;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.saturnclient.client.player.SaturnPlayer;
import org.saturnclient.common.minecraft.bindings.SaturnClientBindings;
import dev.selimaj.session.Session;
public class ServiceClient {
private static Session session;
public static UUID uuid;
public static final List<String> availableCloaks = new ArrayList<>();
public static final List<String> availableHats = new ArrayList<>();
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() {
SaturnClientBindings.platform().onClientStopping(() -> {
if (session != null) {
try {
session.close();
} catch (Exception e) {
SaturnClientBindings.platform().logError("Unable to close Saturn session", e);
}
}
});
try {
String accessToken = SaturnClientBindings.platform().getAccessToken();
uuid = SaturnClientBindings.platform().getUuid();
String username = SaturnClientBindings.platform().getUsername();
if (accessToken == null || uuid == null || username == null) {
SaturnClientBindings.platform().logError("No active Minecraft session found");
return false;
}
SaturnClientBindings.platform().logInfo("Authenticating with UUID: " + uuid);
if (!connectTimeout()) {
SaturnClientBindings.platform().logError("Unable to authenticate: Session Server Timeout");
return false;
}
ServiceMethods.Types.Player response = session.request(ServiceMethods.Authenticate, accessToken)
.get();
for (String availableCloak : response.cloaks()) {
availableCloaks.add(availableCloak);
}
for (String availableHat : response.hats()) {
availableHats.add(availableHat);
}
eventHandlers();
SaturnPlayer.set(new SaturnPlayer(uuid, username, response.cloak(), response.hat()));
return true;
} catch (Exception e) {
SaturnClientBindings.platform().logError("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) {
SaturnClientBindings.platform().logError("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) {
SaturnClientBindings.platform().logError("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 {
availableCloaks.add(itemId);
}
});
} catch (Exception e) {
SaturnClientBindings.platform().logError("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 {
availableHats.add(itemId);
}
});
} catch (Exception e) {
SaturnClientBindings.platform().logError("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) {
SaturnClientBindings.platform().logError("Failed to emote (service): ", e);
}
}
public static void eventHandlers() {
session.onNotification(ServiceMethods.EmoteEvent, (data) -> {
if (data == null) {
return;
}
UUID from;
try {
from = UUID.fromString(data.from());
} catch (Exception e) {
return;
}
if (data.emote() != null && !data.emote().isEmpty()) {
SaturnClientBindings.emotes().setEmote(from, data.emote());
} else {
SaturnClientBindings.emotes().setEmote(from, null);
}
});
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) {
SaturnClientBindings.platform().logError("Failed to get player", e);
}
}
}
@@ -1,113 +0,0 @@
package org.saturnclient.client;
import java.util.UUID;
import org.saturnclient.client.ServiceMethods.Types.SendPlayerRequest;
import org.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,59 +0,0 @@
package org.saturnclient.client.player;
import java.util.Map;
import java.util.UUID;
import org.saturnclient.config.Config;
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,
UUID.fromString("d8dc7023-179b-4460-bcf6-493fbf1289a6"),
Role.CONTRIBUTOR);
/*
* 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 int getIconColor(UUID uuid) {
Role role = ROLES.get(uuid);
if (role == null) {
return 0xFFFFFF;
}
switch (role) {
case OWNER:
return 0xAA0000;
case ADMIN:
return 0xFF5555;
case PARTNER:
return 0xFFAA00;
case CONTRIBUTOR:
return 0x55FFFF;
default:
return 0xFFFFFF;
}
}
public static String getSaturnIndicator() {
return Config.realisticLogo.value ? "\uE003" : "\uE002";
}
}
@@ -1,55 +0,0 @@
package org.saturnclient.client.player;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.saturnclient.common.minecraft.bindings.SaturnClientBindings;
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;
}
public static SaturnPlayer get() {
UUID self = SaturnClientBindings.platform().getUuid();
if (self == null) {
return null;
}
return PLAYERS.get(self);
}
public static SaturnPlayer get(UUID uuid) {
return PLAYERS.get(uuid);
}
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() {
UUID self = SaturnClientBindings.platform().getUuid();
return PLAYERS.keySet().stream().filter(id -> self == null || !id.equals(self)).map(UUID::toString)
.toArray(String[]::new);
}
}
@@ -1,16 +0,0 @@
package org.saturnclient.common.minecraft;
import java.io.File;
import java.io.InputStream;
import org.saturnclient.common.minecraft.bindings.SaturnIdentifier;
import org.saturnclient.common.minecraft.render.IWindow;
public interface IMinecraftClient {
public File getRunDirectory();
public InputStream getResource(SaturnIdentifier identifier);
public IWindow getWindow();
}
@@ -1,18 +0,0 @@
package org.saturnclient.common.minecraft;
public class MinecraftBinding {
public Object inner;
public MinecraftBinding(Object binding) {
this.inner = binding;
}
@SuppressWarnings("unchecked")
public <T> T get() {
try {
return (T) inner;
} catch (ClassCastException e) {
return null;
}
}
}
@@ -1,34 +0,0 @@
package org.saturnclient.common.minecraft;
import org.saturnclient.common.minecraft.bindings.SaturnIdentifier;
import org.saturnclient.ui.SaturnScreen;
import java.awt.image.BufferedImage;
public abstract class MinecraftProvider {
public static MinecraftProvider PROVIDER;
public static enum MinecraftScreen {
SelectWorld,
Multiplayer,
Options
}
public abstract IMinecraftClient getClient();
public abstract void setScreen(SaturnScreen screen);
public abstract void setScreen(MinecraftScreen screen);
public abstract Object createIdentifier(String namespace, String path);
public abstract boolean isKeyPressed(int key);
public abstract int getWidth(String text, int font);
public abstract void registerBufferedImageTexture(SaturnIdentifier i, BufferedImage bi);
public abstract String getKeyName(int key);
public abstract void stop();
}
@@ -1,31 +0,0 @@
package org.saturnclient.common.minecraft.bindings;
public final class SaturnClientBindings {
private static SaturnPlatformBindings platform;
private static SaturnEmoteBindings emotes;
private SaturnClientBindings() {
}
public static void setPlatform(SaturnPlatformBindings bindings) {
platform = bindings;
}
public static SaturnPlatformBindings platform() {
if (platform == null) {
throw new IllegalStateException("SaturnPlatformBindings not initialized");
}
return platform;
}
public static void setEmotes(SaturnEmoteBindings bindings) {
emotes = bindings;
}
public static SaturnEmoteBindings emotes() {
if (emotes == null) {
throw new IllegalStateException("SaturnEmoteBindings not initialized");
}
return emotes;
}
}
@@ -1,7 +0,0 @@
package org.saturnclient.common.minecraft.bindings;
import java.util.UUID;
public interface SaturnEmoteBindings {
void setEmote(UUID fromPlayerUuid, String emoteIdOrNull);
}
@@ -1,33 +0,0 @@
package org.saturnclient.common.minecraft.bindings;
import org.saturnclient.common.minecraft.MinecraftBinding;
import org.saturnclient.common.minecraft.MinecraftProvider;
public class SaturnIdentifier extends MinecraftBinding {
public SaturnIdentifier(String namespace, String path) {
super(MinecraftProvider.PROVIDER.createIdentifier(namespace, path));
}
public SaturnIdentifier(Object o) {
super(o);
}
public static SaturnIdentifier of(String namespace, String path) {
return new SaturnIdentifier(namespace, path);
}
public static SaturnIdentifier of(String fullPath) {
String[] p = fullPath.split(":", 2);
return new SaturnIdentifier(p[0], p[1]);
}
public static SaturnIdentifier ofVanilla(String path) {
return new SaturnIdentifier("minecraft", path);
}
@Override
public String toString() {
return this.inner.toString();
}
}
@@ -1,9 +0,0 @@
package org.saturnclient.common.minecraft.bindings;
import org.saturnclient.common.minecraft.MinecraftBinding;
public class SaturnItemStack extends MinecraftBinding {
public SaturnItemStack(Object binding) {
super(binding);
}
}
@@ -1,19 +0,0 @@
package org.saturnclient.common.minecraft.bindings;
import java.util.UUID;
public interface SaturnPlatformBindings {
String getAccessToken();
UUID getUuid();
String getUsername();
void onClientStopping(Runnable handler);
void logInfo(String message);
void logError(String message);
void logError(String message, Throwable throwable);
}
@@ -1,9 +0,0 @@
package org.saturnclient.common.minecraft.bindings;
import org.saturnclient.common.minecraft.MinecraftBinding;
public class SaturnQuaternionf extends MinecraftBinding {
public SaturnQuaternionf(Object o) {
super(o);
}
}
@@ -1,9 +0,0 @@
package org.saturnclient.common.minecraft.bindings;
import org.saturnclient.common.minecraft.MinecraftBinding;
public class SaturnSprite extends MinecraftBinding {
public SaturnSprite(Object binding) {
super(binding);
}
}
@@ -1,25 +0,0 @@
package org.saturnclient.common.minecraft.render;
import org.saturnclient.common.minecraft.bindings.SaturnQuaternionf;
public interface IMatrixStack {
public void translate(double x, double y, double z);
public void translate(float x, float y, float z);
public void scale(float x, float y, float z);
public void multiply(SaturnQuaternionf quaternion);
public void multiply(SaturnQuaternionf quaternion, float originX, float originY, float originZ);
public void push();
public void pop();
public Object peek();
public boolean isEmpty();
public void loadIdentity();
}
@@ -1,11 +0,0 @@
package org.saturnclient.common.minecraft.render;
public interface IWindow {
public int getWidth();
public int getHeight();
public int getFramebufferWidth();
public int getFramebufferHeight();
}
@@ -1,55 +0,0 @@
package org.saturnclient.config;
import org.saturnclient.config.manager.ConfigManager;
import org.saturnclient.config.manager.Property;
public class AnimationConfig {
public static ConfigManager config;
// Logo
public static final AnimationConfig logo = new AnimationConfig(true, 700, 0);
// Main menu
public static final AnimationConfig mainMenu = new AnimationConfig(true, 700, 120);
// Mod menu
public static final AnimationConfig modMenu = new AnimationConfig(true, 700, 20);
// Shift menu
public static final AnimationConfig shiftMenu = new AnimationConfig(true, 700, 100);
// Cosmetics menu
public static final AnimationConfig cosmeticsMenu = new AnimationConfig(true, 700, 40);
// Emote wheel
public static final AnimationConfig emoteWheel = new AnimationConfig(true, 700, 40);
public static void init(ConfigManager parent) {
config = new ConfigManager(parent, "Animations");
logo.init("Logo");
mainMenu.init("Main Menu");
modMenu.init("Mod Menu");
shiftMenu.init("Shift Menu");
cosmeticsMenu.init("Cosmetics Menu");
emoteWheel.init("Emote Wheel");
}
// Animation properties
public final Property<Integer> duration;
public final Property<Integer> stagger;
public final Property<Integer> curve;
public AnimationConfig(boolean enabled, int delay, int stagger) {
this.duration = Property.integer(delay);
this.stagger = Property.integer(stagger);
this.curve = Property.select(1, "Ease In Out Cubic", "Ease Out Cubic", "Ease In Out Back");
}
public void init(String name) {
// config.property("Enable " + name + " Animations", this.enabled);
config.property(name + " Duration", this.duration);
config.property(name + " Stagger", this.stagger);
config.property(name + " Curve", this.curve);
}
}
@@ -1,41 +0,0 @@
package org.saturnclient.config;
import java.io.File;
import org.saturnclient.common.minecraft.MinecraftProvider;
import org.saturnclient.common.minecraft.bindings.SaturnIdentifier;
import org.saturnclient.config.manager.ConfigManager;
import org.saturnclient.config.manager.Key;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.resources.Textures;
public class Config {
public static ConfigManager config;
public static Property<Boolean> realisticLogo = Property.bool(false);
public static Property<Boolean> saturnTitleScreen = Property.bool(true);
public static Property<Boolean> cloakPhysics = Property.bool(true);
public static Property<Integer> openEmoteWheel = Property.keybinding(Key.GLFW_KEY_B);
public static Property<Boolean> stagger = Property.bool(true);
public static SaturnIdentifier getLogo() {
return realisticLogo.value ? Textures.REALISTIC_LOGO : Textures.LOGO;
}
public static void init() {
File configFile = new File(MinecraftProvider.PROVIDER.getClient().getRunDirectory(), "saturn.json");
config = new ConfigManager(configFile, "Saturn Client");
config.property("Realistic logo", realisticLogo);
config.property("Saturn client title screen", saturnTitleScreen);
config.property("Open Emote Wheel", openEmoteWheel);
config.property("Cloak Physics", cloakPhysics);
config.property("Stagger Animations", stagger);
// Initialize a sub namespace for theme
Theme.init(config);
// Initialize a sub namespace for animations
AnimationConfig.init(config);
}
}
@@ -1,91 +0,0 @@
package org.saturnclient.config;
import org.saturnclient.config.manager.ConfigManager;
import org.saturnclient.config.manager.Property;
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
public static final Property<Integer> FONT = Property.font(2);
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,205 +0,0 @@
package org.saturnclient.config.manager;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.LinkedHashMap;
import java.util.Map;
public class ConfigManager {
private static final ObjectMapper MAPPER = new ObjectMapper();
private static File configFile;
// SINGLE ROOT TREE
private static final Map<String, Property<?>> ROOT = new LinkedHashMap<>();
private static JsonNode cachedJson = null;
private final Map<String, Property<?>> currentMap;
private final String[] path;
/**
* Creates a root namespace
*/
public ConfigManager(File configFile, String namespace) {
ConfigManager.configFile = configFile;
this.currentMap = new LinkedHashMap<>();
this.path = new String[] { namespace };
ROOT.put(namespace, Property.namespace(currentMap));
}
/**
* Creates a root namespace
*/
public ConfigManager(String namespace) {
this.currentMap = new LinkedHashMap<>();
this.path = new String[] { namespace };
ROOT.put(namespace, Property.namespace(currentMap));
}
/**
* Creates a sub-namespace inside a parent
*/
public ConfigManager(ConfigManager parent, String namespace) {
this.currentMap = new LinkedHashMap<>();
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);
loadSingleProperty(name, value);
return value;
}
/**
* Add empty namespace
*/
public ConfigManager sub(String name) {
return new ConfigManager(this, name);
}
/**
* Load entire config
*/
public void load() {
JsonNode json = loadAndCache();
if (json == null || !json.isObject())
return;
loadRecursive((ObjectNode) json, ROOT);
}
/**
* Save entire config
*/
public static void save() {
ObjectNode rootJson = MAPPER.createObjectNode();
saveRecursive(rootJson, ROOT);
try {
MAPPER.writerWithDefaultPrettyPrinter().writeValue(configFile, rootJson);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Recursive save
*/
private static void saveRecursive(ObjectNode json, Map<String, Property<?>> map) {
map.forEach((name, prop) -> {
if (prop.isNamespace()) {
ObjectNode child = json.objectNode();
saveRecursive(child, prop.getNamespaceMap());
json.set(name, child);
} else {
json.set(name, prop.toJson());
}
});
}
/**
* Recursive load
*/
private static void loadRecursive(ObjectNode json, Map<String, Property<?>> map) {
map.forEach((name, prop) -> {
JsonNode element = json.get(name);
if (element == null)
return;
if (prop.isNamespace() && element.isObject()) {
loadRecursive((ObjectNode) element, prop.getNamespaceMap());
} else {
prop.loadFromJson(element);
}
});
}
/**
* Load only a single property immediately after creation
*/
private void loadSingleProperty(String name, Property<?> prop) {
JsonNode source = cachedJson != null ? cachedJson : loadAndCache();
if (source == null || !source.isObject())
return;
JsonNode currentScope = source;
for (String segment : path) {
JsonNode next = currentScope.get(segment);
if (next != null && next.isObject()) {
currentScope = next;
} else {
return;
}
}
JsonNode element = currentScope.get(name);
if (element != null) {
prop.loadFromJson(element);
}
}
/**
* Reads file and caches json
*/
private JsonNode loadAndCache() {
try {
if (!configFile.exists()) {
Files.writeString(configFile.toPath(), "{}");
cachedJson = MAPPER.createObjectNode();
return cachedJson;
}
cachedJson = MAPPER.readTree(configFile);
return cachedJson;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public Map<String, Property<?>> getProperties() {
return currentMap;
}
}
@@ -1,152 +0,0 @@
package org.saturnclient.config.manager;
public class Key {
/** Printable keys. */
public static final int GLFW_KEY_SPACE = 32,
GLFW_KEY_APOSTROPHE = 39,
GLFW_KEY_COMMA = 44,
GLFW_KEY_MINUS = 45,
GLFW_KEY_PERIOD = 46,
GLFW_KEY_SLASH = 47,
GLFW_KEY_0 = 48,
GLFW_KEY_1 = 49,
GLFW_KEY_2 = 50,
GLFW_KEY_3 = 51,
GLFW_KEY_4 = 52,
GLFW_KEY_5 = 53,
GLFW_KEY_6 = 54,
GLFW_KEY_7 = 55,
GLFW_KEY_8 = 56,
GLFW_KEY_9 = 57,
GLFW_KEY_SEMICOLON = 59,
GLFW_KEY_EQUAL = 61,
GLFW_KEY_A = 65,
GLFW_KEY_B = 66,
GLFW_KEY_C = 67,
GLFW_KEY_D = 68,
GLFW_KEY_E = 69,
GLFW_KEY_F = 70,
GLFW_KEY_G = 71,
GLFW_KEY_H = 72,
GLFW_KEY_I = 73,
GLFW_KEY_J = 74,
GLFW_KEY_K = 75,
GLFW_KEY_L = 76,
GLFW_KEY_M = 77,
GLFW_KEY_N = 78,
GLFW_KEY_O = 79,
GLFW_KEY_P = 80,
GLFW_KEY_Q = 81,
GLFW_KEY_R = 82,
GLFW_KEY_S = 83,
GLFW_KEY_T = 84,
GLFW_KEY_U = 85,
GLFW_KEY_V = 86,
GLFW_KEY_W = 87,
GLFW_KEY_X = 88,
GLFW_KEY_Y = 89,
GLFW_KEY_Z = 90,
GLFW_KEY_LEFT_BRACKET = 91,
GLFW_KEY_BACKSLASH = 92,
GLFW_KEY_RIGHT_BRACKET = 93,
GLFW_KEY_GRAVE_ACCENT = 96,
GLFW_KEY_WORLD_1 = 161,
GLFW_KEY_WORLD_2 = 162;
/** Function keys. */
public static final int GLFW_KEY_ESCAPE = 256,
GLFW_KEY_ENTER = 257,
GLFW_KEY_TAB = 258,
GLFW_KEY_BACKSPACE = 259,
GLFW_KEY_INSERT = 260,
GLFW_KEY_DELETE = 261,
GLFW_KEY_RIGHT = 262,
GLFW_KEY_LEFT = 263,
GLFW_KEY_DOWN = 264,
GLFW_KEY_UP = 265,
GLFW_KEY_PAGE_UP = 266,
GLFW_KEY_PAGE_DOWN = 267,
GLFW_KEY_HOME = 268,
GLFW_KEY_END = 269,
GLFW_KEY_CAPS_LOCK = 280,
GLFW_KEY_SCROLL_LOCK = 281,
GLFW_KEY_NUM_LOCK = 282,
GLFW_KEY_PRINT_SCREEN = 283,
GLFW_KEY_PAUSE = 284,
GLFW_KEY_F1 = 290,
GLFW_KEY_F2 = 291,
GLFW_KEY_F3 = 292,
GLFW_KEY_F4 = 293,
GLFW_KEY_F5 = 294,
GLFW_KEY_F6 = 295,
GLFW_KEY_F7 = 296,
GLFW_KEY_F8 = 297,
GLFW_KEY_F9 = 298,
GLFW_KEY_F10 = 299,
GLFW_KEY_F11 = 300,
GLFW_KEY_F12 = 301,
GLFW_KEY_F13 = 302,
GLFW_KEY_F14 = 303,
GLFW_KEY_F15 = 304,
GLFW_KEY_F16 = 305,
GLFW_KEY_F17 = 306,
GLFW_KEY_F18 = 307,
GLFW_KEY_F19 = 308,
GLFW_KEY_F20 = 309,
GLFW_KEY_F21 = 310,
GLFW_KEY_F22 = 311,
GLFW_KEY_F23 = 312,
GLFW_KEY_F24 = 313,
GLFW_KEY_F25 = 314,
GLFW_KEY_KP_0 = 320,
GLFW_KEY_KP_1 = 321,
GLFW_KEY_KP_2 = 322,
GLFW_KEY_KP_3 = 323,
GLFW_KEY_KP_4 = 324,
GLFW_KEY_KP_5 = 325,
GLFW_KEY_KP_6 = 326,
GLFW_KEY_KP_7 = 327,
GLFW_KEY_KP_8 = 328,
GLFW_KEY_KP_9 = 329,
GLFW_KEY_KP_DECIMAL = 330,
GLFW_KEY_KP_DIVIDE = 331,
GLFW_KEY_KP_MULTIPLY = 332,
GLFW_KEY_KP_SUBTRACT = 333,
GLFW_KEY_KP_ADD = 334,
GLFW_KEY_KP_ENTER = 335,
GLFW_KEY_KP_EQUAL = 336,
GLFW_KEY_LEFT_SHIFT = 340,
GLFW_KEY_LEFT_CONTROL = 341,
GLFW_KEY_LEFT_ALT = 342,
GLFW_KEY_LEFT_SUPER = 343,
GLFW_KEY_RIGHT_SHIFT = 344,
GLFW_KEY_RIGHT_CONTROL = 345,
GLFW_KEY_RIGHT_ALT = 346,
GLFW_KEY_RIGHT_SUPER = 347,
GLFW_KEY_MENU = 348,
GLFW_KEY_LAST = GLFW_KEY_MENU;
/** If this bit is set one or more Shift keys were held down. */
public static final int GLFW_MOD_SHIFT = 0x1;
/** If this bit is set one or more Control keys were held down. */
public static final int GLFW_MOD_CONTROL = 0x2;
/** If this bit is set one or more Alt keys were held down. */
public static final int GLFW_MOD_ALT = 0x4;
/** If this bit is set one or more Super keys were held down. */
public static final int GLFW_MOD_SUPER = 0x8;
/**
* If this bit is set the Caps Lock key is enabled and the
* {@link #GLFW_LOCK_KEY_MODS LOCK_KEY_MODS} input mode is set.
*/
public static final int GLFW_MOD_CAPS_LOCK = 0x10;
/**
* If this bit is set the Num Lock key is enabled and the
* {@link #GLFW_LOCK_KEY_MODS LOCK_KEY_MODS} input mode is set.
*/
public static final int GLFW_MOD_NUM_LOCK = 0x20;
}
@@ -1,320 +0,0 @@
package org.saturnclient.config.manager;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.saturnclient.common.minecraft.MinecraftProvider;
import java.util.Map;
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,
INTEGER,
FLOAT,
STRING,
HEX,
NAMESPACE,
SELECT,
KEYBINDING,
}
public T value;
public boolean isReset = false;
private T defaultValue;
private String[] availableValues;
private final PropertyType type;
private boolean wasPressedLastTick = false;
private Property(T value, PropertyType type) {
this.value = value;
this.defaultValue = value;
this.type = type;
}
// ---------- Factory Methods ----------
public static <T> Property<T> from(T value) {
if (value instanceof Boolean)
return new Property<>(value, PropertyType.BOOLEAN);
if (value instanceof Integer)
return new Property<>(value, PropertyType.INTEGER);
if (value instanceof Float)
return new Property<>(value, PropertyType.FLOAT);
if (value instanceof String)
return new Property<>(value, PropertyType.STRING);
if (valueIsNamespace(value))
return new Property<>(value, PropertyType.NAMESPACE);
return null;
}
public static Property<Integer> font(int value) {
return select(value, "Default", "Inter", "Inter bold");
}
public static Property<Integer> select(Integer value, String... availableValues) {
Property<Integer> property = new Property<>(value, PropertyType.SELECT);
property.availableValues = availableValues;
return property;
}
public static Property<Map<String, Property<?>>> namespace(Map<String, Property<?>> value) {
return new Property<>(value, PropertyType.NAMESPACE);
}
public static Property<Boolean> bool(boolean value) {
return new Property<>(value, PropertyType.BOOLEAN);
}
public static Property<Integer> integer(int value) {
return new Property<>(value, PropertyType.INTEGER);
}
public static Property<Float> floatProp(float value) {
return new Property<>(value, PropertyType.FLOAT);
}
public static Property<String> string(String value) {
return new Property<>(value, PropertyType.STRING);
}
public static Property<Integer> color(int value) {
return new Property<>(value, PropertyType.HEX);
}
public static Property<Integer> keybinding(int value) {
return new Property<>(value, PropertyType.KEYBINDING);
}
// ---------- Select Helpers ----------
public void next() {
if (type == PropertyType.SELECT) {
int i = (Integer) value;
setValue((i < availableValues.length - 1) ? i + 1 : 0);
}
}
public void prev() {
if (type == PropertyType.SELECT) {
int i = (Integer) value;
setValue((i > 0) ? i - 1 : availableValues.length - 1);
}
}
public void setSelection(int selection) {
if (type == PropertyType.SELECT && selection >= 0 && selection < availableValues.length) {
setValue(selection);
}
}
public String getSelection() {
return (type == PropertyType.SELECT) ? availableValues[(Integer) value] : null;
}
// ---------- Lifecycle ----------
public void reset() {
value = defaultValue;
isReset = true;
}
public PropertyType getType() {
return type;
}
@SuppressWarnings("unchecked")
public Map<String, Property<?>> getNamespaceValue() {
if (type == PropertyType.NAMESPACE && value instanceof Map) {
return (Map<String, Property<?>>) value;
}
throw new IllegalStateException("Property is not a namespace");
}
// ---------- JSON Serialization ----------
public JsonNode toJson() {
switch (type) {
case BOOLEAN:
return JsonNodeFactory.instance.booleanNode((Boolean) value);
case INTEGER:
return JsonNodeFactory.instance.numberNode((Integer) value);
case FLOAT:
return JsonNodeFactory.instance.numberNode((Float) value);
case STRING:
return JsonNodeFactory.instance.textNode((String) value);
case HEX:
return JsonNodeFactory.instance.textNode(
String.format("#%08X", (Integer) value));
case KEYBINDING:
case SELECT:
return JsonNodeFactory.instance.numberNode((Integer) value);
case NAMESPACE:
ObjectNode nested = JsonNodeFactory.instance.objectNode();
getNamespaceValue().forEach((k, v) -> {
nested.set(k, v.toJson());
});
return nested;
default:
return JsonNodeFactory.instance.textNode(String.valueOf(value));
}
}
public void loadFromJson(JsonNode element) {
if (element == null)
return;
switch (type) {
case BOOLEAN:
if (element.isBoolean())
setValue(element.booleanValue());
break;
case INTEGER:
if (element.isInt())
setValue(element.intValue());
else if (element.isTextual())
setValue(parseHexToInt(element.textValue()));
break;
case FLOAT:
if (element.isNumber())
setValue(element.floatValue());
break;
case STRING:
if (element.isTextual())
setValue(element.textValue());
break;
case HEX:
if (element.isTextual())
setValue(parseHexToInt(element.textValue()));
break;
case NAMESPACE:
if (element.isObject()) {
ObjectNode obj = (ObjectNode) element;
getNamespaceValue().forEach((k, v) -> {
v.loadFromJson(obj.get(k));
});
}
break;
case SELECT:
if (element.isInt())
setSelection(element.intValue());
break;
case KEYBINDING:
if (element.isInt())
setValue(element.intValue());
break;
}
}
// ---------- Utility ----------
@Override
public String toString() {
return String.valueOf(value);
}
public static boolean valueIsNamespace(Object obj) {
return obj instanceof Map;
}
@SuppressWarnings("unchecked")
public void setValue(Object value) {
this.value = (T) value;
}
@SuppressWarnings("unchecked")
public void setValue(Object value, Object defaultValue) {
this.value = (T) value;
this.defaultValue = (T) defaultValue;
}
public NamedProperty<T> named(String name) {
return new NamedProperty<>(name, this);
}
// ---------- Keybindings (no SaturnClient) ----------
public boolean isKeyPressed() {
return (Integer) value != -1 &&
MinecraftProvider.PROVIDER.isKeyPressed((Integer) value);
}
public boolean wasKeyPressed() {
boolean pressed = isKeyPressed();
boolean result = pressed && !wasPressedLastTick;
wasPressedLastTick = pressed;
return result;
}
// ---------- HEX ----------
public static int parseHexToInt(String hex) {
hex = hex.replace("#", "");
if (hex.length() == 6)
hex = "FF" + hex;
if (hex.length() != 8)
throw new IllegalArgumentException(
"Hex must be 6 or 8 chars long, got '" + hex + "'");
return (int) Long.parseLong(hex, 16);
}
// ---------- Misc ----------
public Property<T> copy() {
return new Property<>(this.value, this.type);
}
public boolean isNamespace() {
return type == PropertyType.NAMESPACE;
}
@SuppressWarnings("unchecked")
public Map<String, Property<?>> getNamespaceMap() {
return isNamespace() ? (Map<String, Property<?>>) value : null;
}
}
@@ -1,17 +0,0 @@
package org.saturnclient.cosmetics;
import java.util.ArrayList;
import java.util.List;
public class Emotes {
public static List<String> availableEmotes = new ArrayList<>();
public static void initialize() {
availableEmotes.add("tpose");
availableEmotes.add("back_flip");
availableEmotes.add("facepalm");
availableEmotes.add("cry");
availableEmotes.add("giga_chad");
availableEmotes.add("front_flip");
}
}
@@ -1,31 +0,0 @@
package org.saturnclient.cosmetics;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.saturnclient.client.ServiceClient;
import org.saturnclient.client.player.SaturnPlayer;
public class Hats {
public static final String[] ALL_HATS = { "horns_black", "horns_white", "halo", "halo_black" };
public static List<String> availableHats = new ArrayList<>();
public static void initialize() {
availableHats.add(0, "");
}
public static void setHat(String hatName) {
if (availableHats.contains(hatName)) {
setHat(ServiceClient.uuid, hatName);
ServiceClient.setHat(hatName);
}
}
public static void setHat(UUID uuid, String hatName) {
SaturnPlayer player = SaturnPlayer.get(uuid);
if (player != null) {
player.hat = hatName;
}
}
}
@@ -1,11 +0,0 @@
package org.saturnclient.modules;
import org.saturnclient.ui.RenderScope;
public interface HudMod {
public ModDimensions getDimensions();
public void renderDummy(RenderScope scope);
public void renderHud(RenderScope scope);
}
@@ -1,54 +0,0 @@
package org.saturnclient.modules;
import java.util.Map;
import org.saturnclient.config.manager.Property;
import org.saturnclient.config.manager.Property.NamedProperty;
public class ModDimensions {
public Property<Integer> x = Property.integer(0);
public Property<Integer> y = Property.integer(0);
public Property<Float> scale = Property.floatProp(1.0f);
public Property<Integer> bgColor = Property.color(0x00000000);
public Property<Integer> fgColor = Property.color(0xFFffffff);
public Property<Integer> radius = Property.integer(0);
public Property<Integer> font = Property.font(0);
public boolean renderBackground = true;
public int width = 0;
public int height = 0;
public NamedProperty<Map<String, Property<?>>> prop() {
return Property.namespace(Map.of(
"X", x,
"Y", y,
"Scale", scale,
"Background Color", bgColor,
"Foreground Color", fgColor,
"Corner Radius", radius,
"Font", font)).named("In-Game Display");
}
public ModDimensions() {
}
public ModDimensions(int width, int height) {
this.width = width;
this.height = height;
}
public ModDimensions(int x, int y, int width, int height) {
this.x.value = x;
this.y.value = y;
this.width = width;
this.height = height;
}
public ModDimensions(int x, int y, int width, int height, float scale) {
this.x.value = x;
this.y.value = y;
this.scale.value = scale;
this.width = width;
this.height = height;
}
}
@@ -1,31 +0,0 @@
package org.saturnclient.modules;
import java.util.ArrayList;
import java.util.List;
public class ModManager {
public static List<Module> ENABLED_MODS = new ArrayList<>();
public static Module[] MODS = {};
public static void init(ModuleProvider provider) {
MODS = provider.getMods();
updateEnabledModules();
}
public static synchronized void updateEnabledModules() {
ENABLED_MODS.clear();
for (Module m : MODS) {
if (m.isEnabled()) {
ENABLED_MODS.add(m);
}
}
}
public static Module[] getAllMods() {
return MODS;
}
public interface ModuleProvider {
Module[] getMods();
}
}
@@ -1,59 +0,0 @@
package org.saturnclient.modules;
import org.saturnclient.common.minecraft.bindings.SaturnIdentifier;
import org.saturnclient.config.manager.ConfigManager;
import org.saturnclient.config.manager.Property.NamedProperty;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.Textures;
public abstract class Module {
private final ModuleDetails details;
private final ConfigManager configManager;
public Module(ModuleDetails details, NamedProperty<?>... props) {
this.details = details;
configManager = new ConfigManager(details.name);
for (NamedProperty<?> prop : props) {
configManager.property(prop.name, prop.prop);
}
}
public void render(RenderScope scope) {
}
public void tick() {
}
public abstract boolean isEnabled();
public final void setEnabled(boolean e) {
onEnabled(e);
ModManager.updateEnabledModules();
}
public abstract void onEnabled(boolean e);
public final String getName() {
return details.name;
}
public SaturnIdentifier getIconTexture() {
return Textures.getModIcon(details.namespace);
}
public ConfigManager getConfig() {
return configManager;
}
public String getDescription() {
return details.description;
}
public String[] getTags() {
return details.tags;
}
public String getVersion() {
return details.version;
}
}
@@ -1,39 +0,0 @@
package org.saturnclient.modules;
public class ModuleDetails {
public String name;
public String namespace;
public String description;
public String version;
public String[] tags = {};
public ModuleDetails(String name, String namespace) {
this.name = name;
this.namespace = namespace;
}
public ModuleDetails name(String name) {
this.name = name;
return this;
}
public ModuleDetails namespace(String namespace) {
this.namespace = namespace;
return this;
}
public ModuleDetails description(String description) {
this.description = description;
return this;
}
public ModuleDetails version(String version) {
this.version = version;
return this;
}
public ModuleDetails tags(String... tags) {
this.tags = tags;
return this;
}
}
@@ -1,32 +0,0 @@
package org.saturnclient.modules.interfaces;
import org.saturnclient.common.minecraft.bindings.SaturnItemStack;
public interface ArmorDisplayInterface {
SaturnItemStack getMainHand();
SaturnItemStack getHelmet();
SaturnItemStack getChestplate();
SaturnItemStack getLeggings();
SaturnItemStack getBoots();
SaturnItemStack getDummyMainHand();
SaturnItemStack getDummyHelmet();
SaturnItemStack getDummyChestplate();
SaturnItemStack getDummyLeggings();
SaturnItemStack getDummyBoots();
boolean isEmpty(SaturnItemStack stack);
int getMaxDamage(SaturnItemStack stack);
int getDamage(SaturnItemStack stack);
}
@@ -1,20 +0,0 @@
package org.saturnclient.modules.interfaces;
public interface AutoSprintInterface {
boolean hasPlayer();
boolean hasNetwork();
boolean isForwardPressed();
boolean isBackPressed();
boolean isSneaking();
boolean hasHorizontalCollision();
boolean isUsingItem();
void setSprinting(boolean sprinting);
}
@@ -1,11 +0,0 @@
package org.saturnclient.modules.interfaces;
public interface CoordinatesInterface {
int getPlayerX();
int getPlayerY();
int getPlayerZ();
}
@@ -1,7 +0,0 @@
package org.saturnclient.modules.interfaces;
public interface CrosshairInterface {
boolean isTargetingLivingEntity();
}
@@ -1,7 +0,0 @@
package org.saturnclient.modules.interfaces;
public interface DayCounterInterface {
long getDay();
}
@@ -1,7 +0,0 @@
package org.saturnclient.modules.interfaces;
public interface FpsInterface {
int getFps();
}
@@ -1,11 +0,0 @@
package org.saturnclient.modules.interfaces;
public interface FreelookInterface {
boolean isFirstPerson();
void setFirstPerson();
void setThirdPersonBack();
}
@@ -1,7 +0,0 @@
package org.saturnclient.modules.interfaces;
public interface HealthDisplayInterface {
float getPlayerHealth();
}
@@ -1,13 +0,0 @@
package org.saturnclient.modules.interfaces;
public interface KeystrokesInterface {
boolean isForwardPressed();
boolean isBackPressed();
boolean isLeftPressed();
boolean isRightPressed();
boolean isJumpPressed();
boolean isAttackPressed();
boolean isUsePressed();
}
@@ -1,23 +0,0 @@
package org.saturnclient.modules.interfaces;
public interface NametagsInterface {
public static interface EntityState {
String getCustomName();
float getHealth();
float getMaxHealth();
EntityType getEntityType();
}
public static enum EntityType {
PLAYER,
PASSIVE,
HOSTILE,
OTHER
}
}
@@ -1,8 +0,0 @@
package org.saturnclient.modules.interfaces;
public interface PingInterface {
/** Returns current player ping in milliseconds */
int getPing();
}
@@ -1,20 +0,0 @@
package org.saturnclient.modules.interfaces;
public interface SpeedometerInterface {
/** Returns the current player or vehicle velocity in blocks per tick */
Velocity getVelocity();
/** Returns true if player or vehicle is on ground */
boolean isOnGround();
class Velocity {
public final double x, y, z;
public Velocity(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
}
}
@@ -1,36 +0,0 @@
package org.saturnclient.modules.interfaces;
import java.util.List;
import org.saturnclient.common.minecraft.bindings.SaturnSprite;
public interface StatusEffectsInterface {
/** Returns a list of currently active effects */
List<Effect> getActiveEffects();
/** Returns a dummy list of effects for dummy rendering */
List<Effect> getDummyEffects();
/** Represents a single effect for rendering purposes */
class Effect {
private final SaturnSprite icon;
private final int durationSeconds;
private final boolean infinite;
private final boolean showIcon;
private final String infiniteText;
public Effect(SaturnSprite icon, int durationSeconds, boolean infinite, boolean showIcon, String infiniteText) {
this.icon = icon;
this.durationSeconds = durationSeconds;
this.infinite = infinite;
this.showIcon = showIcon;
this.infiniteText = infiniteText;
}
public SaturnSprite getIcon() { return icon; }
public int getDurationSeconds() { return durationSeconds; }
public boolean isInfinite() { return infinite; }
public boolean shouldShowIcon() { return showIcon; }
public String getInfiniteText() { return infiniteText; }
}
}
@@ -1,118 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.modules.interfaces.ArmorDisplayInterface;
import org.saturnclient.common.minecraft.bindings.SaturnItemStack;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.RenderScope;
public class ArmorDisplay extends Module implements HudMod {
private static Property<Boolean> enabled = Property.bool(false);
private static ModDimensions dimensions = new ModDimensions(40, 75);
private static Property<Boolean> useMainHand = Property.bool(true);
private final ArmorDisplayInterface minecraft;
public ArmorDisplay(ArmorDisplayInterface minecraft) {
super(new ModuleDetails("Armor Display", "armor")
.description("Displays armor durability")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
useMainHand.named("Use Main Hand"),
dimensions.prop());
this.minecraft = minecraft;
}
public void renderArmor(RenderScope scope,
SaturnItemStack mainHand,
SaturnItemStack helmet,
SaturnItemStack chestplate,
SaturnItemStack leggings,
SaturnItemStack boots) {
int row = 0;
if (useMainHand.value && !minecraft.isEmpty(mainHand)) {
scope.drawItem(mainHand, 0, 15 * row);
renderHealth(scope, row, minecraft.getMaxDamage(mainHand), minecraft.getDamage(mainHand));
row++;
}
if (!minecraft.isEmpty(helmet)) {
scope.drawItem(helmet, 0, 15 * row);
renderHealth(scope, row, minecraft.getMaxDamage(helmet), minecraft.getDamage(helmet));
row++;
}
if (!minecraft.isEmpty(chestplate)) {
scope.drawItem(chestplate, 0, 15 * row);
renderHealth(scope, row, minecraft.getMaxDamage(chestplate), minecraft.getDamage(chestplate));
row++;
}
if (!minecraft.isEmpty(leggings)) {
scope.drawItem(leggings, 0, 15 * row);
renderHealth(scope, row, minecraft.getMaxDamage(leggings), minecraft.getDamage(leggings));
row++;
}
if (!minecraft.isEmpty(boots)) {
scope.drawItem(boots, 0, 15 * row);
renderHealth(scope, row, minecraft.getMaxDamage(boots), minecraft.getDamage(boots));
row++;
}
}
public void renderHealth(RenderScope scope, int i, int maxDamage, int damage) {
if (maxDamage > 0) {
scope.drawText(
0.5f,
"" + (maxDamage - damage),
17,
(15 * i) + 3,
dimensions.font.value,
dimensions.fgColor.value);
}
}
@Override
public void renderHud(RenderScope scope) {
renderArmor(scope,
minecraft.getMainHand(),
minecraft.getHelmet(),
minecraft.getChestplate(),
minecraft.getLeggings(),
minecraft.getBoots());
}
@Override
public void renderDummy(RenderScope scope) {
renderArmor(scope,
minecraft.getDummyMainHand(),
minecraft.getDummyHelmet(),
minecraft.getDummyChestplate(),
minecraft.getDummyLeggings(),
minecraft.getDummyBoots());
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
}
@@ -1,50 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.modules.interfaces.AutoSprintInterface;
import org.saturnclient.config.manager.Property;
public class AutoSprint extends Module {
private static Property<Boolean> enabled = Property.bool(false);
private final AutoSprintInterface minecraft;
public AutoSprint(AutoSprintInterface minecraft) {
super(
new ModuleDetails("Auto Sprint", "sprint")
.description("Makes the player always sprint")
.tags("Movement")
.version("v0.1.0"),
enabled.named("Enabled"));
this.minecraft = minecraft;
}
@Override
public void tick() {
if (!minecraft.hasPlayer() || !minecraft.hasNetwork()) {
return;
}
if (minecraft.isForwardPressed()
&& !minecraft.isBackPressed()
&& !minecraft.isSneaking()
&& !minecraft.hasHorizontalCollision()
&& !minecraft.isUsingItem()) {
minecraft.setSprinting(true);
}
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
}
@@ -1,82 +0,0 @@
package org.saturnclient.modules.mods;
import java.time.format.DateTimeFormatter;
import java.time.LocalTime;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.Fonts;
public class Clock extends Module implements HudMod {
private static Property<Boolean> enabled = Property.bool(false);
private static Property<Integer> format = Property.select(0, "24 hour", "12 hour");
private static Property<Boolean> seconds = Property.bool(false);
private static ModDimensions dimensions = new ModDimensions(60, Fonts.getHeight());
public Clock() {
super(new ModuleDetails("Clock", "clock")
.description("Displays the current real-world time")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
format.named("Format"),
seconds.named("Show seconds"),
dimensions.prop());
}
@Override
public void renderDummy(RenderScope scope) {
renderClock(scope);
}
@Override
public void renderHud(RenderScope scope) {
renderClock(scope);
}
public void renderClock(RenderScope scope) {
String text = "";
LocalTime currentTime = LocalTime.now();
switch (format.value) {
case 0:
if (seconds.value) {
text = currentTime.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
} else {
text = currentTime.format(DateTimeFormatter.ofPattern("HH:mm"));
}
break;
case 1:
if (seconds.value) {
text = currentTime.format(DateTimeFormatter.ofPattern("hh:mm:ss a"));
} else {
text = currentTime.format(DateTimeFormatter.ofPattern("hh:mm a"));
}
break;
}
scope.drawText(text,
0, 0, dimensions.font.value, dimensions.fgColor.value);
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
dimensions.height = 18 * text.split("\n").length;
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
}
@@ -1,98 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.modules.interfaces.CoordinatesInterface;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.Fonts;
public class Coordinates extends Module implements HudMod {
private static Property<Boolean> enabled = Property.bool(false);
private static Property<Integer> displayMethod = Property.select(
0,
"Flat",
"Flat annotated",
"Newline",
"Newline annotated");
private static ModDimensions dimensions = new ModDimensions(40, 18);
private final CoordinatesInterface minecraft;
public Coordinates(CoordinatesInterface minecraft) {
super(new ModuleDetails("Coordinates", "coords")
.description("Displays your current coordinates")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
displayMethod.named("Display Method"),
dimensions.prop());
this.minecraft = minecraft;
}
@Override
public void renderDummy(RenderScope scope) {
renderCoords(532, 69, 253, scope);
}
@Override
public void renderHud(RenderScope scope) {
int x = minecraft.getPlayerX();
int y = minecraft.getPlayerY();
int z = minecraft.getPlayerZ();
renderCoords(x, y, z, scope);
}
public void renderCoords(int x, int y, int z, RenderScope scope) {
String f = "%d %d %d";
switch (displayMethod.value) {
case 1:
f = "X: %d Y: %d Z: %d";
break;
case 2:
f = "%d\n%d\n%d";
break;
case 3:
f = "X: %d\nY: %d\nZ: %d";
break;
}
String text = String.format(f, x, y, z);
scope.drawText(
text,
0,
0,
dimensions.font.value,
dimensions.fgColor.value);
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
dimensions.height = 18 * text.split("\n").length;
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
}
@@ -1,55 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.modules.interfaces.CrosshairInterface;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.Textures;
public class Crosshair extends Module {
public static Property<Boolean> enabled = Property.bool(false);
private final CrosshairInterface minecraft;
public Crosshair(CrosshairInterface minecraft) {
super(
new ModuleDetails("Crosshair", "crosshair")
.description("Changes the crosshair if you're aiming at a entity")
.tags("Visuals", "Utility")
.version("v0.1.0"),
enabled.named("Enabled"));
this.minecraft = minecraft;
}
@Override
public void render(RenderScope scope) {
if (enabled.value && minecraft.isTargetingLivingEntity()) {
int scaledWidth = 15;
int scaledHeight = 15;
scope.drawTexture(
Textures.CROSSHAIR_RANGE,
(scope.getScaledWindowWidth() - scaledWidth) / 2,
(scope.getScaledWindowHeight() - scaledHeight) / 2,
0.0F,
0.0F,
scaledWidth,
scaledHeight);
}
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
}
@@ -1,71 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.modules.interfaces.DayCounterInterface;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.Fonts;
public class DayCounter extends Module implements HudMod {
private static Property<Boolean> enabled = Property.bool(false);
private static ModDimensions dimensions = new ModDimensions(40, 18);
private final DayCounterInterface minecraft;
long day = 0;
public DayCounter(DayCounterInterface minecraft) {
super(new ModuleDetails("DayCounter", "day")
.description("Displays the number of days that have passed")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
dimensions.prop());
this.minecraft = minecraft;
}
@Override
public void renderDummy(RenderScope scope) {
renderDay(271, scope);
}
@Override
public void renderHud(RenderScope scope) {
day = minecraft.getDay();
renderDay(day, scope);
}
public void renderDay(long day, RenderScope scope) {
String text = "Day: " + day;
scope.drawText(
text,
0,
0,
dimensions.font.value,
dimensions.fgColor.value);
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
dimensions.height = 18 * text.split("\n").length;
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
}
@@ -1,64 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.modules.interfaces.FpsInterface;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.Fonts;
public class Fps extends Module implements HudMod {
private static Property<Boolean> enabled = Property.bool(false);
private static ModDimensions dimensions = new ModDimensions(60, Fonts.getHeight());
private final FpsInterface minecraft;
public Fps(FpsInterface minecraft) {
super(new ModuleDetails("FPS Display", "fps")
.description("Displays current FPS")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
dimensions.prop());
this.minecraft = minecraft;
}
@Override
public void renderHud(RenderScope scope) {
int fps = minecraft.getFps();
String text = fps + " FPS";
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
}
@Override
public void renderDummy(RenderScope scope) {
String text = "369 FPS";
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
}
@@ -1,88 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.modules.interfaces.FreelookInterface;
import org.saturnclient.config.manager.Key;
import org.saturnclient.config.manager.Property;
public class Freelook extends Module {
private static Property<Boolean> enabled = Property.bool(false);
private static Property<Boolean> toggle = Property.bool(true);
public static Property<Integer> freelookKey = Property.keybinding(Key.GLFW_KEY_H);
public static boolean isFreeLooking = false;
private static boolean was1stPerson = false;
private final FreelookInterface minecraft;
public Freelook(FreelookInterface minecraft) {
super(
new ModuleDetails("Freelook", "freelook")
.description("Allows you to look around freely without moving your character")
.tags("Movement", "Camera")
.version("0.1.0"),
enabled.named("Enabled"),
toggle.named("Toggle freelook"),
freelookKey.named("Freelook Keybinding"));
this.minecraft = minecraft;
}
@Override
public void tick() {
if (toggle.value) {
if (freelookKey.wasKeyPressed()) {
if (isFreeLooking) {
stopFreelook();
} else {
startFreelook();
}
}
} else if (freelookKey.isKeyPressed()) {
if (!isFreeLooking) {
startFreelook();
}
} else if (isFreeLooking) {
stopFreelook();
}
}
private void startFreelook() {
isFreeLooking = true;
was1stPerson = minecraft.isFirstPerson();
minecraft.setThirdPersonBack();
}
private void stopFreelook() {
if (was1stPerson) {
minecraft.setFirstPerson();
}
isFreeLooking = false;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
@Override
public boolean isEnabled() {
return enabled.value;
}
}
@@ -1,39 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.config.manager.Property;
public class Fullbright extends Module {
private static Property<Boolean> enabled = Property.bool(false);
private static Property<Integer> brightness = Property.integer(100);
public Fullbright() {
super(
new ModuleDetails("Fullbright", "fullbright")
.description("Allows you to see in the dark")
.tags("Camera")
.version("v0.1.0"),
enabled.named("Enabled"),
brightness.named("Brightness %"));
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
public static boolean shouldOverrideBrightness() {
return enabled.value;
}
public static float getBrightnessValue() {
return brightness.value / 10;
}
}
@@ -1,97 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.modules.interfaces.HealthDisplayInterface;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.Fonts;
public class HealthDisplay extends Module implements HudMod {
private static Property<Boolean> enabled = Property.bool(false);
private static Property<Integer> displayMode = Property.select(0, "Health", "Hearts");
private static Property<Integer> decimals = Property.select(0, "0", "1", "2");
private static ModDimensions dimensions = new ModDimensions(40, 18);
private final HealthDisplayInterface minecraft;
public HealthDisplay(HealthDisplayInterface minecraft) {
super(new ModuleDetails("Health display", "health")
.description("Displays your current health")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
displayMode.named("Display mode"),
decimals.named("Decimals"),
dimensions.prop());
this.minecraft = minecraft;
}
@Override
public void renderDummy(RenderScope scope) {
renderHealth(10, scope);
}
@Override
public void renderHud(RenderScope scope) {
float health = minecraft.getPlayerHealth();
switch (displayMode.value) {
case 1:
health = health / 2;
break;
}
renderHealth(health, scope);
}
public void renderHealth(float health, RenderScope scope) {
String text = "";
switch (decimals.value) {
case 0:
text = String.format("%.0f ", health);
break;
case 1:
text = String.format("%.1f ", health);
break;
case 2:
text = String.format("%.2f ", health);
break;
}
scope.drawText(
text,
0,
0,
dimensions.font.value,
dimensions.fgColor.value
);
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
dimensions.height = 18 * text.split("\n").length;
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
}
@@ -1,156 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.modules.interfaces.KeystrokesInterface;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.RenderScope;
public class Keystrokes extends Module implements HudMod {
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);
private static final Property<Integer> clickBg = Property.color(0xFFCCCCCC);
private static final Property<Integer> clickFg = Property.color(0xFFFFFFFF);
private static final ModDimensions dimensions = new ModDimensions(78, 54);
private final KeystrokesInterface minecraft;
// 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(KeystrokesInterface minecraft) {
super(new ModuleDetails("Keystrokes", "keystrokes")
.description("Displays movement keystrokes")
.version("v0.2.0")
.tags("Utility"),
enabled.named("Enabled"),
showMouse.named("Show mouse clicks"),
showSpace.named("Show space"),
dimensions.prop(),
clickFg.named("Clicked fg"),
clickBg.named("Clicked bg"));
this.minecraft = minecraft;
dimensions.renderBackground = false;
}
@Override
public void tick() {
w = minecraft.isForwardPressed();
a = minecraft.isLeftPressed();
s = minecraft.isBackPressed();
d = minecraft.isRightPressed();
lmb = minecraft.isAttackPressed();
rmb = minecraft.isUsePressed();
space = minecraft.isJumpPressed();
updateHeight();
}
private void updateHeight() {
int height = KEY_SIZE * 2 + KEY_SPACING;
if (showSpace.value)
height += SPACE_HEIGHT + KEY_SPACING;
if (showMouse.value)
height += MOUSE_HEIGHT + KEY_SPACING;
dimensions.height = height;
}
@Override
public void renderDummy(RenderScope scope) {
render(scope, false);
}
@Override
public void renderHud(RenderScope scope) {
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, 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);
}
}
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;
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 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, MOUSE_WIDTH, MOUSE_HEIGHT, dimensions.radius.value, bg);
scope.drawText(0.6f, text, x + 9, y + 7, dimensions.font.value, fg);
}
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, dimensions.width, SPACE_HEIGHT, dimensions.radius.value, bg);
int lineY = y + SPACE_HEIGHT / 2;
scope.drawRect(x + (dimensions.width - 30) / 2, lineY, 30, 1, fg);
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
}
@@ -1,121 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.config.manager.Property;
import org.saturnclient.modules.interfaces.NametagsInterface;
public class Nametags extends Module {
private static Property<Boolean> enabled = Property.bool(false);
private static Property<Boolean> healthDisplay = Property.bool(false);
private static Property<Boolean> players = Property.bool(true);
private static Property<Boolean> hostile = Property.bool(false);
private static Property<Boolean> passive = Property.bool(false);
private static Property<Boolean> heartEmoji = Property.bool(true);
private static Property<Boolean> obfuscate = Property.bool(false);
private static Property<Integer> unit = Property.select(0, "Health", "Hearts");
private static Property<Integer> format = Property.select(0, "Value", "Value / Total", "%");
private static Property<Integer> nameColor = Property.select(15,
"Black","Dark Blue","Dark Green","Dark Aqua","Dark Red","Dark Purple","Gold","Gray",
"Dark Gray","Blue","Green","Aqua","Red","Light Purple","Yellow","White");
private static Property<Integer> healthColor = Property.select(12,
"Black","Dark Blue","Dark Green","Dark Aqua","Dark Red","Dark Purple","Gold","Gray",
"Dark Gray","Blue","Green","Aqua","Red","Light Purple","Yellow","White");
private static final String[] COLOR_CODES = {
"§0", "§1", "§2", "§3", "§4", "§5", "§6", "§7",
"§8", "§9", "§a", "§b", "§c", "§d", "§e", "§f"
};
public Nametags(NametagsInterface minecraft) {
super(new ModuleDetails("Nametags", "nametags")
.description("Allows you to modify entity nametags")
.tags("Camera", "Utility")
.version("0.1.0"),
enabled.named("Enabled"),
healthDisplay.named("Display health"),
players.named("Players"),
hostile.named("Hostile"),
passive.named("Passive"),
heartEmoji.named("Heart emoji"),
obfuscate.named("Obfuscate enemy names"),
unit.named("Unit"),
format.named("Format"),
nameColor.named("Name color"),
healthColor.named("Health color"));
}
@Override
public void tick() {
// Currently unused
}
public static String getHealthString(NametagsInterface.EntityState state, float health, float maxHealth) {
String ret = "";
switch (format.value) {
case 0: // value
ret = String.format("%.1f", health / (unit.value + 1));
break;
case 1: // value / total
ret = String.format("%.1f", health / (unit.value + 1)) + " / "
+ String.format("%.1f", maxHealth / (unit.value + 1));
break;
case 2: // %
ret = String.format("%.0f%%", (health / maxHealth) * 100);
break;
}
return ret;
}
public static String getNametagString(NametagsInterface.EntityState state) {
if (state.getCustomName() == null)
return null;
if (!healthDisplay.value)
return state.getCustomName(); // name only
float health = state.getHealth();
float maxHealth = state.getMaxHealth();
NametagsInterface.EntityType type = state.getEntityType();
if (type == NametagsInterface.EntityType.PASSIVE && !passive.value)
return null;
if (type == NametagsInterface.EntityType.HOSTILE && !hostile.value)
return null;
if (type == NametagsInterface.EntityType.PLAYER && !players.value)
return null;
if (type == NametagsInterface.EntityType.OTHER)
return null;
if (health < 0f || maxHealth < 0f || health > maxHealth)
return null;
String hpColor = COLOR_CODES[healthColor.value];
String color = COLOR_CODES[nameColor.value];
String heart = heartEmoji.value ? " ❤" : "";
String obf = obfuscate.value ? "§k" : "";
String reset = "§r";
return color + obf + state.getCustomName() + reset + " " + hpColor
+ getHealthString(state, health, maxHealth) + heart;
}
public static boolean shouldReplaceName() {
return enabled.value;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
@Override
public boolean isEnabled() {
return enabled.value;
}
}
@@ -1,39 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.config.manager.Property;
public class NoFog extends Module {
private static Property<Boolean> enabled = Property.bool(false);
private static Property<Boolean> liquids = Property.bool(false);
public NoFog() {
super(
new ModuleDetails("No Fog", "nofog")
.description("Stops fog from rendering")
.tags("Camera")
.version("v0.1.0"),
enabled.named("Enabled"),
liquids.named("See through liquids"));
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
// static for the mixin
public static boolean isActive() {
return enabled.value;
}
public static boolean liquids() {
return liquids.value;
}
}
@@ -1,59 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.config.manager.Property;
import org.saturnclient.modules.interfaces.PingInterface;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.Fonts;
public class Ping extends Module implements HudMod {
private static Property<Boolean> enabled = Property.bool(false);
private static ModDimensions dimensions = new ModDimensions(60, Fonts.getHeight());
private final PingInterface pingProvider;
public Ping(PingInterface pingProvider) {
super(new ModuleDetails("Ping Display", "ping")
.description("Displays ping")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
dimensions.prop());
this.pingProvider = pingProvider;
}
@Override
public void renderHud(RenderScope scope) {
int ping = pingProvider.getPing();
String text = ping + " ms";
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
}
@Override
public void renderDummy(RenderScope scope) {
String text = "10 ms";
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
}
@@ -1,96 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.config.manager.Property;
import org.saturnclient.modules.interfaces.SpeedometerInterface;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.Fonts;
public class Speedometer extends Module implements HudMod {
private static Property<Boolean> enabled = Property.bool(false);
private static Property<Integer> axis = Property.select(1, "Absolute", "X axis", "Y axis");
private static Property<Integer> unitText = Property.select(0, "None", "Blocks/s", "blocks/s", "b/s");
private static Property<Boolean> speedText = Property.bool(true);
private static ModDimensions dimensions = new ModDimensions(40, 18);
private final SpeedometerInterface provider;
private double speed = 0.;
public Speedometer(SpeedometerInterface provider) {
super(new ModuleDetails("Speedometer", "speed")
.description("Displays your current speed")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
axis.named("Speed type"),
unitText.named("Unit text"),
speedText.named("Show speed text"),
dimensions.prop());
this.provider = provider;
}
@Override
public void renderDummy(RenderScope scope) {
renderSpeed(2.71, scope);
}
@Override
public void renderHud(RenderScope scope) {
SpeedometerInterface.Velocity velocity = provider.getVelocity();
boolean onGround = provider.isOnGround();
double y = onGround ? 0.0 : velocity.y;
switch (axis.value) {
case 0: // Absolute
speed = Math.sqrt(Math.pow(velocity.x, 2) + Math.pow(y, 2) + Math.pow(velocity.z, 2));
break;
case 1: // X axis
speed = Math.sqrt(Math.pow(velocity.x, 2) + Math.pow(velocity.z, 2));
break;
case 2: // Y axis
speed = Math.abs(y);
break;
}
renderSpeed(speed * 20, scope); // blocks/tick to blocks/s
}
public void renderSpeed(double speed, RenderScope scope) {
String text = String.format("%.2f ", speed);
switch (unitText.value) {
case 1 -> text += "Blocks/s";
case 2 -> text += "blocks/s";
case 3 -> text += "b/s";
}
if (speedText.value) {
text = "Speed: " + text;
}
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
dimensions.height = 18 * text.split("\n").length;
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
}
@@ -1,86 +0,0 @@
package org.saturnclient.modules.mods;
import java.util.List;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.modules.interfaces.StatusEffectsInterface;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.RenderScope;
public class StatusEffects extends Module implements HudMod {
public static Property<Boolean> enabled = Property.bool(false);
private static ModDimensions dimensions = new ModDimensions(60, 0);
private final StatusEffectsInterface provider;
public StatusEffects(StatusEffectsInterface provider) {
super(new ModuleDetails("Status effects", "effect")
.description("Displays status effects")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
dimensions.prop());
this.provider = provider;
}
private void renderEffects(RenderScope scope, List<StatusEffectsInterface.Effect> effects) {
int row = 0;
for (StatusEffectsInterface.Effect effect : effects) {
if (!effect.shouldShowIcon()) continue;
scope.drawSpriteStretched(effect.getIcon(), 0, 18 * row, 16, 16);
scope.drawText(0.5f, getDurationAsString(effect), 18, 18 * row + 3, dimensions.font.value, -1);
row++;
}
dimensions.height = 18 * row;
}
@Override
public void renderHud(RenderScope scope) {
renderEffects(scope, provider.getActiveEffects());
}
@Override
public void renderDummy(RenderScope scope) {
renderEffects(scope, provider.getDummyEffects());
}
private String getDurationAsString(StatusEffectsInterface.Effect effect) {
if (effect.isInfinite()) {
return effect.getInfiniteText();
}
int totalSeconds = effect.getDurationSeconds();
if (totalSeconds >= 3600) {
return totalSeconds / 3600 + "h";
} else if (totalSeconds >= 60) {
int minutes = totalSeconds / 60;
int seconds = totalSeconds % 60;
return String.format("%d:%02d", minutes, seconds);
} else {
return totalSeconds + "s";
}
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
}
@@ -1,100 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.HudMod;
import org.saturnclient.modules.ModDimensions;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.Fonts;
public class Tps extends Module implements HudMod {
private static Property<Boolean> enabled = Property.bool(false);
private static ModDimensions dimensions = new ModDimensions(60, Fonts.getHeight());
private static long lastWorldAge = -1;
private static long lastRealTime = -1;
private static double currentTps = 20.0;
private static final int SAMPLE_COUNT = 2;
private static final double[] samples = new double[SAMPLE_COUNT];
static {
java.util.Arrays.fill(samples, 20.0);
}
private static int sampleIndex = 0;
private static int samplesFilled = 0;
public Tps() {
super(new ModuleDetails("TPS Display", "tps")
.description("Displays the TPS")
.version("v0.1.0")
.tags("Utility"),
enabled.named("Enabled"),
dimensions.prop());
}
public static void onTimePacket(long worldAge) {
long now = System.currentTimeMillis();
if (lastWorldAge != -1 && lastRealTime != -1) {
long tickDelta = worldAge - lastWorldAge;
long timeDelta = now - lastRealTime;
if (timeDelta > 0 && tickDelta > 0) {
double sample = (tickDelta / (double) timeDelta) * 1000.0;
sample = Math.min(sample, 20.0);
samples[sampleIndex % SAMPLE_COUNT] = sample;
sampleIndex++;
samplesFilled = Math.min(samplesFilled + 1, SAMPLE_COUNT);
double sum = 0;
for (int i = 0; i < samplesFilled; i++)
sum += samples[i];
currentTps = sum / samplesFilled;
}
}
lastWorldAge = worldAge;
lastRealTime = now;
}
public static void reset() {
lastWorldAge = -1;
lastRealTime = -1;
currentTps = 20.0;
sampleIndex = 0;
samplesFilled = SAMPLE_COUNT;
for (int i = 0; i < SAMPLE_COUNT; i++)
samples[i] = 20.0;
}
@Override
public void renderHud(RenderScope scope) {
String text = String.format("%.1f TPS", currentTps);
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
}
@Override
public void renderDummy(RenderScope scope) {
String text = "20.0 TPS";
scope.drawText(text, 0, 0, dimensions.font.value, dimensions.fgColor.value);
dimensions.width = Fonts.getWidth(text, dimensions.font.value);
}
@Override
public boolean isEnabled() {
return enabled.value;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
}
@Override
public ModDimensions getDimensions() {
return dimensions;
}
}
@@ -1,59 +0,0 @@
package org.saturnclient.modules.mods;
import org.saturnclient.modules.Module;
import org.saturnclient.modules.ModuleDetails;
import org.saturnclient.config.manager.Key;
import org.saturnclient.config.manager.Property;
public class Zoom extends Module {
private static Property<Boolean> enabled = Property.bool(false);
private static Property<Boolean> toggle = Property.bool(false);
public static Property<Integer> zoomKey = Property.keybinding(Key.GLFW_KEY_C);
public static Property<Float> zoomLevel = Property.floatProp(3.0f);
public static boolean isZooming = false;
public Zoom() {
super(
new ModuleDetails("Zoom", "zoom")
.description("Allows you to zoom in for a closer view")
.tags("Camera", "Utility")
.version("0.1.0"),
enabled.named("Enabled"),
toggle.named("Toggle zoom"),
zoomKey.named("Zoom Keybinding"),
zoomLevel.named("Zoom Level"));
}
@Override
public void tick() {
if (toggle.value) {
if (zoomKey.wasKeyPressed()) {
isZooming = !isZooming;
}
} else {
isZooming = zoomKey.isKeyPressed();
}
}
public static float getZoomLevel() {
return zoomLevel.value;
}
public static boolean shouldZoom() {
return enabled.value;
}
@Override
public void onEnabled(boolean e) {
enabled.value = e;
if (!e) {
isZooming = false;
}
}
@Override
public boolean isEnabled() {
return enabled.value;
}
}
@@ -1,89 +0,0 @@
package org.saturnclient.ui;
import org.saturnclient.ui.anim.Animation;
public class Element {
public int x;
public int y;
public int width;
public int height;
public float opacity = 1.0f;
public boolean focused;
public Animation animation;
public float scale = 1.0f;
public Integer duration = null; // Element duration, will fade out if not null
public void render(RenderScope renderScope, ElementContext ctx) {
}
public void click(int mouseX, int mouseY) {
}
public void scroll(int mouseX, int mouseY, double horizontalAmount, double verticalAmount) {
}
public void keyPressed(int keyCode, int scanCode, int modifiers) {
}
public void charTyped(char typedChar) {
}
public 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.animation.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;
return this;
}
public Element position(int x, int y) {
this.x = x;
this.y = y;
return this;
}
public Element centerHorizontal(int w, int h, int offsetX, int offsetY) {
this.x = (w - (int) (width * scale)) / 2 + offsetX;
this.y = offsetY;
return this;
}
public Element center(int w, int h) {
this.centerOffset(w, h, 0, 0);
return this;
}
public Element centerOffset(int w, int h, int offsetX, int offsetY) {
this.x = (w - (int) (width * scale)) / 2 + offsetX;
this.y = (h - (int) (height * scale)) / 2 + offsetY;
return this;
}
public Element animation(Animation animation) {
this.animation = animation;
return this;
}
public Element scale(float scale) {
this.scale = scale;
return this;
}
}
@@ -1,25 +0,0 @@
package org.saturnclient.ui;
public class ElementContext {
public int mouseX, mouseY;
public int elementWidth, elementHeight;
public float elementScale;
public long elapsed;
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() {
return Utils.isHovering(mouseX, mouseY, elementWidth, elementHeight, elementScale);
}
public boolean isHovering(int x, int y, int width, int height) {
return Utils.isHovering(mouseX - x, mouseY - y, width, height, elementScale);
}
}
@@ -1,27 +0,0 @@
package org.saturnclient.ui;
import java.util.List;
public abstract class ElementRenderer {
public static ElementRenderer INSTANCE;
public abstract void draw(List<Element> elements, Element element);
public abstract void render(List<Element> elements, long elapsed, RenderScope renderScope, int mouseX, int mouseY);
public abstract void mouseClicked(List<Element> elements, double mouseX, double mouseY, int button);
public abstract void mouseScrolled(
List<Element> elements,
double mouseX,
double mouseY,
double horizontalAmount,
double verticalAmount);
public abstract void keyPressed(List<Element> elements, int keyCode, int scanCode, int modifiers);
public abstract void mouseDragged(List<Element> elements, double mouseX, double mouseY, int button, double deltaX,
double deltaY);
public abstract void mouseReleased(List<Element> elements, double mouseX, double mouseY, int button);
}
@@ -1,67 +0,0 @@
package org.saturnclient.ui;
import org.saturnclient.common.minecraft.bindings.SaturnIdentifier;
import org.saturnclient.common.minecraft.bindings.SaturnItemStack;
import org.saturnclient.common.minecraft.bindings.SaturnSprite;
import org.saturnclient.common.minecraft.render.IMatrixStack;
public interface RenderScope {
public IMatrixStack getMatrixStack();
public void setOpacity(float alpha);
public int getColor(int color);
public void drawRect(int x, int y, int width, int height, int color);
public void drawText(String text, int x, int y, int font, int color);
public void drawText(float scale, String text, int x, int y, int font, int color);
public void drawRoundedRectangle(int x, int y, int width, int height, int radius, int color);
public int getScaledWindowWidth();
public int getScaledWindowHeight();
/* Saturn common sprites */
public void drawTexture(SaturnIdentifier sprite, int x, int y, float u, float v, int width, int height, int color);
public void drawTexture(SaturnIdentifier sprite, int x, int y, float u, float v, int width, int height);
public void drawTexture(SaturnIdentifier sprite, int x, int y, float u, float v, int width, int height,
int regionWith,
int regionHeight, int textureWidth, int textureHeight);
public void drawTexture(SaturnIdentifier sprite, int x, int y, float u, float v, int width, int height,
int regionWidth,
int regionHeight, int textureWidth, int textureHeight, int color);
public void enableScissor(int x1, int y1, int x2, int y2);
public void disableScissor();
public boolean scissorContains(int x, int y);
public void draw();
public void drawItem(SaturnItemStack item, int x, int y);
public void drawItem(SaturnItemStack stack, int x, int y, int seed);
public void drawItem(SaturnItemStack stack, int x, int y, int seed, int z);
public void drawItemWithoutEntity(SaturnItemStack stack, int x, int y);
public void drawItemWithoutEntity(SaturnItemStack stack, int x, int y, int seed);
public void drawSpriteStretched(SaturnSprite sprite, int x, int y, int width, int height);
public void drawSpriteStretched(SaturnSprite sprite, int x, int y, int width, int height, int color);
public void fill(int x1, int y1, int x2, int y2, int color);
public void fill(int x1, int y1, int x2, int y2, int z, int color);
public void drawBorder(int x, int y, int width, int height, int color);
}
@@ -1,121 +0,0 @@
package org.saturnclient.ui;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
public abstract class SaturnScreen {
public static interface ScreenProvider {
public void close();
public int getWidth();
public int getHeight();
}
// General
public int width;
public int height;
public String title;
// Saturn Screen
public List<Element> elements = new ArrayList<>();
public float backgroundOpacity = 1.0f;
protected Instant start = null;
public int backgroundBlur = 10;
// Provider
public ScreenProvider provider;
public SaturnScreen(String title) {
this.title = title;
}
protected void init() {
elements.clear();
width = provider.getWidth();
height = provider.getHeight();
ui(); // abstraction to render the saturn ui and also render extra stuff here
}
public abstract void ui();
public void draw(Element element) {
ElementRenderer.INSTANCE.draw(elements, element);
}
// --------------
// Rendering
// --------------
public void render(RenderScope renderScope, int mouseX, int mouseY, float delta, long elapsed) {
renderScope.getMatrixStack().push();
renderScope.getMatrixStack().scale(0.5f, 0.5f, 0.5f);
ElementRenderer.INSTANCE.render(new ArrayList<>(elements), elapsed, renderScope, mouseX, mouseY);
renderScope.getMatrixStack().pop();
}
// --------------
// Events
// --------------
public boolean mouseClicked(double mouseX, double mouseY, int button) {
mouseX *= 2;
mouseY *= 2;
ElementRenderer.INSTANCE.mouseClicked(elements, mouseX, mouseY, button);
return false;
}
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
mouseX *= 2;
mouseY *= 2;
ElementRenderer.INSTANCE.mouseDragged(elements, mouseX, mouseY, button, deltaX, deltaY);
return false;
}
public boolean mouseReleased(double mouseX, double mouseY, int button) {
mouseX *= 2;
mouseY *= 2;
ElementRenderer.INSTANCE.mouseReleased(elements, mouseX, mouseY, button);
return false;
}
public boolean mouseScrolled(
double mouseX,
double mouseY,
double horizontalAmount,
double verticalAmount) {
mouseX *= 2;
mouseY *= 2;
ElementRenderer.INSTANCE.mouseScrolled(elements, mouseX, mouseY, horizontalAmount, verticalAmount);
return false;
}
public void resize(int width, int height) {
init();
}
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
ElementRenderer.INSTANCE.keyPressed(elements, keyCode, scanCode, modifiers);
return false;
}
public boolean shouldPause() {
return true;
}
public void onClose() {
}
}
@@ -1,62 +0,0 @@
package org.saturnclient.ui;
import java.util.List;
import org.saturnclient.config.manager.Key;
public class Utils {
public static boolean isHovering(int mouseX, int mouseY, int elementWidth, int elementHeight, float elementScale) {
return mouseX >= 0 &&
mouseX <= (elementWidth * elementScale) &&
mouseY >= 0 &&
mouseY <= (elementHeight * elementScale);
}
public static char getCharFromKey(int keyCode, int modifiers) {
boolean shift = (modifiers & Key.GLFW_MOD_SHIFT) != 0;
// A-Z
if (keyCode >= Key.GLFW_KEY_A && keyCode <= Key.GLFW_KEY_Z) {
return (char) (shift
? 'A' + (keyCode - Key.GLFW_KEY_A)
: 'a' + (keyCode - Key.GLFW_KEY_A));
}
// 0-9
if (keyCode >= Key.GLFW_KEY_0 && keyCode <= Key.GLFW_KEY_9) {
return shift
? ")!@#$%^&*(".charAt(keyCode - Key.GLFW_KEY_0)
: (char) ('0' + (keyCode - Key.GLFW_KEY_0));
}
// Special characters
return switch (keyCode) {
case Key.GLFW_KEY_SPACE -> ' ';
case Key.GLFW_KEY_PERIOD -> shift ? '>' : '.';
case Key.GLFW_KEY_COMMA -> shift ? '<' : ',';
case Key.GLFW_KEY_MINUS -> shift ? '_' : '-';
case Key.GLFW_KEY_EQUAL -> shift ? '+' : '=';
case Key.GLFW_KEY_SEMICOLON -> shift ? ':' : ';';
case Key.GLFW_KEY_APOSTROPHE -> shift ? '"' : '\'';
case Key.GLFW_KEY_SLASH -> shift ? '?' : '/';
case Key.GLFW_KEY_BACKSLASH -> shift ? '|' : '\\';
case Key.GLFW_KEY_LEFT_BRACKET -> shift ? '{' : '[';
case Key.GLFW_KEY_RIGHT_BRACKET -> shift ? '}' : ']';
case Key.GLFW_KEY_GRAVE_ACCENT -> shift ? '~' : '`';
default -> '\0';
};
}
public static <T> T getOrNull(List<T> list, int index) {
return (index >= 0 && index < list.size()) ? list.get(index) : null;
}
// public static void notify(NotificationKind kind, String title, String toast) {
// if (SaturnClient.client.currentScreen instanceof SaturnScreen) {
// ((SaturnScreen) SaturnClient.client.currentScreen)
// .draw(new Notification(SaturnClient.client.currentScreen.width,
// SaturnClient.client.currentScreen.height, kind,
// title, toast));
// }
// }
}
@@ -1,37 +0,0 @@
package org.saturnclient.ui.anim;
import java.util.function.Function;
import org.saturnclient.config.AnimationConfig;
import org.saturnclient.ui.Element;
public abstract class Animation {
public int duration;
public int delay = 0;
public Function<Double, Double> curve = Curve::easeInOutCubic;
Animation(AnimationConfig config) {
this.duration = config.duration.value;
switch (config.curve.value) {
case 0:
this.curve = Curve::easeInOutCubic;
break;
case 1:
this.curve = Curve::easeOutCubic;
break;
case 2:
this.curve = Curve::easeInOutBack;
break;
default:
break;
}
}
Animation(int duration) {
this.duration = duration;
}
public abstract void tick(double progress, Element element);
public abstract void init(Element element);
}
@@ -1,22 +0,0 @@
package org.saturnclient.ui.anim;
public class Curve {
public static double easeOutCubic(double x) {
return 1 - Math.pow(1 - x, 3);
}
public static double easeInOutCubic(double x) {
return (x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2);
}
public static double easeInOutBack(double x) {
double c1 = 2.5;
double c2 = c1 * 1.525;
if (x < 0.5) {
return (Math.pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2;
} else {
return (Math.pow(2 * x - 2, 2) * ((c2 + 1) * (2 * x - 2) + c2) + 2) / 2;
}
}
}
@@ -1,27 +0,0 @@
package org.saturnclient.ui.anim;
import org.saturnclient.config.AnimationConfig;
import org.saturnclient.ui.Element;
public class Fade extends Animation {
float targetOpacity;
public Fade(int duration) {
super(duration);
}
public Fade(AnimationConfig config) {
super(config);
}
@Override
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.1f;
}
}
@@ -1,27 +0,0 @@
package org.saturnclient.ui.anim;
import org.saturnclient.config.AnimationConfig;
import org.saturnclient.ui.Element;
public class FadeOut extends Animation {
float targetOpacity;
public FadeOut(int duration) {
super(duration);
}
public FadeOut(AnimationConfig config) {
super(config);
}
@Override
public void tick(double progress, Element element) {
element.opacity = targetOpacity - (float) progress;
}
@Override
public void init(Element element) {
targetOpacity = element.opacity;
// element.opacity = 1.0f;
}
}
@@ -1,39 +0,0 @@
package org.saturnclient.ui.anim;
import org.saturnclient.config.AnimationConfig;
import org.saturnclient.ui.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);
}
public SlideFade(AnimationConfig config, int offset) {
super(config);
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);
}
}
@@ -1,39 +0,0 @@
package org.saturnclient.ui.anim;
import org.saturnclient.config.AnimationConfig;
import org.saturnclient.ui.Element;
public class SlideX extends Animation {
int offset = 20;
double startX;
public SlideX(AnimationConfig config) {
super(config);
}
public SlideX(AnimationConfig config, int offset) {
super(config);
this.offset = offset;
}
public SlideX(int duration) {
super(duration);
}
public SlideX(int duration, int offset) {
super(duration);
this.offset = offset;
}
@Override
public void init(Element element) {
startX = element.x - offset;
element.x = (int) startX;
}
@Override
public void tick(double progress, Element element) {
double currentX = startX + offset * progress;
element.x = (int) currentX;
}
}
@@ -1,39 +0,0 @@
package org.saturnclient.ui.anim;
import org.saturnclient.config.AnimationConfig;
import org.saturnclient.ui.Element;
public class SlideY extends Animation {
int offset = 20;
double startY;
public SlideY(AnimationConfig config) {
super(config);
}
public SlideY(AnimationConfig config, int offset) {
super(config);
this.offset = offset;
}
public SlideY(int duration) {
super(duration);
}
public SlideY(int duration, int offset) {
super(duration);
this.offset = offset;
}
@Override
public void init(Element element) {
startY = element.y - offset;
element.y = (int) startY;
}
@Override
public void tick(double progress, Element element) {
double currentY = startY + offset * progress;
element.y = (int) currentY;
}
}
@@ -1,43 +0,0 @@
package org.saturnclient.ui.components;
import org.saturnclient.common.minecraft.bindings.SaturnIdentifier;
import org.saturnclient.config.Theme;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
public class CosmeticPreview extends Element {
static int padding = 14;
SaturnIdentifier sprite;
private Runnable onClick;
private boolean isSelected;
public CosmeticPreview(boolean isSelected, SaturnIdentifier sprite, Runnable onClick) {
this.sprite = sprite;
this.onClick = onClick;
this.isSelected = isSelected;
this.width = 50;
this.height = 111;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
isSelected ? Theme.ACCENT.value : Theme.PRIMARY.value);
int texWidth = width - padding;
int texHeight = height - padding;
renderScope.drawTexture(sprite, padding / 2, padding / 2, 0, 0, texWidth, texHeight,
Theme.FOREGROUND.value);
}
@Override
public void click(int mouseX, int mouseY) {
if (onClick != null) {
onClick.run();
}
}
}
@@ -1,98 +0,0 @@
package org.saturnclient.ui.components;
import java.util.ArrayList;
import java.util.List;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.ElementRenderer;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.Utils;
public class ElementRendererImpl extends ElementRenderer {
public void draw(List<Element> elements, Element element) {
synchronized (elements) {
elements.add(element);
}
if (element.animation != null) {
element.animation.init(element);
}
}
public void render(List<Element> elements, long elapsed, RenderScope renderScope, int mouseX, int mouseY) {
for (Element element : elements) {
element.playAnimationFrame(elapsed);
renderScope.getMatrixStack().push();
renderScope.setOpacity(element.opacity);
renderScope.getMatrixStack().translate(element.x, element.y, 0);
renderScope.getMatrixStack().scale(element.scale, element.scale, 1.0f);
element.render(renderScope, new ElementContext(elapsed, mouseX, mouseY, element));
renderScope.getMatrixStack().pop();
}
}
public void mouseClicked(List<Element> elements, double mouseX, double mouseY, int button) {
if (button == 0) {
for (Element element : new ArrayList<>(elements)) {
element.focused = false;
int adjustedMouseX = (int) mouseX - element.x;
int adjustedMouseY = (int) mouseY - element.y;
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));
}
}
}
}
public void mouseScrolled(
List<Element> elements,
double mouseX,
double mouseY,
double horizontalAmount,
double verticalAmount) {
for (Element element : new ArrayList<>(elements)) {
if (Utils.isHovering((int) mouseX - element.x, (int) mouseY - element.y, element.width, element.height,
element.scale)) {
element.scroll(
(int) mouseX - element.x,
(int) mouseY - element.y,
horizontalAmount,
verticalAmount);
}
}
}
public void keyPressed(List<Element> elements, int keyCode, int scanCode, int modifiers) {
for (Element element : new ArrayList<>(elements)) {
if (element.focused && element.opacity > 0) {
element.keyPressed(keyCode, scanCode, modifiers);
char typedChar = Utils.getCharFromKey(keyCode, modifiers);
if (typedChar != '\0') {
element.charTyped(typedChar);
}
}
}
}
public 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 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);
}
}
}
@@ -1,161 +0,0 @@
package org.saturnclient.ui.components;
import org.saturnclient.modules.Module;
import org.saturnclient.common.minecraft.MinecraftProvider;
import org.saturnclient.config.Theme;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.Utils;
import org.saturnclient.ui.resources.Fonts;
import org.saturnclient.ui.resources.Textures;
import org.saturnclient.ui.screens.ConfigEditor;
public class SaturnModule extends Element {
private final 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;
this.width = 160;
this.height = 50;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
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());
// Resolve theme colors
int bgColor = Theme.PRIMARY.value;
int fgColor = hovered && !settingsHover ? Theme.ACCENT.value : Theme.PRIMARY_FG.value;
int iconBg = enabled ? Theme.ACCENT.value : Theme.PRIMARY.value;
int iconFg = enabled ? Theme.ACCENT_FG.value : fgColor;
int settingsColor = settingsHover
? Theme.ACCENT.value
: Theme.withAlpha(160, fgColor);
int h = height - (p * 2);
// Main background
renderScope.drawRoundedRectangle(
0, 0,
width, height,
Theme.WIDGET_RADIUS.value,
bgColor);
// Icon background
renderScope.drawRoundedRectangle(
p, p,
h, h,
Theme.WIDGET_RADIUS.value,
iconBg);
// Icon
renderScope.drawTexture(
mod.getIconTexture(),
p + (p / 2),
p + (p / 2),
0, 0,
h - p,
h - p,
iconFg);
// 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 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;
}
// Settings icon
renderScope.drawTexture(
Textures.SETTINGS,
iconX,
iconY,
0, 0,
s, s,
settingsColor);
}
@Override
public void click(int mouseX, int mouseY) {
boolean settingsHover = Utils.isHovering(
mouseX - getSettingsX(),
mouseY - getSettingsY(),
getSettingsHitBox(),
getSettingsHitBox(),
1.0f);
if (settingsHover) {
MinecraftProvider.PROVIDER.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,121 +0,0 @@
package org.saturnclient.ui.components;
import org.saturnclient.common.minecraft.MinecraftProvider;
import org.saturnclient.common.minecraft.bindings.SaturnIdentifier;
import org.saturnclient.config.Config;
import org.saturnclient.config.Theme;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.Utils;
import org.saturnclient.ui.resources.Textures;
import org.saturnclient.ui.screens.ConfigEditor;
import org.saturnclient.ui.screens.ModMenu;
import org.saturnclient.ui.screens.cosmetics.CloakMenu;
import org.saturnclient.ui.screens.store.CloakStore;
public class Sidebar extends Element {
public static class SidebarComponent {
Runnable onClick;
boolean end;
SaturnIdentifier sprite;
public SidebarComponent(SaturnIdentifier sprite, Runnable onClick, boolean end) {
this.onClick = onClick;
this.end = end;
this.sprite = sprite;
}
}
Runnable onClose;
SidebarComponent[] components = {
new Sidebar.SidebarComponent(Textures.MODS_TAB, () -> {
MinecraftProvider.PROVIDER.setScreen(new ModMenu());
}, false),
new Sidebar.SidebarComponent(Textures.SETTINGS, () -> {
MinecraftProvider.PROVIDER.setScreen(new ConfigEditor(Config.config));
}, false),
new Sidebar.SidebarComponent(Textures.SHIRT, () -> {
MinecraftProvider.PROVIDER.setScreen(new CloakMenu());
}, false),
new Sidebar.SidebarComponent(Textures.CLOSE, () -> {
onClose.run();
}, true),
new Sidebar.SidebarComponent(Textures.STORE, () -> {
MinecraftProvider.PROVIDER.setScreen(new CloakStore());
}, true),
};
int active = 0;
public Sidebar(int active, Runnable onClose) {
this.width = 30;
this.height = 350;
this.active = active;
this.onClose = onClose;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
int topRow = 0;
int bottomRow = 0;
int gap = 5; // 5px gap between icons
int padding = 10;
int sp = 30 - padding + gap;
int s = 30 - (padding * 2);
int s2 = s + padding;
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) {
bottomRow += 1;
} else {
topRow += 1;
}
i++;
}
}
@Override
public void click(int mouseX, int mouseY) {
int topRow = 0;
int bottomRow = 0;
int gap = 5;
int padding = 10;
int sp = 30 - padding + gap;
int s = 30 - (padding * 2);
int s2 = s + padding;
for (SidebarComponent component : components) {
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 {
topRow += 1;
}
}
}
}
@@ -1,26 +0,0 @@
package org.saturnclient.ui.components;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
public class SkinPreview extends Element {
public static EntityDrawer DRAWER;
private boolean negativeAngle = false;
private float angle = 0.0f;
public SkinPreview(float angle, boolean negativeAngle) {
this.angle = angle;
this.negativeAngle = negativeAngle;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
DRAWER.render(renderScope, negativeAngle, angle, scale, ctx);
}
public static interface EntityDrawer {
public void render(RenderScope renderScope, boolean negativeAngle, float angle, float scale,
ElementContext ctx);
}
}
@@ -1,59 +0,0 @@
package org.saturnclient.ui.components.inputs;
import org.saturnclient.config.manager.ConfigManager;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.resources.Fonts;
public class FloatInput extends Input {
public Property<Float> prop;
public FloatInput(Property<Float> prop) {
this.width = 120;
this.height = Fonts.getHeight();
this.prop = prop;
this.text = String.valueOf(prop.value);
}
@Override
public void charTyped(char chr) {
if ((Character.isDigit(chr) || chr == '.' || (chr == '-' && cursorPosition == 0)) && text.length() < 10) {
if (text.equals("0") && cursorPosition == 1) {
text = "" + chr;
} else {
text = text.substring(0, cursorPosition) +
chr +
text.substring(cursorPosition);
try {
prop.value = text.isEmpty() ? 0 : Float.parseFloat(text);
cursorPosition++;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@Override
public void backspace() {
try {
if (text.length() == 0) {
text = "0";
cursorPosition = 1;
}
prop.value = Float.parseFloat(text);
ConfigManager.save();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void checkReset() {
if (prop.isReset) {
this.text = String.valueOf(prop.value);
this.cursorPosition = 0;
prop.isReset = false;
}
}
}
@@ -1,183 +0,0 @@
package org.saturnclient.ui.components.inputs;
import java.awt.Color;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
public class HexInput extends Element {
public Property<Integer> prop;
// 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;
syncFromColor();
}
@Override
public void click(int mouseX, int mouseY) {
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) {
// 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;
}
}
@@ -1,112 +0,0 @@
package org.saturnclient.ui.components.inputs;
import org.saturnclient.config.Theme;
import org.saturnclient.config.manager.Key;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.Fonts;
public abstract class Input extends Element {
String text = "";
public int cursorPosition = 0;
public abstract void checkReset();
public abstract void backspace();
public boolean validateBackspace() {
return true;
}
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
if (keyCode == Key.GLFW_KEY_BACKSPACE) {
if (cursorPosition > 0 && validateBackspace()) {
text = text.substring(0, cursorPosition - 1) +
text.substring(cursorPosition);
cursorPosition--;
backspace();
}
} else if (keyCode == Key.GLFW_KEY_LEFT) {
if (cursorPosition > 0) {
cursorPosition--;
}
} else if (keyCode == Key.GLFW_KEY_RIGHT) {
if (cursorPosition < text.length()) {
cursorPosition++;
}
}
}
@Override
public void click(int mouseX, int mouseY) {
int scrollOffset = getScrollOffset();
String visibleText = text.substring(scrollOffset);
int newCursorPos = scrollOffset;
float textWidth = 0;
for (int i = 0; i < visibleText.length(); i++) {
textWidth = Fonts.getWidth(visibleText.substring(0, i + 1), Theme.FONT.value) * 0.6f;
if (textWidth > mouseX) {
break;
}
newCursorPos++;
}
cursorPosition = newCursorPos;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
checkReset();
renderScope.drawRoundedRectangle(0, 0, width, height, 10, 0xFF000000);
int scrollOffset = getScrollOffset();
String visibleText = getVisibleText(scrollOffset);
int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
renderScope.drawText(0.6f, visibleText, 4, 4, Theme.FONT.value, textColor);
if (focused) {
int cursorX = 4 +
(int) (Fonts.getWidth(visibleText.substring(0, cursorPosition - scrollOffset), Theme.FONT.value)
* 0.6f);
renderScope.drawRect(cursorX, 2, 1, height - 4, 0xFFFFFFFF);
}
}
protected int getScrollOffset() {
if (text.isEmpty()) {
return 0;
}
int offset = 0;
int maxWidth = width - 4; // Available width for text
while (offset < cursorPosition) { // Ensure cursor remains visible
String visibleText = text.substring(offset, cursorPosition);
if (Fonts.getWidth(visibleText, Theme.FONT.value) > maxWidth) {
offset++;
} else {
break;
}
}
return offset;
}
protected String getVisibleText(int scrollOffset) {
String visibleText = text.substring(scrollOffset);
// Ensure text does not overflow past the available width
for (int i = 1; i <= visibleText.length(); i++) {
if (Fonts.getWidth(visibleText.substring(0, i), Theme.FONT.value) > width - 4) {
return visibleText.substring(0, i - 1); // Trim overflow
}
}
return visibleText;
}
}
@@ -1,59 +0,0 @@
package org.saturnclient.ui.components.inputs;
import org.saturnclient.config.manager.ConfigManager;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.resources.Fonts;
public class IntInput extends Input {
public Property<Integer> prop;
public IntInput(Property<Integer> prop) {
this.width = 120;
this.height = Fonts.getHeight();
this.prop = prop;
this.text = String.valueOf(prop.value);
}
@Override
public void charTyped(char chr) {
if ((Character.isDigit(chr) || chr == '.' || (chr == '-' && cursorPosition == 0)) && text.length() < 10) {
if (text.equals("0") && cursorPosition == 1) {
text = "" + chr;
} else {
text = text.substring(0, cursorPosition) +
chr +
text.substring(cursorPosition);
try {
prop.value = text.isEmpty() ? 0 : Integer.parseInt(text);
cursorPosition++;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@Override
public void backspace() {
try {
if (text.length() == 0) {
text = "0";
cursorPosition = 1;
}
prop.value = Integer.parseInt(text);
ConfigManager.save();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void checkReset() {
if (prop.isReset) {
this.text = String.valueOf(prop.value);
this.cursorPosition = 0;
prop.isReset = false;
}
}
}
@@ -1,42 +0,0 @@
package org.saturnclient.ui.components.inputs;
import org.saturnclient.common.minecraft.MinecraftProvider;
import org.saturnclient.config.Theme;
import org.saturnclient.config.manager.Key;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.Fonts;
public class KeybindingSelector extends Element {
Property<Integer> prop;
public KeybindingSelector(Property<Integer> prop) {
this.prop = prop;
this.width = 120;
this.height = Fonts.getHeight();
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
int textColor = focused ? 0xFFFFFF : 0xAAAAAA;
renderScope.drawRoundedRectangle(0, 0, width, height, 10, 0xFF000000);
String name = MinecraftProvider.PROVIDER.getKeyName(prop.value);
renderScope.drawText(0.6f,
prop.value == -1 ? "<NONE>"
: name != null ? name.toUpperCase() : "<Invalid-Key>",
4, 4, Theme.FONT.value, textColor);
}
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
if (keyCode == Key.GLFW_KEY_BACKSPACE) {
prop.value = -1;
} else {
prop.value = keyCode;
}
focused = false;
}
}
@@ -1,40 +0,0 @@
package org.saturnclient.ui.components.inputs;
import org.saturnclient.config.Theme;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.Utils;
import org.saturnclient.ui.resources.Fonts;
public class Select extends Element {
private Property<Integer> prop;
public Select(Property<Integer> prop) {
this.prop = prop;
this.width = 150;
this.height = 15;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.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, Theme.FONT.value) * 0.6f)) / 2, 1,
Theme.FONT.value, Theme.FOREGROUND.value);
renderScope.drawText(0.8f, ">", width - 10, 0, Theme.FONT.value, Theme.FOREGROUND.value);
}
@Override
public void click(int mouseX, int mouseY) {
if (Utils.isHovering(mouseX - (width - 10), mouseY, 10, height, 1.0f)) {
prop.next();
} else if (Utils.isHovering(mouseX, mouseY, 10, height, 1.0f)) {
prop.prev();
}
}
}
@@ -1,30 +0,0 @@
package org.saturnclient.ui.components.inputs;
import org.saturnclient.config.Theme;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
public class Toggle extends Element {
private Property<Boolean> prop;
public Toggle(Property<Boolean> prop) {
this.prop = prop;
this.width = 60;
this.height = 30;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
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
public void click(int mouseX, int mouseY) {
prop.value = !prop.value;
}
}
@@ -1,66 +0,0 @@
package org.saturnclient.ui.elements;
import java.util.ArrayList;
import java.util.List;
import org.saturnclient.config.AnimationConfig;
import org.saturnclient.config.Config;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.ElementRenderer;
import org.saturnclient.ui.RenderScope;
public class AnimationStagger extends Element {
protected List<Element> children = new ArrayList<>();
public int delay;
public AnimationStagger(int delay) {
this.delay = delay;
}
public AnimationStagger(AnimationConfig config) {
this.delay = config.stagger.value;
}
public void draw(Element element) {
if (!Config.stagger.value) {
element.animation = null;
}
if (element.animation != null) {
element.animation.delay = delay * children.size();
}
ElementRenderer.INSTANCE.draw(children, element);
this.height = Math.max(this.height, element.y + element.height);
this.width = Math.max(this.width, element.x + element.width);
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
ElementRenderer.INSTANCE.render(children, ctx.elapsed, renderScope,
ctx.mouseX,
ctx.mouseY);
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
ElementRenderer.INSTANCE.mouseClicked(children, mouseX, mouseY, button);
}
@Override
public void keyPressed(int keyCode, int scanCode, int modifiers) {
ElementRenderer.INSTANCE.keyPressed(children, keyCode, scanCode, modifiers);
}
@Override
public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
ElementRenderer.INSTANCE.mouseDragged(children, mouseX, mouseY, button, deltaX, deltaY);
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {
ElementRenderer.INSTANCE.mouseReleased(children, mouseX, mouseY, button);
}
}
@@ -1,35 +0,0 @@
package org.saturnclient.ui.elements;
import org.saturnclient.config.Theme;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.Fonts;
public class Button extends Element {
private String text;
private Runnable onClick;
public Button(String text, Runnable onClick) {
this.text = text;
this.onClick = onClick;
this.dimensions(Fonts.getWidth(text, Theme.FONT.value) + 50, 38);
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
ctx.isHovering() ? Theme.ACCENT.value : Theme.PRIMARY.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
public void click(int mouseX, int mouseY) {
if (onClick != null) {
onClick.run();
}
}
}
@@ -1,30 +0,0 @@
package org.saturnclient.ui.elements;
import org.saturnclient.common.minecraft.bindings.SaturnIdentifier;
import org.saturnclient.config.Theme;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
public class ClickableTexture extends Element {
SaturnIdentifier sprite;
private Runnable onClick;
public ClickableTexture(SaturnIdentifier sprite, Runnable onClick) {
this.sprite = sprite;
this.onClick = onClick;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawTexture(sprite, 0, 0, 0, 0, width, height,
ctx.isHovering() ? Theme.ACCENT.value : Theme.PRIMARY.value);
}
@Override
public void click(int mouseX, int mouseY) {
if (onClick != null) {
onClick.run();
}
}
}
@@ -1,38 +0,0 @@
package org.saturnclient.ui.elements;
import org.saturnclient.common.minecraft.IMinecraftClient;
import org.saturnclient.common.minecraft.MinecraftProvider;
import org.saturnclient.common.minecraft.bindings.SaturnIdentifier;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.SvgTexture;
public class ImageTexture extends Element {
SaturnIdentifier sprite;
public ImageTexture(SaturnIdentifier sprite) {
this.sprite = sprite;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
if (sprite.toString().endsWith(".svg")) {
IMinecraftClient client = MinecraftProvider.PROVIDER.getClient();
int renderWidth = (int) (width * client.getWindow().getFramebufferWidth() / client.getWindow().getWidth());
int renderHeight = (int) (height * client.getWindow().getFramebufferHeight()
/ client.getWindow().getHeight());
SaturnIdentifier pngId = SvgTexture.getSvg(client, sprite, renderWidth * 2, renderHeight * 2);
this.sprite = pngId;
if (pngId == null) {
System.err.println("Failed to render SVG: " + sprite);
return;
}
}
renderScope.drawTexture(this.sprite, 0, 0, 0, 0, width, height);
}
}
@@ -1,58 +0,0 @@
package org.saturnclient.ui.elements;
import org.saturnclient.config.Theme;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.anim.SlideX;
import org.saturnclient.ui.resources.Fonts;
import org.saturnclient.ui.resources.Textures;
public class Notification extends Element {
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,
Info,
Success,
}
NotificationKind kind;
String text;
String title;
public Notification(int width, int height, NotificationKind kind, String title, String text) {
this.kind = kind;
this.text = text;
this.title = title;
this.width = (int) Math.max(Fonts.getWidth(text, 1) * 0.6f, Fonts.getWidth(title, 1)) + 20;
this.height = 50;
this.x = width - this.width - 15;
this.y = height - this.height - 15;
this.animation = new SlideX(700, -width);
this.duration = 3000;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, 10, Theme.PRIMARY.value);
switch (kind) {
case Error:
renderScope.drawTexture(Textures.CIRCLE_X, 9, 9, 0, 0, 15, 15, error.value);
break;
case Info:
renderScope.drawTexture(Textures.INFO, 9, 9, 0, 0, 15, 15, info.value);
break;
case Success:
renderScope.drawTexture(Textures.CHECK, 9, 9, 0, 0, 15, 15, success.value);
break;
}
renderScope.drawText(0.7f, title, 32, 10, 1, -1);
renderScope.drawText(0.5f, text, 32, 28, 1, -1);
}
}
@@ -1,132 +0,0 @@
package org.saturnclient.ui.elements;
import java.util.ArrayList;
import java.util.List;
import org.saturnclient.config.Theme;
import org.saturnclient.config.manager.Property;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.ElementRenderer;
import org.saturnclient.ui.RenderScope;
public class Scroll extends Element {
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;
protected List<Element> children = new ArrayList<>();
int scroll = 0;
int maxScroll = 0;
public Scroll(int padding) {
this.padding = padding;
}
public void draw(Element element) {
ElementRenderer.INSTANCE.draw(children, element);
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
calculateMaxScroll();
renderScope.drawRoundedRectangle(0, 0, width, height,
Theme.BG_RADIUS.value, Theme.BACKGROUND.value);
renderScope.enableScissor(padding, padding, width - padding, height - padding);
renderScope.getMatrixStack().push();
renderScope.getMatrixStack().translate(padding, -scroll + padding, 0);
ElementRenderer.INSTANCE.render(children, ctx.elapsed, renderScope,
ctx.mouseX - padding,
ctx.mouseY - padding + scroll);
renderScope.getMatrixStack().pop();
renderScope.disableScissor();
if (maxScroll > 0) {
renderScope.drawRoundedRectangle(
width - scrollBarWidth.value - scrollBarPadding.value,
calculateScrollBarY(),
scrollBarWidth.value,
calculateScrollBarHeight(),
scrollBarRadius.value,
Theme.SCROLL.value);
}
}
@Override
public void scroll(int mouseX, int mouseY, double horizontalAmount, double verticalAmount) {
scroll -= verticalAmount;
if (scroll < 0) {
scroll = 0;
} else if (scroll > maxScroll) {
scroll = maxScroll;
}
}
@Override
public Element dimensions(int width, int height) {
super.dimensions(width, height);
maxScroll = 0;
for (Element element : children) {
maxScroll = Math.max(maxScroll, (element.y + element.height + padding) - (height - padding * 2));
}
return this;
}
int calculateScrollBarHeight() {
if (maxScroll <= 0)
return height - (scrollBarPadding.value * 2);
return Math.max(20, (height * height) / (height + maxScroll + 10)) - (scrollBarPadding.value * 2);
}
int calculateScrollBarY() {
int scrollBarHeight = calculateScrollBarHeight();
if (maxScroll <= 0)
return scrollBarPadding.value;
int y = (scroll * (height - scrollBarHeight - scrollBarPadding.value * 2)) / maxScroll + scrollBarPadding.value;
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.INSTANCE.keyPressed(children, keyCode, scanCode, modifiers);
}
@Override
public void mouseClicked(double mouseX, double mouseY, int button) {
ElementRenderer.INSTANCE.mouseClicked(children, mouseX - padding, mouseY - padding + scroll, button);
}
@Override
public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
ElementRenderer.INSTANCE.mouseDragged(children, mouseX - padding, mouseY - padding + scroll, button, deltaX,
deltaY);
}
@Override
public void mouseReleased(double mouseX, double mouseY, int button) {
ElementRenderer.INSTANCE.mouseReleased(children, mouseX - padding, mouseY - padding + scroll, button);
}
}
@@ -1,63 +0,0 @@
package org.saturnclient.ui.elements;
import org.saturnclient.common.minecraft.bindings.SaturnIdentifier;
import org.saturnclient.config.Theme;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.Utils;
public class TabMenu extends Element {
public static class TabMenuComponent {
Runnable onClick;
SaturnIdentifier sprite;
public TabMenuComponent(SaturnIdentifier sprite, Runnable onClick) {
this.onClick = onClick;
this.sprite = sprite;
}
}
TabMenuComponent[] components;
public int selectedScreen = 0;
public TabMenu(int active, TabMenuComponent... components) {
this.components = components;
width = 25 * components.length + 6;
height = 30;
this.selectedScreen = active;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, Theme.PRIMARY.value);
int col = 0;
for (TabMenuComponent component : components) {
int x = 25 * col + 5;
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.drawTexture(component.sprite, x + 5, 10, 0, 0, 10, 10,
hovering ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value);
col++;
}
}
@Override
public void click(int mouseX, int mouseY) {
int col = 0;
for (TabMenuComponent component : components) {
int x = 25 * col + 5;
if (Utils.isHovering(mouseX - x, mouseY - 5, 20, 20, 1.0f)) {
component.onClick.run();
}
col++;
}
}
}
@@ -1,22 +0,0 @@
package org.saturnclient.ui.elements;
import org.saturnclient.config.Theme;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
import org.saturnclient.ui.resources.Fonts;
public class Text extends Element {
private String text;
public Text(String text) {
this.text = text;
width = Fonts.getWidth(text, Theme.FONT.value);
height = Fonts.getHeight();
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawText(this.text, 0, 0, Theme.FONT.value, Theme.FOREGROUND.value);
}
}
@@ -1,37 +0,0 @@
package org.saturnclient.ui.elements;
import org.saturnclient.common.minecraft.bindings.SaturnIdentifier;
import org.saturnclient.config.Theme;
import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope;
public class TextureButton extends Element {
SaturnIdentifier sprite;
private Runnable onClick;
static int padding = 14;
public TextureButton(SaturnIdentifier sprite, Runnable onClick) {
this.sprite = sprite;
this.onClick = onClick;
}
@Override
public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
ctx.isHovering() ? Theme.ACCENT.value : Theme.PRIMARY.value);
int texWidth = width - padding;
int texHeight = height - padding;
renderScope.drawTexture(sprite, padding / 2, padding / 2, 0, 0, texWidth, texHeight,
ctx.isHovering() ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value);
}
@Override
public void click(int mouseX, int mouseY) {
if (onClick != null) {
onClick.run();
}
}
}
@@ -1,45 +0,0 @@
package org.saturnclient.ui.resources;
import org.saturnclient.common.minecraft.MinecraftProvider;
import org.saturnclient.common.minecraft.bindings.SaturnIdentifier;
public class Fonts {
public static final SaturnIdentifier INTER = SaturnIdentifier.of("saturnclient", "inter");
public static final SaturnIdentifier INTER_BOLD = SaturnIdentifier.of("saturnclient", "inter_bold");
public static final SaturnIdentifier DEFAULT = SaturnIdentifier.ofVanilla("default");
public static SaturnIdentifier getFont(int font) {
switch (font) {
case 0:
return DEFAULT;
case 1:
return INTER;
default:
return INTER_BOLD;
}
}
public static int getWidth(String text, int font) {
int w = 0;
for (String line : text.split("\n")) {
w = Math.max(w, MinecraftProvider.PROVIDER.getWidth(line, font));
}
if (font == 0) {
return w * 2;
} else {
return w;
}
}
public static int getHeight() {
return 18;
}
public static int centerX(int w, String text, int font) {
return (w - Fonts.getWidth(text, font)) / 2;
}
public static int centerY(int h) {
return (h - 18) / 2;
}
}
@@ -1,74 +0,0 @@
package org.saturnclient.ui.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.saturnclient.common.minecraft.IMinecraftClient;
import org.saturnclient.common.minecraft.MinecraftProvider;
import org.saturnclient.common.minecraft.bindings.SaturnIdentifier;
public class SvgTexture {
private static List<SaturnIdentifier> 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;
}
public static SaturnIdentifier getSvg(IMinecraftClient client, SaturnIdentifier svgImage, int width, int height) {
SaturnIdentifier id = SaturnIdentifier
.of(svgImage.toString().replaceAll("\\.svg$", (width + "_" + height).toString() + ".png"));
if (svgCache.contains(id)) {
return id;
}
try (InputStream svgStream = client.getResource(svgImage)) {
BufferedImage image = renderSvg(svgStream, width, height);
MinecraftProvider.PROVIDER.registerBufferedImageTexture(id, image);
return id;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

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